203 lines
3.6 KiB
C
203 lines
3.6 KiB
C
#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;
|
|
}
|
|
|