strongswan/src/libstrongswan/plugins/pkcs8/pkcs8_builder.c

650 lines
15 KiB
C

/*
* 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 <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 "pkcs8_builder.h"
#include <debug.h>
#include <asn1/oid.h>
#include <asn1/asn1.h>
#include <asn1/asn1_parser.h>
#include <credentials/keys/private_key.h>
/**
* 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, &params);
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;
}
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 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;
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);
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));
hasher->get_hash(hasher, password, NULL);
hasher->get_hash(hasher, salt, hash.ptr);
for (i = 1; i < iterations; i++)
{
hasher->get_hash(hasher, hash, hash.ptr);
}
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[] = {
{ 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, &params);
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;
}