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
);