]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Added support for PKCS#5 v2 schemes when decrypting PKCS#8 files.
authorTobias Brunner <tobias@strongswan.org>
Tue, 31 Jan 2012 17:54:00 +0000 (18:54 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 1 Feb 2012 17:27:46 +0000 (18:27 +0100)
src/libstrongswan/asn1/asn1.c
src/libstrongswan/asn1/oid.txt
src/libstrongswan/plugins/pkcs8/pkcs8_builder.c

index 4fac0a49c1f5526f8a3390685feac5a0642516c8..4cb38d1266cf9057e03b157da1ecaee9741ca387 100644 (file)
@@ -552,17 +552,20 @@ bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const c
  * ASN.1 definition of an algorithmIdentifier
  */
 static const asn1Object_t algorithmIdentifierObjects[] = {
-       { 0, "algorithmIdentifier",     ASN1_SEQUENCE,  ASN1_NONE                       }, /* 0 */
-       { 1,   "algorithm",                     ASN1_OID,               ASN1_BODY                       }, /* 1 */
-       { 1,   "parameters",            ASN1_OID,               ASN1_RAW|ASN1_OPT       }, /* 2 */
-       { 1,   "end opt",                       ASN1_EOC,               ASN1_END                        }, /* 3 */
-       { 1,   "parameters",            ASN1_SEQUENCE,  ASN1_RAW|ASN1_OPT       }, /* 4 */
-       { 1,   "end opt",                       ASN1_EOC,               ASN1_END                        }, /* 5 */
-       { 0, "exit",                            ASN1_EOC,               ASN1_EXIT                       }
+       { 0, "algorithmIdentifier",     ASN1_SEQUENCE,          ASN1_NONE                       }, /* 0 */
+       { 1,   "algorithm",                     ASN1_OID,                       ASN1_BODY                       }, /* 1 */
+       { 1,   "parameters",            ASN1_OID,                       ASN1_RAW|ASN1_OPT       }, /* 2 */
+       { 1,   "end opt",                       ASN1_EOC,                       ASN1_END                        }, /* 3 */
+       { 1,   "parameters",            ASN1_SEQUENCE,          ASN1_RAW|ASN1_OPT       }, /* 4 */
+       { 1,   "end opt",                       ASN1_EOC,                       ASN1_END                        }, /* 5 */
+       { 1,   "parameters",            ASN1_OCTET_STRING,      ASN1_RAW|ASN1_OPT       }, /* 6 */
+       { 1,   "end opt",                       ASN1_EOC,                       ASN1_END                        }, /* 7 */
+       { 0, "exit",                            ASN1_EOC,                       ASN1_EXIT                       }
 };
 #define ALGORITHM_ID_ALG                               1
 #define ALGORITHM_ID_PARAMETERS_OID            2
 #define ALGORITHM_ID_PARAMETERS_SEQ            4
+#define ALGORITHM_ID_PARAMETERS_OCT            6
 
 /*
  * Defined in header
@@ -586,6 +589,7 @@ int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters
                                break;
                        case ALGORITHM_ID_PARAMETERS_OID:
                        case ALGORITHM_ID_PARAMETERS_SEQ:
+                       case ALGORITHM_ID_PARAMETERS_OCT:
                                if (parameters != NULL)
                                {
                                        *parameters = object;
index a9a616301dd46ac34fa023d8ffcb68bc1641b761..1f843643ebe0e3dd859a8dae31d35898ed5dfb4d 100644 (file)
               0x05           "PKCS-5"
                 0x03         "pbeWithMD5AndDES-CBC"            OID_PBE_MD5_DES_CBC
                 0x0A         "pbeWithSHA1AndDES-CBC"   OID_PBE_SHA1_DES_CBC
+                0x0C         "id-PBKDF2"                               OID_PBKDF2
+                0x0D         "id-PBES2"                                        OID_PBES2
               0x07           "PKCS-7"
                 0x01         "data"                                            OID_PKCS7_DATA
                 0x02         "signedData"                              OID_PKCS7_SIGNED_DATA
index 184179b5da292b092b7b0725e096ff66eee8952c..cd3c3dba0784000ed92c50bc0e1784fefdb95efe 100644 (file)
@@ -123,6 +123,146 @@ static bool verify_padding(chunk_t *blob)
        return TRUE;
 }
 
+/**
+ * Prototype for key derivation functions.
+ */
+typedef void (*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.
+ */
+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)
+{
+       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;
+
+               kdf(generator, shared->get_key(shared), salt, iterations, keymat);
+
+               crypter->set_key(crypter, key);
+               crypter->decrypt(crypter, blob, iv, &decrypted);
+               if (verify_padding(&decrypted))
+               {
+                       private_key = parse_private_key(decrypted);
+                       if (private_key)
+                       {
+                               chunk_clear(&decrypted);
+                               break;
+                       }
+               }
+               chunk_free(&decrypted);
+       }
+       enumerator->destroy(enumerator);
+       crypter->destroy(crypter);
+
+       return private_key;
+}
+
+/**
+ * Function F of PBKDF2
+ */
+static void 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));
+       prf->get_bytes(prf, seed, u.ptr);
+       memcpy(block.ptr, u.ptr, block.len);
+
+       for (i = 1; i < iterations; i++)
+       {
+               prf->get_bytes(prf, u, u.ptr);
+               memxor(block.ptr, u.ptr, block.len);
+       }
+}
+
+/**
+ * PBKDF2 key derivation function
+ */
+static void 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;
+
+       prf->set_key(prf, password);
+
+       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);
+               pbkdf2_f(block, prf, seed, iterations);
+       }
+
+       memcpy(key.ptr, keymat.ptr, key.len);
+}
+
+/**
+ * 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
  */
@@ -145,17 +285,14 @@ static void pbkdf1(hasher_t *hasher, chunk_t password, chunk_t salt,
 }
 
 /**
- * Decrypt an encrypted PKCS#8 encoded private key
+ * Decrypt an encrypted PKCS#8 encoded private key according to PBES1
  */
-static private_key_t *decrypt_private_key(chunk_t blob,
-                                                       encryption_algorithm_t encr, size_t key_size,
+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)
 {
-       enumerator_t *enumerator;
-       shared_key_t *shared;
        private_key_t *private_key = NULL;
-       crypter_t *crypter = NULL;
        hasher_t *hasher = NULL;
        chunk_t keymat, key, iv;
 
@@ -166,57 +303,173 @@ static private_key_t *decrypt_private_key(chunk_t blob,
                         hash_algorithm_names, hash);
                goto end;
        }
-       if (hasher->get_hash_size(hasher) < key_size)
+       if (hasher->get_hash_size(hasher) < key_len)
        {
                goto end;
        }
 
-       crypter = lib->crypto->create_crypter(lib->crypto, encr, key_size);
-       if (!crypter)
-       {
-               DBG1(DBG_ASN, "  %N encryption algorithm not available",
-                        encryption_algorithm_names, encr);
-               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];
        }
-       if (blob.len % crypter->get_block_size(crypter))
+       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))
        {
-               DBG1(DBG_ASN, "  data size is not a multiple of block size");
-               goto end;
+               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;
+                       }
+               }
        }
 
-       keymat = chunk_alloca(key_size * 2);
-       key.len = key_size;
-       key.ptr = keymat.ptr;
-       iv.len = key_size;
-       iv.ptr = keymat.ptr + key_size;
+       parser->destroy(parser);
+}
 
-       enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
-                                                                               SHARED_PRIVATE_KEY_PASS, NULL, NULL);
-       while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
-       {
-               chunk_t decrypted;
+/**
+ * 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;
 
-               pbkdf1(hasher, shared->get_key(shared), salt, iterations, keymat);
+       parser = asn1_parser_create(pbes2ParamsObjects, blob);
 
-               crypter->set_key(crypter, key);
-               crypter->decrypt(crypter, blob, iv, &decrypted);
-               if (verify_padding(&decrypted))
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
                {
-                       private_key = parse_private_key(decrypted);
-                       if (private_key)
+                       case PBES2PARAMS_KEY_DERIVATION_FUNC:
                        {
-                               chunk_clear(&decrypted);
+                               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;
                        }
                }
-               chunk_free(&decrypted);
        }
-       enumerator->destroy(enumerator);
 
 end:
-       DESTROY_IF(crypter);
-       DESTROY_IF(hasher);
-       return private_key;
+       parser->destroy(parser);
 }
 
 /**
@@ -254,15 +507,7 @@ static void parse_pbe_parameters(chunk_t blob, chunk_t *salt,
                        }
                        case PBEPARAM_ITERATION_COUNT:
                        {
-                               u_int64_t val = 0;
-                               int i;
-
-                               for (i = 0; i < object.len; i++)
-                               {       /* if it is longer than 8 bytes, we just use the 8 LSBs */
-                                       val <<= 8;
-                                       val |= (u_int64_t)object.ptr[i];
-                               }
-                               *iterations = val;
+                               *iterations = parse_asn1_integer_uint64(object);
                                break;
                        }
                }
@@ -285,18 +530,19 @@ static const asn1Object_t encryptedPKIObjects[] = {
 
 /**
  * Load an encrypted private key from an ASN.1 encoded blob
- * Schemes per PKCS#5 (RFC 2898), currently only a subset of PBES1 is supported
+ * Schemes per PKCS#5 (RFC 2898)
  */
 static private_key_t *parse_encrypted_private_key(chunk_t blob)
 {
        asn1_parser_t *parser;
-       chunk_t object, params = chunk_empty, salt;
+       chunk_t object, params, salt, iv;
        u_int64_t iterations;
        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_size;
+       size_t key_len = 8;
 
        parser = asn1_parser_create(encryptedPKIObjects, blob);
 
@@ -314,15 +560,17 @@ static private_key_t *parse_encrypted_private_key(chunk_t blob)
                                        case OID_PBE_MD5_DES_CBC:
                                                encr = ENCR_DES;
                                                hash = HASH_MD5;
-                                               key_size = 8;
                                                parse_pbe_parameters(params, &salt, &iterations);
                                                break;
                                        case OID_PBE_SHA1_DES_CBC:
                                                encr = ENCR_DES;
                                                hash = HASH_SHA1;
-                                               key_size = 8;
                                                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;
@@ -331,8 +579,16 @@ static private_key_t *parse_encrypted_private_key(chunk_t blob)
                        }
                        case EPKINFO_ENCRYPTED_DATA:
                        {
-                               key = decrypt_private_key(object, encr, key_size, hash, salt,
-                                                                                 iterations);
+                               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);
+                               }
                                break;
                        }
                }