commit 95992f6d6f9d7a548ace16af84d66bc4571bff41
parent 6603f946fc2fd6158778dca4d97e86b0f467a3fc
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Fri, 6 May 2022 15:13:21 +0200
implement crypto::x25519
Signed-off-by: Armin Preiml <apreiml@strohwolke.at>
Diffstat:
5 files changed, 154 insertions(+), 0 deletions(-)
diff --git a/crypto/x25519/+test.ha b/crypto/x25519/+test.ha
@@ -0,0 +1,68 @@
+// License: MPL-2.0
+// (c) 2022 Armin Preiml <apreiml@strohwolke.at>
+use bytes;
+use encoding::hex;
+use crypto::random;
+
+@test fn sample() void = {
+ let seed: [32]u8 = [0xff...];
+ let priv: key = [0...];
+ priv[31] = 0xbf;
+
+ newkey(&priv, &seed);
+
+ let pub: key = [0...];
+ pubkey(&pub, &priv);
+
+ const pexpected: key = [
+ 0x84, 0x7c, 0x0d, 0x2c, 0x37, 0x52, 0x34, 0xf3, 0x65, 0xe6,
+ 0x60, 0x95, 0x51, 0x87, 0xa3, 0x73, 0x5a, 0x0f, 0x76, 0x13,
+ 0xd1, 0x60, 0x9d, 0x3a, 0x6a, 0x4d, 0x8c, 0x53, 0xae, 0xaa,
+ 0x5a, 0x22,
+ ];
+
+ assert(bytes::equal(pexpected, pub));
+
+ const otherpub: key = [
+ 0x28, 0x18, 0x84, 0xe0, 0x0f, 0xae, 0x8a, 0x33, 0x75, 0x05,
+ 0xbf, 0x38, 0x15, 0x2a, 0x97, 0xc0, 0x20, 0x4a, 0x8c, 0x1d,
+ 0x4c, 0xfa, 0x2d, 0x2b, 0x12, 0x99, 0x80, 0xed, 0xe7, 0x32,
+ 0xaf, 0x0d,
+ ];
+
+ const expected: key = [
+ 0x07, 0x4a, 0xaf, 0x3c, 0xa3, 0x87, 0xd5, 0xa3, 0x71, 0x25,
+ 0x9f, 0x50, 0xb3, 0xf0, 0xa1, 0xe9, 0x63, 0x6b, 0x18, 0x1d,
+ 0x5e, 0x4e, 0x6e, 0xb3, 0x1a, 0xe9, 0xda, 0x01, 0x05, 0x4a,
+ 0x8c, 0x3b,
+ ];
+
+ let shared: key = [0...];
+
+ derive(&shared, &priv, &otherpub);
+ assert(bytes::equal(expected, shared));
+};
+
+@test fn random() void = {
+ let seed: [32]u8 = [0...];
+ let priv1: key = [0...];
+ let priv2: key = [0...];
+ let pub1: key = [0...];
+ let pub2: key = [0...];
+ let shared1: key = [0...];
+ let shared2: key = [0...];
+
+ random::buffer(seed);
+ newkey(&priv1, &seed);
+
+ random::buffer(seed);
+ newkey(&priv2, &seed);
+
+ pubkey(&pub1, &priv1);
+ pubkey(&pub2, &priv2);
+
+ derive(&shared1, &priv1, &pub2);
+ derive(&shared2, &priv2, &pub1);
+
+ assert(bytes::equal(&shared1, &shared2));
+};
diff --git a/crypto/x25519/README b/crypto/x25519/README
@@ -0,0 +1,13 @@
+The crypto::x25519 module provides functions to generate key pairs and to
+derive shared keys between them, based on curve25519.
+
+A key pair is created by generating a private key with [[newkey]] and deriving
+the public key with [[pubkey]]. A shared key can be found by using [[derive]].
+
+This is a low-level module which implements cryptographic primitives. Direct use
+of cryptographic primitives is not recommended for non-experts, as incorrect use
+of these primitives can easily lead to the introduction of security
+vulnerabilities. Non-experts are advised to use the high-level operations
+available in the top-level [[crypto]] module.
+
+Be advised that Hare's cryptography implementations have not been audited.
diff --git a/crypto/x25519/x25519.ha b/crypto/x25519/x25519.ha
@@ -0,0 +1,27 @@
+// License: MPL-2.0
+// (c) 2022 Armin Preiml <apreiml@strohwolke.at>
+use crypto::curve25519;
+
+// Type for private, public or shared keys.
+export type key = [32]u8;
+
+// Initializes a new x25519 private key from the provided 32-byte seed,
+// which should be generated with [[crypto::random]].
+export fn newkey(priv: *key, seed: *[32]u8) void = {
+ priv[..] = seed[..];
+ curve25519::clamp(priv);
+};
+
+// Derives the public key from a private key prepared with [[newkey]],
+// writing it to the 'pub' parameter.
+export fn pubkey(pub: *key, priv: const *key) void =
+ curve25519::scalarmult_base(pub, priv);
+
+// Derives a 32-byte shared key from the private key of one key-pair and
+// the public key of a second key-pair.
+export fn derive(shared: *key, priv: *key, pub: *key) void = {
+ curve25519::x25519(shared, priv, pub);
+
+ // TODO figure out if checking for low order points is required
+ // https://github.com/jedisct1/libsodium/blob/cec56d867f741e66f78b9fde37d9081643599a2a/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c#L90
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -471,6 +471,18 @@ crypto_ed25519() {
fi
}
+crypto_x25519() {
+ if [ $testing -eq 0 ]
+ then
+ gen_srcs crypto::x25519 x25519.ha
+ gen_ssa crypto::x25519 crypto::curve25519
+ else
+ gen_srcs crypto::x25519 x25519.ha +test.ha
+ gen_ssa crypto::x25519 bytes crypto::curve25519 encoding::hex \
+ crypto::random
+ fi
+}
+
dirs() {
gen_srcs dirs \
xdg.ha
@@ -1343,6 +1355,7 @@ crypto::sha256
crypto::sha512
crypto::curve25519
crypto::ed25519
+crypto::x25519
datetime linux freebsd
dirs
encoding::base64
diff --git a/stdlib.mk b/stdlib.mk
@@ -270,6 +270,12 @@ stdlib_deps_any += $(stdlib_crypto_ed25519_any)
stdlib_crypto_ed25519_linux = $(stdlib_crypto_ed25519_any)
stdlib_crypto_ed25519_freebsd = $(stdlib_crypto_ed25519_any)
+# gen_lib crypto::x25519 (any)
+stdlib_crypto_x25519_any = $(HARECACHE)/crypto/x25519/crypto_x25519-any.o
+stdlib_deps_any += $(stdlib_crypto_x25519_any)
+stdlib_crypto_x25519_linux = $(stdlib_crypto_x25519_any)
+stdlib_crypto_x25519_freebsd = $(stdlib_crypto_x25519_any)
+
# gen_lib datetime (linux)
stdlib_datetime_linux = $(HARECACHE)/datetime/datetime-linux.o
stdlib_deps_linux += $(stdlib_datetime_linux)
@@ -961,6 +967,16 @@ $(HARECACHE)/crypto/ed25519/crypto_ed25519-any.ssa: $(stdlib_crypto_ed25519_any_
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::ed25519 \
-t$(HARECACHE)/crypto/ed25519/crypto_ed25519.td $(stdlib_crypto_ed25519_any_srcs)
+# crypto::x25519 (+any)
+stdlib_crypto_x25519_any_srcs = \
+ $(STDLIB)/crypto/x25519/x25519.ha
+
+$(HARECACHE)/crypto/x25519/crypto_x25519-any.ssa: $(stdlib_crypto_x25519_any_srcs) $(stdlib_rt) $(stdlib_crypto_curve25519_$(PLATFORM))
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/crypto/x25519
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::x25519 \
+ -t$(HARECACHE)/crypto/x25519/crypto_x25519.td $(stdlib_crypto_x25519_any_srcs)
+
# datetime (+linux)
stdlib_datetime_linux_srcs = \
$(STDLIB)/datetime/arithmetic.ha \
@@ -2316,6 +2332,12 @@ testlib_deps_any += $(testlib_crypto_ed25519_any)
testlib_crypto_ed25519_linux = $(testlib_crypto_ed25519_any)
testlib_crypto_ed25519_freebsd = $(testlib_crypto_ed25519_any)
+# gen_lib crypto::x25519 (any)
+testlib_crypto_x25519_any = $(TESTCACHE)/crypto/x25519/crypto_x25519-any.o
+testlib_deps_any += $(testlib_crypto_x25519_any)
+testlib_crypto_x25519_linux = $(testlib_crypto_x25519_any)
+testlib_crypto_x25519_freebsd = $(testlib_crypto_x25519_any)
+
# gen_lib datetime (linux)
testlib_datetime_linux = $(TESTCACHE)/datetime/datetime-linux.o
testlib_deps_linux += $(testlib_datetime_linux)
@@ -3026,6 +3048,17 @@ $(TESTCACHE)/crypto/ed25519/crypto_ed25519-any.ssa: $(testlib_crypto_ed25519_any
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::ed25519 \
-t$(TESTCACHE)/crypto/ed25519/crypto_ed25519.td $(testlib_crypto_ed25519_any_srcs)
+# crypto::x25519 (+any)
+testlib_crypto_x25519_any_srcs = \
+ $(STDLIB)/crypto/x25519/x25519.ha \
+ $(STDLIB)/crypto/x25519/+test.ha
+
+$(TESTCACHE)/crypto/x25519/crypto_x25519-any.ssa: $(testlib_crypto_x25519_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_curve25519_$(PLATFORM)) $(testlib_encoding_hex_$(PLATFORM)) $(testlib_crypto_random_$(PLATFORM))
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/crypto/x25519
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::x25519 \
+ -t$(TESTCACHE)/crypto/x25519/crypto_x25519.td $(testlib_crypto_x25519_any_srcs)
+
# datetime (+linux)
testlib_datetime_linux_srcs = \
$(STDLIB)/datetime/arithmetic.ha \