diff options
author | Tobias Brunner <tobias@strongswan.org> | 2012-01-31 18:54:00 +0100 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2012-02-01 18:27:46 +0100 |
commit | fd1ff46f614397c951f8bcc6ca2e346db2441b19 (patch) | |
tree | 3739a0317cd9f5d9c595b91e1bc0682b61ae635d /src | |
parent | 1f2e036b3ec2b9cd71242d6b508aa0f5a7b3d5f2 (diff) | |
download | strongswan-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.c | 18 | ||||
-rw-r--r-- | src/libstrongswan/asn1/oid.txt | 2 | ||||
-rw-r--r-- | src/libstrongswan/plugins/pkcs8/pkcs8_builder.c | 364 |
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, ¶ms); + 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, ¶ms); + 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(¶ms, 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; } } |