hare

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

ed25519.ha (3639B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 // Implements the Ed25519 signature scheme.
      5 //
      6 // This implementation is a straightforward port of TweetNaCl,
      7 // with the API of crypto/ed25519 from the Go standard library.
      8 use bytes;
      9 use crypto::sha512;
     10 use hash;
     11 
     12 // The size of an Ed25519 seed.
     13 export def SEEDSZ: size = 32;
     14 
     15 // The size of an Ed25519 public key.
     16 export def PUBKEYSZ: size = 32;
     17 
     18 // The size of an Ed25519 private key.
     19 export def PRIVKEYSZ: size = 64;
     20 
     21 // The size of an Ed25519 signature.
     22 export def SIGNATURESZ: size = 64;
     23 
     24 export type privkey = [PRIVKEYSZ]u8;
     25 export type pubkey = [PUBKEYSZ]u8;
     26 export type seed = [SEEDSZ]u8;
     27 
     28 // Derives a new Ed25519 private key from a given seed. The seed must be
     29 // initialized to cryptographically random data; [[crypto::random::]] is
     30 // recommended for this purpose.
     31 export fn privkey_init(priv: []u8, seed: []u8) void = {
     32 	assert(len(priv) == PRIVKEYSZ);
     33 	assert(len(seed) == SEEDSZ);
     34 
     35 	let h: [64]u8 = [0...];
     36 	let sha = sha512::sha512();
     37 	hash::write(&sha, seed[..]);
     38 	hash::sum(&sha, h[..]);
     39 	hash::close(&sha);
     40 
     41 	let s: scalar = [0...];
     42 	s[..] = h[..SCALARSZ];
     43 	scalar_clamp(&s);
     44 
     45 	let A = point { ... };
     46 	scalarmult_base(&A, &s);
     47 	let A_bytes: [POINTSZ]u8 = [0...];
     48 	point_encode(&A_bytes, &A);
     49 
     50 	priv[0..SEEDSZ] = seed[..];
     51 	priv[SEEDSZ..PRIVKEYSZ] = A_bytes[..];
     52 };
     53 
     54 // Derive the public key for a given private key. '
     55 export fn privkey_getpubkey(priv: []u8) pubkey = {
     56 	assert(len(priv) == PRIVKEYSZ);
     57 	let pk: pubkey = [0...];
     58 	pk[0..] = priv[SEEDSZ..];
     59 	return pk;
     60 };
     61 
     62 // Signs a message with a private key, returning the signature.
     63 export fn sign(priv: []u8, msg: []u8) [SIGNATURESZ]u8 = {
     64 	assert(len(priv) == PRIVKEYSZ);
     65 
     66 	let h: [64]u8 = [0...];
     67 	let sha = sha512::sha512();
     68 	hash::write(&sha, priv[0..SEEDSZ]);
     69 	hash::sum(&sha, h);
     70 	let esk: scalar = [0...];
     71 	esk[..] = h[0..32];
     72 	scalar_clamp(&esk);
     73 
     74 	hash::reset(&sha);
     75 	hash::write(&sha, h[32..64]);
     76 	hash::write(&sha, msg);
     77 	let msg_digest: [64]u8 = [0...];
     78 	hash::sum(&sha, msg_digest);
     79 	let msg_reduced: scalar = [0...];
     80 	scalar_reduce(&msg_reduced, &msg_digest);
     81 
     82 	let R = point {...};
     83 	scalarmult_base(&R, &msg_reduced);
     84 	let R_bytes: [POINTSZ]u8 = [0...];
     85 	point_encode(&R_bytes, &R);
     86 
     87 	hash::reset(&sha);
     88 	hash::write(&sha, R_bytes[..]);
     89 	hash::write(&sha, priv[32..64]);
     90 	hash::write(&sha, msg);
     91 	let hram: [64]u8 = [0...];
     92 	hash::sum(&sha, hram);
     93 	hash::close(&sha);
     94 	let hram_reduced: scalar = [0...];
     95 	scalar_reduce(&hram_reduced, &hram);
     96 
     97 	let s: scalar = [0...];
     98 	scalar_multiply_add(&s, &hram_reduced, &esk, &msg_reduced);
     99 
    100 	let sig: [SIGNATURESZ]u8 =[0...];
    101 	sig[0..32] = R_bytes[..];
    102 	sig[32..64] = s[..];
    103 	return sig;
    104 };
    105 
    106 // Given a public key, verifies a signature produced with the
    107 // corresponding private key for a given message, returning true if the
    108 // signature is valid and false otherwise.
    109 export fn verify(pub: []u8, msg: []u8, sig: []u8) bool = {
    110 	assert(len(pub) == PUBKEYSZ);
    111 	assert(len(sig) == SIGNATURESZ);
    112 
    113 	let A = point { ... };
    114 	if (!point_decode(&A, pub)) {
    115 		return false;
    116 	};
    117 
    118 	let sha = sha512::sha512();
    119 	hash::write(&sha, sig[0..32]);
    120 	hash::write(&sha, pub[..]);
    121 	hash::write(&sha, msg);
    122 	let hram: [64]u8 = [0...];
    123 	hash::sum(&sha, hram);
    124 	hash::close(&sha);
    125 
    126 	let hram_reduced: scalar = [0...];
    127 	scalar_reduce(&hram_reduced, &hram);
    128 	let check_R = point { ... };
    129 	scalarmult(&check_R, &A, &hram_reduced);
    130 
    131 	let s: scalar = [0...];
    132 	s[..] = sig[32..64];
    133 	scalarmult_base(&A, &s);
    134 	point_add(&check_R, &check_R, &A);
    135 	let R_bytes: [POINTSZ]u8 = [0...];
    136 	point_encode(&R_bytes, &check_R);
    137 	return bytes::equal(R_bytes, sig[0..32]);
    138 };