| #include "std.h" |
| #include "dat.h" |
| |
| /* |
| * PKCS #1 v2.0 signatures (aka RSASSA-PKCS1-V1_5) |
| * |
| * You don't want to read the spec. |
| * Here is what you need to know. |
| * |
| * RSA sign (aka RSASP1) is just an RSA encryption. |
| * RSA verify (aka RSAVP1) is just an RSA decryption. |
| * |
| * We sign hashes of messages instead of the messages |
| * themselves. |
| * |
| * The hashes are encoded in ASN.1 DER to identify |
| * the signature type, and then prefixed with 0x01 PAD 0x00 |
| * where PAD is as many 0xFF bytes as desired. |
| */ |
| |
| static int mkasn1(uchar *asn1, DigestAlg *alg, uchar *d, uint dlen); |
| |
| int |
| rsasign(RSApriv *key, DigestAlg *hash, uchar *digest, uint dlen, |
| uchar *sig, uint siglen) |
| { |
| uchar asn1[64], *buf; |
| int n, len, pad; |
| mpint *m, *s; |
| |
| /* |
| * Create ASN.1 |
| */ |
| n = mkasn1(asn1, hash, digest, dlen); |
| |
| /* |
| * Create number to sign. |
| */ |
| len = (mpsignif(key->pub.n)+7)/8 - 1; |
| if(len < n+2){ |
| werrstr("rsa key too short"); |
| return -1; |
| } |
| pad = len - (n+2); |
| if(siglen < len){ |
| werrstr("signature buffer too short"); |
| return -1; |
| } |
| buf = malloc(len); |
| if(buf == nil) |
| return -1; |
| buf[0] = 0x01; |
| memset(buf+1, 0xFF, pad); |
| buf[1+pad] = 0x00; |
| memmove(buf+1+pad+1, asn1, n); |
| m = betomp(buf, len, nil); |
| free(buf); |
| if(m == nil) |
| return -1; |
| |
| /* |
| * Sign it. |
| */ |
| s = rsadecrypt(key, m, nil); |
| mpfree(m); |
| if(s == nil) |
| return -1; |
| mptoberjust(s, sig, len+1); |
| mpfree(s); |
| return len+1; |
| } |
| |
| int |
| rsaverify(RSApub *key, DigestAlg *hash, uchar *digest, uint dlen, |
| uchar *sig, uint siglen) |
| { |
| uchar asn1[64], xasn1[64]; |
| int n, nn; |
| mpint *m, *s; |
| |
| /* |
| * Create ASN.1 |
| */ |
| n = mkasn1(asn1, hash, digest, dlen); |
| |
| /* |
| * Extract plaintext of signature. |
| */ |
| s = betomp(sig, siglen, nil); |
| if(s == nil) |
| return -1; |
| m = rsaencrypt(key, s, nil); |
| mpfree(s); |
| if(m == nil) |
| return -1; |
| nn = mptobe(m, xasn1, sizeof xasn1, nil); |
| mpfree(m); |
| if(n != nn || memcmp(asn1, xasn1, n) != 0){ |
| werrstr("signature did not verify"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* |
| * Mptobe but shift right to fill buffer. |
| */ |
| void |
| mptoberjust(mpint *b, uchar *buf, uint len) |
| { |
| int n; |
| |
| n = mptobe(b, buf, len, nil); |
| assert(n >= 0); |
| if(n < len){ |
| len -= n; |
| memmove(buf+len, buf, n); |
| memset(buf, 0, len); |
| } |
| } |
| |
| /* |
| * Simple ASN.1 encodings. |
| * Lengths < 128 are encoded as 1-bytes constants, |
| * making our life easy. |
| */ |
| |
| /* |
| * Hash OIDs |
| * |
| * SHA1 = 1.3.14.3.2.26 |
| * MDx = 1.2.840.113549.2.x |
| */ |
| #define O0(a,b) ((a)*40+(b)) |
| #define O2(x) \ |
| (((x)>>7)&0x7F)|0x80, \ |
| ((x)&0x7F) |
| #define O3(x) \ |
| (((x)>>14)&0x7F)|0x80, \ |
| (((x)>>7)&0x7F)|0x80, \ |
| ((x)&0x7F) |
| uchar oidsha1[] = { O0(1, 3), 14, 3, 2, 26 }; |
| uchar oidmd2[] = { O0(1, 2), O2(840), O3(113549), 2, 2 }; |
| uchar oidmd5[] = { O0(1, 2), O2(840), O3(113549), 2, 5 }; |
| |
| /* |
| * DigestInfo ::= SEQUENCE { |
| * digestAlgorithm AlgorithmIdentifier, |
| * digest OCTET STRING |
| * } |
| * |
| * except that OpenSSL seems to sign |
| * |
| * DigestInfo ::= SEQUENCE { |
| * SEQUENCE{ digestAlgorithm AlgorithmIdentifier, NULL } |
| * digest OCTET STRING |
| * } |
| * |
| * instead. Sigh. |
| */ |
| static int |
| mkasn1(uchar *asn1, DigestAlg *alg, uchar *d, uint dlen) |
| { |
| uchar *obj, *p; |
| uint olen; |
| |
| if(alg == sha1){ |
| obj = oidsha1; |
| olen = sizeof(oidsha1); |
| }else if(alg == md5){ |
| obj = oidmd5; |
| olen = sizeof(oidmd5); |
| }else{ |
| sysfatal("bad alg in mkasn1"); |
| return -1; |
| } |
| |
| p = asn1; |
| *p++ = 0x30; /* sequence */ |
| p++; |
| |
| *p++ = 0x30; /* another sequence */ |
| p++; |
| |
| *p++ = 0x06; /* object id */ |
| *p++ = olen; |
| memmove(p, obj, olen); |
| p += olen; |
| |
| *p++ = 0x05; /* null */ |
| *p++ = 0; |
| |
| asn1[3] = p - (asn1+4); /* end of inner sequence */ |
| |
| *p++ = 0x04; /* octet string */ |
| *p++ = dlen; |
| memmove(p, d, dlen); |
| p += dlen; |
| |
| asn1[1] = p - (asn1+2); /* end of outer sequence */ |
| return p-asn1; |
| } |
| |