diff options
author | lloyd <[email protected]> | 2014-11-19 12:45:07 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2014-11-19 12:45:07 +0000 |
commit | 840fc0e4dfcb9578b9b1bfd3da0b8fd8a1fa8534 (patch) | |
tree | 8d907b91cfb796bf1357f276b017724ad2354964 /src/lib/constructs | |
parent | 80858693243f3774c2b3cd9084fb5aaafc542b06 (diff) |
Cleanup PBES2 and add GCM support
Diffstat (limited to 'src/lib/constructs')
-rw-r--r-- | src/lib/constructs/pbes2/info.txt | 12 | ||||
-rw-r--r-- | src/lib/constructs/pbes2/pbes2.cpp | 189 | ||||
-rw-r--r-- | src/lib/constructs/pbes2/pbes2.h | 50 |
3 files changed, 251 insertions, 0 deletions
diff --git a/src/lib/constructs/pbes2/info.txt b/src/lib/constructs/pbes2/info.txt new file mode 100644 index 000000000..e1f260966 --- /dev/null +++ b/src/lib/constructs/pbes2/info.txt @@ -0,0 +1,12 @@ +define PKCS5_PBES2 20141119 + +<requires> +algo_factory +asn1 +block +cbc +hash +hmac +oid_lookup +pbkdf2 +</requires> diff --git a/src/lib/constructs/pbes2/pbes2.cpp b/src/lib/constructs/pbes2/pbes2.cpp new file mode 100644 index 000000000..afce0fce1 --- /dev/null +++ b/src/lib/constructs/pbes2/pbes2.cpp @@ -0,0 +1,189 @@ +/* +* PKCS #5 PBES2 +* (C) 1999-2008,2014 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/pbes2.h> +#include <botan/pbkdf2.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/parsing.h> +#include <botan/alg_id.h> +#include <botan/oids.h> +#include <botan/rng.h> +#include <botan/cbc.h> +#include <algorithm> + +#if defined(BOTAN_HAS_AEAD_GCM) + #include <botan/gcm.h> +#endif + +namespace Botan { + +namespace { + +/* +* Encode PKCS#5 PBES2 parameters +*/ +std::vector<byte> encode_pbes2_params(const std::string& cipher, + const std::string& prf, + const secure_vector<byte>& salt, + const secure_vector<byte>& 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<AlgorithmIdentifier, std::vector<byte>> +pbes2_encrypt(const secure_vector<byte>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng, + Algorithm_Factory& af) + { + const std::string prf = "HMAC(" + digest + ")"; + + const std::vector<std::string> cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + + const secure_vector<byte> salt = rng.random_vec(12); + + std::unique_ptr<Keyed_Transform> enc; + + if(cipher_spec[1] == "CBC") + enc.reset(new CBC_Encryption(af.make_block_cipher(cipher_spec[0]), new PKCS7_Padding)); +#if defined(BOTAN_HAS_AEAD_GCM) + else if(cipher_spec[1] == "GCM") + enc.reset(new GCM_Encryption(af.make_block_cipher(cipher_spec[0]))); +#endif + else + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + PKCS5_PBKDF2 pbkdf(af.make_mac(prf)); + + const size_t key_length = enc->key_spec().maximum_keylength(); + size_t iterations = 0; + + secure_vector<byte> iv = rng.random_vec(enc->default_nonce_length()); + + enc->set_key(pbkdf.derive_key(key_length, passphrase, &salt[0], salt.size(), + msec, iterations).bits_of()); + + enc->start(iv); + secure_vector<byte> 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<byte> +pbes2_decrypt(const secure_vector<byte>& key_bits, + const std::string& passphrase, + const std::vector<byte>& params, + Algorithm_Factory& af) + { + 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<byte> 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<std::string> cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Decoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + + if(salt.size() < 8) + throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small"); + + secure_vector<byte> iv; + BER_Decoder(enc_algo.parameters).decode(iv, OCTET_STRING).verify_end(); + + PKCS5_PBKDF2 pbkdf(af.make_mac(OIDS::lookup(prf_algo.oid))); + + std::unique_ptr<Keyed_Transform> dec; + + if(cipher_spec[1] == "CBC") + dec.reset(new CBC_Decryption(af.make_block_cipher(cipher_spec[0]), new PKCS7_Padding)); +#if defined(BOTAN_HAS_AEAD_GCM) + else if(cipher_spec[1] == "GCM") + dec.reset(new GCM_Decryption(af.make_block_cipher(cipher_spec[0]))); +#endif + else + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + if(key_length == 0) + key_length = dec->key_spec().maximum_keylength(); + + dec->set_key(pbkdf.derive_key(key_length, passphrase, &salt[0], salt.size(), + iterations).bits_of()); + + dec->start(iv); + + secure_vector<byte> buf = key_bits; + dec->finish(buf); + + return buf; + } + +} diff --git a/src/lib/constructs/pbes2/pbes2.h b/src/lib/constructs/pbes2/pbes2.h new file mode 100644 index 000000000..ac88f6adb --- /dev/null +++ b/src/lib/constructs/pbes2/pbes2.h @@ -0,0 +1,50 @@ +/* +* PKCS #5 v2.0 PBE +* (C) 1999-2007,2014 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_PBE_PKCS_v20_H__ +#define BOTAN_PBE_PKCS_v20_H__ + +#include <botan/secmem.h> +#include <botan/transform.h> +#include <botan/alg_id.h> +#include <botan/algo_factory.h> +#include <chrono> + +namespace Botan { + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @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<AlgorithmIdentifier, std::vector<byte>> +BOTAN_DLL pbes2_encrypt(const secure_vector<byte>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng, + Algorithm_Factory& af); + +/** +* 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<byte> +BOTAN_DLL pbes2_decrypt(const secure_vector<byte>& key_bits, + const std::string& passphrase, + const std::vector<byte>& params, + Algorithm_Factory& af); + +} + +#endif |