]> git.cryptolib.org Git - arm-crypto-lib.git/blob - host/rsa_oaep_check.rb
updated bigint & rsa
[arm-crypto-lib.git] / host / rsa_oaep_check.rb
1 #!/usr/bin/ruby
2 # rsa_oaep_check.rb
3 =begin
4     This file is part of the AVR-Crypto-Lib.
5     Copyright (C) 2008  Daniel Otte (daniel.otte@rub.de)
6
7     This program is free software: you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation, either version 3 of the License, or
10     (at your option) any later version.
11
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 =end
20
21 require 'rubygems'
22 require 'serialport'
23 require 'getopt/std'
24
25 $buffer_size = 0 # set automatically in init_system
26 $conffile_check = Hash.new
27 $conffile_check.default = 0
28 $debug = false
29 $logfile = nil
30
31 ################################################################################
32 # readconfigfile                                                               #
33 ################################################################################
34
35 def read_line_from_device()
36   repeat_counter = 10000
37   l = nil
38   s = ''
39   begin
40     l = $sp.gets()
41     repeat_counter -= 1
42   end while !l && repeat_counter > 0
43   t = Time.new
44   $logfile.printf("DBG: (%02d:%02d:%02d)<< %s\n", t.hour, t.min, t.sec, l.inspect) if $debug
45   if l && l.include?("AVR-Crypto-Lib")
46     $logfile.printf("DBG: system crashed !!!\n")
47     exit(false)
48   end
49   return l
50 end
51
52 def readconfigfile(fname, conf)
53   return conf if $conffile_check[fname]==1
54   $conffile_check[fname]=1
55   section = "default"
56   if not File.exists?(fname)
57     return conf
58   end
59   file = File.open(fname, "r")
60   until file.eof
61     line = file.gets()
62           next if /[\s]*#/.match(line)
63         if m=/\[[\s]*([^\s]*)[\s]*\]/.match(line)
64             section=m[1]
65             conf[m[1]] = Hash.new
66             next
67           end
68           next if ! /=/.match(line)
69           m=/[\s]*([^\s]*)[\s]*=[\s]*([^\s]*)/.match(line)
70           if m[1]=="include"
71             Dir.glob(m[2]){ |fn| conf = readconfigfile(fn, conf) }
72           else
73           conf[section][m[1]] = m[2]
74           end
75   end
76   file.close()
77   return conf
78 end
79
80 ################################################################################
81 # reset_system                                                                 #
82 ################################################################################
83
84 def reset_system
85   $sp.print("\r")
86   sleep 0.1
87   $sp.print("\r")
88   sleep 0.1
89   $sp.print("echo off\r")
90   sleep 0.1
91 end
92
93
94 def read_block(f)
95   d = Array.new
96   begin
97     l = f.gets
98     x = l.split.collect { |e| e.to_i(16) }
99     d += x
100   end while x.length == 16
101   return d
102 end
103
104 def goto_next_header(f)
105   while l = f.gets()
106     m = /^#\ (=|-)*[=-]{5}/.match(l)
107     t = :subblock  if m && m[1] == '-'
108     t = :mainblock if m && m[1] == '='
109     if !m && n = /^#\ (.*)$/.match(l)
110       id = n[1]
111       id.sub!(/[\r\n]/,'')
112       return t,id
113     end
114     if !m && !id
115       t = nil
116     end
117   end
118   return nil,nil if !l
119 end
120
121 def skip_file_header(f)
122   while l = f.gets()
123     return if m = /^#\ [=]{40}/.match(l)
124   end
125 end
126
127 def test_parse(f)
128   skip_file_header(f)
129   loop do
130     a,b = goto_next_header(f)
131     if !b
132       puts(">>EOF<<")
133       return
134     end
135     if a
136       printf(">>%sblock: %s\n", a==:mainblock ? "main":"sub", b)
137       next
138     end
139     printf(">item: %s\n", b)
140     d = read_block(f)
141     printf(">length: %d (0x%x)\n>data:", d.length, d.length)
142     i = 0
143     d.each do |e|
144       printf("\n>") if i % 16 == 0
145       printf(" %02x", e)
146       i += 1
147     end
148     puts('')
149   end
150 end
151 =begin
152 >item: RSA modulus n:
153 >item: RSA public exponent e: 
154 >item: RSA private exponent d: 
155 >item: Prime p: 
156 >item: Prime q: 
157 >item: p's CRT exponent dP: 
158 >item: q's CRT exponent dQ: 
159 >item: CRT coefficient qInv: 
160 =end
161
162 def read_key(f)
163   h = Hash.new
164   8.times do
165     q,id = goto_next_header(f)
166     d = read_block(f)    
167     m = /[\ \t]([^\ \t]*):[\ \t]*$/.match(id)
168     if m
169       id = m[1]
170     end
171     h[id] = d
172   end
173   req_items = ['n', 'e', 'd', 'p', 'q', 'dP', 'dQ', 'qInv']
174   req_items.each do |e|
175     printf("ERROR: key component %s is missing!\n", e) if !h[e]
176   end
177   h.each_key do |e|     
178     printf("ERROR: unknown item '%s'!\n", e) if !req_items.index(e)
179   end
180   return h
181 end
182
183 =begin
184 >item: Message to be encrypted:
185 >item: Seed:
186 >item: Encryption:
187 =end
188
189 def read_tv(f)
190   subst_hash = {
191     'Message to be encrypted:' => 'msg',
192     'Seed:' => 'seed',
193     'Encryption:' => 'enc'}
194   h = Hash.new
195   3.times do
196     q,id = goto_next_header(f)
197     d = read_block(f)
198     n = subst_hash[id]
199     printf("ERROR: unknown item '%s'!\n", id) if !n
200     h[n] = d
201   end  
202   req_items = ['msg', 'seed', 'enc']
203   req_items.each do |e|
204     printf("ERROR: testvector component %s is missing!\n", e) if !h[e]
205   end
206   while h['enc'][0] == 0
207     h['enc'].delete_at(0)
208   end 
209   return h
210 end
211
212 def wait_for_dot
213   begin
214     s = $sp.gets()
215   end while !s || !s.include?('.')
216 end
217
218 def load_bigint(d)
219   $sp.printf("%d\r", d.length)
220   while l = read_line_from_device()
221     break if /data:/.match(l)
222   end
223   printf "ERROR: got no answer from system!" if !l
224   i = 0
225   d.each do |e|
226     $sp.printf("%02x", e)
227     i += 1
228     if i % 60 == 0
229 # we should now wait for incomming dot
230       wait_for_dot()
231       print('.')
232     end
233   end
234 end
235
236 def hexdump(a)
237   i = 0
238   a.each do |e|
239     printf("\n\t") if i % 16 == 0
240     printf('%02x ', e)
241     i += 1
242   end
243   puts('') if i % 16 != 1
244 end
245
246 def str_hexdump(a)
247   i = 0
248   s = ''
249   a.each do |e|
250     s += "\n\t" if i % 16 == 0
251     s += sprintf('%02x ', e)
252     i += 1
253   end
254   s += "\n" if i % 16 != 1
255   return s
256 end
257
258 def load_key(k)
259   $sp.print("load-key\r")
260   sleep 0.1
261   v = ['n', 'e', 'p', 'q', 'dP', 'dQ', 'qInv']  
262   v.each do |e|
263     load_bigint(k[e])
264     $logfile.printf("DBG: loaded %s\n", e) if $debug
265   end 
266   while l = read_line_from_device()
267     break if />/.match(l)
268   end
269 end
270
271 def check_tv(tv)
272   sleep 0.1
273   $sp.print("seed-test\r")
274   sleep 0.1
275   load_bigint(tv['msg'])
276   $logfile.printf("DBG: loaded %s\n", 'msg') if $debug
277   sleep 0.1
278   tv['seed'].each { |e| $sp.printf(" %02x", e) } 
279   while l = read_line_from_device() 
280     break if /ciphertext:/.match(l)
281   end
282   test_enc = ''
283   loop do 
284     l = read_line_from_device()
285     break if ! /([0-9A-Fa-f]{2}\s*)+/.match(l)
286     test_enc += l if l
287   end
288   test_enc_a = Array.new
289   test_enc = test_enc.split(/[\W\r\n]+/)
290   test_enc.each do |e|
291     v = e.sub(/[^0-9A-Fa-f]/, '') 
292     test_enc_a << v if v.length == 2
293   end
294   test_enc_a.collect!{ |e| e.to_i(16) }
295   enc_ok = (test_enc_a == tv['enc'])
296   if !enc_ok
297     $logfile.printf("DBG: ref = %s test = %s\n", str_hexdump(tv['enc']) , str_hexdump(test_enc_a))
298   end
299   m = nil
300   loop do 
301     l = read_line_from_device() 
302     m = /(>>OK<<|ERROR)/.match(l)
303     break if m
304   end
305   return true if enc_ok && (m[1] == '>>OK<<') 
306   return false
307 end
308
309 def run_test(f,skip_key=1,skip_vec=1)
310   ok = 0
311   fail = 0
312   key_idx = 0
313   vec_idx = 0
314   skip_file_header(f)
315   loop do
316     a,b = goto_next_header(f)
317     $logfile.printf("DBG: a=%s b=%s\n", a.inspect, b.inspect) if $debug
318     return ok,fail if !b
319     if a == :mainblock
320 # Example 1: A 1024-bit RSA Key Pair
321       b.sub!(/[\d]+:/) { |s| sprintf("%3d,", s.to_i)} 
322       printf("\n>> %s: ", b)
323     #  (35-b.length).times { putc(' ')}
324     end
325     if a == :subblock
326       if b == 'Components of the RSA Key Pair'
327         k = read_key(f)
328         key_idx += 1
329         vec_idx = 0
330         load_key(k) if skip_key <= key_idx
331       else
332         tv = read_tv(f)
333         vec_idx += 1
334         if (key_idx > skip_key) || (key_idx == skip_key && vec_idx >= skip_vec)
335           r = check_tv(tv)
336           if r
337             ok += 1
338             putc('*')
339           else
340             fail += 1
341             putc('!')
342           end 
343         else
344           putc('o')
345         end     
346       end
347     end
348   end
349 end
350
351 ########################################
352 # MAIN
353 ########################################
354
355
356 opts = Getopt::Std.getopts("dc:f:il:s:")
357
358 conf = Hash.new
359 conf = readconfigfile("/etc/testport.conf", conf)
360 conf = readconfigfile("~/.testport.conf", conf)
361 conf = readconfigfile("testport.conf", conf)
362 conf = readconfigfile(opts["c"], conf) if opts["c"]
363
364 #puts conf.inspect
365
366 puts("serial port interface version: " + SerialPort::VERSION);
367 $linewidth = 64
368 params = { "baud"       => conf["PORT"]["baud"].to_i,
369             "data_bits" => conf["PORT"]["databits"].to_i,
370             "stop_bits" => conf["PORT"]["stopbits"].to_i,
371             "parity"    => SerialPort::NONE }
372 params["paraty"] = SerialPort::ODD   if conf["PORT"]["paraty"].downcase == "odd"
373 params["paraty"] = SerialPort::EVEN  if conf["PORT"]["paraty"].downcase == "even"
374 params["paraty"] = SerialPort::MARK  if conf["PORT"]["paraty"].downcase == "mark"
375 params["paraty"] = SerialPort::SPACE if conf["PORT"]["paraty"].downcase == "space"
376
377 puts("\nPort: "+conf["PORT"]["port"]+"@"    +
378                 params["baud"].to_s      +
379                 " "                      +
380                 params["data_bits"].to_s +
381                 conf["PORT"]["paraty"][0,1].upcase +
382                 params["stop_bits"].to_s +
383                 "\n")
384
385 $sp = SerialPort.new(conf["PORT"]["port"], params)
386
387 $sp.read_timeout=1000; # 5 minutes
388 $sp.flow_control = SerialPort::SOFT
389
390 $debug = true if opts['d']
391
392 if opts['s'] && m = opts['s'].match(/([\d]+\.([\d]+))/)
393   sk = m[1].to_i
394   sv = m[2].to_i
395 else
396   sk = 1
397   sv = 1
398 end
399
400 if opts['l']
401   $logfile = File.open(opts['l'], 'w')
402 end
403
404 $logfile = STDOUT if ! $logfile
405 $logfile.sync = true
406 reset_system()
407
408 f = File.open(opts['f'], "r")
409 exit if !f
410 ok,fail = run_test(f,sk,sv)
411 printf("\nOK: %d FAIL: %d :-%s\n",ok,fail, fail==0 ? ')':'(')
412
413