[openssh-commits] [openssh] 03/04: upstream: add dummy security key middleware based on work by

git+noreply at mindrot.org git+noreply at mindrot.org
Wed Nov 27 11:03:20 AEDT 2019


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit c6efa8a91af1d4fdb43909a23a0a4ffa012155ad
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Tue Nov 26 23:41:23 2019 +0000

    upstream: add dummy security key middleware based on work by
    
    markus@
    
    This will allow us to test U2F/FIDO2 support in OpenSSH without
    requiring real hardware.
    
    ok markus@
    
    OpenBSD-Regress-ID: 88b309464b8850c320cf7513f26d97ee1fdf9aae
---
 regress/misc/Makefile            |   2 +-
 regress/misc/sk-dummy/Makefile   |  67 +++++
 regress/misc/sk-dummy/sk-dummy.c | 522 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 590 insertions(+), 1 deletion(-)

diff --git a/regress/misc/Makefile b/regress/misc/Makefile
index 14c0c279..cf95f265 100644
--- a/regress/misc/Makefile
+++ b/regress/misc/Makefile
@@ -1,3 +1,3 @@
-SUBDIR=		kexfuzz
+SUBDIR=		kexfuzz sk-dummy
 
 .include <bsd.subdir.mk>
diff --git a/regress/misc/sk-dummy/Makefile b/regress/misc/sk-dummy/Makefile
new file mode 100644
index 00000000..db229aa1
--- /dev/null
+++ b/regress/misc/sk-dummy/Makefile
@@ -0,0 +1,67 @@
+#	$OpenBSD: Makefile,v 1.1 2019/11/26 23:41:23 djm Exp $
+
+.include <bsd.own.mk>
+.include <bsd.obj.mk>
+
+PROG=	sk-dummy.so
+NOMAN=
+
+SSHREL=../../../../../usr.bin/ssh
+.PATH: ${.CURDIR}/${SSHREL}
+
+SRCS=sk-dummy.c
+# From usr.bin/ssh
+SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c
+SRCS+=digest-openssl.c ssherr.c fatal.c sshbuf.c log.c cleanup.c
+OPENSSL?=	yes
+
+CFLAGS+=	-fPIC
+
+.if (${OPENSSL:L} == "yes")
+CFLAGS+=	-DWITH_OPENSSL
+.endif
+
+# enable warnings
+WARNINGS=Yes
+
+DEBUG=-g
+CFLAGS+=	-fstack-protector-all
+CDIAGFLAGS=	-Wall
+CDIAGFLAGS+=	-Wextra
+CDIAGFLAGS+=	-Werror
+CDIAGFLAGS+=	-Wchar-subscripts
+CDIAGFLAGS+=	-Wcomment
+CDIAGFLAGS+=	-Wformat
+CDIAGFLAGS+=	-Wformat-security
+CDIAGFLAGS+=	-Wimplicit
+CDIAGFLAGS+=	-Winline
+CDIAGFLAGS+=	-Wmissing-declarations
+CDIAGFLAGS+=	-Wmissing-prototypes
+CDIAGFLAGS+=	-Wparentheses
+CDIAGFLAGS+=	-Wpointer-arith
+CDIAGFLAGS+=	-Wreturn-type
+CDIAGFLAGS+=	-Wshadow
+CDIAGFLAGS+=	-Wsign-compare
+CDIAGFLAGS+=	-Wstrict-aliasing
+CDIAGFLAGS+=	-Wstrict-prototypes
+CDIAGFLAGS+=	-Wswitch
+CDIAGFLAGS+=	-Wtrigraphs
+CDIAGFLAGS+=	-Wuninitialized
+CDIAGFLAGS+=	-Wunused
+CDIAGFLAGS+=	-Wno-unused-parameter
+.if ${COMPILER_VERSION:L} != "gcc3"
+CDIAGFLAGS+=	-Wold-style-definition
+.endif
+
+CFLAGS+=-I${.CURDIR}/${SSHREL}
+
+.if (${OPENSSL:L} == "yes")
+LDADD+= -lcrypto
+DPADD+= ${LIBCRYPTO}
+.endif
+
+$(PROG): $(OBJS)
+	$(CC) $(LDFLAGS) -shared -o $@ $(OBJS) $(LDADD)
+
+.include <bsd.prog.mk>
+
diff --git a/regress/misc/sk-dummy/sk-dummy.c b/regress/misc/sk-dummy/sk-dummy.c
new file mode 100644
index 00000000..b223b1a0
--- /dev/null
+++ b/regress/misc/sk-dummy/sk-dummy.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2019 Markus Friedl
+ *
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+#include "crypto_api.h"
+
+#include <openssl/opensslv.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/pem.h>
+
+/* #define SK_DEBUG 1 */
+
+/* Compatibility with OpenSSH 1.0.x */
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
+#define ECDSA_SIG_get0(sig, pr, ps) \
+	do { \
+		(*pr) = sig->r; \
+		(*ps) = sig->s; \
+	} while (0)
+#endif
+
+#define SK_VERSION_MAJOR	0x00020000 /* current API version */
+
+/* Flags */
+#define SK_USER_PRESENCE_REQD	0x01
+
+/* Algs */
+#define	SK_ECDSA		0x00
+#define	SK_ED25519		0x01
+
+struct sk_enroll_response {
+	uint8_t *public_key;
+	size_t public_key_len;
+	uint8_t *key_handle;
+	size_t key_handle_len;
+	uint8_t *signature;
+	size_t signature_len;
+	uint8_t *attestation_cert;
+	size_t attestation_cert_len;
+};
+
+struct sk_sign_response {
+	uint8_t flags;
+	uint32_t counter;
+	uint8_t *sig_r;
+	size_t sig_r_len;
+	uint8_t *sig_s;
+	size_t sig_s_len;
+};
+
+/* Return the version of the middleware API */
+uint32_t sk_api_version(void);
+
+/* Enroll a U2F key (private key generation) */
+int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
+    const char *application, uint8_t flags,
+    struct sk_enroll_response **enroll_response);
+
+/* Sign a challenge */
+int sk_sign(int alg, const uint8_t *message, size_t message_len,
+    const char *application, const uint8_t *key_handle, size_t key_handle_len,
+    uint8_t flags, struct sk_sign_response **sign_response);
+
+static void skdebug(const char *func, const char *fmt, ...)
+    __attribute__((__format__ (printf, 2, 3)));
+
+static void
+skdebug(const char *func, const char *fmt, ...)
+{
+#if defined(SK_DEBUG)
+	va_list ap;
+
+	va_start(ap, fmt);
+	fprintf(stderr, "sk-dummy %s: ", func);
+	vfprintf(stderr, fmt, ap);
+	fputc('\n', stderr);
+	va_end(ap);
+#else
+	(void)func; /* XXX */
+	(void)fmt; /* XXX */
+#endif
+}
+
+uint32_t
+sk_api_version(void)
+{
+	return SK_VERSION_MAJOR;
+}
+
+static int
+pack_key_ecdsa(struct sk_enroll_response *response)
+{
+	EC_KEY *key = NULL;
+	const EC_GROUP *g;
+	const EC_POINT *q;
+	int ret = -1;
+	long privlen;
+	BIO *bio = NULL;
+	char *privptr;
+
+	response->public_key = NULL;
+	response->public_key_len = 0;
+	response->key_handle = NULL;
+	response->key_handle_len = 0;
+
+	if ((key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL) {
+		skdebug(__func__, "EC_KEY_new_by_curve_name");
+		goto out;
+	}
+	if (EC_KEY_generate_key(key) != 1) {
+		skdebug(__func__, "EC_KEY_generate_key");
+		goto out;
+	}
+	EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE);
+	if ((bio = BIO_new(BIO_s_mem())) == NULL ||
+	    (g = EC_KEY_get0_group(key)) == NULL ||
+	    (q = EC_KEY_get0_public_key(key)) == NULL) {
+		skdebug(__func__, "couldn't get key parameters");
+		goto out;
+	}
+	response->public_key_len = EC_POINT_point2oct(g, q,
+	    POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
+	if (response->public_key_len == 0 || response->public_key_len > 2048) {
+		skdebug(__func__, "bad pubkey length %zu",
+		    response->public_key_len);
+		goto out;
+	}
+	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
+		skdebug(__func__, "malloc pubkey failed");
+		goto out;
+	}
+	if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
+	    response->public_key, response->public_key_len, NULL) == 0) {
+		skdebug(__func__, "EC_POINT_point2oct failed");
+		goto out;
+	}
+	/* Key handle contains PEM encoded private key */
+	if (!PEM_write_bio_ECPrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) {
+		skdebug(__func__, "PEM_write_bio_ECPrivateKey failed");
+		goto out;
+	}
+	if ((privlen = BIO_get_mem_data(bio, &privptr)) <= 0) {
+		skdebug(__func__, "BIO_get_mem_data failed");
+		goto out;
+	}
+	if ((response->key_handle = malloc(privlen)) == NULL) {
+		skdebug(__func__, "malloc key_handle failed");
+		goto out;
+	}
+	response->key_handle_len = (size_t)privlen;
+	memcpy(response->key_handle, privptr, response->key_handle_len);
+	/* success */
+	ret = 0;
+ out:
+	if (ret != 0) {
+		if (response->public_key != NULL) {
+			memset(response->public_key, 0,
+			    response->public_key_len);
+			free(response->public_key);
+			response->public_key = NULL;
+		}
+		if (response->key_handle != NULL) {
+			memset(response->key_handle, 0,
+			    response->key_handle_len);
+			free(response->key_handle);
+			response->key_handle = NULL;
+		}
+	}
+	BIO_free(bio);
+	EC_KEY_free(key);
+	return ret;
+}
+
+static int
+pack_key_ed25519(struct sk_enroll_response *response)
+{
+	int ret = -1;
+	u_char pk[crypto_sign_ed25519_PUBLICKEYBYTES];
+	u_char sk[crypto_sign_ed25519_SECRETKEYBYTES];
+
+	response->public_key = NULL;
+	response->public_key_len = 0;
+	response->key_handle = NULL;
+	response->key_handle_len = 0;
+
+	memset(pk, 0, sizeof(pk));
+	memset(sk, 0, sizeof(sk));
+	crypto_sign_ed25519_keypair(pk, sk);
+
+	response->public_key_len = sizeof(pk);
+	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
+		skdebug(__func__, "malloc pubkey failed");
+		goto out;
+	}
+	memcpy(response->public_key, pk, sizeof(pk));
+	/* Key handle contains sk */
+	response->key_handle_len = sizeof(sk);
+	if ((response->key_handle = malloc(response->key_handle_len)) == NULL) {
+		skdebug(__func__, "malloc key_handle failed");
+		goto out;
+	}
+	memcpy(response->key_handle, sk, sizeof(sk));
+	/* success */
+	ret = 0;
+ out:
+	if (ret != 0)
+		free(response->public_key);
+	return ret;
+}
+
+int
+sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
+    const char *application, uint8_t flags,
+    struct sk_enroll_response **enroll_response)
+{
+	struct sk_enroll_response *response = NULL;
+	int ret = -1;
+
+	(void)flags; /* XXX; unused */
+
+	if (enroll_response == NULL) {
+		skdebug(__func__, "enroll_response == NULL");
+		goto out;
+	}
+	*enroll_response = NULL;
+	if ((response = calloc(1, sizeof(*response))) == NULL) {
+		skdebug(__func__, "calloc response failed");
+		goto out;
+	}
+	switch(alg) {
+	case SK_ECDSA:
+		if (pack_key_ecdsa(response) != 0)
+			goto out;
+		break;
+	case SK_ED25519:
+		if (pack_key_ed25519(response) != 0)
+			goto out;
+		break;
+	default:
+		skdebug(__func__, "unsupported key type %d", alg);
+		return -1;
+	}
+	/* Have to return something here */
+	if ((response->signature = calloc(1, 1)) == NULL) {
+		skdebug(__func__, "calloc signature failed");
+		goto out;
+	}
+	response->signature_len = 0;
+
+	*enroll_response = response;
+	response = NULL;
+	ret = 0;
+ out:
+	if (response != NULL) {
+		free(response->public_key);
+		free(response->key_handle);
+		free(response->signature);
+		free(response->attestation_cert);
+		free(response);
+	}
+	return ret;
+}
+
+static void
+dump(const char *preamble, const void *sv, size_t l)
+{
+#ifdef SK_DEBUG
+	const u_char *s = (const u_char *)sv;
+	size_t i;
+
+	fprintf(stderr, "%s (len %zu):\n", preamble, l);
+	for (i = 0; i < l; i++) {
+		if (i % 16 == 0)
+			fprintf(stderr, "%04zu: ", i);
+		fprintf(stderr, "%02x", s[i]);
+		if (i % 16 == 15 || i == l - 1)
+			fprintf(stderr, "\n");
+	}
+#endif
+}
+
+static int
+sig_ecdsa(const uint8_t *message, size_t message_len,
+    const char *application, uint32_t counter, uint8_t flags,
+    const uint8_t *key_handle, size_t key_handle_len,
+    struct sk_sign_response *response)
+{
+	ECDSA_SIG *sig = NULL;
+	const BIGNUM *sig_r, *sig_s;
+	int ret = -1;
+	BIO *bio = NULL;
+	EVP_PKEY *pk = NULL;
+	EC_KEY *ec = NULL;
+	SHA256_CTX ctx;
+	uint8_t	apphash[SHA256_DIGEST_LENGTH];
+	uint8_t	sighash[SHA256_DIGEST_LENGTH];
+	uint8_t countbuf[4];
+
+	/* Decode EC_KEY from key handle */
+	if ((bio = BIO_new(BIO_s_mem())) == NULL ||
+	    BIO_write(bio, key_handle, key_handle_len) != (int)key_handle_len) {
+		skdebug(__func__, "BIO setup failed");
+		goto out;
+	}
+	if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, "")) == NULL) {
+		skdebug(__func__, "PEM_read_bio_PrivateKey failed");
+		goto out;
+	}
+	if (EVP_PKEY_base_id(pk) != EVP_PKEY_EC) {
+		skdebug(__func__, "Not an EC key: %d", EVP_PKEY_base_id(pk));
+		goto out;
+	}
+	if ((ec = EVP_PKEY_get1_EC_KEY(pk)) == NULL) {
+		skdebug(__func__, "EVP_PKEY_get1_EC_KEY failed");
+		goto out;
+	}
+	/* Expect message to be pre-hashed */
+	if (message_len != SHA256_DIGEST_LENGTH) {
+		skdebug(__func__, "bad message len %zu", message_len);
+		goto out;
+	}
+	/* Prepare data to be signed */
+	dump("message", message, message_len);
+	SHA256_Init(&ctx);
+	SHA256_Update(&ctx, application, strlen(application));
+	SHA256_Final(apphash, &ctx);
+	dump("apphash", apphash, sizeof(apphash));
+	countbuf[0] = (counter >> 24) & 0xff;
+	countbuf[1] = (counter >> 16) & 0xff;
+	countbuf[2] = (counter >> 8) & 0xff;
+	countbuf[3] = counter & 0xff;
+	dump("countbuf", countbuf, sizeof(countbuf));
+	dump("flags", &flags, sizeof(flags));
+	SHA256_Init(&ctx);
+	SHA256_Update(&ctx, apphash, sizeof(apphash));
+	SHA256_Update(&ctx, &flags, sizeof(flags));
+	SHA256_Update(&ctx, countbuf, sizeof(countbuf));
+	SHA256_Update(&ctx, message, message_len);
+	SHA256_Final(sighash, &ctx);
+	dump("sighash", sighash, sizeof(sighash));
+	/* create and encode signature */
+	if ((sig = ECDSA_do_sign(sighash, sizeof(sighash), ec)) == NULL) {
+		skdebug(__func__, "ECDSA_do_sign failed");
+		goto out;
+	}
+	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
+	response->sig_r_len = BN_num_bytes(sig_r);
+	response->sig_s_len = BN_num_bytes(sig_s);
+	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
+	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
+		skdebug(__func__, "calloc signature failed");
+		goto out;
+	}
+	BN_bn2bin(sig_r, response->sig_r);
+	BN_bn2bin(sig_s, response->sig_s);
+	ret = 0;
+ out:
+	explicit_bzero(&ctx, sizeof(ctx));
+	explicit_bzero(&apphash, sizeof(apphash));
+	explicit_bzero(&sighash, sizeof(sighash));
+	ECDSA_SIG_free(sig);
+	if (ret != 0) {
+		free(response->sig_r);
+		free(response->sig_s);
+		response->sig_r = NULL;
+		response->sig_s = NULL;
+	}
+	BIO_free(bio);
+	EC_KEY_free(ec);
+	EVP_PKEY_free(pk);
+	return ret;
+}
+
+static int
+sig_ed25519(const uint8_t *message, size_t message_len,
+    const char *application, uint32_t counter, uint8_t flags,
+    const uint8_t *key_handle, size_t key_handle_len,
+    struct sk_sign_response *response)
+{
+	size_t o;
+	int ret = -1;
+	SHA256_CTX ctx;
+	uint8_t	apphash[SHA256_DIGEST_LENGTH];
+	uint8_t signbuf[sizeof(apphash) + sizeof(flags) +
+	    sizeof(counter) + SHA256_DIGEST_LENGTH];
+	uint8_t sig[crypto_sign_ed25519_BYTES + sizeof(signbuf)];
+	unsigned long long smlen;
+
+	if (key_handle_len != crypto_sign_ed25519_SECRETKEYBYTES) {
+		skdebug(__func__, "bad key handle length %zu", key_handle_len);
+		goto out;
+	}
+	/* Expect message to be pre-hashed */
+	if (message_len != SHA256_DIGEST_LENGTH) {
+		skdebug(__func__, "bad message len %zu", message_len);
+		goto out;
+	}
+	/* Prepare data to be signed */
+	dump("message", message, message_len);
+	SHA256_Init(&ctx);
+	SHA256_Update(&ctx, application, strlen(application));
+	SHA256_Final(apphash, &ctx);
+	dump("apphash", apphash, sizeof(apphash));
+
+	memcpy(signbuf, apphash, sizeof(apphash));
+	o = sizeof(apphash);
+	signbuf[o++] = flags;
+	signbuf[o++] = (counter >> 24) & 0xff;
+	signbuf[o++] = (counter >> 16) & 0xff;
+	signbuf[o++] = (counter >> 8) & 0xff;
+	signbuf[o++] = counter & 0xff;
+	memcpy(signbuf + o, message, message_len);
+	o += message_len;
+	if (o != sizeof(signbuf)) {
+		skdebug(__func__, "bad sign buf len %zu, expected %zu",
+		    o, sizeof(signbuf));
+		goto out;
+	}
+	dump("signbuf", signbuf, sizeof(signbuf));
+	/* create and encode signature */
+	smlen = sizeof(signbuf);
+	if (crypto_sign_ed25519(sig, &smlen, signbuf, sizeof(signbuf),
+	    key_handle) != 0) {
+		skdebug(__func__, "crypto_sign_ed25519 failed");
+		goto out;
+	}
+	if (smlen <= sizeof(signbuf)) {
+		skdebug(__func__, "bad sign smlen %llu, expected min %zu",
+		    smlen, sizeof(signbuf) + 1);
+		goto out;
+	}
+	response->sig_r_len = (size_t)(smlen - sizeof(signbuf));
+	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
+		skdebug(__func__, "calloc signature failed");
+		goto out;
+	}
+	memcpy(response->sig_r, sig, response->sig_r_len);
+	dump("sig_r", response->sig_r, response->sig_r_len);
+	ret = 0;
+ out:
+	explicit_bzero(&ctx, sizeof(ctx));
+	explicit_bzero(&apphash, sizeof(apphash));
+	explicit_bzero(&signbuf, sizeof(signbuf));
+	explicit_bzero(&sig, sizeof(sig));
+	if (ret != 0) {
+		free(response->sig_r);
+		response->sig_r = NULL;
+	}
+	return ret;
+}
+
+int
+sk_sign(int alg, const uint8_t *message, size_t message_len,
+    const char *application,
+    const uint8_t *key_handle, size_t key_handle_len,
+    uint8_t flags, struct sk_sign_response **sign_response)
+{
+	struct sk_sign_response *response = NULL;
+	int ret = -1;
+
+	if (sign_response == NULL) {
+		skdebug(__func__, "sign_response == NULL");
+		goto out;
+	}
+	*sign_response = NULL;
+	if ((response = calloc(1, sizeof(*response))) == NULL) {
+		skdebug(__func__, "calloc response failed");
+		goto out;
+	}
+	response->flags = flags;
+	response->counter = 0x12345678;
+	switch(alg) {
+	case SK_ECDSA:
+		if (sig_ecdsa(message, message_len, application,
+		    response->counter, flags, key_handle, key_handle_len,
+		    response) != 0)
+			goto out;
+		break;
+	case SK_ED25519:
+		if (sig_ed25519(message, message_len, application,
+		    response->counter, flags, key_handle, key_handle_len,
+		    response) != 0)
+			goto out;
+		break;
+	default:
+		skdebug(__func__, "unsupported key type %d", alg);
+		return -1;
+	}
+	*sign_response = response;
+	response = NULL;
+	ret = 0;
+ out:
+	if (response != NULL) {
+		free(response->sig_r);
+		free(response->sig_s);
+		free(response);
+	}
+	return ret;
+}

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list