pak.c (9388B)
1 /* PAK is an encrypted key exchange protocol designed by Philip MacKenzie et al. */ 2 /* It is patented and use outside Plan 9 requires you get a license. */ 3 /* (All other EKE protocols are patented as well, by Lucent or others.) */ 4 #include <u.h> 5 #include <libc.h> 6 #include <mp.h> 7 #include <libsec.h> 8 #include "SConn.h" 9 #include "secstore.h" 10 11 extern int verbose; 12 13 char VERSION[] = "secstore"; 14 static char *feedback[] = {"alpha","bravo","charlie","delta","echo","foxtrot","golf","hotel"}; 15 16 typedef struct PAKparams{ 17 mpint *q, *p, *r, *g; 18 } PAKparams; 19 20 static PAKparams *pak; 21 22 /* from seed EB7B6E35F7CD37B511D96C67D6688CC4DD440E1E */ 23 static void 24 initPAKparams(void) 25 { 26 if(pak) 27 return; 28 pak = (PAKparams*)emalloc(sizeof(*pak)); 29 pak->q = strtomp("E0F0EF284E10796C5A2A511E94748BA03C795C13", nil, 16, nil); 30 pak->p = strtomp("C41CFBE4D4846F67A3DF7DE9921A49D3B42DC33728427AB159CEC8CBB" 31 "DB12B5F0C244F1A734AEB9840804EA3C25036AD1B61AFF3ABBC247CD4B384224567A86" 32 "3A6F020E7EE9795554BCD08ABAD7321AF27E1E92E3DB1C6E7E94FAAE590AE9C48F96D9" 33 "3D178E809401ABE8A534A1EC44359733475A36A70C7B425125062B1142D", 34 nil, 16, nil); 35 pak->r = strtomp("DF310F4E54A5FEC5D86D3E14863921E834113E060F90052AD332B3241" 36 "CEF2497EFA0303D6344F7C819691A0F9C4A773815AF8EAECFB7EC1D98F039F17A32A7E" 37 "887D97251A927D093F44A55577F4D70444AEBD06B9B45695EC23962B175F266895C67D" 38 "21C4656848614D888A4", nil, 16, nil); 39 pak->g = strtomp("2F1C308DC46B9A44B52DF7DACCE1208CCEF72F69C743ADD4D23271734" 40 "44ED6E65E074694246E07F9FD4AE26E0FDDD9F54F813C40CB9BCD4338EA6F242AB94CD" 41 "410E676C290368A16B1A3594877437E516C53A6EEE5493A038A017E955E218E7819734" 42 "E3E2A6E0BAE08B14258F8C03CC1B30E0DDADFCF7CEDF0727684D3D255F1", 43 nil, 16, nil); 44 } 45 46 /* H = (sha(ver,C,sha(passphrase)))^r mod p, */ 47 /* a hash function expensive to attack by brute force. */ 48 static void 49 longhash(char *ver, char *C, uchar *passwd, mpint *H) 50 { 51 uchar *Cp; 52 int i, n, nver, nC; 53 uchar buf[140], key[1]; 54 55 nver = strlen(ver); 56 nC = strlen(C); 57 n = nver + nC + SHA1dlen; 58 Cp = (uchar*)emalloc(n); 59 memmove(Cp, ver, nver); 60 memmove(Cp+nver, C, nC); 61 memmove(Cp+nver+nC, passwd, SHA1dlen); 62 for(i = 0; i < 7; i++){ 63 key[0] = 'A'+i; 64 hmac_sha1(Cp, n, key, sizeof key, buf+i*SHA1dlen, nil); 65 } 66 memset(Cp, 0, n); 67 free(Cp); 68 betomp(buf, sizeof buf, H); 69 mpmod(H, pak->p, H); 70 mpexp(H, pak->r, pak->p, H); 71 } 72 73 /* Hi = H^-1 mod p */ 74 char * 75 PAK_Hi(char *C, char *passphrase, mpint *H, mpint *Hi) 76 { 77 uchar passhash[SHA1dlen]; 78 79 sha1((uchar *)passphrase, strlen(passphrase), passhash, nil); 80 initPAKparams(); 81 longhash(VERSION, C, passhash, H); 82 mpinvert(H, pak->p, Hi); 83 return mptoa(Hi, 64, nil, 0); 84 } 85 86 /* another, faster, hash function for each party to */ 87 /* confirm that the other has the right secrets. */ 88 static void 89 shorthash(char *mess, char *C, char *S, char *m, char *mu, char *sigma, char *Hi, uchar *digest) 90 { 91 SHA1state *state; 92 93 state = sha1((uchar*)mess, strlen(mess), 0, 0); 94 state = sha1((uchar*)C, strlen(C), 0, state); 95 state = sha1((uchar*)S, strlen(S), 0, state); 96 state = sha1((uchar*)m, strlen(m), 0, state); 97 state = sha1((uchar*)mu, strlen(mu), 0, state); 98 state = sha1((uchar*)sigma, strlen(sigma), 0, state); 99 state = sha1((uchar*)Hi, strlen(Hi), 0, state); 100 state = sha1((uchar*)mess, strlen(mess), 0, state); 101 state = sha1((uchar*)C, strlen(C), 0, state); 102 state = sha1((uchar*)S, strlen(S), 0, state); 103 state = sha1((uchar*)m, strlen(m), 0, state); 104 state = sha1((uchar*)mu, strlen(mu), 0, state); 105 state = sha1((uchar*)sigma, strlen(sigma), 0, state); 106 sha1((uchar*)Hi, strlen(Hi), digest, state); 107 } 108 109 /* On input, conn provides an open channel to the server; */ 110 /* C is the name this client calls itself; */ 111 /* pass is the user's passphrase */ 112 /* On output, session secret has been set in conn */ 113 /* (unless return code is negative, which means failure). */ 114 /* If pS is not nil, it is set to the (alloc'd) name the server calls itself. */ 115 int 116 PAKclient(SConn *conn, char *C, char *pass, char **pS) 117 { 118 char *mess, *mess2, *eol, *S, *hexmu, *ks, *hexm, *hexsigma = nil, *hexHi; 119 char kc[2*SHA1dlen+1]; 120 uchar digest[SHA1dlen]; 121 int rc = -1, n; 122 mpint *x, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0); 123 mpint *H = mpnew(0), *Hi = mpnew(0); 124 125 hexHi = PAK_Hi(C, pass, H, Hi); 126 if(verbose) 127 fprint(2,"%s\n", feedback[H->p[0]&0x7]); /* provide a clue to catch typos */ 128 129 /* random 1<=x<=q-1; send C, m=g**x H */ 130 x = mprand(240, genrandom, nil); 131 mpmod(x, pak->q, x); 132 if(mpcmp(x, mpzero) == 0) 133 mpassign(mpone, x); 134 mpexp(pak->g, x, pak->p, m); 135 mpmul(m, H, m); 136 mpmod(m, pak->p, m); 137 hexm = mptoa(m, 64, nil, 0); 138 mess = (char*)emalloc(2*Maxmsg+2); 139 mess2 = mess+Maxmsg+1; 140 snprint(mess, Maxmsg, "%s\tPAK\nC=%s\nm=%s\n", VERSION, C, hexm); 141 conn->write(conn, (uchar*)mess, strlen(mess)); 142 143 /* recv g**y, S, check hash1(g**xy) */ 144 if(readstr(conn, mess) < 0){ 145 fprint(2, "error: %s\n", mess); 146 writerr(conn, "couldn't read g**y"); 147 goto done; 148 } 149 eol = strchr(mess, '\n'); 150 if(strncmp("mu=", mess, 3) != 0 || !eol || strncmp("\nk=", eol, 3) != 0){ 151 writerr(conn, "verifier syntax error"); 152 goto done; 153 } 154 hexmu = mess+3; 155 *eol = 0; 156 ks = eol+3; 157 eol = strchr(ks, '\n'); 158 if(!eol || strncmp("\nS=", eol, 3) != 0){ 159 writerr(conn, "verifier syntax error for secstore 1.0"); 160 goto done; 161 } 162 *eol = 0; 163 S = eol+3; 164 eol = strchr(S, '\n'); 165 if(!eol){ 166 writerr(conn, "verifier syntax error for secstore 1.0"); 167 goto done; 168 } 169 *eol = 0; 170 if(pS) 171 *pS = estrdup(S); 172 strtomp(hexmu, nil, 64, mu); 173 mpexp(mu, x, pak->p, sigma); 174 hexsigma = mptoa(sigma, 64, nil, 0); 175 shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest); 176 enc64(kc, sizeof kc, digest, SHA1dlen); 177 if(strcmp(ks, kc) != 0){ 178 writerr(conn, "verifier didn't match"); 179 goto done; 180 } 181 182 /* send hash2(g**xy) */ 183 shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest); 184 enc64(kc, sizeof kc, digest, SHA1dlen); 185 snprint(mess2, Maxmsg, "k'=%s\n", kc); 186 conn->write(conn, (uchar*)mess2, strlen(mess2)); 187 188 /* set session key */ 189 shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest); 190 memset(hexsigma, 0, strlen(hexsigma)); 191 n = conn->secret(conn, digest, 0); 192 memset(digest, 0, SHA1dlen); 193 if(n < 0){ 194 writerr(conn, "can't set secret"); 195 goto done; 196 } 197 198 rc = 0; 199 done: 200 mpfree(x); 201 mpfree(sigma); 202 mpfree(mu); 203 mpfree(m); 204 mpfree(Hi); 205 mpfree(H); 206 free(hexsigma); 207 free(hexHi); 208 free(hexm); 209 free(mess); 210 return rc; 211 } 212 213 /* On input, */ 214 /* mess contains first message; */ 215 /* name is name this server should call itself. */ 216 /* On output, session secret has been set in conn; */ 217 /* if pw!=nil, then *pw points to PW struct for authenticated user. */ 218 /* returns -1 if error */ 219 int 220 PAKserver(SConn *conn, char *S, char *mess, PW **pwp) 221 { 222 int rc = -1, n; 223 char mess2[Maxmsg+1], *eol; 224 char *C, ks[41], *kc, *hexm, *hexmu = nil, *hexsigma = nil, *hexHi = nil; 225 uchar digest[SHA1dlen]; 226 mpint *H = mpnew(0), *Hi = mpnew(0); 227 mpint *y = nil, *m = mpnew(0), *mu = mpnew(0), *sigma = mpnew(0); 228 PW *pw = nil; 229 230 /* secstore version and algorithm */ 231 snprint(mess2,Maxmsg,"%s\tPAK\n", VERSION); 232 n = strlen(mess2); 233 if(strncmp(mess,mess2,n) != 0){ 234 writerr(conn, "protocol should start with ver alg"); 235 return -1; 236 } 237 mess += n; 238 initPAKparams(); 239 240 /* parse first message into C, m */ 241 eol = strchr(mess, '\n'); 242 if(strncmp("C=", mess, 2) != 0 || !eol){ 243 fprint(2,"mess[1]=%s\n", mess); 244 writerr(conn, "PAK version mismatch"); 245 goto done; 246 } 247 C = mess+2; 248 *eol = 0; 249 hexm = eol+3; 250 eol = strchr(hexm, '\n'); 251 if(strncmp("m=", hexm-2, 2) != 0 || !eol){ 252 writerr(conn, "PAK version mismatch"); 253 goto done; 254 } 255 *eol = 0; 256 strtomp(hexm, nil, 64, m); 257 mpmod(m, pak->p, m); 258 259 /* lookup client */ 260 if((pw = getPW(C,0)) == nil) { 261 snprint(mess2, sizeof mess2, "%r"); 262 writerr(conn, mess2); 263 goto done; 264 } 265 if(mpcmp(m, mpzero) == 0) { 266 writerr(conn, "account exists"); 267 freePW(pw); 268 pw = nil; 269 goto done; 270 } 271 hexHi = mptoa(pw->Hi, 64, nil, 0); 272 273 /* random y, mu=g**y, sigma=g**xy */ 274 y = mprand(240, genrandom, nil); 275 mpmod(y, pak->q, y); 276 if(mpcmp(y, mpzero) == 0){ 277 mpassign(mpone, y); 278 } 279 mpexp(pak->g, y, pak->p, mu); 280 mpmul(m, pw->Hi, m); 281 mpmod(m, pak->p, m); 282 mpexp(m, y, pak->p, sigma); 283 284 /* send g**y, hash1(g**xy) */ 285 hexmu = mptoa(mu, 64, nil, 0); 286 hexsigma = mptoa(sigma, 64, nil, 0); 287 shorthash("server", C, S, hexm, hexmu, hexsigma, hexHi, digest); 288 enc64(ks, sizeof ks, digest, SHA1dlen); 289 snprint(mess2, sizeof mess2, "mu=%s\nk=%s\nS=%s\n", hexmu, ks, S); 290 conn->write(conn, (uchar*)mess2, strlen(mess2)); 291 292 /* recv hash2(g**xy) */ 293 if(readstr(conn, mess2) < 0){ 294 writerr(conn, "couldn't read verifier"); 295 goto done; 296 } 297 eol = strchr(mess2, '\n'); 298 if(strncmp("k'=", mess2, 3) != 0 || !eol){ 299 writerr(conn, "verifier syntax error"); 300 goto done; 301 } 302 kc = mess2+3; 303 *eol = 0; 304 shorthash("client", C, S, hexm, hexmu, hexsigma, hexHi, digest); 305 enc64(ks, sizeof ks, digest, SHA1dlen); 306 if(strcmp(ks, kc) != 0) { 307 rc = -2; 308 goto done; 309 } 310 311 /* set session key */ 312 shorthash("session", C, S, hexm, hexmu, hexsigma, hexHi, digest); 313 n = conn->secret(conn, digest, 1); 314 if(n < 0){ 315 writerr(conn, "can't set secret"); 316 goto done; 317 } 318 319 rc = 0; 320 done: 321 if(rc<0 && pw){ 322 pw->failed++; 323 putPW(pw); 324 } 325 if(rc==0 && pw && pw->failed>0){ 326 pw->failed = 0; 327 putPW(pw); 328 } 329 if(pwp) 330 *pwp = pw; 331 else 332 freePW(pw); 333 free(hexsigma); 334 free(hexHi); 335 free(hexmu); 336 mpfree(y); 337 mpfree(sigma); 338 mpfree(mu); 339 mpfree(m); 340 mpfree(Hi); 341 mpfree(H); 342 return rc; 343 }