From: Tobias Brunner Date: Thu, 11 Apr 2013 13:02:28 +0000 (+0200) Subject: Move PKCS#12 key derivation to a separate file X-Git-Tag: 5.1.0dr1~149^2~22 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d41e54c68dee58c5675cea2782f9a16103f0afc3;p=thirdparty%2Fstrongswan.git Move PKCS#12 key derivation to a separate file --- diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index 289697a460..75b501fddc 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -17,7 +17,7 @@ credentials/cred_encoding.c credentials/keys/private_key.c \ credentials/keys/public_key.c credentials/keys/shared_key.c \ credentials/certificates/certificate.c credentials/certificates/crl.c \ credentials/certificates/ocsp_response.c \ -credentials/containers/container.c \ +credentials/containers/container.c credentials/containers/pkcs12.c \ credentials/ietf_attributes/ietf_attributes.c credentials/credential_manager.c \ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index ae4fc75b6c..db1f8cf334 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -15,7 +15,7 @@ credentials/cred_encoding.c credentials/keys/private_key.c \ credentials/keys/public_key.c credentials/keys/shared_key.c \ credentials/certificates/certificate.c credentials/certificates/crl.c \ credentials/certificates/ocsp_response.c \ -credentials/containers/container.c \ +credentials/containers/container.c credentials/containers/pkcs12.c \ credentials/ietf_attributes/ietf_attributes.c credentials/credential_manager.c \ credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ @@ -55,6 +55,7 @@ credentials/certificates/pkcs10.h credentials/certificates/ocsp_request.h \ credentials/certificates/ocsp_response.h \ credentials/certificates/pgp_certificate.h \ credentials/containers/container.h credentials/containers/pkcs7.h \ +credentials/containers/pkcs12.h \ credentials/ietf_attributes/ietf_attributes.h \ credentials/credential_manager.h credentials/sets/auth_cfg_wrapper.h \ credentials/sets/ocsp_response_wrapper.h credentials/sets/cert_cache.h \ diff --git a/src/libstrongswan/credentials/containers/pkcs12.c b/src/libstrongswan/credentials/containers/pkcs12.c new file mode 100644 index 0000000000..7b812d27de --- /dev/null +++ b/src/libstrongswan/credentials/containers/pkcs12.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013 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 . + * + * 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 "pkcs12.h" + +#include + +/** + * v * ceiling(len/v) + */ +#define PKCS12_LEN(len, v) (((len) + v-1) & ~(v-1)) + +/** + * Copy src to dst as many times as possible + */ +static inline void copy_chunk(chunk_t dst, chunk_t src) +{ + size_t i; + + for (i = 0; i < dst.len; i++) + { + dst.ptr[i] = src.ptr[i % src.len]; + } +} + +/** + * Treat two chunks as integers in network order and add them together. + * The result is stored in the first chunk, if the second chunk is longer or the + * result overflows this is ignored. + */ +static void add_chunks(chunk_t a, chunk_t b) +{ + u_int16_t sum; + u_int8_t rem = 0; + ssize_t i, j; + + for (i = a.len - 1, j = b.len -1; i >= 0 && j >= 0; i--, j--) + { + sum = a.ptr[i] + b.ptr[j] + rem; + a.ptr[i] = (u_char)sum; + rem = sum >> 8; + } + for (; i >= 0 && rem; i--) + { + sum = a.ptr[i] + rem; + a.ptr[i] = (u_char)sum; + rem = sum >> 8; + } +} + +/** + * Do the actual key derivation with the given hasher, password and id. + */ +static bool derive_key(hash_algorithm_t hash, chunk_t unicode, chunk_t salt, + u_int64_t iterations, char id, chunk_t result) +{ + chunk_t out = result, D, S, P = chunk_empty, I, Ai, B, Ij; + hasher_t *hasher; + size_t Slen, v, u; + u_int64_t i; + bool success = FALSE; + + hasher = lib->crypto->create_hasher(lib->crypto, hash); + if (!hasher) + { + DBG1(DBG_ASN, " %N hash algorithm not available", + hash_algorithm_names, hash); + return FALSE; + } + switch (hash) + { + case HASH_MD2: + case HASH_MD5: + case HASH_SHA1: + case HASH_SHA224: + case HASH_SHA256: + v = 64; + break; + case HASH_SHA384: + case HASH_SHA512: + v = 128; + break; + default: + goto end; + } + u = hasher->get_hash_size(hasher); + + D = chunk_alloca(v); + memset(D.ptr, id, D.len); + + Slen = PKCS12_LEN(salt.len, v); + I = chunk_alloca(Slen + PKCS12_LEN(unicode.len, v)); + S = chunk_create(I.ptr, Slen); + P = chunk_create(I.ptr + Slen, I.len - Slen); + copy_chunk(S, salt); + copy_chunk(P, unicode); + + Ai = chunk_alloca(u); + B = chunk_alloca(v); + + while (TRUE) + { + if (!hasher->get_hash(hasher, D, NULL) || + !hasher->get_hash(hasher, I, Ai.ptr)) + { + goto end; + } + for (i = 1; i < iterations; i++) + { + if (!hasher->get_hash(hasher, Ai, Ai.ptr)) + { + goto end; + } + } + memcpy(out.ptr, Ai.ptr, min(out.len, Ai.len)); + out = chunk_skip(out, Ai.len); + if (!out.len) + { + break; + } + copy_chunk(B, Ai); + /* B = B+1 */ + add_chunks(B, chunk_from_chars(0x01)); + Ij = chunk_create(I.ptr, v); + for (i = 0; i < I.len; i += v, Ij.ptr += v) + { /* Ij = Ij + B + 1 */ + add_chunks(Ij, B); + } + } + success = TRUE; +end: + hasher->destroy(hasher); + return success; +} + +/* + * Described in header + */ +bool pkcs12_derive_key(hash_algorithm_t hash, chunk_t password, chunk_t salt, + u_int64_t iterations, pkcs12_key_type_t type, chunk_t key) +{ + chunk_t unicode = chunk_empty; + bool success; + int i; + + if (password.len) + { /* convert the password to UTF-16BE (without BOM) with 0 terminator */ + unicode = chunk_alloca(password.len * 2 + 2); + for (i = 0; i < password.len; i++) + { + unicode.ptr[i * 2] = 0; + unicode.ptr[i * 2 + 1] = password.ptr[i]; + } + unicode.ptr[i * 2] = 0; + unicode.ptr[i * 2 + 1] = 0; + } + + success = derive_key(hash, unicode, salt, iterations, type, key); + memwipe(unicode.ptr, unicode.len); + return success; +} diff --git a/src/libstrongswan/credentials/containers/pkcs12.h b/src/libstrongswan/credentials/containers/pkcs12.h new file mode 100644 index 0000000000..a6c9746ef6 --- /dev/null +++ b/src/libstrongswan/credentials/containers/pkcs12.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 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 . + * + * 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. + */ + +/** + * @defgroup pkcs12 pkcs12 + * @{ @ingroup containers + */ + +#ifndef PKCS12_H_ +#define PKCS12_H_ + +#include + +typedef enum pkcs12_key_type_t pkcs12_key_type_t; + +/** + * The types of password based keys used by PKCS#12. + */ +enum pkcs12_key_type_t { + PKCS12_KEY_ENCRYPTION = 1, + PKCS12_KEY_IV = 2, + PKCS12_KEY_MAC = 3, +}; + +/** + * Derive the keys used in PKCS#12 for password integrity/privacy mode. + * + * @param hash hash algorithm to use for key derivation + * @param password password (ASCII) + * @param salt salt value + * @param iterations number of iterations + * @param type type of key to derive + * @param key the returned key, must be allocated of desired length + * @return TRUE on success + */ +bool pkcs12_derive_key(hash_algorithm_t hash, chunk_t password, chunk_t salt, + u_int64_t iterations, pkcs12_key_type_t type, chunk_t key); + +#endif /** PKCS12_H_ @}*/ diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c index b0fae52785..7a7339915f 100644 --- a/src/libstrongswan/crypto/pkcs5.c +++ b/src/libstrongswan/crypto/pkcs5.c @@ -19,6 +19,7 @@ #include #include #include +#include typedef struct private_pkcs5_t private_pkcs5_t; @@ -161,156 +162,19 @@ static bool decrypt_generic(private_pkcs5_t *this, chunk_t password, } /** - * v * ceiling(len/v) - */ -#define PKCS12_LEN(len, v) (((len) + v-1) & ~(v-1)) - -/** - * Copy src to dst as many times as possible - */ -static inline void pkcs12_copy_chunk(chunk_t dst, chunk_t src) -{ - size_t i; - - for (i = 0; i < dst.len; i++) - { - dst.ptr[i] = src.ptr[i % src.len]; - } -} - -/** - * Treat two chunks as integers in network order and add them together. - * The result is stored in the first chunk, if the second chunk is longer or the - * result overflows this is ignored. - */ -static void pkcs12_add_chunks(chunk_t a, chunk_t b) -{ - u_int16_t sum; - u_int8_t rem = 0; - ssize_t i, j; - - for (i = a.len - 1, j = b.len -1; i >= 0 && j >= 0; i--, j--) - { - sum = a.ptr[i] + b.ptr[j] + rem; - a.ptr[i] = (u_char)sum; - rem = sum >> 8; - } - for (; i >= 0 && rem; i--) - { - sum = a.ptr[i] + rem; - a.ptr[i] = (u_char)sum; - rem = sum >> 8; - } -} - -/** - * Do the actual key derivation with the given password and id - * id is 1 for encryption keys, 2 for IVs, 3 for MAC keys. - */ -static bool pkcs12_derive(private_pkcs5_t *this, chunk_t unicode, - char id, chunk_t result) -{ - chunk_t out = result, D, S, P = chunk_empty, I, Ai, B, Ij; - hasher_t *hasher; - size_t Slen, v, u; - u_int64_t i; - - switch (this->data.pbes1.hash) - { - case HASH_MD2: - case HASH_MD5: - case HASH_SHA1: - case HASH_SHA224: - case HASH_SHA256: - v = 64; - break; - case HASH_SHA384: - case HASH_SHA512: - v = 128; - break; - default: - return FALSE; - } - hasher = this->data.pbes1.hasher; - u = hasher->get_hash_size(hasher); - - D = chunk_alloca(v); - memset(D.ptr, id, D.len); - - Slen = PKCS12_LEN(this->salt.len, v); - I = chunk_alloca(Slen + PKCS12_LEN(unicode.len, v)); - S = chunk_create(I.ptr, Slen); - P = chunk_create(I.ptr + Slen, I.len - Slen); - pkcs12_copy_chunk(S, this->salt); - pkcs12_copy_chunk(P, unicode); - - Ai = chunk_alloca(u); - B = chunk_alloca(v); - - while (TRUE) - { - if (!hasher->get_hash(hasher, D, NULL) || - !hasher->get_hash(hasher, I, Ai.ptr)) - { - return FALSE; - } - for (i = 1; i < this->iterations; i++) - { - if (!hasher->get_hash(hasher, Ai, Ai.ptr)) - { - return FALSE; - } - } - memcpy(out.ptr, Ai.ptr, min(out.len, Ai.len)); - out = chunk_skip(out, Ai.len); - if (!out.len) - { - break; - } - pkcs12_copy_chunk(B, Ai); - /* B = B+1 */ - pkcs12_add_chunks(B, chunk_from_chars(0x01)); - Ij = chunk_create(I.ptr, v); - while (Ij.len) - { /* Ij = Ij + B + 1 */ - pkcs12_add_chunks(Ij, B); - Ij = chunk_skip(Ij, v); - } - } - return TRUE; -} - -/** - * KDF defined in PKCS#12 + * KDF as used by PKCS#12 */ static bool pkcs12_kdf(private_pkcs5_t *this, chunk_t password, chunk_t keymat) { - chunk_t unicode = chunk_empty, key, iv; - int i; - - if (password.len) - { /* convert the password to UTF-16BE (without BOM) with 0 terminator */ - unicode = chunk_alloca(password.len * 2 + 2); - for (i = 0; i < password.len; i++) - { - unicode.ptr[i * 2] = 0; - unicode.ptr[i * 2 + 1] = password.ptr[i]; - } - unicode.ptr[i * 2] = 0; - unicode.ptr[i * 2 + 1] = 0; - } + chunk_t key, iv; key = chunk_create(keymat.ptr, this->keylen); iv = chunk_create(keymat.ptr + this->keylen, keymat.len - this->keylen); - if (!pkcs12_derive(this, unicode, 1, key) || - !pkcs12_derive(this, unicode, 2, iv)) - { - memwipe(unicode.ptr, unicode.len); - return FALSE; - } - memwipe(unicode.ptr, unicode.len); - return TRUE; + return pkcs12_derive_key(this->data.pbes1.hash, password, this->salt, + this->iterations, PKCS12_KEY_ENCRYPTION, key) && + pkcs12_derive_key(this->data.pbes1.hash, password, this->salt, + this->iterations, PKCS12_KEY_IV, iv); } /** @@ -426,7 +290,6 @@ static bool ensure_crypto_primitives(private_pkcs5_t *this, chunk_t data) switch (this->scheme) { case PKCS5_SCHEME_PBES1: - case PKCS5_SCHEME_PKCS12: { if (!this->data.pbes1.hasher) { @@ -466,6 +329,8 @@ static bool ensure_crypto_primitives(private_pkcs5_t *this, chunk_t data) this->data.pbes2.prf = prf; } } + case PKCS5_SCHEME_PKCS12: + break; } return TRUE; } @@ -699,13 +564,14 @@ METHOD(pkcs5_t, destroy, void, switch (this->scheme) { case PKCS5_SCHEME_PBES1: - case PKCS5_SCHEME_PKCS12: DESTROY_IF(this->data.pbes1.hasher); break; case PKCS5_SCHEME_PBES2: DESTROY_IF(this->data.pbes2.prf); chunk_free(&this->data.pbes2.iv); break; + case PKCS5_SCHEME_PKCS12: + break; } free(this); }