summaryrefslogtreecommitdiff
path: root/sbin/iked/crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/iked/crypto.c')
-rw-r--r--sbin/iked/crypto.c263
1 files changed, 217 insertions, 46 deletions
diff --git a/sbin/iked/crypto.c b/sbin/iked/crypto.c
index fdd7163e7b5..ebee0cb71d4 100644
--- a/sbin/iked/crypto.c
+++ b/sbin/iked/crypto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: crypto.c,v 1.19 2015/10/31 19:28:19 naddy Exp $ */
+/* $OpenBSD: crypto.c,v 1.20 2017/03/27 10:06:41 reyk Exp $ */
/*
* Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -39,36 +39,52 @@
#include "iked.h"
#include "ikev2.h"
-/* RFC 7427, A.1 */
-static const uint8_t sha256WithRSAEncryption[] = {
+/* RFC 7427, A.1 RSA */
+static const uint8_t sha256WithRSA[] = {
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00
};
-static const uint8_t sha384WithRSAEncryption[] = {
+static const uint8_t sha384WithRSA[] = {
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00
};
-static const uint8_t sha512WithRSAEncryption[] = {
+static const uint8_t sha512WithRSA[] = {
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00
};
+/* RFC 7427, A.3 ECDSA */
+static const uint8_t ecdsa_sha256[] = {
+ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x02
+};
+static const uint8_t ecdsa_sha384[] = {
+ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x03
+};
+static const uint8_t ecdsa_sha512[] = {
+ 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x04
+};
-struct {
+static const struct {
+ int sc_keytype;
+ const EVP_MD *(*sc_md)(void);
uint8_t sc_len;
const uint8_t *sc_oid;
- const EVP_MD *(*sc_md)(void);
} schemes[] = {
- { sizeof(sha256WithRSAEncryption),
- sha256WithRSAEncryption, EVP_sha256 },
- { sizeof(sha384WithRSAEncryption),
- sha384WithRSAEncryption, EVP_sha384 },
- { sizeof(sha512WithRSAEncryption),
- sha512WithRSAEncryption, EVP_sha512 },
+ { EVP_PKEY_RSA, EVP_sha256, sizeof(sha256WithRSA), sha256WithRSA },
+ { EVP_PKEY_RSA, EVP_sha384, sizeof(sha384WithRSA), sha384WithRSA },
+ { EVP_PKEY_RSA, EVP_sha512, sizeof(sha512WithRSA), sha512WithRSA },
+ { EVP_PKEY_EC, EVP_sha256, sizeof(ecdsa_sha256), ecdsa_sha256 },
+ { EVP_PKEY_EC, EVP_sha384, sizeof(ecdsa_sha384), ecdsa_sha384 },
+ { EVP_PKEY_EC, EVP_sha512, sizeof(ecdsa_sha512), ecdsa_sha512 },
};
int _dsa_verify_init(struct iked_dsa *, const uint8_t *, size_t);
-size_t _dsa_verify_offset(struct iked_dsa *, uint8_t *);
+int _dsa_verify_prepare(struct iked_dsa *, uint8_t **, size_t *,
+ uint8_t **);
int _dsa_sign_encode(struct iked_dsa *, uint8_t *, size_t *);
+int _dsa_sign_ecdsa(struct iked_dsa *, uint8_t *, size_t);
struct iked_hash *
hash_new(uint8_t type, uint16_t id)
@@ -358,6 +374,7 @@ struct ibuf *
cipher_setiv(struct iked_cipher *encr, void *iv, size_t len)
{
ibuf_release(encr->encr_iv);
+ encr->encr_iv = NULL;
if (iv != NULL) {
if (len < encr->encr_ivlength) {
log_debug("%s: invalid IV length %zu", __func__, len);
@@ -573,6 +590,7 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type)
BIO *rawcert = NULL;
X509 *cert = NULL;
RSA *rsa = NULL;
+ EC_KEY *ec = NULL;
EVP_PKEY *pkey = NULL;
ibuf_release(dsa->dsa_keydata);
@@ -609,7 +627,25 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type)
if (!EVP_PKEY_set1_RSA(pkey, rsa))
goto sslerr;
- RSA_free(rsa); /* pkey now has the reference */
+ RSA_free(rsa); /* pkey now has the reference */
+ dsa->dsa_cert = NULL;
+ dsa->dsa_key = pkey;
+ break;
+ case IKEV2_CERT_ECDSA:
+ if (dsa->dsa_sign) {
+ if ((ec = d2i_ECPrivateKey_bio(rawcert, NULL)) == NULL)
+ goto sslerr;
+ } else {
+ if ((ec = d2i_EC_PUBKEY_bio(rawcert, NULL)) == NULL)
+ goto sslerr;
+ }
+
+ if ((pkey = EVP_PKEY_new()) == NULL)
+ goto sslerr;
+ if (!EVP_PKEY_set1_EC_KEY(pkey, ec))
+ goto sslerr;
+
+ EC_KEY_free(ec); /* pkey now has the reference */
dsa->dsa_cert = NULL;
dsa->dsa_key = pkey;
break;
@@ -629,6 +665,8 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type)
if (rsa != NULL)
RSA_free(rsa);
+ if (ec != NULL)
+ EC_KEY_free(ec);
if (pkey != NULL)
EVP_PKEY_free(pkey);
if (cert != NULL)
@@ -636,6 +674,7 @@ dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type)
if (rawcert != NULL)
BIO_free(rawcert);
ibuf_release(dsa->dsa_keydata);
+ dsa->dsa_keydata = NULL;
return (NULL);
}
@@ -644,6 +683,7 @@ _dsa_verify_init(struct iked_dsa *dsa, const uint8_t *sig, size_t len)
{
uint8_t oidlen;
size_t i;
+ int keytype;
if (dsa->dsa_priv != NULL)
return (0);
@@ -656,23 +696,30 @@ _dsa_verify_init(struct iked_dsa *dsa, const uint8_t *sig, size_t len)
print_map(dsa->dsa_method, ikev2_auth_map));
return (-1);
}
+ if (dsa->dsa_key == NULL) {
+ log_debug("%s: dsa_key not set for %s", __func__,
+ print_map(dsa->dsa_method, ikev2_auth_map));
+ return (-1);
+ }
+ keytype = EVP_PKEY_type(((EVP_PKEY *)dsa->dsa_key)->type);
if (sig == NULL) {
log_debug("%s: signature missing", __func__);
return (-1);
}
- if (len < 1) {
+ if (len < sizeof(oidlen)) {
log_debug("%s: signature (%zu) too small for oid length",
__func__, len);
return (-1);
}
memcpy(&oidlen, sig, sizeof(oidlen));
- if (len < (size_t)oidlen + 1) {
+ if (len < (size_t)oidlen + sizeof(oidlen)) {
log_debug("%s: signature (%zu) too small for oid (%u)",
__func__, len, oidlen);
return (-1);
}
for (i = 0; i < nitems(schemes); i++) {
- if (oidlen == schemes[i].sc_len &&
+ if (keytype == schemes[i].sc_keytype &&
+ oidlen == schemes[i].sc_len &&
memcmp(sig + 1, schemes[i].sc_oid,
schemes[i].sc_len) == 0) {
dsa->dsa_priv = (*schemes[i].sc_md)();
@@ -727,32 +774,98 @@ dsa_update(struct iked_dsa *dsa, const void *buf, size_t len)
int
_dsa_sign_encode(struct iked_dsa *dsa, uint8_t *ptr, size_t *offp)
{
+ int keytype;
+ size_t i;
+
if (offp)
*offp = 0;
if (dsa->dsa_method != IKEV2_AUTH_SIG)
return (0);
- if (dsa->dsa_priv != EVP_sha256())
+ if (dsa->dsa_key == NULL)
+ return (-1);
+ keytype = EVP_PKEY_type(((EVP_PKEY *)dsa->dsa_key)->type);
+ for (i = 0; i < nitems(schemes); i++) {
+ /* XXX should avoid calling sc_md() each time... */
+ if (keytype == schemes[i].sc_keytype &&
+ (dsa->dsa_priv == (*schemes[i].sc_md)()))
+ break;
+ }
+ if (i >= nitems(schemes))
return (-1);
if (ptr) {
- ptr[0] = sizeof(sha256WithRSAEncryption);
- memcpy(ptr + 1, sha256WithRSAEncryption,
- sizeof(sha256WithRSAEncryption));
+ ptr[0] = schemes[i].sc_len;
+ memcpy(ptr + sizeof(ptr[0]), schemes[i].sc_oid,
+ schemes[i].sc_len);
}
if (offp)
- *offp = 1 + sizeof(sha256WithRSAEncryption);
+ *offp = sizeof(ptr[0]) + schemes[i].sc_len;
return (0);
}
+/* Export size of encoded signature hash type */
size_t
-dsa_length(struct iked_dsa *dsa)
+dsa_prefix(struct iked_dsa *dsa)
{
size_t off = 0;
+ if (_dsa_sign_encode(dsa, NULL, &off) < 0)
+ fatal("dsa_prefix: internal error");
+ return off;
+}
+
+size_t
+dsa_length(struct iked_dsa *dsa)
+{
if (dsa->dsa_hmac)
return (EVP_MD_size(dsa->dsa_priv));
- if (_dsa_sign_encode(dsa, NULL, &off) < 0)
- fatal("dsa_length: internal error");
- return (EVP_PKEY_size(dsa->dsa_key) + off);
+ switch (dsa->dsa_method) {
+ case IKEV2_AUTH_ECDSA_256:
+ case IKEV2_AUTH_ECDSA_384:
+ case IKEV2_AUTH_ECDSA_521:
+ /* size of concat(r|s) */
+ return (2 * ((EVP_PKEY_bits(dsa->dsa_key) + 7) / 8));
+ }
+ return (dsa_prefix(dsa) + EVP_PKEY_size(dsa->dsa_key));
+}
+
+int
+_dsa_sign_ecdsa(struct iked_dsa *dsa, uint8_t *ptr, size_t len)
+{
+ ECDSA_SIG *obj = NULL;
+ uint8_t *otmp = NULL, *tmp;
+ unsigned int tmplen;
+ int ret = -1;
+ int bnlen, off;
+
+ if (len % 2)
+ goto done; /* must be even */
+ bnlen = len/2;
+ /*
+ * (a) create DER signature into 'tmp' buffer
+ * (b) convert buffer to ECDSA_SIG object
+ * (c) concatenate the padded r|s BIGNUMS into 'ptr'
+ */
+ if ((tmplen = EVP_PKEY_size(dsa->dsa_key)) == 0)
+ goto done;
+ if ((otmp = tmp = calloc(1, tmplen)) == NULL)
+ goto done;
+ if (!EVP_SignFinal(dsa->dsa_ctx, tmp, &tmplen, dsa->dsa_key))
+ goto done;
+ if (d2i_ECDSA_SIG(&obj, (const uint8_t **)&tmp, tmplen) == NULL)
+ goto done;
+ if (BN_num_bytes(obj->r) > bnlen || BN_num_bytes(obj->s) > bnlen)
+ goto done;
+ memset(ptr, 0, len);
+ off = bnlen - BN_num_bytes(obj->r);
+ BN_bn2bin(obj->r, ptr + off);
+ off = 2 * bnlen - BN_num_bytes(obj->s);
+ BN_bn2bin(obj->s, ptr + off);
+ ret = 0;
+ done:
+ free(otmp);
+ if (obj)
+ ECDSA_SIG_free(obj);
+ return (ret);
}
ssize_t
@@ -769,36 +882,92 @@ dsa_sign_final(struct iked_dsa *dsa, void *buf, size_t len)
if (!HMAC_Final(dsa->dsa_ctx, buf, &siglen))
return (-1);
} else {
- if (_dsa_sign_encode(dsa, ptr, &off) < 0)
- return (-1);
- if (!EVP_SignFinal(dsa->dsa_ctx, ptr + off, &siglen,
- dsa->dsa_key))
- return (-1);
- siglen += off;
+ switch (dsa->dsa_method) {
+ case IKEV2_AUTH_ECDSA_256:
+ case IKEV2_AUTH_ECDSA_384:
+ case IKEV2_AUTH_ECDSA_521:
+ if (_dsa_sign_ecdsa(dsa, buf, len) < 0)
+ return (-1);
+ siglen = len;
+ break;
+ default:
+ if (_dsa_sign_encode(dsa, ptr, &off) < 0)
+ return (-1);
+ if (!EVP_SignFinal(dsa->dsa_ctx, ptr + off, &siglen,
+ dsa->dsa_key))
+ return (-1);
+ siglen += off;
+ break;
+ }
}
return (siglen);
}
-size_t
-_dsa_verify_offset(struct iked_dsa *dsa, uint8_t *ptr)
+int
+_dsa_verify_prepare(struct iked_dsa *dsa, uint8_t **sigp, size_t *lenp,
+ uint8_t **freemep)
{
- /*
- * XXX assumes that _dsa_verify_init() has already checked
- * the encoded method.
- */
- if (dsa->dsa_method == IKEV2_AUTH_SIG)
- return (ptr[0] + 1);
- return (0);
+ ECDSA_SIG *obj = NULL;
+ uint8_t *ptr = NULL;
+ size_t bnlen, len, off;
+ int ret = -1;
+
+ *freemep = NULL; /* don't return garbage in case of an error */
+
+ switch (dsa->dsa_method) {
+ case IKEV2_AUTH_SIG:
+ /*
+ * The first byte of the signature encodes the OID
+ * prefix length which we need to skip.
+ */
+ off = (*sigp)[0] + 1;
+ *sigp = *sigp + off;
+ *lenp = *lenp - off;
+ *freemep = NULL;
+ ret = 0;
+ break;
+ case IKEV2_AUTH_ECDSA_256:
+ case IKEV2_AUTH_ECDSA_384:
+ case IKEV2_AUTH_ECDSA_521:
+ /*
+ * sigp points to concatenation r|s, while EVP_VerifyFinal()
+ * expects the signature as a DER-encoded blob (of the two
+ * values), so we need to convert the signature in a new
+ * buffer (we cannot override the given buffer) and the caller
+ * has to free this buffer ('freeme').
+ */
+ if (*lenp < 64 || *lenp > 132 || *lenp % 2)
+ goto done;
+ bnlen = (*lenp)/2;
+ /* sigp points to concatenation: r|s */
+ if ((obj = ECDSA_SIG_new()) == NULL ||
+ BN_bin2bn(*sigp, bnlen, obj->r) == NULL ||
+ BN_bin2bn(*sigp+bnlen, bnlen, obj->s) == NULL ||
+ (len = i2d_ECDSA_SIG(obj, &ptr)) == 0)
+ goto done;
+ *lenp = len;
+ *sigp = ptr;
+ *freemep = ptr;
+ ptr = NULL;
+ ret = 0;
+ break;
+ default:
+ return (0);
+ }
+ done:
+ free(ptr);
+ if (obj)
+ ECDSA_SIG_free(obj);
+ return (ret);
}
ssize_t
dsa_verify_final(struct iked_dsa *dsa, void *buf, size_t len)
{
uint8_t sig[EVP_MAX_MD_SIZE];
+ uint8_t *ptr = buf, *freeme = NULL;
unsigned int siglen = sizeof(sig);
- uint8_t *ptr = buf;
- size_t off = 0;
if (dsa->dsa_hmac) {
if (!HMAC_Final(dsa->dsa_ctx, sig, &siglen))
@@ -806,13 +975,15 @@ dsa_verify_final(struct iked_dsa *dsa, void *buf, size_t len)
if (siglen != len || memcmp(buf, sig, siglen) != 0)
return (-1);
} else {
- if ((off = _dsa_verify_offset(dsa, ptr)) >= len)
+ if (_dsa_verify_prepare(dsa, &ptr, &len, &freeme) < 0)
return (-1);
- if (EVP_VerifyFinal(dsa->dsa_ctx, ptr + off, len - off,
+ if (EVP_VerifyFinal(dsa->dsa_ctx, ptr, len,
dsa->dsa_key) != 1) {
+ free(freeme);
ca_sslerror(__func__);
return (-1);
}
+ free(freeme);
}
return (0);