aboutsummaryrefslogtreecommitdiffstats
path: root/src/libstrongswan
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2011-11-02 18:59:48 +0100
committerTobias Brunner <tobias@strongswan.org>2011-11-02 20:27:55 +0100
commit36d1627f6e43c9f4787907a58ab3e28ed323a6d5 (patch)
treec1df886592b2588cd08f14b62801b318cab700c5 /src/libstrongswan
parent574261163ffecfdd33c96c160e468450acae58ab (diff)
downloadstrongswan-36d1627f6e43c9f4787907a58ab3e28ed323a6d5.tar.bz2
strongswan-36d1627f6e43c9f4787907a58ab3e28ed323a6d5.tar.xz
pkcs11: Parse ECDSA public keys and find/create them on tokens.
Diffstat (limited to 'src/libstrongswan')
-rw-r--r--src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c179
1 files changed, 177 insertions, 2 deletions
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c
index 4a79cda60..d878c8f7a 100644
--- a/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c
+++ b/src/libstrongswan/plugins/pkcs11/pkcs11_public_key.c
@@ -22,6 +22,9 @@
#include "pkcs11_private_key.h"
#include "pkcs11_manager.h"
+#include <asn1/oid.h>
+#include <asn1/asn1.h>
+#include <asn1/asn1_parser.h>
#include <debug.h>
typedef struct private_pkcs11_public_key_t private_pkcs11_public_key_t;
@@ -72,6 +75,116 @@ struct private_pkcs11_public_key_t {
refcount_t ref;
};
+/**
+ * Helper function that returns the base point order length in bits of the
+ * given named curve.
+ *
+ * Currently only a subset of defined curves is supported (namely the 5 curves
+ * over Fp recommended by NIST). IKEv2 only supports 3 out of these.
+ *
+ * 0 is returned if the given curve is not supported.
+ */
+static size_t basepoint_order_len(int oid)
+{
+ switch (oid)
+ {
+ case OID_PRIME192V1:
+ return 192;
+ case OID_SECT224R1:
+ return 224;
+ case OID_PRIME256V1:
+ return 256;
+ case OID_SECT384R1:
+ return 384;
+ case OID_SECT521R1:
+ return 521;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Parses the given ecParameters (ASN.1) and returns the key length.
+ */
+static bool keylen_from_ecparams(chunk_t ecparams, size_t *keylen)
+{
+ if (!asn1_parse_simple_object(&ecparams, ASN1_OID, 0, "named curve"))
+ {
+ return FALSE;
+ }
+ *keylen = basepoint_order_len(asn1_known_oid(ecparams));
+ return *keylen > 0;
+}
+
+/**
+ * ASN.1 definition of a subjectPublicKeyInfo structure when used with ECDSA
+ * we currently only support named curves.
+ */
+static const asn1Object_t pkinfoObjects[] = {
+ { 0, "subjectPublicKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
+ { 1, "algorithmIdentifier", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */
+ { 2, "algorithm", ASN1_OID, ASN1_BODY }, /* 2 */
+ { 2, "namedCurve", ASN1_OID, ASN1_RAW }, /* 3 */
+ { 1, "subjectPublicKey", ASN1_BIT_STRING, ASN1_BODY }, /* 4 */
+ { 0, "exit", ASN1_EOC, ASN1_EXIT }
+};
+#define PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM 2
+#define PKINFO_SUBJECT_PUBLIC_KEY_NAMEDCURVE 3
+#define PKINFO_SUBJECT_PUBLIC_KEY 4
+
+/**
+ * Extract the DER encoded Parameters and ECPoint from the given DER encoded
+ * subjectPublicKeyInfo.
+ */
+static bool parse_ecdsa_public_key(chunk_t blob, chunk_t *ecparams,
+ chunk_t *ecpoint, size_t *keylen)
+{
+ asn1_parser_t *parser;
+ chunk_t object;
+ int objectID;
+ bool success = FALSE;
+
+ parser = asn1_parser_create(pkinfoObjects, blob);
+
+ while (parser->iterate(parser, &objectID, &object))
+ {
+ switch (objectID)
+ {
+ case PKINFO_SUBJECT_PUBLIC_KEY_ALGORITHM:
+ {
+ if (asn1_known_oid(object) != OID_EC_PUBLICKEY)
+ {
+ goto end;
+ }
+ break;
+ }
+ case PKINFO_SUBJECT_PUBLIC_KEY_NAMEDCURVE:
+ {
+ *ecparams = object;
+ if (!keylen_from_ecparams(object, keylen))
+ {
+ goto end;
+ }
+ break;
+ }
+ case PKINFO_SUBJECT_PUBLIC_KEY:
+ {
+ if (object.len > 0 && *object.ptr == 0x00)
+ { /* skip initial bit string octet defining 0 unused bits */
+ object = chunk_skip(object, 1);
+ }
+ *ecpoint = object;
+ break;
+ }
+ }
+ }
+ success = parser->success(parser);
+end:
+ parser->destroy(parser);
+ return success;
+}
+
+
METHOD(public_key_t, get_type, key_type_t,
private_pkcs11_public_key_t *this)
{
@@ -352,6 +465,24 @@ static private_pkcs11_public_key_t* find_rsa_key(chunk_t n, chunk_t e,
}
/**
+ * Find an ECDSA key object
+ */
+static private_pkcs11_public_key_t* find_ecdsa_key(chunk_t ecparams,
+ chunk_t ecpoint,
+ size_t keylen)
+{
+ CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE type = CKK_ECDSA;
+ CK_ATTRIBUTE tmpl[] = {
+ {CKA_CLASS, &class, sizeof(class)},
+ {CKA_KEY_TYPE, &type, sizeof(type)},
+ {CKA_EC_PARAMS, ecparams.ptr, ecparams.len},
+ {CKA_EC_POINT, ecpoint.ptr, ecpoint.len},
+ };
+ return find_key(KEY_ECDSA, keylen, tmpl, countof(tmpl));
+}
+
+/**
* Create a key object in a suitable token session
*/
static private_pkcs11_public_key_t* create_key(key_type_t type, size_t keylen,
@@ -461,19 +592,45 @@ static private_pkcs11_public_key_t* create_rsa_key(chunk_t n, chunk_t e,
}
/**
+ * Create an ECDSA key object in a suitable token session
+ */
+static private_pkcs11_public_key_t* create_ecdsa_key(chunk_t ecparams,
+ chunk_t ecpoint,
+ size_t keylen)
+{
+ CK_OBJECT_CLASS class = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE type = CKK_ECDSA;
+ CK_ATTRIBUTE tmpl[] = {
+ {CKA_CLASS, &class, sizeof(class)},
+ {CKA_KEY_TYPE, &type, sizeof(type)},
+ {CKA_EC_PARAMS, ecparams.ptr, ecparams.len},
+ {CKA_EC_POINT, ecpoint.ptr, ecpoint.len},
+ };
+ CK_MECHANISM_TYPE mechs[] = {
+ CKM_ECDSA,
+ CKM_ECDSA_SHA1,
+ };
+ return create_key(KEY_ECDSA, keylen, mechs,
+ countof(mechs), tmpl, countof(tmpl));
+}
+
+/**
* See header
*/
pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args)
{
private_pkcs11_public_key_t *this;
- chunk_t n, e;
+ chunk_t n, e, blob;
size_t keylen;
- n = e = chunk_empty;
+ n = e = blob = chunk_empty;
while (TRUE)
{
switch (va_arg(args, builder_part_t))
{
+ case BUILD_BLOB_ASN1_DER:
+ blob = va_arg(args, chunk_t);
+ continue;
case BUILD_RSA_MODULUS:
n = va_arg(args, chunk_t);
continue;
@@ -505,6 +662,24 @@ pkcs11_public_key_t *pkcs11_public_key_load(key_type_t type, va_list args)
return &this->public;
}
}
+ else if (type == KEY_ECDSA && blob.ptr)
+ {
+ chunk_t ecparams, ecpoint;
+ ecparams = ecpoint = chunk_empty;
+ if (parse_ecdsa_public_key(blob, &ecparams, &ecpoint, &keylen))
+ {
+ this = find_ecdsa_key(ecparams, ecpoint, keylen);
+ if (this)
+ {
+ return &this->public;
+ }
+ this = create_ecdsa_key(ecparams, ecpoint, keylen);
+ if (this)
+ {
+ return &this->public;
+ }
+ }
+ }
return NULL;
}