// SPDX-License-Identifier: MPL-2.0
// (c) Hare authors <https://harelang.org>

use io;

// Maxium coordinate size of the modules curves in bits.
export def MAX_COORDBITSZ = 528z;

// Maximum size of a point of the modules curves in bytes.
export def MAX_POINTSZ = P521_POINTSZ;

// Maximum size of a scalar of the modules curves in bytes.
export def MAX_SCALARSZ = P521_SCALARSZ;

// Interface for common operations over a specific curve.
//
// The encoding of points (e.g. public keys for dsa and dh) depends on the curve.
// For the NIST curves ([[p256]], [[p384]] and [[p521]]) the point is required
// to be stored according to the uncompressed format defined in RFC 8422 Chapter
// 5.4.1. That means with a leading byte of value 0x04 that indicates the
// format. Followed by the x and y coordinates, which must be of length
// [[pointsz]] / 2, left padded by 0.
//
// Scalar values (e.g. private keys for dsa and dh) must be provided in
// big-endian encoding and left-padded to fill the indicated space. They MUST be
// non-zero and less than the curve order, otherwise result values will be
// indeterminate and an error code is not guaranteed. The function [[scalarsz]]
// will return the encoded scalar size of any curve implemented by this module.
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 1 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 1 on success.
	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),
};

// Returns the encoded size of any point of curve 'c'.
export fn pointsz(c: *curve) size = c.pointsz;

// Returns the encoded size of any scalar of curve 'c'.
export fn scalarsz(c: *curve) size = len(c.order());

// Invalid curve parameter.
export type invalid = !void;
