diff --git a/ac.c b/ac.c new file mode 100644 index 0000000..ab9e680 --- /dev/null +++ b/ac.c @@ -0,0 +1,301 @@ +/* + * ac.c -- Check access condition + * + * Copyright (C) 2010, 2012, 2013, 2017 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "config.h" + +#include "gnuk.h" +#include "sha256.h" +#include "random.h" + +uint8_t volatile auth_status; /* Initialized to AC_NONE_AUTHORIZED */ + +int +ac_check_status (uint8_t ac_flag) +{ + if (ac_flag == AC_ALWAYS) + return 1; + else if (ac_flag == AC_NEVER) + return 0; + else + return (ac_flag & auth_status)? 1 : 0; +} + +void +ac_reset_pso_cds (void) +{ + gpg_do_clear_prvkey (GPG_KEY_FOR_SIGNING); + auth_status &= ~AC_PSO_CDS_AUTHORIZED; +} + +void +ac_reset_other (void) +{ + gpg_do_clear_prvkey (GPG_KEY_FOR_DECRYPTION); + gpg_do_clear_prvkey (GPG_KEY_FOR_AUTHENTICATION); + auth_status &= ~AC_OTHER_AUTHORIZED; +} + +int +verify_user_0 (uint8_t access, const uint8_t *pw, int buf_len, int pw_len_known, + const uint8_t *ks_pw1, int save_ks) +{ + int pw_len; + int r; + uint8_t keystring[KEYSTRING_MD_SIZE]; + const uint8_t *salt; + int salt_len; + + if (gpg_pw_locked (PW_ERR_PW1)) + return 0; + + if (ks_pw1 == NULL) + { + const uint8_t *initial_pw; + + salt = NULL; + salt_len = 0; + gpg_do_get_initial_pw_setting (0, &pw_len, &initial_pw); + if ((pw_len_known >= 0 && pw_len_known != pw_len) + || buf_len < pw_len + || memcmp (pw, initial_pw, pw_len)) + goto failure; + } + else + { + pw_len = ks_pw1[0] & PW_LEN_MASK; + salt = KS_GET_SALT (ks_pw1); + salt_len = SALT_SIZE; + + if ((pw_len_known >= 0 && pw_len_known != pw_len) + || buf_len < pw_len) + goto failure; + } + + s2k (salt, salt_len, pw, pw_len, keystring); + if (save_ks) + memcpy (keystring_md_pw3, keystring, KEYSTRING_MD_SIZE); + + if (access == AC_PSO_CDS_AUTHORIZED) + r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring); + else + { + int r1, r2; + + r1 = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, BY_USER, keystring); + r2 = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, BY_USER, keystring); + + if (r1 < 0 || r2 < 0) + r = -1; + else if (r1 == 0) + { + if (r2 == 0) + /* No encryption/authentication keys, then, check signing key. */ + r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring); + else + r = r2; + } + else if (r2 == 0) + r = r1; + else + r = 1; + } + + if (r < 0) + { + failure: + gpg_pw_increment_err_counter (PW_ERR_PW1); + return -1; + } + + gpg_pw_reset_err_counter (PW_ERR_PW1); + return pw_len; +} + +/* + * Verify for "Perform Security Operation : Compute Digital Signature" + */ +int +verify_pso_cds (const uint8_t *pw, int pw_len) +{ + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + int r; + + DEBUG_INFO ("verify_pso_cds\r\n"); + DEBUG_BYTE (pw_len); + + r = verify_user_0 (AC_PSO_CDS_AUTHORIZED, pw, pw_len, pw_len, ks_pw1, 0); + if (r > 0) + auth_status |= AC_PSO_CDS_AUTHORIZED; + return r; +} + +int +verify_other (const uint8_t *pw, int pw_len) +{ + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + int r; + + DEBUG_INFO ("verify_other\r\n"); + DEBUG_BYTE (pw_len); + + r = verify_user_0 (AC_OTHER_AUTHORIZED, pw, pw_len, pw_len, ks_pw1, 0); + if (r > 0) + auth_status |= AC_OTHER_AUTHORIZED; + return r; +} + + +static int +verify_admin_00 (const uint8_t *pw, int buf_len, int pw_len_known, + const uint8_t *ks, int save_ks) +{ + int pw_len; + int r; + uint8_t keystring[KEYSTRING_MD_SIZE]; + const uint8_t *salt; + int salt_len; + + pw_len = ks[0] & PW_LEN_MASK; + salt = KS_GET_SALT (ks); + salt_len = SALT_SIZE; + + if ((pw_len_known >= 0 && pw_len_known != pw_len) || buf_len < pw_len) + return -1; + + s2k (salt, salt_len, pw, pw_len, keystring); + if (save_ks) + memcpy (keystring_md_pw3, keystring, KEYSTRING_MD_SIZE); + + r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_ADMIN, keystring); + + if (r < 0) + return -1; + else if (r == 0) + if ((ks[0] & PW_LEN_KEYSTRING_BIT) == 0 + || memcmp (KS_GET_KEYSTRING (ks), keystring, KEYSTRING_MD_SIZE) != 0) + return -1; + + return pw_len; +} + +uint8_t keystring_md_pw3[KEYSTRING_MD_SIZE]; +uint8_t admin_authorized; + +int +verify_admin_0 (const uint8_t *pw, int buf_len, int pw_len_known, + const uint8_t *pw3_keystring, int save_ks) +{ + int pw_len; + + if (pw3_keystring != NULL) + { + if (gpg_pw_locked (PW_ERR_PW3)) + return 0; + + pw_len = verify_admin_00 (pw, buf_len, pw_len_known, pw3_keystring, + save_ks); + if (pw_len < 0) + { + failure: + gpg_pw_increment_err_counter (PW_ERR_PW3); + return -1; + } + + admin_authorized = BY_ADMIN; + success: /* OK, the admin is now authenticated. */ + gpg_pw_reset_err_counter (PW_ERR_PW3); + return pw_len; + } + else + { + const uint8_t *initial_pw; + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + + if (ks_pw1 != NULL) + { /* empty PW3, but PW1 exists */ + int r = verify_user_0 (AC_PSO_CDS_AUTHORIZED, + pw, buf_len, pw_len_known, ks_pw1, save_ks); + + if (r > 0) + admin_authorized = BY_USER; + + return r; + } + + if (gpg_pw_locked (PW_ERR_PW3)) + return 0; + + /* + * For the case of empty PW3 (with empty PW1), passphrase is + * OPENPGP_CARD_INITIAL_PW3, or defined by KDF DO. + */ + gpg_do_get_initial_pw_setting (1, &pw_len, &initial_pw); + if ((pw_len_known >=0 && pw_len_known != pw_len) + || buf_len < pw_len + || memcmp (pw, initial_pw, pw_len)) + goto failure; + + admin_authorized = BY_ADMIN; + if (save_ks) + s2k (NULL, 0, pw, pw_len, keystring_md_pw3); + goto success; + } +} + + +int +verify_admin (const uint8_t *pw, int pw_len) +{ + int r; + const uint8_t *pw3_keystring; + + pw3_keystring = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + r = verify_admin_0 (pw, pw_len, pw_len, pw3_keystring, 1); + if (r <= 0) + return r; + + auth_status |= AC_ADMIN_AUTHORIZED; + return 1; +} + +void +ac_reset_admin (void) +{ + memset (keystring_md_pw3, 0, KEYSTRING_MD_SIZE); + auth_status &= ~AC_ADMIN_AUTHORIZED; + admin_authorized = 0; +} + +void +ac_fini (void) +{ + memset (keystring_md_pw3, 0, KEYSTRING_MD_SIZE); + gpg_do_clear_prvkey (GPG_KEY_FOR_SIGNING); + gpg_do_clear_prvkey (GPG_KEY_FOR_DECRYPTION); + gpg_do_clear_prvkey (GPG_KEY_FOR_AUTHENTICATION); + auth_status = AC_NONE_AUTHORIZED; + admin_authorized = 0; +} diff --git a/aes.c b/aes.c new file mode 100644 index 0000000..ed83434 --- /dev/null +++ b/aes.c @@ -0,0 +1,1352 @@ +/* + * FIPS-197 compliant AES implementation + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. + * + * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_AES_C) + +#include "polarssl/aes.h" +#if defined(POLARSSL_PADLOCK_C) +#include "polarssl/padlock.h" +#endif + +#if !defined(POLARSSL_AES_ALT) + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] ) \ + | ( (uint32_t) (b)[(i) + 1] << 8 ) \ + | ( (uint32_t) (b)[(i) + 2] << 16 ) \ + | ( (uint32_t) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +#if defined(POLARSSL_PADLOCK_C) && \ + ( defined(POLARSSL_HAVE_X86) || defined(PADLOCK_ALIGN16) ) +static int aes_padlock_ace = -1; +#endif + +#if defined(POLARSSL_AES_ROM_TABLES) +/* + * Forward S-box + */ +static const unsigned char FSb[256] = +{ + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, + 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, + 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, + 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, + 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, + 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, + 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, + 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, + 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, + 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, + 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, + 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 +}; + +/* + * Forward tables + */ +#define FT \ +\ + V(A5,63,63,C6), V(84,7C,7C,F8), V(99,77,77,EE), V(8D,7B,7B,F6), \ + V(0D,F2,F2,FF), V(BD,6B,6B,D6), V(B1,6F,6F,DE), V(54,C5,C5,91), \ + V(50,30,30,60), V(03,01,01,02), V(A9,67,67,CE), V(7D,2B,2B,56), \ + V(19,FE,FE,E7), V(62,D7,D7,B5), V(E6,AB,AB,4D), V(9A,76,76,EC), \ + V(45,CA,CA,8F), V(9D,82,82,1F), V(40,C9,C9,89), V(87,7D,7D,FA), \ + V(15,FA,FA,EF), V(EB,59,59,B2), V(C9,47,47,8E), V(0B,F0,F0,FB), \ + V(EC,AD,AD,41), V(67,D4,D4,B3), V(FD,A2,A2,5F), V(EA,AF,AF,45), \ + V(BF,9C,9C,23), V(F7,A4,A4,53), V(96,72,72,E4), V(5B,C0,C0,9B), \ + V(C2,B7,B7,75), V(1C,FD,FD,E1), V(AE,93,93,3D), V(6A,26,26,4C), \ + V(5A,36,36,6C), V(41,3F,3F,7E), V(02,F7,F7,F5), V(4F,CC,CC,83), \ + V(5C,34,34,68), V(F4,A5,A5,51), V(34,E5,E5,D1), V(08,F1,F1,F9), \ + V(93,71,71,E2), V(73,D8,D8,AB), V(53,31,31,62), V(3F,15,15,2A), \ + V(0C,04,04,08), V(52,C7,C7,95), V(65,23,23,46), V(5E,C3,C3,9D), \ + V(28,18,18,30), V(A1,96,96,37), V(0F,05,05,0A), V(B5,9A,9A,2F), \ + V(09,07,07,0E), V(36,12,12,24), V(9B,80,80,1B), V(3D,E2,E2,DF), \ + V(26,EB,EB,CD), V(69,27,27,4E), V(CD,B2,B2,7F), V(9F,75,75,EA), \ + V(1B,09,09,12), V(9E,83,83,1D), V(74,2C,2C,58), V(2E,1A,1A,34), \ + V(2D,1B,1B,36), V(B2,6E,6E,DC), V(EE,5A,5A,B4), V(FB,A0,A0,5B), \ + V(F6,52,52,A4), V(4D,3B,3B,76), V(61,D6,D6,B7), V(CE,B3,B3,7D), \ + V(7B,29,29,52), V(3E,E3,E3,DD), V(71,2F,2F,5E), V(97,84,84,13), \ + V(F5,53,53,A6), V(68,D1,D1,B9), V(00,00,00,00), V(2C,ED,ED,C1), \ + V(60,20,20,40), V(1F,FC,FC,E3), V(C8,B1,B1,79), V(ED,5B,5B,B6), \ + V(BE,6A,6A,D4), V(46,CB,CB,8D), V(D9,BE,BE,67), V(4B,39,39,72), \ + V(DE,4A,4A,94), V(D4,4C,4C,98), V(E8,58,58,B0), V(4A,CF,CF,85), \ + V(6B,D0,D0,BB), V(2A,EF,EF,C5), V(E5,AA,AA,4F), V(16,FB,FB,ED), \ + V(C5,43,43,86), V(D7,4D,4D,9A), V(55,33,33,66), V(94,85,85,11), \ + V(CF,45,45,8A), V(10,F9,F9,E9), V(06,02,02,04), V(81,7F,7F,FE), \ + V(F0,50,50,A0), V(44,3C,3C,78), V(BA,9F,9F,25), V(E3,A8,A8,4B), \ + V(F3,51,51,A2), V(FE,A3,A3,5D), V(C0,40,40,80), V(8A,8F,8F,05), \ + V(AD,92,92,3F), V(BC,9D,9D,21), V(48,38,38,70), V(04,F5,F5,F1), \ + V(DF,BC,BC,63), V(C1,B6,B6,77), V(75,DA,DA,AF), V(63,21,21,42), \ + V(30,10,10,20), V(1A,FF,FF,E5), V(0E,F3,F3,FD), V(6D,D2,D2,BF), \ + V(4C,CD,CD,81), V(14,0C,0C,18), V(35,13,13,26), V(2F,EC,EC,C3), \ + V(E1,5F,5F,BE), V(A2,97,97,35), V(CC,44,44,88), V(39,17,17,2E), \ + V(57,C4,C4,93), V(F2,A7,A7,55), V(82,7E,7E,FC), V(47,3D,3D,7A), \ + V(AC,64,64,C8), V(E7,5D,5D,BA), V(2B,19,19,32), V(95,73,73,E6), \ + V(A0,60,60,C0), V(98,81,81,19), V(D1,4F,4F,9E), V(7F,DC,DC,A3), \ + V(66,22,22,44), V(7E,2A,2A,54), V(AB,90,90,3B), V(83,88,88,0B), \ + V(CA,46,46,8C), V(29,EE,EE,C7), V(D3,B8,B8,6B), V(3C,14,14,28), \ + V(79,DE,DE,A7), V(E2,5E,5E,BC), V(1D,0B,0B,16), V(76,DB,DB,AD), \ + V(3B,E0,E0,DB), V(56,32,32,64), V(4E,3A,3A,74), V(1E,0A,0A,14), \ + V(DB,49,49,92), V(0A,06,06,0C), V(6C,24,24,48), V(E4,5C,5C,B8), \ + V(5D,C2,C2,9F), V(6E,D3,D3,BD), V(EF,AC,AC,43), V(A6,62,62,C4), \ + V(A8,91,91,39), V(A4,95,95,31), V(37,E4,E4,D3), V(8B,79,79,F2), \ + V(32,E7,E7,D5), V(43,C8,C8,8B), V(59,37,37,6E), V(B7,6D,6D,DA), \ + V(8C,8D,8D,01), V(64,D5,D5,B1), V(D2,4E,4E,9C), V(E0,A9,A9,49), \ + V(B4,6C,6C,D8), V(FA,56,56,AC), V(07,F4,F4,F3), V(25,EA,EA,CF), \ + V(AF,65,65,CA), V(8E,7A,7A,F4), V(E9,AE,AE,47), V(18,08,08,10), \ + V(D5,BA,BA,6F), V(88,78,78,F0), V(6F,25,25,4A), V(72,2E,2E,5C), \ + V(24,1C,1C,38), V(F1,A6,A6,57), V(C7,B4,B4,73), V(51,C6,C6,97), \ + V(23,E8,E8,CB), V(7C,DD,DD,A1), V(9C,74,74,E8), V(21,1F,1F,3E), \ + V(DD,4B,4B,96), V(DC,BD,BD,61), V(86,8B,8B,0D), V(85,8A,8A,0F), \ + V(90,70,70,E0), V(42,3E,3E,7C), V(C4,B5,B5,71), V(AA,66,66,CC), \ + V(D8,48,48,90), V(05,03,03,06), V(01,F6,F6,F7), V(12,0E,0E,1C), \ + V(A3,61,61,C2), V(5F,35,35,6A), V(F9,57,57,AE), V(D0,B9,B9,69), \ + V(91,86,86,17), V(58,C1,C1,99), V(27,1D,1D,3A), V(B9,9E,9E,27), \ + V(38,E1,E1,D9), V(13,F8,F8,EB), V(B3,98,98,2B), V(33,11,11,22), \ + V(BB,69,69,D2), V(70,D9,D9,A9), V(89,8E,8E,07), V(A7,94,94,33), \ + V(B6,9B,9B,2D), V(22,1E,1E,3C), V(92,87,87,15), V(20,E9,E9,C9), \ + V(49,CE,CE,87), V(FF,55,55,AA), V(78,28,28,50), V(7A,DF,DF,A5), \ + V(8F,8C,8C,03), V(F8,A1,A1,59), V(80,89,89,09), V(17,0D,0D,1A), \ + V(DA,BF,BF,65), V(31,E6,E6,D7), V(C6,42,42,84), V(B8,68,68,D0), \ + V(C3,41,41,82), V(B0,99,99,29), V(77,2D,2D,5A), V(11,0F,0F,1E), \ + V(CB,B0,B0,7B), V(FC,54,54,A8), V(D6,BB,BB,6D), V(3A,16,16,2C) + +#define V(a,b,c,d) 0x##a##b##c##d +const uint32_t FT0[256] __attribute__((weak,section(".sys.0"))) = { FT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +const uint32_t FT1[256] __attribute__((weak,section(".sys.1"))) = { FT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +const uint32_t FT2[256] __attribute__((weak,section(".sys.2"))) = { FT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const uint32_t FT3[256] = { FT }; +#undef V + +#undef FT + +/* + * Reverse S-box + */ +static const unsigned char RSb[256] = +{ + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, + 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, + 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, + 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, + 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, + 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, + 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, + 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, + 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, + 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, + 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, + 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D +}; + +/* + * Reverse tables + */ +#define RT \ +\ + V(50,A7,F4,51), V(53,65,41,7E), V(C3,A4,17,1A), V(96,5E,27,3A), \ + V(CB,6B,AB,3B), V(F1,45,9D,1F), V(AB,58,FA,AC), V(93,03,E3,4B), \ + V(55,FA,30,20), V(F6,6D,76,AD), V(91,76,CC,88), V(25,4C,02,F5), \ + V(FC,D7,E5,4F), V(D7,CB,2A,C5), V(80,44,35,26), V(8F,A3,62,B5), \ + V(49,5A,B1,DE), V(67,1B,BA,25), V(98,0E,EA,45), V(E1,C0,FE,5D), \ + V(02,75,2F,C3), V(12,F0,4C,81), V(A3,97,46,8D), V(C6,F9,D3,6B), \ + V(E7,5F,8F,03), V(95,9C,92,15), V(EB,7A,6D,BF), V(DA,59,52,95), \ + V(2D,83,BE,D4), V(D3,21,74,58), V(29,69,E0,49), V(44,C8,C9,8E), \ + V(6A,89,C2,75), V(78,79,8E,F4), V(6B,3E,58,99), V(DD,71,B9,27), \ + V(B6,4F,E1,BE), V(17,AD,88,F0), V(66,AC,20,C9), V(B4,3A,CE,7D), \ + V(18,4A,DF,63), V(82,31,1A,E5), V(60,33,51,97), V(45,7F,53,62), \ + V(E0,77,64,B1), V(84,AE,6B,BB), V(1C,A0,81,FE), V(94,2B,08,F9), \ + V(58,68,48,70), V(19,FD,45,8F), V(87,6C,DE,94), V(B7,F8,7B,52), \ + V(23,D3,73,AB), V(E2,02,4B,72), V(57,8F,1F,E3), V(2A,AB,55,66), \ + V(07,28,EB,B2), V(03,C2,B5,2F), V(9A,7B,C5,86), V(A5,08,37,D3), \ + V(F2,87,28,30), V(B2,A5,BF,23), V(BA,6A,03,02), V(5C,82,16,ED), \ + V(2B,1C,CF,8A), V(92,B4,79,A7), V(F0,F2,07,F3), V(A1,E2,69,4E), \ + V(CD,F4,DA,65), V(D5,BE,05,06), V(1F,62,34,D1), V(8A,FE,A6,C4), \ + V(9D,53,2E,34), V(A0,55,F3,A2), V(32,E1,8A,05), V(75,EB,F6,A4), \ + V(39,EC,83,0B), V(AA,EF,60,40), V(06,9F,71,5E), V(51,10,6E,BD), \ + V(F9,8A,21,3E), V(3D,06,DD,96), V(AE,05,3E,DD), V(46,BD,E6,4D), \ + V(B5,8D,54,91), V(05,5D,C4,71), V(6F,D4,06,04), V(FF,15,50,60), \ + V(24,FB,98,19), V(97,E9,BD,D6), V(CC,43,40,89), V(77,9E,D9,67), \ + V(BD,42,E8,B0), V(88,8B,89,07), V(38,5B,19,E7), V(DB,EE,C8,79), \ + V(47,0A,7C,A1), V(E9,0F,42,7C), V(C9,1E,84,F8), V(00,00,00,00), \ + V(83,86,80,09), V(48,ED,2B,32), V(AC,70,11,1E), V(4E,72,5A,6C), \ + V(FB,FF,0E,FD), V(56,38,85,0F), V(1E,D5,AE,3D), V(27,39,2D,36), \ + V(64,D9,0F,0A), V(21,A6,5C,68), V(D1,54,5B,9B), V(3A,2E,36,24), \ + V(B1,67,0A,0C), V(0F,E7,57,93), V(D2,96,EE,B4), V(9E,91,9B,1B), \ + V(4F,C5,C0,80), V(A2,20,DC,61), V(69,4B,77,5A), V(16,1A,12,1C), \ + V(0A,BA,93,E2), V(E5,2A,A0,C0), V(43,E0,22,3C), V(1D,17,1B,12), \ + V(0B,0D,09,0E), V(AD,C7,8B,F2), V(B9,A8,B6,2D), V(C8,A9,1E,14), \ + V(85,19,F1,57), V(4C,07,75,AF), V(BB,DD,99,EE), V(FD,60,7F,A3), \ + V(9F,26,01,F7), V(BC,F5,72,5C), V(C5,3B,66,44), V(34,7E,FB,5B), \ + V(76,29,43,8B), V(DC,C6,23,CB), V(68,FC,ED,B6), V(63,F1,E4,B8), \ + V(CA,DC,31,D7), V(10,85,63,42), V(40,22,97,13), V(20,11,C6,84), \ + V(7D,24,4A,85), V(F8,3D,BB,D2), V(11,32,F9,AE), V(6D,A1,29,C7), \ + V(4B,2F,9E,1D), V(F3,30,B2,DC), V(EC,52,86,0D), V(D0,E3,C1,77), \ + V(6C,16,B3,2B), V(99,B9,70,A9), V(FA,48,94,11), V(22,64,E9,47), \ + V(C4,8C,FC,A8), V(1A,3F,F0,A0), V(D8,2C,7D,56), V(EF,90,33,22), \ + V(C7,4E,49,87), V(C1,D1,38,D9), V(FE,A2,CA,8C), V(36,0B,D4,98), \ + V(CF,81,F5,A6), V(28,DE,7A,A5), V(26,8E,B7,DA), V(A4,BF,AD,3F), \ + V(E4,9D,3A,2C), V(0D,92,78,50), V(9B,CC,5F,6A), V(62,46,7E,54), \ + V(C2,13,8D,F6), V(E8,B8,D8,90), V(5E,F7,39,2E), V(F5,AF,C3,82), \ + V(BE,80,5D,9F), V(7C,93,D0,69), V(A9,2D,D5,6F), V(B3,12,25,CF), \ + V(3B,99,AC,C8), V(A7,7D,18,10), V(6E,63,9C,E8), V(7B,BB,3B,DB), \ + V(09,78,26,CD), V(F4,18,59,6E), V(01,B7,9A,EC), V(A8,9A,4F,83), \ + V(65,6E,95,E6), V(7E,E6,FF,AA), V(08,CF,BC,21), V(E6,E8,15,EF), \ + V(D9,9B,E7,BA), V(CE,36,6F,4A), V(D4,09,9F,EA), V(D6,7C,B0,29), \ + V(AF,B2,A4,31), V(31,23,3F,2A), V(30,94,A5,C6), V(C0,66,A2,35), \ + V(37,BC,4E,74), V(A6,CA,82,FC), V(B0,D0,90,E0), V(15,D8,A7,33), \ + V(4A,98,04,F1), V(F7,DA,EC,41), V(0E,50,CD,7F), V(2F,F6,91,17), \ + V(8D,D6,4D,76), V(4D,B0,EF,43), V(54,4D,AA,CC), V(DF,04,96,E4), \ + V(E3,B5,D1,9E), V(1B,88,6A,4C), V(B8,1F,2C,C1), V(7F,51,65,46), \ + V(04,EA,5E,9D), V(5D,35,8C,01), V(73,74,87,FA), V(2E,41,0B,FB), \ + V(5A,1D,67,B3), V(52,D2,DB,92), V(33,56,10,E9), V(13,47,D6,6D), \ + V(8C,61,D7,9A), V(7A,0C,A1,37), V(8E,14,F8,59), V(89,3C,13,EB), \ + V(EE,27,A9,CE), V(35,C9,61,B7), V(ED,E5,1C,E1), V(3C,B1,47,7A), \ + V(59,DF,D2,9C), V(3F,73,F2,55), V(79,CE,14,18), V(BF,37,C7,73), \ + V(EA,CD,F7,53), V(5B,AA,FD,5F), V(14,6F,3D,DF), V(86,DB,44,78), \ + V(81,F3,AF,CA), V(3E,C4,68,B9), V(2C,34,24,38), V(5F,40,A3,C2), \ + V(72,C3,1D,16), V(0C,25,E2,BC), V(8B,49,3C,28), V(41,95,0D,FF), \ + V(71,01,A8,39), V(DE,B3,0C,08), V(9C,E4,B4,D8), V(90,C1,56,64), \ + V(61,84,CB,7B), V(70,B6,32,D5), V(74,5C,6C,48), V(42,57,B8,D0) + +#define V(a,b,c,d) 0x##a##b##c##d +static const uint32_t RT0[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +static const uint32_t RT1[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +static const uint32_t RT2[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const uint32_t RT3[256] = { RT }; +#undef V + +#undef RT + +/* + * Round constants + */ +static const uint32_t RCON[10] = +{ + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x0000001B, 0x00000036 +}; + +#else + +/* + * Forward S-box & tables + */ +static unsigned char FSb[256]; +static uint32_t FT0[256]; +static uint32_t FT1[256]; +static uint32_t FT2[256]; +static uint32_t FT3[256]; + +/* + * Reverse S-box & tables + */ +static unsigned char RSb[256]; +static uint32_t RT0[256]; +static uint32_t RT1[256]; +static uint32_t RT2[256]; +static uint32_t RT3[256]; + +/* + * Round constants + */ +static uint32_t RCON[10]; + +/* + * Tables generation code + */ +#define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 ) +#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) ) +#define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 ) + +static int aes_init_done = 0; + +static void aes_gen_tables( void ) +{ + int i, x, y, z; + int pow[256]; + int log[256]; + + /* + * compute pow and log tables over GF(2^8) + */ + for( i = 0, x = 1; i < 256; i++ ) + { + pow[i] = x; + log[x] = i; + x = ( x ^ XTIME( x ) ) & 0xFF; + } + + /* + * calculate the round constants + */ + for( i = 0, x = 1; i < 10; i++ ) + { + RCON[i] = (uint32_t) x; + x = XTIME( x ) & 0xFF; + } + + /* + * generate the forward and reverse S-boxes + */ + FSb[0x00] = 0x63; + RSb[0x63] = 0x00; + + for( i = 1; i < 256; i++ ) + { + x = pow[255 - log[i]]; + + y = x; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y; y = ( (y << 1) | (y >> 7) ) & 0xFF; + x ^= y ^ 0x63; + + FSb[i] = (unsigned char) x; + RSb[x] = (unsigned char) i; + } + + /* + * generate the forward and reverse tables + */ + for( i = 0; i < 256; i++ ) + { + x = FSb[i]; + y = XTIME( x ) & 0xFF; + z = ( y ^ x ) & 0xFF; + + FT0[i] = ( (uint32_t) y ) ^ + ( (uint32_t) x << 8 ) ^ + ( (uint32_t) x << 16 ) ^ + ( (uint32_t) z << 24 ); + + FT1[i] = ROTL8( FT0[i] ); + FT2[i] = ROTL8( FT1[i] ); + FT3[i] = ROTL8( FT2[i] ); + + x = RSb[i]; + + RT0[i] = ( (uint32_t) MUL( 0x0E, x ) ) ^ + ( (uint32_t) MUL( 0x09, x ) << 8 ) ^ + ( (uint32_t) MUL( 0x0D, x ) << 16 ) ^ + ( (uint32_t) MUL( 0x0B, x ) << 24 ); + + RT1[i] = ROTL8( RT0[i] ); + RT2[i] = ROTL8( RT1[i] ); + RT3[i] = ROTL8( RT2[i] ); + } +} + +#endif + +/* + * AES key schedule (encryption) + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, unsigned int keysize ) +{ + unsigned int i; + uint32_t *RK; + +#if !defined(POLARSSL_AES_ROM_TABLES) + if( aes_init_done == 0 ) + { + aes_gen_tables(); + aes_init_done = 1; + + } +#endif + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + +#if defined(POLARSSL_PADLOCK_C) && defined(PADLOCK_ALIGN16) + if( aes_padlock_ace == -1 ) + aes_padlock_ace = padlock_supports( PADLOCK_ACE ); + + if( aes_padlock_ace ) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); + else +#endif + ctx->rk = RK = ctx->buf; + + for( i = 0; i < (keysize >> 5); i++ ) + { + GET_UINT32_LE( RK[i], key, i << 2 ); + } + + switch( ctx->nr ) + { + case 10: + + for( i = 0; i < 10; i++, RK += 4 ) + { + RK[4] = RK[0] ^ RCON[i] ^ + ( (uint32_t) FSb[ ( RK[3] >> 8 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[3] >> 16 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[3] >> 24 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[3] ) & 0xFF ] << 24 ); + + RK[5] = RK[1] ^ RK[4]; + RK[6] = RK[2] ^ RK[5]; + RK[7] = RK[3] ^ RK[6]; + } + break; + + case 12: + + for( i = 0; i < 8; i++, RK += 6 ) + { + RK[6] = RK[0] ^ RCON[i] ^ + ( (uint32_t) FSb[ ( RK[5] >> 8 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[5] >> 16 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[5] >> 24 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[5] ) & 0xFF ] << 24 ); + + RK[7] = RK[1] ^ RK[6]; + RK[8] = RK[2] ^ RK[7]; + RK[9] = RK[3] ^ RK[8]; + RK[10] = RK[4] ^ RK[9]; + RK[11] = RK[5] ^ RK[10]; + } + break; + + case 14: + + for( i = 0; i < 7; i++, RK += 8 ) + { + RK[8] = RK[0] ^ RCON[i] ^ + ( (uint32_t) FSb[ ( RK[7] >> 8 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[7] >> 16 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[7] >> 24 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[7] ) & 0xFF ] << 24 ); + + RK[9] = RK[1] ^ RK[8]; + RK[10] = RK[2] ^ RK[9]; + RK[11] = RK[3] ^ RK[10]; + + RK[12] = RK[4] ^ + ( (uint32_t) FSb[ ( RK[11] ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[11] >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[11] >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[11] >> 24 ) & 0xFF ] << 24 ); + + RK[13] = RK[5] ^ RK[12]; + RK[14] = RK[6] ^ RK[13]; + RK[15] = RK[7] ^ RK[14]; + } + break; + + default: + + break; + } + + return( 0 ); +} + +/* + * AES key schedule (decryption) + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, unsigned int keysize ) +{ + int i, j; + aes_context cty; + uint32_t *RK; + uint32_t *SK; + int ret; + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + +#if defined(POLARSSL_PADLOCK_C) && defined(PADLOCK_ALIGN16) + if( aes_padlock_ace == -1 ) + aes_padlock_ace = padlock_supports( PADLOCK_ACE ); + + if( aes_padlock_ace ) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); + else +#endif + ctx->rk = RK = ctx->buf; + + ret = aes_setkey_enc( &cty, key, keysize ); + if( ret != 0 ) + return( ret ); + + SK = cty.rk + cty.nr * 4; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + for( i = ctx->nr - 1, SK -= 8; i > 0; i--, SK -= 8 ) + { + for( j = 0; j < 4; j++, SK++ ) + { + *RK++ = RT0[ FSb[ ( *SK ) & 0xFF ] ] ^ + RT1[ FSb[ ( *SK >> 8 ) & 0xFF ] ] ^ + RT2[ FSb[ ( *SK >> 16 ) & 0xFF ] ] ^ + RT3[ FSb[ ( *SK >> 24 ) & 0xFF ] ]; + } + } + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + memset( &cty, 0, sizeof( aes_context ) ); + + return( 0 ); +} + +#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \ + FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \ + FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y0 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \ + FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \ + FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y2 >> 24 ) & 0xFF ]; \ +} + +#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \ + RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \ + RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y2 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \ + RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \ + RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y0 >> 24 ) & 0xFF ]; \ +} + +/* + * AES-ECB block encryption/decryption + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + int i; + uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; + +#if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) + if( aes_padlock_ace ) + { + if( padlock_xcryptecb( ctx, mode, input, output ) == 0 ) + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + RK = ctx->rk; + + GET_UINT32_LE( X0, input, 0 ); X0 ^= *RK++; + GET_UINT32_LE( X1, input, 4 ); X1 ^= *RK++; + GET_UINT32_LE( X2, input, 8 ); X2 ^= *RK++; + GET_UINT32_LE( X3, input, 12 ); X3 ^= *RK++; + + if( mode == AES_DECRYPT ) + { + for( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y0 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y1 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y2 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y3 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + } + else /* AES_ENCRYPT */ + { + for( i = (ctx->nr >> 1) - 1; i > 0; i-- ) + { + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y0 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y1 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y2 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y3 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + } + + PUT_UINT32_LE( X0, output, 0 ); + PUT_UINT32_LE( X1, output, 4 ); + PUT_UINT32_LE( X2, output, 8 ); + PUT_UINT32_LE( X3, output, 12 ); + + return( 0 ); +} + +/* + * AES-CBC buffer encryption/decryption + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int i; + unsigned char temp[16]; + + if( length % 16 ) + return( POLARSSL_ERR_AES_INVALID_INPUT_LENGTH ); + +#if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) + if( aes_padlock_ace ) + { + if( padlock_xcryptcbc( ctx, mode, length, iv, input, output ) == 0 ) + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + if( mode == AES_DECRYPT ) + { + while( length > 0 ) + { + memcpy( temp, input, 16 ); + aes_crypt_ecb( ctx, mode, input, output ); + + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + else + { + while( length > 0 ) + { + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + aes_crypt_ecb( ctx, mode, output, output ); + memcpy( iv, output, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + + return( 0 ); +} + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/* + * AES-CFB128 buffer encryption/decryption + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int c; + size_t n = *iv_off; + + if( mode == AES_DECRYPT ) + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + c = *input++; + *output++ = (unsigned char)( c ^ iv[n] ); + iv[n] = (unsigned char) c; + + n = (n + 1) & 0x0F; + } + } + else + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); + + n = (n + 1) & 0x0F; + } + } + + *iv_off = n; + + return( 0 ); +} +#endif /*POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/* + * AES-CTR buffer encryption/decryption + */ +int aes_crypt_ctr( aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ) +{ + int c, i; + size_t n = *nc_off; + + while( length-- ) + { + if( n == 0 ) { + aes_crypt_ecb( ctx, AES_ENCRYPT, nonce_counter, stream_block ); + + for( i = 16; i > 0; i-- ) + if( ++nonce_counter[i - 1] != 0 ) + break; + } + c = *input++; + *output++ = (unsigned char)( c ^ stream_block[n] ); + + n = (n + 1) & 0x0F; + } + + *nc_off = n; + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_CTR */ +#endif /* !POLARSSL_AES_ALT */ + +#if defined(POLARSSL_SELF_TEST) + +#include + +/* + * AES test vectors from: + * + * http://csrc.nist.gov/archive/aes/rijndael/rijndael-vals.zip + */ +static const unsigned char aes_test_ecb_dec[3][16] = +{ + { 0x44, 0x41, 0x6A, 0xC2, 0xD1, 0xF5, 0x3C, 0x58, + 0x33, 0x03, 0x91, 0x7E, 0x6B, 0xE9, 0xEB, 0xE0 }, + { 0x48, 0xE3, 0x1E, 0x9E, 0x25, 0x67, 0x18, 0xF2, + 0x92, 0x29, 0x31, 0x9C, 0x19, 0xF1, 0x5B, 0xA4 }, + { 0x05, 0x8C, 0xCF, 0xFD, 0xBB, 0xCB, 0x38, 0x2D, + 0x1F, 0x6F, 0x56, 0x58, 0x5D, 0x8A, 0x4A, 0xDE } +}; + +static const unsigned char aes_test_ecb_enc[3][16] = +{ + { 0xC3, 0x4C, 0x05, 0x2C, 0xC0, 0xDA, 0x8D, 0x73, + 0x45, 0x1A, 0xFE, 0x5F, 0x03, 0xBE, 0x29, 0x7F }, + { 0xF3, 0xF6, 0x75, 0x2A, 0xE8, 0xD7, 0x83, 0x11, + 0x38, 0xF0, 0x41, 0x56, 0x06, 0x31, 0xB1, 0x14 }, + { 0x8B, 0x79, 0xEE, 0xCC, 0x93, 0xA0, 0xEE, 0x5D, + 0xFF, 0x30, 0xB4, 0xEA, 0x21, 0x63, 0x6D, 0xA4 } +}; + +static const unsigned char aes_test_cbc_dec[3][16] = +{ + { 0xFA, 0xCA, 0x37, 0xE0, 0xB0, 0xC8, 0x53, 0x73, + 0xDF, 0x70, 0x6E, 0x73, 0xF7, 0xC9, 0xAF, 0x86 }, + { 0x5D, 0xF6, 0x78, 0xDD, 0x17, 0xBA, 0x4E, 0x75, + 0xB6, 0x17, 0x68, 0xC6, 0xAD, 0xEF, 0x7C, 0x7B }, + { 0x48, 0x04, 0xE1, 0x81, 0x8F, 0xE6, 0x29, 0x75, + 0x19, 0xA3, 0xE8, 0x8C, 0x57, 0x31, 0x04, 0x13 } +}; + +static const unsigned char aes_test_cbc_enc[3][16] = +{ + { 0x8A, 0x05, 0xFC, 0x5E, 0x09, 0x5A, 0xF4, 0x84, + 0x8A, 0x08, 0xD3, 0x28, 0xD3, 0x68, 0x8E, 0x3D }, + { 0x7B, 0xD9, 0x66, 0xD5, 0x3A, 0xD8, 0xC1, 0xBB, + 0x85, 0xD2, 0xAD, 0xFA, 0xE8, 0x7B, 0xB1, 0x04 }, + { 0xFE, 0x3C, 0x53, 0x65, 0x3E, 0x2F, 0x45, 0xB5, + 0x6F, 0xCD, 0x88, 0xB2, 0xCC, 0x89, 0x8F, 0xF0 } +}; + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/* + * AES-CFB128 test vectors from: + * + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + */ +static const unsigned char aes_test_cfb128_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }, + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B }, + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char aes_test_cfb128_iv[16] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; + +static const unsigned char aes_test_cfb128_pt[64] = +{ + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 +}; + +static const unsigned char aes_test_cfb128_ct[3][64] = +{ + { 0x3B, 0x3F, 0xD9, 0x2E, 0xB7, 0x2D, 0xAD, 0x20, + 0x33, 0x34, 0x49, 0xF8, 0xE8, 0x3C, 0xFB, 0x4A, + 0xC8, 0xA6, 0x45, 0x37, 0xA0, 0xB3, 0xA9, 0x3F, + 0xCD, 0xE3, 0xCD, 0xAD, 0x9F, 0x1C, 0xE5, 0x8B, + 0x26, 0x75, 0x1F, 0x67, 0xA3, 0xCB, 0xB1, 0x40, + 0xB1, 0x80, 0x8C, 0xF1, 0x87, 0xA4, 0xF4, 0xDF, + 0xC0, 0x4B, 0x05, 0x35, 0x7C, 0x5D, 0x1C, 0x0E, + 0xEA, 0xC4, 0xC6, 0x6F, 0x9F, 0xF7, 0xF2, 0xE6 }, + { 0xCD, 0xC8, 0x0D, 0x6F, 0xDD, 0xF1, 0x8C, 0xAB, + 0x34, 0xC2, 0x59, 0x09, 0xC9, 0x9A, 0x41, 0x74, + 0x67, 0xCE, 0x7F, 0x7F, 0x81, 0x17, 0x36, 0x21, + 0x96, 0x1A, 0x2B, 0x70, 0x17, 0x1D, 0x3D, 0x7A, + 0x2E, 0x1E, 0x8A, 0x1D, 0xD5, 0x9B, 0x88, 0xB1, + 0xC8, 0xE6, 0x0F, 0xED, 0x1E, 0xFA, 0xC4, 0xC9, + 0xC0, 0x5F, 0x9F, 0x9C, 0xA9, 0x83, 0x4F, 0xA0, + 0x42, 0xAE, 0x8F, 0xBA, 0x58, 0x4B, 0x09, 0xFF }, + { 0xDC, 0x7E, 0x84, 0xBF, 0xDA, 0x79, 0x16, 0x4B, + 0x7E, 0xCD, 0x84, 0x86, 0x98, 0x5D, 0x38, 0x60, + 0x39, 0xFF, 0xED, 0x14, 0x3B, 0x28, 0xB1, 0xC8, + 0x32, 0x11, 0x3C, 0x63, 0x31, 0xE5, 0x40, 0x7B, + 0xDF, 0x10, 0x13, 0x24, 0x15, 0xE5, 0x4B, 0x92, + 0xA1, 0x3E, 0xD0, 0xA8, 0x26, 0x7A, 0xE2, 0xF9, + 0x75, 0xA3, 0x85, 0x74, 0x1A, 0xB9, 0xCE, 0xF8, + 0x20, 0x31, 0x62, 0x3D, 0x55, 0xB1, 0xE4, 0x71 } +}; +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/* + * AES-CTR test vectors from: + * + * http://www.faqs.org/rfcs/rfc3686.html + */ + +static const unsigned char aes_test_ctr_key[3][16] = +{ + { 0xAE, 0x68, 0x52, 0xF8, 0x12, 0x10, 0x67, 0xCC, + 0x4B, 0xF7, 0xA5, 0x76, 0x55, 0x77, 0xF3, 0x9E }, + { 0x7E, 0x24, 0x06, 0x78, 0x17, 0xFA, 0xE0, 0xD7, + 0x43, 0xD6, 0xCE, 0x1F, 0x32, 0x53, 0x91, 0x63 }, + { 0x76, 0x91, 0xBE, 0x03, 0x5E, 0x50, 0x20, 0xA8, + 0xAC, 0x6E, 0x61, 0x85, 0x29, 0xF9, 0xA0, 0xDC } +}; + +static const unsigned char aes_test_ctr_nonce_counter[3][16] = +{ + { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0x6C, 0xB6, 0xDB, 0xC0, 0x54, 0x3B, 0x59, + 0xDA, 0x48, 0xD9, 0x0B, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0xE0, 0x01, 0x7B, 0x27, 0x77, 0x7F, 0x3F, + 0x4A, 0x17, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x01 } +}; + +static const unsigned char aes_test_ctr_pt[3][48] = +{ + { 0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x62, + 0x6C, 0x6F, 0x63, 0x6B, 0x20, 0x6D, 0x73, 0x67 }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23 } +}; + +static const unsigned char aes_test_ctr_ct[3][48] = +{ + { 0xE4, 0x09, 0x5D, 0x4F, 0xB7, 0xA7, 0xB3, 0x79, + 0x2D, 0x61, 0x75, 0xA3, 0x26, 0x13, 0x11, 0xB8 }, + { 0x51, 0x04, 0xA1, 0x06, 0x16, 0x8A, 0x72, 0xD9, + 0x79, 0x0D, 0x41, 0xEE, 0x8E, 0xDA, 0xD3, 0x88, + 0xEB, 0x2E, 0x1E, 0xFC, 0x46, 0xDA, 0x57, 0xC8, + 0xFC, 0xE6, 0x30, 0xDF, 0x91, 0x41, 0xBE, 0x28 }, + { 0xC1, 0xCF, 0x48, 0xA8, 0x9F, 0x2F, 0xFD, 0xD9, + 0xCF, 0x46, 0x52, 0xE9, 0xEF, 0xDB, 0x72, 0xD7, + 0x45, 0x40, 0xA4, 0x2B, 0xDE, 0x6D, 0x78, 0x36, + 0xD5, 0x9A, 0x5C, 0xEA, 0xAE, 0xF3, 0x10, 0x53, + 0x25, 0xB2, 0x07, 0x2F } +}; + +static const int aes_test_ctr_len[3] = + { 16, 32, 36 }; +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +/* + * Checkup routine + */ +int aes_self_test( int verbose ) +{ + int i, j, u, v; + unsigned char key[32]; + unsigned char buf[64]; + unsigned char prv[16]; + unsigned char iv[16]; +#if defined(POLARSSL_CIPHER_MODE_CTR) || defined(POLARSSL_CIPHER_MODE_CFB) + size_t offset; +#endif +#if defined(POLARSSL_CIPHER_MODE_CTR) + int len; + unsigned char nonce_counter[16]; + unsigned char stream_block[16]; +#endif + aes_context ctx; + + memset( key, 0, 32 ); + + /* + * ECB mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-ECB-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( buf, 0, 16 ); + + if( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if( memcmp( buf, aes_test_ecb_dec[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if( memcmp( buf, aes_test_ecb_enc[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + + /* + * CBC mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-CBC-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( iv , 0, 16 ); + memset( prv, 0, 16 ); + memset( buf, 0, 16 ); + + if( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + if( memcmp( buf, aes_test_cbc_dec[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + { + unsigned char tmp[16]; + + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + memcpy( tmp, prv, 16 ); + memcpy( prv, buf, 16 ); + memcpy( buf, tmp, 16 ); + } + + if( memcmp( prv, aes_test_cbc_enc[u], 16 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); + +#if defined(POLARSSL_CIPHER_MODE_CFB) + /* + * CFB128 mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-CFB128-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( iv, aes_test_cfb128_iv, 16 ); + memcpy( key, aes_test_cfb128_key[u], 16 + u * 8 ); + + offset = 0; + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + if( v == AES_DECRYPT ) + { + memcpy( buf, aes_test_cfb128_ct[u], 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if( memcmp( buf, aes_test_cfb128_pt, 64 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + memcpy( buf, aes_test_cfb128_pt, 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if( memcmp( buf, aes_test_cfb128_ct[u], 64 ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) + /* + * CTR mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + printf( " AES-CTR-128 (%s): ", + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( nonce_counter, aes_test_ctr_nonce_counter[u], 16 ); + memcpy( key, aes_test_ctr_key[u], 16 ); + + offset = 0; + aes_setkey_enc( &ctx, key, 128 ); + + if( v == AES_DECRYPT ) + { + len = aes_test_ctr_len[u]; + memcpy( buf, aes_test_ctr_ct[u], len ); + + aes_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, buf, buf ); + + if( memcmp( buf, aes_test_ctr_pt[u], len ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + else + { + len = aes_test_ctr_len[u]; + memcpy( buf, aes_test_ctr_pt[u], len ); + + aes_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, buf, buf ); + + if( memcmp( buf, aes_test_ctr_ct[u], len ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + } + + if( verbose != 0 ) + printf( "\n" ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ + + return( 0 ); +} + +#endif + +#endif diff --git a/affine.h b/affine.h new file mode 100644 index 0000000..2f6e875 --- /dev/null +++ b/affine.h @@ -0,0 +1,8 @@ +/** + * @brief Affine coordinates + */ +typedef struct +{ + bn256 x[1]; + bn256 y[1]; +} ac; diff --git a/bignum.c b/bignum.c new file mode 100644 index 0000000..775a44c --- /dev/null +++ b/bignum.c @@ -0,0 +1,2561 @@ +/* + * Multi-precision integer library + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * This MPI implementation is based on: + * + * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + * http://www.stillhq.com/extracted/gnupg-api/mpi/ + * http://math.libtomcrypt.com/files/tommath.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_BIGNUM_C) + +#include "polarssl/bignum.h" +#include "polarssl/bn_mul.h" + +//#include +#include + +#define ciL (sizeof(t_uint)) /* chars in limb */ +#define biL (ciL << 3) /* bits in limb */ +#define biH (ciL << 2) /* half limb size */ + +/* + * Convert between bits/chars and number of limbs + */ +#define BITS_TO_LIMBS(i) (((i) + biL - 1) / biL) +#define CHARS_TO_LIMBS(i) (((i) + ciL - 1) / ciL) + +/* + * Initialize one MPI + */ +void mpi_init( mpi *X ) +{ + if( X == NULL ) + return; + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Unallocate one MPI + */ +void mpi_free( mpi *X ) +{ + if( X == NULL ) + return; + + if( X->p != NULL ) + { + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Enlarge to the specified number of limbs + */ +int mpi_grow( mpi *X, size_t nblimbs ) +{ + t_uint *p; + + if( nblimbs > POLARSSL_MPI_MAX_LIMBS ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + if( X->n < nblimbs ) + { + if( ( p = (t_uint *) malloc( nblimbs * ciL ) ) == NULL ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + memset( p, 0, nblimbs * ciL ); + + if( X->p != NULL ) + { + memcpy( p, X->p, X->n * ciL ); + memset( X->p, 0, X->n * ciL ); + free( X->p ); + } + + X->n = nblimbs; + X->p = p; + } + + return( 0 ); +} + +/* + * Copy the contents of Y into X + */ +int mpi_copy( mpi *X, const mpi *Y ) +{ + int ret; + size_t i; + + if( X == Y ) + return( 0 ); + + for( i = Y->n - 1; i > 0; i-- ) + if( Y->p[i] != 0 ) + break; + i++; + + X->s = Y->s; + + MPI_CHK( mpi_grow( X, i ) ); + + memset( X->p, 0, X->n * ciL ); + memcpy( X->p, Y->p, i * ciL ); + +cleanup: + + return( ret ); +} + +/* + * Swap the contents of X and Y + */ +void mpi_swap( mpi *X, mpi *Y ) +{ + mpi T; + + memcpy( &T, X, sizeof( mpi ) ); + memcpy( X, Y, sizeof( mpi ) ); + memcpy( Y, &T, sizeof( mpi ) ); +} + +/* + * Set value from integer + */ +int mpi_lset( mpi *X, t_sint z ) +{ + int ret; + + MPI_CHK( mpi_grow( X, 1 ) ); + memset( X->p, 0, X->n * ciL ); + + X->p[0] = ( z < 0 ) ? -z : z; + X->s = ( z < 0 ) ? -1 : 1; + +cleanup: + + return( ret ); +} + +/* + * Get a specific bit + */ +int mpi_get_bit( const mpi *X, size_t pos ) +{ + if( X->n * biL <= pos ) + return( 0 ); + + return ( X->p[pos / biL] >> ( pos % biL ) ) & 0x01; +} + +/* + * Set a bit to a specific value of 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ) +{ + int ret = 0; + size_t off = pos / biL; + size_t idx = pos % biL; + + if( val != 0 && val != 1 ) + return POLARSSL_ERR_MPI_BAD_INPUT_DATA; + + if( X->n * biL <= pos ) + { + if( val == 0 ) + return ( 0 ); + + MPI_CHK( mpi_grow( X, off + 1 ) ); + } + + X->p[off] = ( X->p[off] & ~( 0x01 << idx ) ) | ( val << idx ); + +cleanup: + + return( ret ); +} + +/* + * Return the number of least significant bits + */ +size_t mpi_lsb( const mpi *X ) +{ + size_t i, j, count = 0; + + for( i = 0; i < X->n; i++ ) + for( j = 0; j < biL; j++, count++ ) + if( ( ( X->p[i] >> j ) & 1 ) != 0 ) + return( count ); + + return( 0 ); +} + +#if !defined(POLARSSL_HAVE_UDBL) +/* + * Count leading zero bits in a given integer + */ +static size_t int_clz( const t_uint x ) +{ + size_t j; + t_uint mask = (t_uint) 1 << (biL - 1); + + for( j = 0; j < biL; j++ ) + { + if( x & mask ) break; + + mask >>= 1; + } + + return j; +} +#endif + +/* + * Return the number of most significant bits + */ +size_t mpi_msb( const mpi *X ) +{ + size_t i, j; + + for( i = X->n - 1; i > 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = biL; j > 0; j-- ) + if( ( ( X->p[i] >> ( j - 1 ) ) & 1 ) != 0 ) + break; + + return( ( i * biL ) + j ); +} + +/* + * Return the total size in bytes + */ +size_t mpi_size( const mpi *X ) +{ + return( ( mpi_msb( X ) + 7 ) >> 3 ); +} + +/* + * Convert an ASCII character to digit value + */ +static int mpi_get_digit( t_uint *d, int radix, char c ) +{ + *d = 255; + + if( c >= 0x30 && c <= 0x39 ) *d = c - 0x30; + if( c >= 0x41 && c <= 0x46 ) *d = c - 0x37; + if( c >= 0x61 && c <= 0x66 ) *d = c - 0x57; + + if( *d >= (t_uint) radix ) + return( POLARSSL_ERR_MPI_INVALID_CHARACTER ); + + return( 0 ); +} + +/* + * Import from an ASCII string + */ +int mpi_read_string( mpi *X, int radix, const char *s ) +{ + int ret; + size_t i, j, slen, n; + t_uint d; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &T ); + + slen = strlen( s ); + + if( radix == 16 ) + { + n = BITS_TO_LIMBS( slen << 2 ); + + MPI_CHK( mpi_grow( X, n ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = slen, j = 0; i > 0; i--, j++ ) + { + if( i == 1 && s[i - 1] == '-' ) + { + X->s = -1; + break; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i - 1] ) ); + X->p[j / (2 * ciL)] |= d << ( (j % (2 * ciL)) << 2 ); + } + } + else + { + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = 0; i < slen; i++ ) + { + if( i == 0 && s[i] == '-' ) + { + X->s = -1; + continue; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + MPI_CHK( mpi_mul_int( &T, X, radix ) ); + + if( X->s == 1 ) + { + MPI_CHK( mpi_add_int( X, &T, d ) ); + } + else + { + MPI_CHK( mpi_sub_int( X, &T, d ) ); + } + } + } + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +/* + * Helper to write the digits high-order first + */ +static int mpi_write_hlp( mpi *X, int radix, char **p ) +{ + int ret; + t_uint r; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + MPI_CHK( mpi_mod_int( &r, X, radix ) ); + MPI_CHK( mpi_div_int( X, NULL, X, radix ) ); + + if( mpi_cmp_int( X, 0 ) != 0 ) + MPI_CHK( mpi_write_hlp( X, radix, p ) ); + + if( r < 10 ) + *(*p)++ = (char)( r + 0x30 ); + else + *(*p)++ = (char)( r + 0x37 ); + +cleanup: + + return( ret ); +} + +/* + * Export into an ASCII string + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ) +{ + int ret = 0; + size_t n; + char *p; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + n = mpi_msb( X ); + if( radix >= 4 ) n >>= 1; + if( radix >= 16 ) n >>= 1; + n += 3; + + if( *slen < n ) + { + *slen = n; + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + } + + p = s; + mpi_init( &T ); + + if( X->s == -1 ) + *p++ = '-'; + + if( radix == 16 ) + { + int c; + size_t i, j, k; + + for( i = X->n, k = 0; i > 0; i-- ) + { + for( j = ciL; j > 0; j-- ) + { + c = ( X->p[i - 1] >> ( ( j - 1 ) << 3) ) & 0xFF; + + if( c == 0 && k == 0 && ( i + j + 3 ) != 0 ) + continue; + + *(p++) = "0123456789ABCDEF" [c / 16]; + *(p++) = "0123456789ABCDEF" [c % 16]; + k = 1; + } + } + } + else + { + MPI_CHK( mpi_copy( &T, X ) ); + + if( T.s == -1 ) + T.s = 1; + + MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); + } + + *p++ = '\0'; + *slen = p - s; + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Read X from an opened file + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ) +{ + t_uint d; + size_t slen; + char *p; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + memset( s, 0, sizeof( s ) ); + if( fgets( s, sizeof( s ) - 1, fin ) == NULL ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + + slen = strlen( s ); + if( slen == sizeof( s ) - 2 ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + if( s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; } + if( s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; } + + p = s + slen; + while( --p >= s ) + if( mpi_get_digit( &d, radix, *p ) != 0 ) + break; + + return( mpi_read_string( X, radix, p + 1 ) ); +} + +/* + * Write X into an opened file (or stdout if fout == NULL) + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ) +{ + int ret; + size_t n, slen, plen; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + n = sizeof( s ); + memset( s, 0, n ); + n -= 2; + + MPI_CHK( mpi_write_string( X, radix, s, (size_t *) &n ) ); + + if( p == NULL ) p = ""; + + plen = strlen( p ); + slen = strlen( s ); + s[slen++] = '\r'; + s[slen++] = '\n'; + + if( fout != NULL ) + { + if( fwrite( p, 1, plen, fout ) != plen || + fwrite( s, 1, slen, fout ) != slen ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + } + else + printf( "%s%s", p, s ); + +cleanup: + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * Import X from unsigned binary data, big endian + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t i, j, n; + + for( n = 0; n < buflen; n++ ) + if( buf[n] != 0 ) + break; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = buflen, j = 0; i > n; i--, j++ ) + X->p[j / ciL] |= ((t_uint) buf[i - 1]) << ((j % ciL) << 3); + +cleanup: + + return( ret ); +} + +/* + * Export X into unsigned binary data, big endian + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ) +{ + size_t i, j, n; + + n = mpi_size( X ); + + if( buflen < n ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + memset( buf, 0, buflen ); + + for( i = buflen - 1, j = 0; n > 0; i--, j++, n-- ) + buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) ); + + return( 0 ); +} + +/* + * Left-shift: X <<= count + */ +int mpi_shift_l( mpi *X, size_t count ) +{ + int ret; + size_t i, v0, t1; + t_uint r0 = 0, r1; + + v0 = count / (biL ); + t1 = count & (biL - 1); + + i = mpi_msb( X ) + count; + + if( X->n * biL < i ) + MPI_CHK( mpi_grow( X, BITS_TO_LIMBS( i ) ) ); + + ret = 0; + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = X->n; i > v0; i-- ) + X->p[i - 1] = X->p[i - v0 - 1]; + + for( ; i > 0; i-- ) + X->p[i - 1] = 0; + } + + /* + * shift by count % limb_size + */ + if( t1 > 0 ) + { + for( i = v0; i < X->n; i++ ) + { + r1 = X->p[i] >> (biL - t1); + X->p[i] <<= t1; + X->p[i] |= r0; + r0 = r1; + } + } + +cleanup: + + return( ret ); +} + +/* + * Right-shift: X >>= count + */ +int mpi_shift_r( mpi *X, size_t count ) +{ + size_t i, v0, v1; + t_uint r0 = 0, r1; + + v0 = count / biL; + v1 = count & (biL - 1); + + if( v0 > X->n || ( v0 == X->n && v1 > 0 ) ) + return mpi_lset( X, 0 ); + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = 0; i < X->n - v0; i++ ) + X->p[i] = X->p[i + v0]; + + for( ; i < X->n; i++ ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if( v1 > 0 ) + { + for( i = X->n; i > 0; i-- ) + { + r1 = X->p[i - 1] << (biL - v1); + X->p[i - 1] >>= v1; + X->p[i - 1] |= r0; + r0 = r1; + } + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +static int mpi_cmp_abs_limbs (size_t n, const t_uint *p0, const t_uint *p1) +{ + size_t i, j; + + p0 += n; + for( i = n; i > 0; i-- ) + if( *--p0 != 0 ) + break; + + p1 += n; + for( j = n; j > 0; j-- ) + if( *--p1 != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( 1 ); + if( j > i ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( *p0 > *p1 ) return( 1 ); + if( *p0 < *p1 ) return( -1 ); + p0--; p1--; + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( 1 ); + if( j > i ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( 1 ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -1 ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( X->s ); + if( j > i ) return( -Y->s ); + + if( X->s > 0 && Y->s < 0 ) return( 1 ); + if( Y->s > 0 && X->s < 0 ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( X->s ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -X->s ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_int( const mpi *X, t_sint z ) +{ + mpi Y; + t_uint p[1]; + + *p = ( z < 0 ) ? -z : z; + Y.s = ( z < 0 ) ? -1 : 1; + Y.n = 1; + Y.p = p; + + return( mpi_cmp_mpi( X, &Y ) ); +} + +/* + * Unsigned addition: X = |A| + |B| (HAC 14.7) + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j; + t_uint *o, *p, c; + + if( X == B ) + { + const mpi *T = A; A = X; B = T; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned additions. + */ + X->s = 1; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, j ) ); + + o = B->p; p = X->p; c = 0; + + for( i = 0; i < j; i++, o++, p++ ) + { + *p += c; c = ( *p < c ); + *p += *o; c += ( *p < *o ); + } + + while( c != 0 ) + { + if( i >= X->n ) + { + MPI_CHK( mpi_grow( X, i + 1 ) ); + p = X->p + i; + } + + *p += c; c = ( *p < c ); i++; p++; + } + +cleanup: + + return( ret ); +} + +/* + * Helper for mpi substraction + */ +static t_uint mpi_sub_hlp( size_t n, const t_uint *s, t_uint *d ) +{ + size_t i; + t_uint c, z; + + for( i = c = 0; i < n; i++, s++, d++ ) + { + z = ( *d < c ); *d -= c; + c = ( *d < *s ) + z; *d -= *s; + } + + return c; +} + +/* + * Unsigned substraction: X = |A| - |B| (HAC 14.9) + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ) +{ + mpi TB; + int ret; + size_t n; + t_uint *d; + t_uint c, z; + + if( mpi_cmp_abs( A, B ) < 0 ) + return( POLARSSL_ERR_MPI_NEGATIVE_VALUE ); + + mpi_init( &TB ); + + if( X == B ) + { + MPI_CHK( mpi_copy( &TB, B ) ); + B = &TB; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned substractions. + */ + X->s = 1; + + ret = 0; + + for( n = B->n; n > 0; n-- ) + if( B->p[n - 1] != 0 ) + break; + + c = mpi_sub_hlp( n, B->p, X->p ); + d = X->p + n; + + while( c != 0 ) + { + z = ( *d < c ); *d -= c; + c = z; d++; + } + +cleanup: + + mpi_free( &TB ); + + return( ret ); +} + +/* + * Signed addition: X = A + B + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s < 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed substraction: X = A - B + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s > 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed addition: X = A + b + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_add_mpi( X, A, &_B ) ); +} + +/* + * Signed substraction: X = A - b + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_sub_mpi( X, A, &_B ) ); +} + +/* + * Helper for mpi multiplication + */ +static +#if defined(__APPLE__) && defined(__arm__) +/* + * Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn) + * appears to need this to prevent bad ARM code generation at -O3. + */ +__attribute__ ((noinline)) +#endif +t_uint mpi_mul_hlp( size_t i, const t_uint *s, t_uint *d, t_uint b ) +{ + t_uint c = 0, t = 0; + +#if defined(MULADDC_1024_LOOP) + MULADDC_1024_LOOP + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#elif defined(MULADDC_HUIT) + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_HUIT + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#else + for( ; i >= 16; i -= 16 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#endif + + t++; + + *d += c; c = ( *d < c ); + return c; +} + +/* + * Baseline multiplication: X = A * B (HAC 14.12) + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j, k; + mpi TA, TB; + + mpi_init( &TA ); mpi_init( &TB ); + + if( X == A ) { MPI_CHK( mpi_copy( &TA, A ) ); A = &TA; } + if( X == B ) { MPI_CHK( mpi_copy( &TB, B ) ); B = &TB; } + + for( i = A->n; i > 0; i-- ) + if( A->p[i - 1] != 0 ) + break; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, i + j ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for(k = 0; k < j; k++ ) + mpi_mul_hlp( i, A->p, X->p + k, B->p[k]); + + X->s = A->s * B->s; + +cleanup: + + mpi_free( &TB ); mpi_free( &TA ); + + return( ret ); +} + +/* + * Baseline multiplication: X = A * b + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + _B.s = 1; + _B.n = 1; + _B.p = p; + p[0] = b; + + return( mpi_mul_mpi( X, A, &_B ) ); +} + +/* + * Unsigned integer divide - 64bit dividend and 32bit divisor + */ +static t_uint int_div_int(t_uint u1, t_uint u0, t_uint d, t_uint *r) +{ +#if defined(POLARSSL_HAVE_UDBL) + t_udbl dividend, quotient; +#else + const t_uint radix = (t_uint) 1 << biH; + const t_uint uint_halfword_mask = ( (t_uint) 1 << biH ) - 1; + t_uint d0, d1, q0, q1, rAX, r0, quotient; + t_uint u0_msw, u0_lsw; + size_t s; +#endif + + /* + * Check for overflow + */ + if(( 0 == d ) || ( u1 >= d )) + { + if (r != NULL) *r = (~0UL); + + return (~0UL); + } + +#if defined(POLARSSL_HAVE_UDBL) + dividend = (t_udbl) u1 << biL; + dividend |= (t_udbl) u0; + quotient = dividend / d; + if( quotient > ( (t_udbl) 1 << biL ) - 1 ) + quotient = ( (t_udbl) 1 << biL ) - 1; + + if( r != NULL ) + *r = (t_uint)( dividend - (quotient * d ) ); + + return (t_uint) quotient; +#else + + /* + * Algorithm D, Section 4.3.1 - The Art of Computer Programming + * Vol. 2 - Seminumerical Algorithms, Knuth + */ + + /* + * Normalize the divisor, d, and dividend, u0, u1 + */ + s = int_clz( d ); + d = d << s; + + u1 = u1 << s; + u1 |= ( u0 >> ( biL - s ) ) & ( -(t_sint)s >> ( biL - 1 ) ); + u0 = u0 << s; + + d1 = d >> biH; + d0 = d & uint_halfword_mask; + + u0_msw = u0 >> biH; + u0_lsw = u0 & uint_halfword_mask; + + /* + * Find the first quotient and remainder + */ + q1 = u1 / d1; + r0 = u1 - d1 * q1; + + while( q1 >= radix || ( q1 * d0 > radix * r0 + u0_msw ) ) + { + q1 -= 1; + r0 += d1; + + if ( r0 >= radix ) break; + } + + rAX = (u1 * radix) + (u0_msw - q1 * d); + q0 = rAX / d1; + r0 = rAX - q0 * d1; + + while( q0 >= radix || ( q0 * d0 > radix * r0 + u0_lsw ) ) + { + q0 -= 1; + r0 += d1; + + if ( r0 >= radix ) break; + } + + if (r != NULL) + *r = (rAX * radix + u0_lsw - q0 * d) >> s; + + quotient = q1 * radix + q0; + + return quotient; +#endif +} + +/* + * Division by mpi: A = Q * B + R (HAC 14.20) + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, n, t, k; + mpi X, Y, Z, T1, T2; + + if( mpi_cmp_int( B, 0 ) == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z ); + mpi_init( &T1 ); mpi_init( &T2 ); + + if( mpi_cmp_abs( A, B ) < 0 ) + { + if( Q != NULL ) MPI_CHK( mpi_lset( Q, 0 ) ); + if( R != NULL ) MPI_CHK( mpi_copy( R, A ) ); + return( 0 ); + } + + MPI_CHK( mpi_copy( &X, A ) ); + MPI_CHK( mpi_copy( &Y, B ) ); + X.s = Y.s = 1; + + MPI_CHK( mpi_grow( &Z, A->n + 2 ) ); + MPI_CHK( mpi_lset( &Z, 0 ) ); + MPI_CHK( mpi_grow( &T1, 2 ) ); + MPI_CHK( mpi_grow( &T2, 3 ) ); + + k = mpi_msb( &Y ) % biL; + if( k < biL - 1 ) + { + k = biL - 1 - k; + MPI_CHK( mpi_shift_l( &X, k ) ); + MPI_CHK( mpi_shift_l( &Y, k ) ); + } + else k = 0; + + n = X.n - 1; + t = Y.n - 1; + MPI_CHK( mpi_shift_l( &Y, biL * (n - t) ) ); + + while( mpi_cmp_mpi( &X, &Y ) >= 0 ) + { + Z.p[n - t]++; + mpi_sub_mpi( &X, &X, &Y ); + } + mpi_shift_r( &Y, biL * (n - t) ); + + for( i = n; i > t ; i-- ) + { + if( X.p[i] >= Y.p[t] ) + Z.p[i - t - 1] = ~0UL; + else + { + Z.p[i - t - 1] = int_div_int( X.p[i], X.p[i-1], Y.p[t], NULL); + } + + Z.p[i - t - 1]++; + do + { + Z.p[i - t - 1]--; + + MPI_CHK( mpi_lset( &T1, 0 ) ); + T1.p[0] = (t < 1) ? 0 : Y.p[t - 1]; + T1.p[1] = Y.p[t]; + MPI_CHK( mpi_mul_int( &T1, &T1, Z.p[i - t - 1] ) ); + + MPI_CHK( mpi_lset( &T2, 0 ) ); + T2.p[0] = (i < 2) ? 0 : X.p[i - 2]; + T2.p[1] = (i < 1) ? 0 : X.p[i - 1]; + T2.p[2] = X.p[i]; + } + while( mpi_cmp_mpi( &T1, &T2 ) > 0 ); + + MPI_CHK( mpi_mul_int( &T1, &Y, Z.p[i - t - 1] ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_sub_mpi( &X, &X, &T1 ) ); + + while( mpi_cmp_int( &X, 0 ) < 0 ) + { + MPI_CHK( mpi_copy( &T1, &Y ) ); + MPI_CHK( mpi_shift_l( &T1, biL * (i - t - 1) ) ); + MPI_CHK( mpi_add_mpi( &X, &X, &T1 ) ); + Z.p[i - t - 1]--; + } + } + + if( Q != NULL ) + { + mpi_copy( Q, &Z ); + Q->s = A->s * B->s; + } + + if( R != NULL ) + { + mpi_shift_r( &X, k ); + X.s = A->s; + mpi_copy( R, &X ); + + if( mpi_cmp_int( R, 0 ) == 0 ) + R->s = 1; + } + +cleanup: + + mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z ); + mpi_free( &T1 ); mpi_free( &T2 ); + + return( ret ); +} + +/* + * Division by int: A = Q * b + R + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_div_mpi( Q, R, A, &_B ) ); +} + +/* + * Modulo: R = A mod B + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + + if( mpi_cmp_int( B, 0 ) < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + MPI_CHK( mpi_div_mpi( NULL, R, A, B ) ); + + while( mpi_cmp_int( R, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( R, R, B ) ); + + while( mpi_cmp_mpi( R, B ) >= 0 ) + MPI_CHK( mpi_sub_mpi( R, R, B ) ); + +cleanup: + + return( ret ); +} + +/* + * Modulo: r = A mod b + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ) +{ + size_t i; + t_uint x, y, z; + + if( b == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + if( b < 0 ) + return POLARSSL_ERR_MPI_NEGATIVE_VALUE; + + /* + * handle trivial cases + */ + if( b == 1 ) + { + *r = 0; + return( 0 ); + } + + if( b == 2 ) + { + *r = A->p[0] & 1; + return( 0 ); + } + + /* + * general case + */ + for( i = A->n, y = 0; i > 0; i-- ) + { + x = A->p[i - 1]; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + + x <<= biH; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + } + + /* + * If A is negative, then the current y represents a negative value. + * Flipping it to the positive side. + */ + if( A->s < 0 && y != 0 ) + y = b - y; + + *r = y; + + return( 0 ); +} + +/* + * Fast Montgomery initialization (thanks to Tom St Denis) + */ +static void mpi_montg_init( t_uint *mm, const mpi *N ) +{ + t_uint x, m0 = N->p[0]; + + x = m0; + x += ( ( m0 + 2 ) & 4 ) << 1; + x *= ( 2 - ( m0 * x ) ); + + if( biL >= 16 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 32 ) x *= ( 2 - ( m0 * x ) ); + if( biL >= 64 ) x *= ( 2 - ( m0 * x ) ); + + *mm = ~x + 1; +} + +/* + * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) + * A is placed at the upper half of D. + */ +static void mpi_montmul( size_t n, const t_uint *np, t_uint mm, t_uint *d, + const t_uint *bp ) +{ + size_t i; + t_uint u0, u1, c = 0; + + for( i = 0; i < n; i++ ) + { + /* + * T = (T + u0*B + u1*N) / 2^biL + */ + u0 = d[n]; + d[n] = c; + u1 = ( d[0] + u0 * bp[0] ) * mm; + + mpi_mul_hlp( n, bp, d, u0 ); + c = mpi_mul_hlp( n, np, d, u1 ); + d++; + } + + /* prevent timing attacks */ + if( ((mpi_cmp_abs_limbs ( n, d, np ) >= 0) | c) ) + mpi_sub_hlp( n, np, d ); + else + mpi_sub_hlp( n, d - n, d - n); +} + +/* + * Montgomery reduction: A = A * R^-1 mod N + * A is placed at the upper half of D. + */ +static void mpi_montred( size_t n, const t_uint *np, t_uint mm, t_uint *d ) +{ + size_t i, j; + t_uint u0, u1, c = 0; + + for( i = 0; i < n; i++ ) + { + /* + * T = (T + u0 + u1*N) / 2^biL + */ + u0 = d[n]; + d[n] = c; + u1 = (d[0] + u0) * mm; + + d[0] += u0; + c = (d[0] < u0); + for (j = 1; j < n; j++) + { + d[j] += c; c = ( d[j] < c ); + } + + c = mpi_mul_hlp( n, np, d, u1 ); + d++; + } + + /* prevent timing attacks */ + if( ((mpi_cmp_abs_limbs ( n, d, np ) >= 0) | c) ) + mpi_sub_hlp( n, np, d ); + else + mpi_sub_hlp( n, d - n, d - n); +} + +/* + * Montgomery square: A = A * A * R^-1 mod N + * A is placed at the upper half of D. + * + * n : number of limbs of N + * np: pointer to limbs of bignum N + * mm: m' = -N^(-1) mod b where b = 2^number-of-bit-in-limb + * d (destination): the result [<-- temp -->][<--- A ---->] + * lower part upper part + * n-limb n-limb + */ +static void mpi_montsqr( size_t n, const t_uint *np, t_uint mm, t_uint *d ) +{ +#if defined(POLARSSL_HAVE_ASM) && defined(__arm__) + size_t i; + register t_uint c = 0; + + for (i = 0; i < n; i++) + { + t_uint *wij = &d[i*2]; + t_uint *xj = &d[i+n]; + t_uint x_i; + + x_i = *xj; + *xj++ = c; + +#if defined(__ARM_FEATURE_DSP) + asm (/* (C,R4,R5) := w_i_i + x_i*x_i; w_i_i := R5; */ + "mov %[c], #0\n\t" + "ldr r5, [%[wij]]\n\t" /* R5 := w_i_i; */ + "mov r4, %[c]\n\t" + "umlal r5, r4, %[x_i], %[x_i]\n\t" + "str r5, [%[wij]], #4\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bhi 0f\n\t" + "mov r9, %[c]\n\t" /* R9 := 0, the constant ZERO from here. */ + "beq 1f\n" + "2:\n\t" + "ldmia %[xj]!, { r7, r8 }\n\t" + "ldmia %[wij], { r5, r6 }\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "umaal r5, r4, %[x_i], r7\n\t" + "umlal r5, %[c], %[x_i], r7\n\t" + "umaal r4, %[c], r9, r9\n\t" + /* (C,R4,R6) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "umaal r6, r4, %[x_i], r8\n\t" + "umlal r6, %[c], %[x_i], r8\n\t" + "umaal r4, %[c], r9, r9\n\t" + /**/ + "stmia %[wij]!, { r5, r6 }\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bcc 2b\n\t" + "bne 0f\n" + "1:\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "ldr r5, [%[wij]]\n\t" + "ldr r6, [%[xj]], #4\n\t" + "umaal r5, r4, %[x_i], r6\n\t" + "umlal r5, %[c], %[x_i], r6\n\t" + "umaal r4, %[c], r9, r9\n\t" + "str r5, [%[wij]], #4\n" + "0:\n\t" + "ldr r5, [%[wij]]\n\t" + "adds r4, r4, r5\n\t" + "adc %[c], %[c], #0\n\t" + "str r4, [%[wij]]" + : [c] "=&r" (c), [wij] "=r" (wij), [xj] "=r" (xj) + : [x_i] "r" (x_i), [x_max1] "r" (&d[n*2-1]), + "[wij]" (wij), "[xj]" (xj) + : "r4", "r5", "r6", "r7", "r8", "r9", "memory", "cc"); +#else + asm (/* (C,R4,R5) := w_i_i + x_i*x_i; w_i_i := R5; */ + "mov %[c], #0\n\t" + "ldr r5, [%[wij]]\n\t" /* R5 := w_i_i; */ + "mov r4, %[c]\n\t" + "umlal r5, r4, %[x_i], %[x_i]\n\t" + "str r5, [%[wij]], #4\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bhi 0f\n\t" + "mov r9, %[c]\n\t" /* R9 := 0, the constant ZERO from here. */ + "beq 1f\n" + "2:\n\t" + "ldmia %[xj]!, { r7, r8 }\n\t" + "ldmia %[wij], { r5, r6 }\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "umull r7, r12, %[x_i], r7\n\t" + "adds r5, r5, r4\n\t" + "adc r4, %[c], r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + /* (C,R4,R6) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "adds r6, r6, r4\n\t" + "adc r4, %[c], r9\n\t" + "umull r7, r12, %[x_i], r8\n\t" + "adds r6, r6, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r6, r6, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + /**/ + "stmia %[wij]!, { r5, r6 }\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bcc 2b\n\t" + "bne 0f\n" + "1:\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "ldr r5, [%[wij]]\n\t" + "ldr r6, [%[xj]], #4\n\t" + "adds r5, r5, r4\n\t" + "adc r4, %[c], r9\n\t" + "umull r7, r12, %[x_i], r6\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + "str r5, [%[wij]], #4\n" + "0:\n\t" + "ldr r5, [%[wij]]\n\t" + "adds r4, r4, r5\n\t" + "adc %[c], %[c], #0\n\t" + "str r4, [%[wij]]" + : [c] "=&r" (c), [wij] "=r" (wij), [xj] "=r" (xj) + : [x_i] "r" (x_i), [x_max1] "r" (&d[n*2-1]), + "[wij]" (wij), "[xj]" (xj) + : "r4", "r5", "r6", "r7", "r8", "r9", "r12", "memory", "cc"); +#endif + + c += mpi_mul_hlp( n, np, &d[i], d[i] * mm ); + } + + d += n; + + /* prevent timing attacks */ + if( ((mpi_cmp_abs_limbs ( n, d, np ) >= 0) | c) ) + mpi_sub_hlp( n, np, d ); + else + mpi_sub_hlp( n, d - n, d - n); +#else + t_uint a_input[n]; + + memcpy (a_input, &d[n], sizeof (a_input)); + mpi_montmul (n, np, mm, d, a_input); +#endif +} + +/* + * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + */ +#if MEMORY_SIZE >= 32 +#define MAX_WSIZE 6 +#elif MEMORY_SIZE >= 24 +#define MAX_WSIZE 5 +#else +#define MAX_WSIZE 4 +#endif +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ) +{ + int ret; + size_t i = mpi_msb( E ); + size_t wsize = ( i > 1024 ) ? MAX_WSIZE : + ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : + ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + size_t wbits, one = 1; + size_t nblimbs; + size_t bufsize, nbits; + t_uint ei, mm, state; + mpi RR; + t_uint d[N->n*2]; + t_uint w1[N->n]; + t_uint wn[(one << (wsize - 1))][N->n]; + + if( mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + if( mpi_cmp_int( E, 0 ) < 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + if( A->s == -1 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + /* + * Init temps and window size + */ + mpi_montg_init( &mm, N ); + + /* + * If 1st call, pre-compute R^2 mod N + */ + if( _RR == NULL || _RR->p == NULL ) + { + mpi T; + + mpi_init( &RR ); + T.s = 1; T.n = N->n * 2; T.p = d; + memset (d, 0, 2 * N->n * ciL); /* Set D zero. */ + mpi_sub_hlp( N->n, N->p, d + N->n); + MPI_CHK( mpi_mod_mpi( &RR, &T, N ) ); + MPI_CHK( mpi_grow( &RR, N->n ) ); + + if( _RR != NULL ) + memcpy( _RR, &RR, sizeof( mpi ) ); + + /* The condition of "the lower half of D is all zero" is kept. */ + } + else { + memcpy( &RR, _RR, sizeof( mpi ) ); + memset (d, 0, N->n * ciL); /* Set lower half of D zero. */ + } + + MPI_CHK( mpi_grow( X, N->n ) ); + + /* + * W[1] = A * R^2 * R^-1 mod N = A * R mod N + */ + if( mpi_cmp_mpi( A, N ) >= 0 ) { + mpi W1; + W1.s = 1; W1.n = N->n; W1.p = d + N->n; + mpi_mod_mpi( &W1, A, N ); + } else { + memset (d + N->n, 0, N->n * ciL); + memcpy (d + N->n, A->p, A->n * ciL); + } + + mpi_montmul( N->n, N->p, mm, d, RR.p ); + memcpy (w1, d + N->n, N->n * ciL); + + { + /* + * W[1 << (wsize - 1)] = W[1] ^ ( 2 ^ (wsize - 1) ) + */ + for( i = 0; i < wsize - 1; i++ ) + mpi_montsqr( N->n, N->p, mm, d ); + memcpy (wn[0], d + N->n, N->n * ciL); + + /* + * W[i] = W[i - 1] * W[1] + */ + for( i = 1; i < (one << (wsize - 1)); i++ ) + { + mpi_montmul( N->n, N->p, mm, d, w1 ); + memcpy (wn[i], d + N->n, N->n * ciL); + } + } + + /* + * X = R^2 * R^-1 mod N = R mod N + */ + memcpy (d + N->n, RR.p, N->n * ciL); + mpi_montred( N->n, N->p, mm, d ); + + nblimbs = E->n; + bufsize = 0; + nbits = 0; + wbits = 0; + state = 0; + + while( 1 ) + { + if( bufsize == 0 ) + { + if( nblimbs-- == 0 ) + break; + + bufsize = sizeof( t_uint ) << 3; + } + + bufsize--; + + ei = (E->p[nblimbs] >> bufsize) & 1; + + /* + * skip leading 0s + */ + if( ei == 0 && state == 0 ) + continue; + + if( ei == 0 && state == 1 ) + { + /* + * out of window, square X + */ + mpi_montsqr( N->n, N->p, mm, d ); + continue; + } + + /* + * add ei to current window + */ + state = 2; + + nbits++; + wbits |= (ei << (wsize - nbits)); + + if( nbits == wsize ) + { + /* + * X = X^wsize R^-1 mod N + */ + for( i = 0; i < wsize; i++ ) + mpi_montsqr( N->n, N->p, mm, d ); + + /* + * X = X * W[wbits] R^-1 mod N + */ + mpi_montmul( N->n, N->p, mm, d, wn[wbits - (one << (wsize - 1))]); + + state--; + nbits = 0; + wbits = 0; + } + } + + /* + * process the remaining bits + */ + for( i = 0; i < nbits; i++ ) + { + mpi_montsqr( N->n, N->p, mm, d ); + + wbits <<= 1; + + if( (wbits & (one << wsize)) != 0 ) + mpi_montmul( N->n, N->p, mm, d, w1); + } + + /* + * X = A^E * R * R^-1 mod N = A^E mod N + */ + mpi_montred( N->n, N->p, mm, d ); + memcpy (X->p, d + N->n, N->n * ciL); + +cleanup: + + if( _RR == NULL ) + mpi_free( &RR ); + + return( ret ); +} + +/* + * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ) +{ + int ret; + size_t lz, lzt; + mpi TG, TA, TB; + + mpi_init( &TG ); mpi_init( &TA ); mpi_init( &TB ); + + MPI_CHK( mpi_copy( &TA, A ) ); + MPI_CHK( mpi_copy( &TB, B ) ); + + lz = mpi_lsb( &TA ); + lzt = mpi_lsb( &TB ); + + if ( lzt < lz ) + lz = lzt; + + MPI_CHK( mpi_shift_r( &TA, lz ) ); + MPI_CHK( mpi_shift_r( &TB, lz ) ); + + TA.s = TB.s = 1; + + while( mpi_cmp_int( &TA, 0 ) != 0 ) + { + MPI_CHK( mpi_shift_r( &TA, mpi_lsb( &TA ) ) ); + MPI_CHK( mpi_shift_r( &TB, mpi_lsb( &TB ) ) ); + + if( mpi_cmp_mpi( &TA, &TB ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( &TA, &TA, &TB ) ); + MPI_CHK( mpi_shift_r( &TA, 1 ) ); + } + else + { + MPI_CHK( mpi_sub_abs( &TB, &TB, &TA ) ); + MPI_CHK( mpi_shift_r( &TB, 1 ) ); + } + } + + MPI_CHK( mpi_shift_l( &TB, lz ) ); + MPI_CHK( mpi_copy( G, &TB ) ); + +cleanup: + + mpi_free( &TG ); mpi_free( &TA ); mpi_free( &TB ); + + return( ret ); +} + +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( size ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + MPI_CHK( f_rng( p_rng, (unsigned char *) X->p, size ) ); + +cleanup: + return( ret ); +} + +/* + * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ) +{ + int ret; + mpi G, TA, TU, U1, U2, TB, TV, V1, V2; + + if( mpi_cmp_int( N, 0 ) <= 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &TA ); mpi_init( &TU ); mpi_init( &U1 ); mpi_init( &U2 ); + mpi_init( &G ); mpi_init( &TB ); mpi_init( &TV ); + mpi_init( &V1 ); mpi_init( &V2 ); + + MPI_CHK( mpi_gcd( &G, A, N ) ); + + if( mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + MPI_CHK( mpi_mod_mpi( &TA, A, N ) ); + MPI_CHK( mpi_copy( &TU, &TA ) ); + MPI_CHK( mpi_copy( &TB, N ) ); + MPI_CHK( mpi_copy( &TV, N ) ); + + MPI_CHK( mpi_lset( &U1, 1 ) ); + MPI_CHK( mpi_lset( &U2, 0 ) ); + MPI_CHK( mpi_lset( &V1, 0 ) ); + MPI_CHK( mpi_lset( &V2, 1 ) ); + + do + { + while( ( TU.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TU, 1 ) ); + + if( ( U1.p[0] & 1 ) != 0 || ( U2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &U1, &U1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &U1, 1 ) ); + MPI_CHK( mpi_shift_r( &U2, 1 ) ); + } + + while( ( TV.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TV, 1 ) ); + + if( ( V1.p[0] & 1 ) != 0 || ( V2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &V1, &V1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &V1, 1 ) ); + MPI_CHK( mpi_shift_r( &V2, 1 ) ); + } + + if( mpi_cmp_mpi( &TU, &TV ) >= 0 ) + { + MPI_CHK( mpi_sub_mpi( &TU, &TU, &TV ) ); + MPI_CHK( mpi_sub_mpi( &U1, &U1, &V1 ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &V2 ) ); + } + else + { + MPI_CHK( mpi_sub_mpi( &TV, &TV, &TU ) ); + MPI_CHK( mpi_sub_mpi( &V1, &V1, &U1 ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &U2 ) ); + } + } + while( mpi_cmp_int( &TU, 0 ) != 0 ); + + while( mpi_cmp_int( &V1, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( &V1, &V1, N ) ); + + while( mpi_cmp_mpi( &V1, N ) >= 0 ) + MPI_CHK( mpi_sub_mpi( &V1, &V1, N ) ); + + MPI_CHK( mpi_copy( X, &V1 ) ); + +cleanup: + + mpi_free( &TA ); mpi_free( &TU ); mpi_free( &U1 ); mpi_free( &U2 ); + mpi_free( &G ); mpi_free( &TB ); mpi_free( &TV ); + mpi_free( &V1 ); mpi_free( &V2 ); + + return( ret ); +} + +#if defined(POLARSSL_GENPRIME) + +static const int small_prime[] = +{ +#if 0 + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, +#else + 97, +#endif + 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, + 787, +#if 0 + 797, +#endif + 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, + 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997, +#if 1 + 1009, + 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, + 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, + 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, + 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, + 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, + 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, + 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, + 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, + 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, + 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, + 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, + 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, + 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, + 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, + 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, + 1889, +#endif + + -103 +}; + +/* + * From Public domain code of JKISS RNG. + * + * Reference: David Jones, UCL Bioinformatics Group + * Good Practice in (Pseudo) Random Number Generation for + * Bioinformatics Applications + * + */ +struct jkiss_state { uint32_t x, y, z, c; }; +static struct jkiss_state jkiss_state_v; + +int prng_seed (int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret; + + struct jkiss_state *s = &jkiss_state_v; + + MPI_CHK ( f_rng (p_rng, (unsigned char *)s, sizeof (struct jkiss_state)) ); + while (s->y == 0) + MPI_CHK ( f_rng (p_rng, (unsigned char *)&s->y, sizeof (uint32_t)) ); + s->z |= 1; /* avoiding z=c=0 */ + +cleanup: + return ret; +} + +static uint32_t +jkiss (struct jkiss_state *s) +{ + uint64_t t; + + s->x = 314527869 * s->x + 1234567; + s->y ^= s->y << 5; + s->y ^= s->y >> 7; + s->y ^= s->y << 22; + t = 4294584393ULL * s->z + s->c; + s->c = (uint32_t)(t >> 32); + s->z = (uint32_t)t; + + return s->x + s->y + s->z; +} + +static int mpi_fill_pseudo_random ( mpi *X, size_t size) +{ + int ret; + uint32_t *p, *p_end; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( size ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + /* Assume little endian. */ + p = (uint32_t *)X->p; + p_end = (uint32_t *)(X->p + (size/sizeof (uint32_t))); + while (p < p_end) + *p++ = jkiss (&jkiss_state_v); + + if ((size%sizeof (uint32_t))) + *p = jkiss (&jkiss_state_v) & ((1 << (8*(size % sizeof (uint32_t)))) - 1); + +cleanup: + return ret; +} + +/* + * Miller-Rabin primality test (HAC 4.24) + */ +static +int mpi_is_prime( mpi *X) +{ + int ret, xs; + size_t i, j, n, s; + mpi W, R, T, A, RR; + + if( mpi_cmp_int( X, 0 ) == 0 || + mpi_cmp_int( X, 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + if( mpi_cmp_int( X, 2 ) == 0 ) + return( 0 ); + + mpi_init( &W ); mpi_init( &R ); mpi_init( &T ); mpi_init( &A ); + mpi_init( &RR ); + + xs = X->s; X->s = 1; + ret = 0; + +#if 0 + /* + * test trivial factors first + */ + if( ( X->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); +#endif + + for( i = 0; small_prime[i] > 0; i++ ) + { + t_uint r; + + if( mpi_cmp_int( X, small_prime[i] ) <= 0 ) + return( 0 ); + + MPI_CHK( mpi_mod_int( &r, X, small_prime[i] ) ); + + if( r == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + } + + /* + * W = |X| - 1 + * R = W >> lsb( W ) + */ + MPI_CHK( mpi_sub_int( &W, X, 1 ) ); + s = mpi_lsb( &W ); + MPI_CHK( mpi_copy( &R, &W ) ); + MPI_CHK( mpi_shift_r( &R, s ) ); + i = mpi_msb( X ); + + /* Fermat primality test with 2. */ + mpi_lset (&T, 2); + MPI_CHK( mpi_exp_mod( &T, &T, &W, X, &RR ) ); + if ( mpi_cmp_int (&T, 1) != 0) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + + /* + * HAC, table 4.4 + */ + n = ( ( i >= 1300 ) ? 2 : ( i >= 850 ) ? 3 : + ( i >= 650 ) ? 4 : ( i >= 350 ) ? 8 : + ( i >= 250 ) ? 12 : ( i >= 150 ) ? 18 : 27 ); + + for( i = 0; i < n; i++ ) + { + /* + * pick a random A, 1 < A < |X| - 1 + */ + MPI_CHK( mpi_fill_pseudo_random( &A, X->n * ciL ) ); + + if( mpi_cmp_mpi( &A, &W ) >= 0 ) + { + j = mpi_msb( &A ) - mpi_msb( &W ); + MPI_CHK( mpi_shift_r( &A, j + 1 ) ); + } + A.p[0] |= 3; + + /* + * A = A^R mod |X| + */ + MPI_CHK( mpi_exp_mod( &A, &A, &R, X, &RR ) ); + + if( mpi_cmp_mpi( &A, &W ) == 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + continue; + + j = 1; + while( j < s && mpi_cmp_mpi( &A, &W ) != 0 ) + { + /* + * A = A * A mod |X| + */ + MPI_CHK( mpi_mul_mpi( &T, &A, &A ) ); + MPI_CHK( mpi_mod_mpi( &A, &T, X ) ); + + if( mpi_cmp_int( &A, 1 ) == 0 ) + break; + + j++; + } + + /* + * not prime if A != |X| - 1 or A == 1 + */ + if( mpi_cmp_mpi( &A, &W ) != 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + break; + } + } + +cleanup: + + X->s = xs; + + mpi_free( &W ); mpi_free( &R ); mpi_free( &T ); mpi_free( &A ); + mpi_free( &RR ); + + return( ret ); +} + + +/* + * Value M: multiply all primes up to 701 (except 97) and 797 + * (so that MAX_A will be convenient value) + */ +#ifdef __LP64__ +#define M_LIMBS 16 +#else +#define M_LIMBS 31 +#endif +#define M_SIZE 122 + +static const t_uint limbs_M[] = { /* Little endian */ +#ifdef __LP64__ + 0x9344A6AB84EEB59EUL, 0xEC855CDAFF21529FUL, + 0x477E991E009BAB38UL, 0x2EEA23579F5B86F3UL, + 0xAC17D30441D6502FUL, 0x38FF52B90A468A6DUL, + 0x63630419FD42E5EFUL, 0x48CE17D091DB2572UL, + 0x708AB00AE3B57D0EUL, 0xF8A9DE08CD723598UL, + 0x731411374432C93BUL, 0x554DF2612779FAB3UL, + 0xDEEBDA58953D2BA5UL, 0xD1D66F2F5F57D007UL, + 0xB85C9607E84E9F2BUL, 0x000000000000401DUL +#else + 0x84EEB59E, 0x9344A6AB, 0xFF21529F, 0xEC855CDA, + 0x009BAB38, 0x477E991E, 0x9F5B86F3, 0x2EEA2357, + 0x41D6502F, 0xAC17D304, 0x0A468A6D, 0x38FF52B9, + 0xFD42E5EF, 0x63630419, 0x91DB2572, 0x48CE17D0, + 0xE3B57D0E, 0x708AB00A, 0xCD723598, 0xF8A9DE08, + 0x4432C93B, 0x73141137, 0x2779FAB3, 0x554DF261, + 0x953D2BA5, 0xDEEBDA58, 0x5F57D007, 0xD1D66F2F, + 0xE84E9F2B, 0xB85C9607, 0x0000401D +#endif +}; + +static const mpi M[1] = {{ 1, M_LIMBS, (t_uint *)limbs_M }}; + +/* + * MAX_A : 2^1024 / M - 1 + */ +#ifdef __LP64__ +#define MAX_A_LIMBS 1 +#else +#define MAX_A_LIMBS 2 +#endif +#define MAX_A_FILL_SIZE 6 +static const t_uint limbs_MAX_A[] = { /* Little endian */ +#ifdef __LP64__ + 0x0003FE2556A2B35FUL +#else + 0x56A2B35F, 0x0003FE25 +#endif +}; + +static const mpi MAX_A[1] = {{ 1, MAX_A_LIMBS, (t_uint *)limbs_MAX_A }}; + +/* + * Prime number generation + * + * Special version for 1024-bit only. Ignores DH_FLAG. + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + mpi B[1], G[1]; + + (void)dh_flag; + if (nbits != 1024) + return POLARSSL_ERR_MPI_BAD_INPUT_DATA; + + mpi_init ( B ); mpi_init ( G ); + + /* + * Get random value 1 to M-1 avoiding bias, and proceed when it is + * coprime to all small primes. + */ + do + { + MPI_CHK ( mpi_fill_random ( B, M_SIZE, f_rng, p_rng ) ); + B->p[0] |= 0x1; + B->p[M_LIMBS - 1] &= 0x00007FFF; + if (mpi_cmp_abs (B, M) >= 0) + continue; + + MPI_CHK ( mpi_gcd ( G, B, M ) ); + } + while (mpi_cmp_int ( G, 1 ) != 0); + + /* + * Get random value avoiding bias, comput P with the value, + * check if it's big enough, lastly, check if it's prime. + */ + while (1) + { + MPI_CHK( mpi_fill_random( X, MAX_A_FILL_SIZE, f_rng, p_rng ) ); + MPI_CHK ( mpi_sub_abs (X, MAX_A, X) ); + + MPI_CHK ( mpi_mul_mpi ( X, X, M ) ); + MPI_CHK ( mpi_add_abs ( X, X, B ) ); + if (X->n <= M_LIMBS || (X->p[M_LIMBS-1] & 0xc0000000) == 0) + continue; + ret = mpi_is_prime ( X ); + if (ret == 0 || ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE) + break; + } + +cleanup: + + mpi_free ( B ); mpi_free ( G ); + + return ret; +} + +#endif + +#if defined(POLARSSL_SELF_TEST) + +#define GCD_PAIR_COUNT 3 + +static const int gcd_pairs[GCD_PAIR_COUNT][3] = +{ + { 693, 609, 21 }, + { 1764, 868, 28 }, + { 768454923, 542167814, 1 } +}; + +/* + * Checkup routine + */ +int mpi_self_test( int verbose ) +{ + int ret, i; + mpi A, E, N, X, Y, U, V; + + mpi_init( &A ); mpi_init( &E ); mpi_init( &N ); mpi_init( &X ); + mpi_init( &Y ); mpi_init( &U ); mpi_init( &V ); + + MPI_CHK( mpi_read_string( &A, 16, + "EFE021C2645FD1DC586E69184AF4A31E" \ + "D5F53E93B5F123FA41680867BA110131" \ + "944FE7952E2517337780CB0DB80E61AA" \ + "E7C8DDC6C5C6AADEB34EB38A2F40D5E6" ) ); + + MPI_CHK( mpi_read_string( &E, 16, + "B2E7EFD37075B9F03FF989C7C5051C20" \ + "34D2A323810251127E7BF8625A4F49A5" \ + "F3E27F4DA8BD59C47D6DAABA4C8127BD" \ + "5B5C25763222FEFCCFC38B832366C29E" ) ); + + MPI_CHK( mpi_read_string( &N, 16, + "0066A198186C18C10B2F5ED9B522752A" \ + "9830B69916E535C8F047518A889A43A5" \ + "94B6BED27A168D31D4A52F88925AA8F5" ) ); + + MPI_CHK( mpi_mul_mpi( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "602AB7ECA597A3D6B56FF9829A5E8B85" \ + "9E857EA95A03512E2BAE7391688D264A" \ + "A5663B0341DB9CCFD2C4C5F421FEC814" \ + "8001B72E848A38CAE1C65F78E56ABDEF" \ + "E12D3C039B8A02D6BE593F0BBBDA56F1" \ + "ECF677152EF804370C1A305CAF3B5BF1" \ + "30879B56C61DE584A0F53A2447A51E" ) ); + + if( verbose != 0 ) + printf( " MPI test #1 (mul_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_div_mpi( &X, &Y, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "256567336059E52CAE22925474705F39A94" ) ); + + MPI_CHK( mpi_read_string( &V, 16, + "6613F26162223DF488E9CD48CC132C7A" \ + "0AC93C701B001B092E4E5B9F73BCD27B" \ + "9EE50D0657C77F374E903CDFA4C642" ) ); + + if( verbose != 0 ) + printf( " MPI test #2 (div_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 || + mpi_cmp_mpi( &Y, &V ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + + MPI_CHK( mpi_exp_mod( &X, &A, &E, &N, NULL ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "36E139AEA55215609D2816998ED020BB" \ + "BD96C37890F65171D948E9BC7CBAA4D9" \ + "325D24D6A3C12710F10A09FA08AB87" ) ); + + if( verbose != 0 ) + printf( " MPI test #3 (exp_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); + +#if defined(POLARSSL_GENPRIME) + MPI_CHK( mpi_inv_mod( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \ + "C3DBA76456363A10869622EAC2DD84EC" \ + "C5B8A74DAC4D09E03B5E0BE779F2DF61" ) ); + + if( verbose != 0 ) + printf( " MPI test #4 (inv_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n" ); +#endif + + if( verbose != 0 ) + printf( " MPI test #5 (simple gcd): " ); + + for ( i = 0; i < GCD_PAIR_COUNT; i++) + { + MPI_CHK( mpi_lset( &X, gcd_pairs[i][0] ) ); + MPI_CHK( mpi_lset( &Y, gcd_pairs[i][1] ) ); + + MPI_CHK( mpi_gcd( &A, &X, &Y ) ); + + if( mpi_cmp_int( &A, gcd_pairs[i][2] ) != 0 ) + { + if( verbose != 0 ) + printf( "failed at %d\n", i ); + + return( 1 ); + } + } + + if( verbose != 0 ) + printf( "passed\n" ); + +cleanup: + + if( ret != 0 && verbose != 0 ) + printf( "Unexpected error, return code = %08X\n", ret ); + + mpi_free( &A ); mpi_free( &E ); mpi_free( &N ); mpi_free( &X ); + mpi_free( &Y ); mpi_free( &U ); mpi_free( &V ); + + if( verbose != 0 ) + printf( "\n" ); + + return( ret ); +} + +#endif + +#endif diff --git a/bn.c b/bn.c new file mode 100644 index 0000000..cc23f03 --- /dev/null +++ b/bn.c @@ -0,0 +1,427 @@ +/* + * bn.c -- 256-bit (and 512-bit) bignum calculation + * + * Copyright (C) 2011, 2013, 2014, 2019 + * Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 +#ifndef BN256_NO_RANDOM +#include "random.h" +#endif +#include "bn.h" + +uint32_t +bn256_add (bn256 *X, const bn256 *A, const bn256 *B) +{ + int i; + uint32_t v; + uint32_t carry = 0; + uint32_t *px; + const uint32_t *pa, *pb; + + px = X->word; + pa = A->word; + pb = B->word; + + for (i = 0; i < BN256_WORDS; i++) + { + v = *pb; + *px = *pa + carry; + carry = (*px < carry); + *px += v; + carry += (*px < v); + px++; + pa++; + pb++; + } + + return carry; +} + +uint32_t +bn256_sub (bn256 *X, const bn256 *A, const bn256 *B) +{ + int i; + uint32_t v; + uint32_t borrow = 0; + uint32_t *px; + const uint32_t *pa, *pb; + + px = X->word; + pa = A->word; + pb = B->word; + + for (i = 0; i < BN256_WORDS; i++) + { + uint32_t borrow0 = (*pa < borrow); + + v = *pb; + *px = *pa - borrow; + borrow = (*px < v) + borrow0; + *px -= v; + px++; + pa++; + pb++; + } + + return borrow; +} + +uint32_t +bn256_add_uint (bn256 *X, const bn256 *A, uint32_t w) +{ + int i; + uint32_t carry = w; + uint32_t *px; + const uint32_t *pa; + + px = X->word; + pa = A->word; + + for (i = 0; i < BN256_WORDS; i++) + { + *px = *pa + carry; + carry = (*px < carry); + px++; + pa++; + } + + return carry; +} + +uint32_t +bn256_sub_uint (bn256 *X, const bn256 *A, uint32_t w) +{ + int i; + uint32_t borrow = w; + uint32_t *px; + const uint32_t *pa; + + px = X->word; + pa = A->word; + + for (i = 0; i < BN256_WORDS; i++) + { + uint32_t borrow0 = (*pa < borrow); + + *px = *pa - borrow; + borrow = borrow0; + px++; + pa++; + } + + return borrow; +} + +#ifndef BN256_C_IMPLEMENTATION +#define ASM_IMPLEMENTATION 0 +#endif +void +bn256_mul (bn512 *X, const bn256 *A, const bn256 *B) +{ +#if ASM_IMPLEMENTATION +#include "muladd_256.h" + const uint32_t *s; + uint32_t *d; + uint32_t w; + uint32_t c; + + memset (X->word, 0, sizeof (uint32_t)*BN256_WORDS*2); + + s = A->word; d = &X->word[0]; w = B->word[0]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[1]; w = B->word[1]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[2]; w = B->word[2]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[3]; w = B->word[3]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[4]; w = B->word[4]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[5]; w = B->word[5]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[6]; w = B->word[6]; MULADD_256 (s, d, w, c); + s = A->word; d = &X->word[7]; w = B->word[7]; MULADD_256 (s, d, w, c); +#else + int i, j, k; + int i_beg, i_end; + uint32_t r0, r1, r2; + + r0 = r1 = r2 = 0; + for (k = 0; k <= (BN256_WORDS - 1)*2; k++) + { + if (k < BN256_WORDS) + { + i_beg = 0; + i_end = k; + } + else + { + i_beg = k - BN256_WORDS + 1; + i_end = BN256_WORDS - 1; + } + + for (i = i_beg; i <= i_end; i++) + { + uint64_t uv; + uint32_t u, v; + uint32_t carry; + + j = k - i; + + uv = ((uint64_t )A->word[i])*((uint64_t )B->word[j]); + v = uv; + u = (uv >> 32); + r0 += v; + carry = (r0 < v); + r1 += carry; + carry = (r1 < carry); + r1 += u; + carry += (r1 < u); + r2 += carry; + } + + X->word[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + X->word[k] = r0; +#endif +} + +void +bn256_sqr (bn512 *X, const bn256 *A) +{ +#if ASM_IMPLEMENTATION + int i; + + memset (X->word, 0, sizeof (bn512)); + for (i = 0; i < BN256_WORDS; i++) + { + uint32_t *wij = &X->word[i*2]; + const uint32_t *xj = &A->word[i]; + uint32_t x_i = *xj++; + uint32_t c; + + asm (/* (C,R4,R5) := w_i_i + x_i*x_i; w_i_i := R5; */ + "mov %[c], #0\n\t" + "ldr r5, [%[wij]]\n\t" /* R5 := w_i_i; */ + "mov r4, %[c]\n\t" + "umlal r5, r4, %[x_i], %[x_i]\n\t" + "str r5, [%[wij]], #4\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bhi 0f\n\t" + "mov r9, %[c]\n\t" /* R9 := 0, the constant ZERO from here. */ + "beq 1f\n" + "2:\n\t" + "ldmia %[xj]!, { r7, r8 }\n\t" + "ldmia %[wij], { r5, r6 }\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "umull r7, r12, %[x_i], r7\n\t" + "adds r5, r5, r4\n\t" + "adc r4, %[c], r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + /* (C,R4,R6) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "adds r6, r6, r4\n\t" + "adc r4, %[c], r9\n\t" + "umull r7, r12, %[x_i], r8\n\t" + "adds r6, r6, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r6, r6, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + /**/ + "stmia %[wij]!, { r5, r6 }\n\t" + "cmp %[xj], %[x_max1]\n\t" + "bcc 2b\n\t" + "bne 0f\n" + "1:\n\t" + /* (C,R4,R5) := (C,R4) + w_i_j + 2*x_i*x_j; */ + "ldr r5, [%[wij]]\n\t" + "ldr r6, [%[xj]], #4\n\t" + "adds r5, r5, r4\n\t" + "adc r4, %[c], r9\n\t" + "umull r7, r12, %[x_i], r6\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], r9, r9\n\t" + "adds r5, r5, r7\n\t" + "adcs r4, r4, r12\n\t" + "adc %[c], %[c], r9\n\t" + "str r5, [%[wij]], #4\n" + "0:\n\t" + "ldr r5, [%[wij]]\n\t" + "adds r4, r4, r5\n\t" + "adc %[c], %[c], #0\n\t" + "str r4, [%[wij]], #4" + : [c] "=&r" (c), [wij] "=r" (wij), [xj] "=r" (xj) + : [x_i] "r" (x_i), [x_max1] "r" (&A->word[BN256_WORDS-1]), + "[wij]" (wij), "[xj]" (xj) + : "r4", "r5", "r6", "r7", "r8", "r9", "r12", "memory", "cc"); + + if (i < BN256_WORDS - 1) + *wij = c; + } +#else + int i, j, k; + int i_beg, i_end; + uint32_t r0, r1, r2; + + r0 = r1 = r2 = 0; + for (k = 0; k <= (BN256_WORDS - 1)*2; k++) + { + if (k < BN256_WORDS) + { + i_beg = 0; + i_end = k/2; + } + else + { + i_beg = k - BN256_WORDS + 1; + i_end = k/2; + } + + for (i = i_beg; i <= i_end; i++) + { + uint64_t uv; + uint32_t u, v; + uint32_t carry; + + j = k - i; + + uv = ((uint64_t )A->word[i])*((uint64_t )A->word[j]); + if (i < j) + { + r2 += ((uv >> 63) != 0); + uv <<= 1; + } + v = uv; + u = (uv >> 32); + r0 += v; + carry = (r0 < v); + r1 += carry; + carry = (r1 < carry); + r1 += u; + carry += (r1 < u); + r2 += carry; + } + + X->word[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + X->word[k] = r0; +#endif +} + +uint32_t +bn256_shift (bn256 *X, const bn256 *A, int shift) +{ + int i; + uint32_t carry = 0, next_carry; + + if (shift > 0) + { + for (i = 0; i < BN256_WORDS; i++) + { + next_carry = A->word[i] >> (32 - shift); + X->word[i] = (A->word[i] << shift) | carry; + carry = next_carry; + } + } + else + { + shift = -shift; + + for (i = BN256_WORDS - 1; i >= 0; i--) + { + next_carry = A->word[i] & ((1 << shift) - 1); + X->word[i] = (A->word[i] >> shift) | (carry << (32 - shift)); + carry = next_carry; + } + } + + return carry; +} + +int +bn256_is_zero (const bn256 *X) +{ + int i; + int r = 1; + + for (i = 0; i < BN256_WORDS; i++) + r &= (X->word[i] == 0); + + return r; +} + +int +bn256_is_even (const bn256 *X) +{ + return !(X->word[0] & 1); +} + +int +bn256_is_ge (const bn256 *A, const bn256 *B) +{ + uint32_t borrow; + bn256 tmp[1]; + + borrow = bn256_sub (tmp, A, B); + return borrow == 0; +} + + +int +bn256_cmp (const bn256 *A, const bn256 *B) +{ + uint32_t borrow; + int is_zero; + bn256 tmp[1]; + + borrow = bn256_sub (tmp, A, B); + is_zero = bn256_is_zero (tmp); + return is_zero ? 0 : (borrow ? -1 : 1); +} + + +#ifndef BN256_NO_RANDOM +void +bn256_random (bn256 *X) +{ + int i, j; + const uint8_t *rand; + + for (i = 0; i < 256/256; i++) + { + rand = random_bytes_get (); + for (j = 0; j < BN256_WORDS; j++) + X->word[i*BN256_WORDS+j] = ((uint32_t *)rand)[j]; + random_bytes_free (rand); + } +} +#endif diff --git a/bn.h b/bn.h new file mode 100644 index 0000000..d22eea0 --- /dev/null +++ b/bn.h @@ -0,0 +1,23 @@ +#define BN256_WORDS 8 +typedef struct bn256 { + uint32_t word[ BN256_WORDS ]; /* Little endian */ +} bn256; + +#define BN512_WORDS 16 +typedef struct bn512 { + uint32_t word[ BN512_WORDS ]; /* Little endian */ +} bn512; + +uint32_t bn256_add (bn256 *X, const bn256 *A, const bn256 *B); +uint32_t bn256_sub (bn256 *X, const bn256 *A, const bn256 *B); +uint32_t bn256_add_uint (bn256 *X, const bn256 *A, uint32_t w); +uint32_t bn256_sub_uint (bn256 *X, const bn256 *A, uint32_t w); + +void bn256_mul (bn512 *X, const bn256 *A, const bn256 *B); +void bn256_sqr (bn512 *X, const bn256 *A); +uint32_t bn256_shift (bn256 *X, const bn256 *A, int shift); +int bn256_is_zero (const bn256 *X); +int bn256_is_even (const bn256 *X); +int bn256_is_ge (const bn256 *A, const bn256 *B); +int bn256_cmp (const bn256 *A, const bn256 *B); +void bn256_random (bn256 *X); diff --git a/call-ec.c b/call-ec.c new file mode 100644 index 0000000..c739173 --- /dev/null +++ b/call-ec.c @@ -0,0 +1,136 @@ +/* + * call-ec.c - interface between Gnuk and Elliptic curve over GF(prime) + * + * Copyright (C) 2013, 2014, 2017 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "field-group-select.h" + +/* We are little-endian in the computation, but the protocol is big-endian. */ + +#define ECDSA_BYTE_SIZE 32 +#define ECDH_BYTE_SIZE 32 + +int +FUNC(ecdsa_sign) (const uint8_t *hash, uint8_t *output, + const uint8_t *key_data) +{ + int i; + bn256 r[1], s[1], z[1], d[1]; + uint8_t *p; + + p = (uint8_t *)d; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + p[ECDSA_BYTE_SIZE - i - 1] = key_data[i]; + + p = (uint8_t *)z; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + p[ECDSA_BYTE_SIZE - i - 1] = hash[i]; + + FUNC(ecdsa) (r, s, z, d); + p = (uint8_t *)r; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + *output++ = p[ECDSA_BYTE_SIZE - i - 1]; + p = (uint8_t *)s; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + *output++ = p[ECDSA_BYTE_SIZE - i - 1]; + return 0; +} + +int +FUNC(ecc_compute_public) (const uint8_t *key_data, uint8_t *pubkey) +{ + uint8_t *p, *p1; + ac q[1]; + bn256 k[1]; + int i; + + p = (uint8_t *)k; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + p[ECDSA_BYTE_SIZE - i - 1] = key_data[i]; + if (FUNC(compute_kG) (q, k) < 0) + return -1; + + p = pubkey; + p1 = (uint8_t *)q->x; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + *p++ = p1[ECDSA_BYTE_SIZE - i - 1]; + p1 = (uint8_t *)q->y; + for (i = 0; i < ECDSA_BYTE_SIZE; i++) + *p++ = p1[ECDSA_BYTE_SIZE - i - 1]; + + return 0; +} + +int +FUNC(ecdh_decrypt) (const uint8_t *input, uint8_t *output, + const uint8_t *key_data) +{ + bn256 k[1]; + ac X[1], P[1]; + int i; + uint8_t *p0; + const uint8_t *p1; + int r; + + p0 = (uint8_t *)k; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + p0[ECDH_BYTE_SIZE - i - 1] = key_data[i]; + + p1 = input+1; /* skip '04' */ + p0 = (uint8_t *)P->x; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + p0[ECDH_BYTE_SIZE - i - 1] = *p1++; + p0 = (uint8_t *)P->y; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + p0[ECDH_BYTE_SIZE - i - 1] = *p1++; + + r = FUNC(compute_kP) (X, k, P); + if (r == 0) + { + p0 = output; + p1 = (const uint8_t *)X->x; + *p0++ = 4; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + *p0++ = p1[ECDH_BYTE_SIZE - i - 1]; + p1 = (const uint8_t *)X->y; + for (i = 0; i < ECDH_BYTE_SIZE; i++) + *p0++ = p1[ECDH_BYTE_SIZE - i - 1]; + } + + return r; +} + + +/** + * @brief Check if a secret d0 is valid or not + * + * @param D0 scalar D0: secret + * @param D1 scalar D1: secret candidate N-D0 + * + * Return 0 on error. + * Return -1 when D1 should be used as the secret + * Return 1 when D0 should be used as the secret + */ +int +FUNC(ecc_check_secret) (const uint8_t *d0, uint8_t *d1) +{ + return FUNC(check_secret) ((const bn256 *)d0, (bn256 *)d1); +} diff --git a/call-ec_p256k1.c b/call-ec_p256k1.c new file mode 100644 index 0000000..efc7cad --- /dev/null +++ b/call-ec_p256k1.c @@ -0,0 +1,34 @@ +/* + * call-ec_p256k1.c - interface between Gnuk and Elliptic curve over + * GF(p256k1) + * + * Copyright (C) 2014, 2017 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "bn.h" +#include "affine.h" +#include "jpc-ac_p256k1.h" +#include "ec_p256k1.h" + +#define FIELD p256k1 + +#include "call-ec.c" diff --git a/call-rsa.c b/call-rsa.c new file mode 100644 index 0000000..72f2638 --- /dev/null +++ b/call-rsa.c @@ -0,0 +1,267 @@ +/* + * call-rsa.c -- Glue code between RSA computation and OpenPGP card protocol + * + * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2017 + * Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "config.h" + +#include "gnuk.h" +#include "status-code.h" +#include "random.h" +#include "polarssl/config.h" +#include "polarssl/rsa.h" + +static rsa_context rsa_ctx; +//static struct chx_cleanup clp; + +static void +rsa_cleanup (void *arg) +{ + (void)arg; + rsa_free (&rsa_ctx); +} + + +int +rsa_sign (const uint8_t *raw_message, uint8_t *output, int msg_len, + struct key_data *kd, int pubkey_len) +{ + mpi P1, Q1, H; + int ret = 0; + unsigned char temp[pubkey_len]; + + rsa_init (&rsa_ctx, RSA_PKCS_V15, 0); + + mpi_init (&P1); mpi_init (&Q1); mpi_init (&H); + + rsa_ctx.len = pubkey_len; + MPI_CHK( mpi_lset (&rsa_ctx.E, 0x10001) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.P, &kd->data[0], pubkey_len / 2) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.Q, &kd->data[pubkey_len / 2], + pubkey_len / 2) ); +#if 0 + MPI_CHK( mpi_mul_mpi (&rsa_ctx.N, &rsa_ctx.P, &rsa_ctx.Q) ); +#endif + MPI_CHK( mpi_sub_int (&P1, &rsa_ctx.P, 1) ); + MPI_CHK( mpi_sub_int (&Q1, &rsa_ctx.Q, 1) ); + MPI_CHK( mpi_mul_mpi (&H, &P1, &Q1) ); + MPI_CHK( mpi_inv_mod (&rsa_ctx.D , &rsa_ctx.E, &H) ); + MPI_CHK( mpi_mod_mpi (&rsa_ctx.DP, &rsa_ctx.D, &P1) ); + MPI_CHK( mpi_mod_mpi (&rsa_ctx.DQ, &rsa_ctx.D, &Q1) ); + MPI_CHK( mpi_inv_mod (&rsa_ctx.QP, &rsa_ctx.Q, &rsa_ctx.P) ); + cleanup: + mpi_free (&P1); mpi_free (&Q1); mpi_free (&H); + if (ret == 0) + { + int cs; + + DEBUG_INFO ("RSA sign..."); + //clp.next = NULL; + //clp.routine = rsa_cleanup; + //clp.arg = NULL; + //chopstx_cleanup_push (&clp); + //cs = chopstx_setcancelstate (0); /* Allow cancellation. */ + ret = rsa_rsassa_pkcs1_v15_sign (&rsa_ctx, NULL, NULL, + RSA_PRIVATE, SIG_RSA_RAW, + msg_len, raw_message, temp); + memcpy (output, temp, pubkey_len); + rsa_cleanup(NULL); + //chopstx_setcancelstate (cs); + //chopstx_cleanup_pop (0); + } + + rsa_free (&rsa_ctx); + if (ret != 0) + { + DEBUG_INFO ("fail:"); + DEBUG_SHORT (ret); + return -1; + } + else + { + DEBUG_INFO ("done.\r\n"); + GPG_SUCCESS (); + return 0; + } +} + +/* + * LEN: length in byte + */ +int +modulus_calc (const uint8_t *p, int len, uint8_t *pubkey) +{ + mpi P, Q, N; + int ret; + + mpi_init (&P); mpi_init (&Q); mpi_init (&N); + MPI_CHK( mpi_read_binary (&P, p, len / 2) ); + MPI_CHK( mpi_read_binary (&Q, p + len / 2, len / 2) ); + MPI_CHK( mpi_mul_mpi (&N, &P, &Q) ); + MPI_CHK( mpi_write_binary (&N, pubkey, len) ); + cleanup: + mpi_free (&P); mpi_free (&Q); mpi_free (&N); + if (ret != 0) + return -1; + + return 0; +} + + +int +rsa_decrypt (const uint8_t *input, uint8_t *output, int msg_len, + struct key_data *kd, unsigned int *output_len_p) +{ + mpi P1, Q1, H; + int ret; + + DEBUG_INFO ("RSA decrypt:"); + DEBUG_WORD ((uint32_t)&ret); + + rsa_init (&rsa_ctx, RSA_PKCS_V15, 0); + mpi_init (&P1); mpi_init (&Q1); mpi_init (&H); + + rsa_ctx.len = msg_len; + DEBUG_WORD (msg_len); + + MPI_CHK( mpi_lset (&rsa_ctx.E, 0x10001) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.P, &kd->data[0], msg_len / 2) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.Q, &kd->data[msg_len / 2], msg_len / 2) ); +#if 0 + MPI_CHK( mpi_mul_mpi (&rsa_ctx.N, &rsa_ctx.P, &rsa_ctx.Q) ); +#endif + MPI_CHK( mpi_sub_int (&P1, &rsa_ctx.P, 1) ); + MPI_CHK( mpi_sub_int (&Q1, &rsa_ctx.Q, 1) ); + MPI_CHK( mpi_mul_mpi (&H, &P1, &Q1) ); + MPI_CHK( mpi_inv_mod (&rsa_ctx.D , &rsa_ctx.E, &H) ); + MPI_CHK( mpi_mod_mpi (&rsa_ctx.DP, &rsa_ctx.D, &P1) ); + MPI_CHK( mpi_mod_mpi (&rsa_ctx.DQ, &rsa_ctx.D, &Q1) ); + MPI_CHK( mpi_inv_mod (&rsa_ctx.QP, &rsa_ctx.Q, &rsa_ctx.P) ); + cleanup: + mpi_free (&P1); mpi_free (&Q1); mpi_free (&H); + if (ret == 0) + { + int cs; + + DEBUG_INFO ("RSA decrypt ..."); + //clp.next = NULL; + //clp.routine = rsa_cleanup; + //clp.arg = NULL; + //chopstx_cleanup_push (&clp); + //cs = chopstx_setcancelstate (0); /* Allow cancellation. */ + ret = rsa_rsaes_pkcs1_v15_decrypt (&rsa_ctx, NULL, NULL, + RSA_PRIVATE, output_len_p, input, + output, MAX_RES_APDU_DATA_SIZE); + rsa_cleanup(NULL); + //chopstx_setcancelstate (cs); + //chopstx_cleanup_pop (0); + } + + rsa_free (&rsa_ctx); + if (ret != 0) + { + DEBUG_INFO ("fail:"); + DEBUG_SHORT (ret); + return -1; + } + else + { + DEBUG_INFO ("done.\r\n"); + GPG_SUCCESS (); + return 0; + } +} + +int +rsa_verify (const uint8_t *pubkey, int pubkey_len, + const uint8_t *hash, const uint8_t *sig) +{ + int ret; + + rsa_init (&rsa_ctx, RSA_PKCS_V15, 0); + rsa_ctx.len = pubkey_len; + MPI_CHK( mpi_lset (&rsa_ctx.E, 0x10001) ); + MPI_CHK( mpi_read_binary (&rsa_ctx.N, pubkey, pubkey_len) ); + + DEBUG_INFO ("RSA verify..."); + + MPI_CHK( rsa_rsassa_pkcs1_v15_verify (&rsa_ctx, NULL, NULL, + RSA_PUBLIC, SIG_RSA_SHA256, 32, + hash, sig) ); + cleanup: + rsa_free (&rsa_ctx); + if (ret != 0) + { + DEBUG_INFO ("fail:"); + DEBUG_SHORT (ret); + return -1; + } + else + { + DEBUG_INFO ("verified.\r\n"); + return 0; + } +} + +#define RSA_EXPONENT 0x10001 + +int +rsa_genkey (int pubkey_len, uint8_t *pubkey, uint8_t *p_q) +{ + int ret; + uint8_t index = 0; + uint8_t *p = p_q; + uint8_t *q = p_q + pubkey_len / 2; + int cs; + + extern int prng_seed (int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + extern void neug_flush (void); + + neug_flush (); + prng_seed (random_gen, &index); + rsa_init (&rsa_ctx, RSA_PKCS_V15, 0); + + //clp.next = NULL; + //clp.routine = rsa_cleanup; + //clp.arg = NULL; + //chopstx_cleanup_push (&clp); + //cs = chopstx_setcancelstate (0); /* Allow cancellation. */ + MPI_CHK( rsa_gen_key (&rsa_ctx, random_gen, &index, pubkey_len * 8, + RSA_EXPONENT) ); + MPI_CHK( mpi_write_binary (&rsa_ctx.P, p, pubkey_len / 2) ); + MPI_CHK( mpi_write_binary (&rsa_ctx.Q, q, pubkey_len / 2) ); + MPI_CHK( mpi_write_binary (&rsa_ctx.N, pubkey, pubkey_len) ); + + cleanup: + //chopstx_setcancelstate (cs); + //chopstx_cleanup_pop (1); + rsa_cleanup(NULL); + if (ret != 0) + return -1; + else + return 0; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..c533464 --- /dev/null +++ b/config.h @@ -0,0 +1,17 @@ +#define DEBUG +#ifdef DEBUG +#define ENABLE_VIRTUAL_COM_PORT 1 +#endif +#undef DFU_SUPPORT +#define ORIGIN 0x08000000 +#define ORIGIN_REAL 0x08000000 +#undef PINPAD_SUPPORT + +#define CERTDO_SUPPORT 1 +#undef HID_CARD_CHANGE_SUPPORT +#define LIFE_CYCLE_MANAGEMENT_SUPPORT 1 +#undef ACKBTN_SUPPORT +#define SERIALNO_STR_LEN 12 +#undef KDF_DO_REQUIRED + +#define MHZ 133 \ No newline at end of file diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..392fc8e --- /dev/null +++ b/debug.c @@ -0,0 +1,134 @@ +/* + * debug.c -- Debuging with virtual COM port + * + * Copyright (C) 2010 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "tusb.h" + +void my_write (const char *s, int len) +{ + if (len == 0) + return; + + TU_LOG2(s); +} + + +static void +put_hex (uint8_t nibble) +{ + uint8_t c; + + if (nibble < 0x0a) + c = '0' + nibble; + else + c = 'a' + nibble - 0x0a; + + my_write ((const char *)&c, 1); +} + +void +put_byte (uint8_t b) +{ + put_hex (b >> 4); + put_hex (b &0x0f); + my_write ("\r\n", 2); +} + +void +put_byte_with_no_nl (uint8_t b) +{ + my_write (" ", 1); + put_hex (b >> 4); + put_hex (b &0x0f); +} + +void +put_short (uint16_t x) +{ + put_hex (x >> 12); + put_hex ((x >> 8)&0x0f); + put_hex ((x >> 4)&0x0f); + put_hex (x & 0x0f); + my_write ("\r\n", 2); +} + +void +put_word (uint32_t x) +{ + put_hex (x >> 28); + put_hex ((x >> 24)&0x0f); + put_hex ((x >> 20)&0x0f); + put_hex ((x >> 16)&0x0f); + put_hex ((x >> 12)&0x0f); + put_hex ((x >> 8)&0x0f); + put_hex ((x >> 4)&0x0f); + put_hex (x & 0x0f); + my_write ("\r\n", 2); +} + +void +put_int (uint32_t x) +{ + char s[10]; + int i; + + for (i = 0; i < 10; i++) + { + s[i] = '0' + (x % 10); + x /= 10; + if (x == 0) + break; + } + + while (i) + { + my_write (s+i, 1); + i--; + } + + my_write (s, 1); + my_write ("\r\n", 2); +} + +void +put_binary (const char *s, int len) +{ + int i; + + for (i = 0; i < len; i++) + { + put_byte_with_no_nl (s[i]); + if ((i & 0x0f) == 0x0f) + my_write ("\r\n", 2); + } + my_write ("\r\n", 2); +} + +void +put_string (const char *s) +{ + my_write (s, strlen (s)); +} + + diff --git a/ec_p256k1.c b/ec_p256k1.c new file mode 100644 index 0000000..c63978e --- /dev/null +++ b/ec_p256k1.c @@ -0,0 +1,233 @@ +/* -*- coding: utf-8 -*- + * ec_p256k1.c - Elliptic curve over GF(p256k1) + * + * Copyright (C) 2014 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 . + * + */ + +/* + * Note: we don't take advantage of the specific feature of this curve, + * but use same method of computation as NIST P-256 curve. That's due + * to some software patent(s). + */ + +#include +#include +#include "bn.h" +#include "modp256k1.h" +#include "affine.h" +#include "jpc-ac_p256k1.h" +#include "mod.h" +#include "ec_p256k1.h" + +#define FIELD p256k1 +#define COEFFICIENT_A_IS_ZERO 1 + +/* + * a = 0, b = 7 + */ +#if 0 +static const bn256 coefficient_a[1] = { + {{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }} +}; +#endif + +static const bn256 coefficient_b[1] = { + {{ 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }} +}; + + +static const ac precomputed_KG[15] = { + { + {{{ 0x16f81798, 0x59f2815b, 0x2dce28d9, 0x029bfcdb, + 0xce870b07, 0x55a06295, 0xf9dcbbac, 0x79be667e }}}, + {{{ 0xfb10d4b8, 0x9c47d08f, 0xa6855419, 0xfd17b448, + 0x0e1108a8, 0x5da4fbfc, 0x26a3c465, 0x483ada77 }}} + }, { + {{{ 0x42d0e6bd, 0x13b7e0e7, 0xdb0f5e53, 0xf774d163, + 0x104d6ecb, 0x82a2147c, 0x243c4e25, 0x3322d401 }}}, + {{{ 0x6c28b2a0, 0x24f3a2e9, 0xa2873af6, 0x2805f63e, + 0x4ddaf9b7, 0xbfb019bc, 0xe9664ef5, 0x56e70797 }}} + }, { + {{{ 0x829d122a, 0xdca81127, 0x67e99549, 0x8f17f314, + 0x6a8a9e73, 0x9b889085, 0x846dd99d, 0x583fdfd9 }}}, + {{{ 0x63c4eac4, 0xf3c7719e, 0xb734b37a, 0xb44685a3, + 0x572a47a6, 0x9f92d2d6, 0x2ff57d81, 0xabc6232f }}} + }, { + {{{ 0x9ec4c0da, 0x1b7b444c, 0x723ea335, 0xe88c5678, + 0x981f162e, 0x9239c1ad, 0xf63b5f33, 0x8f68b9d2 }}}, + {{{ 0x501fff82, 0xf23cbf79, 0x95510bfd, 0xbbea2cfe, + 0xb6be215d, 0xde1d90c2, 0xba063986, 0x662a9f2d }}} + }, { + {{{ 0x114cbf09, 0x63c5e885, 0x7be77e3e, 0x2f27ce93, + 0xf54a3e33, 0xdaa6d12d, 0x3eff872c, 0x8b300e51 }}}, + {{{ 0xb3b10a39, 0x26c6ff28, 0x9aaf7169, 0x08f6a7aa, + 0x6b8238ea, 0x446f0d46, 0x7f43c0cc, 0x1cec3067 }}} + }, { + {{{ 0x075e9070, 0xba16ce6a, 0x9b5cfe37, 0xbc26893d, + 0x9c510774, 0xe1ddadfe, 0xfe3ae2f4, 0x90922d88 }}}, + {{{ 0x5c08824a, 0x653943cc, 0xfce8f4bc, 0x06d74475, + 0x533c615d, 0x8d101fa7, 0x742108a9, 0x7b1903f6 }}} + }, { + {{{ 0x6ebdc96c, 0x1bcfa45c, 0x1c7584ba, 0xe400bc04, + 0x74cf531f, 0x6395e20e, 0xc5131b30, 0x1edd0bb1 }}}, + {{{ 0xe358cf9e, 0xa117161b, 0x2724d11c, 0xe490d6f0, + 0xee6dd8c9, 0xf75062f6, 0xfba373e4, 0x31e03b2b }}} + }, { + {{{ 0x2120e2b3, 0x7f3b58fa, 0x7f47f9aa, 0x7a58fdce, + 0x4ce6e521, 0xe7be4ae3, 0x1f51bdba, 0xeaa649f2 }}}, + {{{ 0xba5ad93d, 0xd47a5305, 0xf13f7e59, 0x01a6b965, + 0x9879aa5a, 0xc69a80f8, 0x5bbbb03a, 0xbe3279ed }}} + }, { + {{{ 0x27bb4d71, 0xcf291a33, 0x33524832, 0x6caf7d6b, + 0x766584ee, 0x6e0ee131, 0xd064c589, 0x160cb0f6 }}}, + {{{ 0x17136e8d, 0x9d5de554, 0x1aab720e, 0xe3f2d468, + 0xccf75cc2, 0xd1378b49, 0xc4ff16e1, 0x6920c375 }}} + }, { + {{{ 0x1a9ee611, 0x3eef9e96, 0x9cc37faf, 0xfe4d7bf3, + 0xb321d965, 0x462aa9b3, 0x208736c5, 0x1702da3e }}}, + {{{ 0x3a545ceb, 0xfba57bbf, 0x7ea858f5, 0x6dbcd766, + 0x680d92f1, 0x088e897c, 0xbc626c80, 0x468c1fd8 }}} + }, { + {{{ 0xb188660a, 0xb40f85c7, 0x99bc3c36, 0xc5873c19, + 0x7f33b54c, 0x3c7b4541, 0x1f8c9bf8, 0x4cd3a93c }}}, + {{{ 0x33099cb0, 0xf8dce380, 0x2edd2f33, 0x7a167dd6, + 0x0ffe35b7, 0x576d8987, 0xc68ace5c, 0xd2de0386 }}} + }, { + {{{ 0x6658bb08, 0x9a9e0a72, 0xc589607b, 0xe23c5f2a, + 0xf2bfb4c8, 0xa048ca14, 0xc62c2291, 0x4d9a0f89 }}}, + {{{ 0x0f827294, 0x427b5f31, 0x9f2c35cd, 0x1ea7a8b5, + 0x85a3c00f, 0x95442e56, 0x9b57975a, 0x8cb83121 }}} + }, { + {{{ 0x51f5cf67, 0x4333f0da, 0xf4f0d3cb, 0x6d3ea47c, + 0xa05a831f, 0x442fda14, 0x016d3e81, 0x6a496013 }}}, + {{{ 0xe52e0f48, 0xf647318c, 0x4a0d5ff1, 0x5ff3a66e, + 0x61199ba8, 0x046ed81a, 0x3e79c23a, 0x578edf08 }}} + }, { + {{{ 0x3ea01ea7, 0xb8f996f8, 0x7497bb15, 0xc0045d33, + 0x6205647c, 0xc4749dc9, 0x0efd22c9, 0xd8946054 }}}, + {{{ 0x12774ad5, 0x062dcb09, 0x8be06e3a, 0xcb13f310, + 0x235de1a9, 0xca281d35, 0x69c3645c, 0xaf8a7412 }}} + }, { + {{{ 0xbeb8b1e2, 0x8808ca5f, 0xea0dda76, 0x0262b204, + 0xddeb356b, 0xb6fffffc, 0xfbb83870, 0x52de253a }}}, + {{{ 0x8f8d21ea, 0x961f40c0, 0x002f03ed, 0x89686278, + 0x38e421ea, 0x0ff834d7, 0xd36fb8db, 0x3a270d6f }}} + } +}; + +static const ac precomputed_2E_KG[15] = { + { + {{{ 0x39a48db0, 0xefd7835b, 0x9b3c03bf, 0x9f1215a2, + 0x9b7bde45, 0x2791d0a0, 0x696e7167, 0x100f44da }}}, + {{{ 0x2bc65a09, 0x0fbd5cd6, 0xff5195ac, 0xb7ff4a18, + 0x0c090666, 0x2ec8f330, 0x92a00b77, 0xcdd9e131 }}} + }, { + {{{ 0x40fb27b6, 0x32427e28, 0xbe430576, 0xc76e3db2, + 0x61686aa5, 0x10f238ad, 0xbe778b1b, 0xfea74e3d }}}, + {{{ 0xf23cb96f, 0x701d3db7, 0x973f7b77, 0x126b596b, + 0xccb6af93, 0x7cf674de, 0x9b0b1329, 0x6e0568db }}} + }, { + {{{ 0x2c8118bc, 0x6cac5154, 0x399ddd98, 0x19bd4b34, + 0x2e9c8949, 0x47248a8d, 0x2cefa3b1, 0x734cb6a8 }}}, + {{{ 0x1e410fd5, 0xf1b340ad, 0xc4873539, 0xa2982bee, + 0xd4de4530, 0x7b5a3ea4, 0x42202574, 0xae46e10e }}} + }, { + {{{ 0xac1f98cd, 0xcbfc99c8, 0x4d7f0308, 0x52348905, + 0x1cc66021, 0xfaed8a9c, 0x4a474870, 0x9c3919a8 }}}, + {{{ 0xd4fc599d, 0xbe7e5e03, 0x6c64c8e6, 0x905326f7, + 0xf260e641, 0x584f044b, 0x4a4ddd57, 0xddb84f0f }}} + }, { + {{{ 0xed7cebed, 0xc4aacaa8, 0x4fae424e, 0xb75d2dce, + 0xba20735e, 0xa01585a2, 0xba122399, 0x3d75f24b }}}, + {{{ 0xd5570dce, 0xcbe4606f, 0x2da192c2, 0x9d00bfd7, + 0xa57b7265, 0x9c3ce86b, 0xec4edf5e, 0x987a22f1 }}} + }, { + {{{ 0x73ea0665, 0x211b9715, 0xf3a1abbb, 0x86f485d4, + 0xcd076f0e, 0xabd242d8, 0x0ba5dc88, 0x862332ab }}}, + {{{ 0x7b784911, 0x09af505c, 0xcaf4fae7, 0xc89544e8, + 0xae9a32eb, 0x256625f6, 0x606d1a3f, 0xe2532b72 }}} + }, { + {{{ 0x0deaf885, 0x79e9f313, 0x46df21c9, 0x938ff76e, + 0xa953bb2c, 0x1968f5fb, 0x29155f27, 0xdff538bf }}}, + {{{ 0x31d5d020, 0xf7bae0b1, 0x1a676a8d, 0x5afdc787, + 0xfa9d53ff, 0x11b4f032, 0xc5959167, 0x86ba433e }}} + }, { + {{{ 0x9475b7ba, 0x884fdff0, 0xe4918b3d, 0xe039e730, + 0xf5018cdb, 0x3d3e57ed, 0x1943785c, 0x95939698 }}}, + {{{ 0x7524f2fd, 0xe9b8abf8, 0xc8709385, 0x9c653f64, + 0x4b9cd684, 0x8ba0386a, 0x88c331dd, 0x2e7e5528 }}} + }, { + {{{ 0xeefe79e5, 0x940bef53, 0xbe9b87f3, 0xc518d286, + 0x7833042c, 0x9e0c7c76, 0x11fbe152, 0x104e2cb5 }}}, + {{{ 0x50bbec83, 0xc0d35e0f, 0x4acd0fcc, 0xee4879be, + 0x006085ee, 0xc8d80f5d, 0x72fe1ac1, 0x3c51bc1c }}} + }, { + {{{ 0xb2de976e, 0x06187f61, 0xf5e4b4b6, 0x52869e18, + 0x38d332ca, 0x74d4facd, 0xb3a2f8d9, 0x5c1c90b4 }}}, + {{{ 0xdaa37893, 0x98644d09, 0xabe39818, 0x682435a8, + 0x469c53a0, 0x17e46617, 0x77dc2e64, 0x642f9632 }}} + }, { + {{{ 0x222f6c54, 0xad2101c5, 0xfa74785e, 0xb05c7a58, + 0x489bcdaf, 0xce55fa79, 0xffe88d54, 0xc1f920fd }}}, + {{{ 0x9065e490, 0x32553ab0, 0x35329f74, 0x7611b9af, + 0xab7b24c0, 0x57df19ef, 0x6181c447, 0xb9a78749 }}} + }, { + {{{ 0xa80b7ea8, 0x392f156f, 0x8ae4a8bf, 0x57ab7ca0, + 0x50c4b178, 0xac320747, 0x0e781feb, 0x146041b9 }}}, + {{{ 0x845279b2, 0xd343f075, 0x7387afa5, 0x2d4fe757, + 0xa72f3c39, 0x151e0948, 0x550da168, 0x41a6d54e }}} + }, { + {{{ 0x075a0010, 0xb3134ed3, 0x7ae93e23, 0x9fa76f4b, + 0x7bb4daaa, 0xc0db256f, 0x464dd8a3, 0x7668dc27 }}}, + {{{ 0x9f5da977, 0x150063f5, 0x05efce00, 0x3acac5c8, + 0x884493fe, 0xc8e12ffc, 0x88f06bd2, 0x4ab936d8 }}} + }, { + {{{ 0x5d09ea98, 0x996fde77, 0x4145da58, 0x16ddf512, + 0xdc2fb225, 0xa97a6ca8, 0xfbdcdf5a, 0xc7331f30 }}}, + {{{ 0x86a86e52, 0x838f99e0, 0x77795edd, 0x68d39b29, + 0x9f412aaa, 0xe4e4f97e, 0x30d25352, 0xe5cc2c0a }}} + }, { + {{{ 0x9c21ff71, 0xb3d68650, 0xddbe3884, 0x11e7589d, + 0x423bac67, 0x7efd4055, 0x46957425, 0x587a7293 }}}, + {{{ 0x8f5a8fc6, 0x360adc2e, 0xbd69f12e, 0x6f8bbafb, + 0x0a3f3b4d, 0xf671f423, 0x59942dc3, 0xb49acb47 }}} + } +}; + +/* + * N: order of G + * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + */ +static const bn256 N[1] = { + {{ 0xd0364141, 0xbfd25e8c, 0xaf48a03b, 0xbaaedce6, + 0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff }} +}; + +/* + * MU = 2^512 / N + * MU = ( (1 << 256) | MU_lower ) + */ +static const bn256 MU_lower[1] = { + {{ 0x2fc9bec0, 0x402da173, 0x50b75fc4, 0x45512319, + 0x1, 0x0, 0x0, 0x0 }} +}; + + +#include "ecc.c" diff --git a/ec_p256k1.h b/ec_p256k1.h new file mode 100644 index 0000000..ec0e213 --- /dev/null +++ b/ec_p256k1.h @@ -0,0 +1,4 @@ +int compute_kP_p256k1 (ac *X, const bn256 *K, const ac *P); +int compute_kG_p256k1 (ac *X, const bn256 *K); +void ecdsa_p256k1 (bn256 *r, bn256 *s, const bn256 *z, const bn256 *d); +int check_secret_p256k1 (const bn256 *q, bn256 *d1); diff --git a/ecc-ed25519.c b/ecc-ed25519.c new file mode 100644 index 0000000..d55571b --- /dev/null +++ b/ecc-ed25519.c @@ -0,0 +1,952 @@ +/* -*- coding: utf-8 -*- + * ecc-ed25519.c - Elliptic curve computation for + * the twisted Edwards curve: -x^2 + y^2 = 1 + d*x^2*y^2 + * d = -121665/121666 + * + * Copyright (C) 2014, 2017 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "bn.h" +#include "mod.h" +#include "mod25638.h" +#include "sha512.h" + +/* + * References: + * + * [1] Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe, Bo-Yin Yang. + * High-speed high-security signatures. + * Journal of Cryptographic Engineering 2 (2012), 77--89. + * http://cr.yp.to/papers.html#ed25519 + * + * [2] Daniel J. Bernstein, Peter Birkner, Marc Joye, Tanja Lange, + * Christiane Peters. + * Twisted Edwards curves. + * Pages 389--405 in Progress in cryptology---AFRICACRYPT 2008. + * http://cr.yp.to/papers.html#twisted + */ + +/* + * IMPLEMENTATION NOTE + * + * (0) We assume that the processor has no cache, nor branch target + * prediction. Thus, we don't avoid indexing by secret value. + * We don't avoid conditional jump if both cases have same timing, + * either. + * + * (1) We use Radix-32 field arithmetic. It's a representation like + * 2^256-38, but it's more redundant. For example, "1" can be + * represented in three ways in 256-bit: 1, 2^255-18, and + * 2^256-37. + * + * (2) We use fixed base comb multiplication. Scalar is 252-bit. + * There are various possible choices for 252 = 2 * 2 * 3 * 3 * 7. + * Current choice of total size is 3KB. We use three tables, and + * a table has 16 points (3 * 1KB). + * + * Window size W = 4-bit, E = 21. + * <--21-bit- + * <---42-bit---------- + * [ ][########][////////][ ][########][////////] + * <-------63-bit---------------- + * <-----------84-bit---------------------- + * <--------------105-bit---------------------------- + * + * [ ][########][////////][ ][########][////////] + * <-126-bit- + * <-147-bit- + * <----168-bit-------- + * + * <-------189-bit--------------- + * <----------210-bit---------------------- + * <-------------231-bit----------------------------- + */ + +/* + * Identity element: (0,1) + * Negation: -(x,y) = (-x,y) + * + * d: -0x2DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235ECA6874A + * order: + * 0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED + * Gx: 0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A + * Gy: 0x6666666666666666666666666666666666666666666666666666666666666658 + */ + +/* d + 2^255 - 19 */ +static const bn256 coefficient_d[1] = { + {{ 0x135978a3, 0x75eb4dca, 0x4141d8ab, 0x00700a4d, + 0x7779e898, 0x8cc74079, 0x2b6ffe73, 0x52036cee }} }; + + +/** + * @brief Projective Twisted Coordinates + */ +typedef struct +{ + bn256 x[1]; + bn256 y[1]; + bn256 z[1]; +} ptc; + +#include "affine.h" + + +static int +mod25519_is_neg (const bn256 *a) +{ + return (a->word[0] & 1); +} + + +/** + * @brief X = 2 * A + * + * Compute (X3 : Y3 : Z3) = 2 * (X1 : Y1 : Z1) + */ +static void +point_double (ptc *X, const ptc *A) +{ + bn256 b[1], d[1], e[1]; + + /* Compute: B = (X1 + Y1)^2 */ + mod25638_add (b, A->x, A->y); + mod25638_sqr (b, b); + + /* Compute: C = X1^2 : E */ + mod25638_sqr (e, A->x); + + /* Compute: D = Y1^2 */ + mod25638_sqr (d, A->y); + + /* E = aC; where a = -1 */ + /* Compute: D - E = D + C : Y3_tmp */ + mod25638_add (X->y, e, d); + + /* Compute: -F = -(E + D) = C - D; where a = -1 : E */ + mod25638_sub (e, e, d); + + /* Compute: H = Z1^2 : D */ + mod25638_sqr (d, A->z); + + /* Compute: -J = 2*H - F : D */ + mod25638_add (d, d, d); + mod25638_add (d, d, e); + + /* Compute: X3 = (B-C-D)*J = -J*(C+D-B) = -J*(Y3_tmp-B) */ + mod25638_sub (X->x, X->y, b); + mod25638_mul (X->x, X->x, d); + + /* Compute: Y3 = -F*(D-E) = -F*Y3_tmp */ + mod25638_mul (X->y, X->y, e); + + /* Z3 = -F*-J */ + mod25638_mul (X->z, e, d); +} + + +/** + * @brief X = A + B + * + * @param X Destination PTC + * @param A PTC + * @param B AC + * + * Compute: (X3 : Y3 : Z3) = (X1 : Y1 : Z1) + (X2 : Y2 : 1) + */ +static void +point_add (ptc *X, const ptc *A, const ac *B) +{ + bn256 c[1], d[1], e[1], tmp[1]; + + /* Compute: C = X1 * X2 */ + mod25638_mul (c, A->x, B->x); + + /* Compute: D = Y1 * Y2 */ + mod25638_mul (d, A->y, B->y); + + /* Compute: E = d * C * D */ + mod25638_mul (e, c, d); + mod25638_mul (e, coefficient_d, e); + + /* Compute: C_1 = C + D */ + mod25638_add (c, c, d); + + /* Compute: D_1 = Z1^2 : B */ + mod25638_sqr (d, A->z); + + /* tmp = D_1 - E : F */ + mod25638_sub (tmp, d, e); + + /* D_2 = D_1 + E : G */ + mod25638_add (d, d, e); + + /* X3_final = Z1 * tmp * ((X1 + Y1) * (X2 + Y2) - C_1) */ + mod25638_add (X->x, A->x, A->y); + mod25638_add (e, B->x, B->y); + mod25638_mul (e, X->x, e); + mod25638_sub (e, e, c); + mod25638_mul (e, tmp, e); + mod25638_mul (X->x, A->z, e); + + /* Y3_final = Z1 * D_2 * C_1 */ + mod25638_mul (c, d, c); + mod25638_mul (X->y, A->z, c); + + /* Z3_final = tmp * D_2 */ + mod25638_mul (X->z, tmp, d); + + /* A = Z1 */ + /* B = A^2 */ + /* C = X1 * X2 */ + /* D = Y1 * Y2 */ + /* E = d * C * D */ + /* F = B - E */ + /* G = B + E */ + /* X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D) */ + /* Y3 = A * G * (D - aC); where a = -1 */ + /* Z3 = F * G */ +} + + +/** + * @brief X = convert A + * + * @param X Destination AC + * @param A PTC + * + * (X1:Y1:Z1) represents the affine point (x=X1/Z1, y=Y1/Z1) + */ +static void +point_ptc_to_ac (ac *X, const ptc *A) +{ + bn256 z_inv[1]; + + /* + * A->z may be bigger than p25519, or two times bigger than p25519. + * But this is no problem for computation of mod_inv. + */ + mod_inv (z_inv, A->z, p25519); + + mod25638_mul (X->x, A->x, z_inv); + mod25519_reduce (X->x); + mod25638_mul (X->y, A->y, z_inv); + mod25519_reduce (X->y); +} + + +static const ac precomputed_KG[16] = { + { {{{ 0, 0, 0, 0, 0, 0, 0, 0 }}}, + {{{ 1, 0, 0, 0, 0, 0, 0, 0 }}} }, + { {{{ 0x8f25d51a, 0xc9562d60, 0x9525a7b2, 0x692cc760, + 0xfdd6dc5c, 0xc0a4e231, 0xcd6e53fe, 0x216936d3 }}}, + {{{ 0x66666658, 0x66666666, 0x66666666, 0x66666666, + 0x66666666, 0x66666666, 0x66666666, 0x66666666 }}} }, + { {{{ 0x3713af22, 0xac7137bd, 0xac634604, 0x25ed77a4, + 0xa815e038, 0xce0d0064, 0xbca90151, 0x041c030f }}}, + {{{ 0x0780f989, 0xe9b33fcf, 0x3d4445e7, 0xe4e97c2a, + 0x655e5c16, 0xc67dc71c, 0xee43fb7a, 0x72467625 }}} }, + { {{{ 0x3ee99893, 0x76a19171, 0x7ba9b065, 0xe647edd9, + 0x6aeae260, 0x31f39299, 0x5f4a9bb2, 0x6d9e4545 }}}, + {{{ 0x94cae280, 0xc41433da, 0x79061211, 0x8e842de8, + 0xa259dc8a, 0xaab95e0b, 0x99013cd0, 0x28bd5fc3 }}} }, + { {{{ 0x7d23ea24, 0x59e22c56, 0x0460850e, 0x1e745a88, + 0xda13ef4b, 0x4583ff4c, 0x95083f85, 0x1f13202c }}}, + {{{ 0x90275f48, 0xad42025c, 0xb55c4778, 0x0085087e, + 0xfdfd7ffa, 0xf21109e7, 0x6c381b7e, 0x66336d35 }}} }, + { {{{ 0xd00851f2, 0xaa9476ab, 0x4a61600b, 0xe7838534, + 0x1a52df87, 0x0de65625, 0xbd675870, 0x5f0dd494 }}}, + {{{ 0xe23493ba, 0xf20aec1b, 0x3414b0a8, 0x8f7f2741, + 0xa80e1eb6, 0x497e74bd, 0xe9365b15, 0x1648eaac }}} }, + { {{{ 0x04ac2b69, 0x5b78dcec, 0x32001a73, 0xecdb66ce, + 0xb34cf697, 0xb75832f4, 0x3a2bce94, 0x7aaf57c5 }}}, + {{{ 0x60fdfc6f, 0xb32ed2ce, 0x757924c6, 0x77bf20be, + 0x48742dd1, 0xaebd15dd, 0x55d38439, 0x6311bb16 }}} }, + { {{{ 0x42ff5c97, 0x139cdd73, 0xdbd82964, 0xee4c359e, + 0x70611a3f, 0x91c1cd94, 0x8075dbcb, 0x1d0c34f6 }}}, + {{{ 0x5f931219, 0x43eaa549, 0xa23d35a6, 0x3737aba7, + 0x46f167bb, 0x54b1992f, 0xb74a9944, 0x01a11f3c }}} }, + { {{{ 0xba46b161, 0x67a5310e, 0xd9d67f6c, 0x790f8527, + 0x2f6cc814, 0x359c5b5f, 0x7786383d, 0x7b6a5565 }}}, + {{{ 0x663ab0d3, 0xf1431b60, 0x09995826, 0x14a32d8f, + 0xeddb8571, 0x61d526f6, 0x0eac739a, 0x0cb7acea }}} }, + { {{{ 0x4a2d009f, 0x5eb1a697, 0xd8df987a, 0xdacb43b4, + 0x8397f958, 0x4870f214, 0x8a175fbb, 0x5aa0c67c }}}, + {{{ 0x78887db3, 0x27dbbd4c, 0x64e322ab, 0xe327b707, + 0x7cbe4e3b, 0x87e293fa, 0xbda72395, 0x17040799 }}} }, + { {{{ 0x99d1e696, 0xc833a5a2, 0x2d9d5877, 0x969bff8e, + 0x2216fa67, 0x383a533a, 0x684d3925, 0x338bbe0a }}}, + {{{ 0xd6cfb491, 0x35b5aae8, 0xaa12f3f8, 0x4a588279, + 0x2e30380e, 0xa7c2e708, 0x9e4b3d62, 0x69f13e09 }}} }, + { {{{ 0x27f1cd56, 0xec0dc2ef, 0xdb11cc97, 0x1af11548, + 0x9ebc7613, 0xb642f86a, 0xcb77c3b9, 0x5ce45e73 }}}, + {{{ 0x3eddd6de, 0x5d128786, 0x4859eab7, 0x16f9a6b4, + 0xd8782345, 0x55c53916, 0xdb7b202a, 0x6b1dfa87 }}} }, + { {{{ 0x19e30528, 0x2461a8ed, 0x665cfb1c, 0xaf756bf9, + 0x3a6e8673, 0x0fcafd1d, 0x45d10f48, 0x0d264435 }}}, + {{{ 0x5431db67, 0x543fd4c6, 0x60932432, 0xc153a5b3, + 0xd2119aa4, 0x41d5b8eb, 0x8b09b6a5, 0x36bd9ab4 }}} }, + { {{{ 0x21e06738, 0x6d39f935, 0x3765dd86, 0x4e6a7c59, + 0xa4730880, 0xefc0dd80, 0x4079fe2f, 0x40617e56 }}}, + {{{ 0x921439b9, 0xbc83cdff, 0x98833c09, 0xd5cccc06, + 0xda13cdcb, 0xe315c425, 0x67ff5370, 0x37bc6e84 }}} }, + { {{{ 0xf643b5f5, 0x65e7f028, 0x0ffbf5a8, 0x5b0d4831, + 0xf4085f62, 0x0f540498, 0x0db7bd1b, 0x6f0bb035 }}}, + {{{ 0x9733742c, 0x51f65571, 0xf513409f, 0x2fc047a0, + 0x355facf6, 0x07f45010, 0x3a989a9c, 0x5cd416a9 }}} }, + { {{{ 0x748f2a67, 0x0bdd7208, 0x415b7f7f, 0x0cf0b80b, + 0x57aa0119, 0x44afdd5f, 0x430dc946, 0x05d68802 }}}, + {{{ 0x1a60eeb2, 0x420c46e5, 0x665024f5, 0xc60a9b33, + 0x48c51347, 0x37520265, 0x00a21bfb, 0x6f4be0af }}} } +}; + +static const ac precomputed_2E_KG[16] = { + { {{{ 0, 0, 0, 0, 0, 0, 0, 0 }}}, + {{{ 1, 0, 0, 0, 0, 0, 0, 0 }}} }, + { {{{ 0x199c4f7d, 0xec314ac0, 0xb2ebaaf9, 0x66a39c16, + 0xedd4d15f, 0xab1c92b8, 0x57d9eada, 0x482a4cdf }}}, + {{{ 0x6e4eb04b, 0xbd513b11, 0x25e4fd6a, 0x3f115fa5, + 0x14519298, 0x0b3c5fc6, 0x81c2f7a8, 0x7391de43 }}} }, + { {{{ 0x1254fe02, 0xa57dca18, 0x6da34368, 0xa56a2a14, + 0x63e7328e, 0x44c6e34f, 0xca63ab3e, 0x3f748617 }}}, + {{{ 0x7dc1641e, 0x5a13dc52, 0xee4e9ca1, 0x4cbb2899, + 0x1ba9acee, 0x3938a289, 0x420fc47b, 0x0fed89e6 }}} }, + { {{{ 0x49cbad08, 0x3c193f32, 0x15e80ef5, 0xdda71ef1, + 0x9d128c33, 0xda44186c, 0xbf98c24f, 0x54183ede }}}, + {{{ 0x93d165c1, 0x2cb483f7, 0x177f44aa, 0x51762ace, + 0xb4ab035d, 0xb3fe651b, 0xa0b0d4e5, 0x426c99c3 }}} }, + { {{{ 0xef3f3fb1, 0xb3fcf4d8, 0x065060a0, 0x7052292b, + 0x24240b15, 0x18795ff8, 0x9989ffcc, 0x13aea184 }}}, + {{{ 0xc2b81f44, 0x1930c101, 0x10600555, 0x672d6ca4, + 0x1b25e570, 0xfbddbff2, 0x8ca12b70, 0x0884949c }}} }, + { {{{ 0x00564bbf, 0x9983a033, 0xde61b72d, 0x95587d25, + 0xeb17ad71, 0xb6719dfb, 0xc0bc3517, 0x46871ad0 }}}, + {{{ 0xe95a6693, 0xb034fb61, 0x76eabad9, 0x5b0d8d18, + 0x884785dc, 0xad295dd0, 0x74a1276a, 0x359debad }}} }, + { {{{ 0xe89fb5ca, 0x2e5a2686, 0x5656c6c5, 0xd3d200ba, + 0x9c969001, 0xef4c051e, 0x02cb45f4, 0x0d4ea946 }}}, + {{{ 0x76d6e506, 0xa6f8a422, 0x63209e23, 0x454c768f, + 0x2b372386, 0x5c12fd04, 0xdbfee11f, 0x1aedbd3e }}} }, + { {{{ 0x00dbf569, 0x700ab50f, 0xd335b313, 0x9553643c, + 0xa17dc97e, 0xeea9bddf, 0x3350a2bd, 0x0d12fe3d }}}, + {{{ 0xa16a3dee, 0xe5ac35fe, 0xf81950c3, 0x4ae4664a, + 0x3dbbf921, 0x75c63df4, 0x2958a5a6, 0x545b109c }}} }, + { {{{ 0x0a61b29c, 0xd7a52a98, 0x65aca9ee, 0xe21e0acb, + 0x5985dcbe, 0x57a69c0f, 0xeb87a534, 0x3c0c1e7b }}}, + {{{ 0x6384bd2f, 0xf0a0b50d, 0xc6939e4b, 0xff349a34, + 0x6e2f1973, 0x922c4554, 0xf1347631, 0x74e826b2 }}} }, + { {{{ 0xa655803c, 0xd7eaa066, 0x38292c5c, 0x09504e76, + 0x2c874953, 0xe298a02e, 0x8932b73f, 0x225093ed }}}, + {{{ 0xe69c3efd, 0xf93e2b4d, 0x8a87c799, 0xa2cbd5fc, + 0x85dba986, 0xdf41da94, 0xccee8edc, 0x36fe85e7 }}} }, + { {{{ 0x7d742813, 0x78df7dc5, 0x4a193e64, 0x333bcc6d, + 0x6a966d2d, 0x8242aa25, 0x4cd36d32, 0x03500a94 }}}, + {{{ 0x580505d7, 0xd5d110fc, 0xfa11e1e9, 0xb2f47e16, + 0x06eab6b4, 0xd0030f92, 0x62c91d46, 0x2dc80d5f }}} }, + { {{{ 0x2a75e492, 0x5788b01a, 0xbae31352, 0x992acf54, + 0x8159db27, 0x4591b980, 0xd3d84740, 0x36c6533c }}}, + {{{ 0x103883b5, 0xc44c7c00, 0x515d0820, 0x10329423, + 0x71b9dc16, 0xbd306903, 0xf88f8d32, 0x7edd5a95 }}} }, + { {{{ 0x005523d7, 0xfd63b1ac, 0xad70dd21, 0x74482e0d, + 0x02b56105, 0x67c9d9d0, 0x5971b456, 0x4d318012 }}}, + {{{ 0x841106df, 0xdc9a6f6d, 0xa326987f, 0x7c52ed9d, + 0x00607ea0, 0x4dbeaa6f, 0x6959e688, 0x115c221d }}} }, + { {{{ 0xc80f7c16, 0xf8718464, 0xe9930634, 0x05dc8f40, + 0xc2e9d5f4, 0xefa699bb, 0x021da209, 0x2469e813 }}}, + {{{ 0xc602a3c4, 0x75c02845, 0x0a200f9d, 0x49d1b2ce, + 0x2fb3ec8f, 0xd21b75e4, 0xd72a7545, 0x10dd726a }}} }, + { {{{ 0x63ef1a6c, 0xeda58527, 0x051705e0, 0xb3fc0e72, + 0x44f1161f, 0xbda6f3ee, 0xf339efe5, 0x7680aebf }}}, + {{{ 0xb1b070a7, 0xe8d3fd01, 0xdbfbaaa0, 0xc3ff7dbf, + 0xa320c916, 0xd81ef6f2, 0x62a3b54d, 0x3e22a1fb }}} }, + { {{{ 0xb1fa18c8, 0xcdbb9187, 0xcb483a17, 0x8ddb5f6b, + 0xea49af98, 0xc0a880b9, 0xf2dfddd0, 0x53bf600b }}}, + {{{ 0x9e25b164, 0x4217404c, 0xafb74aa7, 0xfabf06ee, + 0x2b9f233c, 0xb17712ae, 0xd0eb909e, 0x71f0b344 }}} } +}; + +static const ac precomputed_4E_KG[16] = { + { {{{ 0, 0, 0, 0, 0, 0, 0, 0 }}}, + {{{ 1, 0, 0, 0, 0, 0, 0, 0 }}} }, + { {{{ 0xe388a820, 0xbb6ec091, 0x5182278a, 0xa928b283, + 0xa9a6eb83, 0x2259174d, 0x45500054, 0x184b48cb }}}, + {{{ 0x26e77c33, 0xfe324dba, 0x83faf453, 0x6679a5e3, + 0x2380ef73, 0xdd60c268, 0x03dc33a9, 0x3ee0e07a }}} }, + { {{{ 0xce974493, 0x403aff28, 0x9bf6f5c4, 0x84076bf4, + 0xecd898fb, 0xec57038c, 0xb663ed49, 0x2898ffaa }}}, + {{{ 0xf335163d, 0xf4b3bc46, 0xfa4fb6c6, 0xe613a0f4, + 0xb9934557, 0xe759d6bc, 0xab6c9477, 0x094f3b96 }}} }, + { {{{ 0x6afffe9e, 0x168bb5a0, 0xee748c29, 0x950f7ad7, + 0xda17203d, 0xa4850a2b, 0x77289e0f, 0x0062f7a7 }}}, + {{{ 0x4b3829fa, 0x6265d4e9, 0xbdfcd386, 0x4f155ada, + 0x475795f6, 0x9f38bda4, 0xdece4a4c, 0x560ed4b3 }}} }, + { {{{ 0x141e648a, 0xdad4570a, 0x019b965c, 0x8bbf674c, + 0xdb08fe30, 0xd7a8d50d, 0xa2851109, 0x7efb45d3 }}}, + {{{ 0xd0c28cda, 0x52e818ac, 0xa321d436, 0x792257dd, + 0x9d71f8b7, 0x867091c6, 0x11a1bf56, 0x0fe1198b }}} }, + { {{{ 0x06137ab1, 0x4e848339, 0x3e6674cc, 0x5673e864, + 0x0140502b, 0xad882043, 0x6ea1e46a, 0x34b5c0cb }}}, + {{{ 0x1d70aa7c, 0x29786814, 0x8cdbb8aa, 0x840ae3f9, + 0xbd4801fb, 0x78b4d622, 0xcf18ae9a, 0x6cf4e146 }}} }, + { {{{ 0x36297168, 0x95c270ad, 0x942e7812, 0x2303ce80, + 0x0205cf0e, 0x71908cc2, 0x32bcd754, 0x0cc15edd }}}, + {{{ 0x2c7ded86, 0x1db94364, 0xf141b22c, 0xc694e39b, + 0x5e5a9312, 0xf22f64ef, 0x3c5e6155, 0x649b8859 }}} }, + { {{{ 0xb6417945, 0x0d5611c6, 0xac306c97, 0x9643fdbf, + 0x0df500ff, 0xe81faaa4, 0x6f50e615, 0x0792c79b }}}, + {{{ 0xd2af8c8d, 0xb45bbc49, 0x84f51bfe, 0x16c615ab, + 0xc1d02d32, 0xdc57c526, 0x3c8aaa55, 0x5fb9a9a6 }}} }, + { {{{ 0xdee40b98, 0x82faa8db, 0x6d520674, 0xff8a5208, + 0x446ac562, 0x1f8c510f, 0x2cc6b66e, 0x4676d381 }}}, + {{{ 0x2e7429f4, 0x8f1aa780, 0x8ed6bdf6, 0x2a95c1bf, + 0x457fa0eb, 0x051450a0, 0x744c57b1, 0x7d89e2b7 }}} }, + { {{{ 0x3f95ea15, 0xb6bdacd2, 0x2f1a5d69, 0xc9a9d1b1, + 0xf4d22d72, 0xd4c2f1a9, 0x4dc516b5, 0x73ecfdf1 }}}, + {{{ 0x05391e08, 0xa1ce93cd, 0x7b8aac17, 0x98f1e99e, + 0xa098cbb3, 0x9ba84f2e, 0xf9bdd37a, 0x1425aa8b }}} }, + { {{{ 0x966abfc0, 0x8a385bf4, 0xf081a640, 0x55e5e8bc, + 0xee26f5ff, 0x835dff85, 0xe509e1ea, 0x4927e622 }}}, + {{{ 0x352334b0, 0x164c8dbc, 0xa3fea31f, 0xcac1ad63, + 0x682fd457, 0x9b87a676, 0x1a53145f, 0x75f382ff }}} }, + { {{{ 0xc3efcb46, 0x16b944f5, 0x68cb184c, 0x1fb55714, + 0x9ccf2dc8, 0xf1c2b116, 0x808283d8, 0x7417e00f }}}, + {{{ 0x930199ba, 0x1ea67a22, 0x718990d8, 0x9fbaf765, + 0x8f3d5d57, 0x231fc664, 0xe5853194, 0x38141a19 }}} }, + { {{{ 0x2f81290d, 0xb9f00390, 0x04a9ca6c, 0x44877827, + 0xe1dbdd65, 0x65d7f9b9, 0xf7c6698a, 0x7133424c }}}, + {{{ 0xa7cd250f, 0x604cfb3c, 0x5acc18f3, 0x460c3c4b, + 0xb518e3eb, 0xa53e50e0, 0x98a40196, 0x2b4b9267 }}} }, + { {{{ 0xc5dbd06c, 0x591b0672, 0xaa1eeb65, 0x10d43dca, + 0xcd2517af, 0x420cdef8, 0x0b695a8a, 0x513a307e }}}, + {{{ 0x66503215, 0xee9d6a7b, 0x088fd9a4, 0xdea58720, + 0x973afe12, 0x8f3cbbea, 0x872f2538, 0x005c2350 }}} }, + { {{{ 0x35af3291, 0xe5024b70, 0x4f5e669a, 0x1d3eec2d, + 0x6e79d539, 0xc1f6d766, 0x795b5248, 0x34ec043f }}}, + {{{ 0x400960b6, 0xb2763511, 0x29e57df0, 0xff7a3d84, + 0x1666c1f1, 0xaeac7792, 0x66084bc0, 0x72426e97 }}} }, + { {{{ 0x44f826ca, 0x5b1c3199, 0x790aa408, 0x68b00b73, + 0x69e9b92b, 0xaf0984b4, 0x3ffe9093, 0x5fe6736f }}}, + {{{ 0xffd49312, 0xd67f2889, 0x5cb9ed21, 0x3520d747, + 0x3c65a606, 0x94f893b1, 0x2d65496f, 0x2fee5e8c }}} } +}; + +/** + * @brief X = k * G + * + * @param K scalar k + * + * Return -1 on error. + * Return 0 on success. + */ +static void +compute_kG_25519 (ac *X, const bn256 *K) +{ + ptc Q[1]; + int i; + + /* identity element */ + memset (Q, 0, sizeof (ptc)); + Q->y->word[0] = 1; + Q->z->word[0] = 1; + + for (i = 20; i >= 0; i--) + { + int k0, k1, k2; + + k0 = ((K->word[0] >> i) & 1) + | (i < 1 ? ((K->word[1] >> 30) & 2) + : (((K->word[2] >> (i-1)) & 1) << 1)) + | (i < 2 ? ((K->word[3] >> (i+28)) & 4) + : (((K->word[4] >> (i-2)) & 1) << 2)) + | (i < 3 ? ((K->word[5] >> (i+26)) & 8) + : (((K->word[6] >> (i-3)) & 1) << 3)); + + k1 = (i < 11 ? ((K->word[0] >> (i+21)) & 1) + : ((K->word[1] >> (i-11)) & 1)) + | (i < 12 ? ((K->word[2] >> (i+19)) & 2) + : (((K->word[3] >> (i-12)) & 1) << 1)) + | (i < 13 ? ((K->word[4] >> (i+17)) & 4) + : (((K->word[5] >> (i-13)) & 1) << 2)) + | (i < 14 ? ((K->word[6] >> (i+15)) & 8) + : (((K->word[7] >> (i-14)) & 1) << 3)); + + k2 = ((K->word[1] >> (i+10)) & 1) + | ((K->word[3] >> (i+8)) & 2) + | ((K->word[5] >> (i+6)) & 4) + | ((K->word[7] >> (i+4)) & 8); + + point_double (Q, Q); + point_add (Q, Q, &precomputed_KG[k0]); + point_add (Q, Q, &precomputed_2E_KG[k1]); + point_add (Q, Q, &precomputed_4E_KG[k2]); + } + + point_ptc_to_ac (X, Q); +} + + +#define BN416_WORDS 13 +#define BN128_WORDS 4 + +/* M: The order of the generator G. */ +static const bn256 M[1] = { + {{ 0x5CF5D3ED, 0x5812631A, 0xA2F79CD6, 0x14DEF9DE, + 0x00000000, 0x00000000, 0x00000000, 0x10000000 }} +}; + +#define C ((const uint32_t *)M) + +static void +bnX_mul_C (uint32_t *r, const uint32_t *q, int q_size) +{ + int i, j, k; + int i_beg, i_end; + uint32_t r0, r1, r2; + + r0 = r1 = r2 = 0; + for (k = 0; k <= q_size + BN128_WORDS - 2; k++) + { + if (q_size < BN128_WORDS) + if (k < q_size) + { + i_beg = 0; + i_end = k; + } + else + { + i_beg = k - q_size + 1; + i_end = k; + if (i_end > BN128_WORDS - 1) + i_end = BN128_WORDS - 1; + } + else + if (k < BN128_WORDS) + { + i_beg = 0; + i_end = k; + } + else + { + i_beg = k - BN128_WORDS + 1; + i_end = k; + if (i_end > q_size - 1) + i_end = q_size - 1; + } + + for (i = i_beg; i <= i_end; i++) + { + uint64_t uv; + uint32_t u, v; + uint32_t carry; + + j = k - i; + if (q_size < BN128_WORDS) + uv = ((uint64_t )q[j])*((uint64_t )C[i]); + else + uv = ((uint64_t )q[i])*((uint64_t )C[j]); + v = uv; + u = (uv >> 32); + r0 += v; + carry = (r0 < v); + r1 += carry; + carry = (r1 < carry); + r1 += u; + carry += (r1 < u); + r2 += carry; + } + + r[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + r[k] = r0; +} + +/** + * @brief R = A mod M (using M=2^252+C) (Barret reduction) + * + * See HAC 14.47 and 14.52. + */ +static void +mod_reduce_M (bn256 *R, const bn512 *A) +{ + uint32_t q[BN256_WORDS+1]; + uint32_t tmp[BN416_WORDS]; + bn256 r[1]; + uint32_t carry, next_carry; + int i; +#define borrow carry + + q[8] = A->word[15]>>28; + carry = A->word[15] & 0x0fffffff; + for (i = BN256_WORDS - 1; i >= 0; i--) + { + next_carry = A->word[i+7] & 0x0fffffff; + q[i] = (A->word[i+7] >> 28) | (carry << 4); + carry = next_carry; + } + memcpy (R, A, sizeof (bn256)); + R->word[7] &= 0x0fffffff; + + /* Q_size: 9 */ + bnX_mul_C (tmp, q, 9); /* TMP = Q*C */ + /* Q = tmp / 2^252 */ + carry = tmp[12] & 0x0fffffff; + for (i = 4; i >= 0; i--) + { + next_carry = tmp[i+7] & 0x0fffffff; + q[i] = (tmp[i+7] >> 28) | (carry << 4); + carry = next_carry; + } + /* R' = tmp % 2^252 */ + memcpy (r, tmp, sizeof (bn256)); + r->word[7] &= 0x0fffffff; + /* R -= R' */ + borrow = bn256_sub (R, R, r); + if (borrow) + bn256_add (R, R, M); + else + bn256_add ((bn256 *)tmp, R, M); + + /* Q_size: 5 */ + bnX_mul_C (tmp, q, 5); /* TMP = Q*C */ + carry = tmp[8] & 0x0fffffff; + q[0] = (tmp[7] >> 28) | (carry << 4); + /* R' = tmp % 2^252 */ + memcpy (r, tmp, sizeof (bn256)); + r->word[7] &= 0x0fffffff; + /* R += R' */ + bn256_add (R, R, r); + borrow = bn256_sub (R, R, M); + if (borrow) + bn256_add (R, R, M); + else + bn256_add ((bn256 *)tmp, R, M); + + /* Q_size: 1 */ + bnX_mul_C (tmp, q, 1); /* TMP = Q*C */ + /* R' = tmp % 2^252 */ + memset (((uint8_t *)r)+(sizeof (uint32_t)*5), 0, sizeof (uint32_t)*3); + memcpy (r, tmp, sizeof (uint32_t)*5); + /* R -= R' */ + borrow = bn256_sub (R, R, r); + if (borrow) + bn256_add (R, R, M); + else + bn256_add ((bn256 *)tmp, R, M); +#undef borrow +} + + +int +eddsa_sign_25519 (const uint8_t *input, size_t ilen, uint32_t *out, + const bn256 *a, const uint8_t *seed, const bn256 *pk) +{ + bn256 *r, *s; + sha512_context ctx; + uint8_t hash[64]; + bn256 tmp[1]; + ac R[1]; + uint32_t carry, borrow; + + r = (bn256 *)out; + s = (bn256 *)(out+(32/4)); + + sha512_start (&ctx); + sha512_update (&ctx, seed, sizeof (bn256)); /* It's upper half of the hash */ + sha512_update (&ctx, input, ilen); + sha512_finish (&ctx, hash); + + mod_reduce_M (r, (bn512 *)hash); + compute_kG_25519 (R, r); + + /* EdDSA encoding. */ + memcpy (tmp, R->y, sizeof (bn256)); + tmp->word[7] ^= mod25519_is_neg (R->x) * 0x80000000; + + sha512_start (&ctx); + sha512_update (&ctx, (uint8_t *)tmp, sizeof (bn256)); + sha512_update (&ctx, (uint8_t *)pk, sizeof (bn256)); + sha512_update (&ctx, input, ilen); + sha512_finish (&ctx, (uint8_t *)hash); + + mod_reduce_M (s, (bn512 *)hash); + bn256_mul ((bn512 *)hash, s, a); + mod_reduce_M (s, (bn512 *)hash); + carry = bn256_add (s, s, r); + borrow = bn256_sub (s, s, M); + + memcpy (r, tmp, sizeof (bn256)); + + if ((borrow && !carry)) + bn256_add (s, s, M); + else + bn256_add (tmp, s, M); + + return 0; +} + +static void +eddsa_public_key_25519 (bn256 *pk, const bn256 *a) +{ + ac R[1]; + ptc X[1]; + bn256 a0[1]; + + bn256_shift (a0, a, -3); + compute_kG_25519 (R, a0); + memcpy (X, R, sizeof (ac)); + memset (X->z, 0, sizeof (bn256)); + X->z->word[0] = 1; + point_double (X, X); + point_double (X, X); + point_double (X, X); + point_ptc_to_ac (R, X); + /* EdDSA encoding. */ + memcpy (pk, R->y, sizeof (bn256)); + pk->word[7] ^= mod25519_is_neg (R->x) * 0x80000000; +} + + +void +eddsa_compute_public_25519 (const uint8_t *kd, uint8_t *pubkey) +{ + eddsa_public_key_25519 ((bn256 *)pubkey, (const bn256 *)kd); +} + + +#if 0 +/** + * check if P is on the curve. + * + * Return -1 on error. + * Return 0 on success. + */ +static int +point_is_on_the_curve (const ac *P) +{ + bn256 s[1], t[1]; + + /* Twisted Edwards curve: a*x^2 + y^2 = 1 + d*x^2*y^2 */ +} + +int +compute_kP_25519 (ac *X, const bn256 *K, const ac *P); +#endif + +#ifdef PRINT_OUT_TABLE +static const ptc G[1] = {{ + {{{ 0x8f25d51a, 0xc9562d60, 0x9525a7b2, 0x692cc760, + 0xfdd6dc5c, 0xc0a4e231, 0xcd6e53fe, 0x216936d3 }}}, + {{{ 0x66666658, 0x66666666, 0x66666666, 0x66666666, + 0x66666666, 0x66666666, 0x66666666, 0x66666666 }}}, + {{{ 1, 0, 0, 0, 0, 0, 0, 0 }}}, +}}; + +#include + +#ifdef TESTING_EDDSA +static void +print_bn256 (const bn256 *X) +{ + int i; + + for (i = 7; i >= 0; i--) + printf ("%08x", X->word[i]); + puts (""); +} +#endif + +#if 0 +static void +print_point (const ac *X) +{ + int i; + +#ifdef PRINT_OUT_TABLE_AS_C + fputs (" { {{{ ", stdout); + for (i = 0; i < 4; i++) + printf ("0x%08x, ", X->x->word[i]); + fputs ("\n ", stdout); + for (; i < 7; i++) + printf ("0x%08x, ", X->x->word[i]); + printf ("0x%08x }}},\n", X->x->word[i]); + fputs (" {{{ ", stdout); + for (i = 0; i < 4; i++) + printf ("0x%08x, ", X->y->word[i]); + fputs ("\n ", stdout); + for (; i < 7; i++) + printf ("0x%08x, ", X->y->word[i]); + printf ("0x%08x }}} },\n", X->y->word[i]); +#else + puts ("--"); + for (i = 7; i >= 0; i--) + printf ("%08x", X->x->word[i]); + puts (""); + for (i = 7; i >= 0; i--) + printf ("%08x", X->y->word[i]); + puts (""); + puts ("--"); +#endif +} + +static void +print_point_ptc (const ptc *X) +{ + int i; + + puts ("---"); + for (i = 7; i >= 0; i--) + printf ("%08x", X->x->word[i]); + puts (""); + for (i = 7; i >= 0; i--) + printf ("%08x", X->y->word[i]); + puts (""); + for (i = 7; i >= 0; i--) + printf ("%08x", X->z->word[i]); + puts (""); + puts ("---"); +} +#endif + + +#ifndef TESTING_EDDSA +static void power_2 (ac *A, ptc *a, int N) +{ + int i; + + for (i = 0; i < N; i++) + ed_double_25638 (a, a); + ptc_to_ac_25519 (A, a); +} + +static void print_table (ac *a0001, ac *a0010, ac *a0100, ac *a1000) +{ + int i; + ptc a[1]; + ac x[1]; + + for (i = 1; i < 16; i++) + { + /* A := Identity Element */ + memset (a, 0, sizeof (ptc)); + a->y->word[0] = 1; + a->z->word[0] = 1; + + if ((i & 1)) + ed_add_25638 (a, a, a0001); + if ((i & 2)) + ed_add_25638 (a, a, a0010); + if ((i & 4)) + ed_add_25638 (a, a, a0100); + if ((i & 8)) + ed_add_25638 (a, a, a1000); + + ptc_to_ac_25519 (x, a); + print_point (x); + } + + fputs ("\n", stdout); +} + +static void compute_and_print_table (ac *a0001, ac *a0010, ac *a0100, ac *a1000) +{ + ptc a[1]; + + memcpy (a, a0001, sizeof (ac)); + memset (a->z, 0, sizeof (bn256)); + a->z->word[0] = 1; + power_2 (a0010, a, 63); + power_2 (a0100, a, 63); + power_2 (a1000, a, 63); + print_table (a0001, a0010, a0100, a1000); +} +#endif + +int +main (int argc, char *argv[]) +{ +#ifdef TESTING_EDDSA + uint8_t hash[64]; + bn256 a[1]; + uint8_t r_s[64]; + bn256 pk[1]; + bn256 *r, *s; + + const bn256 sk[1] = { + {{ 0x9db1619d, 0x605afdef, 0xf44a84ba, 0xc42cec92, + 0x69c54944, 0x1969327b, 0x03ac3b70, 0x607fae1c }} }; + + const bn256 r_expected[1] = { + {{ 0x004356e5, 0x72ac60c3, 0xcce28690, 0x8a826e80, + 0x1e7f8784, 0x74d9e5b8, 0x65e073d8, 0x55014922 }} }; + + const bn256 s_expected[1] = { + {{ 0x1582b85f, 0xac3ba390, 0x70391ec6, 0x6bb4f91c, + 0xf0f55bd2, 0x24be5b59, 0x43415165, 0x0b107a8e }} }; + + r = (bn256 *)r_s; + s = (bn256 *)(r_s+32); + + sha512 ((uint8_t *)sk, sizeof (bn256), hash); + hash[0] &= 248; + hash[31] &= 127; + hash[31] |= 64; + memcpy (a, hash, sizeof (bn256)); + + eddsa_public_key_25519 (pk, a); + eddsa_sign_25519 ((const uint8_t *)"", 0, r_s, a, hash+32, pk); + + if (memcmp (r, r_expected, sizeof (bn256)) != 0 + || memcmp (s, s_expected, sizeof (bn256)) != 0) + { + print_bn256 (r); + print_bn256 (s); + return 1; + } +#else + ac a0001[1], a0010[1], a0100[1], a1000[1]; + ptc a[1]; + + memcpy (a, G, sizeof (ptc)); + ptc_to_ac_25519 (a0001, a); + compute_and_print_table (a0001, a0010, a0100, a1000); + + memcpy (a, a0001, sizeof (ac)); + memset (a->z, 0, sizeof (bn256)); + a->z->word[0] = 1; + power_2 (a0001, a, 21); + compute_and_print_table (a0001, a0010, a0100, a1000); + + memcpy (a, a0001, sizeof (ac)); + memset (a->z, 0, sizeof (bn256)); + a->z->word[0] = 1; + power_2 (a0001, a, 21); + compute_and_print_table (a0001, a0010, a0100, a1000); +#endif + + return 0; +} +#endif diff --git a/ecc-ed448.c b/ecc-ed448.c new file mode 100644 index 0000000..70141c4 --- /dev/null +++ b/ecc-ed448.c @@ -0,0 +1,824 @@ +/* -*- coding: utf-8 -*- + * ecc-ed448.c - Elliptic curve computation for + * the twisted Edwards curve: -x^2 + y^2 = 1 + d*x^2*y^2 + * d = -39081 + * + * Copyright (C) 2021 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 . + * + */ + +/* + * IMPLEMENTATION NOTE + * + * (0) We assume that the processor has no cache, nor branch target + * prediction. Thus, we don't avoid indexing by secret value. + * We don't avoid conditional jump if both cases have same timing, + * either. + * + * (1) We use fixed base comb multiplication. Scalar is 448-bit. + * We use two tables, and a table has 16 points. + * Window size W = 4-bit, E = 56. + * + */ + +#include +#include + +#include "p448.h" +#include "shake256.h" + + +#define C_WORDS 7 +#define BN448_WORDS 14 +#define BN690_WORDS 22 +#define BN896_WORDS 28 +#define BN912_WORDS 29 /* 28.5 */ + +typedef struct bn448 { + uint32_t word[ BN448_WORDS ]; /* Little endian */ +} bn448; + +typedef struct bn896 { + uint32_t word[ BN896_WORDS ]; /* Little endian */ +} bn896; + +typedef struct bn912 { + uint32_t word[ BN912_WORDS ]; /* Little endian */ +} bn912; + +static const bn448 M[1] = {{{ + 0xab5844f3, 0x2378c292, 0x8dc58f55, 0x216cc272, + 0xaed63690, 0xc44edb49, 0x7cca23e9, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0x3fffffff +}}}; + +static const uint32_t C[C_WORDS] = { + 0x54a7bb0d, 0xdc873d6d, 0x723a70aa, 0xde933d8d, + 0x5129c96f, 0x3bb124b6, 0x8335dc16 +}; + + +static uint32_t +bn448_add (bn448 *X, const bn448 *A, const bn448 *B) +{ + int i; + uint32_t v; + uint32_t carry = 0; + uint32_t *px; + const uint32_t *pa, *pb; + + px = X->word; + pa = A->word; + pb = B->word; + + for (i = 0; i < BN448_WORDS; i++) + { + v = *pb; + *px = *pa + carry; + carry = (*px < carry); + *px += v; + carry += (*px < v); + px++; + pa++; + pb++; + } + + return carry; +} + +static uint32_t +bn448_sub (bn448 *X, const bn448 *A, const bn448 *B) +{ + int i; + uint32_t v; + uint32_t borrow = 0; + uint32_t *px; + const uint32_t *pa, *pb; + + px = X->word; + pa = A->word; + pb = B->word; + + for (i = 0; i < BN448_WORDS; i++) + { + uint32_t borrow0 = (*pa < borrow); + + v = *pb; + *px = *pa - borrow; + borrow = (*px < v) + borrow0; + *px -= v; + px++; + pa++; + pb++; + } + + return borrow; +} + + +static void +bnX_mul_C (uint32_t *r, const uint32_t *q, int q_size) +{ + int i, j, k; + int i_beg, i_end; + uint32_t r0, r1, r2; + + r0 = r1 = r2 = 0; + for (k = 0; k <= q_size + C_WORDS - 2; k++) + { + if (q_size < C_WORDS) + if (k < q_size) + { + i_beg = 0; + i_end = k; + } + else + { + i_beg = k - q_size + 1; + i_end = k; + if (i_end > C_WORDS - 1) + i_end = C_WORDS - 1; + } + else + if (k < C_WORDS) + { + i_beg = 0; + i_end = k; + } + else + { + i_beg = k - C_WORDS + 1; + i_end = k; + if (i_end > q_size - 1) + i_end = q_size - 1; + } + + for (i = i_beg; i <= i_end; i++) + { + uint64_t uv; + uint32_t u, v; + uint32_t carry; + + j = k - i; + if (q_size < C_WORDS) + uv = ((uint64_t)q[j])*((uint64_t)C[i]); + else + uv = ((uint64_t)q[i])*((uint64_t)C[j]); + v = uv; + u = (uv >> 32); + r0 += v; + carry = (r0 < v); + r1 += carry; + carry = (r1 < carry); + r1 += u; + carry += (r1 < u); + r2 += carry; + } + + r[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + r[k] = r0; +} + +/* X <= X + A when COND!=0 */ +/* X <= X when COND==0 */ +static void +bn448_add_cond (bn448 *X, const bn448 *A, int cond) +{ + int i; + uint32_t v; + uint32_t carry = 0; + uint32_t *px; + const uint32_t *pa; + uint32_t mask = -(!!cond); + + px = X->word; + pa = A->word; + + for (i = 0; i < BN448_WORDS; i++) + { + v = *px; + *px = (*pa & mask) + carry; + carry = (*px < carry); + *px += v; + carry += (*px < v); + px++; + pa++; + } +} + + +/* X <= X + A mod M */ +static void +bn448_addm (bn448 *X, const bn448 *A) +{ + uint32_t borrow; + + bn448_add (X, X, A); + borrow = bn448_sub (X, X, M); + bn448_add_cond (X, M, borrow); +} + +/** + * @brief R = A mod M (using M=2^446-C) (Barret reduction) + * + * See HAC 14.47. + */ +void +mod_reduce_M (bn448 *R, const bn912 *A) +{ + uint32_t q[BN448_WORDS+1]; + uint32_t tmp[BN690_WORDS]; + bn448 r[1]; + uint32_t carry, next_carry; + int i; + + /* Q = A / 2^446 *//* 466-bit */ + /* Upper half of A->word[28] must be zero. */ + q[14] = (A->word[28] << 2) | (A->word[27] >> 30); + carry = A->word[27] & 0x3fffffff; + for (i = BN448_WORDS - 1; i >= 0; i--) + { + next_carry = A->word[i+13] & 0x3fffffff; + q[i] = (A->word[i+13] >> 30) | (carry << 2); + carry = next_carry; + } + memcpy (R, A, sizeof (bn448)); + R->word[13] &= 0x3fffffff; + + /* Q_size: 15 *//* 466-bit */ + bnX_mul_C (tmp, q, 15); /* TMP = Q*C *//* 690-bit */ + /* Q = tmp / 2^446 *//* 244-bit */ + carry = tmp[21]; + for (i = 7; i >= 0; i--) + { + next_carry = tmp[i+13] & 0x3fffffff; + q[i] = (tmp[i+13] >> 30) | (carry << 2); + carry = next_carry; + } + /* R' = tmp % 2^446 */ + memcpy (r, tmp, sizeof (bn448)); + r->word[13] &= 0x3fffffff; + /* R += R' */ + bn448_addm (R, r); + + /* Q_size: 8 *//* 244-bit */ + bnX_mul_C (tmp, q, 8); /* TMP = Q*C *//* 468-bit */ + /* Q = tmp / 2^446 *//* 22-bit */ + carry = tmp[14]; + q[0] = (tmp[13] >> 30) | (carry << 2); + /* R' = tmp % 2^446 */ + memcpy (r, tmp, sizeof (bn448)); + r->word[13] &= 0x3fffffff; + /* R += R' */ + bn448_addm (R, r); + + /* Q_size: 1 */ + bnX_mul_C (tmp, q, 1); /* TMP = Q*C *//* 246-bit */ + /* R' = tmp % 2^446 */ + memset (((uint8_t *)r)+(sizeof (uint32_t)*8), 0, sizeof (uint32_t)*6); + memcpy (r, tmp, sizeof (uint32_t)*8); + /* R += R' */ + bn448_addm (R, r); +} + + +static void +bn448_mul (bn896 *X, const bn448 *A, const bn448 *B) +{ + int i, j, k; + int i_beg, i_end; + uint32_t r0, r1, r2; + + r0 = r1 = r2 = 0; + for (k = 0; k <= (BN448_WORDS - 1)*2; k++) + { + if (k < BN448_WORDS) + { + i_beg = 0; + i_end = k; + } + else + { + i_beg = k - BN448_WORDS + 1; + i_end = BN448_WORDS - 1; + } + + for (i = i_beg; i <= i_end; i++) + { + uint64_t uv; + uint32_t u, v; + uint32_t carry; + + j = k - i; + + uv = ((uint64_t )A->word[i])*((uint64_t )B->word[j]); + v = uv; + u = (uv >> 32); + r0 += v; + carry = (r0 < v); + r1 += carry; + carry = (r1 < carry); + r1 += u; + carry += (r1 < u); + r2 += carry; + } + + X->word[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + X->word[k] = r0; +} + +static const p448_t nGx0[16] = { + { { 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { { 0x070cc05e, 0x026a82bc, 0x00938e26, 0x080e18b0, + 0x0511433b, 0x0f72ab66, 0x0412ae1a, 0x0a3d3a46, + 0x0a6de324, 0x00f1767e, 0x04657047, 0x036da9e1, + 0x05a622bf, 0x0ed221d1, 0x066bed0d, 0x04f1970c } }, + { { 0x0464238e, 0x00079817, 0x00d381ca, 0x02110302, + 0x0d9f01b5, 0x01cc4c6e, 0x05a131b1, 0x05e35dc5, + 0x006944eb, 0x0b61848d, 0x029631a3, 0x083792a0, + 0x0afca0dd, 0x0be1017f, 0x0782fcbb, 0x070aaa01 } }, + { { 0x0e7661f9, 0x0b2f9f62, 0x009fae89, 0x03b99803, + 0x066014d2, 0x067900ef, 0x06556c10, 0x0c8eacf3, + 0x0ad4a82e, 0x020a44d0, 0x00572f1c, 0x0e7819e7, + 0x0fd08cdf, 0x0c0ed140, 0x09aee1da, 0x0a16934a } }, + { { 0x091780c7, 0x0a7ea989, 0x0d2476b6, 0x004e4ecc, + 0x0c494b68, 0x00af9f58, 0x0dee64fd, 0x0e0f269f, + 0x0021bd26, 0x085a61f6, 0x0b5d284b, 0x0c265c35, + 0x03775afd, 0x058755ea, 0x02ecf2c6, 0x0617f174 } }, + { { 0x067f4947, 0x0dbf4eb6, 0x0b8716d9, 0x02206a2a, + 0x0e7cad5a, 0x04a148b0, 0x0e483133, 0x0fbf12cd, + 0x0c6458f7, 0x0e022d5a, 0x01b7e39d, 0x0a60afe6, + 0x05a5208c, 0x0c62f458, 0x03311553, 0x0a08a4c3 } }, + { { 0x0054a90d, 0x0ad5dc54, 0x00ac9fd6, 0x097f2af4, + 0x0f4ddbc7, 0x01b0f7b3, 0x0324ce0b, 0x01d5d092, + 0x0cd2798f, 0x08cb96e2, 0x0957bc39, 0x0bd045b5, + 0x0f76fbfb, 0x046308a9, 0x0ef679ce, 0x0c86d628 } }, + { { 0x0d5d9262, 0x0f251539, 0x0711a956, 0x0240708f, + 0x04a0b0bc, 0x07f7e4dd, 0x055b70a8, 0x065dd24f, + 0x07ef8979, 0x0e83cec7, 0x09589db8, 0x0f1db2d1, + 0x09d93037, 0x0fcc7e8a, 0x04e0b8f4, 0x0cb99f0b } }, + { { 0x04acea57, 0x06f24100, 0x0da68597, 0x0dace1c6, + 0x050ce77f, 0x0ea7dd41, 0x01585884, 0x01aecb84, + 0x0ea4a85c, 0x092ff208, 0x088eebd2, 0x0de9433c, + 0x03f4d289, 0x053cd318, 0x026539af, 0x03970858 } }, + { { 0x0d229665, 0x06e9fd2b, 0x0878dd51, 0x049345aa, + 0x0f45bacf, 0x0ccde72a, 0x0be16b6f, 0x0bc249d1, + 0x0448a61d, 0x0a25bae9, 0x0d773878, 0x0c93b6ea, + 0x02cda508, 0x055f708a, 0x08cf49e6, 0x0fa56852 } }, + { { 0x093bfef9, 0x07bec8db, 0x0fafda3d, 0x0ce4dcdc, + 0x06f62ed7, 0x0a75c872, 0x07b3dadd, 0x0c39ac92, + 0x0f926d90, 0x0ae1b8d1, 0x048da0a9, 0x0d7dbeca, + 0x02a52b3b, 0x0ec13f74, 0x0d4c5ce2, 0x02071cee } }, + { { 0x05a644a6, 0x0e56b0a9, 0x0be6360b, 0x01ecf90e, + 0x023b73a8, 0x0c3bbcf7, 0x0292054b, 0x05417d25, + 0x07b91b46, 0x0ca1ea05, 0x07ea6c44, 0x01560b21, + 0x04f12989, 0x0463cd2a, 0x03d7e086, 0x0092781c } }, + { { 0x0d59796d, 0x0ce08d7e, 0x055bc822, 0x0e464443, + 0x0d243cc4, 0x0542002f, 0x098259b3, 0x044fc576, + 0x012781de, 0x08650550, 0x0055e6b4, 0x0137f762, + 0x0fbf007e, 0x0a391ccc, 0x039fe6f6, 0x0a9c9ad3 } }, + { { 0x01ca2765, 0x0ccddbb0, 0x0563b46c, 0x05d18f4c, + 0x0462647e, 0x02ff700d, 0x0822dc83, 0x0670b143, + 0x00013963, 0x01627d78, 0x055dbfb9, 0x0435f413, + 0x063d41e8, 0x066c95cd, 0x0c797bba, 0x08e27dfb } }, + { { 0x03da4531, 0x01ff4dd6, 0x0cd39a3c, 0x02d0de4c, + 0x0bc9da8d, 0x0003561e, 0x033e1e9a, 0x001eea00, + 0x078bf710, 0x05458c53, 0x0f56338e, 0x069043ab, + 0x061ffba0, 0x0637cf41, 0x039fb551, 0x0fc09757 } }, + { { 0x0256141f, 0x0f1e0e38, 0x00ab2673, 0x0efd5f47, + 0x0af4a4af, 0x0b749116, 0x0ac6540b, 0x04242f82, + 0x0abaf195, 0x0b26730c, 0x0d06842d, 0x076fbe60, + 0x0580cad8, 0x02613d91, 0x0b568ae0, 0x0c2e5b1d } } +}; + +static const p448_t nGy0[16] = { + { { 0x00000001, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { { 0x0230fa14, 0x008795bf, 0x07c8ad98, 0x0132c4ed, + 0x09c4fdbd, 0x01ce67c3, 0x073ad3ff, 0x005a0c2d, + 0x07789c1e, 0x0a398408, 0x0a73736c, 0x0c7624be, + 0x003756c9, 0x02488762, 0x016eb6bc, 0x0693f467 } }, + { { 0x099945e7, 0x0c63b7a0, 0x0c4486c1, 0x0e9164ec, + 0x0885f2c1, 0x0b133e35, 0x0c99ae02, 0x0186f0d3, + 0x02bf53e6, 0x02fca492, 0x048a02bc, 0x0f922aa2, + 0x00dd3dca, 0x04fe6490, 0x0f6a8207, 0x0e8c313f } }, + { { 0x0579a4e2, 0x0a1ffe8b, 0x0ce472b4, 0x01d006b3, + 0x089def96, 0x07c8f689, 0x0a32ae93, 0x079d7bd1, + 0x03a02760, 0x0ebb4776, 0x05b4c55e, 0x019b3c6c, + 0x07da436f, 0x066ff782, 0x0659536d, 0x0ee40076 } }, + { { 0x05ec556a, 0x050109e2, 0x0fd57e39, 0x0235366b, + 0x044b6b2e, 0x07b3c976, 0x0b2b7b9c, 0x0f7f9e82, + 0x00ec6409, 0x0b6196ab, 0x00a20d9e, 0x088f1d16, + 0x0586f761, 0x0e3be3b4, 0x0e26395d, 0x09983c26 } }, + { { 0x0fab8e56, 0x0ded288e, 0x057277e6, 0x0a4e6f4e, + 0x0e949681, 0x0a2a4c4f, 0x0721fdb3, 0x0508a46c, + 0x0fb44de2, 0x0f98049e, 0x02fb0f31, 0x071f3724, + 0x09067763, 0x0d3fbbb3, 0x0a83faaa, 0x0696ec4a } }, + { { 0x07a04bb0, 0x0f52ae70, 0x0ae14cdb, 0x0784d14b, + 0x034acc37, 0x09aa3869, 0x09703f7b, 0x08f79c87, + 0x0264026c, 0x0859cde5, 0x0486b035, 0x0b2a45f7, + 0x03d5144b, 0x0809740f, 0x0416dc87, 0x0dcf324d } }, + { { 0x0a0c8bc7, 0x04125cec, 0x0eac3f20, 0x0d30ff7e, + 0x029ad678, 0x06901f05, 0x04805ff1, 0x033c307d, + 0x049d6a79, 0x080f0710, 0x02dece6c, 0x0d1ba22b, + 0x0778cccb, 0x01692a0b, 0x02df78fb, 0x0f8c02d3 } }, + { { 0x0b827d87, 0x04b57599, 0x03d77638, 0x0dc82ac0, + 0x052f6e61, 0x06943366, 0x0ad5e8a6, 0x0b8fc4b0, + 0x0f388642, 0x01b6f7dc, 0x0a74dd57, 0x06f24533, + 0x041750cf, 0x0c669378, 0x028a37af, 0x006757eb } }, + { { 0x080128d5, 0x0ef186a8, 0x04a54843, 0x01ceb43b, + 0x045be148, 0x0c112a42, 0x01ac9412, 0x0621b93a, + 0x05e16552, 0x0a2ca24f, 0x086301c0, 0x0cf3fecf, + 0x05c2e2e0, 0x05108805, 0x09e9d8ab, 0x0d2ba341 } }, + { { 0x02138911, 0x0f0d3e4c, 0x0c1a371b, 0x062382ce, + 0x05b3a392, 0x09d954e7, 0x0517d2a1, 0x0047d71a, + 0x07f70073, 0x09cd1733, 0x0efc3aea, 0x0549d0d1, + 0x0df78457, 0x0666e074, 0x0a48e084, 0x0f67e924 } }, + { { 0x0b3114fe, 0x073bec50, 0x0e8b6172, 0x01c5e7b6, + 0x0e896bcc, 0x0a1c3ae1, 0x0bcd8cab, 0x0bb3f870, + 0x07e9fa9d, 0x0eea8546, 0x0042e2cf, 0x056431f0, + 0x0469e8d2, 0x08eb9b9c, 0x0a9adf2c, 0x06856458 } }, + { { 0x07b2cfdd, 0x01855530, 0x073bd43a, 0x01816246, + 0x08897062, 0x02f82d12, 0x03563816, 0x06517857, + 0x0394a8c7, 0x0529bf2e, 0x075a3141, 0x0660c4f2, + 0x018e5a16, 0x0787c8ad, 0x045b679e, 0x0abaec01 } }, + { { 0x06d87d9e, 0x07c9fabb, 0x03b2a99d, 0x0673b28a, + 0x068816ee, 0x0efb205e, 0x0dd5e3d5, 0x03d21920, + 0x07544f4d, 0x085f40c2, 0x06fb538d, 0x057d045b, + 0x05470e4e, 0x028a93c3, 0x063adfd4, 0x0d1cf7a5 } }, + { { 0x06699694, 0x0c83c837, 0x0386dade, 0x0621103f, + 0x0f247dc3, 0x06058f43, 0x0aec07c3, 0x0b1ac29a, + 0x0bde5d50, 0x06e35e33, 0x078fd31c, 0x0516263c, + 0x00a9d127, 0x04a13379, 0x078bec6e, 0x0f39316a } }, + { { 0x0e26ea19, 0x05ecf40e, 0x03bdf1b5, 0x07c284a0, + 0x06f461fa, 0x08393462, 0x064a69aa, 0x07d4f6a5, + 0x06e88ea4, 0x023059e9, 0x0f92bd0b, 0x0c4a8035, + 0x0c5c44a2, 0x0fccec22, 0x07f57ea1, 0x0598207c } } +}; + +static const p448_t nGx1[16] = { + { { 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { { 0x0528af6f, 0x078c6f13, 0x094b74d9, 0x00001fe2, + 0x001aab44, 0x0ae77425, 0x0ef0039c, 0x07cbe937, + 0x00fa2a67, 0x0af3e4f0, 0x0da1378e, 0x0e28175f, + 0x08ccd90e, 0x072adeed, 0x000af22f, 0x016a8ce1 } }, + { { 0x0fa0459e, 0x0f31f53f, 0x0315cd6b, 0x0f8742a1, + 0x0ae64e97, 0x0abe2f50, 0x09b9da48, 0x0bd78741, + 0x051e526e, 0x04521a33, 0x0e10ba45, 0x0fa05935, + 0x0e8f903c, 0x05c947e1, 0x05a754ee, 0x00aa47d1 } }, + { { 0x00d9a33b, 0x0284f76f, 0x0e4d41e7, 0x09461141, + 0x0cc79344, 0x015371b9, 0x03dd8bdd, 0x0173f667, + 0x053f866b, 0x0c0d0f83, 0x030b45ea, 0x08b7d59b, + 0x0044dc82, 0x02b4cdec, 0x094fa772, 0x0e245b21 } }, + { { 0x04ddc8a8, 0x02fe182d, 0x0ac056bf, 0x088d6e79, + 0x00e41e4e, 0x0c3ff2d1, 0x02c3679f, 0x032ec7f9, + 0x04e61051, 0x03561f09, 0x06c6250a, 0x04553f5a, + 0x0dd25c5b, 0x02b765ef, 0x06a1cd7f, 0x0e3a40a2 } }, + { { 0x05e1f4b2, 0x0e9485c4, 0x070a1e6b, 0x01d85e53, + 0x077730a7, 0x0db61fa9, 0x050d418e, 0x0201a6bd, + 0x02774433, 0x0e78a475, 0x0622ea3a, 0x016424e5, + 0x0d5b9631, 0x01c7734d, 0x0f5064f2, 0x0c7586d3 } }, + { { 0x0af6151d, 0x0c3ed603, 0x0aa19b93, 0x05a5e4a6, + 0x0536ff03, 0x07e465ce, 0x0b0be710, 0x0bbb36bf, + 0x09249bff, 0x0d15454d, 0x03736654, 0x0ba934d9, + 0x0370dc86, 0x0675c04e, 0x0d86eb3b, 0x06cd21cb } }, + { { 0x030c7ce7, 0x04217221, 0x0e9dba4d, 0x0ec314cd, + 0x05439062, 0x0d7196cd, 0x0dd96166, 0x0b8295cd, + 0x0c15796f, 0x0c767da7, 0x00ab2036, 0x059120e7, + 0x0b7d07ec, 0x0e1562a9, 0x0231cdd9, 0x07d5c89f } }, + { { 0x01a82a12, 0x091a5884, 0x080f3a62, 0x0a754175, + 0x0f73417a, 0x0399009f, 0x00a8c5cd, 0x02db1fb9, + 0x0c046d51, 0x082c8912, 0x08f18274, 0x00a3f577, + 0x026ccae2, 0x02ad0ede, 0x08a4e9c2, 0x07d6bd8b } }, + { { 0x0afd28b4, 0x02b7b7be, 0x0298d67e, 0x0e834401, + 0x04b11493, 0x0e070d60, 0x063ce6fb, 0x04b67725, + 0x0a0cfb04, 0x0d3a0f67, 0x0f08f1b2, 0x0debe82e, + 0x0b402b9e, 0x07114482, 0x0b307043, 0x0af532e6 } }, + { { 0x049ab457, 0x0f6483c2, 0x0818ac81, 0x05aced0a, + 0x0a900e3a, 0x080916bc, 0x02948675, 0x0145adb9, + 0x0d8b7821, 0x04fe2b0e, 0x0b1a62cc, 0x0a9e1bce, + 0x096c2408, 0x048f1f80, 0x0ac552fe, 0x0d17e7a0 } }, + { { 0x08ce3344, 0x0ea48915, 0x0434ae70, 0x0c6cf019, + 0x0c48f5d2, 0x089d3c0f, 0x0ca7aa7e, 0x0c550a00, + 0x017fb3ab, 0x09f8b49f, 0x024844a0, 0x0366a6d5, + 0x0ceb4a83, 0x0f1f5bf4, 0x03b782f0, 0x099fd2f7 } }, + { { 0x052daf76, 0x038fbbd7, 0x0bced01d, 0x0ffb0a8b, + 0x07c6bd6c, 0x0dc3b0ff, 0x041d595c, 0x03814ee7, + 0x01941d44, 0x0e1f8343, 0x0f89b18d, 0x0c083601, + 0x0e52ec62, 0x0fc338ff, 0x0e971788, 0x04601008 } }, + { { 0x0add862e, 0x0e8c3a8e, 0x033cea23, 0x06d00cf1, + 0x0cdc039a, 0x0d7bda40, 0x0e0a2ac3, 0x04750dcb, + 0x0bec4388, 0x0a1bb0bc, 0x0d20c0f9, 0x077a4a7b, + 0x0b9e1f0b, 0x02ff072d, 0x07bd3e06, 0x0bd796d7 } }, + { { 0x08e321b4, 0x08757de1, 0x0151699c, 0x06ba6bd4, + 0x0a156df0, 0x02ec93a1, 0x0dad4f9e, 0x04e547c5, + 0x0ee9310d, 0x01dcc8bf, 0x0f7b5016, 0x0355f710, + 0x0ce8f36d, 0x0389d7a9, 0x02b8056d, 0x0ff83804 } }, + { { 0x060f6dcf, 0x0dcaa234, 0x0285b23d, 0x0ec8d56f, + 0x083dac2b, 0x01042255, 0x08e1bed7, 0x0c3fe788, + 0x0832c0af, 0x07258b0e, 0x02b2affc, 0x0a901bdb, + 0x0038f36e, 0x01a28d5f, 0x0dbb618d, 0x080838af } } +}; + +static const p448_t nGy1[16] = { + { { 0x00000001, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { { 0x0cbf63dd, 0x069fae17, 0x09e39e26, 0x06786172, + 0x0f827a18, 0x0e92b3d5, 0x08403682, 0x04d75e41, + 0x09056a79, 0x001a4fd9, 0x020008f5, 0x089efb2d, + 0x0b78ff15, 0x0a2f6918, 0x0a3437f5, 0x0f41c870 } }, + { { 0x0d814825, 0x0b2849ef, 0x05c9968d, 0x09c2a5d2, + 0x004e634c, 0x024dbb26, 0x0db38194, 0x033f3a4c, + 0x0c8a2b6b, 0x0e04f609, 0x0abbbfdb, 0x0caefd8e, + 0x0404498b, 0x0683119a, 0x08b21cbd, 0x024ab7a9 } }, + { { 0x0ede77b3, 0x0043b728, 0x0a043f1d, 0x003cf736, + 0x0ab4e700, 0x0d95a612, 0x0c8fe17c, 0x05ccaac2, + 0x0177bd28, 0x0dc3bd14, 0x05360c86, 0x0b3d5c96, + 0x04ec7e48, 0x01880c26, 0x04bb47c6, 0x0fd5dba8 } }, + { { 0x05d821dd, 0x0b27309b, 0x0c2c17ca, 0x0950fb8d, + 0x08fb0d4c, 0x0feed015, 0x0f550179, 0x0762c479, + 0x0e095840, 0x0306cf44, 0x0d379e66, 0x084b413a, + 0x0bb2e4f1, 0x0d6e5d5a, 0x094b085d, 0x08bc12b7 } }, + { { 0x0b8a16f6, 0x0b4dacd9, 0x003afc96, 0x0000b9b9, + 0x03f19cbf, 0x0ab930b8, 0x0b077171, 0x0541f92e, + 0x019baa42, 0x08758d9c, 0x0fea31a2, 0x0299b935, + 0x081d9e24, 0x03bc7232, 0x09d91676, 0x0fc081c2 } }, + { { 0x02f05282, 0x04ca6fb6, 0x02e9801e, 0x051928b6, + 0x0b609dcb, 0x0c6f37b6, 0x06e32803, 0x06617fd7, + 0x0166f0bb, 0x07d1bffb, 0x0ac137d4, 0x0bfdebdd, + 0x0df8f3cb, 0x0d558ac9, 0x08fabbb4, 0x00217c7c } }, + { { 0x0f5d72ad, 0x04c71050, 0x008880dd, 0x093209a0, + 0x07c3fef0, 0x0e1857c5, 0x022b21d2, 0x07584709, + 0x0e52fe8a, 0x039aeffa, 0x0a384e66, 0x0bd7c58b, + 0x0bfbbfe2, 0x022fc035, 0x0506e447, 0x0bc96411 } }, + { { 0x04b3de44, 0x0aa0d797, 0x096ac9bb, 0x0f8658b9, + 0x05f6c334, 0x031e7be2, 0x04df12c9, 0x023836ce, + 0x059eb5c9, 0x0029027b, 0x05b8649d, 0x02f22531, + 0x0d907162, 0x0a0fdf03, 0x09e80226, 0x0101d9df } }, + { { 0x05237b19, 0x00d0c997, 0x04a2bcdb, 0x0692bae3, + 0x0805b9e0, 0x0a0d3a98, 0x08c7dd07, 0x0a253f11, + 0x0e19738e, 0x0c0794d0, 0x019812a1, 0x041a8569, + 0x025d360c, 0x078e4ebd, 0x07ee8567, 0x0f02e9d6 } }, + { { 0x00548584, 0x0bb1ee61, 0x0549030f, 0x0026e17a, + 0x0b4c52fb, 0x0a4e4e61, 0x0a1ca8f9, 0x0339754c, + 0x0ee8806f, 0x03d2a45e, 0x0e2028fa, 0x03c44782, + 0x0072e42b, 0x03328ae4, 0x0d21c91f, 0x07e98738 } }, + { { 0x0b9618ad, 0x07f781fa, 0x09cf7662, 0x0855bfab, + 0x0c316a14, 0x0d98f9ff, 0x07b3046a, 0x0109f273, + 0x042cecfe, 0x0cc21cdc, 0x05be5a36, 0x05236b10, + 0x058a0700, 0x0ff2cf95, 0x005ad57d, 0x09cbf152 } }, + { { 0x0ebe90d2, 0x049f0de4, 0x02243779, 0x0221424d, + 0x09051808, 0x0b52f44b, 0x0bb9c3fb, 0x0a5d64e3, + 0x07690354, 0x0d8bf65d, 0x0bc06e3f, 0x05d039f6, + 0x033a3443, 0x04e11c79, 0x04147a83, 0x06a7e42c } }, + { { 0x082e4773, 0x00d276be, 0x0e1b9057, 0x0e9dd324, + 0x0369bc97, 0x0b3181ef, 0x002f04fa, 0x01d08726, + 0x07c2c5d3, 0x0bf49cbf, 0x09ecb59b, 0x098eae7e, + 0x02e09293, 0x052e08b6, 0x0c40f3e6, 0x04096c37 } }, + { { 0x06074e1f, 0x07bc94ed, 0x0790175a, 0x040b2a81, + 0x0e307782, 0x0b7958e8, 0x089ff273, 0x07ed27c6, + 0x026db869, 0x0b6a32f8, 0x03d2e15c, 0x00446ef9, + 0x0777e1ac, 0x0492d2de, 0x01b69b63, 0x06b8dbab } }, + { { 0x07e98bea, 0x0e7c9e7a, 0x02e17335, 0x09302c64, + 0x0acc1e93, 0x05dcdcd8, 0x04d90baa, 0x05982bae, + 0x0c686ed6, 0x07c08c6c, 0x0fce2c72, 0x04dd3cce, + 0x01dc8f12, 0x029ca465, 0x0161cbd7, 0x09324c0a } } +}; + +static void +compute_kG_448 (uint8_t *out, const uint32_t k[16]) +{ + int i; + p448_t x0[1], y0[1], z0[1]; /* P0 */ + p448_t tmp0[1], tmp1[1]; + + /* P0 <= O */ + memset (x0, 0, sizeof (p448_t)); + memset (y0, 0, sizeof (p448_t)); + memset (z0, 0, sizeof (p448_t)); + y0->limb[0] = 1; + z0->limb[0] = 1; + + for (i = 0; i < 56; i++) + { + p448_t b[1], c[1], d[1]; + p448_t e[1], f[1], g[1], h[1]; + int index0, index1; + + if (i < 28) + { + int i0 = 28 - i - 1; + + index0 = ((k[1] >> i0) & 1) | (((k[5] >> i0) & 1)<<1) + | (((k[ 9] >> i0) & 1)<<2) | (((k[13] >> i0) & 1)<<3); + index1 = ((k[3] >> i0) & 1) | (((k[7] >> i0) & 1)<<1) + | (((k[11] >> i0) & 1)<<2) | (((k[15] >> i0) & 1)<<3); + } + else + { + int i0 = 56 - i - 1; + + index0 = ((k[0] >> i0) & 1) | (((k[4] >> i0) & 1)<<1) + | (((k[ 8] >> i0) & 1)<<2) | (((k[12] >> i0) & 1)<<3); + index1 = ((k[2] >> i0) & 1) | (((k[6] >> i0) & 1)<<1) + | (((k[10] >> i0) & 1)<<2) | (((k[14] >> i0) & 1)<<3); + } + + /* Point double P0' <= P0 + P0 */ + p448_add (tmp0, x0, y0); + p448_sqr (b, tmp0); + p448_sqr (c, x0); + p448_sqr (d, y0); + p448_add (e, c, d); + p448_sqr (h, z0); + p448_add (tmp0, h, h); + p448_sub (tmp1, e, tmp0); + p448_sub (tmp0, b, e); + p448_mul (x0, tmp0, tmp1); + p448_sub (tmp0, c, d); + p448_mul (y0, e, tmp0); + p448_mul (z0, e, tmp1); + /* + B = (X1+Y1)^2 + C = X1^2 + D = Y1^2 + E = C+D + H = Z1^2 + J = E-2*H + X3 = (B-E)*J + Y3 = E*(C-D) + Z3 = E*J + */ + + /* Point addition P0' <= P0 + [v0(index0)]G */ + p448_sqr (b, z0); + p448_mul (c, x0, &nGx0[index0]); + p448_mul (d, y0, &nGy0[index0]); + p448_mul (tmp0, c, d); + p448_mul_39081 (e, tmp0); + p448_add (f, b, e); + p448_sub (g, b, e); + p448_add (tmp0, x0, y0); + p448_add (tmp1, &nGx0[index0], &nGy0[index0]); + p448_mul (h, tmp0, tmp1); + p448_sub (tmp0, h, c); + p448_sub (tmp1, tmp0, d); + p448_mul (tmp0, f, tmp1); + p448_mul (x0, z0, tmp0); + p448_sub (tmp0, d, c); + p448_mul (tmp1, g, tmp0); + p448_mul (y0, z0, tmp1); + p448_mul (z0, f, g); + /* + A = Z1*Z2 + B = A^2 + C = X1*X2 + D = Y1*Y2 + E = d*C*D + F = B-E + G = B+E + H = (X1+Y1)*(X2+Y2) + X3 = A*F*(H-C-D) + Y3 = A*G*(D-C) + Z3 = F*G + */ + /* Point addition P0' <= P0 + [v1(index1)]G */ + p448_sqr (b, z0); + p448_mul (c, x0, &nGx1[index1]); + p448_mul (d, y0, &nGy1[index1]); + p448_mul (tmp0, c, d); + p448_mul_39081 (e, tmp0); + p448_add (f, b, e); + p448_sub (g, b, e); + p448_add (tmp0, x0, y0); + p448_add (tmp1, &nGx1[index1], &nGy1[index1]); + p448_mul (h, tmp0, tmp1); + p448_sub (tmp0, h, c); + p448_sub (tmp1, tmp0, d); + p448_mul (tmp0, f, tmp1); + p448_mul (x0, z0, tmp0); + p448_sub (tmp0, d, c); + p448_mul (tmp1, g, tmp0); + p448_mul (y0, z0, tmp1); + p448_mul (z0, f, g); + } + + /* Convert to affine coordinate. */ + p448_inv (tmp0, z0); + p448_mul (tmp1, x0, tmp0); + p448_serialize (out, tmp1); + /* EdDSA encoding. */ + out[56] = (out[0] & 1) << 7; + p448_mul (tmp1, y0, tmp0); + p448_serialize (out, tmp1); +} + + +#define SEED_SIZE 57 + +#define DOM448 (const uint8_t *)"SigEd448" +#define DOM448_LEN 8 + +int +ed448_sign (uint8_t *out, const uint8_t *input, unsigned int ilen, + const uint8_t *a_in, const uint8_t *seed, const uint8_t *pk) +{ + bn448 a[1], k[1], s[1]; + shake_context ctx; + const unsigned char x_olen[2] = { 0, 0 }; + uint32_t hash[BN912_WORDS]; + uint8_t r[57]; + uint32_t carry, borrow; + p448_t k_redundant[1]; + + memset (hash, 0, sizeof (hash)); + + memcpy (a, a_in, sizeof (bn448)); + a->word[13] |= 0x80000000; + a->word[0] &= ~3; + + shake256_start (&ctx); + shake256_update (&ctx, DOM448, DOM448_LEN); + shake256_update (&ctx, x_olen, 2); + shake256_update (&ctx, seed, 57); + shake256_update (&ctx, input, ilen); + shake256_finish (&ctx, (uint8_t *)hash, 2*57); + + mod_reduce_M (k, (const bn912 *)hash); + p448_deserialize (k_redundant, (uint8_t *)k); + compute_kG_448 (r, (uint32_t *)k_redundant); + + shake256_start (&ctx); + shake256_update (&ctx, DOM448, DOM448_LEN); + shake256_update (&ctx, x_olen, 2); + shake256_update (&ctx, r, 57); + shake256_update (&ctx, pk, 57); + shake256_update (&ctx, input, ilen); + shake256_finish (&ctx, (uint8_t *)hash, 2*57); + + mod_reduce_M (s, (const bn912 *)hash); + + memset (hash, 0, sizeof (hash)); + bn448_mul ((bn896 *)hash, s, a); + mod_reduce_M (s, (const bn912 *)hash); + + carry = bn448_add (s, s, k); + borrow = bn448_sub (s, s, M); + bn448_add_cond (s, M, (borrow && !carry)); + + memcpy (out, r, 57); + memcpy (out+57, s, 56); + out[114-1] = 0; + + return 0; +} + + +void +ed448_compute_public (uint8_t *pk, const uint8_t *a_in) +{ + p448_t a[1]; + + p448_deserialize (a, a_in); + a->limb[15] |= 0x08000000; + a->limb[0] &= ~3; + + compute_kG_448 (pk, (uint32_t *)a); +} diff --git a/ecc-mont.c b/ecc-mont.c new file mode 100644 index 0000000..68bd30a --- /dev/null +++ b/ecc-mont.c @@ -0,0 +1,226 @@ +/* -*- coding: utf-8 -*- + * ecc-mont.c - Elliptic curve computation for + * the Montgomery curve: y^2 = x^3 + 486662*x^2 + x. + * + * Copyright (C) 2014, 2015, 2017 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "bn.h" +#include "mod25638.h" +#include "mod.h" + +/* + * References: + * + * [1] D. J. Bernstein. Curve25519: new Diffie-Hellman speed records. + * Proceedings of PKC 2006, to appear. + * http://cr.yp.to/papers.html#curve25519. Date: 2006.02.09. + * + * [2] D. J. Bernstein. Can we avoid tests for zero in fast + * elliptic-curve arithmetic? + * http://cr.yp.to/papers.html#curvezero. Date: 2006.07.26. + * + */ + +/* + * IMPLEMENTATION NOTE + * + * (0) We assume that the processor has no cache, nor branch target + * prediction. Thus, we don't avoid indexing by secret value. + * We don't avoid conditional jump if both cases have same timing, + * either. + * + * (1) We use Radix-32 field arithmetic. It's a representation like + * 2^256-38, but it's more redundant. For example, "1" can be + * represented in three ways in 256-bit: 1, 2^255-18, and + * 2^256-37. + * + * (2) We use Montgomery double-and-add. + * + */ + +#ifndef BN256_C_IMPLEMENTATION +#define ASM_IMPLEMENTATION 0 +#endif +/* + * + * 121665 = 0x1db41 + * 1 1101 1011 0100 0001 + */ +static void +mod25638_mul_121665 (bn256 *x, const bn256 *a) +{ +#if ASM_IMPLEMENTATION +#include "muladd_256.h" + const uint32_t *s; + uint32_t *d; + uint32_t w; + uint32_t c; + + s = a->word; + d = x->word; + memset (d, 0, sizeof (bn256)); + w = 121665; + MULADD_256_ASM (s, d, w, c); +#else + uint32_t c, c1; + bn256 m[1]; + + c = c1 = bn256_shift (m, a, 6); c += bn256_add (x, a, m); + c1 <<= 2; c1 |= bn256_shift (m, m, 2); c = c + c1 + bn256_add (x, x, m); + c1 <<= 1; c1 |= bn256_shift (m, m, 1); c = c + c1 + bn256_add (x, x, m); + c1 <<= 2; c1 |= bn256_shift (m, m, 2); c = c + c1 + bn256_add (x, x, m); + c1 <<= 1; c1 |= bn256_shift (m, m, 1); c = c + c1 + bn256_add (x, x, m); + c1 <<= 2; c1 |= bn256_shift (m, m, 2); c = c + c1 + bn256_add (x, x, m); + c1 <<= 1; c1 |= bn256_shift (m, m, 1); c = c + c1 + bn256_add (x, x, m); + c1 <<= 1; c1 |= bn256_shift (m, m, 1); c = c + c1 + bn256_add (x, x, m); +#endif + c = bn256_add_uint (x, x, c*38); + x->word[0] += c * 38; +} + + +typedef struct +{ + bn256 x[1]; + bn256 z[1]; +} pt; + + +/** + * @brief Process Montgomery double-and-add + * + * With Q0, Q1, DIF (= Q0 - Q1), compute PRD = 2Q0, SUM = Q0 + Q1 + * Q0 and Q1 are clobbered. + * + */ +static void +mont_d_and_a (pt *prd, pt *sum, pt *q0, pt *q1, const bn256 *dif_x) +{ + mod25638_add (sum->x, q1->x, q1->z); + mod25638_sub (q1->z, q1->x, q1->z); + mod25638_add (prd->x, q0->x, q0->z); + mod25638_sub (q0->z, q0->x, q0->z); + mod25638_mul (q1->x, q0->z, sum->x); + mod25638_mul (q1->z, prd->x, q1->z); + mod25638_sqr (q0->x, prd->x); + mod25638_sqr (q0->z, q0->z); + mod25638_add (sum->x, q1->x, q1->z); + mod25638_sub (q1->z, q1->x, q1->z); + mod25638_mul (prd->x, q0->x, q0->z); + mod25638_sub (q0->z, q0->x, q0->z); + mod25638_sqr (sum->x, sum->x); + mod25638_sqr (sum->z, q1->z); + mod25638_mul_121665 (prd->z, q0->z); + mod25638_mul (sum->z, sum->z, dif_x); + mod25638_add (prd->z, q0->x, prd->z); + mod25638_mul (prd->z, prd->z, q0->z); +} + + +/** + * @brief RES = x-coordinate of [n]Q + * + * @param N Scalar N (three least significant bits are 000) + * @param Q_X x-coordinate of Q + * + */ +static void +compute_nQ (bn256 *res, const bn256 *n, const bn256 *q_x) +{ + int i, j; + pt p0[1], p1[1], p0_[1], p1_[1]; + + /* P0 = O = (1:0) */ + memset (p0->x, 0, sizeof (bn256)); + p0->x->word[0] = 1; + memset (p0->z, 0, sizeof (bn256)); + + /* P1 = (X:1) */ + memcpy (p1->x, q_x, sizeof (bn256)); + memset (p1->z, 0, sizeof (bn256)); + p1->z->word[0] = 1; + + for (i = 0; i < 8; i++) + { + uint32_t u = n->word[7-i]; + + for (j = 0; j < 16; j++) + { + pt *q0, *q1; + pt *sum_n, *prd_n; + + if ((u & 0x80000000)) + q0 = p1, q1 = p0, sum_n = p0_, prd_n = p1_; + else + q0 = p0, q1 = p1, sum_n = p1_, prd_n = p0_; + mont_d_and_a (prd_n, sum_n, q0, q1, q_x); + + if ((u & 0x40000000)) + q0 = p1_, q1 = p0_, sum_n = p0, prd_n = p1; + else + q0 = p0_, q1 = p1_, sum_n = p1, prd_n = p0; + mont_d_and_a (prd_n, sum_n, q0, q1, q_x); + + u <<= 2; + } + } + + /* We know the LSB of N is always 0. Thus, result is always in P0. */ + /* + * p0->z may be zero here, but our mod_inv doesn't raise error for 0, + * but returns 0 (like the implementation of z^(p-2)), thus, RES will + * be 0 in that case, which is correct value. + */ + mod_inv (res, p0->z, p25519); + mod25638_mul (res, res, p0->x); + mod25519_reduce (res); +} + + +void +ecdh_compute_public_25519 (const uint8_t *key_data, uint8_t *pubkey) +{ + bn256 gx[1]; + bn256 k[1]; + + memset (gx, 0, sizeof (bn256)); + gx[0].word[0] = 9; /* Gx = 9 */ + memcpy (k, key_data, sizeof (bn256)); + + compute_nQ ((bn256 *)pubkey, k, gx); +} + +int +ecdh_decrypt_curve25519 (const uint8_t *input, uint8_t *output, + const uint8_t *key_data) +{ + bn256 q_x[1]; + bn256 k[1]; + bn256 shared[1]; + + memcpy (q_x, input, sizeof (bn256)); + memcpy (k, key_data, sizeof (bn256)); + compute_nQ (shared, k, q_x); + memcpy (output, shared, sizeof (bn256)); + return 0; +} diff --git a/ecc-x448.c b/ecc-x448.c new file mode 100644 index 0000000..7201501 --- /dev/null +++ b/ecc-x448.c @@ -0,0 +1,177 @@ +/* -*- coding: utf-8 -*- + * ecc-x448.c - Elliptic curve computation for + * the Montgomery curve: y^2 = x^3 + 156326*x^2 + x + * + * Copyright (C) 2021 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 . + * + */ + +/* + * IMPLEMENTATION NOTE + * + * (0) We assume that the processor has no cache, nor branch target + * prediction. + * We don't avoid conditional jump if both cases have same timing, + * either. + * + */ + +#include +#include + +#include "p448.h" + +#define N_LIMBS 14 + +/** + * @brief Process Montgomery double-and-add + * + * With Q0, Q1, DIF (= Q0 - Q1), compute PRD = 2Q0 into Q0, + * and computute SUM = Q0 + Q1 into Q1 + * + */ +static void +mont_d_and_a (p448_t q0_x[1], p448_t q0_z[1], p448_t q1_x[1], p448_t q1_z[1], + const p448_t dif_x[1]) +{ + p448_t reg0[1], reg1[1]; +#define c reg0 +#define d reg1 +#define a q1_x +#define b q1_z +#define cb q0_x +#define da reg0 +#define aa reg1 +#define bb q0_z +#define da_plus_cb q1_z +#define da_minus_cb q1_x +#define e reg0 +#define dacb_2 q0_z +#define a24_e q1_x +#define aa_ aa /* override is allowed by p448_add */ + + p448_add (c, q1_x, q1_z); + p448_sub (d, q1_x, q1_z); + p448_add (a, q0_x, q0_z); + p448_sub (b, q0_x, q0_z); + p448_mul (cb, c, b); + p448_mul (da, d, a); + p448_sqr (aa, a); + p448_sqr (bb, b); + p448_add (da_plus_cb, da, cb); + p448_sub (da_minus_cb, da, cb); + p448_mul (q0_x, aa, bb); + p448_sub (e, aa, bb); + p448_sqr (dacb_2, da_minus_cb); + p448_mul_39081 (a24_e, e); + p448_add (aa_, aa, a24_e); + p448_sqr (q1_x, da_plus_cb); + p448_mul (q1_z, dacb_2, dif_x); + p448_mul (q0_z, e, aa_); +} + + +typedef struct +{ + p448_t x[1]; + p448_t z[1]; +} pt; + + +/** + * @brief RES = x-coordinate of [n]Q + * + * @param N Scalar N (three least significant bits are 00) + * @param Q_X x-coordinate of Q + * + */ +static void +compute_nQ (uint8_t *res, const uint32_t n[N_LIMBS], const p448_t q_x[1]) +{ + int i, j; + pt p0[1], p1[1]; +#define tmp0 p0->z +#define tmp1 p1->z + + /* P0 = O = (1:0) */ + memset (p0->x, 0, sizeof (p0->x)); + p0->x->limb[0] = 1; + memset (p0->z, 0, sizeof (p0->z)); + + /* P1 = (X:1) */ + memcpy (p1->x, q_x, N_REDUNDANT_LIMBS*4); + memset (p1->z, 0, sizeof (p1->z)); + p1->z->limb[0] = 1; + + for (i = 0; i < N_LIMBS; i++) + { + uint32_t u = n[N_LIMBS-i-1]; + + for (j = 0; j < 32; j++) + { + p448_t *q0_x, *q0_z, *q1_x, *q1_z; + + if ((u & 0x80000000)) + q0_x = p1->x, q0_z = p1->z, q1_x = p0->x, q1_z = p0->z; + else + q0_x = p0->x, q0_z = p0->z, q1_x = p1->x, q1_z = p1->z; + mont_d_and_a (q0_x, q0_z, q1_x, q1_z, q_x); + + u <<= 1; + } + } + + /* We know the LSB of N is always 0. Thus, result is always in P0. */ + /* + * p0->z may be zero here, but our inverse function doesn't raise + * error for 0, but returns 0, thus, RES will be 0 in that case, + * which is correct value. + */ + p448_inv (tmp1, p0->z); + p448_mul (tmp0, tmp1, p0->x); + p448_serialize (res, tmp0); +} + + +void +ecdh_compute_public_x448 (uint8_t *pubkey, const uint8_t *key_data) +{ + const p448_t gx[1] = { { { 5, 0, }, } }; + uint32_t k[N_LIMBS]; + + memcpy (k, key_data, N_LIMBS*4); + k[0] &= ~3; + k[N_LIMBS-1] |= 0x80000000; + compute_nQ (pubkey, k, gx); +} + +int +ecdh_decrypt_x448 (uint8_t *output, const uint8_t *input, + const uint8_t *key_data) +{ + p448_t q_x[1]; + uint32_t k[N_LIMBS]; + + p448_deserialize (q_x, input); + memcpy (k, key_data, N_LIMBS*4); + k[0] &= ~3; + k[N_LIMBS-1] |= 0x80000000; + compute_nQ (output, k, q_x); + return 0; +} diff --git a/ecc.c b/ecc.c new file mode 100644 index 0000000..2d637e9 --- /dev/null +++ b/ecc.c @@ -0,0 +1,398 @@ +/* -*- coding: utf-8 -*- + * ecc.c - Elliptic curve over GF(prime) + * + * Copyright (C) 2011, 2013, 2014, 2015 + * Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 . + * + */ + +/* + * References: + * + * [1] Suite B Implementer's Guide to FIPS 186-3 (ECDSA), February 3, 2010. + * + * [2] Michael Brown, Darrel Hankerson, Julio López, and Alfred Menezes, + * Software Implementation of the NIST Elliptic Curves Over Prime Fields, + * Proceedings of the 2001 Conference on Topics in Cryptology: The + * Cryptographer's Track at RSA + * Pages 250-265, Springer-Verlag London, UK, 2001 + * ISBN:3-540-41898-9 + * + * [3] Mustapha Hedabou, Pierre Pinel, Lucien Bénéteau, + * A comb method to render ECC resistant against Side Channel Attacks, + * 2004 + */ + +#include "field-group-select.h" + +/* + * Coefficients + */ +/* + * static const bn256 *coefficient_a; + * static const bn256 *coefficient_b; + */ +/* + * N: order of G + */ +/* + * static const bn256 N[1]; + */ +/* + * MU = 2^512 / N + * MU = ( (1 << 256) | MU_lower ) + */ +/* + * static const bn256 MU_lower[1]; + */ + +/* + * w = 4 + * m = 256 + * d = 64 + * e = 32 + */ + +/* + * static const ac precomputed_KG[15]; + * static const ac precomputed_2E_KG[15]; + */ + +#if TEST +/* + * Generator of Elliptic curve over GF(p256) + */ +const ac *G = &precomputed_KG[0]; +#endif + + +static int +get_vk (const bn256 *K, int i) +{ + uint32_t w0, w1, w2, w3; + + if (i < 32) + { + w3 = K->word[6]; w2 = K->word[4]; w1 = K->word[2]; w0 = K->word[0]; + } + else + { + w3 = K->word[7]; w2 = K->word[5]; w1 = K->word[3]; w0 = K->word[1]; + i -= 32; + } + + w3 >>= i; w2 >>= i; w1 >>= i; w0 >>= i; + return ((w3 & 1) << 3) | ((w2 & 1) << 2) | ((w1 & 1) << 1) | (w0 & 1); +} + + +/** + * @brief X = k * G + * + * @param K scalar k + * + * Return -1 on error. + * Return 0 on success. + */ +int +FUNC(compute_kG) (ac *X, const bn256 *K) +{ + uint8_t index[64]; /* Lower 4-bit for index absolute value, msb is + for sign (encoded as: 0 means 1, 1 means -1). */ + bn256 K_dash[1]; + jpc Q[1], tmp[1], *dst; + int i; + int vk; + uint32_t k_is_even = bn256_is_even (K); + + bn256_sub_uint (K_dash, K, k_is_even); + /* It keeps the condition: 1 <= K' <= N - 2, and K' is odd. */ + + /* Fill index. */ + vk = get_vk (K_dash, 0); + for (i = 1; i < 64; i++) + { + int vk_next, is_zero; + + vk_next = get_vk (K_dash, i); + is_zero = (vk_next == 0); + index[i-1] = (vk - 1) | (is_zero << 7); + vk = (is_zero ? vk : vk_next); + } + index[63] = vk - 1; + + memset (Q->z, 0, sizeof (bn256)); /* infinity */ + for (i = 31; i >= 0; i--) + { + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac_signed) (Q, Q, &precomputed_2E_KG[index[i+32]&0x0f], + index[i+32] >> 7); + FUNC(jpc_add_ac_signed) (Q, Q, &precomputed_KG[index[i]&0x0f], + index[i] >> 7); + } + + dst = k_is_even ? Q : tmp; + FUNC(jpc_add_ac) (dst, Q, &precomputed_KG[0]); + + return FUNC(jpc_to_ac) (X, Q); +} + + + +/** + * check if P is on the curve. + * + * Return -1 on error. + * Return 0 on success. + */ +static int +point_is_on_the_curve (const ac *P) +{ + bn256 s[1], t[1]; + + /* Elliptic curve: y^2 = x^3 + a*x + b */ + MFNC(sqr) (s, P->x); + MFNC(mul) (s, s, P->x); + +#ifndef COEFFICIENT_A_IS_ZERO + MFNC(mul) (t, coefficient_a, P->x); + MFNC(add) (s, s, t); +#endif + MFNC(add) (s, s, coefficient_b); + + MFNC(sqr) (t, P->y); + if (bn256_cmp (s, t) == 0) + return 0; + else + return -1; +} + + +static int +get_vk_kP (const bn256 *K, int i) +{ + uint32_t w; + uint8_t blk = i/32; + uint8_t pos = i%32; + uint8_t col = 3*(pos % 11) + (pos >= 11) + (pos >= 22); + uint8_t word_index = (blk * 3) + (pos / 11); + + w = ((K->word[word_index] >> col) & 7); + if (word_index < 7 && (pos == 10 || pos == 21)) + { + uint8_t mask; + uint8_t shift; + + word_index++; + if (pos == 10) + { + shift = 2; + mask = 4; + } + else + { + shift = 1; + mask = 6; + } + + w |= ((K->word[word_index] << shift) & mask); + } + + return w; +} + +/** + * @brief X = k * P + * + * @param K scalar k + * @param P P in affine coordiate + * + * Return -1 on error. + * Return 0 on success. + * + * For the curve (cofactor is 1 and n is prime), possible error cases are: + * + * P is not on the curve. + * P = G, k = n + * Something wrong in the code. + * + * Mathmatically, k=1 and P=O is another possible case, but O cannot be + * represented by affine coordinate. + */ +int +FUNC(compute_kP) (ac *X, const bn256 *K, const ac *P) +{ + uint8_t index[86]; /* Lower 2-bit for index absolute value, msb is + for sign (encoded as: 0 means 1, 1 means -1). */ + bn256 K_dash[1]; + uint32_t k_is_even = bn256_is_even (K); + jpc Q[1], tmp[1], *dst; + int i; + int vk; + ac P3[1], P5[1], P7[1]; + const ac *p_Pi[4]; + + if (point_is_on_the_curve (P) < 0) + return -1; + + if (bn256_sub (K_dash, K, N) == 0) /* >= N, it's too big. */ + return -1; + + bn256_sub_uint (K_dash, K, k_is_even); + /* It keeps the condition: 1 <= K' <= N - 2, and K' is odd. */ + + p_Pi[0] = P; + p_Pi[1] = P3; + p_Pi[2] = P5; + p_Pi[3] = P7; + + { + jpc Q1[1]; + + memcpy (Q->x, P->x, sizeof (bn256)); + memcpy (Q->y, P->y, sizeof (bn256)); + memset (Q->z, 0, sizeof (bn256)); + Q->z->word[0] = 1; + + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac) (Q1, Q, P); + if (FUNC(jpc_to_ac) (P3, Q1) < 0) /* Never occurs, except coding errors. */ + return -1; + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac) (Q1, Q, P); + if (FUNC(jpc_to_ac) (P5, Q1) < 0) /* Never occurs, except coding errors. */ + return -1; + + memcpy (Q->x, P3->x, sizeof (bn256)); + memcpy (Q->y, P3->y, sizeof (bn256)); + memset (Q->z, 0, sizeof (bn256)); + Q->z->word[0] = 1; + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac) (Q1, Q, P); + if (FUNC(jpc_to_ac) (P7, Q1) < 0) /* Never occurs, except coding errors. */ + return -1; + } + + /* Fill index. */ + vk = get_vk_kP (K_dash, 0); + for (i = 1; i < 86; i++) + { + int vk_next, is_even; + + vk_next = get_vk_kP (K_dash, i); + is_even = ((vk_next & 1) == 0); + index[i-1] = (is_even << 7) | ((is_even?7-vk:vk-1) >> 1); + vk = vk_next + is_even; + } + index[85] = ((vk - 1) >> 1); + + memset (Q->z, 0, sizeof (bn256)); /* infinity */ + for (i = 85; i >= 0; i--) + { + FUNC(jpc_double) (Q, Q); + FUNC(jpc_double) (Q, Q); + FUNC(jpc_double) (Q, Q); + FUNC(jpc_add_ac_signed) (Q, Q, p_Pi[index[i]&0x03], index[i] >> 7); + } + + dst = k_is_even ? Q : tmp; + FUNC(jpc_add_ac) (dst, Q, P); + + return FUNC(jpc_to_ac) (X, Q); +} + + +/** + * @brief Compute signature (r,s) of hash string z with secret key d + */ +void +FUNC(ecdsa) (bn256 *r, bn256 *s, const bn256 *z, const bn256 *d) +{ + bn256 k[1]; + ac KG[1]; + bn512 tmp[1]; + bn256 k_inv[1]; + uint32_t carry; +#define borrow carry +#define tmp_k k_inv + + do + { + do + { + bn256_random (k); + if (bn256_add_uint (k, k, 1)) + continue; + if (bn256_sub (tmp_k, k, N) == 0) /* >= N, it's too big. */ + continue; + /* 1 <= k <= N - 1 */ + FUNC(compute_kG) (KG, k); + borrow = bn256_sub (r, KG->x, N); + if (borrow) + memcpy (r, KG->x, sizeof (bn256)); + else + memcpy (KG->x, r, sizeof (bn256)); + } + while (bn256_is_zero (r)); + + mod_inv (k_inv, k, N); + bn256_mul (tmp, r, d); + mod_reduce (s, tmp, N, MU_lower); + carry = bn256_add (s, s, z); + if (carry) + bn256_sub (s, s, N); + else + bn256_sub ((bn256 *)tmp, s, N); + bn256_mul (tmp, s, k_inv); + mod_reduce (s, tmp, N, MU_lower); + } + while (bn256_is_zero (s)); + +#undef tmp_k +#undef borrow +} + + +/** + * @brief Check if a secret d0 is valid or not + * + * @param D0 scalar D0: secret + * @param D1 scalar D1: secret candidate N-D0 + * + * Return 0 on error. + * Return -1 when D1 should be used as the secret + * Return 1 when D0 should be used as the secret + */ +int +FUNC(check_secret) (const bn256 *d0, bn256 *d1) +{ + ac Q0[1], Q1[1]; + + if (bn256_is_zero (d0) || bn256_sub (d1, N, d0) != 0) + /* == 0 or >= N, it's not valid. */ + return 0; + + FUNC(compute_kG) (Q0, d0); + FUNC(compute_kG) (Q1, d1); + + /* + * Jivsov compliant key check + */ + return bn256_cmp (Q1[0].y, Q0[0].y); +} diff --git a/field-group-select.h b/field-group-select.h new file mode 100644 index 0000000..78cb6a5 --- /dev/null +++ b/field-group-select.h @@ -0,0 +1,7 @@ +#define CONCAT0(a,b) a##b +#define CONCAT1(a,b) CONCAT0(a,b) +#define CONCAT2(a,b,c) CONCAT1(a,b##c) +#define CONCAT3(a,b,c) CONCAT2(a,b,c) + +#define FUNC(func) CONCAT1(func##_,FIELD) +#define MFNC(func) CONCAT3(mod,FIELD,_##func) diff --git a/flash.c b/flash.c new file mode 100644 index 0000000..f7a6e90 --- /dev/null +++ b/flash.c @@ -0,0 +1,738 @@ +/* + * flash.c -- Data Objects (DO) and GPG Key handling on Flash ROM + * + * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + * Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 . + * + */ + +/* + * We assume single DO size is less than 256. + * + * NOTE: "Card holder certificate" (which size is larger than 256) is + * not put into data pool, but is implemented by its own flash + * page(s). + */ + +#include +#include + +#include "config.h" + +#include "sys.h" +#include "gnuk.h" + +#include "pico/stdlib.h" +#include "hardware/flash.h" +#include "tusb.h" + +/* + * Flash memory map + * + * _text + * .text + * .ctors + * .dtors + * _etext + * .data + * _bss_start + * .bss + * _end + * + * ch_certificate_startp + * <2048 bytes> + * _keystore_pool + * Three flash pages for keystore + * a page contains a key data of: + * For RSA-2048: 512-byte (p, q and N) + * For RSA-4096: 1024-byte (p, q and N) + * For ECDSA/ECDH and EdDSA, there are padding after public key + * _data_pool + * + */ + +#define FLASH_DATA_POOL_HEADER_SIZE 2 +#define FLASH_DATA_POOL_SIZE (2048*1024) + +static uint16_t flash_page_size; +static const uint8_t *data_pool; +static uint8_t *last_p; + +/* The first halfword is generation for the data page (little endian) */ +const uint8_t flash_data[4] __attribute__ ((section (".gnuk_data"))) = { + 0x00, 0x00, 0xff, 0xff +}; + +#define FLASH_TARGET_OFFSET (4096 * 1024) // DATA starts at the mid of flash + + +const uint8_t *flash_addr_key_storage_start = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET); +const uint8_t *flash_addr_data_storage_start = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET + 2048 * 1024); // 2 MB +const uint8_t *ch_certificate_start = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET - FLASH_SECTOR_SIZE); +#define FLASH_ADDR_KEY_STORAGE_START flash_addr_key_storage_start +#define FLASH_ADDR_DATA_STORAGE_START flash_addr_data_storage_start + +extern int flash_erase_page (uintptr_t addr); +extern int flash_program_halfword (uintptr_t addr, uint16_t data); +extern int flash_check_blank (const uint8_t *p_start, size_t size); +extern int flash_write (uintptr_t dst_addr, const uint8_t *src, size_t len); + +static int key_available_at (const uint8_t *k, int key_size) +{ + int i; + + for (i = 0; i < key_size; i++) + if (k[i]) + break; + if (i == key_size) /* It's ZERO. Released key. */ + return 0; + + for (i = 0; i < key_size; i++) + if (k[i] != 0xff) + break; + if (i == key_size) /* It's FULL. Unused key. */ + return 0; + + return 1; +} + +void +flash_do_storage_init (const uint8_t **p_do_start, const uint8_t **p_do_end) +{ + uint16_t gen0, gen1; + uint16_t *gen0_p = (uint16_t *)FLASH_ADDR_DATA_STORAGE_START; + uint16_t *gen1_p; + + flash_page_size = FLASH_SECTOR_SIZE; + + gen1_p = (uint16_t *)(FLASH_ADDR_DATA_STORAGE_START + flash_page_size); + data_pool = FLASH_ADDR_DATA_STORAGE_START; + + /* Check data pool generation and choose the page */ + gen0 = *gen0_p; + gen1 = *gen1_p; + + if (gen0 == 0xffff && gen1 == 0xffff) + { + /* It's terminated. */ + *p_do_start = *p_do_end = NULL; + return; + } + + if (gen0 == 0xffff) + /* Use another page if a page is erased. */ + data_pool = FLASH_ADDR_DATA_STORAGE_START + flash_page_size; + else if (gen1 == 0xffff) + /* Or use different page if another page is erased. */ + data_pool = FLASH_ADDR_DATA_STORAGE_START; + else if ((gen0 == 0xfffe && gen1 == 0) || gen1 > gen0) + /* When both pages have valid header, use newer page. */ + data_pool = FLASH_ADDR_DATA_STORAGE_START + flash_page_size; + + *p_do_start = data_pool + FLASH_DATA_POOL_HEADER_SIZE; + *p_do_end = data_pool + flash_page_size; +} + +static uint8_t *flash_key_getpage (enum kind_of_key kk); + +void +flash_terminate (void) +{ + int i; + + for (i = 0; i < 3; i++) + flash_erase_page ((uintptr_t)flash_key_getpage (i)); + flash_erase_page ((uintptr_t)FLASH_ADDR_DATA_STORAGE_START); + flash_erase_page ((uintptr_t)(FLASH_ADDR_DATA_STORAGE_START + flash_page_size)); + data_pool = FLASH_ADDR_DATA_STORAGE_START; + last_p = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START + FLASH_DATA_POOL_HEADER_SIZE; +#if defined(CERTDO_SUPPORT) + flash_erase_page ((uintptr_t)ch_certificate_start); + if (FLASH_CH_CERTIFICATE_SIZE > flash_page_size) + flash_erase_page ((uintptr_t)(ch_certificate_start + flash_page_size)); +#endif +} + +void +flash_activate (void) +{ + flash_program_halfword ((uintptr_t)FLASH_ADDR_DATA_STORAGE_START, 0); +} + + +void +flash_key_storage_init (void) +{ + const uint8_t *p; + int i; + + /* For each key, find its address. */ + p = FLASH_ADDR_KEY_STORAGE_START; + for (i = 0; i < 3; i++) + { + const uint8_t *k; + int key_size = gpg_get_algo_attr_key_size (i, GPG_KEY_STORAGE); + + kd[i].pubkey = NULL; + for (k = p; k < p + flash_page_size; k += key_size) + if (key_available_at (k, key_size)) + { + int prv_len = gpg_get_algo_attr_key_size (i, GPG_KEY_PRIVATE); + + kd[i].pubkey = k + prv_len; + break; + } + + p += flash_page_size; + } +} + +/* + * Flash data pool managenent + * + * Flash data pool consists of two parts: + * 2-byte header + * contents + * + * Flash data pool objects: + * Data Object (DO) (of smart card) + * Internal objects: + * NONE (0x0000) + * 123-counter + * 14-bit counter + * bool object + * small enum + * + * Format of a Data Object: + * NR: 8-bit tag_number + * LEN: 8-bit length + * DATA: data * LEN + * PAD: optional byte for 16-bit alignment + */ + +void +flash_set_data_pool_last (const uint8_t *p) +{ + last_p = (uint8_t *)p; +} + +/* + * We use two pages + */ +static int +flash_copying_gc (void) +{ + uint8_t *src, *dst; + uint16_t generation; + + if (data_pool == FLASH_ADDR_DATA_STORAGE_START) + { + src = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START; + dst = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START + flash_page_size; + } + else + { + src = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START + flash_page_size; + dst = (uint8_t *)FLASH_ADDR_DATA_STORAGE_START; + } + + generation = *(uint16_t *)src; + data_pool = dst; + gpg_data_copy (data_pool + FLASH_DATA_POOL_HEADER_SIZE); + if (generation == 0xfffe) + generation = 0; + else + generation++; + flash_program_halfword ((uintptr_t)dst, generation); + flash_erase_page ((uintptr_t)src); + return 0; +} + +static int +is_data_pool_full (size_t size) +{ + return last_p + size > data_pool + flash_page_size; +} + +static uint8_t * +flash_data_pool_allocate (size_t size) +{ + uint8_t *p; + + size = (size + 1) & ~1; /* allocation unit is 1-halfword (2-byte) */ + + if (is_data_pool_full (size)) + if (flash_copying_gc () < 0 || /*still*/ is_data_pool_full (size)) + TU_LOG1 ("!!!! FATAL: %d\r\n",FATAL_FLASH); + + p = last_p; + last_p += size; + return p; +} + +void +flash_do_write_internal (const uint8_t *p, int nr, const uint8_t *data, int len) +{ + uint16_t hw; + uintptr_t addr; + int i; + + addr = (uintptr_t)p; + hw = nr | (len << 8); + if (flash_program_halfword (addr, hw) != 0) + flash_warning ("DO WRITE ERROR"); + addr += 2; + + for (i = 0; i < len/2; i++) + { + hw = data[i*2] | (data[i*2+1]<<8); + if (flash_program_halfword (addr, hw) != 0) + flash_warning ("DO WRITE ERROR"); + addr += 2; + } + + if ((len & 1)) + { + hw = data[i*2] | 0xff00; + if (flash_program_halfword (addr, hw) != 0) + flash_warning ("DO WRITE ERROR"); + } +} + +const uint8_t * +flash_do_write (uint8_t nr, const uint8_t *data, int len) +{ + const uint8_t *p; + + DEBUG_INFO ("flash DO\r\n"); + + p = flash_data_pool_allocate (2 + len); + if (p == NULL) + { + DEBUG_INFO ("flash data pool allocation failure.\r\n"); + return NULL; + } + + flash_do_write_internal (p, nr, data, len); + DEBUG_INFO ("flash DO...done\r\n"); + return p + 1; +} + +void +flash_warning (const char *msg) +{ + (void)msg; + DEBUG_INFO ("FLASH: "); + DEBUG_INFO (msg); + DEBUG_INFO ("\r\n"); +} + +void +flash_do_release (const uint8_t *do_data) +{ + uintptr_t addr = (uintptr_t)do_data - 1; + uintptr_t addr_tag = addr; + int i; + int len = do_data[0]; + + /* Don't filling zero for data in code (such as ds_count_initial_value) */ + if (do_data < FLASH_ADDR_DATA_STORAGE_START + || do_data > FLASH_ADDR_DATA_STORAGE_START + FLASH_DATA_POOL_SIZE) + return; + + addr += 2; + + /* Fill zero for content and pad */ + for (i = 0; i < len/2; i ++) + { + if (flash_program_halfword (addr, 0) != 0) + flash_warning ("fill-zero failure"); + addr += 2; + } + + if ((len & 1)) + { + if (flash_program_halfword (addr, 0) != 0) + flash_warning ("fill-zero pad failure"); + } + + /* Fill 0x0000 for "tag_number and length" word */ + if (flash_program_halfword (addr_tag, 0) != 0) + flash_warning ("fill-zero tag_nr failure"); +} + + +static uint8_t * +flash_key_getpage (enum kind_of_key kk) +{ + /* There is a page for each KK. */ + return (uint8_t *)FLASH_ADDR_KEY_STORAGE_START + (flash_page_size * kk); +} + +uint8_t * +flash_key_alloc (enum kind_of_key kk) +{ + uint8_t *k, *k0 = flash_key_getpage (kk); + int i; + int key_size = gpg_get_algo_attr_key_size (kk, GPG_KEY_STORAGE); + + /* Seek free space in the page. */ + for (k = k0; k < k0 + flash_page_size; k += key_size) + { + const uint32_t *p = (const uint32_t *)k; + + for (i = 0; i < key_size/4; i++) + if (p[i] != 0xffffffff) + break; + + if (i == key_size/4) /* Yes, it's empty. */ + return k; + } + + /* Should not happen as we have enough free space all time, but just + in case. */ + return NULL; +} + +int +flash_key_write (uint8_t *key_addr, + const uint8_t *key_data, int key_data_len, + const uint8_t *pubkey, int pubkey_len) +{ + uint16_t hw; + uintptr_t addr; + int i; + + addr = (uintptr_t)key_addr; + for (i = 0; i < key_data_len/2; i ++) + { + hw = key_data[i*2] | (key_data[i*2+1]<<8); + if (flash_program_halfword (addr, hw) != 0) + return -1; + addr += 2; + } + + for (i = 0; i < pubkey_len/2; i ++) + { + hw = pubkey[i*2] | (pubkey[i*2+1]<<8); + if (flash_program_halfword (addr, hw) != 0) + return -1; + addr += 2; + } + + return 0; +} + +static int +flash_check_all_other_keys_released (const uint8_t *key_addr, int key_size) +{ + uintptr_t start = (uintptr_t)key_addr & ~(flash_page_size - 1); + const uint32_t *p = (const uint32_t *)start; + + while (p < (const uint32_t *)(start + flash_page_size)) + if (p == (const uint32_t *)key_addr) + p += key_size/4; + else + if (*p) + return 0; + else + p++; + + return 1; +} + +static void +flash_key_fill_zero_as_released (uint8_t *key_addr, int key_size) +{ + int i; + uintptr_t addr = (uintptr_t)key_addr; + + for (i = 0; i < key_size/2; i++) + flash_program_halfword (addr + i*2, 0); +} + +void +flash_key_release (uint8_t *key_addr, int key_size) +{ + if (flash_check_all_other_keys_released (key_addr, key_size)) + flash_erase_page (((uintptr_t)key_addr & ~(flash_page_size - 1))); + else + flash_key_fill_zero_as_released (key_addr, key_size); +} + +void +flash_key_release_page (enum kind_of_key kk) +{ + flash_erase_page ((uintptr_t)flash_key_getpage (kk)); +} + + +void +flash_clear_halfword (uintptr_t addr) +{ + flash_program_halfword (addr, 0); +} + + +void +flash_put_data_internal (const uint8_t *p, uint16_t hw) +{ + flash_program_halfword ((uintptr_t)p, hw); +} + +void +flash_put_data (uint16_t hw) +{ + uint8_t *p; + + p = flash_data_pool_allocate (2); + if (p == NULL) + { + DEBUG_INFO ("data allocation failure.\r\n"); + } + + flash_program_halfword ((uintptr_t)p, hw); +} + + +void +flash_bool_clear (const uint8_t **addr_p) +{ + const uint8_t *p; + + if ((p = *addr_p) == NULL) + return; + + flash_program_halfword ((uintptr_t)p, 0); + *addr_p = NULL; +} + +void +flash_bool_write_internal (const uint8_t *p, int nr) +{ + flash_program_halfword ((uintptr_t)p, nr); +} + +const uint8_t * +flash_bool_write (uint8_t nr) +{ + uint8_t *p; + uint16_t hw = nr; + + p = flash_data_pool_allocate (2); + if (p == NULL) + { + DEBUG_INFO ("bool allocation failure.\r\n"); + return NULL; + } + + flash_program_halfword ((uintptr_t)p, hw); + return p; +} + + +void +flash_enum_clear (const uint8_t **addr_p) +{ + flash_bool_clear (addr_p); +} + +void +flash_enum_write_internal (const uint8_t *p, int nr, uint8_t v) +{ + uint16_t hw = nr | (v << 8); + + flash_program_halfword ((uintptr_t)p, hw); +} + +const uint8_t * +flash_enum_write (uint8_t nr, uint8_t v) +{ + uint8_t *p; + uint16_t hw = nr | (v << 8); + + p = flash_data_pool_allocate (2); + if (p == NULL) + { + DEBUG_INFO ("enum allocation failure.\r\n"); + return NULL; + } + + flash_program_halfword ((uintptr_t)p, hw); + return p; +} + + +int +flash_cnt123_get_value (const uint8_t *p) +{ + if (p == NULL) + return 0; + else + { + uint8_t v = *p; + + /* + * After erase, a halfword in flash memory becomes 0xffff. + * The halfword can be programmed to any value. + * Then, the halfword can be programmed to zero. + * + * Thus, we can represent value 1, 2, and 3. + */ + if (v == 0xff) + return 1; + else if (v == 0x00) + return 3; + else + return 2; + } +} + +void +flash_cnt123_write_internal (const uint8_t *p, int which, int v) +{ + uint16_t hw; + + hw = NR_COUNTER_123 | (which << 8); + flash_program_halfword ((uintptr_t)p, hw); + + if (v == 1) + return; + else if (v == 2) + flash_program_halfword ((uintptr_t)p+2, 0xc3c3); + else /* v == 3 */ + flash_program_halfword ((uintptr_t)p+2, 0); +} + +void +flash_cnt123_increment (uint8_t which, const uint8_t **addr_p) +{ + const uint8_t *p; + uint16_t hw; + + if ((p = *addr_p) == NULL) + { + p = flash_data_pool_allocate (4); + if (p == NULL) + { + DEBUG_INFO ("cnt123 allocation failure.\r\n"); + return; + } + hw = NR_COUNTER_123 | (which << 8); + flash_program_halfword ((uintptr_t)p, hw); + *addr_p = p + 2; + } + else + { + uint8_t v = *p; + + if (v == 0) + return; + + if (v == 0xff) + hw = 0xc3c3; + else + hw = 0; + + flash_program_halfword ((uintptr_t)p, hw); + } +} + +void +flash_cnt123_clear (const uint8_t **addr_p) +{ + const uint8_t *p; + + if ((p = *addr_p) == NULL) + return; + + flash_program_halfword ((uintptr_t)p, 0); + p -= 2; + flash_program_halfword ((uintptr_t)p, 0); + *addr_p = NULL; +} + + +#if defined(CERTDO_SUPPORT) +int +flash_erase_binary (uint8_t file_id) +{ + if (file_id == FILEID_CH_CERTIFICATE) + { + const uint8_t *p = ch_certificate_start; + if (flash_check_blank (p, FLASH_CH_CERTIFICATE_SIZE) == 0) + { + flash_erase_page ((uintptr_t)p); + if (FLASH_CH_CERTIFICATE_SIZE > flash_page_size) + flash_erase_page ((uintptr_t)p + flash_page_size); + } + + return 0; + } + + return -1; +} +#endif + + +int +flash_write_binary (uint8_t file_id, const uint8_t *data, + uint16_t len, uint16_t offset) +{ + uint16_t maxsize; + const uint8_t *p; + + if (file_id == FILEID_SERIAL_NO) + { + maxsize = 6; + p = &openpgpcard_aid[8]; + } +#if defined(CERTDO_SUPPORT) + else if (file_id == FILEID_CH_CERTIFICATE) + { + maxsize = FLASH_CH_CERTIFICATE_SIZE; + p = ch_certificate_start; + } +#endif + else + return -1; + + if (offset + len > maxsize || (offset&1) || (len&1)) + return -1; + else + { + uint16_t hw; + uintptr_t addr; + int i; + + if (flash_check_blank (p + offset, len) == 0) + return -1; + + addr = (uintptr_t)p + offset; + for (i = 0; i < len/2; i++) + { + hw = data[i*2] | (data[i*2+1]<<8); + if (flash_program_halfword (addr, hw) != 0) + flash_warning ("DO WRITE ERROR"); + addr += 2; + } + + return 0; + } +} diff --git a/gnuk.h b/gnuk.h new file mode 100644 index 0000000..ab10542 --- /dev/null +++ b/gnuk.h @@ -0,0 +1,501 @@ +/* + * Application layer <-> CCID layer data structure + */ +struct apdu { + uint8_t seq; + + /* command APDU */ + uint8_t *cmd_apdu_head; /* CLS INS P1 P2 [ internal Lc ] */ + uint8_t *cmd_apdu_data; + uint16_t cmd_apdu_data_len; /* Nc, calculated by Lc field */ + uint16_t expected_res_size; /* Ne, calculated by Le field */ + + /* response APDU */ + uint16_t sw; + uint16_t res_apdu_data_len; + uint8_t *res_apdu_data; +}; + +extern struct apdu apdu; + +#define CARD_CHANGE_INSERT 0 +#define CARD_CHANGE_REMOVE 1 +#define CARD_CHANGE_TOGGLE 2 +void ccid_card_change_signal (int how); + +/* CCID thread */ +#define EV_CARD_CHANGE 1 +#define EV_TX_FINISHED 2 /* CCID Tx finished */ +#define EV_EXEC_ACK_REQUIRED 4 /* OpenPGPcard Execution ACK required */ +#define EV_EXEC_FINISHED 8 /* OpenPGPcard Execution finished */ +#define EV_RX_DATA_READY 16 /* USB Rx data available */ + +/* OpenPGPcard thread */ +#define EV_MODIFY_CMD_AVAILABLE 1 +#define EV_VERIFY_CMD_AVAILABLE 2 +#define EV_CMD_AVAILABLE 4 +#define EV_EXIT 8 +#define EV_PINPAD_INPUT_DONE 16 + +/* Maximum cmd apdu data is key import 24+4+256+256 (proc_key_import) */ +#define MAX_CMD_APDU_DATA_SIZE (24+4+256+256) /* without header */ +/* Maximum res apdu data is public key 5+9+512 (gpg_do_public_key) */ +#define MAX_RES_APDU_DATA_SIZE (5+9+512) /* without trailer */ + +#define CCID_MSG_HEADER_SIZE 10 + +#define res_APDU apdu.res_apdu_data +#define res_APDU_size apdu.res_apdu_data_len + +/* USB buffer size of LL (Low-level): size of single Bulk transaction */ +#define USB_LL_BUF_SIZE 64 + +enum ccid_state { + CCID_STATE_NOCARD, /* No card available */ + CCID_STATE_START, /* Initial */ + CCID_STATE_WAIT, /* Waiting APDU */ + + CCID_STATE_EXECUTE, /* Executing command */ + CCID_STATE_ACK_REQUIRED_0, /* Ack required (executing)*/ + CCID_STATE_ACK_REQUIRED_1, /* Waiting user's ACK (execution finished) */ + + CCID_STATE_EXITED, /* CCID Thread Terminated */ + CCID_STATE_EXEC_REQUESTED, /* Exec requested */ +}; + + +enum ccid_state ccid_get_ccid_state (void); + +extern volatile uint8_t auth_status; +#define AC_NONE_AUTHORIZED 0x00 +#define AC_PSO_CDS_AUTHORIZED 0x01 /* PW1 with 0x81 verified */ +#define AC_OTHER_AUTHORIZED 0x02 /* PW1 with 0x82 verified */ +#define AC_ADMIN_AUTHORIZED 0x04 /* PW3 verified */ +#define AC_NEVER 0x80 +#define AC_ALWAYS 0xFF + +#define PW_ERR_PW1 0 +#define PW_ERR_RC 1 +#define PW_ERR_PW3 2 +int gpg_pw_get_retry_counter (int who); +int gpg_pw_locked (uint8_t which); +void gpg_pw_reset_err_counter (uint8_t which); +void gpg_pw_increment_err_counter (uint8_t which); + +int ac_check_status (uint8_t ac_flag); +int verify_pso_cds (const uint8_t *pw, int pw_len); +int verify_other (const uint8_t *pw, int pw_len); +int verify_user_0 (uint8_t access, const uint8_t *pw, int buf_len, + int pw_len_known, const uint8_t *ks_pw1, int saveks); +int verify_admin (const uint8_t *pw, int pw_len); +int verify_admin_0 (const uint8_t *pw, int buf_len, int pw_len_known, + const uint8_t *ks_pw3, int saveks); + +void ac_reset_pso_cds (void); +void ac_reset_other (void); +void ac_reset_admin (void); +void ac_fini (void); + + +void set_res_sw (uint8_t sw1, uint8_t sw2); +extern uint8_t file_selection; +extern const uint8_t historical_bytes[]; +extern uint16_t data_objects_number_of_bytes; + +#define CHALLENGE_LEN 32 + +void gpg_data_scan (const uint8_t *start, const uint8_t *end); +void gpg_data_copy (const uint8_t *p); +void gpg_do_terminate (void); +void gpg_do_get_data (uint16_t tag, int with_tag); +void gpg_do_put_data (uint16_t tag, const uint8_t *data, int len); +void gpg_do_public_key (uint8_t kk_byte); +void gpg_do_keygen (uint8_t *buf); + +const uint8_t *gpg_get_firmware_update_key (uint8_t keyno); + +/* Constants: algo+size */ +#define ALGO_RSA4K 0 +/* #define ALGO_NISTP256R1 1 */ +#define ALGO_SECP256K1 2 +#define ALGO_ED25519 3 +#define ALGO_CURVE25519 4 +#define ALGO_X448 5 +#define ALGO_ED448 6 +#define ALGO_RSA2K 255 + +enum kind_of_key { + GPG_KEY_FOR_SIGNING = 0, + GPG_KEY_FOR_DECRYPTION = 1, + GPG_KEY_FOR_AUTHENTICATION = 2, +}; + +enum size_of_key { + GPG_KEY_STORAGE = 0, /* PUBKEY + PRVKEY rounded to 2^N */ + GPG_KEY_PUBLIC, + GPG_KEY_PRIVATE, +}; + +int gpg_get_algo_attr (enum kind_of_key kk); +int gpg_get_algo_attr_key_size (enum kind_of_key kk, enum size_of_key s); + +void flash_do_storage_init (const uint8_t **, const uint8_t **); +void flash_terminate (void); +void flash_activate (void); +void flash_key_storage_init (void); +void flash_do_release (const uint8_t *); +const uint8_t *flash_do_write (uint8_t nr, const uint8_t *data, int len); +uint8_t *flash_key_alloc (enum kind_of_key); +void flash_key_release (uint8_t *, int); +void flash_key_release_page (enum kind_of_key); +int flash_key_write (uint8_t *key_addr, + const uint8_t *key_data, int key_data_len, + const uint8_t *pubkey, int pubkey_len); +void flash_set_data_pool_last (const uint8_t *p); +void flash_clear_halfword (uintptr_t addr); +void flash_increment_counter (uint8_t counter_tag_nr); +void flash_reset_counter (uint8_t counter_tag_nr); + +#define FILEID_SERIAL_NO 0 +#define FILEID_UPDATE_KEY_0 1 +#define FILEID_UPDATE_KEY_1 2 +#define FILEID_UPDATE_KEY_2 3 +#define FILEID_UPDATE_KEY_3 4 +#define FILEID_CH_CERTIFICATE 5 +int flash_erase_binary (uint8_t file_id); +int flash_write_binary (uint8_t file_id, const uint8_t *data, + uint16_t len, uint16_t offset); + +#define FLASH_CH_CERTIFICATE_SIZE 2048 + +extern const uint8_t *ch_certificate_start; + +#define FIRMWARE_UPDATE_KEY_CONTENT_LEN 256 /* RSA-2048 (p and q) */ + +#define INITIAL_VECTOR_SIZE 16 +#define DATA_ENCRYPTION_KEY_SIZE 16 + +#define MAX_PRVKEY_LEN 512 /* Maximum is the case for RSA 4096-bit. */ + +struct key_data { + const uint8_t *pubkey; /* Pointer to public key */ + uint8_t data[MAX_PRVKEY_LEN]; /* decrypted private key data content */ +}; + +struct prvkey_data { + /* + * IV: Initial Vector + */ + uint8_t iv[INITIAL_VECTOR_SIZE]; + /* + * Checksum + */ + uint8_t checksum_encrypted[DATA_ENCRYPTION_KEY_SIZE]; + /* + * DEK (Data Encryption Key) encrypted + */ + uint8_t dek_encrypted_1[DATA_ENCRYPTION_KEY_SIZE]; /* For user */ + uint8_t dek_encrypted_2[DATA_ENCRYPTION_KEY_SIZE]; /* For resetcode */ + uint8_t dek_encrypted_3[DATA_ENCRYPTION_KEY_SIZE]; /* For admin */ +}; + +#define BY_USER 1 +#define BY_RESETCODE 2 +#define BY_ADMIN 3 + +/* + * Maximum length of pass phrase is 127. + * We use the top bit (0x80) to encode if keystring is available within DO. + */ +#define PW_LEN_MAX 127 +#define PW_LEN_MASK 0x7f +#define PW_LEN_KEYSTRING_BIT 0x80 + +#define SALT_SIZE 8 + +void s2k (const unsigned char *salt, size_t slen, + const unsigned char *input, size_t ilen, unsigned char output[32]); + +#define KEYSTRING_PASSLEN_SIZE 1 +#define KEYSTRING_SALT_SIZE SALT_SIZE +#define KEYSTRING_MD_SIZE 32 +#define KEYSTRING_SIZE (KEYSTRING_PASSLEN_SIZE + KEYSTRING_SALT_SIZE \ + + KEYSTRING_MD_SIZE) +#define KS_META_SIZE (KEYSTRING_PASSLEN_SIZE + KEYSTRING_SALT_SIZE) +#define KS_GET_SALT(ks) (ks + KEYSTRING_PASSLEN_SIZE) +#define KS_GET_KEYSTRING(ks) (ks + KS_META_SIZE) + +void gpg_do_clear_prvkey (enum kind_of_key kk); +int gpg_do_load_prvkey (enum kind_of_key kk, int who, const uint8_t *keystring); +int gpg_do_chks_prvkey (enum kind_of_key kk, + int who_old, const uint8_t *old_ks, + int who_new, const uint8_t *new_ks); + +int gpg_change_keystring (int who_old, const uint8_t *old_ks, + int who_new, const uint8_t *new_ks); + +extern struct key_data kd[3]; + +#ifdef DEBUG +void stdout_init (void); +#define DEBUG_MORE 1 +/* + * Debug functions in debug.c + */ +void put_byte (uint8_t b); +void put_byte_with_no_nl (uint8_t b); +void put_short (uint16_t x); +void put_word (uint32_t x); +void put_int (uint32_t x); +void put_string (const char *s); +void put_binary (const char *s, int len); + +#define DEBUG_INFO(msg) put_string (msg) +#define DEBUG_WORD(w) put_word (w) +#define DEBUG_SHORT(h) put_short (h) +#define DEBUG_BYTE(b) put_byte (b) +#define DEBUG_BINARY(s,len) put_binary ((const char *)s,len) +#else +#define DEBUG_INFO(msg) +#define DEBUG_WORD(w) +#define DEBUG_SHORT(h) +#define DEBUG_BYTE(b) +#define DEBUG_BINARY(s,len) +#endif + +int rsa_sign (const uint8_t *, uint8_t *, int, struct key_data *, int); +int modulus_calc (const uint8_t *, int, uint8_t *); +int rsa_decrypt (const uint8_t *, uint8_t *, int, struct key_data *, + unsigned int *); +int rsa_verify (const uint8_t *, int, const uint8_t *, const uint8_t *); +int rsa_genkey (int, uint8_t *, uint8_t *); + +int ecdsa_sign_p256k1 (const uint8_t *hash, uint8_t *output, + const uint8_t *key_data); +int ecc_compute_public_p256k1 (const uint8_t *key_data, uint8_t *); +int ecc_check_secret_p256k1 (const uint8_t *d0, uint8_t *d1); +int ecdh_decrypt_p256k1 (const uint8_t *input, uint8_t *output, + const uint8_t *key_data); + +int eddsa_sign_25519 (const uint8_t *input, size_t ilen, uint32_t *output, + const uint8_t *sk_a, const uint8_t *seed, + const uint8_t *pk); +void eddsa_compute_public_25519 (const uint8_t *a, uint8_t *); +void ecdh_compute_public_25519 (const uint8_t *a, uint8_t *); +int ecdh_decrypt_curve25519 (const uint8_t *input, uint8_t *output, + const uint8_t *key_data); + +void ecdh_compute_public_x448 (uint8_t *pubkey, const uint8_t *key_data); +int ecdh_decrypt_x448 (uint8_t *output, const uint8_t *input, + const uint8_t *key_data); + +int ed448_sign (uint8_t *out, const uint8_t *input, unsigned int ilen, + const uint8_t *a_in, const uint8_t *seed, const uint8_t *pk); +void ed448_compute_public (uint8_t *pk, const uint8_t *a_in); + + +const uint8_t *gpg_do_read_simple (uint8_t); +void gpg_do_write_simple (uint8_t, const uint8_t *, int); +void gpg_increment_digital_signature_counter (void); +void gpg_do_get_initial_pw_setting (int is_pw3, int *r_len, + const uint8_t **r_p); +int gpg_do_kdf_check (int len, int how_many); +int gpg_do_get_uif (enum kind_of_key kk); + + +void fatal (uint8_t code) __attribute__ ((noreturn)); +#define FATAL_FLASH 1 +#define FATAL_RANDOM 2 +#define FATAL_HEAP 3 + +extern uint8_t keystring_md_pw3[KEYSTRING_MD_SIZE]; +extern uint8_t admin_authorized; + +/*** Flash memory tag values ***/ +/* Data objects */ +/* + * Representation of data object: + * + * <-1 halfword-> <--len/2 halfwords-> + * <-tag-><-len-> <---data content---> + */ +#define NR_DO_SEX 0x00 +#define NR_DO_FP_SIG 0x01 +#define NR_DO_FP_DEC 0x02 +#define NR_DO_FP_AUT 0x03 +#define NR_DO_CAFP_1 0x04 +#define NR_DO_CAFP_2 0x05 +#define NR_DO_CAFP_3 0x06 +#define NR_DO_KGTIME_SIG 0x07 +#define NR_DO_KGTIME_DEC 0x08 +#define NR_DO_KGTIME_AUT 0x09 +#define NR_DO_LOGIN_DATA 0x0a +#define NR_DO_URL 0x0b +#define NR_DO_NAME 0x0c +#define NR_DO_LANGUAGE 0x0d +#define NR_DO_PRVKEY_SIG 0x0e +#define NR_DO_PRVKEY_DEC 0x0f +#define NR_DO_PRVKEY_AUT 0x10 +#define NR_DO_KEYSTRING_PW1 0x11 +#define NR_DO_KEYSTRING_RC 0x12 +#define NR_DO_KEYSTRING_PW3 0x13 +#define NR_DO_KDF 0x14 +#define NR_DO__LAST__ 21 /* == 0x15 */ +/* 14-bit counter for DS: Recorded in flash memory by 1-halfword (2-byte). */ +/* + * Representation of 14-bit counter: + * 0: 0x8000 + * 1: 0x8001 + * ... + * 16383: 0xbfff + */ +#define NR_COUNTER_DS 0x80 /* ..0xbf */ +/* 10-bit counter for DS: Recorded in flash memory by 1-halfword (2-byte). */ +/* + * Representation of 10-bit counter: + * 0: 0xc000 + * 1: 0xc001 + * ... + * 1023: 0xc3ff + */ +#define NR_COUNTER_DS_LSB 0xc0 /* ..0xc3 */ +/* + * Boolean object, small enum, or 8-bit integer: + * Recorded in flash memory by 1-halfword (2-byte) + */ +/* + * Representation of Boolean object: + * 0: No record in flash memory + * 1: 0xf000 + */ +#define NR_BOOL_PW1_LIFETIME 0xf0 +/* + * Representation of algorithm attribute object: + * RSA-2048: No record in flash memory + * RSA-4096: 0xf?00 + * ECC p256r1: 0xf?01 + * ECC p256k1: 0xf?02 + * ECC Ed25519: 0xf?03 + * ECC Curve25519: 0xf?04 + * where == 1 (signature), 2 (decryption) or 3 (authentication) + */ +#define NR_KEY_ALGO_ATTR_SIG 0xf1 +#define NR_KEY_ALGO_ATTR_DEC 0xf2 +#define NR_KEY_ALGO_ATTR_AUT 0xf3 +/* + * Representation of User Interaction Flag: + * 0 (UIF disabled): 0xf?00 or No record in flash memory + * 1 (UIF enabled): 0xf?01 + * 2 (UIF permanently enabled): 0xf?02 + * + */ +#define NR_DO_UIF_SIG 0xf6 +#define NR_DO_UIF_DEC 0xf7 +#define NR_DO_UIF_AUT 0xf8 +/* + * NR_UINT_SOMETHING could be here... Use 0xf[459abcd] + */ +/* 123-counters: Recorded in flash memory by 2-halfword (4-byte). */ +/* + * Representation of 123-counters: + * 0: No record in flash memory + * 1: 0xfe?? 0xffff + * 2: 0xfe?? 0xc3c3 + * 3: 0xfe?? 0x0000 + * where is placed at second byte + */ +#define NR_COUNTER_123 0xfe +#define NR_EMPTY 0xff + +#define SIZE_PW_STATUS_BYTES 7 + + +#define NUM_ALL_PRV_KEYS 3 /* SIG, DEC and AUT */ + +#if !defined(OPENPGP_CARD_INITIAL_PW1) +#define OPENPGP_CARD_INITIAL_PW1 "123456" +#endif + +#if !defined(OPENPGP_CARD_INITIAL_PW3) +#define OPENPGP_CARD_INITIAL_PW3 "12345678" +#endif + +extern const uint8_t openpgpcard_aid[14]; + +void flash_bool_clear (const uint8_t **addr_p); +const uint8_t *flash_bool_write (uint8_t nr); +void flash_enum_clear (const uint8_t **addr_p); +const uint8_t *flash_enum_write (uint8_t nr, uint8_t v); +int flash_cnt123_get_value (const uint8_t *p); +void flash_cnt123_increment (uint8_t which, const uint8_t **addr_p); +void flash_cnt123_clear (const uint8_t **addr_p); +void flash_put_data (uint16_t hw); +void flash_warning (const char *msg); + +void flash_put_data_internal (const uint8_t *p, uint16_t hw); +void flash_bool_write_internal (const uint8_t *p, int nr); +void flash_enum_write_internal (const uint8_t *p, int nr, uint8_t v); +void flash_cnt123_write_internal (const uint8_t *p, int which, int v); +void flash_do_write_internal (const uint8_t *p, int nr, + const uint8_t *data, int len); + +extern const uint8_t gnuk_string_serial[]; + +#define LED_ONESHOT 1 +#define LED_TWOSHOTS 2 +#define LED_SHOW_STATUS 4 +#define LED_FATAL 8 +#define LED_SYNC 16 +#define LED_GNUK_EXEC 32 +#define LED_START_COMMAND 64 +#define LED_FINISH_COMMAND 128 +#define LED_WAIT_FOR_BUTTON 256 +#define LED_OFF LED_FINISH_COMMAND +void led_blink (int spec); + +#if defined(PINPAD_SUPPORT) +# if defined(PINPAD_CIR_SUPPORT) +void cir_init (void); +# elif defined(PINPAD_DIAL_SUPPORT) +void dial_sw_disable (void); +void dial_sw_enable (void); +# elif defined(PINPAD_DND_SUPPORT) +void msc_init (void); +void msc_media_insert_change (int available); +int msc_scsi_write (uint32_t lba, const uint8_t *buf, size_t size); +int msc_scsi_read (uint32_t lba, const uint8_t **sector_p); +void msc_scsi_stop (uint8_t code); +# endif +#define PIN_INPUT_CURRENT 1 +#define PIN_INPUT_NEW 2 +#define PIN_INPUT_CONFIRM 3 +#define MAX_PIN_CHARS 32 +extern uint8_t pin_input_buffer[MAX_PIN_CHARS]; +extern uint8_t pin_input_len; + +int pinpad_getline (int msg_code, uint32_t timeout_usec); + +#endif + + +extern uint8_t _regnual_start, __heap_end__[]; + +uint8_t * sram_address (uint32_t offset); + +static inline const uint8_t * +unique_device_id (void) +{ + /* + * STM32F103 has 96-bit unique device identifier. + * This routine mimics that. + */ + + static const uint8_t id[] = { /* My RSA fingerprint */ + 0x12, 0x41, 0x24, 0xBD, 0x3B, 0x48, 0x62, 0xAF, + 0x7A, 0x0A, 0x42, 0xF1, 0x00, 0xB4, 0x5E, 0xBD, + 0x4C, 0xA7, 0xBA, 0xBE + }; + + return id; +} + diff --git a/jpc-ac_p256k1.h b/jpc-ac_p256k1.h new file mode 100644 index 0000000..d8624bd --- /dev/null +++ b/jpc-ac_p256k1.h @@ -0,0 +1,14 @@ +/** + * @brief Jacobian projective coordinates + */ +typedef struct +{ + bn256 x[1]; + bn256 y[1]; + bn256 z[1]; +} jpc; + +void jpc_double_p256k1 (jpc *X, const jpc *A); +void jpc_add_ac_p256k1 (jpc *X, const jpc *A, const ac *B); +void jpc_add_ac_signed_p256k1 (jpc *X, const jpc *A, const ac *B, int minus); +int jpc_to_ac_p256k1 (ac *X, const jpc *A); diff --git a/jpc.c b/jpc.c new file mode 100644 index 0000000..e6a1f7d --- /dev/null +++ b/jpc.c @@ -0,0 +1,199 @@ +/* + * jpc.c -- arithmetic on Jacobian projective coordinates. + * + * Copyright (C) 2011, 2013 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "field-group-select.h" + +/** + * @brief X = 2 * A + * + * @param X Destination JPC + * @param A JPC + */ +void +FUNC(jpc_double) (jpc *X, const jpc *A) +{ + bn256 a[1], b[1], c[1], tmp0[1]; + bn256 *d; + + if (bn256_is_zero (A->z)) /* A is infinite */ + return; + + d = X->x; + MFNC(sqr) (a, A->y); + memcpy (b, a, sizeof (bn256)); + MFNC(mul) (a, a, A->x); + MFNC(shift) (a, a, 2); + + MFNC(sqr) (b, b); + MFNC(shift) (b, b, 3); + +#if defined(COEFFICIENT_A_IS_MINUS_3) + MFNC(sqr) (tmp0, A->z); + MFNC(sub) (c, A->x, tmp0); + MFNC(add) (tmp0, tmp0, A->x); + MFNC(mul) (tmp0, tmp0, c); + MFNC(shift) (c, tmp0, 1); + MFNC(add) (c, c, tmp0); +#elif defined (COEFFICIENT_A_IS_ZERO) + MFNC(sqr) (tmp0, A->x); + MFNC(shift) (c, tmp0, 1); + MFNC(add) (c, c, tmp0); +#else +#error "not supported." +#endif + + MFNC(sqr) (d, c); + MFNC(shift) (tmp0, a, 1); + MFNC(sub) (d, d, tmp0); + + MFNC(mul) (X->z, A->y, A->z); + MFNC(shift) (X->z, X->z, 1); + + MFNC(sub) (tmp0, a, d); + MFNC(mul) (tmp0, c, tmp0); + MFNC(sub) (X->y, tmp0, b); +} + +/** + * @brief X = A + B + * + * @param X Destination JPC + * @param A JPC + * @param B AC + * @param MINUS if 1 subtraction, addition otherwise. + */ +void +FUNC(jpc_add_ac_signed) (jpc *X, const jpc *A, const ac *B, int minus) +{ + bn256 a[1], b[1], c[1], d[1], tmp[1]; +#define minus_B_y c +#define c_sqr a +#define c_cube b +#define x1_c_sqr c +#define x1_c_sqr_2 c +#define c_cube_plus_x1_c_sqr_2 c +#define x1_c_sqr_copy a +#define y3_tmp c +#define y1_c_cube a + + if (bn256_is_zero (A->z)) /* A is infinite */ + { + memcpy (X->x, B->x, sizeof (bn256)); + if (minus) + { + memcpy (tmp, B->y, sizeof (bn256)); + bn256_sub (X->y, CONST_P256, B->y); + } + else + { + memcpy (X->y, B->y, sizeof (bn256)); + bn256_sub (tmp, CONST_P256, B->y); + } + memset (X->z, 0, sizeof (bn256)); + X->z->word[0] = 1; + return; + } + + MFNC(sqr) (a, A->z); + memcpy (b, a, sizeof (bn256)); + MFNC(mul) (a, a, B->x); + + MFNC(mul) (b, b, A->z); + if (minus) + { + bn256_sub (minus_B_y, CONST_P256, B->y); + MFNC(mul) (b, b, minus_B_y); + } + else + { + bn256_sub (tmp, CONST_P256, B->y); + MFNC(mul) (b, b, B->y); + } + + if (bn256_cmp (A->x, a) == 0 && bn256_cmp (A->y, b) == 0) + { + FUNC(jpc_double) (X, A); + return; + } + + MFNC(sub) (c, a, A->x); + MFNC(sub) (d, b, A->y); + + MFNC(mul) (X->z, A->z, c); + + MFNC(sqr) (c_sqr, c); + MFNC(mul) (c_cube, c_sqr, c); + + MFNC(mul) (x1_c_sqr, A->x, c_sqr); + + MFNC(sqr) (X->x, d); + memcpy (x1_c_sqr_copy, x1_c_sqr, sizeof (bn256)); + MFNC(shift) (x1_c_sqr_2, x1_c_sqr, 1); + MFNC(add) (c_cube_plus_x1_c_sqr_2, x1_c_sqr_2, c_cube); + MFNC(sub) (X->x, X->x, c_cube_plus_x1_c_sqr_2); + + MFNC(sub) (y3_tmp, x1_c_sqr_copy, X->x); + MFNC(mul) (y3_tmp, y3_tmp, d); + MFNC(mul) (y1_c_cube, A->y, c_cube); + MFNC(sub) (X->y, y3_tmp, y1_c_cube); +} + +/** + * @brief X = A + B + * + * @param X Destination JPC + * @param A JPC + * @param B AC + */ +void +FUNC(jpc_add_ac) (jpc *X, const jpc *A, const ac *B) +{ + FUNC(jpc_add_ac_signed) (X, A, B, 0); +} + +/** + * @brief X = convert A + * + * @param X Destination AC + * @param A JPC + * + * Return -1 on error (infinite). + * Return 0 on success. + */ +int +FUNC(jpc_to_ac) (ac *X, const jpc *A) +{ + bn256 z_inv[1], z_inv_sqr[1]; + + if (bn256_is_zero (A->z)) + return -1; + + mod_inv (z_inv, A->z, CONST_P256); + + MFNC(sqr) (z_inv_sqr, z_inv); + MFNC(mul) (z_inv, z_inv, z_inv_sqr); + + MFNC(mul) (X->x, A->x, z_inv_sqr); + MFNC(mul) (X->y, A->y, z_inv); + return 0; +} diff --git a/jpc_p256k1.c b/jpc_p256k1.c new file mode 100644 index 0000000..a903b12 --- /dev/null +++ b/jpc_p256k1.c @@ -0,0 +1,36 @@ +/* + * jpc_p256k1.c -- arithmetic on Jacobian projective coordinates for p256k1. + * + * Copyright (C) 2014 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "bn.h" +#include "mod.h" +#include "modp256k1.h" +#include "affine.h" +#include "jpc-ac_p256k1.h" + +#define FIELD p256k1 +#define CONST_P256 P256K1 +#define COEFFICIENT_A_IS_ZERO 1 + +#include "jpc.c" diff --git a/low_flash.c b/low_flash.c new file mode 100644 index 0000000..db8310c --- /dev/null +++ b/low_flash.c @@ -0,0 +1,54 @@ +#include +#include +#include + +#include "pico/stdlib.h" +#include "hardware/flash.h" +#include "hardware/sync.h" +#include + +int +flash_program_halfword (uintptr_t addr, uint16_t data) +{ + off_t offset; + uint8_t buf[FLASH_PAGE_SIZE]; + memset(buf, 0, sizeof(uint8_t)*FLASH_PAGE_SIZE); + + buf[0] = (data & 0xff); + buf[1] = (data >> 8); + uint32_t ints = save_and_disable_interrupts(); + flash_range_program(addr-XIP_BASE, buf, FLASH_PAGE_SIZE); + restore_interrupts (ints); + return 0; +} + +static const uint8_t erased[] = { [0 ... 1023 ] = 0xff }; + +int +flash_erase_page (uintptr_t addr) +{ + uint32_t ints = save_and_disable_interrupts(); + flash_range_erase(addr-XIP_BASE, FLASH_SECTOR_SIZE); + restore_interrupts (ints); + return 0; +} + +int +flash_check_blank (const uint8_t *p_start, size_t size) +{ + const uint8_t *p; + + for (p = p_start; p < p_start + size; p++) + if (*p != 0xff) + return 0; + + return 1; +} + +int +flash_write (uintptr_t dst_addr, const uint8_t *src, size_t len) +{ + uint32_t ints = save_and_disable_interrupts(); + flash_range_program(dst_addr-XIP_BASE, src, (len%FLASH_PAGE_SIZE == 0 ? len : ((size_t)(len/FLASH_PAGE_SIZE)+1)*FLASH_PAGE_SIZE)); + restore_interrupts (ints); +} diff --git a/mod.c b/mod.c new file mode 100644 index 0000000..340fc16 --- /dev/null +++ b/mod.c @@ -0,0 +1,352 @@ +/* + * mod.c -- modulo arithmetic + * + * Copyright (C) 2011, 2014 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "bn.h" + +/** + * @brief X = A mod B (using MU=(1<<(256)+MU_lower)) (Barret reduction) + * + */ +void +mod_reduce (bn256 *X, const bn512 *A, const bn256 *B, const bn256 *MU_lower) +{ + bn256 q[1]; + bn512 q_big[1], tmp[1]; + uint32_t carry; +#define borrow carry + + memset (q, 0, sizeof (bn256)); + q->word[0] = A->word[15]; + bn256_mul (tmp, q, MU_lower); + tmp->word[8] += A->word[15]; + carry = (tmp->word[8] < A->word[15]); + tmp->word[9] += carry; + + q->word[7] = A->word[14]; + q->word[6] = A->word[13]; + q->word[5] = A->word[12]; + q->word[4] = A->word[11]; + q->word[3] = A->word[10]; + q->word[2] = A->word[9]; + q->word[1] = A->word[8]; + q->word[0] = A->word[7]; + bn256_mul (q_big, q, MU_lower); + bn256_add ((bn256 *)&q_big->word[8], (bn256 *)&q_big->word[8], q); + + q->word[0] = q_big->word[9] + tmp->word[1]; + carry = (q->word[0] < tmp->word[1]); + + q->word[1] = q_big->word[10] + carry; + carry = (q->word[1] < carry); + q->word[1] += tmp->word[2]; + carry += (q->word[1] < tmp->word[2]); + + q->word[2] = q_big->word[11] + carry; + carry = (q->word[2] < carry); + q->word[2] += tmp->word[3]; + carry += (q->word[2] < tmp->word[3]); + + q->word[3] = q_big->word[12] + carry; + carry = (q->word[3] < carry); + q->word[3] += tmp->word[4]; + carry += (q->word[3] < tmp->word[4]); + + q->word[4] = q_big->word[13] + carry; + carry = (q->word[4] < carry); + q->word[4] += tmp->word[5]; + carry += (q->word[4] < tmp->word[5]); + + q->word[5] = q_big->word[14] + carry; + carry = (q->word[5] < carry); + q->word[5] += tmp->word[6]; + carry += (q->word[5] < tmp->word[6]); + + q->word[6] = q_big->word[15] + carry; + carry = (q->word[6] < carry); + q->word[6] += tmp->word[7]; + carry += (q->word[6] < tmp->word[7]); + + q->word[7] = carry; + q->word[7] += tmp->word[8]; + carry = (q->word[7] < tmp->word[8]); + + memset (q_big, 0, sizeof (bn512)); + q_big->word[8] = A->word[8]; + q_big->word[7] = A->word[7]; + q_big->word[6] = A->word[6]; + q_big->word[5] = A->word[5]; + q_big->word[4] = A->word[4]; + q_big->word[3] = A->word[3]; + q_big->word[2] = A->word[2]; + q_big->word[1] = A->word[1]; + q_big->word[0] = A->word[0]; + + bn256_mul (tmp, q, B); + tmp->word[8] += carry * B->word[0]; + tmp->word[15] = tmp->word[14] = tmp->word[13] = tmp->word[12] + = tmp->word[11] = tmp->word[10] = tmp->word[9] = 0; + + borrow = bn256_sub (X, (bn256 *)&q_big->word[0], (bn256 *)&tmp->word[0]); + q_big->word[8] -= borrow; + q_big->word[8] -= tmp->word[8]; + + carry = q_big->word[8]; + if (carry) + carry -= bn256_sub (X, X, B); + else + bn256_sub (q, X, B); + + if (carry) + bn256_sub (X, X, B); + else + bn256_sub (q, X, B); + + borrow = bn256_sub (q, X, B); + if (borrow) + memcpy (q, X, sizeof (bn256)); + else + memcpy (X, q, sizeof (bn256)); +#undef borrow +} + +/* + * Reference: + * Donald E. Knuth, The Art of Computer Programming, Vol. 2: + * Seminumerical Algorithms, 3rd ed. Reading, MA: Addison-Wesley, 1998 + * + * Max loop: X=0x8000...0000 and N=0xffff...ffff + */ +#define MAX_GCD_STEPS_BN256 (3*256-2) + +/** + * @brief C = X^(-1) mod N + * + * Assume X and N are co-prime (or N is prime). + * NOTE: If X==0, it return 0. + * + */ +void +mod_inv (bn256 *C, const bn256 *X, const bn256 *N) +{ + bn256 u[1], v[1], tmp[1]; + bn256 A[1] = { { { 1, 0, 0, 0, 0, 0, 0, 0 } } }; + uint32_t carry; +#define borrow carry + int n = MAX_GCD_STEPS_BN256; + + memset (tmp, 0, sizeof (bn256)); + memset (C, 0, sizeof (bn256)); + memcpy (u, X, sizeof (bn256)); + memcpy (v, N, sizeof (bn256)); + + while (n--) + { + int c = (bn256_is_even (u) << 1) + bn256_is_even (v); + + switch (c) + { + case 3: + bn256_shift (u, u, -1); + if (bn256_is_even (A)) + { + bn256_add (tmp, A, N); + carry = 0; + } + else + carry = bn256_add (A, A, N); + + bn256_shift (A, A, -1); + A->word[7] |= carry * 0x80000000; + + bn256_shift (v, v, -1); + if (bn256_is_even (C)) + { + bn256_add (tmp, C, N); + carry = 0; + } + else + carry = bn256_add (C, C, N); + + bn256_shift (C, C, -1); + C->word[7] |= carry * 0x80000000; + + if (bn256_is_ge (tmp, tmp)) + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, A, N); + } + else + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, tmp, N); + } + break; + + case 1: + bn256_shift (tmp, tmp, -1); + if (bn256_is_even (tmp)) + { + bn256_add (tmp, tmp, N); + carry = 0; + } + else + carry = bn256_add (tmp, tmp, N); + + bn256_shift (tmp, tmp, -1); + tmp->word[7] |= carry * 0x80000000; + + bn256_shift (v, v, -1); + if (bn256_is_even (C)) + { + bn256_add (tmp, C, N); + carry = 0; + } + else + carry = bn256_add (C, C, N); + + bn256_shift (C, C, -1); + C->word[7] |= carry * 0x80000000; + + if (bn256_is_ge (tmp, tmp)) + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, A, N); + } + else + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, tmp, N); + } + break; + + case 2: + bn256_shift (u, u, -1); + if (bn256_is_even (A)) + { + bn256_add (tmp, A, N); + carry = 0; + } + else + carry = bn256_add (A, A, N); + + bn256_shift (A, A, -1); + A->word[7] |= carry * 0x80000000; + + bn256_shift (tmp, tmp, -1); + if (bn256_is_even (tmp)) + { + bn256_add (tmp, tmp, N); + carry = 0; + } + else + carry = bn256_add (tmp, tmp, N); + + bn256_shift (tmp, tmp, -1); + tmp->word[7] |= carry * 0x80000000; + + if (bn256_is_ge (tmp, tmp)) + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, A, N); + } + else + { + bn256_sub (tmp, tmp, tmp); + borrow = bn256_sub (tmp, tmp, tmp); + if (borrow) + bn256_add (tmp, tmp, tmp); + else + bn256_add (tmp, tmp, N); + } + break; + + case 0: + bn256_shift (tmp, tmp, -1); + if (bn256_is_even (tmp)) + { + bn256_add (tmp, tmp, N); + carry = 0; + } + else + carry = bn256_add (tmp, tmp, N); + + bn256_shift (tmp, tmp, -1); + tmp->word[7] |= carry * 0x80000000; + + bn256_shift (tmp, tmp, -1); + if (bn256_is_even (tmp)) + { + bn256_add (tmp, tmp, N); + carry = 0; + } + else + carry = bn256_add (tmp, tmp, N); + + bn256_shift (tmp, tmp, -1); + tmp->word[7] |= carry * 0x80000000; + + if (bn256_is_ge (u, v)) + { + bn256_sub (u, u, v); + borrow = bn256_sub (A, A, C); + if (borrow) + bn256_add (A, A, N); + else + bn256_add (tmp, A, N); + } + else + { + bn256_sub (v, v, u); + borrow = bn256_sub (C, C, A); + if (borrow) + bn256_add (C, C, N); + else + bn256_add (tmp, C, N); + } + break; + } + } +#undef borrow +} diff --git a/mod.h b/mod.h new file mode 100644 index 0000000..117bcc7 --- /dev/null +++ b/mod.h @@ -0,0 +1,3 @@ +void mod_reduce (bn256 *X, const bn512 *A, const bn256 *B, + const bn256 *MU_lower); +void mod_inv (bn256 *X, const bn256 *A, const bn256 *N); diff --git a/mod25638.c b/mod25638.c new file mode 100644 index 0000000..975a6b4 --- /dev/null +++ b/mod25638.c @@ -0,0 +1,287 @@ +/* + * mod25638.c -- modulo arithmetic of 2^256-38 for 2^255-19 field + * + * Copyright (C) 2014 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 . + * + */ + +/* + * The field is \Z/(2^255-19) + * + * We use radix-32. During computation, it's not reduced to 2^255-19, + * but it is represented in 256-bit (it is redundant representation), + * that is, something like 2^256-38. + * + * The idea is, keeping within 256-bit until it will be converted to + * affine coordinates. + */ + +#include +#include + +#include "bn.h" +#include "mod25638.h" + +#ifndef BN256_C_IMPLEMENTATION +#define ASM_IMPLEMENTATION 0 +#endif + +#if ASM_IMPLEMENTATION +#include "muladd_256.h" +#define ADDWORD_256(d_,s_,w_,c_) \ + asm ( "ldmia %[s]!, { r4, r5, r6, r7 } \n\t" \ + "adds r4, r4, %[w] \n\t" \ + "adcs r5, r5, #0 \n\t" \ + "adcs r6, r6, #0 \n\t" \ + "adcs r7, r7, #0 \n\t" \ + "stmia %[d]!, { r4, r5, r6, r7 }\n\t" \ + "ldmia %[s]!, { r4, r5, r6, r7 } \n\t" \ + "adcs r4, r4, #0 \n\t" \ + "adcs r5, r5, #0 \n\t" \ + "adcs r6, r6, #0 \n\t" \ + "adcs r7, r7, #0 \n\t" \ + "stmia %[d]!, { r4, r5, r6, r7 }\n\t" \ + "mov %[c], #0 \n\t" \ + "adc %[c], %[c], #0" \ + : [s] "=&r" (s_), [d] "=&r" (d_), [c] "=&r" (c_) \ + : "[s]" (s_), "[d]" (d_), [w] "r" (w_) \ + : "r4", "r5", "r6", "r7", "memory", "cc" ) +#endif + +/* +256 224 192 160 128 96 64 32 0 +2^256 + 1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +2^256 - 16 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffff0 +2^256 - 16 - 2 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffee +2^256 - 16 - 2 - 1 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffed +*/ +const bn256 p25519[1] = { + {{ 0xffffffed, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff }} }; + + +/* + * Implementation Note. + * + * It's not always modulo n25638. The representation is redundant + * during computation. For example, when we add the number - 1 and 1, + * it won't overflow to 2^256, and the result is represented within + * 256-bit. + */ + + +/** + * @brief X = (A + B) mod 2^256-38 + */ +void +mod25638_add (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t carry; + + carry = bn256_add (X, A, B); + carry = bn256_add_uint (X, X, carry*38); + X->word[0] += carry * 38; +} + +/** + * @brief X = (A - B) mod 2^256-38 + */ +void +mod25638_sub (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t borrow; + + borrow = bn256_sub (X, A, B); + borrow = bn256_sub_uint (X, X, borrow*38); + X->word[0] -= borrow * 38; +} + + +/** + * @brief X = A mod 2^256-38 + * + * Note that the second argument is not "const bn512 *". + * A is modified during the computation of modulo. + * + * It's not precisely modulo 2^256-38 for all cases, + * but result may be redundant. + */ +static void +mod25638_reduce (bn256 *X, bn512 *A) +{ + const uint32_t *s; + uint32_t *d; + uint32_t w; + +#if ASM_IMPLEMENTATION + uint32_t c, c0; + + s = &A->word[8]; d = &A->word[0]; w = 38; MULADD_256 (s, d, w, c); + c0 = A->word[8] * 38; + d = &X->word[0]; + s = &A->word[0]; + ADDWORD_256 (d, s, c0, c); + X->word[0] += c * 38; +#else + s = &A->word[8]; d = &A->word[0]; w = 38; + { + int i; + uint64_t r; + uint32_t carry; + + r = 0; + for (i = 0; i < BN256_WORDS; i++) + { + uint64_t uv; + + r += d[i]; + carry = (r < d[i]); + + uv = ((uint64_t)s[i])*w; + r += uv; + carry += (r < uv); + + d[i] = (uint32_t)r; + r = ((r >> 32) | ((uint64_t)carry << 32)); + } + + carry = bn256_add_uint (X, (bn256 *)A, r * 38); + X->word[0] += carry * 38; + } +#endif +} + +/** + * @brief X = (A * B) mod 2^256-38 + */ +void +mod25638_mul (bn256 *X, const bn256 *A, const bn256 *B) +{ + bn512 tmp[1]; + + bn256_mul (tmp, A, B); + mod25638_reduce (X, tmp); +} + +/** + * @brief X = A * A mod 2^256-38 + */ +void +mod25638_sqr (bn256 *X, const bn256 *A) +{ + bn512 tmp[1]; + + bn256_sqr (tmp, A); + mod25638_reduce (X, tmp); +} + + +/** + * @brief X = (A << shift) mod 2^256-38 + * @note shift < 32 + */ +void +mod25638_shift (bn256 *X, const bn256 *A, int shift) +{ + uint32_t carry; + bn256 tmp[1]; + + carry = bn256_shift (X, A, shift); + if (shift < 0) + return; + + memset (tmp, 0, sizeof (bn256)); + tmp->word[0] = (carry << 1); + /* tmp->word[1] = (carry >> 31); always zero. */ + tmp->word[0] = tmp->word[0] + (carry << 2); + tmp->word[1] = (tmp->word[0] < (carry << 2)) + (carry >> 30); + tmp->word[0] = tmp->word[0] + (carry << 5); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 5)) + (carry >> 27); + + mod25638_add (X, X, tmp); +} + + +/* + * @brief X = A mod 2^255-19 + * + * It's precisely modulo 2^255-19 (unlike mod25638_reduce). + */ +void +mod25519_reduce (bn256 *X) +{ + uint32_t q; + bn256 r0[1], r1[1]; + int flag; + + memcpy (r0, X, sizeof (bn256)); + q = (r0->word[7] >> 31); + r0->word[7] &= 0x7fffffff; + if (q) + { + bn256_add_uint (r0, r0, 19); + q = (r0->word[7] >> 31); + r0->word[7] &= 0x7fffffff; + if (q) + { + bn256_add_uint (r1, r0, 19); + q = (r1->word[7] >> 31); + r1->word[7] &= 0x7fffffff; + flag = 0; + } + else + flag = 1; + } + else + { + bn256_add_uint (r1, r0, 19); + q = (r1->word[7] >> 31); /* dummy */ + r1->word[7] &= 0x7fffffff; /* dummy */ + if (q) + flag = 2; + else + flag = 3; + } + + if (flag) + { + bn256_add_uint (r1, r0, 19); + q = (r1->word[7] >> 31); + r1->word[7] &= 0x7fffffff; + if (q) + memcpy (X, r1, sizeof (bn256)); + else + memcpy (X, r0, sizeof (bn256)); + } + else + { + if (q) + { + asm volatile ("" : : "r" (q) : "memory"); + memcpy (X, r1, sizeof (bn256)); + asm volatile ("" : : "r" (q) : "memory"); + } + else + memcpy (X, r1, sizeof (bn256)); + } +} diff --git a/mod25638.h b/mod25638.h new file mode 100644 index 0000000..8773611 --- /dev/null +++ b/mod25638.h @@ -0,0 +1,7 @@ +extern const bn256 p25519[1]; + +void mod25638_add (bn256 *X, const bn256 *A, const bn256 *B); +void mod25638_sub (bn256 *X, const bn256 *A, const bn256 *B); +void mod25638_mul (bn256 *X, const bn256 *A, const bn256 *B); +void mod25638_sqr (bn256 *X, const bn256 *A); +void mod25519_reduce (bn256 *X); diff --git a/modp256k1.c b/modp256k1.c new file mode 100644 index 0000000..299647b --- /dev/null +++ b/modp256k1.c @@ -0,0 +1,315 @@ +/* + * modp256k1.c -- modulo arithmetic for p256k1 + * + * Copyright (C) 2014, 2016, 2020 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 . + * + */ + +/* + * p256k1 = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + */ +#include +#include + +#include "bn.h" +#include "modp256k1.h" + +/* +256 224 192 160 128 96 64 32 0 +2^256 + 1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 +2^256 - 2^32 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 00000000 +2^256 - 2^32 - 2^9 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffe00 +2^256 - 2^32 - 2^9 - 2^8 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffd00 +2^256 - 2^32 - 2^9 - 2^8 - 2^7 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc80 +2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc40 +2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc30 +2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1 + 0 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f +*/ +const bn256 p256k1 = { {0xfffffc2f, 0xfffffffe, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff } }; + +/* + * Implementation Note. + * + * It's always modulo p256k1. + * + * Once, I tried redundant representation which caused wrong + * calculation. Implementation could be correct with redundant + * representation, but it found that it's more expensive. + * + */ + +/** + * @brief X = (A + B) mod p256k1 + */ +void +modp256k1_add (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t cond; + bn256 tmp[1]; + bn256 dummy[1]; + + cond = (bn256_add (X, A, B) == 0); + cond &= bn256_sub (tmp, X, P256K1); + memcpy (cond?dummy:X, tmp, sizeof (bn256)); + asm ("" : "=m" (dummy) : "m" (dummy) : "memory"); +} + +/** + * @brief X = (A - B) mod p256 + */ +void +modp256k1_sub (bn256 *X, const bn256 *A, const bn256 *B) +{ + uint32_t borrow; + bn256 tmp[1]; + bn256 dummy[1]; + + borrow = bn256_sub (X, A, B); + bn256_add (tmp, X, P256K1); + memcpy (borrow?X:dummy, tmp, sizeof (bn256)); + asm ("" : "=m" (dummy) : "m" (dummy) : "memory"); +} + +/** + * @brief X = A mod p256k1 + */ +void +modp256k1_reduce (bn256 *X, const bn512 *A) +{ + bn256 tmp[1]; + uint32_t carry; +#define borrow carry + uint32_t s0, s1; +#define s00 tmp->word[0] +#define s01 tmp->word[1] +#define s02 tmp->word[2] + +#define W0 X +#define W1 tmp +#define W2 tmp +#define W3 tmp +#define W4 tmp +#define W5 tmp +#define W6 tmp +#define W7 tmp +#define S tmp + + /* + * Suppose: P256K1 = 2^256 - CONST + * Then, compute: W = A_low + A_high * CONST + * 256-bit W0 = W mod 2^256 + * 64-bit (S1, S0) = W / 2^256 + * where: CONST = 2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1 + */ + + /* W0 = A_low */ + /* W7 = A_high */ + /* W0 += W7 */ + carry = bn256_add (W0, (const bn256 *)&A->word[8], (const bn256 *)A); + + /* W6 = W7 << 4 */ + /* W0 += W6 */ + bn256_shift (W6, (const bn256 *)&A->word[8], 4); + carry += bn256_add (W0, W0, W6); + + /* W5 = W6 << 2 */ + /* W0 += W5 */ + bn256_shift (W5, W6, 2); + carry += bn256_add (W0, W0, W5); + + /* W4 = W5 << 1 */ + /* W0 += W4 */ + bn256_shift (W4, W5, 1); + carry += bn256_add (W0, W0, W4); + + /* W3 = W4 << 1 */ + /* W0 += W3 */ + bn256_shift (W3, W4, 1); + carry += bn256_add (W0, W0, W3); + + /* W2 = W3 << 1 */ + /* W0 += W2 */ + bn256_shift (W2, W3, 1); + carry += bn256_add (W0, W0, W2); + + /* W1 = A_high << 32 */ + /* W0 += W1 */ + W1->word[7] = A->word[14]; + W1->word[6] = A->word[13]; + W1->word[5] = A->word[12]; + W1->word[4] = A->word[11]; + W1->word[3] = A->word[10]; + W1->word[2] = A->word[9]; + W1->word[1] = A->word[8]; + W1->word[0] = 0; + carry += bn256_add (W0, W0, W1); + + /* (S1, S0) = W / 2^256 */ + s0 = A->word[15]; + carry += (s0 >> 28) + (s0 >> 26) + (s0 >> 25) + (s0 >> 24) + (s0 >> 23); + carry += s0; + s1 = (carry < s0) ? 1 : 0; + s0 = carry; + + /* + * Compute: S:=(S02, S01, S00), S = (S1,S0)*CONST + */ + S->word[7] = S->word[6] = S->word[5] = S->word[4] = S->word[3] = 0; + + /* (S02, S01, S00) = (S1, S0) + (S1, S0)*2^32 */ + s00 = s0; + s01 = s0 + s1; + s02 = s1 + ((s01 < s0)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^9 */ + carry = (s0 >> 23) + s01; + s02 += (s1 >> 23) + ((carry < s01)? 1 : 0); + s01 = (s1 << 9) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 9); + carry = ((s00 < (s0 << 9))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^8 */ + carry = (s0 >> 24) + s01; + s02 += (s1 >> 24) + ((carry < s01)? 1 : 0); + s01 = (s1 << 8) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 8); + carry = ((s00 < (s0 << 8))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^7 */ + carry = (s0 >> 25) + s01; + s02 += (s1 >> 25) + ((carry < s01)? 1 : 0); + s01 = (s1 << 7) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 7); + carry = ((s00 < (s0 << 7))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^6 */ + carry = (s0 >> 26) + s01; + s02 += (s1 >> 26) + ((carry < s01)? 1 : 0); + s01 = (s1 << 6) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 6); + carry = ((s00 < (s0 << 6))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* (S02, S01, S00) += (S1, S0)*2^4 */ + carry = (s0 >> 28) + s01; + s02 += (s1 >> 28) + ((carry < s01)? 1 : 0); + s01 = (s1 << 4) + carry; + s02 += ((s01 < carry)? 1 : 0); + s00 += (s0 << 4); + carry = ((s00 < (s0 << 4))? 1 : 0); + s01 += carry; + s02 += ((s01 < carry)? 1 : 0); + + /* W0 += S */ + modp256k1_add (W0, W0, S); + + borrow = bn256_sub (tmp, W0, P256K1); + if (borrow) + memcpy (tmp, W0, sizeof (bn256)); + else + memcpy (W0, tmp, sizeof (bn256)); + +#undef W0 +#undef W1 +#undef W2 +#undef W3 +#undef W4 +#undef W5 +#undef W6 +#undef W7 +#undef S +#undef s00 +#undef s01 +#undef s02 +#undef borrow +} + +/** + * @brief X = (A * B) mod p256k1 + */ +void +modp256k1_mul (bn256 *X, const bn256 *A, const bn256 *B) +{ + bn512 AB[1]; + + bn256_mul (AB, A, B); + modp256k1_reduce (X, AB); +} + +/** + * @brief X = A * A mod p256k1 + */ +void +modp256k1_sqr (bn256 *X, const bn256 *A) +{ + bn512 AA[1]; + + bn256_sqr (AA, A); + modp256k1_reduce (X, AA); +} + + +/** + * @brief X = (A << shift) mod p256k1 + * @note shift < 32 + */ +void +modp256k1_shift (bn256 *X, const bn256 *A, int shift) +{ + uint32_t carry; + bn256 tmp[1]; + + carry = bn256_shift (X, A, shift); + if (shift < 0) + return; + + memset (tmp, 0, sizeof (bn256)); + tmp->word[0] = carry + (carry << 9); + tmp->word[1] = carry + (tmp->word[0] < (carry << 9)) + (carry >> 23); + tmp->word[0] = tmp->word[0] + (carry << 8); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 8)) + (carry >> 24); + tmp->word[0] = tmp->word[0] + (carry << 7); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 7)) + (carry >> 25); + tmp->word[0] = tmp->word[0] + (carry << 6); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 6)) + (carry >> 26); + tmp->word[0] = tmp->word[0] + (carry << 4); + tmp->word[1] = tmp->word[1] + (tmp->word[0] < (carry << 4)) + (carry >> 28); + + modp256k1_add (X, X, tmp); +} diff --git a/modp256k1.h b/modp256k1.h new file mode 100644 index 0000000..3e20ab5 --- /dev/null +++ b/modp256k1.h @@ -0,0 +1,9 @@ +extern const bn256 p256k1; +#define P256K1 (&p256k1) + +void modp256k1_add (bn256 *X, const bn256 *A, const bn256 *B); +void modp256k1_sub (bn256 *X, const bn256 *A, const bn256 *B); +void modp256k1_reduce (bn256 *X, const bn512 *A); +void modp256k1_mul (bn256 *X, const bn256 *A, const bn256 *B); +void modp256k1_sqr (bn256 *X, const bn256 *A); +void modp256k1_shift (bn256 *X, const bn256 *A, int shift); diff --git a/muladd_256.h b/muladd_256.h new file mode 100644 index 0000000..f37d976 --- /dev/null +++ b/muladd_256.h @@ -0,0 +1,50 @@ +#define MULADD_256_ASM(s_,d_,w_,c_) \ + asm ( "ldmia %[s]!, { r8, r9, r10 } \n\t" \ + "ldmia %[d], { r5, r6, r7 } \n\t" \ + "umull r4, r8, %[w], r8 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, r8 \n\t" \ + "umull r4, r8, %[w], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adcs r7, r7, %[c] \n\t" \ + "umull r4, r8, %[w], r10 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r7, r7, r4 \n\t" \ + "stmia %[d]!, { r5, r6, r7 } \n\t" \ + "ldmia %[s]!, { r8, r9, r10 } \n\t" \ + "ldmia %[d], { r5, r6, r7 } \n\t" \ + "adcs r5, r5, %[c] \n\t" \ + "umull r4, r8, %[w], r8 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, %[w], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adcs r7, r7, %[c] \n\t" \ + "umull r4, r8, %[w], r10 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r7, r7, r4 \n\t" \ + "stmia %[d]!, { r5, r6, r7 } \n\t" \ + "ldmia %[s]!, { r8, r9 } \n\t" \ + "ldmia %[d], { r5, r6 } \n\t" \ + "adcs r5, r5, %[c] \n\t" \ + "umull r4, r8, %[w], r8 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, %[w], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adc %[c], %[c], #0 \n\t" \ + "stmia %[d]!, { r5, r6 }" \ + : [s] "=&r" (s_), [d] "=&r" (d_), [c] "=&r" (c_) \ + : "[s]" (s_), "[d]" (d_), [w] "r" (w_) \ + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "memory", "cc" ) + +#define MULADD_256(s__,d__,w__,c__) do { \ + MULADD_256_ASM(s__,d__,w__,c__); \ + *d__ = c__; \ +} while (0) diff --git a/openpgp-do.c b/openpgp-do.c new file mode 100644 index 0000000..f76ce1e --- /dev/null +++ b/openpgp-do.c @@ -0,0 +1,2637 @@ +/* + * openpgp-do.c -- OpenPGP card Data Objects (DO) handling + * + * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, + * 2020, 2021 + * Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "config.h" + +#include "sys.h" +#include "gnuk.h" +#include "status-code.h" +#include "random.h" +#include "polarssl/config.h" +#include "polarssl/aes.h" +#include "sha512.h" +#include "shake256.h" + +/* Forward declaration */ +#define CLEAN_PAGE_FULL 1 +#define CLEAN_SINGLE 0 +static void gpg_do_delete_prvkey (enum kind_of_key kk, int clean_page_full); +static void gpg_reset_digital_signature_counter (void); + +#define PASSWORD_ERRORS_MAX 3 /* >= errors, it will be locked */ +static const uint8_t *pw_err_counter_p[3]; + +static int +gpg_pw_get_err_counter (uint8_t which) +{ + return flash_cnt123_get_value (pw_err_counter_p[which]); +} + +int +gpg_pw_get_retry_counter (int who) +{ + if (who == 0x81 || who == 0x82) + return PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW1); + else if (who == 0x83) + return PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW3); + else + return PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_RC); +} + +int +gpg_pw_locked (uint8_t which) +{ + if (gpg_pw_get_err_counter (which) >= PASSWORD_ERRORS_MAX) + return 1; + else + return 0; +} + +void +gpg_pw_reset_err_counter (uint8_t which) +{ + flash_cnt123_clear (&pw_err_counter_p[which]); + if (pw_err_counter_p[which] != NULL) + GPG_MEMORY_FAILURE (); +} + +void +gpg_pw_increment_err_counter (uint8_t which) +{ + flash_cnt123_increment (which, &pw_err_counter_p[which]); +} + + +uint16_t data_objects_number_of_bytes; + +/* + * Compile time vars: + * Historical Bytes, Extended Capabilities. + */ + +/* Historical Bytes */ +const uint8_t historical_bytes[] __attribute__ ((aligned (1))) = { + 10, + 0x00, + 0x31, 0x84, /* Full DF name, GET DATA, MF */ + 0x73, + 0x80, 0x01, 0x80, /* Full DF name */ + /* 1-byte */ + /* Command chaining, No extended Lc and Le */ +#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT + 0x05, +#else + 0x00, +#endif + 0x90, 0x00 /* Status info */ +}; + +/* Extended Capabilities */ +static const uint8_t extended_capabilities[] __attribute__ ((aligned (1))) = { + 10, + 0x75, /* + * No Secure Messaging supported + * GET CHALLENGE supported + * Key import supported + * PW status byte can be put + * No private_use_DO + * Algorithm attrs are changable + * No DEC with AES + * KDF-DO available + */ + 0, /* Secure Messaging Algorithm: N/A (TDES=0, AES=1) */ + 0x00, CHALLENGE_LEN, /* Max size of GET CHALLENGE */ +#ifdef CERTDO_SUPPORT + 0x08, 0x00, /* max. length of cardholder certificate (2KiB) */ +#else + 0x00, 0x00, +#endif + /* Max. length of command APDU data */ + 0x00, 0xff, + /* Max. length of response APDU data */ + 0x01, 0x00, +}; + +#ifdef ACKBTN_SUPPORT +/* General Feature Management */ +static const uint8_t feature_mngmnt[] __attribute__ ((aligned (1))) = { + 3, + 0x81, 0x01, 0x20, +}; +#endif + +/* Algorithm Attributes */ +#define OPENPGP_ALGO_RSA 0x01 +#define OPENPGP_ALGO_ECDH 0x12 +#define OPENPGP_ALGO_ECDSA 0x13 +#define OPENPGP_ALGO_EDDSA 0x16 /* It catches 22, finally. */ + +static const uint8_t algorithm_attr_ed448[] __attribute__ ((aligned (1))) = { + 4, + OPENPGP_ALGO_EDDSA, + /* OID of Ed448 */ + 0x2b, 0x65, 0x71 +}; + +static const uint8_t algorithm_attr_x448[] __attribute__ ((aligned (1))) = { + 4, + OPENPGP_ALGO_ECDH, + /* OID of X448 */ + 0x2b, 0x65, 0x6f +}; + +static const uint8_t algorithm_attr_rsa2k[] __attribute__ ((aligned (1))) = { + 6, + OPENPGP_ALGO_RSA, + 0x08, 0x00, /* Length modulus (in bit): 2048 */ + 0x00, 0x20, /* Length exponent (in bit): 32 */ + 0x00 /* 0: Acceptable format is: P and Q */ +}; + +static const uint8_t algorithm_attr_rsa4k[] __attribute__ ((aligned (1))) = { + 6, + OPENPGP_ALGO_RSA, + 0x10, 0x00, /* Length modulus (in bit): 4096 */ + 0x00, 0x20, /* Length exponent (in bit): 32 */ + 0x00 /* 0: Acceptable format is: P and Q */ +}; + +static const uint8_t algorithm_attr_p256k1[] __attribute__ ((aligned (1))) = { + 6, + OPENPGP_ALGO_ECDSA, + 0x2b, 0x81, 0x04, 0x00, 0x0a /* OID of curve secp256k1 */ +}; + +static const uint8_t algorithm_attr_ed25519[] __attribute__ ((aligned (1))) = { + 10, + OPENPGP_ALGO_EDDSA, + /* OID of the curve Ed25519 */ + 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 +}; + +static const uint8_t algorithm_attr_cv25519[] __attribute__ ((aligned (1))) = { + 11, + OPENPGP_ALGO_ECDH, + /* OID of the curve Curve25519 */ + 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 +}; + + +/* + * Representation of PW1_LIFETIME: + * 0: PW1_LIEFTIME_P == NULL : PW1 is valid for single PSO:CDS command + * 1: PW1_LIEFTIME_P != NULL : PW1 is valid for several PSO:CDS commands + * + * The address in the variable PW1_LIEFTIME_P is used when filling zero + * in flash memory + */ +static const uint8_t *pw1_lifetime_p; + +static int +gpg_get_pw1_lifetime (void) +{ + if (pw1_lifetime_p == NULL) + return 0; + else + return 1; +} + + +/* + * Representation of algorithm attributes: + * 0: ALGO_ATTR_<>_P == NULL : RSA-2048 + * N: ALGO_ATTR_<>_P != NULL : + * + */ +static const uint8_t *algo_attr_sig_p; +static const uint8_t *algo_attr_dec_p; +static const uint8_t *algo_attr_aut_p; + +static const uint8_t ** +get_algo_attr_pointer (enum kind_of_key kk) +{ + if (kk == GPG_KEY_FOR_SIGNING) + return &algo_attr_sig_p; + else if (kk == GPG_KEY_FOR_DECRYPTION) + return &algo_attr_dec_p; + else + return &algo_attr_aut_p; +} + +static int +kk_to_nr (enum kind_of_key kk) +{ + int nr; + + if (kk == GPG_KEY_FOR_SIGNING) + nr = NR_KEY_ALGO_ATTR_SIG; + else if (kk == GPG_KEY_FOR_DECRYPTION) + nr = NR_KEY_ALGO_ATTR_DEC; + else + nr = NR_KEY_ALGO_ATTR_AUT; + + return nr; +} + +int +gpg_get_algo_attr (enum kind_of_key kk) +{ + const uint8_t *algo_attr_p = *get_algo_attr_pointer (kk); + + if (algo_attr_p == NULL) + return ALGO_RSA2K; + + return algo_attr_p[1]; +} + +static void +gpg_reset_algo_attr (enum kind_of_key kk) +{ + gpg_do_delete_prvkey (kk, CLEAN_PAGE_FULL); + if (kk == GPG_KEY_FOR_SIGNING) + { + gpg_reset_digital_signature_counter (); + gpg_do_write_simple (NR_DO_FP_SIG, NULL, 0); + gpg_do_write_simple (NR_DO_KGTIME_SIG, NULL, 0); + } + else if (kk == GPG_KEY_FOR_DECRYPTION) + { + gpg_do_write_simple (NR_DO_FP_DEC, NULL, 0); + gpg_do_write_simple (NR_DO_KGTIME_DEC, NULL, 0); + } + else + { + gpg_do_write_simple (NR_DO_FP_AUT, NULL, 0); + gpg_do_write_simple (NR_DO_KGTIME_AUT, NULL, 0); + } +} + +static const uint8_t * +get_algo_attr_data_object (enum kind_of_key kk) +{ + const uint8_t *algo_attr_p = *get_algo_attr_pointer (kk); + + if (algo_attr_p == NULL) + return algorithm_attr_rsa2k; + + switch (algo_attr_p[1]) + { + case ALGO_RSA4K: + return algorithm_attr_rsa4k; + case ALGO_SECP256K1: + return algorithm_attr_p256k1; + case ALGO_CURVE25519: + return algorithm_attr_cv25519; + case ALGO_ED25519: + return algorithm_attr_ed25519; + case ALGO_ED448: + return algorithm_attr_ed448; + case ALGO_X448: + return algorithm_attr_x448; + default: + return algorithm_attr_rsa2k; + } +} + +int +gpg_get_algo_attr_key_size (enum kind_of_key kk, enum size_of_key s) +{ + const uint8_t *algo_attr_p = *get_algo_attr_pointer (kk); + + if (algo_attr_p == NULL) /* RSA-2048 */ + goto rsa2k; + + switch (algo_attr_p[1]) + { + case ALGO_RSA4K: + if (s == GPG_KEY_STORAGE) + return 1024; + else + return 512; + case ALGO_SECP256K1: + if (s == GPG_KEY_STORAGE) + return 128; + else if (s == GPG_KEY_PUBLIC) + return 64; + else + return 32; + case ALGO_CURVE25519: + if (s == GPG_KEY_STORAGE) + return 64; + else + return 32; + case ALGO_ED25519: + if (s == GPG_KEY_STORAGE) + return 128; + else if (s == GPG_KEY_PUBLIC) + return 32; + else + return 64; + case ALGO_ED448: + if (s == GPG_KEY_STORAGE) + return 256; + else if (s == GPG_KEY_PUBLIC) + return 57; + else + return 128; + case ALGO_X448: + if (s == GPG_KEY_STORAGE) + return 112; + else + return 56; + default: + rsa2k: + if (s == GPG_KEY_STORAGE) + return 512; + else + return 256; + } +} + + +static uint32_t digital_signature_counter; + +static const uint8_t * +gpg_write_digital_signature_counter (const uint8_t *p, uint32_t dsc) +{ + uint16_t hw0, hw1; + + if ((dsc >> 10) == 0) + { /* no upper bits */ + hw1 = NR_COUNTER_DS_LSB | ((dsc & 0x0300) >> 8) | ((dsc & 0x00ff) << 8); + flash_put_data_internal (p, hw1); + return p+2; + } + else + { + hw0 = NR_COUNTER_DS | ((dsc & 0xfc0000) >> 18) | ((dsc & 0x03fc00) >> 2); + hw1 = NR_COUNTER_DS_LSB | ((dsc & 0x0300) >> 8) | ((dsc & 0x00ff) << 8); + flash_put_data_internal (p, hw0); + flash_put_data_internal (p+2, hw1); + return p+4; + } +} + +static void +gpg_reset_digital_signature_counter (void) +{ + if (digital_signature_counter != 0) + { + flash_put_data (NR_COUNTER_DS); + flash_put_data (NR_COUNTER_DS_LSB); + digital_signature_counter = 0; + } +} + +void +gpg_increment_digital_signature_counter (void) +{ + uint16_t hw0, hw1; + uint32_t dsc = (digital_signature_counter + 1) & 0x00ffffff; + + if ((dsc & 0x03ff) == 0) + { /* carry occurs from l10 to h14 */ + hw0 = NR_COUNTER_DS | ((dsc & 0xfc0000) >> 18) | ((dsc & 0x03fc00) >> 2); + hw1 = NR_COUNTER_DS_LSB; /* zero */ + flash_put_data (hw0); + flash_put_data (hw1); + } + else + { + hw1 = NR_COUNTER_DS_LSB | ((dsc & 0x0300) >> 8) | ((dsc & 0x00ff) << 8); + flash_put_data (hw1); + } + + digital_signature_counter = dsc; + + if (gpg_get_pw1_lifetime () == 0) + ac_reset_pso_cds (); +} + + +#define SIZE_FINGER_PRINT 20 +#define SIZE_KEYGEN_TIME 4 /* RFC4880 */ + +enum do_type { + DO_FIXED, + DO_VAR, + DO_CMP_READ, + DO_PROC_READ, + DO_PROC_WRITE, + DO_PROC_READWRITE, +}; + +struct do_table_entry { + uint16_t tag; + enum do_type do_type; + uint8_t ac_read; + uint8_t ac_write; + const void *obj; +}; + +static uint8_t *res_p; + +static void copy_do_1 (uint16_t tag, const uint8_t *do_data, int with_tag); +static const struct do_table_entry *get_do_entry (uint16_t tag); + +#define GPG_DO_AID 0x004f +#define GPG_DO_NAME 0x005b +#define GPG_DO_LOGIN_DATA 0x005e +#define GPG_DO_CH_DATA 0x0065 +#define GPG_DO_APP_DATA 0x006e +#define GPG_DO_DISCRETIONARY 0x0073 +#define GPG_DO_SS_TEMP 0x007a +#define GPG_DO_DS_COUNT 0x0093 +#define GPG_DO_EXTCAP 0x00c0 +#define GPG_DO_ALG_SIG 0x00c1 +#define GPG_DO_ALG_DEC 0x00c2 +#define GPG_DO_ALG_AUT 0x00c3 +#define GPG_DO_PW_STATUS 0x00c4 +#define GPG_DO_FP_ALL 0x00c5 +#define GPG_DO_CAFP_ALL 0x00c6 +#define GPG_DO_FP_SIG 0x00c7 +#define GPG_DO_FP_DEC 0x00c8 +#define GPG_DO_FP_AUT 0x00c9 +#define GPG_DO_CAFP_1 0x00ca +#define GPG_DO_CAFP_2 0x00cb +#define GPG_DO_CAFP_3 0x00cc +#define GPG_DO_KGTIME_ALL 0x00cd +#define GPG_DO_KGTIME_SIG 0x00ce +#define GPG_DO_KGTIME_DEC 0x00cf +#define GPG_DO_KGTIME_AUT 0x00d0 +#define GPG_DO_RESETTING_CODE 0x00d3 +#define GPG_DO_UIF_SIG 0x00d6 +#define GPG_DO_UIF_DEC 0x00d7 +#define GPG_DO_UIF_AUT 0x00d8 +#define GPG_DO_KDF 0x00f9 +#define GPG_DO_ALG_INFO 0x00fa +#define GPG_DO_KEY_IMPORT 0x3fff +#define GPG_DO_LANGUAGE 0x5f2d +#define GPG_DO_SEX 0x5f35 +#define GPG_DO_URL 0x5f50 +#define GPG_DO_HIST_BYTES 0x5f52 +#define GPG_DO_CH_CERTIFICATE 0x7f21 +#define GPG_DO_FEATURE_MNGMNT 0x7f74 + +static const uint8_t *do_ptr[NR_DO__LAST__]; + +static int +do_tag_to_nr (uint16_t tag) +{ + switch (tag) + { + case GPG_DO_SEX: + return NR_DO_SEX; + case GPG_DO_FP_SIG: + return NR_DO_FP_SIG; + case GPG_DO_FP_DEC: + return NR_DO_FP_DEC; + case GPG_DO_FP_AUT: + return NR_DO_FP_AUT; + case GPG_DO_CAFP_1: + return NR_DO_CAFP_1; + case GPG_DO_CAFP_2: + return NR_DO_CAFP_2; + case GPG_DO_CAFP_3: + return NR_DO_CAFP_3; + case GPG_DO_KGTIME_SIG: + return NR_DO_KGTIME_SIG; + case GPG_DO_KGTIME_DEC: + return NR_DO_KGTIME_DEC; + case GPG_DO_KGTIME_AUT: + return NR_DO_KGTIME_AUT; + case GPG_DO_LOGIN_DATA: + return NR_DO_LOGIN_DATA; + case GPG_DO_URL: + return NR_DO_URL; + case GPG_DO_NAME: + return NR_DO_NAME; + case GPG_DO_LANGUAGE: + return NR_DO_LANGUAGE; + case GPG_DO_KDF: + return NR_DO_KDF; + default: + return -1; + } +} + +static void +copy_tag (uint16_t tag) +{ + if (tag < 0x0100) + *res_p++ = (tag & 0xff); + else + { + *res_p++ = (tag >> 8); + *res_p++ = (tag & 0xff); + } +} + + +#define SIZE_FP 20 +#define SIZE_KGTIME 4 + +static void +do_fp_all (uint16_t tag, int with_tag) +{ + const uint8_t *data; + + if (with_tag) + { + copy_tag (tag); + *res_p++ = SIZE_FP*3; + } + + data = gpg_do_read_simple (NR_DO_FP_SIG); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + + data = gpg_do_read_simple (NR_DO_FP_DEC); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + + data = gpg_do_read_simple (NR_DO_FP_AUT); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; +} + +static void +do_cafp_all (uint16_t tag, int with_tag) +{ + const uint8_t *data; + + if (with_tag) + { + copy_tag (tag); + *res_p++ = SIZE_FP*3; + } + + data = gpg_do_read_simple (NR_DO_CAFP_1); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + + data = gpg_do_read_simple (NR_DO_CAFP_2); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; + + data = gpg_do_read_simple (NR_DO_CAFP_2); + if (data) + memcpy (res_p, data, SIZE_FP); + else + memset (res_p, 0, SIZE_FP); + res_p += SIZE_FP; +} + +static void +do_kgtime_all (uint16_t tag, int with_tag) +{ + const uint8_t *data; + + if (with_tag) + { + copy_tag (tag); + *res_p++ = SIZE_KGTIME*3; + } + + data = gpg_do_read_simple (NR_DO_KGTIME_SIG); + if (data) + memcpy (res_p, data, SIZE_KGTIME); + else + memset (res_p, 0, SIZE_KGTIME); + res_p += SIZE_KGTIME; + + data = gpg_do_read_simple (NR_DO_KGTIME_DEC); + if (data) + memcpy (res_p, data, SIZE_KGTIME); + else + memset (res_p, 0, SIZE_KGTIME); + res_p += SIZE_KGTIME; + + data = gpg_do_read_simple (NR_DO_KGTIME_AUT); + if (data) + memcpy (res_p, data, SIZE_KGTIME); + else + memset (res_p, 0, SIZE_KGTIME); + res_p += SIZE_KGTIME; +} + +const uint8_t openpgpcard_aid[] = { + 0xd2, 0x76, /* D: National, 276: DEU ISO 3166-1 */ + 0x00, 0x01, 0x24, /* Registered Application Provider Identifier */ + 0x01, /* Application: OpenPGPcard */ + 0x02, 0x00, /* Version 2.0 */ + /* v. id */ /* serial number */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* To be overwritten */ +}; + +static void +do_openpgpcard_aid (uint16_t tag, int with_tag) +{ + const volatile uint8_t *p = openpgpcard_aid; + uint16_t vid = (p[8] << 8) | p[9]; + + if (with_tag) + { + copy_tag (tag); + *res_p++ = 16; + } + + if (vid == 0xffff || vid == 0x0000) + { + const uint8_t *u = unique_device_id () + (MHZ < 96 ? 8: 0); + + memcpy (res_p, openpgpcard_aid, 8); + res_p += 8; + + /* vid == 0xfffe: serial number is four random bytes */ + *res_p++ = 0xff; + *res_p++ = 0xfe; + + *res_p++ = u[3]; + *res_p++ = u[2]; + *res_p++ = u[1]; + *res_p++ = u[0]; + } + else + { + memcpy (res_p, openpgpcard_aid, 14); + res_p += 14; + } + + *res_p++ = 0; + *res_p++ = 0; +} + +static void +do_ds_count (uint16_t tag, int with_tag) +{ + if (with_tag) + { + copy_tag (tag); + *res_p++ = 3; + } + + *res_p++ = (digital_signature_counter >> 16) & 0xff; + *res_p++ = (digital_signature_counter >> 8) & 0xff; + *res_p++ = digital_signature_counter & 0xff; +} + +static void +do_alg_info (uint16_t tag, int with_tag) +{ + uint8_t *len_p = NULL; + int i; + + if (with_tag) + { + copy_tag (tag); + len_p = res_p; + *res_p++ = 0; /* Filled later, assuming length is <= 127 */ + } + + for (i = 0; i < 3; i++) + { + uint16_t tag_algo = GPG_DO_ALG_SIG + i; + + copy_do_1 (tag_algo, algorithm_attr_rsa2k, 1); + copy_do_1 (tag_algo, algorithm_attr_rsa4k, 1); + copy_do_1 (tag_algo, algorithm_attr_p256k1, 1); + if (i == 0 || i == 2) + { + copy_do_1 (tag_algo, algorithm_attr_ed25519, 1); + copy_do_1 (tag_algo, algorithm_attr_ed448, 1); + } + if (i == 1) + { + copy_do_1 (tag_algo, algorithm_attr_cv25519, 1); + copy_do_1 (tag_algo, algorithm_attr_x448, 1); + } + }; + + if (len_p) + *len_p = res_p - len_p - 1; /* Actually, it's 127-byte long. */ +} + +static int +rw_pw_status (uint16_t tag, int with_tag, + const uint8_t *data, int len, int is_write) +{ + if (is_write) + { + if (len != 1) + return 0; /* Failure */ + + /* The first byte of DATA specifies the lifetime. */ + if (data[0] == 0 && pw1_lifetime_p != NULL) + { + flash_bool_clear (&pw1_lifetime_p); + if (pw1_lifetime_p != NULL) /* No change after update */ + return 0; + } + else if (pw1_lifetime_p == NULL) + { + pw1_lifetime_p = flash_bool_write (NR_BOOL_PW1_LIFETIME); + if (pw1_lifetime_p == NULL) /* No change after update */ + return 0; + } + + return 1; /* Success */ + } + else + { + if (with_tag) + { + copy_tag (tag); + *res_p++ = SIZE_PW_STATUS_BYTES; + } + + *res_p++ = gpg_get_pw1_lifetime (); + *res_p++ = PW_LEN_MAX; + *res_p++ = PW_LEN_MAX; + *res_p++ = PW_LEN_MAX; + *res_p++ = PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW1); + *res_p++ = PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_RC); + *res_p++ = PASSWORD_ERRORS_MAX - gpg_pw_get_err_counter (PW_ERR_PW3); + return 1; + } +} + +static int +rw_algorithm_attr (uint16_t tag, int with_tag, + const uint8_t *data, int len, int is_write) +{ + enum kind_of_key kk; + + if (tag == GPG_DO_ALG_SIG) + kk = GPG_KEY_FOR_SIGNING; + else if (tag == GPG_DO_ALG_DEC) + kk = GPG_KEY_FOR_DECRYPTION; + else + kk = GPG_KEY_FOR_AUTHENTICATION; + + if (is_write) + { + int algo = -1; + const uint8_t **algo_attr_pp = get_algo_attr_pointer (kk); + + if (len == 4) + { + if (memcmp (data, algorithm_attr_ed448+1, 4) == 0) + algo = ALGO_ED448; + else if (memcmp (data, algorithm_attr_x448+1, 4) == 0) + algo = ALGO_X448; + } + if (len == 6) + { + if (memcmp (data, algorithm_attr_rsa2k+1, 6) == 0) + algo = ALGO_RSA2K; + else if (memcmp (data, algorithm_attr_rsa4k+1, 6) == 0) + algo = ALGO_RSA4K; + else if ((tag != GPG_DO_ALG_DEC + && memcmp (data, algorithm_attr_p256k1+1, 6) == 0) + || (tag == GPG_DO_ALG_DEC && data[0]==OPENPGP_ALGO_ECDH + && memcmp (data+1, algorithm_attr_p256k1+2, 5) == 0)) + algo = ALGO_SECP256K1; + } + else if (len == 10 && memcmp (data, algorithm_attr_ed25519+1, 10) == 0) + algo = ALGO_ED25519; + else if (len == 11 && memcmp (data, algorithm_attr_cv25519+1, 11) == 0) + algo = ALGO_CURVE25519; + + if (algo < 0) + return 0; /* Error. */ + else if (algo == ALGO_RSA2K && *algo_attr_pp != NULL) + { + gpg_reset_algo_attr (kk); + /* Read it again, since GC may occur. */ + algo_attr_pp = get_algo_attr_pointer (kk); + flash_enum_clear (algo_attr_pp); + if (*algo_attr_pp != NULL) + return 0; + } + else if ((algo != ALGO_RSA2K && *algo_attr_pp == NULL) || + (*algo_attr_pp != NULL && (*algo_attr_pp)[1] != algo)) + { + gpg_reset_algo_attr (kk); + /* Read it again, since GC may occur. */ + algo_attr_pp = get_algo_attr_pointer (kk); + if (*algo_attr_pp) + flash_enum_clear (algo_attr_pp); + *algo_attr_pp = flash_enum_write (kk_to_nr (kk), algo); + if (*algo_attr_pp == NULL) + return 0; + } + + return 1; + } + else + { + const uint8_t *algo_attr_do = get_algo_attr_data_object (kk); + + copy_do_1 (tag, algo_attr_do, with_tag); + /* Override the byte when GPG_DO_ALG_DEC. */ + if (tag == GPG_DO_ALG_DEC && algo_attr_do[1] == OPENPGP_ALGO_ECDSA) + *(res_p - algo_attr_do[0]) = OPENPGP_ALGO_ECDH; + return 1; + } +} + + +static uint8_t uif_flags; /* Six bits of flags */ + +#ifdef ACKBTN_SUPPORT +int +gpg_do_get_uif (enum kind_of_key kk) +{ + return ((uif_flags >> (kk * 2)) & 3) != 0; +} + +static int +rw_uif (uint16_t tag, int with_tag, const uint8_t *data, int len, int is_write) +{ + uint8_t nr; + int v; + + if (tag != GPG_DO_UIF_SIG && tag != GPG_DO_UIF_DEC && tag != GPG_DO_UIF_AUT) + return 0; /* Failure */ + + nr = (tag - GPG_DO_UIF_SIG) + NR_DO_UIF_SIG; + v = (uif_flags >> ((tag - GPG_DO_UIF_SIG) * 2)) & 3; + if (is_write) + { + const uint8_t *p; + + if (len != 2 || data[1] != 0x20) + return 0; + + if (v == 2) + return 0; + + if (data[0] != 0x00 && data[0] != 0x01 && data[0] != 0x02) + return 0; + + p = flash_enum_write (nr, data[0]); + if (p == NULL) + return 0; + + uif_flags &= ~(3 << ((nr - NR_DO_UIF_SIG) * 2)); + uif_flags |= (data[0] & 3) << ((nr - NR_DO_UIF_SIG) * 2); + return 1; + } + else + { + if (with_tag) + { + copy_tag (tag); + *res_p++ = 2; + } + + *res_p++ = v; + *res_p++ = 0x20; + return 1; + } +} +#endif + + +#define SIZE_OF_KDF_DO_MIN 90 +#define SIZE_OF_KDF_DO_MAX 110 +#define OPENPGP_KDF_ITERSALTED_S2K 3 +#define OPENPGP_SHA256 8 + +static int +rw_kdf (uint16_t tag, int with_tag, const uint8_t *data, int len, int is_write) +{ + if (tag != GPG_DO_KDF) + return 0; /* Failure */ + + if (is_write) + { + const uint8_t **do_data_p = (const uint8_t **)&do_ptr[NR_DO_KDF]; + + /* KDF DO can be changed only when no keys are registered. */ + if (do_ptr[NR_DO_PRVKEY_SIG] || do_ptr[NR_DO_PRVKEY_DEC] + || do_ptr[NR_DO_PRVKEY_AUT]) + return 0; + + /* The valid data format is: + Deleting: + nothing + Minimum (for admin-less): + 81 01 03 = KDF_ITERSALTED_S2K + 82 01 08 = SHA256 + 83 04 4-byte... = count + 84 08 8-byte... = salt + 87 20 32-byte user hash + 88 20 32-byte admin hash + Full: + 81 01 03 = KDF_ITERSALTED_S2K + 82 01 08 = SHA256 + 83 04 4-byte... = count + 84 08 8-byte... = salt user + 85 08 8-byte... = salt reset-code + 86 08 8-byte... = salt admin + 87 20 32-byte user hash + 88 20 32-byte admin hash + */ + if (!(len == 0 + || (len == SIZE_OF_KDF_DO_MIN && + (data[0] == 0x81 && data[3] == 0x82 && data[6] == 0x83 + && data[12] == 0x84 && data[22] == 0x87 && data[56] == 0x88)) + || (len == SIZE_OF_KDF_DO_MAX && + (data[0] == 0x81 && data[3] == 0x82 && data[6] == 0x83 + && data[12] == 0x84 && data[22] == 0x85 && data[32] == 0x86 + && data[42] == 0x87 && data[76] == 0x88)))) + return 0; + + if (*do_data_p) + flash_do_release (*do_data_p); + + /* Clear all keystrings and auth states */ + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, NULL, 0); + gpg_do_write_simple (NR_DO_KEYSTRING_RC, NULL, 0); + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0); + ac_reset_admin (); + ac_reset_pso_cds (); + ac_reset_other (); + + if (len == 0) + { + *do_data_p = NULL; + return 1; + } + else + { + *do_data_p = flash_do_write (NR_DO_KDF, data, len); + if (*do_data_p) + return 1; + else + return 0; + } + } + else + { + if (do_ptr[NR_DO_KDF]) + copy_do_1 (tag, do_ptr[NR_DO_KDF], with_tag); + else + return 0; + + return 1; + } +} + + +/* + * Check LEN is valid for HOW_MANY of passphrase string. + * + * HOW_MANY = 1: LEN is valid for a single passphrase string. + * HOW_MANY = 2: LEN is valid for two single passphrase strings. + * This is used to change passphrase. + * The second passphrase may be nothing. + * + * LEN = 0: Check if KDF-DO is available. + */ +int +gpg_do_kdf_check (int len, int how_many) +{ + const uint8_t *kdf_do = do_ptr[NR_DO_KDF]; + + if (len == 0) + return kdf_do != NULL; + + if (kdf_do) + { + const uint8_t *kdf_spec = kdf_do+1; + int kdf_do_len = kdf_do[0]; + int hash_len; + + if (kdf_do_len == SIZE_OF_KDF_DO_MIN) + hash_len = kdf_spec[23]; + else + hash_len = kdf_spec[43]; + + if ((hash_len * how_many) != len && hash_len != len) + return 0; + } + + return 1; +} + +void +gpg_do_get_initial_pw_setting (int is_pw3, int *r_len, const uint8_t **r_p) +{ + const uint8_t *kdf_do = do_ptr[NR_DO_KDF]; + + if (kdf_do) + { + int len = kdf_do[0]; + const uint8_t *kdf_spec = kdf_do+1; + + *r_len = 32; + + if (len == SIZE_OF_KDF_DO_MIN) + { + if (is_pw3) + *r_p = kdf_spec + 58; + else + *r_p = kdf_spec + 24; + } + else + { + if (is_pw3) + *r_p = kdf_spec + 78; + else + *r_p = kdf_spec + 44; + } + } + else + { + if (is_pw3) + { + *r_len = strlen (OPENPGP_CARD_INITIAL_PW3); + *r_p = (const uint8_t *)OPENPGP_CARD_INITIAL_PW3; + } + else + { + *r_len = strlen (OPENPGP_CARD_INITIAL_PW1); + *r_p = (const uint8_t *)OPENPGP_CARD_INITIAL_PW1; + } + } +} + +static int +proc_resetting_code (const uint8_t *data, int len) +{ + const uint8_t *old_ks = keystring_md_pw3; + uint8_t new_ks0[KEYSTRING_SIZE]; + uint8_t *new_ks = KS_GET_KEYSTRING (new_ks0); + const uint8_t *newpw; + int newpw_len; + int r; + uint8_t *salt = KS_GET_SALT (new_ks0); + + DEBUG_INFO ("Resetting Code!\r\n"); + + if (len == 0) + { /* Removal of resetting code. */ + enum kind_of_key kk0; + + for (kk0 = 0; kk0 <= GPG_KEY_FOR_AUTHENTICATION; kk0++) + gpg_do_chks_prvkey (kk0, BY_RESETCODE, NULL, 0, NULL); + gpg_do_write_simple (NR_DO_KEYSTRING_RC, NULL, 0); + } + else + { + if (gpg_do_kdf_check (len, 1) == 0) + return 0; + + newpw_len = len; + newpw = data; + new_ks0[0] = newpw_len; + random_get_salt (salt); + s2k (salt, SALT_SIZE, newpw, newpw_len, new_ks); + r = gpg_change_keystring (admin_authorized, old_ks, BY_RESETCODE, new_ks); + if (r <= -2) + { + DEBUG_INFO ("memory error.\r\n"); + return 0; + } + else if (r < 0) + { + DEBUG_INFO ("security error.\r\n"); + return 0; + } + else if (r == 0) + { + DEBUG_INFO ("error (no prvkey).\r\n"); + return 0; + } + else + { + DEBUG_INFO ("done.\r\n"); + gpg_do_write_simple (NR_DO_KEYSTRING_RC, new_ks0, KS_META_SIZE); + } + } + + gpg_pw_reset_err_counter (PW_ERR_RC); + return 1; +} + +static void +encrypt (const uint8_t *key, const uint8_t *iv, uint8_t *data, int len) +{ + aes_context aes; + uint8_t iv0[INITIAL_VECTOR_SIZE]; + size_t iv_offset; + + DEBUG_INFO ("ENC\r\n"); + DEBUG_BINARY (data, len); + + aes_setkey_enc (&aes, key, 128); + memcpy (iv0, iv, INITIAL_VECTOR_SIZE); + iv_offset = 0; + aes_crypt_cfb128 (&aes, AES_ENCRYPT, len, &iv_offset, iv0, data, data); +} + +/* For three keys: Signing, Decryption, and Authentication */ +struct key_data kd[3]; + +static void +decrypt (const uint8_t *key, const uint8_t *iv, uint8_t *data, int len) +{ + aes_context aes; + uint8_t iv0[INITIAL_VECTOR_SIZE]; + size_t iv_offset; + + aes_setkey_enc (&aes, key, 128); /* This is setkey_enc, because of CFB. */ + memcpy (iv0, iv, INITIAL_VECTOR_SIZE); + iv_offset = 0; + aes_crypt_cfb128 (&aes, AES_DECRYPT, len, &iv_offset, iv0, data, data); + + DEBUG_INFO ("DEC\r\n"); + DEBUG_BINARY (data, len); +} + +static void +encrypt_dek (const uint8_t *key_string, uint8_t *dek) +{ + aes_context aes; + + aes_setkey_enc (&aes, key_string, 128); + aes_crypt_ecb (&aes, AES_ENCRYPT, dek, dek); +} + +static void +decrypt_dek (const uint8_t *key_string, uint8_t *dek) +{ + aes_context aes; + + aes_setkey_dec (&aes, key_string, 128); + aes_crypt_ecb (&aes, AES_DECRYPT, dek, dek); +} + +static uint8_t +get_do_ptr_nr_for_kk (enum kind_of_key kk) +{ + switch (kk) + { + case GPG_KEY_FOR_SIGNING: + return NR_DO_PRVKEY_SIG; + case GPG_KEY_FOR_DECRYPTION: + return NR_DO_PRVKEY_DEC; + case GPG_KEY_FOR_AUTHENTICATION: + return NR_DO_PRVKEY_AUT; + } + return NR_DO_PRVKEY_SIG; +} + +void +gpg_do_clear_prvkey (enum kind_of_key kk) +{ + memset (kd[kk].data, 0, MAX_PRVKEY_LEN); +} + + +#define CHECKSUM_ADDR(kdi,prvkey_len) \ + (&(kdi).data[prvkey_len / sizeof (uint32_t)]) +#define kdi_len(prvkey_len) (prvkey_len+DATA_ENCRYPTION_KEY_SIZE) +struct key_data_internal { + uint32_t data[(MAX_PRVKEY_LEN+DATA_ENCRYPTION_KEY_SIZE) / sizeof (uint32_t)]; + /* + * Secret key data. + * RSA: p and q, ECDSA/ECDH: d, EdDSA: a+seed + */ + /* Checksum */ +}; + +#define CKDC_CALC 0 +#define CKDC_CHECK 1 +static int +compute_key_data_checksum (struct key_data_internal *kdi, int prvkey_len, + int check_or_calc) +{ + unsigned int i; + uint32_t d[4] = { 0, 0, 0, 0 }; + uint32_t *checksum = CHECKSUM_ADDR (*kdi, prvkey_len); + + for (i = 0; i < prvkey_len / sizeof (uint32_t); i++) + d[i&3] ^= kdi->data[i]; + + if (check_or_calc == CKDC_CALC) /* store */ + { + memcpy (checksum, d, DATA_ENCRYPTION_KEY_SIZE); + return 0; + } + else /* check */ + return memcmp (checksum, d, DATA_ENCRYPTION_KEY_SIZE) == 0; +} + +/* + * Return 1 on success, + * 0 if none, + * -1 on error, + */ +int +gpg_do_load_prvkey (enum kind_of_key kk, int who, const uint8_t *keystring) +{ + uint8_t nr = get_do_ptr_nr_for_kk (kk); + int prvkey_len = gpg_get_algo_attr_key_size (kk, GPG_KEY_PRIVATE); + const uint8_t *do_data = do_ptr[nr]; + const uint8_t *key_addr; + uint8_t dek[DATA_ENCRYPTION_KEY_SIZE]; + const uint8_t *iv; + struct key_data_internal kdi; + + DEBUG_INFO ("Loading private key: "); + DEBUG_BYTE (kk); + + if (do_data == NULL) + return 0; + + key_addr = kd[kk].pubkey - prvkey_len; + memcpy (kdi.data, key_addr, prvkey_len); + iv = &do_data[1]; + memcpy (CHECKSUM_ADDR (kdi, prvkey_len), + iv + INITIAL_VECTOR_SIZE, DATA_ENCRYPTION_KEY_SIZE); + + memcpy (dek, iv + DATA_ENCRYPTION_KEY_SIZE*(who+1), DATA_ENCRYPTION_KEY_SIZE); + decrypt_dek (keystring, dek); + + decrypt (dek, iv, (uint8_t *)&kdi, kdi_len (prvkey_len)); + memset (dek, 0, DATA_ENCRYPTION_KEY_SIZE); + if (!compute_key_data_checksum (&kdi, prvkey_len, CKDC_CHECK)) + { + DEBUG_INFO ("gpg_do_load_prvkey failed.\r\n"); + return -1; + } + + memcpy (kd[kk].data, kdi.data, prvkey_len); + DEBUG_BINARY (kd[kk].data, prvkey_len); + return 1; +} + + +static int8_t num_prv_keys; + +static void +gpg_do_delete_prvkey (enum kind_of_key kk, int clean_page_full) +{ + uint8_t nr = get_do_ptr_nr_for_kk (kk); + const uint8_t *do_data = do_ptr[nr]; + uint8_t *key_addr; + int prvkey_len = gpg_get_algo_attr_key_size (kk, GPG_KEY_PRIVATE); + int key_size = gpg_get_algo_attr_key_size (kk, GPG_KEY_STORAGE); + + if (do_data == NULL) + { + if (clean_page_full) + flash_key_release_page (kk); + return; + } + + do_ptr[nr] = NULL; + flash_do_release (do_data); + key_addr = (uint8_t *)kd[kk].pubkey - prvkey_len; + kd[kk].pubkey = NULL; + if (clean_page_full) + flash_key_release_page (kk); + else + flash_key_release (key_addr, key_size); + + if (admin_authorized == BY_ADMIN && kk == GPG_KEY_FOR_SIGNING) + { /* Recover admin keystring DO. */ + const uint8_t *ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + + if (ks_pw3 != NULL) + { + uint8_t ks0[KEYSTRING_SIZE]; + + ks0[0] = ks_pw3[0] | PW_LEN_KEYSTRING_BIT; + memcpy (KS_GET_SALT (ks0), KS_GET_SALT (ks_pw3), SALT_SIZE); + memcpy (KS_GET_KEYSTRING (ks0), keystring_md_pw3, KEYSTRING_MD_SIZE); + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, ks0, KEYSTRING_SIZE); + } + } + + if (--num_prv_keys == 0) + { + /* Delete PW1 and RC if any. */ + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, NULL, 0); + gpg_do_write_simple (NR_DO_KEYSTRING_RC, NULL, 0); + + ac_reset_pso_cds (); + ac_reset_other (); + if (admin_authorized == BY_USER) + ac_reset_admin (); + } +} + +void +gpg_do_terminate (void) +{ + int i; + + for (i = 0; i < 3; i++) + kd[i].pubkey = NULL; + + for (i = 0; i < NR_DO__LAST__; i++) + do_ptr[i] = NULL; + + num_prv_keys = 0; + data_objects_number_of_bytes = 0; + digital_signature_counter = 0; + + pw1_lifetime_p = NULL; + pw_err_counter_p[PW_ERR_PW1] = NULL; + pw_err_counter_p[PW_ERR_RC] = NULL; + pw_err_counter_p[PW_ERR_PW3] = NULL; + algo_attr_sig_p = algo_attr_dec_p = algo_attr_aut_p = NULL; +} + +static int +gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, + int prvkey_len, const uint8_t *keystring_admin, + const uint8_t *pubkey) +{ + uint8_t nr = get_do_ptr_nr_for_kk (kk); + int attr = gpg_get_algo_attr (kk);; + const uint8_t *p; + int r; + struct prvkey_data prv; + struct prvkey_data *pd = &prv; + uint8_t *key_addr; + const uint8_t *dek, *iv; + struct key_data_internal kdi; + int pubkey_len; + uint8_t ks[KEYSTRING_MD_SIZE]; + enum kind_of_key kk0; + int pw_len; + const uint8_t *initial_pw; + + DEBUG_INFO ("Key import\r\n"); + DEBUG_SHORT (prvkey_len); + + /* Delete it first, if any. */ + gpg_do_delete_prvkey (kk, CLEAN_SINGLE); + + if (attr == ALGO_SECP256K1) + { + pubkey_len = prvkey_len * 2; + if (prvkey_len != 32) + return -1; + } + else if (attr == ALGO_CURVE25519) + { + pubkey_len = prvkey_len; + if (prvkey_len != 32) + return -1; + } + else if (attr == ALGO_ED25519) + { + pubkey_len = prvkey_len / 2; + if (prvkey_len != 64) + return -1; + } + else if (attr == ALGO_ED448) + { + pubkey_len = 57 + 1; /* +1 to be even. */ + if (prvkey_len != 128) + return -1; + } + else if (attr == ALGO_X448) + { + pubkey_len = prvkey_len; + if (prvkey_len != 56) + return -1; + } + else /* RSA */ + { + int key_size = gpg_get_algo_attr_key_size (kk, GPG_KEY_STORAGE); + + pubkey_len = prvkey_len; + if (prvkey_len + pubkey_len != key_size) + return -1; + } + + DEBUG_INFO ("Getting keystore address...\r\n"); + key_addr = flash_key_alloc (kk); + if (key_addr == NULL) + return -1; + + kd[kk].pubkey = key_addr + prvkey_len; + + num_prv_keys++; + + DEBUG_INFO ("key_addr: "); + DEBUG_WORD ((uint32_t)key_addr); + + memcpy (kdi.data, key_data, prvkey_len); + memset ((uint8_t *)kdi.data + prvkey_len, 0, MAX_PRVKEY_LEN - prvkey_len); + + compute_key_data_checksum (&kdi, prvkey_len, CKDC_CALC); + + dek = random_bytes_get (); /* 32-byte random bytes */ + iv = dek + DATA_ENCRYPTION_KEY_SIZE; + memcpy (pd->dek_encrypted_1, dek, DATA_ENCRYPTION_KEY_SIZE); + memcpy (pd->dek_encrypted_2, dek, DATA_ENCRYPTION_KEY_SIZE); + memcpy (pd->dek_encrypted_3, dek, DATA_ENCRYPTION_KEY_SIZE); + + gpg_do_get_initial_pw_setting (0, &pw_len, &initial_pw); + s2k (NULL, 0, initial_pw, pw_len, ks); + + /* Handle existing keys and keystring DOs. */ + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, NULL, 0); + gpg_do_write_simple (NR_DO_KEYSTRING_RC, NULL, 0); + for (kk0 = 0; kk0 <= GPG_KEY_FOR_AUTHENTICATION; kk0++) + if (kk0 != kk) + { + gpg_do_chks_prvkey (kk0, admin_authorized, keystring_md_pw3, + BY_USER, ks); + gpg_do_chks_prvkey (kk0, BY_RESETCODE, NULL, 0, NULL); + } + + encrypt (dek, iv, (uint8_t *)&kdi, kdi_len (prvkey_len)); + + r = flash_key_write (key_addr, (const uint8_t *)kdi.data, prvkey_len, + pubkey, pubkey_len); + if (r < 0) + { + random_bytes_free (dek); + memset (pd, 0, sizeof (struct prvkey_data)); + return r; + } + + memcpy (pd->iv, iv, INITIAL_VECTOR_SIZE); + memcpy (pd->checksum_encrypted, CHECKSUM_ADDR (kdi, prvkey_len), + DATA_ENCRYPTION_KEY_SIZE); + + encrypt_dek (ks, pd->dek_encrypted_1); + + memset (pd->dek_encrypted_2, 0, DATA_ENCRYPTION_KEY_SIZE); + + if (keystring_admin) + encrypt_dek (keystring_admin, pd->dek_encrypted_3); + else + memset (pd->dek_encrypted_3, 0, DATA_ENCRYPTION_KEY_SIZE); + + p = flash_do_write (nr, (const uint8_t *)pd, sizeof (struct prvkey_data)); + do_ptr[nr] = p; + + random_bytes_free (dek); + memset (pd, 0, sizeof (struct prvkey_data)); + if (p == NULL) + return -1; + + if (keystring_admin && kk == GPG_KEY_FOR_SIGNING) + { + const uint8_t *ks_admin = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + uint8_t ks_info[KS_META_SIZE]; + + if (ks_admin != NULL && (ks_admin[0] & PW_LEN_KEYSTRING_BIT)) + { + ks_info[0] = ks_admin[0] & PW_LEN_MASK; + memcpy (KS_GET_SALT (ks_info), KS_GET_SALT (ks_admin), SALT_SIZE); + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, ks_info, KS_META_SIZE); + } + else + { + DEBUG_INFO ("No admin keystring!\r\n"); + } + } + + return 0; +} + +int +gpg_do_chks_prvkey (enum kind_of_key kk, + int who_old, const uint8_t *old_ks, + int who_new, const uint8_t *new_ks) +{ + uint8_t nr = get_do_ptr_nr_for_kk (kk); + const uint8_t *do_data = do_ptr[nr]; + uint8_t dek[DATA_ENCRYPTION_KEY_SIZE]; + struct prvkey_data prv; + struct prvkey_data *pd = &prv; + uint8_t *dek_p; + int update_needed = 0; + int r = 1; /* Success */ + + if (do_data == NULL) + return 0; /* No private key */ + + memcpy (pd, &do_data[1], sizeof (struct prvkey_data)); + + dek_p = ((uint8_t *)pd) + INITIAL_VECTOR_SIZE + + DATA_ENCRYPTION_KEY_SIZE * who_old; + memcpy (dek, dek_p, DATA_ENCRYPTION_KEY_SIZE); + if (who_new == 0) /* Remove */ + { + int i; + + for (i = 0; i < DATA_ENCRYPTION_KEY_SIZE; i++) + if (dek_p[i] != 0) + { + update_needed = 1; + dek_p[i] = 0; + } + } + else + { + decrypt_dek (old_ks, dek); + encrypt_dek (new_ks, dek); + dek_p += DATA_ENCRYPTION_KEY_SIZE * (who_new - who_old); + if (memcmp (dek_p, dek, DATA_ENCRYPTION_KEY_SIZE) != 0) + { + memcpy (dek_p, dek, DATA_ENCRYPTION_KEY_SIZE); + update_needed = 1; + } + } + + if (update_needed) + { + const uint8_t *p; + + flash_do_release (do_data); + do_ptr[nr] = NULL; + p = flash_do_write (nr, (const uint8_t *)pd, sizeof (struct prvkey_data)); + do_ptr[nr] = p; + if (p == NULL) + r = -1; + } + + memset (pd, 0, sizeof (struct prvkey_data)); + + return r; +} + + +static enum kind_of_key +kkb_to_kk (uint8_t kk_byte) +{ + enum kind_of_key kk; + + if (kk_byte == 0xb6) + kk = GPG_KEY_FOR_SIGNING; + else if (kk_byte == 0xb8) + kk = GPG_KEY_FOR_DECRYPTION; + else /* 0xa4 */ + kk = GPG_KEY_FOR_AUTHENTICATION; + return kk; +} + +/* + * RSA-2048: + * 4d, xx, xx, xx: Extended Header List + * b6 00 (SIG) / b8 00 (DEC) / a4 00 (AUT) + * 7f48, xx: cardholder private key template + * 91 L: 91=tag of E, L: length of E + * 92 Lh

Ll

: 92=tag of P, L

: length of P + * 93 Lh Ll: 93=tag of Q, L: length of Q + * 5f48, xx xx xx: cardholder private key + * , , + * + * RSA-4096: + * 4d, 82, 02, 18: Extended Header List + * b6 00 (SIG) / b8 00 (DEC) / a4 00 (AUT) + * 7f48, 0a: cardholder private key template + * 91 L: 91=tag of E, L: length of E + * 92 82 Lh

Ll

: 92=tag of P, L

: length of P + * 93 82 Lh Ll: 93=tag of Q, L: length of Q + * 5f48, 82 02 04: cardholder private key + * , , + * + * ECDSA / ECDH / EdDSA: + * 4d, 2a: Extended Header List + * b6 00 (SIG) / b8 00 (DEC) / a4 00 (AUT) + * 7f48, 02: cardholder private key template + * 9x LEN: 9x=tag of private key d, LEN=length of d + * 5f48, 20: cardholder private key + * + */ +static int +proc_key_import (const uint8_t *data, int len) +{ + int r = -1; + enum kind_of_key kk; + const uint8_t *keystring_admin; + int attr; + const uint8_t *p = data; + uint8_t pubkey[512]; + +#ifdef KDF_DO_REQUIRED + const uint8_t *kdf_do = do_ptr[NR_DO_KDF]; + + if (kdf_do == NULL) + return 0; /* Error. */ +#endif + + if (admin_authorized == BY_ADMIN) + keystring_admin = keystring_md_pw3; + else + keystring_admin = NULL; + + DEBUG_BINARY (data, len); + + if (*p++ != 0x4d) + return 0; + + /* length field */ + if (*p == 0x82) + p += 3; + else if (*p == 0x81) + p += 2; + else + p += 1; + + kk = kkb_to_kk (*p); + if (kk == GPG_KEY_FOR_SIGNING) + { + ac_reset_pso_cds (); + gpg_reset_digital_signature_counter (); + } + else + ac_reset_other (); + + attr = gpg_get_algo_attr (kk); + + if ((len <= 12 && (attr == ALGO_SECP256K1 || attr == ALGO_CURVE25519 + || attr == ALGO_ED25519 || attr == ALGO_ED448 + || attr == ALGO_X448)) + || (len <= 22 && attr == ALGO_RSA2K) || (len <= 24 && attr == ALGO_RSA4K)) + { /* Deletion of the key */ + gpg_do_delete_prvkey (kk, CLEAN_SINGLE); + return 1; + } + + if (attr == ALGO_RSA2K) + { + /* It should starts with 00 01 00 01 (E), skiping E (4-byte) */ + r = modulus_calc (&data[26], len - 26, pubkey); + if (r >= 0) + r = gpg_do_write_prvkey (kk, &data[26], len - 26, keystring_admin, + pubkey); + } + else if (attr == ALGO_RSA4K) + { + /* It should starts with 00 01 00 01 (E), skiping E (4-byte) */ + r = modulus_calc (&data[28], len - 28, pubkey); + if (r >= 0) + r = gpg_do_write_prvkey (kk, &data[28], len - 28, keystring_admin, + pubkey); + } + else if (attr == ALGO_SECP256K1) + { + r = ecc_compute_public_p256k1 (&data[12], pubkey); + if (r >= 0) + r = gpg_do_write_prvkey (kk, &data[12], len - 12, keystring_admin, + pubkey); + } + else if (attr == ALGO_CURVE25519) + { + uint8_t priv[32]; + int i; + + if (len - 12 != 32) + return 0; /* Error. */ + + for (i = 0; i < 32; i++) + priv[31-i] = data[12+i]; + ecdh_compute_public_25519 (priv, pubkey); + r = gpg_do_write_prvkey (kk, priv, 32, keystring_admin, pubkey); + } + else if (attr == ALGO_ED25519) + { + uint8_t hash[64]; + + if (len - 12 != 32) + return 0; /* Error. */ + + sha512 (&data[12], 32, hash); + hash[0] &= 248; + hash[31] &= 127; + hash[31] |= 64; + eddsa_compute_public_25519 (hash, pubkey); + r = gpg_do_write_prvkey (kk, hash, 64, keystring_admin, pubkey); + } + else if (attr == ALGO_ED448) + { + shake_context ctx; + uint8_t hash[128]; + + if (len - 12 != 57) + return 0; /* Error. */ + + shake256_start (&ctx); + shake256_update (&ctx, &data[12], 57); + shake256_finish (&ctx, hash, 2*57); + memset (hash+114, 0, 128-114); + ed448_compute_public (pubkey, hash); + pubkey[57] = 0; + r = gpg_do_write_prvkey (kk, hash, 128, keystring_admin, pubkey); + } + else if (attr == ALGO_X448) + { + uint8_t priv[56]; + + if (len - 12 != 56) + return 0; /* Error. */ + + memcpy (priv, data+12, 56); + ecdh_compute_public_x448 (pubkey, priv); + r = gpg_do_write_prvkey (kk, priv, 56, keystring_admin, pubkey); + } + + if (r < 0) + return 0; + else + return 1; +} + +static const uint16_t cmp_ch_data[] = { + 3, + GPG_DO_NAME, + GPG_DO_LANGUAGE, + GPG_DO_SEX, +}; + +static const uint16_t cmp_app_data[] = { +#ifdef ACKBTN_SUPPORT + 4, +#else + 3, +#endif + GPG_DO_AID, + GPG_DO_HIST_BYTES, + GPG_DO_DISCRETIONARY, +#ifdef ACKBTN_SUPPORT + GPG_DO_FEATURE_MNGMNT, +#endif +}; + +static const uint16_t cmp_discretionary[] = { +#ifdef ACKBTN_SUPPORT + 11, +#else + 8, +#endif + GPG_DO_EXTCAP, + GPG_DO_ALG_SIG, GPG_DO_ALG_DEC, GPG_DO_ALG_AUT, + GPG_DO_PW_STATUS, + GPG_DO_FP_ALL, GPG_DO_CAFP_ALL, GPG_DO_KGTIME_ALL, +#ifdef ACKBTN_SUPPORT + GPG_DO_UIF_SIG, GPG_DO_UIF_DEC, GPG_DO_UIF_AUT +#endif +}; + +static const uint16_t cmp_ss_temp[] = { 1, GPG_DO_DS_COUNT }; + +static const struct do_table_entry +gpg_do_table[] = { + /* Variables: Fixed size */ + { GPG_DO_SEX, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[0] }, + { GPG_DO_FP_SIG, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[1] }, + { GPG_DO_FP_DEC, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[2] }, + { GPG_DO_FP_AUT, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[3] }, + { GPG_DO_CAFP_1, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[4] }, + { GPG_DO_CAFP_2, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[5] }, + { GPG_DO_CAFP_3, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[6] }, + { GPG_DO_KGTIME_SIG, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[7] }, + { GPG_DO_KGTIME_DEC, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[8] }, + { GPG_DO_KGTIME_AUT, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[9] }, + /* Variables: Variable size */ + { GPG_DO_LOGIN_DATA, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[10] }, + { GPG_DO_URL, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[11] }, + { GPG_DO_NAME, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[12] }, + { GPG_DO_LANGUAGE, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, &do_ptr[13] }, + /* Pseudo DO READ: calculated */ + { GPG_DO_FP_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_fp_all }, + { GPG_DO_CAFP_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_cafp_all }, + { GPG_DO_KGTIME_ALL, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_kgtime_all }, + /* Pseudo DO READ: calculated, not changeable by user */ + { GPG_DO_DS_COUNT, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_ds_count }, + { GPG_DO_AID, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_openpgpcard_aid }, + { GPG_DO_ALG_INFO, DO_PROC_READ, AC_ALWAYS, AC_NEVER, do_alg_info }, + /* Pseudo DO READ/WRITE: calculated */ + { GPG_DO_PW_STATUS, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, + rw_pw_status }, + { GPG_DO_ALG_SIG, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, + rw_algorithm_attr }, + { GPG_DO_ALG_DEC, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, + rw_algorithm_attr }, + { GPG_DO_ALG_AUT, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, + rw_algorithm_attr }, +#ifdef ACKBTN_SUPPORT + { GPG_DO_UIF_SIG, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, rw_uif }, + { GPG_DO_UIF_DEC, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, rw_uif }, + { GPG_DO_UIF_AUT, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, rw_uif }, +#endif + { GPG_DO_KDF, DO_PROC_READWRITE, AC_ALWAYS, AC_ADMIN_AUTHORIZED, + rw_kdf }, + /* Fixed data */ + { GPG_DO_HIST_BYTES, DO_FIXED, AC_ALWAYS, AC_NEVER, historical_bytes }, + { GPG_DO_EXTCAP, DO_FIXED, AC_ALWAYS, AC_NEVER, extended_capabilities }, +#ifdef ACKBTN_SUPPORT + { GPG_DO_FEATURE_MNGMNT, DO_FIXED, AC_ALWAYS, AC_NEVER, feature_mngmnt }, +#endif + /* Compound data: Read access only */ + { GPG_DO_CH_DATA, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_ch_data }, + { GPG_DO_APP_DATA, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_app_data }, + { GPG_DO_DISCRETIONARY, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_discretionary }, + { GPG_DO_SS_TEMP, DO_CMP_READ, AC_ALWAYS, AC_NEVER, cmp_ss_temp }, + /* Simple data: write access only */ + { GPG_DO_RESETTING_CODE, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED, + proc_resetting_code }, + /* Compound data: Write access only */ + { GPG_DO_KEY_IMPORT, DO_PROC_WRITE, AC_NEVER, AC_ADMIN_AUTHORIZED, + proc_key_import }, +#if 0 + /* Card holder certificate is handled in special way, as its size is big */ + { GPG_DO_CH_CERTIFICATE, DO_VAR, AC_ALWAYS, AC_ADMIN_AUTHORIZED, NULL }, +#endif +}; + +#define NUM_DO_ENTRIES (int)(sizeof (gpg_do_table) \ + / sizeof (struct do_table_entry)) + +/* + * Reading data from Flash ROM, initialize DO_PTR, PW_ERR_COUNTERS, etc. + */ +void +gpg_data_scan (const uint8_t *do_start, const uint8_t *do_end) +{ + const uint8_t *p; + int i; + const uint8_t *dsc_h14_p, *dsc_l10_p; + int dsc_h14, dsc_l10; + + dsc_h14_p = dsc_l10_p = NULL; + pw1_lifetime_p = NULL; + pw_err_counter_p[PW_ERR_PW1] = NULL; + pw_err_counter_p[PW_ERR_RC] = NULL; + pw_err_counter_p[PW_ERR_PW3] = NULL; + algo_attr_sig_p = algo_attr_dec_p = algo_attr_aut_p = NULL; + digital_signature_counter = 0; + uif_flags = 0; + + /* Clear all data objects. */ + for (i = 0; i < NR_DO__LAST__; i++) + do_ptr[i] = NULL; + + /* When the card is terminated no data objects are valid. */ + if (do_start == NULL) + return; + + /* Traverse DO, counters, etc. in DATA pool */ + p = do_start; + while (p < do_end && *p != NR_EMPTY) + { + uint8_t nr = *p++; + uint8_t second_byte = *p; + + if (nr == 0x00 && second_byte == 0x00) + p++; /* Skip released word */ + else + { + if (nr < 0x80) + { + /* It's Data Object */ + if (nr < NR_DO__LAST__) + do_ptr[nr] = p; + + p += second_byte + 1; /* second_byte has length */ + + if (((uintptr_t)p & 1)) + p++; + } + else if (nr >= 0x80 && nr <= 0xbf) + /* Encoded data of Digital Signature Counter: upper 14-bit */ + { + dsc_h14_p = p - 1; + p++; + } + else if (nr >= 0xc0 && nr <= 0xc3) + /* Encoded data of Digital Signature Counter: lower 10-bit */ + { + dsc_l10_p = p - 1; + p++; + } + else + switch (nr) + { + case NR_BOOL_PW1_LIFETIME: + pw1_lifetime_p = p - 1; + p++; + break; + case NR_KEY_ALGO_ATTR_SIG: + algo_attr_sig_p = p - 1; + p++; + break; + case NR_KEY_ALGO_ATTR_DEC: + algo_attr_dec_p = p - 1; + p++; + break; + case NR_KEY_ALGO_ATTR_AUT: + algo_attr_aut_p = p - 1; + p++; + break; + case NR_DO_UIF_SIG: + case NR_DO_UIF_DEC: + case NR_DO_UIF_AUT: + uif_flags &= ~(3 << ((nr - NR_DO_UIF_SIG) * 2)); + uif_flags |= (second_byte & 3) << ((nr - NR_DO_UIF_SIG) * 2); + p++; + break; + case NR_COUNTER_123: + p++; + if (second_byte <= PW_ERR_PW3) + pw_err_counter_p[second_byte] = p; + p += 2; + break; + default: + /* Something going wrong. ignore this word. */ + p++; + break; + } + } + } + + flash_set_data_pool_last (p); + + num_prv_keys = 0; + if (do_ptr[NR_DO_PRVKEY_SIG] != NULL) + num_prv_keys++; + if (do_ptr[NR_DO_PRVKEY_DEC] != NULL) + num_prv_keys++; + if (do_ptr[NR_DO_PRVKEY_AUT] != NULL) + num_prv_keys++; + + data_objects_number_of_bytes = 0; + for (i = 0; i < NR_DO__LAST__; i++) + if (do_ptr[i] != NULL) + data_objects_number_of_bytes += *do_ptr[i]; + + if (dsc_l10_p == NULL) + dsc_l10 = 0; + else + dsc_l10 = ((*dsc_l10_p - 0xc0) << 8) | *(dsc_l10_p + 1); + + if (dsc_h14_p == NULL) + dsc_h14 = 0; + else + { + dsc_h14 = ((*dsc_h14_p - 0x80) << 8) | *(dsc_h14_p + 1); + if (dsc_l10_p == NULL) + DEBUG_INFO ("something wrong in DSC\r\n"); /* weird??? */ + else if (dsc_l10_p < dsc_h14_p) + /* Possibly, power off during writing dsc_l10 */ + dsc_l10 = 0; + } + + digital_signature_counter = (dsc_h14 << 10) | dsc_l10; +} + +/* + * Write all data to newly allocated Flash ROM page (from P_START), + * updating PW1_LIFETIME_P, PW_ERR_COUNTER_P, and DO_PTR. + * Called by flash_copying_gc. + */ +void +gpg_data_copy (const uint8_t *p_start) +{ + const uint8_t *p; + int i; + int v; + + p = gpg_write_digital_signature_counter (p_start, digital_signature_counter); + + if (pw1_lifetime_p != NULL) + { + flash_bool_write_internal (p, NR_BOOL_PW1_LIFETIME); + pw1_lifetime_p = p; + p += 2; + } + + if (algo_attr_sig_p != NULL) + { + flash_enum_write_internal (p, NR_KEY_ALGO_ATTR_SIG, algo_attr_sig_p[1]); + algo_attr_sig_p = p; + p += 2; + } + + if (algo_attr_dec_p != NULL) + { + flash_enum_write_internal (p, NR_KEY_ALGO_ATTR_DEC, algo_attr_dec_p[1]); + algo_attr_dec_p = p; + p += 2; + } + + if (algo_attr_aut_p != NULL) + { + flash_enum_write_internal (p, NR_KEY_ALGO_ATTR_AUT, algo_attr_aut_p[1]); + algo_attr_aut_p = p; + p += 2; + } + + for (i = 0; i < 3; i++) + if ((v = flash_cnt123_get_value (pw_err_counter_p[i])) != 0) + { + flash_cnt123_write_internal (p, i, v); + pw_err_counter_p[i] = p + 2; + p += 4; + } + + for (i = 0; i < 3; i++) + if ((v = (uif_flags >> (i * 2)) & 3)) + { + flash_enum_write_internal (p, NR_DO_UIF_SIG + i, v); + p += 2; + } + + data_objects_number_of_bytes = 0; + for (i = 0; i < NR_DO__LAST__; i++) + if (do_ptr[i] != NULL) + { + const uint8_t *do_data = do_ptr[i]; + int len = do_data[0]; + + flash_do_write_internal (p, i, &do_data[1], len); + do_ptr[i] = p + 1; + p += 2 + ((len + 1) & ~1); + data_objects_number_of_bytes += len; + } + + flash_set_data_pool_last (p); +} + +static const struct do_table_entry * +get_do_entry (uint16_t tag) +{ + int i; + + for (i = 0; i < NUM_DO_ENTRIES; i++) + if (gpg_do_table[i].tag == tag) + return &gpg_do_table[i]; + + return NULL; +} + +static void +copy_do_1 (uint16_t tag, const uint8_t *do_data, int with_tag) +{ + int len; + + if (with_tag) + { + copy_tag (tag); + + if (do_data[0] >= 128) + *res_p++ = 0x81; + + len = do_data[0] + 1; + } + else + { + len = do_data[0]; + do_data++; + } + + memcpy (res_p, do_data, len); + res_p += len; +} + +static int +copy_do (const struct do_table_entry *do_p, int with_tag) +{ + if (do_p == NULL) + return 0; + + if (!ac_check_status (do_p->ac_read)) + return -1; + + switch (do_p->do_type) + { + case DO_FIXED: + { + const uint8_t *do_data = (const uint8_t *)do_p->obj; + if (do_data == NULL) + return 0; + else + copy_do_1 (do_p->tag, do_data, with_tag); + break; + } + case DO_VAR: + { + const uint8_t *do_data = *(const uint8_t **)do_p->obj; + if (do_data == NULL) + return 0; + else + copy_do_1 (do_p->tag, do_data, with_tag); + break; + } + case DO_CMP_READ: + { + int i; + const uint16_t *cmp_data = (const uint16_t *)do_p->obj; + int num_components = cmp_data[0]; + uint8_t *len_p = NULL; + + if (with_tag) + { + copy_tag (do_p->tag); + *res_p++ = 0x81; /* Assume it's less than 256 */ + len_p = res_p; + *res_p++ = 0; /* for now */ + } + + for (i = 0; i < num_components; i++) + { + uint16_t tag0; + const struct do_table_entry *do0_p; + + tag0 = cmp_data[i+1]; + do0_p = get_do_entry (tag0); + if (copy_do (do0_p, 1) < 0) + return -1; + } + + if (len_p) + *len_p = res_p - len_p - 1; + break; + } + case DO_PROC_READ: + { + void (*do_func)(uint16_t, int) = (void (*)(uint16_t, int))do_p->obj; + + do_func (do_p->tag, with_tag); + return 1; + } + case DO_PROC_READWRITE: + { + int (*rw_func)(uint16_t, int, const uint8_t *, int, int) + = (int (*)(uint16_t, int, const uint8_t *, int, int))do_p->obj; + + return rw_func (do_p->tag, with_tag, NULL, 0, 0); + } + case DO_PROC_WRITE: + return -1; + } + + return 1; +} + +/* + * Process GET_DATA request on Data Object specified by TAG + * Call write_res_adpu to fill data returned + */ +void +gpg_do_get_data (uint16_t tag, int with_tag) +{ +#if defined(CERTDO_SUPPORT) + if (tag == GPG_DO_CH_CERTIFICATE) + { + apdu.res_apdu_data = (uint8_t *)ch_certificate_start; + apdu.res_apdu_data_len = ((apdu.res_apdu_data[2] << 8) | apdu.res_apdu_data[3]); + if (apdu.res_apdu_data_len == 0xffff) + { + apdu.res_apdu_data_len = 0; + GPG_NO_RECORD (); + } + else + /* Add length of (tag+len) */ + apdu.res_apdu_data_len += 4; + } + else +#endif + { + const struct do_table_entry *do_p = get_do_entry (tag); + + res_p = res_APDU; + + DEBUG_INFO (" "); + DEBUG_SHORT (tag); + + if (do_p) + { + if (copy_do (do_p, with_tag) < 0) + /* Overwriting partially written result */ + GPG_SECURITY_FAILURE (); + else + { + res_APDU_size = res_p - res_APDU; + GPG_SUCCESS (); + } + } + else + GPG_NO_RECORD (); + } +} + +void +gpg_do_put_data (uint16_t tag, const uint8_t *data, int len) +{ + const struct do_table_entry *do_p = get_do_entry (tag); + + DEBUG_INFO (" "); + DEBUG_SHORT (tag); + + if (do_p) + { + if (!ac_check_status (do_p->ac_write)) + { + GPG_SECURITY_FAILURE (); + return; + } + + switch (do_p->do_type) + { + case DO_FIXED: + case DO_CMP_READ: + case DO_PROC_READ: + GPG_SECURITY_FAILURE (); + break; + case DO_VAR: + { + const uint8_t **do_data_p = (const uint8_t **)do_p->obj; + + if (*do_data_p) + flash_do_release (*do_data_p); + + if (len == 0) + { + /* make DO empty */ + *do_data_p = NULL; + GPG_SUCCESS (); + } + else if (len > 255) + GPG_MEMORY_FAILURE (); + else + { + int nr = do_tag_to_nr (tag); + + if (nr < 0) + GPG_MEMORY_FAILURE (); + else + { + *do_data_p = NULL; + *do_data_p = flash_do_write (nr, data, len); + if (*do_data_p) + GPG_SUCCESS (); + else + GPG_MEMORY_FAILURE (); + } + } + break; + } + case DO_PROC_READWRITE: + { + int (*rw_func)(uint16_t, int, const uint8_t *, int, int) + = (int (*)(uint16_t, int, const uint8_t *, int, int))do_p->obj; + + if (rw_func (tag, 0, data, len, 1)) + GPG_SUCCESS (); + else + GPG_ERROR (); + break; + } + case DO_PROC_WRITE: + { + int (*proc_func)(const uint8_t *, int) + = (int (*)(const uint8_t *, int))do_p->obj; + + if (proc_func (data, len)) + GPG_SUCCESS (); + else + GPG_ERROR (); + break; + } + } + } + else + GPG_NO_RECORD (); +} + +void +gpg_do_public_key (uint8_t kk_byte) +{ + enum kind_of_key kk = kkb_to_kk (kk_byte); + int attr = gpg_get_algo_attr (kk); + int pubkey_len = gpg_get_algo_attr_key_size (kk, GPG_KEY_PUBLIC); + const uint8_t *pubkey = kd[kk].pubkey; + + DEBUG_INFO ("Public key\r\n"); + DEBUG_BYTE (kk_byte); + + if (pubkey == NULL) + { + DEBUG_INFO ("none.\r\n"); + GPG_NO_RECORD (); + return; + } + + res_p = res_APDU; + + /* TAG */ + *res_p++ = 0x7f; *res_p++ = 0x49; + + if (attr == ALGO_SECP256K1) + { /* ECDSA or ECDH */ + /* LEN */ + *res_p++ = 2 + 1 + 64; + { + /*TAG*/ /* LEN = 1+64 */ + *res_p++ = 0x86; *res_p++ = 0x41; + *res_p++ = 0x04; /* No compression of EC point. */ + /* 64-byte binary (big endian): X || Y */ + memcpy (res_p, pubkey, 64); + res_p += 64; + } + } + else if (attr == ALGO_ED25519 || attr == ALGO_CURVE25519) + { /* EdDSA or ECDH on curve25519 */ + /* LEN */ + *res_p++ = 2 + 32; + { + /*TAG*/ /* LEN = 32 */ + *res_p++ = 0x86; *res_p++ = 0x20; + /* 32-byte binary (little endian): Y with parity or X */ + memcpy (res_p, pubkey, 32); + res_p += 32; + } + } + else if (attr == ALGO_ED448) + { /* EdDSA using Ed448 */ + /* LEN */ + *res_p++ = 2 + 57; + { + /*TAG*/ /* LEN = 57 */ + *res_p++ = 0x86; *res_p++ = 0x39; + /* 57-byte binary (little endian): X */ + memcpy (res_p, pubkey, 57); + res_p += 57; + } + } + else if (attr == ALGO_X448) + { /* ECDH using X448 */ + /* LEN */ + *res_p++ = 2 + 56; + { + /*TAG*/ /* LEN = 56 */ + *res_p++ = 0x86; *res_p++ = 0x38; + /* 56-byte binary (little endian): X */ + memcpy (res_p, pubkey, 56); + res_p += 56; + } + } + else + { /* RSA */ + /* LEN = 9+256or512 */ + *res_p++ = 0x82; *res_p++ = pubkey_len > 256? 0x02: 0x01; *res_p++ = 0x09; + + { + /*TAG*/ /* LEN = 256or512 */ + *res_p++ = 0x81; + *res_p++ = 0x82; *res_p++ = pubkey_len > 256? 0x02: 0x01;*res_p++ = 0x00; + /* PUBKEY_LEN-byte binary (big endian) */ + memcpy (res_p, pubkey, pubkey_len); + res_p += pubkey_len; + } + { + /*TAG*/ /* LEN= 3 */ + *res_p++ = 0x82; *res_p++ = 3; + /* 3-byte E=0x10001 (big endian) */ + *res_p++ = 0x01; *res_p++ = 0x00; *res_p++ = 0x01; + } + } + + /* Success */ + res_APDU_size = res_p - res_APDU; + GPG_SUCCESS (); + + DEBUG_INFO ("done.\r\n"); + return; +} + +const uint8_t * +gpg_do_read_simple (uint8_t nr) +{ + const uint8_t *do_data; + + do_data = do_ptr[nr]; + if (do_data == NULL) + return NULL; + + return do_data+1; +} + +void +gpg_do_write_simple (uint8_t nr, const uint8_t *data, int size) +{ + const uint8_t **do_data_p; + + do_data_p = (const uint8_t **)&do_ptr[nr]; + if (*do_data_p) + flash_do_release (*do_data_p); + + if (data != NULL) + { + *do_data_p = NULL; + *do_data_p = flash_do_write (nr, data, size); + if (*do_data_p == NULL) + flash_warning ("DO WRITE ERROR"); + } + else + *do_data_p = NULL; +} + +void +gpg_do_keygen (uint8_t *buf) +{ + uint8_t kk_byte = buf[0]; + enum kind_of_key kk = kkb_to_kk (kk_byte); + int attr = gpg_get_algo_attr (kk);; + int prvkey_len = gpg_get_algo_attr_key_size (kk, GPG_KEY_PRIVATE); + const uint8_t *prv; + const uint8_t *rnd; + int r = 0; +#define p_q (&buf[3]) +#define d (&buf[3]) +#define d1 (&buf[3+64]) +#define pubkey (&buf[3+256]) + + DEBUG_INFO ("Keygen\r\n"); + DEBUG_BYTE (kk_byte); + + if (attr == ALGO_RSA2K || attr == ALGO_RSA4K) + { + if (rsa_genkey (prvkey_len, pubkey, p_q) < 0) + { + GPG_MEMORY_FAILURE (); + return; + } + + prv = p_q; + } + else if (attr == ALGO_SECP256K1) + { + const uint8_t *p; + int i; + + rnd = NULL; + do + { + if (rnd) + random_bytes_free (rnd); + rnd = random_bytes_get (); + r = ecc_check_secret_p256k1 (rnd, d1); + } + while (r == 0); + + /* Convert it to big endian */ + + if (r < 0) + p = (const uint8_t *)d1; + else + p = rnd; + for (i = 0; i < 32; i++) + d[32 - i - 1] = p[i]; + + random_bytes_free (rnd); + + prv = d; + r = ecc_compute_public_p256k1 (prv, pubkey); + } + else if (attr == ALGO_CURVE25519) + { + rnd = random_bytes_get (); + memcpy (d, rnd, 32); + random_bytes_free (rnd); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + prv = d; + ecdh_compute_public_25519 (prv, pubkey); + } + else if (attr == ALGO_ED25519) + { + rnd = random_bytes_get (); + sha512 (rnd, 32, d); + random_bytes_free (rnd); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + prv = d; + eddsa_compute_public_25519 (d, pubkey); + } + else if (attr == ALGO_ED448) + { + shake_context ctx; + rnd = random_bytes_get (); + shake256_start (&ctx); + shake256_update (&ctx, rnd, 32); + random_bytes_free (rnd); + rnd = random_bytes_get (); + shake256_update (&ctx, rnd, 25); + shake256_finish (&ctx, d, 2*57); + random_bytes_free (rnd); + prv = d; + ed448_compute_public (pubkey, prv); + pubkey[57] = 0; + } + else if (attr == ALGO_X448) + { + rnd = random_bytes_get (); + memcpy (d, rnd, 32); + random_bytes_free (rnd); + rnd = random_bytes_get (); + memcpy (d+32, rnd, 24); + prv = d; + ecdh_compute_public_x448 (pubkey, prv); + } + else + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + if (r >= 0) + { + const uint8_t *keystring_admin; + + if (admin_authorized == BY_ADMIN) + keystring_admin = keystring_md_pw3; + else + keystring_admin = NULL; + + r = gpg_do_write_prvkey (kk, prv, prvkey_len, keystring_admin, pubkey); + } + + /* Clear private key data in the buffer. */ + memset (buf, 0, 256); + + if (r < 0) + { + GPG_ERROR (); + return; + } + + DEBUG_INFO ("Calling gpg_do_public_key...\r\n"); + + if (kk == GPG_KEY_FOR_SIGNING) + { + int pw_len; + const uint8_t *initial_pw; + uint8_t keystring[KEYSTRING_MD_SIZE]; + + /* GnuPG expects it's ready for signing. */ + /* Don't call ac_reset_pso_cds here, but load the private key */ + + gpg_reset_digital_signature_counter (); + gpg_do_get_initial_pw_setting (0, &pw_len, &initial_pw); + s2k (NULL, 0, initial_pw, pw_len, keystring); + gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring); + } + else + ac_reset_other (); + + gpg_do_public_key (kk_byte); +} diff --git a/openpgp.c b/openpgp.c new file mode 100644 index 0000000..298764b --- /dev/null +++ b/openpgp.c @@ -0,0 +1,1704 @@ +/* + * openpgp.c -- OpenPGP card protocol support + * + * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + * 2019, 2021 + * Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "config.h" + +#include "gnuk.h" +//#include "sys.h" +#include "status-code.h" +#include "sha256.h" +#include "random.h" +#include "pico/util/queue.h" +#include "pico/multicore.h" + +static queue_t *openpgp_comm; + +#define USER_PASSWD_MINLEN 6 +#define ADMIN_PASSWD_MINLEN 8 + +#define CLS(a) a.cmd_apdu_head[0] +#define INS(a) a.cmd_apdu_head[1] +#define P1(a) a.cmd_apdu_head[2] +#define P2(a) a.cmd_apdu_head[3] + +#define INS_VERIFY 0x20 +#define INS_CHANGE_REFERENCE_DATA 0x24 +#define INS_PSO 0x2a +#define INS_RESET_RETRY_COUNTER 0x2c +#define INS_ACTIVATE_FILE 0x44 +#define INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR 0x47 +#define INS_EXTERNAL_AUTHENTICATE 0x82 +#define INS_GET_CHALLENGE 0x84 +#define INS_INTERNAL_AUTHENTICATE 0x88 +#define INS_SELECT_FILE 0xa4 +#define INS_READ_BINARY 0xb0 +#define INS_GET_DATA 0xca +#define INS_WRITE_BINARY 0xd0 +#define INS_UPDATE_BINARY 0xd6 +#define INS_PUT_DATA 0xda +#define INS_PUT_DATA_ODD 0xdb /* For key import */ +#define INS_TERMINATE_DF 0xe6 + +static const uint8_t *challenge; /* Random bytes */ + +static const uint8_t +select_file_TOP_result[] __attribute__ ((aligned (1))) = { + 0x00, 0x00, /* unused */ + 0x00, 0x00, /* number of bytes in this directory: to be filled */ + 0x3f, 0x00, /* field of selected file: MF, 3f00 */ + 0x38, /* it's DF */ + 0xff, /* unused */ + 0xff, 0x44, 0x44, /* access conditions */ + 0x01, /* status of the selected file (OK, unblocked) */ + 0x05, /* number of bytes of data follow */ + 0x03, /* Features: unused */ + 0x01, /* number of subdirectories (OpenPGP) */ + 0x01, /* number of elementary files (SerialNo) */ + 0x00, /* number of secret codes */ + 0x00, /* Unused */ + 0x00, 0x00 /* PIN status: OK, PIN blocked?: No */ +}; + +void +set_res_sw (uint8_t sw1, uint8_t sw2) +{ + apdu.sw = (sw1 << 8) | sw2; +} + +#define FILE_NONE 0 +#define FILE_DF_OPENPGP 1 +#define FILE_MF 2 +#define FILE_EF_DIR 3 +#define FILE_EF_SERIAL_NO 4 +#define FILE_EF_UPDATE_KEY_0 5 +#define FILE_EF_UPDATE_KEY_1 6 +#define FILE_EF_UPDATE_KEY_2 7 +#define FILE_EF_UPDATE_KEY_3 8 +#define FILE_EF_CH_CERTIFICATE 9 +#define FILE_CARD_TERMINATED 255 + +uint8_t file_selection; + +static void +gpg_init (void) +{ + const uint8_t *flash_do_start; + const uint8_t *flash_do_end; + + flash_do_storage_init (&flash_do_start, &flash_do_end); + + if (flash_do_start == NULL) + file_selection = FILE_CARD_TERMINATED; + else + file_selection = FILE_NONE; + + gpg_data_scan (flash_do_start, flash_do_end); + flash_key_storage_init (); +} + +static void +gpg_fini (void) +{ + ac_fini (); +} + +#if defined(PINPAD_SUPPORT) +/* + * Let user input PIN string. + * Return length of the string. + * The string itself is in PIN_INPUT_BUFFER. + */ +static int +get_pinpad_input (int msg_code) +{ + int r; + + led_blink (LED_START_COMMAND); + r = pinpad_getline (msg_code, 8000000); + led_blink (LED_FINISH_COMMAND); + return r; +} +#endif + +static void +cmd_verify (queue_t *ccid_comm) +{ + int len; + uint8_t p1 = P1 (apdu); + uint8_t p2 = P2 (apdu); + int r; + const uint8_t *pw; + + (void)ccid_comm; + DEBUG_INFO (" - VERIFY\r\n"); + DEBUG_BYTE (p2); + + len = apdu.cmd_apdu_data_len; + pw = apdu.cmd_apdu_data; + + if (len == 0) + { + if (p1 == 0) + { /* This is to examine status. */ + if (p2 == 0x81) + r = ac_check_status (AC_PSO_CDS_AUTHORIZED); + else if (p2 == 0x82) + r = ac_check_status (AC_OTHER_AUTHORIZED); + else + r = ac_check_status (AC_ADMIN_AUTHORIZED); + + if (r) + /* If authentication done already, return success. */ + GPG_SUCCESS (); + else + { /* If not, return retry counter, encoded. */ + r = gpg_pw_get_retry_counter (p2); + set_res_sw (0x63, 0xc0 | (r&0x0f)); + } + } + else if (p1 == 0xff) + { /* Reset the status. */ + if (p2 == 0x81) + ac_reset_pso_cds (); + else if (p2 == 0x82) + ac_reset_other (); + else + ac_reset_admin (); + GPG_SUCCESS (); + } + else + GPG_BAD_P1_P2 (); + return; + } + + if (gpg_do_kdf_check (len, 1) == 0) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + /* This is real authentication. */ + if (p2 == 0x81) + r = verify_pso_cds (pw, len); + else if (p2 == 0x82) + r = verify_other (pw, len); + else + r = verify_admin (pw, len); + + if (r < 0) + { + DEBUG_INFO ("failed\r\n"); + GPG_SECURITY_FAILURE (); + } + else if (r == 0) + { + DEBUG_INFO ("blocked\r\n"); + GPG_SECURITY_AUTH_BLOCKED (); + } + else + { + DEBUG_INFO ("good\r\n"); + GPG_SUCCESS (); + } +} + +int +gpg_change_keystring (int who_old, const uint8_t *old_ks, + int who_new, const uint8_t *new_ks) +{ + int r; + int prv_keys_exist = 0; + + r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, who_old, old_ks); + if (r < 0) + return r; + + if (r > 0) + prv_keys_exist++; + + r = gpg_do_chks_prvkey (GPG_KEY_FOR_SIGNING, who_old, old_ks, + who_new, new_ks); + if (r < 0) + return -2; + + r = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, who_old, old_ks); + if (r < 0) + return r; + + if (r > 0) + prv_keys_exist++; + + r = gpg_do_chks_prvkey (GPG_KEY_FOR_DECRYPTION, who_old, old_ks, + who_new, new_ks); + if (r < 0) + return -2; + + r = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, who_old, old_ks); + if (r < 0) + return r; + + if (r > 0) + prv_keys_exist++; + + r = gpg_do_chks_prvkey (GPG_KEY_FOR_AUTHENTICATION, who_old, old_ks, + who_new, new_ks); + if (r < 0) + return -2; + + if (prv_keys_exist) + return 1; + else + return 0; +} + +static void +cmd_change_password (queue_t *ccid_comm) +{ + uint8_t old_ks[KEYSTRING_MD_SIZE]; + uint8_t new_ks0[KEYSTRING_SIZE]; + uint8_t *new_salt = KS_GET_SALT (new_ks0); + int newsalt_len = SALT_SIZE; + uint8_t *new_ks = KS_GET_KEYSTRING (new_ks0); + uint8_t p1 = P1 (apdu); /* 0: change (old+new), 1: exchange (new) */ + uint8_t p2 = P2 (apdu); + int len; + uint8_t *pw, *newpw; + int pw_len, newpw_len; + int who = p2 - 0x80; + int who_old; + int r; + int pw3_null = 0; + const uint8_t *salt; + int salt_len; + const uint8_t *ks_pw3; + + (void)ccid_comm; + DEBUG_INFO ("Change PW\r\n"); + DEBUG_BYTE (who); + + len = apdu.cmd_apdu_data_len; + pw = apdu.cmd_apdu_data; + + if (p1 != 0) + { + GPG_FUNCTION_NOT_SUPPORTED (); + return; + } + + if (gpg_do_kdf_check (len, 2) == 0) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + if (who == BY_USER) /* PW1 */ + { + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + + who_old = who; + pw_len = verify_user_0 (AC_PSO_CDS_AUTHORIZED, pw, len, -1, ks_pw1, 0); + + if (ks_pw1 == NULL) + { + salt = NULL; + salt_len = 0; + } + else + { + salt = KS_GET_SALT (ks_pw1); + salt_len = SALT_SIZE; + } + + if (pw_len < 0) + { + DEBUG_INFO ("permission denied.\r\n"); + GPG_SECURITY_FAILURE (); + return; + } + else if (pw_len == 0) + { + DEBUG_INFO ("blocked.\r\n"); + GPG_SECURITY_AUTH_BLOCKED (); + return; + } + else + { + newpw = pw + pw_len; + newpw_len = len - pw_len; + ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + + /* Check length of password */ + if ((ks_pw3 == NULL && newpw_len < ADMIN_PASSWD_MINLEN) + || newpw_len < USER_PASSWD_MINLEN) + { + DEBUG_INFO ("new password length is too short."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + } + } + else /* PW3 (0x83) */ + { + ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + pw_len = verify_admin_0 (pw, len, -1, ks_pw3, 0); + + if (ks_pw3 == NULL) + { + if (admin_authorized == BY_USER) + { + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + + if (ks_pw1 == NULL) + { + GPG_SECURITY_FAILURE (); + return; + } + + salt = KS_GET_SALT (ks_pw1); + salt_len = SALT_SIZE; + } + else + { + salt = NULL; + salt_len = 0; + } + } + else + { + salt = KS_GET_SALT (ks_pw3); + salt_len = SALT_SIZE; + } + + if (pw_len < 0) + { + DEBUG_INFO ("permission denied.\r\n"); + GPG_SECURITY_FAILURE (); + return; + } + else if (pw_len == 0) + { + DEBUG_INFO ("blocked.\r\n"); + GPG_SECURITY_AUTH_BLOCKED (); + return; + } + else + { + newpw = pw + pw_len; + newpw_len = len - pw_len; + + if (newpw_len == 0 && admin_authorized == BY_ADMIN) + { + const uint8_t *initial_pw; + + gpg_do_get_initial_pw_setting (1, &newpw_len, &initial_pw); + memcpy (newpw, initial_pw, newpw_len); + newsalt_len = 0; + pw3_null = 1; + } + else if (newpw_len < ADMIN_PASSWD_MINLEN) + { + DEBUG_INFO ("new password length is too short."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + who_old = admin_authorized; + } + } + + if (newsalt_len != 0) + random_get_salt (new_salt); + s2k (salt, salt_len, pw, pw_len, old_ks); + s2k (new_salt, newsalt_len, newpw, newpw_len, new_ks); + new_ks0[0] = newpw_len; + + r = gpg_change_keystring (who_old, old_ks, who, new_ks); + if (r <= -2) + { + DEBUG_INFO ("memory error.\r\n"); + GPG_MEMORY_FAILURE (); + } + else if (r < 0) + { + DEBUG_INFO ("security error.\r\n"); + GPG_SECURITY_FAILURE (); + } + else if (r == 0 && who == BY_USER) /* no prvkey */ + { + DEBUG_INFO ("user pass change not supported with no keys.\r\n"); + GPG_CONDITION_NOT_SATISFIED (); + } + else if (r > 0 && who == BY_USER) + { + /* When it was already admin-less mode, admin_authorized is + * BY_USER. If no PW3 keystring, it's becoming admin-less mode, + * now. For these two cases, we need to reset admin + * authorization status. */ + if (admin_authorized == BY_USER) + ac_reset_admin (); + else if (ks_pw3 == NULL) + { + enum kind_of_key kk0; + + /* Remove keystrings for BY_ADMIN. */ + for (kk0 = 0; kk0 <= GPG_KEY_FOR_AUTHENTICATION; kk0++) + gpg_do_chks_prvkey (kk0, BY_ADMIN, NULL, 0, NULL); + + ac_reset_admin (); + } + + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KS_META_SIZE); + ac_reset_pso_cds (); + ac_reset_other (); + DEBUG_INFO ("Changed length of DO_KEYSTRING_PW1.\r\n"); + GPG_SUCCESS (); + } + else if (r > 0 && who == BY_ADMIN) + { + if (pw3_null) + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0); + else + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, new_ks0, KS_META_SIZE); + + ac_reset_admin (); + DEBUG_INFO ("Changed length of DO_KEYSTRING_PW3.\r\n"); + GPG_SUCCESS (); + } + else /* r == 0 && who == BY_ADMIN */ /* no prvkey */ + { + if (pw3_null) + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, NULL, 0); + else + { + new_ks0[0] |= PW_LEN_KEYSTRING_BIT; + gpg_do_write_simple (NR_DO_KEYSTRING_PW3, new_ks0, KEYSTRING_SIZE); + } + DEBUG_INFO ("Changed DO_KEYSTRING_PW3.\r\n"); + ac_reset_admin (); + GPG_SUCCESS (); + } +} + + +#ifndef S2KCOUNT +/* + * OpenPGP uses the value 65535 for the key on disk. + * Given the condition that the access to flash ROM is harder than disk, + * that is, the threat model is different, we chose the default value 192. + */ +#define S2KCOUNT 192 +#endif +void +s2k (const unsigned char *salt, size_t slen, + const unsigned char *input, size_t ilen, unsigned char output[32]) +{ + sha256_context ctx; + size_t count = S2KCOUNT; + const uint8_t *unique = unique_device_id (); + + sha256_start (&ctx); + + sha256_update (&ctx, unique, 12); + + while (count > slen + ilen) + { + if (slen) + sha256_update (&ctx, salt, slen); + sha256_update (&ctx, input, ilen); + count -= slen + ilen; + } + + if (count <= slen) + sha256_update (&ctx, salt, count); + else + { + if (slen) + { + sha256_update (&ctx, salt, slen); + count -= slen; + } + sha256_update (&ctx, input, count); + } + + sha256_finish (&ctx, output); +} + + +static void +cmd_reset_user_password (queue_t *ccid_comm) +{ + uint8_t p1 = P1 (apdu); + int len; + const uint8_t *pw; + const uint8_t *newpw; + int pw_len, newpw_len; + int r; + uint8_t new_ks0[KEYSTRING_SIZE]; + uint8_t *new_ks = KS_GET_KEYSTRING (new_ks0); + uint8_t *new_salt = KS_GET_SALT (new_ks0); + const uint8_t *ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + const uint8_t *salt; + int salt_len; + + (void)ccid_comm; + DEBUG_INFO ("Reset PW1\r\n"); + DEBUG_BYTE (p1); + + len = apdu.cmd_apdu_data_len; + pw = apdu.cmd_apdu_data; + + if (p1 == 0x00) /* by User with Reseting Code */ + { + const uint8_t *ks_rc = gpg_do_read_simple (NR_DO_KEYSTRING_RC); + uint8_t old_ks[KEYSTRING_MD_SIZE]; + + if (gpg_do_kdf_check (len, 2) == 0) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + if (gpg_pw_locked (PW_ERR_RC)) + { + DEBUG_INFO ("blocked.\r\n"); + GPG_SECURITY_AUTH_BLOCKED (); + return; + } + + if (ks_rc == NULL) + { + DEBUG_INFO ("security error.\r\n"); + GPG_SECURITY_FAILURE (); + return; + } + + pw_len = ks_rc[0] & PW_LEN_MASK; + salt = KS_GET_SALT (ks_rc); + salt_len = SALT_SIZE; + newpw = pw + pw_len; + newpw_len = len - pw_len; + + /* Check length of new password */ + if ((ks_pw3 == NULL && newpw_len < ADMIN_PASSWD_MINLEN) + || newpw_len < USER_PASSWD_MINLEN) + { + DEBUG_INFO ("new password length is too short."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + random_get_salt (new_salt); + s2k (salt, salt_len, pw, pw_len, old_ks); + s2k (new_salt, SALT_SIZE, newpw, newpw_len, new_ks); + new_ks0[0] = newpw_len; + r = gpg_change_keystring (BY_RESETCODE, old_ks, BY_USER, new_ks); + if (r <= -2) + { + DEBUG_INFO ("memory error.\r\n"); + GPG_MEMORY_FAILURE (); + } + else if (r < 0) + { + DEBUG_INFO ("failed.\r\n"); + gpg_pw_increment_err_counter (PW_ERR_RC); + GPG_SECURITY_FAILURE (); + } + else if (r == 0) + { + DEBUG_INFO ("user pass change not supported with no keys.\r\n"); + GPG_CONDITION_NOT_SATISFIED (); + } + else + { + DEBUG_INFO ("done.\r\n"); + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KS_META_SIZE); + ac_reset_pso_cds (); + ac_reset_other (); + if (admin_authorized == BY_USER) + ac_reset_admin (); + gpg_pw_reset_err_counter (PW_ERR_RC); + gpg_pw_reset_err_counter (PW_ERR_PW1); + GPG_SUCCESS (); + } + } + else /* by Admin (p1 == 0x02) */ + { + const uint8_t *old_ks = keystring_md_pw3; + + if (!ac_check_status (AC_ADMIN_AUTHORIZED)) + { + DEBUG_INFO ("permission denied.\r\n"); + GPG_SECURITY_FAILURE (); + return; + } + + if (gpg_do_kdf_check (len, 1) == 0) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + newpw_len = len; + newpw = pw; + + /* Check length of new password */ + if ((ks_pw3 == NULL && newpw_len < ADMIN_PASSWD_MINLEN) + || newpw_len < USER_PASSWD_MINLEN) + { + DEBUG_INFO ("new password length is too short."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + random_get_salt (new_salt); + s2k (new_salt, SALT_SIZE, newpw, newpw_len, new_ks); + new_ks0[0] = newpw_len; + r = gpg_change_keystring (admin_authorized, old_ks, BY_USER, new_ks); + if (r <= -2) + { + DEBUG_INFO ("memory error.\r\n"); + GPG_MEMORY_FAILURE (); + } + else if (r < 0) + { + DEBUG_INFO ("security error.\r\n"); + GPG_SECURITY_FAILURE (); + } + else if (r == 0) + { + DEBUG_INFO ("user pass change not supported with no keys.\r\n"); + GPG_CONDITION_NOT_SATISFIED (); + } + else + { + DEBUG_INFO ("done.\r\n"); + gpg_do_write_simple (NR_DO_KEYSTRING_PW1, new_ks0, KS_META_SIZE); + ac_reset_pso_cds (); + ac_reset_other (); + if (admin_authorized == BY_USER) + ac_reset_admin (); + gpg_pw_reset_err_counter (PW_ERR_PW1); + GPG_SUCCESS (); + } + } +} + +static void +cmd_put_data (queue_t *ccid_comm) +{ + uint8_t *data; + uint16_t tag; + int len; + + (void)ccid_comm; + DEBUG_INFO (" - PUT DATA\r\n"); + + tag = ((P1 (apdu)<<8) | P2 (apdu)); + len = apdu.cmd_apdu_data_len; + data = apdu.cmd_apdu_data; + gpg_do_put_data (tag, data, len); +} + +static void +cmd_pgp_gakp (queue_t *ccid_comm) +{ + (void)ccid_comm; + DEBUG_INFO (" - Generate Asymmetric Key Pair\r\n"); + DEBUG_BYTE (P1 (apdu)); + + if (P1 (apdu) == 0x81) + /* Get public key */ + gpg_do_public_key (apdu.cmd_apdu_data[0]); + else + { + if (!ac_check_status (AC_ADMIN_AUTHORIZED)) + GPG_SECURITY_FAILURE (); +#ifdef KDF_DO_REQUIRED + else if (!gpg_do_kdf_check (0, 0)) + GPG_CONDITION_NOT_SATISFIED (); +#endif + else + gpg_do_keygen (&apdu.cmd_apdu_data[0]); + } +} + +#ifdef FLASH_UPGRADE_SUPPORT +const uint8_t * +gpg_get_firmware_update_key (uint8_t keyno) +{ + extern uint8_t _updatekey_store[1024]; + const uint8_t *p; + + p = _updatekey_store + keyno * FIRMWARE_UPDATE_KEY_CONTENT_LEN; + return p; +} +#endif + +#ifdef CERTDO_SUPPORT +#define FILEID_CH_CERTIFICATE_IS_VALID 1 +#else +#define FILEID_CH_CERTIFICATE_IS_VALID 0 +#endif + +static void +cmd_read_binary (queue_t *ccid_comm) +{ + int is_short_EF = (P1 (apdu) & 0x80) != 0; + uint8_t file_id; + uint16_t offset; + + (void)ccid_comm; + DEBUG_INFO (" - Read binary\r\n"); + + if (is_short_EF) + file_id = (P1 (apdu) & 0x1f); + else + file_id = file_selection - FILE_EF_SERIAL_NO + FILEID_SERIAL_NO; + + if (is_short_EF) + { + file_selection = file_id - FILEID_SERIAL_NO + FILE_EF_SERIAL_NO; + offset = P2 (apdu); + } + else + offset = (P1 (apdu) << 8) | P2 (apdu); + + if (file_id == FILEID_SERIAL_NO) + { + if (offset != 0) + GPG_BAD_P1_P2 (); + else + { + gpg_do_get_data (0x004f, 1); /* Get AID... */ + res_APDU[0] = 0x5a; /* ... and overwrite the first byte of data. */ + } + return; + } +#ifdef FLASH_UPGRADE_SUPPORT + else if (file_id >= FILEID_UPDATE_KEY_0 && file_id <= FILEID_UPDATE_KEY_3) + { + if (offset != 0) + GPG_MEMORY_FAILURE (); + else + { + const uint8_t *p; + + p = gpg_get_firmware_update_key (file_id - FILEID_UPDATE_KEY_0); + res_APDU_size = FIRMWARE_UPDATE_KEY_CONTENT_LEN; + memcpy (res_APDU, p, FIRMWARE_UPDATE_KEY_CONTENT_LEN); + GPG_SUCCESS (); + } + } +#endif +#if defined(CERTDO_SUPPORT) + else if (file_id == FILEID_CH_CERTIFICATE) + { + const uint8_t *p; + uint16_t len = 256; + + p = ch_certificate_start; + if (offset >= FLASH_CH_CERTIFICATE_SIZE) + GPG_MEMORY_FAILURE (); + else + { + if (offset + len >= FLASH_CH_CERTIFICATE_SIZE) + len = FLASH_CH_CERTIFICATE_SIZE - offset; + + res_APDU_size = len; + memcpy (res_APDU, p + offset, len); + GPG_SUCCESS (); + } + } +#endif + else + { + GPG_NO_FILE (); + return; + } +} + +static void +cmd_select_file (queue_t *ccid_comm) +{ + (void)ccid_comm; + if (P1 (apdu) == 4) /* Selection by DF name */ + { + DEBUG_INFO (" - select DF by name\r\n"); + + /* name = D2 76 00 01 24 01 */ + if (apdu.cmd_apdu_data_len != 6 + || memcmp (openpgpcard_aid, apdu.cmd_apdu_data, 6) != 0) + { + DEBUG_SHORT (apdu.cmd_apdu_data_len); + DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len); + + GPG_NO_FILE (); + return; + } + + if (file_selection == FILE_CARD_TERMINATED) + { + GPG_APPLICATION_TERMINATED (); + return; + } + + file_selection = FILE_DF_OPENPGP; + + /* Behave just like original OpenPGP card. */ + GPG_SUCCESS (); + } + else if (apdu.cmd_apdu_data_len == 2 + && apdu.cmd_apdu_data[0] == 0x2f && apdu.cmd_apdu_data[1] == 0x02) + { + DEBUG_INFO (" - select 0x2f02 EF\r\n"); + /* + * MF.EF-GDO -- Serial number of the card and name of the owner + */ + GPG_SUCCESS (); + file_selection = FILE_EF_SERIAL_NO; + } + else if (apdu.cmd_apdu_data_len == 2 + && apdu.cmd_apdu_data[0] == 0x3f && apdu.cmd_apdu_data[1] == 0x00) + { + DEBUG_INFO (" - select ROOT MF\r\n"); + if (P2 (apdu) == 0x0c) + { + GPG_SUCCESS (); + } + else + { + int len = sizeof (select_file_TOP_result); + + res_APDU_size = len; + memcpy (res_APDU, select_file_TOP_result, len); + res_APDU[2] = (data_objects_number_of_bytes & 0xff); + res_APDU[3] = (data_objects_number_of_bytes >> 8); + GPG_SUCCESS (); + } + + file_selection = FILE_MF; + ac_fini (); /* Reset authentication */ + } + else + { + DEBUG_INFO (" - select ?? \r\n"); + + file_selection = FILE_NONE; + GPG_NO_FILE (); + } +} + +static void +cmd_get_data (queue_t *ccid_comm) +{ + uint16_t tag = ((P1 (apdu)<<8) | P2 (apdu)); + + (void)ccid_comm; + DEBUG_INFO (" - Get Data\r\n"); + + gpg_do_get_data (tag, 0); +} + +#define ECDSA_HASH_LEN 32 +#define ECDSA_SIGNATURE_LENGTH 64 + +#define EDDSA_HASH_LEN_MAX 256 +#define ED25519_SIGNATURE_LENGTH 64 +#define ED448_SIGNATURE_LENGTH 114 + +#define ECC_CIPHER_DO_HEADER_SIZE 7 + +static void +cmd_pso (queue_t *ccid_comm) +{ + int len = apdu.cmd_apdu_data_len; + int r = -1; + int attr; + int pubkey_len; + unsigned int result_len = 0; + int cs; + + DEBUG_INFO (" - PSO: "); + DEBUG_WORD ((uint32_t)&r); + DEBUG_BINARY (apdu.cmd_apdu_data, apdu.cmd_apdu_data_len); + DEBUG_SHORT (len); + + if (P1 (apdu) == 0x9e && P2 (apdu) == 0x9a) + { + attr = gpg_get_algo_attr (GPG_KEY_FOR_SIGNING); + pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_SIGNING, + GPG_KEY_PUBLIC); + + if (!ac_check_status (AC_PSO_CDS_AUTHORIZED)) + { + DEBUG_INFO ("security error."); + GPG_SECURITY_FAILURE (); + return; + } + +#ifdef ACKBTN_SUPPORT + if (gpg_do_get_uif (GPG_KEY_FOR_SIGNING)) + eventflag_signal (ccid_comm, EV_EXEC_ACK_REQUIRED); +#endif + + if (attr == ALGO_RSA2K || attr == ALGO_RSA4K) + { + /* Check size of digestInfo */ + if (len != 34 /* MD5 */ + && len != 35 /* SHA1 / RIPEMD-160 */ + && len != 47 /* SHA224 */ + && len != 51 /* SHA256 */ + && len != 67 /* SHA384 */ + && len != 83) /* SHA512 */ + { + DEBUG_INFO (" wrong length"); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + DEBUG_BINARY (kd[GPG_KEY_FOR_SIGNING].data, pubkey_len); + + result_len = pubkey_len; + r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len, + &kd[GPG_KEY_FOR_SIGNING], pubkey_len); + } + else if (attr == ALGO_SECP256K1) + { + /* ECDSA with p256r1/p256k1 for signature */ + if (len != ECDSA_HASH_LEN) + { + DEBUG_INFO (" wrong length"); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + //cs = chopstx_setcancelstate (0); + result_len = ECDSA_SIGNATURE_LENGTH; + r = ecdsa_sign_p256k1 (apdu.cmd_apdu_data, res_APDU, + kd[GPG_KEY_FOR_SIGNING].data); + //chopstx_setcancelstate (cs); + } + else if (attr == ALGO_ED25519) + { + uint32_t output[ED25519_SIGNATURE_LENGTH/4]; /* Require 4-byte alignment. */ + + //cs = chopstx_setcancelstate (0); + result_len = ED25519_SIGNATURE_LENGTH; + r = eddsa_sign_25519 (apdu.cmd_apdu_data, len, output, + kd[GPG_KEY_FOR_SIGNING].data, + kd[GPG_KEY_FOR_SIGNING].data+32, + kd[GPG_KEY_FOR_SIGNING].pubkey); + //chopstx_setcancelstate (cs); + memcpy (res_APDU, output, ED25519_SIGNATURE_LENGTH); + } + else if (attr == ALGO_ED448) + { + //cs = chopstx_setcancelstate (0); + result_len = ED448_SIGNATURE_LENGTH; + r = ed448_sign (res_APDU, apdu.cmd_apdu_data, len, + kd[GPG_KEY_FOR_SIGNING].data, + kd[GPG_KEY_FOR_SIGNING].data+57, + kd[GPG_KEY_FOR_SIGNING].pubkey); + //chopstx_setcancelstate (cs); + } + else + { + DEBUG_INFO ("unknown algo."); + GPG_FUNCTION_NOT_SUPPORTED (); + return; + } + + if (r == 0) + { + res_APDU_size = result_len; + gpg_increment_digital_signature_counter (); + } + else /* Failure */ + ac_reset_pso_cds (); + } + else if (P1 (apdu) == 0x80 && P2 (apdu) == 0x86) + { + attr = gpg_get_algo_attr (GPG_KEY_FOR_DECRYPTION); + pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_DECRYPTION, + GPG_KEY_PUBLIC); + + DEBUG_BINARY (kd[GPG_KEY_FOR_DECRYPTION].data, pubkey_len); + + if (!ac_check_status (AC_OTHER_AUTHORIZED)) + { + DEBUG_INFO ("security error."); + GPG_SECURITY_FAILURE (); + return; + } + +#ifdef ACKBTN_SUPPORT + if (gpg_do_get_uif (GPG_KEY_FOR_DECRYPTION)) + eventflag_signal (ccid_comm, EV_EXEC_ACK_REQUIRED); +#else + (void)ccid_comm; +#endif + + if (attr == ALGO_RSA2K || attr == ALGO_RSA4K) + { + /* Skip padding 0x00 */ + len--; + if (len != pubkey_len) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + r = rsa_decrypt (apdu.cmd_apdu_data+1, res_APDU, len, + &kd[GPG_KEY_FOR_DECRYPTION], &result_len); + } + else if (attr == ALGO_SECP256K1) + { + int header = ECC_CIPHER_DO_HEADER_SIZE; + + /* Format is in big endian MPI: 04 || x || y */ + if (len != 65 + ECC_CIPHER_DO_HEADER_SIZE + || apdu.cmd_apdu_data[header] != 0x04) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + //cs = chopstx_setcancelstate (0); + result_len = 65; + r = ecdh_decrypt_p256k1 (apdu.cmd_apdu_data + header, res_APDU, + kd[GPG_KEY_FOR_DECRYPTION].data); + //chopstx_setcancelstate (cs); + } + else if (attr == ALGO_CURVE25519) + { + int header = ECC_CIPHER_DO_HEADER_SIZE; + + if (len != 32 + ECC_CIPHER_DO_HEADER_SIZE) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + //cs = chopstx_setcancelstate (0); + result_len = 32; + r = ecdh_decrypt_curve25519 (apdu.cmd_apdu_data + header, res_APDU, + kd[GPG_KEY_FOR_DECRYPTION].data); + //chopstx_setcancelstate (cs); + } + else if (attr == ALGO_X448) + { + int header = ECC_CIPHER_DO_HEADER_SIZE; + + if (len != 56 + ECC_CIPHER_DO_HEADER_SIZE) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + //cs = chopstx_setcancelstate (0); + result_len = 56; + r = ecdh_decrypt_x448 (res_APDU, apdu.cmd_apdu_data + header, + kd[GPG_KEY_FOR_DECRYPTION].data); + //chopstx_setcancelstate (cs); + } + else + { + DEBUG_INFO ("unknown algo."); + GPG_FUNCTION_NOT_SUPPORTED (); + return; + } + + if (r == 0) + res_APDU_size = result_len; + } + + if (r < 0) + { + DEBUG_INFO (" - ??"); + DEBUG_BYTE (P1 (apdu)); + DEBUG_INFO (" - ??"); + DEBUG_BYTE (P2 (apdu)); + GPG_ERROR (); + } + + DEBUG_INFO ("PSO done.\r\n"); +} + + +#define MAX_RSA_DIGEST_INFO_LEN 102 /* 40% */ +static void +cmd_internal_authenticate (queue_t *ccid_comm) +{ + int attr = gpg_get_algo_attr (GPG_KEY_FOR_AUTHENTICATION); + int pubkey_len = gpg_get_algo_attr_key_size (GPG_KEY_FOR_AUTHENTICATION, + GPG_KEY_PUBLIC); + int len = apdu.cmd_apdu_data_len; + int r = -1; + unsigned int result_len = 0; + int cs; + + DEBUG_INFO (" - INTERNAL AUTHENTICATE\r\n"); + + if (P1 (apdu) != 0x00 || P2 (apdu) != 0x00) + { + DEBUG_INFO (" - ??"); + DEBUG_BYTE (P1 (apdu)); + DEBUG_INFO (" - ??"); + DEBUG_BYTE (P2 (apdu)); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + DEBUG_SHORT (len); + if (!ac_check_status (AC_OTHER_AUTHORIZED)) + { + DEBUG_INFO ("security error."); + GPG_SECURITY_FAILURE (); + return; + } + +#ifdef ACKBTN_SUPPORT + if (gpg_do_get_uif (GPG_KEY_FOR_AUTHENTICATION)) + eventflag_signal (ccid_comm, EV_EXEC_ACK_REQUIRED); +#else + (void)ccid_comm; +#endif + + if (attr == ALGO_RSA2K || attr == ALGO_RSA4K) + { + if (len > MAX_RSA_DIGEST_INFO_LEN) + { + DEBUG_INFO ("input is too long."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + result_len = pubkey_len; + r = rsa_sign (apdu.cmd_apdu_data, res_APDU, len, + &kd[GPG_KEY_FOR_AUTHENTICATION], pubkey_len); + } + else if (attr == ALGO_SECP256K1) + { + if (len != ECDSA_HASH_LEN) + { + DEBUG_INFO ("wrong hash length."); + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + //cs = chopstx_setcancelstate (0); + result_len = ECDSA_SIGNATURE_LENGTH; + r = ecdsa_sign_p256k1 (apdu.cmd_apdu_data, res_APDU, + kd[GPG_KEY_FOR_AUTHENTICATION].data); + //chopstx_setcancelstate (cs); + } + else if (attr == ALGO_ED25519) + { + uint32_t output[ED25519_SIGNATURE_LENGTH/4]; /* Require 4-byte alignment. */ + + //cs = chopstx_setcancelstate (0); + result_len = ED25519_SIGNATURE_LENGTH; + r = eddsa_sign_25519 (apdu.cmd_apdu_data, len, output, + kd[GPG_KEY_FOR_AUTHENTICATION].data, + kd[GPG_KEY_FOR_AUTHENTICATION].data+32, + kd[GPG_KEY_FOR_AUTHENTICATION].pubkey); + //chopstx_setcancelstate (cs); + memcpy (res_APDU, output, ED25519_SIGNATURE_LENGTH); + } + else if (attr == ALGO_ED448) + { + //cs = chopstx_setcancelstate (0); + result_len = ED448_SIGNATURE_LENGTH; + r = ed448_sign (res_APDU, apdu.cmd_apdu_data, len, + kd[GPG_KEY_FOR_AUTHENTICATION].data, + kd[GPG_KEY_FOR_AUTHENTICATION].data+57, + kd[GPG_KEY_FOR_AUTHENTICATION].pubkey); + //chopstx_setcancelstate (cs); + } + + if (r == 0) + res_APDU_size = result_len; + else + GPG_ERROR (); + + DEBUG_INFO ("INTERNAL AUTHENTICATE done.\r\n"); +} + + +#define MBD_OPRATION_WRITE 0 +#define MBD_OPRATION_UPDATE 1 + +static void +modify_binary (uint8_t op, uint8_t p1, uint8_t p2, int len) +{ + uint8_t file_id; + uint16_t offset; + int is_short_EF = (p1 & 0x80) != 0; + int r; + + if (!ac_check_status (AC_ADMIN_AUTHORIZED)) + { + DEBUG_INFO ("security error."); + GPG_SECURITY_FAILURE (); + return; + } + + if (is_short_EF) + file_id = (p1 & 0x1f); + else + file_id = file_selection - FILE_EF_SERIAL_NO + FILEID_SERIAL_NO; + + if (!FILEID_CH_CERTIFICATE_IS_VALID && file_id == FILEID_CH_CERTIFICATE) + { + GPG_NO_FILE (); + return; + } + + if (op == MBD_OPRATION_UPDATE && file_id != FILEID_CH_CERTIFICATE) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + if (file_id > FILEID_CH_CERTIFICATE) + { + GPG_NO_FILE (); + return; + } + + if (is_short_EF) + { + file_selection = file_id - FILEID_SERIAL_NO + FILE_EF_SERIAL_NO; + offset = p2; + + if (op == MBD_OPRATION_UPDATE) + { + r = flash_erase_binary (file_id); + if (r < 0) + { + DEBUG_INFO ("memory error.\r\n"); + GPG_MEMORY_FAILURE (); + return; + } + } + } + else + offset = (p1 << 8) | p2; + + DEBUG_SHORT (len); + DEBUG_SHORT (offset); + + if (file_id == FILEID_CH_CERTIFICATE && (len&1)) + /* It's OK the size of last write is odd. */ + apdu.cmd_apdu_data[len++] = 0xff; + + r = flash_write_binary (file_id, apdu.cmd_apdu_data, len, offset); + if (r < 0) + { + DEBUG_INFO ("memory error.\r\n"); + GPG_MEMORY_FAILURE (); + return; + } + +#ifdef FLASH_UPGRADE_SUPPORT + if (file_id >= FILEID_UPDATE_KEY_0 && file_id <= FILEID_UPDATE_KEY_3 + && len == 0 && offset == 0) + { + int i; + const uint8_t *p; + + for (i = 0; i < 4; i++) + { + p = gpg_get_firmware_update_key (i); + if (p[0] != 0x00 || p[1] != 0x00) /* still valid */ + break; + } + + if (i == 4) /* all update keys are removed */ + { + p = gpg_get_firmware_update_key (0); + flash_erase_page ((uintptr_t)p); + } + } +#endif + + GPG_SUCCESS (); +} + + +#if defined(CERTDO_SUPPORT) +static void +cmd_update_binary (queue_t *ccid_comm) +{ + int len = apdu.cmd_apdu_data_len; + + (void)ccid_comm; + DEBUG_INFO (" - UPDATE BINARY\r\n"); + modify_binary (MBD_OPRATION_UPDATE, P1 (apdu), P2 (apdu), len); + DEBUG_INFO ("UPDATE BINARY done.\r\n"); +} +#endif + + +static void +cmd_write_binary (queue_t *ccid_comm) +{ + int len = apdu.cmd_apdu_data_len; + + (void)ccid_comm; + DEBUG_INFO (" - WRITE BINARY\r\n"); + modify_binary (MBD_OPRATION_WRITE, P1 (apdu), P2 (apdu), len); + DEBUG_INFO ("WRITE BINARY done.\r\n"); +} + + +#ifdef FLASH_UPGRADE_SUPPORT +static void +cmd_external_authenticate (queue_t *ccid_comm) +{ + const uint8_t *pubkey; + const uint8_t *signature = apdu.cmd_apdu_data; + int len = apdu.cmd_apdu_data_len; + uint8_t keyno = P2 (apdu); + int r; + + (void)ccid_comm; + DEBUG_INFO (" - EXTERNAL AUTHENTICATE\r\n"); + + if (keyno >= 4) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + pubkey = gpg_get_firmware_update_key (keyno); + if (len != 256 + || (pubkey[0] == 0xff && pubkey[1] == 0xff) /* not registered */ + || (pubkey[0] == 0x00 && pubkey[1] == 0x00) /* removed */) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + + r = rsa_verify (pubkey, FIRMWARE_UPDATE_KEY_CONTENT_LEN, + challenge, signature); + random_bytes_free (challenge); + challenge = NULL; + + if (r < 0) + { + GPG_SECURITY_FAILURE (); + return; + } + + eventflag_signal (openpgp_comm, EV_EXIT); /* signal to self. */ + set_res_sw (0xff, 0xff); + DEBUG_INFO ("EXTERNAL AUTHENTICATE done.\r\n"); +} +#endif + +static void +cmd_get_challenge (queue_t *ccid_comm) +{ + int len = apdu.expected_res_size; + + (void)ccid_comm; + DEBUG_INFO (" - GET CHALLENGE\r\n"); + + if (len > CHALLENGE_LEN) + { + GPG_CONDITION_NOT_SATISFIED (); + return; + } + else if (len == 0) + /* Le is not specified. Return full-sized challenge by GET_RESPONSE. */ + len = CHALLENGE_LEN; + + if (challenge) + random_bytes_free (challenge); + +#ifdef ACKBTN_SUPPORT + if (gpg_do_get_uif (GPG_KEY_FOR_SIGNING) + || gpg_do_get_uif (GPG_KEY_FOR_DECRYPTION) + || gpg_do_get_uif (GPG_KEY_FOR_AUTHENTICATION)) + eventflag_signal (ccid_comm, EV_EXEC_ACK_REQUIRED); +#endif + + challenge = random_bytes_get (); + memcpy (res_APDU, challenge, len); + res_APDU_size = len; + GPG_SUCCESS (); + DEBUG_INFO ("GET CHALLENGE done.\r\n"); +} + + +#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT +static void +cmd_activate_file (queue_t *ccid_comm) +{ + (void)ccid_comm; + if (file_selection != FILE_CARD_TERMINATED) + { + GPG_NO_RECORD (); + return; + } + + flash_activate (); + file_selection = FILE_DF_OPENPGP; + GPG_SUCCESS (); +} + +static void +cmd_terminate_df (queue_t *ccid_comm) +{ + const uint8_t *ks_pw3; + uint8_t p1 = P1 (apdu); + uint8_t p2 = P2 (apdu); + + (void)ccid_comm; + if (file_selection != FILE_DF_OPENPGP) + { + GPG_NO_RECORD (); + return; + } + + if (p1 != 0 || p2 != 0) + { + GPG_BAD_P1_P2(); + return; + } + + if (apdu.cmd_apdu_data_len != 0) + { + GPG_WRONG_LENGTH(); + return; + } + + ks_pw3 = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); + + if (!ac_check_status (AC_ADMIN_AUTHORIZED) + && !((ks_pw3 && gpg_pw_locked (PW_ERR_PW3)) + || (ks_pw3 == NULL && gpg_pw_locked (PW_ERR_PW1)))) + { + /* Only allow the case admin authorized, or, admin pass is locked. */ + GPG_SECURITY_FAILURE(); + return; + } + + ac_reset_admin (); + ac_reset_pso_cds (); + ac_reset_other (); + gpg_do_terminate (); + flash_terminate (); + file_selection = FILE_CARD_TERMINATED; + GPG_SUCCESS (); +} +#endif + + +struct command +{ + uint8_t command; + void (*cmd_handler) (queue_t *ccid_comm); +}; + +const struct command cmds[] = { + { INS_VERIFY, cmd_verify }, + { INS_CHANGE_REFERENCE_DATA, cmd_change_password }, + { INS_PSO, cmd_pso }, + { INS_RESET_RETRY_COUNTER, cmd_reset_user_password }, +#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT + { INS_ACTIVATE_FILE, cmd_activate_file }, +#endif + { INS_PGP_GENERATE_ASYMMETRIC_KEY_PAIR, cmd_pgp_gakp }, +#ifdef FLASH_UPGRADE_SUPPORT + { INS_EXTERNAL_AUTHENTICATE, /* Not in OpenPGP card protocol */ + cmd_external_authenticate }, +#endif + { INS_GET_CHALLENGE, cmd_get_challenge }, /* Not in OpenPGP card protocol */ + { INS_INTERNAL_AUTHENTICATE, cmd_internal_authenticate }, + { INS_SELECT_FILE, cmd_select_file }, + { INS_READ_BINARY, cmd_read_binary }, /* Not in OpenPGP card protocol */ + { INS_GET_DATA, cmd_get_data }, + { INS_WRITE_BINARY, cmd_write_binary}, /* Not in OpenPGP card protocol */ +#if defined(CERTDO_SUPPORT) + { INS_UPDATE_BINARY, cmd_update_binary }, /* Not in OpenPGP card protocol */ +#endif + { INS_PUT_DATA, cmd_put_data }, + { INS_PUT_DATA_ODD, cmd_put_data }, +#ifdef LIFE_CYCLE_MANAGEMENT_SUPPORT + { INS_TERMINATE_DF, cmd_terminate_df}, +#endif +}; +#define NUM_CMDS ((int)(sizeof (cmds) / sizeof (struct command))) + +static void +process_command_apdu (queue_t *ccid_comm) +{ + int i; + uint8_t cmd = INS (apdu); + + for (i = 0; i < NUM_CMDS; i++) + if (cmds[i].command == cmd) + break; + + if (i < NUM_CMDS) + { + if (file_selection == FILE_CARD_TERMINATED + && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE + && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE) + GPG_APPLICATION_TERMINATED (); + else if (file_selection != FILE_DF_OPENPGP + && cmd != INS_SELECT_FILE && cmd != INS_ACTIVATE_FILE + && cmd != INS_GET_CHALLENGE && cmd != INS_EXTERNAL_AUTHENTICATE + && cmd != INS_WRITE_BINARY && cmd != INS_UPDATE_BINARY + && cmd != INS_READ_BINARY) + GPG_NO_RECORD (); + else + { + //chopstx_setcancelstate (1); + cmds[i].cmd_handler (ccid_comm); + //chopstx_setcancelstate (0); + } + } + else + { + DEBUG_INFO (" - ??"); + DEBUG_BYTE (cmd); + GPG_NO_INS (); + } +} + +void openpgp_card_thread () +{ + queue_t *ccid_comm = (queue_t *)multicore_fifo_pop_blocking(); + openpgp_comm = (queue_t *)multicore_fifo_pop_blocking(); + + gpg_init (); + + while (1) + { +#if defined(PINPAD_SUPPORT) + int len, pw_len, newpw_len; +#endif + + uint32_t m; + queue_remove_blocking(openpgp_comm, &m); + + DEBUG_INFO ("GPG!: "); + + if (m == EV_VERIFY_CMD_AVAILABLE) + { +#if defined(PINPAD_SUPPORT) + if (INS (apdu) != INS_VERIFY) + { + GPG_CONDITION_NOT_SATISFIED (); + goto done; + } + + pw_len = get_pinpad_input (PIN_INPUT_CURRENT); + if (pw_len < 0) + { + GPG_ERROR (); + goto done; + } + memcpy (apdu.cmd_apdu_data, pin_input_buffer, pw_len); + apdu.cmd_apdu_data_len = pw_len; +#else + GPG_ERROR (); + goto done; +#endif + } + else if (m == EV_MODIFY_CMD_AVAILABLE) + { +#if defined(PINPAD_SUPPORT) + uint8_t bConfirmPIN = apdu.cmd_apdu_data[0]; + uint8_t *p = apdu.cmd_apdu_data; + + if (INS (apdu) != INS_CHANGE_REFERENCE_DATA + && INS (apdu) != INS_RESET_RETRY_COUNTER + && INS (apdu) != INS_PUT_DATA) + { + GPG_CONDITION_NOT_SATISFIED (); + goto done; + } + + if ((bConfirmPIN & 2)) /* Require old PIN */ + { + pw_len = get_pinpad_input (PIN_INPUT_CURRENT); + if (pw_len < 0) + { + GPG_ERROR (); + goto done; + } + memcpy (p, pin_input_buffer, pw_len); + p += pw_len; + } + else + pw_len = 0; + + newpw_len = get_pinpad_input (PIN_INPUT_NEW); + if (newpw_len < 0) + { + GPG_ERROR (); + goto done; + } + memcpy (p, pin_input_buffer, newpw_len); + + if ((bConfirmPIN & 1)) /* New PIN twice */ + { + len = get_pinpad_input (PIN_INPUT_CONFIRM); + if (len < 0) + { + GPG_ERROR (); + goto done; + } + + if (len != newpw_len || memcmp (p, pin_input_buffer, len) != 0) + { + GPG_SECURITY_FAILURE (); + goto done; + } + } + + apdu.cmd_apdu_data_len = pw_len + newpw_len; +#else + GPG_ERROR (); + goto done; +#endif + } + else if (m == EV_EXIT) + break; + + process_command_apdu (ccid_comm); + done:; + uint32_t flag = EV_EXEC_FINISHED; + queue_add_blocking(ccid_comm, &flag); + } + + gpg_fini (); +} diff --git a/p448.c b/p448.c new file mode 100644 index 0000000..2ce7a37 --- /dev/null +++ b/p448.c @@ -0,0 +1,666 @@ +/* -*- coding: utf-8 -*- + * p448.c - Modular calculation with p448: 2^448 - 2^224 - 1 + * + * Copyright (C) 2021 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "p448.h" + +#define MASK_28BITS 0x0fffffff + +static void +p448_add_raw (p448_t *x, const p448_t *a, const p448_t *b) +{ + int i; + + for (i = 0; i < N_REDUNDANT_LIMBS; i++) + x->limb[i] = a->limb[i] + b->limb[i]; +} + +static void +p448_sub_raw (p448_t *x, const p448_t *a, const p448_t *b) +{ + int i; + + for (i = 0; i < N_REDUNDANT_LIMBS; i++) + x->limb[i] = a->limb[i] - b->limb[i]; +} + +static uint64_t +mul64_32x32 (const uint32_t a, const uint32_t b) +{ + return ((uint64_t)a) * b; +} + +/** + * Compute X = A * B mod p448 + */ +/* + * When we set phi = 2^224, p448 can be expressed as: + * + * p448 = phi^2 - phy - 1 + * + * Here, using the right hand side and make a fomula + * + * phi^2 - phy - 1 = 0 + * + * it is the fomula where it's solution is golden ratio. + * + * By analogy, so, p448 is called "golden-ratio prime". + * + * When we set phi = 2^224, Karatsuba multiplication goes like: + * + * (p + q * phi) * (r + s * phi) + * = pr + (ps + qr)*phy + qs*phi^2 + * == (pr + qs) + (ps + qr + qs) * phy (mod p448) + * = (pr + qs) + ((p + q)*(r + s) - pr) * phy + * + * That is, it can be done by three times of 224-bit multiplications + * (instead of four). + * + * Let us see more detail. + * + * The formula above is calculated to: + * = lower224(pr + qs) + upper224(pr + qs)*phy + * + lower224((p + q)*(r + s) - pr)*phy + * + upper224((p + q)*(r + s) - pr)*phy^2 (mod p448) + * == lower224(pr + qs) + * + upper224((p + q)*(r + s) - pr) + * + (upper224(pr + qs) + * + lower224((p + q)*(r + s) - pr) + * + upper224((p + q)*(r + s) - pr))*phy (mod p448) + * = lower224(pr + qs) + * + upper224((p + q)*(r + s) - pr) + * + (lower224((p + q)*(r + s) - pr) + * + upper224((p + q)*(r + s) + qs)) * phy + * + */ +/* + +Here is a figure of: multiplication by 8-limb * 8-limb + + a b c d e f g h + * i j k l m n o p +--------------------------------------------- + ap bp cp dp ep fp gp hp + ao bo co do eo fo go ho + an bn cn dn en fn gn hn + am bm cm dm em fm gm hm + al bl cl dl el fl gl hl + ak bk ck dk ek fk gk hk + aj bj cj dj ej fj gj hj +ai bi ci di ei fi gi hi + +Considering lower224, it's: + ap bp cp dp ep fp gp hp + bo co do eo fo go ho + cn dn en fn gn hn + dm em fm gm hm + el fl gl hl + fk gk hk + gj hj + hi + +Considering upper224, it's: + ao + an bn + am bm cm + al bl cl dl + ak bk ck dk ek + aj bj cj dj ej fj + ai bi ci di ei fi gi +*/ +void +p448_mul (p448_t *__restrict__ x, const p448_t *a, const p448_t *b) +{ + int i, j; + uint64_t v64_0, v64_1, v64_2; + uint32_t p_q[8], r_s[8]; + uint32_t *px; + const uint32_t *pa, *pb; + + px = x->limb; + pa = a->limb; + pb = b->limb; + + /* Firstly, we do Karatsuba preparation. */ + for (i = 0; i < 8; i++) + { + p_q[i] = pa[i] + pa[i+8]; + r_s[i] = pb[i] + pb[i+8]; + } + + v64_0 = v64_1 = 0; + + for (j = 0; j < 8; j++) + { + v64_2 = 0; + + /* Compute lower half of limbs (lower224) */ + /* __ <-- j + * | / | + * |/ v i + * + */ + for (i = 0; i <= j; i++) + { + v64_0 += mul64_32x32 (pa[8+j-i], pb[8+i]);/* accumulating q*s */ + v64_1 += mul64_32x32 (p_q[j-i], r_s[i]); /* accumulating p_q*r_s */ + v64_2 += mul64_32x32 (pa[j-i], pb[i]); /* accumulating p*r */ + } + + v64_0 += v64_2; /* Compute pr+qs. */ + v64_1 -= v64_2; /* Compute p_q*r_s - pr. */ + + v64_2 = 0; + + /* Compute upper half of limbs (upper224) */ + /* <-- j + * /| | + * /_| v i + * + */ + for (; i < 8; i++) + { + v64_0 -= mul64_32x32 (pa[8+j-i], pb[i]); /* accumulating -p*r */ + v64_1 += mul64_32x32 (pa[16+j-i], pb[8+i]);/* accumulating q*s */ + v64_2 += mul64_32x32 (p_q[8+j-i], r_s[i]); /* accumulating p_q*r_s */ + } + + v64_0 += v64_2; /* Compute p_q*r_s - pr. */ + v64_1 += v64_2; /* Compute p_q*r_s + qs. */ + + px[j] = v64_0 & MASK_28BITS; + px[j+8] = v64_1 & MASK_28BITS; + + v64_0 >>= 28; + v64_1 >>= 28; + } + + /* "Carry" remains as: 2^448 * v64_1 + 2^224 * v64_0 */ + /* + * Subtract p448 times v64_1 to clear msbs, meaning, clear those + * bits and adding v64_1 to px[0] and px[8] (in mod p448 + * calculation). + */ + v64_0 += v64_1; + v64_0 += px[8]; + v64_1 += px[0]; + px[8] = v64_0 & MASK_28BITS; + px[0] = v64_1 & MASK_28BITS; + + /* Still, it carries to... */ + v64_0 >>= 28; + v64_1 >>= 28; + px[9] += v64_0; + px[1] += v64_1; + /* DONE. */ +} + + +/** + * Compute X = A * 39081 + */ +void +p448_mul_39081 (p448_t *x, const p448_t *a) +{ + int i; + const uint32_t w = 39081; + uint32_t *px; + const uint32_t *pa; + uint64_t v64; + uint32_t carry; + + px = x->limb; + pa = a->limb; + + v64 = 0; + for (i = 0; i < N_REDUNDANT_LIMBS; i++) + { + v64 += mul64_32x32 (w, pa[i]); + px[i] = v64 & MASK_28BITS; + v64 >>= 28; + } + + carry = v64; + carry += px[0]; + px[0] = carry & MASK_28BITS; + px[1] += carry >> 28; + + carry = v64; + carry += px[8]; + px[8] = carry & MASK_28BITS; + px[9] += carry >> 28; +} + +/* + ah bh ch dh eh fh gh HH + bg cg dg eg fg GG + cf df ef FF + de EE + DD + CC dc ec + BB cb db eb fb + AA ba ca da ea fa ga + + */ +/** + * Compute X = A^2 mod p448 + */ +void +p448_sqr (p448_t *__restrict__ x, const p448_t *a) +{ + int i, j; + uint64_t v64_0, v64_1, v64_2, v64_3; + uint32_t p_q[8]; + uint32_t *px; + const uint32_t *pa; + + px = x->limb; + pa = a->limb; + + /* Firstly, we do Karatsuba preparation. */ + for (i = 0; i < 8; i++) + p_q[i] = pa[i] + pa[i+8]; + + v64_0 = v64_1 = 0; + + for (j = 0; j < 8; j++) + { + v64_2 = 0; + + /* Compute lower half of limbs (lower224) */ + /* __ <-- j + * | / | + * |/ v i + * + */ + for (i = 0; i <= j/2; i++) + { + int cond = ((j & 1) || i != j/2); + + v64_3 = mul64_32x32 (pa[8+j-i], pa[8+i]);/* accumulating q*q */ + v64_0 += (v64_3 << cond); + v64_3 = mul64_32x32 (p_q[j-i], p_q[i]); /* accumulating p_q^2 */ + v64_1 += (v64_3 << cond); + v64_3 = mul64_32x32 (pa[j-i], pa[i]); /* accumulating p*p */ + v64_2 += (v64_3 << cond); + } + + v64_0 += v64_2; /* Compute pp+qq. */ + v64_1 -= v64_2; /* Compute p_q^2 - pp. */ + + v64_2 = 0; + /* Compute upper half of limbs (upper224) */ + /* <-- j + * /| | + * /_| v i + * + */ + if (!(j & 1)) + { + v64_0 -= mul64_32x32 (pa[4+i-1], pa[4+i-1]); /* accumulating -p*p */ + v64_1 += mul64_32x32 (pa[12+i-1], pa[12+i-1]);/* accumulating q*q */ + v64_2 += mul64_32x32 (p_q[4+i-1], p_q[4+i-1]);/* accumulating p_q^2 */ + } + + for (; i < 4; i++) + { + v64_3 = mul64_32x32 (pa[4+j-i], pa[4+i]); + v64_0 -= (v64_3 << 1); /* accumulating -p*p */ + v64_3 = mul64_32x32 (pa[12+j-i], pa[12+i]); + v64_1 += (v64_3 << 1); /* accumulating q*q */ + v64_3 = mul64_32x32 (p_q[4+j-i], p_q[4+i]); + v64_2 += (v64_3 << 1); /* accumulating p_q^2 */ + } + + v64_0 += v64_2; /* Compute p_q^2 - p^2. */ + v64_1 += v64_2; /* Compute p_q^2 + q^2. */ + + px[j] = v64_0 & MASK_28BITS; + px[j+8] = v64_1 & MASK_28BITS; + + v64_0 >>= 28; + v64_1 >>= 28; + } + + /* "Carry" remains as: 2^448 * v64_1 + 2^224 * v64_0 */ + /* + * Subtract p448 times v64_1 to clear msbs, meaning, clear those + * bits and adding v64_1 to px[0] and px[8] (in mod p448 + * calculation). + */ + v64_0 += v64_1; + v64_0 += px[8]; + v64_1 += px[0]; + px[8] = v64_0 & MASK_28BITS; + px[0] = v64_1 & MASK_28BITS; + + /* Still, it carries to... */ + v64_0 >>= 28; + v64_1 >>= 28; + px[9] += v64_0; + px[1] += v64_1; + /* DONE. */ +} + +/** + * Weak reduce - Make each limb of redundunt representation smaller. + * Do our best weakly to zeroing most significant 4-bit. + * + * Note that: p448 = 2^448 - 2^224 - 1 + * + * Subtracting p448 means that subtracting 2^448 then adding 2^224 + 1. + */ +void +p448_weak_reduce (p448_t *a) +{ + int i; + uint32_t tmp = a->limb[15] >> 28; + + a->limb[8] += tmp; /* Adding TMP * 2^224 (28 * 8 = 224) */ + + /* Compute top to bottom. */ + for (i = 0; i < N_REDUNDANT_LIMBS - 1; i++) + a->limb[N_REDUNDANT_LIMBS - i - 1] = + (a->limb[N_REDUNDANT_LIMBS - i - 1] & MASK_28BITS) + + (a->limb[N_REDUNDANT_LIMBS - i - 2] >> 28); + + a->limb[0] = (a->limb[0] & MASK_28BITS) + tmp; +} + +static const p448_t p448[1] = { + { + { + 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff, + 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff, + 0x0ffffffe, 0x0fffffff, 0x0fffffff, 0x0fffffff, + 0x0fffffff, 0x0fffffff, 0x0fffffff, 0x0fffffff + } + } +}; + + +static uint32_t +p448_add_carry_cond (p448_t *x, const p448_t *a, const p448_t *b, + uint32_t cond) +{ + int i; + uint32_t v; + uint32_t carry = 0; + uint32_t *px; + const uint32_t *pa, *pb; + + cond = cond * MASK_28BITS; + + px = x->limb; + pa = a->limb; + pb = b->limb; + + for (i = 0; i < N_REDUNDANT_LIMBS; i++) + { + v = *pb & cond; + *px = *pa + carry; + carry = (*px < carry); + *px = (*px + v) & MASK_28BITS; + carry += (*px < v); + px++; + pa++; + pb++; + } + + return carry; +} + + +static uint32_t +p448_sub_borrow (p448_t *x, const p448_t *a, const p448_t *b) +{ + int i; + uint32_t v; + uint32_t borrow = 0; + uint32_t *px; + const uint32_t *pa, *pb; + + px = x->limb; + pa = a->limb; + pb = b->limb; + + for (i = 0; i < N_REDUNDANT_LIMBS; i++) + { + uint32_t borrow0 = (*pa < borrow); + + v = *pb; + *px = *pa - borrow; + borrow = (*px < v) + borrow0; + *px = (*px - v) & MASK_28BITS; + px++; + pa++; + pb++; + } + + return borrow; +} + +/** + * Strong reduce - Make sure that each limb of redundunt + * representation has zeros of significant 4-bit. + */ +void +p448_strong_reduce (p448_t *a) +{ + uint32_t tmp; + uint32_t is_negative; + + /* + * Clear the 4-bit of the last (top) limb. As stated in the comment + * of weak_reduce, subtracting p448 means that subtracting 2^448 + * then adding 2^224 + 1. + */ + tmp = a->limb[15] >> 28; + a->limb[8] += tmp; + a->limb[0] += tmp; + a->limb[15] &= MASK_28BITS; + + /* + * Here, it's: 0 <= v < 2*p448 + * + * When v > p448, subtract p448 from v, then it becomes strongly reduced. + * Otherwise, it's already strongly reduced. + */ + + /* Subtract p448 */ + is_negative = p448_sub_borrow (a, a, p448); + + /* Add p448 conditionally, when it becomes negative. */ + p448_add_carry_cond (a, a, p448, is_negative); +} + +/** + * Convert to wire-format from internal redundant representation. + */ +void +p448_serialize (uint8_t serial[56], const struct p448_t *x) +{ + int i; + p448_t tmp[1]; + uint8_t *p = serial; + + *tmp = *x; + p448_strong_reduce (tmp); + + for (i = 0; i < 8; i++) + { + uint32_t limb0 = tmp->limb[2*i]; + uint32_t limb1 = tmp->limb[2*i+1]; + + *p++ = limb0; + *p++ = (limb0 >> 8); + *p++ = (limb0 >> 16); + *p++ = ((limb0 >> 24) & 0x0f) | ((limb1 & 0x0f )<< 4); + *p++ = (limb1 >> 4); + *p++ = (limb1 >> 12); + *p++ = (limb1 >> 20); + } +} + +/** + * Convert from wire-format to internal redundant representation. + */ +void +p448_deserialize (p448_t *x, const uint8_t serial[56]) +{ + int i; + const uint8_t *p = serial + 56; + + for (i = 0; i < 8; i++) + { + uint32_t v; + + v = *--p; + v <<= 8; + v |= *--p; + v <<= 8; + v |= *--p; + v <<= 8; + v |= *--p; + + x->limb[N_REDUNDANT_LIMBS-2*i-1] = (v >> 4); + + v = (v & 0x0f); + v <<= 8; + v |= *--p; + v <<= 8; + v |= *--p; + v <<= 8; + v |= *--p; + + x->limb[N_REDUNDANT_LIMBS-2*i-2] = v & MASK_28BITS; + } +} + + +/* X = A^(2*N) */ +static void +p448_sqrn (p448_t *__restrict__ x, const p448_t *a, int n) +{ + p448_t tmp[1]; + + if ((n&1)) + { + p448_sqr (x, a); + n--; + } + else + { + p448_sqr (tmp, a); + p448_sqr (x, tmp); + n -= 2; + } + + for (; n; n -= 2) + { + p448_sqr (tmp, x); + p448_sqr (x, tmp); + } +} + +/** + * Compute X = A^(-1) mod p448 (if A=0, return X = 0) + * + * Internally, do A^(p448 - 2) to get A^(-1). + */ +void +p448_inv (p448_t *__restrict__ x, const p448_t *a) +{ + p448_t t[1], u[1]; + + /* + * Bit pattern of p448-2: 1{223} 0 1{222}01 + * + * 222-bit can be composed by 3-bit three times to get 9-bit, 9-bit + * two times to get 18-bit, 18-bit two times plus 1-bit to get 37-bit. + * 37-bit three times to get 111-bit, and lastly 111-bit two times. + * 222 = 111*2 = 37*3*2 = (18*2+1)*3*2 = (9*2*2+1)*3*2 = (3*3*2*2+1)*3*2 + */ + p448_sqr ( x, a ); /* 10 */ + p448_mul ( t, a, x ); /* 11 */ + p448_sqr ( x, t ); /* 110 */ + p448_mul ( t, a, x ); /* 111 */ + p448_sqrn ( x, t, 3 ); /* 111000 */ + p448_mul ( u, t, x ); /* 111111 */ + p448_sqrn ( x, u, 3 ); /* 111111000 */ + p448_mul ( u, t, x ); /* 111111111 */ + p448_sqrn ( t, u, 9 ); /* 1{9} 0{9} */ + p448_mul ( x, u, t ); /* 1{18} */ + p448_sqr ( t, x ); /* 1{18} 0 */ + p448_mul ( u, a, t ); /* 1{19} */ + p448_sqrn ( t, u, 18 ); /* 1{19} 0{18} */ + p448_mul ( u, x, t ); /* 1{37} */ + p448_sqrn ( t, u, 37 ); /* 1{37} 0{37} */ + p448_mul ( x, u, t ); /* 1{74} */ + p448_sqrn ( t, x, 37 ); /* 1{74} 0{37} */ + p448_mul ( x, u, t ); /* 1{111} */ + p448_sqrn ( t, x, 111 ); /* 1{111} 0{111} */ + p448_mul ( u, x, t ); /* 1{222} */ + p448_sqr ( t, u ); /* 1{222} 0 */ + p448_mul ( x, a, t ); /* 1{223} */ + p448_sqrn ( u, x, 224 ); /* 1{223} 0{224} */ + p448_mul ( x, u, t ); /* 1{223} 0 1{222}0 */ + p448_sqr ( t, x ); /* 1{223} 0 1{222}00 */ + p448_mul ( x, a, t ); /* 1{223} 0 1{222}01 */ +} + +static const p448_t p448_times_2[1] = { + { + { + 0x1ffffffe, 0x1ffffffe, 0x1ffffffe, 0x1ffffffe, + 0x1ffffffe, 0x1ffffffe, 0x1ffffffe, 0x1ffffffe, + 0x1ffffffc, 0x1ffffffe, 0x1ffffffe, 0x1ffffffe, + 0x1ffffffe, 0x1ffffffe, 0x1ffffffe, 0x1ffffffe + } + } +}; + +/** + * Compute X = A + B mod p448, result is weakly reduced. + * + */ +void +p448_add (p448_t *x, const p448_t *a, const p448_t *b) +{ + p448_add_raw (x, a, b); + p448_weak_reduce (x); +} + +/** + * Compute X = A - B mod p448, result is weakly reduced. + * + */ +void +p448_sub (p448_t *x, const p448_t *a, const p448_t *b) +{ + p448_t tmp[1]; + + p448_sub_raw (tmp, a, b); + p448_add_raw (x, p448_times_2, tmp); + p448_weak_reduce (x); +} diff --git a/p448.h b/p448.h new file mode 100644 index 0000000..5261c6b --- /dev/null +++ b/p448.h @@ -0,0 +1,15 @@ +#define N_REDUNDANT_LIMBS 16 +typedef struct p448_t +{ + uint32_t limb[N_REDUNDANT_LIMBS]; +} p448_t; + +void p448_add (p448_t *x, const p448_t *a, const p448_t *b); +void p448_sub (p448_t *x, const p448_t *a, const p448_t *b); +void p448_mul (p448_t *__restrict__ x, const p448_t *a, const p448_t *b); +void p448_mul_39081 (p448_t *x, const p448_t *a); +void p448_sqr (p448_t *__restrict__ c, const p448_t *a); +void p448_inv (p448_t *__restrict__ x, const p448_t *a); +void p448_serialize (uint8_t serial[56], const p448_t *x); +void p448_deserialize (p448_t *x, const uint8_t serial[56]); +void p448_strong_reduce (p448_t *a); diff --git a/polarssl/aes.h b/polarssl/aes.h new file mode 100644 index 0000000..f83da34 --- /dev/null +++ b/polarssl/aes.h @@ -0,0 +1,204 @@ +/** + * \file aes.h + * + * \brief AES block cipher + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_AES_H +#define POLARSSL_AES_H + +#include "config.h" + +#include + +#ifdef _MSC_VER +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +#define POLARSSL_ERR_AES_INVALID_KEY_LENGTH -0x0020 /**< Invalid key length. */ +#define POLARSSL_ERR_AES_INVALID_INPUT_LENGTH -0x0022 /**< Invalid data input length. */ + +#if !defined(POLARSSL_AES_ALT) +// Regular implementation +// + +/** + * \brief AES context structure + */ +typedef struct +{ + int nr; /*!< number of rounds */ + uint32_t *rk; /*!< AES round keys */ + uint32_t buf[68]; /*!< unaligned data */ +} +aes_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief AES key schedule (encryption) + * + * \param ctx AES context to be initialized + * \param key encryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, unsigned int keysize ); + +/** + * \brief AES key schedule (decryption) + * + * \param ctx AES context to be initialized + * \param key decryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, unsigned int keysize ); + +/** + * \brief AES-ECB block encryption/decryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 if successful + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +#if 0 +/** + * \brief AES-CBC buffer encryption/decryption + * Length should be a multiple of the block + * size (16 bytes) + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_INPUT_LENGTH + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); +#endif + +/** + * \brief AES-CFB128 buffer encryption/decryption. + * + * Note: Due to the nature of CFB you should use the same key schedule for + * both encryption and decryption. So a context initialized with + * aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT. + * + * both + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv_off offset in IV (updated after use) + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +/** + * \brief AES-CTR buffer encryption/decryption + * + * Warning: You have to keep the maximum use of your counter in mind! + * + * Note: Due to the nature of CTR you should use the same key schedule for + * both encryption and decryption. So a context initialized with + * aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT. + * + * \param length The length of the data + * \param nc_off The offset in the current stream_block (for resuming + * within current cipher stream). The offset pointer to + * should be 0 at the start of a stream. + * \param nonce_counter The 128-bit nonce and counter. + * \param stream_block The saved stream-block for resuming. Is overwritten + * by the function. + * \param input The input data stream + * \param output The output data stream + * + * \return 0 if successful + */ +int aes_crypt_ctr( aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_AES_ALT */ +#include "aes_alt.h" +#endif /* POLARSSL_AES_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int aes_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* aes.h */ diff --git a/polarssl/bignum.h b/polarssl/bignum.h new file mode 100644 index 0000000..7d4d2f2 --- /dev/null +++ b/polarssl/bignum.h @@ -0,0 +1,687 @@ +/** + * \file bignum.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_BIGNUM_H +#define POLARSSL_BIGNUM_H + +#include +#include + +#include "config.h" + +#ifdef _MSC_VER +#include +#if (_MSC_VER <= 1200) +typedef signed short int16_t; +typedef unsigned short uint16_t; +#else +typedef INT16 int16_t; +typedef UINT16 uint16_t; +#endif +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; +#else +#include +#endif + +#define POLARSSL_ERR_MPI_FILE_IO_ERROR -0x0002 /**< An error occurred while reading from or writing to a file. */ +#define POLARSSL_ERR_MPI_BAD_INPUT_DATA -0x0004 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_MPI_INVALID_CHARACTER -0x0006 /**< There is an invalid character in the digit string. */ +#define POLARSSL_ERR_MPI_BUFFER_TOO_SMALL -0x0008 /**< The buffer is too small to write to. */ +#define POLARSSL_ERR_MPI_NEGATIVE_VALUE -0x000A /**< The input arguments are negative or result in illegal output. */ +#define POLARSSL_ERR_MPI_DIVISION_BY_ZERO -0x000C /**< The input argument for division is zero, which is not allowed. */ +#define POLARSSL_ERR_MPI_NOT_ACCEPTABLE -0x000E /**< The input arguments are not acceptable. */ +#define POLARSSL_ERR_MPI_MALLOC_FAILED -0x0010 /**< Memory allocation failed. */ + +#define MPI_CHK(f) if( ( ret = f ) != 0 ) goto cleanup + +/* + * Maximum size MPIs are allowed to grow to in number of limbs. + */ +#define POLARSSL_MPI_MAX_LIMBS 10000 + +#if !defined(POLARSSL_CONFIG_OPTIONS) +/* + * Maximum window size used for modular exponentiation. Default: 6 + * Minimum value: 1. Maximum value: 6. + * + * Result is an array of ( 2 << POLARSSL_MPI_WINDOW_SIZE ) MPIs used + * for the sliding window calculation. (So 64 by default) + * + * Reduction in size, reduces speed. + */ +#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ + +/* + * Maximum size of MPIs allowed in bits and bytes for user-MPIs. + * ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits ) + * + * Note: Calculations can results temporarily in larger MPIs. So the number + * of limbs required (POLARSSL_MPI_MAX_LIMBS) is higher. + */ +#define POLARSSL_MPI_MAX_SIZE 256 /**< Maximum number of bytes for usable MPIs. */ + +#endif /* !POLARSSL_CONFIG_OPTIONS */ + +#define POLARSSL_MPI_MAX_BITS ( 8 * POLARSSL_MPI_MAX_SIZE ) /**< Maximum number of bits for usable MPIs. */ + +/* + * When reading from files with mpi_read_file() and writing to files with + * mpi_write_file() the buffer should have space + * for a (short) label, the MPI (in the provided radix), the newline + * characters and the '\0'. + * + * By default we assume at least a 10 char label, a minimum radix of 10 + * (decimal) and a maximum of 4096 bit numbers (1234 decimal chars). + * Autosized at compile time for at least a 10 char label, a minimum radix + * of 10 (decimal) for a number of POLARSSL_MPI_MAX_BITS size. + * + * This used to be statically sized to 1250 for a maximum of 4096 bit + * numbers (1234 decimal chars). + * + * Calculate using the formula: + * POLARSSL_MPI_RW_BUFFER_SIZE = ceil(POLARSSL_MPI_MAX_BITS / ln(10) * ln(2)) + + * LabelSize + 6 + */ +#define POLARSSL_MPI_MAX_BITS_SCALE100 ( 100 * POLARSSL_MPI_MAX_BITS ) +#define LN_2_DIV_LN_10_SCALE100 332 +#define POLARSSL_MPI_RW_BUFFER_SIZE ( ((POLARSSL_MPI_MAX_BITS_SCALE100 + LN_2_DIV_LN_10_SCALE100 - 1) / LN_2_DIV_LN_10_SCALE100) + 10 + 6 ) + +/* + * Define the base integer type, architecture-wise + */ +#if defined(POLARSSL_HAVE_INT8) +typedef signed char t_sint; +typedef unsigned char t_uint; +typedef uint16_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else +#if defined(POLARSSL_HAVE_INT16) +typedef int16_t t_sint; +typedef uint16_t t_uint; +typedef uint32_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else + #if ( defined(_MSC_VER) && defined(_M_AMD64) ) + typedef int64_t t_sint; + typedef uint64_t t_uint; + #else + #if ( defined(__GNUC__) && ( \ + defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) || \ + (defined(__sparc__) && defined(__arch64__)) || \ + defined(__s390x__) ) ) + typedef int64_t t_sint; + typedef uint64_t t_uint; + typedef unsigned int t_udbl __attribute__((mode(TI))); + #define POLARSSL_HAVE_UDBL + #else + typedef int32_t t_sint; + typedef uint32_t t_uint; + #if ( defined(_MSC_VER) && defined(_M_IX86) ) + typedef uint64_t t_udbl; + #define POLARSSL_HAVE_UDBL + #else + #if defined( POLARSSL_HAVE_LONGLONG ) + typedef unsigned long long t_udbl; + #define POLARSSL_HAVE_UDBL + #endif + #endif + #endif + #endif +#endif /* POLARSSL_HAVE_INT16 */ +#endif /* POLARSSL_HAVE_INT8 */ + +/** + * \brief MPI structure + */ +typedef struct +{ + int s; /*!< integer sign */ + size_t n; /*!< total # of limbs */ + t_uint *p; /*!< pointer to limbs */ +} +mpi; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize one MPI + * + * \param X One MPI to initialize. + */ +void mpi_init( mpi *X ); + +/** + * \brief Unallocate one MPI + * + * \param X One MPI to unallocate. + */ +void mpi_free( mpi *X ); + +/** + * \brief Enlarge to the specified number of limbs + * + * \param X MPI to grow + * \param nblimbs The target number of limbs + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_grow( mpi *X, size_t nblimbs ); + +/** + * \brief Copy the contents of Y into X + * + * \param X Destination MPI + * \param Y Source MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_copy( mpi *X, const mpi *Y ); + +/** + * \brief Swap the contents of X and Y + * + * \param X First MPI value + * \param Y Second MPI value + */ +void mpi_swap( mpi *X, mpi *Y ); + +/** + * \brief Set value from integer + * + * \param X MPI to set + * \param z Value to use + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_lset( mpi *X, t_sint z ); + +/** + * \brief Get a specific bit from X + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * + * \return Either a 0 or a 1 + */ +int mpi_get_bit( const mpi *X, size_t pos ); + +/** + * \brief Set a bit of X to a specific value of 0 or 1 + * + * \note Will grow X if necessary to set a bit to 1 in a not yet + * existing limb. Will not grow if bit should be set to 0 + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * \param val The value to set the bit to (0 or 1) + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if val is not 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ); + +/** + * \brief Return the number of zero-bits before the least significant + * '1' bit + * + * Note: Thus also the zero-based index of the least significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_lsb( const mpi *X ); + +/** + * \brief Return the number of bits up to and including the most + * significant '1' bit' + * + * Note: Thus also the one-based index of the most significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_msb( const mpi *X ); + +/** + * \brief Return the total size in bytes + * + * \param X MPI to use + */ +size_t mpi_size( const mpi *X ); + +/** + * \brief Import from an ASCII string + * + * \param X Destination MPI + * \param radix Input numeric base + * \param s Null-terminated string buffer + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_string( mpi *X, int radix, const char *s ); + +/** + * \brief Export into an ASCII string + * + * \param X Source MPI + * \param radix Output numeric base + * \param s String buffer + * \param slen String buffer size + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code. + * *slen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *slen = 0 to obtain the + * minimum required buffer size in *slen. + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Read X from an opened file + * + * \param X Destination MPI + * \param radix Input numeric base + * \param fin Input file handle + * + * \return 0 if successful, POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if + * the file read buffer is too small or a + * POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ); + +/** + * \brief Write X into an opened file, or stdout if fout is NULL + * + * \param p Prefix, can be NULL + * \param X Source MPI + * \param radix Output numeric base + * \param fout Output file handle (can be NULL) + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + * + * \note Set fout == NULL to print X on the console. + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief Import X from unsigned binary data, big endian + * + * \param X Destination MPI + * \param buf Input buffer + * \param buflen Input buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ); + +/** + * \brief Export X into unsigned binary data, big endian + * + * \param X Source MPI + * \param buf Output buffer + * \param buflen Output buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ); + +/** + * \brief Left-shift: X <<= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_l( mpi *X, size_t count ); + +/** + * \brief Right-shift: X >>= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_r( mpi *X, size_t count ); + +/** + * \brief Compare unsigned values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if |X| is greater than |Y|, + * -1 if |X| is lesser than |Y| or + * 0 if |X| is equal to |Y| + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if X is greater than Y, + * -1 if X is lesser than Y or + * 0 if X is equal to Y + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param z The integer value to compare to + * + * \return 1 if X is greater than z, + * -1 if X is lesser than z or + * 0 if X is equal to z + */ +int mpi_cmp_int( const mpi *X, t_sint z ); + +/** + * \brief Unsigned addition: X = |A| + |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Unsigned substraction: X = |A| - |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B is greater than A + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed substraction: X = A - B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to add + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Signed substraction: X = A - b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to subtract + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Baseline multiplication: X = A * B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Baseline multiplication: X = A * b + * Note: b is an unsigned integer type, thus + * Negative values of b are ignored. + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to multiply with + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Division by mpi: A = Q * B + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Division by int: A = Q * b + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ); + +/** + * \brief Modulo: R = A mod B + * + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B < 0 + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Modulo: r = A mod b + * + * \param r Destination t_uint + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if b < 0 + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ); + +/** + * \brief Sliding-window exponentiation: X = A^E mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param E Exponent MPI + * \param N Modular MPI + * \param _RR Speed-up MPI used for recalculations + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or even or if + * E is negative + * + * \note _RR is used to avoid re-computing R*R mod N across + * multiple calls, which speeds up things a bit. It can + * be set to NULL if the extra performance is unneeded. + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ); + +/** + * \brief Fill an MPI X with size bytes of random + * + * \param X Destination MPI + * \param size Size in bytes + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Greatest common divisor: G = gcd(A, B) + * + * \param G Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ); + +/** + * \brief Modular inverse: X = A^-1 mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param N Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or nil + POLARSSL_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ); + +#if 0 +/** + * \brief Miller-Rabin primality test + * + * \param X MPI to check + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_NOT_ACCEPTABLE if X is not prime + */ +int mpi_is_prime( mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); +#endif + +/** + * \brief Prime number generation + * + * \param X Destination MPI + * \param nbits Required size of X in bits ( 3 <= nbits <= POLARSSL_MPI_MAX_BITS ) + * \param dh_flag If 1, then (X-1)/2 will be prime too + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if nbits is < 3 + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mpi_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/polarssl/bn_mul.h b/polarssl/bn_mul.h new file mode 100644 index 0000000..2ae2ed3 --- /dev/null +++ b/polarssl/bn_mul.h @@ -0,0 +1,901 @@ +/** + * \file bn_mul.h + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef POLARSSL_BN_MUL_H +#define POLARSSL_BN_MUL_H + +#include "polarssl/config.h" + +#if defined(POLARSSL_HAVE_ASM) + +#if defined(__GNUC__) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( " \ + movl %%ebx, %0; \ + movl %5, %%esi; \ + movl %6, %%edi; \ + movl %7, %%ecx; \ + movl %8, %%ebx; \ + " + +#define MULADDC_CORE \ + " \ + lodsl; \ + mull %%ebx; \ + addl %%ecx, %%eax; \ + adcl $0, %%edx; \ + addl (%%edi), %%eax; \ + adcl $0, %%edx; \ + movl %%edx, %%ecx; \ + stosl; \ + " + +#if defined(POLARSSL_HAVE_SSE2) + +#define MULADDC_HUIT \ + " \ + movd %%ecx, %%mm1; \ + movd %%ebx, %%mm0; \ + movd (%%edi), %%mm3; \ + paddq %%mm3, %%mm1; \ + movd (%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + movd 4(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + movd 8(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd 12(%%esi), %%mm7; \ + pmuludq %%mm0, %%mm7; \ + paddq %%mm2, %%mm1; \ + movd 4(%%edi), %%mm3; \ + paddq %%mm4, %%mm3; \ + movd 8(%%edi), %%mm5; \ + paddq %%mm6, %%mm5; \ + movd 12(%%edi), %%mm4; \ + paddq %%mm4, %%mm7; \ + movd %%mm1, (%%edi); \ + movd 16(%%esi), %%mm2; \ + pmuludq %%mm0, %%mm2; \ + psrlq $32, %%mm1; \ + movd 20(%%esi), %%mm4; \ + pmuludq %%mm0, %%mm4; \ + paddq %%mm3, %%mm1; \ + movd 24(%%esi), %%mm6; \ + pmuludq %%mm0, %%mm6; \ + movd %%mm1, 4(%%edi); \ + psrlq $32, %%mm1; \ + movd 28(%%esi), %%mm3; \ + pmuludq %%mm0, %%mm3; \ + paddq %%mm5, %%mm1; \ + movd 16(%%edi), %%mm5; \ + paddq %%mm5, %%mm2; \ + movd %%mm1, 8(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm7, %%mm1; \ + movd 20(%%edi), %%mm5; \ + paddq %%mm5, %%mm4; \ + movd %%mm1, 12(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm2, %%mm1; \ + movd 24(%%edi), %%mm5; \ + paddq %%mm5, %%mm6; \ + movd %%mm1, 16(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm4, %%mm1; \ + movd 28(%%edi), %%mm5; \ + paddq %%mm5, %%mm3; \ + movd %%mm1, 20(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm6, %%mm1; \ + movd %%mm1, 24(%%edi); \ + psrlq $32, %%mm1; \ + paddq %%mm3, %%mm1; \ + movd %%mm1, 28(%%edi); \ + addl $32, %%edi; \ + addl $32, %%esi; \ + psrlq $32, %%mm1; \ + movd %%mm1, %%ecx; \ + " + +#define MULADDC_STOP \ + " \ + emms; \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); + +#else + +#define MULADDC_STOP \ + " \ + movl %4, %%ebx; \ + movl %%ecx, %1; \ + movl %%edi, %2; \ + movl %%esi, %3; \ + " \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( "movq %0, %%rsi " :: "m" (s)); \ + asm( "movq %0, %%rdi " :: "m" (d)); \ + asm( "movq %0, %%rcx " :: "m" (c)); \ + asm( "movq %0, %%rbx " :: "m" (b)); \ + asm( "xorq %r8, %r8 " ); + +#define MULADDC_CORE \ + asm( "movq (%rsi),%rax " ); \ + asm( "mulq %rbx " ); \ + asm( "addq $8, %rsi " ); \ + asm( "addq %rcx, %rax " ); \ + asm( "movq %r8, %rcx " ); \ + asm( "adcq $0, %rdx " ); \ + asm( "nop " ); \ + asm( "addq %rax, (%rdi) " ); \ + asm( "adcq %rdx, %rcx " ); \ + asm( "addq $8, %rdi " ); + +#define MULADDC_STOP \ + asm( "movq %%rcx, %0 " : "=m" (c)); \ + asm( "movq %%rdi, %0 " : "=m" (d)); \ + asm( "movq %%rsi, %0 " : "=m" (s) :: \ + "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( "movl %0, %%a2 " :: "m" (s)); \ + asm( "movl %0, %%a3 " :: "m" (d)); \ + asm( "movl %0, %%d3 " :: "m" (c)); \ + asm( "movl %0, %%d2 " :: "m" (b)); \ + asm( "moveq #0, %d0 " ); + +#define MULADDC_CORE \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "moveq #0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d4, %d3 " ); + +#define MULADDC_STOP \ + asm( "movl %%d3, %0 " : "=m" (c)); \ + asm( "movl %%a3, %0 " : "=m" (d)); \ + asm( "movl %%a2, %0 " : "=m" (s) :: \ + "d0", "d1", "d2", "d3", "d4", "a2", "a3" ); + +#define MULADDC_HUIT \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d4:%d1 " ); \ + asm( "addxl %d3, %d1 " ); \ + asm( "addxl %d0, %d4 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "movel %a2@+, %d1 " ); \ + asm( "mulul %d2, %d3:%d1 " ); \ + asm( "addxl %d4, %d1 " ); \ + asm( "addxl %d0, %d3 " ); \ + asm( "addl %d1, %a3@+ " ); \ + asm( "addxl %d0, %d3 " ); + +#endif /* MC68000 */ + +#if defined(__powerpc__) || defined(__ppc__) +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "ld r3, %0 " :: "m" (s)); \ + asm( "ld r4, %0 " :: "m" (d)); \ + asm( "ld r5, %0 " :: "m" (c)); \ + asm( "ld r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -8 " ); \ + asm( "addi r4, r4, -8 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu r7, 8(r3) " ); \ + asm( "mulld r8, r7, r6 " ); \ + asm( "mulhdu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "ld r7, 8(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stdu r8, 8(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 8 " ); \ + asm( "addi r3, r3, 8 " ); \ + asm( "std r5, %0 " : "=m" (c)); \ + asm( "std r4, %0 " : "=m" (d)); \ + asm( "std r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "ld %%r3, %0 " :: "m" (s)); \ + asm( "ld %%r4, %0 " :: "m" (d)); \ + asm( "ld %%r5, %0 " :: "m" (c)); \ + asm( "ld %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -8 " ); \ + asm( "addi %r4, %r4, -8 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "ldu %r7, 8(%r3) " ); \ + asm( "mulld %r8, %r7, %r6 " ); \ + asm( "mulhdu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "ld %r7, 8(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stdu %r8, 8(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 8 " ); \ + asm( "addi %r3, %r3, 8 " ); \ + asm( "std %%r5, %0 " : "=m" (c)); \ + asm( "std %%r4, %0 " : "=m" (d)); \ + asm( "std %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#else /* PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( "lwz r3, %0 " :: "m" (s)); \ + asm( "lwz r4, %0 " :: "m" (d)); \ + asm( "lwz r5, %0 " :: "m" (c)); \ + asm( "lwz r6, %0 " :: "m" (b)); \ + asm( "addi r3, r3, -4 " ); \ + asm( "addi r4, r4, -4 " ); \ + asm( "addic r5, r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu r7, 4(r3) " ); \ + asm( "mullw r8, r7, r6 " ); \ + asm( "mulhwu r9, r7, r6 " ); \ + asm( "adde r8, r8, r5 " ); \ + asm( "lwz r7, 4(r4) " ); \ + asm( "addze r5, r9 " ); \ + asm( "addc r8, r8, r7 " ); \ + asm( "stwu r8, 4(r4) " ); + +#define MULADDC_STOP \ + asm( "addze r5, r5 " ); \ + asm( "addi r4, r4, 4 " ); \ + asm( "addi r3, r3, 4 " ); \ + asm( "stw r5, %0 " : "=m" (c)); \ + asm( "stw r4, %0 " : "=m" (d)); \ + asm( "stw r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#else + +#define MULADDC_INIT \ + asm( "lwz %%r3, %0 " :: "m" (s)); \ + asm( "lwz %%r4, %0 " :: "m" (d)); \ + asm( "lwz %%r5, %0 " :: "m" (c)); \ + asm( "lwz %%r6, %0 " :: "m" (b)); \ + asm( "addi %r3, %r3, -4 " ); \ + asm( "addi %r4, %r4, -4 " ); \ + asm( "addic %r5, %r5, 0 " ); + +#define MULADDC_CORE \ + asm( "lwzu %r7, 4(%r3) " ); \ + asm( "mullw %r8, %r7, %r6 " ); \ + asm( "mulhwu %r9, %r7, %r6 " ); \ + asm( "adde %r8, %r8, %r5 " ); \ + asm( "lwz %r7, 4(%r4) " ); \ + asm( "addze %r5, %r9 " ); \ + asm( "addc %r8, %r8, %r7 " ); \ + asm( "stwu %r8, 4(%r4) " ); + +#define MULADDC_STOP \ + asm( "addze %r5, %r5 " ); \ + asm( "addi %r4, %r4, 4 " ); \ + asm( "addi %r3, %r3, 4 " ); \ + asm( "stw %%r5, %0 " : "=m" (c)); \ + asm( "stw %%r4, %0 " : "=m" (d)); \ + asm( "stw %%r3, %0 " : "=m" (s) :: \ + "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + +#endif + +#endif /* PPC32 */ +#endif /* PPC64 */ + +#if defined(__sparc__) + +#define MULADDC_INIT \ + asm( "ld %0, %%o0 " :: "m" (s)); \ + asm( "ld %0, %%o1 " :: "m" (d)); \ + asm( "ld %0, %%o2 " :: "m" (c)); \ + asm( "ld %0, %%o3 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ld [%o0], %o4 " ); \ + asm( "inc 4, %o0 " ); \ + asm( "ld [%o1], %o5 " ); \ + asm( "umul %o3, %o4, %o4 " ); \ + asm( "addcc %o4, %o2, %o4 " ); \ + asm( "rd %y, %g1 " ); \ + asm( "addx %g1, 0, %g1 " ); \ + asm( "addcc %o4, %o5, %o4 " ); \ + asm( "st %o4, [%o1] " ); \ + asm( "addx %g1, 0, %o2 " ); \ + asm( "inc 4, %o1 " ); + +#define MULADDC_STOP \ + asm( "st %%o2, %0 " : "=m" (c)); \ + asm( "st %%o1, %0 " : "=m" (d)); \ + asm( "st %%o0, %0 " : "=m" (s) :: \ + "g1", "o0", "o1", "o2", "o3", "o4", "o5" ); + +#endif /* SPARCv8 */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( "lwi r3, %0 " :: "m" (s)); \ + asm( "lwi r4, %0 " :: "m" (d)); \ + asm( "lwi r5, %0 " :: "m" (c)); \ + asm( "lwi r6, %0 " :: "m" (b)); \ + asm( "andi r7, r6, 0xffff" ); \ + asm( "bsrli r6, r6, 16 " ); + +#define MULADDC_CORE \ + asm( "lhui r8, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "lhui r9, r3, 0 " ); \ + asm( "addi r3, r3, 2 " ); \ + asm( "mul r10, r9, r6 " ); \ + asm( "mul r11, r8, r7 " ); \ + asm( "mul r12, r9, r7 " ); \ + asm( "mul r13, r8, r6 " ); \ + asm( "bsrli r8, r10, 16 " ); \ + asm( "bsrli r9, r11, 16 " ); \ + asm( "add r13, r13, r8 " ); \ + asm( "add r13, r13, r9 " ); \ + asm( "bslli r10, r10, 16 " ); \ + asm( "bslli r11, r11, 16 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r11 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "lwi r10, r4, 0 " ); \ + asm( "add r12, r12, r10 " ); \ + asm( "addc r13, r13, r0 " ); \ + asm( "add r12, r12, r5 " ); \ + asm( "addc r5, r13, r0 " ); \ + asm( "swi r12, r4, 0 " ); \ + asm( "addi r4, r4, 4 " ); + +#define MULADDC_STOP \ + asm( "swi r5, %0 " : "=m" (c)); \ + asm( "swi r4, %0 " : "=m" (d)); \ + asm( "swi r3, %0 " : "=m" (s) :: \ + "r3", "r4" , "r5" , "r6" , "r7" , "r8" , \ + "r9", "r10", "r11", "r12", "r13" ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( "ld.a %%a2, %0 " :: "m" (s)); \ + asm( "ld.a %%a3, %0 " :: "m" (d)); \ + asm( "ld.w %%d4, %0 " :: "m" (c)); \ + asm( "ld.w %%d1, %0 " :: "m" (b)); \ + asm( "xor %d5, %d5 " ); + +#define MULADDC_CORE \ + asm( "ld.w %d0, [%a2+] " ); \ + asm( "madd.u %e2, %e4, %d0, %d1 " ); \ + asm( "ld.w %d0, [%a3] " ); \ + asm( "addx %d2, %d2, %d0 " ); \ + asm( "addc %d3, %d3, 0 " ); \ + asm( "mov %d4, %d3 " ); \ + asm( "st.w [%a3+], %d2 " ); + +#define MULADDC_STOP \ + asm( "st.w %0, %%d4 " : "=m" (c)); \ + asm( "st.a %0, %%a3 " : "=m" (d)); \ + asm( "st.a %0, %%a2 " : "=m" (s) :: \ + "d0", "d1", "e2", "d4", "a2", "a3" ); + +#endif /* TriCore */ + +#if defined(__arm__) +#if defined(__ARM_FEATURE_DSP) +/* The ARM DSP instructions are available on Cortex M4, M7 and + Cortex A CPUs */ + +#define MULADDC_1024_CORE \ + "ldmia %[s]!, { r7, r8, r9, r10 } \n\t" \ + "ldmia %[d], { r3, r4, r5, r6 } \n\t" \ + "umaal r3, %2, %[b], r7 \n\t" \ + "umaal r4, %2, %[b], r8 \n\t" \ + "umaal r5, %2, %[b], r9 \n\t" \ + "umaal r6, %2, %[b], r10 \n\t" \ + "stmia %[d]!, {r3, r4, r5, r6} \n\t" + +#define MULADDC_1024_LOOP \ + asm( "tst %[i], #0xfe0 \n\t" \ + "beq 0f \n" \ +"1: sub %[i], %[i], #32 \n\t" \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + "tst %[i], #0xfe0 \n\t" \ + "bne 1b \n" \ +"0:" \ + : [s] "=r" (s), [d] "=r" (d), [c] "=r" (c), [i] "=r" (i) \ + : [b] "r" (b), "[s]" (s), "[d]" (d), "[c]" (c), "[i]" (i) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "memory", "cc" ); + +#define MULADDC_INIT \ + asm( + +#define MULADDC_CORE \ + "ldr r0, [%0], #4 \n\t" \ + "ldr r1, [%1] \n\t" \ + "umaal r1, %2, %3, r0 \n\t" \ + "str r1, [%1], #4 \n\t" + +#define MULADDC_HUIT \ + "ldmia %0!, {r0, r1, r2, r3} \n\t" \ + "ldmia %1, {r4, r5, r6, r7} \n\t" \ + "umaal r4, %2, %3, r0 \n\t" \ + "umaal r5, %2, %3, r1 \n\t" \ + "umaal r6, %2, %3, r2 \n\t" \ + "umaal r7, %2, %3, r3 \n\t" \ + "stmia %1!, {r4, r5, r6, r7} \n\t" \ + "ldmia %0!, {r0, r1, r2, r3} \n\t" \ + "ldmia %1, {r4, r5, r6, r7} \n\t" \ + "umaal r4, %2, %3, r0 \n\t" \ + "umaal r5, %2, %3, r1 \n\t" \ + "umaal r6, %2, %3, r2 \n\t" \ + "umaal r7, %2, %3, r3 \n\t" \ + "stmia %1!, {r4, r5, r6, r7} \n\t" + +#define MULADDC_STOP \ + : "=r" (s), "=r" (d), "=r" (c) \ + : "r" (b), "0" (s), "1" (d), "2" (c) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "memory"); + +#else /* __ARM_FEATURE_DSP */ + +#define MULADDC_1024_CORE \ + "ldmia %[s]!, { r8, r9, r10 } \n\t" \ + "ldmia %[d], { r5, r6, r7 } \n\t" \ + "adcs r5, r5, %[c] \n\t" \ + "umull r4, r8, r8, %[b] \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, r9, %[b] \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adcs r7, r7, %[c] \n\t" \ + "umull r4, r8, r10, %[b] \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r7, r7, r4 \n\t" \ + "stmia %[d]!, { r5, r6, r7 } \n\t" + +#define MULADDC_1024_LOOP \ + asm( "tst %[i], #0xfe0 \n\t" \ + "beq 0f \n" \ +"1: ldmia %[s]!, { r8, r9, r10 } \n\t" \ + "ldmia %[d], { r5, r6, r7 } \n\t" \ + "sub %[i], %[i], #32 \n\t" \ + "adds r5, r5, %[c] \n\t" \ + "umull r4, r8, %[b], r8 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, %[b], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adcs r7, r7, %[c] \n\t" \ + "umull r4, r8, %[b], r10 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r7, r7, r4 \n\t" \ + "stmia %[d]!, { r5, r6, r7 } \n\t" \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE MULADDC_1024_CORE \ + MULADDC_1024_CORE \ + "ldmia %[s]!, { r8, r9 } \n\t" \ + "ldmia %[d], { r5, r6 } \n\t" \ + "adcs r5, r5, %[c] \n\t" \ + "umull r4, r8, %[b], r8 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r5, r5, r4 \n\t" \ + "adcs r6, r6, %[c] \n\t" \ + "umull r4, r8, %[b], r9 \n\t" \ + "adc %[c], r8, #0 \n\t" \ + "adds r6, r6, r4 \n\t" \ + "adc %[c], %[c], #0 \n\t" \ + "stmia %[d]!, { r5, r6 } \n\t" \ + "tst %[i], #0xfe0 \n\t" \ + "bne 1b \n" \ +"0:" \ + : [s] "=r" (s), [d] "=r" (d), [c] "=r" (c), [i] "=r" (i) \ + : [b] "r" (b), "[s]" (s), "[d]" (d), "[c]" (c), "[i]" (i) \ + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "memory", "cc" ); + +/* Just for reference (dead code) */ +#define MULADDC_HUIT_DEAD \ + "ldmia %0!, { r4, r5 } \n\t" \ + "ldmia %1, { r8, r9 } \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r8, r8, r6 \n\t" \ + "umull r6, r7, %3, r5 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "stmia %1!, { r8, r9 } \n\t" \ + "ldmia %0!, { r4, r5 } \n\t" \ + "ldmia %1, { r8, r9 } \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r8, r8, r6 \n\t" \ + "umull r6, r7, %3, r5 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "stmia %1!, { r8, r9 } \n\t" \ + "ldmia %0!, { r4, r5 } \n\t" \ + "ldmia %1, { r8, r9 } \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r8, r8, r6 \n\t" \ + "umull r6, r7, %3, r5 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "stmia %1!, { r8, r9 } \n\t" \ + "ldmia %0!, { r4, r5 } \n\t" \ + "ldmia %1, { r8, r9 } \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r8, r8, r6 \n\t" \ + "umull r6, r7, %3, r5 \n\t" \ + "adcs r6, r6, %2 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "stmia %1!, { r8, r9 } \n\t" + +#define MULADDC_INIT \ + asm( "adds %0, #0 \n\t" + +#define MULADDC_CORE \ + "ldr r5, [%1] \n\t" \ + "ldr r4, [%0], #4 \n\t" \ + "adcs r5, r5, %2 \n\t" \ + "umull r6, r7, %3, r4 \n\t" \ + "adc %2, r7, #0 \n\t" \ + "adds r5, r5, r6 \n\t" \ + "str r5, [%1], #4 \n\t" + +#define MULADDC_STOP \ + "adc %2, %2, #0 " \ + : "=r" (s), "=r" (d), "=r" (c) \ + : "r" (b), "0" (s), "1" (d), "2" (c) \ + : "r4", "r5", "r6", "r7", "memory", "cc" ); + +#endif /* __ARM_FEATURE_DSP */ +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( "ldq $1, %0 " :: "m" (s)); \ + asm( "ldq $2, %0 " :: "m" (d)); \ + asm( "ldq $3, %0 " :: "m" (c)); \ + asm( "ldq $4, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "ldq $6, 0($1) " ); \ + asm( "addq $1, 8, $1 " ); \ + asm( "mulq $6, $4, $7 " ); \ + asm( "umulh $6, $4, $6 " ); \ + asm( "addq $7, $3, $7 " ); \ + asm( "cmpult $7, $3, $3 " ); \ + asm( "ldq $5, 0($2) " ); \ + asm( "addq $7, $5, $7 " ); \ + asm( "cmpult $7, $5, $5 " ); \ + asm( "stq $7, 0($2) " ); \ + asm( "addq $2, 8, $2 " ); \ + asm( "addq $6, $3, $3 " ); \ + asm( "addq $5, $3, $3 " ); + +#define MULADDC_STOP \ + asm( "stq $3, %0 " : "=m" (c)); \ + asm( "stq $2, %0 " : "=m" (d)); \ + asm( "stq $1, %0 " : "=m" (s) :: \ + "$1", "$2", "$3", "$4", "$5", "$6", "$7" ); + +#endif /* Alpha */ + +#if defined(__mips__) + +#define MULADDC_INIT \ + asm( "lw $10, %0 " :: "m" (s)); \ + asm( "lw $11, %0 " :: "m" (d)); \ + asm( "lw $12, %0 " :: "m" (c)); \ + asm( "lw $13, %0 " :: "m" (b)); + +#define MULADDC_CORE \ + asm( "lw $14, 0($10) " ); \ + asm( "multu $13, $14 " ); \ + asm( "addi $10, $10, 4 " ); \ + asm( "mflo $14 " ); \ + asm( "mfhi $9 " ); \ + asm( "addu $14, $12, $14 " ); \ + asm( "lw $15, 0($11) " ); \ + asm( "sltu $12, $14, $12 " ); \ + asm( "addu $15, $14, $15 " ); \ + asm( "sltu $14, $15, $14 " ); \ + asm( "addu $12, $12, $9 " ); \ + asm( "sw $15, 0($11) " ); \ + asm( "addu $12, $12, $14 " ); \ + asm( "addi $11, $11, 4 " ); + +#define MULADDC_STOP \ + asm( "sw $12, %0 " : "=m" (c)); \ + asm( "sw $11, %0 " : "=m" (d)); \ + asm( "sw $10, %0 " : "=m" (s) :: \ + "$9", "$10", "$11", "$12", "$13", "$14", "$15" ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(POLARSSL_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* POLARSSL_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(POLARSSL_HAVE_LONGLONG) + +#define MULADDC_INIT \ +{ \ + t_dbl r; \ + t_int r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (t_dbl) b; \ + r0 = r; \ + r1 = r >> biL; \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + t_uint s0, s1, b0, b1; \ + t_uint r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ diff --git a/polarssl/config.h b/polarssl/config.h new file mode 100644 index 0000000..1e0ae2b --- /dev/null +++ b/polarssl/config.h @@ -0,0 +1,1018 @@ +/** + * \file config.h + * + * \brief Configuration options (set of defines) + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +#ifndef POLARSSL_CONFIG_H +#define POLARSSL_CONFIG_H + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +/** + * \name SECTION: System support + * + * This section sets system specific settings. + * \{ + */ + +/** + * \def POLARSSL_HAVE_INT8 + * + * The system uses 8-bit wide native integers. + * + * Uncomment if native integers are 8-bit wide. +#define POLARSSL_HAVE_INT8 + */ + +/** + * \def POLARSSL_HAVE_INT16 + * + * The system uses 16-bit wide native integers. + * + * Uncomment if native integers are 16-bit wide. +#define POLARSSL_HAVE_INT16 + */ + +/** + * \def POLARSSL_HAVE_LONGLONG + * + * The compiler supports the 'long long' type. + * (Only used on 32-bit platforms) + * +#define POLARSSL_HAVE_LONGLONG + */ + +/** + * \def POLARSSL_HAVE_ASM + * + * The compiler has support for asm() + * + * Uncomment to enable the use of assembly code. + * + * Requires support for asm() in compiler. + * + * Used in: + * library/timing.c + * library/padlock.c + * include/polarssl/bn_mul.h + * + */ +//#define POLARSSL_HAVE_ASM + +/** + * \def POLARSSL_HAVE_SSE2 + * + * CPU supports SSE2 instruction set. + * + * Uncomment if the CPU supports SSE2 (IA-32 specific). + * +#define POLARSSL_HAVE_SSE2 + */ +/* \} name */ + +/** + * \name SECTION: PolarSSL feature support + * + * This section sets support for features that are or are not needed + * within the modules that are enabled. + * \{ + */ + +/** + * \def POLARSSL_XXX_ALT + * + * Uncomment a macro to let PolarSSL use your alternate core implementation of + * a symmetric or hash algorithm (e.g. platform specific assembly optimized + * implementations). Keep in mind that the function prototypes should remain + * the same. + * + * Example: In case you uncomment POLARSSL_AES_ALT, PolarSSL will no longer + * provide the "struct aes_context" definition and omit the base function + * declarations and implementations. "aes_alt.h" will be included from + * "aes.h" to include the new function definitions. + * + * Uncomment a macro to enable alternate implementation for core algorithm + * functions +#define POLARSSL_AES_ALT +#define POLARSSL_ARC4_ALT +#define POLARSSL_BLOWFISH_ALT +#define POLARSSL_CAMELLIA_ALT +#define POLARSSL_DES_ALT +#define POLARSSL_XTEA_ALT +#define POLARSSL_MD2_ALT +#define POLARSSL_MD4_ALT +#define POLARSSL_MD5_ALT +#define POLARSSL_SHA1_ALT +#define POLARSSL_SHA2_ALT +#define POLARSSL_SHA4_ALT + */ + +/** + * \def POLARSSL_AES_ROM_TABLES + * + * Store the AES tables in ROM. + * + * Uncomment this macro to store the AES tables in ROM. + * + */ +#define POLARSSL_AES_ROM_TABLES + +/** + * \def POLARSSL_CIPHER_MODE_CFB + * + * Enable Cipher Feedback mode (CFB) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CFB + +/** + * \def POLARSSL_CIPHER_MODE_CTR + * + * Enable Counter Block Cipher mode (CTR) for symmetric ciphers. + * +#define POLARSSL_CIPHER_MODE_CTR + */ + +/** + * \def POLARSSL_CIPHER_NULL_CIPHER + * + * Enable NULL cipher. + * Warning: Only do so when you know what you are doing. This allows for + * encryption or channels without any security! + * + * Requires POLARSSL_ENABLE_WEAK_CIPHERSUITES as well to enable + * the following ciphersuites: + * TLS_RSA_WITH_NULL_MD5 + * TLS_RSA_WITH_NULL_SHA + * TLS_RSA_WITH_NULL_SHA256 + * + * Uncomment this macro to enable the NULL cipher and ciphersuites +#define POLARSSL_CIPHER_NULL_CIPHER + */ + +/** + * \def POLARSSL_ENABLE_WEAK_CIPHERSUITES + * + * Enable weak ciphersuites in SSL / TLS + * Warning: Only do so when you know what you are doing. This allows for + * channels with virtually no security at all! + * + * This enables the following ciphersuites: + * TLS_RSA_WITH_DES_CBC_SHA + * TLS_DHE_RSA_WITH_DES_CBC_SHA + * + * Uncomment this macro to enable weak ciphersuites +#define POLARSSL_ENABLE_WEAK_CIPHERSUITES + */ + +/** + * \def POLARSSL_ERROR_STRERROR_DUMMY + * + * Enable a dummy error function to make use of error_strerror() in + * third party libraries easier. + * + * Disable if you run into name conflicts and want to really remove the + * error_strerror() + */ +#define POLARSSL_ERROR_STRERROR_DUMMY + +/** + * \def POLARSSL_GENPRIME + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_RSA_C + * + * Enable the RSA prime-number generation code. + */ +#define POLARSSL_GENPRIME + +/** + * \def POLARSSL_FS_IO + * + * Enable functions that use the filesystem. + * +#define POLARSSL_FS_IO + */ + +/** + * \def POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + * + * Do not add default entropy sources. These are the platform specific, + * hardclock and HAVEGE based poll functions. + * + * This is useful to have more control over the added entropy sources in an + * application. + * + * Uncomment this macro to prevent loading of default entropy functions. +#define POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + */ + +/** + * \def POLARSSL_NO_PLATFORM_ENTROPY + * + * Do not use built-in platform entropy functions. + * This is useful if your platform does not support + * standards like the /dev/urandom or Windows CryptoAPI. + * + * Uncomment this macro to disable the built-in platform entropy functions. +#define POLARSSL_NO_PLATFORM_ENTROPY + */ + +/** + * \def POLARSSL_PKCS1_V21 + * + * Requires: POLARSSL_MD_C, POLARSSL_RSA_C + * + * Enable support for PKCS#1 v2.1 encoding. + * This enables support for RSAES-OAEP and RSASSA-PSS operations. + * +#define POLARSSL_PKCS1_V21 + */ + +/** + * \def POLARSSL_RSA_NO_CRT + * + * Do not use the Chinese Remainder Theorem for the RSA private operation. + * + * Uncomment this macro to disable the use of CRT in RSA. + * +#define POLARSSL_RSA_NO_CRT + */ + +/** + * \def POLARSSL_SELF_TEST + * + * Enable the checkup functions (*_self_test). + * +#define POLARSSL_SELF_TEST + */ + +/** + * \def POLARSSL_SSL_ALL_ALERT_MESSAGES + * + * Enable sending of alert messages in case of encountered errors as per RFC. + * If you choose not to send the alert messages, PolarSSL can still communicate + * with other servers, only debugging of failures is harder. + * + * The advantage of not sending alert messages, is that no information is given + * about reasons for failures thus preventing adversaries of gaining intel. + * + * Enable sending of all alert messages + */ +#define POLARSSL_SSL_ALERT_MESSAGES + +/** + * \def POLARSSL_SSL_DEBUG_ALL + * + * Enable the debug messages in SSL module for all issues. + * Debug messages have been disabled in some places to prevent timing + * attacks due to (unbalanced) debugging function calls. + * + * If you need all error reporting you should enable this during debugging, + * but remove this for production servers that should log as well. + * + * Uncomment this macro to report all debug messages on errors introducing + * a timing side-channel. + * +#define POLARSSL_SSL_DEBUG_ALL + */ + +/** + * \def POLARSSL_SSL_HW_RECORD_ACCEL + * + * Enable hooking functions in SSL module for hardware acceleration of + * individual records. + * + * Uncomment this macro to enable hooking functions. +#define POLARSSL_SSL_HW_RECORD_ACCEL + */ + +/** + * \def POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + * + * Enable support for receiving and parsing SSLv2 Client Hello messages for the + * SSL Server module (POLARSSL_SSL_SRV_C) + * + * Comment this macro to disable support for SSLv2 Client Hello messages. + */ +#define POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + +/** + * \def POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an unknown critical extension. + * + * Uncomment to prevent an error. + * +#define POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + */ + +/** + * \def POLARSSL_ZLIB_SUPPORT + * + * If set, the SSL/TLS module uses ZLIB to support compression and + * decompression of packet data. + * + * Used in: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This feature requires zlib library and headers to be present. + * + * Uncomment to enable use of ZLIB +#define POLARSSL_ZLIB_SUPPORT + */ +/* \} name */ + +/** + * \name SECTION: PolarSSL modules + * + * This section enables or disables entire modules in PolarSSL + * \{ + */ + +/** + * \def POLARSSL_AES_C + * + * Enable the AES block cipher. + * + * Module: library/aes.c + * Caller: library/ssl_tls.c + * library/pem.c + * library/ctr_drbg.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_AES_128_CBC_SHA + * TLS_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_RSA_WITH_AES_128_CBC_SHA256 + * TLS_RSA_WITH_AES_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_256_GCM_SHA384 + * + * PEM uses AES for decrypting encrypted keys. + */ +#define POLARSSL_AES_C + +/** + * \def POLARSSL_ARC4_C + * + * Enable the ARCFOUR stream cipher. + * + * Module: library/arc4.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites: + * TLS_RSA_WITH_RC4_128_MD5 + * TLS_RSA_WITH_RC4_128_SHA + */ +#define POLARSSL_ARC4_C + +/** + * \def POLARSSL_ASN1_PARSE_C + * + * Enable the generic ASN1 parser. + * + * Module: library/asn1.c + * Caller: library/x509parse.c + */ +#define POLARSSL_ASN1_PARSE_C + +/** + * \def POLARSSL_ASN1_WRITE_C + * + * Enable the generic ASN1 writer. + * + * Module: library/asn1write.c + */ +#define POLARSSL_ASN1_WRITE_C + +/** + * \def POLARSSL_BASE64_C + * + * Enable the Base64 module. + * + * Module: library/base64.c + * Caller: library/pem.c + * + * This module is required for PEM support (required by X.509). + */ +#define POLARSSL_BASE64_C + +/** + * \def POLARSSL_BIGNUM_C + * + * Enable the multi-precision integer library. + * + * Module: library/bignum.c + * Caller: library/dhm.c + * library/rsa.c + * library/ssl_tls.c + * library/x509parse.c + * + * This module is required for RSA and DHM support. + */ +#define POLARSSL_BIGNUM_C + +/** + * \def POLARSSL_BLOWFISH_C + * + * Enable the Blowfish block cipher. + * + * Module: library/blowfish.c + */ +#define POLARSSL_BLOWFISH_C + +/** + * \def POLARSSL_CAMELLIA_C + * + * Enable the Camellia block cipher. + * + * Module: library/camellia.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + */ +#define POLARSSL_CAMELLIA_C + +/** + * \def POLARSSL_CERTS_C + * + * Enable the test certificates. + * + * Module: library/certs.c + * Caller: + * + * This module is used for testing (ssl_client/server). + */ +#define POLARSSL_CERTS_C + +/** + * \def POLARSSL_CIPHER_C + * + * Enable the generic cipher layer. + * + * Module: library/cipher.c + * Caller: + * + * Uncomment to enable generic cipher wrappers. + */ +#define POLARSSL_CIPHER_C + +/** + * \def POLARSSL_CTR_DRBG_C + * + * Enable the CTR_DRBG AES-256-based random generator + * + * Module: library/ctr_drbg.c + * Caller: + * + * Requires: POLARSSL_AES_C + * + * This module provides the CTR_DRBG AES-256 random number generator. + */ +#define POLARSSL_CTR_DRBG_C + +/** + * \def POLARSSL_DEBUG_C + * + * Enable the debug functions. + * + * Module: library/debug.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module provides debugging functions. + */ +#define POLARSSL_DEBUG_C + +/** + * \def POLARSSL_DES_C + * + * Enable the DES block cipher. + * + * Module: library/des.c + * Caller: library/pem.c + * library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + * + * PEM uses DES/3DES for decrypting encrypted keys. + */ +#define POLARSSL_DES_C + +/** + * \def POLARSSL_DHM_C + * + * Enable the Diffie-Hellman-Merkle key exchange. + * + * Module: library/dhm.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_DHE_RSA_WITH_DES_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + */ +#define POLARSSL_DHM_C + +/** + * \def POLARSSL_ENTROPY_C + * + * Enable the platform-specific entropy code. + * + * Module: library/entropy.c + * Caller: + * + * Requires: POLARSSL_SHA4_C + * + * This module provides a generic entropy pool + */ +#define POLARSSL_ENTROPY_C + +/** + * \def POLARSSL_ERROR_C + * + * Enable error code to error string conversion. + * + * Module: library/error.c + * Caller: + * + * This module enables err_strerror(). + */ +#define POLARSSL_ERROR_C + +/** + * \def POLARSSL_GCM_C + * + * Enable the Galois/Counter Mode (GCM) for AES + * + * Module: library/gcm.c + * + * Requires: POLARSSL_AES_C + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_256_GCM_SHA384 + */ +#define POLARSSL_GCM_C + +/** + * \def POLARSSL_HAVEGE_C + * + * Enable the HAVEGE random generator. + * + * Warning: the HAVEGE random generator is not suitable for virtualized + * environments + * + * Warning: the HAVEGE random generator is dependent on timing and specific + * processor traits. It is therefore not advised to use HAVEGE as + * your applications primary random generator or primary entropy pool + * input. As a secondary input to your entropy pool, it IS able add + * the (limited) extra entropy it provides. + * + * Module: library/havege.c + * Caller: + * + * Requires: POLARSSL_TIMING_C + * + * Uncomment to enable the HAVEGE random generator. +#define POLARSSL_HAVEGE_C + */ + +/** + * \def POLARSSL_MD_C + * + * Enable the generic message digest layer. + * + * Module: library/md.c + * Caller: + * + * Uncomment to enable generic message digest wrappers. + */ +#define POLARSSL_MD_C + +/** + * \def POLARSSL_MD2_C + * + * Enable the MD2 hash algorithm + * + * Module: library/md2.c + * Caller: library/x509parse.c + * + * Uncomment to enable support for (rare) MD2-signed X.509 certs. + * +#define POLARSSL_MD2_C + */ + +/** + * \def POLARSSL_MD4_C + * + * Enable the MD4 hash algorithm + * + * Module: library/md4.c + * Caller: library/x509parse.c + * + * Uncomment to enable support for (rare) MD4-signed X.509 certs. + * +#define POLARSSL_MD4_C + */ + +/** + * \def POLARSSL_MD5_C + * + * Enable the MD5 hash algorithm + * + * Module: library/md5.c + * Caller: library/pem.c + * library/ssl_tls.c + * library/x509parse.c + * + * This module is required for SSL/TLS and X.509. + * PEM uses MD5 for decrypting encrypted keys. + */ +#define POLARSSL_MD5_C + +/** + * \def POLARSSL_NET_C + * + * Enable the TCP/IP networking routines. + * + * Module: library/net.c + * Caller: + * + * This module provides TCP/IP networking routines. + */ +#define POLARSSL_NET_C + +/** + * \def POLARSSL_PADLOCK_C + * + * Enable VIA Padlock support on x86. + * + * Module: library/padlock.c + * Caller: library/aes.c + * + * This modules adds support for the VIA PadLock on x86. + * +#define POLARSSL_PADLOCK_C + */ + +/** + * \def POLARSSL_PBKDF2_C + * + * Enable PKCS#5 PBKDF2 key derivation function + * DEPRECATED: Use POLARSSL_PKCS5_C instead + * + * Module: library/pbkdf2.c + * + * Requires: POLARSSL_PKCS5_C + * + * This module adds support for the PKCS#5 PBKDF2 key derivation function. +#define POLARSSL_PBKDF2_C + */ + +/** + * \def POLARSSL_PEM_C + * + * Enable PEM decoding + * + * Module: library/pem.c + * Caller: library/x509parse.c + * + * Requires: POLARSSL_BASE64_C + * + * This modules adds support for decoding PEM files. + */ +#define POLARSSL_PEM_C + +/** + * \def POLARSSL_PKCS5_C + * + * Enable PKCS#5 functions + * + * Module: library/pkcs5.c + * + * Requires: POLARSSL_MD_C + * + * This module adds support for the PKCS#5 functions. + */ +#define POLARSSL_PKCS5_C + +/** + * \def POLARSSL_PKCS11_C + * + * Enable wrapper for PKCS#11 smartcard support. + * + * Module: library/ssl_srv.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module enables SSL/TLS PKCS #11 smartcard support. + * Requires the presence of the PKCS#11 helper library (libpkcs11-helper) +#define POLARSSL_PKCS11_C + */ + +/** + * \def POLARSSL_PKCS12_C + * + * Enable PKCS#12 PBE functions + * Adds algorithms for parsing PKCS#8 encrypted private keys + * + * Module: library/pkcs12.c + * Caller: library/x509parse.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_CIPHER_C, POLARSSL_MD_C + * Can use: POLARSSL_ARC4_C + * + * This module enables PKCS#12 functions. + */ +#define POLARSSL_PKCS12_C + +/** + * \def POLARSSL_RSA_C + * + * Enable the RSA public-key cryptosystem. + * + * Module: library/rsa.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509.c + * + * Requires: POLARSSL_BIGNUM_C + * + * This module is required for SSL/TLS and MD5-signed certificates. + */ +#define POLARSSL_RSA_C + +/** + * \def POLARSSL_SHA1_C + * + * Enable the SHA1 cryptographic hash algorithm. + * + * Module: library/sha1.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509parse.c + * + * This module is required for SSL/TLS and SHA1-signed certificates. + */ +#define POLARSSL_SHA1_C + +/** + * \def POLARSSL_SHA2_C + * + * Enable the SHA-224 and SHA-256 cryptographic hash algorithms. + * + * Module: library/sha2.c + * Caller: library/md_wrap.c + * library/x509parse.c + * + * This module adds support for SHA-224 and SHA-256. + * This module is required for the SSL/TLS 1.2 PRF function. + */ +#define POLARSSL_SHA2_C + +/** + * \def POLARSSL_SHA4_C + * + * Enable the SHA-384 and SHA-512 cryptographic hash algorithms. + * + * Module: library/sha4.c + * Caller: library/md_wrap.c + * library/x509parse.c + * + * This module adds support for SHA-384 and SHA-512. + */ +#define POLARSSL_SHA4_C + +/** + * \def POLARSSL_SSL_CACHE_C + * + * Enable simple SSL cache implementation. + * + * Module: library/ssl_cache.c + * Caller: + * + * Requires: POLARSSL_SSL_CACHE_C + */ +#define POLARSSL_SSL_CACHE_C + +/** + * \def POLARSSL_SSL_CLI_C + * + * Enable the SSL/TLS client code. + * + * Module: library/ssl_cli.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS client support. + */ +#define POLARSSL_SSL_CLI_C + +/** + * \def POLARSSL_SSL_SRV_C + * + * Enable the SSL/TLS server code. + * + * Module: library/ssl_srv.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS server support. + */ +#define POLARSSL_SSL_SRV_C + +/** + * \def POLARSSL_SSL_TLS_C + * + * Enable the generic SSL/TLS code. + * + * Module: library/ssl_tls.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_MD5_C, POLARSSL_SHA1_C, POLARSSL_X509_PARSE_C + * + * This module is required for SSL/TLS. + */ +#define POLARSSL_SSL_TLS_C + +/** + * \def POLARSSL_TIMING_C + * + * Enable the portable timing interface. + * + * Module: library/timing.c + * Caller: library/havege.c + * + * This module is used by the HAVEGE random number generator. + */ +#define POLARSSL_TIMING_C + +/** + * \def POLARSSL_VERSION_C + * + * Enable run-time version information. + * + * Module: library/version.c + * + * This module provides run-time version information. + */ +#define POLARSSL_VERSION_C + +/** + * \def POLARSSL_X509_PARSE_C + * + * Enable X.509 certificate parsing. + * + * Module: library/x509parse.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_BIGNUM_C, POLARSSL_RSA_C + * + * This module is required for X.509 certificate parsing. + */ +#define POLARSSL_X509_PARSE_C + +/** + * \def POLARSSL_X509_WRITE_C + * + * Enable X.509 buffer writing. + * + * Module: library/x509write.c + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_RSA_C + * + * This module is required for X.509 certificate request writing. + */ +#define POLARSSL_X509_WRITE_C + +/** + * \def POLARSSL_XTEA_C + * + * Enable the XTEA block cipher. + * + * Module: library/xtea.c + * Caller: + */ +#define POLARSSL_XTEA_C +/* \} name */ + +/** + * \name SECTION: Module configuration options + * + * This section allows for the setting of module specific sizes and + * configuration options. The default values are already present in the + * relevant header files and should suffice for the regular use cases. + * Our advice is to enable POLARSSL_CONFIG_OPTIONS and change values here + * only if you have a good reason and know the consequences. + * + * If POLARSSL_CONFIG_OPTIONS is undefined here the options in the module + * header file take precedence. + * + * Please check the respective header file for documentation on these + * parameters (to prevent duplicate documentation). + * + * Uncomment POLARSSL_CONFIG_OPTIONS to enable using the values defined here. + * \{ + */ +//#define POLARSSL_CONFIG_OPTIONS /**< Enable config.h module value configuration */ + +#if defined(POLARSSL_CONFIG_OPTIONS) + +// MPI / BIGNUM options +// +#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ +#define POLARSSL_MPI_MAX_SIZE 512 /**< Maximum number of bytes for usable MPIs. */ + +// CTR_DRBG options +// +#define CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default */ +#define CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +#define CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +#define CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +#define CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +// Entropy options +// +#define ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +#define ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ + +// SSL Cache options +// +#define SSL_CACHE_DEFAULT_TIMEOUT 86400 /**< 1 day */ +#define SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /**< Maximum entries in cache */ + +// SSL options +// +#define SSL_MAX_CONTENT_LEN 16384 /**< Size of the input / output buffer */ + +#endif /* POLARSSL_CONFIG_OPTIONS */ + +/* \} name */ +#endif /* config.h */ diff --git a/polarssl/rsa.h b/polarssl/rsa.h new file mode 100644 index 0000000..0377e98 --- /dev/null +++ b/polarssl/rsa.h @@ -0,0 +1,633 @@ +/** + * \file rsa.h + * + * \brief The RSA public-key cryptosystem + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_RSA_H +#define POLARSSL_RSA_H + +#include "bignum.h" + +/* + * RSA Error codes + */ +#define POLARSSL_ERR_RSA_BAD_INPUT_DATA -0x4080 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_RSA_INVALID_PADDING -0x4100 /**< Input data contains invalid padding and is rejected. */ +#define POLARSSL_ERR_RSA_KEY_GEN_FAILED -0x4180 /**< Something failed during generation of a key. */ +#define POLARSSL_ERR_RSA_KEY_CHECK_FAILED -0x4200 /**< Key failed to pass the libraries validity check. */ +#define POLARSSL_ERR_RSA_PUBLIC_FAILED -0x4280 /**< The public key operation failed. */ +#define POLARSSL_ERR_RSA_PRIVATE_FAILED -0x4300 /**< The private key operation failed. */ +#define POLARSSL_ERR_RSA_VERIFY_FAILED -0x4380 /**< The PKCS#1 verification failed. */ +#define POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE -0x4400 /**< The output buffer for decryption is not large enough. */ +#define POLARSSL_ERR_RSA_RNG_FAILED -0x4480 /**< The random generator failed to generate non-zeros. */ + +/* + * PKCS#1 constants + */ +#define SIG_RSA_RAW 0 +#define SIG_RSA_MD2 2 +#define SIG_RSA_MD4 3 +#define SIG_RSA_MD5 4 +#define SIG_RSA_SHA1 5 +#define SIG_RSA_SHA224 14 +#define SIG_RSA_SHA256 11 +#define SIG_RSA_SHA384 12 +#define SIG_RSA_SHA512 13 + +#define RSA_PUBLIC 0 +#define RSA_PRIVATE 1 + +#define RSA_PKCS_V15 0 +#define RSA_PKCS_V21 1 + +#define RSA_SIGN 1 +#define RSA_CRYPT 2 + +#define ASN1_STR_CONSTRUCTED_SEQUENCE "\x30" +#define ASN1_STR_NULL "\x05" +#define ASN1_STR_OID "\x06" +#define ASN1_STR_OCTET_STRING "\x04" + +#define OID_DIGEST_ALG_MDX "\x2A\x86\x48\x86\xF7\x0D\x02\x00" +#define OID_HASH_ALG_SHA1 "\x2b\x0e\x03\x02\x1a" +#define OID_HASH_ALG_SHA2X "\x60\x86\x48\x01\x65\x03\x04\x02\x00" + +#define OID_ISO_MEMBER_BODIES "\x2a" +#define OID_ISO_IDENTIFIED_ORG "\x2b" + +/* + * ISO Member bodies OID parts + */ +#define OID_COUNTRY_US "\x86\x48" +#define OID_RSA_DATA_SECURITY "\x86\xf7\x0d" + +/* + * ISO Identified organization OID parts + */ +#define OID_OIW_SECSIG_SHA1 "\x0e\x03\x02\x1a" + +/* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ +#define ASN1_HASH_MDX \ +( \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x20" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0C" \ + ASN1_STR_OID "\x08" \ + OID_DIGEST_ALG_MDX \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x10" \ +) + +#define ASN1_HASH_SHA1 \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x21" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x09" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA1_ALT \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x1F" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x07" \ + ASN1_STR_OID "\x05" \ + OID_HASH_ALG_SHA1 \ + ASN1_STR_OCTET_STRING "\x14" + +#define ASN1_HASH_SHA2X \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x11" \ + ASN1_STR_CONSTRUCTED_SEQUENCE "\x0d" \ + ASN1_STR_OID "\x09" \ + OID_HASH_ALG_SHA2X \ + ASN1_STR_NULL "\x00" \ + ASN1_STR_OCTET_STRING "\x00" + +/** + * \brief RSA context structure + */ +typedef struct +{ + int ver; /*!< always 0 */ + size_t len; /*!< size(N) in chars */ + + mpi N; /*!< public modulus */ + mpi E; /*!< public exponent */ + + mpi D; /*!< private exponent */ + mpi P; /*!< 1st prime factor */ + mpi Q; /*!< 2nd prime factor */ + mpi DP; /*!< D % (P - 1) */ + mpi DQ; /*!< D % (Q - 1) */ + mpi QP; /*!< 1 / (Q % P) */ + + mpi RN; /*!< cached R^2 mod N */ + mpi RP; /*!< cached R^2 mod P */ + mpi RQ; /*!< cached R^2 mod Q */ + + int padding; /*!< RSA_PKCS_V15 for 1.5 padding and + RSA_PKCS_v21 for OAEP/PSS */ + int hash_id; /*!< Hash identifier of md_type_t as + specified in the md.h header file + for the EME-OAEP and EMSA-PSS + encoding */ +} +rsa_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize an RSA context + * + * Note: Set padding to RSA_PKCS_V21 for the RSAES-OAEP + * encryption scheme and the RSASSA-PSS signature scheme. + * + * \param ctx RSA context to be initialized + * \param padding RSA_PKCS_V15 or RSA_PKCS_V21 + * \param hash_id RSA_PKCS_V21 hash identifier + * + * \note The hash_id parameter is actually ignored + * when using RSA_PKCS_V15 padding. + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id); + +/** + * \brief Generate an RSA keypair + * + * \param ctx RSA context that will hold the key + * \param f_rng RNG function + * \param p_rng RNG parameter + * \param nbits size of the public key in bits + * \param exponent public exponent (e.g., 65537) + * + * \note rsa_init() must be called beforehand to setup + * the RSA context. + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ); + +/** + * \brief Check a public RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_pubkey( const rsa_context *ctx ); + +/** + * \brief Check a private RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_privkey( const rsa_context *ctx ); + +/** + * \brief Do an RSA public key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note This function does NOT take care of message + * padding. Also, be sure to set input[0] = 0 or assure that + * input is smaller than N. + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Do an RSA private key operation + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for blinding) + * \param p_rng RNG parameter + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_private( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 encryption using the + * mode from the context. Add the message padding, then do an + * RSA operation. + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding + * and RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v1.5 encryption (RSAES-PKCS1-v1_5-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP encryption (RSAES-OAEP-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding + * and RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 decryption using the + * mode from the context. Do an RSA operation, then remove + * the message padding + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v1.5 decryption (RSAES-PKCS1-v1_5-DECRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP decryption (RSAES-OAEP-DECRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Generic wrapper to perform a PKCS#1 signature using the + * mode from the context. Do a private RSA operation to sign + * a message digest + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding and for + * RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * encoding. hash_id in the function call is the type of hash + * that is encoded. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 signature (RSASSA-PKCS1-v1_5-SIGN) + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS signature (RSASSA-PSS-SIGN) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding and for + * RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * encoding. hash_id in the function call is the type of hash + * that is encoded. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Generic wrapper to perform a PKCS#1 verification using the + * mode from the context. Do a public RSA operation and check + * the message digest + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * verification. hash_id in the function call is the type of hash + * that is verified. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 verification (RSASSA-PKCS1-v1_5-VERIFY) + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS verification (RSASSA-PSS-VERIFY) + * \brief Do a public RSA and check the message digest + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param hash_id SIG_RSA_RAW, SIG_RSA_MD{2,4,5} or SIG_RSA_SHA{1,224,256,384,512} + * \param hashlen message digest length (for SIG_RSA_RAW only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding keep in mind that + * the hash_id in the RSA context is the one used for the + * verification. hash_id in the function call is the type of hash + * that is verified. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Free the components of an RSA key + * + * \param ctx RSA Context to free + */ +void rsa_free( rsa_context *ctx ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int rsa_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* rsa.h */ diff --git a/random.c b/random.c new file mode 100644 index 0000000..ec957a7 --- /dev/null +++ b/random.c @@ -0,0 +1,120 @@ +/* + * random.c -- get random bytes + * + * Copyright (C) 2010, 2011, 2012, 2013, 2015 + * Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 "gnuk.h" +#include "neug.h" + +#define RANDOM_BYTES_LENGTH 32 +static uint32_t random_word[RANDOM_BYTES_LENGTH/sizeof (uint32_t)]; + +void +random_init (void) +{ + int i; + + neug_init (random_word, RANDOM_BYTES_LENGTH/sizeof (uint32_t)); + + for (i = 0; i < NEUG_PRE_LOOP; i++) + (void)neug_get (NEUG_KICK_FILLING); +} + +void +random_fini (void) +{ + neug_fini (); +} + +/* + * Return pointer to random 32-byte + */ +const uint8_t * +random_bytes_get (void) +{ + neug_wait_full (); + return (const uint8_t *)random_word; +} + +/* + * Free pointer to random 32-byte + */ +void +random_bytes_free (const uint8_t *p) +{ + (void)p; + memset (random_word, 0, RANDOM_BYTES_LENGTH); + neug_flush (); +} + +/* + * Return 4-byte salt + */ +void +random_get_salt (uint8_t *p) +{ + uint32_t rnd; + + rnd = neug_get (NEUG_KICK_FILLING); + memcpy (p, &rnd, sizeof (uint32_t)); + rnd = neug_get (NEUG_KICK_FILLING); + memcpy (p + sizeof (uint32_t), &rnd, sizeof (uint32_t)); +} + + +/* + * Random byte iterator + */ +int +random_gen (void *arg, unsigned char *out, size_t out_len) +{ + uint8_t *index_p = (uint8_t *)arg; + uint8_t index = *index_p; + size_t n; + + while (out_len) + { + neug_wait_full (); + + n = RANDOM_BYTES_LENGTH - index; + if (n > out_len) + n = out_len; + + memcpy (out, ((unsigned char *)random_word) + index, n); + out += n; + out_len -= n; + index += n; + + if (index >= RANDOM_BYTES_LENGTH) + { + index = 0; + neug_flush (); + } + } + + *index_p = index; + + return 0; +} diff --git a/random.h b/random.h new file mode 100644 index 0000000..026922a --- /dev/null +++ b/random.h @@ -0,0 +1,12 @@ +void random_init (void); +void random_fini (void); + +/* 32-byte random bytes */ +const uint8_t *random_bytes_get (void); +void random_bytes_free (const uint8_t *p); + +/* 8-byte salt */ +void random_get_salt (uint8_t *p); + +/* iterator returning a byta at a time */ +int random_gen (void *arg, unsigned char *output, size_t output_len); diff --git a/rsa.c b/rsa.c new file mode 100644 index 0000000..3e9b829 --- /dev/null +++ b/rsa.c @@ -0,0 +1,1521 @@ +/* + * The RSA public-key cryptosystem + * + * Copyright (C) 2006-2011, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * RSA was designed by Ron Rivest, Adi Shamir and Len Adleman. + * + * http://theory.lcs.mit.edu/~rivest/rsapaper.pdf + * http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_RSA_C) + +#include "polarssl/rsa.h" + +#if defined(POLARSSL_PKCS1_V21) +#include "polarssl/md.h" +#endif + +#include + +/* + * Initialize an RSA context + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id ) +{ + memset( ctx, 0, sizeof( rsa_context ) ); + + ctx->padding = padding; + ctx->hash_id = hash_id; +} + +#if defined(POLARSSL_GENPRIME) + +/* + * Generate an RSA keypair + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ) +{ + int ret; + mpi P1, Q1, H, G; + + if( f_rng == NULL || nbits < 128 || exponent < 3 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + mpi_init( &P1 ); mpi_init( &Q1 ); mpi_init( &H ); mpi_init( &G ); + + /* + * find primes P and Q with Q < P so that: + * GCD( E, (P-1)*(Q-1) ) == 1 + */ + MPI_CHK( mpi_lset( &ctx->E, exponent ) ); + + do + { + MPI_CHK( mpi_gen_prime( &ctx->P, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + MPI_CHK( mpi_gen_prime( &ctx->Q, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) < 0 ) + mpi_swap( &ctx->P, &ctx->Q ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) == 0 ) + continue; + + MPI_CHK( mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) ); + if( mpi_msb( &ctx->N ) != nbits ) + continue; + + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + } + while( mpi_cmp_int( &G, 1 ) != 0 ); + + /* + * D = E^-1 mod ((P-1)*(Q-1)) + * DP = D mod (P - 1) + * DQ = D mod (Q - 1) + * QP = Q^-1 mod P + */ + MPI_CHK( mpi_inv_mod( &ctx->D , &ctx->E, &H ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &ctx->QP, &ctx->Q, &ctx->P ) ); + + ctx->len = ( mpi_msb( &ctx->N ) + 7 ) >> 3; + +cleanup: + + mpi_free( &P1 ); mpi_free( &Q1 ); mpi_free( &H ); mpi_free( &G ); + + if( ret != 0 ) + { + rsa_free( ctx ); + return( POLARSSL_ERR_RSA_KEY_GEN_FAILED + ret ); + } + + return( 0 ); +} + +#endif + +/* + * Check a public RSA key + */ +int rsa_check_pubkey( const rsa_context *ctx ) +{ + if( !ctx->N.p || !ctx->E.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( ( ctx->N.p[0] & 1 ) == 0 || + ( ctx->E.p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->N ) < 128 || + mpi_msb( &ctx->N ) > POLARSSL_MPI_MAX_BITS ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->E ) < 2 || + mpi_msb( &ctx->E ) > 64 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + return( 0 ); +} + +/* + * Check a private RSA key + */ +int rsa_check_privkey( const rsa_context *ctx ) +{ + int ret; + mpi PQ, DE, P1, Q1, H, I, G, G2, L1, L2, DP, DQ, QP; + + if( ( ret = rsa_check_pubkey( ctx ) ) != 0 ) + return( ret ); + + if( !ctx->P.p || !ctx->Q.p || !ctx->D.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + mpi_init( &PQ ); mpi_init( &DE ); mpi_init( &P1 ); mpi_init( &Q1 ); + mpi_init( &H ); mpi_init( &I ); mpi_init( &G ); mpi_init( &G2 ); + mpi_init( &L1 ); mpi_init( &L2 ); mpi_init( &DP ); mpi_init( &DQ ); + mpi_init( &QP ); + + MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) ); + MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) ); + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + + MPI_CHK( mpi_gcd( &G2, &P1, &Q1 ) ); + MPI_CHK( mpi_div_mpi( &L1, &L2, &H, &G2 ) ); + MPI_CHK( mpi_mod_mpi( &I, &DE, &L1 ) ); + + MPI_CHK( mpi_mod_mpi( &DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &QP, &ctx->Q, &ctx->P ) ); + /* + * Check for a valid PKCS1v2 private key + */ + if( mpi_cmp_mpi( &PQ, &ctx->N ) != 0 || + mpi_cmp_mpi( &DP, &ctx->DP ) != 0 || + mpi_cmp_mpi( &DQ, &ctx->DQ ) != 0 || + mpi_cmp_mpi( &QP, &ctx->QP ) != 0 || + mpi_cmp_int( &L2, 0 ) != 0 || + mpi_cmp_int( &I, 1 ) != 0 || + mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_RSA_KEY_CHECK_FAILED; + } + +cleanup: + mpi_free( &PQ ); mpi_free( &DE ); mpi_free( &P1 ); mpi_free( &Q1 ); + mpi_free( &H ); mpi_free( &I ); mpi_free( &G ); mpi_free( &G2 ); + mpi_free( &L1 ); mpi_free( &L2 ); mpi_free( &DP ); mpi_free( &DQ ); + mpi_free( &QP ); + + if( ret == POLARSSL_ERR_RSA_KEY_CHECK_FAILED ) + return( ret ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED + ret ); + + return( 0 ); +} + +/* + * Do an RSA public key operation + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T; + + mpi_init( &T ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + olen = ctx->len; + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N, &ctx->RN ) ); + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PUBLIC_FAILED + ret ); + + return( 0 ); +} + +/* + * Do an RSA private key operation + */ +int rsa_private( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T, T1, T2, Vi, Vf; + + mpi_init( &T ); mpi_init( &T1 ); mpi_init( &T2 ); + mpi_init( &Vi ); mpi_init( &Vf ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + +#if 0 + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } +#endif + +#if defined(POLARSSL_RSA_NO_CRT) + ((void) f_rng); + ((void) p_rng); + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) ); +#else + if( f_rng != NULL ) + { + /* + * Blinding + * T = T * Vi mod N + */ + /* Unblinding value: Vf = random number */ + MPI_CHK( mpi_fill_random( &Vf, ctx->len - 1, f_rng, p_rng ) ); + + /* Mathematically speaking, the algorithm should check Vf + * against 0, P and Q (Vf should be relatively prime to N, and 0 < Vf < N), + * so that Vf^-1 exists. + */ + + /* Blinding value: Vi = Vf^(-e) mod N */ + MPI_CHK( mpi_inv_mod( &Vi, &Vf, &ctx->N ) ); + MPI_CHK( mpi_exp_mod( &Vi, &Vi, &ctx->E, &ctx->N, &ctx->RN ) ); + + MPI_CHK( mpi_mul_mpi( &T, &T, &Vi ) ); + MPI_CHK( mpi_mod_mpi( &T, &T, &ctx->N ) ); + } + + /* + * faster decryption using the CRT + * + * T1 = input ^ dP mod P + * T2 = input ^ dQ mod Q + */ + MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) ); + MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) ); + + /* + * T = (T1 - T2) * (Q^-1 mod P) mod P + */ + MPI_CHK( mpi_sub_mpi( &T, &T1, &T2 ) ); + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) ); + MPI_CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) ); + + /* + * output = T2 + T * Q + */ + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) ); + MPI_CHK( mpi_add_mpi( &T, &T2, &T1 ) ); + + if( f_rng != NULL ) + { + /* + * Unblind + * T = T * Vf mod N + */ + MPI_CHK( mpi_mul_mpi( &T, &T, &Vf ) ); + MPI_CHK( mpi_mod_mpi( &T, &T, &ctx->N ) ); + } +#endif + + olen = ctx->len; + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T ); mpi_free( &T1 ); mpi_free( &T2 ); + mpi_free( &Vi ); mpi_free( &Vf ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PRIVATE_FAILED + ret ); + + return( 0 ); +} + +#if defined(POLARSSL_PKCS1_V21) +/** + * Generate and apply the MGF1 operation (from PKCS#1 v2.1) to a buffer. + * + * \param dst buffer to mask + * \param dlen length of destination buffer + * \param src source of the mask generation + * \param slen length of the source buffer + * \param md_ctx message digest context to use + */ +static void mgf_mask( unsigned char *dst, size_t dlen, unsigned char *src, size_t slen, + md_context_t *md_ctx ) +{ + unsigned char mask[POLARSSL_MD_MAX_SIZE]; + unsigned char counter[4]; + unsigned char *p; + unsigned int hlen; + size_t i, use_len; + + memset( mask, 0, POLARSSL_MD_MAX_SIZE ); + memset( counter, 0, 4 ); + + hlen = md_ctx->md_info->size; + + // Generate and apply dbMask + // + p = dst; + + while( dlen > 0 ) + { + use_len = hlen; + if( dlen < hlen ) + use_len = dlen; + + md_starts( md_ctx ); + md_update( md_ctx, src, slen ); + md_update( md_ctx, counter, 4 ); + md_finish( md_ctx, mask ); + + for( i = 0; i < use_len; ++i ) + *p++ ^= mask[i]; + + counter[3]++; + + dlen -= use_len; + } +} +#endif + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-ENCRYPT function + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t olen; + int ret; + unsigned char *p = output; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_info = md_info_from_type( ctx->hash_id ); + + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + hlen = md_get_size( md_info ); + + if( olen < ilen + 2 * hlen + 2 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( output, 0, olen ); + + *p++ = 0; + + // Generate a random octet string seed + // + if( ( ret = f_rng( p_rng, p, hlen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + p += hlen; + + // Construct DB + // + md( md_info, label, label_len, p ); + p += hlen; + p += olen - 2 * hlen - 2 - ilen; + *p++ = 1; + memcpy( p, input, ilen ); + + md_init_ctx( &md_ctx, md_info ); + + // maskedDB: Apply dbMask to DB + // + mgf_mask( output + hlen + 1, olen - hlen - 1, output + 1, hlen, + &md_ctx ); + + // maskedSeed: Apply seedMask to seed + // + mgf_mask( output + 1, hlen, output + hlen + 1, olen - hlen - 1, + &md_ctx ); + + md_free_ctx( &md_ctx ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, f_rng, p_rng, output, output ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-ENCRYPT function + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t nb_pad, olen; + int ret; + unsigned char *p = output; + + if( ctx->padding != RSA_PKCS_V15 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + if( olen < ilen + 11 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + nb_pad = olen - 3 - ilen; + + *p++ = 0; + if( mode == RSA_PUBLIC ) + { + *p++ = RSA_CRYPT; + + while( nb_pad-- > 0 ) + { + int rng_dl = 100; + + do { + ret = f_rng( p_rng, p, 1 ); + } while( *p == 0 && --rng_dl && ret == 0 ); + + // Check if RNG failed to generate data + // + if( rng_dl == 0 || ret != 0) + return POLARSSL_ERR_RSA_RNG_FAILED + ret; + + p++; + } + } + else + { + *p++ = RSA_SIGN; + + while( nb_pad-- > 0 ) + *p++ = 0xFF; + } + + *p++ = 0; + memcpy( p, input, ilen ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, f_rng, p_rng, output, output ) ); +} + +/* + * Add the message padding, then do an RSA operation + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_encrypt( ctx, f_rng, p_rng, mode, ilen, + input, output ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_encrypt( ctx, f_rng, p_rng, mode, NULL, 0, + ilen, input, output ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-DECRYPT function + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ) +{ + int ret; + size_t ilen; + unsigned char *p; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char lhash[POLARSSL_MD_MAX_SIZE]; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16 || ilen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, f_rng, p_rng, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + + md_init_ctx( &md_ctx, md_info ); + + // Generate lHash + // + md( md_info, label, label_len, lhash ); + + // seed: Apply seedMask to maskedSeed + // + mgf_mask( buf + 1, hlen, buf + hlen + 1, ilen - hlen - 1, + &md_ctx ); + + // DB: Apply dbMask to maskedDB + // + mgf_mask( buf + hlen + 1, ilen - hlen - 1, buf + 1, hlen, + &md_ctx ); + + p += hlen; + md_free_ctx( &md_ctx ); + + // Check validity + // + if( memcmp( lhash, p, hlen ) != 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + p += hlen; + + while( *p == 0 && p < buf + ilen ) + p++; + + if( p == buf + ilen ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if( *p++ != 0x01 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if (ilen - (p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-DECRYPT function + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + int ret, correct = 1; + size_t ilen, pad_count = 0; + unsigned char *p, *q; + unsigned char bt; + unsigned char buf[ctx->len]; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, f_rng, p_rng, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 ) + correct = 0; + + bt = *p++; + if( ( bt != RSA_CRYPT && mode == RSA_PRIVATE ) || + ( bt != RSA_SIGN && mode == RSA_PUBLIC ) ) + { + correct = 0; + } + + if( bt == RSA_CRYPT ) + { + while( *p != 0 && p < buf + ilen - 1 ) + pad_count += ( *p++ != 0 ); + + correct &= ( *p == 0 && p < buf + ilen - 1 ); + + q = p; + + // Also pass over all other bytes to reduce timing differences + // + while ( q < buf + ilen - 1 ) + pad_count += ( *q++ != 0 ); + + // Prevent compiler optimization of pad_count + // + correct |= pad_count & 0x100000; /* Always 0 unless 1M bit keys */ + p++; + } + else + { + while( *p == 0xFF && p < buf + ilen - 1 ) + pad_count += ( *p++ == 0xFF ); + + correct &= ( *p == 0 && p < buf + ilen - 1 ); + + q = p; + + // Also pass over all other bytes to reduce timing differences + // + while ( q < buf + ilen - 1 ) + pad_count += ( *q++ != 0 ); + + // Prevent compiler optimization of pad_count + // + correct |= pad_count & 0x100000; /* Always 0 unless 1M bit keys */ + p++; + } + + if( correct == 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if (ilen - (p - buf) > output_max_len) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} + +/* + * Do an RSA operation, then remove the message padding + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_decrypt( ctx, f_rng, p_rng, mode, olen, + input, output, output_max_len ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_decrypt( ctx, f_rng, p_rng, mode, NULL, 0, + olen, input, output, output_max_len ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-SIGN function + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t olen; + unsigned char *p = sig; + unsigned char salt[POLARSSL_MD_MAX_SIZE]; + unsigned int slen, hlen, offset = 0; + int ret; + size_t msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 || f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + switch( hash_id ) + { + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + hashlen = 16; + break; + + case SIG_RSA_SHA1: + hashlen = 20; + break; + + case SIG_RSA_SHA224: + hashlen = 28; + break; + + case SIG_RSA_SHA256: + hashlen = 32; + break; + + case SIG_RSA_SHA384: + hashlen = 48; + break; + + case SIG_RSA_SHA512: + hashlen = 64; + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = hlen; + + if( olen < hlen + slen + 2 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( sig, 0, olen ); + + msb = mpi_msb( &ctx->N ) - 1; + + // Generate salt of length slen + // + if( ( ret = f_rng( p_rng, salt, slen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + // Note: EMSA-PSS encoding is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + p += olen - hlen * 2 - 2; + *p++ = 0x01; + memcpy( p, salt, slen ); + p += slen; + + md_init_ctx( &md_ctx, md_info ); + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, p, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, salt, slen ); + md_finish( &md_ctx, p ); + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + offset = 1; + + // maskedDB: Apply dbMask to DB + // + mgf_mask( sig + offset, olen - hlen - 1 - offset, p, hlen, &md_ctx ); + + md_free_ctx( &md_ctx ); + + msb = mpi_msb( &ctx->N ) - 1; + sig[0] &= 0xFF >> ( olen * 8 - msb ); + + p += hlen; + *p++ = 0xBC; + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, f_rng, p_rng, sig, sig ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-V1_5-SIGN function + */ +/* + * Do an RSA operation to sign the message digest + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t nb_pad, olen; + unsigned char *p = sig; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + switch( hash_id ) + { + case SIG_RSA_RAW: + nb_pad = olen - 3 - hashlen; + break; + + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + nb_pad = olen - 3 - 34; + break; + + case SIG_RSA_SHA1: + nb_pad = olen - 3 - 35; + break; + + case SIG_RSA_SHA224: + nb_pad = olen - 3 - 47; + break; + + case SIG_RSA_SHA256: + nb_pad = olen - 3 - 51; + break; + + case SIG_RSA_SHA384: + nb_pad = olen - 3 - 67; + break; + + case SIG_RSA_SHA512: + nb_pad = olen - 3 - 83; + break; + + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + if( ( nb_pad < 8 ) || ( nb_pad > olen ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + *p++ = 0; + *p++ = RSA_SIGN; + memset( p, 0xFF, nb_pad ); + p += nb_pad; + *p++ = 0; + + switch( hash_id ) + { + case SIG_RSA_RAW: + memcpy( p, hash, hashlen ); + break; + + case SIG_RSA_MD2: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 2; break; + + case SIG_RSA_MD4: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 4; break; + + case SIG_RSA_MD5: + memcpy( p, ASN1_HASH_MDX, 18 ); + memcpy( p + 18, hash, 16 ); + p[13] = 5; break; + + case SIG_RSA_SHA1: + memcpy( p, ASN1_HASH_SHA1, 15 ); + memcpy( p + 15, hash, 20 ); + break; + + case SIG_RSA_SHA224: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 28 ); + p[1] += 28; p[14] = 4; p[18] += 28; break; + + case SIG_RSA_SHA256: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 32 ); + p[1] += 32; p[14] = 1; p[18] += 32; break; + + case SIG_RSA_SHA384: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 48 ); + p[1] += 48; p[14] = 2; p[18] += 48; break; + + case SIG_RSA_SHA512: + memcpy( p, ASN1_HASH_SHA2X, 19 ); + memcpy( p + 19, hash, 64 ); + p[1] += 64; p[14] = 3; p[18] += 64; break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, f_rng, p_rng, sig, sig ) ); +} + +/* + * Do an RSA operation to sign the message digest + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + (void)f_rng; + (void)p_rng; + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_sign( ctx, f_rng, p_rng, mode, hash_id, + hashlen, hash, sig ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_sign( ctx, f_rng, p_rng, mode, hash_id, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-VERIFY function + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + int ret; + size_t siglen; + unsigned char *p; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char result[POLARSSL_MD_MAX_SIZE]; + unsigned char zeros[8]; + unsigned int hlen; + size_t slen, msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, f_rng, p_rng, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( buf[siglen - 1] != 0xBC ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + switch( hash_id ) + { + case SIG_RSA_MD2: + case SIG_RSA_MD4: + case SIG_RSA_MD5: + hashlen = 16; + break; + + case SIG_RSA_SHA1: + hashlen = 20; + break; + + case SIG_RSA_SHA224: + hashlen = 28; + break; + + case SIG_RSA_SHA256: + hashlen = 32; + break; + + case SIG_RSA_SHA384: + hashlen = 48; + break; + + case SIG_RSA_SHA512: + hashlen = 64; + break; + + default: + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = siglen - hlen - 1; + + memset( zeros, 0, 8 ); + + // Note: EMSA-PSS verification is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + { + p++; + siglen -= 1; + } + if( buf[0] >> ( 8 - siglen * 8 + msb ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_init_ctx( &md_ctx, md_info ); + + mgf_mask( p, siglen - hlen - 1, p + siglen - hlen - 1, hlen, &md_ctx ); + + buf[0] &= 0xFF >> ( siglen * 8 - msb ); + + while( *p == 0 && p < buf + siglen ) + p++; + + if( p == buf + siglen || + *p++ != 0x01 ) + { + md_free_ctx( &md_ctx ); + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + slen -= p - buf; + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, zeros, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, p, slen ); + md_finish( &md_ctx, result ); + + md_free_ctx( &md_ctx ); + + if( memcmp( p + slen, result, hlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-v1_5-VERIFY function + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ) +{ + int ret; + size_t len, siglen; + unsigned char *p, c; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + + if( ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, f_rng, p_rng, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 || *p++ != RSA_SIGN ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + while( *p != 0 ) + { + if( p >= buf + siglen - 1 || *p != 0xFF ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + p++; + } + p++; + + len = siglen - ( p - buf ); + + if( len == 33 && hash_id == SIG_RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1_ALT, 13 ) == 0 && + memcmp( p + 13, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + if( len == 34 ) + { + c = p[13]; + p[13] = 0; + + if( memcmp( p, ASN1_HASH_MDX, 18 ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( c == 2 && hash_id == SIG_RSA_MD2 ) || + ( c == 4 && hash_id == SIG_RSA_MD4 ) || + ( c == 5 && hash_id == SIG_RSA_MD5 ) ) + { + if( memcmp( p + 18, hash, 16 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + } + + if( len == 35 && hash_id == SIG_RSA_SHA1 ) + { + if( memcmp( p, ASN1_HASH_SHA1, 15 ) == 0 && + memcmp( p + 15, hash, 20 ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + if( ( len == 19 + 28 && p[14] == 4 && hash_id == SIG_RSA_SHA224 ) || + ( len == 19 + 32 && p[14] == 1 && hash_id == SIG_RSA_SHA256 ) || + ( len == 19 + 48 && p[14] == 2 && hash_id == SIG_RSA_SHA384 ) || + ( len == 19 + 64 && p[14] == 3 && hash_id == SIG_RSA_SHA512 ) ) + { + c = p[1] - 17; + p[1] = 17; + p[14] = 0; + + if( p[18] == c && + memcmp( p, ASN1_HASH_SHA2X, 18 ) == 0 && + memcmp( p + 19, hash, c ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + if( len == hashlen && hash_id == SIG_RSA_RAW ) + { + if( memcmp( p, hash, hashlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + return( POLARSSL_ERR_RSA_INVALID_PADDING ); +} + +/* + * Do an RSA operation and check the message digest + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + int hash_id, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ) +{ + switch( ctx->padding ) + { + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_verify( ctx, f_rng, p_rng, mode, + hash_id, hashlen, hash, sig ); + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_verify( ctx, f_rng, p_rng, mode, hash_id, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +/* + * Free the components of an RSA key + */ +void rsa_free( rsa_context *ctx ) +{ + mpi_free( &ctx->RQ ); mpi_free( &ctx->RP ); mpi_free( &ctx->RN ); + mpi_free( &ctx->QP ); mpi_free( &ctx->DQ ); mpi_free( &ctx->DP ); + mpi_free( &ctx->Q ); mpi_free( &ctx->P ); mpi_free( &ctx->D ); + mpi_free( &ctx->E ); mpi_free( &ctx->N ); +} + +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/sha1.h" + +/* + * Example RSA-1024 keypair, for test purposes + */ +#define KEY_LEN 128 + +#define RSA_N "9292758453063D803DD603D5E777D788" \ + "8ED1D5BF35786190FA2F23EBC0848AEA" \ + "DDA92CA6C3D80B32C4D109BE0F36D6AE" \ + "7130B9CED7ACDF54CFC7555AC14EEBAB" \ + "93A89813FBF3C4F8066D2D800F7C38A8" \ + "1AE31942917403FF4946B0A83D3D3E05" \ + "EE57C6F5F5606FB5D4BC6CD34EE0801A" \ + "5E94BB77B07507233A0BC7BAC8F90F79" + +#define RSA_E "10001" + +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ + "3C94D22288ACD763FD8E5600ED4A702D" \ + "F84198A5F06C2E72236AE490C93F07F8" \ + "3CC559CD27BC2D1CA488811730BB5725" + +#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ + "D8AAEA56749EA28623272E4F7D0592AF" \ + "7C1F1313CAC9471B5C523BFE592F517B" \ + "407A1BD76C164B93DA2D32A383E58357" + +#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ + "F38D18D2B2F0E2DD275AA977E2BF4411" \ + "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ + "A74206CEC169D74BF5A8C50D6F48EA08" + +#define PT_LEN 24 +#define RSA_PT "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \ + "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD" + +static int myrand( void *rng_state, unsigned char *output, size_t len ) +{ + size_t i; + + if( rng_state != NULL ) + rng_state = NULL; + + for( i = 0; i < len; ++i ) + output[i] = rand(); + + return( 0 ); +} + +/* + * Checkup routine + */ +int rsa_self_test( int verbose ) +{ + size_t len; + rsa_context rsa; + unsigned char rsa_plaintext[PT_LEN]; + unsigned char rsa_decrypted[PT_LEN]; + unsigned char rsa_ciphertext[KEY_LEN]; +#if defined(POLARSSL_SHA1_C) + unsigned char sha1sum[20]; +#endif + + rsa_init( &rsa, RSA_PKCS_V15, 0 ); + + rsa.len = KEY_LEN; + mpi_read_string( &rsa.N , 16, RSA_N ); + mpi_read_string( &rsa.E , 16, RSA_E ); + mpi_read_string( &rsa.D , 16, RSA_D ); + mpi_read_string( &rsa.P , 16, RSA_P ); + mpi_read_string( &rsa.Q , 16, RSA_Q ); + mpi_read_string( &rsa.DP, 16, RSA_DP ); + mpi_read_string( &rsa.DQ, 16, RSA_DQ ); + mpi_read_string( &rsa.QP, 16, RSA_QP ); + + if( verbose != 0 ) + printf( " RSA key validation: " ); + + if( rsa_check_pubkey( &rsa ) != 0 || + rsa_check_privkey( &rsa ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 encryption : " ); + + memcpy( rsa_plaintext, RSA_PT, PT_LEN ); + + if( rsa_pkcs1_encrypt( &rsa, &myrand, NULL, RSA_PUBLIC, PT_LEN, + rsa_plaintext, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 decryption : " ); + + if( rsa_pkcs1_decrypt( &rsa, &myrand, NULL, RSA_PRIVATE, &len, + rsa_ciphertext, rsa_decrypted, + sizeof(rsa_decrypted) ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + +#if defined(POLARSSL_SHA1_C) + if( verbose != 0 ) + printf( "passed\n PKCS#1 data sign : " ); + + sha1( rsa_plaintext, PT_LEN, sha1sum ); + + if( rsa_pkcs1_sign( &rsa, &myrand, NULL, RSA_PRIVATE, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n PKCS#1 sig. verify: " ); + + if( rsa_pkcs1_verify( &rsa, &myrand, NULL, RSA_PUBLIC, SIG_RSA_SHA1, 20, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + printf( "passed\n\n" ); +#endif /* POLARSSL_SHA1_C */ + + rsa_free( &rsa ); + + return( 0 ); +} + +#endif + +#endif diff --git a/sha256.c b/sha256.c new file mode 100644 index 0000000..52e13a9 --- /dev/null +++ b/sha256.c @@ -0,0 +1,225 @@ +/* + * sha256.c -- Compute SHA-256 hash + * + * Just for little endian architecture. + * + * Code taken from: + * http://gladman.plushost.co.uk/oldsite/cryptography_technology/sha/index.php + * + * File names are sha2.c, sha2.h, brg_types.h, brg_endian.h + * in the archive sha2-07-01-07.zip. + * + * Code is modified in the style of PolarSSL API. + * + * See original copyright notice below. + */ +/* + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 01/08/2005 +*/ + +#include +#include +#include "sha256.h" + +#define SHA256_MASK (SHA256_BLOCK_SIZE - 1) + +static void memcpy_output_bswap32 (unsigned char *dst, const uint32_t *p) +{ + int i; + uint32_t q = 0; + + for (i = 0; i < 32; i++) + { + if ((i & 3) == 0) + q = __builtin_bswap32 (p[i >> 2]); /* bswap32 is GCC extention */ + dst[i] = q >> ((i & 3) * 8); + } +} + +#define rotr32(x,n) (((x) >> n) | ((x) << (32 - n))) + +#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) ^ (y)))) + +/* round transforms for SHA256 compression functions */ +#define vf(n,i) v[(n - i) & 7] + +#define hf(i) (p[i & 15] += \ + g_1(p[(i + 14) & 15]) + p[(i + 9) & 15] + g_0(p[(i + 1) & 15])) + +#define v_cycle0(i) \ + p[i] = __builtin_bswap32 (p[i]); \ + vf(7,i) += p[i] + k_0[i] \ + + s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \ + vf(3,i) += vf(7,i); \ + vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i)) + +#define v_cycle(i, j) \ + vf(7,i) += hf(i) + k_0[i+j] \ + + s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \ + vf(3,i) += vf(7,i); \ + vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i)) + +#define s_0(x) (rotr32((x), 2) ^ rotr32((x), 13) ^ rotr32((x), 22)) +#define s_1(x) (rotr32((x), 6) ^ rotr32((x), 11) ^ rotr32((x), 25)) +#define g_0(x) (rotr32((x), 7) ^ rotr32((x), 18) ^ ((x) >> 3)) +#define g_1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10)) +#define k_0 k256 + +static const uint32_t k256[64] = { + 0X428A2F98, 0X71374491, 0XB5C0FBCF, 0XE9B5DBA5, + 0X3956C25B, 0X59F111F1, 0X923F82A4, 0XAB1C5ED5, + 0XD807AA98, 0X12835B01, 0X243185BE, 0X550C7DC3, + 0X72BE5D74, 0X80DEB1FE, 0X9BDC06A7, 0XC19BF174, + 0XE49B69C1, 0XEFBE4786, 0X0FC19DC6, 0X240CA1CC, + 0X2DE92C6F, 0X4A7484AA, 0X5CB0A9DC, 0X76F988DA, + 0X983E5152, 0XA831C66D, 0XB00327C8, 0XBF597FC7, + 0XC6E00BF3, 0XD5A79147, 0X06CA6351, 0X14292967, + 0X27B70A85, 0X2E1B2138, 0X4D2C6DFC, 0X53380D13, + 0X650A7354, 0X766A0ABB, 0X81C2C92E, 0X92722C85, + 0XA2BFE8A1, 0XA81A664B, 0XC24B8B70, 0XC76C51A3, + 0XD192E819, 0XD6990624, 0XF40E3585, 0X106AA070, + 0X19A4C116, 0X1E376C08, 0X2748774C, 0X34B0BCB5, + 0X391C0CB3, 0X4ED8AA4A, 0X5B9CCA4F, 0X682E6FF3, + 0X748F82EE, 0X78A5636F, 0X84C87814, 0X8CC70208, + 0X90BEFFFA, 0XA4506CEB, 0XBEF9A3F7, 0XC67178F2, +}; + +void +sha256_process (sha256_context *ctx) +{ + uint32_t i; + uint32_t *p = ctx->wbuf; + uint32_t v[8]; + + memcpy (v, ctx->state, 8 * sizeof (uint32_t)); + + v_cycle0 ( 0); v_cycle0 ( 1); v_cycle0 ( 2); v_cycle0 ( 3); + v_cycle0 ( 4); v_cycle0 ( 5); v_cycle0 ( 6); v_cycle0 ( 7); + v_cycle0 ( 8); v_cycle0 ( 9); v_cycle0 (10); v_cycle0 (11); + v_cycle0 (12); v_cycle0 (13); v_cycle0 (14); v_cycle0 (15); + + for (i = 16; i < 64; i += 16) + { + v_cycle ( 0, i); v_cycle ( 1, i); v_cycle ( 2, i); v_cycle ( 3, i); + v_cycle ( 4, i); v_cycle ( 5, i); v_cycle ( 6, i); v_cycle ( 7, i); + v_cycle ( 8, i); v_cycle ( 9, i); v_cycle (10, i); v_cycle (11, i); + v_cycle (12, i); v_cycle (13, i); v_cycle (14, i); v_cycle (15, i); + } + + ctx->state[0] += v[0]; + ctx->state[1] += v[1]; + ctx->state[2] += v[2]; + ctx->state[3] += v[3]; + ctx->state[4] += v[4]; + ctx->state[5] += v[5]; + ctx->state[6] += v[6]; + ctx->state[7] += v[7]; +} + +void +sha256_update (sha256_context *ctx, const unsigned char *input, + unsigned int ilen) +{ + uint32_t left = (ctx->total[0] & SHA256_MASK); + uint32_t fill = SHA256_BLOCK_SIZE - left; + + ctx->total[0] += ilen; + if (ctx->total[0] < ilen) + ctx->total[1]++; + + while (ilen >= fill) + { + memcpy (((unsigned char*)ctx->wbuf) + left, input, fill); + sha256_process (ctx); + input += fill; + ilen -= fill; + left = 0; + fill = SHA256_BLOCK_SIZE; + } + + memcpy (((unsigned char*)ctx->wbuf) + left, input, ilen); +} + +void +sha256_finish (sha256_context *ctx, unsigned char output[32]) +{ + uint32_t last = (ctx->total[0] & SHA256_MASK); + + ctx->wbuf[last >> 2] = __builtin_bswap32 (ctx->wbuf[last >> 2]); + ctx->wbuf[last >> 2] &= 0xffffff80 << (8 * (~last & 3)); + ctx->wbuf[last >> 2] |= 0x00000080 << (8 * (~last & 3)); + ctx->wbuf[last >> 2] = __builtin_bswap32 (ctx->wbuf[last >> 2]); + + if (last > SHA256_BLOCK_SIZE - 9) + { + if (last < 60) + ctx->wbuf[15] = 0; + sha256_process (ctx); + last = 0; + } + else + last = (last >> 2) + 1; + + while (last < 14) + ctx->wbuf[last++] = 0; + + ctx->wbuf[14] = __builtin_bswap32 ((ctx->total[0] >> 29) | (ctx->total[1] << 3)); + ctx->wbuf[15] = __builtin_bswap32 (ctx->total[0] << 3); + sha256_process (ctx); + + memcpy_output_bswap32 (output, ctx->state); + memset (ctx, 0, sizeof (sha256_context)); +} + +static const uint32_t initial_state[8] = +{ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +void +sha256_start (sha256_context *ctx) +{ + ctx->total[0] = ctx->total[1] = 0; + memcpy (ctx->state, initial_state, 8 * sizeof(uint32_t)); +} + +void +sha256 (const unsigned char *input, unsigned int ilen, + unsigned char output[32]) +{ + sha256_context ctx; + + sha256_start (&ctx); + sha256_update (&ctx, input, ilen); + sha256_finish (&ctx, output); +} diff --git a/sha256.h b/sha256.h new file mode 100644 index 0000000..1454c3f --- /dev/null +++ b/sha256.h @@ -0,0 +1,17 @@ +#define SHA256_DIGEST_SIZE 32 +#define SHA256_BLOCK_SIZE 64 + +typedef struct +{ + uint32_t total[2]; + uint32_t state[8]; + uint32_t wbuf[16]; +} sha256_context; + +void sha256 (const unsigned char *input, unsigned int ilen, + unsigned char output[32]); +void sha256_start (sha256_context *ctx); +void sha256_finish (sha256_context *ctx, unsigned char output[32]); +void sha256_update (sha256_context *ctx, const unsigned char *input, + unsigned int ilen); +void sha256_process (sha256_context *ctx); diff --git a/sha512.c b/sha512.c new file mode 100644 index 0000000..4931a31 --- /dev/null +++ b/sha512.c @@ -0,0 +1,215 @@ +/* + * sha512.c -- Compute SHA-512 hash (for little endian architecture). + * + * This module is written by gniibe, following the API of sha256.c. + * + * Copyright (C) 2014 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 . + * + */ + +/* + * Reference: + * + * [1] FIPS PUB 180-4: Secure hash Standard (SHS), March, 2012. + * + */ + +#include +#include +#include "sha512.h" + +#define SHA512_MASK (SHA512_BLOCK_SIZE - 1) + +static void memcpy_output_bswap64 (unsigned char dst[64], const uint64_t *p) +{ + int i; + uint64_t q = 0; + + for (i = 0; i < 64; i++) + { + if ((i & 7) == 0) + q = __builtin_bswap64 (p[i >> 3]); /* bswap64 is GCC extention */ + dst[i] = q >> ((i & 7) * 8); + } +} + +#define rotr64(x,n) (((x) >> n) | ((x) << (64 - n))) + +#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) ^ (y)))) + +/* round transforms for SHA512 compression functions */ +#define vf(n,i) v[(n - i) & 7] + +#define hf(i) (p[i & 15] += \ + g_1(p[(i + 14) & 15]) + p[(i + 9) & 15] + g_0(p[(i + 1) & 15])) + +#define v_cycle0(i) \ + p[i] = __builtin_bswap64 (p[i]); \ + vf(7,i) += p[i] + k_0[i] \ + + s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \ + vf(3,i) += vf(7,i); \ + vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i)) + +#define v_cycle(i, j) \ + vf(7,i) += hf(i) + k_0[i+j] \ + + s_1(vf(4,i)) + ch(vf(4,i),vf(5,i),vf(6,i)); \ + vf(3,i) += vf(7,i); \ + vf(7,i) += s_0(vf(0,i))+ maj(vf(0,i),vf(1,i),vf(2,i)) + +#define s_0(x) (rotr64((x), 28) ^ rotr64((x), 34) ^ rotr64((x), 39)) +#define s_1(x) (rotr64((x), 14) ^ rotr64((x), 18) ^ rotr64((x), 41)) +#define g_0(x) (rotr64((x), 1) ^ rotr64((x), 8) ^ ((x) >> 7)) +#define g_1(x) (rotr64((x), 19) ^ rotr64((x), 61) ^ ((x) >> 6)) +#define k_0 k512 + +/* Taken from section 4.2.3 of [1]. */ +static const uint64_t k512[80] = { +0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, +0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, +0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, +0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, +0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, +0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, +0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, +0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, +0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, +0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, +0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, +0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, +0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, +0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, +0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, +0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, +0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, +0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, +0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, +0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817 +}; + +void +sha512_process (sha512_context *ctx) +{ + uint32_t i; + uint64_t *p = ctx->wbuf; + uint64_t v[8]; + + memcpy (v, ctx->state, 8 * sizeof (uint64_t)); + + v_cycle0 ( 0); v_cycle0 ( 1); v_cycle0 ( 2); v_cycle0 ( 3); + v_cycle0 ( 4); v_cycle0 ( 5); v_cycle0 ( 6); v_cycle0 ( 7); + v_cycle0 ( 8); v_cycle0 ( 9); v_cycle0 (10); v_cycle0 (11); + v_cycle0 (12); v_cycle0 (13); v_cycle0 (14); v_cycle0 (15); + + for (i = 16; i < 80; i += 16) + { + v_cycle ( 0, i); v_cycle ( 1, i); v_cycle ( 2, i); v_cycle ( 3, i); + v_cycle ( 4, i); v_cycle ( 5, i); v_cycle ( 6, i); v_cycle ( 7, i); + v_cycle ( 8, i); v_cycle ( 9, i); v_cycle (10, i); v_cycle (11, i); + v_cycle (12, i); v_cycle (13, i); v_cycle (14, i); v_cycle (15, i); + } + + ctx->state[0] += v[0]; + ctx->state[1] += v[1]; + ctx->state[2] += v[2]; + ctx->state[3] += v[3]; + ctx->state[4] += v[4]; + ctx->state[5] += v[5]; + ctx->state[6] += v[6]; + ctx->state[7] += v[7]; +} + +void +sha512_update (sha512_context *ctx, const unsigned char *input, + unsigned int ilen) +{ + uint32_t left = (ctx->total[0] & SHA512_MASK); + uint32_t fill = SHA512_BLOCK_SIZE - left; + + ctx->total[0] += ilen; + if (ctx->total[0] < ilen) + ctx->total[1]++; + + while (ilen >= fill) + { + memcpy (((unsigned char*)ctx->wbuf) + left, input, fill); + sha512_process (ctx); + input += fill; + ilen -= fill; + left = 0; + fill = SHA512_BLOCK_SIZE; + } + + memcpy (((unsigned char*)ctx->wbuf) + left, input, ilen); +} + +void +sha512_finish (sha512_context *ctx, unsigned char output[64]) +{ + uint32_t last = (ctx->total[0] & SHA512_MASK); + + ctx->wbuf[last >> 3] = __builtin_bswap64 (ctx->wbuf[last >> 3]); + ctx->wbuf[last >> 3] &= 0xffffffffffffff80LL << (8 * (~last & 7)); + ctx->wbuf[last >> 3] |= 0x0000000000000080LL << (8 * (~last & 7)); + ctx->wbuf[last >> 3] = __builtin_bswap64 (ctx->wbuf[last >> 3]); + + if (last > SHA512_BLOCK_SIZE - 17) + { + if (last < 120) + ctx->wbuf[15] = 0; + sha512_process (ctx); + last = 0; + } + else + last = (last >> 3) + 1; + + while (last < 14) + ctx->wbuf[last++] = 0; + + ctx->wbuf[14] = __builtin_bswap64 ((ctx->total[0] >> 61) | (ctx->total[1] << 3)); + ctx->wbuf[15] = __builtin_bswap64 (ctx->total[0] << 3); + sha512_process (ctx); + + memcpy_output_bswap64 (output, ctx->state); + memset (ctx, 0, sizeof (sha512_context)); +} + +/* Taken from section 5.3.5 of [1]. */ +static const uint64_t initial_state[8] = { +0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, +0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 +}; + +void +sha512_start (sha512_context *ctx) +{ + ctx->total[0] = ctx->total[1] = 0; + memcpy (ctx->state, initial_state, 8 * sizeof(uint64_t)); +} + +void +sha512 (const unsigned char *input, unsigned int ilen, + unsigned char output[64]) +{ + sha512_context ctx; + + sha512_start (&ctx); + sha512_update (&ctx, input, ilen); + sha512_finish (&ctx, output); +} diff --git a/sha512.h b/sha512.h new file mode 100644 index 0000000..221f81e --- /dev/null +++ b/sha512.h @@ -0,0 +1,17 @@ +#define SHA512_DIGEST_SIZE 64 +#define SHA512_BLOCK_SIZE 128 + +typedef struct +{ + uint64_t total[2]; + uint64_t state[8]; + uint64_t wbuf[16]; +} sha512_context; + +void sha512 (const unsigned char *input, unsigned int ilen, + unsigned char output[64]); +void sha512_start (sha512_context *ctx); +void sha512_finish (sha512_context *ctx, unsigned char output[64]); +void sha512_update (sha512_context *ctx, const unsigned char *input, + unsigned int ilen); +void sha512_process (sha512_context *ctx); diff --git a/shake256.c b/shake256.c new file mode 100644 index 0000000..c1cfa60 --- /dev/null +++ b/shake256.c @@ -0,0 +1,202 @@ +/* + * shake256.c -- Compute SHAKE hash. + * + * Copyright (C) 2021 Free Software Initiative of Japan + * Author: NIIBE Yutaka + * + * This file is a part of Gnuk, a GnuPG USB Token implementation. + * + * Gnuk 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. + * + * Gnuk 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 . + * + */ + +/* + * Reference: + * + * [1] FIPS PUB 202: SHA-3 Standard: + * Permutation-Based Hash and Extendable-Output Functions, + * August 2015. + */ + +#define SHAKE_BITS 256 +#define SHAKE_INDEX_MAX (200 - (SHAKE_BITS >> 2)) + +/* + * b=1600 + * nr = 24 iterations + * l = 6 + * + * state: 25x64-bit == 5 x 5 x 64 + * row column bit + */ + +#include +#include +#include "shake256.h" + +/* Round constants in iota step. */ +static const uint64_t rc[24] = { + UINT64_C (0x0000000000000001), UINT64_C (0x0000000000008082), + UINT64_C (0x800000000000808a), UINT64_C (0x8000000080008000), + UINT64_C (0x000000000000808b), UINT64_C (0x0000000080000001), + UINT64_C (0x8000000080008081), UINT64_C (0x8000000000008009), + UINT64_C (0x000000000000008a), UINT64_C (0x0000000000000088), + UINT64_C (0x0000000080008009), UINT64_C (0x000000008000000a), + UINT64_C (0x000000008000808b), UINT64_C (0x800000000000008b), + UINT64_C (0x8000000000008089), UINT64_C (0x8000000000008003), + UINT64_C (0x8000000000008002), UINT64_C (0x8000000000000080), + UINT64_C (0x000000000000800a), UINT64_C (0x800000008000000a), + UINT64_C (0x8000000080008081), UINT64_C (0x8000000000008080), + UINT64_C (0x0000000080000001), UINT64_C (0x8000000080008008), +}; + +static const uint8_t rho[25-1] = { + 1, 62, 28, 27, + 36, 44, 6, 55, 20, + 3, 10, 43, 25, 39, + 41, 45, 15, 21, 8, + 18, 2, 61, 56, 14 +}; + +static const uint8_t pi[24] = { + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, +}; + +static uint64_t +rotl64 (uint64_t x, uint64_t y) +{ + return (x << y) | (x >> (64U - y)); +} + +static void +absorb (uint64_t *dst, uint8_t index, uint8_t v) +{ + dst[index >> 3] ^= ((uint64_t)v) << ((index & 7) << 3); +} + +static uint8_t +squeeze (const uint64_t *src, uint8_t index) +{ + return src[index >> 3] >> ((index & 7) << 3); +} + +/* The permutation function. */ +static void +keccak_f1600 (uint64_t s[25]) +{ + uint64_t lane[5]; + int i, j, round; + + for (round = 0; round < 24; round++) + { + uint64_t t; + + /* STEP: theta */ + for (i = 0; i < 5; i++) + lane[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]; + + for (i = 0; i < 5; i++) + { + t = lane[(i + 4) % 5] ^ rotl64 (lane[(i + 1) % 5], 1); + for (j = 0; j < 25; j += 5) + s[j + i] ^= t; + } + + /* STEP: rho */ + for (i = 1; i < 25; i++) + s[i] = rotl64(s[i], rho[i-1]); + + /* STEP: pi */ + t = s[1]; + for (i = 0; i < 25-1; i++) + { + uint64_t tmp; + + j = pi[i]; + tmp = s[j]; + s[j] = t; + t = tmp; + } + + /* STEP: chi */ + for (i = 0; i < 25; i += 5) + { + for (j = 0; j < 5; j++) + lane[j] = s[i + j]; + for (j = 0; j < 5; j++) + s[i + j] ^= (~lane[(j + 1) % 5]) & lane[(j + 2) % 5]; + } + + /* STEP: iota */ + s[0] ^= rc[round]; + } +} + +void +shake256_start (struct shake_context *shake) +{ + memset (shake, 0, sizeof (shake_context)); +} + +void +shake256_update (struct shake_context *shake, + const unsigned char *src, unsigned int size) +{ + if (size == 0) + return; + + while (1) + { + absorb (shake->state, shake->index, *src++); + if (++shake->index == SHAKE_INDEX_MAX) + { + keccak_f1600 (shake->state); + shake->index = 0; + } + if (--size == 0) + break; + } +} + +void +shake256_finish (struct shake_context *shake, + unsigned char *dst, unsigned int size) +{ + if (size == 0) + return; + + /* + * SHAKE is defined appending 11 at the end to RawSHAKE, + * RawSHAKE is defined adding 11 at the end to KECCAK, + * and KECCACK uses pad10*1 at the end. + * This means adding 111110*1 at the end. + */ + absorb (shake->state, shake->index, 0x1F); + absorb (shake->state, SHAKE_INDEX_MAX - 1, 0x80); + keccak_f1600 (shake->state); + shake->index = 0; + + while (1) + { + *dst++ = squeeze (shake->state, shake->index); + if (--size == 0) + break; + if (++shake->index == SHAKE_INDEX_MAX) + { + keccak_f1600 (shake->state); + shake->index = 0; + } + } +} diff --git a/shake256.h b/shake256.h new file mode 100644 index 0000000..4a4a210 --- /dev/null +++ b/shake256.h @@ -0,0 +1,13 @@ +#include + +struct shake_context { + uint64_t state[25]; + uint32_t index; +}; +typedef struct shake_context shake_context; + +void shake256_start (struct shake_context *shake); +void shake256_update (struct shake_context *shake, + const unsigned char *src, unsigned int size); +void shake256_finish (struct shake_context *shake, + unsigned char *dst, unsigned int size); diff --git a/status-code.h b/status-code.h new file mode 100644 index 0000000..537d56c --- /dev/null +++ b/status-code.h @@ -0,0 +1,14 @@ +#define GPG_APPLICATION_TERMINATED() set_res_sw (0x62, 0x85) +#define GPG_MEMORY_FAILURE() set_res_sw (0x65, 0x81) +#define GPG_WRONG_LENGTH() set_res_sw (0x67, 0x00) +#define GPG_SECURITY_FAILURE() set_res_sw (0x69, 0x82) +#define GPG_SECURITY_AUTH_BLOCKED() set_res_sw (0x69, 0x83) +#define GPG_CONDITION_NOT_SATISFIED() set_res_sw (0x69, 0x85) +#define GPG_COMMAND_NOT_ALLOWED() set_res_sw (0x69, 0x86) +#define GPG_FUNCTION_NOT_SUPPORTED() set_res_sw (0x6a, 0x81) +#define GPG_NO_FILE() set_res_sw (0x6a, 0x82) +#define GPG_NO_RECORD() set_res_sw (0x6a, 0x88) +#define GPG_BAD_P1_P2() set_res_sw (0x6b, 0x00) +#define GPG_NO_INS() set_res_sw (0x6d, 0x00) +#define GPG_ERROR() set_res_sw (0x6f, 0x00) +#define GPG_SUCCESS() set_res_sw (0x90, 0x00) diff --git a/sys.h b/sys.h new file mode 100644 index 0000000..e69de29