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