diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/pubkey/pbes2')
-rw-r--r-- | comm/third_party/botan/src/lib/pubkey/pbes2/info.txt | 10 | ||||
-rw-r--r-- | comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp | 339 | ||||
-rw-r--r-- | comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.h | 87 |
3 files changed, 436 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/pubkey/pbes2/info.txt b/comm/third_party/botan/src/lib/pubkey/pbes2/info.txt new file mode 100644 index 0000000000..f8c6d37196 --- /dev/null +++ b/comm/third_party/botan/src/lib/pubkey/pbes2/info.txt @@ -0,0 +1,10 @@ +<defines> +PKCS5_PBES2 -> 20141119 +</defines> + +<requires> +asn1 +cbc +hmac +pbkdf2 +</requires> diff --git a/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp b/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp new file mode 100644 index 0000000000..1360de67c5 --- /dev/null +++ b/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.cpp @@ -0,0 +1,339 @@ +/* +* PKCS #5 PBES2 +* (C) 1999-2008,2014 Jack Lloyd +* (C) 2018 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/pbes2.h> +#include <botan/cipher_mode.h> +#include <botan/pbkdf.h> +#include <botan/pwdhash.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/parsing.h> +#include <botan/asn1_obj.h> +#include <botan/oids.h> +#include <botan/rng.h> + +#if defined(BOTAN_HAS_SCRYPT) + #include <botan/scrypt.h> +#endif + +namespace Botan { + +namespace { + +bool known_pbes_cipher_mode(const std::string& mode) + { + return (mode == "CBC" || mode == "GCM" || mode == "SIV"); + } + +SymmetricKey derive_key(const std::string& passphrase, + const AlgorithmIdentifier& kdf_algo, + size_t default_key_size) + { + if(kdf_algo.get_oid() == OID::from_string("PKCS5.PBKDF2")) + { + secure_vector<uint8_t> salt; + size_t iterations = 0, key_length = 0; + + AlgorithmIdentifier prf_algo; + BER_Decoder(kdf_algo.get_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)) + .end_cons(); + + if(salt.size() < 8) + throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small"); + + if(key_length == 0) + key_length = default_key_size; + + const std::string prf = OIDS::oid2str_or_throw(prf_algo.get_oid()); + std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(" + prf + ")")); + return pbkdf->pbkdf_iterations(key_length, passphrase, salt.data(), salt.size(), iterations); + } +#if defined(BOTAN_HAS_SCRYPT) + else if(kdf_algo.get_oid() == OID::from_string("Scrypt")) + { + secure_vector<uint8_t> salt; + size_t N = 0, r = 0, p = 0; + size_t key_length = 0; + + AlgorithmIdentifier prf_algo; + BER_Decoder(kdf_algo.get_parameters()) + .start_cons(SEQUENCE) + .decode(salt, OCTET_STRING) + .decode(N) + .decode(r) + .decode(p) + .decode_optional(key_length, INTEGER, UNIVERSAL) + .end_cons(); + + if(key_length == 0) + key_length = default_key_size; + + secure_vector<uint8_t> output(key_length); + scrypt(output.data(), output.size(), passphrase, + salt.data(), salt.size(), N, r, p); + + return SymmetricKey(output); + } +#endif + else + throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " + + kdf_algo.get_oid().to_string()); + } + +secure_vector<uint8_t> derive_key(const std::string& passphrase, + const std::string& digest, + RandomNumberGenerator& rng, + size_t* msec_in_iterations_out, + size_t iterations_if_msec_null, + size_t key_length, + AlgorithmIdentifier& kdf_algo) + { + const secure_vector<uint8_t> salt = rng.random_vec(12); + + if(digest == "Scrypt") + { +#if defined(BOTAN_HAS_SCRYPT) + + std::unique_ptr<PasswordHashFamily> pwhash_fam = PasswordHashFamily::create_or_throw("Scrypt"); + + std::unique_ptr<PasswordHash> pwhash; + + if(msec_in_iterations_out) + { + const std::chrono::milliseconds msec(*msec_in_iterations_out); + pwhash = pwhash_fam->tune(key_length, msec); + } + else + { + pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); + } + + secure_vector<uint8_t> key(key_length); + pwhash->derive_key(key.data(), key.size(), + passphrase.c_str(), passphrase.size(), + salt.data(), salt.size()); + + const size_t N = pwhash->memory_param(); + const size_t r = pwhash->iterations(); + const size_t p = pwhash->parallelism(); + + if(msec_in_iterations_out) + *msec_in_iterations_out = 0; + + std::vector<uint8_t> scrypt_params; + DER_Encoder(scrypt_params) + .start_cons(SEQUENCE) + .encode(salt, OCTET_STRING) + .encode(N) + .encode(r) + .encode(p) + .encode(key_length) + .end_cons(); + + kdf_algo = AlgorithmIdentifier(OID::from_string("Scrypt"), scrypt_params); + return key; +#else + throw Not_Implemented("Scrypt is not available in this build"); +#endif + } + else + { + const std::string prf = "HMAC(" + digest + ")"; + const std::string pbkdf_name = "PBKDF2(" + prf + ")"; + + std::unique_ptr<PasswordHashFamily> pwhash_fam = PasswordHashFamily::create(pbkdf_name); + if(!pwhash_fam) + throw Invalid_Argument("Unknown password hash digest " + digest); + + std::unique_ptr<PasswordHash> pwhash; + + if(msec_in_iterations_out) + { + const std::chrono::milliseconds msec(*msec_in_iterations_out); + pwhash = pwhash_fam->tune(key_length, msec); + } + else + { + pwhash = pwhash_fam->from_iterations(iterations_if_msec_null); + } + + secure_vector<uint8_t> key(key_length); + pwhash->derive_key(key.data(), key.size(), + passphrase.c_str(), passphrase.size(), + salt.data(), salt.size()); + + std::vector<uint8_t> pbkdf2_params; + + const size_t iterations = pwhash->iterations(); + + if(msec_in_iterations_out) + *msec_in_iterations_out = iterations; + + DER_Encoder(pbkdf2_params) + .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(); + + kdf_algo = AlgorithmIdentifier("PKCS5.PBKDF2", pbkdf2_params); + return key; + } + } + +/* +* PKCS#5 v2.0 PBE Encryption +*/ +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_shared(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t* msec_in_iterations_out, + size_t iterations_if_msec_null, + const std::string& cipher, + const std::string& prf, + RandomNumberGenerator& rng) + { + const std::vector<std::string> cipher_spec = split_on(cipher, '/'); + if(cipher_spec.size() != 2) + throw Encoding_Error("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher); + + if(!known_pbes_cipher_mode(cipher_spec[1])) + throw Encoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + const OID cipher_oid = OIDS::str2oid_or_empty(cipher); + if(cipher_oid.empty()) + throw Encoding_Error("PBE-PKCS5 v2.0: No OID assigned for " + cipher); + + std::unique_ptr<Cipher_Mode> enc = Cipher_Mode::create(cipher, ENCRYPTION); + + if(!enc) + throw Decoding_Error("PBE-PKCS5 cannot encrypt no cipher " + cipher); + + const size_t key_length = enc->key_spec().maximum_keylength(); + + const secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length()); + + AlgorithmIdentifier kdf_algo; + + const secure_vector<uint8_t> derived_key = + derive_key(passphrase, prf, rng, + msec_in_iterations_out, iterations_if_msec_null, + key_length, kdf_algo); + + enc->set_key(derived_key); + enc->start(iv); + secure_vector<uint8_t> ctext = key_bits; + enc->finish(ctext); + + std::vector<uint8_t> encoded_iv; + DER_Encoder(encoded_iv).encode(iv, OCTET_STRING); + + std::vector<uint8_t> pbes2_params; + DER_Encoder(pbes2_params) + .start_cons(SEQUENCE) + .encode(kdf_algo) + .encode(AlgorithmIdentifier(cipher, encoded_iv)) + .end_cons(); + + AlgorithmIdentifier id(OID::from_string("PBE-PKCS5v20"), pbes2_params); + + return std::make_pair(id, unlock(ctext)); + } + +} + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + size_t msec_in_iterations_out = static_cast<size_t>(msec.count()); + return pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng); + // return value msec_in_iterations_out discarded + } + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + size_t* out_iterations_if_nonnull, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + size_t msec_in_iterations_out = static_cast<size_t>(msec.count()); + + auto ret = pbes2_encrypt_shared(key_bits, passphrase, &msec_in_iterations_out, 0, cipher, digest, rng); + + if(out_iterations_if_nonnull) + *out_iterations_if_nonnull = msec_in_iterations_out; + + return ret; + } + +std::pair<AlgorithmIdentifier, std::vector<uint8_t>> +pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t pbkdf_iter, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + return pbes2_encrypt_shared(key_bits, passphrase, nullptr, pbkdf_iter, cipher, digest, rng); + } + +secure_vector<uint8_t> +pbes2_decrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + const std::vector<uint8_t>& params) + { + AlgorithmIdentifier kdf_algo, enc_algo; + + BER_Decoder(params) + .start_cons(SEQUENCE) + .decode(kdf_algo) + .decode(enc_algo) + .end_cons(); + + const std::string cipher = OIDS::oid2str_or_throw(enc_algo.get_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(!known_pbes_cipher_mode(cipher_spec[1])) + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + secure_vector<uint8_t> iv; + BER_Decoder(enc_algo.get_parameters()).decode(iv, OCTET_STRING).verify_end(); + + std::unique_ptr<Cipher_Mode> dec = Cipher_Mode::create(cipher, DECRYPTION); + if(!dec) + throw Decoding_Error("PBE-PKCS5 cannot decrypt no cipher " + cipher); + + dec->set_key(derive_key(passphrase, kdf_algo, dec->key_spec().maximum_keylength())); + + dec->start(iv); + + secure_vector<uint8_t> buf = key_bits; + dec->finish(buf); + + return buf; + } + +} diff --git a/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.h b/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.h new file mode 100644 index 0000000000..a5051a8fd9 --- /dev/null +++ b/comm/third_party/botan/src/lib/pubkey/pbes2/pbes2.h @@ -0,0 +1,87 @@ +/* +* 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 <botan/asn1_obj.h> +#include <chrono> + +BOTAN_FUTURE_INTERNAL_HEADER(pbes2.h) + +namespace Botan { + +class RandomNumberGenerator; + +/** +* 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<AlgorithmIdentifier, std::vector<uint8_t>> +BOTAN_PUBLIC_API(2,0) pbes2_encrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* 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 out_iterations_if_nonnull if not null, set to the number +* of PBKDF iterations used +* @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<uint8_t>> +BOTAN_PUBLIC_API(2,1) pbes2_encrypt_msec(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + size_t* out_iterations_if_nonnull, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng); + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param key_bits the input +* @param passphrase the passphrase to use for encryption +* @param iterations how many iterations 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<uint8_t>> +BOTAN_PUBLIC_API(2,1) pbes2_encrypt_iter(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + size_t iterations, + 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<uint8_t> +BOTAN_PUBLIC_API(2,0) pbes2_decrypt(const secure_vector<uint8_t>& key_bits, + const std::string& passphrase, + const std::vector<uint8_t>& params); + +} + +#endif |