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 };