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