mirror of
https://github.com/strongswan/strongswan.git
synced 2025-11-22 00:01:45 -05:00
Extract PKCS#5 handling from pkcs8 plugin to separate helper class
This commit is contained in:
parent
b715176ec4
commit
4076e3ee91
@ -8,7 +8,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
|
|||||||
collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \
|
collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \
|
||||||
collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \
|
collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \
|
||||||
crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
|
crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
|
||||||
crypto/prfs/prf.c crypto/prfs/mac_prf.c \
|
crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \
|
||||||
crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \
|
crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \
|
||||||
crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \
|
crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \
|
||||||
crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \
|
crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \
|
||||||
|
|||||||
@ -6,7 +6,7 @@ asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
|
|||||||
collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \
|
collections/blocking_queue.c collections/enumerator.c collections/hashtable.c \
|
||||||
collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \
|
collections/linked_list.c crypto/crypters/crypter.c crypto/hashers/hasher.c \
|
||||||
crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
|
crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
|
||||||
crypto/prfs/prf.c crypto/prfs/mac_prf.c \
|
crypto/prfs/prf.c crypto/prfs/mac_prf.c crypto/pkcs5.c \
|
||||||
crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \
|
crypto/rngs/rng.c crypto/prf_plus.c crypto/signers/signer.c \
|
||||||
crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \
|
crypto/signers/mac_signer.c crypto/crypto_factory.c crypto/crypto_tester.c \
|
||||||
crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \
|
crypto/diffie_hellman.c crypto/aead.c crypto/transform.c \
|
||||||
@ -45,7 +45,7 @@ crypto/proposal/proposal_keywords.h crypto/proposal/proposal_keywords_static.h \
|
|||||||
crypto/prfs/prf.h crypto/prfs/mac_prf.h crypto/rngs/rng.h crypto/nonce_gen.h \
|
crypto/prfs/prf.h crypto/prfs/mac_prf.h crypto/rngs/rng.h crypto/nonce_gen.h \
|
||||||
crypto/prf_plus.h crypto/signers/signer.h crypto/signers/mac_signer.h \
|
crypto/prf_plus.h crypto/signers/signer.h crypto/signers/mac_signer.h \
|
||||||
crypto/crypto_factory.h crypto/crypto_tester.h crypto/diffie_hellman.h \
|
crypto/crypto_factory.h crypto/crypto_tester.h crypto/diffie_hellman.h \
|
||||||
crypto/aead.h crypto/transform.h \
|
crypto/aead.h crypto/transform.h crypto/pkcs5.h \
|
||||||
credentials/credential_factory.h credentials/builder.h \
|
credentials/credential_factory.h credentials/builder.h \
|
||||||
credentials/cred_encoding.h credentials/keys/private_key.h \
|
credentials/cred_encoding.h credentials/keys/private_key.h \
|
||||||
credentials/keys/public_key.h credentials/keys/shared_key.h \
|
credentials/keys/public_key.h credentials/keys/shared_key.h \
|
||||||
|
|||||||
630
src/libstrongswan/crypto/pkcs5.c
Normal file
630
src/libstrongswan/crypto/pkcs5.c
Normal file
@ -0,0 +1,630 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012-2013 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 "pkcs5.h"
|
||||||
|
|
||||||
|
#include <utils/debug.h>
|
||||||
|
#include <asn1/oid.h>
|
||||||
|
#include <asn1/asn1.h>
|
||||||
|
#include <asn1/asn1_parser.h>
|
||||||
|
|
||||||
|
typedef struct private_pkcs5_t private_pkcs5_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data of a pkcs5_t object
|
||||||
|
*/
|
||||||
|
struct private_pkcs5_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements pkcs5_t.
|
||||||
|
*/
|
||||||
|
pkcs5_t public;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Salt used during encryption
|
||||||
|
*/
|
||||||
|
chunk_t salt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterations for key derivation
|
||||||
|
*/
|
||||||
|
u_int64_t iterations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encryption algorithm
|
||||||
|
*/
|
||||||
|
encryption_algorithm_t encr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encryption key length
|
||||||
|
*/
|
||||||
|
size_t keylen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crypter
|
||||||
|
*/
|
||||||
|
crypter_t *crypter;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The encryption scheme
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
PKCS5_SCHEME_PBES1,
|
||||||
|
PKCS5_SCHEME_PBES2,
|
||||||
|
} scheme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data used for individual schemes
|
||||||
|
*/
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
/**
|
||||||
|
* Hash algorithm
|
||||||
|
*/
|
||||||
|
hash_algorithm_t hash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hasher
|
||||||
|
*/
|
||||||
|
hasher_t *hasher;
|
||||||
|
|
||||||
|
} pbes1;
|
||||||
|
struct {
|
||||||
|
/**
|
||||||
|
* PRF algorithm
|
||||||
|
*/
|
||||||
|
pseudo_random_function_t prf_alg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PRF
|
||||||
|
*/
|
||||||
|
prf_t * prf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IV
|
||||||
|
*/
|
||||||
|
chunk_t iv;
|
||||||
|
|
||||||
|
} pbes2;
|
||||||
|
} data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)(private_pkcs5_t *this, chunk_t password, chunk_t key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to decrypt the given data with the given password 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 bool decrypt_generic(private_pkcs5_t *this, chunk_t password,
|
||||||
|
chunk_t data, chunk_t *decrypted, kdf_t kdf,
|
||||||
|
chunk_t keymat, chunk_t key, chunk_t iv)
|
||||||
|
{
|
||||||
|
if (!kdf(this, password, keymat))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!this->crypter->set_key(this->crypter, key) ||
|
||||||
|
!this->crypter->decrypt(this->crypter, data, iv, decrypted))
|
||||||
|
{
|
||||||
|
memwipe(keymat.ptr, keymat.len);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
memwipe(keymat.ptr, keymat.len);
|
||||||
|
if (verify_padding(decrypted))
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
chunk_free(decrypted);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 for PBES2, key must be allocated
|
||||||
|
*/
|
||||||
|
static bool pbkdf2(private_pkcs5_t *this, chunk_t password, chunk_t key)
|
||||||
|
{
|
||||||
|
prf_t *prf;
|
||||||
|
chunk_t keymat, block, seed;
|
||||||
|
size_t blocks;
|
||||||
|
u_int32_t i = 0;
|
||||||
|
|
||||||
|
prf = this->data.pbes2.prf;
|
||||||
|
|
||||||
|
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", this->salt, chunk_from_thing(i));
|
||||||
|
|
||||||
|
for (; i < blocks; i++)
|
||||||
|
{
|
||||||
|
htoun32(seed.ptr + this->salt.len, i + 1);
|
||||||
|
block.ptr = keymat.ptr + (i * block.len);
|
||||||
|
if (!pbkdf2_f(block, prf, seed, this->iterations))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(key.ptr, keymat.ptr, key.len);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PBKDF1 key derivation function for PBES1, key must be allocated
|
||||||
|
*/
|
||||||
|
static bool pbkdf1(private_pkcs5_t *this, chunk_t password, chunk_t key)
|
||||||
|
{
|
||||||
|
hasher_t *hasher;
|
||||||
|
chunk_t hash;
|
||||||
|
u_int64_t i;
|
||||||
|
|
||||||
|
hasher = this->data.pbes1.hasher;
|
||||||
|
|
||||||
|
hash = chunk_alloca(hasher->get_hash_size(hasher));
|
||||||
|
if (!hasher->get_hash(hasher, password, NULL) ||
|
||||||
|
!hasher->get_hash(hasher, this->salt, hash.ptr))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < this->iterations; i++)
|
||||||
|
{
|
||||||
|
if (!hasher->get_hash(hasher, hash, hash.ptr))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(key.ptr, hash.ptr, key.len);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ensure_crypto_primitives(private_pkcs5_t *this, chunk_t data)
|
||||||
|
{
|
||||||
|
if (!this->crypter)
|
||||||
|
{
|
||||||
|
this->crypter = lib->crypto->create_crypter(lib->crypto, this->encr,
|
||||||
|
this->keylen);
|
||||||
|
if (!this->crypter)
|
||||||
|
{
|
||||||
|
DBG1(DBG_ASN, " %N encryption algorithm not available",
|
||||||
|
encryption_algorithm_names, this->encr);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.len % this->crypter->get_block_size(this->crypter))
|
||||||
|
{
|
||||||
|
DBG1(DBG_ASN, " data size is not a multiple of block size");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
switch (this->scheme)
|
||||||
|
{
|
||||||
|
case PKCS5_SCHEME_PBES1:
|
||||||
|
{
|
||||||
|
if (!this->data.pbes1.hasher)
|
||||||
|
{
|
||||||
|
hasher_t *hasher;
|
||||||
|
|
||||||
|
hasher = lib->crypto->create_hasher(lib->crypto,
|
||||||
|
this->data.pbes1.hash);
|
||||||
|
if (!hasher)
|
||||||
|
{
|
||||||
|
DBG1(DBG_ASN, " %N hash algorithm not available",
|
||||||
|
hash_algorithm_names, this->data.pbes1.hash);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (hasher->get_hash_size(hasher) < this->keylen)
|
||||||
|
{
|
||||||
|
hasher->destroy(hasher);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
this->data.pbes1.hasher = hasher;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PKCS5_SCHEME_PBES2:
|
||||||
|
{
|
||||||
|
if (!this->data.pbes2.prf)
|
||||||
|
{
|
||||||
|
prf_t *prf;
|
||||||
|
|
||||||
|
prf = lib->crypto->create_prf(lib->crypto,
|
||||||
|
this->data.pbes2.prf_alg);
|
||||||
|
if (!prf)
|
||||||
|
{
|
||||||
|
DBG1(DBG_ASN, " %N prf algorithm not available",
|
||||||
|
pseudo_random_function_names,
|
||||||
|
this->data.pbes2.prf_alg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
this->data.pbes2.prf = prf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(pkcs5_t, decrypt, bool,
|
||||||
|
private_pkcs5_t *this, chunk_t password, chunk_t data, chunk_t *decrypted)
|
||||||
|
{
|
||||||
|
chunk_t keymat, key, iv;
|
||||||
|
kdf_t kdf;
|
||||||
|
|
||||||
|
if (!ensure_crypto_primitives(this, data) || !decrypted)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
switch (this->scheme)
|
||||||
|
{
|
||||||
|
case PKCS5_SCHEME_PBES1:
|
||||||
|
kdf = pbkdf1;
|
||||||
|
keymat = chunk_alloca(this->keylen * 2);
|
||||||
|
key = chunk_create(keymat.ptr, this->keylen);
|
||||||
|
iv = chunk_create(keymat.ptr + this->keylen, this->keylen);
|
||||||
|
break;
|
||||||
|
case PKCS5_SCHEME_PBES2:
|
||||||
|
kdf = pbkdf2;
|
||||||
|
keymat = chunk_alloca(this->keylen);
|
||||||
|
key = keymat;
|
||||||
|
iv = this->data.pbes2.iv;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return decrypt_generic(this, password, data, decrypted, kdf,
|
||||||
|
keymat, key, iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an ASN.1 INTEGER object to an u_int64_t. If the INTEGER is longer
|
||||||
|
* than 8 bytes only the 8 LSBs are returned.
|
||||||
|
*
|
||||||
|
* @param blob body of an ASN.1 coded integer object
|
||||||
|
* @return converted integer
|
||||||
|
*/
|
||||||
|
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 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 bool parse_pbes1_params(private_pkcs5_t *this, chunk_t blob, int level0)
|
||||||
|
{
|
||||||
|
asn1_parser_t *parser;
|
||||||
|
chunk_t object;
|
||||||
|
int objectID;
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
parser = asn1_parser_create(pbeParameterObjects, blob);
|
||||||
|
parser->set_top_level(parser, level0);
|
||||||
|
|
||||||
|
while (parser->iterate(parser, &objectID, &object))
|
||||||
|
{
|
||||||
|
switch (objectID)
|
||||||
|
{
|
||||||
|
case PBEPARAM_SALT:
|
||||||
|
{
|
||||||
|
this->salt = chunk_clone(object);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PBEPARAM_ITERATION_COUNT:
|
||||||
|
{
|
||||||
|
this->iterations = parse_asn1_integer_uint64(object);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success = parser->success(parser);
|
||||||
|
parser->destroy(parser);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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_KEYLENGTH 3
|
||||||
|
#define PBKDF2_PRF 5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a PBKDF2-params structure
|
||||||
|
*/
|
||||||
|
static bool parse_pbkdf2_params(private_pkcs5_t *this, chunk_t blob, int level0)
|
||||||
|
{
|
||||||
|
asn1_parser_t *parser;
|
||||||
|
chunk_t object;
|
||||||
|
int objectID;
|
||||||
|
bool success;
|
||||||
|
|
||||||
|
parser = asn1_parser_create(pbkdf2ParamsObjects, blob);
|
||||||
|
parser->set_top_level(parser, level0);
|
||||||
|
|
||||||
|
/* keylen is optional */
|
||||||
|
this->keylen = 0;
|
||||||
|
|
||||||
|
while (parser->iterate(parser, &objectID, &object))
|
||||||
|
{
|
||||||
|
switch (objectID)
|
||||||
|
{
|
||||||
|
case PBKDF2_SALT:
|
||||||
|
{
|
||||||
|
this->salt = chunk_clone(object);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PBKDF2_ITERATION_COUNT:
|
||||||
|
{
|
||||||
|
this->iterations = parse_asn1_integer_uint64(object);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PBKDF2_KEYLENGTH:
|
||||||
|
{
|
||||||
|
this->keylen = (size_t)parse_asn1_integer_uint64(object);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PBKDF2_PRF:
|
||||||
|
{ /* defaults to id-hmacWithSHA1, no other is currently defined */
|
||||||
|
this->data.pbes2.prf_alg = PRF_HMAC_SHA1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success = parser->success(parser);
|
||||||
|
parser->destroy(parser);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 bool parse_pbes2_params(private_pkcs5_t *this, chunk_t blob, int level0)
|
||||||
|
{
|
||||||
|
asn1_parser_t *parser;
|
||||||
|
chunk_t object, params;
|
||||||
|
int objectID;
|
||||||
|
bool success = FALSE;
|
||||||
|
|
||||||
|
parser = asn1_parser_create(pbes2ParamsObjects, blob);
|
||||||
|
parser->set_top_level(parser, level0);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (!parse_pbkdf2_params(this, params,
|
||||||
|
parser->get_level(parser) + 1))
|
||||||
|
{
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
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 (this->keylen <= 0)
|
||||||
|
{ /* default key length for DES-EDE3-CBC-Pad */
|
||||||
|
this->keylen = 24;
|
||||||
|
}
|
||||||
|
if (!asn1_parse_simple_object(¶ms, ASN1_OCTET_STRING,
|
||||||
|
parser->get_level(parser) + 1, "IV"))
|
||||||
|
{
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
this->encr = ENCR_3DES;
|
||||||
|
this->data.pbes2.iv = chunk_clone(params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
success = parser->success(parser);
|
||||||
|
end:
|
||||||
|
parser->destroy(parser);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(pkcs5_t, destroy, void,
|
||||||
|
private_pkcs5_t *this)
|
||||||
|
{
|
||||||
|
DESTROY_IF(this->crypter);
|
||||||
|
chunk_free(&this->salt);
|
||||||
|
switch (this->scheme)
|
||||||
|
{
|
||||||
|
case PKCS5_SCHEME_PBES1:
|
||||||
|
DESTROY_IF(this->data.pbes1.hasher);
|
||||||
|
break;
|
||||||
|
case PKCS5_SCHEME_PBES2:
|
||||||
|
DESTROY_IF(this->data.pbes2.prf);
|
||||||
|
chunk_free(&this->data.pbes2.iv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Described in header
|
||||||
|
*/
|
||||||
|
pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0)
|
||||||
|
{
|
||||||
|
private_pkcs5_t *this;
|
||||||
|
chunk_t params;
|
||||||
|
int oid;
|
||||||
|
|
||||||
|
INIT(this,
|
||||||
|
.public = {
|
||||||
|
.decrypt = _decrypt,
|
||||||
|
.destroy = _destroy,
|
||||||
|
},
|
||||||
|
.scheme = PKCS5_SCHEME_PBES1,
|
||||||
|
.keylen = 8,
|
||||||
|
);
|
||||||
|
|
||||||
|
oid = asn1_parse_algorithmIdentifier(blob, level0, ¶ms);
|
||||||
|
|
||||||
|
switch (oid)
|
||||||
|
{
|
||||||
|
case OID_PBE_MD5_DES_CBC:
|
||||||
|
this->encr = ENCR_DES;
|
||||||
|
this->data.pbes1.hash = HASH_MD5;
|
||||||
|
break;
|
||||||
|
case OID_PBE_SHA1_DES_CBC:
|
||||||
|
this->encr = ENCR_DES;
|
||||||
|
this->data.pbes1.hash = HASH_SHA1;
|
||||||
|
break;
|
||||||
|
case OID_PBES2:
|
||||||
|
this->scheme = PKCS5_SCHEME_PBES2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* encryption scheme not supported */
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this->scheme)
|
||||||
|
{
|
||||||
|
case PKCS5_SCHEME_PBES1:
|
||||||
|
if (!parse_pbes1_params(this, params, level0))
|
||||||
|
{
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PKCS5_SCHEME_PBES2:
|
||||||
|
if (!parse_pbes2_params(this, params, level0))
|
||||||
|
{
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return &this->public;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
61
src/libstrongswan/crypto/pkcs5.h
Normal file
61
src/libstrongswan/crypto/pkcs5.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup pkcs5 pkcs5
|
||||||
|
* @{ @ingroup crypto
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PKCS5_H_
|
||||||
|
#define PKCS5_H_
|
||||||
|
|
||||||
|
typedef struct pkcs5_t pkcs5_t;
|
||||||
|
|
||||||
|
#include <utils/chunk.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PKCS#5 helper class
|
||||||
|
*/
|
||||||
|
struct pkcs5_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt the given data using the given password and the scheme derived
|
||||||
|
* from the initial AlgorithmIdentifier object.
|
||||||
|
*
|
||||||
|
* @param password password used for decryption
|
||||||
|
* @param data data to decrypt
|
||||||
|
* @param decrypted decrypted data gets allocated
|
||||||
|
* @return TRUE on success, FALSE otherwise
|
||||||
|
*/
|
||||||
|
bool (*decrypt)(pkcs5_t *this, chunk_t password, chunk_t data,
|
||||||
|
chunk_t *decrypted) __attribute__((warn_unused_result));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the object and any associated cryptographic primitive.
|
||||||
|
*/
|
||||||
|
void (*destroy)(pkcs5_t *this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a PKCS#5 helper object from an ASN.1 encoded AlgorithmIdentifier
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* @param blob ASN.1 encoded AlgorithmIdentifier
|
||||||
|
* @param level0 ASN.1 parser level
|
||||||
|
* @return pkcs5_t object, NULL on failure
|
||||||
|
*/
|
||||||
|
pkcs5_t *pkcs5_from_algorithmIdentifier(chunk_t blob, int level0);
|
||||||
|
|
||||||
|
#endif /** PKCS5_H_ @}*/
|
||||||
@ -19,6 +19,7 @@
|
|||||||
#include <asn1/oid.h>
|
#include <asn1/oid.h>
|
||||||
#include <asn1/asn1.h>
|
#include <asn1/asn1.h>
|
||||||
#include <asn1/asn1_parser.h>
|
#include <asn1/asn1_parser.h>
|
||||||
|
#include <crypto/pkcs5.h>
|
||||||
#include <credentials/keys/private_key.h>
|
#include <credentials/keys/private_key.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,450 +101,39 @@ end:
|
|||||||
return key;
|
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
|
* Try to decrypt the given blob with multiple passwords using the given
|
||||||
* key derivation function. keymat is where the kdf function writes the key
|
* pkcs5 object.
|
||||||
* to, key and iv point to the actual keys and initialization vectors resp.
|
|
||||||
*/
|
*/
|
||||||
static private_key_t *decrypt_private_key(chunk_t blob,
|
static private_key_t *decrypt_private_key(pkcs5_t *pkcs5, 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;
|
enumerator_t *enumerator;
|
||||||
shared_key_t *shared;
|
shared_key_t *shared;
|
||||||
crypter_t *crypter;
|
|
||||||
private_key_t *private_key = NULL;
|
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,
|
enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
|
||||||
SHARED_PRIVATE_KEY_PASS, NULL, NULL);
|
SHARED_PRIVATE_KEY_PASS, NULL, NULL);
|
||||||
while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
|
while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
|
||||||
{
|
{
|
||||||
chunk_t decrypted;
|
chunk_t decrypted;
|
||||||
|
|
||||||
if (!kdf(generator, shared->get_key(shared), salt, iterations, keymat))
|
if (!pkcs5->decrypt(pkcs5, shared->get_key(shared), blob, &decrypted))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!crypter->set_key(crypter, key) ||
|
private_key = parse_private_key(decrypted);
|
||||||
!crypter->decrypt(crypter, blob, iv, &decrypted))
|
if (private_key)
|
||||||
{
|
{
|
||||||
continue;
|
chunk_clear(&decrypted);
|
||||||
}
|
break;
|
||||||
if (verify_padding(&decrypted))
|
|
||||||
{
|
|
||||||
private_key = parse_private_key(decrypted);
|
|
||||||
if (private_key)
|
|
||||||
{
|
|
||||||
chunk_clear(&decrypted);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
chunk_free(&decrypted);
|
chunk_free(&decrypted);
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
crypter->destroy(crypter);
|
|
||||||
|
|
||||||
return private_key;
|
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
|
* ASN.1 definition of an encryptedPrivateKeyInfo structure
|
||||||
*/
|
*/
|
||||||
@ -563,14 +153,10 @@ static const asn1Object_t encryptedPKIObjects[] = {
|
|||||||
static private_key_t *parse_encrypted_private_key(chunk_t blob)
|
static private_key_t *parse_encrypted_private_key(chunk_t blob)
|
||||||
{
|
{
|
||||||
asn1_parser_t *parser;
|
asn1_parser_t *parser;
|
||||||
chunk_t object, params, salt = chunk_empty, iv = chunk_empty;
|
chunk_t object;
|
||||||
u_int64_t iterations = 0;
|
|
||||||
int objectID;
|
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;
|
private_key_t *key = NULL;
|
||||||
size_t key_len = 8;
|
pkcs5_t *pkcs5 = NULL;
|
||||||
|
|
||||||
parser = asn1_parser_create(encryptedPKIObjects, blob);
|
parser = asn1_parser_create(encryptedPKIObjects, blob);
|
||||||
|
|
||||||
@ -580,49 +166,24 @@ static private_key_t *parse_encrypted_private_key(chunk_t blob)
|
|||||||
{
|
{
|
||||||
case EPKINFO_ENCRYPTION_ALGORITHM:
|
case EPKINFO_ENCRYPTION_ALGORITHM:
|
||||||
{
|
{
|
||||||
int oid = asn1_parse_algorithmIdentifier(object,
|
pkcs5 = pkcs5_from_algorithmIdentifier(object,
|
||||||
parser->get_level(parser) + 1, ¶ms);
|
parser->get_level(parser) + 1);
|
||||||
|
if (!pkcs5)
|
||||||
switch (oid)
|
|
||||||
{
|
{
|
||||||
case OID_PBE_MD5_DES_CBC:
|
goto end;
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
case EPKINFO_ENCRYPTED_DATA:
|
case EPKINFO_ENCRYPTED_DATA:
|
||||||
{
|
{
|
||||||
if (prf != PRF_UNDEFINED)
|
key = decrypt_private_key(pkcs5, object);
|
||||||
{
|
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
|
DESTROY_IF(pkcs5);
|
||||||
parser->destroy(parser);
|
parser->destroy(parser);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user