- djm@cvs.openbsd.org 2014/01/29 06:18:35
[Makefile.in auth.h auth2-jpake.c auth2.c jpake.c jpake.h monitor.c] [monitor.h monitor_wrap.c monitor_wrap.h readconf.c readconf.h] [schnorr.c schnorr.h servconf.c servconf.h ssh2.h sshconnect2.c] remove experimental, never-enabled JPAKE code; ok markus@
This commit is contained in:
parent
b0f26544cf
commit
7cc194f70d
|
@ -20,6 +20,11 @@
|
|||
they are equivalent, but SUSv2 describes the latter as having undefined
|
||||
behaviour; from portable; ok dtucker
|
||||
(Id sync only; change is already in portable)
|
||||
- djm@cvs.openbsd.org 2014/01/29 06:18:35
|
||||
[Makefile.in auth.h auth2-jpake.c auth2.c jpake.c jpake.h monitor.c]
|
||||
[monitor.h monitor_wrap.c monitor_wrap.h readconf.c readconf.h]
|
||||
[schnorr.c schnorr.h servconf.c servconf.h ssh2.h sshconnect2.c]
|
||||
remove experimental, never-enabled JPAKE code; ok markus@
|
||||
|
||||
20140131
|
||||
- (djm) [sandbox-seccomp-filter.c sandbox-systrace.c] Allow shutdown(2)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $Id: Makefile.in,v 1.355 2014/02/04 00:07:14 djm Exp $
|
||||
# $Id: Makefile.in,v 1.356 2014/02/04 00:12:56 djm Exp $
|
||||
|
||||
# uncomment if you run a non bourne compatable shell. Ie. csh
|
||||
#SHELL = @SH@
|
||||
|
@ -73,7 +73,7 @@ LIBSSH_OBJS=authfd.o authfile.o bufaux.o bufbn.o buffer.o \
|
|||
monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
|
||||
kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \
|
||||
msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
|
||||
jpake.o schnorr.o ssh-pkcs11.o krl.o smult_curve25519_ref.o \
|
||||
ssh-pkcs11.o krl.o smult_curve25519_ref.o \
|
||||
kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \
|
||||
ssh-ed25519.o digest-openssl.o hmac.o \
|
||||
sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o
|
||||
|
@ -88,7 +88,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
|
|||
auth.o auth1.o auth2.o auth-options.o session.o \
|
||||
auth-chall.o auth2-chall.o groupaccess.o \
|
||||
auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
|
||||
auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-jpake.o \
|
||||
auth2-none.o auth2-passwd.o auth2-pubkey.o \
|
||||
monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o kexecdhs.o \
|
||||
kexc25519s.o auth-krb5.o \
|
||||
auth2-gss.o gss-serv.o gss-serv-krb5.o \
|
||||
|
|
6
auth.h
6
auth.h
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: auth.h,v 1.76 2013/07/19 07:37:48 markus Exp $ */
|
||||
/* $OpenBSD: auth.h,v 1.77 2014/01/29 06:18:35 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
|
@ -61,7 +61,6 @@ struct Authctxt {
|
|||
char *style;
|
||||
void *kbdintctxt;
|
||||
char *info; /* Extra info for next auth_log */
|
||||
void *jpake_ctx;
|
||||
#ifdef BSD_AUTH
|
||||
auth_session_t *as;
|
||||
#endif
|
||||
|
@ -175,9 +174,6 @@ int bsdauth_respond(void *, u_int, char **);
|
|||
int skey_query(void *, char **, char **, u_int *, char ***, u_int **);
|
||||
int skey_respond(void *, u_int, char **);
|
||||
|
||||
void auth2_jpake_get_pwdata(Authctxt *, BIGNUM **, char **, char **);
|
||||
void auth2_jpake_stop(Authctxt *);
|
||||
|
||||
int allowed_user(struct passwd *);
|
||||
struct passwd * getpwnamallow(const char *user);
|
||||
|
||||
|
|
563
auth2-jpake.c
563
auth2-jpake.c
|
@ -1,563 +0,0 @@
|
|||
/* $OpenBSD: auth2-jpake.c,v 1.6 2013/05/17 00:13:13 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008 Damien Miller. All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Server side of zero-knowledge password auth using J-PAKE protocol
|
||||
* as described in:
|
||||
*
|
||||
* F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
|
||||
* 16th Workshop on Security Protocols, Cambridge, April 2008
|
||||
*
|
||||
* http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
|
||||
*/
|
||||
|
||||
#ifdef JPAKE
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <login_cap.h>
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "xmalloc.h"
|
||||
#include "ssh2.h"
|
||||
#include "key.h"
|
||||
#include "hostfile.h"
|
||||
#include "auth.h"
|
||||
#include "buffer.h"
|
||||
#include "packet.h"
|
||||
#include "dispatch.h"
|
||||
#include "log.h"
|
||||
#include "servconf.h"
|
||||
#include "auth-options.h"
|
||||
#include "canohost.h"
|
||||
#ifdef GSSAPI
|
||||
#include "ssh-gss.h"
|
||||
#endif
|
||||
#include "monitor_wrap.h"
|
||||
|
||||
#include "schnorr.h"
|
||||
#include "jpake.h"
|
||||
|
||||
/*
|
||||
* XXX options->permit_empty_passwd (at the moment, they will be refused
|
||||
* anyway because they will mismatch on fake salt.
|
||||
*/
|
||||
|
||||
/* Dispatch handlers */
|
||||
static void input_userauth_jpake_client_step1(int, u_int32_t, void *);
|
||||
static void input_userauth_jpake_client_step2(int, u_int32_t, void *);
|
||||
static void input_userauth_jpake_client_confirm(int, u_int32_t, void *);
|
||||
|
||||
static int auth2_jpake_start(Authctxt *);
|
||||
|
||||
/* import */
|
||||
extern ServerOptions options;
|
||||
extern u_char *session_id2;
|
||||
extern u_int session_id2_len;
|
||||
|
||||
/*
|
||||
* Attempt J-PAKE authentication.
|
||||
*/
|
||||
static int
|
||||
userauth_jpake(Authctxt *authctxt)
|
||||
{
|
||||
int authenticated = 0;
|
||||
|
||||
packet_check_eom();
|
||||
|
||||
debug("jpake-01@openssh.com requested");
|
||||
|
||||
if (authctxt->user != NULL) {
|
||||
if (authctxt->jpake_ctx == NULL)
|
||||
authctxt->jpake_ctx = jpake_new();
|
||||
if (options.zero_knowledge_password_authentication)
|
||||
authenticated = auth2_jpake_start(authctxt);
|
||||
}
|
||||
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
Authmethod method_jpake = {
|
||||
"jpake-01@openssh.com",
|
||||
userauth_jpake,
|
||||
&options.zero_knowledge_password_authentication
|
||||
};
|
||||
|
||||
/* Clear context and callbacks */
|
||||
void
|
||||
auth2_jpake_stop(Authctxt *authctxt)
|
||||
{
|
||||
/* unregister callbacks */
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
|
||||
if (authctxt->jpake_ctx != NULL) {
|
||||
jpake_free(authctxt->jpake_ctx);
|
||||
authctxt->jpake_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns 1 if 'c' is a valid crypt(3) salt character, 0 otherwise */
|
||||
static int
|
||||
valid_crypt_salt(int c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return 1;
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return 1;
|
||||
if (c >= '.' && c <= '9')
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Derive fake salt as H(username || first_private_host_key)
|
||||
* This provides relatively stable fake salts for non-existent
|
||||
* users and avoids the jpake method becoming an account validity
|
||||
* oracle.
|
||||
*/
|
||||
static void
|
||||
derive_rawsalt(const char *username, u_char *rawsalt, u_int len)
|
||||
{
|
||||
u_char *digest;
|
||||
u_int digest_len;
|
||||
Buffer b;
|
||||
Key *k;
|
||||
|
||||
buffer_init(&b);
|
||||
buffer_put_cstring(&b, username);
|
||||
if ((k = get_hostkey_by_index(0)) == NULL ||
|
||||
(k->flags & KEY_FLAG_EXT))
|
||||
fatal("%s: no hostkeys", __func__);
|
||||
switch (k->type) {
|
||||
case KEY_RSA1:
|
||||
case KEY_RSA:
|
||||
if (k->rsa->p == NULL || k->rsa->q == NULL)
|
||||
fatal("%s: RSA key missing p and/or q", __func__);
|
||||
buffer_put_bignum2(&b, k->rsa->p);
|
||||
buffer_put_bignum2(&b, k->rsa->q);
|
||||
break;
|
||||
case KEY_DSA:
|
||||
if (k->dsa->priv_key == NULL)
|
||||
fatal("%s: DSA key missing priv_key", __func__);
|
||||
buffer_put_bignum2(&b, k->dsa->priv_key);
|
||||
break;
|
||||
case KEY_ECDSA:
|
||||
if (EC_KEY_get0_private_key(k->ecdsa) == NULL)
|
||||
fatal("%s: ECDSA key missing priv_key", __func__);
|
||||
buffer_put_bignum2(&b, EC_KEY_get0_private_key(k->ecdsa));
|
||||
break;
|
||||
default:
|
||||
fatal("%s: unknown key type %d", __func__, k->type);
|
||||
}
|
||||
if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(),
|
||||
&digest, &digest_len) != 0)
|
||||
fatal("%s: hash_buffer", __func__);
|
||||
buffer_free(&b);
|
||||
if (len > digest_len)
|
||||
fatal("%s: not enough bytes for rawsalt (want %u have %u)",
|
||||
__func__, len, digest_len);
|
||||
memcpy(rawsalt, digest, len);
|
||||
bzero(digest, digest_len);
|
||||
free(digest);
|
||||
}
|
||||
|
||||
/* ASCII an integer [0, 64) for inclusion in a password/salt */
|
||||
static char
|
||||
pw_encode64(u_int i64)
|
||||
{
|
||||
const u_char e64[] =
|
||||
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
return e64[i64 % 64];
|
||||
}
|
||||
|
||||
/* Generate ASCII salt bytes for user */
|
||||
static char *
|
||||
makesalt(u_int want, const char *user)
|
||||
{
|
||||
u_char rawsalt[32];
|
||||
static char ret[33];
|
||||
u_int i;
|
||||
|
||||
if (want > sizeof(ret) - 1)
|
||||
fatal("%s: want %u", __func__, want);
|
||||
|
||||
derive_rawsalt(user, rawsalt, sizeof(rawsalt));
|
||||
bzero(ret, sizeof(ret));
|
||||
for (i = 0; i < want; i++)
|
||||
ret[i] = pw_encode64(rawsalt[i]);
|
||||
bzero(rawsalt, sizeof(rawsalt));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select the system's default password hashing scheme and generate
|
||||
* a stable fake salt under it for use by a non-existent account.
|
||||
* Prevents jpake method being used to infer the validity of accounts.
|
||||
*/
|
||||
static void
|
||||
fake_salt_and_scheme(Authctxt *authctxt, char **salt, char **scheme)
|
||||
{
|
||||
char *rounds_s, *style;
|
||||
long long rounds;
|
||||
login_cap_t *lc;
|
||||
|
||||
|
||||
if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL &&
|
||||
(lc = login_getclass(NULL)) == NULL)
|
||||
fatal("%s: login_getclass failed", __func__);
|
||||
style = login_getcapstr(lc, "localcipher", NULL, NULL);
|
||||
if (style == NULL)
|
||||
style = xstrdup("blowfish,6");
|
||||
login_close(lc);
|
||||
|
||||
if ((rounds_s = strchr(style, ',')) != NULL)
|
||||
*rounds_s++ = '\0';
|
||||
rounds = strtonum(rounds_s, 1, 1<<31, NULL);
|
||||
|
||||
if (strcmp(style, "md5") == 0) {
|
||||
xasprintf(salt, "$1$%s$", makesalt(8, authctxt->user));
|
||||
*scheme = xstrdup("md5");
|
||||
} else if (strcmp(style, "old") == 0) {
|
||||
*salt = xstrdup(makesalt(2, authctxt->user));
|
||||
*scheme = xstrdup("crypt");
|
||||
} else if (strcmp(style, "newsalt") == 0) {
|
||||
rounds = MAX(rounds, 7250);
|
||||
rounds = MIN(rounds, (1<<24) - 1);
|
||||
xasprintf(salt, "_%c%c%c%c%s",
|
||||
pw_encode64(rounds), pw_encode64(rounds >> 6),
|
||||
pw_encode64(rounds >> 12), pw_encode64(rounds >> 18),
|
||||
makesalt(4, authctxt->user));
|
||||
*scheme = xstrdup("crypt-extended");
|
||||
} else {
|
||||
/* Default to blowfish */
|
||||
rounds = MAX(rounds, 3);
|
||||
rounds = MIN(rounds, 31);
|
||||
xasprintf(salt, "$2a$%02lld$%s", rounds,
|
||||
makesalt(22, authctxt->user));
|
||||
*scheme = xstrdup("bcrypt");
|
||||
}
|
||||
free(style);
|
||||
debug3("%s: fake %s salt for user %s: %s",
|
||||
__func__, *scheme, authctxt->user, *salt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch password hashing scheme, password salt and derive shared secret
|
||||
* for user. If user does not exist, a fake but stable and user-unique
|
||||
* salt will be returned.
|
||||
*/
|
||||
void
|
||||
auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s,
|
||||
char **hash_scheme, char **salt)
|
||||
{
|
||||
char *cp;
|
||||
u_char *secret;
|
||||
u_int secret_len, salt_len;
|
||||
|
||||
#ifdef JPAKE_DEBUG
|
||||
debug3("%s: valid %d pw %.5s...", __func__,
|
||||
authctxt->valid, authctxt->pw->pw_passwd);
|
||||
#endif
|
||||
|
||||
*salt = NULL;
|
||||
*hash_scheme = NULL;
|
||||
if (authctxt->valid) {
|
||||
if (strncmp(authctxt->pw->pw_passwd, "$2$", 3) == 0 &&
|
||||
strlen(authctxt->pw->pw_passwd) > 28) {
|
||||
/*
|
||||
* old-variant bcrypt:
|
||||
* "$2$", 2 digit rounds, "$", 22 bytes salt
|
||||
*/
|
||||
salt_len = 3 + 2 + 1 + 22 + 1;
|
||||
*salt = xmalloc(salt_len);
|
||||
strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
|
||||
*hash_scheme = xstrdup("bcrypt");
|
||||
} else if (strncmp(authctxt->pw->pw_passwd, "$2a$", 4) == 0 &&
|
||||
strlen(authctxt->pw->pw_passwd) > 29) {
|
||||
/*
|
||||
* current-variant bcrypt:
|
||||
* "$2a$", 2 digit rounds, "$", 22 bytes salt
|
||||
*/
|
||||
salt_len = 4 + 2 + 1 + 22 + 1;
|
||||
*salt = xmalloc(salt_len);
|
||||
strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
|
||||
*hash_scheme = xstrdup("bcrypt");
|
||||
} else if (strncmp(authctxt->pw->pw_passwd, "$1$", 3) == 0 &&
|
||||
strlen(authctxt->pw->pw_passwd) > 5) {
|
||||
/*
|
||||
* md5crypt:
|
||||
* "$1$", salt until "$"
|
||||
*/
|
||||
cp = strchr(authctxt->pw->pw_passwd + 3, '$');
|
||||
if (cp != NULL) {
|
||||
salt_len = (cp - authctxt->pw->pw_passwd) + 1;
|
||||
*salt = xmalloc(salt_len);
|
||||
strlcpy(*salt, authctxt->pw->pw_passwd,
|
||||
salt_len);
|
||||
*hash_scheme = xstrdup("md5crypt");
|
||||
}
|
||||
} else if (strncmp(authctxt->pw->pw_passwd, "_", 1) == 0 &&
|
||||
strlen(authctxt->pw->pw_passwd) > 9) {
|
||||
/*
|
||||
* BSDI extended crypt:
|
||||
* "_", 4 digits count, 4 chars salt
|
||||
*/
|
||||
salt_len = 1 + 4 + 4 + 1;
|
||||
*salt = xmalloc(salt_len);
|
||||
strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
|
||||
*hash_scheme = xstrdup("crypt-extended");
|
||||
} else if (strlen(authctxt->pw->pw_passwd) == 13 &&
|
||||
valid_crypt_salt(authctxt->pw->pw_passwd[0]) &&
|
||||
valid_crypt_salt(authctxt->pw->pw_passwd[1])) {
|
||||
/*
|
||||
* traditional crypt:
|
||||
* 2 chars salt
|
||||
*/
|
||||
salt_len = 2 + 1;
|
||||
*salt = xmalloc(salt_len);
|
||||
strlcpy(*salt, authctxt->pw->pw_passwd, salt_len);
|
||||
*hash_scheme = xstrdup("crypt");
|
||||
}
|
||||
if (*salt == NULL) {
|
||||
debug("%s: unrecognised crypt scheme for user %s",
|
||||
__func__, authctxt->pw->pw_name);
|
||||
}
|
||||
}
|
||||
if (*salt == NULL)
|
||||
fake_salt_and_scheme(authctxt, salt, hash_scheme);
|
||||
|
||||
if (hash_buffer(authctxt->pw->pw_passwd,
|
||||
strlen(authctxt->pw->pw_passwd), EVP_sha256(),
|
||||
&secret, &secret_len) != 0)
|
||||
fatal("%s: hash_buffer", __func__);
|
||||
if ((*s = BN_bin2bn(secret, secret_len, NULL)) == NULL)
|
||||
fatal("%s: BN_bin2bn (secret)", __func__);
|
||||
#ifdef JPAKE_DEBUG
|
||||
debug3("%s: salt = %s (len %u)", __func__,
|
||||
*salt, (u_int)strlen(*salt));
|
||||
debug3("%s: scheme = %s", __func__, *hash_scheme);
|
||||
JPAKE_DEBUG_BN((*s, "%s: s = ", __func__));
|
||||
#endif
|
||||
bzero(secret, secret_len);
|
||||
free(secret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Begin authentication attempt.
|
||||
* Note, sets authctxt->postponed while in subprotocol
|
||||
*/
|
||||
static int
|
||||
auth2_jpake_start(Authctxt *authctxt)
|
||||
{
|
||||
struct jpake_ctx *pctx = authctxt->jpake_ctx;
|
||||
u_char *x3_proof, *x4_proof;
|
||||
u_int x3_proof_len, x4_proof_len;
|
||||
char *salt, *hash_scheme;
|
||||
|
||||
debug("%s: start", __func__);
|
||||
|
||||
PRIVSEP(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));
|
||||
|
||||
PRIVSEP(auth2_jpake_get_pwdata(authctxt, &pctx->s,
|
||||
&hash_scheme, &salt));
|
||||
|
||||
if (!use_privsep)
|
||||
JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__));
|
||||
|
||||
packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1);
|
||||
packet_put_cstring(hash_scheme);
|
||||
packet_put_cstring(salt);
|
||||
packet_put_string(pctx->server_id, pctx->server_id_len);
|
||||
packet_put_bignum2(pctx->g_x3);
|
||||
packet_put_bignum2(pctx->g_x4);
|
||||
packet_put_string(x3_proof, x3_proof_len);
|
||||
packet_put_string(x4_proof, x4_proof_len);
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
|
||||
bzero(hash_scheme, strlen(hash_scheme));
|
||||
bzero(salt, strlen(salt));
|
||||
free(hash_scheme);
|
||||
free(salt);
|
||||
bzero(x3_proof, x3_proof_len);
|
||||
bzero(x4_proof, x4_proof_len);
|
||||
free(x3_proof);
|
||||
free(x4_proof);
|
||||
|
||||
/* Expect step 1 packet from peer */
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1,
|
||||
input_userauth_jpake_client_step1);
|
||||
|
||||
authctxt->postponed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
input_userauth_jpake_client_step1(int type, u_int32_t seq, void *ctxt)
|
||||
{
|
||||
Authctxt *authctxt = ctxt;
|
||||
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;
|
||||
|
||||
/* Disable this message */
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1, NULL);
|
||||
|
||||
/* Fetch step 1 values */
|
||||
if ((pctx->g_x1 = BN_new()) == NULL ||
|
||||
(pctx->g_x2 = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
pctx->client_id = packet_get_string(&pctx->client_id_len);
|
||||
packet_get_bignum2(pctx->g_x1);
|
||||
packet_get_bignum2(pctx->g_x2);
|
||||
x1_proof = packet_get_string(&x1_proof_len);
|
||||
x2_proof = packet_get_string(&x2_proof_len);
|
||||
packet_check_eom();
|
||||
|
||||
if (!use_privsep)
|
||||
JPAKE_DEBUG_CTX((pctx, "step 1 received in %s", __func__));
|
||||
|
||||
PRIVSEP(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));
|
||||
|
||||
bzero(x1_proof, x1_proof_len);
|
||||
bzero(x2_proof, x2_proof_len);
|
||||
free(x1_proof);
|
||||
free(x2_proof);
|
||||
|
||||
if (!use_privsep)
|
||||
JPAKE_DEBUG_CTX((pctx, "step 2 sending in %s", __func__));
|
||||
|
||||
/* Send values for step 2 */
|
||||
packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2);
|
||||
packet_put_bignum2(pctx->b);
|
||||
packet_put_string(x4_s_proof, x4_s_proof_len);
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
|
||||
bzero(x4_s_proof, x4_s_proof_len);
|
||||
free(x4_s_proof);
|
||||
|
||||
/* Expect step 2 packet from peer */
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2,
|
||||
input_userauth_jpake_client_step2);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
input_userauth_jpake_client_step2(int type, u_int32_t seq, void *ctxt)
|
||||
{
|
||||
Authctxt *authctxt = ctxt;
|
||||
struct jpake_ctx *pctx = authctxt->jpake_ctx;
|
||||
u_char *x2_s_proof;
|
||||
u_int x2_s_proof_len;
|
||||
|
||||
/* Disable this message */
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2, NULL);
|
||||
|
||||
if ((pctx->a = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
|
||||
/* Fetch step 2 values */
|
||||
packet_get_bignum2(pctx->a);
|
||||
x2_s_proof = packet_get_string(&x2_s_proof_len);
|
||||
packet_check_eom();
|
||||
|
||||
if (!use_privsep)
|
||||
JPAKE_DEBUG_CTX((pctx, "step 2 received in %s", __func__));
|
||||
|
||||
/* Derive shared key and calculate confirmation hash */
|
||||
PRIVSEP(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));
|
||||
|
||||
bzero(x2_s_proof, x2_s_proof_len);
|
||||
free(x2_s_proof);
|
||||
|
||||
if (!use_privsep)
|
||||
JPAKE_DEBUG_CTX((pctx, "confirm sending in %s", __func__));
|
||||
|
||||
/* Send key confirmation proof */
|
||||
packet_start(SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM);
|
||||
packet_put_string(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
|
||||
/* Expect confirmation from peer */
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM,
|
||||
input_userauth_jpake_client_confirm);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
input_userauth_jpake_client_confirm(int type, u_int32_t seq, void *ctxt)
|
||||
{
|
||||
Authctxt *authctxt = ctxt;
|
||||
struct jpake_ctx *pctx = authctxt->jpake_ctx;
|
||||
int authenticated = 0;
|
||||
|
||||
/* Disable this message */
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM, NULL);
|
||||
|
||||
pctx->h_k_cid_sessid = packet_get_string(&pctx->h_k_cid_sessid_len);
|
||||
packet_check_eom();
|
||||
|
||||
if (!use_privsep)
|
||||
JPAKE_DEBUG_CTX((pctx, "confirm received in %s", __func__));
|
||||
|
||||
/* Verify expected confirmation hash */
|
||||
if (PRIVSEP(jpake_check_confirm(pctx->k,
|
||||
pctx->client_id, pctx->client_id_len,
|
||||
session_id2, session_id2_len,
|
||||
pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len)) == 1)
|
||||
authenticated = authctxt->valid ? 1 : 0;
|
||||
else
|
||||
debug("%s: confirmation mismatch", __func__);
|
||||
|
||||
/* done */
|
||||
authctxt->postponed = 0;
|
||||
jpake_free(authctxt->jpake_ctx);
|
||||
authctxt->jpake_ctx = NULL;
|
||||
userauth_finish(authctxt, authenticated, method_jpake.name, NULL);
|
||||
}
|
||||
|
||||
#endif /* JPAKE */
|
||||
|
11
auth2.c
11
auth2.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: auth2.c,v 1.129 2013/05/19 02:42:42 djm Exp $ */
|
||||
/* $OpenBSD: auth2.c,v 1.130 2014/01/29 06:18:35 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
*
|
||||
|
@ -71,18 +71,12 @@ extern Authmethod method_hostbased;
|
|||
#ifdef GSSAPI
|
||||
extern Authmethod method_gssapi;
|
||||
#endif
|
||||
#ifdef JPAKE
|
||||
extern Authmethod method_jpake;
|
||||
#endif
|
||||
|
||||
Authmethod *authmethods[] = {
|
||||
&method_none,
|
||||
&method_pubkey,
|
||||
#ifdef GSSAPI
|
||||
&method_gssapi,
|
||||
#endif
|
||||
#ifdef JPAKE
|
||||
&method_jpake,
|
||||
#endif
|
||||
&method_passwd,
|
||||
&method_kbdint,
|
||||
|
@ -270,9 +264,6 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
|
|||
}
|
||||
/* reset state */
|
||||
auth2_challenge_stop(authctxt);
|
||||
#ifdef JPAKE
|
||||
auth2_jpake_stop(authctxt);
|
||||
#endif
|
||||
|
||||
#ifdef GSSAPI
|
||||
/* XXX move to auth2_gssapi_stop() */
|
||||
|
|
456
jpake.c
456
jpake.c
|
@ -1,456 +0,0 @@
|
|||
/* $OpenBSD: jpake.c,v 1.8 2013/05/17 00:13:13 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008 Damien Miller. All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Shared components of zero-knowledge password auth using J-PAKE protocol
|
||||
* as described in:
|
||||
*
|
||||
* F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
|
||||
* 16th Workshop on Security Protocols, Cambridge, April 2008
|
||||
*
|
||||
* http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "xmalloc.h"
|
||||
#include "ssh2.h"
|
||||
#include "key.h"
|
||||
#include "hostfile.h"
|
||||
#include "auth.h"
|
||||
#include "buffer.h"
|
||||
#include "packet.h"
|
||||
#include "dispatch.h"
|
||||
#include "log.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "jpake.h"
|
||||
#include "schnorr.h"
|
||||
|
||||
#ifdef JPAKE
|
||||
|
||||
/* RFC3526 group 5, 1536 bits */
|
||||
#define JPAKE_GROUP_G "2"
|
||||
#define JPAKE_GROUP_P \
|
||||
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74" \
|
||||
"020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437" \
|
||||
"4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05" \
|
||||
"98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB" \
|
||||
"9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
|
||||
|
||||
struct modp_group *
|
||||
jpake_default_group(void)
|
||||
{
|
||||
return modp_group_from_g_and_safe_p(JPAKE_GROUP_G, JPAKE_GROUP_P);
|
||||
}
|
||||
|
||||
struct jpake_ctx *
|
||||
jpake_new(void)
|
||||
{
|
||||
struct jpake_ctx *ret;
|
||||
|
||||
ret = xcalloc(1, sizeof(*ret));
|
||||
|
||||
ret->grp = jpake_default_group();
|
||||
|
||||
ret->s = ret->k = NULL;
|
||||
ret->x1 = ret->x2 = ret->x3 = ret->x4 = NULL;
|
||||
ret->g_x1 = ret->g_x2 = ret->g_x3 = ret->g_x4 = NULL;
|
||||
ret->a = ret->b = NULL;
|
||||
|
||||
ret->client_id = ret->server_id = NULL;
|
||||
ret->h_k_cid_sessid = ret->h_k_sid_sessid = NULL;
|
||||
|
||||
debug3("%s: alloc %p", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
jpake_free(struct jpake_ctx *pctx)
|
||||
{
|
||||
debug3("%s: free %p", __func__, pctx);
|
||||
|
||||
#define JPAKE_BN_CLEAR_FREE(v) \
|
||||
do { \
|
||||
if ((v) != NULL) { \
|
||||
BN_clear_free(v); \
|
||||
(v) = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
#define JPAKE_BUF_CLEAR_FREE(v, l) \
|
||||
do { \
|
||||
if ((v) != NULL) { \
|
||||
bzero((v), (l)); \
|
||||
free(v); \
|
||||
(v) = NULL; \
|
||||
(l) = 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
JPAKE_BN_CLEAR_FREE(pctx->s);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->k);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->x1);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->x2);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->x3);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->x4);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->g_x1);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->g_x2);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->g_x3);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->g_x4);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->a);
|
||||
JPAKE_BN_CLEAR_FREE(pctx->b);
|
||||
|
||||
JPAKE_BUF_CLEAR_FREE(pctx->client_id, pctx->client_id_len);
|
||||
JPAKE_BUF_CLEAR_FREE(pctx->server_id, pctx->server_id_len);
|
||||
JPAKE_BUF_CLEAR_FREE(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len);
|
||||
JPAKE_BUF_CLEAR_FREE(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
|
||||
|
||||
#undef JPAKE_BN_CLEAR_FREE
|
||||
#undef JPAKE_BUF_CLEAR_FREE
|
||||
|
||||
bzero(pctx, sizeof(*pctx));
|
||||
free(pctx);
|
||||
}
|
||||
|
||||
/* dump entire jpake_ctx. NB. includes private values! */
|
||||
void
|
||||
jpake_dump(struct jpake_ctx *pctx, const char *fmt, ...)
|
||||
{
|
||||
char *out;
|
||||
va_list args;
|
||||
|
||||
out = NULL;
|
||||
va_start(args, fmt);
|
||||
vasprintf(&out, fmt, args);
|
||||
va_end(args);
|
||||
if (out == NULL)
|
||||
fatal("%s: vasprintf failed", __func__);
|
||||
|
||||
debug3("%s: %s (ctx at %p)", __func__, out, pctx);
|
||||
if (pctx == NULL) {
|
||||
free(out);
|
||||
return;
|
||||
}
|
||||
|
||||
#define JPAKE_DUMP_BN(a) do { \
|
||||
if ((a) != NULL) \
|
||||
JPAKE_DEBUG_BN(((a), "%s = ", #a)); \
|
||||
} while (0)
|
||||
#define JPAKE_DUMP_BUF(a, b) do { \
|
||||
if ((a) != NULL) \
|
||||
JPAKE_DEBUG_BUF((a, b, "%s", #a)); \
|
||||
} while (0)
|
||||
|
||||
JPAKE_DUMP_BN(pctx->s);
|
||||
JPAKE_DUMP_BN(pctx->k);
|
||||
JPAKE_DUMP_BN(pctx->x1);
|
||||
JPAKE_DUMP_BN(pctx->x2);
|
||||
JPAKE_DUMP_BN(pctx->x3);
|
||||
JPAKE_DUMP_BN(pctx->x4);
|
||||
JPAKE_DUMP_BN(pctx->g_x1);
|
||||
JPAKE_DUMP_BN(pctx->g_x2);
|
||||
JPAKE_DUMP_BN(pctx->g_x3);
|
||||
JPAKE_DUMP_BN(pctx->g_x4);
|
||||
JPAKE_DUMP_BN(pctx->a);
|
||||
JPAKE_DUMP_BN(pctx->b);
|
||||
|
||||
JPAKE_DUMP_BUF(pctx->client_id, pctx->client_id_len);
|
||||
JPAKE_DUMP_BUF(pctx->server_id, pctx->server_id_len);
|
||||
JPAKE_DUMP_BUF(pctx->h_k_cid_sessid, pctx->h_k_cid_sessid_len);
|
||||
JPAKE_DUMP_BUF(pctx->h_k_sid_sessid, pctx->h_k_sid_sessid_len);
|
||||
|
||||
debug3("%s: %s done", __func__, out);
|
||||
free(out);
|
||||
}
|
||||
|
||||
/* Shared parts of step 1 exchange calculation */
|
||||
void
|
||||
jpake_step1(struct modp_group *grp,
|
||||
u_char **id, u_int *id_len,
|
||||
BIGNUM **priv1, BIGNUM **priv2, BIGNUM **g_priv1, BIGNUM **g_priv2,
|
||||
u_char **priv1_proof, u_int *priv1_proof_len,
|
||||
u_char **priv2_proof, u_int *priv2_proof_len)
|
||||
{
|
||||
BN_CTX *bn_ctx;
|
||||
|
||||
if ((bn_ctx = BN_CTX_new()) == NULL)
|
||||
fatal("%s: BN_CTX_new", __func__);
|
||||
|
||||
/* Random nonce to prevent replay */
|
||||
*id = xmalloc(KZP_ID_LEN);
|
||||
*id_len = KZP_ID_LEN;
|
||||
arc4random_buf(*id, *id_len);
|
||||
|
||||
/*
|
||||
* x1/x3 is a random element of Zq
|
||||
* x2/x4 is a random element of Z*q
|
||||
* We also exclude [1] from x1/x3 candidates and [0, 1] from
|
||||
* x2/x4 candiates to avoid possible degeneracy (i.e. g^0, g^1).
|
||||
*/
|
||||
if ((*priv1 = bn_rand_range_gt_one(grp->q)) == NULL ||
|
||||
(*priv2 = bn_rand_range_gt_one(grp->q)) == NULL)
|
||||
fatal("%s: bn_rand_range_gt_one", __func__);
|
||||
|
||||
/*
|
||||
* client: g_x1 = g^x1 mod p / server: g_x3 = g^x3 mod p
|
||||
* client: g_x2 = g^x2 mod p / server: g_x4 = g^x4 mod p
|
||||
*/
|
||||
if ((*g_priv1 = BN_new()) == NULL ||
|
||||
(*g_priv2 = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
if (BN_mod_exp(*g_priv1, grp->g, *priv1, grp->p, bn_ctx) == -1)
|
||||
fatal("%s: BN_mod_exp", __func__);
|
||||
if (BN_mod_exp(*g_priv2, grp->g, *priv2, grp->p, bn_ctx) == -1)
|
||||
fatal("%s: BN_mod_exp", __func__);
|
||||
|
||||
/* Generate proofs for holding x1/x3 and x2/x4 */
|
||||
if (schnorr_sign_buf(grp->p, grp->q, grp->g,
|
||||
*priv1, *g_priv1, *id, *id_len,
|
||||
priv1_proof, priv1_proof_len) != 0)
|
||||
fatal("%s: schnorr_sign", __func__);
|
||||
if (schnorr_sign_buf(grp->p, grp->q, grp->g,
|
||||
*priv2, *g_priv2, *id, *id_len,
|
||||
priv2_proof, priv2_proof_len) != 0)
|
||||
fatal("%s: schnorr_sign", __func__);
|
||||
|
||||
BN_CTX_free(bn_ctx);
|
||||
}
|
||||
|
||||
/* Shared parts of step 2 exchange calculation */
|
||||
void
|
||||
jpake_step2(struct modp_group *grp, BIGNUM *s,
|
||||
BIGNUM *mypub1, BIGNUM *theirpub1, BIGNUM *theirpub2, BIGNUM *mypriv2,
|
||||
const u_char *theirid, u_int theirid_len,
|
||||
const u_char *myid, u_int myid_len,
|
||||
const u_char *theirpub1_proof, u_int theirpub1_proof_len,
|
||||
const u_char *theirpub2_proof, u_int theirpub2_proof_len,
|
||||
BIGNUM **newpub,
|
||||
u_char **newpub_exponent_proof, u_int *newpub_exponent_proof_len)
|
||||
{
|
||||
BN_CTX *bn_ctx;
|
||||
BIGNUM *tmp, *exponent;
|
||||
|
||||
/* Validate peer's step 1 values */
|
||||
if (BN_cmp(theirpub1, BN_value_one()) <= 0)
|
||||
fatal("%s: theirpub1 <= 1", __func__);
|
||||
if (BN_cmp(theirpub1, grp->p) >= 0)
|
||||
fatal("%s: theirpub1 >= p", __func__);
|
||||
if (BN_cmp(theirpub2, BN_value_one()) <= 0)
|
||||
fatal("%s: theirpub2 <= 1", __func__);
|
||||
if (BN_cmp(theirpub2, grp->p) >= 0)
|
||||
fatal("%s: theirpub2 >= p", __func__);
|
||||
|
||||
if (schnorr_verify_buf(grp->p, grp->q, grp->g, theirpub1,
|
||||
theirid, theirid_len, theirpub1_proof, theirpub1_proof_len) != 1)
|
||||
fatal("%s: schnorr_verify theirpub1 failed", __func__);
|
||||
if (schnorr_verify_buf(grp->p, grp->q, grp->g, theirpub2,
|
||||
theirid, theirid_len, theirpub2_proof, theirpub2_proof_len) != 1)
|
||||
fatal("%s: schnorr_verify theirpub2 failed", __func__);
|
||||
|
||||
if ((bn_ctx = BN_CTX_new()) == NULL)
|
||||
fatal("%s: BN_CTX_new", __func__);
|
||||
|
||||
if ((*newpub = BN_new()) == NULL ||
|
||||
(tmp = BN_new()) == NULL ||
|
||||
(exponent = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
|
||||
/*
|
||||
* client: exponent = x2 * s mod p
|
||||
* server: exponent = x4 * s mod p
|
||||
*/
|
||||
if (BN_mod_mul(exponent, mypriv2, s, grp->q, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_mul (exponent = mypriv2 * s mod p)",
|
||||
__func__);
|
||||
|
||||
/*
|
||||
* client: tmp = g^(x1 + x3 + x4) mod p
|
||||
* server: tmp = g^(x1 + x2 + x3) mod p
|
||||
*/
|
||||
if (BN_mod_mul(tmp, mypub1, theirpub1, grp->p, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_mul (tmp = mypub1 * theirpub1 mod p)",
|
||||
__func__);
|
||||
if (BN_mod_mul(tmp, tmp, theirpub2, grp->p, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_mul (tmp = tmp * theirpub2 mod p)", __func__);
|
||||
|
||||
/*
|
||||
* client: a = tmp^exponent = g^((x1+x3+x4) * x2 * s) mod p
|
||||
* server: b = tmp^exponent = g^((x1+x2+x3) * x4 * s) mod p
|
||||
*/
|
||||
if (BN_mod_exp(*newpub, tmp, exponent, grp->p, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_mul (newpub = tmp^exponent mod p)", __func__);
|
||||
|
||||
JPAKE_DEBUG_BN((tmp, "%s: tmp = ", __func__));
|
||||
JPAKE_DEBUG_BN((exponent, "%s: exponent = ", __func__));
|
||||
|
||||
/* Note the generator here is 'tmp', not g */
|
||||
if (schnorr_sign_buf(grp->p, grp->q, tmp, exponent, *newpub,
|
||||
myid, myid_len,
|
||||
newpub_exponent_proof, newpub_exponent_proof_len) != 0)
|
||||
fatal("%s: schnorr_sign newpub", __func__);
|
||||
|
||||
BN_clear_free(tmp); /* XXX stash for later use? */
|
||||
BN_clear_free(exponent); /* XXX stash for later use? (yes, in conf) */
|
||||
|
||||
BN_CTX_free(bn_ctx);
|
||||
}
|
||||
|
||||
/* Confirmation hash calculation */
|
||||
void
|
||||
jpake_confirm_hash(const BIGNUM *k,
|
||||
const u_char *endpoint_id, u_int endpoint_id_len,
|
||||
const u_char *sess_id, u_int sess_id_len,
|
||||
u_char **confirm_hash, u_int *confirm_hash_len)
|
||||
{
|
||||
Buffer b;
|
||||
|
||||
/*
|
||||
* Calculate confirmation proof:
|
||||
* client: H(k || client_id || session_id)
|
||||
* server: H(k || server_id || session_id)
|
||||
*/
|
||||
buffer_init(&b);
|
||||
buffer_put_bignum2(&b, k);
|
||||
buffer_put_string(&b, endpoint_id, endpoint_id_len);
|
||||
buffer_put_string(&b, sess_id, sess_id_len);
|
||||
if (hash_buffer(buffer_ptr(&b), buffer_len(&b), EVP_sha256(),
|
||||
confirm_hash, confirm_hash_len) != 0)
|
||||
fatal("%s: hash_buffer", __func__);
|
||||
buffer_free(&b);
|
||||
}
|
||||
|
||||
/* Shared parts of key derivation and confirmation calculation */
|
||||
void
|
||||
jpake_key_confirm(struct modp_group *grp, BIGNUM *s, BIGNUM *step2_val,
|
||||
BIGNUM *mypriv2, BIGNUM *mypub1, BIGNUM *mypub2,
|
||||
BIGNUM *theirpub1, BIGNUM *theirpub2,
|
||||
const u_char *my_id, u_int my_id_len,
|
||||
const u_char *their_id, u_int their_id_len,
|
||||
const u_char *sess_id, u_int sess_id_len,
|
||||
const u_char *theirpriv2_s_proof, u_int theirpriv2_s_proof_len,
|
||||
BIGNUM **k,
|
||||
u_char **confirm_hash, u_int *confirm_hash_len)
|
||||
{
|
||||
BN_CTX *bn_ctx;
|
||||
BIGNUM *tmp;
|
||||
|
||||
if ((bn_ctx = BN_CTX_new()) == NULL)
|
||||
fatal("%s: BN_CTX_new", __func__);
|
||||
if ((tmp = BN_new()) == NULL ||
|
||||
(*k = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
|
||||
/* Validate step 2 values */
|
||||
if (BN_cmp(step2_val, BN_value_one()) <= 0)
|
||||
fatal("%s: step2_val <= 1", __func__);
|
||||
if (BN_cmp(step2_val, grp->p) >= 0)
|
||||
fatal("%s: step2_val >= p", __func__);
|
||||
|
||||
/*
|
||||
* theirpriv2_s_proof is calculated with a different generator:
|
||||
* tmp = g^(mypriv1+mypriv2+theirpub1) = g^mypub1*g^mypub2*g^theirpub1
|
||||
* Calculate it here so we can check the signature.
|
||||
*/
|
||||
if (BN_mod_mul(tmp, mypub1, mypub2, grp->p, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_mul (tmp = mypub1 * mypub2 mod p)", __func__);
|
||||
if (BN_mod_mul(tmp, tmp, theirpub1, grp->p, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_mul (tmp = tmp * theirpub1 mod p)", __func__);
|
||||
|
||||
JPAKE_DEBUG_BN((tmp, "%s: tmp = ", __func__));
|
||||
|
||||
if (schnorr_verify_buf(grp->p, grp->q, tmp, step2_val,
|
||||
their_id, their_id_len,
|
||||
theirpriv2_s_proof, theirpriv2_s_proof_len) != 1)
|
||||
fatal("%s: schnorr_verify theirpriv2_s_proof failed", __func__);
|
||||
|
||||
/*
|
||||
* Derive shared key:
|
||||
* client: k = (b / g^(x2*x4*s))^x2 = g^((x1+x3)*x2*x4*s)
|
||||
* server: k = (a / g^(x2*x4*s))^x4 = g^((x1+x3)*x2*x4*s)
|
||||
*
|
||||
* Computed as:
|
||||
* client: k = (g_x4^(q - (x2 * s)) * b)^x2 mod p
|
||||
* server: k = (g_x2^(q - (x4 * s)) * b)^x4 mod p
|
||||
*/
|
||||
if (BN_mul(tmp, mypriv2, s, bn_ctx) != 1)
|
||||
fatal("%s: BN_mul (tmp = mypriv2 * s)", __func__);
|
||||
if (BN_mod_sub(tmp, grp->q, tmp, grp->q, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_sub (tmp = q - tmp mod q)", __func__);
|
||||
if (BN_mod_exp(tmp, theirpub2, tmp, grp->p, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_exp (tmp = theirpub2^tmp) mod p", __func__);
|
||||
if (BN_mod_mul(tmp, tmp, step2_val, grp->p, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_mul (tmp = tmp * step2_val) mod p", __func__);
|
||||
if (BN_mod_exp(*k, tmp, mypriv2, grp->p, bn_ctx) != 1)
|
||||
fatal("%s: BN_mod_exp (k = tmp^mypriv2) mod p", __func__);
|
||||
|
||||
BN_CTX_free(bn_ctx);
|
||||
BN_clear_free(tmp);
|
||||
|
||||
jpake_confirm_hash(*k, my_id, my_id_len, sess_id, sess_id_len,
|
||||
confirm_hash, confirm_hash_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate and check confirmation hash from peer. Returns 1 on success
|
||||
* 0 on failure/mismatch.
|
||||
*/
|
||||
int
|
||||
jpake_check_confirm(const BIGNUM *k,
|
||||
const u_char *peer_id, u_int peer_id_len,
|
||||
const u_char *sess_id, u_int sess_id_len,
|
||||
const u_char *peer_confirm_hash, u_int peer_confirm_hash_len)
|
||||
{
|
||||
u_char *expected_confirm_hash;
|
||||
u_int expected_confirm_hash_len;
|
||||
int success = 0;
|
||||
|
||||
/* Calculate and verify expected confirmation hash */
|
||||
jpake_confirm_hash(k, peer_id, peer_id_len, sess_id, sess_id_len,
|
||||
&expected_confirm_hash, &expected_confirm_hash_len);
|
||||
|
||||
JPAKE_DEBUG_BUF((expected_confirm_hash, expected_confirm_hash_len,
|
||||
"%s: expected confirm hash", __func__));
|
||||
JPAKE_DEBUG_BUF((peer_confirm_hash, peer_confirm_hash_len,
|
||||
"%s: received confirm hash", __func__));
|
||||
|
||||
if (peer_confirm_hash_len != expected_confirm_hash_len)
|
||||
error("%s: confirmation length mismatch (my %u them %u)",
|
||||
__func__, expected_confirm_hash_len, peer_confirm_hash_len);
|
||||
else if (timingsafe_bcmp(peer_confirm_hash, expected_confirm_hash,
|
||||
expected_confirm_hash_len) == 0)
|
||||
success = 1;
|
||||
bzero(expected_confirm_hash, expected_confirm_hash_len);
|
||||
free(expected_confirm_hash);
|
||||
debug3("%s: success = %d", __func__, success);
|
||||
return success;
|
||||
}
|
||||
|
||||
/* XXX main() function with tests */
|
||||
|
||||
#endif /* JPAKE */
|
||||
|
114
jpake.h
114
jpake.h
|
@ -1,114 +0,0 @@
|
|||
/* $OpenBSD: jpake.h,v 1.2 2009/03/05 07:18:19 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008 Damien Miller. All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JPAKE_H
|
||||
#define JPAKE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openssl/bn.h>
|
||||
|
||||
/* Set JPAKE_DEBUG in CFLAGS for privacy-violating debugging */
|
||||
#ifndef JPAKE_DEBUG
|
||||
# define JPAKE_DEBUG_BN(a)
|
||||
# define JPAKE_DEBUG_BUF(a)
|
||||
# define JPAKE_DEBUG_CTX(a)
|
||||
#else
|
||||
# define JPAKE_DEBUG_BN(a) debug3_bn a
|
||||
# define JPAKE_DEBUG_BUF(a) debug3_buf a
|
||||
# define JPAKE_DEBUG_CTX(a) jpake_dump a
|
||||
#endif /* JPAKE_DEBUG */
|
||||
|
||||
#define KZP_ID_LEN 16 /* Length of client and server IDs */
|
||||
|
||||
struct jpake_ctx {
|
||||
/* Parameters */
|
||||
struct modp_group *grp;
|
||||
|
||||
/* Private values shared by client and server */
|
||||
BIGNUM *s; /* Secret (salted, crypted password) */
|
||||
BIGNUM *k; /* Derived key */
|
||||
|
||||
/* Client private values (NULL for server) */
|
||||
BIGNUM *x1; /* random in Zq */
|
||||
BIGNUM *x2; /* random in Z*q */
|
||||
|
||||
/* Server private values (NULL for server) */
|
||||
BIGNUM *x3; /* random in Zq */
|
||||
BIGNUM *x4; /* random in Z*q */
|
||||
|
||||
/* Step 1: C->S */
|
||||
u_char *client_id; /* Anti-replay nonce */
|
||||
u_int client_id_len;
|
||||
BIGNUM *g_x1; /* g^x1 */
|
||||
BIGNUM *g_x2; /* g^x2 */
|
||||
|
||||
/* Step 1: S->C */
|
||||
u_char *server_id; /* Anti-replay nonce */
|
||||
u_int server_id_len;
|
||||
BIGNUM *g_x3; /* g^x3 */
|
||||
BIGNUM *g_x4; /* g^x4 */
|
||||
|
||||
/* Step 2: C->S */
|
||||
BIGNUM *a; /* g^((x1+x3+x4)*x2*s) */
|
||||
|
||||
/* Step 2: S->C */
|
||||
BIGNUM *b; /* g^((x1+x2+x3)*x4*s) */
|
||||
|
||||
/* Confirmation: C->S */
|
||||
u_char *h_k_cid_sessid; /* H(k || client_id || session_id) */
|
||||
u_int h_k_cid_sessid_len;
|
||||
|
||||
/* Confirmation: S->C */
|
||||
u_char *h_k_sid_sessid; /* H(k || server_id || session_id) */
|
||||
u_int h_k_sid_sessid_len;
|
||||
};
|
||||
|
||||
/* jpake.c */
|
||||
struct modp_group *jpake_default_group(void);
|
||||
void jpake_dump(struct jpake_ctx *, const char *, ...)
|
||||
__attribute__((__nonnull__ (2)))
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
struct jpake_ctx *jpake_new(void);
|
||||
void jpake_free(struct jpake_ctx *);
|
||||
|
||||
void jpake_step1(struct modp_group *, u_char **, u_int *,
|
||||
BIGNUM **, BIGNUM **, BIGNUM **, BIGNUM **,
|
||||
u_char **, u_int *, u_char **, u_int *);
|
||||
|
||||
void jpake_step2(struct modp_group *, BIGNUM *,
|
||||
BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
|
||||
const u_char *, u_int, const u_char *, u_int,
|
||||
const u_char *, u_int, const u_char *, u_int,
|
||||
BIGNUM **, u_char **, u_int *);
|
||||
|
||||
void jpake_confirm_hash(const BIGNUM *,
|
||||
const u_char *, u_int,
|
||||
const u_char *, u_int,
|
||||
u_char **, u_int *);
|
||||
|
||||
void jpake_key_confirm(struct modp_group *, BIGNUM *, BIGNUM *,
|
||||
BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
|
||||
const u_char *, u_int, const u_char *, u_int,
|
||||
const u_char *, u_int, const u_char *, u_int,
|
||||
BIGNUM **, u_char **, u_int *);
|
||||
|
||||
int jpake_check_confirm(const BIGNUM *, const u_char *, u_int,
|
||||
const u_char *, u_int, const u_char *, u_int);
|
||||
|
||||
#endif /* JPAKE_H */
|
||||
|
226
monitor.c
226
monitor.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: monitor.c,v 1.128 2013/11/04 11:51:16 markus Exp $ */
|
||||
/* $OpenBSD: monitor.c,v 1.129 2014/01/29 06:18:35 djm Exp $ */
|
||||
/*
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* Copyright 2002 Markus Friedl <markus@openbsd.org>
|
||||
|
@ -95,7 +95,6 @@
|
|||
#include "misc.h"
|
||||
#include "compat.h"
|
||||
#include "ssh2.h"
|
||||
#include "jpake.h"
|
||||
#include "roaming.h"
|
||||
#include "authfd.h"
|
||||
|
||||
|
@ -161,11 +160,6 @@ int mm_answer_rsa_challenge(int, Buffer *);
|
|||
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 *);
|
||||
|
@ -253,13 +247,6 @@ struct mon_table mon_dispatch_proto20[] = {
|
|||
{MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx},
|
||||
{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}
|
||||
};
|
||||
|
@ -427,15 +414,6 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
|
|||
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)
|
||||
|
@ -2159,205 +2137,3 @@ mm_answer_gss_userok(int sock, Buffer *m)
|
|||
}
|
||||
#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);
|
||||
free(x3_proof);
|
||||
free(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));
|
||||
free(hash_scheme);
|
||||
free(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);
|
||||
free(x1_proof);
|
||||
free(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);
|
||||
free(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);
|
||||
free(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 */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: monitor.h,v 1.17 2012/12/02 20:34:10 djm Exp $ */
|
||||
/* $OpenBSD: monitor.h,v 1.18 2014/01/29 06:18:35 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
|
@ -56,11 +56,6 @@ enum monitor_reqtype {
|
|||
MONITOR_REQ_GSSUSEROK = 46, MONITOR_ANS_GSSUSEROK = 47,
|
||||
MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49,
|
||||
MONITOR_REQ_TERM = 50,
|
||||
MONITOR_REQ_JPAKE_STEP1 = 52, MONITOR_ANS_JPAKE_STEP1 = 53,
|
||||
MONITOR_REQ_JPAKE_GET_PWDATA = 54, MONITOR_ANS_JPAKE_GET_PWDATA = 55,
|
||||
MONITOR_REQ_JPAKE_STEP2 = 56, MONITOR_ANS_JPAKE_STEP2 = 57,
|
||||
MONITOR_REQ_JPAKE_KEY_CONFIRM = 58, MONITOR_ANS_JPAKE_KEY_CONFIRM = 59,
|
||||
MONITOR_REQ_JPAKE_CHECK_CONFIRM = 60, MONITOR_ANS_JPAKE_CHECK_CONFIRM = 61,
|
||||
|
||||
MONITOR_REQ_PAM_START = 100,
|
||||
MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103,
|
||||
|
|
165
monitor_wrap.c
165
monitor_wrap.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: monitor_wrap.c,v 1.77 2013/11/06 16:52:11 markus Exp $ */
|
||||
/* $OpenBSD: monitor_wrap.c,v 1.78 2014/01/29 06:18:35 djm Exp $ */
|
||||
/*
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* Copyright 2002 Markus Friedl <markus@openbsd.org>
|
||||
|
@ -71,8 +71,6 @@
|
|||
#include "atomicio.h"
|
||||
#include "monitor_fdpass.h"
|
||||
#include "misc.h"
|
||||
#include "schnorr.h"
|
||||
#include "jpake.h"
|
||||
#include "uuencode.h"
|
||||
|
||||
#include "channels.h"
|
||||
|
@ -1292,164 +1290,3 @@ mm_ssh_gssapi_userok(char *user)
|
|||
}
|
||||
#endif /* GSSAPI */
|
||||
|
||||
#ifdef JPAKE
|
||||
void
|
||||
mm_auth2_jpake_get_pwdata(Authctxt *authctxt, BIGNUM **s,
|
||||
char **hash_scheme, char **salt)
|
||||
{
|
||||
Buffer m;
|
||||
|
||||
debug3("%s entering", __func__);
|
||||
|
||||
buffer_init(&m);
|
||||
mm_request_send(pmonitor->m_recvfd,
|
||||
MONITOR_REQ_JPAKE_GET_PWDATA, &m);
|
||||
|
||||
debug3("%s: waiting for MONITOR_ANS_JPAKE_GET_PWDATA", __func__);
|
||||
mm_request_receive_expect(pmonitor->m_recvfd,
|
||||
MONITOR_ANS_JPAKE_GET_PWDATA, &m);
|
||||
|
||||
*hash_scheme = buffer_get_string(&m, NULL);
|
||||
*salt = buffer_get_string(&m, NULL);
|
||||
|
||||
buffer_free(&m);
|
||||
}
|
||||
|
||||
void
|
||||
mm_jpake_step1(struct modp_group *grp,
|
||||
u_char **id, u_int *id_len,
|
||||
BIGNUM **priv1, BIGNUM **priv2, BIGNUM **g_priv1, BIGNUM **g_priv2,
|
||||
u_char **priv1_proof, u_int *priv1_proof_len,
|
||||
u_char **priv2_proof, u_int *priv2_proof_len)
|
||||
{
|
||||
Buffer m;
|
||||
|
||||
debug3("%s entering", __func__);
|
||||
|
||||
buffer_init(&m);
|
||||
mm_request_send(pmonitor->m_recvfd,
|
||||
MONITOR_REQ_JPAKE_STEP1, &m);
|
||||
|
||||
debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP1", __func__);
|
||||
mm_request_receive_expect(pmonitor->m_recvfd,
|
||||
MONITOR_ANS_JPAKE_STEP1, &m);
|
||||
|
||||
if ((*priv1 = BN_new()) == NULL ||
|
||||
(*priv2 = BN_new()) == NULL ||
|
||||
(*g_priv1 = BN_new()) == NULL ||
|
||||
(*g_priv2 = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
|
||||
*id = buffer_get_string(&m, id_len);
|
||||
/* priv1 and priv2 are, well, private */
|
||||
buffer_get_bignum2(&m, *g_priv1);
|
||||
buffer_get_bignum2(&m, *g_priv2);
|
||||
*priv1_proof = buffer_get_string(&m, priv1_proof_len);
|
||||
*priv2_proof = buffer_get_string(&m, priv2_proof_len);
|
||||
|
||||
buffer_free(&m);
|
||||
}
|
||||
|
||||
void
|
||||
mm_jpake_step2(struct modp_group *grp, BIGNUM *s,
|
||||
BIGNUM *mypub1, BIGNUM *theirpub1, BIGNUM *theirpub2, BIGNUM *mypriv2,
|
||||
const u_char *theirid, u_int theirid_len,
|
||||
const u_char *myid, u_int myid_len,
|
||||
const u_char *theirpub1_proof, u_int theirpub1_proof_len,
|
||||
const u_char *theirpub2_proof, u_int theirpub2_proof_len,
|
||||
BIGNUM **newpub,
|
||||
u_char **newpub_exponent_proof, u_int *newpub_exponent_proof_len)
|
||||
{
|
||||
Buffer m;
|
||||
|
||||
debug3("%s entering", __func__);
|
||||
|
||||
buffer_init(&m);
|
||||
/* monitor already has all bignums except theirpub1, theirpub2 */
|
||||
buffer_put_bignum2(&m, theirpub1);
|
||||
buffer_put_bignum2(&m, theirpub2);
|
||||
/* monitor already knows our id */
|
||||
buffer_put_string(&m, theirid, theirid_len);
|
||||
buffer_put_string(&m, theirpub1_proof, theirpub1_proof_len);
|
||||
buffer_put_string(&m, theirpub2_proof, theirpub2_proof_len);
|
||||
|
||||
mm_request_send(pmonitor->m_recvfd,
|
||||
MONITOR_REQ_JPAKE_STEP2, &m);
|
||||
|
||||
debug3("%s: waiting for MONITOR_ANS_JPAKE_STEP2", __func__);
|
||||
mm_request_receive_expect(pmonitor->m_recvfd,
|
||||
MONITOR_ANS_JPAKE_STEP2, &m);
|
||||
|
||||
if ((*newpub = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
|
||||
buffer_get_bignum2(&m, *newpub);
|
||||
*newpub_exponent_proof = buffer_get_string(&m,
|
||||
newpub_exponent_proof_len);
|
||||
|
||||
buffer_free(&m);
|
||||
}
|
||||
|
||||
void
|
||||
mm_jpake_key_confirm(struct modp_group *grp, BIGNUM *s, BIGNUM *step2_val,
|
||||
BIGNUM *mypriv2, BIGNUM *mypub1, BIGNUM *mypub2,
|
||||
BIGNUM *theirpub1, BIGNUM *theirpub2,
|
||||
const u_char *my_id, u_int my_id_len,
|
||||
const u_char *their_id, u_int their_id_len,
|
||||
const u_char *sess_id, u_int sess_id_len,
|
||||
const u_char *theirpriv2_s_proof, u_int theirpriv2_s_proof_len,
|
||||
BIGNUM **k,
|
||||
u_char **confirm_hash, u_int *confirm_hash_len)
|
||||
{
|
||||
Buffer m;
|
||||
|
||||
debug3("%s entering", __func__);
|
||||
|
||||
buffer_init(&m);
|
||||
/* monitor already has all bignums except step2_val */
|
||||
buffer_put_bignum2(&m, step2_val);
|
||||
/* monitor already knows all the ids */
|
||||
buffer_put_string(&m, theirpriv2_s_proof, theirpriv2_s_proof_len);
|
||||
|
||||
mm_request_send(pmonitor->m_recvfd,
|
||||
MONITOR_REQ_JPAKE_KEY_CONFIRM, &m);
|
||||
|
||||
debug3("%s: waiting for MONITOR_ANS_JPAKE_KEY_CONFIRM", __func__);
|
||||
mm_request_receive_expect(pmonitor->m_recvfd,
|
||||
MONITOR_ANS_JPAKE_KEY_CONFIRM, &m);
|
||||
|
||||
/* 'k' is sensitive and stays in the monitor */
|
||||
*confirm_hash = buffer_get_string(&m, confirm_hash_len);
|
||||
|
||||
buffer_free(&m);
|
||||
}
|
||||
|
||||
int
|
||||
mm_jpake_check_confirm(const BIGNUM *k,
|
||||
const u_char *peer_id, u_int peer_id_len,
|
||||
const u_char *sess_id, u_int sess_id_len,
|
||||
const u_char *peer_confirm_hash, u_int peer_confirm_hash_len)
|
||||
{
|
||||
Buffer m;
|
||||
int success = 0;
|
||||
|
||||
debug3("%s entering", __func__);
|
||||
|
||||
buffer_init(&m);
|
||||
/* k is dummy in slave, ignored */
|
||||
/* monitor knows all the ids */
|
||||
buffer_put_string(&m, peer_confirm_hash, peer_confirm_hash_len);
|
||||
mm_request_send(pmonitor->m_recvfd,
|
||||
MONITOR_REQ_JPAKE_CHECK_CONFIRM, &m);
|
||||
|
||||
debug3("%s: waiting for MONITOR_ANS_JPAKE_CHECK_CONFIRM", __func__);
|
||||
mm_request_receive_expect(pmonitor->m_recvfd,
|
||||
MONITOR_ANS_JPAKE_CHECK_CONFIRM, &m);
|
||||
|
||||
success = buffer_get_int(&m);
|
||||
buffer_free(&m);
|
||||
|
||||
debug3("%s: success = %d", __func__, success);
|
||||
return success;
|
||||
}
|
||||
#endif /* JPAKE */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: monitor_wrap.h,v 1.23 2011/06/17 21:44:31 djm Exp $ */
|
||||
/* $OpenBSD: monitor_wrap.h,v 1.24 2014/01/29 06:18:35 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
|
@ -102,26 +102,6 @@ int mm_bsdauth_respond(void *, u_int, char **);
|
|||
int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **);
|
||||
int mm_skey_respond(void *, u_int, char **);
|
||||
|
||||
/* jpake */
|
||||
struct modp_group;
|
||||
void mm_auth2_jpake_get_pwdata(struct Authctxt *, BIGNUM **, char **, char **);
|
||||
void mm_jpake_step1(struct modp_group *, u_char **, u_int *,
|
||||
BIGNUM **, BIGNUM **, BIGNUM **, BIGNUM **,
|
||||
u_char **, u_int *, u_char **, u_int *);
|
||||
void mm_jpake_step2(struct modp_group *, BIGNUM *,
|
||||
BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
|
||||
const u_char *, u_int, const u_char *, u_int,
|
||||
const u_char *, u_int, const u_char *, u_int,
|
||||
BIGNUM **, u_char **, u_int *);
|
||||
void mm_jpake_key_confirm(struct modp_group *, BIGNUM *, BIGNUM *,
|
||||
BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *, BIGNUM *,
|
||||
const u_char *, u_int, const u_char *, u_int,
|
||||
const u_char *, u_int, const u_char *, u_int,
|
||||
BIGNUM **, u_char **, u_int *);
|
||||
int mm_jpake_check_confirm(const BIGNUM *,
|
||||
const u_char *, u_int, const u_char *, u_int, const u_char *, u_int);
|
||||
|
||||
|
||||
/* zlib allocation hooks */
|
||||
|
||||
void *mm_zalloc(struct mm_master *, u_int, u_int);
|
||||
|
|
17
readconf.c
17
readconf.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.c,v 1.215 2013/12/06 13:39:49 markus Exp $ */
|
||||
/* $OpenBSD: readconf.c,v 1.216 2014/01/29 06:18:35 djm Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -145,7 +145,7 @@ typedef enum {
|
|||
oSendEnv, oControlPath, oControlMaster, oControlPersist,
|
||||
oHashKnownHosts,
|
||||
oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
|
||||
oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
|
||||
oVisualHostKey, oUseRoaming,
|
||||
oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass,
|
||||
oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots,
|
||||
oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs,
|
||||
|
@ -252,12 +252,6 @@ static struct {
|
|||
{ "permitlocalcommand", oPermitLocalCommand },
|
||||
{ "visualhostkey", oVisualHostKey },
|
||||
{ "useroaming", oUseRoaming },
|
||||
#ifdef JPAKE
|
||||
{ "zeroknowledgepasswordauthentication",
|
||||
oZeroKnowledgePasswordAuthentication },
|
||||
#else
|
||||
{ "zeroknowledgepasswordauthentication", oUnsupported },
|
||||
#endif
|
||||
{ "kexalgorithms", oKexAlgorithms },
|
||||
{ "ipqos", oIPQoS },
|
||||
{ "requesttty", oRequestTTY },
|
||||
|
@ -804,10 +798,6 @@ parse_time:
|
|||
intptr = &options->password_authentication;
|
||||
goto parse_flag;
|
||||
|
||||
case oZeroKnowledgePasswordAuthentication:
|
||||
intptr = &options->zero_knowledge_password_authentication;
|
||||
goto parse_flag;
|
||||
|
||||
case oKbdInteractiveAuthentication:
|
||||
intptr = &options->kbd_interactive_authentication;
|
||||
goto parse_flag;
|
||||
|
@ -1550,7 +1540,6 @@ initialize_options(Options * options)
|
|||
options->permit_local_command = -1;
|
||||
options->use_roaming = -1;
|
||||
options->visual_host_key = -1;
|
||||
options->zero_knowledge_password_authentication = -1;
|
||||
options->ip_qos_interactive = -1;
|
||||
options->ip_qos_bulk = -1;
|
||||
options->request_tty = -1;
|
||||
|
@ -1706,8 +1695,6 @@ fill_default_options(Options * options)
|
|||
options->use_roaming = 1;
|
||||
if (options->visual_host_key == -1)
|
||||
options->visual_host_key = 0;
|
||||
if (options->zero_knowledge_password_authentication == -1)
|
||||
options->zero_knowledge_password_authentication = 0;
|
||||
if (options->ip_qos_interactive == -1)
|
||||
options->ip_qos_interactive = IPTOS_LOWDELAY;
|
||||
if (options->ip_qos_bulk == -1)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: readconf.h,v 1.99 2013/10/16 22:49:38 djm Exp $ */
|
||||
/* $OpenBSD: readconf.h,v 1.100 2014/01/29 06:18:35 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
|
@ -59,7 +59,6 @@ typedef struct {
|
|||
* authentication. */
|
||||
int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
|
||||
char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */
|
||||
int zero_knowledge_password_authentication; /* Try jpake */
|
||||
int batch_mode; /* Batch mode: do not ask for passwords. */
|
||||
int check_host_ip; /* Also keep track of keys for IP address */
|
||||
int strict_host_key_checking; /* Strict host key checking. */
|
||||
|
|
668
schnorr.c
668
schnorr.c
|
@ -1,668 +0,0 @@
|
|||
/* $OpenBSD: schnorr.c,v 1.9 2014/01/09 23:20:00 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008 Damien Miller. All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implementation of Schnorr signatures / zero-knowledge proofs, based on
|
||||
* description in:
|
||||
*
|
||||
* F. Hao, P. Ryan, "Password Authenticated Key Exchange by Juggling",
|
||||
* 16th Workshop on Security Protocols, Cambridge, April 2008
|
||||
*
|
||||
* http://grouper.ieee.org/groups/1363/Research/contributions/hao-ryan-2008.pdf
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/bn.h>
|
||||
|
||||
#include "xmalloc.h"
|
||||
#include "buffer.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "schnorr.h"
|
||||
#include "digest.h"
|
||||
|
||||
#include "openbsd-compat/openssl-compat.h"
|
||||
|
||||
/* #define SCHNORR_DEBUG */ /* Privacy-violating debugging */
|
||||
/* #define SCHNORR_MAIN */ /* Include main() selftest */
|
||||
|
||||
#ifndef SCHNORR_DEBUG
|
||||
# define SCHNORR_DEBUG_BN(a)
|
||||
# define SCHNORR_DEBUG_BUF(a)
|
||||
#else
|
||||
# define SCHNORR_DEBUG_BN(a) debug3_bn a
|
||||
# define SCHNORR_DEBUG_BUF(a) debug3_buf a
|
||||
#endif /* SCHNORR_DEBUG */
|
||||
|
||||
/*
|
||||
* Calculate hash component of Schnorr signature H(g || g^v || g^x || id)
|
||||
* using the hash function defined by "hash_alg". Returns signature as
|
||||
* bignum or NULL on error.
|
||||
*/
|
||||
static BIGNUM *
|
||||
schnorr_hash(const BIGNUM *p, const BIGNUM *q, const BIGNUM *g,
|
||||
int hash_alg, const BIGNUM *g_v, const BIGNUM *g_x,
|
||||
const u_char *id, u_int idlen)
|
||||
{
|
||||
u_char *digest;
|
||||
u_int digest_len;
|
||||
BIGNUM *h;
|
||||
Buffer b;
|
||||
int success = -1;
|
||||
|
||||
if ((h = BN_new()) == NULL) {
|
||||
error("%s: BN_new", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer_init(&b);
|
||||
|
||||
/* h = H(g || p || q || g^v || g^x || id) */
|
||||
buffer_put_bignum2(&b, g);
|
||||
buffer_put_bignum2(&b, p);
|
||||
buffer_put_bignum2(&b, q);
|
||||
buffer_put_bignum2(&b, g_v);
|
||||
buffer_put_bignum2(&b, g_x);
|
||||
buffer_put_string(&b, id, idlen);
|
||||
|
||||
SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b),
|
||||
"%s: hashblob", __func__));
|
||||
if (hash_buffer(buffer_ptr(&b), buffer_len(&b), hash_alg,
|
||||
&digest, &digest_len) != 0) {
|
||||
error("%s: hash_buffer", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (BN_bin2bn(digest, (int)digest_len, h) == NULL) {
|
||||
error("%s: BN_bin2bn", __func__);
|
||||
goto out;
|
||||
}
|
||||
success = 0;
|
||||
SCHNORR_DEBUG_BN((h, "%s: h = ", __func__));
|
||||
out:
|
||||
buffer_free(&b);
|
||||
bzero(digest, digest_len);
|
||||
free(digest);
|
||||
digest_len = 0;
|
||||
if (success == 0)
|
||||
return h;
|
||||
BN_clear_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate Schnorr signature to prove knowledge of private value 'x' used
|
||||
* in public exponent g^x, under group defined by 'grp_p', 'grp_q' and 'grp_g'
|
||||
* using the hash function "hash_alg".
|
||||
* 'idlen' bytes from 'id' will be included in the signature hash as an anti-
|
||||
* replay salt.
|
||||
*
|
||||
* On success, 0 is returned. The signature values are returned as *e_p
|
||||
* (g^v mod p) and *r_p (v - xh mod q). The caller must free these values.
|
||||
* On failure, -1 is returned.
|
||||
*/
|
||||
int
|
||||
schnorr_sign(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
|
||||
int hash_alg, const BIGNUM *x, const BIGNUM *g_x,
|
||||
const u_char *id, u_int idlen, BIGNUM **r_p, BIGNUM **e_p)
|
||||
{
|
||||
int success = -1;
|
||||
BIGNUM *h, *tmp, *v, *g_v, *r;
|
||||
BN_CTX *bn_ctx;
|
||||
|
||||
SCHNORR_DEBUG_BN((x, "%s: x = ", __func__));
|
||||
SCHNORR_DEBUG_BN((g_x, "%s: g_x = ", __func__));
|
||||
|
||||
/* Avoid degenerate cases: g^0 yields a spoofable signature */
|
||||
if (BN_cmp(g_x, BN_value_one()) <= 0) {
|
||||
error("%s: g_x < 1", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (BN_cmp(g_x, grp_p) >= 0) {
|
||||
error("%s: g_x > g", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
h = g_v = r = tmp = v = NULL;
|
||||
if ((bn_ctx = BN_CTX_new()) == NULL) {
|
||||
error("%s: BN_CTX_new", __func__);
|
||||
goto out;
|
||||
}
|
||||
if ((g_v = BN_new()) == NULL ||
|
||||
(r = BN_new()) == NULL ||
|
||||
(tmp = BN_new()) == NULL) {
|
||||
error("%s: BN_new", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* v must be a random element of Zq, so 1 <= v < q
|
||||
* we also exclude v = 1, since g^1 looks dangerous
|
||||
*/
|
||||
if ((v = bn_rand_range_gt_one(grp_p)) == NULL) {
|
||||
error("%s: bn_rand_range2", __func__);
|
||||
goto out;
|
||||
}
|
||||
SCHNORR_DEBUG_BN((v, "%s: v = ", __func__));
|
||||
|
||||
/* g_v = g^v mod p */
|
||||
if (BN_mod_exp(g_v, grp_g, v, grp_p, bn_ctx) == -1) {
|
||||
error("%s: BN_mod_exp (g^v mod p)", __func__);
|
||||
goto out;
|
||||
}
|
||||
SCHNORR_DEBUG_BN((g_v, "%s: g_v = ", __func__));
|
||||
|
||||
/* h = H(g || g^v || g^x || id) */
|
||||
if ((h = schnorr_hash(grp_p, grp_q, grp_g, hash_alg, g_v, g_x,
|
||||
id, idlen)) == NULL) {
|
||||
error("%s: schnorr_hash failed", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* r = v - xh mod q */
|
||||
if (BN_mod_mul(tmp, x, h, grp_q, bn_ctx) == -1) {
|
||||
error("%s: BN_mod_mul (tmp = xv mod q)", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (BN_mod_sub(r, v, tmp, grp_q, bn_ctx) == -1) {
|
||||
error("%s: BN_mod_mul (r = v - tmp)", __func__);
|
||||
goto out;
|
||||
}
|
||||
SCHNORR_DEBUG_BN((g_v, "%s: e = ", __func__));
|
||||
SCHNORR_DEBUG_BN((r, "%s: r = ", __func__));
|
||||
|
||||
*e_p = g_v;
|
||||
*r_p = r;
|
||||
|
||||
success = 0;
|
||||
out:
|
||||
BN_CTX_free(bn_ctx);
|
||||
if (h != NULL)
|
||||
BN_clear_free(h);
|
||||
if (v != NULL)
|
||||
BN_clear_free(v);
|
||||
BN_clear_free(tmp);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate Schnorr signature to prove knowledge of private value 'x' used
|
||||
* in public exponent g^x, under group defined by 'grp_p', 'grp_q' and 'grp_g'
|
||||
* using a SHA256 hash.
|
||||
* 'idlen' bytes from 'id' will be included in the signature hash as an anti-
|
||||
* replay salt.
|
||||
* On success, 0 is returned and *siglen bytes of signature are returned in
|
||||
* *sig (caller to free). Returns -1 on failure.
|
||||
*/
|
||||
int
|
||||
schnorr_sign_buf(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
|
||||
const BIGNUM *x, const BIGNUM *g_x, const u_char *id, u_int idlen,
|
||||
u_char **sig, u_int *siglen)
|
||||
{
|
||||
Buffer b;
|
||||
BIGNUM *r, *e;
|
||||
|
||||
if (schnorr_sign(grp_p, grp_q, grp_g, SSH_DIGEST_SHA256,
|
||||
x, g_x, id, idlen, &r, &e) != 0)
|
||||
return -1;
|
||||
|
||||
/* Signature is (e, r) */
|
||||
buffer_init(&b);
|
||||
/* XXX sigtype-hash as string? */
|
||||
buffer_put_bignum2(&b, e);
|
||||
buffer_put_bignum2(&b, r);
|
||||
*siglen = buffer_len(&b);
|
||||
*sig = xmalloc(*siglen);
|
||||
memcpy(*sig, buffer_ptr(&b), *siglen);
|
||||
SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b),
|
||||
"%s: sigblob", __func__));
|
||||
buffer_free(&b);
|
||||
|
||||
BN_clear_free(r);
|
||||
BN_clear_free(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify Schnorr signature { r (v - xh mod q), e (g^v mod p) } against
|
||||
* public exponent g_x (g^x) under group defined by 'grp_p', 'grp_q' and
|
||||
* 'grp_g' using hash "hash_alg".
|
||||
* Signature hash will be salted with 'idlen' bytes from 'id'.
|
||||
* Returns -1 on failure, 0 on incorrect signature or 1 on matching signature.
|
||||
*/
|
||||
int
|
||||
schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
|
||||
int hash_alg, const BIGNUM *g_x, const u_char *id, u_int idlen,
|
||||
const BIGNUM *r, const BIGNUM *e)
|
||||
{
|
||||
int success = -1;
|
||||
BIGNUM *h = NULL, *g_xh = NULL, *g_r = NULL, *gx_q = NULL;
|
||||
BIGNUM *expected = NULL;
|
||||
BN_CTX *bn_ctx;
|
||||
|
||||
SCHNORR_DEBUG_BN((g_x, "%s: g_x = ", __func__));
|
||||
|
||||
/* Avoid degenerate cases: g^0 yields a spoofable signature */
|
||||
if (BN_cmp(g_x, BN_value_one()) <= 0) {
|
||||
error("%s: g_x <= 1", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (BN_cmp(g_x, grp_p) >= 0) {
|
||||
error("%s: g_x >= p", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
h = g_xh = g_r = expected = NULL;
|
||||
if ((bn_ctx = BN_CTX_new()) == NULL) {
|
||||
error("%s: BN_CTX_new", __func__);
|
||||
goto out;
|
||||
}
|
||||
if ((g_xh = BN_new()) == NULL ||
|
||||
(g_r = BN_new()) == NULL ||
|
||||
(gx_q = BN_new()) == NULL ||
|
||||
(expected = BN_new()) == NULL) {
|
||||
error("%s: BN_new", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
SCHNORR_DEBUG_BN((e, "%s: e = ", __func__));
|
||||
SCHNORR_DEBUG_BN((r, "%s: r = ", __func__));
|
||||
|
||||
/* gx_q = (g^x)^q must === 1 mod p */
|
||||
if (BN_mod_exp(gx_q, g_x, grp_q, grp_p, bn_ctx) == -1) {
|
||||
error("%s: BN_mod_exp (g_x^q mod p)", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (BN_cmp(gx_q, BN_value_one()) != 0) {
|
||||
error("%s: Invalid signature (g^x)^q != 1 mod p", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
SCHNORR_DEBUG_BN((g_xh, "%s: g_xh = ", __func__));
|
||||
/* h = H(g || g^v || g^x || id) */
|
||||
if ((h = schnorr_hash(grp_p, grp_q, grp_g, hash_alg, e, g_x,
|
||||
id, idlen)) == NULL) {
|
||||
error("%s: schnorr_hash failed", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* g_xh = (g^x)^h */
|
||||
if (BN_mod_exp(g_xh, g_x, h, grp_p, bn_ctx) == -1) {
|
||||
error("%s: BN_mod_exp (g_x^h mod p)", __func__);
|
||||
goto out;
|
||||
}
|
||||
SCHNORR_DEBUG_BN((g_xh, "%s: g_xh = ", __func__));
|
||||
|
||||
/* g_r = g^r */
|
||||
if (BN_mod_exp(g_r, grp_g, r, grp_p, bn_ctx) == -1) {
|
||||
error("%s: BN_mod_exp (g_x^h mod p)", __func__);
|
||||
goto out;
|
||||
}
|
||||
SCHNORR_DEBUG_BN((g_r, "%s: g_r = ", __func__));
|
||||
|
||||
/* expected = g^r * g_xh */
|
||||
if (BN_mod_mul(expected, g_r, g_xh, grp_p, bn_ctx) == -1) {
|
||||
error("%s: BN_mod_mul (expected = g_r mod p)", __func__);
|
||||
goto out;
|
||||
}
|
||||
SCHNORR_DEBUG_BN((expected, "%s: expected = ", __func__));
|
||||
|
||||
/* Check e == expected */
|
||||
success = BN_cmp(expected, e) == 0;
|
||||
out:
|
||||
BN_CTX_free(bn_ctx);
|
||||
if (h != NULL)
|
||||
BN_clear_free(h);
|
||||
if (gx_q != NULL)
|
||||
BN_clear_free(gx_q);
|
||||
if (g_xh != NULL)
|
||||
BN_clear_free(g_xh);
|
||||
if (g_r != NULL)
|
||||
BN_clear_free(g_r);
|
||||
if (expected != NULL)
|
||||
BN_clear_free(expected);
|
||||
return success;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify Schnorr signature 'sig' of length 'siglen' against public exponent
|
||||
* g_x (g^x) under group defined by 'grp_p', 'grp_q' and 'grp_g' using a
|
||||
* SHA256 hash.
|
||||
* Signature hash will be salted with 'idlen' bytes from 'id'.
|
||||
* Returns -1 on failure, 0 on incorrect signature or 1 on matching signature.
|
||||
*/
|
||||
int
|
||||
schnorr_verify_buf(const BIGNUM *grp_p, const BIGNUM *grp_q,
|
||||
const BIGNUM *grp_g,
|
||||
const BIGNUM *g_x, const u_char *id, u_int idlen,
|
||||
const u_char *sig, u_int siglen)
|
||||
{
|
||||
Buffer b;
|
||||
int ret = -1;
|
||||
u_int rlen;
|
||||
BIGNUM *r, *e;
|
||||
|
||||
e = r = NULL;
|
||||
if ((e = BN_new()) == NULL ||
|
||||
(r = BN_new()) == NULL) {
|
||||
error("%s: BN_new", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Extract g^v and r from signature blob */
|
||||
buffer_init(&b);
|
||||
buffer_append(&b, sig, siglen);
|
||||
SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b),
|
||||
"%s: sigblob", __func__));
|
||||
buffer_get_bignum2(&b, e);
|
||||
buffer_get_bignum2(&b, r);
|
||||
rlen = buffer_len(&b);
|
||||
buffer_free(&b);
|
||||
if (rlen != 0) {
|
||||
error("%s: remaining bytes in signature %d", __func__, rlen);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = schnorr_verify(grp_p, grp_q, grp_g, SSH_DIGEST_SHA256,
|
||||
g_x, id, idlen, r, e);
|
||||
out:
|
||||
BN_clear_free(e);
|
||||
BN_clear_free(r);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Helper functions */
|
||||
|
||||
/*
|
||||
* Generate uniformly distributed random number in range (1, high).
|
||||
* Return number on success, NULL on failure.
|
||||
*/
|
||||
BIGNUM *
|
||||
bn_rand_range_gt_one(const BIGNUM *high)
|
||||
{
|
||||
BIGNUM *r, *tmp;
|
||||
int success = -1;
|
||||
|
||||
if ((tmp = BN_new()) == NULL) {
|
||||
error("%s: BN_new", __func__);
|
||||
return NULL;
|
||||
}
|
||||
if ((r = BN_new()) == NULL) {
|
||||
error("%s: BN_new failed", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (BN_set_word(tmp, 2) != 1) {
|
||||
error("%s: BN_set_word(tmp, 2)", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (BN_sub(tmp, high, tmp) == -1) {
|
||||
error("%s: BN_sub failed (tmp = high - 2)", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (BN_rand_range(r, tmp) == -1) {
|
||||
error("%s: BN_rand_range failed", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (BN_set_word(tmp, 2) != 1) {
|
||||
error("%s: BN_set_word(tmp, 2)", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (BN_add(r, r, tmp) == -1) {
|
||||
error("%s: BN_add failed (r = r + 2)", __func__);
|
||||
goto out;
|
||||
}
|
||||
success = 0;
|
||||
out:
|
||||
BN_clear_free(tmp);
|
||||
if (success == 0)
|
||||
return r;
|
||||
BN_clear_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* XXX convert all callers of this to use ssh_digest_memory() directly */
|
||||
/*
|
||||
* Hash contents of buffer 'b' with hash 'md'. Returns 0 on success,
|
||||
* with digest via 'digestp' (caller to free) and length via 'lenp'.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int
|
||||
hash_buffer(const u_char *buf, u_int len, int hash_alg,
|
||||
u_char **digestp, u_int *lenp)
|
||||
{
|
||||
u_char digest[SSH_DIGEST_MAX_LENGTH];
|
||||
u_int digest_len = ssh_digest_bytes(hash_alg);
|
||||
|
||||
if (digest_len == 0) {
|
||||
error("%s: invalid hash", __func__);
|
||||
return -1;
|
||||
}
|
||||
if (ssh_digest_memory(hash_alg, buf, len, digest, digest_len) != 0) {
|
||||
error("%s: digest_memory failed", __func__);
|
||||
return -1;
|
||||
}
|
||||
*digestp = xmalloc(digest_len);
|
||||
*lenp = digest_len;
|
||||
memcpy(*digestp, digest, *lenp);
|
||||
bzero(digest, sizeof(digest));
|
||||
digest_len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* print formatted string followed by bignum */
|
||||
void
|
||||
debug3_bn(const BIGNUM *n, const char *fmt, ...)
|
||||
{
|
||||
char *out, *h;
|
||||
va_list args;
|
||||
int ret;
|
||||
|
||||
out = NULL;
|
||||
va_start(args, fmt);
|
||||
ret = vasprintf(&out, fmt, args);
|
||||
va_end(args);
|
||||
if (ret == -1 || out == NULL)
|
||||
fatal("%s: vasprintf failed", __func__);
|
||||
|
||||
if (n == NULL)
|
||||
debug3("%s(null)", out);
|
||||
else {
|
||||
h = BN_bn2hex(n);
|
||||
debug3("%s0x%s", out, h);
|
||||
free(h);
|
||||
}
|
||||
free(out);
|
||||
}
|
||||
|
||||
/* print formatted string followed by buffer contents in hex */
|
||||
void
|
||||
debug3_buf(const u_char *buf, u_int len, const char *fmt, ...)
|
||||
{
|
||||
char *out, h[65];
|
||||
u_int i, j;
|
||||
va_list args;
|
||||
int ret;
|
||||
|
||||
out = NULL;
|
||||
va_start(args, fmt);
|
||||
ret = vasprintf(&out, fmt, args);
|
||||
va_end(args);
|
||||
if (ret == -1 || out == NULL)
|
||||
fatal("%s: vasprintf failed", __func__);
|
||||
|
||||
debug3("%s length %u%s", out, len, buf == NULL ? " (null)" : "");
|
||||
free(out);
|
||||
if (buf == NULL)
|
||||
return;
|
||||
|
||||
*h = '\0';
|
||||
for (i = j = 0; i < len; i++) {
|
||||
snprintf(h + j, sizeof(h) - j, "%02x", buf[i]);
|
||||
j += 2;
|
||||
if (j >= sizeof(h) - 1 || i == len - 1) {
|
||||
debug3(" %s", h);
|
||||
*h = '\0';
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a MODP group from hex strings p (which must be a safe
|
||||
* prime) and g, automatically calculating subgroup q as (p / 2)
|
||||
*/
|
||||
struct modp_group *
|
||||
modp_group_from_g_and_safe_p(const char *grp_g, const char *grp_p)
|
||||
{
|
||||
struct modp_group *ret;
|
||||
|
||||
ret = xcalloc(1, sizeof(*ret));
|
||||
ret->p = ret->q = ret->g = NULL;
|
||||
if (BN_hex2bn(&ret->p, grp_p) == 0 ||
|
||||
BN_hex2bn(&ret->g, grp_g) == 0)
|
||||
fatal("%s: BN_hex2bn", __func__);
|
||||
/* Subgroup order is p/2 (p is a safe prime) */
|
||||
if ((ret->q = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
if (BN_rshift1(ret->q, ret->p) != 1)
|
||||
fatal("%s: BN_rshift1", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
modp_group_free(struct modp_group *grp)
|
||||
{
|
||||
if (grp->g != NULL)
|
||||
BN_clear_free(grp->g);
|
||||
if (grp->p != NULL)
|
||||
BN_clear_free(grp->p);
|
||||
if (grp->q != NULL)
|
||||
BN_clear_free(grp->q);
|
||||
bzero(grp, sizeof(*grp));
|
||||
free(grp);
|
||||
}
|
||||
|
||||
/* main() function for self-test */
|
||||
|
||||
#ifdef SCHNORR_MAIN
|
||||
static void
|
||||
schnorr_selftest_one(const BIGNUM *grp_p, const BIGNUM *grp_q,
|
||||
const BIGNUM *grp_g, const BIGNUM *x)
|
||||
{
|
||||
BIGNUM *g_x;
|
||||
u_char *sig;
|
||||
u_int siglen;
|
||||
BN_CTX *bn_ctx;
|
||||
|
||||
if ((bn_ctx = BN_CTX_new()) == NULL)
|
||||
fatal("%s: BN_CTX_new", __func__);
|
||||
if ((g_x = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
|
||||
if (BN_mod_exp(g_x, grp_g, x, grp_p, bn_ctx) == -1)
|
||||
fatal("%s: g_x", __func__);
|
||||
if (schnorr_sign_buf(grp_p, grp_q, grp_g, x, g_x, "junk", 4,
|
||||
&sig, &siglen))
|
||||
fatal("%s: schnorr_sign", __func__);
|
||||
if (schnorr_verify_buf(grp_p, grp_q, grp_g, g_x, "junk", 4,
|
||||
sig, siglen) != 1)
|
||||
fatal("%s: verify fail", __func__);
|
||||
if (schnorr_verify_buf(grp_p, grp_q, grp_g, g_x, "JUNK", 4,
|
||||
sig, siglen) != 0)
|
||||
fatal("%s: verify should have failed (bad ID)", __func__);
|
||||
sig[4] ^= 1;
|
||||
if (schnorr_verify_buf(grp_p, grp_q, grp_g, g_x, "junk", 4,
|
||||
sig, siglen) != 0)
|
||||
fatal("%s: verify should have failed (bit error)", __func__);
|
||||
free(sig);
|
||||
BN_free(g_x);
|
||||
BN_CTX_free(bn_ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
schnorr_selftest(void)
|
||||
{
|
||||
BIGNUM *x;
|
||||
struct modp_group *grp;
|
||||
u_int i;
|
||||
char *hh;
|
||||
|
||||
grp = jpake_default_group();
|
||||
if ((x = BN_new()) == NULL)
|
||||
fatal("%s: BN_new", __func__);
|
||||
SCHNORR_DEBUG_BN((grp->p, "%s: grp->p = ", __func__));
|
||||
SCHNORR_DEBUG_BN((grp->q, "%s: grp->q = ", __func__));
|
||||
SCHNORR_DEBUG_BN((grp->g, "%s: grp->g = ", __func__));
|
||||
|
||||
/* [1, 20) */
|
||||
for (i = 1; i < 20; i++) {
|
||||
printf("x = %u\n", i);
|
||||
fflush(stdout);
|
||||
if (BN_set_word(x, i) != 1)
|
||||
fatal("%s: set x word", __func__);
|
||||
schnorr_selftest_one(grp->p, grp->q, grp->g, x);
|
||||
}
|
||||
|
||||
/* 100 x random [0, p) */
|
||||
for (i = 0; i < 100; i++) {
|
||||
if (BN_rand_range(x, grp->p) != 1)
|
||||
fatal("%s: BN_rand_range", __func__);
|
||||
hh = BN_bn2hex(x);
|
||||
printf("x = (random) 0x%s\n", hh);
|
||||
free(hh);
|
||||
fflush(stdout);
|
||||
schnorr_selftest_one(grp->p, grp->q, grp->g, x);
|
||||
}
|
||||
|
||||
/* [q-20, q) */
|
||||
if (BN_set_word(x, 20) != 1)
|
||||
fatal("%s: BN_set_word (x = 20)", __func__);
|
||||
if (BN_sub(x, grp->q, x) != 1)
|
||||
fatal("%s: BN_sub (q - x)", __func__);
|
||||
for (i = 0; i < 19; i++) {
|
||||
hh = BN_bn2hex(x);
|
||||
printf("x = (q - %d) 0x%s\n", 20 - i, hh);
|
||||
free(hh);
|
||||
fflush(stdout);
|
||||
schnorr_selftest_one(grp->p, grp->q, grp->g, x);
|
||||
if (BN_add(x, x, BN_value_one()) != 1)
|
||||
fatal("%s: BN_add (x + 1)", __func__);
|
||||
}
|
||||
BN_free(x);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
log_init(argv[0], SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_USER, 1);
|
||||
|
||||
schnorr_selftest();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
60
schnorr.h
60
schnorr.h
|
@ -1,60 +0,0 @@
|
|||
/* $OpenBSD: schnorr.h,v 1.2 2014/01/09 23:20:00 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2009 Damien Miller. All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SCHNORR_H
|
||||
#define SCHNORR_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openssl/bn.h>
|
||||
|
||||
struct modp_group {
|
||||
BIGNUM *p, *q, *g;
|
||||
};
|
||||
|
||||
BIGNUM *bn_rand_range_gt_one(const BIGNUM *high);
|
||||
int hash_buffer(const u_char *, u_int, int, u_char **, u_int *);
|
||||
void debug3_bn(const BIGNUM *, const char *, ...)
|
||||
__attribute__((__nonnull__ (2)))
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
void debug3_buf(const u_char *, u_int, const char *, ...)
|
||||
__attribute__((__nonnull__ (3)))
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
struct modp_group *modp_group_from_g_and_safe_p(const char *, const char *);
|
||||
void modp_group_free(struct modp_group *);
|
||||
|
||||
/* Signature and verification functions */
|
||||
int
|
||||
schnorr_sign(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
|
||||
int hash_alg, const BIGNUM *x, const BIGNUM *g_x,
|
||||
const u_char *id, u_int idlen, BIGNUM **r_p, BIGNUM **e_p);
|
||||
int
|
||||
schnorr_sign_buf(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
|
||||
const BIGNUM *x, const BIGNUM *g_x, const u_char *id, u_int idlen,
|
||||
u_char **sig, u_int *siglen);
|
||||
int
|
||||
schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g,
|
||||
int hash_alg, const BIGNUM *g_x, const u_char *id, u_int idlen,
|
||||
const BIGNUM *r, const BIGNUM *e);
|
||||
int
|
||||
schnorr_verify_buf(const BIGNUM *grp_p, const BIGNUM *grp_q,
|
||||
const BIGNUM *grp_g,
|
||||
const BIGNUM *g_x, const u_char *id, u_int idlen,
|
||||
const u_char *sig, u_int siglen);
|
||||
|
||||
#endif /* JPAKE_H */
|
||||
|
21
servconf.c
21
servconf.c
|
@ -1,5 +1,5 @@
|
|||
|
||||
/* $OpenBSD: servconf.c,v 1.248 2013/12/06 13:39:49 markus Exp $ */
|
||||
/* $OpenBSD: servconf.c,v 1.249 2014/01/29 06:18:35 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
* All rights reserved
|
||||
|
@ -147,7 +147,6 @@ initialize_server_options(ServerOptions *options)
|
|||
options->chroot_directory = NULL;
|
||||
options->authorized_keys_command = NULL;
|
||||
options->authorized_keys_command_user = NULL;
|
||||
options->zero_knowledge_password_authentication = -1;
|
||||
options->revoked_keys_file = NULL;
|
||||
options->trusted_user_ca_keys = NULL;
|
||||
options->authorized_principals_file = NULL;
|
||||
|
@ -295,8 +294,6 @@ fill_default_server_options(ServerOptions *options)
|
|||
}
|
||||
if (options->permit_tun == -1)
|
||||
options->permit_tun = SSH_TUNMODE_NO;
|
||||
if (options->zero_knowledge_password_authentication == -1)
|
||||
options->zero_knowledge_password_authentication = 0;
|
||||
if (options->ip_qos_interactive == -1)
|
||||
options->ip_qos_interactive = IPTOS_LOWDELAY;
|
||||
if (options->ip_qos_bulk == -1)
|
||||
|
@ -346,7 +343,7 @@ typedef enum {
|
|||
sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
|
||||
sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
|
||||
sUsePrivilegeSeparation, sAllowAgentForwarding,
|
||||
sZeroKnowledgePasswordAuthentication, sHostCertificate,
|
||||
sHostCertificate,
|
||||
sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
|
||||
sKexAlgorithms, sIPQoS, sVersionAddendum,
|
||||
sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
|
||||
|
@ -418,11 +415,6 @@ static struct {
|
|||
{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
|
||||
{ "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL },
|
||||
{ "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */
|
||||
#ifdef JPAKE
|
||||
{ "zeroknowledgepasswordauthentication", sZeroKnowledgePasswordAuthentication, SSHCFG_ALL },
|
||||
#else
|
||||
{ "zeroknowledgepasswordauthentication", sUnsupported, SSHCFG_ALL },
|
||||
#endif
|
||||
{ "checkmail", sDeprecated, SSHCFG_GLOBAL },
|
||||
{ "listenaddress", sListenAddress, SSHCFG_GLOBAL },
|
||||
{ "addressfamily", sAddressFamily, SSHCFG_GLOBAL },
|
||||
|
@ -1102,10 +1094,6 @@ process_server_config_line(ServerOptions *options, char *line,
|
|||
intptr = &options->password_authentication;
|
||||
goto parse_flag;
|
||||
|
||||
case sZeroKnowledgePasswordAuthentication:
|
||||
intptr = &options->zero_knowledge_password_authentication;
|
||||
goto parse_flag;
|
||||
|
||||
case sKbdInteractiveAuthentication:
|
||||
intptr = &options->kbd_interactive_authentication;
|
||||
goto parse_flag;
|
||||
|
@ -1767,7 +1755,6 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
|
|||
M_CP_INTOPT(hostbased_authentication);
|
||||
M_CP_INTOPT(hostbased_uses_name_from_packet_only);
|
||||
M_CP_INTOPT(kbd_interactive_authentication);
|
||||
M_CP_INTOPT(zero_knowledge_password_authentication);
|
||||
M_CP_INTOPT(permit_root_login);
|
||||
M_CP_INTOPT(permit_empty_passwd);
|
||||
|
||||
|
@ -2009,10 +1996,6 @@ dump_config(ServerOptions *o)
|
|||
#ifdef GSSAPI
|
||||
dump_cfg_fmtint(sGssAuthentication, o->gss_authentication);
|
||||
dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds);
|
||||
#endif
|
||||
#ifdef JPAKE
|
||||
dump_cfg_fmtint(sZeroKnowledgePasswordAuthentication,
|
||||
o->zero_knowledge_password_authentication);
|
||||
#endif
|
||||
dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
|
||||
dump_cfg_fmtint(sKbdInteractiveAuthentication,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: servconf.h,v 1.111 2013/12/05 01:16:41 djm Exp $ */
|
||||
/* $OpenBSD: servconf.h,v 1.112 2014/01/29 06:18:35 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
|
@ -117,8 +117,6 @@ typedef struct {
|
|||
* authentication. */
|
||||
int kbd_interactive_authentication; /* If true, permit */
|
||||
int challenge_response_authentication;
|
||||
int zero_knowledge_password_authentication;
|
||||
/* If true, permit jpake auth */
|
||||
int permit_empty_passwd; /* If false, do not permit empty
|
||||
* passwords. */
|
||||
int permit_user_env; /* If true, read ~/.ssh/environment */
|
||||
|
|
8
ssh2.h
8
ssh2.h
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ssh2.h,v 1.14 2010/08/31 11:54:45 djm Exp $ */
|
||||
/* $OpenBSD: ssh2.h,v 1.15 2014/01/29 06:18:35 djm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
|
@ -115,12 +115,6 @@
|
|||
#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60
|
||||
#define SSH2_MSG_USERAUTH_INFO_REQUEST 60
|
||||
#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61
|
||||
#define SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1 60
|
||||
#define SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1 61
|
||||
#define SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP2 62
|
||||
#define SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP2 63
|
||||
#define SSH2_MSG_USERAUTH_JPAKE_CLIENT_CONFIRM 64
|
||||
#define SSH2_MSG_USERAUTH_JPAKE_SERVER_CONFIRM 65
|
||||
|
||||
/* connection protocol: generic */
|
||||
|
||||
|
|
293
sshconnect2.c
293
sshconnect2.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: sshconnect2.c,v 1.201 2014/01/09 23:20:00 djm Exp $ */
|
||||
/* $OpenBSD: sshconnect2.c,v 1.202 2014/01/29 06:18:35 djm Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2008 Damien Miller. All rights reserved.
|
||||
|
@ -70,8 +70,6 @@
|
|||
#include "pathnames.h"
|
||||
#include "uidswap.h"
|
||||
#include "hostfile.h"
|
||||
#include "schnorr.h"
|
||||
#include "jpake.h"
|
||||
|
||||
#ifdef GSSAPI
|
||||
#include "ssh-gss.h"
|
||||
|
@ -289,18 +287,12 @@ void input_userauth_error(int, u_int32_t, void *);
|
|||
void input_userauth_info_req(int, u_int32_t, void *);
|
||||
void input_userauth_pk_ok(int, u_int32_t, void *);
|
||||
void input_userauth_passwd_changereq(int, u_int32_t, void *);
|
||||
void input_userauth_jpake_server_step1(int, u_int32_t, void *);
|
||||
void input_userauth_jpake_server_step2(int, u_int32_t, void *);
|
||||
void input_userauth_jpake_server_confirm(int, u_int32_t, void *);
|
||||
|
||||
int userauth_none(Authctxt *);
|
||||
int userauth_pubkey(Authctxt *);
|
||||
int userauth_passwd(Authctxt *);
|
||||
int userauth_kbdint(Authctxt *);
|
||||
int userauth_hostbased(Authctxt *);
|
||||
int userauth_jpake(Authctxt *);
|
||||
|
||||
void userauth_jpake_cleanup(Authctxt *);
|
||||
|
||||
#ifdef GSSAPI
|
||||
int userauth_gssapi(Authctxt *authctxt);
|
||||
|
@ -340,13 +332,6 @@ Authmethod authmethods[] = {
|
|||
NULL,
|
||||
&options.pubkey_authentication,
|
||||
NULL},
|
||||
#ifdef JPAKE
|
||||
{"jpake-01@openssh.com",
|
||||
userauth_jpake,
|
||||
userauth_jpake_cleanup,
|
||||
&options.zero_knowledge_password_authentication,
|
||||
&options.batch_mode},
|
||||
#endif
|
||||
{"keyboard-interactive",
|
||||
userauth_kbdint,
|
||||
NULL,
|
||||
|
@ -965,209 +950,6 @@ input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt)
|
|||
&input_userauth_passwd_changereq);
|
||||
}
|
||||
|
||||
#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), SSH_DIGEST_SHA1,
|
||||
&secret, &secret_len) != 0)
|
||||
fatal("%s: hash_buffer", __func__);
|
||||
|
||||
bzero(password, strlen(password));
|
||||
bzero(crypted, strlen(crypted));
|
||||
free(password);
|
||||
free(crypted);
|
||||
|
||||
if ((ret = BN_bin2bn(secret, secret_len, NULL)) == NULL)
|
||||
fatal("%s: BN_bin2bn (secret)", __func__);
|
||||
bzero(secret, secret_len);
|
||||
free(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));
|
||||
free(crypt_scheme);
|
||||
free(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);
|
||||
free(x3_proof);
|
||||
free(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);
|
||||
free(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);
|
||||
free(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 */
|
||||
|
||||
static int
|
||||
identity_sign(Identity *id, u_char **sigp, u_int *lenp,
|
||||
u_char *data, u_int datalen)
|
||||
|
@ -1783,79 +1565,6 @@ userauth_hostbased(Authctxt *authctxt)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef JPAKE
|
||||
int
|
||||
userauth_jpake(Authctxt *authctxt)
|
||||
{
|
||||
struct jpake_ctx *pctx;
|
||||
u_char *x1_proof, *x2_proof;
|
||||
u_int x1_proof_len, x2_proof_len;
|
||||
static int attempt = 0; /* XXX share with userauth_password's? */
|
||||
|
||||
if (attempt++ >= options.number_of_password_prompts)
|
||||
return 0;
|
||||
if (attempt != 1)
|
||||
error("Permission denied, please try again.");
|
||||
|
||||
if (authctxt->methoddata != NULL)
|
||||
fatal("%s: authctxt->methoddata already set (%p)",
|
||||
__func__, authctxt->methoddata);
|
||||
|
||||
authctxt->methoddata = pctx = jpake_new();
|
||||
|
||||
/*
|
||||
* Send request immediately, to get the protocol going while
|
||||
* we do the initial computations.
|
||||
*/
|
||||
packet_start(SSH2_MSG_USERAUTH_REQUEST);
|
||||
packet_put_cstring(authctxt->server_user);
|
||||
packet_put_cstring(authctxt->service);
|
||||
packet_put_cstring(authctxt->method->name);
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
|
||||
jpake_step1(pctx->grp,
|
||||
&pctx->client_id, &pctx->client_id_len,
|
||||
&pctx->x1, &pctx->x2, &pctx->g_x1, &pctx->g_x2,
|
||||
&x1_proof, &x1_proof_len,
|
||||
&x2_proof, &x2_proof_len);
|
||||
|
||||
JPAKE_DEBUG_CTX((pctx, "step 1 sending in %s", __func__));
|
||||
|
||||
packet_start(SSH2_MSG_USERAUTH_JPAKE_CLIENT_STEP1);
|
||||
packet_put_string(pctx->client_id, pctx->client_id_len);
|
||||
packet_put_bignum2(pctx->g_x1);
|
||||
packet_put_bignum2(pctx->g_x2);
|
||||
packet_put_string(x1_proof, x1_proof_len);
|
||||
packet_put_string(x2_proof, x2_proof_len);
|
||||
packet_send();
|
||||
|
||||
bzero(x1_proof, x1_proof_len);
|
||||
bzero(x2_proof, x2_proof_len);
|
||||
free(x1_proof);
|
||||
free(x2_proof);
|
||||
|
||||
/* Expect step 1 packet from peer */
|
||||
dispatch_set(SSH2_MSG_USERAUTH_JPAKE_SERVER_STEP1,
|
||||
input_userauth_jpake_server_step1);
|
||||
dispatch_set(SSH2_MSG_USERAUTH_SUCCESS,
|
||||
&input_userauth_success_unexpected);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
userauth_jpake_cleanup(Authctxt *authctxt)
|
||||
{
|
||||
debug3("%s: clean up", __func__);
|
||||
if (authctxt->methoddata != NULL) {
|
||||
jpake_free(authctxt->methoddata);
|
||||
authctxt->methoddata = NULL;
|
||||
}
|
||||
dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success);
|
||||
}
|
||||
#endif /* JPAKE */
|
||||
|
||||
/* find auth method */
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue