Changeset View
Changeset View
Standalone View
Standalone View
kexc25519.c
/* $OpenBSD: kexc25519.c,v 1.10 2016/05/02 08:49:03 djm Exp $ */ | /* $OpenBSD: kexc25519.c,v 1.17 2019/01/21 10:40:11 djm Exp $ */ | ||||
/* | /* | ||||
* Copyright (c) 2001, 2013 Markus Friedl. All rights reserved. | * Copyright (c) 2019 Markus Friedl. All rights reserved. | ||||
* Copyright (c) 2010 Damien Miller. All rights reserved. | * Copyright (c) 2010 Damien Miller. All rights reserved. | ||||
* Copyright (c) 2013 Aris Adamantiadis. All rights reserved. | * Copyright (c) 2013 Aris Adamantiadis. All rights reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
Show All 12 Lines | |||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
#include "includes.h" | #include "includes.h" | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <signal.h> | #include <stdio.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <signal.h> | |||||
#include <openssl/bn.h> | |||||
#include <openssl/evp.h> | |||||
#include "sshbuf.h" | |||||
#include "ssh2.h" | |||||
#include "sshkey.h" | #include "sshkey.h" | ||||
#include "cipher.h" | |||||
#include "kex.h" | #include "kex.h" | ||||
#include "log.h" | #include "sshbuf.h" | ||||
#include "digest.h" | #include "digest.h" | ||||
#include "ssherr.h" | #include "ssherr.h" | ||||
#include "ssh2.h" | |||||
extern int crypto_scalarmult_curve25519(u_char a[CURVE25519_SIZE], | extern int crypto_scalarmult_curve25519(u_char a[CURVE25519_SIZE], | ||||
const u_char b[CURVE25519_SIZE], const u_char c[CURVE25519_SIZE]) | const u_char b[CURVE25519_SIZE], const u_char c[CURVE25519_SIZE]) | ||||
__attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) | __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) | ||||
__attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))) | __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))) | ||||
__attribute__((__bounded__(__minbytes__, 3, CURVE25519_SIZE))); | __attribute__((__bounded__(__minbytes__, 3, CURVE25519_SIZE))); | ||||
void | void | ||||
kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) | kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) | ||||
{ | { | ||||
static const u_char basepoint[CURVE25519_SIZE] = {9}; | static const u_char basepoint[CURVE25519_SIZE] = {9}; | ||||
arc4random_buf(key, CURVE25519_SIZE); | arc4random_buf(key, CURVE25519_SIZE); | ||||
crypto_scalarmult_curve25519(pub, key, basepoint); | crypto_scalarmult_curve25519(pub, key, basepoint); | ||||
} | } | ||||
int | int | ||||
kexc25519_shared_key(const u_char key[CURVE25519_SIZE], | kexc25519_shared_key_ext(const u_char key[CURVE25519_SIZE], | ||||
const u_char pub[CURVE25519_SIZE], struct sshbuf *out) | const u_char pub[CURVE25519_SIZE], struct sshbuf *out, int raw) | ||||
{ | { | ||||
u_char shared_key[CURVE25519_SIZE]; | u_char shared_key[CURVE25519_SIZE]; | ||||
u_char zero[CURVE25519_SIZE]; | |||||
int r; | int r; | ||||
/* Check for all-zero public key */ | crypto_scalarmult_curve25519(shared_key, key, pub); | ||||
explicit_bzero(shared_key, CURVE25519_SIZE); | |||||
if (timingsafe_bcmp(pub, shared_key, CURVE25519_SIZE) == 0) | /* Check for all-zero shared secret */ | ||||
explicit_bzero(zero, CURVE25519_SIZE); | |||||
if (timingsafe_bcmp(zero, shared_key, CURVE25519_SIZE) == 0) | |||||
return SSH_ERR_KEY_INVALID_EC_VALUE; | return SSH_ERR_KEY_INVALID_EC_VALUE; | ||||
crypto_scalarmult_curve25519(shared_key, key, pub); | |||||
#ifdef DEBUG_KEXECDH | #ifdef DEBUG_KEXECDH | ||||
dump_digest("shared secret", shared_key, CURVE25519_SIZE); | dump_digest("shared secret", shared_key, CURVE25519_SIZE); | ||||
#endif | #endif | ||||
sshbuf_reset(out); | if (raw) | ||||
r = sshbuf_put(out, shared_key, CURVE25519_SIZE); | |||||
else | |||||
r = sshbuf_put_bignum2_bytes(out, shared_key, CURVE25519_SIZE); | r = sshbuf_put_bignum2_bytes(out, shared_key, CURVE25519_SIZE); | ||||
explicit_bzero(shared_key, CURVE25519_SIZE); | explicit_bzero(shared_key, CURVE25519_SIZE); | ||||
return r; | return r; | ||||
} | } | ||||
int | int | ||||
kex_c25519_hash( | kexc25519_shared_key(const u_char key[CURVE25519_SIZE], | ||||
int hash_alg, | const u_char pub[CURVE25519_SIZE], struct sshbuf *out) | ||||
const char *client_version_string, | |||||
const char *server_version_string, | |||||
const u_char *ckexinit, size_t ckexinitlen, | |||||
const u_char *skexinit, size_t skexinitlen, | |||||
const u_char *serverhostkeyblob, size_t sbloblen, | |||||
const u_char client_dh_pub[CURVE25519_SIZE], | |||||
const u_char server_dh_pub[CURVE25519_SIZE], | |||||
const u_char *shared_secret, size_t secretlen, | |||||
u_char *hash, size_t *hashlen) | |||||
{ | { | ||||
struct sshbuf *b; | return kexc25519_shared_key_ext(key, pub, out, 0); | ||||
} | |||||
int | |||||
kex_c25519_keypair(struct kex *kex) | |||||
{ | |||||
struct sshbuf *buf = NULL; | |||||
u_char *cp = NULL; | |||||
int r; | int r; | ||||
if (*hashlen < ssh_digest_bytes(hash_alg)) | if ((buf = sshbuf_new()) == NULL) | ||||
return SSH_ERR_INVALID_ARGUMENT; | |||||
if ((b = sshbuf_new()) == NULL) | |||||
return SSH_ERR_ALLOC_FAIL; | return SSH_ERR_ALLOC_FAIL; | ||||
if ((r = sshbuf_put_cstring(b, client_version_string)) < 0 || | if ((r = sshbuf_reserve(buf, CURVE25519_SIZE, &cp)) != 0) | ||||
(r = sshbuf_put_cstring(b, server_version_string)) < 0 || | goto out; | ||||
/* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ | kexc25519_keygen(kex->c25519_client_key, cp); | ||||
(r = sshbuf_put_u32(b, ckexinitlen+1)) < 0 || | #ifdef DEBUG_KEXECDH | ||||
(r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) < 0 || | dump_digest("client public key c25519:", cp, CURVE25519_SIZE); | ||||
(r = sshbuf_put(b, ckexinit, ckexinitlen)) < 0 || | #endif | ||||
(r = sshbuf_put_u32(b, skexinitlen+1)) < 0 || | kex->client_pub = buf; | ||||
(r = sshbuf_put_u8(b, SSH2_MSG_KEXINIT)) < 0 || | buf = NULL; | ||||
(r = sshbuf_put(b, skexinit, skexinitlen)) < 0 || | out: | ||||
(r = sshbuf_put_string(b, serverhostkeyblob, sbloblen)) < 0 || | sshbuf_free(buf); | ||||
(r = sshbuf_put_string(b, client_dh_pub, CURVE25519_SIZE)) < 0 || | |||||
(r = sshbuf_put_string(b, server_dh_pub, CURVE25519_SIZE)) < 0 || | |||||
(r = sshbuf_put(b, shared_secret, secretlen)) < 0) { | |||||
sshbuf_free(b); | |||||
return r; | return r; | ||||
} | } | ||||
#ifdef DEBUG_KEX | |||||
sshbuf_dump(b, stderr); | int | ||||
kex_c25519_enc(struct kex *kex, const struct sshbuf *client_blob, | |||||
struct sshbuf **server_blobp, struct sshbuf **shared_secretp) | |||||
{ | |||||
struct sshbuf *server_blob = NULL; | |||||
struct sshbuf *buf = NULL; | |||||
const u_char *client_pub; | |||||
u_char *server_pub; | |||||
u_char server_key[CURVE25519_SIZE]; | |||||
int r; | |||||
*server_blobp = NULL; | |||||
*shared_secretp = NULL; | |||||
if (sshbuf_len(client_blob) != CURVE25519_SIZE) { | |||||
r = SSH_ERR_SIGNATURE_INVALID; | |||||
goto out; | |||||
} | |||||
client_pub = sshbuf_ptr(client_blob); | |||||
#ifdef DEBUG_KEXECDH | |||||
dump_digest("client public key 25519:", client_pub, CURVE25519_SIZE); | |||||
#endif | #endif | ||||
if (ssh_digest_buffer(hash_alg, b, hash, *hashlen) != 0) { | /* allocate space for encrypted KEM key and ECDH pub key */ | ||||
sshbuf_free(b); | if ((server_blob = sshbuf_new()) == NULL) { | ||||
return SSH_ERR_LIBCRYPTO_ERROR; | r = SSH_ERR_ALLOC_FAIL; | ||||
goto out; | |||||
} | } | ||||
sshbuf_free(b); | if ((r = sshbuf_reserve(server_blob, CURVE25519_SIZE, &server_pub)) != 0) | ||||
*hashlen = ssh_digest_bytes(hash_alg); | goto out; | ||||
#ifdef DEBUG_KEX | kexc25519_keygen(server_key, server_pub); | ||||
dump_digest("hash", hash, *hashlen); | /* allocate shared secret */ | ||||
if ((buf = sshbuf_new()) == NULL) { | |||||
r = SSH_ERR_ALLOC_FAIL; | |||||
goto out; | |||||
} | |||||
if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 0)) < 0) | |||||
goto out; | |||||
#ifdef DEBUG_KEXECDH | |||||
dump_digest("server public key 25519:", server_pub, CURVE25519_SIZE); | |||||
dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); | |||||
#endif | #endif | ||||
return 0; | *server_blobp = server_blob; | ||||
*shared_secretp = buf; | |||||
server_blob = NULL; | |||||
buf = NULL; | |||||
out: | |||||
explicit_bzero(server_key, sizeof(server_key)); | |||||
sshbuf_free(server_blob); | |||||
sshbuf_free(buf); | |||||
return r; | |||||
} | |||||
int | |||||
kex_c25519_dec(struct kex *kex, const struct sshbuf *server_blob, | |||||
struct sshbuf **shared_secretp) | |||||
{ | |||||
struct sshbuf *buf = NULL; | |||||
const u_char *server_pub; | |||||
int r; | |||||
*shared_secretp = NULL; | |||||
if (sshbuf_len(server_blob) != CURVE25519_SIZE) { | |||||
r = SSH_ERR_SIGNATURE_INVALID; | |||||
goto out; | |||||
} | |||||
server_pub = sshbuf_ptr(server_blob); | |||||
#ifdef DEBUG_KEXECDH | |||||
dump_digest("server public key c25519:", server_pub, CURVE25519_SIZE); | |||||
#endif | |||||
/* shared secret */ | |||||
if ((buf = sshbuf_new()) == NULL) { | |||||
r = SSH_ERR_ALLOC_FAIL; | |||||
goto out; | |||||
} | |||||
if ((r = kexc25519_shared_key_ext(kex->c25519_client_key, server_pub, | |||||
buf, 0)) < 0) | |||||
goto out; | |||||
#ifdef DEBUG_KEXECDH | |||||
dump_digest("encoded shared secret:", sshbuf_ptr(buf), sshbuf_len(buf)); | |||||
#endif | |||||
*shared_secretp = buf; | |||||
buf = NULL; | |||||
out: | |||||
sshbuf_free(buf); | |||||
return r; | |||||
} | } |