Michrochipが採用している暗号ロジック。ひょんなことから、ちくと調べたりしたので記録を残す。GHDLで動かすところまで。進化したやつじゃなくレガシーなやつ。
参考ページ
仕様が超簡単。で、C言語よりもVHDLのほうが実装しやすいのでやってみる。
暗号化のブロック図(Wikipediaより)。
32bitの入力(上のブロック)を64bitの鍵を使いながら528回こねくり回して暗号化する。
VHDLで実装するにあたっては、クロックごとにシフトを実行し、NLFやらXORやらは組み合わせ回路として実装する。
復号化のブロック。
こちらも同じ方法でできそう。
で、さっそく。暗号化から。
keeloq_enc.vhd
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity keeloq_enc is port ( i_pr : in std_logic_vector(31 downto 0); i_kr : in std_logic_vector(63 downto 0); o_busy : out std_logic; o_cr : out std_logic_vector(31 downto 0); i_start : in std_logic; i_clk : in std_logic ); end keeloq_enc; architecture ppl_type of keeloq_enc is signal i_start_prev : std_logic:='0'; signal i_start_pres : std_logic:='0'; signal i_start_rising : std_logic:='0'; signal indicator_busy : std_logic:='0'; type t_state is (STATE_IDLE,STATE_RUN); signal state : t_state := STATE_IDLE; signal pr : std_logic_vector(31 downto 0):=X"00000000"; signal kr : std_logic_vector(63 downto 0):=X"0000000000000000"; signal nlf_arg : std_logic_vector(4 downto 0):="00000"; signal nlf : std_logic:='0'; constant nlf_table : std_logic_vector(31 downto 0):=X"3a5c742e"; signal newz : std_logic:='0'; signal cnt : integer range 0 to 1023:=0; constant cnt_max : integer range 0 to 1023:=528; begin i_start_pres<=i_start; i_start_rising<='1' when (i_start_prev='0' and i_start_pres='1') else '0'; o_busy<=indicator_busy; o_cr<=pr when indicator_busy='0' else X"00000000"; nlf_arg<=pr(31)&pr(26)&pr(20)&pr(9)&pr(1); nlf<=nlf_table(CONV_INTEGER(nlf_arg)); newz<=(nlf xor (pr(16) xor pr(0)) xor kr(0)); process (i_clk,i_start_rising,state) begin if i_clk'event and i_clk='1' then i_start_prev<=i_start; if i_start_rising='1' and state=STATE_IDLE then pr<=i_pr; kr<=i_kr; indicator_busy<='1'; state<=STATE_RUN; cnt<=0; end if; if state=STATE_RUN then if cnt<cnt_max then pr<=newz&pr(31 downto 1); kr<=kr(0)&kr(63 downto 1); cnt<=cnt+1; else indicator_busy<='0'; state<=STATE_IDLE; end if; end if; end if; end process; end;
keeloq_enc_tb.vhd
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity keeloq_enc_tb is end keeloq_enc_tb; architecture sim of keeloq_enc_tb is component keeloq_enc is port ( i_pr : in std_logic_vector(31 downto 0); i_kr : in std_logic_vector(63 downto 0); o_busy : out std_logic; o_cr : out std_logic_vector(31 downto 0); i_start : in std_logic; i_clk : in std_logic ); end component; signal clksys : std_logic:='0'; constant STEPSYS : TIME := 6.25 ns; signal i_pr_tb : std_logic_vector(31 downto 0):=X"f741e2db"; signal i_kr_tb : std_logic_vector(63 downto 0):=X"5cec6701b79fd949"; signal o_busy_tb : std_logic; signal o_cr_tb : std_logic_vector(31 downto 0); signal i_start_tb : std_logic:='0'; signal countup_to_start : integer range 0 to 255:=0; begin keeloq_enc_inst : component keeloq_enc port map( i_pr=>i_pr_tb, i_kr=>i_kr_tb, o_busy=>o_busy_tb, o_cr=>o_cr_tb, i_start=>i_start_tb, i_clk=>clksys ); clksys <= not(clksys) after STEPSYS/2; process(clksys)begin if clksys'event and clksys='1' then if countup_to_start<30 then i_start_tb<='0'; countup_to_start<=countup_to_start+1; elsif countup_to_start=30 then i_start_tb<='1'; countup_to_start<=countup_to_start+1; else i_start_tb<='0'; end if; end if; end process; process begin wait for 5 us; assert (false) report "Simulation End!" severity failure; end process; end;
Makefile
VHDLC=/usr/bin/ghdl SRC=keeloq_enc.vhd TBSRC=keeloq_enc_tb.vhd VCD=keeloq_enc.vcd SIMRTL=keeloq_enc_tb all : @make comp @make tb @make tb_e @make vcd comp : $(VHDLC) -a --ieee=synopsys -fexplicit $(SRC) tb : $(VHDLC) -a --ieee=synopsys -fexplicit $(TBSRC) tb_e : $(VHDLC) -e --ieee=synopsys -fexplicit $(SIMRTL) vcd : $(VHDLC) -r --ieee=synopsys -fexplicit $(SIMRTL) --vcd=$(VCD)
Hosein Hadipourさんのテストベクタの上のほうのやつと同じテストベクタにして、同じ結果になっています。
では、復号化。
keeloq_dec.vhd
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity keeloq_dec is port ( i_cr : in std_logic_vector(31 downto 0); i_kr : in std_logic_vector(63 downto 0); o_busy : out std_logic; o_pr : out std_logic_vector(31 downto 0); i_start : in std_logic; i_clk : in std_logic ); end keeloq_dec; architecture ppl_type of keeloq_dec is signal i_start_prev : std_logic:='0'; signal i_start_pres : std_logic:='0'; signal i_start_rising : std_logic:='0'; signal indicator_busy : std_logic:='0'; type t_state is (STATE_IDLE,STATE_RUN); signal state : t_state := STATE_IDLE; signal cr : std_logic_vector(31 downto 0):=X"00000000"; signal kr : std_logic_vector(63 downto 0):=X"0000000000000000"; signal nlf_arg : std_logic_vector(4 downto 0):="00000"; signal nlf : std_logic:='0'; constant nlf_table : std_logic_vector(31 downto 0):=X"3a5c742e"; signal newz : std_logic:='0'; signal cnt : integer range 0 to 1023:=0; constant cnt_max : integer range 0 to 1023:=528; begin i_start_pres<=i_start; i_start_rising<='1' when (i_start_prev='0' and i_start_pres='1') else '0'; o_busy<=indicator_busy; o_pr<=cr when indicator_busy='0' else X"00000000"; nlf_arg<=cr(30)&cr(25)&cr(19)&cr(8)&cr(0); nlf<=nlf_table(CONV_INTEGER(nlf_arg)); newz<=((nlf xor cr(31) xor kr(15)) xor cr(15)); process (i_clk,i_start_rising,state) begin if i_clk'event and i_clk='1' then i_start_prev<=i_start; if i_start_rising='1' and state=STATE_IDLE then cr<=i_cr; kr<=i_kr; indicator_busy<='1'; state<=STATE_RUN; cnt<=0; end if; if state=STATE_RUN then if cnt<cnt_max then cr<=cr(30 downto 0)&newz; kr<=kr(62 downto 0)&kr(63); cnt<=cnt+1; else indicator_busy<='0'; state<=STATE_IDLE; end if; end if; end if; end process; end;
keeloq_dec_tb.vhd
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity keeloq_dec_tb is end keeloq_dec_tb; architecture sim of keeloq_dec_tb is component keeloq_dec is port ( i_cr : in std_logic_vector(31 downto 0); i_kr : in std_logic_vector(63 downto 0); o_busy : out std_logic; o_pr : out std_logic_vector(31 downto 0); i_start : in std_logic; i_clk : in std_logic ); end component; signal clksys : std_logic:='0'; constant STEPSYS : TIME := 6.25 ns; signal i_cr_tb : std_logic_vector(31 downto 0):=X"e44f4cdf"; signal i_kr_tb : std_logic_vector(63 downto 0):=X"5cec6701b79fd949"; signal o_busy_tb : std_logic; signal o_pr_tb : std_logic_vector(31 downto 0); signal i_start_tb : std_logic:='0'; signal countup_to_start : integer range 0 to 255:=0; begin keeloq_dec_inst : component keeloq_dec port map( i_cr=>i_cr_tb, i_kr=>i_kr_tb, o_busy=>o_busy_tb, o_pr=>o_pr_tb, i_start=>i_start_tb, i_clk=>clksys ); clksys <= not(clksys) after STEPSYS/2; process(clksys)begin if clksys'event and clksys='1' then if countup_to_start<30 then i_start_tb<='0'; countup_to_start<=countup_to_start+1; elsif countup_to_start=30 then i_start_tb<='1'; countup_to_start<=countup_to_start+1; else i_start_tb<='0'; end if; end if; end process; process begin wait for 5 us; assert (false) report "Simulation End!" severity failure; end process; end;
Makefileは省略。
はい。ちゃんと元の値に復号できています。
で、このアルゴリズムですが、FPGAを160MHzで動かして3.4usかかるわけです。
暗号文と平文が1組わかっているとして総当たりで秘密鍵を割り出そうとします。そすと、
3.4e-6*(2^64)秒=200万年
かかります。まぁ総当たりでの攻撃は鍵の長さとラウンド数に依存するので、こんな簡単なアルゴリズムでも鍵を割り出すのは困難です。世の中にはいろんな方法を考える人がいて、このレガシーな暗号生成アルゴリズムはすでに、、、
ひさしぶりの更新。いやー完全にさぼりです。なんかやる気起きんのよねー。
コメントをお書きください