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