/* * Тестовая реализация шифра Speck для платформы Arduino. * Автор: Александр Венедюхин (https://dxdt.ru/). * Лицензия: Свободное использование. * Дата: 03/03/2017. * * Шифр Speck - "малый" блочный шифр, предложенный в 2013 году сотрудниками АНБ * в качестве шифра для встроенных применений (опубликовано описание сразу двух * шифров: Speck и Simon; второй - оптимизирован для аппаратной реализации). * Исходная публикация: https://eprint.iacr.org/2013/404.pdf * * Шифр определён авторами в нескольких вариантах - с различной разрядностью блока * и ключа. Данный исходный код реализует шифр в версии с 64-битным блоком и * 128-битным ключом. * * Представлена функция зашифрования (SpeckCipher()), а также функции развёртывания * ключа. * * */ void PrintBlock(byte *block) /* * Вспомогательная функция. Выводит в текстовом виде 8-байтовый блок через * последовательный порт. */ { for(int i = 0; i<8; i++){ Serial.print('['); if( block[i]<=15 ){ Serial.print("0"); } Serial.print(block[i], HEX); Serial.print(']'); } Serial.println(); } int SpeckCipher(byte *r_keys, byte *plain_text, byte *cipher_text) /* * Функция, реализующая блочный шифр Speck на inline-ассемблере AVR. * * Шифр реализован в версии с 64-битным блоком и 128-битным ключом. * Раундовые ключи поступают на вход данной функции в развёрнутом виде. * Функции для получения набора раундовых ключей даны ниже. * * Параметры: * r_keys - раундовые ключи (байтовый массив); * plain_text, cipher_text - открытый текст, шифротекст (байтовые массивы). * * (Контроль размеров массивов, переполнения - не производится.) * * Исходный блок (64 бита) разделён на два 32-битных слова: x,y. * Требуется четыре байтовых регистра на каждое слово. * * r10, r11, r12, r13 = x * r16, r17, r18, r19 = y * * */ { asm volatile( "ld r10,X+\n" // Загружаем байты открытого текста в регистры. "ld r11,X+\n" // Регистры r10-13 - "старшая" часть (x). "ld r12,X+\n" "ld r13,X+\n" "ld r16,X+\n" // Регистры r16-19 - "младшая" часть (y). "ld r17,X+\n" "ld r18,X+\n" "ld r19,X+\n" /* * Далее - реализация раунда шифра. * * Шифр состоит из 27 раундов, каждый из которых использует отдельный 32-битный * раундовый ключ. * */ "ldi r21, 27\n" // 27 раундов. "r:\n" // Метка начала раундового цикла. // Начало раунда (реализация раундовой функции). "ld r6,Y+\n" // Загружаем ключ действующего раунда в регистры r6-9. "ld r7,Y+\n" // Раундовые ключи имеют разрядность 32 бита, "ld r8,Y+\n" // поэтому для хранения одного ключа нужно 4 регистра. "ld r9,Y+\n" "mov r14,r13\n" // Циклический сдвиг x вправо на 8 разрядов. "mov r13,r12\n" "mov r12,r11\n" "mov r11,r10\n" "mov r10,r14\n" "add r13,r19\n" // Вычисляем сумму x+y (mod 2^32). "adc r12,r18\n" "adc r11,r17\n" "adc r10,r16\n" "eor r10,r6\n" // XOR с раундовым ключом. "eor r11,r7\n" "eor r12,r8\n" "eor r13,r9\n" "ldi r23,3\n" // Циклический сдвиг влево, на три разряда. Цикл с r23. "l:\n" // Метка цикла. "lsl r19\n" // Логический сдвиг на один бит, выбывший разряд - перемещается в флаг переноса. "rol r18\n" // Циклический сдвиг, на основе флага переноса. "rol r17\n" "bst r16,7\n" // Сохранение значения старшего (левого) бита регистра r16. "rol r16\n" "bld r19,0\n" // Установка сохранённого бита в r19 (правый). "dec r23\n" "brne l\n" // Цикл. "eor r16,r10\n" // y XOR x. "eor r17,r11\n" "eor r18,r12\n" "eor r19,r13\n" "dec r21\n" // Подсчёт раундов. "brne r\n" // Основной цикл. "st Z+,r10\n" // Выгружаем шифротекст в память (указатель Z). "st Z+,r11\n" "st Z+,r12\n" "st Z+,r13\n" "st Z+,r16\n" "st Z+,r17\n" "st Z+,r18\n" "st Z+,r19\n" // Параметры для ассемблера. Раундовые ключи - указатель Y; открытый текст - указатель X; шифротекст - указатель Z. : : "x" (plain_text), "y" (r_keys), "z" (cipher_text) : "r6","r7","r8","r9","r10","r11","r12","r13","r14","r16","r17","r18","r19","r21","r23","memory" ); return 1; } int SpeckRound(byte *key, byte *x, byte *y) /* * Отдельная реализация фукнции раунда шифра Speck. Служит для получения раундовых ключей. * Работает полностью аналогично раунду из SpeckCipher(). */ { asm volatile( "push r26\n" "push r27\n" "push r28\n" "push r29\n" "ld r10,X+\n" // Загружаем в регистры r10-13 значение x. "ld r11,X+\n" "ld r12,X+\n" "ld r13,X+\n" "ld r16,Y+\n" // Регистры r16-19 - значение y. "ld r17,Y+\n" "ld r18,Y+\n" "ld r19,Y+\n" "mov r28,%A0\n" // Указатель на блок памяти, который содержит ключ. "mov r29,%B0\n" "ld r6,Y+\n" // Загружаем ключ действующего раунда в регистры r6-9. "ld r7,Y+\n" "ld r8,Y+\n" "ld r9,Y+\n" "mov r14,r13\n" // Циклический сдвиг x вправо на 8 разрядов. "mov r13,r12\n" "mov r12,r11\n" "mov r11,r10\n" "mov r10,r14\n" "add r13,r19\n" // Вычисляем сумму. "adc r12,r18\n" "adc r11,r17\n" "adc r10,r16\n" "eor r10,r6\n" // XOR с раундовым ключом. "eor r11,r7\n" "eor r12,r8\n" "eor r13,r9\n" "ldi r23,3\n" // Циклический сдвиг влево, на три разряда. Цикл с r23. "lk:\n" // Метка цикла. "lsl r19\n" // Логический сдвиг на один бит, выбывший разряд - перемещается в флаг переноса. "rol r18\n" // Циклический сдвиг, на основе флага переноса. "rol r17\n" "bst r16,7\n" // Сохранение значения старшего (левого) бита регистра r16. "rol r16\n" "bld r19,0\n" // Установка сохранённого бита в r19 (правый). "dec r23\n" "brne lk\n" // Цикл. "eor r16,r10\n" // XOR. "eor r17,r11\n" "eor r18,r12\n" "eor r19,r13\n" "pop r29\n" "pop r28\n" "pop r27\n" "pop r26\n" "st Y+,r16\n" // Выгружаем результат в память. "st Y+,r17\n" "st Y+,r18\n" "st Y+,r19\n" "st X+,r10\n" "st X+,r11\n" "st X+,r12\n" "st X+,r13\n" : : "r" (key), "x" (x), "y" (y) : "r6","r7","r8","r9","r10","r11","r12","r13","r14","r16","r17","r18","r19","r23","memory" ); return 1; } int SpeckKeyExpand(byte *src_key, byte *round_keys) /* * Функция развёртывания ключа Speck. * * На вход поступает основной ключ, имеющий, в данной версии Speck, * разрядность 128 бит (16 байт). * Из этого ключа получается последовательность 27 раундовых ключей, * каждый разрядностью 32 бита (половина блока). * * Параметры: * src_key - основной ключ (байтовый массив); * round_keys - массив ключей раундов (байтовый). * * (Контроль размеров массивов, переполнения - не производится.) */ { int i,t,rk; byte p[4],q[4],k[4]; byte l[29*4]; for(t=0;t<(27*4);t++){ round_keys[t] = 0; } k[0] = k[1] = k[2] = k[3] = 0; q[0] = round_keys[0] = src_key[12]; q[1] = round_keys[1] = src_key[13]; q[2] = round_keys[2] = src_key[14]; q[3] = round_keys[3] = src_key[15]; rk = 4; for(i=0;i<3;i++){ for(t=0;t<4;t++){ l[i*4+t] = src_key[8-(i*4)+t]; } } for(i=0;i<(27-1);i++){ for(t=0;t<4;t++){ p[t] = l[i*4+t]; } SpeckRound(k,p,q); for(t=0;t<4;t++){ l[(i+3)*4+t] = p[t]; } for(t=0;t<4;t++){ round_keys[rk++] = q[t]; } k[3]++; } return 1; } void setup() { Serial.begin(19200); Serial.println("Started test."); } void loop() { byte test_vector_key[16] = {0x1b,0x1a,0x19,0x18,0x13,0x12,0x11,0x10,0x0b,0x0a,0x09,0x08,0x03,0x02,0x01,0x00}; byte test_vector_pt[8]={0x3b,0x72,0x65,0x74,0x74,0x75,0x43,0x2d}; byte test_vector_ct[8]={0x8c,0x6f,0xa5,0x48,0x45,0x4e,0x02,0x8b}; byte buf[8]; byte rkeys[27*4]; byte flag; Serial.print("Expand key..."); SpeckKeyExpand(test_vector_key,rkeys); Serial.println("done."); Serial.print("Plain text: "); PrintBlock(test_vector_pt); SpeckCipher(rkeys,test_vector_pt,buf); Serial.print("Cipher text: "); PrintBlock(buf); Serial.print("Test result: "); flag = 1; for(int i=0; i<8; i++){ if(buf[i] != test_vector_ct[i]){ flag = 0; break; } } if(!flag){ Serial.println("FAILED!"); }else{ Serial.println("PASSED!"); } delay(17000); Serial.println("Repeat."); }