| #include "std.h" |
| #include "dat.h" |
| |
| /* |
| * RSA authentication. |
| * |
| * Encrypt/Decrypt: |
| * start n=xxx ek=xxx |
| * write msg |
| * read encrypt/decrypt(msg) |
| * |
| * Sign (PKCS #1 using hash=sha1 or hash=md5) |
| * start n=xxx ek=xxx |
| * write hash(msg) |
| * read signature(hash(msg)) |
| * |
| * Verify: |
| * start n=xxx ek=xxx |
| * write hash(msg) |
| * write signature(hash(msg)) |
| * read ok or fail |
| * |
| * all numbers are hexadecimal biginits parsable with strtomp. |
| * must be lower case for attribute matching in start. |
| */ |
| |
| static int |
| xrsadecrypt(Conv *c) |
| { |
| char *txt, buf[4096], *role; |
| int n, ret; |
| mpint *m, *mm; |
| Key *k; |
| RSApriv *key; |
| |
| ret = -1; |
| txt = nil; |
| m = nil; |
| mm = nil; |
| |
| /* fetch key */ |
| c->state = "keylookup"; |
| k = keylookup("%A", c->attr); |
| if(k == nil) |
| goto out; |
| key = k->priv; |
| |
| /* make sure have private half if needed */ |
| role = strfindattr(c->attr, "role"); |
| if(strcmp(role, "decrypt") == 0 && !key->c2){ |
| werrstr("missing private half of key -- cannot decrypt"); |
| goto out; |
| } |
| |
| /* read text */ |
| c->state = "read"; |
| if((n=convreadm(c, &txt)) < 0) |
| goto out; |
| if(n < 32){ |
| convprint(c, "data too short"); |
| goto out; |
| } |
| |
| /* encrypt/decrypt */ |
| m = betomp((uchar*)txt, n, nil); |
| if(m == nil) |
| goto out; |
| if(strcmp(role, "decrypt") == 0) |
| mm = rsadecrypt(key, m, nil); |
| else |
| mm = rsaencrypt(&key->pub, m, nil); |
| if(mm == nil) |
| goto out; |
| n = mptobe(mm, (uchar*)buf, sizeof buf, nil); |
| |
| /* send response */ |
| c->state = "write"; |
| convwrite(c, buf, n); |
| ret = 0; |
| |
| out: |
| mpfree(m); |
| mpfree(mm); |
| keyclose(k); |
| free(txt); |
| return ret; |
| } |
| |
| static int |
| xrsasign(Conv *c) |
| { |
| char *hash, *role; |
| int dlen, n, ret; |
| DigestAlg *hashfn; |
| Key *k; |
| RSApriv *key; |
| uchar sig[1024], digest[64]; |
| char *sig2; |
| |
| ret = -1; |
| |
| /* fetch key */ |
| c->state = "keylookup"; |
| k = keylookup("%A", c->attr); |
| if(k == nil) |
| goto out; |
| |
| /* make sure have private half if needed */ |
| key = k->priv; |
| role = strfindattr(c->attr, "role"); |
| if(strcmp(role, "sign") == 0 && !key->c2){ |
| werrstr("missing private half of key -- cannot sign"); |
| goto out; |
| } |
| |
| /* get hash type from key */ |
| hash = strfindattr(k->attr, "hash"); |
| if(hash == nil) |
| hash = "sha1"; |
| if(strcmp(hash, "sha1") == 0){ |
| hashfn = sha1; |
| dlen = SHA1dlen; |
| }else if(strcmp(hash, "md5") == 0){ |
| hashfn = md5; |
| dlen = MD5dlen; |
| }else{ |
| werrstr("unknown hash function %s", hash); |
| goto out; |
| } |
| |
| /* read hash */ |
| c->state = "read hash"; |
| if((n=convread(c, digest, dlen)) < 0) |
| goto out; |
| |
| if(strcmp(role, "sign") == 0){ |
| /* sign */ |
| if((n=rsasign(key, hashfn, digest, dlen, sig, sizeof sig)) < 0) |
| goto out; |
| |
| /* write */ |
| convwrite(c, sig, n); |
| }else{ |
| /* read signature */ |
| if((n = convreadm(c, &sig2)) < 0) |
| goto out; |
| |
| /* verify */ |
| if(rsaverify(&key->pub, hashfn, digest, dlen, (uchar*)sig2, n) == 0) |
| convprint(c, "ok"); |
| else |
| convprint(c, "signature does not verify"); |
| free(sig2); |
| } |
| ret = 0; |
| |
| out: |
| keyclose(k); |
| return ret; |
| } |
| |
| /* |
| * convert to canonical form (lower case) |
| * for use in attribute matches. |
| */ |
| static void |
| strlwr(char *a) |
| { |
| for(; *a; a++){ |
| if('A' <= *a && *a <= 'Z') |
| *a += 'a' - 'A'; |
| } |
| } |
| |
| 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; |
| strlwr(a); |
| if((a=strfindattr(k->attr, "n"))==nil |
| || (priv->pub.n=strtomp(a, nil, 16, nil))==nil) |
| goto Error; |
| strlwr(a); |
| 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; |
| strlwr(a); |
| if((a=strfindattr(k->privattr, "!q"))==nil |
| || (priv->q=strtomp(a, nil, 16, nil))==nil) |
| goto Error; |
| strlwr(a); |
| if(!probably_prime(priv->p, 20) || !probably_prime(priv->q, 20)) { |
| werrstr("rsa: p or q not prime"); |
| goto Error; |
| } |
| if((a=strfindattr(k->privattr, "!kp"))==nil |
| || (priv->kp=strtomp(a, nil, 16, nil))==nil) |
| goto Error; |
| strlwr(a); |
| if((a=strfindattr(k->privattr, "!kq"))==nil |
| || (priv->kq=strtomp(a, nil, 16, nil))==nil) |
| goto Error; |
| strlwr(a); |
| if((a=strfindattr(k->privattr, "!c2"))==nil |
| || (priv->c2=strtomp(a, nil, 16, nil))==nil) |
| goto Error; |
| strlwr(a); |
| if((a=strfindattr(k->privattr, "!dk"))==nil |
| || (priv->dk=strtomp(a, nil, 16, nil))==nil) |
| goto Error; |
| strlwr(a); |
| return priv; |
| |
| Error: |
| rsaprivfree(priv); |
| return nil; |
| } |
| |
| static int |
| rsacheck(Key *k) |
| { |
| static int first = 1; |
| |
| if(first){ |
| fmtinstall('B', mpfmt); |
| first = 0; |
| } |
| |
| if((k->priv = readrsapriv(k)) == nil){ |
| werrstr("malformed key data"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static void |
| rsaclose(Key *k) |
| { |
| rsaprivfree(k->priv); |
| k->priv = nil; |
| } |
| |
| static Role |
| rsaroles[] = |
| { |
| "sign", xrsasign, |
| "verify", xrsasign, /* public operation */ |
| "decrypt", xrsadecrypt, |
| "encrypt", xrsadecrypt, /* public operation */ |
| 0 |
| }; |
| |
| Proto rsa = { |
| "rsa", |
| rsaroles, |
| nil, |
| rsacheck, |
| rsaclose |
| }; |