1 /* $OpenBSD: auth2-jpake.c,v 1.4 2010/08/31 11:54:45 djm Exp $ */
3 * Copyright (c) 2008 Damien Miller. All rights reserved.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 * Server side of zero-knowledge password auth using J-PAKE protocol
22 * F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
23 * 16th Workshop on Security Protocols, Cambridge, April 2008
25 * http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
30 #include <sys/types.h>
31 #include <sys/param.h>
36 #include <login_cap.h>
38 #include <openssl/bn.h>
39 #include <openssl/evp.h>
51 #include "auth-options.h"
56 #include "monitor_wrap.h"
62 * XXX options->permit_empty_passwd (at the moment, they will be refused
63 * anyway because they will mismatch on fake salt.
66 /* Dispatch handlers */
67 static void input_userauth_jpake_client_step1(int, u_int32_t, void *);
68 static void input_userauth_jpake_client_step2(int, u_int32_t, void *);
69 static void input_userauth_jpake_client_confirm(int, u_int32_t, void *);
71 static int auth2_jpake_start(Authctxt *);
74 extern ServerOptions options;
75 extern u_char *session_id2;
76 extern u_int session_id2_len;
79 * Attempt J-PAKE authentication.
82 userauth_jpake(Authctxt *authctxt)
84 int authenticated = 0;
88 debug("jpake-01@openssh.com requested");
90 if (authctxt->user != NULL) {
91 if (authctxt->jpake_ctx == NULL)
92 authctxt->jpake_ctx = jpake_new();
93 if (options.zero_knowledge_password_authentication)
94 authenticated = auth2_jpake_start(authctxt);
100 Authmethod method_jpake = {
101 "jpake-01@openssh.com",
103 &options.zero_knowledge_password_authentication
106 /* Clear context and callbacks */
108 auth2_jpake_stop(Authctxt *authctxt)
110 /* unregister callbacks */
111 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
112 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
113 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
114 if (authctxt->jpake_ctx != NULL) {
115 jpake_free(authctxt->jpake_ctx);
116 authctxt->jpake_ctx = NULL;
120 /* Returns 1 if 'c' is a valid crypt(3) salt character, 0 otherwise */
122 valid_crypt_salt(int c)
124 if (c >= 'A' && c <= 'Z')
126 if (c >= 'a' && c <= 'z')
128 if (c >= '.' && c <= '9')
134 * Derive fake salt as H(username || first_private_host_key)
135 * This provides relatively stable fake salts for non-existent
136 * users and avoids the jpake method becoming an account validity
140 derive_rawsalt(const char *username, u_char *rawsalt, u_int len)
148 buffer_put_cstring(&b, username);
149 if ((k = get_hostkey_by_index(0)) == NULL ||
150 (k->flags & KEY_FLAG_EXT))
151 fatal("%s: no hostkeys", __func__);
155 if (k->rsa->p == NULL || k->rsa->q == NULL)
156 fatal("%s: RSA key missing p and/or q", __func__);
157 buffer_put_bignum2(&b, k->rsa->p);
158 buffer_put_bignum2(&b, k->rsa->q);
161 if (k->dsa->priv_key == NULL)
162 fatal("%s: DSA key missing priv_key", __func__);
163 buffer_put_bignum2(&b, k->dsa->priv_key);
166 if (EC_KEY_get0_private_key(k->ecdsa) == NULL)
167 fatal("%s: ECDSA key missing priv_key", __func__);
168 buffer_put_bignum2(&b, EC_KEY_get0_private_key(k->ecdsa));
171 fatal("%s: unknown key type %d", __func__, k->type);
173 if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(),
174 &digest, &digest_len) != 0)
175 fatal("%s: hash_buffer", __func__);
177 if (len > digest_len)
178 fatal("%s: not enough bytes for rawsalt (want %u have %u)",
179 __func__, len, digest_len);
180 memcpy(rawsalt, digest, len);
181 bzero(digest, digest_len);
185 /* ASCII an integer [0, 64) for inclusion in a password/salt */
187 pw_encode64(u_int i64)
190 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
191 return e64[i64 % 64];
194 /* Generate ASCII salt bytes for user */
196 makesalt(u_int want, const char *user)
202 if (want > sizeof(ret) - 1)
203 fatal("%s: want %u", __func__, want);
205 derive_rawsalt(user, rawsalt, sizeof(rawsalt));
206 bzero(ret, sizeof(ret));
207 for (i = 0; i < want; i++)
208 ret[i] = pw_encode64(rawsalt[i]);
209 bzero(rawsalt, sizeof(rawsalt));
215 * Select the system's default password hashing scheme and generate
216 * a stable fake salt under it for use by a non-existent account.
217 * Prevents jpake method being used to infer the validity of accounts.
220 fake_salt_and_scheme(Authctxt *authctxt, char **salt, char **scheme)
222 char *rounds_s, *style;
227 if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL &&
228 (lc = login_getclass(NULL)) == NULL)
229 fatal("%s: login_getclass failed", __func__);
230 style = login_getcapstr(lc, "localcipher", NULL, NULL);
232 style = xstrdup("blowfish,6");
235 if ((rounds_s = strchr(style, ',')) != NULL)
237 rounds = strtonum(rounds_s, 1, 1<<31, NULL);
239 if (strcmp(style, "md5") == 0) {
240 xasprintf(salt, "$1$%s$", makesalt(8, authctxt->user));
241 *scheme = xstrdup("md5");
242 } else if (strcmp(style, "old") == 0) {
243 *salt = xstrdup(makesalt(2, authctxt->user));
244 *scheme = xstrdup("crypt");
245 } else if (strcmp(style, "newsalt") == 0) {
246 rounds = MAX(rounds, 7250);
247 rounds = MIN(rounds, (1<<24) - 1);
248 xasprintf(salt, "_%c%c%c%c%s",
249 pw_encode64(rounds), pw_encode64(rounds >> 6),
250 pw_encode64(rounds >> 12), pw_encode64(rounds >> 18),
251 makesalt(4, authctxt->user));
252 *scheme = xstrdup("crypt-extended");
254 /* Default to blowfish */
255 rounds = MAX(rounds, 3);
256 rounds = MIN(rounds, 31);
257 xasprintf(salt, "$2a$%02lld$%s", rounds,
258 makesalt(22, authctxt->user));
259 *scheme = xstrdup("bcrypt");
262 debug3("%s: fake %s salt for user %s: %s",
263 __func__, *scheme, authctxt->user, *salt);
267 * Fetch password hashing scheme, password salt and derive shared secret
268 * for user. If user does not exist, a fake but stable and user-unique
269 * salt will be returned.
272 auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s,
273 char **hash_scheme, char **salt)
277 u_int secret_len, salt_len;
280 debug3("%s: valid %d pw %.5s...", __func__,
281 authctxt->valid, authctxt->pw->pw_passwd);
286 if (authctxt->valid) {
287 if (strncmp(authctxt->pw->pw_passwd, "$2$", 3) == 0 &&
288 strlen(authctxt->pw->pw_passwd) > 28) {
290 * old-variant bcrypt:
291 * "$2$", 2 digit rounds, "$", 22 bytes salt
293 salt_len = 3 + 2 + 1 + 22 + 1;
294 *salt = xmalloc(salt_len);
295 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
296 *hash_scheme = xstrdup("bcrypt");
297 } else if (strncmp(authctxt->pw->pw_passwd, "$2a$", 4) == 0 &&
298 strlen(authctxt->pw->pw_passwd) > 29) {
300 * current-variant bcrypt:
301 * "$2a$", 2 digit rounds, "$", 22 bytes salt
303 salt_len = 4 + 2 + 1 + 22 + 1;
304 *salt = xmalloc(salt_len);
305 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
306 *hash_scheme = xstrdup("bcrypt");
307 } else if (strncmp(authctxt->pw->pw_passwd, "$1$", 3) == 0 &&
308 strlen(authctxt->pw->pw_passwd) > 5) {
311 * "$1$", salt until "$"
313 cp = strchr(authctxt->pw->pw_passwd + 3, '$');
315 salt_len = (cp - authctxt->pw->pw_passwd) + 1;
316 *salt = xmalloc(salt_len);
317 strlcpy(*salt, authctxt->pw->pw_passwd,
319 *hash_scheme = xstrdup("md5crypt");
321 } else if (strncmp(authctxt->pw->pw_passwd, "_", 1) == 0 &&
322 strlen(authctxt->pw->pw_passwd) > 9) {
324 * BSDI extended crypt:
325 * "_", 4 digits count, 4 chars salt
327 salt_len = 1 + 4 + 4 + 1;
328 *salt = xmalloc(salt_len);
329 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
330 *hash_scheme = xstrdup("crypt-extended");
331 } else if (strlen(authctxt->pw->pw_passwd) == 13 &&
332 valid_crypt_salt(authctxt->pw->pw_passwd[0]) &&
333 valid_crypt_salt(authctxt->pw->pw_passwd[1])) {
339 *salt = xmalloc(salt_len);
340 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
341 *hash_scheme = xstrdup("crypt");
344 debug("%s: unrecognised crypt scheme for user %s",
345 __func__, authctxt->pw->pw_name);
349 fake_salt_and_scheme(authctxt, salt, hash_scheme);
351 if (hash_buffer(authctxt->pw->pw_passwd,
352 strlen(authctxt->pw->pw_passwd), EVP_sha256(),
353 &secret, &secret_len) != 0)
354 fatal("%s: hash_buffer", __func__);
355 if ((*s = BN_bin2bn(secret, secret_len, NULL)) == NULL)
356 fatal("%s: BN_bin2bn (secret)", __func__);
358 debug3("%s: salt = %s (len %u)", __func__,
359 *salt, (u_int)strlen(*salt));
360 debug3("%s: scheme = %s", __func__, *hash_scheme);
361 JPAKE_DEBUG_BN((*s, "%s: s = ", __func__));
363 bzero(secret, secret_len);
368 * Begin authentication attempt.
369 * Note, sets authctxt->postponed while in subprotocol
372 auth2_jpake_start(Authctxt *authctxt)
374 struct jpake_ctx *pctx = authctxt->jpake_ctx;
375 u_char *x3_proof, *x4_proof;
376 u_int x3_proof_len, x4_proof_len;
377 char *salt, *hash_scheme;
379 debug("%s: start", __func__);
381 PRIVSEP(jpake_step1(pctx->grp,
382 &pctx->server_id, &pctx->server_id_len,
383 &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4,
384 &x3_proof, &x3_proof_len,
385 &x4_proof, &x4_proof_len));
387 PRIVSEP(auth2_jpake_get_pwdata(authctxt, &pctx->s,
388 &hash_scheme, &salt));
391 JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__));
393 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1);
394 packet_put_cstring(hash_scheme);
395 packet_put_cstring(salt);
396 packet_put_string(pctx->server_id, pctx->server_id_len);
397 packet_put_bignum2(pctx->g_x3);
398 packet_put_bignum2(pctx->g_x4);
399 packet_put_string(x3_proof, x3_proof_len);
400 packet_put_string(x4_proof, x4_proof_len);
404 bzero(hash_scheme, strlen(hash_scheme));
405 bzero(salt, strlen(salt));
408 bzero(x3_proof, x3_proof_len);
409 bzero(x4_proof, x4_proof_len);
413 /* Expect step 1 packet from peer */
414 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1,
415 input_userauth_jpake_client_step1);
417 authctxt->postponed = 1;
423 input_userauth_jpake_client_step1(int type, u_int32_t seq, void *ctxt)
425 Authctxt *authctxt = ctxt;
426 struct jpake_ctx *pctx = authctxt->jpake_ctx;
427 u_char *x1_proof, *x2_proof, *x4_s_proof;
428 u_int x1_proof_len, x2_proof_len, x4_s_proof_len;
430 /* Disable this message */
431 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
433 /* Fetch step 1 values */
434 if ((pctx->g_x1 = BN_new()) == NULL ||
435 (pctx->g_x2 = BN_new()) == NULL)
436 fatal("%s: BN_new", __func__);
437 pctx->client_id = packet_get_string(&pctx->client_id_len);
438 packet_get_bignum2(pctx->g_x1);
439 packet_get_bignum2(pctx->g_x2);
440 x1_proof = packet_get_string(&x1_proof_len);
441 x2_proof = packet_get_string(&x2_proof_len);
445 JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__));
447 PRIVSEP(jpake_step2(pctx->grp, pctx->s, pctx->g_x3,
448 pctx->g_x1, pctx->g_x2, pctx->x4,
449 pctx->client_id, pctx->client_id_len,
450 pctx->server_id, pctx->server_id_len,
451 x1_proof, x1_proof_len,
452 x2_proof, x2_proof_len,
454 &x4_s_proof, &x4_s_proof_len));
456 bzero(x1_proof, x1_proof_len);
457 bzero(x2_proof, x2_proof_len);
462 JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__));
464 /* Send values for step 2 */
465 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2);
466 packet_put_bignum2(pctx->b);
467 packet_put_string(x4_s_proof, x4_s_proof_len);
471 bzero(x4_s_proof, x4_s_proof_len);
474 /* Expect step 2 packet from peer */
475 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2,
476 input_userauth_jpake_client_step2);
481 input_userauth_jpake_client_step2(int type, u_int32_t seq, void *ctxt)
483 Authctxt *authctxt = ctxt;
484 struct jpake_ctx *pctx = authctxt->jpake_ctx;
486 u_int x2_s_proof_len;
488 /* Disable this message */
489 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
491 if ((pctx->a = BN_new()) == NULL)
492 fatal("%s: BN_new", __func__);
494 /* Fetch step 2 values */
495 packet_get_bignum2(pctx->a);
496 x2_s_proof = packet_get_string(&x2_s_proof_len);
500 JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__));
502 /* Derive shared key and calculate confirmation hash */
503 PRIVSEP(jpake_key_confirm(pctx->grp, pctx->s, pctx->a,
504 pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2,
505 pctx->server_id, pctx->server_id_len,
506 pctx->client_id, pctx->client_id_len,
507 session_id2, session_id2_len,
508 x2_s_proof, x2_s_proof_len,
510 &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len));
512 bzero(x2_s_proof, x2_s_proof_len);
516 JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__));
518 /* Send key confirmation proof */
519 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM);
520 packet_put_string(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
524 /* Expect confirmation from peer */
525 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM,
526 input_userauth_jpake_client_confirm);
531 input_userauth_jpake_client_confirm(int type, u_int32_t seq, void *ctxt)
533 Authctxt *authctxt = ctxt;
534 struct jpake_ctx *pctx = authctxt->jpake_ctx;
535 int authenticated = 0;
537 /* Disable this message */
538 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
540 pctx->h_k_cid_sessid = packet_get_string(&pctx->h_k_cid_sessid_len);
544 JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__));
546 /* Verify expected confirmation hash */
547 if (PRIVSEP(jpake_check_confirm(pctx->k,
548 pctx->client_id, pctx->client_id_len,
549 session_id2, session_id2_len,
550 pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len)) == 1)
551 authenticated = authctxt->valid ? 1 : 0;
553 debug("%s: confirmation mismatch", __func__);
556 authctxt->postponed = 0;
557 jpake_free(authctxt->jpake_ctx);
558 authctxt->jpake_ctx = NULL;
559 userauth_finish(authctxt, authenticated, method_jpake.name);