Extract PKCS#5 handling from pkcs8 plugin to separate helper class
authorTobias Brunner <tobias@strongswan.org>
Mon, 8 Apr 2013 16:13:03 +0000 (18:13 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 8 May 2013 12:53:08 +0000 (14:53 +0200)
src/libstrongswan/Android.mk
src/libstrongswan/Makefile.am
src/libstrongswan/crypto/pkcs5.c [new file with mode: 0644]
src/libstrongswan/crypto/pkcs5.h [new file with mode: 0644]
src/libstrongswan/plugins/pkcs8/pkcs8_builder.c

index a46b0d9..289697a 100644 (file)
@@ -8,7 +8,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
 collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \
 collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \
 crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
-crypto/prfs/prf.c crypto/prfs/mac_prf.c \
+crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \
 crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \
 crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \
 crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \
index b5a4b9b..5968a3b 100644 (file)
@@ -6,7 +6,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
 collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \
 collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \
 crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
-crypto/prfs/prf.c crypto/prfs/mac_prf.c \
+crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \
 crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \
 crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \
 crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \
@@ -45,7 +45,7 @@ crypto/proposal/proposal_keywords.h crypto/proposal/proposal_keywords_static.h \
 crypto/prfs/prf.h crypto/prfs/mac_prf.h crypto/rngs/rng.h crypto/nonce_gen.h \
 crypto/prf_plus.h crypto/signers/signer.h crypto/signers/mac_signer.h \
 crypto/crypto_factory.h crypto/crypto_tester.h crypto/diffie_hellman.h \
-crypto/aead.h crypto/transform.h \
+crypto/aead.h crypto/transform.h crypto/pkcs5.h \
 credentials/credential_factory.h credentials/builder.h \
 credentials/cred_encoding.h credentials/keys/private_key.h \
 credentials/keys/public_key.h credentials/keys/shared_key.h \
diff --git a/src/libstrongswan/crypto/pkcs5.c b/src/libstrongswan/crypto/pkcs5.c
new file mode 100644 (file)
index 0000000..76a67fc
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+ * Copyright (C) 2012-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 <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 "pkcs5.h"
+
+#include <utils/debug.h>
+#include <asn1/oid.h>
+#include <asn1/asn1.h>
+#include <asn1/asn1_parser.h>
+
+typedef struct private_pkcs5_t private_pkcs5_t;
+
+/**
+ * Private data of a pkcs5_t object
+ */
+struct private_pkcs5_t {
+
+       /**
+        * Implements pkcs5_t.
+        */
+       pkcs5_t public;
+
+       /**
+        * Salt used during encryption
+        */
+       chunk_t salt;
+
+       /**
+        * Iterations for key derivation
+        */
+       u_int64_t iterations;
+
+       /**
+        * Encryption algorithm
+        */
+       encryption_algorithm_t encr;
+
+       /**
+        * Encryption key length
+        */
+       size_t keylen;
+
+       /**
+        * Crypter
+        */
+       crypter_t *crypter;
+
+
+       /**
+        * The encryption scheme
+        */
+       enum {
+               PKCS5_SCHEME_PBES1,
+               PKCS5_SCHEME_PBES2,
+       } scheme;
+
+       /**
+        * Data used for individual schemes
+        */
+       union {
+               struct {
+                       /**
+                        * Hash algorithm
+                        */
+                       hash_algorithm_t hash;
+
+                       /**
+                        * Hasher
+                        */
+                       hasher_t *hasher;
+
+               } pbes1;
+               struct {
+                       /**
+                        * PRF algorithm
+                        */
+                       pseudo_random_function_t prf_alg;
+
+                       /**
+                        * PRF
+                        */
+                       prf_t * prf;
+
+                       /**
+                        * IV
+                        */
+                       chunk_t iv;
+
+               } pbes2;
+       } data;
+};
+
+/**
+ * Verify padding of decrypted blob.
+ * Length of blob is adjusted accordingly.
+ */
+static bool verify_padding(chunk_t *blob)
+{
+       u_int8_t padding, count;
+
+       padding = count = blob->ptr[blob->len - 1];
+
+       if (padding > 8)
+       {
+               return FALSE;
+       }
+       for (; blob->len && count; --blob->len, --count)
+       {
+               if (blob->ptr[blob->len - 1] != padding)
+               {
+                       return FALSE;
+               }
+       }
+       return TRUE;
+}
+
+/**
+ * Prototype for key derivation functions.
+ */
+typedef bool (*kdf_t)(private_pkcs5_t *this, chunk_t password, chunk_t key);
+
+/**
+ * Try to decrypt the given data with the given password using the given
+ * key derivation function. keymat is where the kdf function writes the key
+ * to, key and iv point to the actual keys and initialization vectors resp.
+ */
+static bool decrypt_generic(private_pkcs5_t *this, chunk_t password,
+                                                       chunk_t data, chunk_t *decrypted, kdf_t kdf,
+                                                       chunk_t keymat, chunk_t key, chunk_t iv)
+{
+       if (!kdf(this, password, keymat))
+       {
+               return FALSE;
+       }
+       if (!this->crypter->set_key(this->crypter, key) ||
+               !this->crypter->decrypt(this->crypter, data, iv, decrypted))
+       {
+               memwipe(keymat.ptr, keymat.len);
+               return FALSE;
+       }
+       memwipe(keymat.ptr, keymat.len);
+       if (verify_padding(decrypted))
+       {
+               return TRUE;
+       }
+       chunk_free(decrypted);
+       return FALSE;
+}
+
+/**
+ * Function F of PBKDF2
+ */
+static bool pbkdf2_f(chunk_t block, prf_t *prf, chunk_t seed,
+                                        u_int64_t iterations)
+{
+       chunk_t u;
+       u_int64_t i;
+
+       u = chunk_alloca(prf->get_block_size(prf));
+       if (!prf->get_bytes(prf, seed, u.ptr))
+       {
+               return FALSE;
+       }
+       memcpy(block.ptr, u.ptr, block.len);
+
+       for (i = 1; i < iterations; i++)
+       {
+               if (!prf->get_bytes(prf, u, u.ptr))
+               {
+                       return FALSE;
+               }
+               memxor(block.ptr, u.ptr, block.len);
+       }
+       return TRUE;
+}
+
+/**
+ * PBKDF2 key derivation function for PBES2, key must be allocated
+ */
+static bool pbkdf2(private_pkcs5_t *this, chunk_t password, chunk_t key)
+{
+       prf_t *prf;
+       chunk_t keymat, block, seed;
+       size_t blocks;
+       u_int32_t i = 0;
+
+       prf = this->data.pbes2.prf;
+
+       if (!prf->set_key(prf, password))
+       {
+               return FALSE;
+       }
+
+       block.len = prf->get_block_size(prf);
+       blocks = (key.len - 1) / block.len + 1;
+       keymat = chunk_alloca(blocks * block.len);
+
+       seed = chunk_cata("cc", this->salt, chunk_from_thing(i));
+
+       for (; i < blocks; i++)
+       {
+               htoun32(seed.ptr + this->salt.len, i + 1);
+               block.ptr = keymat.ptr + (i * block.len);
+               if (!pbkdf2_f(block, prf, seed, this->iterations))
+               {
+                       return FALSE;
+               }
+       }
+       memcpy(key.ptr, keymat.ptr, key.len);
+       return TRUE;
+}
+
+/**
+ * PBKDF1 key derivation function for PBES1, key must be allocated
+ */
+static bool pbkdf1(private_pkcs5_t *this, chunk_t password, chunk_t key)
+{
+       hasher_t *hasher;
+       chunk_t hash;
+       u_int64_t i;
+
+       hasher = this->data.pbes1.hasher;
+
+       hash = chunk_alloca(hasher->get_hash_size(hasher));
+       if (!hasher->get_hash(hasher, password, NULL) ||
+               !hasher->get_hash(hasher, this->salt, hash.ptr))
+       {
+               return FALSE;
+       }
+
+       for (i = 1; i < this->iterations; i++)
+       {
+               if (!hasher->get_hash(hasher, hash, hash.ptr))
+               {
+                       return FALSE;
+               }
+       }
+       memcpy(key.ptr, hash.ptr, key.len);
+       return TRUE;
+}
+
+static bool ensure_crypto_primitives(private_pkcs5_t *this, chunk_t data)
+{
+       if (!this->crypter)
+       {
+               this->crypter = lib->crypto->create_crypter(lib->crypto, this->encr,
+                                                                                                       this->keylen);
+               if (!this->crypter)
+               {
+                       DBG1(DBG_ASN, "  %N encryption algorithm not available",
+                                encryption_algorithm_names, this->encr);
+                       return FALSE;
+               }
+       }
+       if (data.len % this->crypter->get_block_size(this->crypter))
+       {
+               DBG1(DBG_ASN, "  data size is not a multiple of block size");
+               return FALSE;
+       }
+       switch (this->scheme)
+       {
+               case PKCS5_SCHEME_PBES1:
+               {
+                       if (!this->data.pbes1.hasher)
+                       {
+                               hasher_t *hasher;
+
+                               hasher = lib->crypto->create_hasher(lib->crypto,
+                                                                                                       this->data.pbes1.hash);
+                               if (!hasher)
+                               {
+                                       DBG1(DBG_ASN, "  %N hash algorithm not available",
+                                                hash_algorithm_names, this->data.pbes1.hash);
+                                       return  FALSE;
+                               }
+                               if (hasher->get_hash_size(hasher) < this->keylen)
+                               {
+                                       hasher->destroy(hasher);
+                                       return FALSE;
+                               }
+                               this->data.pbes1.hasher = hasher;
+                       }
+               }
+               case PKCS5_SCHEME_PBES2:
+               {
+                       if (!this->data.pbes2.prf)
+                       {
+                               prf_t *prf;
+
+                               prf = lib->crypto->create_prf(lib->crypto,
+                                                                                         this->data.pbes2.prf_alg);
+                               if (!prf)
+                               {
+                                       DBG1(DBG_ASN, "  %N prf algorithm not available",
+                                                pseudo_random_function_names,
+                                                this->data.pbes2.prf_alg);
+                                       return FALSE;
+                               }
+                               this->data.pbes2.prf = prf;
+                       }
+               }
+       }
+       return TRUE;
+}
+
+METHOD(pkcs5_t, decrypt, bool,
+       private_pkcs5_t *this, chunk_t password, chunk_t data, chunk_t *decrypted)
+{
+       chunk_t keymat, key, iv;
+       kdf_t kdf;
+
+       if (!ensure_crypto_primitives(this, data) || !decrypted)
+       {
+               return FALSE;
+       }
+       switch (this->scheme)
+       {
+               case PKCS5_SCHEME_PBES1:
+                       kdf = pbkdf1;
+                       keymat = chunk_alloca(this->keylen * 2);
+                       key = chunk_create(keymat.ptr, this->keylen);
+                       iv = chunk_create(keymat.ptr + this->keylen, this->keylen);
+                       break;
+               case PKCS5_SCHEME_PBES2:
+                       kdf = pbkdf2;
+                       keymat = chunk_alloca(this->keylen);
+                       key = keymat;
+                       iv = this->data.pbes2.iv;
+                       break;
+               default:
+                       return FALSE;
+       }
+       return decrypt_generic(this, password, data, decrypted, kdf,
+                                                  keymat, key, iv);
+}
+
+/**
+ * Converts an ASN.1 INTEGER object to an u_int64_t. If the INTEGER is longer
+ * than 8 bytes only the 8 LSBs are returned.
+ *
+ * @param blob         body of an ASN.1 coded integer object
+ * @return                     converted integer
+ */
+static u_int64_t parse_asn1_integer_uint64(chunk_t blob)
+{
+       u_int64_t val = 0;
+       int i;
+
+       for (i = 0; i < blob.len; i++)
+       {       /* if it is longer than 8 bytes, we just use the 8 LSBs */
+               val <<= 8;
+               val |= (u_int64_t)blob.ptr[i];
+       }
+       return val;
+}
+
+/**
+ * ASN.1 definition of a PBEParameter structure
+ */
+static const asn1Object_t pbeParameterObjects[] = {
+       { 0, "PBEParameter",            ASN1_SEQUENCE,          ASN1_NONE       }, /* 0 */
+       { 1,   "salt",                          ASN1_OCTET_STRING,      ASN1_BODY       }, /* 1 */
+       { 1,   "iterationCount",        ASN1_INTEGER,           ASN1_BODY       }, /* 2 */
+       { 0, "exit",                            ASN1_EOC,                       ASN1_EXIT       }
+};
+#define PBEPARAM_SALT                                  1
+#define PBEPARAM_ITERATION_COUNT               2
+
+/**
+ * Parse a PBEParameter structure
+ */
+static bool parse_pbes1_params(private_pkcs5_t *this, chunk_t blob, int level0)
+{
+       asn1_parser_t *parser;
+       chunk_t object;
+       int objectID;
+       bool success;
+
+       parser = asn1_parser_create(pbeParameterObjects, blob);
+       parser->set_top_level(parser, level0);
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
+               {
+                       case PBEPARAM_SALT:
+                       {
+                               this->salt = chunk_clone(object);
+                               break;
+                       }
+                       case PBEPARAM_ITERATION_COUNT:
+                       {
+                               this->iterations = parse_asn1_integer_uint64(object);
+                               break;
+                       }
+               }
+       }
+       success = parser->success(parser);
+       parser->destroy(parser);
+       return success;
+}
+
+/**
+ * ASN.1 definition of a PBKDF2-params structure
+ * The salt is actually a CHOICE and could be an AlgorithmIdentifier from
+ * PBKDF2-SaltSources (but as per RFC 2898 that's for future versions).
+ */
+static const asn1Object_t pbkdf2ParamsObjects[] = {
+       { 0, "PBKDF2-params",   ASN1_SEQUENCE,          ASN1_NONE                       }, /* 0 */
+       { 1,   "salt",                  ASN1_OCTET_STRING,      ASN1_BODY                       }, /* 1 */
+       { 1,   "iterationCount",ASN1_INTEGER,           ASN1_BODY                       }, /* 2 */
+       { 1,   "keyLength",             ASN1_INTEGER,           ASN1_OPT|ASN1_BODY      }, /* 3 */
+       { 1,   "end opt",               ASN1_EOC,                       ASN1_END                        }, /* 4 */
+       { 1,   "prf",                   ASN1_EOC,                       ASN1_DEF|ASN1_RAW       }, /* 5 */
+       { 0, "exit",                    ASN1_EOC,                       ASN1_EXIT                       }
+};
+#define PBKDF2_SALT                                    1
+#define PBKDF2_ITERATION_COUNT         2
+#define PBKDF2_KEYLENGTH                       3
+#define PBKDF2_PRF                                     5
+
+/**
+ * Parse a PBKDF2-params structure
+ */
+static bool parse_pbkdf2_params(private_pkcs5_t *this, chunk_t blob, int level0)
+{
+       asn1_parser_t *parser;
+       chunk_t object;
+       int objectID;
+       bool success;
+
+       parser = asn1_parser_create(pbkdf2ParamsObjects, blob);
+       parser->set_top_level(parser, level0);
+
+       /* keylen is optional */
+       this->keylen = 0;
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
+               {
+                       case PBKDF2_SALT:
+                       {
+                               this->salt = chunk_clone(object);
+                               break;
+                       }
+                       case PBKDF2_ITERATION_COUNT:
+                       {
+                               this->iterations = parse_asn1_integer_uint64(object);
+                               break;
+                       }
+                       case PBKDF2_KEYLENGTH:
+                       {
+                               this->keylen = (size_t)parse_asn1_integer_uint64(object);
+                               break;
+                       }
+                       case PBKDF2_PRF:
+                       {       /* defaults to id-hmacWithSHA1, no other is currently defined */
+                               this->data.pbes2.prf_alg = PRF_HMAC_SHA1;
+                               break;
+                       }
+               }
+       }
+       success = parser->success(parser);
+       parser->destroy(parser);
+       return success;
+}
+
+/**
+ * ASN.1 definition of a PBES2-params structure
+ */
+static const asn1Object_t pbes2ParamsObjects[] = {
+       { 0, "PBES2-params",            ASN1_SEQUENCE,          ASN1_NONE       }, /* 0 */
+       { 1,   "keyDerivationFunc",     ASN1_EOC,                       ASN1_RAW        }, /* 1 */
+       { 1,   "encryptionScheme",      ASN1_EOC,                       ASN1_RAW        }, /* 2 */
+       { 0, "exit",                            ASN1_EOC,                       ASN1_EXIT       }
+};
+#define PBES2PARAMS_KEY_DERIVATION_FUNC                1
+#define PBES2PARAMS_ENCRYPTION_SCHEME          2
+
+/**
+ * Parse a PBES2-params structure
+ */
+static bool parse_pbes2_params(private_pkcs5_t *this, chunk_t blob, int level0)
+{
+       asn1_parser_t *parser;
+       chunk_t object, params;
+       int objectID;
+       bool success = FALSE;
+
+       parser = asn1_parser_create(pbes2ParamsObjects, blob);
+       parser->set_top_level(parser, level0);
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
+               {
+                       case PBES2PARAMS_KEY_DERIVATION_FUNC:
+                       {
+                               int oid = asn1_parse_algorithmIdentifier(object,
+                                                                       parser->get_level(parser) + 1, &params);
+                               if (oid != OID_PBKDF2)
+                               {       /* unsupported key derivation function */
+                                       goto end;
+                               }
+                               if (!parse_pbkdf2_params(this, params,
+                                                                                parser->get_level(parser) + 1))
+                               {
+                                       goto end;
+                               }
+                               break;
+                       }
+                       case PBES2PARAMS_ENCRYPTION_SCHEME:
+                       {
+                               int oid = asn1_parse_algorithmIdentifier(object,
+                                                                       parser->get_level(parser) + 1, &params);
+                               if (oid != OID_3DES_EDE_CBC)
+                               {       /* unsupported encryption scheme */
+                                       goto end;
+                               }
+                               if (this->keylen <= 0)
+                               {       /* default key length for DES-EDE3-CBC-Pad */
+                                       this->keylen = 24;
+                               }
+                               if (!asn1_parse_simple_object(&params, ASN1_OCTET_STRING,
+                                                                       parser->get_level(parser) + 1, "IV"))
+                               {
+                                       goto end;
+                               }
+                               this->encr = ENCR_3DES;
+                               this->data.pbes2.iv = chunk_clone(params);
+                               break;
+                       }
+               }
+       }
+       success = parser->success(parser);
+end:
+       parser->destroy(parser);
+       return success;
+}
+
+METHOD(pkcs5_t, destroy, void,
+       private_pkcs5_t *this)
+{
+       DESTROY_IF(this->crypter);
+       chunk_free(&this->salt);
+       switch (this->scheme)
+       {
+               case PKCS5_SCHEME_PBES1:
+                       DESTROY_IF(this->data.pbes1.hasher);
+                       break;
+               case PKCS5_SCHEME_PBES2:
+                       DESTROY_IF(this->data.pbes2.prf);
+                       chunk_free(&this->data.pbes2.iv);
+                       break;
+       }
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0)
+{
+       private_pkcs5_t *this;
+       chunk_t params;
+       int oid;
+
+       INIT(this,
+               .public = {
+                       .decrypt = _decrypt,
+                       .destroy = _destroy,
+               },
+               .scheme = PKCS5_SCHEME_PBES1,
+               .keylen = 8,
+       );
+
+       oid = asn1_parse_algorithmIdentifier(blob, level0, &params);
+
+       switch (oid)
+       {
+               case OID_PBE_MD5_DES_CBC:
+                       this->encr = ENCR_DES;
+                       this->data.pbes1.hash = HASH_MD5;
+                       break;
+               case OID_PBE_SHA1_DES_CBC:
+                       this->encr = ENCR_DES;
+                       this->data.pbes1.hash = HASH_SHA1;
+                       break;
+               case OID_PBES2:
+                       this->scheme = PKCS5_SCHEME_PBES2;
+                       break;
+               default:
+                       /* encryption scheme not supported */
+                       goto failure;
+       }
+
+       switch (this->scheme)
+       {
+               case PKCS5_SCHEME_PBES1:
+                       if (!parse_pbes1_params(this, params, level0))
+                       {
+                               goto failure;
+                       }
+                       break;
+               case PKCS5_SCHEME_PBES2:
+                       if (!parse_pbes2_params(this, params, level0))
+                       {
+                               goto failure;
+                       }
+                       break;
+       }
+       return &this->public;
+
+failure:
+       destroy(this);
+       return NULL;
+}
diff --git a/src/libstrongswan/crypto/pkcs5.h b/src/libstrongswan/crypto/pkcs5.h
new file mode 100644 (file)
index 0000000..b16d373
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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 <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.
+ */
+
+/**
+ * @defgroup pkcs5 pkcs5
+ * @{ @ingroup crypto
+ */
+
+#ifndef PKCS5_H_
+#define PKCS5_H_
+
+typedef struct pkcs5_t pkcs5_t;
+
+#include <utils/chunk.h>
+
+/**
+ * PKCS#5 helper class
+ */
+struct pkcs5_t {
+
+       /**
+        * Decrypt the given data using the given password and the scheme derived
+        * from the initial AlgorithmIdentifier object.
+        *
+        * @param password      password used for decryption
+        * @param data          data to decrypt
+        * @param decrypted     decrypted data gets allocated
+        * @return                      TRUE on success, FALSE otherwise
+        */
+       bool (*decrypt)(pkcs5_t *this, chunk_t password, chunk_t data,
+                                       chunk_t *decrypted) __attribute__((warn_unused_result));
+
+       /**
+        * Destroy the object and any associated cryptographic primitive.
+        */
+       void (*destroy)(pkcs5_t *this);
+};
+
+/**
+ * Create a PKCS#5 helper object from an ASN.1 encoded AlgorithmIdentifier
+ * object.
+ *
+ * @param blob                 ASN.1 encoded AlgorithmIdentifier
+ * @param level0               ASN.1 parser level
+ * @return                             pkcs5_t object, NULL on failure
+ */
+pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0);
+
+#endif /** PKCS5_H_ @}*/
index 26a3620..e93a836 100644 (file)
@@ -19,6 +19,7 @@
 #include <asn1/oid.h>
 #include <asn1/asn1.h>
 #include <asn1/asn1_parser.h>
+#include <crypto/pkcs5.h>
 #include <credentials/keys/private_key.h>
 
 /**
@@ -101,450 +102,39 @@ end:
 }
 
 /**
- * Verify padding of decrypted blob.
- * Length of blob is adjusted accordingly.
- */
-static bool verify_padding(chunk_t *blob)
-{
-       u_int8_t padding, count;
-
-       padding = count = blob->ptr[blob->len - 1];
-       if (padding > 8)
-       {
-               return FALSE;
-       }
-       for (; blob->len && count; --blob->len, --count)
-       {
-               if (blob->ptr[blob->len - 1] != padding)
-               {
-                       return FALSE;
-               }
-       }
-       return TRUE;
-}
-
-/**
- * Prototype for key derivation functions.
- */
-typedef bool (*kdf_t)(void *generator, chunk_t password, chunk_t salt,
-                                         u_int64_t iterations, chunk_t key);
-
-/**
  * Try to decrypt the given blob with multiple passwords using the given
- * key derivation function. keymat is where the kdf function writes the key
- * to, key and iv point to the actual keys and initialization vectors resp.
+ * pkcs5 object.
  */
-static private_key_t *decrypt_private_key(chunk_t blob,
-                                       encryption_algorithm_t encr, size_t key_len, kdf_t kdf,
-                                       void *generator, chunk_t salt, u_int64_t iterations,
-                                       chunk_t keymat, chunk_t key, chunk_t iv)
+static private_key_t *decrypt_private_key(pkcs5_t *pkcs5, chunk_t blob)
 {
        enumerator_t *enumerator;
        shared_key_t *shared;
-       crypter_t *crypter;
        private_key_t *private_key = NULL;
 
-       crypter = lib->crypto->create_crypter(lib->crypto, encr, key_len);
-       if (!crypter)
-       {
-               DBG1(DBG_ASN, "  %N encryption algorithm not available",
-                        encryption_algorithm_names, encr);
-               return NULL;
-       }
-       if (blob.len % crypter->get_block_size(crypter))
-       {
-               DBG1(DBG_ASN, "  data size is not a multiple of block size");
-               crypter->destroy(crypter);
-               return NULL;
-       }
-
        enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
                                                                                SHARED_PRIVATE_KEY_PASS, NULL, NULL);
        while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
        {
                chunk_t decrypted;
 
-               if (!kdf(generator, shared->get_key(shared), salt, iterations, keymat))
-               {
-                       continue;
-               }
-               if (!crypter->set_key(crypter, key) ||
-                       !crypter->decrypt(crypter, blob, iv, &decrypted))
+               if (!pkcs5->decrypt(pkcs5, shared->get_key(shared), blob, &decrypted))
                {
                        continue;
                }
-               if (verify_padding(&decrypted))
+               private_key = parse_private_key(decrypted);
+               if (private_key)
                {
-                       private_key = parse_private_key(decrypted);
-                       if (private_key)
-                       {
-                               chunk_clear(&decrypted);
-                               break;
-                       }
+                       chunk_clear(&decrypted);
+                       break;
                }
                chunk_free(&decrypted);
        }
        enumerator->destroy(enumerator);
-       crypter->destroy(crypter);
-
-       return private_key;
-}
-
-/**
- * Function F of PBKDF2
- */
-static bool pbkdf2_f(chunk_t block, prf_t *prf, chunk_t seed,
-                                        u_int64_t iterations)
-{
-       chunk_t u;
-       u_int64_t i;
-
-       u = chunk_alloca(prf->get_block_size(prf));
-       if (!prf->get_bytes(prf, seed, u.ptr))
-       {
-               return FALSE;
-       }
-       memcpy(block.ptr, u.ptr, block.len);
-
-       for (i = 1; i < iterations; i++)
-       {
-               if (!prf->get_bytes(prf, u, u.ptr))
-               {
-                       return FALSE;
-               }
-               memxor(block.ptr, u.ptr, block.len);
-       }
-       return TRUE;
-}
-
-/**
- * PBKDF2 key derivation function
- */
-static bool pbkdf2(prf_t *prf, chunk_t password, chunk_t salt,
-                                  u_int64_t iterations, chunk_t key)
-{
-       chunk_t keymat, block, seed;
-       size_t blocks;
-       u_int32_t i = 0, *ni;
-
-       if (!prf->set_key(prf, password))
-       {
-               return FALSE;
-       }
-
-       block.len = prf->get_block_size(prf);
-       blocks = (key.len - 1) / block.len + 1;
-       keymat = chunk_alloca(blocks * block.len);
-
-       seed = chunk_cata("cc", salt, chunk_from_thing(i));
-       ni = (u_int32_t*)(seed.ptr + salt.len);
-
-       for (; i < blocks; i++)
-       {
-               *ni = htonl(i + 1);
-               block.ptr = keymat.ptr + (i * block.len);
-               if (!pbkdf2_f(block, prf, seed, iterations))
-               {
-                       return FALSE;
-               }
-       }
 
-       memcpy(key.ptr, keymat.ptr, key.len);
-
-       return TRUE;
-}
-
-/**
- * Decrypt an encrypted PKCS#8 encoded private key according to PBES2
- */
-static private_key_t *decrypt_private_key_pbes2(chunk_t blob,
-                                                       encryption_algorithm_t encr, size_t key_len,
-                                                       chunk_t iv, pseudo_random_function_t prf_func,
-                                                       chunk_t salt, u_int64_t iterations)
-{
-       private_key_t *private_key;
-       prf_t *prf;
-       chunk_t key;
-
-       prf = lib->crypto->create_prf(lib->crypto, prf_func);
-       if (!prf)
-       {
-               DBG1(DBG_ASN, "  %N prf algorithm not available",
-                        pseudo_random_function_names, prf_func);
-               return NULL;
-       }
-
-       key = chunk_alloca(key_len);
-
-       private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf2, prf,
-                                                                         salt, iterations, key, key, iv);
-
-       prf->destroy(prf);
-       return private_key;
-}
-
-/**
- * PBKDF1 key derivation function
- */
-static bool pbkdf1(hasher_t *hasher, chunk_t password, chunk_t salt,
-                                  u_int64_t iterations, chunk_t key)
-{
-       chunk_t hash;
-       u_int64_t i;
-
-       hash = chunk_alloca(hasher->get_hash_size(hasher));
-       if (!hasher->get_hash(hasher, password, NULL) ||
-               !hasher->get_hash(hasher, salt, hash.ptr))
-       {
-               return FALSE;
-       }
-
-       for (i = 1; i < iterations; i++)
-       {
-               if (!hasher->get_hash(hasher, hash, hash.ptr))
-               {
-                       return FALSE;
-               }
-       }
-
-       memcpy(key.ptr, hash.ptr, key.len);
-
-       return TRUE;
-}
-
-/**
- * Decrypt an encrypted PKCS#8 encoded private key according to PBES1
- */
-static private_key_t *decrypt_private_key_pbes1(chunk_t blob,
-                                                       encryption_algorithm_t encr, size_t key_len,
-                                                       hash_algorithm_t hash, chunk_t salt,
-                                                       u_int64_t iterations)
-{
-       private_key_t *private_key = NULL;
-       hasher_t *hasher = NULL;
-       chunk_t keymat, key, iv;
-
-       hasher = lib->crypto->create_hasher(lib->crypto, hash);
-       if (!hasher)
-       {
-               DBG1(DBG_ASN, "  %N hash algorithm not available",
-                        hash_algorithm_names, hash);
-               goto end;
-       }
-       if (hasher->get_hash_size(hasher) < key_len)
-       {
-               goto end;
-       }
-
-       keymat = chunk_alloca(key_len * 2);
-       key.len = key_len;
-       key.ptr = keymat.ptr;
-       iv.len = key_len;
-       iv.ptr = keymat.ptr + key_len;
-
-       private_key = decrypt_private_key(blob, encr, key_len, (kdf_t)pbkdf1,
-                                                                         hasher, salt, iterations, keymat,
-                                                                         key, iv);
-
-end:
-       DESTROY_IF(hasher);
        return private_key;
 }
 
 /**
- * Parse an ASN1_INTEGER to a u_int64_t.
- */
-static u_int64_t parse_asn1_integer_uint64(chunk_t blob)
-{
-       u_int64_t val = 0;
-       int i;
-
-       for (i = 0; i < blob.len; i++)
-       {       /* if it is longer than 8 bytes, we just use the 8 LSBs */
-               val <<= 8;
-               val |= (u_int64_t)blob.ptr[i];
-       }
-       return val;
-}
-
-/**
- * ASN.1 definition of a PBKDF2-params structure
- * The salt is actually a CHOICE and could be an AlgorithmIdentifier from
- * PBKDF2-SaltSources (but as per RFC 2898 that's for future versions).
- */
-static const asn1Object_t pbkdf2ParamsObjects[] = {
-       { 0, "PBKDF2-params",   ASN1_SEQUENCE,          ASN1_NONE                       }, /* 0 */
-       { 1,   "salt",                  ASN1_OCTET_STRING,      ASN1_BODY                       }, /* 1 */
-       { 1,   "iterationCount",ASN1_INTEGER,           ASN1_BODY                       }, /* 2 */
-       { 1,   "keyLength",             ASN1_INTEGER,           ASN1_OPT|ASN1_BODY      }, /* 3 */
-       { 1,   "end opt",               ASN1_EOC,                       ASN1_END                        }, /* 4 */
-       { 1,   "prf",                   ASN1_EOC,                       ASN1_DEF|ASN1_RAW       }, /* 5 */
-       { 0, "exit",                    ASN1_EOC,                       ASN1_EXIT                       }
-};
-#define PBKDF2_SALT                                    1
-#define PBKDF2_ITERATION_COUNT         2
-#define PBKDF2_KEY_LENGTH                      3
-#define PBKDF2_PRF                                     5
-
-/**
- * Parse a PBKDF2-params structure
- */
-static void parse_pbkdf2_params(chunk_t blob, chunk_t *salt,
-                                                               u_int64_t *iterations, size_t *key_len,
-                                                               pseudo_random_function_t *prf)
-{
-       asn1_parser_t *parser;
-       chunk_t object;
-       int objectID;
-
-       parser = asn1_parser_create(pbkdf2ParamsObjects, blob);
-
-       *key_len = 0; /* key_len is optional */
-
-       while (parser->iterate(parser, &objectID, &object))
-       {
-               switch (objectID)
-               {
-                       case PBKDF2_SALT:
-                       {
-                               *salt = object;
-                               break;
-                       }
-                       case PBKDF2_ITERATION_COUNT:
-                       {
-                               *iterations = parse_asn1_integer_uint64(object);
-                               break;
-                       }
-                       case PBKDF2_KEY_LENGTH:
-                       {
-                               *key_len = (size_t)parse_asn1_integer_uint64(object);
-                               break;
-                       }
-                       case PBKDF2_PRF:
-                       {       /* defaults to id-hmacWithSHA1 */
-                               *prf = PRF_HMAC_SHA1;
-                               break;
-                       }
-               }
-       }
-
-       parser->destroy(parser);
-}
-
-/**
- * ASN.1 definition of a PBES2-params structure
- */
-static const asn1Object_t pbes2ParamsObjects[] = {
-       { 0, "PBES2-params",            ASN1_SEQUENCE,          ASN1_NONE       }, /* 0 */
-       { 1,   "keyDerivationFunc",     ASN1_EOC,                       ASN1_RAW        }, /* 1 */
-       { 1,   "encryptionScheme",      ASN1_EOC,                       ASN1_RAW        }, /* 2 */
-       { 0, "exit",                            ASN1_EOC,                       ASN1_EXIT       }
-};
-#define PBES2PARAMS_KEY_DERIVATION_FUNC                1
-#define PBES2PARAMS_ENCRYPTION_SCHEME          2
-
-/**
- * Parse a PBES2-params structure
- */
-static void parse_pbes2_params(chunk_t blob, chunk_t *salt,
-                                                          u_int64_t *iterations, size_t *key_len,
-                                                          pseudo_random_function_t *prf,
-                                                          encryption_algorithm_t *encr, chunk_t *iv)
-{
-       asn1_parser_t *parser;
-       chunk_t object, params;
-       int objectID;
-
-       parser = asn1_parser_create(pbes2ParamsObjects, blob);
-
-       while (parser->iterate(parser, &objectID, &object))
-       {
-               switch (objectID)
-               {
-                       case PBES2PARAMS_KEY_DERIVATION_FUNC:
-                       {
-                               int oid = asn1_parse_algorithmIdentifier(object,
-                                                                       parser->get_level(parser) + 1, &params);
-                               if (oid != OID_PBKDF2)
-                               {       /* unsupported key derivation function */
-                                       goto end;
-                               }
-                               parse_pbkdf2_params(params, salt, iterations, key_len, prf);
-                               break;
-                       }
-                       case PBES2PARAMS_ENCRYPTION_SCHEME:
-                       {
-                               int oid = asn1_parse_algorithmIdentifier(object,
-                                                                       parser->get_level(parser) + 1, &params);
-                               if (oid != OID_3DES_EDE_CBC)
-                               {       /* unsupported encryption scheme */
-                                       goto end;
-                               }
-                               if (*key_len <= 0)
-                               {       /* default key len for DES-EDE3-CBC-Pad */
-                                       *key_len = 24;
-                               }
-                               if (!asn1_parse_simple_object(&params, ASN1_OCTET_STRING,
-                                                                       parser->get_level(parser) + 1, "IV"))
-                               {
-                                       goto end;
-                               }
-                               *encr = ENCR_3DES;
-                               *iv = params;
-                               break;
-                       }
-               }
-       }
-
-end:
-       parser->destroy(parser);
-}
-
-/**
- * ASN.1 definition of a PBEParameter structure
- */
-static const asn1Object_t pbeParameterObjects[] = {
-       { 0, "PBEParameter",            ASN1_SEQUENCE,          ASN1_NONE       }, /* 0 */
-       { 1,   "salt",                          ASN1_OCTET_STRING,      ASN1_BODY       }, /* 1 */
-       { 1,   "iterationCount",        ASN1_INTEGER,           ASN1_BODY       }, /* 2 */
-       { 0, "exit",                            ASN1_EOC,                       ASN1_EXIT       }
-};
-#define PBEPARAM_SALT                                  1
-#define PBEPARAM_ITERATION_COUNT               2
-
-/**
- * Parse a PBEParameter structure
- */
-static void parse_pbe_parameters(chunk_t blob, chunk_t *salt,
-                                                                u_int64_t *iterations)
-{
-       asn1_parser_t *parser;
-       chunk_t object;
-       int objectID;
-
-       parser = asn1_parser_create(pbeParameterObjects, blob);
-
-       while (parser->iterate(parser, &objectID, &object))
-       {
-               switch (objectID)
-               {
-                       case PBEPARAM_SALT:
-                       {
-                               *salt = object;
-                               break;
-                       }
-                       case PBEPARAM_ITERATION_COUNT:
-                       {
-                               *iterations = parse_asn1_integer_uint64(object);
-                               break;
-                       }
-               }
-       }
-
-       parser->destroy(parser);
-}
-
-/**
  * ASN.1 definition of an encryptedPrivateKeyInfo structure
  */
 static const asn1Object_t encryptedPKIObjects[] = {
@@ -563,14 +153,10 @@ static const asn1Object_t encryptedPKIObjects[] = {
 static private_key_t *parse_encrypted_private_key(chunk_t blob)
 {
        asn1_parser_t *parser;
-       chunk_t object, params, salt = chunk_empty, iv = chunk_empty;
-       u_int64_t iterations = 0;
+       chunk_t object;
        int objectID;
-       encryption_algorithm_t encr = ENCR_UNDEFINED;
-       hash_algorithm_t hash = HASH_UNKNOWN;
-       pseudo_random_function_t prf = PRF_UNDEFINED;
        private_key_t *key = NULL;
-       size_t key_len = 8;
+       pkcs5_t *pkcs5 = NULL;
 
        parser = asn1_parser_create(encryptedPKIObjects, blob);
 
@@ -580,49 +166,24 @@ static private_key_t *parse_encrypted_private_key(chunk_t blob)
                {
                        case EPKINFO_ENCRYPTION_ALGORITHM:
                        {
-                               int oid = asn1_parse_algorithmIdentifier(object,
-                                                                       parser->get_level(parser) + 1, &params);
-
-                               switch (oid)
+                               pkcs5 = pkcs5_from_algorithmIdentifier(object,
+                                                                                               parser->get_level(parser) + 1);
+                               if (!pkcs5)
                                {
-                                       case OID_PBE_MD5_DES_CBC:
-                                               encr = ENCR_DES;
-                                               hash = HASH_MD5;
-                                               parse_pbe_parameters(params, &salt, &iterations);
-                                               break;
-                                       case OID_PBE_SHA1_DES_CBC:
-                                               encr = ENCR_DES;
-                                               hash = HASH_SHA1;
-                                               parse_pbe_parameters(params, &salt, &iterations);
-                                               break;
-                                       case OID_PBES2:
-                                               parse_pbes2_params(params, &salt, &iterations,
-                                                                                  &key_len, &prf, &encr, &iv);
-                                               break;
-                                       default:
-                                               /* encryption scheme not supported */
-                                               goto end;
+                                       goto end;
                                }
                                break;
                        }
                        case EPKINFO_ENCRYPTED_DATA:
                        {
-                               if (prf != PRF_UNDEFINED)
-                               {
-                                       key = decrypt_private_key_pbes2(object, encr, key_len, iv,
-                                                                                                       prf, salt, iterations);
-                               }
-                               else
-                               {
-                                       key = decrypt_private_key_pbes1(object, encr, key_len, hash,
-                                                                                                       salt, iterations);
-                               }
+                               key = decrypt_private_key(pkcs5, object);
                                break;
                        }
                }
        }
 
 end:
+       DESTROY_IF(pkcs5);
        parser->destroy(parser);
        return key;
 }