[Gnuk-users] Experimental Curve25519 support

NIIBE Yutaka gniibe at fsij.org
Tue Jul 7 23:58:20 UTC 2015


Hello,

I committed the following change to master branch of Gnuk.

It works well with the patched version of gnupg+libgcrypt.  You need
to apply my change to gnupg as well as to libgcrypt, to play with
this.

Please note that this is highly experimental.  There is no consensus
yet for the OpenPGP format with Curve25519, OpenPGPcard specification
V3.0 not yet define Curve25519 (but only NIST and Brainpool), either.

Nevertheless, I think that this change of Gnuk is relatively stable
than the paches of gnupg+libgcrypt.  Thus, this commit.


    Curve25519 support

diff --git a/ChangeLog b/ChangeLog
index 6dc8cb2..84eed6e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2015-07-07  Niibe Yutaka  <gniibe at fsij.org>
+
+	* 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  <gniibe at fsij.org>

 	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 <gniibe at fsij.org>
  *
  * This file is a part of Gnuk, a GnuPG USB Token implementation.
@@ -24,6 +24,7 @@

 #include <stdint.h>
 #include <string.h>
+#include <stdlib.h>
 #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.");
--



More information about the gnuk-users mailing list