1 /* $OpenBSD: auth2-jpake.c,v 1.3 2009/03/05 07:18:19 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 fatal("%s: unknown key type %d", __func__
, k
->type
);
168 if (hash_buffer(buffer_ptr(&b
), buffer_len(&b
), EVP_sha256(),
169 &digest
, &digest_len
) != 0)
170 fatal("%s: hash_buffer", __func__
);
172 if (len
> digest_len
)
173 fatal("%s: not enough bytes for rawsalt (want %u have %u)",
174 __func__
, len
, digest_len
);
175 memcpy(rawsalt
, digest
, len
);
176 bzero(digest
, digest_len
);
180 /* ASCII an integer [0, 64) for inclusion in a password/salt */
182 pw_encode64(u_int i64
)
185 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
186 return e64
[i64
% 64];
189 /* Generate ASCII salt bytes for user */
191 makesalt(u_int want
, const char *user
)
197 if (want
> sizeof(ret
) - 1)
198 fatal("%s: want %u", __func__
, want
);
200 derive_rawsalt(user
, rawsalt
, sizeof(rawsalt
));
201 bzero(ret
, sizeof(ret
));
202 for (i
= 0; i
< want
; i
++)
203 ret
[i
] = pw_encode64(rawsalt
[i
]);
204 bzero(rawsalt
, sizeof(rawsalt
));
210 * Select the system's default password hashing scheme and generate
211 * a stable fake salt under it for use by a non-existent account.
212 * Prevents jpake method being used to infer the validity of accounts.
215 fake_salt_and_scheme(Authctxt
*authctxt
, char **salt
, char **scheme
)
217 char *rounds_s
, *style
;
222 if ((lc
= login_getclass(authctxt
->pw
->pw_class
)) == NULL
&&
223 (lc
= login_getclass(NULL
)) == NULL
)
224 fatal("%s: login_getclass failed", __func__
);
225 style
= login_getcapstr(lc
, "localcipher", NULL
, NULL
);
227 style
= xstrdup("blowfish,6");
230 if ((rounds_s
= strchr(style
, ',')) != NULL
)
232 rounds
= strtonum(rounds_s
, 1, 1<<31, NULL
);
234 if (strcmp(style
, "md5") == 0) {
235 xasprintf(salt
, "$1$%s$", makesalt(8, authctxt
->user
));
236 *scheme
= xstrdup("md5");
237 } else if (strcmp(style
, "old") == 0) {
238 *salt
= xstrdup(makesalt(2, authctxt
->user
));
239 *scheme
= xstrdup("crypt");
240 } else if (strcmp(style
, "newsalt") == 0) {
241 rounds
= MAX(rounds
, 7250);
242 rounds
= MIN(rounds
, (1<<24) - 1);
243 xasprintf(salt
, "_%c%c%c%c%s",
244 pw_encode64(rounds
), pw_encode64(rounds
>> 6),
245 pw_encode64(rounds
>> 12), pw_encode64(rounds
>> 18),
246 makesalt(4, authctxt
->user
));
247 *scheme
= xstrdup("crypt-extended");
249 /* Default to blowfish */
250 rounds
= MAX(rounds
, 3);
251 rounds
= MIN(rounds
, 31);
252 xasprintf(salt
, "$2a$%02lld$%s", rounds
,
253 makesalt(22, authctxt
->user
));
254 *scheme
= xstrdup("bcrypt");
257 debug3("%s: fake %s salt for user %s: %s",
258 __func__
, *scheme
, authctxt
->user
, *salt
);
262 * Fetch password hashing scheme, password salt and derive shared secret
263 * for user. If user does not exist, a fake but stable and user-unique
264 * salt will be returned.
267 auth2_jpake_get_pwdata(Authctxt
*authctxt
, BIGNUM
**s
,
268 char **hash_scheme
, char **salt
)
272 u_int secret_len
, salt_len
;
275 debug3("%s: valid %d pw %.5s...", __func__
,
276 authctxt
->valid
, authctxt
->pw
->pw_passwd
);
281 if (authctxt
->valid
) {
282 if (strncmp(authctxt
->pw
->pw_passwd
, "$2$", 3) == 0 &&
283 strlen(authctxt
->pw
->pw_passwd
) > 28) {
285 * old-variant bcrypt:
286 * "$2$", 2 digit rounds, "$", 22 bytes salt
288 salt_len
= 3 + 2 + 1 + 22 + 1;
289 *salt
= xmalloc(salt_len
);
290 strlcpy(*salt
, authctxt
->pw
->pw_passwd
, salt_len
);
291 *hash_scheme
= xstrdup("bcrypt");
292 } else if (strncmp(authctxt
->pw
->pw_passwd
, "$2a$", 4) == 0 &&
293 strlen(authctxt
->pw
->pw_passwd
) > 29) {
295 * current-variant bcrypt:
296 * "$2a$", 2 digit rounds, "$", 22 bytes salt
298 salt_len
= 4 + 2 + 1 + 22 + 1;
299 *salt
= xmalloc(salt_len
);
300 strlcpy(*salt
, authctxt
->pw
->pw_passwd
, salt_len
);
301 *hash_scheme
= xstrdup("bcrypt");
302 } else if (strncmp(authctxt
->pw
->pw_passwd
, "$1$", 3) == 0 &&
303 strlen(authctxt
->pw
->pw_passwd
) > 5) {
306 * "$1$", salt until "$"
308 cp
= strchr(authctxt
->pw
->pw_passwd
+ 3, '$');
310 salt_len
= (cp
- authctxt
->pw
->pw_passwd
) + 1;
311 *salt
= xmalloc(salt_len
);
312 strlcpy(*salt
, authctxt
->pw
->pw_passwd
,
314 *hash_scheme
= xstrdup("md5crypt");
316 } else if (strncmp(authctxt
->pw
->pw_passwd
, "_", 1) == 0 &&
317 strlen(authctxt
->pw
->pw_passwd
) > 9) {
319 * BSDI extended crypt:
320 * "_", 4 digits count, 4 chars salt
322 salt_len
= 1 + 4 + 4 + 1;
323 *salt
= xmalloc(salt_len
);
324 strlcpy(*salt
, authctxt
->pw
->pw_passwd
, salt_len
);
325 *hash_scheme
= xstrdup("crypt-extended");
326 } else if (strlen(authctxt
->pw
->pw_passwd
) == 13 &&
327 valid_crypt_salt(authctxt
->pw
->pw_passwd
[0]) &&
328 valid_crypt_salt(authctxt
->pw
->pw_passwd
[1])) {
334 *salt
= xmalloc(salt_len
);
335 strlcpy(*salt
, authctxt
->pw
->pw_passwd
, salt_len
);
336 *hash_scheme
= xstrdup("crypt");
339 debug("%s: unrecognised crypt scheme for user %s",
340 __func__
, authctxt
->pw
->pw_name
);
344 fake_salt_and_scheme(authctxt
, salt
, hash_scheme
);
346 if (hash_buffer(authctxt
->pw
->pw_passwd
,
347 strlen(authctxt
->pw
->pw_passwd
), EVP_sha256(),
348 &secret
, &secret_len
) != 0)
349 fatal("%s: hash_buffer", __func__
);
350 if ((*s
= BN_bin2bn(secret
, secret_len
, NULL
)) == NULL
)
351 fatal("%s: BN_bin2bn (secret)", __func__
);
353 debug3("%s: salt = %s (len %u)", __func__
,
354 *salt
, (u_int
)strlen(*salt
));
355 debug3("%s: scheme = %s", __func__
, *hash_scheme
);
356 JPAKE_DEBUG_BN((*s
, "%s: s = ", __func__
));
358 bzero(secret
, secret_len
);
363 * Begin authentication attempt.
364 * Note, sets authctxt->postponed while in subprotocol
367 auth2_jpake_start(Authctxt
*authctxt
)
369 struct jpake_ctx
*pctx
= authctxt
->jpake_ctx
;
370 u_char
*x3_proof
, *x4_proof
;
371 u_int x3_proof_len
, x4_proof_len
;
372 char *salt
, *hash_scheme
;
374 debug("%s: start", __func__
);
376 PRIVSEP(jpake_step1(pctx
->grp
,
377 &pctx
->server_id
, &pctx
->server_id_len
,
378 &pctx
->x3
, &pctx
->x4
, &pctx
->g_x3
, &pctx
->g_x4
,
379 &x3_proof
, &x3_proof_len
,
380 &x4_proof
, &x4_proof_len
));
382 PRIVSEP(auth2_jpake_get_pwdata(authctxt
, &pctx
->s
,
383 &hash_scheme
, &salt
));
386 JPAKE_DEBUG_CTX((pctx
, "step 1 sending in %s", __func__
));
388 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1
);
389 packet_put_cstring(hash_scheme
);
390 packet_put_cstring(salt
);
391 packet_put_string(pctx
->server_id
, pctx
->server_id_len
);
392 packet_put_bignum2(pctx
->g_x3
);
393 packet_put_bignum2(pctx
->g_x4
);
394 packet_put_string(x3_proof
, x3_proof_len
);
395 packet_put_string(x4_proof
, x4_proof_len
);
399 bzero(hash_scheme
, strlen(hash_scheme
));
400 bzero(salt
, strlen(salt
));
403 bzero(x3_proof
, x3_proof_len
);
404 bzero(x4_proof
, x4_proof_len
);
408 /* Expect step 1 packet from peer */
409 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1
,
410 input_userauth_jpake_client_step1
);
412 authctxt
->postponed
= 1;
418 input_userauth_jpake_client_step1(int type
, u_int32_t seq
, void *ctxt
)
420 Authctxt
*authctxt
= ctxt
;
421 struct jpake_ctx
*pctx
= authctxt
->jpake_ctx
;
422 u_char
*x1_proof
, *x2_proof
, *x4_s_proof
;
423 u_int x1_proof_len
, x2_proof_len
, x4_s_proof_len
;
425 /* Disable this message */
426 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1
, NULL
);
428 /* Fetch step 1 values */
429 if ((pctx
->g_x1
= BN_new()) == NULL
||
430 (pctx
->g_x2
= BN_new()) == NULL
)
431 fatal("%s: BN_new", __func__
);
432 pctx
->client_id
= packet_get_string(&pctx
->client_id_len
);
433 packet_get_bignum2(pctx
->g_x1
);
434 packet_get_bignum2(pctx
->g_x2
);
435 x1_proof
= packet_get_string(&x1_proof_len
);
436 x2_proof
= packet_get_string(&x2_proof_len
);
440 JPAKE_DEBUG_CTX((pctx
, "step 1 received in %s", __func__
));
442 PRIVSEP(jpake_step2(pctx
->grp
, pctx
->s
, pctx
->g_x3
,
443 pctx
->g_x1
, pctx
->g_x2
, pctx
->x4
,
444 pctx
->client_id
, pctx
->client_id_len
,
445 pctx
->server_id
, pctx
->server_id_len
,
446 x1_proof
, x1_proof_len
,
447 x2_proof
, x2_proof_len
,
449 &x4_s_proof
, &x4_s_proof_len
));
451 bzero(x1_proof
, x1_proof_len
);
452 bzero(x2_proof
, x2_proof_len
);
457 JPAKE_DEBUG_CTX((pctx
, "step 2 sending in %s", __func__
));
459 /* Send values for step 2 */
460 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2
);
461 packet_put_bignum2(pctx
->b
);
462 packet_put_string(x4_s_proof
, x4_s_proof_len
);
466 bzero(x4_s_proof
, x4_s_proof_len
);
469 /* Expect step 2 packet from peer */
470 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2
,
471 input_userauth_jpake_client_step2
);
476 input_userauth_jpake_client_step2(int type
, u_int32_t seq
, void *ctxt
)
478 Authctxt
*authctxt
= ctxt
;
479 struct jpake_ctx
*pctx
= authctxt
->jpake_ctx
;
481 u_int x2_s_proof_len
;
483 /* Disable this message */
484 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2
, NULL
);
486 if ((pctx
->a
= BN_new()) == NULL
)
487 fatal("%s: BN_new", __func__
);
489 /* Fetch step 2 values */
490 packet_get_bignum2(pctx
->a
);
491 x2_s_proof
= packet_get_string(&x2_s_proof_len
);
495 JPAKE_DEBUG_CTX((pctx
, "step 2 received in %s", __func__
));
497 /* Derive shared key and calculate confirmation hash */
498 PRIVSEP(jpake_key_confirm(pctx
->grp
, pctx
->s
, pctx
->a
,
499 pctx
->x4
, pctx
->g_x3
, pctx
->g_x4
, pctx
->g_x1
, pctx
->g_x2
,
500 pctx
->server_id
, pctx
->server_id_len
,
501 pctx
->client_id
, pctx
->client_id_len
,
502 session_id2
, session_id2_len
,
503 x2_s_proof
, x2_s_proof_len
,
505 &pctx
->h_k_sid_sessid
, &pctx
->h_k_sid_sessid_len
));
507 bzero(x2_s_proof
, x2_s_proof_len
);
511 JPAKE_DEBUG_CTX((pctx
, "confirm sending in %s", __func__
));
513 /* Send key confirmation proof */
514 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM
);
515 packet_put_string(pctx
->h_k_sid_sessid
, pctx
->h_k_sid_sessid_len
);
519 /* Expect confirmation from peer */
520 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM
,
521 input_userauth_jpake_client_confirm
);
526 input_userauth_jpake_client_confirm(int type
, u_int32_t seq
, void *ctxt
)
528 Authctxt
*authctxt
= ctxt
;
529 struct jpake_ctx
*pctx
= authctxt
->jpake_ctx
;
530 int authenticated
= 0;
532 /* Disable this message */
533 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM
, NULL
);
535 pctx
->h_k_cid_sessid
= packet_get_string(&pctx
->h_k_cid_sessid_len
);
539 JPAKE_DEBUG_CTX((pctx
, "confirm received in %s", __func__
));
541 /* Verify expected confirmation hash */
542 if (PRIVSEP(jpake_check_confirm(pctx
->k
,
543 pctx
->client_id
, pctx
->client_id_len
,
544 session_id2
, session_id2_len
,
545 pctx
->h_k_cid_sessid
, pctx
->h_k_cid_sessid_len
)) == 1)
546 authenticated
= authctxt
->valid
? 1 : 0;
548 debug("%s: confirmation mismatch", __func__
);
551 authctxt
->postponed
= 0;
552 jpake_free(authctxt
->jpake_ctx
);
553 authctxt
->jpake_ctx
= NULL
;
554 userauth_finish(authctxt
, authenticated
, method_jpake
.name
);