commit 19421dfe21b90f482c1b979feb3d329102cea52b
parent 280907287633f7bc1f5be24ec16986ed6bc51864
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Thu, 9 May 2024 13:31:01 +0200
crypto::ecdsa: key management functions
Signed-off-by: Armin Preiml <apreiml@strohwolke.at>
Diffstat:
A | crypto/ecdsa/key.ha | | | 188 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 188 insertions(+), 0 deletions(-)
diff --git a/crypto/ecdsa/key.ha b/crypto/ecdsa/key.ha
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use crypto::ec;
+use io;
+
+// Invalid key.
+export type invalidkey = !void;
+
+export type pubkey = struct {
+ curve: *ec::curve,
+ get_q: *fn (pub: *pubkey) []u8,
+};
+
+export type privkey = struct {
+ curve: *ec::curve,
+ get_x: *fn (priv: *privkey) []u8,
+};
+
+export type p256privkey = struct {
+ priv: privkey,
+ x: [ec::P256_SCALARSZ]u8,
+};
+
+export type p384privkey = struct {
+ priv: privkey,
+ x: [ec::P384_SCALARSZ]u8,
+};
+
+export type p521privkey = struct {
+ priv: privkey,
+ x: [ec::P521_SCALARSZ]u8,
+};
+
+fn p256_get_x(priv: *privkey) []u8 = (priv: *p256privkey).x;
+fn p384_get_x(priv: *privkey) []u8 = (priv: *p384privkey).x;
+fn p521_get_x(priv: *privkey) []u8 = (priv: *p521privkey).x;
+
+// Creates an unitialized p256 [[privkey]]. The curve is also known as secp256r1
+// or prime256. The key must be initialized using [[newkey]].
+export fn p256priv() p256privkey = p256privkey {
+ priv = privkey {
+ curve = ec::p256,
+ get_x = &p256_get_x,
+ },
+ ...
+};
+
+// Creates an unitialized p384 [[privkey]]. The curve is also known as
+// secp384r1. The key must be initialized using [[newkey]].
+export fn p384priv() p384privkey = p384privkey {
+ priv = privkey {
+ curve = ec::p384,
+ get_x = &p384_get_x,
+ },
+ ...
+};
+
+// Creates an unitialized p521 [[privkey]]. The curve is also known as
+// secp521r1. The key must be initialized using [[newkey]].
+export fn p521priv() p521privkey = p521privkey {
+ priv = privkey {
+ curve = ec::p521,
+ get_x = &p521_get_x,
+ },
+ ...
+};
+
+
+fn p256_get_q(pub: *pubkey) []u8 = (pub: *p256pubkey).q;
+fn p384_get_q(pub: *pubkey) []u8 = (pub: *p384pubkey).q;
+fn p521_get_q(pub: *pubkey) []u8 = (pub: *p521pubkey).q;
+
+// 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)?;
+};
+
+// Returns the buffer to the encoded key. See [[crypto::ec::curve]] on how the
+// scalar must be encoded. The key must be valid, otherwise undefined behaviour
+// may result. The function [[privkey_validate]] checks if the scalar is valid
+// for given curve.
+export fn privkey_buf(priv: *privkey) []u8 = priv.get_x(priv);
+
+// Checks whether 'priv' is a valid private key.
+export fn privkey_validate(priv: *privkey) (void | invalidkey) = {
+ match (ec::validate_scalar(priv.curve, priv.get_x(priv))) {
+ case void => void;
+ case ec::invalid =>
+ return invalidkey;
+ };
+};
+
+export type p256pubkey = struct {
+ pub: pubkey,
+ q: [ec::P256_POINTSZ]u8,
+};
+
+export type p384pubkey = struct {
+ pub: pubkey,
+ q: [ec::P384_POINTSZ]u8,
+};
+
+export type p521pubkey = struct {
+ pub: pubkey,
+ q: [ec::P521_POINTSZ]u8,
+};
+
+// Creates an unitialized p256 [[pubkey]]. The curve is also known as secp256r1
+// or prime256.
+export fn p256pub() p256pubkey = p256pubkey {
+ pub = pubkey {
+ curve = ec::p256,
+ get_q = &p256_get_q,
+ },
+ ...
+};
+
+export fn p384pub() p384pubkey = p384pubkey {
+ pub = pubkey {
+ curve = ec::p384,
+ get_q = &p384_get_q,
+ },
+ ...
+};
+
+export fn p521pub() p521pubkey = p521pubkey {
+ pub = pubkey {
+ curve = ec::p521,
+ get_q = &p521_get_q,
+ },
+ ...
+};
+
+// Initializes the pubkey 'pub' from the coordinates 'r' and 's' of the public
+// point 'q'.
+//
+// Does not validate if the point is on curve. [[verify]] will fail, if such is
+// the case.
+export fn pubkey_init(pub: *pubkey, x: []u8, y: []u8) (void | invalidkey) = {
+ const csz = pub.curve.pointsz / 2;
+ if (len(x) > csz || len(y) > csz) {
+ return invalidkey;
+ };
+
+ let q = pub.get_q(pub);
+ q[..] = [0x04, 0x00...];
+
+ const xoff = 1 + (csz - len(x));
+ const yoff = 1 + xoff + (csz - len(y));
+
+ q[xoff..xoff + len(x)] = x[..];
+ q[yoff..] = y[..];
+};
+
+// Derives the public key from given 'priv' and stores it into 'pub'.
+export fn pubkey_derive(pub: *pubkey, priv: *privkey) void = {
+ assert(pub.curve == priv.curve);
+ priv.curve.mulgen(pub.get_q(pub), priv.get_x(priv));
+};
+
+// Returns the buffer to the point stored in 'pub' to be able to store or read
+// the point in encoded form. See [[crypto::ec::curve]] for how the point is
+// and must be encoded.
+export fn pubkey_buf(pub: *pubkey) []u8 = pub.get_q(pub);
+
+// Validates if the pubkey is encoded properly. Does not check if the point is
+// on curve. [[verify]] will fail, if the point is not on the curve.
+export fn pubkey_validate_format(pub: *pubkey) (void | invalidkey) = {
+ match (ec::validate_pointformat(pub.curve, pub.get_q(pub))) {
+ case void => void;
+ case ec::invalid =>
+ return invalidkey;
+ };
+};
+
+// Validates the key of 'pub' and checks whether the point is on the curve.
+// This operation is expensive and is not strictly necessary, since this is
+// done during [[verify]] also.
+export fn pubkey_validate(pub: *pubkey) (void | invalidkey) = {
+ match (ec::validate_point(pub.curve, pub.get_q(pub))) {
+ case void => void;
+ case ec::invalid =>
+ return invalidkey;
+ };
+};