From 9bbca07033a02a014a0513b58a47ecfc049ada1e Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Tue, 7 Jul 2015 15:42:00 +0900 Subject: [PATCH] Curve25519 support --- ChangeLog | 16 ++++++ chopstx | 2 +- src/Makefile.in | 2 +- src/ecc-mont.c | 40 ++++++++++++++- src/gnuk.h | 4 ++ src/openpgp-do.c | 127 ++++++++++++++++++++++++++++++++--------------- src/openpgp.c | 28 +++++++---- 7 files changed, 166 insertions(+), 53 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6dc8cb2..84eed6e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2015-07-07 Niibe Yutaka + + * src/Makefile.in (CSRC): Add ecc-mont.c. + + * src/ecc-mont.c (mod25638_mul_121665): Fix. + (ecdh_compute_public_25519, ecdh_decrypt_curve25519): New. + + * src/openpgp.c (cmd_pso): Support ALGO_CURVE25519. + + * src/openpgp-do.c (algorithm_attr_cv25519): New. + (rw_algorithm_attr, get_algo_attr_data_object) + (gpg_get_algo_attr_key_size, gpg_do_write_prvkey) + (proc_key_import, gpg_do_public_key): Support ALGO_CURVE25519. + + * src/gnuk.h (ALGO_CURVE25519): New. + 2015-07-06 Niibe Yutaka Enhancement for FSM-55. diff --git a/chopstx b/chopstx index 43bd3bc..2bb0e0d 160000 --- a/chopstx +++ b/chopstx @@ -1 +1 @@ -Subproject commit 43bd3bcefd6dbd28e3fd68ca8ff6b9a877009774 +Subproject commit 2bb0e0de5d7d25d0bb036e6ee9cc0c94b62375ec diff --git a/src/Makefile.in b/src/Makefile.in index 0e916e0..2dd3360 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -15,7 +15,7 @@ CSRC = main.c usb_stm32f103.c adc_stm32f103.c \ bn.c mod.c \ modp256r1.c jpc_p256r1.c ec_p256r1.c call-ec_p256r1.c \ modp256k1.c jpc_p256k1.c ec_p256k1.c call-ec_p256k1.c \ - mod25638.c ecc-edwards.c sha512.c \ + mod25638.c ecc-edwards.c ecc-mont.c sha512.c \ random.c neug.c sha256.c sys.c INCDIR = diff --git a/src/ecc-mont.c b/src/ecc-mont.c index 804419d..25f2fdd 100644 --- a/src/ecc-mont.c +++ b/src/ecc-mont.c @@ -2,7 +2,7 @@ * ecc-mont.c - Elliptic curve computation for * the Montgomery curve: y^2 = x^3 + 486662*x^2 + x. * - * Copyright (C) 2014 Free Software Initiative of Japan + * Copyright (C) 2014, 2015 Free Software Initiative of Japan * Author: NIIBE Yutaka * * This file is a part of Gnuk, a GnuPG USB Token implementation. @@ -24,6 +24,7 @@ #include #include +#include #include "bn.h" #include "mod25638.h" #include "mod.h" @@ -78,6 +79,7 @@ mod25638_mul_121665 (bn256 *x, const bn256 *a) s = a->word; d = x->word; + memset (d, 0, sizeof (bn256)); w = 121665; MULADD_256_ASM (s, d, w, c); #else @@ -143,7 +145,7 @@ mont_d_and_a (pt *prd, pt *sum, pt *q0, pt *q1, const bn256 *dif_x) * @param Q_X x-coordinate of Q * */ -void +static void compute_nQ (bn256 *res, const bn256 *n, const bn256 *q_x) { int i, j; @@ -194,3 +196,37 @@ compute_nQ (bn256 *res, const bn256 *n, const bn256 *q_x) mod25638_mul (res, res, p0->x); mod25519_reduce (res); } + + +uint8_t * +ecdh_compute_public_25519 (const uint8_t *key_data) +{ + uint8_t *p; + bn256 gx[1]; + bn256 k[1]; + + memset (gx, 0, sizeof (bn256)); + gx[0].word[0] = 9; /* Gx = 9 */ + memcpy (k, key_data, sizeof (bn256)); + p = (uint8_t *)malloc (sizeof (bn256)); + if (p == NULL) + return NULL; + + compute_nQ ((bn256 *)p, k, gx); + return p; +} + +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/src/gnuk.h b/src/gnuk.h index e096f78..1afb25a 100644 --- a/src/gnuk.h +++ b/src/gnuk.h @@ -116,6 +116,7 @@ const uint8_t *gpg_get_firmware_update_key (uint8_t keyno); #define ALGO_NISTP256R1 1 #define ALGO_SECP256K1 2 #define ALGO_ED25519 3 +#define ALGO_CURVE25519 4 #define ALGO_RSA2K 255 enum kind_of_key { @@ -279,6 +280,9 @@ 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); uint8_t *eddsa_compute_public_25519 (const uint8_t *a); +uint8_t *ecdh_compute_public_25519 (const uint8_t *a); +int ecdh_decrypt_curve25519 (const uint8_t *input, uint8_t *output, + const uint8_t *key_data); const uint8_t *gpg_do_read_simple (uint8_t); void gpg_do_write_simple (uint8_t, const uint8_t *, int); diff --git a/src/openpgp-do.c b/src/openpgp-do.c index 6d91b69..d9d4dc3 100644 --- a/src/openpgp-do.c +++ b/src/openpgp-do.c @@ -170,6 +170,13 @@ static const uint8_t algorithm_attr_ed25519[] __attribute__ ((aligned (1))) = { 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: @@ -246,16 +253,21 @@ get_algo_attr_data_object (enum kind_of_key kk) if (algo_attr_p == NULL) return algorithm_attr_rsa2k; - if (algo_attr_p[1] == ALGO_RSA4K) - return algorithm_attr_rsa4k; - else if (algo_attr_p[1] == ALGO_NISTP256R1) - return algorithm_attr_p256r1; - else if (algo_attr_p[1] == ALGO_SECP256K1) - return algorithm_attr_p256k1; - else if (algo_attr_p[1] == ALGO_ED25519) - return algorithm_attr_ed25519; - - return algorithm_attr_rsa2k; + switch (algo_attr_p[1]) + { + case ALGO_RSA4K: + return algorithm_attr_rsa4k; + case ALGO_NISTP256R1: + return algorithm_attr_p256r1; + case ALGO_SECP256K1: + return algorithm_attr_p256k1; + case ALGO_ED25519: + return algorithm_attr_ed25519; + case ALGO_CURVE25519: + return algorithm_attr_cv25519; + default: + return algorithm_attr_rsa2k; + } } int @@ -263,30 +275,43 @@ 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) - if (s == GPG_KEY_STORAGE) - return 512; - else - return 256; - else if (algo_attr_p[1] == ALGO_RSA4K) - if (s == GPG_KEY_STORAGE) - return 1024; - else - return 512; - else if (algo_attr_p[1] == ALGO_NISTP256R1 || algo_attr_p[1] == ALGO_SECP256K1) - if (s == GPG_KEY_STORAGE) - return 128; - else if (s == GPG_KEY_PUBLIC) - return 64; - else - return 32; - else /* ED25519 */ - if (s == GPG_KEY_STORAGE) - return 128; - else if (s == GPG_KEY_PUBLIC) - return 32; - else - return 64; + 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_NISTP256R1: + case ALGO_SECP256K1: + if (s == GPG_KEY_STORAGE) + return 128; + else if (s == GPG_KEY_PUBLIC) + 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_CURVE25519: + if (s == GPG_KEY_STORAGE) + return 64; + else + return 32; + default: + rsa2k: + if (s == GPG_KEY_STORAGE) + return 512; + else + return 256; + } } @@ -724,6 +749,8 @@ rw_algorithm_attr (uint16_t tag, int with_tag, algo = ALGO_NISTP256R1; 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 */ @@ -1049,6 +1076,12 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, if (prvkey_len != 64) return -1; } + else if (attr == ALGO_CURVE25519) + { + pubkey_len = prvkey_len; + if (prvkey_len != 32) + return -1; + } else /* RSA */ { int key_size = gpg_get_algo_attr_key_size (kk, GPG_KEY_STORAGE); @@ -1066,6 +1099,8 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, pubkey_allocated_here = ecc_compute_public_p256r1 (key_data); else if (attr == ALGO_ED25519) pubkey_allocated_here = eddsa_compute_public_25519 (key_data); + else if (attr == ALGO_CURVE25519) + pubkey_allocated_here = ecdh_compute_public_25519 (key_data); else /* RSA */ pubkey_allocated_here = modulus_calc (key_data, prvkey_len); @@ -1292,7 +1327,7 @@ kkb_to_kk (uint8_t kk_byte) static int proc_key_import (const uint8_t *data, int len) { - int r; + int r = -1; enum kind_of_key kk; const uint8_t *keystring_admin; int attr; @@ -1328,7 +1363,7 @@ proc_key_import (const uint8_t *data, int len) attr = gpg_get_algo_attr (kk); if ((len <= 12 && (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1 - || attr == ALGO_ED25519)) + || attr == ALGO_ED25519 || attr == ALGO_CURVE25519)) || (len <= 22 && attr == ALGO_RSA2K) || (len <= 24 && attr == ALGO_RSA4K)) { /* Deletion of the key */ gpg_do_delete_prvkey (kk, CLEAN_SINGLE); @@ -1343,7 +1378,7 @@ proc_key_import (const uint8_t *data, int len) r = gpg_do_write_prvkey (kk, &data[28], len - 28, keystring_admin, NULL); else if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1) r = gpg_do_write_prvkey (kk, &data[12], len - 12, keystring_admin, NULL); - else /* if (attr == ALGO_ED25519) */ + else if (attr == ALGO_ED25519) { uint8_t hash[64]; @@ -1356,6 +1391,18 @@ proc_key_import (const uint8_t *data, int len) hash[31] |= 64; r = gpg_do_write_prvkey (kk, hash, 64, keystring_admin, NULL); } + else if (attr == ALGO_CURVE25519) + { + uint8_t priv[32]; + int i; + + if (len - 12 != 32) + return 1; /* Error. */ + + for (i = 0; i < 32; i++) + priv[31-i] = data[12+i]; + r = gpg_do_write_prvkey (kk, priv, 32, keystring_admin, NULL); + } if (r < 0) return 0; @@ -1910,14 +1957,14 @@ gpg_do_public_key (uint8_t kk_byte) res_p += 64; } } - else if (attr == ALGO_ED25519) - { /* EdDSA */ + 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 */ + /* 32-byte binary (little endian): Y with parity or X*/ memcpy (res_p, pubkey, 32); res_p += 32; } diff --git a/src/openpgp.c b/src/openpgp.c index 9468ee7..a89b540 100644 --- a/src/openpgp.c +++ b/src/openpgp.c @@ -942,15 +942,11 @@ cmd_pso (void) } else if (attr == ALGO_NISTP256R1 || attr == ALGO_SECP256K1) { - int header_size = -1; - - if (len == 65) - header_size = 0; - else if (len == 65 + ECC_CIPHER_DO_HEADER_SIZE) - header_size = ECC_CIPHER_DO_HEADER_SIZE; + int header = ECC_CIPHER_DO_HEADER_SIZE; /* Format is in big endian MPI: 04 || x || y */ - if (header_size < 0 || apdu.cmd_apdu_data[header_size] != 4) + if (len != 65 + ECC_CIPHER_DO_HEADER_SIZE + || apdu.cmd_apdu_data[header] != 0x04) { GPG_CONDITION_NOT_SATISFIED (); return; @@ -958,12 +954,26 @@ cmd_pso (void) result_len = 65; if (attr == ALGO_NISTP256R1) - r = ecdh_decrypt_p256r1 (apdu.cmd_apdu_data + header_size, res_APDU, + r = ecdh_decrypt_p256r1 (apdu.cmd_apdu_data + header, res_APDU, kd[GPG_KEY_FOR_DECRYPTION].data); else - r = ecdh_decrypt_p256k1 (apdu.cmd_apdu_data + header_size, res_APDU, + r = ecdh_decrypt_p256k1 (apdu.cmd_apdu_data + header, res_APDU, kd[GPG_KEY_FOR_DECRYPTION].data); } + 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; + } + + result_len = 32; + r = ecdh_decrypt_curve25519 (apdu.cmd_apdu_data + header, res_APDU, + kd[GPG_KEY_FOR_DECRYPTION].data); + } else { DEBUG_INFO ("unknown algo.");