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