/* * Copyright (C) 2012 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include "pkcs8_builder.h" #include #include #include #include #include /** * ASN.1 definition of a privateKeyInfo structure */ static const asn1Object_t pkinfoObjects[] = { { 0, "privateKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ { 1, "privateKeyAlgorithm", ASN1_EOC, ASN1_RAW }, /* 2 */ { 1, "privateKey", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */ { 1, "attributes", ASN1_CONTEXT_C_0, ASN1_OPT }, /* 4 */ { 1, "end opt", ASN1_EOC, ASN1_END }, /* 5 */ { 0, "exit", ASN1_EOC, ASN1_EXIT } }; #define PKINFO_PRIVATE_KEY_ALGORITHM 2 #define PKINFO_PRIVATE_KEY 3 /** * Load a generic private key from an ASN.1 encoded blob */ static private_key_t *parse_private_key(chunk_t blob) { asn1_parser_t *parser; chunk_t object, params = chunk_empty; int objectID; private_key_t *key = NULL; key_type_t type = KEY_ANY; parser = asn1_parser_create(pkinfoObjects, blob); parser->set_flags(parser, FALSE, TRUE); while (parser->iterate(parser, &objectID, &object)) { switch (objectID) { case PKINFO_PRIVATE_KEY_ALGORITHM: { int oid = asn1_parse_algorithmIdentifier(object, parser->get_level(parser) + 1, ¶ms); switch (oid) { case OID_RSA_ENCRYPTION: type = KEY_RSA; break; case OID_EC_PUBLICKEY: type = KEY_ECDSA; break; default: /* key type not supported */ goto end; } break; } case PKINFO_PRIVATE_KEY: { DBG2(DBG_ASN, "-- > --"); if (params.ptr) { key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type, BUILD_BLOB_ALGID_PARAMS, params, BUILD_BLOB_ASN1_DER, object, BUILD_END); } else { key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type, BUILD_BLOB_ASN1_DER, object, BUILD_END); } DBG2(DBG_ASN, "-- < --"); break; } } } end: parser->destroy(parser); return key; } /** * 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. */ 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; if (!kdf(generator, shared->get_key(shared), salt, iterations, keymat)) { continue; } if (!crypter->set_key(crypter, key) || !crypter->decrypt(crypter, blob, iv, &decrypted)) { continue; } 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 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, ¶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; } } } 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[] = { { 0, "encryptedPrivateKeyInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ { 1, "encryptionAlgorithm", ASN1_EOC, ASN1_RAW }, /* 1 */ { 1, "encryptedData", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */ { 0, "exit", ASN1_EOC, ASN1_EXIT } }; #define EPKINFO_ENCRYPTION_ALGORITHM 1 #define EPKINFO_ENCRYPTED_DATA 2 /** * Load an encrypted private key from an ASN.1 encoded blob * 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, salt = chunk_empty, iv = chunk_empty; u_int64_t iterations = 0; 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; parser = asn1_parser_create(encryptedPKIObjects, blob); while (parser->iterate(parser, &objectID, &object)) { switch (objectID) { case EPKINFO_ENCRYPTION_ALGORITHM: { int oid = asn1_parse_algorithmIdentifier(object, parser->get_level(parser) + 1, ¶ms); switch (oid) { 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; } 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); } break; } } } end: parser->destroy(parser); return key; } /** * See header. */ private_key_t *pkcs8_private_key_load(key_type_t type, va_list args) { chunk_t blob = chunk_empty; private_key_t *key; while (TRUE) { switch (va_arg(args, builder_part_t)) { case BUILD_BLOB_ASN1_DER: blob = va_arg(args, chunk_t); continue; case BUILD_END: break; default: return NULL; } break; } /* we don't know whether it is encrypted or not, try both ways */ key = parse_encrypted_private_key(blob); if (!key) { key = parse_private_key(blob); } return key; }