123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- /*
- * RSA authentication.
- *
- * Old ssh client protocol:
- * read public key
- * if you don't like it, read another, repeat
- * write challenge
- * read response
- *
- * all numbers are hexadecimal biginits parsable with strtomp.
- *
- * Sign (PKCS #1 using hash=sha1 or hash=md5)
- * write hash(msg)
- * read signature(hash(msg))
- *
- * Verify:
- * write hash(msg)
- * write signature(hash(msg))
- * read ok or fail
- */
- #include "dat.h"
- enum {
- CHavePub,
- CHaveResp,
- VNeedHash,
- VNeedSig,
- VHaveResp,
- SNeedHash,
- SHaveResp,
- Maxphase,
- };
- static char *phasenames[] = {
- [CHavePub] "CHavePub",
- [CHaveResp] "CHaveResp",
- [VNeedHash] "VNeedHash",
- [VNeedSig] "VNeedSig",
- [VHaveResp] "VHaveResp",
- [SNeedHash] "SNeedHash",
- [SHaveResp] "SHaveResp",
- };
- struct State
- {
- RSApriv *priv;
- mpint *resp;
- int off;
- Key *key;
- mpint *digest;
- int sigresp;
- };
- static mpint* mkdigest(RSApub *key, char *hashalg, uint8_t *hash,
- uint dlen);
- static RSApriv*
- readrsapriv(Key *k)
- {
- char *a;
- RSApriv *priv;
- priv = rsaprivalloc();
- if((a=_strfindattr(k->attr, "ek"))==nil || (priv->pub.ek=strtomp(a, nil, 16, nil))==nil)
- goto Error;
- if((a=_strfindattr(k->attr, "n"))==nil || (priv->pub.n=strtomp(a, nil, 16, nil))==nil)
- goto Error;
- if(k->privattr == nil) /* only public half */
- return priv;
- if((a=_strfindattr(k->privattr, "!p"))==nil || (priv->p=strtomp(a, nil, 16, nil))==nil)
- goto Error;
- if((a=_strfindattr(k->privattr, "!q"))==nil || (priv->q=strtomp(a, nil, 16, nil))==nil)
- goto Error;
- if((a=_strfindattr(k->privattr, "!kp"))==nil || (priv->kp=strtomp(a, nil, 16, nil))==nil)
- goto Error;
- if((a=_strfindattr(k->privattr, "!kq"))==nil || (priv->kq=strtomp(a, nil, 16, nil))==nil)
- goto Error;
- if((a=_strfindattr(k->privattr, "!c2"))==nil || (priv->c2=strtomp(a, nil, 16, nil))==nil)
- goto Error;
- if((a=_strfindattr(k->privattr, "!dk"))==nil || (priv->dk=strtomp(a, nil, 16, nil))==nil)
- goto Error;
- return priv;
- Error:
- rsaprivfree(priv);
- return nil;
- }
- static int
- rsainit(Proto* p, Fsstate *fss)
- {
- Keyinfo ki;
- State *s;
- char *role;
- if((role = _strfindattr(fss->attr, "role")) == nil)
- return failure(fss, "rsa role not specified");
- if(strcmp(role, "client") == 0)
- fss->phase = CHavePub;
- else if(strcmp(role, "sign") == 0)
- fss->phase = SNeedHash;
- else if(strcmp(role, "verify") == 0)
- fss->phase = VNeedHash;
- else
- return failure(fss, "rsa role %s unimplemented", role);
- s = emalloc(sizeof *s);
- fss->phasename = phasenames;
- fss->maxphase = Maxphase;
- fss->ps = s;
- switch(fss->phase){
- case SNeedHash:
- case VNeedHash:
- mkkeyinfo(&ki, fss, nil);
- if(findkey(&s->key, &ki, nil) != RpcOk)
- return failure(fss, nil);
- /* signing needs private key */
- if(fss->phase == SNeedHash && s->key->privattr == nil)
- return failure(fss,
- "missing private half of key -- cannot sign");
- }
- return RpcOk;
- }
- static int
- rsaread(Fsstate *fss, void *va, uint *n)
- {
- RSApriv *priv;
- State *s;
- mpint *m;
- Keyinfo ki;
- int len, r;
- s = fss->ps;
- switch(fss->phase){
- default:
- return phaseerror(fss, "read");
- case CHavePub:
- if(s->key){
- closekey(s->key);
- s->key = nil;
- }
- mkkeyinfo(&ki, fss, nil);
- ki.skip = s->off;
- ki.noconf = 1;
- if(findkey(&s->key, &ki, nil) != RpcOk)
- return failure(fss, nil);
- s->off++;
- priv = s->key->priv;
- *n = snprint(va, *n, "%B", priv->pub.n);
- return RpcOk;
- case CHaveResp:
- *n = snprint(va, *n, "%B", s->resp);
- fss->phase = Established;
- return RpcOk;
- case SHaveResp:
- priv = s->key->priv;
- len = (mpsignif(priv->pub.n)+7)/8;
- if(len > *n)
- return failure(fss, "signature buffer too short");
- m = rsadecrypt(priv, s->digest, nil);
- r = mptobe(m, (uint8_t*)va, len, nil);
- if(r < len){
- memmove((uint8_t*)va+len-r, va, r);
- memset(va, 0, len-r);
- }
- *n = len;
- mpfree(m);
- fss->phase = Established;
- return RpcOk;
- case VHaveResp:
- *n = snprint(va, *n, "%s", s->sigresp == 0? "ok":
- "signature does not verify");
- fss->phase = Established;
- return RpcOk;
- }
- }
- static int
- rsawrite(Fsstate *fss, void *va, uint n)
- {
- RSApriv *priv;
- mpint *m, *mm;
- State *s;
- char *hash;
- int dlen;
- s = fss->ps;
- switch(fss->phase){
- default:
- return phaseerror(fss, "write");
- case CHavePub:
- if(s->key == nil)
- return failure(fss, "no current key");
- switch(canusekey(fss, s->key)){
- case -1:
- return RpcConfirm;
- case 0:
- return failure(fss, "confirmation denied");
- case 1:
- break;
- }
- m = strtomp(va, nil, 16, nil);
- if(m == nil)
- return failure(fss, "invalid challenge value");
- m = rsadecrypt(s->key->priv, m, m);
- s->resp = m;
- fss->phase = CHaveResp;
- return RpcOk;
- case SNeedHash:
- case VNeedHash:
- /* get hash type from key */
- hash = _strfindattr(s->key->attr, "hash");
- if(hash == nil)
- hash = "sha1";
- if(strcmp(hash, "sha1") == 0)
- dlen = SHA1dlen;
- else if(strcmp(hash, "md5") == 0)
- dlen = MD5dlen;
- else
- return failure(fss, "unknown hash function %s", hash);
- if(n != dlen)
- return failure(fss, "hash length %d should be %d",
- n, dlen);
- priv = s->key->priv;
- s->digest = mkdigest(&priv->pub, hash, (uint8_t *)va, n);
- if(s->digest == nil)
- return failure(fss, nil);
- if(fss->phase == VNeedHash)
- fss->phase = VNeedSig;
- else
- fss->phase = SHaveResp;
- return RpcOk;
- case VNeedSig:
- priv = s->key->priv;
- m = betomp((uint8_t*)va, n, nil);
- mm = rsaencrypt(&priv->pub, m, nil);
- s->sigresp = mpcmp(s->digest, mm);
- mpfree(m);
- mpfree(mm);
- fss->phase = VHaveResp;
- return RpcOk;
- }
- }
- static void
- rsaclose(Fsstate *fss)
- {
- State *s;
- s = fss->ps;
- if(s->key)
- closekey(s->key);
- if(s->resp)
- mpfree(s->resp);
- if(s->digest)
- mpfree(s->digest);
- free(s);
- }
- static int
- rsaaddkey(Key *k, int before)
- {
- fmtinstall('B', mpfmt);
- if((k->priv = readrsapriv(k)) == nil){
- werrstr("malformed key data");
- return -1;
- }
- return replacekey(k, before);
- }
- static void
- rsaclosekey(Key *k)
- {
- rsaprivfree(k->priv);
- }
- Proto rsa = {
- .name= "rsa",
- .init= rsainit,
- .write= rsawrite,
- .read= rsaread,
- .close= rsaclose,
- .addkey= rsaaddkey,
- .closekey= rsaclosekey,
- };
- /*
- * 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)
- uint8_t oidsha1[] = { O0(1, 3), 14, 3, 2, 26 };
- uint8_t oidmd2[] = { O0(1, 2), O2(840), O3(113549), 2, 2 };
- uint8_t 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(uint8_t *asn1, char *alg, uint8_t *d, uint dlen)
- {
- uint8_t *obj, *p;
- uint olen;
- if(strcmp(alg, "sha1") == 0){
- obj = oidsha1;
- olen = sizeof(oidsha1);
- }else if(strcmp(alg, "md5") == 0){
- 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;
- }
- static mpint*
- mkdigest(RSApub *key, char *hashalg, uint8_t *hash, uint dlen)
- {
- mpint *m;
- uint8_t asn1[512], *buf;
- int len, n, pad;
- /*
- * Create ASN.1
- */
- n = mkasn1(asn1, hashalg, hash, dlen);
- /*
- * PKCS#1 padding
- */
- len = (mpsignif(key->n)+7)/8 - 1;
- if(len < n+2){
- werrstr("rsa key too short");
- return nil;
- }
- pad = len - (n+2);
- buf = emalloc(len);
- 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);
- return m;
- }
|