blob: f3278454cdf0fef9a405da7cd942f2b37df9238e [file] [log] [blame]
#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;
}