ecdh.ha (2876B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bytes; 5 use crypto::ec; 6 use io; 7 8 9 // Size of the shared secret in bytes when using p256 curves. 10 export def P256_SHAREDSZ = ec::P256_POINTSZ / 2; 11 12 // Size of the shared secret in bytes when using p384 curves. 13 export def P384_SHAREDSZ = ec::P384_POINTSZ / 2; 14 15 // Size of the shared secret in bytes when using p521 curves. 16 export def P521_SHAREDSZ = ec::P521_POINTSZ / 2; 17 18 // Key is either not of expected size or is not a valid point on given curve. 19 export type invalidkey = !void; 20 21 export type privkey = struct { 22 curve: *ec::curve, 23 get_x: *fn (priv: *privkey) []u8, 24 }; 25 26 fn p256_get_x(priv: *privkey) []u8 = (priv: *p256key).x; 27 fn p384_get_x(priv: *privkey) []u8 = (priv: *p384key).x; 28 fn p521_get_x(priv: *privkey) []u8 = (priv: *p521key).x; 29 30 export type p256key = struct { 31 priv: privkey, 32 x: [ec::P256_SCALARSZ]u8, 33 }; 34 35 export type p384key = struct { 36 priv: privkey, 37 x: [ec::P384_SCALARSZ]u8, 38 }; 39 40 export type p521key = struct { 41 priv: privkey, 42 x: [ec::P521_SCALARSZ]u8, 43 }; 44 45 // Creates an unitialized p256 key. The curve is also known as secp256r1 or 46 // prime256. The key must be initialized using [[newkey]]. 47 export fn p256() p256key = p256key { 48 priv = privkey { 49 curve = ec::p256, 50 get_x = &p256_get_x, 51 }, 52 ... 53 }; 54 55 // Creates an unitialized p384 key. The curve is also known as secp384r1. The 56 // key must be initialized using [[newkey]]. 57 export fn p384() p384key = p384key { 58 priv = privkey { 59 curve = ec::p384, 60 get_x = &p384_get_x, 61 }, 62 ... 63 }; 64 65 // Creates an unitialized p521 key. The curve is also known as secp521r1. The 66 // key must be initialized using [[newkey]]. 67 export fn p521() p521key = p521key { 68 priv = privkey { 69 curve = ec::p521, 70 get_x = &p521_get_x, 71 }, 72 ... 73 }; 74 75 // Generates a key seeding from the 'rand' stream and stores it in 'priv'. 76 // 'rand' must be a cryptographic random generator like 77 // [[crypto::random::stream]]. 78 export fn newkey(priv: *privkey, rand: io::handle) (void | io::error) = { 79 ec::keygen(priv.curve, priv.get_x(priv), rand)?; 80 }; 81 82 // Derives the public key from given 'priv' and stores it into 'pub'. Returns 83 // the number of key bytes written to 'pub'. 84 export fn pubkey(pub: []u8, priv: *privkey) size = 85 priv.curve.mulgen(pub, priv.get_x(priv)); 86 87 // Derives a shared secret with the private key 'priv' and the peer's public 88 // key 'pub' and stores it in 'shared'. 89 export fn derive( 90 shared: []u8, 91 priv: *privkey, 92 pub: []u8 93 ) (size | invalidkey) = { 94 match (ec::validate_pointformat(priv.curve, pub)) { 95 case ec::invalid => 96 return invalidkey; 97 case void => void; 98 }; 99 100 let buf: [ec::MAX_POINTSZ]u8 = [0...]; 101 let buf = buf[..len(pub)]; 102 buf[..] = pub[..]; 103 104 if (priv.curve.mul(buf, priv.get_x(priv)) == 0) { 105 return invalidkey; 106 }; 107 108 const csz = priv.curve.pointsz / 2; 109 shared[..] = buf[1..csz + 1]; 110 bytes::zero(buf); 111 return csz; 112 };