+#ifdef JPAKE
+static char *
+pw_encrypt(const char *password, const char *crypt_scheme, const char *salt)
+{
+ /* OpenBSD crypt(3) handles all of these */
+ if (strcmp(crypt_scheme, "crypt") == 0 ||
+ strcmp(crypt_scheme, "bcrypt") == 0 ||
+ strcmp(crypt_scheme, "md5crypt") == 0 ||
+ strcmp(crypt_scheme, "crypt-extended") == 0)
+ return xstrdup(crypt(password, salt));
+ error("%s: unsupported password encryption scheme \"%.100s\"",
+ __func__, crypt_scheme);
+ return NULL;
+}
+
+static BIGNUM *
+jpake_password_to_secret(Authctxt *authctxt, const char *crypt_scheme,
+ const char *salt)
+{
+ char prompt[256], *password, *crypted;
+ u_char *secret;
+ u_int secret_len;
+ BIGNUM *ret;
+
+ snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password (JPAKE): ",
+ authctxt->server_user, authctxt->host);
+ password = read_passphrase(prompt, 0);
+
+ if ((crypted = pw_encrypt(password, crypt_scheme, salt)) == NULL) {
+ logit("Disabling %s authentication", authctxt->method->name);
+ authctxt->method->enabled = NULL;
+ /* Continue with an empty password to fail gracefully */
+ crypted = xstrdup("");
+ }
+
+#ifdef JPAKE_DEBUG
+ debug3("%s: salt = %s", __func__, salt);
+ debug3("%s: scheme = %s", __func__, crypt_scheme);
+ debug3("%s: crypted = %s", __func__, crypted);
+#endif
+
+ if (hash_buffer(crypted, strlen(crypted), EVP_sha256(),
+ &secret, &secret_len) != 0)
+ fatal("%s: hash_buffer", __func__);
+
+ bzero(password, strlen(password));
+ bzero(crypted, strlen(crypted));
+ xfree(password);
+ xfree(crypted);
+
+ if ((ret = BN_bin2bn(secret, secret_len, NULL)) == NULL)
+ fatal("%s: BN_bin2bn (secret)", __func__);
+ bzero(secret, secret_len);
+ xfree(secret);
+
+ return ret;
+}
+
+/* ARGSUSED */
+void
+input_userauth_jpake_server_step1(int type, u_int32_t seq, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ struct jpake_ctx *pctx = authctxt->methoddata;
+ u_char *x3_proof, *x4_proof, *x2_s_proof;
+ u_int x3_proof_len, x4_proof_len, x2_s_proof_len;
+ char *crypt_scheme, *salt;
+
+ /* Disable this message */
+ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1, NULL);
+
+ if ((pctx->g_x3 = BN_new()) == NULL ||
+ (pctx->g_x4 = BN_new()) == NULL)
+ fatal("%s: BN_new", __func__);
+
+ /* Fetch step 1 values */
+ crypt_scheme = packet_get_string(NULL);
+ salt = packet_get_string(NULL);
+ pctx->server_id = packet_get_string(&pctx->server_id_len);
+ packet_get_bignum2(pctx->g_x3);
+ packet_get_bignum2(pctx->g_x4);
+ x3_proof = packet_get_string(&x3_proof_len);
+ x4_proof = packet_get_string(&x4_proof_len);
+ packet_check_eom();
+
+ JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__));
+
+ /* Obtain password and derive secret */
+ pctx->s = jpake_password_to_secret(authctxt, crypt_scheme, salt);
+ bzero(crypt_scheme, strlen(crypt_scheme));
+ bzero(salt, strlen(salt));
+ xfree(crypt_scheme);
+ xfree(salt);
+ JPAKE_DEBUG_BN((pctx->s, "%s: s = ", __func__));
+
+ /* Calculate step 2 values */
+ jpake_step2(pctx->grp, pctx->s, pctx->g_x1,
+ pctx->g_x3, pctx->g_x4, pctx->x2,
+ pctx->server_id, pctx->server_id_len,
+ pctx->client_id, pctx->client_id_len,
+ x3_proof, x3_proof_len,
+ x4_proof, x4_proof_len,
+ &pctx->a,
+ &x2_s_proof, &x2_s_proof_len);
+
+ bzero(x3_proof, x3_proof_len);
+ bzero(x4_proof, x4_proof_len);
+ xfree(x3_proof);
+ xfree(x4_proof);
+
+ JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__));
+
+ /* Send values for step 2 */
+ packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2);
+ packet_put_bignum2(pctx->a);
+ packet_put_string(x2_s_proof, x2_s_proof_len);
+ packet_send();
+
+ bzero(x2_s_proof, x2_s_proof_len);
+ xfree(x2_s_proof);
+
+ /* Expect step 2 packet from peer */
+ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2,
+ input_userauth_jpake_server_step2);
+}
+
+/* ARGSUSED */
+void
+input_userauth_jpake_server_step2(int type, u_int32_t seq, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ struct jpake_ctx *pctx = authctxt->methoddata;
+ u_char *x4_s_proof;
+ u_int x4_s_proof_len;
+
+ /* Disable this message */
+ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2, NULL);
+
+ if ((pctx->b = BN_new()) == NULL)
+ fatal("%s: BN_new", __func__);
+
+ /* Fetch step 2 values */
+ packet_get_bignum2(pctx->b);
+ x4_s_proof = packet_get_string(&x4_s_proof_len);
+ packet_check_eom();
+
+ JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__));
+
+ /* Derive shared key and calculate confirmation hash */
+ jpake_key_confirm(pctx->grp, pctx->s, pctx->b,
+ pctx->x2, pctx->g_x1, pctx->g_x2, pctx->g_x3, pctx->g_x4,
+ pctx->client_id, pctx->client_id_len,
+ pctx->server_id, pctx->server_id_len,
+ session_id2, session_id2_len,
+ x4_s_proof, x4_s_proof_len,
+ &pctx->k,
+ &pctx->h_k_cid_sessid, &pctx->h_k_cid_sessid_len);
+
+ bzero(x4_s_proof, x4_s_proof_len);
+ xfree(x4_s_proof);
+
+ JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__));
+
+ /* Send key confirmation proof */
+ packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM);
+ packet_put_string(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len);
+ packet_send();
+
+ /* Expect confirmation from peer */
+ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM,
+ input_userauth_jpake_server_confirm);
+}
+
+/* ARGSUSED */
+void
+input_userauth_jpake_server_confirm(int type, u_int32_t seq, void *ctxt)
+{
+ Authctxt *authctxt = ctxt;
+ struct jpake_ctx *pctx = authctxt->methoddata;
+
+ /* Disable this message */
+ dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM, NULL);
+
+ pctx->h_k_sid_sessid = packet_get_string(&pctx->h_k_sid_sessid_len);
+ packet_check_eom();
+
+ JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__));
+
+ /* Verify expected confirmation hash */
+ if (jpake_check_confirm(pctx->k,
+ pctx->server_id, pctx->server_id_len,
+ session_id2, session_id2_len,
+ pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len) == 1)
+ debug("%s: %s success", __func__, authctxt->method->name);
+ else {
+ debug("%s: confirmation mismatch", __func__);
+ /* XXX stash this so if auth succeeds then we can warn/kill */
+ }
+
+ userauth_jpake_cleanup(authctxt);
+}
+#endif /* JPAKE */
+