> On Sun, Jul 03, 2016 at 11:07:27AM +0200, Ren?? Ammerlaan wrote:
> > I???ve created a patch for ecdsa support in iked. Also found a bug
> > in handling auth_eap, because that value is never initialised to 0. I
> > also updated the dsa sign functions with the newer EVP_Digest so
> > it???s aligned with the rest of the code, but it???s not required for
> > ecdsa support.
> >
> > The ecdsa signature should contain only plain r and s, so the
> > signature is converted to that format. I???ve tested compatibility
> > with OSX and IOS and both seem to be working fine.
> >
>
The first auth_eap diff has been committed.
I'm re-sending your remaining diff with minor changes:
- style, wrap long lines, tabs instead of spaces
- sort man page entry
There are open questions:
- why aren't the p2d_ecdsa_sig() and d2p_ecdsa_sig in ikev2_msg.c in crypto.c?
- why not using the new flexible ike signature type?
- aren't there other encoding formats?
Comments?
Reyk
Index: sbin/iked/ca.c
===================================================================
RCS file: /cvs/src/sbin/iked/ca.c,v
retrieving revision 1.40
diff -u -p -u -p -r1.40 ca.c
--- sbin/iked/ca.c 7 Dec 2015 12:46:37 -0000 1.40
+++ sbin/iked/ca.c 20 Jul 2016 12:47:38 -0000
@@ -378,6 +378,9 @@ ca_getcert(struct iked *env, struct imsg
case IKEV2_CERT_RSA_KEY:
ret = ca_validate_pubkey(env, &id, ptr, len);
break;
+ case IKEV2_CERT_ECDSA_KEY:
+ ret = ca_validate_pubkey(env, &id, ptr, len);
+ break;
default:
log_debug("%s: unsupported cert type %d", __func__, type);
ret = -1;
@@ -815,6 +818,7 @@ int
ca_pubkey_serialize(EVP_PKEY *key, struct iked_id *id)
{
RSA *rsa = NULL;
+ EC_KEY *ecdsa = NULL;
uint8_t *d;
int len = 0;
int ret = -1;
@@ -840,6 +844,26 @@ ca_pubkey_serialize(EVP_PKEY *key, struc
id->id_type = IKEV2_CERT_RSA_KEY;
break;
+ case EVP_PKEY_EC:
+ id->id_type = 0;
+ id->id_offset = 0;
+ ibuf_release(id->id_buf);
+
+ if ((ecdsa = EVP_PKEY_get1_EC_KEY(key)) == NULL)
+ goto done;
+ if ((len = i2o_ECPublicKey(ecdsa, NULL)) <= 0)
+ goto done;
+ if ((id->id_buf = ibuf_new(NULL, len)) == NULL)
+ goto done;
+
+ d = ibuf_data(id->id_buf);
+ if (i2o_ECPublicKey(ecdsa, &d) != len) {
+ ibuf_release(id->id_buf);
+ goto done;
+ }
+
+ id->id_type = IKEV2_CERT_ECDSA_KEY;
+ break;
default:
log_debug("%s: unsupported key type %d", __func__, key->type);
return (-1);
@@ -852,6 +876,8 @@ ca_pubkey_serialize(EVP_PKEY *key, struc
done:
if (rsa != NULL)
RSA_free(rsa);
+ if (ecdsa != NULL)
+ EC_KEY_free(ecdsa);
return (ret);
}
@@ -859,6 +885,7 @@ int
ca_privkey_serialize(EVP_PKEY *key, struct iked_id *id)
{
RSA *rsa = NULL;
+ EC_KEY *ecdsa = NULL;
uint8_t *d;
int len = 0;
int ret = -1;
@@ -884,6 +911,26 @@ ca_privkey_serialize(EVP_PKEY *key, stru
id->id_type = IKEV2_CERT_RSA_KEY;
break;
+ case EVP_PKEY_EC:
+ id->id_type = 0;
+ id->id_offset = 0;
+ ibuf_release(id->id_buf);
+
+ if ((ecdsa = EVP_PKEY_get1_EC_KEY(key)) == NULL)
+ goto done;
+ if ((len = i2d_ECPrivateKey(ecdsa, NULL)) <= 0)
+ goto done;
+ if ((id->id_buf = ibuf_new(NULL, len)) == NULL)
+ goto done;
+
+ d = ibuf_data(id->id_buf);
+ if (i2d_ECPrivateKey(ecdsa, &d) != len) {
+ ibuf_release(id->id_buf);
+ goto done;
+ }
+
+ id->id_type = IKEV2_CERT_ECDSA_KEY;
+ break;
default:
log_debug("%s: unsupported key type %d", __func__, key->type);
return (-1);
@@ -896,6 +943,8 @@ ca_privkey_serialize(EVP_PKEY *key, stru
done:
if (rsa != NULL)
RSA_free(rsa);
+ if (ecdsa != NULL)
+ EC_KEY_free(ecdsa);
return (ret);
}
@@ -1029,6 +1078,7 @@ ca_validate_pubkey(struct iked *env, str
{
BIO *rawcert = NULL;
RSA *peerrsa = NULL, *localrsa = NULL;
+ EC_KEY *peerecdsa = NULL, *localecdsa = NULL;
EVP_PKEY *peerkey = NULL, *localkey = NULL;
int ret = -1;
FILE *fp = NULL;
@@ -1066,11 +1116,17 @@ ca_validate_pubkey(struct iked *env, str
if ((rawcert = BIO_new_mem_buf(data, len)) == NULL)
goto done;
- if ((peerrsa = d2i_RSAPublicKey_bio(rawcert, NULL)) == NULL)
- goto sslerr;
if ((peerkey = EVP_PKEY_new()) == NULL)
goto sslerr;
- if (!EVP_PKEY_set1_RSA(peerkey, peerrsa))
+
+ if ((peerrsa = d2i_RSAPublicKey_bio(rawcert, NULL)) != NULL) {
+ if (!EVP_PKEY_set1_RSA(peerkey, peerrsa))
+ goto sslerr;
+ } else if ((peerecdsa =
+ d2i_EC_PUBKEY_bio(rawcert, NULL)) != NULL) {
+ if (!EVP_PKEY_set1_EC_KEY(peerkey, peerecdsa))
+ goto sslerr;
+ } else
goto sslerr;
}
@@ -1086,12 +1142,17 @@ ca_validate_pubkey(struct iked *env, str
/* reading PKCS #8 failed, try PEM */
rewind(fp);
localrsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
+ localecdsa = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL);
fclose(fp);
- if (localrsa == NULL)
- goto sslerr;
if ((localkey = EVP_PKEY_new()) == NULL)
goto sslerr;
- if (!EVP_PKEY_set1_RSA(localkey, localrsa))
+ if (localrsa != NULL) {
+ if (!EVP_PKEY_set1_RSA(localkey, localrsa))
+ goto sslerr;
+ } else if (localecdsa != NULL) {
+ if (!EVP_PKEY_set1_EC_KEY(localkey, localecdsa))
+ goto sslerr;
+ } else
goto sslerr;
} else {
fclose(fp);
@@ -1116,8 +1177,12 @@ ca_validate_pubkey(struct iked *env, str
EVP_PKEY_free(localkey);
if (peerrsa != NULL)
RSA_free(peerrsa);
+ if (peerecdsa != NULL)
+ EC_KEY_free(peerecdsa);
if (localrsa != NULL)
RSA_free(localrsa);
+ if (localecdsa != NULL)
+ EC_KEY_free(localecdsa);
if (rawcert != NULL)
BIO_free(rawcert);
Index: sbin/iked/crypto.c
===================================================================
RCS file: /cvs/src/sbin/iked/crypto.c,v
retrieving revision 1.19
diff -u -p -u -p -r1.19 crypto.c
--- sbin/iked/crypto.c 31 Oct 2015 19:28:19 -0000 1.19
+++ sbin/iked/crypto.c 20 Jul 2016 12:48:26 -0000
@@ -573,6 +573,7 @@ dsa_setkey(struct iked_dsa *dsa, void *k
BIO *rawcert = NULL;
X509 *cert = NULL;
RSA *rsa = NULL;
+ EC_KEY *ecdsa = NULL;
EVP_PKEY *pkey = NULL;
ibuf_release(dsa->dsa_keydata);
@@ -613,6 +614,26 @@ dsa_setkey(struct iked_dsa *dsa, void *k
dsa->dsa_cert = NULL;
dsa->dsa_key = pkey;
break;
+ case IKEV2_CERT_ECDSA_KEY:
+ if (dsa->dsa_sign) {
+ if ((ecdsa = d2i_ECPrivateKey_bio(rawcert,
+ NULL)) == NULL)
+ goto sslerr;
+ } else {
+ if ((ecdsa = d2i_EC_PUBKEY_bio(rawcert,
+ NULL)) == NULL)
+ goto sslerr;
+ }
+
+ if ((pkey = EVP_PKEY_new()) == NULL)
+ goto sslerr;
+ if (!EVP_PKEY_set1_EC_KEY(pkey, ecdsa))
+ goto sslerr;
+
+ EC_KEY_free(ecdsa); /* pkey now has the reference */
+ dsa->dsa_cert = NULL;
+ dsa->dsa_key = pkey;
+ break;
default:
if (dsa->dsa_hmac)
break;
@@ -629,6 +650,8 @@ dsa_setkey(struct iked_dsa *dsa, void *k
if (rsa != NULL)
RSA_free(rsa);
+ if (ecdsa != NULL)
+ EC_KEY_free(ecdsa);
if (pkey != NULL)
EVP_PKEY_free(pkey);
if (cert != NULL)
@@ -698,11 +721,13 @@ dsa_init(struct iked_dsa *dsa, const voi
}
if (dsa->dsa_sign)
- ret = EVP_SignInit_ex(dsa->dsa_ctx, dsa->dsa_priv, NULL);
+ ret = EVP_DigestSignInit(dsa->dsa_ctx, NULL, dsa->dsa_priv,
+ NULL, dsa->dsa_key);
else {
if ((ret = _dsa_verify_init(dsa, buf, len)) != 0)
return (ret);
- ret = EVP_VerifyInit_ex(dsa->dsa_ctx, dsa->dsa_priv, NULL);
+ ret = EVP_DigestVerifyInit(dsa->dsa_ctx, NULL, dsa->dsa_priv,
+ NULL, dsa->dsa_key);
}
return (ret ? 0 : -1);
@@ -712,13 +737,12 @@ int
dsa_update(struct iked_dsa *dsa, const void *buf, size_t len)
{
int ret = 1;
-
if (dsa->dsa_hmac)
ret = HMAC_Update(dsa->dsa_ctx, buf, len);
else if (dsa->dsa_sign)
- ret = EVP_SignUpdate(dsa->dsa_ctx, buf, len);
+ ret = EVP_DigestSignUpdate(dsa->dsa_ctx, buf, len);
else
- ret = EVP_VerifyUpdate(dsa->dsa_ctx, buf, len);
+ ret = EVP_DigestVerifyUpdate(dsa->dsa_ctx, buf, len);
return (ret ? 0 : -1);
}
@@ -758,21 +782,21 @@ dsa_length(struct iked_dsa *dsa)
ssize_t
dsa_sign_final(struct iked_dsa *dsa, void *buf, size_t len)
{
- unsigned int siglen;
- size_t off = 0;
+ unsigned int usiglen;
+ size_t siglen, off = 0;
uint8_t *ptr = buf;
if (len < dsa_length(dsa))
return (-1);
if (dsa->dsa_hmac) {
- if (!HMAC_Final(dsa->dsa_ctx, buf, &siglen))
+ if (!HMAC_Final(dsa->dsa_ctx, buf, &usiglen))
return (-1);
+ return (usiglen);
} else {
if (_dsa_sign_encode(dsa, ptr, &off) < 0)
return (-1);
- if (!EVP_SignFinal(dsa->dsa_ctx, ptr + off, &siglen,
- dsa->dsa_key))
+ if (!EVP_DigestSignFinal(dsa->dsa_ctx, ptr + off, &siglen))
return (-1);
siglen += off;
}
@@ -808,12 +832,79 @@ dsa_verify_final(struct iked_dsa *dsa, v
} else {
if ((off = _dsa_verify_offset(dsa, ptr)) >= len)
return (-1);
- if (EVP_VerifyFinal(dsa->dsa_ctx, ptr + off, len - off,
- dsa->dsa_key) != 1) {
+ if (EVP_DigestVerifyFinal(dsa->dsa_ctx, ptr +off,
+ len - off) != 1) {
ca_sslerror(__func__);
return (-1);
}
}
+ return (0);
+}
+
+size_t
+d2p_ecdsa_sig(void *buf, size_t len)
+{
+ ECDSA_SIG *ecdsa_sig = NULL;
+ uint8_t *ptr = buf;
+ const uint8_t *cptr = ptr;
+ size_t rlen, slen, rslen, siglen;
+
+ ecdsa_sig = ECDSA_SIG_new();
+ if (!ecdsa_sig)
+ return (0);
+
+ if (!d2i_ECDSA_SIG(&ecdsa_sig, &cptr, len))
+ goto err;
+
+ rlen = BN_num_bytes(ecdsa_sig->r);
+ slen = BN_num_bytes(ecdsa_sig->s);
+ if ((rslen = rlen > slen ? rlen : slen) >= len)
+ goto err;
+
+ siglen = (rslen * 2);
+ bzero(ptr, len);
+ if (BN_bn2bin(ecdsa_sig->r, ptr + rslen - rlen) != (ssize_t)rlen)
+ goto err;
+ if (BN_bn2bin(ecdsa_sig->s, ptr + siglen - slen) != (ssize_t)slen)
+ goto err;
+
+ ECDSA_SIG_free(ecdsa_sig);
+ return (siglen);
+err:
+ if (ecdsa_sig)
+ ECDSA_SIG_free(ecdsa_sig);
+ return (0);
+}
+
+size_t
+p2d_ecdsa_sig(void *buf, size_t len)
+{
+ ECDSA_SIG *ecdsa_sig;
+ size_t rslen, siglen;
+ uint8_t *ptr = buf;
+
+ if ((len % 2) != 0)
+ return (0);
+
+ ecdsa_sig = ECDSA_SIG_new();
+ if (!ecdsa_sig)
+ return (0);
+
+ rslen = (len / 2);
+ ecdsa_sig->r = BN_bin2bn(ptr, rslen, ecdsa_sig->r);
+ ecdsa_sig->s = BN_bin2bn(ptr + rslen, rslen, ecdsa_sig->s);
+ if (!ecdsa_sig->r || !ecdsa_sig->s)
+ goto err;
+
+ bzero(ptr, len);
+ if ((siglen = i2d_ECDSA_SIG(ecdsa_sig, &ptr)) == 0)
+ goto err;
+
+ ECDSA_SIG_free(ecdsa_sig);
+ return (siglen);
+ err:
+ if (ecdsa_sig)
+ ECDSA_SIG_free(ecdsa_sig);
return (0);
}
Index: sbin/iked/iked.8
===================================================================
RCS file: /cvs/src/sbin/iked/iked.8,v
retrieving revision 1.19
diff -u -p -u -p -r1.19 iked.8
--- sbin/iked/iked.8 10 Nov 2014 13:57:32 -0000 1.19
+++ sbin/iked/iked.8 20 Jul 2016 12:48:26 -0000
@@ -44,7 +44,7 @@ is provided by
.Xr isakmpd 8 .
.Pp
.Nm
-supports mutual authentication using RSA public keys and X.509 certificates.
+supports mutual authentication using public keys and X.509 certificates.
See the
.Sx PUBLIC KEY AUTHENTICATION
section below and PKI AND CERTIFICATE AUTHORITY COMMANDS in
Index: sbin/iked/iked.conf.5
===================================================================
RCS file: /cvs/src/sbin/iked/iked.conf.5,v
retrieving revision 1.44
diff -u -p -u -p -r1.44 iked.conf.5
--- sbin/iked/iked.conf.5 9 Dec 2015 21:41:49 -0000 1.44
+++ sbin/iked/iked.conf.5 20 Jul 2016 12:48:27 -0000
@@ -471,7 +471,7 @@ Please note that rekeying must happen at
IPsec security heavily depends on the frequent key renewals.
.It Op Ar ikeauth
Specify the mode to mutually authenticate the peers.
-Non-psk modes will require to set up certificates and RSA public keys;
+Non-psk modes will require to set up certificates and public keys;
see
.Xr iked 8
for more information.
@@ -483,7 +483,11 @@ The only supported EAP
.Ar type
is currently
.Ar MSCHAP-V2 .
-The responder will use RSA public key authentication.
+The responder will use public key authentication.
+.It Ic ecdsa-256
+.It Ic ecdsa-384
+.It Ic ecdsa-521
+Use ECDSA public key authentication.
.It Ic psk Ar string
Use a pre-shared key
.Ar string
Index: sbin/iked/iked.h
===================================================================
RCS file: /cvs/src/sbin/iked/iked.h,v
retrieving revision 1.96
diff -u -p -u -p -r1.96 iked.h
--- sbin/iked/iked.h 1 Jun 2016 11:16:41 -0000 1.96
+++ sbin/iked/iked.h 20 Jul 2016 12:48:27 -0000
@@ -744,6 +744,8 @@ size_t dsa_length(struct iked_dsa *);
int dsa_update(struct iked_dsa *, const void *, size_t);
ssize_t dsa_sign_final(struct iked_dsa *, void *, size_t);
ssize_t dsa_verify_final(struct iked_dsa *, void *, size_t);
+size_t d2p_ecdsa_sig(void *, size_t);
+size_t p2d_ecdsa_sig(void *, size_t);
/* ikev2.c */
pid_t ikev2(struct privsep *, struct privsep_proc *);
Index: sbin/iked/ikev2.h
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2.h,v
retrieving revision 1.23
diff -u -p -u -p -r1.23 ikev2.h
--- sbin/iked/ikev2.h 31 Oct 2015 19:28:19 -0000 1.23
+++ sbin/iked/ikev2.h 20 Jul 2016 12:48:28 -0000
@@ -411,6 +411,7 @@ struct ikev2_cert {
#define IKEV2_CERT_HASHURL_X509 12 /* RFC4306 */
#define IKEV2_CERT_HASHURL_X509_BUNDLE 13 /* RFC4306 */
#define IKEV2_CERT_OCSP 14 /* RFC4806 */
+#define IKEV2_CERT_ECDSA_KEY 201 /* RFC4754 */
extern struct iked_constmap ikev2_cert_map[];
Index: sbin/iked/ikev2_msg.c
===================================================================
RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v
retrieving revision 1.45
diff -u -p -u -p -r1.45 ikev2_msg.c
--- sbin/iked/ikev2_msg.c 19 Oct 2015 11:25:35 -0000 1.45
+++ sbin/iked/ikev2_msg.c 20 Jul 2016 12:48:28 -0000
@@ -786,6 +786,16 @@ ikev2_msg_authverify(struct iked *env, s
goto done;
}
+ if ((dsa->dsa_method == IKEV2_AUTH_ECDSA_256) ||
+ (dsa->dsa_method == IKEV2_AUTH_ECDSA_384) ||
+ (dsa->dsa_method == IKEV2_AUTH_ECDSA_521)) {
+ if ((len = p2d_ecdsa_sig(buf, len)) == 0) {
+ log_debug("%s: failed to convert ecdsa signature"
+ " to der format", __func__);
+ goto done;
+ }
+ }
+
if ((ret = dsa_verify_final(dsa, buf, len)) == 0) {
log_debug("%s: authentication successful", __func__);
sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS);
@@ -872,9 +882,20 @@ ikev2_msg_authsign(struct iked *env, str
goto done;
}
+ if ((dsa->dsa_method == IKEV2_AUTH_ECDSA_256) ||
+ (dsa->dsa_method == IKEV2_AUTH_ECDSA_384) ||
+ (dsa->dsa_method == IKEV2_AUTH_ECDSA_521)) {
+ if ((buf->wpos =
+ d2p_ecdsa_sig(ibuf_data(buf), ibuf_size(buf))) == 0) {
+ log_debug("%s: failed to convert ecdsa signature"
+ " to plain format", __func__);
+ ibuf_release(buf);
+ goto done;
+ }
+ }
+
sa->sa_localauth.id_type = auth->auth_method;
sa->sa_localauth.id_buf = buf;
-
ret = 0;
done:
free(psk);
Index: sbin/iked/parse.y
===================================================================
RCS file: /cvs/src/sbin/iked/parse.y,v
retrieving revision 1.56
diff -u -p -u -p -r1.56 parse.y
--- sbin/iked/parse.y 20 Jul 2016 12:31:00 -0000 1.56
+++ sbin/iked/parse.y 20 Jul 2016 12:48:29 -0000
@@ -382,7 +382,7 @@ typedef struct {
%token PASSIVE ACTIVE ANY TAG TAP PROTO LOCAL GROUP NAME CONFIG EAP USER
%token IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET
%token INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT
-%token IPCOMP OCSP IKELIFETIME
+%token IPCOMP OCSP IKELIFETIME ECDSA_256 ECDSA_384 ECDSA_521
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.string> string
@@ -819,6 +819,21 @@ ikeauth : /* empty */ {
$$.auth_eap = 0;
$$.auth_length = 0;
}
+ | ECDSA_256 {
+ $$.auth_method = IKEV2_AUTH_ECDSA_256;
+ $$.auth_eap = 0;
+ $$.auth_length = 0;
+ }
+ | ECDSA_384 {
+ $$.auth_method = IKEV2_AUTH_ECDSA_384;
+ $$.auth_eap = 0;
+ $$.auth_length = 0;
+ }
+ | ECDSA_521 {
+ $$.auth_method = IKEV2_AUTH_ECDSA_521;
+ $$.auth_eap = 0;
+ $$.auth_length = 0;
+ }
| PSK keyspec {
memcpy(&$$, &$2, sizeof($$));
$$.auth_method = IKEV2_AUTH_SHARED_KEY_MIC;
@@ -1097,6 +1112,9 @@ lookup(char *s)
{ "default", DEFAULT },
{ "dstid", DSTID },
{ "eap", EAP },
+ { "ecdsa-256", ECDSA_256 },
+ { "ecdsa-384", ECDSA_384 },
+ { "ecdsa-521", ECDSA_521 },
{ "enc", ENCXF },
{ "esp", ESP },
{ "file", FILENAME },