1 /* $NetBSD: auth2-jpake.c,v 1.1.1.2 2009/12/27 01:06:49 christos Exp $ */
2 /* $OpenBSD: auth2-jpake.c,v 1.3 2009/03/05 07:18:19 djm Exp $ */
4 * Copyright (c) 2008 Damien Miller. All rights reserved.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 * Server side of zero-knowledge password auth using J-PAKE protocol
23 * F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
24 * 16th Workshop on Security Protocols, Cambridge, April 2008
26 * http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
31 #include <sys/types.h>
32 #include <sys/param.h>
37 #include <login_cap.h>
39 #include <openssl/bn.h>
40 #include <openssl/evp.h>
52 #include "auth-options.h"
57 #include "monitor_wrap.h"
63 * XXX options->permit_empty_passwd (at the moment, they will be refused
64 * anyway because they will mismatch on fake salt.
67 /* Dispatch handlers */
68 static void input_userauth_jpake_client_step1(int, u_int32_t
, void *);
69 static void input_userauth_jpake_client_step2(int, u_int32_t
, void *);
70 static void input_userauth_jpake_client_confirm(int, u_int32_t
, void *);
72 static int auth2_jpake_start(Authctxt
*);
75 extern ServerOptions options
;
76 extern u_char
*session_id2
;
77 extern u_int session_id2_len
;
80 * Attempt J-PAKE authentication.
83 userauth_jpake(Authctxt
*authctxt
)
85 int authenticated
= 0;
89 debug("jpake-01@openssh.com requested");
91 if (authctxt
->user
!= NULL
) {
92 if (authctxt
->jpake_ctx
== NULL
)
93 authctxt
->jpake_ctx
= jpake_new();
94 if (options
.zero_knowledge_password_authentication
)
95 authenticated
= auth2_jpake_start(authctxt
);
101 Authmethod method_jpake
= {
102 "jpake-01@openssh.com",
104 &options
.zero_knowledge_password_authentication
107 /* Clear context and callbacks */
109 auth2_jpake_stop(Authctxt
*authctxt
)
111 /* unregister callbacks */
112 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1
, NULL
);
113 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2
, NULL
);
114 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM
, NULL
);
115 if (authctxt
->jpake_ctx
!= NULL
) {
116 jpake_free(authctxt
->jpake_ctx
);
117 authctxt
->jpake_ctx
= NULL
;
121 /* Returns 1 if 'c' is a valid crypt(3) salt character, 0 otherwise */
123 valid_crypt_salt(int c
)
125 if (c
>= 'A' && c
<= 'Z')
127 if (c
>= 'a' && c
<= 'z')
129 if (c
>= '.' && c
<= '9')
135 * Derive fake salt as H(username || first_private_host_key)
136 * This provides relatively stable fake salts for non-existent
137 * users and avoids the jpake method becoming an account validity
141 derive_rawsalt(const char *username
, u_char
*rawsalt
, u_int len
)
149 buffer_put_cstring(&b
, username
);
150 if ((k
= get_hostkey_by_index(0)) == NULL
||
151 (k
->flags
& KEY_FLAG_EXT
))
152 fatal("%s: no hostkeys", __func__
);
156 if (k
->rsa
->p
== NULL
|| k
->rsa
->q
== NULL
)
157 fatal("%s: RSA key missing p and/or q", __func__
);
158 buffer_put_bignum2(&b
, k
->rsa
->p
);
159 buffer_put_bignum2(&b
, k
->rsa
->q
);
162 if (k
->dsa
->priv_key
== NULL
)
163 fatal("%s: DSA key missing priv_key", __func__
);
164 buffer_put_bignum2(&b
, k
->dsa
->priv_key
);
167 fatal("%s: unknown key type %d", __func__
, k
->type
);
169 if (hash_buffer(buffer_ptr(&b
), buffer_len(&b
), EVP_sha256(),
170 &digest
, &digest_len
) != 0)
171 fatal("%s: hash_buffer", __func__
);
173 if (len
> digest_len
)
174 fatal("%s: not enough bytes for rawsalt (want %u have %u)",
175 __func__
, len
, digest_len
);
176 memcpy(rawsalt
, digest
, len
);
177 bzero(digest
, digest_len
);
181 /* ASCII an integer [0, 64) for inclusion in a password/salt */
183 pw_encode64(u_int i64
)
186 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
187 return e64
[i64
% 64];
190 /* Generate ASCII salt bytes for user */
192 makesalt(u_int want
, const char *user
)
198 if (want
> sizeof(ret
) - 1)
199 fatal("%s: want %u", __func__
, want
);
201 derive_rawsalt(user
, rawsalt
, sizeof(rawsalt
));
202 bzero(ret
, sizeof(ret
));
203 for (i
= 0; i
< want
; i
++)
204 ret
[i
] = pw_encode64(rawsalt
[i
]);
205 bzero(rawsalt
, sizeof(rawsalt
));
211 * Select the system's default password hashing scheme and generate
212 * a stable fake salt under it for use by a non-existent account.
213 * Prevents jpake method being used to infer the validity of accounts.
216 fake_salt_and_scheme(Authctxt
*authctxt
, char **salt
, char **scheme
)
218 char *rounds_s
, *style
;
223 if ((lc
= login_getclass(authctxt
->pw
->pw_class
)) == NULL
&&
224 (lc
= login_getclass(NULL
)) == NULL
)
225 fatal("%s: login_getclass failed", __func__
);
226 style
= login_getcapstr(lc
, "localcipher", NULL
, NULL
);
228 style
= xstrdup("blowfish,6");
231 if ((rounds_s
= strchr(style
, ',')) != NULL
)
233 rounds
= strtonum(rounds_s
, 1, 1<<31, NULL
);
235 if (strcmp(style
, "md5") == 0) {
236 xasprintf(salt
, "$1$%s$", makesalt(8, authctxt
->user
));
237 *scheme
= xstrdup("md5");
238 } else if (strcmp(style
, "old") == 0) {
239 *salt
= xstrdup(makesalt(2, authctxt
->user
));
240 *scheme
= xstrdup("crypt");
241 } else if (strcmp(style
, "newsalt") == 0) {
242 rounds
= MAX(rounds
, 7250);
243 rounds
= MIN(rounds
, (1<<24) - 1);
244 xasprintf(salt
, "_%c%c%c%c%s",
245 pw_encode64(rounds
), pw_encode64(rounds
>> 6),
246 pw_encode64(rounds
>> 12), pw_encode64(rounds
>> 18),
247 makesalt(4, authctxt
->user
));
248 *scheme
= xstrdup("crypt-extended");
250 /* Default to blowfish */
251 rounds
= MAX(rounds
, 3);
252 rounds
= MIN(rounds
, 31);
253 xasprintf(salt
, "$2a$%02lld$%s", rounds
,
254 makesalt(22, authctxt
->user
));
255 *scheme
= xstrdup("bcrypt");
258 debug3("%s: fake %s salt for user %s: %s",
259 __func__
, *scheme
, authctxt
->user
, *salt
);
263 * Fetch password hashing scheme, password salt and derive shared secret
264 * for user. If user does not exist, a fake but stable and user-unique
265 * salt will be returned.
268 auth2_jpake_get_pwdata(Authctxt
*authctxt
, BIGNUM
**s
,
269 char **hash_scheme
, char **salt
)
273 u_int secret_len
, salt_len
;
276 debug3("%s: valid %d pw %.5s...", __func__
,
277 authctxt
->valid
, authctxt
->pw
->pw_passwd
);
282 if (authctxt
->valid
) {
283 if (strncmp(authctxt
->pw
->pw_passwd
, "$2$", 3) == 0 &&
284 strlen(authctxt
->pw
->pw_passwd
) > 28) {
286 * old-variant bcrypt:
287 * "$2$", 2 digit rounds, "$", 22 bytes salt
289 salt_len
= 3 + 2 + 1 + 22 + 1;
290 *salt
= xmalloc(salt_len
);
291 strlcpy(*salt
, authctxt
->pw
->pw_passwd
, salt_len
);
292 *hash_scheme
= xstrdup("bcrypt");
293 } else if (strncmp(authctxt
->pw
->pw_passwd
, "$2a$", 4) == 0 &&
294 strlen(authctxt
->pw
->pw_passwd
) > 29) {
296 * current-variant bcrypt:
297 * "$2a$", 2 digit rounds, "$", 22 bytes salt
299 salt_len
= 4 + 2 + 1 + 22 + 1;
300 *salt
= xmalloc(salt_len
);
301 strlcpy(*salt
, authctxt
->pw
->pw_passwd
, salt_len
);
302 *hash_scheme
= xstrdup("bcrypt");
303 } else if (strncmp(authctxt
->pw
->pw_passwd
, "$1$", 3) == 0 &&
304 strlen(authctxt
->pw
->pw_passwd
) > 5) {
307 * "$1$", salt until "$"
309 cp
= strchr(authctxt
->pw
->pw_passwd
+ 3, '$');
311 salt_len
= (cp
- authctxt
->pw
->pw_passwd
) + 1;
312 *salt
= xmalloc(salt_len
);
313 strlcpy(*salt
, authctxt
->pw
->pw_passwd
,
315 *hash_scheme
= xstrdup("md5crypt");
317 } else if (strncmp(authctxt
->pw
->pw_passwd
, "_", 1) == 0 &&
318 strlen(authctxt
->pw
->pw_passwd
) > 9) {
320 * BSDI extended crypt:
321 * "_", 4 digits count, 4 chars salt
323 salt_len
= 1 + 4 + 4 + 1;
324 *salt
= xmalloc(salt_len
);
325 strlcpy(*salt
, authctxt
->pw
->pw_passwd
, salt_len
);
326 *hash_scheme
= xstrdup("crypt-extended");
327 } else if (strlen(authctxt
->pw
->pw_passwd
) == 13 &&
328 valid_crypt_salt(authctxt
->pw
->pw_passwd
[0]) &&
329 valid_crypt_salt(authctxt
->pw
->pw_passwd
[1])) {
335 *salt
= xmalloc(salt_len
);
336 strlcpy(*salt
, authctxt
->pw
->pw_passwd
, salt_len
);
337 *hash_scheme
= xstrdup("crypt");
340 debug("%s: unrecognised crypt scheme for user %s",
341 __func__
, authctxt
->pw
->pw_name
);
345 fake_salt_and_scheme(authctxt
, salt
, hash_scheme
);
347 if (hash_buffer(authctxt
->pw
->pw_passwd
,
348 strlen(authctxt
->pw
->pw_passwd
), EVP_sha256(),
349 &secret
, &secret_len
) != 0)
350 fatal("%s: hash_buffer", __func__
);
351 if ((*s
= BN_bin2bn(secret
, secret_len
, NULL
)) == NULL
)
352 fatal("%s: BN_bin2bn (secret)", __func__
);
354 debug3("%s: salt = %s (len %u)", __func__
,
355 *salt
, (u_int
)strlen(*salt
));
356 debug3("%s: scheme = %s", __func__
, *hash_scheme
);
357 JPAKE_DEBUG_BN((*s
, "%s: s = ", __func__
));
359 bzero(secret
, secret_len
);
364 * Begin authentication attempt.
365 * Note, sets authctxt->postponed while in subprotocol
368 auth2_jpake_start(Authctxt
*authctxt
)
370 struct jpake_ctx
*pctx
= authctxt
->jpake_ctx
;
371 u_char
*x3_proof
, *x4_proof
;
372 u_int x3_proof_len
, x4_proof_len
;
373 char *salt
, *hash_scheme
;
375 debug("%s: start", __func__
);
377 PRIVSEP(jpake_step1(pctx
->grp
,
378 &pctx
->server_id
, &pctx
->server_id_len
,
379 &pctx
->x3
, &pctx
->x4
, &pctx
->g_x3
, &pctx
->g_x4
,
380 &x3_proof
, &x3_proof_len
,
381 &x4_proof
, &x4_proof_len
));
383 PRIVSEP(auth2_jpake_get_pwdata(authctxt
, &pctx
->s
,
384 &hash_scheme
, &salt
));
387 JPAKE_DEBUG_CTX((pctx
, "step 1 sending in %s", __func__
));
389 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1
);
390 packet_put_cstring(hash_scheme
);
391 packet_put_cstring(salt
);
392 packet_put_string(pctx
->server_id
, pctx
->server_id_len
);
393 packet_put_bignum2(pctx
->g_x3
);
394 packet_put_bignum2(pctx
->g_x4
);
395 packet_put_string(x3_proof
, x3_proof_len
);
396 packet_put_string(x4_proof
, x4_proof_len
);
400 bzero(hash_scheme
, strlen(hash_scheme
));
401 bzero(salt
, strlen(salt
));
404 bzero(x3_proof
, x3_proof_len
);
405 bzero(x4_proof
, x4_proof_len
);
409 /* Expect step 1 packet from peer */
410 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1
,
411 input_userauth_jpake_client_step1
);
413 authctxt
->postponed
= 1;
419 input_userauth_jpake_client_step1(int type
, u_int32_t seq
, void *ctxt
)
421 Authctxt
*authctxt
= ctxt
;
422 struct jpake_ctx
*pctx
= authctxt
->jpake_ctx
;
423 u_char
*x1_proof
, *x2_proof
, *x4_s_proof
;
424 u_int x1_proof_len
, x2_proof_len
, x4_s_proof_len
;
426 /* Disable this message */
427 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1
, NULL
);
429 /* Fetch step 1 values */
430 if ((pctx
->g_x1
= BN_new()) == NULL
||
431 (pctx
->g_x2
= BN_new()) == NULL
)
432 fatal("%s: BN_new", __func__
);
433 pctx
->client_id
= packet_get_string(&pctx
->client_id_len
);
434 packet_get_bignum2(pctx
->g_x1
);
435 packet_get_bignum2(pctx
->g_x2
);
436 x1_proof
= packet_get_string(&x1_proof_len
);
437 x2_proof
= packet_get_string(&x2_proof_len
);
441 JPAKE_DEBUG_CTX((pctx
, "step 1 received in %s", __func__
));
443 PRIVSEP(jpake_step2(pctx
->grp
, pctx
->s
, pctx
->g_x3
,
444 pctx
->g_x1
, pctx
->g_x2
, pctx
->x4
,
445 pctx
->client_id
, pctx
->client_id_len
,
446 pctx
->server_id
, pctx
->server_id_len
,
447 x1_proof
, x1_proof_len
,
448 x2_proof
, x2_proof_len
,
450 &x4_s_proof
, &x4_s_proof_len
));
452 bzero(x1_proof
, x1_proof_len
);
453 bzero(x2_proof
, x2_proof_len
);
458 JPAKE_DEBUG_CTX((pctx
, "step 2 sending in %s", __func__
));
460 /* Send values for step 2 */
461 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2
);
462 packet_put_bignum2(pctx
->b
);
463 packet_put_string(x4_s_proof
, x4_s_proof_len
);
467 bzero(x4_s_proof
, x4_s_proof_len
);
470 /* Expect step 2 packet from peer */
471 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2
,
472 input_userauth_jpake_client_step2
);
477 input_userauth_jpake_client_step2(int type
, u_int32_t seq
, void *ctxt
)
479 Authctxt
*authctxt
= ctxt
;
480 struct jpake_ctx
*pctx
= authctxt
->jpake_ctx
;
482 u_int x2_s_proof_len
;
484 /* Disable this message */
485 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2
, NULL
);
487 if ((pctx
->a
= BN_new()) == NULL
)
488 fatal("%s: BN_new", __func__
);
490 /* Fetch step 2 values */
491 packet_get_bignum2(pctx
->a
);
492 x2_s_proof
= packet_get_string(&x2_s_proof_len
);
496 JPAKE_DEBUG_CTX((pctx
, "step 2 received in %s", __func__
));
498 /* Derive shared key and calculate confirmation hash */
499 PRIVSEP(jpake_key_confirm(pctx
->grp
, pctx
->s
, pctx
->a
,
500 pctx
->x4
, pctx
->g_x3
, pctx
->g_x4
, pctx
->g_x1
, pctx
->g_x2
,
501 pctx
->server_id
, pctx
->server_id_len
,
502 pctx
->client_id
, pctx
->client_id_len
,
503 session_id2
, session_id2_len
,
504 x2_s_proof
, x2_s_proof_len
,
506 &pctx
->h_k_sid_sessid
, &pctx
->h_k_sid_sessid_len
));
508 bzero(x2_s_proof
, x2_s_proof_len
);
512 JPAKE_DEBUG_CTX((pctx
, "confirm sending in %s", __func__
));
514 /* Send key confirmation proof */
515 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM
);
516 packet_put_string(pctx
->h_k_sid_sessid
, pctx
->h_k_sid_sessid_len
);
520 /* Expect confirmation from peer */
521 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM
,
522 input_userauth_jpake_client_confirm
);
527 input_userauth_jpake_client_confirm(int type
, u_int32_t seq
, void *ctxt
)
529 Authctxt
*authctxt
= ctxt
;
530 struct jpake_ctx
*pctx
= authctxt
->jpake_ctx
;
531 int authenticated
= 0;
533 /* Disable this message */
534 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM
, NULL
);
536 pctx
->h_k_cid_sessid
= packet_get_string(&pctx
->h_k_cid_sessid_len
);
540 JPAKE_DEBUG_CTX((pctx
, "confirm received in %s", __func__
));
542 /* Verify expected confirmation hash */
543 if (PRIVSEP(jpake_check_confirm(pctx
->k
,
544 pctx
->client_id
, pctx
->client_id_len
,
545 session_id2
, session_id2_len
,
546 pctx
->h_k_cid_sessid
, pctx
->h_k_cid_sessid_len
)) == 1)
547 authenticated
= authctxt
->valid
? 1 : 0;
549 debug("%s: confirmation mismatch", __func__
);
552 authctxt
->postponed
= 0;
553 jpake_free(authctxt
->jpake_ctx
);
554 authctxt
->jpake_ctx
= NULL
;
555 userauth_finish(authctxt
, authenticated
, method_jpake
.name
);