diff --git a/ChangeLog b/ChangeLog index 30fb049..ae9ca35 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2012-06-07 Niibe Yutaka + Implement key generation. + * src/openpgp.c (cmd_pgp_gakp): Call gpg_do_keygen. + * src/openpgp-do.c (proc_key_import): Call with modulus = NULL. + (gpg_do_keygen): New function. + (gpg_reset_digital_signature_counter): New function. + (gpg_do_write_prvkey): New argument MODULUS. Call + gpg_reset_digital_signature_counter. + * src/call-rsa.c (rsa_genkey): New function. + * src/random.c (random_byte): New function. + PolarSSL modification. * polarssl-0.14.0/library/rsa.c (rsa_gen_key): Don't set D, DP, DQ, and QP. It's only for key generation. diff --git a/README b/README index 8ff5925..2424da6 100644 --- a/README +++ b/README @@ -135,6 +135,7 @@ Tested features are: * Card holder certificate * Removal of keys (Overriding key import is not supported, but you can remove all keys to import again). + * Key generation on device side It is known not-working well: @@ -142,10 +143,6 @@ It is known not-working well: work well. Please make sure to disable DEBUG option if it doesn't work well. -Not supported feature(s): - - * Key generation on device side - Targets ======= @@ -566,9 +563,12 @@ command is: Note that the factory setting of user password is "123456" and admin password is "12345678" as the specification. -No, Gnuk doesn't support key generation. You need to create your -keys on your computer, and import them to Gnuk Token. After you create -your keys (they must be 2048-bit RSA), you can import them. +It is recommended to create your keys on your computer, and import +them to Gnuk Token. After you create your keys (they must be 2048-bit +RSA), you can import them. + +Gnuk supports key generation, but this feature is young and should be +considered experimental. For detail, please see doc/DEMO and doc/DEMO-2. @@ -577,7 +577,7 @@ you can import the keys (again) to (possibly another) Gnuk Token. In this case, you can use GnuPG's option to specify the home directory by --homedir. -After creating keys by: +After creating keys on your computer by: $ gpg --gen-key ... diff --git a/src/call-rsa.c b/src/call-rsa.c index fc5dc48..2f2a887 100644 --- a/src/call-rsa.c +++ b/src/call-rsa.c @@ -1,7 +1,7 @@ /* * call-rsa.c -- Glue code between RSA computation and OpenPGP card protocol * - * Copyright (C) 2010, 2011 Free Software Initiative of Japan + * Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan * Author: NIIBE Yutaka * * This file is a part of Gnuk, a GnuPG USB Token implementation. @@ -211,3 +211,35 @@ rsa_verify (const uint8_t *pubkey, const uint8_t *hash, const uint8_t *sig) return 0; } } + +#define RSA_EXPONENT 0x10001 + +const uint8_t * +rsa_genkey (void) +{ + int r; + uint8_t index = 0; + uint8_t *p_q_modulus = (uint8_t *)malloc (KEY_CONTENT_LEN*2); + uint8_t *p = p_q_modulus; + uint8_t *q = p_q_modulus + KEY_CONTENT_LEN/2; + uint8_t *modulus = p_q_modulus + KEY_CONTENT_LEN; + + if (p_q_modulus == NULL) + return NULL; + + rsa_init (&rsa_ctx, RSA_PKCS_V15, 0); + r = rsa_gen_key (&rsa_ctx, random_byte, &index, + KEY_CONTENT_LEN * 8, RSA_EXPONENT); + if (r < 0) + { + free (p_q_modulus); + rsa_free (&rsa_ctx); + return NULL; + } + + mpi_write_binary (&rsa_ctx.P, p, KEY_CONTENT_LEN/2); + mpi_write_binary (&rsa_ctx.Q, q, KEY_CONTENT_LEN/2); + mpi_write_binary (&rsa_ctx.N, modulus, KEY_CONTENT_LEN); + rsa_free (&rsa_ctx); + return p_q_modulus; +} diff --git a/src/gnuk.h b/src/gnuk.h index 1488bd0..7b637d8 100644 --- a/src/gnuk.h +++ b/src/gnuk.h @@ -120,6 +120,7 @@ extern void gpg_data_copy (const uint8_t *p); extern void gpg_do_get_data (uint16_t tag, int with_tag); extern void gpg_do_put_data (uint16_t tag, const uint8_t *data, int len); extern void gpg_do_public_key (uint8_t kk_byte); +extern void gpg_do_keygen (uint8_t kk_byte); extern const uint8_t *gpg_get_firmware_update_key (uint8_t keyno); @@ -234,6 +235,7 @@ extern void modulus_free (const uint8_t *); extern int rsa_decrypt (const uint8_t *, uint8_t *, int, struct key_data *); extern int rsa_verify (const uint8_t *pubkey, const uint8_t *hash, const uint8_t *signature); +extern const uint8_t *rsa_genkey (void); extern const uint8_t *gpg_do_read_simple (uint8_t); extern void gpg_do_write_simple (uint8_t, const uint8_t *, int); @@ -327,6 +329,8 @@ extern const uint8_t *random_bytes_get (void); extern void random_bytes_free (const uint8_t *); /* 4-byte salt */ extern uint32_t get_salt (void); +/* iterator returning a byta at a time */ +extern uint8_t random_byte (void *arg); extern uint32_t hardclock (void); diff --git a/src/openpgp-do.c b/src/openpgp-do.c index 5849f6f..8f797db 100644 --- a/src/openpgp-do.c +++ b/src/openpgp-do.c @@ -1,7 +1,7 @@ /* * openpgp-do.c -- OpenPGP card Data Objects (DO) handling * - * Copyright (C) 2010, 2011 Free Software Initiative of Japan + * Copyright (C) 2010, 2011, 2012 Free Software Initiative of Japan * Author: NIIBE Yutaka * * This file is a part of Gnuk, a GnuPG USB Token implementation. @@ -163,6 +163,17 @@ gpg_write_digital_signature_counter (const uint8_t *p, uint32_t dsc) } } +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) { @@ -673,12 +684,11 @@ static int8_t num_prv_keys; static int gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, - const uint8_t *keystring_admin) + const uint8_t *keystring_admin, const uint8_t *modulus) { uint8_t nr = get_do_ptr_nr_for_kk (kk); const uint8_t *p; int r; - const uint8_t *modulus; struct prvkey_data *pd; uint8_t *key_addr; const uint8_t *dek; @@ -686,10 +696,7 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, const uint8_t *ks_pw1; const uint8_t *ks_rc; struct key_data_internal kdi; - -#if 0 - assert (key_len == KEY_CONTENT_LEN); -#endif + int modulus_allocated_here = 0; DEBUG_INFO ("Key import\r\n"); DEBUG_SHORT (key_len); @@ -698,15 +705,23 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, /* No replace support, you need to remove it first. */ return -1; + if (key_len != KEY_CONTENT_LEN) + return -1; + pd = (struct prvkey_data *)malloc (sizeof (struct prvkey_data)); if (pd == NULL) return -1; - modulus = modulus_calc (key_data, key_len); if (modulus == NULL) { - free (pd); - return -1; + modulus = modulus_calc (key_data, key_len); + if (modulus == NULL) + { + free (pd); + return -1; + } + + modulus_allocated_here = 1; } DEBUG_INFO ("Getting keystore address...\r\n"); @@ -714,7 +729,8 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, if (key_addr == NULL) { free (pd); - modulus_free (modulus); + if (modulus_allocated_here) + modulus_free (modulus); return -1; } @@ -736,7 +752,8 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, encrypt (dek, (uint8_t *)&kdi, sizeof (struct key_data_internal)); r = flash_key_write (key_addr, kdi.data, modulus); - modulus_free (modulus); + if (modulus_allocated_here) + modulus_free (modulus); if (r < 0) { @@ -749,7 +766,10 @@ gpg_do_write_prvkey (enum kind_of_key kk, const uint8_t *key_data, int key_len, memcpy (pd->crm_encrypted, (uint8_t *)&kdi.check, ADDITIONAL_DATA_SIZE); if (kk == GPG_KEY_FOR_SIGNING) - ac_reset_pso_cds (); + { + ac_reset_pso_cds (); + gpg_reset_digital_signature_counter (); + } else ac_reset_other (); @@ -908,7 +928,7 @@ proc_key_import (const uint8_t *data, int len) /* It should starts with 00 01 00 01 (E) */ /* Skip E, 4-byte */ - r = gpg_do_write_prvkey (kk, &data[26], len - 26, keystring_admin); + r = gpg_do_write_prvkey (kk, &data[26], len - 26, keystring_admin, NULL); if (r < 0) return 0; else @@ -1460,3 +1480,75 @@ gpg_do_write_simple (uint8_t nr, const uint8_t *data, int size) else *do_data_p = NULL; } + +void +gpg_do_keygen (uint8_t kk_byte) +{ + enum kind_of_key kk; + const uint8_t *keystring_admin; + const uint8_t *p_q_modulus; + const uint8_t *p_q; + const uint8_t *modulus; + int r; + + DEBUG_INFO ("Keygen\r\n"); + DEBUG_BYTE (kk_byte); + + 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; + + if (admin_authorized == BY_ADMIN) + keystring_admin = keystring_md_pw3; + else + keystring_admin = NULL; + + p_q_modulus = rsa_genkey (); + if (p_q_modulus == NULL) + { + GPG_MEMORY_FAILURE (); + return; + } + + p_q = p_q_modulus; + modulus = p_q_modulus + KEY_CONTENT_LEN; + + r = gpg_do_write_prvkey (kk, p_q, KEY_CONTENT_LEN, + keystring_admin, modulus); + free ((uint8_t *)p_q_modulus); + if (r < 0) + { + GPG_ERROR (); + return; + } + + DEBUG_INFO ("Calling gpg_do_public_key...\r\n"); + + if (kk == GPG_KEY_FOR_SIGNING) + { + /* Authintication has been reset within gpg_do_write_prvkey. */ + /* But GnuPG expects it's ready for signing. */ + /* Thus, we call verify_pso_cds here. */ + const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); + const uint8_t *pw; + int pw_len; + + if (ks_pw1) + { + pw = ks_pw1+1; + pw_len = ks_pw1[0]; + } + else + { + pw = (const uint8_t *)OPENPGP_CARD_INITIAL_PW1; + pw_len = strlen (OPENPGP_CARD_INITIAL_PW3); + } + + verify_pso_cds (pw, pw_len); + } + + gpg_do_public_key (kk_byte); +} diff --git a/src/openpgp.c b/src/openpgp.c index f7e225a..c5013af 100644 --- a/src/openpgp.c +++ b/src/openpgp.c @@ -480,12 +480,11 @@ cmd_pgp_gakp (void) /* Get public key */ gpg_do_public_key (apdu.cmd_apdu_data[0]); else - { /* Generate key pair */ + { if (!ac_check_status (AC_ADMIN_AUTHORIZED)) GPG_SECURITY_FAILURE (); - - /* XXX: Not yet supported */ - GPG_ERROR (); + /* Generate key pair */ + gpg_do_keygen (apdu.cmd_apdu_data[0]); } } diff --git a/src/random.c b/src/random.c index 95299f6..8fb06bc 100644 --- a/src/random.c +++ b/src/random.c @@ -70,3 +70,30 @@ get_salt (void) { return neug_get (NEUG_KICK_FILLING); } + + +/* + * Rundom byte iterator + */ +uint8_t +random_byte (void *arg) +{ + uint8_t *index_p = (uint8_t *)arg; + uint8_t index = *index_p; + uint8_t *p = ((uint8_t *)random_word) + index; + uint8_t v; + + neug_wait_full (); + + v = *p; + + if (++index >= RANDOM_BYTES_LENGTH) + { + index = 0; + neug_flush (); + } + + *index_p = index; + + return v; +}