- 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@
diff --git a/monitor.c b/monitor.c
index 73cf6bc..39deedc 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.99 2008/07/10 18:08:11 markus Exp $ */
+/* $OpenBSD: monitor.c,v 1.100 2008/11/04 08:22:13 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -87,6 +87,7 @@
 #include "misc.h"
 #include "compat.h"
 #include "ssh2.h"
+#include "jpake.h"
 
 #ifdef GSSAPI
 static Gssctxt *gsscontext = NULL;
@@ -149,6 +150,11 @@
 int mm_answer_rsa_response(int, Buffer *);
 int mm_answer_sesskey(int, Buffer *);
 int mm_answer_sessid(int, Buffer *);
+int mm_answer_jpake_get_pwdata(int, Buffer *);
+int mm_answer_jpake_step1(int, Buffer *);
+int mm_answer_jpake_step2(int, Buffer *);
+int mm_answer_jpake_key_confirm(int, Buffer *);
+int mm_answer_jpake_check_confirm(int, Buffer *);
 
 #ifdef USE_PAM
 int mm_answer_pam_start(int, Buffer *);
@@ -234,6 +240,13 @@
     {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok},
     {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, mm_answer_gss_checkmic},
 #endif
+#ifdef JPAKE
+    {MONITOR_REQ_JPAKE_GET_PWDATA, MON_ONCE, mm_answer_jpake_get_pwdata},
+    {MONITOR_REQ_JPAKE_STEP1, MON_ISAUTH, mm_answer_jpake_step1},
+    {MONITOR_REQ_JPAKE_STEP2, MON_ONCE, mm_answer_jpake_step2},
+    {MONITOR_REQ_JPAKE_KEY_CONFIRM, MON_ONCE, mm_answer_jpake_key_confirm},
+    {MONITOR_REQ_JPAKE_CHECK_CONFIRM, MON_AUTH, mm_answer_jpake_check_confirm},
+#endif
     {0, 0, NULL}
 };
 
@@ -379,6 +392,15 @@
 			if (!authenticated)
 				authctxt->failures++;
 		}
+#ifdef JPAKE
+		/* Cleanup JPAKE context after authentication */
+		if (ent->flags & MON_AUTHDECIDE) {
+			if (authctxt->jpake_ctx != NULL) {
+				jpake_free(authctxt->jpake_ctx);
+				authctxt->jpake_ctx = NULL;
+			}
+		}
+#endif
 	}
 
 	if (!authctxt->valid)
@@ -1969,3 +1991,206 @@
 	return (authenticated);
 }
 #endif /* GSSAPI */
+
+#ifdef JPAKE
+int
+mm_answer_jpake_step1(int sock, Buffer *m)
+{
+	struct jpake_ctx *pctx;
+	u_char *x3_proof, *x4_proof;
+	u_int x3_proof_len, x4_proof_len;
+
+	if (!options.zero_knowledge_password_authentication)
+		fatal("zero_knowledge_password_authentication disabled");
+
+	if (authctxt->jpake_ctx != NULL)
+		fatal("%s: authctxt->jpake_ctx already set (%p)",
+		    __func__, authctxt->jpake_ctx);
+	authctxt->jpake_ctx = pctx = jpake_new();
+
+	jpake_step1(pctx->grp,
+	    &pctx->server_id, &pctx->server_id_len,
+	    &pctx->x3, &pctx->x4, &pctx->g_x3, &pctx->g_x4,
+	    &x3_proof, &x3_proof_len,
+	    &x4_proof, &x4_proof_len);
+
+	JPAKE_DEBUG_CTX((pctx, "step1 done in %s", __func__));
+
+	buffer_clear(m);
+
+	buffer_put_string(m, pctx->server_id, pctx->server_id_len);
+	buffer_put_bignum2(m, pctx->g_x3);
+	buffer_put_bignum2(m, pctx->g_x4);
+	buffer_put_string(m, x3_proof, x3_proof_len);
+	buffer_put_string(m, x4_proof, x4_proof_len);
+
+	debug3("%s: sending step1", __func__);
+	mm_request_send(sock, MONITOR_ANS_JPAKE_STEP1, m);
+
+	bzero(x3_proof, x3_proof_len);
+	bzero(x4_proof, x4_proof_len);
+	xfree(x3_proof);
+	xfree(x4_proof);
+
+	monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_GET_PWDATA, 1);
+	monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 0);
+
+	return 0;
+}
+
+int
+mm_answer_jpake_get_pwdata(int sock, Buffer *m)
+{
+	struct jpake_ctx *pctx = authctxt->jpake_ctx;
+	char *hash_scheme, *salt;
+
+	if (pctx == NULL)
+		fatal("%s: pctx == NULL", __func__);
+
+	auth2_jpake_get_pwdata(authctxt, &pctx->s, &hash_scheme, &salt);
+
+	buffer_clear(m);
+	/* pctx->s is sensitive, not returned to slave */
+	buffer_put_cstring(m, hash_scheme);
+	buffer_put_cstring(m, salt);
+
+	debug3("%s: sending pwdata", __func__);
+	mm_request_send(sock, MONITOR_ANS_JPAKE_GET_PWDATA, m);
+
+	bzero(hash_scheme, strlen(hash_scheme));
+	bzero(salt, strlen(salt));
+	xfree(hash_scheme);
+	xfree(salt);
+
+	monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP2, 1);
+
+	return 0;
+}
+
+int
+mm_answer_jpake_step2(int sock, Buffer *m)
+{
+	struct jpake_ctx *pctx = authctxt->jpake_ctx;
+	u_char *x1_proof, *x2_proof, *x4_s_proof;
+	u_int x1_proof_len, x2_proof_len, x4_s_proof_len;
+
+	if (pctx == NULL)
+		fatal("%s: pctx == NULL", __func__);
+
+	if ((pctx->g_x1 = BN_new()) == NULL ||
+	    (pctx->g_x2 = BN_new()) == NULL)
+		fatal("%s: BN_new", __func__);
+	buffer_get_bignum2(m, pctx->g_x1);
+	buffer_get_bignum2(m, pctx->g_x2);
+	pctx->client_id = buffer_get_string(m, &pctx->client_id_len);
+	x1_proof = buffer_get_string(m, &x1_proof_len);
+	x2_proof = buffer_get_string(m, &x2_proof_len);
+
+	jpake_step2(pctx->grp, pctx->s, pctx->g_x3,
+	    pctx->g_x1, pctx->g_x2, pctx->x4,
+	    pctx->client_id, pctx->client_id_len,
+	    pctx->server_id, pctx->server_id_len,
+	    x1_proof, x1_proof_len,
+	    x2_proof, x2_proof_len,
+	    &pctx->b,
+	    &x4_s_proof, &x4_s_proof_len);
+
+	JPAKE_DEBUG_CTX((pctx, "step2 done in %s", __func__));
+
+	bzero(x1_proof, x1_proof_len);
+	bzero(x2_proof, x2_proof_len);
+	xfree(x1_proof);
+	xfree(x2_proof);
+
+	buffer_clear(m);
+
+	buffer_put_bignum2(m, pctx->b);
+	buffer_put_string(m, x4_s_proof, x4_s_proof_len);
+
+	debug3("%s: sending step2", __func__);
+	mm_request_send(sock, MONITOR_ANS_JPAKE_STEP2, m);
+
+	bzero(x4_s_proof, x4_s_proof_len);
+	xfree(x4_s_proof);
+
+	monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_KEY_CONFIRM, 1);
+
+	return 0;
+}
+
+int
+mm_answer_jpake_key_confirm(int sock, Buffer *m)
+{
+	struct jpake_ctx *pctx = authctxt->jpake_ctx;
+	u_char *x2_s_proof;
+	u_int x2_s_proof_len;
+
+	if (pctx == NULL)
+		fatal("%s: pctx == NULL", __func__);
+
+	if ((pctx->a = BN_new()) == NULL)
+		fatal("%s: BN_new", __func__);
+	buffer_get_bignum2(m, pctx->a);
+	x2_s_proof = buffer_get_string(m, &x2_s_proof_len);
+
+	jpake_key_confirm(pctx->grp, pctx->s, pctx->a,
+	    pctx->x4, pctx->g_x3, pctx->g_x4, pctx->g_x1, pctx->g_x2,
+	    pctx->server_id, pctx->server_id_len,
+	    pctx->client_id, pctx->client_id_len,
+	    session_id2, session_id2_len,
+	    x2_s_proof, x2_s_proof_len,
+	    &pctx->k,
+	    &pctx->h_k_sid_sessid, &pctx->h_k_sid_sessid_len);
+
+	JPAKE_DEBUG_CTX((pctx, "key_confirm done in %s", __func__));
+
+	bzero(x2_s_proof, x2_s_proof_len);
+	buffer_clear(m);
+
+	/* pctx->k is sensitive, not sent */
+	buffer_put_string(m, pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
+
+	debug3("%s: sending confirmation hash", __func__);
+	mm_request_send(sock, MONITOR_ANS_JPAKE_KEY_CONFIRM, m);
+
+	monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_CHECK_CONFIRM, 1);
+
+	return 0;
+}
+
+int
+mm_answer_jpake_check_confirm(int sock, Buffer *m)
+{
+	int authenticated = 0;
+	u_char *peer_confirm_hash;
+	u_int peer_confirm_hash_len;
+	struct jpake_ctx *pctx = authctxt->jpake_ctx;
+
+	if (pctx == NULL)
+		fatal("%s: pctx == NULL", __func__);
+
+	peer_confirm_hash = buffer_get_string(m, &peer_confirm_hash_len);
+
+	authenticated = jpake_check_confirm(pctx->k,
+	    pctx->client_id, pctx->client_id_len,
+	    session_id2, session_id2_len,
+	    peer_confirm_hash, peer_confirm_hash_len) && authctxt->valid;
+
+	JPAKE_DEBUG_CTX((pctx, "check_confirm done in %s", __func__));
+
+	bzero(peer_confirm_hash, peer_confirm_hash_len);
+	xfree(peer_confirm_hash);
+
+	buffer_clear(m);
+	buffer_put_int(m, authenticated);
+
+	debug3("%s: sending result %d", __func__, authenticated);
+	mm_request_send(sock, MONITOR_ANS_JPAKE_CHECK_CONFIRM, m);
+
+	monitor_permit(mon_dispatch, MONITOR_REQ_JPAKE_STEP1, 1);
+
+	auth_method = "jpake-01@openssh.com";
+	return authenticated;
+}
+
+#endif /* JPAKE */