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