implement key generation
authorNIIBE Yutaka <gniibe@fsij.org>
Thu, 7 Jun 2012 01:48:25 +0000 (10:48 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Thu, 7 Jun 2012 04:12:27 +0000 (13:12 +0900)
ChangeLog
README
src/call-rsa.c
src/gnuk.h
src/openpgp-do.c
src/openpgp.c
src/random.c

index 30fb049..ae9ca35 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2012-06-07  Niibe Yutaka  <gniibe@fsij.org>
 
+       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 (file)
--- 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
   ...
index fc5dc48..2f2a887 100644 (file)
@@ -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 <gniibe@fsij.org>
  *
  * 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;
+}
index 1488bd0..7b637d8 100644 (file)
@@ -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);
 
index 5849f6f..8f797db 100644 (file)
@@ -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 <gniibe@fsij.org>
  *
  * 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);
+}
index f7e225a..c5013af 100644 (file)
@@ -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]);
     }
 }
 
index 95299f6..8fb06bc 100644 (file)
@@ -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;
+}