From 056b130e8185a29017a3f3feb0b7db4e84080b09 Mon Sep 17 00:00:00 2001 From: bg Date: Mon, 29 Mar 2010 09:27:25 +0000 Subject: [PATCH] more mqq stuff --- hfal-performance.c | 9 - host/data2wiki.rb | 20 +- mkfiles/mqq160-sign.mk | 12 + mkfiles/mqq160-sign_c.mk | 12 + mqq-sign/memxor.S | 66 ++++ mqq-sign/memxor.h | 7 + mqq-sign/mqq160-sign-asm.S | 540 ++++++++++++++++++++++++++++++ mqq-sign/mqq160-sign.c | 199 +++++++++++ mqq-sign/mqq160-sign.h | 33 ++ mqq-sign/mqq160-sign_P-asm.S | 547 +++++++++++++++++++++++++++++++ mqq-sign/mqq160-sign_P-stub.c | 158 +++++++++ mqq-sign/mqq160-sign_P.c | 217 ++++++++++++ mqq-sign/mqq160-sign_P.h | 27 ++ mqq-sign/mqq160-sign_testkey.c | 113 +++++++ mqq-sign/mqq160-sign_testkey.h | 42 +++ test_src/cli.h | 1 + test_src/main-mqq160-sign-test.c | 204 ++++++++++++ 17 files changed, 2196 insertions(+), 11 deletions(-) create mode 100644 mkfiles/mqq160-sign.mk create mode 100644 mkfiles/mqq160-sign_c.mk create mode 100644 mqq-sign/memxor.S create mode 100644 mqq-sign/memxor.h create mode 100644 mqq-sign/mqq160-sign-asm.S create mode 100644 mqq-sign/mqq160-sign.c create mode 100644 mqq-sign/mqq160-sign.h create mode 100644 mqq-sign/mqq160-sign_P-asm.S create mode 100644 mqq-sign/mqq160-sign_P-stub.c create mode 100644 mqq-sign/mqq160-sign_P.c create mode 100644 mqq-sign/mqq160-sign_P.h create mode 100644 mqq-sign/mqq160-sign_testkey.c create mode 100644 mqq-sign/mqq160-sign_testkey.h create mode 100644 test_src/main-mqq160-sign-test.c diff --git a/hfal-performance.c b/hfal-performance.c index 47fccf8..6f51dc6 100644 --- a/hfal-performance.c +++ b/hfal-performance.c @@ -146,15 +146,6 @@ void hfal_stacksize(const hfdesc_t* hd){ cli_putstr_P(PSTR("\r\n\r\n === ")); cli_putstr_P(hf.name); cli_putstr_P(PSTR(" stack-usage === " - "\r\n type: hashfunction" - "\r\n hashsize (bits): ")); - printvalue(hf.hashsize_b); - - cli_putstr_P(PSTR("\r\n ctxsize (bytes): ")); - printvalue(hf.ctxsize_B); - - cli_putstr_P(PSTR("\r\n blocksize (bits): ")); - printvalue(hf.blocksize_b); cli(); stack_measure_init(&smctx, PATTERN_A); diff --git a/host/data2wiki.rb b/host/data2wiki.rb index e1bc699..7250917 100644 --- a/host/data2wiki.rb +++ b/host/data2wiki.rb @@ -73,10 +73,26 @@ def process_hashfunction(fin, name, fsize) lb = fin.readline() m = lb.match(/ctx2hash \(cycles\):[\s]*([\d]*)/) convtime = m[1].to_i() + begin + lb = fin.readline() + end until m = lb.match(/init \(bytes\):[\s]*([\d]*)/) + initstack = m[1].to_i() + lb = fin.readline() + m = lb.match(/nextBlock \(bytes\):[\s]*([\d]*)/) + nextblockstack = m[1].to_i() + lb = fin.readline() + m = lb.match(/lastBlock \(bytes\):[\s]*([\d]*)/) + lastblockstack = m[1].to_i() + lb = fin.readline() + m = lb.match(/ctx2hash \(bytes\):[\s]*([\d]*)/) + convstack = m[1].to_i() + s1 = (initstack>nextblockstack)?initstack:nextblockstack + s2 = (lastblockstack>convstack)?lastblockstack:convstack + stack = (s1>s2)?s1:s2 size = get_size_string(fsize) - printf("| %20s || %3s || %3s \n| %s \n| %4d || || %4d || %4d ||" + + printf("| %20s || %3s || %3s \n| %s \n| %4d || %4d || %4d || %4d ||" + " %6d || %6d || %7.2f || %6d || || || \n|-\n" , - name, $lang, $lang, size, ctxsize, hashsize, blocksize, + name, $lang, $lang, size, ctxsize, stack, hashsize, blocksize, inittime, nextblocktime, nextblocktime.to_f/(blocksize/8), lastblocktime+convtime) end diff --git a/mkfiles/mqq160-sign.mk b/mkfiles/mqq160-sign.mk new file mode 100644 index 0000000..7970f11 --- /dev/null +++ b/mkfiles/mqq160-sign.mk @@ -0,0 +1,12 @@ +# Makefile for MQQ160-sign +ALGO_NAME := MQQ160-SIGN + +# comment out the following line for removement of MQQ160-sign from the build process +SIGNATURE += $(ALGO_NAME) + +$(ALGO_NAME)_DIR := mqq-sign/ +$(ALGO_NAME)_OBJ := mqq160-sign-asm.o mqq160-sign_P-asm.o mqq160-sign_testkey.o +$(ALGO_NAME)_TEST_BIN := main-mqq160-sign-test.o performance_test.o stack_measuring.o $(CLI_STD) +$(ALGO_NAME)_NESSIE_TEST := test +$(ALGO_NAME)_PERFORMANCE_TEST := performance + diff --git a/mkfiles/mqq160-sign_c.mk b/mkfiles/mqq160-sign_c.mk new file mode 100644 index 0000000..7b97083 --- /dev/null +++ b/mkfiles/mqq160-sign_c.mk @@ -0,0 +1,12 @@ +# Makefile for MQQ160-sign +ALGO_NAME := MQQ160-SIGN_C + +# comment out the following line for removement of MQQ160-sign from the build process +SIGNATURE += $(ALGO_NAME) + +$(ALGO_NAME)_DIR := mqq-sign/ +$(ALGO_NAME)_OBJ := mqq160-sign.o mqq160-sign_P.o mqq160-sign_testkey.o memxor.o +$(ALGO_NAME)_TEST_BIN := main-mqq160-sign-test.o performance_test.o stack_measuring.o $(CLI_STD) +$(ALGO_NAME)_NESSIE_TEST := test +$(ALGO_NAME)_PERFORMANCE_TEST := performance + diff --git a/mqq-sign/memxor.S b/mqq-sign/memxor.S new file mode 100644 index 0000000..a32058b --- /dev/null +++ b/mqq-sign/memxor.S @@ -0,0 +1,66 @@ +/* memxor.S */ +/* + 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 . +*/ + +/* + * File: memxor.S + * Author: Daniel Otte + * Date: 2008-08-07 + * License: GPLv3 or later + * Description: memxor, XORing one block into another + * + */ + +/* + * void memxor(void* dest, const void* src, uint16_t n); + */ + /* + * param dest is passed in r24:r25 + * param src is passed in r22:r23 + * param n is passed in r20:r21 + */ +.global memxor +memxor: + movw r30, r24 + movw r26, r22 + movw r24, r20 + adiw r24, 0 + breq 2f +1: + ld r20, X+ + ld r21, Z + eor r20, r21 + st Z+, r20 + sbiw r24, 1 + brne 1b +2: + ret + + + + + + + + + + + + + + diff --git a/mqq-sign/memxor.h b/mqq-sign/memxor.h new file mode 100644 index 0000000..a62a616 --- /dev/null +++ b/mqq-sign/memxor.h @@ -0,0 +1,7 @@ +#ifndef MEMXOR_H_ +#define MEMXOR_H_ +#include + +void memxor(void* dest, const void* src, uint16_t n); + +#endif diff --git a/mqq-sign/mqq160-sign-asm.S b/mqq-sign/mqq160-sign-asm.S new file mode 100644 index 0000000..c2f494f --- /dev/null +++ b/mqq-sign/mqq160-sign-asm.S @@ -0,0 +1,540 @@ +/* mqq160-sign_P-asm.S */ +/* + This file is part of the AVR-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 . +*/ +/** + * \file mqq160-sign_P-asm.S + * \email daniel.otte@rub.de + * \author Daniel Otte + * \date 2010-03-21 + * \license GPLv3 or later + * + */ + +#include "avr-asm-macros.S" + +#if 0 +static void mqq_inv_affine_transformation(uint8_t* input_bytes, uint8_t* result, const mqq160_sign_key_t* key){ + /* The matrix SInv is given as two permutations of 160 elements. */ + uint8_t j, byteindex, bitindex, bitindex_d, byteindex_d, rp1, rp5; + uint8_t *r1_ptr, *r5_ptr; + uint8_t h1[20]; + + /* Initialize H1 and H2 = 0 */ + memset(h1, 0, 20); + memset(result, 0, 20); + + /* + Fill H1 with bits of InputBytes accordingly to RP1 permutation + and fill H2 with bits of InputBytes accordingly to RP5 permutation + */ + bitindex_d = 0x80; + byteindex_d = 0; + j=160; + r1_ptr = key->rp1; + r5_ptr = key->rp5; + do{ + rp1 = pgm_read_byte(r1_ptr++); + rp5 = pgm_read_byte(r5_ptr++); + byteindex = rp1>>3; + bitindex = 0x80 >> (rp1&0x07); + if (input_bytes[byteindex] & bitindex){ + h1[byteindex_d] ^= bitindex_d; + } + + byteindex = rp5>>3; + bitindex = 0x80 >> (rp5&0x07); + if (input_bytes[byteindex] & bitindex){ + result[byteindex_d] ^= bitindex_d; + } + bitindex_d >>= 1; + if(bitindex_d==0){ + ++byteindex_d; + bitindex_d = 0x80; + } + }while(--j); + + for (j=0; j<20; j++){ + result[j] ^= h1[j] ^ h1[pgm_read_byte(j+mod20_table)] + ^ h1[pgm_read_byte(8+j+mod20_table)] + ^ h1[pgm_read_byte(12+j+mod20_table)]; + } +} +#endif + +fetch_bit: + ld r0, Z+ + mov r28, r0 + ldi r29, 0x80 + andi r28, 7 + breq 3f +2: lsr r29 + dec r28 + brne 2b +3: mov r28, r0 + lsr r28 + lsr r28 + lsr r28 + mov r0, r29 + clr r29 + add r28, r24 + adc r29, r25 + ld r28, Y + clt + and r28, r0 + breq 4f + set +4: ret + +xres_0 = 18 +xres_1 = 19 +h_0 = 20 +h_1 = 21 +xrp5_0 = 22 +xrp5_1 = 23 +inp_0 = 24 +inp_1 = 25 +tmp_0 = 22 +tmp_1 = 23 +tmp_2 = 24 +tmp_3 = 25 +tmp_4 = 18 + +/* + param input_bytes: r24:r25 + param result: r22:r23 + param key: r20:r21 +*/ +;.global mqq_inv_affine_transformation +mqq_inv_affine_transformation: + push r17 +; push r28 +; push r29 + stack_alloc 20 + adiw r30, 1 /* Z points to stack space for h1 */ + movw r28, r20 /* Y points to the key struct in RAM */ + movw xres_0, r22 + movw r26, r30 /* X points to h1[0] */ + ldd xrp5_0, Y+8 /* load pointer rp5 to xrp5 */ + ldd xrp5_1, Y+9 + movw h_0, r30 + ldd r30, Y+6 /* load pointer to rp1 in Z */ + ldd r31, Y+7 + ldi r17, 20 +20: rcall fetch_bit + bld r1, 7 + rcall fetch_bit + bld r1, 6 + rcall fetch_bit + bld r1, 5 + rcall fetch_bit + bld r1, 4 + rcall fetch_bit + bld r1, 3 + rcall fetch_bit + bld r1, 2 + rcall fetch_bit + bld r1, 1 + rcall fetch_bit + bld r1, 0 + st X+, r1 + dec r17 + brne 20b +;---- + movw r26, xres_0 /* X points to result */ + movw r30, xrp5_0 + ldi r17, 20 +20: rcall fetch_bit + bld r1, 7 + rcall fetch_bit + bld r1, 6 + rcall fetch_bit + bld r1, 5 + rcall fetch_bit + bld r1, 4 + rcall fetch_bit + bld r1, 3 + rcall fetch_bit + bld r1, 2 + rcall fetch_bit + bld r1, 1 + rcall fetch_bit + bld r1, 0 + st X+, r1 + dec r17 + brne 20b + clr r1 +; --- now we mix result with h1 + sbiw r26, 20 /* adjusting X to point at result[0] */ + movw tmp_2, h_0 + ldi r30, lo8(affine_mix_lut) + ldi r31, hi8(affine_mix_lut) + ldi r17, 20 +30: + ld tmp_0, X + movw r28, tmp_2 + ld tmp_1, Y+ + movw tmp_2, r28 + eor tmp_0, tmp_1 + movw r28, h_0 + lpm r0, Z+ + mov tmp_4, r0 + andi tmp_4, 0x0f + add r28, tmp_4 + adc r29, r1 + ld tmp_1, Y + eor tmp_0, tmp_1 + adiw r28, 4 + sbrc r0, 7 + adiw r28, 4 + ld tmp_1, Y + eor tmp_0, tmp_1 + adiw r28, 4 + sbrc r0, 6 + adiw r28, 4 + ld tmp_1, Y + eor tmp_0, tmp_1 + st X+, tmp_0 + dec r17 + brne 30b + + stack_free 20 +; pop r29 +; pop r28 + pop r17 + ret + +affine_mix_lut: + .byte 0x84, 0x85, 0x86, 0x87 + .byte 0xC0, 0xC1, 0xC2, 0xC3 + .byte 0x40, 0x41, 0x42, 0x43 + .byte 0x44, 0x45, 0x46, 0x47 + .byte 0x80, 0x81, 0x82, 0x83 + +/******************************************************************************/ + +xres = 20 +tmp_0 = 23 +tmp_1 = 22 +tmp_2 = 21 +tmp_3 = 19 +/* + param i: r24 + param b1: r22 + param b2: r20 + param key: r18:r19 +*/ +;.global mqq_q +mqq_q: +; push r28 +; push r29 +; stack_alloc 25, r26, r27 +; adiw r26, 1 /* X points to e[0] */ + movw r28, r18 + sbrs r24, 0 + adiw r28, 2 + ldd r30, Y+2 + ldd r31, Y+3 + ldi r28, 9 +10: ld r0, Z+ + st X+, r0 + dec r28 + brne 10b + sbiw r26, 9 /* adjust X to point at e[0] */ +;--- + movw r28, r18 + ld r30, Y+ /* Z points to a[0] in progmem */ + ld r31, Y + sbrs r24, 0 + rjmp 40f +20: + sbrs r22, 7 + rjmp 30f + ldi r25, 9 + movw r28, r30 +25: ld r0, Z + adiw r30, 9 + ld r24, X + eor r24, r0 + st X+, r24 + dec r25 + brne 25b + movw r30, r28 + sbiw r26, 9 +30: + adiw r30, 1 + lsl r22 + breq 60f + rjmp 20b +40: + sbrs r22, 7 + rjmp 50f + ldi r25, 9 + movw r28, r30 +45: ld r0, Z+ + ld r24, X + eor r24, r0 + st X+, r24 + dec r25 + brne 45b + movw r30, r28 + sbiw r26, 9 +50: + adiw r30, 9 + lsl r22 + breq 60f + rjmp 40b +60: +;------ all inputs are consumed, X points at e[0] +;------ So we finished with obtaining e0 .. e7 and e8 + movw r28, r26 + ldd r0, Y+8 + eor xres, r0 +;--- + +/* + We can look at the bits of e0 .. e7 as a columns of a given matrix. We want to define 8 variables that have the rows + of that matrix. The variables need to be 16-bit because we will put into the upper 8 bits the bits of e0 .. e7, + and the bits of the variable result will be the Least Significant Bits of a[0] ... a[7]. +*/ + adiw r28, 9 /* Y points at a[0] */ + ldi r25, 8 +63: + ldi r24, 8 + clr tmp_0 +65: ld tmp_1, X + lsl tmp_1 + st X+, tmp_1 + rol tmp_0 + dec r24 + brne 65b +;--- + clr tmp_1 + lsl xres + rol tmp_1 + st Y+, tmp_1 + st Y+, tmp_0 + sbiw r26, 8 + dec r25 + brne 63b +;------- First we apply upper triangular transformation + sbiw r28, 16 /* Y points at a[0] */ + movw r30, r28 /* Z points at a[0] */ + +col = 25 + ldi r24, 8 + clr col +70: + mov r1, col + ldi tmp_3, 0x80 + tst r1 + breq 72f +71: lsr tmp_3 + dec r1 + brne 71b +72: + clt + movw r28, r30 /* Y points at a[row]*/ +73: ldd tmp_0, Y+1 + and tmp_0, tmp_3 + brne 74f + set + adiw r28, 2 + rjmp 73b +74: + /* Y points at a[row] */ + /* if T is set we have to permute [Y] and [Z] */ + brtc 75f + ld tmp_0, Y + ld tmp_1, Z + st Y, tmp_1 + st Z, tmp_0 + ldd tmp_0, Y+1 + ldd tmp_1, Z+1 + std Y+1, tmp_1 + std Z+1, tmp_0 +75: /* permutation done */ + ldi r26, 7 + sub r26, col + breq 78f + movw r28, r30 +76: adiw r28, 2 + ldd tmp_0, Y+1 + and tmp_0, tmp_3 + breq 77f + ld tmp_0, Y + ld tmp_1, Z + eor tmp_0, tmp_1 + st Y, tmp_0 + ldd tmp_0, Y+1 + ldd tmp_1, Z+1 + eor tmp_0, tmp_1 + std Y+1, tmp_0 +77: + dec r26 + brne 76b +78: + adiw r30, 2 + inc col + dec r24 + brne 70b +79: +;------ Then we eliminate 1s above the main diagonal + + ldi col, 7 + ldi tmp_3, 1 + sbiw r30, 2 +80: + movw r28, r30 + mov r26, col +81: + sbiw r28, 2 + ldd tmp_0, Y+1 + and tmp_0, tmp_3 + breq 82f + ld tmp_0, Y + ld tmp_1, Z + eor tmp_0, tmp_1 + st Y, tmp_0 + ldd tmp_0, Y+1 + ldd tmp_1, Z+1 + eor tmp_0, tmp_1 + std Y+1, tmp_0 +82: + dec r26 + brne 81b + sbiw r30, 2 + lsl tmp_3 + dec col + brne 80b +89: +;------ The result is in the Least Significant Bits of a[0] ... a[7] + /* Z should point at a[0] */ + ldi r25, 8 + clr r24 +90: + ld tmp_0, Z + adiw r30, 2 + lsr tmp_0 + rol r24 + dec r25 + brne 90b +mqq_q_exit: +; stack_free 25 +; pop r29 +; pop r28 + ret + +/******************************************************************************/ + +/* + param dest: r24:r25 + param hash: r22:r23 + param key: r20:r21 +*/ + +dest_0 = 2 +dest_1 = 3 +xr1_0 = 4 +xr1_1 = 5 +key_0 = 6 +key_1 = 7 +i = 8 +c = 9 +qstack_0 = 10 +qstack_1 = 11 + +.global mqq160_sign +mqq160_sign: + push_range 2, 11 + push_range 28, 29 + stack_alloc 20, r26, r27 /* r1[20] + key */ + adiw r26, 1 /* X points to stack memory */ + movw key_0, r20 + movw xr1_0, r26 + movw dest_0, r24 + /* call to mqq_inv_affine_transformation(hash, dest, &key); */ + movw r24, r22 + movw r22, dest_0 + movw r20, key_0 + rcall mqq_inv_affine_transformation + + /* r1[0]=((uint8_t*)dest)[0]; */ + movw r26, dest_0 + movw r30, xr1_0 + ld r0, X + st Z, r0 +;---- + ldi r18, 19 + mov c, r18 + clr i + inc i + stack_alloc 25, r28, r29 + adiw r28, 1 + movw qstack_0, r28 +20: mov r24, i + movw r26, xr1_0 + add r26, i + adc r27, r1 + sbiw r26, 1 + ld r22, X + movw r26, dest_0 + add r26, i + adc r27, r1 + ld r20, X + movw r18, key_0 + movw r26, qstack_0 + rcall mqq_q + movw r26, xr1_0 + add r26, i + adc r27, r1 + st X, r24 + inc i + dec c + brne 20b + stack_free 25 +;----- + + + movw r28, key_0 + ldd r30, Y+8 + ldd r31, Y+9 + movw r26, xr1_0 + ldi r18, 20 +30: ld r20, Z+ + swap r20 + andi r20, 0xF0 + ld r21, Z+ + andi r21, 0x0F + or r20, r21 + ld r21, X + eor r21, r20 + st X+, r21 + dec r18 + brne 30b +;---- + + movw r24, xr1_0 + movw r22, dest_0 + movw r20, key_0 + rcall mqq_inv_affine_transformation + stack_free 20 + pop_range 28, 29 + pop_range 2, 11 + ret + + diff --git a/mqq-sign/mqq160-sign.c b/mqq-sign/mqq160-sign.c new file mode 100644 index 0000000..2843f7b --- /dev/null +++ b/mqq-sign/mqq160-sign.c @@ -0,0 +1,199 @@ +/* mqq160-sign.c */ +/* + C code for MQQ160-SIGN suitable for 8-bit smart cards + + It is supposed that the private key is "engraved" in + the ROM of the smart card - thus it is here stored as + predefined const arrays in "MQQ160-SIGN-PrivateKey.h" + + Programmed by + Danilo Gligoroski and Rune Jensen and Daniel Otte + March 2010. + + Verified by Danilo Gligoroski + March 2010. + +*/ + +#include +#include +#include +#include "memxor.h" +#include "mqq160-sign.h" + +#include "cli.h" + +static uint8_t mod20_table[32] PROGMEM = { + 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, +}; + +static void memxor_idx(void* dest, const void* src, uint16_t length, uint8_t dist){ + while(length--){ + *((uint8_t*)dest) ^= *((uint8_t*)src); + dest = (uint8_t*)dest + 1; + src = (uint8_t*)src + dist; + } +} +/* +This is just for testing purposes. +It should be programmed in a more flexible way +in the MQQ160-SIGN C Library. +*/ + +static void mqq_inv_affine_transformation(const uint8_t* input_bytes, uint8_t* result, const mqq160_sign_key_t* key){ + /* The matrix SInv is given as two permutations of 160 elements. */ + uint8_t j, byteindex, bitindex, bitindex_d, byteindex_d, rp1, rp5; + uint8_t *rp1_ptr, *rp5_ptr; + uint8_t h1[20]; + + + /* Initialize H1 and H2 = 0 */ + memset(h1, 0, 20); + memset(result, 0, 20); + + /* + Fill H1 with bits of InputBytes accordingly to RP1 permutation + and fill H2 with bits of InputBytes accordingly to RP5 permutation + */ + j=160; + byteindex_d = 0; + bitindex_d = 0x80; + rp1_ptr = key->rp1; + rp5_ptr = key->rp5; + do{ + rp1 = *rp1_ptr++; + rp5 = *rp5_ptr++; + byteindex = rp1>>3; + bitindex = 0x80 >> (rp1&0x07); + if (input_bytes[byteindex] & bitindex){ + h1[byteindex_d] ^= bitindex_d; + } + + byteindex = rp5>>3; + bitindex = 0x80 >> (rp5&0x07); + if (input_bytes[byteindex] & bitindex){ + result[byteindex_d] ^= bitindex_d; + } + bitindex_d >>= 1; + if(bitindex_d==0){ + ++byteindex_d; + bitindex_d = 0x80; + } + }while(--j); +// cli_putstr_P(PSTR("\r\nDBG (ref): ")); +// cli_hexdump(h1, 20); + for (j=0; j<20; j++){ + result[j] ^= h1[j] ^ h1[pgm_read_byte(j+mod20_table)] + ^ h1[pgm_read_byte(8+j+mod20_table)] + ^ h1[pgm_read_byte(12+j+mod20_table)]; + } +} + +static uint16_t MaskShort[8] = {0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100}; + +static uint8_t mqq_q(uint8_t i, uint8_t b1, uint8_t b2, const mqq160_sign_key_t* key){ + uint8_t e[9]; + uint16_t a[8]; + uint8_t result, column, row, k; + int8_t j; + uint16_t temp; + uint8_t *tmp_ptr=key->a; + if(i&1){ + memcpy(e, key->cc1, 9); + while(b1){ + if(b1&0x80){ + memxor_idx((uint8_t*)e, tmp_ptr, 9, 9); + } + tmp_ptr++; + b1 <<= 1; + } + }else{ + memcpy(e, key->cc2, 9); + while(b1){ + if(b1&0x80){ + memxor((uint8_t*)e, tmp_ptr, 9); + } + tmp_ptr+=9; + b1 <<= 1; + } + } + /* So we finished with obtaining e0 .. e7 and e8 */ + + /* We XOR e[8] with b2 and that will be initial value to transform in order to solve a linear system of equations */ + result=b2 ^ e[8]; + + /* + We can look at the bits of e0 .. e7 as a columns of a given matrix. We want to define 8 variables that have the rows + of that matrix. The variables need to be 16-bit because we will put into the upper 8 bits the bits of e0 .. e7, + and the bits of the variable result will be the Least Significant Bits of a[0] ... a[7]. + */ + for(j=0; j<8; ++j){ + row = 0; + for(k=0; k<8; ++k){ + row |= (e[k]&0x80)>>(k); + e[k]<<=1; + } + a[j]=(((uint16_t)row)<<8) | (result>>7); + result <<= 1; + } + + /* Now we finally realize Gausian elimination */ + + /* First we apply upper triangular transformation */ + for(column=0; column<8; column++) + { + row=column; + while ((a[row] & MaskShort[column]) == 0){ + row++; + } + if(row>column) + { + temp=a[column]; + a[column]=a[row]; + a[row]=temp; + } + for (j=column+1; j<8; j++) + if ((a[j]&MaskShort[column]) !=0){ + a[j] ^= a[column]; + } + } + + /* Then we eliminate 1s above the main diagonal */ + for (column=7; column>0; column--){ + for (j=column-1; j>=0; j--){ + if ((a[j]&MaskShort[column]) !=0){ + a[j] ^= a[column]; + } + } + } + /* The result is in the Least Significant Bits of a[0] ... a[7] */ + result = 0; + for(j=0; j<8; ++j){ + result <<=1; + result |= a[j]&1; + } + return(result); +} + +void mqq160_sign(void* dest, const void* hash, const mqq160_sign_key_t* key){ + uint8_t i, r1[20], byteindex; + mqq_inv_affine_transformation((uint8_t*)hash, (uint8_t*)dest, key); + r1[0]=((uint8_t*)dest)[0]; + for(i=1; i<20; ++i){ + r1[i] = mqq_q(i, r1[i-1], ((uint8_t*)dest)[i], key); + } + /* + Affine transformation is just for the second call. The constant is extracted + from the 4 LSBs of the first 40 bytes of RP5[] and xor-ed to input_bytes[]. + */ + byteindex = 0; + for (i=0; i<20; i++){ + r1[i] ^= (uint8_t)((key->rp5[byteindex])<<4) + | (uint8_t)(key->rp5[byteindex+1]&0x0F); + byteindex += 2; + } + mqq_inv_affine_transformation(r1, (uint8_t*)dest, key); +} diff --git a/mqq-sign/mqq160-sign.h b/mqq-sign/mqq160-sign.h new file mode 100644 index 0000000..bf6c5f7 --- /dev/null +++ b/mqq-sign/mqq160-sign.h @@ -0,0 +1,33 @@ +/* mqq160-sign.h */ +/* + This file is part of the AVR-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 . +*/ + +#ifndef MQQ160SIGN_H_ +#define MQQ160SIGN_H_ + +typedef struct{ + uint8_t *a; + uint8_t *cc1; + uint8_t *cc2; + uint8_t *rp1; + uint8_t *rp5; +} mqq160_sign_key_t; + +void mqq160_sign(void* dest, const void* hash, const mqq160_sign_key_t* key); + +#endif /* MQQ160SIGN_H_ */ diff --git a/mqq-sign/mqq160-sign_P-asm.S b/mqq-sign/mqq160-sign_P-asm.S new file mode 100644 index 0000000..1123455 --- /dev/null +++ b/mqq-sign/mqq160-sign_P-asm.S @@ -0,0 +1,547 @@ +/* mqq160-sign_P-asm.S */ +/* + This file is part of the AVR-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 . +*/ +/** + * \file mqq160-sign_P-asm.S + * \email daniel.otte@rub.de + * \author Daniel Otte + * \date 2010-03-21 + * \license GPLv3 or later + * + */ + +#include "avr-asm-macros.S" + +#if 0 +static void mqq_inv_affine_transformation(uint8_t* input_bytes, uint8_t* result, const mqq160_sign_key_t* key){ + /* The matrix SInv is given as two permutations of 160 elements. */ + uint8_t j, byteindex, bitindex, bitindex_d, byteindex_d, rp1, rp5; + uint8_t *r1_ptr, *r5_ptr; + uint8_t h1[20]; + + /* Initialize H1 and H2 = 0 */ + memset(h1, 0, 20); + memset(result, 0, 20); + + /* + Fill H1 with bits of InputBytes accordingly to RP1 permutation + and fill H2 with bits of InputBytes accordingly to RP5 permutation + */ + bitindex_d = 0x80; + byteindex_d = 0; + j=160; + r1_ptr = key->rp1; + r5_ptr = key->rp5; + do{ + rp1 = pgm_read_byte(r1_ptr++); + rp5 = pgm_read_byte(r5_ptr++); + byteindex = rp1>>3; + bitindex = 0x80 >> (rp1&0x07); + if (input_bytes[byteindex] & bitindex){ + h1[byteindex_d] ^= bitindex_d; + } + + byteindex = rp5>>3; + bitindex = 0x80 >> (rp5&0x07); + if (input_bytes[byteindex] & bitindex){ + result[byteindex_d] ^= bitindex_d; + } + bitindex_d >>= 1; + if(bitindex_d==0){ + ++byteindex_d; + bitindex_d = 0x80; + } + }while(--j); + + for (j=0; j<20; j++){ + result[j] ^= h1[j] ^ h1[pgm_read_byte(j+mod20_table)] + ^ h1[pgm_read_byte(8+j+mod20_table)] + ^ h1[pgm_read_byte(12+j+mod20_table)]; + } +} +#endif + +fetch_bit: + lpm r0, Z+ + mov r28, r0 + ldi r29, 0x80 + andi r28, 7 + breq 3f +2: lsr r29 + dec r28 + brne 2b +3: mov r28, r0 + lsr r28 + lsr r28 + lsr r28 + mov r0, r29 + clr r29 + add r28, r24 + adc r29, r25 + ld r28, Y + clt + and r28, r0 + breq 4f + set +4: ret + +xres_0 = 18 +xres_1 = 19 +h_0 = 20 +h_1 = 21 +xrp5_0 = 22 +xrp5_1 = 23 +inp_0 = 24 +inp_1 = 25 +tmp_0 = 22 +tmp_1 = 23 +tmp_2 = 24 +tmp_3 = 25 +tmp_4 = 18 + +/* + param input_bytes: r24:r25 + param result: r22:r23 + param key: r20:r21 +*/ +;.global mqq_inv_affine_transformation +mqq_inv_affine_transformation: + push r17 +; push r28 +; push r29 + stack_alloc 20 + adiw r30, 1 /* Z points to stack space for h1 */ + movw r28, r20 /* Y points to the key struct in RAM */ + movw xres_0, r22 + movw r26, r30 /* X points to h1[0] */ + ldd xrp5_0, Y+8 /* load pointer rp5 to xrp5 */ + ldd xrp5_1, Y+9 + movw h_0, r30 + ldd r30, Y+6 /* load pointer to rp1 in Z */ + ldd r31, Y+7 + ldi r17, 20 +20: rcall fetch_bit + bld r1, 7 + rcall fetch_bit + bld r1, 6 + rcall fetch_bit + bld r1, 5 + rcall fetch_bit + bld r1, 4 + rcall fetch_bit + bld r1, 3 + rcall fetch_bit + bld r1, 2 + rcall fetch_bit + bld r1, 1 + rcall fetch_bit + bld r1, 0 + st X+, r1 + dec r17 + brne 20b +;---- + movw r26, xres_0 /* X points to result */ + movw r30, xrp5_0 + ldi r17, 20 +20: rcall fetch_bit + bld r1, 7 + rcall fetch_bit + bld r1, 6 + rcall fetch_bit + bld r1, 5 + rcall fetch_bit + bld r1, 4 + rcall fetch_bit + bld r1, 3 + rcall fetch_bit + bld r1, 2 + rcall fetch_bit + bld r1, 1 + rcall fetch_bit + bld r1, 0 + st X+, r1 + dec r17 + brne 20b + clr r1 +; --- now we mix result with h1 + sbiw r26, 20 /* adjusting X to point at result[0] */ + movw tmp_2, h_0 + ldi r30, lo8(affine_mix_lut) + ldi r31, hi8(affine_mix_lut) + ldi r17, 20 +30: + ld tmp_0, X + movw r28, tmp_2 + ld tmp_1, Y+ + movw tmp_2, r28 + eor tmp_0, tmp_1 + movw r28, h_0 + lpm r0, Z+ + mov tmp_4, r0 + andi tmp_4, 0x0f + add r28, tmp_4 + adc r29, r1 + ld tmp_1, Y + eor tmp_0, tmp_1 + adiw r28, 4 + sbrc r0, 7 + adiw r28, 4 + ld tmp_1, Y + eor tmp_0, tmp_1 + adiw r28, 4 + sbrc r0, 6 + adiw r28, 4 + ld tmp_1, Y + eor tmp_0, tmp_1 + st X+, tmp_0 + dec r17 + brne 30b + + stack_free 20 +; pop r29 +; pop r28 + pop r17 + ret + +affine_mix_lut: + .byte 0x84, 0x85, 0x86, 0x87 + .byte 0xC0, 0xC1, 0xC2, 0xC3 + .byte 0x40, 0x41, 0x42, 0x43 + .byte 0x44, 0x45, 0x46, 0x47 + .byte 0x80, 0x81, 0x82, 0x83 + +/******************************************************************************/ + +xres = 20 +tmp_0 = 23 +tmp_1 = 22 +tmp_2 = 21 +tmp_3 = 19 +/* + param i: r24 + param b1: r22 + param b2: r20 + param key: r18:r19 +*/ +;.global mqq_q +mqq_q: +; push r28 +; push r29 +; stack_alloc 25, r26, r27 +; adiw r26, 1 /* X points to e[0] */ + movw r28, r18 + sbrs r24, 0 + adiw r28, 2 + ldd r30, Y+2 + ldd r31, Y+3 + ldi r28, 9 +10: lpm r0, Z+ + st X+, r0 + dec r28 + brne 10b + sbiw r26, 9 /* adjust X to point at e[0] */ +;--- + movw r28, r18 + ld r30, Y+ /* Z points to a[0] in progmem */ + ld r31, Y + sbrs r24, 0 + rjmp 40f +20: + sbrs r22, 7 + rjmp 30f + ldi r25, 9 + movw r28, r30 +25: lpm r0, Z + adiw r30, 9 + ld r24, X + eor r24, r0 + st X+, r24 + dec r25 + brne 25b + movw r30, r28 + sbiw r26, 9 +30: + adiw r30, 1 + lsl r22 + breq 60f + rjmp 20b +40: + sbrs r22, 7 + rjmp 50f + ldi r25, 9 + movw r28, r30 +45: lpm r0, Z+ + ld r24, X + eor r24, r0 + st X+, r24 + dec r25 + brne 45b + movw r30, r28 + sbiw r26, 9 +50: + adiw r30, 9 + lsl r22 + breq 60f + rjmp 40b +60: +;------ all inputs are consumed, X points at e[0] +;------ So we finished with obtaining e0 .. e7 and e8 + movw r28, r26 + ldd r0, Y+8 + eor xres, r0 +;--- + +/* + We can look at the bits of e0 .. e7 as a columns of a given matrix. We want to define 8 variables that have the rows + of that matrix. The variables need to be 16-bit because we will put into the upper 8 bits the bits of e0 .. e7, + and the bits of the variable result will be the Least Significant Bits of a[0] ... a[7]. +*/ + adiw r28, 9 /* Y points at a[0] */ + ldi r25, 8 +63: + ldi r24, 8 + clr tmp_0 +65: ld tmp_1, X + lsl tmp_1 + st X+, tmp_1 + rol tmp_0 + dec r24 + brne 65b +;--- + clr tmp_1 + lsl xres + rol tmp_1 + st Y+, tmp_1 + st Y+, tmp_0 + sbiw r26, 8 + dec r25 + brne 63b +;------- First we apply upper triangular transformation + sbiw r28, 16 /* Y points at a[0] */ + movw r30, r28 /* Z points at a[0] */ + +col = 25 + ldi r24, 8 + clr col +70: + mov r1, col + ldi tmp_3, 0x80 + tst r1 + breq 72f +71: lsr tmp_3 + dec r1 + brne 71b +72: + clt + movw r28, r30 /* Y points at a[row]*/ +73: ldd tmp_0, Y+1 + and tmp_0, tmp_3 + brne 74f + set + adiw r28, 2 + rjmp 73b +74: + /* Y points at a[row] */ + /* if T is set we have to permute [Y] and [Z] */ + brtc 75f + ld tmp_0, Y + ld tmp_1, Z + st Y, tmp_1 + st Z, tmp_0 + ldd tmp_0, Y+1 + ldd tmp_1, Z+1 + std Y+1, tmp_1 + std Z+1, tmp_0 +75: /* permutation done */ + ldi r26, 7 + sub r26, col + breq 78f + movw r28, r30 +76: adiw r28, 2 + ldd tmp_0, Y+1 + and tmp_0, tmp_3 + breq 77f + ld tmp_0, Y + ld tmp_1, Z + eor tmp_0, tmp_1 + st Y, tmp_0 + ldd tmp_0, Y+1 + ldd tmp_1, Z+1 + eor tmp_0, tmp_1 + std Y+1, tmp_0 +77: + dec r26 + brne 76b +78: + adiw r30, 2 + inc col + dec r24 + brne 70b +79: +;------ Then we eliminate 1s above the main diagonal + + ldi col, 7 + ldi tmp_3, 1 + sbiw r30, 2 +80: + movw r28, r30 + mov r26, col +81: + sbiw r28, 2 + ldd tmp_0, Y+1 + and tmp_0, tmp_3 + breq 82f + ld tmp_0, Y + ld tmp_1, Z + eor tmp_0, tmp_1 + st Y, tmp_0 + ldd tmp_0, Y+1 + ldd tmp_1, Z+1 + eor tmp_0, tmp_1 + std Y+1, tmp_0 +82: + dec r26 + brne 81b + sbiw r30, 2 + lsl tmp_3 + dec col + brne 80b +89: +;------ The result is in the Least Significant Bits of a[0] ... a[7] + /* Z should point at a[0] */ + ldi r25, 8 + clr r24 +90: + ld tmp_0, Z + adiw r30, 2 + lsr tmp_0 + rol r24 + dec r25 + brne 90b +mqq_q_exit: +; stack_free 25 +; pop r29 +; pop r28 + ret + +/******************************************************************************/ + +/* + param dest: r24:r25 + param hash: r22:r23 + param key: r20:r21 +*/ + +dest_0 = 2 +dest_1 = 3 +xr1_0 = 4 +xr1_1 = 5 +key_0 = 6 +key_1 = 7 +i = 8 +c = 9 +qstack_0 = 10 +qstack_1 = 11 + +.global mqq160_sign_P +mqq160_sign_P: + push_range 2, 11 + push_range 28, 29 + stack_alloc 10+20, r26, r27 /* r1[20] + key */ + adiw r26, 1 /* X points to stack memory */ + movw key_0, r26 + /* load key structure */ + movw r30, r20 + ldi r18, 10 +10: lpm r0, Z+ + st X+, r0 + dec r18 + brne 10b + movw xr1_0, r26 + movw dest_0, r24 + /* call to mqq_inv_affine_transformation(hash, dest, &key); */ + movw r24, r22 + movw r22, dest_0 + movw r20, key_0 + rcall mqq_inv_affine_transformation + /* r1[0]=((uint8_t*)dest)[0]; */ + + movw r26, dest_0 + movw r30, xr1_0 + ld r0, X + st Z, r0 +;---- + ldi r18, 19 + mov c, r18 + clr i + inc i + stack_alloc 25, r28, r29 + adiw r28, 1 + movw qstack_0, r28 +20: mov r24, i + movw r26, xr1_0 + add r26, i + adc r27, r1 + sbiw r26, 1 + ld r22, X + movw r26, dest_0 + add r26, i + adc r27, r1 + ld r20, X + movw r18, key_0 + movw r26, qstack_0 + rcall mqq_q + movw r26, xr1_0 + add r26, i + adc r27, r1 + st X, r24 + inc i + dec c + brne 20b + stack_free 25 +;----- + + + movw r28, key_0 + ldd r30, Y+8 + ldd r31, Y+9 + movw r26, xr1_0 + ldi r18, 20 +30: lpm r20, Z+ + swap r20 + andi r20, 0xF0 + lpm r21, Z+ + andi r21, 0x0F + or r20, r21 + ld r21, X + eor r21, r20 + st X+, r21 + dec r18 + brne 30b +;---- + + movw r24, xr1_0 + movw r22, dest_0 + movw r20, key_0 + rcall mqq_inv_affine_transformation + stack_free 30 + pop_range 28, 29 + pop_range 2, 11 + ret + + diff --git a/mqq-sign/mqq160-sign_P-stub.c b/mqq-sign/mqq160-sign_P-stub.c new file mode 100644 index 0000000..d8c889f --- /dev/null +++ b/mqq-sign/mqq160-sign_P-stub.c @@ -0,0 +1,158 @@ +/* mqq160-sign.c */ +/* + This file is part of the AVR-Crypto-Lib. + Copyright (C) 2010 Danilo Gligoroski, 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 . +*/ +/* + C code for MQQ160-SIGN suitable for 8-bit smart cards + + It is supposed that the private key is "engraved" in + the ROM of the smart card - thus it is here stored as + predefined const arrays in "MQQ160-SIGN-PrivateKey.h" + + Programmed by Danilo Gligoroski, 18 Mar 2010. +*/ + +#include +#include +#include +#include +#include +#include "memxor.h" +#include "mqq160-sign.h" + +/* +This is just for testing purposes. +It should be programmed in a more flexible way +in the MQQ160-SIGN C Library. +*/ + + +void mqq_inv_affine_transformation(uint8_t* input_bytes, uint8_t* result, const mqq160_sign_key_t* key); +uint8_t mqq_q(uint8_t i, uint8_t b1, uint8_t b2, const mqq160_sign_key_t* key); + + +#if 0 +static uint16_t MaskShort[8] = {0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100}; + +static uint8_t mqq_q(uint8_t i, uint8_t b1, uint8_t b2, const mqq160_sign_key_t* key){ + uint8_t e[9]; + uint16_t a[8]; + uint8_t result, column, row, k; + int8_t j; + uint16_t temp; + uint8_t *tmp_ptr=key->a; + if(i&1){ + memcpy_P(e, key->cc1, 9); + while(b1){ + if(b1&0x80){ + memxor_idx_P((uint8_t*)e, tmp_ptr, 9, 9); + } + tmp_ptr++; + b1 <<= 1; + } + }else{ + memcpy_P(e, key->cc2, 9); + while(b1){ + if(b1&0x80){ + memxor_P((uint8_t*)e, tmp_ptr, 9); + } + tmp_ptr+=9; + b1 <<= 1; + } + } + /* So we finished with obtaining e0 .. e7 and e8 */ + + /* We XOR e[8] with b2 and that will be initial value to transform in order to solve a linear system of equations */ + result=b2 ^ e[8]; + + /* + We can look at the bits of e0 .. e7 as a columns of a given matrix. We want to define 8 variables that have the rows + of that matrix. The variables need to be 16-bit because we will put into the upper 8 bits the bits of e0 .. e7, + and the bits of the variable result will be the Least Significant Bits of a[0] ... a[7]. + */ + for(j=0; j<8; ++j){ + row = 0; + for(k=0; k<8; ++k){ + row |= (e[k]&0x80)>>(k); + e[k]<<=1; + } + a[j]=(((uint16_t)row)<<8) | (result>>7); + result <<= 1; + } + + /* Now we finally realize Gausian elimination */ + + /* First we apply upper triangular transformation */ + for(column=0; column<8; column++) + { + row=column; + while ((a[row] & MaskShort[column]) == 0){ + row++; + } + if(row>column) + { + temp=a[column]; + a[column]=a[row]; + a[row]=temp; + } + for (j=column+1; j<8; j++) + if ((a[j]&MaskShort[column]) !=0) + a[j] ^= a[column]; + } + + /* Then we eliminate 1s above the main diagonal */ + for (column=7; column>0; column--){ + for (j=column-1; j>=0; j--){ + if ((a[j]&MaskShort[column]) !=0){ + a[j] ^= a[column]; + } + } + } + /* The result is in the Least Significant Bits of a[0] ... a[7] */ + result = 0; + for(j=0; j<8; ++j){ + result <<=1; + result |= a[j]&1; + } + return(result); +} + +#endif + +void mqq160_sign_P(void* dest, const void* hash, const mqq160_sign_key_t* key_P){ + uint8_t i, r1[20], byteindex; + mqq160_sign_key_t key; + + memcpy_P(&key, key_P, sizeof(mqq160_sign_key_t)); + + mqq_inv_affine_transformation((uint8_t*)hash, (uint8_t*)dest, &key); + r1[0]=((uint8_t*)dest)[0]; + for(i=1; i<20; ++i){ + r1[i] = mqq_q(i, r1[i-1], ((uint8_t*)dest)[i], &key); + } + /* + Affine transformation is just for the second call. The constant is extracted + from the 4 LSBs of the first 40 bytes of RP5[] and xor-ed to input_bytes[]. + */ + byteindex = 0; + for (i=0; i<20; i++){ + r1[i] ^= (uint8_t)(pgm_read_byte(key.rp5+byteindex)<<4) + | (uint8_t)(pgm_read_byte(key.rp5+byteindex+1)&0x0F); + byteindex += 2; + } + mqq_inv_affine_transformation(r1, (uint8_t*)dest, &key); +} diff --git a/mqq-sign/mqq160-sign_P.c b/mqq-sign/mqq160-sign_P.c new file mode 100644 index 0000000..82c90ea --- /dev/null +++ b/mqq-sign/mqq160-sign_P.c @@ -0,0 +1,217 @@ +/* mqq160-sign.c */ +/* + This file is part of the AVR-Crypto-Lib. + Copyright (C) 2010 Danilo Gligoroski, 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 . +*/ +/* + C code for MQQ160-SIGN suitable for 8-bit smart cards + + It is supposed that the private key is "engraved" in + the ROM of the smart card - thus it is here stored as + predefined const arrays in "MQQ160-SIGN-PrivateKey.h" + + Programmed by Danilo Gligoroski, 18 Mar 2010. +*/ + +#include +#include +#include +#include +#include +#include "memxor.h" +#include "mqq160-sign.h" + +static uint8_t mod20_table[32] PROGMEM = { + 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, +}; + +static void memxor_P(void* dest, const void* src, uint16_t length){ + while(length--){ + *((uint8_t*)dest) ^= pgm_read_byte(src); + dest = (uint8_t*)dest +1; + src = (uint8_t*)src +1; + } +} + +static void memxor_idx_P(uint8_t* dest, const uint8_t* src, uint16_t length, uint8_t dist){ + while(length--){ + *((uint8_t*)dest) ^= pgm_read_byte((uint8_t*)src); + dest = (uint8_t*)dest + 1; + src = (uint8_t*)src + dist; + } +} +/* +This is just for testing purposes. +It should be programmed in a more flexible way +in the MQQ160-SIGN C Library. +*/ + +static void mqq_inv_affine_transformation(uint8_t* input_bytes, uint8_t* result, const mqq160_sign_key_t* key){ + /* The matrix SInv is given as two permutations of 160 elements. */ + uint8_t j, byteindex, bitindex, bitindex_d, byteindex_d, rp1, rp5; + uint8_t *r1_ptr, *r5_ptr; + uint8_t h1[20]; + + /* Initialize H1 and H2 = 0 */ + memset(h1, 0, 20); + memset(result, 0, 20); + + /* + Fill H1 with bits of InputBytes accordingly to RP1 permutation + and fill H2 with bits of InputBytes accordingly to RP5 permutation + */ + bitindex_d = 0x80; + byteindex_d = 0; + j=160; + r1_ptr = key->rp1; + r5_ptr = key->rp5; + do{ + rp1 = pgm_read_byte(r1_ptr++); + rp5 = pgm_read_byte(r5_ptr++); + byteindex = rp1>>3; + bitindex = 0x80 >> (rp1&0x07); + if (input_bytes[byteindex] & bitindex){ + h1[byteindex_d] ^= bitindex_d; + } + + byteindex = rp5>>3; + bitindex = 0x80 >> (rp5&0x07); + if (input_bytes[byteindex] & bitindex){ + result[byteindex_d] ^= bitindex_d; + } + bitindex_d >>= 1; + if(bitindex_d==0){ + ++byteindex_d; + bitindex_d = 0x80; + } + }while(--j); + + for (j=0; j<20; j++){ + result[j] ^= h1[j] ^ h1[pgm_read_byte(j+mod20_table)] + ^ h1[pgm_read_byte(8+j+mod20_table)] + ^ h1[pgm_read_byte(12+j+mod20_table)]; + } +} + +static uint16_t MaskShort[8] = {0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100}; + +static uint8_t mqq_q(uint8_t i, uint8_t b1, uint8_t b2, const mqq160_sign_key_t* key){ + uint8_t e[9]; + uint16_t a[8]; + uint8_t result, column, row, k; + int8_t j; + uint16_t temp; + uint8_t *tmp_ptr=key->a; + if(i&1){ + memcpy_P(e, key->cc1, 9); + while(b1){ + if(b1&0x80){ + memxor_idx_P((uint8_t*)e, tmp_ptr, 9, 9); + } + tmp_ptr++; + b1 <<= 1; + } + }else{ + memcpy_P(e, key->cc2, 9); + while(b1){ + if(b1&0x80){ + memxor_P((uint8_t*)e, tmp_ptr, 9); + } + tmp_ptr+=9; + b1 <<= 1; + } + } + /* So we finished with obtaining e0 .. e7 and e8 */ + + /* We XOR e[8] with b2 and that will be initial value to transform in order to solve a linear system of equations */ + result=b2 ^ e[8]; + + /* + We can look at the bits of e0 .. e7 as a columns of a given matrix. We want to define 8 variables that have the rows + of that matrix. The variables need to be 16-bit because we will put into the upper 8 bits the bits of e0 .. e7, + and the bits of the variable result will be the Least Significant Bits of a[0] ... a[7]. + */ + for(j=0; j<8; ++j){ + row = 0; + for(k=0; k<8; ++k){ + row |= (e[k]&0x80)>>(k); + e[k]<<=1; + } + a[j]=(((uint16_t)row)<<8) | (result>>7); + result <<= 1; + } + + /* Now we finally realize Gausian elimination */ + + /* First we apply upper triangular transformation */ + for(column=0; column<8; column++) + { + row=column; + while ((a[row] & MaskShort[column]) == 0){ + row++; + } + if(row>column) + { + temp=a[column]; + a[column]=a[row]; + a[row]=temp; + } + for (j=column+1; j<8; j++) + if ((a[j]&MaskShort[column]) !=0) + a[j] ^= a[column]; + } + + /* Then we eliminate 1s above the main diagonal */ + for (column=7; column>0; column--){ + for (j=column-1; j>=0; j--){ + if ((a[j]&MaskShort[column]) !=0){ + a[j] ^= a[column]; + } + } + } + /* The result is in the Least Significant Bits of a[0] ... a[7] */ + result = 0; + for(j=0; j<8; ++j){ + result <<=1; + result |= a[j]&1; + } + return(result); +} + +void mqq160_sign_P(void* dest, const void* hash, const mqq160_sign_key_t* key_P){ + uint8_t i, r1[20], byteindex; + mqq160_sign_key_t key; + memcpy_P(&key, key_P, sizeof(mqq160_sign_key_t)); + mqq_inv_affine_transformation((uint8_t*)hash, (uint8_t*)dest, &key); + r1[0]=((uint8_t*)dest)[0]; + for(i=1; i<20; ++i){ + r1[i] = mqq_q(i, r1[i-1], ((uint8_t*)dest)[i], &key); + } + /* + Affine transformation is just for the second call. The constant is extracted + from the 4 LSBs of the first 40 bytes of RP5[] and xor-ed to input_bytes[]. + */ + byteindex = 0; + for (i=0; i<20; i++){ + r1[i] ^= (uint8_t)(pgm_read_byte(key.rp5+byteindex)<<4) + | (uint8_t)(pgm_read_byte(key.rp5+byteindex+1)&0x0F); + byteindex += 2; + } + mqq_inv_affine_transformation(r1, (uint8_t*)dest, &key); +} diff --git a/mqq-sign/mqq160-sign_P.h b/mqq-sign/mqq160-sign_P.h new file mode 100644 index 0000000..19208a2 --- /dev/null +++ b/mqq-sign/mqq160-sign_P.h @@ -0,0 +1,27 @@ +/* mqq160-sign.h */ +/* + This file is part of the AVR-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 . +*/ + +#ifndef MQQ160SIGN_P_H_ +#define MQQ160SIGN_P_H_ + +#include "mqq160-sign.h" + +void mqq160_sign_P(void* dest, const void* hash, const mqq160_sign_key_t* key_P); + +#endif /* MQQ160SIGN_P_H_ */ diff --git a/mqq-sign/mqq160-sign_testkey.c b/mqq-sign/mqq160-sign_testkey.c new file mode 100644 index 0000000..8fef41a --- /dev/null +++ b/mqq-sign/mqq160-sign_testkey.c @@ -0,0 +1,113 @@ +/* mqq160-sign_testkey.c */ +/* + This file is part of the AVR-Crypto-Lib. + Copyright (C) 2010 Danilo Gligoroski, 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 "mqq160-sign.h" +#include "mqq160-sign_testkey.h" + +/* +This is the private key of MQQ defined by one +quadratic quasigroup of order 2^8 given as 81 uint8_ts +and one nonsingular matrix SInv given as two arrays +RP1[] and RP5[] of 160 uint8_ts. +*/ + + +static uint8_t a[9*9-1] PROGMEM = + { 171, 171, 165, 56, 121, 136, 79, 108, 2, + 255, 255, 165, 108, 45, 220, 79, 108, 88, + 54, 108, 103, 21, 74, 119, 141, 204, 221, + 210, 220, 30, 201, 215, 199, 74, 95, 173, + 165, 241, 160, 190, 38, 134, 68, 103, 140, + 84, 84, 68, 157, 81, 65, 30, 11, 48, + 136, 136, 79, 21, 136, 199, 79, 0, 171, + 136, 210, 27, 27, 220, 157, 65, 84, 45, + 225, 61, 8, 232, 235, 49, 22, 146 }; + + +static uint8_t cc1[9] PROGMEM = { 2, 88, 221, 173, 140, 48, 171, 45, 252 }; +static uint8_t cc2[9] PROGMEM = {225, 61, 8, 232, 235, 49, 22, 146, 252 }; + +/* The matrix SInv is stored in ROM as two onedimensional +arrays RP1[] and RP5[] of 160 uint8_ts */ +static uint8_t rp1[160] PROGMEM = { + 111, 137, 49, 134, 9, 116, 11, 52, 43, 55, + 74, 130, 119, 144, 31, 7, 72, 79, 105, 59, + 57, 120, 50, 94, 141, 135, 149, 44, 109, 100, + 113, 1, 143, 126, 117, 37, 65, 67, 152, 107, + 10, 98, 15, 23, 138, 19, 121, 18, 28, 156, + 123, 106, 48, 29, 97, 34, 85, 157, 64, 3, + 60, 35, 24, 32, 108, 147, 158, 21, 129, 84, + 5, 70, 118, 112, 30, 68, 47, 40, 150, 13, + 61, 73, 132, 22, 95, 153, 4, 76, 87, 114, + 127, 62, 27, 36, 125, 45, 142, 39, 101, 63, + 88, 96, 12, 115, 82, 91, 159, 93, 155, 154, + 148, 110, 25, 0, 41, 20, 54, 26, 14, 83, + 81, 80, 131, 33, 78, 77, 124, 104, 133, 17, + 145, 139, 122, 102, 42, 56, 75, 66, 2, 16, + 86, 140, 71, 136, 69, 99, 58, 6, 92, 90, + 8, 103, 128, 38, 46, 146, 89, 151, 51, 53 }; + +static uint8_t rp5[160] PROGMEM = { + 90, 113, 130, 115, 132, 27, 46, 72, 33, 50, + 35, 136, 42, 148, 146, 143, 116, 158, 98, 41, + 39, 5, 54, 86, 106, 56, 30, 138, 80, 44, + 91, 49, 1, 149, 159, 101, 74, 9, 110, 131, + 25, 51, 123, 76, 104, 28, 82, 140, 2, 108, + 120, 144, 10, 145, 124, 119, 62, 57, 117, 121, + 17, 73, 105, 69, 155, 7, 154, 75, 100, 141, + 157, 38, 14, 60, 47, 112, 95, 85, 43, 93, + 24, 12, 4, 71, 81, 13, 94, 68, 107, 67, + 142, 150, 61, 6, 122, 26, 139, 59, 102, 153, + 109, 48, 103, 65, 23, 92, 87, 40, 135, 133, + 129, 134, 8, 55, 83, 125, 31, 96, 147, 36, + 0, 126, 70, 64, 20, 11, 137, 78, 89, 58, + 21, 114, 127, 111, 99, 34, 152, 79, 66, 97, + 22, 15, 151, 32, 84, 37, 77, 88, 16, 29, + 3, 128, 118, 18, 156, 19, 52, 45, 53, 63 }; + +mqq160_sign_key_t testkey_P PROGMEM = {a, cc1, cc2, rp1, rp5 }; + +void mqq_load_pgm_key(void* buffer, mqq160_sign_key_t* key, const mqq160_sign_key_t* key_P){ + uint8_t *buf_ptr; + buf_ptr = buffer; + memcpy_P(key, key_P, sizeof(mqq160_sign_key_t)); + + memcpy_P(buf_ptr, key->a, MQQ160SIGN_A_SIZE); + key->a = buf_ptr; + buf_ptr += MQQ160SIGN_A_SIZE; + + memcpy_P(buf_ptr, key->cc1, MQQ160SIGN_CC1_SIZE); + key->cc1 = buf_ptr; + buf_ptr += MQQ160SIGN_CC1_SIZE; + + memcpy_P(buf_ptr, key->cc2, MQQ160SIGN_CC2_SIZE); + key->cc2 = buf_ptr; + buf_ptr += MQQ160SIGN_CC2_SIZE; + + memcpy_P(buf_ptr, key->rp1, MQQ160SIGN_RP1_SIZE); + key->rp1 = buf_ptr; + buf_ptr += MQQ160SIGN_RP1_SIZE; + + memcpy_P(buf_ptr, key->rp5, MQQ160SIGN_RP5_SIZE); + key->rp5 = buf_ptr; + +} + diff --git a/mqq-sign/mqq160-sign_testkey.h b/mqq-sign/mqq160-sign_testkey.h new file mode 100644 index 0000000..cd601d9 --- /dev/null +++ b/mqq-sign/mqq160-sign_testkey.h @@ -0,0 +1,42 @@ +/* mqq160-sign_testkey.h */ +/* + This file is part of the AVR-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 . +*/ + +#ifndef MQQ160SIGN_TESTKEY_H_ +#define MQQ160SIGN_TESTKEY_H_ + +#include + +#define MQQ160SIGN_A_SIZE 80 +#define MQQ160SIGN_RP1_SIZE 160 +#define MQQ160SIGN_RP5_SIZE 160 +#define MQQ160SIGN_CC1_SIZE 9 +#define MQQ160SIGN_CC2_SIZE 9 +#define MQQ160SIGN_KEY_SIZE (9+9+160+160+80) + +/* +This is the private key of MQQ defined by one +quadratic quasigroup of order 2^8 given as 81 uint8_ts +and one nonsingular matrix SInv given as two arrays +RP1[] and RP5[] of 160 uint8_ts. +*/ +extern mqq160_sign_key_t testkey_P; + +void mqq_load_pgm_key(void* buffer, mqq160_sign_key_t* key, const mqq160_sign_key_t* key_P); + +#endif /* MQQ160SIGN_TESTKEY_H_ */ diff --git a/test_src/cli.h b/test_src/cli.h index 5fb2a17..f95f26e 100644 --- a/test_src/cli.h +++ b/test_src/cli.h @@ -61,6 +61,7 @@ uint8_t cli_getsn(char* s, uint16_t n); uint8_t cli_getsn_cecho(char* s, uint16_t n); void cli_putstr(const char* s); void cli_putstr_P(PGM_P s); +void cli_hexdump_byte(uint8_t byte); void cli_hexdump(const void* data, uint16_t length); void cli_hexdump_rev(const void* data, uint16_t length); void cli_hexdump2(const void* data, uint16_t length); diff --git a/test_src/main-mqq160-sign-test.c b/test_src/main-mqq160-sign-test.c new file mode 100644 index 0000000..a5d3f1c --- /dev/null +++ b/test_src/main-mqq160-sign-test.c @@ -0,0 +1,204 @@ +/* main-mqq160-sign-test.c */ +/* + This file is part of the AVR-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 . +*/ +/* + * MQQ160-sign test-suit + * +*/ + +#include "config.h" +#include "uart_i.h" +#include "debug.h" + +#include "cli.h" +#include "mqq160-sign.h" +#include "mqq160-sign_P.h" +#include "mqq160-sign_testkey.h" +#include "performance_test.h" +#include +#include +#include +#include +#include "stack_measuring.h" + +char* algo_name = "MQQ160-sign"; + + +/***************************************************************************** + * additional validation-functions * + *****************************************************************************/ +uint8_t test_hash[20] PROGMEM = +{ + (uint8_t)0x64, (uint8_t)0xFE, (uint8_t)0x2A, (uint8_t)0x85, + (uint8_t)0xBB, (uint8_t)0x8C, (uint8_t)0x54, (uint8_t)0x5C, + (uint8_t)0x65, (uint8_t)0x74, (uint8_t)0xA0, (uint8_t)0xF3, + (uint8_t)0xD0, (uint8_t)0xAF, (uint8_t)0x96, (uint8_t)0xB9, + (uint8_t)0x0F, (uint8_t)0x17, (uint8_t)0xF3, (uint8_t)0xAD +}; + +void performance_mqq(void){ + uint8_t hash[20]; + uint8_t signature[20]; + long t; + char str[3*sizeof(long)+2]; + uint8_t tmp; + uint16_t s1, s2; + stack_measuring_ctx_t smctx; + memcpy_P(hash, test_hash, 20); + + uint8_t key_buffer[MQQ160SIGN_KEY_SIZE]; + mqq160_sign_key_t key; + mqq_load_pgm_key(key_buffer, &key, &testkey_P); + + cli_putstr_P(PSTR("\r\n=== Performance of MQQ160-SIGN ===")); + calibrateTimer(); + startTimer(0); + START_TIMER; + mqq160_sign_P(signature, hash, &testkey_P); + STOP_TIMER; + t = stopTimer(); + ltoa(t, str, 10); + cli_putstr_P(PSTR("\r\n cycles for mqq160_sign_P: ")); + tmp = 12-strlen(str); + while(tmp--){ + cli_putc(' '); + } + cli_putstr(str); + + calibrateTimer(); + startTimer(0); + START_TIMER; + mqq160_sign(signature, hash, &key); + STOP_TIMER; + t = stopTimer(); + ltoa(t, str, 10); + cli_putstr_P(PSTR("\r\n cycles for mqq160_sign: ")); + tmp = 12-strlen(str); + while(tmp--){ + cli_putc(' '); + } + cli_putstr(str); + + stack_measure_init(&smctx, 0xAA); + mqq160_sign_P(signature, hash, &testkey_P); + s1 = stack_measure_final(&smctx); + stack_measure_init(&smctx, 0x55); + mqq160_sign_P(signature, hash, &testkey_P); + s2 = stack_measure_final(&smctx); + s1 = (s1>s2)?s1:s2; + ltoa((long)s1, str, 10); + cli_putstr_P(PSTR("\r\n stack for mqq160_sign_P: ")); + tmp = 12-strlen(str); + while(tmp--){ + cli_putc(' '); + } + cli_putstr(str); + stack_measure_init(&smctx, 0xAA); + mqq160_sign(signature, hash, &key); + s1 = stack_measure_final(&smctx); + stack_measure_init(&smctx, 0x55); + mqq160_sign_P(signature, hash, &testkey_P); + s2 = stack_measure_final(&smctx); + s1 = (s1>s2)?s1:s2; + ltoa((long)s1, str, 10); + cli_putstr_P(PSTR("\r\n stack for mqq160_sign: ")); + tmp = 12-strlen(str); + while(tmp--){ + cli_putc(' '); + } + cli_putstr(str); + + cli_putstr_P(PSTR("\r\n=== End of performance figures ===")); +} + +void testrun_mqq_mem(void){ + uint8_t hash[20]; + uint8_t signature[20]; + memcpy_P(hash, test_hash, 20); + + uint8_t key_buffer[MQQ160SIGN_KEY_SIZE]; + mqq160_sign_key_t key; + mqq_load_pgm_key(key_buffer, &key, &testkey_P); + mqq160_sign(signature, hash, &key); + cli_putstr_P(PSTR("\r\ntest signature (RAM): ")); + cli_hexdump(signature, 20); +} + +void testrun_mqq_flash(void){ + uint8_t hash[20]; + uint8_t signature[20]; + memcpy_P(hash, test_hash, 20); + + mqq160_sign_P(signature, hash, &testkey_P); + cli_putstr_P(PSTR("\r\ntest signature (FLASH): ")); + cli_hexdump(signature, 20); +} + +void testrun_mqq(void){ + uint8_t hash[20]; + uint8_t signature[20]; + memcpy_P(hash, test_hash, 20); + + uint8_t key_buffer[MQQ160SIGN_KEY_SIZE]; + mqq160_sign_key_t key; + mqq_load_pgm_key(key_buffer, &key, &testkey_P); + mqq160_sign(signature, hash, &key); + cli_putstr_P(PSTR("\r\ntest signature (RAM): ")); + cli_hexdump(signature, 20); + + mqq160_sign_P(signature, hash, &testkey_P); + cli_putstr_P(PSTR("\r\ntest signature (FLASH): ")); + cli_hexdump(signature, 20); +} + +/***************************************************************************** + * main * + *****************************************************************************/ + +const char test_str[] PROGMEM = "test"; +const char test_flash_str[] PROGMEM = "flash"; +const char test_mem_str[] PROGMEM = "mem"; +const char performance_str[] PROGMEM = "performance"; +const char echo_str[] PROGMEM = "echo"; + +cmdlist_entry_t cmdlist[] PROGMEM = { + { test_str, NULL, testrun_mqq }, + { test_flash_str, NULL, testrun_mqq_flash }, + { test_mem_str, NULL, testrun_mqq_mem }, + { performance_str, NULL, performance_mqq }, + { echo_str, (void*)1, (void_fpt)echo_ctrl }, + { NULL, NULL, NULL } +}; + +int main (void){ + DEBUG_INIT(); + + cli_rx = (cli_rx_fpt)uart0_getc; + cli_tx = (cli_tx_fpt)uart0_putc; + for(;;){ + cli_putstr_P(PSTR("\r\n\r\nCrypto-VS (")); + cli_putstr(algo_name); + cli_putstr_P(PSTR("; ")); + cli_putstr(__DATE__); + cli_putstr_P(PSTR(" ")); + cli_putstr(__TIME__); + cli_putstr_P(PSTR(")\r\nloaded and running\r\n")); + + cmd_interface(cmdlist); + } +} -- 2.39.5