aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2012-01-31 18:54:00 +0100
committerTobias Brunner <tobias@strongswan.org>2012-02-01 18:27:46 +0100
commitfd1ff46f614397c951f8bcc6ca2e346db2441b19 (patch)
tree3739a0317cd9f5d9c595b91e1bc0682b61ae635d /src
parent1f2e036b3ec2b9cd71242d6b508aa0f5a7b3d5f2 (diff)
downloadstrongswan-fd1ff46f614397c951f8bcc6ca2e346db2441b19.tar.bz2
strongswan-fd1ff46f614397c951f8bcc6ca2e346db2441b19.tar.xz
Added support for PKCS#5 v2 schemes when decrypting PKCS#8 files.
Diffstat (limited to 'src')
-rw-r--r--src/libstrongswan/asn1/asn1.c18
-rw-r--r--src/libstrongswan/asn1/oid.txt2
-rw-r--r--src/libstrongswan/plugins/pkcs8/pkcs8_builder.c364
3 files changed, 323 insertions, 61 deletions
diff --git a/src/libstrongswan/asn1/asn1.c b/src/libstrongswan/asn1/asn1.c
index 4fac0a49c..4cb38d126 100644
--- a/src/libstrongswan/asn1/asn1.c
+++ b/src/libstrongswan/asn1/asn1.c
@@ -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;
diff --git a/src/libstrongswan/asn1/oid.txt b/src/libstrongswan/asn1/oid.txt
index a9a616301..1f843643e 100644
--- a/src/libstrongswan/asn1/oid.txt
+++ b/src/libstrongswan/asn1/oid.txt
@@ -100,6 +100,8 @@
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
diff --git a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c
index 184179b5d..cd3c3dba0 100644
--- a/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c
+++ b/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c
@@ -124,6 +124,146 @@ static bool verify_padding(chunk_t *blob)
}
/**
+ * 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
*/
static void pbkdf1(hasher_t *hasher, chunk_t password, chunk_t salt,
@@ -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;
}
}