diff options
author | Yves-Alexis Perez <corsac@corsac.net> | 2012-06-28 21:16:07 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@corsac.net> | 2012-06-28 21:16:07 +0200 |
commit | b34738ed08c2227300d554b139e2495ca5da97d6 (patch) | |
tree | 62f33b52820f2e49f0e53c0f8c636312037c8054 /src/libstrongswan/plugins/pkcs11/pkcs11_dh.c | |
parent | 0a9d51a49042a68daa15b0c74a2b7f152f52606b (diff) | |
download | vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.tar.gz vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.zip |
Imported Upstream version 4.6.4
Diffstat (limited to 'src/libstrongswan/plugins/pkcs11/pkcs11_dh.c')
-rw-r--r-- | src/libstrongswan/plugins/pkcs11/pkcs11_dh.c | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/pkcs11/pkcs11_dh.c b/src/libstrongswan/plugins/pkcs11/pkcs11_dh.c new file mode 100644 index 000000000..c870370c8 --- /dev/null +++ b/src/libstrongswan/plugins/pkcs11/pkcs11_dh.c @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2011 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pkcs11_dh.h" + +#include <debug.h> +#include <library.h> +#include <asn1/asn1.h> +#include <asn1/oid.h> + +#include "pkcs11_manager.h" + +typedef struct private_pkcs11_dh_t private_pkcs11_dh_t; + +/** + * Private data of an pkcs11_dh_t object. + */ +struct private_pkcs11_dh_t { + + /** + * Public pkcs11_dh_t interface + */ + pkcs11_dh_t public; + + /** + * PKCS#11 library + */ + pkcs11_library_t *lib; + + /** + * Session handle for this objct + */ + CK_SESSION_HANDLE session; + + /** + * Diffie Hellman group number. + */ + u_int16_t group; + + /** + * Handle for own private value + */ + CK_OBJECT_HANDLE pri_key; + + /** + * Own public value + */ + chunk_t pub_key; + + /** + * Shared secret + */ + chunk_t secret; + + /** + * Mechanism to use to generate a key pair + */ + CK_MECHANISM_TYPE mech_key; + + /** + * Mechanism to use to derive a shared secret + */ + CK_MECHANISM_TYPE mech_derive; + +}; + +/** + * Derive a DH/ECDH shared secret. + * + * If this succeeds the shared secret is stored in this->secret. + */ +static void derive_secret(private_pkcs11_dh_t *this, chunk_t other) +{ + CK_OBJECT_CLASS klass = CKO_SECRET_KEY; + CK_KEY_TYPE type = CKK_GENERIC_SECRET; + CK_ATTRIBUTE attr[] = { + { CKA_CLASS, &klass, sizeof(klass) }, + { CKA_KEY_TYPE, &type, sizeof(type) }, + }; + CK_MECHANISM mech = { + this->mech_derive, + other.ptr, + other.len, + }; + CK_OBJECT_HANDLE secret; + CK_RV rv; + + rv = this->lib->f->C_DeriveKey(this->session, &mech, this->pri_key, + attr, countof(attr), &secret); + if (rv != CKR_OK) + { + DBG1(DBG_CFG, "C_DeriveKey() error: %N", ck_rv_names, rv); + return; + } + if (!this->lib->get_ck_attribute(this->lib, this->session, secret, + CKA_VALUE, &this->secret)) + { + chunk_free(&this->secret); + return; + } +} + +METHOD(diffie_hellman_t, set_other_public_value, void, + private_pkcs11_dh_t *this, chunk_t value) +{ + switch (this->group) + { + case ECP_192_BIT: + case ECP_224_BIT: + case ECP_256_BIT: + case ECP_384_BIT: + case ECP_521_BIT: + { /* we expect the public value to just be the concatenated x and y + * coordinates, so we tag the value as an uncompressed ECPoint */ + chunk_t tag = chunk_from_chars(0x04); + chunk_t pubkey = chunk_cata("cc", tag, value); + CK_ECDH1_DERIVE_PARAMS params = { + CKD_NULL, + 0, + NULL, + pubkey.len, + pubkey.ptr, + }; + + if (!lib->settings->get_bool(lib->settings, + "libstrongswan.ecp_x_coordinate_only", TRUE)) + { /* we only get the x coordinate back */ + return; + } + value = chunk_from_thing(params); + break; + } + default: + break; + } + derive_secret(this, value); +} + +METHOD(diffie_hellman_t, get_my_public_value, void, + private_pkcs11_dh_t *this, chunk_t *value) +{ + *value = chunk_clone(this->pub_key); +} + +METHOD(diffie_hellman_t, get_shared_secret, status_t, + private_pkcs11_dh_t *this, chunk_t *secret) +{ + if (!this->secret.ptr) + { + return FAILED; + } + *secret = chunk_clone(this->secret); + return SUCCESS; +} + +METHOD(diffie_hellman_t, get_dh_group, diffie_hellman_group_t, + private_pkcs11_dh_t *this) +{ + return this->group; +} + +METHOD(diffie_hellman_t, destroy, void, + private_pkcs11_dh_t *this) +{ + this->lib->f->C_CloseSession(this->session); + chunk_clear(&this->pub_key); + chunk_clear(&this->secret); + free(this); +} + +/** + * Generate a DH/ECDH key pair. + * + * If this succeeds, this->pri_key has a handle to the private key and + * this->pub_key stores the public key. + */ +static bool generate_key_pair(private_pkcs11_dh_t *this, CK_ATTRIBUTE_PTR pub, + int pub_len, CK_ATTRIBUTE_PTR pri, int pri_len, + CK_ATTRIBUTE_TYPE attr) +{ + CK_MECHANISM mech = { + this->mech_key, + NULL, + 0, + }; + CK_OBJECT_HANDLE pub_key; + CK_RV rv; + + rv = this->lib->f->C_GenerateKeyPair(this->session, &mech, pub, pub_len, + pri, pri_len, &pub_key, &this->pri_key); + if (rv != CKR_OK) + { + DBG1(DBG_CFG, "C_GenerateKeyPair() error: %N", ck_rv_names, rv); + return FALSE; + } + if (!this->lib->get_ck_attribute(this->lib, this->session, pub_key, + attr, &this->pub_key)) + { + chunk_free(&this->pub_key); + return FALSE; + } + return TRUE; +} + +/** + * Generate DH key pair. + */ +static bool generate_key_pair_modp(private_pkcs11_dh_t *this, size_t exp_len, + chunk_t g, chunk_t p) +{ + CK_BBOOL ck_true = CK_TRUE; + CK_ATTRIBUTE pub_attr[] = { + { CKA_DERIVE, &ck_true, sizeof(ck_true) }, + { CKA_PRIME, p.ptr, p.len }, + { CKA_BASE, g.ptr, g.len }, + }; + CK_ULONG bits = exp_len * 8; + CK_ATTRIBUTE pri_attr[] = { + { CKA_DERIVE, &ck_true, sizeof(ck_true) }, + { CKA_VALUE_BITS, &bits, sizeof(bits) }, + }; + return generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr, + countof(pri_attr), CKA_VALUE); +} + +/** + * Generate ECDH key pair. + */ +static bool generate_key_pair_ecp(private_pkcs11_dh_t *this, + chunk_t ecparams) +{ + CK_BBOOL ck_true = CK_TRUE; + CK_ATTRIBUTE pub_attr[] = { + { CKA_DERIVE, &ck_true, sizeof(ck_true) }, + { CKA_EC_PARAMS, ecparams.ptr, ecparams.len }, + }; + CK_ATTRIBUTE pri_attr[] = { + { CKA_DERIVE, &ck_true, sizeof(ck_true) }, + }; + chunk_t pub_key; + if (!generate_key_pair(this, pub_attr, countof(pub_attr), pri_attr, + countof(pri_attr), CKA_EC_POINT)) + { + return FALSE; + } + if (this->pub_key.len <= 0 || this->pub_key.ptr[0] != 0x04) + { /* we currently only support the point in uncompressed form which + * looks like this: 0x04 || x || y */ + chunk_clear(&this->pub_key); + return FALSE; + } + pub_key = chunk_clone(chunk_skip(this->pub_key, 1)); + chunk_clear(&this->pub_key); + this->pub_key = pub_key; + return TRUE; +} + +/** + * Find a token we can use for DH/ECDH algorithm + */ +static pkcs11_library_t *find_token(private_pkcs11_dh_t *this, + CK_SESSION_HANDLE *session) +{ + enumerator_t *tokens, *mechs; + pkcs11_manager_t *manager; + pkcs11_library_t *current, *found = NULL; + CK_MECHANISM_TYPE type; + CK_SLOT_ID slot; + + manager = lib->get(lib, "pkcs11-manager"); + if (!manager) + { + return NULL; + } + tokens = manager->create_token_enumerator(manager); + while (tokens->enumerate(tokens, ¤t, &slot)) + { + mechs = current->create_mechanism_enumerator(current, slot); + while (mechs->enumerate(mechs, &type, NULL)) + { /* we assume we can generate key pairs if the derive mechanism + * is supported */ + if (type == this->mech_derive) + { + if (current->f->C_OpenSession(slot, CKF_SERIAL_SESSION, + NULL, NULL, session) == CKR_OK) + { + found = current; + break; + } + } + } + mechs->destroy(mechs); + if (found) + { + break; + } + } + tokens->destroy(tokens); + return found; +} + +/** + * Generic internal constructor + */ +static private_pkcs11_dh_t *create_generic(diffie_hellman_group_t group, + CK_MECHANISM_TYPE key, + CK_MECHANISM_TYPE derive) +{ + private_pkcs11_dh_t *this; + + INIT(this, + .public = { + .dh = { + .get_shared_secret = _get_shared_secret, + .set_other_public_value = _set_other_public_value, + .get_my_public_value = _get_my_public_value, + .get_dh_group = _get_dh_group, + .destroy = _destroy, + }, + }, + .group = group, + .mech_key = key, + .mech_derive = derive, + ); + + this->lib = find_token(this, &this->session); + if (!this->lib) + { + free(this); + return NULL; + } + return this; +} + +static pkcs11_dh_t *create_ecp(diffie_hellman_group_t group, chunk_t ecparam) +{ + private_pkcs11_dh_t *this = create_generic(group, CKM_EC_KEY_PAIR_GEN, + CKM_ECDH1_DERIVE); + + if (this) + { + if (generate_key_pair_ecp(this, ecparam)) + { + chunk_free(&ecparam); + return &this->public; + } + chunk_free(&ecparam); + free(this); + } + return NULL; +} + +/** + * Constructor for MODP DH + */ +static pkcs11_dh_t *create_modp(diffie_hellman_group_t group, size_t exp_len, + chunk_t g, chunk_t p) +{ + private_pkcs11_dh_t *this = create_generic(group, CKM_DH_PKCS_KEY_PAIR_GEN, + CKM_DH_PKCS_DERIVE); + + if (this) + { + if (generate_key_pair_modp(this, exp_len, g, p)) + { + return &this->public; + } + free(this); + } + return NULL; +} + +/** + * Lookup the EC params for the given group. + */ +static chunk_t ecparams_lookup(diffie_hellman_group_t group) +{ + switch (group) + { + case ECP_192_BIT: + return asn1_build_known_oid(OID_PRIME192V1); + case ECP_224_BIT: + return asn1_build_known_oid(OID_SECT224R1); + case ECP_256_BIT: + return asn1_build_known_oid(OID_PRIME256V1); + case ECP_384_BIT: + return asn1_build_known_oid(OID_SECT384R1); + case ECP_521_BIT: + return asn1_build_known_oid(OID_SECT521R1); + default: + break; + } + return chunk_empty; +} + +/** + * Described in header. + */ +pkcs11_dh_t *pkcs11_dh_create(diffie_hellman_group_t group, + chunk_t g, chunk_t p) +{ + switch (group) + { + case MODP_CUSTOM: + { + return create_modp(group, p.len, g, p); + } + case ECP_192_BIT: + case ECP_224_BIT: + case ECP_256_BIT: + case ECP_384_BIT: + case ECP_521_BIT: + { + chunk_t params = ecparams_lookup(group); + if (params.ptr) + { + return create_ecp(group, params); + } + break; + } + default: + { + diffie_hellman_params_t *params = diffie_hellman_get_params(group); + if (params) + { + return create_modp(group, params->exp_len, params->generator, + params->prime); + } + break; + } + } + return NULL; +} + |