From 0af5685495731ce04c5ea8a6c8ae52936aa217d5 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Mon, 3 Jan 2022 02:02:39 +0100 Subject: [PATCH] Adding the rest of files: - ASM is disabled - Neug needs full rewrite - Flash is based on PiMoroni 4MB flash (needs adjust) Signed-off-by: Pol Henarejos --- ac.c | 301 +++++ aes.c | 1352 ++++++++++++++++++++++ affine.h | 8 + bignum.c | 2561 ++++++++++++++++++++++++++++++++++++++++ bn.c | 427 +++++++ bn.h | 23 + call-ec.c | 136 +++ call-ec_p256k1.c | 34 + call-rsa.c | 267 +++++ config.h | 17 + debug.c | 134 +++ ec_p256k1.c | 233 ++++ ec_p256k1.h | 4 + ecc-ed25519.c | 952 +++++++++++++++ ecc-ed448.c | 824 +++++++++++++ ecc-mont.c | 226 ++++ ecc-x448.c | 177 +++ ecc.c | 398 +++++++ field-group-select.h | 7 + flash.c | 738 ++++++++++++ gnuk.h | 501 ++++++++ jpc-ac_p256k1.h | 14 + jpc.c | 199 ++++ jpc_p256k1.c | 36 + low_flash.c | 54 + mod.c | 352 ++++++ mod.h | 3 + mod25638.c | 287 +++++ mod25638.h | 7 + modp256k1.c | 315 +++++ modp256k1.h | 9 + muladd_256.h | 50 + openpgp-do.c | 2637 ++++++++++++++++++++++++++++++++++++++++++ openpgp.c | 1704 +++++++++++++++++++++++++++ p448.c | 666 +++++++++++ p448.h | 15 + polarssl/aes.h | 204 ++++ polarssl/bignum.h | 687 +++++++++++ polarssl/bn_mul.h | 901 +++++++++++++++ polarssl/config.h | 1018 ++++++++++++++++ polarssl/rsa.h | 633 ++++++++++ random.c | 120 ++ random.h | 12 + rsa.c | 1521 ++++++++++++++++++++++++ sha256.c | 225 ++++ sha256.h | 17 + sha512.c | 215 ++++ sha512.h | 17 + shake256.c | 202 ++++ shake256.h | 13 + status-code.h | 14 + sys.h | 0 52 files changed, 21467 insertions(+) create mode 100644 ac.c create mode 100644 aes.c create mode 100644 affine.h create mode 100644 bignum.c create mode 100644 bn.c create mode 100644 bn.h create mode 100644 call-ec.c create mode 100644 call-ec_p256k1.c create mode 100644 call-rsa.c create mode 100644 config.h create mode 100644 debug.c create mode 100644 ec_p256k1.c create mode 100644 ec_p256k1.h create mode 100644 ecc-ed25519.c create mode 100644 ecc-ed448.c create mode 100644 ecc-mont.c create mode 100644 ecc-x448.c create mode 100644 ecc.c create mode 100644 field-group-select.h create mode 100644 flash.c create mode 100644 gnuk.h create mode 100644 jpc-ac_p256k1.h create mode 100644 jpc.c create mode 100644 jpc_p256k1.c create mode 100644 low_flash.c create mode 100644 mod.c create mode 100644 mod.h create mode 100644 mod25638.c create mode 100644 mod25638.h create mode 100644 modp256k1.c create mode 100644 modp256k1.h create mode 100644 muladd_256.h create mode 100644 openpgp-do.c create mode 100644 openpgp.c create mode 100644 p448.c create mode 100644 p448.h create mode 100644 polarssl/aes.h create mode 100644 polarssl/bignum.h create mode 100644 polarssl/bn_mul.h create mode 100644 polarssl/config.h create mode 100644 polarssl/rsa.h create mode 100644 random.c create mode 100644 random.h create mode 100644 rsa.c create mode 100644 sha256.c create mode 100644 sha256.h create mode 100644 sha512.c create mode 100644 sha512.h create mode 100644 shake256.c create mode 100644 shake256.h create mode 100644 status-code.h create mode 100644 sys.h 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