hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 3e63ef6198195ed141bd4b4a879dc56345fec0e3
parent 2c4624269c783fb01d9ab5e4427d47ad8f5ad1c5
Author: Armin Preiml <apreiml@strohwolke.at>
Date:   Thu,  7 Mar 2024 14:50:08 +0100

add crypto::ecdh

This commit introduces ecdh on top of the curves implemented in
crypto::ec.

Signed-off-by: Armin Preiml <apreiml@strohwolke.at>

Diffstat:
Acrypto/ecdh/README | 10++++++++++
Acrypto/ecdh/ecdh.ha | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 123 insertions(+), 0 deletions(-)

diff --git a/crypto/ecdh/README b/crypto/ecdh/README @@ -0,0 +1,10 @@ +The crypto::ecdh module implements eliptic-curve diffie hellman key generation +for curves implemented in [[crypto::ec]]. + +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/ecdh/ecdh.ha b/crypto/ecdh/ecdh.ha @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use bytes; +use crypto::ec; +use crypto::random; +use io; + + +// Size of the shared secret in bytes when using p256 curves. +export def P256_SHAREDSZ = ec::P256_POINTSZ / 2; + +// Size of the shared secret in bytes when using p384 curves. +export def P384_SHAREDSZ = ec::P384_POINTSZ / 2; + +// Size of the shared secret in bytes when using p521 curves. +export def P521_SHAREDSZ = ec::P521_POINTSZ / 2; + +// Key is either not of expected size or is not a valid point on given curve. +export type invalidkey = !void; + +export type privkey = struct { + curve: *ec::curve, + get_x: *fn (priv: *privkey) []u8, +}; + +fn p256_get_x(priv: *privkey) []u8 = (priv: *p256key).x; +fn p384_get_x(priv: *privkey) []u8 = (priv: *p384key).x; +fn p521_get_x(priv: *privkey) []u8 = (priv: *p521key).x; + +export type p256key = struct { + priv: privkey, + x: [ec::P256_SCALARSZ]u8, +}; + +export type p384key = struct { + priv: privkey, + x: [ec::P384_SCALARSZ]u8, +}; + +export type p521key = struct { + priv: privkey, + x: [ec::P521_SCALARSZ]u8, +}; + +// Creates an unitialized p256 key. The curve is also known as secp256r1 or +// prime256. The key must be initialized using [[newkey]]. +export fn p256() p256key = p256key { + priv = privkey { + curve = ec::p256, + get_x = &p256_get_x, + }, + ... +}; + +// Creates an unitialized p384 key. The curve is also known as secp384r1. The +// key must be initialized using [[newkey]]. +export fn p384() p384key = p384key { + priv = privkey { + curve = ec::p384, + get_x = &p384_get_x, + }, + ... +}; + +// Creates an unitialized p521 key. The curve is also known as secp521r1. The +// key must be initialized using [[newkey]]. +export fn p521() p521key = p521key { + priv = privkey { + curve = ec::p521, + get_x = &p521_get_x, + }, + ... +}; + +// Generates a key seeding from the 'rand' stream and stores it in 'priv'. +// 'rand' must be a cryptographic random generator like +// [[crypto::random::stream]]. +export fn newkey(priv: *privkey, rand: io::handle) (void | io::error) = + ec::keygen(priv.curve, priv.get_x(priv), rand)?; + +// Derives the public key from given 'priv' and stores it into 'pub'. Returns +// the number of key bytes written to 'pub'. +export fn pubkey(pub: []u8, priv: *privkey) size = + priv.curve.mulgen(pub, priv.get_x(priv)); + +// Derives a shared secret with the private key 'priv' and the peer's public +// key 'pub' and stores it in 'shared'. +export fn derive( + shared: []u8, + priv: *privkey, + pub: []u8 +) (size | invalidkey) = { + match (ec::validate_pointformat(priv.curve, pub)) { + case void => + yield; + case ec::invalid => + return invalidkey; + }; + + let buf: [ec::MAX_POINTSZ]u8 = [0...]; + let buf = buf[..len(pub)]; + buf[..] = pub[..]; + + if (priv.curve.mul(buf, priv.get_x(priv)) == 0) { + return invalidkey; + }; + + const csz = priv.curve.pointsz / 2; + shared[..] = buf[1..csz + 1]; + bytes::zero(buf); + return csz; +};