shithub: front

Download patch

ref: 9ee2b3a6867504b2ad37c13f2ea9fe25a9165b64
parent: 185e69f5ab6792270f71b54270880d3ba18df689
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Jun 14 12:24:35 EDT 2025

libsec: add x25519() function (thanks nepomuk)

Add a x25519 function as described in rfc7748,
and then base curve25519_dh_*() ontop of it.

also, a test is provided with test vectors.

--- a/sys/include/ape/libsec.h
+++ b/sys/include/ape/libsec.h
@@ -589,6 +589,9 @@
 /* Curve25519 elliptic curve, public key function */
 void curve25519(uchar mypublic[32], uchar secret[32], uchar basepoint[32]);
 
+/* x25519 used in Curve25519 diffie hellman */
+int x25519(uchar out[32], uchar s[32], uchar u[32]);
+
 /* Curve25519 diffie hellman */
 void curve25519_dh_new(uchar x[32], uchar y[32]);
 int curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32]);
--- a/sys/include/libsec.h
+++ b/sys/include/libsec.h
@@ -582,6 +582,9 @@
 /* Curve25519 elliptic curve, public key function */
 void curve25519(uchar mypublic[32], uchar secret[32], uchar basepoint[32]);
 
+/* x25519 used in Curve25519 diffie hellman */
+int x25519(uchar out[32], uchar s[32], uchar u[32]);
+
 /* Curve25519 diffie hellman */
 void curve25519_dh_new(uchar x[32], uchar y[32]);
 int curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32]);
--- a/sys/src/libsec/port/curve25519_dh.c
+++ b/sys/src/libsec/port/curve25519_dh.c
@@ -5,18 +5,47 @@
 static uchar nine[32] = {9};
 static uchar zero[32] = {0};
 
+int
+x25519(uchar out[32], uchar s[32], uchar u[32])
+{
+	uchar sf, sl, ul;
+
+	sf = s[0];
+	sl = s[31];
+	ul = u[31];
+
+	/* clamp */
+	s[0] &= ~7;			/* clear bit 0,1,2 */
+	s[31] = 0x40 | (s[31] & 0x7f);	/* set bit 254, clear bit 255 */
+
+	/*
+		Implementations MUST accept non-canonical values and process them as
+   		if they had been reduced modulo the field prime.  The non-canonical
+   		values are 2^255 - 19 through 2^255 - 1 for X25519
+	*/
+	u[31] &= 0x7f;
+	
+	curve25519(out, s, u);
+
+	s[0] = sf;
+	s[31] = sl;
+	u[31] = ul;
+
+	return tsmemcmp(out, zero, 32) != 0;
+}
+
 void
 curve25519_dh_new(uchar x[32], uchar y[32])
 {
-	uchar b;
-
 	/* new public/private key pair */
 	genrandom(x, 32);
-	b = x[31];
-	x[0] &= ~7;			/* clear bit 0,1,2 */
-	x[31] = 0x40 | (b & 0x7f);	/* set bit 254, clear bit 255 */
-	curve25519(y, x, nine);
+	uchar b = x[31];
 
+	/* don't check for zero: the scalar is never
+		zero because of clamping, and the basepoint is not the identity
+		in the prime-order subgroup(s). */
+	x25519(y, x, nine);
+
 	/* bit 255 is always 0, so make it random */
 	y[31] |= b & 0x80;
 }
@@ -24,14 +53,16 @@
 int
 curve25519_dh_finish(uchar x[32], uchar y[32], uchar z[32])
 {
+	int r;
+
 	/* remove the random bit */
 	y[31] &= 0x7f;
 
 	/* calculate dhx key */
-	curve25519(z, x, y);
+	r = x25519(z, x, y);
 
 	memset(x, 0, 32);
 	memset(y, 0, 32);
 
-	return tsmemcmp(z, zero, 32) != 0;
+	return r;
 }
--- a/sys/src/libsec/test/mkfile
+++ b/sys/src/libsec/test/mkfile
@@ -11,6 +11,7 @@
 	prime\
 	rsa\
 	sha2\
+	x25519\
 
 CFLAGS=$CFLAGS -I../../libmp/port
 
--- /dev/null
+++ b/sys/src/libsec/test/x25519.c
@@ -1,0 +1,137 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+#include <mp.h>
+/* https://www.rfc-editor.org/rfc/rfc7748.html#section-5.2 */
+
+struct {
+	char *s, *u, *exp;
+	int r;
+} tests_x25519[] = {
+	{
+		"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
+		"e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
+		"c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
+		1,
+	},
+	{
+		"4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d",
+		"e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493",
+		"95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957",
+		1,
+	},
+	{
+		"0900000000000000000000000000000000000000000000000000000000000000",
+		"0900000000000000000000000000000000000000000000000000000000000000",
+		"422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079",
+		1,
+	},
+	{
+		"0900000000000000000000000000000000000000000000000000000000000000",
+		"0900000000000000000000000000000000000000000000000000000000000000",
+		"684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51",
+		1000,
+	},
+/* takes too long on my machine
+	{
+		"0900000000000000000000000000000000000000000000000000000000000000",
+		"0900000000000000000000000000000000000000000000000000000000000000",
+		"7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424",
+		1000000,
+	},
+*/
+	/* Diffie-Hellman Test vector */
+	{
+		"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
+		"0900000000000000000000000000000000000000000000000000000000000000",
+		"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
+		1,
+	},
+	{
+		"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb",
+		"0900000000000000000000000000000000000000000000000000000000000000",
+		"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
+		1,
+	},
+	{
+		"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
+		"de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
+		"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
+		1,
+	},
+	{
+		"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb",
+		"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
+		"4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
+		1,
+	},
+	
+};
+
+int chatty;
+
+int
+parsehex(char *s, uchar *h, char *l)
+{
+	char *e;
+	mpint *m;
+	int n;
+
+	n = strlen(s);
+	if(n == 0)
+		return 0;
+	assert((n & 1) == 0);
+	n >>= 1;
+	e = nil;
+	m = strtomp(s, &e, 16, nil);
+	if(m == nil || *e != '\0')
+		abort();
+	mptober(m, h, n);
+	if(l != nil && chatty)
+		print("%s = %.*H\n", l, n, h);
+	return n;
+}
+
+void
+main(int argc, char **argv)
+{
+	uchar s[32], u[32], out[32];
+	char buf[64+1];
+	int i, j;
+
+	fmtinstall('H', encodefmt);
+
+	ARGBEGIN {
+	case 'd':
+		chatty++;
+		break;
+	} ARGEND;
+
+	for(i = 0; i < nelem(tests_x25519); i++){
+		parsehex(tests_x25519[i].s, s, "scalar");
+		parsehex(tests_x25519[i].u, u, "u-coordiante");
+
+		for(j = 0; j < tests_x25519[i].r; j++) {
+			x25519(out, s, u);
+			memcpy(u, s, sizeof(u));
+			memcpy(s, out, sizeof(s));
+		}
+	
+		snprint(buf, sizeof buf, "%.*lH", 64, out);
+		if(strcmp(buf, tests_x25519[i].exp) != 0){
+			fprint(2, 
+				"Test %d  (%d rounds):\n"
+				"\ts in: %s\n"
+				"\tu in: %s\n"
+				"Exp: %s\n"
+				"Got: %s\n\n",
+				i, tests_x25519[i].r,
+				tests_x25519[i].s, tests_x25519[i].u, 
+				tests_x25519[i].exp, buf
+			);
+			exits("fail");
+		}
+	}
+	
+	exits(nil);
+}
--