keys.ha (9068B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use bytes; 5 use crypto::bigint; 6 use crypto::math::*; 7 use endian; 8 use errors; 9 use io; 10 use memio; 11 use types; 12 13 // The default bit size of RSA keys is 4096-bit. Used as base for buffer sizes. 14 export def BITSZ: size = 4096; 15 16 // The minimum bit size of RSA keys used only for validation during key init. 17 // The default value is 1024-bit. 18 export def MINBITSZ: size = 1024; 19 20 // RSA key parameters for initializing public keys with [[pubkey_init]]. 21 export type pubparams = struct { 22 // Modulus in big-endian order 23 n: []u8, 24 25 // Public exponent in big-endian order 26 e: []u8, 27 }; 28 29 // RSA key parameters for initializing private keys with [[privkey_init]]. If 30 // the private exponent d is available, [[privkey_initd]] may be used, which 31 // derives 'dp' and 'dq'. All big integer values are in big-endian order. 32 export type privparams = struct { 33 // Bit length of the modulus n. If unknown, the modulus can be provided 34 // to the init function, which derivces the length. 35 nbitlen: size, 36 37 // First prime factor. 38 p: []u8, 39 40 // Second prime factor 41 q: []u8, 42 43 // First exponent. dp = d mod (p - 1) where d is the private exponent. 44 // May be omitted on [[privkey_initd]]. 45 dp: []u8, 46 47 // Second exponent. dq = d mod (q - 1) where d is the private exponent. 48 // May be omitted on [[privkey_initd]]. 49 dq: []u8, 50 51 // Coefficient. iq = q^-1 mod p. 52 iq: []u8, 53 }; 54 55 // Size required to store a public key of [[BITSZ]] length. 56 export def PUBKEYSZ: size = 5 + 2 * (BITSZ >> 3); 57 58 // Initializes a public key from given [[pubparams]] 'x'. The data format 59 // of 'pubkey' is subject to change and must not be used to serialize the key. 60 // [[PUBKEYSZ]] defines the required size to store a key of [[BITSZ]]. 61 // 62 // If given key does not fit into 'pubkey' or is too small, [[errors::overflow]] 63 // is returned. Returns [[errors::invalid]], if given key parameters are 64 // invalid. Returns the number of bytes written to 'pubkey' on success. 65 export fn pubkey_init(pubkey: []u8, x: pubparams) (size | error) = { 66 let e = ltrim(x.e); 67 let n = ltrim(x.n); 68 69 if (len(pubkey) < pubkey_len(n, e) || len(n) > types::U16_MAX 70 || len(e) > types::U16_MAX) { 71 return errors::overflow; 72 }; 73 74 // Very basic key checks that only catch obvious errors. 75 if ((len(e) == 1 && e[0] == 1) || len(e) > len(n)) { 76 return errors::invalid; 77 }; 78 if (bitlen(n) < MINBITSZ) { 79 return errors::invalid; 80 }; 81 82 let w = memio::fixed(pubkey); 83 84 let s = 0z; 85 s += writeslice(&w, e)!; 86 s += writeslice(&w, n)!; 87 return s; 88 }; 89 90 // Returns the length of the modulus 'n' of given public key. 91 export fn pubkey_nbitlen(pubkey: []u8) size = { 92 let p = pubkey_params(pubkey); 93 return bitlen(p.n); 94 }; 95 96 // Returns the length the public key would require in its encoded form. 97 fn pubkey_len(n: []u8, e: []u8) size = 1z + 2 + len(n) + 2 + len(e); 98 99 // Returns the slice without preceeding zeroes. 100 fn ltrim(s: []u8) []u8 = { 101 for (len(s) > 0 && s[0] == 0) { 102 s = s[1..]; 103 }; 104 return s; 105 }; 106 107 fn writeslice(dest: io::handle, a: []u8) (size | io::error) = { 108 let lenbuf: [2]u8 = [0...]; 109 endian::beputu16(lenbuf, len(a): u16); 110 let s = io::write(dest, lenbuf)?; 111 s += io::write(dest, a)?; 112 return s; 113 }; 114 115 // Counts the bits for given slice 'n'. 116 fn bitlen(s: []u8) size = { 117 let i = 0z; 118 for (i < len(s) && s[i] == 0; i += 1) void; 119 return countbits(s[i]) + 8 * (len(s) - i - 1); 120 }; 121 122 fn countbits(x: u8) size = { 123 let k: u32 = nequ32(x, 0); 124 let c: u32 = 0; 125 126 c = gtu32(x, 0x0f); 127 x = muxu32(c, x >> 4, x): u8; 128 k += c << 2; 129 130 c = gtu32(x, 0x03); 131 x = muxu32(c, x >> 2, x): u8; 132 k += c << 1; 133 134 k += gtu32(x, 0x01); 135 136 return k; 137 }; 138 139 @test fn countbits() void = { 140 assert(countbits(0xf0) == 8); 141 assert(countbits(0x70) == 7); 142 assert(countbits(0x30) == 6); 143 assert(countbits(0x10) == 5); 144 assert(countbits(0x08) == 4); 145 assert(countbits(0x04) == 3); 146 assert(countbits(0x03) == 2); 147 assert(countbits(0x01) == 1); 148 assert(countbits(0x00) == 0); 149 }; 150 151 // Returns the public key parameters, borrowed from given 'pubkey'. 152 export fn pubkey_params(pubkey: []u8) pubparams = { 153 let keybuf = pubkey; 154 return pubparams { 155 e = nextslice(&keybuf), 156 n = nextslice(&keybuf), 157 }; 158 }; 159 160 fn nextslice(key: *[]u8) []u8 = { 161 const l = endian::begetu16(key[..2]); 162 let s = key[2..2 + l]; 163 *key = key[2 + l..]; 164 return s; 165 }; 166 167 // Size required to store a private key of [[BITSZ]] length. 168 export def PRIVKEYSZ: size = 13 + (MAXFACTOR >> 3) * 5; 169 170 fn privkey_len(x: *privparams) size = 171 13z + len(x.p) + len(x.q) + len(x.dp) + len(x.dq) + len(x.iq); 172 173 // Initializes the private key 'privkey' using the values from 'x'. 'nbitlen' of 174 // 'x' may be omitted, if the modulus 'n' is passed. All other values of 'x' 175 // must be present. If 'x' is missing 'dp' and 'dq' use [[privkey_initd]]. 176 // 177 // In case of invalid parameters or if the key is too small, [[errors::invalid]] 178 // is returned. If the key does not fit 'privkey', [[errors::overflow]] is 179 // returned. On success the number of bytes written to 'privkey' is returned. 180 export fn privkey_init(privkey: []u8, x: privparams, n: []u8...) (size | error) = { 181 privkey_normalize(privkey, &x)?; 182 183 if (len(x.dp) == 0 || len(x.dq) == 0) { 184 return errors::invalid; 185 }; 186 187 let s = privkey_writehead(privkey, &x, n...)?; 188 let w = memio::fixed(privkey[s..]); 189 190 s += writeslice(&w, x.dp)!; 191 s += writeslice(&w, x.dq)!; 192 193 s += writeslice(&w, x.iq)!; 194 s += writeslice(&w, x.p)!; 195 s += writeslice(&w, x.q)!; 196 return s; 197 }; 198 199 // Trims key parameters and also does basic key checks. 200 fn privkey_normalize(privkey: []u8, x: *privparams) (void | error) = { 201 x.p = ltrim(x.p); 202 x.q = ltrim(x.q); 203 x.dp = ltrim(x.dp); 204 x.dq = ltrim(x.dq); 205 x.iq = ltrim(x.iq); 206 207 if (len(privkey) < privkey_len(x) 208 || len(x.p) > types::U16_MAX 209 || len(x.q) > types::U16_MAX 210 || len(x.dp) > types::U16_MAX 211 || len(x.dq) > types::U16_MAX 212 || len(x.iq) > types::U16_MAX) { 213 return errors::overflow; 214 }; 215 216 if (len(x.p) == 0 || len(x.q) == 0 || len(x.iq) == 0 217 || !isodd(x.p) || !isodd(x.q)) { 218 return errors::invalid; 219 }; 220 }; 221 222 fn isodd(x: []u8) bool = { 223 assert(len(x) > 0); 224 return x[len(x)-1] & 1 == 1; 225 }; 226 227 fn privkey_writehead( 228 privkey: []u8, 229 p: *privparams, 230 n: []u8... 231 ) (size | error) = { 232 assert(len(n) <= 1); 233 const nbitlen = if (len(n) == 1) bitlen(n[0]) else p.nbitlen; 234 if (nbitlen > types::U16_MAX) { 235 return errors::overflow; 236 }; 237 if (nbitlen < MINBITSZ) { 238 return errors::invalid; 239 }; 240 241 let w = memio::fixed(privkey); 242 let lenbuf: [2]u8 = [0...]; 243 endian::beputu16(lenbuf, nbitlen: u16); 244 return io::write(&w, lenbuf)!; 245 }; 246 247 // Initializes the private key 'privkey' using the values from 'x' and the 248 // secret exponent 'd'. 'dp' and 'dq' will be derived from 'p' and 'q' of 'x'. 249 // 'nbitlen' of 'x' may be omitted, if the modulus 'n' is passed. 'x' must 250 // provide 'iq'. 251 // 252 // In case of invalid parameters or if the key is too small, [[errors::invalid]] 253 // is returned. If the key does not fit 'privkey', [[errors::overflow]] is 254 // returned. On success the number of bytes written to 'privkey' is returend. 255 export fn privkey_initd( 256 privkey: []u8, 257 x: privparams, 258 d: []u8, 259 n: []u8... 260 ) (size | error) = { 261 privkey_normalize(privkey, &x)?; 262 263 let s = privkey_writehead(privkey, &x, n...)?; 264 265 // the order is important. The dmod operation uses the space for the 266 // remaining factors as buffer. 267 s += privkey_dmod(privkey[s..], d, x.p); 268 s += privkey_dmod(privkey[s..], d, x.q); 269 270 let w = memio::fixed(privkey[s..]); 271 s += writeslice(&w, x.iq)!; 272 s += writeslice(&w, x.p)!; 273 s += writeslice(&w, x.q)!; 274 275 // zero out tail in case the privkey_dmod operation left buffered values 276 bytes::zero(privkey[s..]); 277 return s; 278 }; 279 280 // Calculates 'x' = 'd' mod 'y' - 1 and stores 'x' into 'out' preceeding a 281 // u16 len. 'out' will also be used as a calculation buffer. 'y' must be odd. 282 fn privkey_dmod(out: []u8, d: []u8, y: []u8) size = { 283 const encwordlen = bigint::encodelen(y); 284 const enclen = encwordlen * size(bigint::word); 285 const xlen = len(y); 286 287 assert(len(out) >= 2 + xlen + 2 * enclen); 288 assert(isodd(y)); 289 290 let buf = out[2 + xlen..]; 291 // XXX: this may be only done once for both dp and dq 292 let by = (buf[..enclen]: *[*]bigint::word)[..encwordlen]; 293 bigint::encode(by, y); 294 bigint::decrodd(by); 295 296 let bx = (buf[enclen..2 * enclen]: *[*]bigint::word)[..encwordlen]; 297 bigint::encodereduce(bx, d, by); 298 299 out[0] = (xlen >> 8): u8; 300 out[1] = xlen: u8; 301 bigint::decode(out[2..2 + xlen], bx); 302 return 2 + xlen; 303 }; 304 305 // Returns the private key parameters borrowed from 'privkey'. 306 export fn privkey_params(privkey: []u8) privparams = { 307 let keybuf = privkey[2..]; 308 return privparams { 309 nbitlen = privkey_nbitlen(privkey), 310 dp = nextslice(&keybuf), 311 dq = nextslice(&keybuf), 312 iq = nextslice(&keybuf), 313 p = nextslice(&keybuf), 314 q = nextslice(&keybuf), 315 ... 316 }; 317 }; 318 319 // Returns the length of the modulus 'n'. 320 export fn privkey_nbitlen(privkey: []u8) size = { 321 return endian::begetu16(privkey[0..2]); 322 }; 323 324 // Returns the number of bytes that are required to store a value modulo 'n'. 325 export fn privkey_nsize(privkey: []u8) size = { 326 return (privkey_nbitlen(privkey) + 7) / 8; 327 };