// PAK is an encrypted key exchange protocol designed by Philip MacKenzie et al. // It is patented and use outside Plan 9 requires you get a license. // (All other EKE protocols are patented as well, by Lucent or others.) #include #include #include #include #include "SConn.h" #include "secstore.h" extern int verbose; char VERSION[] = "secstore"; static char *feedback[] = {"alpha","bravo","charlie","delta","echo","foxtrot","golf","hotel"}; typedef struct PAKparams{ mpint *q, *p, *r, *g; } PAKparams; static PAKparams *pak; // from seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E static void initPAKparams(void) { if(pak) return; pak = (PAKparams*)emalloc(sizeof(*pak)); pak->q = strtomp("E0F0EF284E10796C5A2A511E94748BA03C795C13", nil, 16, nil); pak->p = strtomp("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBB" "DB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86" "3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9" "3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", nil, 16, nil); pak->r = strtomp("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241" "CEF2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E" "887D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D" "21C4656848614D888A4", nil, 16, nil); pak->g = strtomp("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D23271734" "44ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD" "410E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734" "E3E2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", nil, 16, nil); } // H = (sha(ver,C,sha(passphrase)))^r mod p, // a hash function expensive to attack by brute force. static void longhash(char *ver, char *C, uchar *passwd, mpint *H) { uchar *Cp; int i, n, nver, nC; uchar buf[140], key[1]; nver = strlen(ver); nC = strlen(C); n = nver + nC + SHA1dlen; Cp = (uchar*)emalloc(n); memmove(Cp, ver, nver); memmove(Cp+nver, C, nC); memmove(Cp+nver+nC, passwd, SHA1dlen); for(i = 0; i < 7; i++){ key[0] = 'A'+i; hmac_sha1(Cp, n, key, sizeof key, buf+i*SHA1dlen, nil); } memset(Cp, 0, n); free(Cp); betomp(buf, sizeof buf, H); mpmod(H, pak->p, H); mpexp(H, pak->r, pak->p, H); } // Hi = H^-1 mod p char * PAK_Hi(char *C, char *passphrase, mpint *H, mpint *Hi) { uchar passhash[SHA1dlen]; sha1((uchar *)passphrase, strlen(passphrase), passhash, nil); initPAKparams(); longhash(VERSION, C, passhash, H); mpinvert(H, pak->p, Hi); return mptoa(Hi, 64, nil, 0); } // another, faster, hash function for each party to // confirm that the other has the right secrets. static void shorthash(char *mess, char *C, char *S, char *m, char *mu, char *sigma, char *Hi, uchar *digest) { SHA1state *state; state = sha1((uchar*)mess, strlen(mess), 0, 0); state = sha1((uchar*)C, strlen(C), 0, state); state = sha1((uchar*)S, strlen(S), 0, state); state = sha1((uchar*)m, strlen(m), 0, state); state = sha1((uchar*)mu, strlen(mu), 0, state); state = sha1((uchar*)sigma, strlen(sigma), 0, state); state = sha1((uchar*)Hi, strlen(Hi), 0, state); state = sha1((uchar*)mess, strlen(mess), 0, state); state = sha1((uchar*)C, strlen(C), 0, state); state = sha1((uchar*)S, strlen(S), 0, state); state = sha1((uchar*)m, strlen(m), 0, state); state = sha1((uchar*)mu, strlen(mu), 0, state); state = sha1((uchar*)sigma, strlen(sigma), 0, state); sha1((uchar*)Hi, strlen(Hi), digest, state); } // On input, conn provides an open channel to the server; // C is the name this client calls itself; // pass is the user's passphrase // On output, session secret has been set in conn // (unless return code is negative, which means failure). // If pS is not nil, it is set to the (alloc'd) name the server calls itself. int PAKclient(SConn *conn, char *C, char *pass, char **pS) { char *mess, *mess2, *eol, *S, *hexmu, *ks, *hexm, *hexsigma = nil, *hexHi; char kc[2*SHA1dlen+1]; uchar digest[SHA1dlen]; int rc = -1, n; mpint *x, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0); mpint *H = mpnew(0), *Hi = mpnew(0); hexHi = PAK_Hi(C, pass, H, Hi); if(verbose) fprint(2, "%s\n", feedback[H->p[0]&0x7]); // provide a clue to catch typos // random 1<=x<=q-1; send C, m=g**x H x = mprand(240, genrandom, nil); mpmod(x, pak->q, x); if(mpcmp(x, mpzero) == 0) mpassign(mpone, x); mpexp(pak->g, x, pak->p, m); mpmul(m, H, m); mpmod(m, pak->p, m); hexm = mptoa(m, 64, nil, 0); mess = (char*)emalloc(2*Maxmsg+2); mess2 = mess+Maxmsg+1; snprint(mess, Maxmsg, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm); conn->write(conn, (uchar*)mess, strlen(mess)); // recv g**y, S, check hash1(g**xy) if(readstr(conn, mess) < 0){ fprint(2, "%s: error: %s\n", argv0, mess); writerr(conn, "couldn't read g**y"); goto done; } eol = strchr(mess, '\n'); if(strncmp("mu=", mess, 3) != 0 || !eol || strncmp("\nk=", eol, 3) != 0){ writerr(conn, "verifier syntax error"); goto done; } hexmu = mess+3; *eol = 0; ks = eol+3; eol = strchr(ks, '\n'); if(!eol || strncmp("\nS=", eol, 3) != 0){ writerr(conn, "verifier syntax error for secstore 1.0"); goto done; } *eol = 0; S = eol+3; eol = strchr(S, '\n'); if(!eol){ writerr(conn, "verifier syntax error for secstore 1.0"); goto done; } *eol = 0; if(pS) *pS = estrdup(S); strtomp(hexmu, nil, 64, mu); mpexp(mu, x, pak->p, sigma); hexsigma = mptoa(sigma, 64, nil, 0); shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest); enc64(kc, sizeof kc, digest, SHA1dlen); if(strcmp(ks, kc) != 0){ writerr(conn, "verifier didn't match"); goto done; } // send hash2(g**xy) shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest); enc64(kc, sizeof kc, digest, SHA1dlen); snprint(mess2, Maxmsg, "k'=%s\n", kc); conn->write(conn, (uchar*)mess2, strlen(mess2)); // set session key shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest); memset(hexsigma, 0, strlen(hexsigma)); n = conn->secret(conn, digest, 0); memset(digest, 0, SHA1dlen); if(n < 0){ writerr(conn, "can't set secret"); goto done; } rc = 0; done: mpfree(x); mpfree(sigma); mpfree(mu); mpfree(m); mpfree(Hi); mpfree(H); free(hexsigma); free(hexHi); free(hexm); free(mess); return rc; } // On input, // mess contains first message; // name is name this server should call itself. // On output, session secret has been set in conn; // if pw!=nil, then *pw points to PW struct for authenticated user. // returns -1 if error int PAKserver(SConn *conn, char *S, char *mess, PW **pwp) { int rc = -1, n; char mess2[Maxmsg+1], *eol; char *C, ks[41], *kc, *hexm, *hexmu = nil, *hexsigma = nil, *hexHi = nil; uchar digest[SHA1dlen]; mpint *H = mpnew(0), *Hi = mpnew(0); mpint *y = nil, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0); PW *pw = nil; // secstore version and algorithm snprint(mess2,Maxmsg,"%s\tPAK\n", VERSION); n = strlen(mess2); if(strncmp(mess,mess2,n) != 0){ writerr(conn, "protocol should start with ver alg"); return -1; } mess += n; initPAKparams(); // parse first message into C, m eol = strchr(mess, '\n'); if(strncmp("C=", mess, 2) != 0 || !eol){ fprint(2, "%s: mess[1]=%s\n", argv0, mess); writerr(conn, "PAK version mismatch"); goto done; } C = mess+2; *eol = 0; hexm = eol+3; eol = strchr(hexm, '\n'); if(strncmp("m=", hexm-2, 2) != 0 || !eol){ writerr(conn, "PAK version mismatch"); goto done; } *eol = 0; strtomp(hexm, nil, 64, m); mpmod(m, pak->p, m); // lookup client if((pw = getPW(C,0)) == nil) { snprint(mess2, sizeof mess2, "%r"); writerr(conn, mess2); goto done; } if(mpcmp(m, mpzero) == 0) { writerr(conn, "account exists"); freePW(pw); pw = nil; goto done; } hexHi = mptoa(pw->Hi, 64, nil, 0); // random y, mu=g**y, sigma=g**xy y = mprand(240, genrandom, nil); mpmod(y, pak->q, y); if(mpcmp(y, mpzero) == 0){ mpassign(mpone, y); } mpexp(pak->g, y, pak->p, mu); mpmul(m, pw->Hi, m); mpmod(m, pak->p, m); mpexp(m, y, pak->p, sigma); // send g**y, hash1(g**xy) hexmu = mptoa(mu, 64, nil, 0); hexsigma = mptoa(sigma, 64, nil, 0); shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest); enc64(ks, sizeof ks, digest, SHA1dlen); snprint(mess2, sizeof mess2, "mu=%s\nk=%s\nS=%s\n", hexmu, ks, S); conn->write(conn, (uchar*)mess2, strlen(mess2)); // recv hash2(g**xy) if(readstr(conn, mess2) < 0){ writerr(conn, "couldn't read verifier"); goto done; } eol = strchr(mess2, '\n'); if(strncmp("k'=", mess2, 3) != 0 || !eol){ writerr(conn, "verifier syntax error"); goto done; } kc = mess2+3; *eol = 0; shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest); enc64(ks, sizeof ks, digest, SHA1dlen); if(strcmp(ks, kc) != 0) { rc = -2; goto done; } // set session key shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest); n = conn->secret(conn, digest, 1); if(n < 0){ writerr(conn, "can't set secret"); goto done; } rc = 0; done: if(rc<0 && pw){ pw->failed++; putPW(pw); } if(rc==0 && pw && pw->failed>0){ pw->failed = 0; putPW(pw); } if(pwp) *pwp = pw; else freePW(pw); free(hexsigma); free(hexHi); free(hexmu); mpfree(y); mpfree(sigma); mpfree(mu); mpfree(m); mpfree(Hi); mpfree(H); return rc; }