From 341fd32b46363cad4c2caee3fca166695100ba07 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Thu, 3 Nov 2016 10:30:13 -0400 Subject: Move cert/x509 to top level and pem and pbes2 to pubkey. The `cert` dir was just an artifact of having previously supported CVC (smartcard cert format), removed a long time ago. The pem and pbes2 code is directly related to the pubkey code, in fact the only caller of pbes2 (likely anywhere, not just in the library) is in pkcs8.cpp --- src/lib/pubkey/pbes2/info.txt | 9 +++ src/lib/pubkey/pbes2/pbes2.cpp | 176 +++++++++++++++++++++++++++++++++++++++++ src/lib/pubkey/pbes2/pbes2.h | 47 +++++++++++ 3 files changed, 232 insertions(+) create mode 100644 src/lib/pubkey/pbes2/info.txt create mode 100644 src/lib/pubkey/pbes2/pbes2.cpp create mode 100644 src/lib/pubkey/pbes2/pbes2.h (limited to 'src/lib/pubkey/pbes2') diff --git a/src/lib/pubkey/pbes2/info.txt b/src/lib/pubkey/pbes2/info.txt new file mode 100644 index 000000000..ed88ac3eb --- /dev/null +++ b/src/lib/pubkey/pbes2/info.txt @@ -0,0 +1,9 @@ +define PKCS5_PBES2 20141119 + + +asn1 +cbc +hmac +oid_lookup +pbkdf2 + diff --git a/src/lib/pubkey/pbes2/pbes2.cpp b/src/lib/pubkey/pbes2/pbes2.cpp new file mode 100644 index 000000000..c66b293e8 --- /dev/null +++ b/src/lib/pubkey/pbes2/pbes2.cpp @@ -0,0 +1,176 @@ +/* +* PKCS #5 PBES2 +* (C) 1999-2008,2014 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Botan { + +namespace { + +/* +* Encode PKCS#5 PBES2 parameters +*/ +std::vector encode_pbes2_params(const std::string& cipher, + const std::string& prf, + const secure_vector& salt, + const secure_vector& iv, + size_t iterations, + size_t key_length) + { + return DER_Encoder() + .start_cons(SEQUENCE) + .encode( + AlgorithmIdentifier("PKCS5.PBKDF2", + DER_Encoder() + .start_cons(SEQUENCE) + .encode(salt, OCTET_STRING) + .encode(iterations) + .encode(key_length) + .encode_if( + prf != "HMAC(SHA-160)", + AlgorithmIdentifier(prf, AlgorithmIdentifier::USE_NULL_PARAM)) + .end_cons() + .get_contents_unlocked() + ) + ) + .encode( + AlgorithmIdentifier(cipher, + DER_Encoder().encode(iv, OCTET_STRING).get_contents_unlocked() + ) + ) + .end_cons() + .get_contents_unlocked(); + } + +} + +/* +* PKCS#5 v2.0 PBE Constructor +*/ +std::pair> +pbes2_encrypt(const secure_vector& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + const std::string prf = "HMAC(" + digest + ")"; + + const std::vector cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + + const secure_vector salt = rng.random_vec(12); + + if(cipher_spec[1] != "CBC" && cipher_spec[1] != "GCM") + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + std::unique_ptr enc(get_cipher_mode(cipher, ENCRYPTION)); + + if(!enc) + throw Decoding_Error("PBE-PKCS5 cannot encrypt no cipher " + cipher); + + std::unique_ptr pbkdf(get_pbkdf("PBKDF2(" + prf + ")")); + + const size_t key_length = enc->key_spec().maximum_keylength(); + size_t iterations = 0; + + secure_vector iv = rng.random_vec(enc->default_nonce_length()); + + enc->set_key(pbkdf->derive_key(key_length, passphrase, salt.data(), salt.size(), + msec, iterations).bits_of()); + + enc->start(iv); + secure_vector buf = key_bits; + enc->finish(buf); + + AlgorithmIdentifier id( + OIDS::lookup("PBE-PKCS5v20"), + encode_pbes2_params(cipher, prf, salt, iv, iterations, key_length)); + + return std::make_pair(id, unlock(buf)); + } + +secure_vector +pbes2_decrypt(const secure_vector& key_bits, + const std::string& passphrase, + const std::vector& params) + { + AlgorithmIdentifier kdf_algo, enc_algo; + + BER_Decoder(params) + .start_cons(SEQUENCE) + .decode(kdf_algo) + .decode(enc_algo) + .verify_end() + .end_cons(); + + AlgorithmIdentifier prf_algo; + + if(kdf_algo.oid != OIDS::lookup("PKCS5.PBKDF2")) + throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " + + kdf_algo.oid.as_string()); + + secure_vector salt; + size_t iterations = 0, key_length = 0; + + BER_Decoder(kdf_algo.parameters) + .start_cons(SEQUENCE) + .decode(salt, OCTET_STRING) + .decode(iterations) + .decode_optional(key_length, INTEGER, UNIVERSAL) + .decode_optional(prf_algo, SEQUENCE, CONSTRUCTED, + AlgorithmIdentifier("HMAC(SHA-160)", + AlgorithmIdentifier::USE_NULL_PARAM)) + .verify_end() + .end_cons(); + + const std::string cipher = OIDS::lookup(enc_algo.oid); + const std::vector cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + if(cipher_spec[1] != "CBC" && cipher_spec[1] != "GCM") + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + if(salt.size() < 8) + throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small"); + + secure_vector iv; + BER_Decoder(enc_algo.parameters).decode(iv, OCTET_STRING).verify_end(); + + const std::string prf = OIDS::lookup(prf_algo.oid); + + std::unique_ptr pbkdf(get_pbkdf("PBKDF2(" + prf + ")")); + + std::unique_ptr dec(get_cipher_mode(cipher, DECRYPTION)); + if(!dec) + throw Decoding_Error("PBE-PKCS5 cannot decrypt no cipher " + cipher); + + if(key_length == 0) + key_length = dec->key_spec().maximum_keylength(); + + dec->set_key(pbkdf->pbkdf_iterations(key_length, passphrase, salt.data(), salt.size(), iterations)); + + dec->start(iv); + + secure_vector buf = key_bits; + dec->finish(buf); + + return buf; + } + +} diff --git a/src/lib/pubkey/pbes2/pbes2.h b/src/lib/pubkey/pbes2/pbes2.h new file mode 100644 index 000000000..7c8c4095d --- /dev/null +++ b/src/lib/pubkey/pbes2/pbes2.h @@ -0,0 +1,47 @@ +/* +* PKCS #5 v2.0 PBE +* (C) 1999-2007,2014 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PBE_PKCS_v20_H__ +#define BOTAN_PBE_PKCS_v20_H__ + +#include +#include +#include + +namespace Botan { + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param msec how many milliseconds to run PBKDF2 +* @param cipher specifies the block cipher to use to encrypt +* @param digest specifies the PRF to use with PBKDF2 (eg "HMAC(SHA-1)") +* @param rng a random number generator +*/ +std::pair> +BOTAN_DLL pbes2_encrypt(const secure_vector& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Decrypt a PKCS #5 v2.0 encrypted stream +* @param key_bits the input +* @param passphrase the passphrase to use for decryption +* @param params the PBES2 parameters +*/ +secure_vector +BOTAN_DLL pbes2_decrypt(const secure_vector& key_bits, + const std::string& passphrase, + const std::vector& params); + +} + +#endif -- cgit v1.2.3