hare

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

commit dd80eb36907f0c8f5d01145cf240f49fdd56f5e9
parent 0ac2780a5a11f54461a4fba5c460582f2e1c555d
Author: Armin Preiml <apreiml@strohwolke.at>
Date:   Thu,  7 Mar 2024 14:50:04 +0100

add crypto::ec

Add the ec interface adopted from BearSSL

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

Diffstat:
Acrypto/ec/README | 10++++++++++
Acrypto/ec/keygen.ha | 44++++++++++++++++++++++++++++++++++++++++++++
Acrypto/ec/types.ha | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/ec/validate.ha | 43+++++++++++++++++++++++++++++++++++++++++++
4 files changed, 161 insertions(+), 0 deletions(-)

diff --git a/crypto/ec/README b/crypto/ec/README @@ -0,0 +1,10 @@ +The crypto::ec module provides implementations for a selection of eliptic +curves and their operations. + +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/ec/keygen.ha b/crypto/ec/keygen.ha @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use io; + + +// Generates a random private key scalar suitable for given curve 'c'. +// 'rand' must be cryptographic random stream like the one provided by +// [[crypto::random::stream]]. +export fn keygen(c: *curve, priv: []u8, rand: io::handle) (size | io::error) = + c.keygen(c, priv, rand); + +// A keygen that generates random keys until one is found that fits within +// the order of curve 'c'. +fn mask_keygen( + c: *curve, + priv: []u8, + rand: io::handle +) (size | io::error) = { + const order = c.order(); + assert(len(priv) == len(order)); + assert(order[0] != 0); + + // mask all bits until including the highest value one. + let mask = order[0]; + mask |= (mask >> 1); + mask |= (mask >> 2); + mask |= (mask >> 4); + + for (true) { + match (io::readall(rand, priv)?) { + case let s: size => + assert(s == len(priv)); + case io::EOF => + return (0: io::underread): io::error; + }; + priv[0] &= mask; + + if (validate_scalar(c, priv) is void) { + return len(priv); + }; + }; +}; + diff --git a/crypto/ec/types.ha b/crypto/ec/types.ha @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +use io; + + +// Interface for common operations over a specific curve. +// +// The encoding of points depends on the curve. For the NIST curves +// ([[p256]], [[p384]] and [[p521]] the point is required to be +// uncompressed with a leading byte of value 0x04. The coordinates must be of +// length 'pointsz' / 2, left padded by 0x0. +// +// Scalar values must be provided in big-endian encoding. They MUST be non zero +// and less than the order, otherwise result values will be indeterminate and +// an error code is not guaranteed. +export type curve = struct { + // Size in bytes of an encoded point. + pointsz: size, + + // Returns the order of the subgroup generated by the conventional + // generator. Unsigned big-endian encoding is used. + order: *fn () const []u8, // XXX: change to const []u8, when possible + + // Get the conventional generator as an encoded curve point. + generator: *fn () const []u8, // XXX: change to const []u8, when possible + + // Multiply curve point 'p' by scalar 'x'. The result is stored in 'r'. + // Returns a value > 0 on success. + // + // Point 'p' must be a valid point on the curve subgroup. If this is + // not the case the function fails with 0 as result. + // + // On error the results in 'p' are indeterminate. + mul: *fn (p: []u8, x: []u8) u32, + + // Multiply the generator by the scalar 'x' and write the result to 'r'. + // + // Returns the encoded point length in bytes. + mulgen: *fn (r: []u8, x: []u8) size, + + // Multiply two curve points ('a' and 'b') by two integers ('x' and 'y') + // and stores the sum in 'a' ('a' = 'a' * 'x' + 'b' * 'y'). + // + // If an empty slice is given as 'b', the curve generator is used + // instead of 'b'. + // + // Returns 0 in case of failure. Validates that the provided points are + // part of the relevant curve subgroup. + // + // Returns a value > 0 on success and 0 otherwise. + muladd: *fn (a: []u8, b: []u8, x: []u8, y: []u8) u32, + + // Generate a private key from given random seed 'rand'. The function + // may read repeatedly from 'rand' until a suitable key is found. + // + // Returns the size of bytes read into 'priv' on success or + // [[io::error]], if reading from 'rand' failed. + keygen: *fn (c: *curve, priv: []u8, rand: io::handle) (size | io::error), +}; + +// Invalid curve parameter. +export type invalid = !void; + diff --git a/crypto/ec/validate.ha b/crypto/ec/validate.ha @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MPL-2.0 +// (c) Hare authors <https://harelang.org> + +// Checks whether the point is encoded in the curves point format. Does NOT +// check if it is a valid point on the curve. For such point validation use +// [[validate_point]]. +export fn validate_pointformat(c: *curve, p: []u8) (void | invalid) = { + if (len(p) != c.pointsz || p[0] != 0x04) { + return invalid; + }; +}; + +// Checks if given point is properly encoded and a valid point on given curve +// 'c'. This operation is quite expensive. Note that in any case point +// validation will be done on every mul and muladd operation. +export fn validate_point(c: *curve, p: []u8) (void | invalid) = { + validate_pointformat(c, p)?; + + static let scalarbuf: [133]u8 = [0...]; + let scalarbuf = scalarbuf[..len(c.order())]; + scalarbuf[len(scalarbuf) - 1] = 1; + + if (c.mul(p, scalarbuf) == 0) { + return invalid; + }; +}; + +// Validates if given scalar is less than the curve order and greater then zero. +export fn validate_scalar(c: *curve, n: []u8) (void | invalid) = { + const order = c.order(); + let cc: u16 = 0; + let zz: u8 = 0; + for (let i = len(n); i > 0; i -= 1) { + // subtraction with carry + cc = ((n[i - 1]: u16 - order[i - 1] - cc) >> 8) & 1; + zz |= n[i - 1]; + }; + + // cc == 0 means the carry is not set because order < priv + if (cc == 0 || zz == 0) { + return invalid; + }; +};