summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2008-11-05 16:20:46 +1100
committerDamien Miller <djm@mindrot.org>2008-11-05 16:20:46 +1100
commit01ed2272a1545336173bf3aef66fbccc3494c8d8 (patch)
treea77f115d3b8964f0b6fcc604f9dea87d15143d7e
parent6f66d34308af787613d5525729953665f26367ee (diff)
- djm@cvs.openbsd.org 2008/11/04 08:22:13
[auth.h auth2.c monitor.c monitor.h monitor_wrap.c monitor_wrap.h] [readconf.c readconf.h servconf.c servconf.h ssh2.h ssh_config.5] [sshconnect2.c sshd_config.5 jpake.c jpake.h schnorr.c auth2-jpake.c] [Makefile.in] Add support for an experimental zero-knowledge password authentication method using the J-PAKE protocol described in F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling", 16th Workshop on Security Protocols, Cambridge, April 2008. This method allows password-based authentication without exposing the password to the server. Instead, the client and server exchange cryptographic proofs to demonstrate of knowledge of the password while revealing nothing useful to an attacker or compromised endpoint. This is experimental, work-in-progress code and is presently compiled-time disabled (turn on -DJPAKE in Makefile.inc). "just commit it. It isn't too intrusive." deraadt@
-rw-r--r--ChangeLog21
-rw-r--r--Makefile.in6
-rw-r--r--auth.h6
-rw-r--r--auth2-jpake.c557
-rw-r--r--auth2.c12
-rw-r--r--jpake.c604
-rw-r--r--jpake.h134
-rw-r--r--monitor.c227
-rw-r--r--monitor.h9
-rw-r--r--monitor_wrap.c167
-rw-r--r--monitor_wrap.h22
-rw-r--r--readconf.c18
-rw-r--r--readconf.h3
-rw-r--r--schnorr.c407
-rw-r--r--servconf.c20
-rw-r--r--servconf.h4
-rw-r--r--ssh2.h9
-rw-r--r--ssh_config.515
-rw-r--r--sshconnect2.c303
-rw-r--r--sshd_config.518
20 files changed, 2537 insertions, 25 deletions
diff --git a/ChangeLog b/ChangeLog
index cf80ff1e6..012749b0d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,25 @@
7 [auth.c] 7 [auth.c]
8 need unistd.h for close() prototype 8 need unistd.h for close() prototype
9 (ID sync only) 9 (ID sync only)
10 - djm@cvs.openbsd.org 2008/11/04 08:22:13
11 [auth.h auth2.c monitor.c monitor.h monitor_wrap.c monitor_wrap.h]
12 [readconf.c readconf.h servconf.c servconf.h ssh2.h ssh_config.5]
13 [sshconnect2.c sshd_config.5 jpake.c jpake.h schnorr.c auth2-jpake.c]
14 [Makefile.in]
15 Add support for an experimental zero-knowledge password authentication
16 method using the J-PAKE protocol described in F. Hao, P. Ryan,
17 "Password Authenticated Key Exchange by Juggling", 16th Workshop on
18 Security Protocols, Cambridge, April 2008.
19
20 This method allows password-based authentication without exposing
21 the password to the server. Instead, the client and server exchange
22 cryptographic proofs to demonstrate of knowledge of the password while
23 revealing nothing useful to an attacker or compromised endpoint.
24
25 This is experimental, work-in-progress code and is presently
26 compiled-time disabled (turn on -DJPAKE in Makefile.inc).
27
28 "just commit it. It isn't too intrusive." deraadt@
10 29
1120081103 3020081103
12 - OpenBSD CVS Sync 31 - OpenBSD CVS Sync
@@ -4857,4 +4876,4 @@
4857 OpenServer 6 and add osr5bigcrypt support so when someone migrates 4876 OpenServer 6 and add osr5bigcrypt support so when someone migrates
4858 passwords between UnixWare and OpenServer they will still work. OK dtucker@ 4877 passwords between UnixWare and OpenServer they will still work. OK dtucker@
4859 4878
4860$Id: ChangeLog,v 1.5129 2008/11/05 05:12:54 djm Exp $ 4879$Id: ChangeLog,v 1.5130 2008/11/05 05:20:46 djm Exp $
diff --git a/Makefile.in b/Makefile.in
index c1b7ab5a6..312b8d2b1 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
1# $Id: Makefile.in,v 1.297 2008/07/08 14:21:12 djm Exp $ 1# $Id: Makefile.in,v 1.298 2008/11/05 05:20:46 djm Exp $
2 2
3# uncomment if you run a non bourne compatable shell. Ie. csh 3# uncomment if you run a non bourne compatable shell. Ie. csh
4#SHELL = @SH@ 4#SHELL = @SH@
@@ -71,7 +71,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \
71 atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ 71 atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
72 monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \ 72 monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \
73 kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \ 73 kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \
74 entropy.o scard-opensc.o gss-genr.o umac.o 74 entropy.o scard-opensc.o gss-genr.o umac.o jpake.o schnorr.o
75 75
76SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ 76SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
77 sshconnect.o sshconnect1.o sshconnect2.o mux.o 77 sshconnect.o sshconnect1.o sshconnect2.o mux.o
@@ -81,7 +81,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
81 auth.o auth1.o auth2.o auth-options.o session.o \ 81 auth.o auth1.o auth2.o auth-options.o session.o \
82 auth-chall.o auth2-chall.o groupaccess.o \ 82 auth-chall.o auth2-chall.o groupaccess.o \
83 auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ 83 auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
84 auth2-none.o auth2-passwd.o auth2-pubkey.o \ 84 auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-jpake.o \
85 monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \ 85 monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \
86 auth-krb5.o \ 86 auth-krb5.o \
87 auth2-gss.o gss-serv.o gss-serv-krb5.o \ 87 auth2-gss.o gss-serv.o gss-serv-krb5.o \
diff --git a/auth.h b/auth.h
index 6a70f0eb6..3a70f4421 100644
--- a/auth.h
+++ b/auth.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth.h,v 1.61 2008/07/02 12:03:51 dtucker Exp $ */ 1/* $OpenBSD: auth.h,v 1.62 2008/11/04 08:22:12 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -59,6 +59,7 @@ struct Authctxt {
59 struct passwd *pw; /* set if 'valid' */ 59 struct passwd *pw; /* set if 'valid' */
60 char *style; 60 char *style;
61 void *kbdintctxt; 61 void *kbdintctxt;
62 void *jpake_ctx;
62#ifdef BSD_AUTH 63#ifdef BSD_AUTH
63 auth_session_t *as; 64 auth_session_t *as;
64#endif 65#endif
@@ -156,6 +157,9 @@ int bsdauth_respond(void *, u_int, char **);
156int skey_query(void *, char **, char **, u_int *, char ***, u_int **); 157int skey_query(void *, char **, char **, u_int *, char ***, u_int **);
157int skey_respond(void *, u_int, char **); 158int skey_respond(void *, u_int, char **);
158 159
160void auth2_jpake_get_pwdata(Authctxt *, BIGNUM **, char **, char **);
161void auth2_jpake_stop(Authctxt *);
162
159int allowed_user(struct passwd *); 163int allowed_user(struct passwd *);
160struct passwd * getpwnamallow(const char *user); 164struct passwd * getpwnamallow(const char *user);
161 165
diff --git a/auth2-jpake.c b/auth2-jpake.c
new file mode 100644
index 000000000..0029ec26b
--- /dev/null
+++ b/auth2-jpake.c
@@ -0,0 +1,557 @@
1/* $OpenBSD: auth2-jpake.c,v 1.1 2008/11/04 08:22:12 djm Exp $ */
2/*
3 * Copyright (c) 2008 Damien Miller. All rights reserved.
4 *
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.
8 *
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.
16 */
17
18/*
19 * Server side of zero-knowledge password auth using J-PAKE protocol
20 * as described in:
21 *
22 * F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
23 * 16th Workshop on Security Protocols, Cambridge, April 2008
24 *
25 * http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
26 */
27
28#include <sys/types.h>
29#include <sys/param.h>
30
31#include <pwd.h>
32#include <stdio.h>
33#include <string.h>
34#include <login_cap.h>
35
36#include <openssl/bn.h>
37#include <openssl/evp.h>
38
39#include "xmalloc.h"
40#include "ssh2.h"
41#include "key.h"
42#include "hostfile.h"
43#include "buffer.h"
44#include "auth.h"
45#include "packet.h"
46#include "dispatch.h"
47#include "log.h"
48#include "servconf.h"
49#include "auth-options.h"
50#include "canohost.h"
51#ifdef GSSAPI
52#include "ssh-gss.h"
53#endif
54#include "monitor_wrap.h"
55
56#include "jpake.h"
57
58#ifdef JPAKE
59
60/*
61 * XXX options->permit_empty_passwd (at the moment, they will be refused
62 * anyway because they will mismatch on fake salt.
63 */
64
65/* Dispatch handlers */
66static void input_userauth_jpake_client_step1(int, u_int32_t, void *);
67static void input_userauth_jpake_client_step2(int, u_int32_t, void *);
68static void input_userauth_jpake_client_confirm(int, u_int32_t, void *);
69
70static int auth2_jpake_start(Authctxt *);
71
72/* import */
73extern ServerOptions options;
74extern u_char *session_id2;
75extern u_int session_id2_len;
76
77/*
78 * Attempt J-PAKE authentication.
79 */
80static int
81userauth_jpake(Authctxt *authctxt)
82{
83 int authenticated = 0;
84
85 packet_check_eom();
86
87 debug("jpake-01@openssh.com requested");
88
89 if (authctxt->user != NULL) {
90 if (authctxt->jpake_ctx == NULL)
91 authctxt->jpake_ctx = jpake_new();
92 if (options.zero_knowledge_password_authentication)
93 authenticated = auth2_jpake_start(authctxt);
94 }
95
96 return authenticated;
97}
98
99Authmethod method_jpake = {
100 "jpake-01@openssh.com",
101 userauth_jpake,
102 &options.zero_knowledge_password_authentication
103};
104
105/* Clear context and callbacks */
106void
107auth2_jpake_stop(Authctxt *authctxt)
108{
109 /* unregister callbacks */
110 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
111 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
112 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
113 if (authctxt->jpake_ctx != NULL) {
114 jpake_free(authctxt->jpake_ctx);
115 authctxt->jpake_ctx = NULL;
116 }
117}
118
119/* Returns 1 if 'c' is a valid crypt(3) salt character, 0 otherwise */
120static int
121valid_crypt_salt(int c)
122{
123 if (c >= 'A' && c <= 'Z')
124 return 1;
125 if (c >= 'a' && c <= 'z')
126 return 1;
127 if (c >= '.' && c <= '9')
128 return 1;
129 return 0;
130}
131
132/*
133 * Derive fake salt as H(username || first_private_host_key)
134 * This provides relatively stable fake salts for non-existent
135 * users and avoids the jpake method becoming an account validity
136 * oracle.
137 */
138static void
139derive_rawsalt(const char *username, u_char *rawsalt, u_int len)
140{
141 u_char *digest;
142 u_int digest_len;
143 Buffer b;
144 Key *k;
145
146 buffer_init(&b);
147 buffer_put_cstring(&b, username);
148 if ((k = get_hostkey_by_index(0)) == NULL ||
149 (k->flags & KEY_FLAG_EXT))
150 fatal("%s: no hostkeys", __func__);
151 switch (k->type) {
152 case KEY_RSA1:
153 case KEY_RSA:
154 if (k->rsa->p == NULL || k->rsa->q == NULL)
155 fatal("%s: RSA key missing p and/or q", __func__);
156 buffer_put_bignum2(&b, k->rsa->p);
157 buffer_put_bignum2(&b, k->rsa->q);
158 break;
159 case KEY_DSA:
160 if (k->dsa->priv_key == NULL)
161 fatal("%s: DSA key missing priv_key", __func__);
162 buffer_put_bignum2(&b, k->dsa->priv_key);
163 break;
164 default:
165 fatal("%s: unknown key type %d", __func__, k->type);
166 }
167 if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(),
168 &digest, &digest_len) != 0)
169 fatal("%s: hash_buffer", __func__);
170 buffer_free(&b);
171 if (len > digest_len)
172 fatal("%s: not enough bytes for rawsalt (want %u have %u)",
173 __func__, len, digest_len);
174 memcpy(rawsalt, digest, len);
175 bzero(digest, digest_len);
176 xfree(digest);
177}
178
179/* ASCII an integer [0, 64) for inclusion in a password/salt */
180static char
181pw_encode64(u_int i64)
182{
183 const u_char e64[] =
184 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
185 return e64[i64 % 64];
186}
187
188/* Generate ASCII salt bytes for user */
189static char *
190makesalt(u_int want, const char *user)
191{
192 u_char rawsalt[32];
193 static char ret[33];
194 u_int i;
195
196 if (want > sizeof(ret) - 1)
197 fatal("%s: want %u", __func__, want);
198
199 derive_rawsalt(user, rawsalt, sizeof(rawsalt));
200 bzero(ret, sizeof(ret));
201 for (i = 0; i < want; i++)
202 ret[i] = pw_encode64(rawsalt[i]);
203 bzero(rawsalt, sizeof(rawsalt));
204
205 return ret;
206}
207
208/*
209 * Select the system's default password hashing scheme and generate
210 * a stable fake salt under it for use by a non-existent account.
211 * Prevents jpake method being used to infer the validity of accounts.
212 */
213static void
214fake_salt_and_scheme(Authctxt *authctxt, char **salt, char **scheme)
215{
216 char *rounds_s, *style;
217 long long rounds;
218 login_cap_t *lc;
219
220
221 if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL &&
222 (lc = login_getclass(NULL)) == NULL)
223 fatal("%s: login_getclass failed", __func__);
224 style = login_getcapstr(lc, "localcipher", NULL, NULL);
225 if (style == NULL)
226 style = xstrdup("blowfish,6");
227 login_close(lc);
228
229 if ((rounds_s = strchr(style, ',')) != NULL)
230 *rounds_s++ = '\0';
231 rounds = strtonum(rounds_s, 1, 1<<31, NULL);
232
233 if (strcmp(style, "md5") == 0) {
234 xasprintf(salt, "$1$%s$", makesalt(8, authctxt->user));
235 *scheme = xstrdup("md5");
236 } else if (strcmp(style, "old") == 0) {
237 *salt = xstrdup(makesalt(2, authctxt->user));
238 *scheme = xstrdup("crypt");
239 } else if (strcmp(style, "newsalt") == 0) {
240 rounds = MAX(rounds, 7250);
241 rounds = MIN(rounds, (1<<24) - 1);
242 xasprintf(salt, "_%c%c%c%c%s",
243 pw_encode64(rounds), pw_encode64(rounds >> 6),
244 pw_encode64(rounds >> 12), pw_encode64(rounds >> 18),
245 makesalt(4, authctxt->user));
246 *scheme = xstrdup("crypt-extended");
247 } else {
248 /* Default to blowfish */
249 rounds = MAX(rounds, 3);
250 rounds = MIN(rounds, 31);
251 xasprintf(salt, "$2a$%02lld$%s", rounds,
252 makesalt(22, authctxt->user));
253 *scheme = xstrdup("bcrypt");
254 }
255 xfree(style);
256 debug3("%s: fake %s salt for user %s: %s",
257 __func__, *scheme, authctxt->user, *salt);
258}
259
260/*
261 * Fetch password hashing scheme, password salt and derive shared secret
262 * for user. If user does not exist, a fake but stable and user-unique
263 * salt will be returned.
264 */
265void
266auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s,
267 char **hash_scheme, char **salt)
268{
269 char *cp;
270 u_char *secret;
271 u_int secret_len, salt_len;
272
273#ifdef JPAKE_DEBUG
274 debug3("%s: valid %d pw %.5s...", __func__,
275 authctxt->valid, authctxt->pw->pw_passwd);
276#endif
277
278 *salt = NULL;
279 *hash_scheme = NULL;
280 if (authctxt->valid) {
281 if (strncmp(authctxt->pw->pw_passwd, "$2$", 3) == 0 &&
282 strlen(authctxt->pw->pw_passwd) > 28) {
283 /*
284 * old-variant bcrypt:
285 * "$2$", 2 digit rounds, "$", 22 bytes salt
286 */
287 salt_len = 3 + 2 + 1 + 22 + 1;
288 *salt = xmalloc(salt_len);
289 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
290 *hash_scheme = xstrdup("bcrypt");
291 } else if (strncmp(authctxt->pw->pw_passwd, "$2a$", 4) == 0 &&
292 strlen(authctxt->pw->pw_passwd) > 29) {
293 /*
294 * current-variant bcrypt:
295 * "$2a$", 2 digit rounds, "$", 22 bytes salt
296 */
297 salt_len = 4 + 2 + 1 + 22 + 1;
298 *salt = xmalloc(salt_len);
299 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
300 *hash_scheme = xstrdup("bcrypt");
301 } else if (strncmp(authctxt->pw->pw_passwd, "$1$", 3) == 0 &&
302 strlen(authctxt->pw->pw_passwd) > 5) {
303 /*
304 * md5crypt:
305 * "$1$", salt until "$"
306 */
307 cp = strchr(authctxt->pw->pw_passwd + 3, '$');
308 if (cp != NULL) {
309 salt_len = (cp - authctxt->pw->pw_passwd) + 1;
310 *salt = xmalloc(salt_len);
311 strlcpy(*salt, authctxt->pw->pw_passwd,
312 salt_len);
313 *hash_scheme = xstrdup("md5crypt");
314 }
315 } else if (strncmp(authctxt->pw->pw_passwd, "_", 1) == 0 &&
316 strlen(authctxt->pw->pw_passwd) > 9) {
317 /*
318 * BSDI extended crypt:
319 * "_", 4 digits count, 4 chars salt
320 */
321 salt_len = 1 + 4 + 4 + 1;
322 *salt = xmalloc(salt_len);
323 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
324 *hash_scheme = xstrdup("crypt-extended");
325 } else if (strlen(authctxt->pw->pw_passwd) == 13 &&
326 valid_crypt_salt(authctxt->pw->pw_passwd[0]) &&
327 valid_crypt_salt(authctxt->pw->pw_passwd[1])) {
328 /*
329 * traditional crypt:
330 * 2 chars salt
331 */
332 salt_len = 2 + 1;
333 *salt = xmalloc(salt_len);
334 strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
335 *hash_scheme = xstrdup("crypt");
336 }
337 if (*salt == NULL) {
338 debug("%s: unrecognised crypt scheme for user %s",
339 __func__, authctxt->pw->pw_name);
340 }
341 }
342 if (*salt == NULL)
343 fake_salt_and_scheme(authctxt, salt, hash_scheme);
344
345 if (hash_buffer(authctxt->pw->pw_passwd,
346 strlen(authctxt->pw->pw_passwd), EVP_sha256(),
347 &secret, &secret_len) != 0)
348 fatal("%s: hash_buffer", __func__);
349 if ((*s = BN_bin2bn(secret, secret_len, NULL)) == NULL)
350 fatal("%s: BN_bin2bn (secret)", __func__);
351#ifdef JPAKE_DEBUG
352 debug3("%s: salt = %s (len %u)", __func__,
353 *salt, (u_int)strlen(*salt));
354 debug3("%s: scheme = %s", __func__, *hash_scheme);
355 JPAKE_DEBUG_BN((*s, "%s: s = ", __func__));
356#endif
357 bzero(secret, secret_len);
358 xfree(secret);
359}
360
361/*
362 * Being authentication attempt.
363 * Note, sets authctxt->postponed while in subprotocol
364 */
365static int
366auth2_jpake_start(Authctxt *authctxt)
367{
368 struct jpake_ctx *pctx = authctxt->jpake_ctx;
369 u_char *x3_proof, *x4_proof;
370 u_int x3_proof_len, x4_proof_len;
371 char *salt, *hash_scheme;
372
373 debug("%s: start", __func__);
374
375 PRIVSEP(jpake_step1(pctx->grp,
376 &pctx->server_id, &pctx->server_id_len,
377 &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4,
378 &x3_proof, &x3_proof_len,
379 &x4_proof, &x4_proof_len));
380
381 PRIVSEP(auth2_jpake_get_pwdata(authctxt, &pctx->s,
382 &hash_scheme, &salt));
383
384 if (!use_privsep)
385 JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__));
386
387 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1);
388 packet_put_cstring(hash_scheme);
389 packet_put_cstring(salt);
390 packet_put_string(pctx->server_id, pctx->server_id_len);
391 packet_put_bignum2(pctx->g_x3);
392 packet_put_bignum2(pctx->g_x4);
393 packet_put_string(x3_proof, x3_proof_len);
394 packet_put_string(x4_proof, x4_proof_len);
395 packet_send();
396 packet_write_wait();
397
398 bzero(hash_scheme, strlen(hash_scheme));
399 bzero(salt, strlen(salt));
400 xfree(hash_scheme);
401 xfree(salt);
402 bzero(x3_proof, x3_proof_len);
403 bzero(x4_proof, x4_proof_len);
404 xfree(x3_proof);
405 xfree(x4_proof);
406
407 /* Expect step 1 packet from peer */
408 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1,
409 input_userauth_jpake_client_step1);
410
411 authctxt->postponed = 1;
412 return 0;
413}
414
415/* ARGSUSED */
416static void
417input_userauth_jpake_client_step1(int type, u_int32_t seq, void *ctxt)
418{
419 Authctxt *authctxt = ctxt;
420 struct jpake_ctx *pctx = authctxt->jpake_ctx;
421 u_char *x1_proof, *x2_proof, *x4_s_proof;
422 u_int x1_proof_len, x2_proof_len, x4_s_proof_len;
423
424 /* Disable this message */
425 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
426
427 /* Fetch step 1 values */
428 if ((pctx->g_x1 = BN_new()) == NULL ||
429 (pctx->g_x2 = BN_new()) == NULL)
430 fatal("%s: BN_new", __func__);
431 pctx->client_id = packet_get_string(&pctx->client_id_len);
432 packet_get_bignum2(pctx->g_x1);
433 packet_get_bignum2(pctx->g_x2);
434 x1_proof = packet_get_string(&x1_proof_len);
435 x2_proof = packet_get_string(&x2_proof_len);
436 packet_check_eom();
437
438 if (!use_privsep)
439 JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__));
440
441 PRIVSEP(jpake_step2(pctx->grp, pctx->s, pctx->g_x3,
442 pctx->g_x1, pctx->g_x2, pctx->x4,
443 pctx->client_id, pctx->client_id_len,
444 pctx->server_id, pctx->server_id_len,
445 x1_proof, x1_proof_len,
446 x2_proof, x2_proof_len,
447 &pctx->b,
448 &x4_s_proof, &x4_s_proof_len));
449
450 bzero(x1_proof, x1_proof_len);
451 bzero(x2_proof, x2_proof_len);
452 xfree(x1_proof);
453 xfree(x2_proof);
454
455 if (!use_privsep)
456 JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__));
457
458 /* Send values for step 2 */
459 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2);
460 packet_put_bignum2(pctx->b);
461 packet_put_string(x4_s_proof, x4_s_proof_len);
462 packet_send();
463 packet_write_wait();
464
465 bzero(x4_s_proof, x4_s_proof_len);
466 xfree(x4_s_proof);
467
468 /* Expect step 2 packet from peer */
469 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2,
470 input_userauth_jpake_client_step2);
471}
472
473/* ARGSUSED */
474static void
475input_userauth_jpake_client_step2(int type, u_int32_t seq, void *ctxt)
476{
477 Authctxt *authctxt = ctxt;
478 struct jpake_ctx *pctx = authctxt->jpake_ctx;
479 u_char *x2_s_proof;
480 u_int x2_s_proof_len;
481
482 /* Disable this message */
483 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
484
485 if ((pctx->a = BN_new()) == NULL)
486 fatal("%s: BN_new", __func__);
487
488 /* Fetch step 2 values */
489 packet_get_bignum2(pctx->a);
490 x2_s_proof = packet_get_string(&x2_s_proof_len);
491 packet_check_eom();
492
493 if (!use_privsep)
494 JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__));
495
496 /* Derive shared key and calculate confirmation hash */
497 PRIVSEP(jpake_key_confirm(pctx->grp, pctx->s, pctx->a,
498 pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2,
499 pctx->server_id, pctx->server_id_len,
500 pctx->client_id, pctx->client_id_len,
501 session_id2, session_id2_len,
502 x2_s_proof, x2_s_proof_len,
503 &pctx->k,
504 &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len));
505
506 bzero(x2_s_proof, x2_s_proof_len);
507 xfree(x2_s_proof);
508
509 if (!use_privsep)
510 JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__));
511
512 /* Send key confirmation proof */
513 packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM);
514 packet_put_string(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
515 packet_send();
516 packet_write_wait();
517
518 /* Expect confirmation from peer */
519 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM,
520 input_userauth_jpake_client_confirm);
521}
522
523/* ARGSUSED */
524static void
525input_userauth_jpake_client_confirm(int type, u_int32_t seq, void *ctxt)
526{
527 Authctxt *authctxt = ctxt;
528 struct jpake_ctx *pctx = authctxt->jpake_ctx;
529 int authenticated = 0;
530
531 /* Disable this message */
532 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
533
534 pctx->h_k_cid_sessid = packet_get_string(&pctx->h_k_cid_sessid_len);
535 packet_check_eom();
536
537 if (!use_privsep)
538 JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__));
539
540 /* Verify expected confirmation hash */
541 if (PRIVSEP(jpake_check_confirm(pctx->k,
542 pctx->client_id, pctx->client_id_len,
543 session_id2, session_id2_len,
544 pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len)) == 1)
545 authenticated = authctxt->valid ? 1 : 0;
546 else
547 debug("%s: confirmation mismatch", __func__);
548
549 /* done */
550 authctxt->postponed = 0;
551 jpake_free(authctxt->jpake_ctx);
552 authctxt->jpake_ctx = NULL;
553 userauth_finish(authctxt, authenticated, method_jpake.name);
554}
555
556#endif /* JPAKE */
557
diff --git a/auth2.c b/auth2.c
index a835abfc6..ecf857052 100644
--- a/auth2.c
+++ b/auth2.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth2.c,v 1.119 2008/07/04 23:30:16 djm Exp $ */ 1/* $OpenBSD: auth2.c,v 1.120 2008/11/04 08:22:12 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -71,6 +71,9 @@ extern Authmethod method_hostbased;
71#ifdef GSSAPI 71#ifdef GSSAPI
72extern Authmethod method_gssapi; 72extern Authmethod method_gssapi;
73#endif 73#endif
74#ifdef JPAKE
75extern Authmethod method_jpake;
76#endif
74 77
75Authmethod *authmethods[] = { 78Authmethod *authmethods[] = {
76 &method_none, 79 &method_none,
@@ -78,6 +81,9 @@ Authmethod *authmethods[] = {
78#ifdef GSSAPI 81#ifdef GSSAPI
79 &method_gssapi, 82 &method_gssapi,
80#endif 83#endif
84#ifdef JPAKE
85 &method_jpake,
86#endif
81 &method_passwd, 87 &method_passwd,
82 &method_kbdint, 88 &method_kbdint,
83 &method_hostbased, 89 &method_hostbased,
@@ -257,8 +263,12 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
257 } 263 }
258 /* reset state */ 264 /* reset state */
259 auth2_challenge_stop(authctxt); 265 auth2_challenge_stop(authctxt);
266#ifdef JPAKE
267 auth2_jpake_stop(authctxt);
268#endif
260 269
261#ifdef GSSAPI 270#ifdef GSSAPI
271 /* XXX move to auth2_gssapi_stop() */
262 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); 272 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
263 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); 273 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
264#endif 274#endif
diff --git a/jpake.c b/jpake.c
new file mode 100644
index 000000000..565f2e255
--- /dev/null
+++ b/jpake.c
@@ -0,0 +1,604 @@
1/* $OpenBSD: jpake.c,v 1.1 2008/11/04 08:22:12 djm Exp $ */
2/*
3 * Copyright (c) 2008 Damien Miller. All rights reserved.
4 *
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.
8 *
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.
16 */
17
18/*
19 * Shared components of zero-knowledge password auth using J-PAKE protocol
20 * as described in:
21 *
22 * F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
23 * 16th Workshop on Security Protocols, Cambridge, April 2008
24 *
25 * http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
26 */
27
28#include "includes.h"
29
30#include <sys/types.h>
31
32#include <stdio.h>
33#include <string.h>
34#include <stdarg.h>
35
36#include <openssl/bn.h>
37#include <openssl/evp.h>
38
39#include "xmalloc.h"
40#include "ssh2.h"
41#include "key.h"
42#include "hostfile.h"
43#include "auth.h"
44#include "buffer.h"
45#include "packet.h"
46#include "dispatch.h"
47#include "log.h"
48
49#include "jpake.h"
50
51#ifdef JPAKE
52
53/* RFC3526 group 5, 1536 bits */
54#define JPAKE_GROUP_G "2"
55#define JPAKE_GROUP_P \
56 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74" \
57 "020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437" \
58 "4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
59 "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05" \
60 "98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB" \
61 "9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
62
63struct jpake_group *
64jpake_default_group(void)
65{
66 struct jpake_group *ret;
67
68 ret = xmalloc(sizeof(*ret));
69 ret->p = ret->q = ret->g = NULL;
70 if (BN_hex2bn(&ret->p, JPAKE_GROUP_P) == 0 ||
71 BN_hex2bn(&ret->g, JPAKE_GROUP_G) == 0)
72 fatal("%s: BN_hex2bn", __func__);
73 /* Subgroup order is p/2 (p is a safe prime) */
74 if ((ret->q = BN_new()) == NULL)
75 fatal("%s: BN_new", __func__);
76 if (BN_rshift1(ret->q, ret->p) != 1)
77 fatal("%s: BN_rshift1", __func__);
78
79 return ret;
80}
81
82/*
83 * Generate uniformly distributed random number in range (1, high).
84 * Return number on success, NULL on failure.
85 */
86BIGNUM *
87bn_rand_range_gt_one(const BIGNUM *high)
88{
89 BIGNUM *r, *tmp;
90 int success = -1;
91
92 if ((tmp = BN_new()) == NULL) {
93 error("%s: BN_new", __func__);
94 return NULL;
95 }
96 if ((r = BN_new()) == NULL) {
97 error("%s: BN_new failed", __func__);
98 goto out;
99 }
100 if (BN_set_word(tmp, 2) != 1) {
101 error("%s: BN_set_word(tmp, 2)", __func__);
102 goto out;
103 }
104 if (BN_sub(tmp, high, tmp) == -1) {
105 error("%s: BN_sub failed (tmp = high - 2)", __func__);
106 goto out;
107 }
108 if (BN_rand_range(r, tmp) == -1) {
109 error("%s: BN_rand_range failed", __func__);
110 goto out;
111 }
112 if (BN_set_word(tmp, 2) != 1) {
113 error("%s: BN_set_word(tmp, 2)", __func__);
114 goto out;
115 }
116 if (BN_add(r, r, tmp) == -1) {
117 error("%s: BN_add failed (r = r + 2)", __func__);
118 goto out;
119 }
120 success = 0;
121 out:
122 BN_clear_free(tmp);
123 if (success == 0)
124 return r;
125 BN_clear_free(r);
126 return NULL;
127}
128
129/*
130 * Hash contents of buffer 'b' with hash 'md'. Returns 0 on success,
131 * with digest via 'digestp' (caller to free) and length via 'lenp'.
132 * Returns -1 on failure.
133 */
134int
135hash_buffer(const u_char *buf, u_int len, const EVP_MD *md,
136 u_char **digestp, u_int *lenp)
137{
138 u_char digest[EVP_MAX_MD_SIZE];
139 u_int digest_len;
140 EVP_MD_CTX evp_md_ctx;
141 int success = -1;
142
143 EVP_MD_CTX_init(&evp_md_ctx);
144
145 if (EVP_DigestInit_ex(&evp_md_ctx, md, NULL) != 1) {
146 error("%s: EVP_DigestInit_ex", __func__);
147 goto out;
148 }
149 if (EVP_DigestUpdate(&evp_md_ctx, buf, len) != 1) {
150 error("%s: EVP_DigestUpdate", __func__);
151 goto out;
152 }
153 if (EVP_DigestFinal_ex(&evp_md_ctx, digest, &digest_len) != 1) {
154 error("%s: EVP_DigestFinal_ex", __func__);
155 goto out;
156 }
157 *digestp = xmalloc(digest_len);
158 *lenp = digest_len;
159 memcpy(*digestp, digest, *lenp);
160 success = 0;
161 out:
162 EVP_MD_CTX_cleanup(&evp_md_ctx);
163 bzero(digest, sizeof(digest));
164 digest_len = 0;
165 return success;
166}
167
168/* print formatted string followed by bignum */
169void
170jpake_debug3_bn(const BIGNUM *n, const char *fmt, ...)
171{
172 char *out, *h;
173 va_list args;
174
175 out = NULL;
176 va_start(args, fmt);
177 vasprintf(&out, fmt, args);
178 va_end(args);
179 if (out == NULL)
180 fatal("%s: vasprintf failed", __func__);
181
182 if (n == NULL)
183 debug3("%s(null)", out);
184 else {
185 h = BN_bn2hex(n);
186 debug3("%s0x%s", out, h);
187 free(h);
188 }
189 free(out);
190}
191
192/* print formatted string followed by buffer contents in hex */
193void
194jpake_debug3_buf(const u_char *buf, u_int len, const char *fmt, ...)
195{
196 char *out, h[65];
197 u_int i, j;
198 va_list args;
199
200 out = NULL;
201 va_start(args, fmt);
202 vasprintf(&out, fmt, args);
203 va_end(args);
204 if (out == NULL)
205 fatal("%s: vasprintf failed", __func__);
206
207 debug3("%s length %u%s", out, len, buf == NULL ? " (null)" : "");
208 free(out);
209 if (buf == NULL)
210 return;
211
212 *h = '\0';
213 for (i = j = 0; i < len; i++) {
214 snprintf(h + j, sizeof(h) - j, "%02x", buf[i]);
215 j += 2;
216 if (j >= sizeof(h) - 1 || i == len - 1) {
217 debug3(" %s", h);
218 *h = '\0';
219 j = 0;
220 }
221 }
222}
223
224struct jpake_ctx *
225jpake_new(void)
226{
227 struct jpake_ctx *ret;
228
229 ret = xcalloc(1, sizeof(*ret));
230
231 ret->grp = jpake_default_group();
232
233 ret->s = ret->k = NULL;
234 ret->x1 = ret->x2 = ret->x3 = ret->x4 = NULL;
235 ret->g_x1 = ret->g_x2 = ret->g_x3 = ret->g_x4 = NULL;
236 ret->a = ret->b = NULL;
237
238 ret->client_id = ret->server_id = NULL;
239 ret->h_k_cid_sessid = ret->h_k_sid_sessid = NULL;
240
241 debug3("%s: alloc %p", __func__, ret);
242
243 return ret;
244}
245
246
247void
248jpake_free(struct jpake_ctx *pctx)
249{
250 debug3("%s: free %p", __func__, pctx);
251
252#define JPAKE_BN_CLEAR_FREE(v) \
253 do { \
254 if ((v) != NULL) { \
255 BN_clear_free(v); \
256 (v) = NULL; \
257 } \
258 } while (0)
259#define JPAKE_BUF_CLEAR_FREE(v, l) \
260 do { \
261 if ((v) != NULL) { \
262 bzero((v), (l)); \
263 xfree(v); \
264 (v) = NULL; \
265 (l) = 0; \
266 } \
267 } while (0)
268
269 JPAKE_BN_CLEAR_FREE(pctx->s);
270 JPAKE_BN_CLEAR_FREE(pctx->k);
271 JPAKE_BN_CLEAR_FREE(pctx->x1);
272 JPAKE_BN_CLEAR_FREE(pctx->x2);
273 JPAKE_BN_CLEAR_FREE(pctx->x3);
274 JPAKE_BN_CLEAR_FREE(pctx->x4);
275 JPAKE_BN_CLEAR_FREE(pctx->g_x1);
276 JPAKE_BN_CLEAR_FREE(pctx->g_x2);
277 JPAKE_BN_CLEAR_FREE(pctx->g_x3);
278 JPAKE_BN_CLEAR_FREE(pctx->g_x4);
279 JPAKE_BN_CLEAR_FREE(pctx->a);
280 JPAKE_BN_CLEAR_FREE(pctx->b);
281
282 JPAKE_BUF_CLEAR_FREE(pctx->client_id, pctx->client_id_len);
283 JPAKE_BUF_CLEAR_FREE(pctx->server_id, pctx->server_id_len);
284 JPAKE_BUF_CLEAR_FREE(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len);
285 JPAKE_BUF_CLEAR_FREE(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
286
287#undef JPAKE_BN_CLEAR_FREE
288#undef JPAKE_BUF_CLEAR_FREE
289
290 bzero(pctx, sizeof(pctx));
291 xfree(pctx);
292}
293
294/* dump entire jpake_ctx. NB. includes private values! */
295void
296jpake_dump(struct jpake_ctx *pctx, const char *fmt, ...)
297{
298 char *out;
299 va_list args;
300
301 out = NULL;
302 va_start(args, fmt);
303 vasprintf(&out, fmt, args);
304 va_end(args);
305 if (out == NULL)
306 fatal("%s: vasprintf failed", __func__);
307
308 debug3("%s: %s (ctx at %p)", __func__, out, pctx);
309 if (pctx == NULL) {
310 free(out);
311 return;
312 }
313
314#define JPAKE_DUMP_BN(a) do { \
315 if ((a) != NULL) \
316 JPAKE_DEBUG_BN(((a), "%s = ", #a)); \
317 } while (0)
318#define JPAKE_DUMP_BUF(a, b) do { \
319 if ((a) != NULL) \
320 JPAKE_DEBUG_BUF((a, b, "%s", #a)); \
321 } while (0)
322
323 JPAKE_DUMP_BN(pctx->s);
324 JPAKE_DUMP_BN(pctx->k);
325 JPAKE_DUMP_BN(pctx->x1);
326 JPAKE_DUMP_BN(pctx->x2);
327 JPAKE_DUMP_BN(pctx->x3);
328 JPAKE_DUMP_BN(pctx->x4);
329 JPAKE_DUMP_BN(pctx->g_x1);
330 JPAKE_DUMP_BN(pctx->g_x2);
331 JPAKE_DUMP_BN(pctx->g_x3);
332 JPAKE_DUMP_BN(pctx->g_x4);
333 JPAKE_DUMP_BN(pctx->a);
334 JPAKE_DUMP_BN(pctx->b);
335
336 JPAKE_DUMP_BUF(pctx->client_id, pctx->client_id_len);
337 JPAKE_DUMP_BUF(pctx->server_id, pctx->server_id_len);
338 JPAKE_DUMP_BUF(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len);
339 JPAKE_DUMP_BUF(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
340
341 debug3("%s: %s done", __func__, out);
342 free(out);
343}
344
345/* Shared parts of step 1 exchange calculation */
346void
347jpake_step1(struct jpake_group *grp,
348 u_char **id, u_int *id_len,
349 BIGNUM **priv1, BIGNUM **priv2, BIGNUM **g_priv1, BIGNUM **g_priv2,
350 u_char **priv1_proof, u_int *priv1_proof_len,
351 u_char **priv2_proof, u_int *priv2_proof_len)
352{
353 BN_CTX *bn_ctx;
354
355 if ((bn_ctx = BN_CTX_new()) == NULL)
356 fatal("%s: BN_CTX_new", __func__);
357
358 /* Random nonce to prevent replay */
359 *id = xmalloc(KZP_ID_LEN);
360 *id_len = KZP_ID_LEN;
361 arc4random_buf(*id, *id_len);
362
363 /*
364 * x1/x3 is a random element of Zq
365 * x2/x4 is a random element of Z*q
366 * We also exclude [1] from x1/x3 candidates and [0, 1] from
367 * x2/x4 candiates to avoid possible degeneracy (i.e. g^0, g^1).
368 */
369 if ((*priv1 = bn_rand_range_gt_one(grp->q)) == NULL ||
370 (*priv2 = bn_rand_range_gt_one(grp->q)) == NULL)
371 fatal("%s: bn_rand_range_gt_one", __func__);
372
373 /*
374 * client: g_x1 = g^x1 mod p / server: g_x3 = g^x3 mod p
375 * client: g_x2 = g^x2 mod p / server: g_x4 = g^x4 mod p
376 */
377 if ((*g_priv1 = BN_new()) == NULL ||
378 (*g_priv2 = BN_new()) == NULL)
379 fatal("%s: BN_new", __func__);
380 if (BN_mod_exp(*g_priv1, grp->g, *priv1, grp->p, bn_ctx) == -1)
381 fatal("%s: BN_mod_exp", __func__);
382 if (BN_mod_exp(*g_priv2, grp->g, *priv2, grp->p, bn_ctx) == -1)
383 fatal("%s: BN_mod_exp", __func__);
384
385 /* Generate proofs for holding x1/x3 and x2/x4 */
386 if (schnorr_sign(grp->p, grp->q, grp->g,
387 *priv1, *g_priv1, *id, *id_len,
388 priv1_proof, priv1_proof_len) != 0)
389 fatal("%s: schnorr_sign", __func__);
390 if (schnorr_sign(grp->p, grp->q, grp->g,
391 *priv2, *g_priv2, *id, *id_len,
392 priv2_proof, priv2_proof_len) != 0)
393 fatal("%s: schnorr_sign", __func__);
394
395 BN_CTX_free(bn_ctx);
396}
397
398/* Shared parts of step 2 exchange calculation */
399void
400jpake_step2(struct jpake_group *grp, BIGNUM *s,
401 BIGNUM *mypub1, BIGNUM *theirpub1, BIGNUM *theirpub2, BIGNUM *mypriv2,
402 const u_char *theirid, u_int theirid_len,
403 const u_char *myid, u_int myid_len,
404 const u_char *theirpub1_proof, u_int theirpub1_proof_len,
405 const u_char *theirpub2_proof, u_int theirpub2_proof_len,
406 BIGNUM **newpub,
407 u_char **newpub_exponent_proof, u_int *newpub_exponent_proof_len)
408{
409 BN_CTX *bn_ctx;
410 BIGNUM *tmp, *exponent;
411
412 /* Validate peer's step 1 values */
413 if (BN_cmp(theirpub1, BN_value_one()) <= 0)
414 fatal("%s: theirpub1 <= 1", __func__);
415 if (BN_cmp(theirpub2, BN_value_one()) <= 0)
416 fatal("%s: theirpub2 <= 1", __func__);
417
418 if (schnorr_verify(grp->p, grp->q, grp->g, theirpub1,
419 theirid, theirid_len, theirpub1_proof, theirpub1_proof_len) != 1)
420 fatal("%s: schnorr_verify theirpub1 failed", __func__);
421 if (schnorr_verify(grp->p, grp->q, grp->g, theirpub2,
422 theirid, theirid_len, theirpub2_proof, theirpub2_proof_len) != 1)
423 fatal("%s: schnorr_verify theirpub2 failed", __func__);
424
425 if ((bn_ctx = BN_CTX_new()) == NULL)
426 fatal("%s: BN_CTX_new", __func__);
427
428 if ((*newpub = BN_new()) == NULL ||
429 (tmp = BN_new()) == NULL ||
430 (exponent = BN_new()) == NULL)
431 fatal("%s: BN_new", __func__);
432
433 /*
434 * client: exponent = x2 * s mod p
435 * server: exponent = x4 * s mod p
436 */
437 if (BN_mod_mul(exponent, mypriv2, s, grp->q, bn_ctx) != 1)
438 fatal("%s: BN_mod_mul (exponent = mypriv2 * s mod p)",
439 __func__);
440
441 /*
442 * client: tmp = g^(x1 + x3 + x4) mod p
443 * server: tmp = g^(x1 + x2 + x3) mod p
444 */
445 if (BN_mod_mul(tmp, mypub1, theirpub1, grp->p, bn_ctx) != 1)
446 fatal("%s: BN_mod_mul (tmp = mypub1 * theirpub1 mod p)",
447 __func__);
448 if (BN_mod_mul(tmp, tmp, theirpub2, grp->p, bn_ctx) != 1)
449 fatal("%s: BN_mod_mul (tmp = tmp * theirpub2 mod p)", __func__);
450
451 /*
452 * client: a = tmp^exponent = g^((x1+x3+x4) * x2 * s) mod p
453 * server: b = tmp^exponent = g^((x1+x2+x3) * x4 * s) mod p
454 */
455 if (BN_mod_exp(*newpub, tmp, exponent, grp->p, bn_ctx) != 1)
456 fatal("%s: BN_mod_mul (newpub = tmp^exponent mod p)", __func__);
457
458 JPAKE_DEBUG_BN((tmp, "%s: tmp = ", __func__));
459 JPAKE_DEBUG_BN((exponent, "%s: exponent = ", __func__));
460
461 /* Note the generator here is 'tmp', not g */
462 if (schnorr_sign(grp->p, grp->q, tmp, exponent, *newpub,
463 myid, myid_len,
464 newpub_exponent_proof, newpub_exponent_proof_len) != 0)
465 fatal("%s: schnorr_sign newpub", __func__);
466
467 BN_clear_free(tmp); /* XXX stash for later use? */
468 BN_clear_free(exponent); /* XXX stash for later use? (yes, in conf) */
469
470 BN_CTX_free(bn_ctx);
471}
472
473/* Confirmation hash calculation */
474void
475jpake_confirm_hash(const BIGNUM *k,
476 const u_char *endpoint_id, u_int endpoint_id_len,
477 const u_char *sess_id, u_int sess_id_len,
478 u_char **confirm_hash, u_int *confirm_hash_len)
479{
480 Buffer b;
481
482 /*
483 * Calculate confirmation proof:
484 * client: H(k || client_id || session_id)
485 * server: H(k || server_id || session_id)
486 */
487 buffer_init(&b);
488 buffer_put_bignum2(&b, k);
489 buffer_put_string(&b, endpoint_id, endpoint_id_len);
490 buffer_put_string(&b, sess_id, sess_id_len);
491 if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(),
492 confirm_hash, confirm_hash_len) != 0)
493 fatal("%s: hash_buffer", __func__);
494 buffer_free(&b);
495}
496
497/* Shared parts of key derivation and confirmation calculation */
498void
499jpake_key_confirm(struct jpake_group *grp, BIGNUM *s, BIGNUM *step2_val,
500 BIGNUM *mypriv2, BIGNUM *mypub1, BIGNUM *mypub2,
501 BIGNUM *theirpub1, BIGNUM *theirpub2,
502 const u_char *my_id, u_int my_id_len,
503 const u_char *their_id, u_int their_id_len,
504 const u_char *sess_id, u_int sess_id_len,
505 const u_char *theirpriv2_s_proof, u_int theirpriv2_s_proof_len,
506 BIGNUM **k,
507 u_char **confirm_hash, u_int *confirm_hash_len)
508{
509 BN_CTX *bn_ctx;
510 BIGNUM *tmp;
511
512 if ((bn_ctx = BN_CTX_new()) == NULL)
513 fatal("%s: BN_CTX_new", __func__);
514 if ((tmp = BN_new()) == NULL ||
515 (*k = BN_new()) == NULL)
516 fatal("%s: BN_new", __func__);
517
518 /* Validate step 2 values */
519 if (BN_cmp(step2_val, BN_value_one()) <= 0)
520 fatal("%s: step2_val <= 1", __func__);
521
522 /*
523 * theirpriv2_s_proof is calculated with a different generator:
524 * tmp = g^(mypriv1+mypriv2+theirpub1) = g^mypub1*g^mypub2*g^theirpub1
525 * Calculate it here so we can check the signature.
526 */
527 if (BN_mod_mul(tmp, mypub1, mypub2, grp->p, bn_ctx) != 1)
528 fatal("%s: BN_mod_mul (tmp = mypub1 * mypub2 mod p)", __func__);
529 if (BN_mod_mul(tmp, tmp, theirpub1, grp->p, bn_ctx) != 1)
530 fatal("%s: BN_mod_mul (tmp = tmp * theirpub1 mod p)", __func__);
531
532 JPAKE_DEBUG_BN((tmp, "%s: tmp = ", __func__));
533
534 if (schnorr_verify(grp->p, grp->q, tmp, step2_val,
535 their_id, their_id_len,
536 theirpriv2_s_proof, theirpriv2_s_proof_len) != 1)
537 fatal("%s: schnorr_verify theirpriv2_s_proof failed", __func__);
538
539 /*
540 * Derive shared key:
541 * client: k = (b / g^(x2*x4*s))^x2 = g^((x1+x3)*x2*x4*s)
542 * server: k = (a / g^(x2*x4*s))^x4 = g^((x1+x3)*x2*x4*s)
543 *
544 * Computed as:
545 * client: k = (g_x4^(q - (x2 * s)) * b)^x2 mod p
546 * server: k = (g_x2^(q - (x4 * s)) * b)^x4 mod p
547 */
548 if (BN_mul(tmp, mypriv2, s, bn_ctx) != 1)
549 fatal("%s: BN_mul (tmp = mypriv2 * s)", __func__);
550 if (BN_mod_sub(tmp, grp->q, tmp, grp->q, bn_ctx) != 1)
551 fatal("%s: BN_mod_sub (tmp = q - tmp mod q)", __func__);
552 if (BN_mod_exp(tmp, theirpub2, tmp, grp->p, bn_ctx) != 1)
553 fatal("%s: BN_mod_exp (tmp = theirpub2^tmp) mod p", __func__);
554 if (BN_mod_mul(tmp, tmp, step2_val, grp->p, bn_ctx) != 1)
555 fatal("%s: BN_mod_mul (tmp = tmp * step2_val) mod p", __func__);
556 if (BN_mod_exp(*k, tmp, mypriv2, grp->p, bn_ctx) != 1)
557 fatal("%s: BN_mod_exp (k = tmp^mypriv2) mod p", __func__);
558
559 BN_CTX_free(bn_ctx);
560 BN_clear_free(tmp);
561
562 jpake_confirm_hash(*k, my_id, my_id_len, sess_id, sess_id_len,
563 confirm_hash, confirm_hash_len);
564}
565
566/*
567 * Calculate and check confirmation hash from peer. Returns 1 on success
568 * 0 on failure/mismatch.
569 */
570int
571jpake_check_confirm(const BIGNUM *k,
572 const u_char *peer_id, u_int peer_id_len,
573 const u_char *sess_id, u_int sess_id_len,
574 const u_char *peer_confirm_hash, u_int peer_confirm_hash_len)
575{
576 u_char *expected_confirm_hash;
577 u_int expected_confirm_hash_len;
578 int success = 0;
579
580 /* Calculate and verify expected confirmation hash */
581 jpake_confirm_hash(k, peer_id, peer_id_len, sess_id, sess_id_len,
582 &expected_confirm_hash, &expected_confirm_hash_len);
583
584 JPAKE_DEBUG_BUF((expected_confirm_hash, expected_confirm_hash_len,
585 "%s: expected confirm hash", __func__));
586 JPAKE_DEBUG_BUF((peer_confirm_hash, peer_confirm_hash_len,
587 "%s: received confirm hash", __func__));
588
589 if (peer_confirm_hash_len != expected_confirm_hash_len)
590 error("%s: confirmation length mismatch (my %u them %u)",
591 __func__, expected_confirm_hash_len, peer_confirm_hash_len);
592 else if (memcmp(peer_confirm_hash, expected_confirm_hash,
593 expected_confirm_hash_len) == 0)
594 success = 1;
595 bzero(expected_confirm_hash, expected_confirm_hash_len);
596 xfree(expected_confirm_hash);
597 debug3("%s: success = %d", __func__, success);
598 return success;
599}
600
601/* XXX main() function with tests */
602
603#endif /* JPAKE */
604
diff --git a/jpake.h b/jpake.h
new file mode 100644
index 000000000..a3d800cd3
--- /dev/null
+++ b/jpake.h
@@ -0,0 +1,134 @@
1/* $OpenBSD: jpake.h,v 1.1 2008/11/04 08:22:13 djm Exp $ */
2/*
3 * Copyright (c) 2008 Damien Miller. All rights reserved.
4 *
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.
8 *
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.
16 */
17
18#ifndef JPAKE_H
19#define JPAKE_H
20
21#include <sys/types.h>
22
23#include <openssl/bn.h>
24
25/* Set JPAKE_DEBUG in CFLAGS for privacy-violating debugging */
26#ifndef JPAKE_DEBUG
27# define JPAKE_DEBUG_BN(a)
28# define JPAKE_DEBUG_BUF(a)
29# define JPAKE_DEBUG_CTX(a)
30#else
31# define JPAKE_DEBUG_BN(a) jpake_debug3_bn a
32# define JPAKE_DEBUG_BUF(a) jpake_debug3_buf a
33# define JPAKE_DEBUG_CTX(a) jpake_dump a
34#endif /* SCHNORR_DEBUG */
35
36struct jpake_group {
37 BIGNUM *p, *q, *g;
38};
39
40#define KZP_ID_LEN 16 /* Length of client and server IDs */
41
42struct jpake_ctx {
43 /* Parameters */
44 struct jpake_group *grp;
45
46 /* Private values shared by client and server */
47 BIGNUM *s; /* Secret (salted, crypted password) */
48 BIGNUM *k; /* Derived key */
49
50 /* Client private values (NULL for server) */
51 BIGNUM *x1; /* random in Zq */
52 BIGNUM *x2; /* random in Z*q */
53
54 /* Server private values (NULL for server) */
55 BIGNUM *x3; /* random in Zq */
56 BIGNUM *x4; /* random in Z*q */
57
58 /* Step 1: C->S */
59 u_char *client_id; /* Anti-replay nonce */
60 u_int client_id_len;
61 BIGNUM *g_x1; /* g^x1 */
62 BIGNUM *g_x2; /* g^x2 */
63
64 /* Step 1: S->C */
65 u_char *server_id; /* Anti-replay nonce */
66 u_int server_id_len;
67 BIGNUM *g_x3; /* g^x3 */
68 BIGNUM *g_x4; /* g^x4 */
69
70 /* Step 2: C->S */
71 BIGNUM *a; /* g^((x1+x3+x4)*x2*s) */
72
73 /* Step 2: S->C */
74 BIGNUM *b; /* g^((x1+x2+x3)*x4*s) */
75
76 /* Confirmation: C->S */
77 u_char *h_k_cid_sessid; /* H(k || client_id || session_id) */
78 u_int h_k_cid_sessid_len;
79
80 /* Confirmation: S->C */
81 u_char *h_k_sid_sessid; /* H(k || server_id || session_id) */
82 u_int h_k_sid_sessid_len;
83};
84
85/* jpake.c */
86struct jpake_group *jpake_default_group(void);
87BIGNUM *bn_rand_range_gt_one(const BIGNUM *high);
88int hash_buffer(const u_char *, u_int, const EVP_MD *, u_char **, u_int *);
89void jpake_debug3_bn(const BIGNUM *, const char *, ...)
90 __attribute__((__nonnull__ (2)))
91 __attribute__((format(printf, 2, 3)));
92void jpake_debug3_buf(const u_char *, u_int, const char *, ...)
93 __attribute__((__nonnull__ (3)))
94 __attribute__((format(printf, 3, 4)));
95void jpake_dump(struct jpake_ctx *, const char *, ...)
96 __attribute__((__nonnull__ (2)))
97 __attribute__((format(printf, 2, 3)));
98struct jpake_ctx *jpake_new(void);
99void jpake_free(struct jpake_ctx *);
100
101void jpake_step1(struct jpake_group *, u_char **, u_int *,
102 BIGNUM **, BIGNUM **, BIGNUM **, BIGNUM **,
103 u_char **, u_int *, u_char **, u_int *);
104
105void jpake_step2(struct jpake_group *, BIGNUM *,
106 BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
107 const u_char *, u_int, const u_char *, u_int,
108 const u_char *, u_int, const u_char *, u_int,
109 BIGNUM **, u_char **, u_int *);
110
111void jpake_confirm_hash(const BIGNUM *,
112 const u_char *, u_int,
113 const u_char *, u_int,
114 u_char **, u_int *);
115
116void jpake_key_confirm(struct jpake_group *, BIGNUM *, BIGNUM *,
117 BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
118 const u_char *, u_int, const u_char *, u_int,
119 const u_char *, u_int, const u_char *, u_int,
120 BIGNUM **, u_char **, u_int *);
121
122int jpake_check_confirm(const BIGNUM *, const u_char *, u_int,
123 const u_char *, u_int, const u_char *, u_int);
124
125/* schnorr.c */
126int schnorr_sign(const BIGNUM *, const BIGNUM *, const BIGNUM *,
127 const BIGNUM *, const BIGNUM *, const u_char *, u_int ,
128 u_char **, u_int *);
129int schnorr_verify(const BIGNUM *, const BIGNUM *, const BIGNUM *,
130 const BIGNUM *, const u_char *, u_int,
131 const u_char *, u_int);
132
133#endif /* JPAKE_H */
134
diff --git a/monitor.c b/monitor.c
index 73cf6bc9b..39deedc8f 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: monitor.c,v 1.99 2008/07/10 18:08:11 markus Exp $ */ 1/* $OpenBSD: monitor.c,v 1.100 2008/11/04 08:22:13 djm Exp $ */
2/* 2/*
3 * Copyright 2002 Niels Provos <provos@citi.umich.edu> 3 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
4 * Copyright 2002 Markus Friedl <markus@openbsd.org> 4 * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -87,6 +87,7 @@
87#include "misc.h" 87#include "misc.h"
88#include "compat.h" 88#include "compat.h"
89#include "ssh2.h" 89#include "ssh2.h"
90#include "jpake.h"
90 91
91#ifdef GSSAPI 92#ifdef GSSAPI
92static Gssctxt *gsscontext = NULL; 93static Gssctxt *gsscontext = NULL;
@@ -149,6 +150,11 @@ int mm_answer_rsa_challenge(int, Buffer *);
149int mm_answer_rsa_response(int, Buffer *); 150int mm_answer_rsa_response(int, Buffer *);
150int mm_answer_sesskey(int, Buffer *); 151int mm_answer_sesskey(int, Buffer *);
151int mm_answer_sessid(int, Buffer *); 152int mm_answer_sessid(int, Buffer *);
153int mm_answer_jpake_get_pwdata(int, Buffer *);
154int mm_answer_jpake_step1(int, Buffer *);
155int mm_answer_jpake_step2(int, Buffer *);
156int mm_answer_jpake_key_confirm(int, Buffer *);
157int mm_answer_jpake_check_confirm(int, Buffer *);
152 158
153#ifdef USE_PAM 159#ifdef USE_PAM
154int mm_answer_pam_start(int, Buffer *); 160int mm_answer_pam_start(int, Buffer *);
@@ -234,6 +240,13 @@ struct mon_table mon_dispatch_proto20[] = {
234 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, 240 {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
235 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic}, 241 {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
236#endif 242#endif
243#ifdef JPAKE
244 {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata},
245 {MONITOR_REQ_JPAKE_STEP1, MON_ISAUTH, mm_answer_jpake_step1},
246 {MONITOR_REQ_JPAKE_STEP2, MON_ONCE, mm_answer_jpake_step2},
247 {MONITOR_REQ_JPAKE_KEY_CONFIRM, MON_ONCE, mm_answer_jpake_key_confirm},
248 {MONITOR_REQ_JPAKE_CHECK_CONFIRM, MON_AUTH, mm_answer_jpake_check_confirm},
249#endif
237 {0, 0, NULL} 250 {0, 0, NULL}
238}; 251};
239 252
@@ -379,6 +392,15 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
379 if (!authenticated) 392 if (!authenticated)
380 authctxt->failures++; 393 authctxt->failures++;
381 } 394 }
395#ifdef JPAKE
396 /* Cleanup JPAKE context after authentication */
397 if (ent->flags & MON_AUTHDECIDE) {
398 if (authctxt->jpake_ctx != NULL) {
399 jpake_free(authctxt->jpake_ctx);
400 authctxt->jpake_ctx = NULL;
401 }
402 }
403#endif
382 } 404 }
383 405
384 if (!authctxt->valid) 406 if (!authctxt->valid)
@@ -1969,3 +1991,206 @@ mm_answer_gss_userok(int sock, Buffer *m)
1969 return (authenticated); 1991 return (authenticated);
1970} 1992}
1971#endif /* GSSAPI */ 1993#endif /* GSSAPI */
1994
1995#ifdef JPAKE
1996int
1997mm_answer_jpake_step1(int sock, Buffer *m)
1998{
1999 struct jpake_ctx *pctx;
2000 u_char *x3_proof, *x4_proof;
2001 u_int x3_proof_len, x4_proof_len;
2002
2003 if (!options.zero_knowledge_password_authentication)
2004 fatal("zero_knowledge_password_authentication disabled");
2005
2006 if (authctxt->jpake_ctx != NULL)
2007 fatal("%s: authctxt->jpake_ctx already set (%p)",
2008 __func__, authctxt->jpake_ctx);
2009 authctxt->jpake_ctx = pctx = jpake_new();
2010
2011 jpake_step1(pctx->grp,
2012 &pctx->server_id, &pctx->server_id_len,
2013 &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4,
2014 &x3_proof, &x3_proof_len,
2015 &x4_proof, &x4_proof_len);
2016
2017 JPAKE_DEBUG_CTX((pctx, "step1 done in %s", __func__));
2018
2019 buffer_clear(m);
2020
2021 buffer_put_string(m, pctx->server_id, pctx->server_id_len);
2022 buffer_put_bignum2(m, pctx->g_x3);
2023 buffer_put_bignum2(m, pctx->g_x4);
2024 buffer_put_string(m, x3_proof, x3_proof_len);
2025 buffer_put_string(m, x4_proof, x4_proof_len);
2026
2027 debug3("%s: sending step1", __func__);
2028 mm_request_send(sock, MONITOR_ANS_JPAKE_STEP1, m);
2029
2030 bzero(x3_proof, x3_proof_len);
2031 bzero(x4_proof, x4_proof_len);
2032 xfree(x3_proof);
2033 xfree(x4_proof);
2034
2035 monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_GET_PWDATA, 1);
2036 monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 0);
2037
2038 return 0;
2039}
2040
2041int
2042mm_answer_jpake_get_pwdata(int sock, Buffer *m)
2043{
2044 struct jpake_ctx *pctx = authctxt->jpake_ctx;
2045 char *hash_scheme, *salt;
2046
2047 if (pctx == NULL)
2048 fatal("%s: pctx == NULL", __func__);
2049
2050 auth2_jpake_get_pwdata(authctxt, &pctx->s, &hash_scheme, &salt);
2051
2052 buffer_clear(m);
2053 /* pctx->s is sensitive, not returned to slave */
2054 buffer_put_cstring(m, hash_scheme);
2055 buffer_put_cstring(m, salt);
2056
2057 debug3("%s: sending pwdata", __func__);
2058 mm_request_send(sock, MONITOR_ANS_JPAKE_GET_PWDATA, m);
2059
2060 bzero(hash_scheme, strlen(hash_scheme));
2061 bzero(salt, strlen(salt));
2062 xfree(hash_scheme);
2063 xfree(salt);
2064
2065 monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP2, 1);
2066
2067 return 0;
2068}
2069
2070int
2071mm_answer_jpake_step2(int sock, Buffer *m)
2072{
2073 struct jpake_ctx *pctx = authctxt->jpake_ctx;
2074 u_char *x1_proof, *x2_proof, *x4_s_proof;
2075 u_int x1_proof_len, x2_proof_len, x4_s_proof_len;
2076
2077 if (pctx == NULL)
2078 fatal("%s: pctx == NULL", __func__);
2079
2080 if ((pctx->g_x1 = BN_new()) == NULL ||
2081 (pctx->g_x2 = BN_new()) == NULL)
2082 fatal("%s: BN_new", __func__);
2083 buffer_get_bignum2(m, pctx->g_x1);
2084 buffer_get_bignum2(m, pctx->g_x2);
2085 pctx->client_id = buffer_get_string(m, &pctx->client_id_len);
2086 x1_proof = buffer_get_string(m, &x1_proof_len);
2087 x2_proof = buffer_get_string(m, &x2_proof_len);
2088
2089 jpake_step2(pctx->grp, pctx->s, pctx->g_x3,
2090 pctx->g_x1, pctx->g_x2, pctx->x4,
2091 pctx->client_id, pctx->client_id_len,
2092 pctx->server_id, pctx->server_id_len,
2093 x1_proof, x1_proof_len,
2094 x2_proof, x2_proof_len,
2095 &pctx->b,
2096 &x4_s_proof, &x4_s_proof_len);
2097
2098 JPAKE_DEBUG_CTX((pctx, "step2 done in %s", __func__));
2099
2100 bzero(x1_proof, x1_proof_len);
2101 bzero(x2_proof, x2_proof_len);
2102 xfree(x1_proof);
2103 xfree(x2_proof);
2104
2105 buffer_clear(m);
2106
2107 buffer_put_bignum2(m, pctx->b);
2108 buffer_put_string(m, x4_s_proof, x4_s_proof_len);
2109
2110 debug3("%s: sending step2", __func__);
2111 mm_request_send(sock, MONITOR_ANS_JPAKE_STEP2, m);
2112
2113 bzero(x4_s_proof, x4_s_proof_len);
2114 xfree(x4_s_proof);
2115
2116 monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_KEY_CONFIRM, 1);
2117
2118 return 0;
2119}
2120
2121int
2122mm_answer_jpake_key_confirm(int sock, Buffer *m)
2123{
2124 struct jpake_ctx *pctx = authctxt->jpake_ctx;
2125 u_char *x2_s_proof;
2126 u_int x2_s_proof_len;
2127
2128 if (pctx == NULL)
2129 fatal("%s: pctx == NULL", __func__);
2130
2131 if ((pctx->a = BN_new()) == NULL)
2132 fatal("%s: BN_new", __func__);
2133 buffer_get_bignum2(m, pctx->a);
2134 x2_s_proof = buffer_get_string(m, &x2_s_proof_len);
2135
2136 jpake_key_confirm(pctx->grp, pctx->s, pctx->a,
2137 pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2,
2138 pctx->server_id, pctx->server_id_len,
2139 pctx->client_id, pctx->client_id_len,
2140 session_id2, session_id2_len,
2141 x2_s_proof, x2_s_proof_len,
2142 &pctx->k,
2143 &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len);
2144
2145 JPAKE_DEBUG_CTX((pctx, "key_confirm done in %s", __func__));
2146
2147 bzero(x2_s_proof, x2_s_proof_len);
2148 buffer_clear(m);
2149
2150 /* pctx->k is sensitive, not sent */
2151 buffer_put_string(m, pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
2152
2153 debug3("%s: sending confirmation hash", __func__);
2154 mm_request_send(sock, MONITOR_ANS_JPAKE_KEY_CONFIRM, m);
2155
2156 monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_CHECK_CONFIRM, 1);
2157
2158 return 0;
2159}
2160
2161int
2162mm_answer_jpake_check_confirm(int sock, Buffer *m)
2163{
2164 int authenticated = 0;
2165 u_char *peer_confirm_hash;
2166 u_int peer_confirm_hash_len;
2167 struct jpake_ctx *pctx = authctxt->jpake_ctx;
2168
2169 if (pctx == NULL)
2170 fatal("%s: pctx == NULL", __func__);
2171
2172 peer_confirm_hash = buffer_get_string(m, &peer_confirm_hash_len);
2173
2174 authenticated = jpake_check_confirm(pctx->k,
2175 pctx->client_id, pctx->client_id_len,
2176 session_id2, session_id2_len,
2177 peer_confirm_hash, peer_confirm_hash_len) && authctxt->valid;
2178
2179 JPAKE_DEBUG_CTX((pctx, "check_confirm done in %s", __func__));
2180
2181 bzero(peer_confirm_hash, peer_confirm_hash_len);
2182 xfree(peer_confirm_hash);
2183
2184 buffer_clear(m);
2185 buffer_put_int(m, authenticated);
2186
2187 debug3("%s: sending result %d", __func__, authenticated);
2188 mm_request_send(sock, MONITOR_ANS_JPAKE_CHECK_CONFIRM, m);
2189
2190 monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 1);
2191
2192 auth_method = "jpake-01@openssh.com";
2193 return authenticated;
2194}
2195
2196#endif /* JPAKE */
diff --git a/monitor.h b/monitor.h
index 464009ad8..a8a2c0c19 100644
--- a/monitor.h
+++ b/monitor.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: monitor.h,v 1.14 2006/03/25 22:22:43 djm Exp $ */ 1/* $OpenBSD: monitor.h,v 1.15 2008/11/04 08:22:13 djm Exp $ */
2 2
3/* 3/*
4 * Copyright 2002 Niels Provos <provos@citi.umich.edu> 4 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
@@ -60,7 +60,12 @@ enum monitor_reqtype {
60 MONITOR_REQ_PAM_RESPOND, MONITOR_ANS_PAM_RESPOND, 60 MONITOR_REQ_PAM_RESPOND, MONITOR_ANS_PAM_RESPOND,
61 MONITOR_REQ_PAM_FREE_CTX, MONITOR_ANS_PAM_FREE_CTX, 61 MONITOR_REQ_PAM_FREE_CTX, MONITOR_ANS_PAM_FREE_CTX,
62 MONITOR_REQ_AUDIT_EVENT, MONITOR_REQ_AUDIT_COMMAND, 62 MONITOR_REQ_AUDIT_EVENT, MONITOR_REQ_AUDIT_COMMAND,
63 MONITOR_REQ_TERM 63 MONITOR_REQ_TERM,
64 MONITOR_REQ_JPAKE_STEP1, MONITOR_ANS_JPAKE_STEP1,
65 MONITOR_REQ_JPAKE_GET_PWDATA, MONITOR_ANS_JPAKE_GET_PWDATA,
66 MONITOR_REQ_JPAKE_STEP2, MONITOR_ANS_JPAKE_STEP2,
67 MONITOR_REQ_JPAKE_KEY_CONFIRM, MONITOR_ANS_JPAKE_KEY_CONFIRM,
68 MONITOR_REQ_JPAKE_CHECK_CONFIRM, MONITOR_ANS_JPAKE_CHECK_CONFIRM,
64}; 69};
65 70
66struct mm_master; 71struct mm_master;
diff --git a/monitor_wrap.c b/monitor_wrap.c
index 40463d078..0986fc518 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: monitor_wrap.c,v 1.63 2008/07/10 18:08:11 markus Exp $ */ 1/* $OpenBSD: monitor_wrap.c,v 1.64 2008/11/04 08:22:13 djm Exp $ */
2/* 2/*
3 * Copyright 2002 Niels Provos <provos@citi.umich.edu> 3 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
4 * Copyright 2002 Markus Friedl <markus@openbsd.org> 4 * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -40,6 +40,7 @@
40 40
41#include <openssl/bn.h> 41#include <openssl/bn.h>
42#include <openssl/dh.h> 42#include <openssl/dh.h>
43#include <openssl/evp.h>
43 44
44#include "openbsd-compat/sys-queue.h" 45#include "openbsd-compat/sys-queue.h"
45#include "xmalloc.h" 46#include "xmalloc.h"
@@ -70,7 +71,7 @@
70#include "atomicio.h" 71#include "atomicio.h"
71#include "monitor_fdpass.h" 72#include "monitor_fdpass.h"
72#include "misc.h" 73#include "misc.h"
73#include "servconf.h" 74#include "jpake.h"
74 75
75#include "channels.h" 76#include "channels.h"
76#include "session.h" 77#include "session.h"
@@ -1256,3 +1257,165 @@ mm_ssh_gssapi_userok(char *user)
1256 return (authenticated); 1257 return (authenticated);
1257} 1258}
1258#endif /* GSSAPI */ 1259#endif /* GSSAPI */
1260
1261#ifdef JPAKE
1262void
1263mm_auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s,
1264 char **hash_scheme, char **salt)
1265{
1266 Buffer m;
1267
1268 debug3("%s entering", __func__);
1269
1270 buffer_init(&m);
1271 mm_request_send(pmonitor->m_recvfd,
1272 MONITOR_REQ_JPAKE_GET_PWDATA, &m);
1273
1274 debug3("%s: waiting for MONITOR_ANS_JPAKE_GET_PWDATA", __func__);
1275 mm_request_receive_expect(pmonitor->m_recvfd,
1276 MONITOR_ANS_JPAKE_GET_PWDATA, &m);
1277
1278 *hash_scheme = buffer_get_string(&m, NULL);
1279 *salt = buffer_get_string(&m, NULL);
1280
1281 buffer_free(&m);
1282}
1283
1284void
1285mm_jpake_step1(struct jpake_group *grp,
1286 u_char **id, u_int *id_len,
1287 BIGNUM **priv1, BIGNUM **priv2, BIGNUM **g_priv1, BIGNUM **g_priv2,
1288 u_char **priv1_proof, u_int *priv1_proof_len,
1289 u_char **priv2_proof, u_int *priv2_proof_len)
1290{
1291 Buffer m;
1292
1293 debug3("%s entering", __func__);
1294
1295 buffer_init(&m);
1296 mm_request_send(pmonitor->m_recvfd,
1297 MONITOR_REQ_JPAKE_STEP1, &m);
1298
1299 debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP1", __func__);
1300 mm_request_receive_expect(pmonitor->m_recvfd,
1301 MONITOR_ANS_JPAKE_STEP1, &m);
1302
1303 if ((*priv1 = BN_new()) == NULL ||
1304 (*priv2 = BN_new()) == NULL ||
1305 (*g_priv1 = BN_new()) == NULL ||
1306 (*g_priv2 = BN_new()) == NULL)
1307 fatal("%s: BN_new", __func__);
1308
1309 *id = buffer_get_string(&m, id_len);
1310 /* priv1 and priv2 are, well, private */
1311 buffer_get_bignum2(&m, *g_priv1);
1312 buffer_get_bignum2(&m, *g_priv2);
1313 *priv1_proof = buffer_get_string(&m, priv1_proof_len);
1314 *priv2_proof = buffer_get_string(&m, priv2_proof_len);
1315
1316 buffer_free(&m);
1317}
1318
1319void
1320mm_jpake_step2(struct jpake_group *grp, BIGNUM *s,
1321 BIGNUM *mypub1, BIGNUM *theirpub1, BIGNUM *theirpub2, BIGNUM *mypriv2,
1322 const u_char *theirid, u_int theirid_len,
1323 const u_char *myid, u_int myid_len,
1324 const u_char *theirpub1_proof, u_int theirpub1_proof_len,
1325 const u_char *theirpub2_proof, u_int theirpub2_proof_len,
1326 BIGNUM **newpub,
1327 u_char **newpub_exponent_proof, u_int *newpub_exponent_proof_len)
1328{
1329 Buffer m;
1330
1331 debug3("%s entering", __func__);
1332
1333 buffer_init(&m);
1334 /* monitor already has all bignums except theirpub1, theirpub2 */
1335 buffer_put_bignum2(&m, theirpub1);
1336 buffer_put_bignum2(&m, theirpub2);
1337 /* monitor already knows our id */
1338 buffer_put_string(&m, theirid, theirid_len);
1339 buffer_put_string(&m, theirpub1_proof, theirpub1_proof_len);
1340 buffer_put_string(&m, theirpub2_proof, theirpub2_proof_len);
1341
1342 mm_request_send(pmonitor->m_recvfd,
1343 MONITOR_REQ_JPAKE_STEP2, &m);
1344
1345 debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP2", __func__);
1346 mm_request_receive_expect(pmonitor->m_recvfd,
1347 MONITOR_ANS_JPAKE_STEP2, &m);
1348
1349 if ((*newpub = BN_new()) == NULL)
1350 fatal("%s: BN_new", __func__);
1351
1352 buffer_get_bignum2(&m, *newpub);
1353 *newpub_exponent_proof = buffer_get_string(&m,
1354 newpub_exponent_proof_len);
1355
1356 buffer_free(&m);
1357}
1358
1359void
1360mm_jpake_key_confirm(struct jpake_group *grp, BIGNUM *s, BIGNUM *step2_val,
1361 BIGNUM *mypriv2, BIGNUM *mypub1, BIGNUM *mypub2,
1362 BIGNUM *theirpub1, BIGNUM *theirpub2,
1363 const u_char *my_id, u_int my_id_len,
1364 const u_char *their_id, u_int their_id_len,
1365 const u_char *sess_id, u_int sess_id_len,
1366 const u_char *theirpriv2_s_proof, u_int theirpriv2_s_proof_len,
1367 BIGNUM **k,
1368 u_char **confirm_hash, u_int *confirm_hash_len)
1369{
1370 Buffer m;
1371
1372 debug3("%s entering", __func__);
1373
1374 buffer_init(&m);
1375 /* monitor already has all bignums except step2_val */
1376 buffer_put_bignum2(&m, step2_val);
1377 /* monitor already knows all the ids */
1378 buffer_put_string(&m, theirpriv2_s_proof, theirpriv2_s_proof_len);
1379
1380 mm_request_send(pmonitor->m_recvfd,
1381 MONITOR_REQ_JPAKE_KEY_CONFIRM, &m);
1382
1383 debug3("%s: waiting for MONITOR_ANS_JPAKE_KEY_CONFIRM", __func__);
1384 mm_request_receive_expect(pmonitor->m_recvfd,
1385 MONITOR_ANS_JPAKE_KEY_CONFIRM, &m);
1386
1387 /* 'k' is sensitive and stays in the monitor */
1388 *confirm_hash = buffer_get_string(&m, confirm_hash_len);
1389
1390 buffer_free(&m);
1391}
1392
1393int
1394mm_jpake_check_confirm(const BIGNUM *k,
1395 const u_char *peer_id, u_int peer_id_len,
1396 const u_char *sess_id, u_int sess_id_len,
1397 const u_char *peer_confirm_hash, u_int peer_confirm_hash_len)
1398{
1399 Buffer m;
1400 int success = 0;
1401
1402 debug3("%s entering", __func__);
1403
1404 buffer_init(&m);
1405 /* k is dummy in slave, ignored */
1406 /* monitor knows all the ids */
1407 buffer_put_string(&m, peer_confirm_hash, peer_confirm_hash_len);
1408 mm_request_send(pmonitor->m_recvfd,
1409 MONITOR_REQ_JPAKE_CHECK_CONFIRM, &m);
1410
1411 debug3("%s: waiting for MONITOR_ANS_JPAKE_CHECK_CONFIRM", __func__);
1412 mm_request_receive_expect(pmonitor->m_recvfd,
1413 MONITOR_ANS_JPAKE_CHECK_CONFIRM, &m);
1414
1415 success = buffer_get_int(&m);
1416 buffer_free(&m);
1417
1418 debug3("%s: success = %d", __func__, success);
1419 return success;
1420}
1421#endif /* JPAKE */
diff --git a/monitor_wrap.h b/monitor_wrap.h
index 329189c2a..55c4b99f3 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: monitor_wrap.h,v 1.20 2006/08/03 03:34:42 deraadt Exp $ */ 1/* $OpenBSD: monitor_wrap.h,v 1.21 2008/11/04 08:22:13 djm Exp $ */
2 2
3/* 3/*
4 * Copyright 2002 Niels Provos <provos@citi.umich.edu> 4 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
@@ -101,6 +101,26 @@ int mm_bsdauth_respond(void *, u_int, char **);
101int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **); 101int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **);
102int mm_skey_respond(void *, u_int, char **); 102int mm_skey_respond(void *, u_int, char **);
103 103
104/* jpake */
105struct jpake_group;
106void mm_auth2_jpake_get_pwdata(struct Authctxt *, BIGNUM **, char **, char **);
107void mm_jpake_step1(struct jpake_group *, u_char **, u_int *,
108 BIGNUM **, BIGNUM **, BIGNUM **, BIGNUM **,
109 u_char **, u_int *, u_char **, u_int *);
110void mm_jpake_step2(struct jpake_group *, BIGNUM *,
111 BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
112 const u_char *, u_int, const u_char *, u_int,
113 const u_char *, u_int, const u_char *, u_int,
114 BIGNUM **, u_char **, u_int *);
115void mm_jpake_key_confirm(struct jpake_group *, BIGNUM *, BIGNUM *,
116 BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
117 const u_char *, u_int, const u_char *, u_int,
118 const u_char *, u_int, const u_char *, u_int,
119 BIGNUM **, u_char **, u_int *);
120int mm_jpake_check_confirm(const BIGNUM *,
121 const u_char *, u_int, const u_char *, u_int, const u_char *, u_int);
122
123
104/* zlib allocation hooks */ 124/* zlib allocation hooks */
105 125
106void *mm_zalloc(struct mm_master *, u_int, u_int); 126void *mm_zalloc(struct mm_master *, u_int, u_int);
diff --git a/readconf.c b/readconf.c
index 7f7bbfee2..ba70d9da0 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.c,v 1.170 2008/11/03 02:44:41 stevesk Exp $ */ 1/* $OpenBSD: readconf.c,v 1.171 2008/11/04 08:22:13 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -130,7 +130,7 @@ typedef enum {
130 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, 130 oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
131 oSendEnv, oControlPath, oControlMaster, oHashKnownHosts, 131 oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
132 oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, 132 oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
133 oVisualHostKey, 133 oVisualHostKey, oZeroKnowledgePasswordAuthentication,
134 oDeprecated, oUnsupported 134 oDeprecated, oUnsupported
135} OpCodes; 135} OpCodes;
136 136
@@ -228,6 +228,13 @@ static struct {
228 { "localcommand", oLocalCommand }, 228 { "localcommand", oLocalCommand },
229 { "permitlocalcommand", oPermitLocalCommand }, 229 { "permitlocalcommand", oPermitLocalCommand },
230 { "visualhostkey", oVisualHostKey }, 230 { "visualhostkey", oVisualHostKey },
231#ifdef JPAKE
232 { "zeroknowledgepasswordauthentication",
233 oZeroKnowledgePasswordAuthentication },
234#else
235 { "zeroknowledgepasswordauthentication", oUnsupported },
236#endif
237
231 { NULL, oBadOption } 238 { NULL, oBadOption }
232}; 239};
233 240
@@ -412,6 +419,10 @@ parse_flag:
412 intptr = &options->password_authentication; 419 intptr = &options->password_authentication;
413 goto parse_flag; 420 goto parse_flag;
414 421
422 case oZeroKnowledgePasswordAuthentication:
423 intptr = &options->zero_knowledge_password_authentication;
424 goto parse_flag;
425
415 case oKbdInteractiveAuthentication: 426 case oKbdInteractiveAuthentication:
416 intptr = &options->kbd_interactive_authentication; 427 intptr = &options->kbd_interactive_authentication;
417 goto parse_flag; 428 goto parse_flag;
@@ -1054,6 +1065,7 @@ initialize_options(Options * options)
1054 options->local_command = NULL; 1065 options->local_command = NULL;
1055 options->permit_local_command = -1; 1066 options->permit_local_command = -1;
1056 options->visual_host_key = -1; 1067 options->visual_host_key = -1;
1068 options->zero_knowledge_password_authentication = -1;
1057} 1069}
1058 1070
1059/* 1071/*
@@ -1190,6 +1202,8 @@ fill_default_options(Options * options)
1190 options->permit_local_command = 0; 1202 options->permit_local_command = 0;
1191 if (options->visual_host_key == -1) 1203 if (options->visual_host_key == -1)
1192 options->visual_host_key = 0; 1204 options->visual_host_key = 0;
1205 if (options->zero_knowledge_password_authentication == -1)
1206 options->zero_knowledge_password_authentication = 0;
1193 /* options->local_command should not be set by default */ 1207 /* options->local_command should not be set by default */
1194 /* options->proxy_command should not be set by default */ 1208 /* options->proxy_command should not be set by default */
1195 /* options->user will be set in the main program if appropriate */ 1209 /* options->user will be set in the main program if appropriate */
diff --git a/readconf.h b/readconf.h
index c1387a896..c9e5f6a41 100644
--- a/readconf.h
+++ b/readconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.h,v 1.75 2008/11/01 17:40:33 stevesk Exp $ */ 1/* $OpenBSD: readconf.h,v 1.76 2008/11/04 08:22:13 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -49,6 +49,7 @@ typedef struct {
49 * authentication. */ 49 * authentication. */
50 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ 50 int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
51 char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ 51 char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */
52 int zero_knowledge_password_authentication; /* Try jpake */
52 int batch_mode; /* Batch mode: do not ask for passwords. */ 53 int batch_mode; /* Batch mode: do not ask for passwords. */
53 int check_host_ip; /* Also keep track of keys for IP address */ 54 int check_host_ip; /* Also keep track of keys for IP address */
54 int strict_host_key_checking; /* Strict host key checking. */ 55 int strict_host_key_checking; /* Strict host key checking. */
diff --git a/schnorr.c b/schnorr.c
new file mode 100644
index 000000000..e3abe5702
--- /dev/null
+++ b/schnorr.c
@@ -0,0 +1,407 @@
1/* $OpenBSD: schnorr.c,v 1.1 2008/11/04 08:22:13 djm Exp $ */
2/*
3 * Copyright (c) 2008 Damien Miller. All rights reserved.
4 *
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.
8 *
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.
16 */
17
18/*
19 * Implementation of Schnorr signatures / zero-knowledge proofs, based on
20 * description in:
21 *
22 * F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
23 * 16th Workshop on Security Protocols, Cambridge, April 2008
24 *
25 * http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
26 */
27
28#include "includes.h"
29
30#include <sys/types.h>
31
32#include <string.h>
33#include <stdarg.h>
34#include <stdio.h>
35
36#include <openssl/evp.h>
37#include <openssl/bn.h>
38
39#include "xmalloc.h"
40#include "buffer.h"
41#include "log.h"
42
43#include "jpake.h"
44
45/* #define SCHNORR_DEBUG */ /* Privacy-violating debugging */
46/* #define SCHNORR_MAIN */ /* Include main() selftest */
47
48/* XXX */
49/* Parametise signature hash? (sha256, sha1, etc.) */
50/* Signature format - include type name, hash type, group params? */
51
52#ifndef SCHNORR_DEBUG
53# define SCHNORR_DEBUG_BN(a)
54# define SCHNORR_DEBUG_BUF(a)
55#else
56# define SCHNORR_DEBUG_BN(a) jpake_debug3_bn a
57# define SCHNORR_DEBUG_BUF(a) jpake_debug3_buf a
58#endif /* SCHNORR_DEBUG */
59
60/*
61 * Calculate hash component of Schnorr signature H(g || g^v || g^x || id)
62 * using SHA1. Returns signature as bignum or NULL on error.
63 */
64static BIGNUM *
65schnorr_hash(const BIGNUM *p, const BIGNUM *q, const BIGNUM *g,
66 const BIGNUM *g_v, const BIGNUM *g_x,
67 const u_char *id, u_int idlen)
68{
69 u_char *digest;
70 u_int digest_len;
71 BIGNUM *h;
72 EVP_MD_CTX evp_md_ctx;
73 Buffer b;
74 int success = -1;
75
76 if ((h = BN_new()) == NULL) {
77 error("%s: BN_new", __func__);
78 return NULL;
79 }
80
81 buffer_init(&b);
82 EVP_MD_CTX_init(&evp_md_ctx);
83
84 /* h = H(g || g^v || g^x || id) */
85 buffer_put_bignum2(&b, g);
86 buffer_put_bignum2(&b, g_v);
87 buffer_put_bignum2(&b, g_x);
88 buffer_put_string(&b, id, idlen);
89
90 SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b),
91 "%s: hashblob", __func__));
92 if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(),
93 &digest, &digest_len) != 0) {
94 error("%s: hash_buffer", __func__);
95 goto out;
96 }
97 if (BN_bin2bn(digest, (int)digest_len, h) == NULL) {
98 error("%s: BN_bin2bn", __func__);
99 goto out;
100 }
101 success = 0;
102 SCHNORR_DEBUG_BN((h, "%s: h = ", __func__));
103 out:
104 buffer_free(&b);
105 EVP_MD_CTX_cleanup(&evp_md_ctx);
106 bzero(digest, digest_len);
107 xfree(digest);
108 digest_len = 0;
109 if (success == 0)
110 return h;
111 BN_clear_free(h);
112 return NULL;
113}
114
115/*
116 * Generate Schnorr signature to prove knowledge of private value 'x' used
117 * in public exponent g^x, under group defined by 'grp_p', 'grp_q' and 'grp_g'
118 * 'idlen' bytes from 'id' will be included in the signature hash as an anti-
119 * replay salt.
120 * On success, 0 is returned and *siglen bytes of signature are returned in
121 * *sig (caller to free). Returns -1 on failure.
122 */
123int
124schnorr_sign(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
125 const BIGNUM *x, const BIGNUM *g_x, const u_char *id, u_int idlen,
126 u_char **sig, u_int *siglen)
127{
128 int success = -1;
129 Buffer b;
130 BIGNUM *h, *tmp, *v, *g_v, *r;
131 BN_CTX *bn_ctx;
132
133 SCHNORR_DEBUG_BN((x, "%s: x = ", __func__));
134 SCHNORR_DEBUG_BN((g_x, "%s: g_x = ", __func__));
135
136 /* Avoid degenerate cases: g^0 yields a spoofable signature */
137 if (BN_cmp(g_x, BN_value_one()) <= 0) {
138 error("%s: g_x < 1", __func__);
139 return -1;
140 }
141
142 h = g_v = r = tmp = v = NULL;
143 if ((bn_ctx = BN_CTX_new()) == NULL) {
144 error("%s: BN_CTX_new", __func__);
145 goto out;
146 }
147 if ((g_v = BN_new()) == NULL ||
148 (r = BN_new()) == NULL ||
149 (tmp = BN_new()) == NULL) {
150 error("%s: BN_new", __func__);
151 goto out;
152 }
153
154 /*
155 * v must be a random element of Zq, so 1 <= v < q
156 * we also exclude v = 1, since g^1 looks dangerous
157 */
158 if ((v = bn_rand_range_gt_one(grp_p)) == NULL) {
159 error("%s: bn_rand_range2", __func__);
160 goto out;
161 }
162 SCHNORR_DEBUG_BN((v, "%s: v = ", __func__));
163
164 /* g_v = g^v mod p */
165 if (BN_mod_exp(g_v, grp_g, v, grp_p, bn_ctx) == -1) {
166 error("%s: BN_mod_exp (g^v mod p)", __func__);
167 goto out;
168 }
169 SCHNORR_DEBUG_BN((g_v, "%s: g_v = ", __func__));
170
171 /* h = H(g || g^v || g^x || id) */
172 if ((h = schnorr_hash(grp_p, grp_q, grp_g, g_v, g_x,
173 id, idlen)) == NULL) {
174 error("%s: schnorr_hash failed", __func__);
175 goto out;
176 }
177
178 /* r = v - xh mod q */
179 if (BN_mod_mul(tmp, x, h, grp_q, bn_ctx) == -1) {
180 error("%s: BN_mod_mul (tmp = xv mod q)", __func__);
181 goto out;
182 }
183 if (BN_mod_sub(r, v, tmp, grp_q, bn_ctx) == -1) {
184 error("%s: BN_mod_mul (r = v - tmp)", __func__);
185 goto out;
186 }
187 SCHNORR_DEBUG_BN((r, "%s: r = ", __func__));
188
189 /* Signature is (g_v, r) */
190 buffer_init(&b);
191 /* XXX sigtype-hash as string? */
192 buffer_put_bignum2(&b, g_v);
193 buffer_put_bignum2(&b, r);
194 *siglen = buffer_len(&b);
195 *sig = xmalloc(*siglen);
196 memcpy(*sig, buffer_ptr(&b), *siglen);
197 SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b),
198 "%s: sigblob", __func__));
199 buffer_free(&b);
200 success = 0;
201 out:
202 BN_CTX_free(bn_ctx);
203 if (h != NULL)
204 BN_clear_free(h);
205 if (v != NULL)
206 BN_clear_free(v);
207 BN_clear_free(r);
208 BN_clear_free(g_v);
209 BN_clear_free(tmp);
210
211 return success;
212}
213
214/*
215 * Verify Schnorr signature 'sig' of length 'siglen' against public exponent
216 * g_x (g^x) under group defined by 'grp_p', 'grp_q' and 'grp_g'.
217 * Signature hash will be salted with 'idlen' bytes from 'id'.
218 * Returns -1 on failure, 0 on incorrect signature or 1 on matching signature.
219 */
220int
221schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
222 const BIGNUM *g_x, const u_char *id, u_int idlen,
223 const u_char *sig, u_int siglen)
224{
225 int success = -1;
226 Buffer b;
227 BIGNUM *g_v, *h, *r, *g_xh, *g_r, *expected;
228 BN_CTX *bn_ctx;
229 u_int rlen;
230
231 SCHNORR_DEBUG_BN((g_x, "%s: g_x = ", __func__));
232
233 /* Avoid degenerate cases: g^0 yields a spoofable signature */
234 if (BN_cmp(g_x, BN_value_one()) <= 0) {
235 error("%s: g_x < 1", __func__);
236 return -1;
237 }
238
239 g_v = h = r = g_xh = g_r = expected = NULL;
240 if ((bn_ctx = BN_CTX_new()) == NULL) {
241 error("%s: BN_CTX_new", __func__);
242 goto out;
243 }
244 if ((g_v = BN_new()) == NULL ||
245 (r = BN_new()) == NULL ||
246 (g_xh = BN_new()) == NULL ||
247 (g_r = BN_new()) == NULL ||
248 (expected = BN_new()) == NULL) {
249 error("%s: BN_new", __func__);
250 goto out;
251 }
252
253 /* Extract g^v and r from signature blob */
254 buffer_init(&b);
255 buffer_append(&b, sig, siglen);
256 SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b),
257 "%s: sigblob", __func__));
258 buffer_get_bignum2(&b, g_v);
259 buffer_get_bignum2(&b, r);
260 rlen = buffer_len(&b);
261 buffer_free(&b);
262 if (rlen != 0) {
263 error("%s: remaining bytes in signature %d", __func__, rlen);
264 goto out;
265 }
266 buffer_free(&b);
267 SCHNORR_DEBUG_BN((g_v, "%s: g_v = ", __func__));
268 SCHNORR_DEBUG_BN((r, "%s: r = ", __func__));
269
270 /* h = H(g || g^v || g^x || id) */
271 if ((h = schnorr_hash(grp_p, grp_q, grp_g, g_v, g_x,
272 id, idlen)) == NULL) {
273 error("%s: schnorr_hash failed", __func__);
274 goto out;
275 }
276
277 /* g_xh = (g^x)^h */
278 if (BN_mod_exp(g_xh, g_x, h, grp_p, bn_ctx) == -1) {
279 error("%s: BN_mod_exp (g_x^h mod p)", __func__);
280 goto out;
281 }
282 SCHNORR_DEBUG_BN((g_xh, "%s: g_xh = ", __func__));
283
284 /* g_r = g^r */
285 if (BN_mod_exp(g_r, grp_g, r, grp_p, bn_ctx) == -1) {
286 error("%s: BN_mod_exp (g_x^h mod p)", __func__);
287 goto out;
288 }
289 SCHNORR_DEBUG_BN((g_r, "%s: g_r = ", __func__));
290
291 /* expected = g^r * g_xh */
292 if (BN_mod_mul(expected, g_r, g_xh, grp_p, bn_ctx) == -1) {
293 error("%s: BN_mod_mul (expected = g_r mod p)", __func__);
294 goto out;
295 }
296 SCHNORR_DEBUG_BN((expected, "%s: expected = ", __func__));
297
298 /* Check g_v == expected */
299 success = BN_cmp(expected, g_v) == 0;
300 out:
301 BN_CTX_free(bn_ctx);
302 if (h != NULL)
303 BN_clear_free(h);
304 BN_clear_free(g_v);
305 BN_clear_free(r);
306 BN_clear_free(g_xh);
307 BN_clear_free(g_r);
308 BN_clear_free(expected);
309 return success;
310}
311
312#ifdef SCHNORR_MAIN
313static void
314schnorr_selftest_one(const BIGNUM *grp_p, const BIGNUM *grp_q,
315 const BIGNUM *grp_g, const BIGNUM *x)
316{
317 BIGNUM *g_x;
318 u_char *sig;
319 u_int siglen;
320 BN_CTX *bn_ctx;
321
322 if ((bn_ctx = BN_CTX_new()) == NULL)
323 fatal("%s: BN_CTX_new", __func__);
324 if ((g_x = BN_new()) == NULL)
325 fatal("%s: BN_new", __func__);
326
327 if (BN_mod_exp(g_x, grp_g, x, grp_p, bn_ctx) == -1)
328 fatal("%s: g_x", __func__);
329 if (schnorr_sign(grp_p, grp_q, grp_g, x, g_x, "junk", 4, &sig, &siglen))
330 fatal("%s: schnorr_sign", __func__);
331 if (schnorr_verify(grp_p, grp_q, grp_g, g_x, "junk", 4,
332 sig, siglen) != 1)
333 fatal("%s: verify fail", __func__);
334 if (schnorr_verify(grp_p, grp_q, grp_g, g_x, "JUNK", 4,
335 sig, siglen) != 0)
336 fatal("%s: verify should have failed (bad ID)", __func__);
337 sig[4] ^= 1;
338 if (schnorr_verify(grp_p, grp_q, grp_g, g_x, "junk", 4,
339 sig, siglen) != 0)
340 fatal("%s: verify should have failed (bit error)", __func__);
341 xfree(sig);
342 BN_free(g_x);
343 BN_CTX_free(bn_ctx);
344}
345
346static void
347schnorr_selftest(void)
348{
349 BIGNUM *x;
350 struct jpake_group *grp;
351 u_int i;
352 char *hh;
353
354 grp = jpake_default_group();
355 if ((x = BN_new()) == NULL)
356 fatal("%s: BN_new", __func__);
357 SCHNORR_DEBUG_BN((grp->p, "%s: grp->p = ", __func__));
358 SCHNORR_DEBUG_BN((grp->q, "%s: grp->q = ", __func__));
359 SCHNORR_DEBUG_BN((grp->g, "%s: grp->g = ", __func__));
360
361 /* [1, 20) */
362 for (i = 1; i < 20; i++) {
363 printf("x = %u\n", i);
364 fflush(stdout);
365 if (BN_set_word(x, i) != 1)
366 fatal("%s: set x word", __func__);
367 schnorr_selftest_one(grp->p, grp->q, grp->g, x);
368 }
369
370 /* 100 x random [0, p) */
371 for (i = 0; i < 100; i++) {
372 if (BN_rand_range(x, grp->p) != 1)
373 fatal("%s: BN_rand_range", __func__);
374 hh = BN_bn2hex(x);
375 printf("x = (random) 0x%s\n", hh);
376 free(hh);
377 fflush(stdout);
378 schnorr_selftest_one(grp->p, grp->q, grp->g, x);
379 }
380
381 /* [q-20, q) */
382 if (BN_set_word(x, 20) != 1)
383 fatal("%s: BN_set_word (x = 20)", __func__);
384 if (BN_sub(x, grp->q, x) != 1)
385 fatal("%s: BN_sub (q - x)", __func__);
386 for (i = 0; i < 19; i++) {
387 hh = BN_bn2hex(x);
388 printf("x = (q - %d) 0x%s\n", 20 - i, hh);
389 free(hh);
390 fflush(stdout);
391 schnorr_selftest_one(grp->p, grp->q, grp->g, x);
392 if (BN_add(x, x, BN_value_one()) != 1)
393 fatal("%s: BN_add (x + 1)", __func__);
394 }
395 BN_free(x);
396}
397
398int
399main(int argc, char **argv)
400{
401 log_init(argv[0], SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_USER, 1);
402
403 schnorr_selftest();
404 return 0;
405}
406#endif
407
diff --git a/servconf.c b/servconf.c
index f2d414334..c7d2d0b94 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.c,v 1.189 2008/11/03 08:59:41 djm Exp $ */ 1/* $OpenBSD: servconf.c,v 1.190 2008/11/04 08:22:13 djm Exp $ */
2/* 2/*
3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 * All rights reserved 4 * All rights reserved
@@ -127,6 +127,7 @@ initialize_server_options(ServerOptions *options)
127 options->num_permitted_opens = -1; 127 options->num_permitted_opens = -1;
128 options->adm_forced_command = NULL; 128 options->adm_forced_command = NULL;
129 options->chroot_directory = NULL; 129 options->chroot_directory = NULL;
130 options->zero_knowledge_password_authentication = -1;
130} 131}
131 132
132void 133void
@@ -258,6 +259,8 @@ fill_default_server_options(ServerOptions *options)
258 options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; 259 options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
259 if (options->permit_tun == -1) 260 if (options->permit_tun == -1)
260 options->permit_tun = SSH_TUNMODE_NO; 261 options->permit_tun = SSH_TUNMODE_NO;
262 if (options->zero_knowledge_password_authentication == -1)
263 options->zero_knowledge_password_authentication = 0;
261 264
262 /* Turn privilege separation on by default */ 265 /* Turn privilege separation on by default */
263 if (use_privsep == -1) 266 if (use_privsep == -1)
@@ -302,6 +305,7 @@ typedef enum {
302 sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, 305 sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
303 sMatch, sPermitOpen, sForceCommand, sChrootDirectory, 306 sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
304 sUsePrivilegeSeparation, sAllowAgentForwarding, 307 sUsePrivilegeSeparation, sAllowAgentForwarding,
308 sZeroKnowledgePasswordAuthentication,
305 sDeprecated, sUnsupported 309 sDeprecated, sUnsupported
306} ServerOpCodes; 310} ServerOpCodes;
307 311
@@ -368,6 +372,11 @@ static struct {
368 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, 372 { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
369 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, 373 { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
370 { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ 374 { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */
375#ifdef JPAKE
376 { "zeroknowledgepasswordauthentication", sZeroKnowledgePasswordAuthentication, SSHCFG_ALL },
377#else
378 { "zeroknowledgepasswordauthentication", sUnsupported, SSHCFG_ALL },
379#endif
371 { "checkmail", sDeprecated, SSHCFG_GLOBAL }, 380 { "checkmail", sDeprecated, SSHCFG_GLOBAL },
372 { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, 381 { "listenaddress", sListenAddress, SSHCFG_GLOBAL },
373 { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, 382 { "addressfamily", sAddressFamily, SSHCFG_GLOBAL },
@@ -890,6 +899,10 @@ process_server_config_line(ServerOptions *options, char *line,
890 intptr = &options->password_authentication; 899 intptr = &options->password_authentication;
891 goto parse_flag; 900 goto parse_flag;
892 901
902 case sZeroKnowledgePasswordAuthentication:
903 intptr = &options->zero_knowledge_password_authentication;
904 goto parse_flag;
905
893 case sKbdInteractiveAuthentication: 906 case sKbdInteractiveAuthentication:
894 intptr = &options->kbd_interactive_authentication; 907 intptr = &options->kbd_interactive_authentication;
895 goto parse_flag; 908 goto parse_flag;
@@ -1377,6 +1390,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
1377 M_CP_INTOPT(kerberos_authentication); 1390 M_CP_INTOPT(kerberos_authentication);
1378 M_CP_INTOPT(hostbased_authentication); 1391 M_CP_INTOPT(hostbased_authentication);
1379 M_CP_INTOPT(kbd_interactive_authentication); 1392 M_CP_INTOPT(kbd_interactive_authentication);
1393 M_CP_INTOPT(zero_knowledge_password_authentication);
1380 M_CP_INTOPT(permit_root_login); 1394 M_CP_INTOPT(permit_root_login);
1381 M_CP_INTOPT(permit_empty_passwd); 1395 M_CP_INTOPT(permit_empty_passwd);
1382 1396
@@ -1579,6 +1593,10 @@ dump_config(ServerOptions *o)
1579 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); 1593 dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
1580 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); 1594 dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
1581#endif 1595#endif
1596#ifdef JPAKE
1597 dump_cfg_fmtint(sZeroKnowledgePasswordAuthentication,
1598 o->zero_knowledge_password_authentication);
1599#endif
1582 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); 1600 dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
1583 dump_cfg_fmtint(sKbdInteractiveAuthentication, 1601 dump_cfg_fmtint(sKbdInteractiveAuthentication,
1584 o->kbd_interactive_authentication); 1602 o->kbd_interactive_authentication);
diff --git a/servconf.h b/servconf.h
index 40ac64f13..1d4c3a01a 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.h,v 1.85 2008/06/10 04:50:25 dtucker Exp $ */ 1/* $OpenBSD: servconf.h,v 1.86 2008/11/04 08:22:13 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -96,6 +96,8 @@ typedef struct {
96 * authentication. */ 96 * authentication. */
97 int kbd_interactive_authentication; /* If true, permit */ 97 int kbd_interactive_authentication; /* If true, permit */
98 int challenge_response_authentication; 98 int challenge_response_authentication;
99 int zero_knowledge_password_authentication;
100 /* If true, permit jpake auth */
99 int permit_empty_passwd; /* If false, do not permit empty 101 int permit_empty_passwd; /* If false, do not permit empty
100 * passwords. */ 102 * passwords. */
101 int permit_user_env; /* If true, read ~/.ssh/environment */ 103 int permit_user_env; /* If true, read ~/.ssh/environment */
diff --git a/ssh2.h b/ssh2.h
index cf56bc4ee..1c33dc268 100644
--- a/ssh2.h
+++ b/ssh2.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh2.h,v 1.10 2006/03/25 22:22:43 djm Exp $ */ 1/* $OpenBSD: ssh2.h,v 1.11 2008/11/04 08:22:13 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -111,6 +111,12 @@
111#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 111#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60
112#define SSH2_MSG_USERAUTH_INFO_REQUEST 60 112#define SSH2_MSG_USERAUTH_INFO_REQUEST 60
113#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61 113#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61
114#define SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1 60
115#define SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1 61
116#define SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2 62
117#define SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2 63
118#define SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM 64
119#define SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM 65
114 120
115/* connection protocol: generic */ 121/* connection protocol: generic */
116 122
@@ -159,3 +165,4 @@
159#define SSH2_OPEN_RESOURCE_SHORTAGE 4 165#define SSH2_OPEN_RESOURCE_SHORTAGE 4
160 166
161#define SSH2_EXTENDED_DATA_STDERR 1 167#define SSH2_EXTENDED_DATA_STDERR 1
168
diff --git a/ssh_config.5 b/ssh_config.5
index 254940ef8..abc3b0b16 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -34,8 +34,8 @@
34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36.\" 36.\"
37.\" $OpenBSD: ssh_config.5,v 1.114 2008/10/17 18:36:24 stevesk Exp $ 37.\" $OpenBSD: ssh_config.5,v 1.115 2008/11/04 08:22:13 djm Exp $
38.Dd $Mdocdate: October 17 2008 $ 38.Dd $Mdocdate: November 4 2008 $
39.Dt SSH_CONFIG 5 39.Dt SSH_CONFIG 5
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -1079,6 +1079,17 @@ Specifies the full pathname of the
1079program. 1079program.
1080The default is 1080The default is
1081.Pa /usr/X11R6/bin/xauth . 1081.Pa /usr/X11R6/bin/xauth .
1082.It Cm ZeroKnowledgePasswordAuthentication
1083Specifies whether to use zero knowledge password authentication.
1084This authentication method avoids exposure of password to untrusted
1085hosts.
1086The argument to this keyword must be
1087.Dq yes
1088or
1089.Dq no .
1090The default is currently
1091.Dq no
1092as this method is considered experimental.
1082.El 1093.El
1083.Sh PATTERNS 1094.Sh PATTERNS
1084A 1095A
diff --git a/sshconnect2.c b/sshconnect2.c
index 7d0c5e825..a762eec3b 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,6 +1,7 @@
1/* $OpenBSD: sshconnect2.c,v 1.169 2008/11/01 04:50:08 djm Exp $ */ 1/* $OpenBSD: sshconnect2.c,v 1.170 2008/11/04 08:22:13 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2008 Damien Miller. All rights reserved.
4 * 5 *
5 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 7 * modification, are permitted provided that the following conditions
@@ -67,6 +68,7 @@
67#include "msg.h" 68#include "msg.h"
68#include "pathnames.h" 69#include "pathnames.h"
69#include "uidswap.h" 70#include "uidswap.h"
71#include "jpake.h"
70 72
71#ifdef GSSAPI 73#ifdef GSSAPI
72#include "ssh-gss.h" 74#include "ssh-gss.h"
@@ -201,6 +203,7 @@ struct Authctxt {
201struct Authmethod { 203struct Authmethod {
202 char *name; /* string to compare against server's list */ 204 char *name; /* string to compare against server's list */
203 int (*userauth)(Authctxt *authctxt); 205 int (*userauth)(Authctxt *authctxt);
206 void (*cleanup)(Authctxt *authctxt);
204 int *enabled; /* flag in option struct that enables method */ 207 int *enabled; /* flag in option struct that enables method */
205 int *batch_flag; /* flag in option struct that disables method */ 208 int *batch_flag; /* flag in option struct that disables method */
206}; 209};
@@ -212,12 +215,18 @@ void input_userauth_error(int, u_int32_t, void *);
212void input_userauth_info_req(int, u_int32_t, void *); 215void input_userauth_info_req(int, u_int32_t, void *);
213void input_userauth_pk_ok(int, u_int32_t, void *); 216void input_userauth_pk_ok(int, u_int32_t, void *);
214void input_userauth_passwd_changereq(int, u_int32_t, void *); 217void input_userauth_passwd_changereq(int, u_int32_t, void *);
218void input_userauth_jpake_server_step1(int, u_int32_t, void *);
219void input_userauth_jpake_server_step2(int, u_int32_t, void *);
220void input_userauth_jpake_server_confirm(int, u_int32_t, void *);
215 221
216int userauth_none(Authctxt *); 222int userauth_none(Authctxt *);
217int userauth_pubkey(Authctxt *); 223int userauth_pubkey(Authctxt *);
218int userauth_passwd(Authctxt *); 224int userauth_passwd(Authctxt *);
219int userauth_kbdint(Authctxt *); 225int userauth_kbdint(Authctxt *);
220int userauth_hostbased(Authctxt *); 226int userauth_hostbased(Authctxt *);
227int userauth_jpake(Authctxt *);
228
229void userauth_jpake_cleanup(Authctxt *);
221 230
222#ifdef GSSAPI 231#ifdef GSSAPI
223int userauth_gssapi(Authctxt *authctxt); 232int userauth_gssapi(Authctxt *authctxt);
@@ -243,30 +252,43 @@ Authmethod authmethods[] = {
243#ifdef GSSAPI 252#ifdef GSSAPI
244 {"gssapi-with-mic", 253 {"gssapi-with-mic",
245 userauth_gssapi, 254 userauth_gssapi,
255 NULL,
246 &options.gss_authentication, 256 &options.gss_authentication,
247 NULL}, 257 NULL},
248#endif 258#endif
249 {"hostbased", 259 {"hostbased",
250 userauth_hostbased, 260 userauth_hostbased,
261 NULL,
251 &options.hostbased_authentication, 262 &options.hostbased_authentication,
252 NULL}, 263 NULL},
253 {"publickey", 264 {"publickey",
254 userauth_pubkey, 265 userauth_pubkey,
266 NULL,
255 &options.pubkey_authentication, 267 &options.pubkey_authentication,
256 NULL}, 268 NULL},
269#ifdef JPAKE
270 {"jpake-01@openssh.com",
271 userauth_jpake,
272 userauth_jpake_cleanup,
273 &options.zero_knowledge_password_authentication,
274 &options.batch_mode},
275#endif
257 {"keyboard-interactive", 276 {"keyboard-interactive",
258 userauth_kbdint, 277 userauth_kbdint,
278 NULL,
259 &options.kbd_interactive_authentication, 279 &options.kbd_interactive_authentication,
260 &options.batch_mode}, 280 &options.batch_mode},
261 {"password", 281 {"password",
262 userauth_passwd, 282 userauth_passwd,
283 NULL,
263 &options.password_authentication, 284 &options.password_authentication,
264 &options.batch_mode}, 285 &options.batch_mode},
265 {"none", 286 {"none",
266 userauth_none, 287 userauth_none,
267 NULL, 288 NULL,
289 NULL,
268 NULL}, 290 NULL},
269 {NULL, NULL, NULL, NULL} 291 {NULL, NULL, NULL, NULL, NULL}
270}; 292};
271 293
272void 294void
@@ -334,6 +356,9 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host,
334void 356void
335userauth(Authctxt *authctxt, char *authlist) 357userauth(Authctxt *authctxt, char *authlist)
336{ 358{
359 if (authctxt->method != NULL && authctxt->method->cleanup != NULL)
360 authctxt->method->cleanup(authctxt);
361
337 if (authctxt->methoddata) { 362 if (authctxt->methoddata) {
338 xfree(authctxt->methoddata); 363 xfree(authctxt->methoddata);
339 authctxt->methoddata = NULL; 364 authctxt->methoddata = NULL;
@@ -851,6 +876,209 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt)
851 &input_userauth_passwd_changereq); 876 &input_userauth_passwd_changereq);
852} 877}
853 878
879#ifdef JPAKE
880static char *
881pw_encrypt(const char *password, const char *crypt_scheme, const char *salt)
882{
883 /* OpenBSD crypt(3) handles all of these */
884 if (strcmp(crypt_scheme, "crypt") == 0 ||
885 strcmp(crypt_scheme, "bcrypt") == 0 ||
886 strcmp(crypt_scheme, "md5crypt") == 0 ||
887 strcmp(crypt_scheme, "crypt-extended") == 0)
888 return xstrdup(crypt(password, salt));
889 error("%s: unsupported password encryption scheme \"%.100s\"",
890 __func__, crypt_scheme);
891 return NULL;
892}
893
894static BIGNUM *
895jpake_password_to_secret(Authctxt *authctxt, const char *crypt_scheme,
896 const char *salt)
897{
898 char prompt[256], *password, *crypted;
899 u_char *secret;
900 u_int secret_len;
901 BIGNUM *ret;
902
903 snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password (JPAKE): ",
904 authctxt->server_user, authctxt->host);
905 password = read_passphrase(prompt, 0);
906
907 if ((crypted = pw_encrypt(password, crypt_scheme, salt)) == NULL) {
908 logit("Disabling %s authentication", authctxt->method->name);
909 authctxt->method->enabled = NULL;
910 /* Continue with an empty password to fail gracefully */
911 crypted = xstrdup("");
912 }
913
914#ifdef JPAKE_DEBUG
915 debug3("%s: salt = %s", __func__, salt);
916 debug3("%s: scheme = %s", __func__, crypt_scheme);
917 debug3("%s: crypted = %s", __func__, crypted);
918#endif
919
920 if (hash_buffer(crypted, strlen(crypted), EVP_sha256(),
921 &secret, &secret_len) != 0)
922 fatal("%s: hash_buffer", __func__);
923
924 bzero(password, strlen(password));
925 bzero(crypted, strlen(crypted));
926 xfree(password);
927 xfree(crypted);
928
929 if ((ret = BN_bin2bn(secret, secret_len, NULL)) == NULL)
930 fatal("%s: BN_bin2bn (secret)", __func__);
931 bzero(secret, secret_len);
932 xfree(secret);
933
934 return ret;
935}
936
937/* ARGSUSED */
938void
939input_userauth_jpake_server_step1(int type, u_int32_t seq, void *ctxt)
940{
941 Authctxt *authctxt = ctxt;
942 struct jpake_ctx *pctx = authctxt->methoddata;
943 u_char *x3_proof, *x4_proof, *x2_s_proof;
944 u_int x3_proof_len, x4_proof_len, x2_s_proof_len;
945 char *crypt_scheme, *salt;
946
947 /* Disable this message */
948 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1, NULL);
949
950 if ((pctx->g_x3 = BN_new()) == NULL ||
951 (pctx->g_x4 = BN_new()) == NULL)
952 fatal("%s: BN_new", __func__);
953
954 /* Fetch step 1 values */
955 crypt_scheme = packet_get_string(NULL);
956 salt = packet_get_string(NULL);
957 pctx->server_id = packet_get_string(&pctx->server_id_len);
958 packet_get_bignum2(pctx->g_x3);
959 packet_get_bignum2(pctx->g_x4);
960 x3_proof = packet_get_string(&x3_proof_len);
961 x4_proof = packet_get_string(&x4_proof_len);
962 packet_check_eom();
963
964 JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__));
965
966 /* Obtain password and derive secret */
967 pctx->s = jpake_password_to_secret(authctxt, crypt_scheme, salt);
968 bzero(crypt_scheme, strlen(crypt_scheme));
969 bzero(salt, strlen(salt));
970 xfree(crypt_scheme);
971 xfree(salt);
972 JPAKE_DEBUG_BN((pctx->s, "%s: s = ", __func__));
973
974 /* Calculate step 2 values */
975 jpake_step2(pctx->grp, pctx->s, pctx->g_x1,
976 pctx->g_x3, pctx->g_x4, pctx->x2,
977 pctx->server_id, pctx->server_id_len,
978 pctx->client_id, pctx->client_id_len,
979 x3_proof, x3_proof_len,
980 x4_proof, x4_proof_len,
981 &pctx->a,
982 &x2_s_proof, &x2_s_proof_len);
983
984 bzero(x3_proof, x3_proof_len);
985 bzero(x4_proof, x4_proof_len);
986 xfree(x3_proof);
987 xfree(x4_proof);
988
989 JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__));
990
991 /* Send values for step 2 */
992 packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2);
993 packet_put_bignum2(pctx->a);
994 packet_put_string(x2_s_proof, x2_s_proof_len);
995 packet_send();
996
997 bzero(x2_s_proof, x2_s_proof_len);
998 xfree(x2_s_proof);
999
1000 /* Expect step 2 packet from peer */
1001 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2,
1002 input_userauth_jpake_server_step2);
1003}
1004
1005/* ARGSUSED */
1006void
1007input_userauth_jpake_server_step2(int type, u_int32_t seq, void *ctxt)
1008{
1009 Authctxt *authctxt = ctxt;
1010 struct jpake_ctx *pctx = authctxt->methoddata;
1011 u_char *x4_s_proof;
1012 u_int x4_s_proof_len;
1013
1014 /* Disable this message */
1015 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2, NULL);
1016
1017 if ((pctx->b = BN_new()) == NULL)
1018 fatal("%s: BN_new", __func__);
1019
1020 /* Fetch step 2 values */
1021 packet_get_bignum2(pctx->b);
1022 x4_s_proof = packet_get_string(&x4_s_proof_len);
1023 packet_check_eom();
1024
1025 JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__));
1026
1027 /* Derive shared key and calculate confirmation hash */
1028 jpake_key_confirm(pctx->grp, pctx->s, pctx->b,
1029 pctx->x2, pctx->g_x1, pctx->g_x2, pctx->g_x3, pctx->g_x4,
1030 pctx->client_id, pctx->client_id_len,
1031 pctx->server_id, pctx->server_id_len,
1032 session_id2, session_id2_len,
1033 x4_s_proof, x4_s_proof_len,
1034 &pctx->k,
1035 &pctx->h_k_cid_sessid, &pctx->h_k_cid_sessid_len);
1036
1037 bzero(x4_s_proof, x4_s_proof_len);
1038 xfree(x4_s_proof);
1039
1040 JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__));
1041
1042 /* Send key confirmation proof */
1043 packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM);
1044 packet_put_string(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len);
1045 packet_send();
1046
1047 /* Expect confirmation from peer */
1048 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM,
1049 input_userauth_jpake_server_confirm);
1050}
1051
1052/* ARGSUSED */
1053void
1054input_userauth_jpake_server_confirm(int type, u_int32_t seq, void *ctxt)
1055{
1056 Authctxt *authctxt = ctxt;
1057 struct jpake_ctx *pctx = authctxt->methoddata;
1058
1059 /* Disable this message */
1060 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM, NULL);
1061
1062 pctx->h_k_sid_sessid = packet_get_string(&pctx->h_k_sid_sessid_len);
1063 packet_check_eom();
1064
1065 JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__));
1066
1067 /* Verify expected confirmation hash */
1068 if (jpake_check_confirm(pctx->k,
1069 pctx->server_id, pctx->server_id_len,
1070 session_id2, session_id2_len,
1071 pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len) == 1)
1072 debug("%s: %s success", __func__, authctxt->method->name);
1073 else {
1074 debug("%s: confirmation mismatch", __func__);
1075 /* XXX stash this so if auth succeeds then we can warn/kill */
1076 }
1077
1078 userauth_jpake_cleanup(authctxt);
1079}
1080#endif /* JPAKE */
1081
854static int 1082static int
855identity_sign(Identity *id, u_char **sigp, u_int *lenp, 1083identity_sign(Identity *id, u_char **sigp, u_int *lenp,
856 u_char *data, u_int datalen) 1084 u_char *data, u_int datalen)
@@ -1425,6 +1653,76 @@ userauth_hostbased(Authctxt *authctxt)
1425 return 1; 1653 return 1;
1426} 1654}
1427 1655
1656#ifdef JPAKE
1657int
1658userauth_jpake(Authctxt *authctxt)
1659{
1660 struct jpake_ctx *pctx;
1661 u_char *x1_proof, *x2_proof;
1662 u_int x1_proof_len, x2_proof_len;
1663 static int attempt = 0; /* XXX share with userauth_password's? */
1664
1665 if (attempt++ >= options.number_of_password_prompts)
1666 return 0;
1667 if (attempt != 1)
1668 error("Permission denied, please try again.");
1669
1670 if (authctxt->methoddata != NULL)
1671 fatal("%s: authctxt->methoddata already set (%p)",
1672 __func__, authctxt->methoddata);
1673
1674 authctxt->methoddata = pctx = jpake_new();
1675
1676 /*
1677 * Send request immediately, to get the protocol going while
1678 * we do the initial computations.
1679 */
1680 packet_start(SSH2_MSG_USERAUTH_REQUEST);
1681 packet_put_cstring(authctxt->server_user);
1682 packet_put_cstring(authctxt->service);
1683 packet_put_cstring(authctxt->method->name);
1684 packet_send();
1685 packet_write_wait();
1686
1687 jpake_step1(pctx->grp,
1688 &pctx->client_id, &pctx->client_id_len,
1689 &pctx->x1, &pctx->x2, &pctx->g_x1, &pctx->g_x2,
1690 &x1_proof, &x1_proof_len,
1691 &x2_proof, &x2_proof_len);
1692
1693 JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__));
1694
1695 packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1);
1696 packet_put_string(pctx->client_id, pctx->client_id_len);
1697 packet_put_bignum2(pctx->g_x1);
1698 packet_put_bignum2(pctx->g_x2);
1699 packet_put_string(x1_proof, x1_proof_len);
1700 packet_put_string(x2_proof, x2_proof_len);
1701 packet_send();
1702
1703 bzero(x1_proof, x1_proof_len);
1704 bzero(x2_proof, x2_proof_len);
1705 xfree(x1_proof);
1706 xfree(x2_proof);
1707
1708 /* Expect step 1 packet from peer */
1709 dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1,
1710 input_userauth_jpake_server_step1);
1711
1712 return 1;
1713}
1714
1715void
1716userauth_jpake_cleanup(Authctxt *authctxt)
1717{
1718 debug3("%s: clean up", __func__);
1719 if (authctxt->methoddata != NULL) {
1720 jpake_free(authctxt->methoddata);
1721 authctxt->methoddata = NULL;
1722 }
1723}
1724#endif /* JPAKE */
1725
1428/* find auth method */ 1726/* find auth method */
1429 1727
1430/* 1728/*
@@ -1526,3 +1824,4 @@ authmethods_get(void)
1526 buffer_free(&b); 1824 buffer_free(&b);
1527 return list; 1825 return list;
1528} 1826}
1827
diff --git a/sshd_config.5 b/sshd_config.5
index 06fe5fd38..a4a4be6e5 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -34,8 +34,8 @@
34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36.\" 36.\"
37.\" $OpenBSD: sshd_config.5,v 1.97 2008/10/09 03:50:54 djm Exp $ 37.\" $OpenBSD: sshd_config.5,v 1.98 2008/11/04 08:22:13 djm Exp $
38.Dd $Mdocdate: October 9 2008 $ 38.Dd $Mdocdate: November 4 2008 $
39.Dt SSHD_CONFIG 5 39.Dt SSHD_CONFIG 5
40.Os 40.Os
41.Sh NAME 41.Sh NAME
@@ -612,8 +612,9 @@ Available keywords are
612.Cm RSAAuthentication , 612.Cm RSAAuthentication ,
613.Cm X11DisplayOffset , 613.Cm X11DisplayOffset ,
614.Cm X11Forwarding , 614.Cm X11Forwarding ,
615.Cm X11UseLocalHost ,
615and 616and
616.Cm X11UseLocalHost . 617.Cm ZeroKnowledgePasswordAuthentication .
617.It Cm MaxAuthTries 618.It Cm MaxAuthTries
618Specifies the maximum number of authentication attempts permitted per 619Specifies the maximum number of authentication attempts permitted per
619connection. 620connection.
@@ -1004,6 +1005,17 @@ Specifies the full pathname of the
1004program. 1005program.
1005The default is 1006The default is
1006.Pa /usr/X11R6/bin/xauth . 1007.Pa /usr/X11R6/bin/xauth .
1008.It Cm ZeroKnowledgePasswordAuthentication
1009Specifies whether to use zero knowledge password authentication.
1010This authentication method avoids exposure of password to untrusted
1011hosts.
1012The argument to this keyword must be
1013.Dq yes
1014or
1015.Dq no .
1016The default is currently
1017.Dq no
1018as this method is considered experimental.
1007.El 1019.El
1008.Sh TIME FORMATS 1020.Sh TIME FORMATS
1009.Xr sshd 8 1021.Xr sshd 8