From: bg Date: Tue, 3 Apr 2012 19:20:45 +0000 (+0000) Subject: added rsa pkcs#1 v1.5 encryption X-Git-Url: https://git.cryptolib.org/?a=commitdiff_plain;h=06d9213f132d05f61b65acc51de6f7ad1b42f48a;p=avr-crypto-lib.git added rsa pkcs#1 v1.5 encryption --- diff --git a/host/rsa_pkcs15_check.rb b/host/rsa_pkcs15_check.rb new file mode 100644 index 0000000..cdafa8a --- /dev/null +++ b/host/rsa_pkcs15_check.rb @@ -0,0 +1,390 @@ +#!/usr/bin/ruby +# rsa_pkcs15_check.rb +=begin + This file is part of the AVR-Crypto-Lib. + Copyright (C) 2008 Daniel Otte (daniel.otte@rub.de) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +=end + +require 'rubygems' +require 'serialport' +require 'getopt/std' + +$buffer_size = 0 # set automatically in init_system +$conffile_check = Hash.new +$conffile_check.default = 0 +$debug = false +$logfile = nil + +################################################################################ +# readconfigfile # +################################################################################ + +def read_line_from_device() + repeat_counter = 10000 + l = nil + s = '' + begin + l = $sp.gets() + repeat_counter -= 1 + end while !l && repeat_counter > 0 + t = Time.new + $logfile.printf("DBG: (%02d:%02d:%02d)<< %s\n", t.hour, t.min, t.sec, l.inspect) if $debug + if l && l.include?("AVR-Crypto-Lib") + $logfile.printf("DBG: system crashed !!!\n") + exit(false) + end + return l +end + +def readconfigfile(fname, conf) + return conf if $conffile_check[fname]==1 + $conffile_check[fname]=1 + section = "default" + if not File.exists?(fname) + return conf + end + file = File.open(fname, "r") + until file.eof + line = file.gets() + next if /[\s]*#/.match(line) + if m=/\[[\s]*([^\s]*)[\s]*\]/.match(line) + section=m[1] + conf[m[1]] = Hash.new + next + end + next if ! /=/.match(line) + m=/[\s]*([^\s]*)[\s]*=[\s]*([^\s]*)/.match(line) + if m[1]=="include" + Dir.glob(m[2]){ |fn| conf = readconfigfile(fn, conf) } + else + conf[section][m[1]] = m[2] + end + end + file.close() + return conf +end + +################################################################################ +# reset_system # +################################################################################ + +def reset_system + $sp.print("\r") + sleep 0.1 + $sp.print("\r") + sleep 0.1 + $sp.print("echo off\r") + sleep 0.1 +end + + +def read_block(f) + d = Array.new + begin + l = f.gets + x = l.split.collect { |e| e.to_i(16) } + d += x + end while x.length == 16 + return d +end + +=begin +# Modulus: +# Exponent: +# Modulus: +# Public exponent: +# Exponent: +# Prime 1: +# Prime 2: +# Prime exponent 1: +# Prime exponent 2: +# Coefficient: +# Message: +# Seed: +# Encryption: + +=end + +def get_next_block(f) + ret = Hash.new + data = Array.new + begin + l = f.gets + end while l && ! m= l.match(/^#[\s](.*):[\s]*$/) + return nil if ! l + ret['tag'] = m[1] + ret['line'] = f.lineno + data = read_block(f) + ret['data'] = data + return ret +end + +$key_sequence = [ + 'Modulus', # 0 + 'Exponent', # 1 + 'Modulus', # 2 + 'Public exponent', # 3 + 'Exponent', # 4 + 'Prime 1', # 5 + 'Prime 2', # 6 + 'Prime exponent 1', # 7 + 'Prime exponent 2', # 8 + 'Coefficient', # 9 +] + +def key_consitency_check(k) + return true +end + +def process_file(f, skip_key=1, skip_vec=1) + a = get_next_block(f) + key_no = 0 + ok_counter = 0 + fail_counter = 0 + begin + if !a || ! a['tag'] == 'Modulus' + printf("ERROR: a = %s %d\n", a.inspect, __LINE__) + return + end + k_seq = Array.new + k_seq[0] = a + (1..($key_sequence.length-1)).each do |i| + a = get_next_block(f) + if ! a || a['tag'] != $key_sequence[i] + printf("ERROR: (expecting: %s) a = %s %d\n", $key_sequence[i], a.inspect, __LINE__) + end + k_seq[i] = a + end + key = convert_key(k_seq) + printf("ERROR: %d\n", __LINE__) if ! key + key_no += 1 + vec_no = 0 + printf("\n run %3d: ", key_no) + skip_key_flag = (key_no < skip_key) + load_key(key) if ! skip_key_flag + test_seq = Array.new + a = get_next_block(f) + printf("ERROR: %d\n", __LINE__) if ! a + begin + vec_no += 1 + b = get_next_block(f) + c = get_next_block(f) + tv = Hash.new + tv['msg'] = a['data'] + tv['seed'] = b['data'] + tv['enc'] = c['data'] + skip_vec_flag = (skip_key_flag || (key_no == skip_key && vec_no < skip_vec)) + if skip_vec_flag + printf('o') + else + v = check_tv(tv) + if(v == true) + printf('*') + $logfile.printf("[[Test %2d.%02d = OK]]\n", key_no, vec_no) + ok_counter += 1 + else + printf('%c', v ? '*' : '!') + $logfile.printf("[[Test %2d.%02d = FAIL]]\n", key_no, vec_no) + fail_counter += 1 + end + end + a = get_next_block(f) + end while a && a['tag'] == 'Message' + end while a && a['tag'] = 'Modulus' +# printf("\nResult: %d OK / %d FAIL ==> %s \nFinished\n", ok_counter, fail_counter, fail_counter==0 ? ':-)' : ':-(') + return ok_counter,fail_counter +end + +def convert_key(k_seq) + l = ['n', 'e', 'd', 'p', 'q', 'dP', 'dQ', 'qInv'] + r = Hash.new + return nil if k_seq[0]['data'] != k_seq[2]['data'] + return nil if k_seq[1]['data'] != k_seq[3]['data'] + 8.times do |i| + r[l[i]] = k_seq[2 + i]['data'] + end + return r +end + +def wait_for_dot + begin + s = $sp.gets() + end while !s || !s.include?('.') +end + +def load_bigint(d) + $sp.printf("%d\r", d.length) + while l = read_line_from_device() + break if /data:/.match(l) + end + printf "ERROR: got no answer from system!" if !l + i = 0 + d.each do |e| + $sp.printf("%02x", e) + i += 1 + if i % 60 == 0 +# we should now wait for incomming dot + wait_for_dot() + print('.') + end + end +end + +def hexdump(a) + i = 0 + a.each do |e| + printf("\n\t") if i % 16 == 0 + printf('%02x ', e) + i += 1 + end + puts('') if i % 16 != 1 +end + +def str_hexdump(a) + i = 0 + s = '' + a.each do |e| + s += "\n\t" if i % 16 == 0 + s += sprintf('%02x ', e) + i += 1 + end + s += "\n" if i % 16 != 1 + return s +end + +def load_key(k) + $sp.print("load-key\r") + sleep 0.1 + v = ['n', 'e', 'p', 'q', 'dP', 'dQ', 'qInv'] + v.each do |e| + load_bigint(k[e]) + $logfile.printf("DBG: loaded %s\n", e) if $debug + end + while l = read_line_from_device() + break if />/.match(l) + end +end + +def strip_leading_zeros(a) + loop do + return [] if a.length == 0 + return a if a[0] != 0 + a.delete_at(0) + end +end + +def check_tv(tv) + sleep 0.1 + $sp.print("seed-test\r") + sleep 0.1 + load_bigint(tv['msg']) + $logfile.printf("DBG: loaded %s\n", 'msg') if $debug + sleep 0.1 + tv['seed'].each { |e| $sp.printf(" %02x", e) } + while l = read_line_from_device() + break if /ciphertext:/.match(l) + end + test_enc = '' + loop do + l = read_line_from_device() + break if ! /([0-9A-Fa-f]{2}\s*)+/.match(l) + test_enc += l if l + end + test_enc_a = Array.new + test_enc = test_enc.split(/[\W\r\n]+/) + test_enc.each do |e| + v = e.sub(/[^0-9A-Fa-f]/, '') + test_enc_a << v if v.length == 2 + end + test_enc_a.collect!{ |e| e.to_i(16) } + strip_leading_zeros(test_enc_a) + strip_leading_zeros(tv['enc']) + enc_ok = (test_enc_a == tv['enc']) + if !enc_ok + $logfile.printf("DBG: ref = %s test = %s\n", str_hexdump(tv['enc']) , str_hexdump(test_enc_a)) + end + m = nil + loop do + l = read_line_from_device() + m = /(>>OK<<|ERROR)/.match(l) + break if m + end + return true if enc_ok && (m[1] == '>>OK<<') + return false +end + +######################################## +# MAIN +######################################## + + +opts = Getopt::Std.getopts('dc:f:il:s:') + +conf = Hash.new +conf = readconfigfile("/etc/testport.conf", conf) +conf = readconfigfile("~/.testport.conf", conf) +conf = readconfigfile("testport.conf", conf) +conf = readconfigfile(opts["c"], conf) if opts["c"] + +#puts conf.inspect + +puts("serial port interface version: " + SerialPort::VERSION); +$linewidth = 64 +params = { "baud" => conf["PORT"]["baud"].to_i, + "data_bits" => conf["PORT"]["databits"].to_i, + "stop_bits" => conf["PORT"]["stopbits"].to_i, + "parity" => SerialPort::NONE } +params["paraty"] = SerialPort::ODD if conf["PORT"]["paraty"].downcase == "odd" +params["paraty"] = SerialPort::EVEN if conf["PORT"]["paraty"].downcase == "even" +params["paraty"] = SerialPort::MARK if conf["PORT"]["paraty"].downcase == "mark" +params["paraty"] = SerialPort::SPACE if conf["PORT"]["paraty"].downcase == "space" + +puts("\nPort: "+conf["PORT"]["port"]+"@" + + params["baud"].to_s + + " " + + params["data_bits"].to_s + + conf["PORT"]["paraty"][0,1].upcase + + params["stop_bits"].to_s + + "\n") + +$sp = SerialPort.new(conf["PORT"]["port"], params) + +$sp.read_timeout=1000; # 5 minutes +$sp.flow_control = SerialPort::SOFT + +$debug = true if opts['d'] + +if opts['l'] + $logfile = File.open(opts['l'], 'w') +end + +$logfile = STDOUT if ! $logfile +reset_system() + +if opts['s'] && m = opts['s'].match(/([\d]+\.([\d]+))/) + sk = m[1].to_i + sv = m[2].to_i +else + sk = 1 + sv = 1 +end + +f = File.open(opts['f'], "r") +exit if !f +ok,fail = process_file(f,sk,sv) +printf("\nOK: %d FAIL: %d :-%s\n",ok,fail, fail==0 ? ')':'(') + + diff --git a/mkfiles/rsa_pkcs15.mk b/mkfiles/rsa_pkcs15.mk new file mode 100644 index 0000000..89c9fb1 --- /dev/null +++ b/mkfiles/rsa_pkcs15.mk @@ -0,0 +1,14 @@ +# Makefile for RSA +ALGO_NAME := RSA_PKCS15 + +# comment out the following line for removement of RSA from the build process +SIGNATURE += $(ALGO_NAME) + +$(ALGO_NAME)_DIR := rsa/ +$(ALGO_NAME)_INCDIR := memxor/ bigint/ noekeon/ +$(ALGO_NAME)_OBJ := bigint.o bigint_io.o rsa_basic.o rsa_pkcs15.o +$(ALGO_NAME)_TESTBIN := main-rsa_pkcs15-test.o $(CLI_STD) random_dummy.o \ + noekeon.o noekeon_prng.o memxor.o + +$(ALGO_NAME)_PERFORMANCE_TEST := performance + diff --git a/rsa/rsa_pkcs15.c b/rsa/rsa_pkcs15.c new file mode 100644 index 0000000..b1ec7ca --- /dev/null +++ b/rsa/rsa_pkcs15.c @@ -0,0 +1,134 @@ +/* rsa_pkcs15.c */ +/* + This file is part of the ARM-Crypto-Lib. + Copyright (C) 2006-2011 Daniel Otte (daniel.otte@rub.de) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include "bigint.h" +#include "rsa_basic.h" + +#define DEBUG 0 + +#if DEBUG +#include "bigint_io.h" +#include "cli.h" +#endif + +#include "random_dummy.h" + +uint16_t rsa_pkcs15_compute_padlength_B(bigint_t* modulus, uint16_t msg_length_B){ + return bigint_get_first_set_bit(modulus) / 8 + 1 - msg_length_B - 3; +} + +uint8_t rsa_encrypt_pkcs15(void* dest, uint16_t* out_length, const void* src, + uint16_t length_B, rsa_publickey_t* key, const void* pad){ + int16_t pad_length; + bigint_t x; + pad_length = rsa_pkcs15_compute_padlength_B(key->modulus, length_B); + if(pad_length<8){ +#if DEBUG + cli_putstr_P(PSTR("\r\nERROR: pad_length<8; pad_length: ")); + cli_hexdump_rev(&pad_length, 2); +#endif + return 2; /* message to long */ + } + if(!pad){ +#if DEBUG + cli_putstr_P(PSTR("\r\nauto-generating pad ...")); +#endif + uint16_t i; + uint8_t c; + for(i=0; i=m_length){ + return 1; + } + ++idx; + while(((uint8_t*)x.wordv)[idx+pad_length]!=0 && (idx+pad_length)=m_length){ + return 2; + } + *out_length = m_length - idx - pad_length - 1; + if(pad){ +#if DEBUG + cli_putstr_P(PSTR("\r\npadding block:")); + cli_hexdump_block(((uint8_t*)x.wordv)+idx, pad_length, 4, 16); + cli_putstr_P(PSTR("\r\npad @ 0x")); + cli_hexdump_rev(&pad, 2); + cli_putstr_P(PSTR("\r\ndst @ 0x")); + cli_hexdump_rev(&dest, 2); +#endif + memcpy(pad, ((uint8_t*)x.wordv)+idx, pad_length); + } + memmove(dest, ((uint8_t*)x.wordv) + idx + pad_length + 1, m_length - idx - pad_length - 1); + + return 0; +} + diff --git a/rsa/rsa_pkcs15.h b/rsa/rsa_pkcs15.h new file mode 100644 index 0000000..ff1b26d --- /dev/null +++ b/rsa/rsa_pkcs15.h @@ -0,0 +1,35 @@ +/* rsa_pkcs15.h */ +/* + This file is part of the AVR-Crypto-Lib. + Copyright (C) 2011 Daniel Otte (daniel.otte@rub.de) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef RSA_PKCS15_H_ +#define RSA_PKCS15_H_ + +#include +#include "bigint.h" + +uint16_t rsa_pkcs15_compute_padlength_B(bigint_t* modulus, uint16_t msg_length_B); + + +uint8_t rsa_encrypt_pkcs15(void* dest, uint16_t* out_length, const void* src, + uint16_t length_B, rsa_publickey_t* key, const void* pad); + +uint8_t rsa_decrypt_pkcs15(void* dest, uint16_t* out_length, const void* src, + uint16_t length_B, rsa_privatekey_t* key, void* pad); + +#endif /* RSA_PKCS15_H_ */ diff --git a/test_src/main-rsa_pkcs15-test.c b/test_src/main-rsa_pkcs15-test.c new file mode 100644 index 0000000..9d6471e --- /dev/null +++ b/test_src/main-rsa_pkcs15-test.c @@ -0,0 +1,672 @@ +/* main-dsa-test.c */ +/* + This file is part of the ARM-Crypto-Lib. + Copyright (C) 2010 Daniel Otte (daniel.otte@rub.de) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/* + * RSA test-suit + * +*/ +#include "main-test-common.h" + +#include "noekeon.h" +#include "noekeon_prng.h" +#include "bigint.h" +#include "bigint_io.h" +#include "random_dummy.h" +#include "rsa_basic.h" +#include "rsa_pkcs15.h" + +#include "performance_test.h" + +const char* algo_name = "RSA-PKCS15"; + +#define BIGINT_CEIL(x) ((((x) + sizeof(bigint_word_t) - 1) / sizeof(bigint_word_t)) * sizeof(bigint_word_t)) +#define BIGINT_OFF(x) ((sizeof(bigint_word_t) - (x) % sizeof(bigint_word_t)) % sizeof(bigint_word_t)) + +/***************************************************************************** + * additional validation-functions * + *****************************************************************************/ + +/* Modulus: */ +const uint8_t modulus[] PROGMEM = { +0xa8, 0xb3, 0xb2, 0x84, 0xaf, 0x8e, 0xb5, 0x0b, 0x38, 0x70, 0x34, 0xa8, 0x60, 0xf1, 0x46, 0xc4, +0x91, 0x9f, 0x31, 0x87, 0x63, 0xcd, 0x6c, 0x55, 0x98, 0xc8, 0xae, 0x48, 0x11, 0xa1, 0xe0, 0xab, +0xc4, 0xc7, 0xe0, 0xb0, 0x82, 0xd6, 0x93, 0xa5, 0xe7, 0xfc, 0xed, 0x67, 0x5c, 0xf4, 0x66, 0x85, +0x12, 0x77, 0x2c, 0x0c, 0xbc, 0x64, 0xa7, 0x42, 0xc6, 0xc6, 0x30, 0xf5, 0x33, 0xc8, 0xcc, 0x72, +0xf6, 0x2a, 0xe8, 0x33, 0xc4, 0x0b, 0xf2, 0x58, 0x42, 0xe9, 0x84, 0xbb, 0x78, 0xbd, 0xbf, 0x97, +0xc0, 0x10, 0x7d, 0x55, 0xbd, 0xb6, 0x62, 0xf5, 0xc4, 0xe0, 0xfa, 0xb9, 0x84, 0x5c, 0xb5, 0x14, +0x8e, 0xf7, 0x39, 0x2d, 0xd3, 0xaa, 0xff, 0x93, 0xae, 0x1e, 0x6b, 0x66, 0x7b, 0xb3, 0xd4, 0x24, +0x76, 0x16, 0xd4, 0xf5, 0xba, 0x10, 0xd4, 0xcf, 0xd2, 0x26, 0xde, 0x88, 0xd3, 0x9f, 0x16, 0xfb +}; + +/* Public exponent: */ +const uint8_t pub_exponent[] PROGMEM = { 0x01, 0x00, 0x01 }; + +/* Exponent: */ +const uint8_t priv_exponent[] PROGMEM = { +0x53, 0x33, 0x9c, 0xfd, 0xb7, 0x9f, 0xc8, 0x46, 0x6a, 0x65, 0x5c, 0x73, 0x16, 0xac, 0xa8, 0x5c, +0x55, 0xfd, 0x8f, 0x6d, 0xd8, 0x98, 0xfd, 0xaf, 0x11, 0x95, 0x17, 0xef, 0x4f, 0x52, 0xe8, 0xfd, +0x8e, 0x25, 0x8d, 0xf9, 0x3f, 0xee, 0x18, 0x0f, 0xa0, 0xe4, 0xab, 0x29, 0x69, 0x3c, 0xd8, 0x3b, +0x15, 0x2a, 0x55, 0x3d, 0x4a, 0xc4, 0xd1, 0x81, 0x2b, 0x8b, 0x9f, 0xa5, 0xaf, 0x0e, 0x7f, 0x55, +0xfe, 0x73, 0x04, 0xdf, 0x41, 0x57, 0x09, 0x26, 0xf3, 0x31, 0x1f, 0x15, 0xc4, 0xd6, 0x5a, 0x73, +0x2c, 0x48, 0x31, 0x16, 0xee, 0x3d, 0x3d, 0x2d, 0x0a, 0xf3, 0x54, 0x9a, 0xd9, 0xbf, 0x7c, 0xbf, +0xb7, 0x8a, 0xd8, 0x84, 0xf8, 0x4d, 0x5b, 0xeb, 0x04, 0x72, 0x4d, 0xc7, 0x36, 0x9b, 0x31, 0xde, +0xf3, 0x7d, 0x0c, 0xf5, 0x39, 0xe9, 0xcf, 0xcd, 0xd3, 0xde, 0x65, 0x37, 0x29, 0xea, 0xd5, 0xd1 +}; + +/* Prime 1: */ +const uint8_t p[] PROGMEM = { +0xd3, 0x27, 0x37, 0xe7, 0x26, 0x7f, 0xfe, 0x13, 0x41, 0xb2, 0xd5, 0xc0, 0xd1, 0x50, 0xa8, 0x1b, +0x58, 0x6f, 0xb3, 0x13, 0x2b, 0xed, 0x2f, 0x8d, 0x52, 0x62, 0x86, 0x4a, 0x9c, 0xb9, 0xf3, 0x0a, +0xf3, 0x8b, 0xe4, 0x48, 0x59, 0x8d, 0x41, 0x3a, 0x17, 0x2e, 0xfb, 0x80, 0x2c, 0x21, 0xac, 0xf1, +0xc1, 0x1c, 0x52, 0x0c, 0x2f, 0x26, 0xa4, 0x71, 0xdc, 0xad, 0x21, 0x2e, 0xac, 0x7c, 0xa3, 0x9d +}; + +/* Prime 2: */ +const uint8_t q[] PROGMEM = { +0xcc, 0x88, 0x53, 0xd1, 0xd5, 0x4d, 0xa6, 0x30, 0xfa, 0xc0, 0x04, 0xf4, 0x71, 0xf2, 0x81, 0xc7, +0xb8, 0x98, 0x2d, 0x82, 0x24, 0xa4, 0x90, 0xed, 0xbe, 0xb3, 0x3d, 0x3e, 0x3d, 0x5c, 0xc9, 0x3c, +0x47, 0x65, 0x70, 0x3d, 0x1d, 0xd7, 0x91, 0x64, 0x2f, 0x1f, 0x11, 0x6a, 0x0d, 0xd8, 0x52, 0xbe, +0x24, 0x19, 0xb2, 0xaf, 0x72, 0xbf, 0xe9, 0xa0, 0x30, 0xe8, 0x60, 0xb0, 0x28, 0x8b, 0x5d, 0x77 +}; + +/* Prime exponent 1: */ +const uint8_t dp[] PROGMEM = { +0x0e, 0x12, 0xbf, 0x17, 0x18, 0xe9, 0xce, 0xf5, 0x59, 0x9b, 0xa1, 0xc3, 0x88, 0x2f, 0xe8, 0x04, +0x6a, 0x90, 0x87, 0x4e, 0xef, 0xce, 0x8f, 0x2c, 0xcc, 0x20, 0xe4, 0xf2, 0x74, 0x1f, 0xb0, 0xa3, +0x3a, 0x38, 0x48, 0xae, 0xc9, 0xc9, 0x30, 0x5f, 0xbe, 0xcb, 0xd2, 0xd7, 0x68, 0x19, 0x96, 0x7d, +0x46, 0x71, 0xac, 0xc6, 0x43, 0x1e, 0x40, 0x37, 0x96, 0x8d, 0xb3, 0x78, 0x78, 0xe6, 0x95, 0xc1 +}; + +/* Prime exponent 2: */ +const uint8_t dq[] PROGMEM = { +0x95, 0x29, 0x7b, 0x0f, 0x95, 0xa2, 0xfa, 0x67, 0xd0, 0x07, 0x07, 0xd6, 0x09, 0xdf, 0xd4, 0xfc, +0x05, 0xc8, 0x9d, 0xaf, 0xc2, 0xef, 0x6d, 0x6e, 0xa5, 0x5b, 0xec, 0x77, 0x1e, 0xa3, 0x33, 0x73, +0x4d, 0x92, 0x51, 0xe7, 0x90, 0x82, 0xec, 0xda, 0x86, 0x6e, 0xfe, 0xf1, 0x3c, 0x45, 0x9e, 0x1a, +0x63, 0x13, 0x86, 0xb7, 0xe3, 0x54, 0xc8, 0x99, 0xf5, 0xf1, 0x12, 0xca, 0x85, 0xd7, 0x15, 0x83 +}; + +/* Coefficient: */ +const uint8_t qinv[] PROGMEM = { +0x4f, 0x45, 0x6c, 0x50, 0x24, 0x93, 0xbd, 0xc0, 0xed, 0x2a, 0xb7, 0x56, 0xa3, 0xa6, 0xed, 0x4d, +0x67, 0x35, 0x2a, 0x69, 0x7d, 0x42, 0x16, 0xe9, 0x32, 0x12, 0xb1, 0x27, 0xa6, 0x3d, 0x54, 0x11, +0xce, 0x6f, 0xa9, 0x8d, 0x5d, 0xbe, 0xfd, 0x73, 0x26, 0x3e, 0x37, 0x28, 0x14, 0x27, 0x43, 0x81, +0x81, 0x66, 0xed, 0x7d, 0xd6, 0x36, 0x87, 0xdd, 0x2a, 0x8c, 0xa1, 0xd2, 0xf4, 0xfb, 0xd8, 0xe1 +}; + +/* PKCS#1 v1.5 encryption of 0x20, random messages with random s0xee,ds + * --------------------------------------------------------------------------- + */ + +/* Message: */ +const uint8_t message_x[] PROGMEM = { +0x66, 0x28, 0x19, 0x4e, 0x12, 0x07, 0x3d, 0xb0, 0x3b, 0xa9, 0x4c, 0xda, 0x9e, 0xf9, 0x53, 0x23, +0x97, 0xd5, 0x0d, 0xba, 0x79, 0xb9, 0x87, 0x00, 0x4a, 0xfe, 0xfe, 0x34 +}; + +/* Seed: */ +const uint8_t seed_x[] PROGMEM = { +0x01, 0x73, 0x41, 0xae, 0x38, 0x75, 0xd5, 0xf8, 0x71, 0x01, 0xf8, 0xcc, 0x4f, 0xa9, 0xb9, 0xbc, +0x15, 0x6b, 0xb0, 0x46, 0x28, 0xfc, 0xcd, 0xb2, 0xf4, 0xf1, 0x1e, 0x90, 0x5b, 0xd3, 0xa1, 0x55, +0xd3, 0x76, 0xf5, 0x93, 0xbd, 0x73, 0x04, 0x21, 0x08, 0x74, 0xeb, 0xa0, 0x8a, 0x5e, 0x22, 0xbc, +0xcc, 0xb4, 0xc9, 0xd3, 0x88, 0x2a, 0x93, 0xa5, 0x4d, 0xb0, 0x22, 0xf5, 0x03, 0xd1, 0x63, 0x38, +0xb6, 0xb7, 0xce, 0x16, 0xdc, 0x7f, 0x4b, 0xbf, 0x9a, 0x96, 0xb5, 0x97, 0x72, 0xd6, 0x60, 0x6e, +0x97, 0x47, 0xc7, 0x64, 0x9b, 0xf9, 0xe0, 0x83, 0xdb, 0x98, 0x18, 0x84, 0xa9, 0x54, 0xab, 0x3c, +0x6f }; + +/* Encryption: */ +const uint8_t encrypted_x[] PROGMEM = { +0x50, 0xb4, 0xc1, 0x41, 0x36, 0xbd, 0x19, 0x8c, 0x2f, 0x3c, 0x3e, 0xd2, 0x43, 0xfc, 0xe0, 0x36, +0xe1, 0x68, 0xd5, 0x65, 0x17, 0x98, 0x4a, 0x26, 0x3c, 0xd6, 0x64, 0x92, 0xb8, 0x08, 0x04, 0xf1, +0x69, 0xd2, 0x10, 0xf2, 0xb9, 0xbd, 0xfb, 0x48, 0xb1, 0x2f, 0x9e, 0xa0, 0x50, 0x09, 0xc7, 0x7d, +0xa2, 0x57, 0xcc, 0x60, 0x0c, 0xce, 0xfe, 0x3a, 0x62, 0x83, 0x78, 0x9d, 0x8e, 0xa0, 0xe6, 0x07, +0xac, 0x58, 0xe2, 0x69, 0x0e, 0xc4, 0xeb, 0xc1, 0x01, 0x46, 0xe8, 0xcb, 0xaa, 0x5e, 0xd4, 0xd5, +0xcc, 0xe6, 0xfe, 0x7b, 0x0f, 0xf9, 0xef, 0xc1, 0xea, 0xbb, 0x56, 0x4d, 0xbf, 0x49, 0x82, 0x85, +0xf4, 0x49, 0xee, 0x61, 0xdd, 0x7b, 0x42, 0xee, 0x5b, 0x58, 0x92, 0xcb, 0x90, 0x60, 0x1f, 0x30, +0xcd, 0xa0, 0x7b, 0xf2, 0x64, 0x89, 0x31, 0x0b, 0xcd, 0x23, 0xb5, 0x28, 0xce, 0xab, 0x3c, 0x31 +}; + +uint8_t keys_allocated = 0; +rsa_publickey_t pub_key; +rsa_privatekey_t priv_key; + +#if 1 + #define MSG message_x + #define SEED seed_x + #define ENCRYPTED encrypted_x + #define MODULUS modulus + #define PUB_EXPONENT pub_exponent + #define PRIV_EXPONENT priv_exponent + #define P p + #define Q q + #define DP dp + #define DQ dq + #define QINV qinv +#endif + + +uint8_t convert_nibble(uint8_t c){ + if(c>='0' && c<='9'){ + return c - '0'; + } + c |= 'A' ^ 'a'; + if(c>='a' && c<='f'){ + return c - 'a' + 10; + } + return 0xff; +} + +const char *block_ignore_string=" \t\r\n,;"; +#define BUFFER_LIMIT 120 +uint16_t read_os(void* dst, uint16_t length, const char* ignore_string){ + uint16_t counter = 0; + uint16_t c; + uint8_t v, tmp = 0, idx = 0; + if(!ignore_string){ + ignore_string = block_ignore_string; + } + while(counter < length){ + c = cli_getc(); + if(c > 0xff){ + return counter; + } + if(strchr(ignore_string, c)){ + continue; + } + v = convert_nibble(c); + if(v > 0x0f){ + return counter; + } + if(idx){ + ((uint8_t*)dst)[counter++] = (tmp << 4) | v; + idx = 0; + if(counter % (BUFFER_LIMIT/2) == 0){ + cli_putc('.'); + } + }else{ + tmp = v; + idx = 1; + } + } + return counter; +} + +uint16_t own_atou(const char* str){ + uint16_t r=0; + while(*str && *str >= '0' && *str <= '9'){ + r *= 10; + r += *str++ - '0'; + } + return r; +} + +uint8_t read_bigint(bigint_t* a, char* prompt){ + uint16_t read_length, actual_length; + uint8_t off; + uint8_t *buffer; + char read_int_str[18]; + cli_putstr(prompt); + cli_putstr_P(PSTR("\r\n length: ")); + cli_getsn(read_int_str, 16); + read_length = own_atou(read_int_str); + off = (sizeof(bigint_word_t) - (read_length % sizeof(bigint_word_t))) % sizeof(bigint_word_t); + buffer = malloc(((read_length + sizeof(bigint_word_t) - 1) / sizeof(bigint_word_t)) * sizeof(bigint_word_t)); + if(!buffer){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return 2; + } + cli_putstr_P(PSTR("\r\n data: ")); + memset(buffer, 0, sizeof(bigint_word_t)); + actual_length = read_os(buffer + off, read_length, NULL); + if(actual_length != read_length){ + cli_putstr_P(PSTR("\r\nERROR: unexpected end of data!")); + free(buffer); + return 1; + } + a->wordv = (bigint_word_t*)buffer; + a->length_B = (read_length + sizeof(bigint_word_t) - 1) / sizeof(bigint_word_t); + bigint_changeendianess(a); + bigint_adjust(a); + return 0; +} + +uint8_t pre_alloc_key_crt(void){ + uint8_t c; + pub_key.modulus = malloc(sizeof(bigint_t)); + if(!pub_key.modulus){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return 2; + } + priv_key.modulus = pub_key.modulus; + priv_key.n = 5; + priv_key.components = malloc(5 * sizeof(bigint_t*)); + if(!priv_key.components){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return 2; + } + pub_key.exponent = malloc(sizeof(bigint_t)); + if(!pub_key.exponent){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return 2; + } + for(c=0; c<5; ++c){ + priv_key.components[c] = malloc(sizeof(bigint_t)); + if(!priv_key.components[c]){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return 2; + } + } + return 0; +} + +void free_key(void){ + uint8_t c; + free(pub_key.modulus->wordv); + free(pub_key.exponent->wordv); + free(pub_key.modulus); + pub_key.modulus = priv_key.modulus = NULL; + free(pub_key.exponent); + pub_key.exponent = NULL; + for(c = 0; c < priv_key.n; ++c){ + free(priv_key.components[c]->wordv); + free(priv_key.components[c]); + } + free(priv_key.components); + priv_key.components = NULL; +} + +uint8_t read_key_crt(void){ + uint8_t r; + cli_putstr_P(PSTR("\r\n== reading key (crt) ==")); + r = pre_alloc_key_crt(); + if(r) return r; + r = read_bigint(pub_key.modulus,"\r\n = module ="); + if(r) return r; + r = read_bigint(pub_key.exponent,"\r\n = public exponent ="); + if(r) return r; + r = read_bigint(priv_key.components[0],"\r\n = p (first prime) ="); + if(r) return r; + r = read_bigint(priv_key.components[1],"\r\n = q (second prime) ="); + if(r) return r; + r = read_bigint(priv_key.components[2],"\r\n = dp (p's exponent) ="); + if(r) return r; + r = read_bigint(priv_key.components[3],"\r\n = dq (q's exponent) ="); + if(r) return r; + r = read_bigint(priv_key.components[4],"\r\n = qInv (q' coefficient) ="); +/* + cli_putstr_P(PSTR("\r\nmodulus:")); + bigint_print_hex(pub_key.modulus); + cli_putstr_P(PSTR("\r\npublic exponent:")); + bigint_print_hex(pub_key.exponent); + cli_putstr_P(PSTR("\r\np:")); + bigint_print_hex(priv_key.components[0]); + cli_putstr_P(PSTR("\r\nq:")); + bigint_print_hex(priv_key.components[1]); + cli_putstr_P(PSTR("\r\ndP:")); + bigint_print_hex(priv_key.components[2]); + cli_putstr_P(PSTR("\r\ndQ:")); + bigint_print_hex(priv_key.components[3]); + cli_putstr_P(PSTR("\r\nqInv:")); + bigint_print_hex(priv_key.components[4]); +*/ + return r; +} + +uint8_t read_key_conv(void){ + uint8_t r; + cli_putstr_P(PSTR("\r\n== reading key (crt) ==")); + pub_key.modulus = malloc(sizeof(bigint_t)); + if(!pub_key.modulus){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return 2; + } + r = read_bigint(pub_key.modulus,"\r\n = module ="); + if(r) return r; + priv_key.modulus = pub_key.modulus; + priv_key.n = 1; + pub_key.exponent = malloc(sizeof(bigint_t)); + if(!pub_key.exponent){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return 2; + } + priv_key.components = malloc(sizeof(bigint_t*)); + if(!priv_key.components){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return 2; + } + priv_key.components[0] = malloc(sizeof(bigint_t)); + if(!priv_key.components[0]){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return 2; + } + r = read_bigint(pub_key.exponent,"\r\n = public exponent ="); + if(r) return r; + r = read_bigint(priv_key.components[0],"\r\n = private exponent ="); + return r; +} + +void load_priv_conventional(void){ + bigint_t *epriv; + epriv = malloc(sizeof(bigint_t)); + if(!epriv){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + epriv->length_B = (sizeof(PRIV_EXPONENT) + sizeof(bigint_word_t) - 1) / sizeof(bigint_word_t); + epriv->wordv = malloc(epriv->length_B * sizeof(bigint_word_t)); + if(!epriv->wordv){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + memcpy(epriv->wordv, PRIV_EXPONENT, sizeof(PRIV_EXPONENT)); + priv_key.components = malloc(sizeof(bigint_t*)); + priv_key.components[0] = epriv; + priv_key.n = 1; + bigint_changeendianess(epriv); + bigint_adjust(epriv); +} + + +void load_priv_crt_mono(void){ + bigint_t **v; + const uint8_t *bv[5] = {P,Q,DP,DQ,QINV}; + uint16_t sv[5] = {sizeof(P), sizeof(Q), sizeof(DP), sizeof(DQ), sizeof(QINV)}; + uint8_t i; + v = malloc(5 * sizeof(bigint_t)); + if(!v){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + priv_key.components = malloc(5*sizeof(bigint_t*)); + if(!priv_key.components){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + priv_key.n = 5; + for(i=0; i<5; ++i){ + v[i] = malloc(sizeof(bigint_t)); + v[i]->info = 0; + v[i]->length_B = (sv[i] + sizeof(bigint_word_t) - 1) / sizeof(bigint_word_t); + v[i]->wordv = calloc(v[i]->length_B , sizeof(bigint_word_t)); + if(!v[i]->wordv){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + memcpy(v[i]->wordv, bv[i], sv[i]); + bigint_changeendianess(v[i]); + bigint_adjust(v[i]); + priv_key.components[i] = v[i]; + } +} + +uint8_t load_bigint_from_os(bigint_t* a, PGM_VOID_P os, uint16_t length_B){ + a->length_B = BIGINT_CEIL(length_B) / sizeof(bigint_word_t); + a->wordv = malloc(BIGINT_CEIL(length_B)); + if(!a->wordv){ + cli_putstr_P(PSTR("\r\nOOM!\r\n")); + return 1; + } + memset(a->wordv, 0, sizeof(bigint_word_t)); + memcpy_P((uint8_t*)a->wordv + BIGINT_OFF(length_B), os, length_B); + a->info = 0; + bigint_changeendianess(a); + bigint_adjust(a); + return 0; +} + +void load_fix_rsa(void){ + if(keys_allocated){ + free_key(); + } + keys_allocated = 1; + + if(pre_alloc_key_crt()){ + cli_putstr_P(PSTR("\r\nOOM!\r\n")); + return; + } + + load_bigint_from_os(pub_key.modulus, MODULUS, sizeof(MODULUS)); + load_bigint_from_os(pub_key.exponent, PUB_EXPONENT, sizeof(PUB_EXPONENT)); + priv_key.n = 5; + load_bigint_from_os(priv_key.components[0], P, sizeof(P)); + load_bigint_from_os(priv_key.components[1], Q, sizeof(Q)); + load_bigint_from_os(priv_key.components[2], DP, sizeof(DP)); + load_bigint_from_os(priv_key.components[3], DQ, sizeof(DQ)); + load_bigint_from_os(priv_key.components[4], QINV, sizeof(QINV)); + +// load_priv_conventional(); +// load_priv_crt_mono(); +} + +void quick_test(void){ + uint8_t *ciphertext, *plaintext, rc; + uint8_t seed[sizeof(SEED)], seed_out[sizeof(SEED)]; + uint16_t clen, plen; + if(!keys_allocated){ + load_fix_rsa(); + } + ciphertext = malloc(clen = pub_key.modulus->length_B * sizeof(bigint_word_t)); + plaintext = malloc(pub_key.modulus->length_B * sizeof(bigint_word_t)); + memcpy_P(plaintext, MSG, sizeof(MSG)); + memcpy_P(seed, SEED, sizeof(SEED)); + cli_putstr_P(PSTR("\r\nplaintext:")); + cli_hexdump_block(plaintext, sizeof(MSG), 4, 16); + cli_putstr_P(PSTR("\r\nseed:")); + cli_hexdump_block(seed, sizeof(SEED), 4, 16); + cli_putstr_P(PSTR("\r\nencrypting: ...")); + + rc = rsa_encrypt_pkcs15(ciphertext, &clen, plaintext, sizeof(MSG), &pub_key, seed); + if(rc){ + cli_putstr_P(PSTR("\r\nERROR: rsa_encrypt_pkcs15 returned: ")); + cli_hexdump_byte(rc); + return; + + } + + cli_putstr_P(PSTR("\r\n\r\nciphertext:")); + cli_hexdump_block(ciphertext, clen, 4, 16); + if(clen!=sizeof(ENCRYPTED)){ + cli_putstr_P(PSTR("\r\n>>FAIL (no size match)<<")); + }else{ + if(memcmp_P(ciphertext, ENCRYPTED, clen)){ + cli_putstr_P(PSTR("\r\n>>FAIL (no content match)<<")); + }else{ + cli_putstr_P(PSTR("\r\n>>OK<<")); + } + } + + cli_putstr_P(PSTR("\r\ndecrypting: ...")); + rc = rsa_decrypt_pkcs15(plaintext, &plen, ciphertext, clen, &priv_key, seed_out); + if(rc){ + cli_putstr_P(PSTR("\r\nERROR: rsa_decrypt_pkcs15 returned: ")); + cli_hexdump_byte(rc); + return; + } + cli_putstr_P(PSTR("\r\n\r\nplaintext:")); + cli_hexdump_block(plaintext, plen, 4, 16); + cli_putstr_P(PSTR("\r\n\r\nseed (out):")); + cli_hexdump_block(seed_out, sizeof(SEED), 4, 16); + + free(ciphertext); + free(plaintext); +} + +void run_seed_test(void){ + uint8_t *msg, *ciph, *msg_; + uint16_t msg_len, ciph_len, msg_len_; + uint16_t seed_len; + uint8_t *seed, *seed_out; + char read_int_str[18]; + cli_putstr_P(PSTR("\r\n== test with given seed ==")); + cli_putstr_P(PSTR("\r\n = message =")); + cli_putstr_P(PSTR("\r\n length: ")); + cli_getsn(read_int_str, 16); + msg_len = own_atou(read_int_str); + seed_len = rsa_pkcs15_compute_padlength_B(pub_key.modulus, msg_len); + seed = malloc(seed_len); + if(!seed){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + seed_out = malloc(seed_len); + if(!seed_out){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + msg = malloc(msg_len); + if(!msg){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + ciph = malloc(bigint_length_B(pub_key.modulus)); + if(!ciph){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + msg_ = malloc(bigint_length_B(pub_key.modulus)); + if(!msg_){ + cli_putstr_P(PSTR("\r\nERROR: OOM!")); + return; + } + cli_putstr_P(PSTR("\r\n data: ")); + read_os(msg, msg_len, NULL); + cli_putstr_P(PSTR("\r\n seed (0x")); + cli_hexdump_rev(&seed_len, 2); + cli_putstr_P(PSTR(" bytes): ")); + read_os(seed, seed_len, NULL); + + cli_putstr_P(PSTR("\r\n encrypting ...")); +/* + cli_putstr_P(PSTR("\r\n plaintext:")); + cli_hexdump_block(msg, msg_len, 4, 16); + cli_putstr_P(PSTR("\r\n seed:")); + cli_hexdump_block(seed, seed_len, 4, 16); +*/ + rsa_encrypt_pkcs15(ciph, &ciph_len, msg, msg_len, &pub_key, seed); + cli_putstr_P(PSTR("\r\n ciphertext:")); + cli_hexdump_block(ciph, ciph_len, 4, 16); + cli_putstr_P(PSTR("\r\n decrypting ... ")); + rsa_decrypt_pkcs15(msg_, &msg_len_, ciph, ciph_len, &priv_key, seed_out); + cli_putstr_P(PSTR("[done]")); + if(msg_len != msg_len_){ + char tstr[16]; + cli_putstr_P(PSTR("\r\nERROR: wrong decrypted message length (")); + itoa(msg_len_, tstr, 10); + cli_putstr(tstr); + cli_putstr_P(PSTR(" instead of ")); + itoa(msg_len, tstr, 10); + cli_putstr(tstr); + cli_putc(')'); + goto end; + } + if(memcmp(msg, msg_, msg_len)){ + cli_putstr_P(PSTR("\r\nERROR: wrong decrypted message:")); + cli_hexdump_block(msg_, msg_len_, 4, 16); + cli_putstr_P(PSTR("\r\nreference:")); + cli_hexdump_block(msg, msg_len, 4, 16); + goto end; + } + + if(memcmp(seed, seed_out, seed_len)){ + cli_putstr_P(PSTR("\r\nERROR: wrong decrypted seed:")); + cli_hexdump_block(seed_out, seed_len, 4, 16); + cli_putstr_P(PSTR("\r\nreference:")); + cli_hexdump_block(seed, seed_len, 4, 16); + goto end; + } + cli_putstr_P(PSTR("\r\n >>OK<<")); +end: + free(ciph); + free(msg_); + free(msg); + free(seed_out); + free(seed); +} + +void reset_prng(void){ + uint8_t buf[16]; + memset(buf, 0, 16); + random_seed(buf); + cli_putstr_P(PSTR("\r\nPRNG reset")); +} + +void rsa_init(void){ + prng_get_byte = random8; +} + +void load_key(void){ + if(keys_allocated){ + free_key(); + } + keys_allocated = 1; + read_key_crt(); +} + +void test_dump(void){ + char lstr[16]; + int len; + cli_putstr_P(PSTR("\r\nenter dump length: ")); + cli_getsn_cecho(lstr, 15); + len = own_atou(lstr); + cli_putstr_P(PSTR("\r\ndumping 0x")); + cli_hexdump_rev(&len, 2); + cli_putstr_P(PSTR(" byte:")); + cli_hexdump_block(pub_key.modulus->wordv, len, 4, 8); +} + +/***************************************************************************** + * main * + *****************************************************************************/ + +const char echo_test_str[] PROGMEM = "echo-test"; +const char reset_prng_str[] PROGMEM = "reset-prng"; +const char load_key_str[] PROGMEM = "load-key"; +const char load_fix_key_str[] PROGMEM = "load-fix-key"; +const char quick_test_str[] PROGMEM = "quick-test"; +const char seed_test_str[] PROGMEM = "seed-test"; +const char dump_test_str[] PROGMEM = "dump-test"; +const char performance_str[] PROGMEM = "performance"; +const char echo_str[] PROGMEM = "echo"; + +const cmdlist_entry_t cmdlist[] PROGMEM = { + { reset_prng_str, NULL, reset_prng }, + { load_key_str, NULL, load_key }, + { load_fix_key_str, NULL, load_fix_rsa }, + { quick_test_str, NULL, quick_test }, + { seed_test_str, NULL, run_seed_test }, + { dump_test_str, NULL, test_dump }, +// { performance_str, NULL, testrun_performance_bigint }, + { echo_str, (void*)1, (void_fpt)echo_ctrl }, + { NULL, NULL, NULL } +}; + +void dump_sp(void){ + uint8_t x; + uint8_t *xa = &x; + cli_putstr_P(PSTR("\r\nstack pointer: ~")); + cli_hexdump_rev(&xa, 4); +} + +int main (void){ + main_setup(); + + for(;;){ + welcome_msg(algo_name); + rsa_init(); + cmd_interface(cmdlist); + } +}