hare

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

pkcs1.ha (4281B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use crypto::math;
      5 use crypto::sha1;
      6 use crypto::sha256;
      7 use crypto::sha512;
      8 use errors;
      9 
     10 // Supported hash algorithms for [[pkcs1_sign]] and [[pkcs1_verify]].
     11 export type pkcs1_hashalgo = enum {
     12 	SHA1,
     13 	// SHA224, We don't have this one yet
     14 	SHA256,
     15 	SHA384,
     16 	SHA512,
     17 	SHA512_224,
     18 	SHA512_256,
     19 };
     20 
     21 const OID_SHA1: [_]u8 = [
     22 	0x30,  0x21,  0x30,  0x09,  0x06,  0x05,  0x2b,  0x0e,  0x03,  0x02,
     23 	0x1a,  0x05,  0x00,  0x04,  0x14,
     24 ];
     25 
     26 const OID_SHA224: [_]u8 = [
     27 	0x30,  0x2d,  0x30,  0x0d,  0x06,  0x09,  0x60,  0x86,  0x48,  0x01,
     28 	0x65,  0x03,  0x04,  0x02,  0x04,  0x05,  0x00,  0x04,  0x1c,
     29 ];
     30 
     31 const OID_SHA256: [_]u8 = [
     32 	0x30,  0x31,  0x30,  0x0d,  0x06,  0x09,  0x60,  0x86,  0x48,  0x01,
     33 	0x65,  0x03,  0x04,  0x02,  0x01,  0x05,  0x00,  0x04,  0x20,
     34 ];
     35 
     36 const OID_SHA384: [_]u8 = [
     37 	0x30,  0x41,  0x30,  0x0d,  0x06,  0x09,  0x60,  0x86,  0x48,  0x01,
     38 	0x65,  0x03,  0x04,  0x02,  0x02,  0x05,  0x00,  0x04,  0x30,
     39 ];
     40 
     41 const OID_SHA512: [_]u8 = [
     42 	0x30,  0x51,  0x30,  0x0d,  0x06,  0x09,  0x60,  0x86,  0x48,  0x01,
     43 	0x65,  0x03,  0x04,  0x02,  0x03,  0x05,  0x00,  0x04,  0x40,
     44 ];
     45 
     46 const OID_SHA512_224: [_]u8 = [
     47 	0x30,  0x2d,  0x30,  0x0d,  0x06,  0x09,  0x60,  0x86,  0x48,  0x01,
     48 	0x65,  0x03,  0x04,  0x02,  0x05,  0x05,  0x00,  0x04,  0x1c,
     49 ];
     50 
     51 const OID_SHA512_256: [_]u8 = [
     52 	0x30,  0x31,  0x30,  0x0d,  0x06,  0x09,  0x60,  0x86,  0x48,  0x01,
     53 	0x65,  0x03,  0x04,  0x02,  0x06,  0x05,  0x00,  0x04,  0x20,
     54 ];
     55 
     56 // Required buffer size for [[pkcs1_verify]].
     57 export def PKCS1_VERIFYBUFSZ: size = PUBEXP_BUFSZ + (BITSZ / 8);
     58 
     59 // Verifies a PKCS#1 v1.5 signature given a public key 'pubkey', the message
     60 // hash 'msghash', the signature 'sig' and the hash algorithm 'algo'. 'algo'
     61 // must reflect the hash algorithm 'sig' was created with.
     62 //
     63 // A temporary buffer 'buf' of size [[PKCS1_VERIFYBUFSZ]] must be provided.
     64 export fn pkcs1_verify(
     65 	pubkey: []u8,
     66 	msghash: []u8,
     67 	sig: []u8,
     68 	algo: pkcs1_hashalgo,
     69 	buf: []u8
     70 ) (void | error) = {
     71 	let pub = pubkey_params(pubkey);
     72 
     73 	let actualsig = buf[..len(sig)];
     74 	let pubbuf = buf[len(sig)..];
     75 
     76 	actualsig[..] = sig[..];
     77 	match (pubexp(&pub, actualsig, pubbuf)) {
     78 	case let e: error =>
     79 		return e;
     80 	case void => void;
     81 	};
     82 
     83 	let expectedsig = pubbuf[..len(sig)];
     84 	pkcs1_sig_encode(expectedsig, msghash, algo)?;
     85 
     86 	if (math::eqslice(expectedsig, actualsig) == 0) {
     87 		return badsig;
     88 	};
     89 };
     90 
     91 // Required buffer size for [[pkcs1_sign]].
     92 export def PKCS1_SIGNBUFSZ: size = PRIVEXP_BUFSZ;
     93 
     94 // Signs a message hash 'msghash' using the PKCS#1 V1.5 signature scheme. The
     95 // signature will be written to 'sig' which must be in the the size of the
     96 // modulus n (see [[privkey_nsize]]). 'algo' defines the hash algorithm
     97 // 'msghash' was created with.
     98 //
     99 // A temporary buffer 'buf' of size [[PKCS1_SIGNBUFSZ]]  must be provided.
    100 export fn pkcs1_sign(
    101 	priv: []u8,
    102 	msghash: []u8,
    103 	sig: []u8,
    104 	algo: pkcs1_hashalgo,
    105 	buf: []u8
    106 ) (void | error) = {
    107 	let priv = privkey_params(priv);
    108 	pkcs1_sig_encode(sig, msghash, algo)?;
    109 	privexp(&priv, sig, buf)?;
    110 };
    111 
    112 // Returns hash id and hash size for given 'algo'.
    113 fn pkcs1_hashinfo(algo: pkcs1_hashalgo) (const []u8, size) = {
    114 	switch (algo) {
    115 	case pkcs1_hashalgo::SHA1 =>
    116 		return (OID_SHA1, sha1::SZ);
    117 	case pkcs1_hashalgo::SHA256 =>
    118 		return (OID_SHA256, sha256::SZ);
    119 	case pkcs1_hashalgo::SHA384 =>
    120 		return (OID_SHA384, sha512::SZ384);
    121 	case pkcs1_hashalgo::SHA512 =>
    122 		return (OID_SHA512, sha512::SZ);
    123 	case pkcs1_hashalgo::SHA512_224 =>
    124 		return (OID_SHA512_224, sha512::SZ224);
    125 	case pkcs1_hashalgo::SHA512_256 =>
    126 		return (OID_SHA512_256, sha512::SZ256);
    127 	case =>
    128 		abort("unreachable");
    129 	};
    130 };
    131 
    132 // Creates an unauthenticated signature of 'msg' and writes it into 'sig' using
    133 // given hash algorithm 'algo'.
    134 fn pkcs1_sig_encode(
    135 	sig: []u8,
    136 	msghash: []u8,
    137 	algo: pkcs1_hashalgo
    138 ) (void | error) = {
    139 	let (hid, hsz) = pkcs1_hashinfo(algo);
    140 	if (len(msghash) != hsz) {
    141 		return errors::invalid;
    142 	};
    143 
    144 	let tlen = len(hid) + hsz;
    145 	if (len(sig) < tlen + 11) {
    146 		return badsig;
    147 	};
    148 
    149 	const hsep = len(sig) - tlen - 1;
    150 
    151 	sig[..2] = [0x00, 0x01];
    152 	for (let i = 2z; i < hsep; i += 1) {
    153 		sig[i] = 0xff;
    154 	};
    155 	sig[hsep] = 0x00;
    156 	sig[hsep + 1..len(sig) - hsz] = hid[..];
    157 	sig[len(sig) - hsz..] = msghash[..];
    158 };