diff options
-rw-r--r-- | checks/validate.dat | 31 | ||||
-rw-r--r-- | src/engine/core_engine/lookup_pbkdf.cpp | 9 | ||||
-rw-r--r-- | src/pbe/get_pbe.cpp | 29 | ||||
-rw-r--r-- | src/pbe/get_pbe.h | 9 | ||||
-rw-r--r-- | src/pbe/pbe.h | 18 | ||||
-rw-r--r-- | src/pbe/pbes1/pbes1.cpp | 155 | ||||
-rw-r--r-- | src/pbe/pbes1/pbes1.h | 31 | ||||
-rw-r--r-- | src/pbe/pbes2/pbes2.cpp | 142 | ||||
-rw-r--r-- | src/pbe/pbes2/pbes2.h | 19 | ||||
-rw-r--r-- | src/pbkdf/pbkdf.h | 44 | ||||
-rw-r--r-- | src/pbkdf/pbkdf1/pbkdf1.cpp | 38 | ||||
-rw-r--r-- | src/pbkdf/pbkdf1/pbkdf1.h | 10 | ||||
-rw-r--r-- | src/pbkdf/pbkdf2/pbkdf2.cpp | 67 | ||||
-rw-r--r-- | src/pbkdf/pbkdf2/pbkdf2.h | 16 | ||||
-rw-r--r-- | src/pbkdf/pgps2k/info.txt | 5 | ||||
-rw-r--r-- | src/pbkdf/pgps2k/pgp_s2k.cpp | 57 | ||||
-rw-r--r-- | src/pbkdf/pgps2k/pgp_s2k.h | 49 | ||||
-rw-r--r-- | src/pubkey/pkcs8.cpp | 29 | ||||
-rw-r--r-- | src/pubkey/pkcs8.h | 70 |
19 files changed, 373 insertions, 455 deletions
diff --git a/checks/validate.dat b/checks/validate.dat index 7b221db49..f50b63547 100644 --- a/checks/validate.dat +++ b/checks/validate.dat @@ -68860,37 +68860,6 @@ A09661392376F7044D9052A397883246B67F5F1EF63EB5FB::24 # PBKDF format: passphrase:output:salt:out_len:iterations -[OpenPGP-S2K(SHA-1)] -666F6F:0BEEC7B5EA3F0FDBC95D0DD47F3C5BC275DA8A335A8CAA4039FDBC02C01A649C::32:0 -616263:A9993E364706816ABA3E25717850C26C9CD0D89DDD3742EC1A4D2A5B563A2B62::32:0 - -666F6F:DFFE49EEC99E3530FF75A794773E1F8429A46835925DAED4A27FA2957BBD29B5:\ -698619A932D101BE:32:0 - -666F6F:8051BB97BB42199330C9D52383D1B56532FF9BDFB180BD2BD61F24A25265639F:\ -EFB0A8DDE02BAB42:32:65536 - -696C696B65706965:A32F874A4CF95DFAD8359302B395455C:2AE6E5831A717917:16:65536 - -666F6F626172:9B1D52CC0DA89C9D85B91EC84B6780FB:AD8FC3C853BBB225:16:65536 - -717765727479:67CACC2B4B6F1B76E620748C1E777070:3891D354B8C86161:16:0 - -4141414141414141414141414141414141414141414141414141414141414141\ -414141414141414141414141414141414141:080316AFB4E11D98120B29D1070CE749::16:0 - -696C6F766573616B757261:109D161363DF1E97322112F82169911A::16:0 - -[OpenPGP-S2K(MD5)] -666F6F626172:\ -3858F62230AC3C915F300C664312C63F26E4AEBF38BF1BB6AD49BA54BCACD9DB::32:0 - -616263646566:99C8FD9A0516337E7C0F71563D937B09B14F7E7B6CB53FB56B5B8137E189DA20:\ -B89343B95A46FECE:32:65536 - -666F6F:CC364F2BC41FACAD36E0D45E581D61F707BA925C982BEF7E:\ -760566702FEE54C8:24:65536 - [PBKDF1(MD2)] 71616C7A73656774:7C1991F3F38A09D70CF3B1ACADB70BC6:\ 40CF117C3865E0CF:16:1000 diff --git a/src/engine/core_engine/lookup_pbkdf.cpp b/src/engine/core_engine/lookup_pbkdf.cpp index 2419f4373..bfd2c8bc2 100644 --- a/src/engine/core_engine/lookup_pbkdf.cpp +++ b/src/engine/core_engine/lookup_pbkdf.cpp @@ -17,10 +17,6 @@ #include <botan/pbkdf2.h> #endif -#if defined(BOTAN_HAS_PGPS2K) - #include <botan/pgp_s2k.h> -#endif - namespace Botan { PBKDF* Core_Engine::find_pbkdf(const SCAN_Name& algo_spec, @@ -41,11 +37,6 @@ PBKDF* Core_Engine::find_pbkdf(const SCAN_Name& algo_spec, } #endif -#if defined(BOTAN_HAS_PGPS2K) - if(algo_spec.algo_name() == "OpenPGP-S2K" && algo_spec.arg_count() == 1) - return new OpenPGP_S2K(af.make_hash_function(algo_spec.arg(0))); -#endif - return nullptr; } diff --git a/src/pbe/get_pbe.cpp b/src/pbe/get_pbe.cpp index 3217101ef..9ce830639 100644 --- a/src/pbe/get_pbe.cpp +++ b/src/pbe/get_pbe.cpp @@ -24,7 +24,10 @@ namespace Botan { /* * Get an encryption PBE, set new parameters */ -PBE* get_pbe(const std::string& algo_spec) +PBE* get_pbe(const std::string& algo_spec, + const std::string& passphrase, + std::chrono::milliseconds msec, + RandomNumberGenerator& rng) { SCAN_Name request(algo_spec); @@ -59,13 +62,18 @@ PBE* get_pbe(const std::string& algo_spec) if(pbe == "PBE-PKCS5v15") return new PBE_PKCS5v15(block_cipher->clone(), hash_function->clone(), - ENCRYPTION); + passphrase, + msec, + rng); #endif #if defined(BOTAN_HAS_PBE_PKCS_V20) if(pbe == "PBE-PKCS5v20") return new PBE_PKCS5v20(block_cipher->clone(), - hash_function->clone()); + hash_function->clone(), + passphrase, + msec, + rng); #endif throw Algorithm_Not_Found(algo_spec); @@ -74,7 +82,9 @@ PBE* get_pbe(const std::string& algo_spec) /* * Get a decryption PBE, decode parameters */ -PBE* get_pbe(const OID& pbe_oid, DataSource& params) +PBE* get_pbe(const OID& pbe_oid, + const std::vector<byte>& params, + const std::string& passphrase) { SCAN_Name request(OIDS::lookup(pbe_oid)); @@ -111,17 +121,16 @@ PBE* get_pbe(const OID& pbe_oid, DataSource& params) if(!hash_function) throw Algorithm_Not_Found(digest_name); - PBE* pbe = new PBE_PKCS5v15(block_cipher->clone(), - hash_function->clone(), - DECRYPTION); - pbe->decode_params(params); - return pbe; + return new PBE_PKCS5v15(block_cipher->clone(), + hash_function->clone(), + params, + passphrase); } #endif #if defined(BOTAN_HAS_PBE_PKCS_V20) if(pbe == "PBE-PKCS5v20") - return new PBE_PKCS5v20(params); + return new PBE_PKCS5v20(params, passphrase); #endif throw Algorithm_Not_Found(pbe_oid.as_string()); diff --git a/src/pbe/get_pbe.h b/src/pbe/get_pbe.h index 73c53497c..719b92c30 100644 --- a/src/pbe/get_pbe.h +++ b/src/pbe/get_pbe.h @@ -9,6 +9,7 @@ #define BOTAN_LOOKUP_PBE_H__ #include <botan/pbe.h> +#include <vector> #include <string> namespace Botan { @@ -18,7 +19,10 @@ namespace Botan { * @param algo_spec the name of the PBE algorithm to retrieve * @return pointer to a PBE with randomly created parameters */ -BOTAN_DLL PBE* get_pbe(const std::string& algo_spec); +BOTAN_DLL PBE* get_pbe(const std::string& algo_spec, + const std::string& passphrase, + std::chrono::milliseconds msec, + RandomNumberGenerator& rng); /** * Factory function for PBEs. @@ -27,7 +31,8 @@ BOTAN_DLL PBE* get_pbe(const std::string& algo_spec); * @return pointer to the PBE with the specified parameters */ BOTAN_DLL PBE* get_pbe(const OID& pbe_oid, - DataSource& params); + const std::vector<byte>& params, + const std::string& password); } diff --git a/src/pbe/pbe.h b/src/pbe/pbe.h index 975f3e6c7..45c98e2c8 100644 --- a/src/pbe/pbe.h +++ b/src/pbe/pbe.h @@ -22,30 +22,12 @@ class BOTAN_DLL PBE : public Filter { public: /** - * Set this filter's key. - * @param pw the password to be used for the encryption - */ - virtual void set_key(const std::string& pw) = 0; - - /** - * Create a new random salt value and set the default iterations value. - * @param rng a random number generator - */ - virtual void new_params(RandomNumberGenerator& rng) = 0; - - /** * DER encode the params (the number of iterations and the salt value) * @return encoded params */ virtual std::vector<byte> encode_params() const = 0; /** - * Decode params and use them inside this Filter. - * @param src a data source to read the encoded params from - */ - virtual void decode_params(DataSource& src) = 0; - - /** * Get this PBE's OID. * @return object identifier */ diff --git a/src/pbe/pbes1/pbes1.cpp b/src/pbe/pbes1/pbes1.cpp index 41a793a24..e86a496ac 100644 --- a/src/pbe/pbes1/pbes1.cpp +++ b/src/pbe/pbes1/pbes1.cpp @@ -19,7 +19,7 @@ namespace Botan { */ void PBE_PKCS5v15::write(const byte input[], size_t length) { - pipe.write(input, length); + m_pipe.write(input, length); flush_pipe(true); } @@ -28,18 +28,18 @@ void PBE_PKCS5v15::write(const byte input[], size_t length) */ void PBE_PKCS5v15::start_msg() { - if(direction == ENCRYPTION) - pipe.append(new CBC_Encryption(block_cipher->clone(), - new PKCS7_Padding, - key, iv)); + if(m_direction == ENCRYPTION) + m_pipe.append(new CBC_Encryption(m_block_cipher->clone(), + new PKCS7_Padding, + m_key, m_iv)); else - pipe.append(new CBC_Decryption(block_cipher->clone(), - new PKCS7_Padding, - key, iv)); + m_pipe.append(new CBC_Decryption(m_block_cipher->clone(), + new PKCS7_Padding, + m_key, m_iv)); - pipe.start_msg(); - if(pipe.message_count() > 1) - pipe.set_default_msg(pipe.default_msg() + 1); + m_pipe.start_msg(); + if(m_pipe.message_count() > 1) + m_pipe.set_default_msg(m_pipe.default_msg() + 1); } /* @@ -47,9 +47,9 @@ void PBE_PKCS5v15::start_msg() */ void PBE_PKCS5v15::end_msg() { - pipe.end_msg(); + m_pipe.end_msg(); flush_pipe(false); - pipe.reset(); + m_pipe.reset(); } /* @@ -57,81 +57,39 @@ void PBE_PKCS5v15::end_msg() */ void PBE_PKCS5v15::flush_pipe(bool safe_to_skip) { - if(safe_to_skip && pipe.remaining() < 64) + if(safe_to_skip && m_pipe.remaining() < 64) return; secure_vector<byte> buffer(DEFAULT_BUFFERSIZE); - while(pipe.remaining()) + while(m_pipe.remaining()) { - size_t got = pipe.read(&buffer[0], buffer.size()); + size_t got = m_pipe.read(&buffer[0], buffer.size()); send(buffer, got); } } /* -* Set the passphrase to use -*/ -void PBE_PKCS5v15::set_key(const std::string& passphrase) - { - PKCS5_PBKDF1 pbkdf(hash_function->clone()); - - secure_vector<byte> key_and_iv = pbkdf.derive_key(16, passphrase, - &salt[0], salt.size(), - iterations).bits_of(); - - key.resize(8); - iv.resize(8); - copy_mem(&key[0], &key_and_iv[0], 8); - copy_mem(&iv[0], &key_and_iv[8], 8); - } - -/* -* Create a new set of PBES1 parameters -*/ -void PBE_PKCS5v15::new_params(RandomNumberGenerator& rng) - { - iterations = 10000; - salt = rng.random_vec(8); - } - -/* * Encode PKCS#5 PBES1 parameters */ std::vector<byte> PBE_PKCS5v15::encode_params() const { return DER_Encoder() .start_cons(SEQUENCE) - .encode(salt, OCTET_STRING) - .encode(iterations) + .encode(m_salt, OCTET_STRING) + .encode(m_iterations) .end_cons() .get_contents_unlocked(); } /* -* Decode PKCS#5 PBES1 parameters -*/ -void PBE_PKCS5v15::decode_params(DataSource& source) - { - BER_Decoder(source) - .start_cons(SEQUENCE) - .decode(salt, OCTET_STRING) - .decode(iterations) - .verify_end() - .end_cons(); - - if(salt.size() != 8) - throw Decoding_Error("PBES1: Encoded salt is not 8 octets"); - } - -/* * Return an OID for this PBES1 type */ OID PBE_PKCS5v15::get_oid() const { const OID base_pbes1_oid("1.2.840.113549.1.5"); - const std::string cipher = block_cipher->name(); - const std::string digest = hash_function->name(); + const std::string cipher = m_block_cipher->name(); + const std::string digest = m_hash_function->name(); if(cipher == "DES" && digest == "MD2") return (base_pbes1_oid + 1); @@ -151,17 +109,52 @@ OID PBE_PKCS5v15::get_oid() const std::string PBE_PKCS5v15::name() const { - return "PBE-PKCS5v15(" + block_cipher->name() + "," + - hash_function->name() + ")"; + return "PBE-PKCS5v15(" + m_block_cipher->name() + "," + + m_hash_function->name() + ")"; + } + +PBE_PKCS5v15::PBE_PKCS5v15(BlockCipher* cipher, + HashFunction* hash, + const std::string& passphrase, + std::chrono::milliseconds msec, + RandomNumberGenerator& rng) : + m_direction(ENCRYPTION), + m_block_cipher(cipher), + m_hash_function(hash), + m_salt(rng.random_vec(8)) + { + if(cipher->name() != "DES" && cipher->name() != "RC2") + { + throw Invalid_Argument("PBE_PKCS5v1.5: Unknown cipher " + + cipher->name()); + } + + if(hash->name() != "MD2" && hash->name() != "MD5" && + hash->name() != "SHA-160") + { + throw Invalid_Argument("PBE_PKCS5v1.5: Unknown hash " + + hash->name()); + } + + PKCS5_PBKDF1 pbkdf(m_hash_function->clone()); + + secure_vector<byte> key_and_iv = + pbkdf.derive_key(16, passphrase, + &m_salt[0], m_salt.size(), + msec, m_iterations).bits_of(); + + m_key.assign(&key_and_iv[0], &key_and_iv[8]); + m_iv.assign(&key_and_iv[8], &key_and_iv[16]); + } -/* -* PKCS#5 v1.5 PBE Constructor -*/ PBE_PKCS5v15::PBE_PKCS5v15(BlockCipher* cipher, HashFunction* hash, - Cipher_Dir dir) : - direction(dir), block_cipher(cipher), hash_function(hash) + const std::vector<byte>& params, + const std::string& passphrase) : + m_direction(DECRYPTION), + m_block_cipher(cipher), + m_hash_function(hash) { if(cipher->name() != "DES" && cipher->name() != "RC2") { @@ -175,12 +168,32 @@ PBE_PKCS5v15::PBE_PKCS5v15(BlockCipher* cipher, throw Invalid_Argument("PBE_PKCS5v1.5: Unknown hash " + hash->name()); } + + BER_Decoder(params) + .start_cons(SEQUENCE) + .decode(m_salt, OCTET_STRING) + .decode(m_iterations) + .verify_end() + .end_cons(); + + if(m_salt.size() != 8) + throw Decoding_Error("PBES1: Encoded salt is not 8 octets"); + + PKCS5_PBKDF1 pbkdf(m_hash_function->clone()); + + secure_vector<byte> key_and_iv = + pbkdf.derive_key(16, passphrase, + &m_salt[0], m_salt.size(), + m_iterations).bits_of(); + + m_key.assign(&key_and_iv[0], &key_and_iv[8]); + m_iv.assign(&key_and_iv[8], &key_and_iv[16]); } PBE_PKCS5v15::~PBE_PKCS5v15() { - delete block_cipher; - delete hash_function; + delete m_block_cipher; + delete m_hash_function; } } diff --git a/src/pbe/pbes1/pbes1.h b/src/pbe/pbes1/pbes1.h index bbdbd5b9d..0c921dadd 100644 --- a/src/pbe/pbes1/pbes1.h +++ b/src/pbe/pbes1/pbes1.h @@ -21,6 +21,10 @@ namespace Botan { class BOTAN_DLL PBE_PKCS5v15 : public PBE { public: + OID get_oid() const; + + std::vector<byte> encode_params() const; + std::string name() const; void write(const byte[], size_t); @@ -30,29 +34,30 @@ class BOTAN_DLL PBE_PKCS5v15 : public PBE /** * @param cipher the block cipher to use (DES or RC2) * @param hash the hash function to use - * @param direction are we encrypting or decrypting */ PBE_PKCS5v15(BlockCipher* cipher, HashFunction* hash, - Cipher_Dir direction); + const std::string& passphrase, + std::chrono::milliseconds msec, + RandomNumberGenerator& rng); + + PBE_PKCS5v15(BlockCipher* cipher, + HashFunction* hash, + const std::vector<byte>& params, + const std::string& passphrase); ~PBE_PKCS5v15(); private: - void set_key(const std::string&); - void new_params(RandomNumberGenerator& rng); - std::vector<byte> encode_params() const; - void decode_params(DataSource&); - OID get_oid() const; void flush_pipe(bool); - Cipher_Dir direction; - BlockCipher* block_cipher; - HashFunction* hash_function; + Cipher_Dir m_direction; + BlockCipher* m_block_cipher; + HashFunction* m_hash_function; - secure_vector<byte> salt, key, iv; - size_t iterations; - Pipe pipe; + secure_vector<byte> m_salt, m_key, m_iv; + size_t m_iterations; + Pipe m_pipe; }; } diff --git a/src/pbe/pbes2/pbes2.cpp b/src/pbe/pbes2/pbes2.cpp index 0036359cc..dc8ffbbcd 100644 --- a/src/pbe/pbes2/pbes2.cpp +++ b/src/pbe/pbes2/pbes2.cpp @@ -76,30 +76,6 @@ void PBE_PKCS5v20::flush_pipe(bool safe_to_skip) } /* -* Set the passphrase to use -*/ -void PBE_PKCS5v20::set_key(const std::string& passphrase) - { - PKCS5_PBKDF2 pbkdf(new HMAC(hash_function->clone())); - - key = pbkdf.derive_key(key_length, passphrase, - &salt[0], salt.size(), - iterations).bits_of(); - } - -/* -* Create a new set of PBES2 parameters -*/ -void PBE_PKCS5v20::new_params(RandomNumberGenerator& rng) - { - iterations = 10000; - key_length = block_cipher->maximum_keylength(); - - salt = rng.random_vec(12); - iv = rng.random_vec(block_cipher->block_size()); - } - -/* * Encode PKCS#5 PBES2 parameters */ std::vector<byte> PBE_PKCS5v20::encode_params() const @@ -129,13 +105,74 @@ std::vector<byte> PBE_PKCS5v20::encode_params() const } /* -* Decode PKCS#5 PBES2 parameters +* Return an OID for PBES2 +*/ +OID PBE_PKCS5v20::get_oid() const + { + return OIDS::lookup("PBE-PKCS5v20"); + } + +/* +* Check if this is a known PBES2 cipher +*/ +bool PBE_PKCS5v20::known_cipher(const std::string& algo) + { + if(algo == "AES-128" || algo == "AES-192" || algo == "AES-256") + return true; + + if(algo == "DES" || algo == "TripleDES") + return true; + + return false; + } + +std::string PBE_PKCS5v20::name() const + { + return "PBE-PKCS5v20(" + block_cipher->name() + "," + + hash_function->name() + ")"; + } + +/* +* PKCS#5 v2.0 PBE Constructor */ -void PBE_PKCS5v20::decode_params(DataSource& source) +PBE_PKCS5v20::PBE_PKCS5v20(BlockCipher* cipher, + HashFunction* digest, + const std::string& passphrase, + std::chrono::milliseconds msec, + RandomNumberGenerator& rng) : + direction(ENCRYPTION), + block_cipher(cipher), + hash_function(digest), + salt(rng.random_vec(12)), + iv(rng.random_vec(block_cipher->block_size())), + iterations(0), + key_length(block_cipher->maximum_keylength()) { + if(!known_cipher(block_cipher->name())) + throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid cipher " + cipher->name()); + if(hash_function->name() != "SHA-160") + throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid digest " + digest->name()); + + PKCS5_PBKDF2 pbkdf(new HMAC(hash_function->clone())); + + key = pbkdf.derive_key(key_length, passphrase, + &salt[0], salt.size(), + msec, iterations).bits_of(); + } + +/* +* PKCS#5 v2.0 PBE Constructor +*/ +PBE_PKCS5v20::PBE_PKCS5v20(const std::vector<byte>& params, + const std::string& passphrase) : +direction(DECRYPTION) + { + hash_function = nullptr; + block_cipher = nullptr; + AlgorithmIdentifier kdf_algo, enc_algo; - BER_Decoder(source) + BER_Decoder(params) .start_cons(SEQUENCE) .decode(kdf_algo) .decode(enc_algo) @@ -177,55 +214,12 @@ void PBE_PKCS5v20::decode_params(DataSource& source) if(salt.size() < 8) throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small"); - } - -/* -* Return an OID for PBES2 -*/ -OID PBE_PKCS5v20::get_oid() const - { - return OIDS::lookup("PBE-PKCS5v20"); - } -/* -* Check if this is a known PBES2 cipher -*/ -bool PBE_PKCS5v20::known_cipher(const std::string& algo) - { - if(algo == "AES-128" || algo == "AES-192" || algo == "AES-256") - return true; - if(algo == "DES" || algo == "TripleDES") - return true; - return false; - } - -std::string PBE_PKCS5v20::name() const - { - return "PBE-PKCS5v20(" + block_cipher->name() + "," + - hash_function->name() + ")"; - } - -/* -* PKCS#5 v2.0 PBE Constructor -*/ -PBE_PKCS5v20::PBE_PKCS5v20(BlockCipher* cipher, - HashFunction* digest) : - direction(ENCRYPTION), block_cipher(cipher), hash_function(digest) - { - if(!known_cipher(block_cipher->name())) - throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid cipher " + cipher->name()); - if(hash_function->name() != "SHA-160") - throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid digest " + digest->name()); - } + PKCS5_PBKDF2 pbkdf(new HMAC(hash_function->clone())); -/* -* PKCS#5 v2.0 PBE Constructor -*/ -PBE_PKCS5v20::PBE_PKCS5v20(DataSource& params) : direction(DECRYPTION) - { - hash_function = nullptr; - block_cipher = nullptr; - decode_params(params); + key = pbkdf.derive_key(key_length, passphrase, + &salt[0], salt.size(), + iterations).bits_of(); } PBE_PKCS5v20::~PBE_PKCS5v20() diff --git a/src/pbe/pbes2/pbes2.h b/src/pbe/pbes2/pbes2.h index 5593c9091..635837b42 100644 --- a/src/pbe/pbes2/pbes2.h +++ b/src/pbe/pbes2/pbes2.h @@ -27,6 +27,10 @@ class BOTAN_DLL PBE_PKCS5v20 : public PBE */ static bool known_cipher(const std::string& cipher); + OID get_oid() const; + + std::vector<byte> encode_params() const; + std::string name() const; void write(const byte[], size_t); @@ -37,22 +41,21 @@ class BOTAN_DLL PBE_PKCS5v20 : public PBE * Load a PKCS #5 v2.0 encrypted stream * @param input is the input source */ - PBE_PKCS5v20(DataSource& input); + PBE_PKCS5v20(const std::vector<byte>& params, + const std::string& passphrase); /** * @param cipher the block cipher to use * @param hash the hash function to use */ - PBE_PKCS5v20(BlockCipher* cipher, HashFunction* hash); + PBE_PKCS5v20(BlockCipher* cipher, + HashFunction* hash, + const std::string& passphrase, + std::chrono::milliseconds msec, + RandomNumberGenerator& rng); ~PBE_PKCS5v20(); private: - void set_key(const std::string&); - void new_params(RandomNumberGenerator& rng); - std::vector<byte> encode_params() const; - void decode_params(DataSource&); - OID get_oid() const; - void flush_pipe(bool); Cipher_Dir direction; diff --git a/src/pbkdf/pbkdf.h b/src/pbkdf/pbkdf.h index e951b5673..176ed0302 100644 --- a/src/pbkdf/pbkdf.h +++ b/src/pbkdf/pbkdf.h @@ -1,6 +1,6 @@ /* * PBKDF -* (C) 1999-2007 Jack Lloyd +* (C) 1999-2007,2012 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -10,6 +10,7 @@ #include <botan/algo_base.h> #include <botan/symkey.h> +#include <chrono> namespace Botan { @@ -37,10 +38,43 @@ class BOTAN_DLL PBKDF : public Algorithm * @param salt_len length of salt in bytes * @param iterations the number of iterations to use (use 10K or more) */ - virtual OctetString derive_key(size_t output_len, - const std::string& passphrase, - const byte salt[], size_t salt_len, - size_t iterations) const = 0; + OctetString derive_key(size_t output_len, + const std::string& passphrase, + const byte salt[], size_t salt_len, + size_t iterations) const; + + /** + * Derive a key from a passphrase + * @param output_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param msec is how long to run the PBKDF + * @param iterations is set to the number of iterations used + */ + OctetString derive_key(size_t output_len, + const std::string& passphrase, + const byte salt[], size_t salt_len, + std::chrono::milliseconds msec, + size_t& iterations) const; + + /** + * Derive a key from a passphrase for a number of iterations + * specified by either iterations or if iterations == 0 then + * running until seconds time has elapsed. + * + * @param output_len the desired length of the key to produce + * @param passphrase the password to derive the key from + * @param salt a randomly chosen salt + * @param salt_len length of salt in bytes + * @param iterations the number of iterations to use (use 10K or more) + */ + virtual std::pair<size_t, OctetString> + key_derivation(size_t output_len, + const std::string& passphrase, + const byte salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const = 0; }; /** diff --git a/src/pbkdf/pbkdf1/pbkdf1.cpp b/src/pbkdf/pbkdf1/pbkdf1.cpp index 7f0939b8f..d51cbdc18 100644 --- a/src/pbkdf/pbkdf1/pbkdf1.cpp +++ b/src/pbkdf/pbkdf1/pbkdf1.cpp @@ -13,28 +13,46 @@ namespace Botan { /* * Return a PKCS#5 PBKDF1 derived key */ -OctetString PKCS5_PBKDF1::derive_key(size_t key_len, - const std::string& passphrase, - const byte salt[], size_t salt_size, - size_t iterations) const +std::pair<size_t, OctetString> +PKCS5_PBKDF1::key_derivation(size_t key_len, + const std::string& passphrase, + const byte salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const { - if(iterations == 0) - throw Invalid_Argument("PKCS5_PBKDF1: Invalid iteration count"); - if(key_len > hash->output_length()) throw Invalid_Argument("PKCS5_PBKDF1: Requested output length too long"); hash->update(passphrase); - hash->update(salt, salt_size); + hash->update(salt, salt_len); secure_vector<byte> key = hash->final(); - for(size_t j = 1; j != iterations; ++j) + const auto start = std::chrono::high_resolution_clock::now(); + size_t iterations_performed = 1; + + while(true) { + if(iterations == 0) + { + if(iterations_performed % 8192 == 0) + { + auto time_taken = std::chrono::high_resolution_clock::now() - start; + auto msec_taken = std::chrono::duration_cast<std::chrono::milliseconds>(time_taken); + if(msec_taken > msec) + break; + } + } + else if(iterations_performed == iterations) + break; + hash->update(key); hash->final(&key[0]); + + ++iterations_performed; } - return OctetString(&key[0], std::min<size_t>(key_len, key.size())); + return std::make_pair(iterations_performed, + OctetString(&key[0], std::min(key_len, key.size()))); } } diff --git a/src/pbkdf/pbkdf1/pbkdf1.h b/src/pbkdf/pbkdf1/pbkdf1.h index f8e2dbe69..783b70ed9 100644 --- a/src/pbkdf/pbkdf1/pbkdf1.h +++ b/src/pbkdf/pbkdf1/pbkdf1.h @@ -46,10 +46,12 @@ class BOTAN_DLL PKCS5_PBKDF1 : public PBKDF return new PKCS5_PBKDF1(hash->clone()); } - OctetString derive_key(size_t output_len, - const std::string& passphrase, - const byte salt[], size_t salt_len, - size_t iterations) const; + std::pair<size_t, OctetString> + key_derivation(size_t output_len, + const std::string& passphrase, + const byte salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const override; private: HashFunction* hash; }; diff --git a/src/pbkdf/pbkdf2/pbkdf2.cpp b/src/pbkdf/pbkdf2/pbkdf2.cpp index 699ce7c6b..c116b10ab 100644 --- a/src/pbkdf/pbkdf2/pbkdf2.cpp +++ b/src/pbkdf/pbkdf2/pbkdf2.cpp @@ -8,19 +8,22 @@ #include <botan/pbkdf2.h> #include <botan/get_byte.h> #include <botan/internal/xor_buf.h> +#include <botan/internal/rounding.h> namespace Botan { /* * Return a PKCS #5 PBKDF2 derived key */ -OctetString PKCS5_PBKDF2::derive_key(size_t key_len, - const std::string& passphrase, - const byte salt[], size_t salt_size, - size_t iterations) const +std::pair<size_t, OctetString> +PKCS5_PBKDF2::key_derivation(size_t key_len, + const std::string& passphrase, + const byte salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const { - if(iterations == 0) - throw Invalid_Argument("PKCS#5 PBKDF2: Invalid iteration count"); + if(key_len == 0) + return std::make_pair(iterations, OctetString()); try { @@ -39,22 +42,62 @@ OctetString PKCS5_PBKDF2::derive_key(size_t key_len, secure_vector<byte> U(mac->output_length()); + const size_t blocks_needed = round_up(key_len, mac->output_length()) / mac->output_length(); + + std::chrono::microseconds usec_per_block = + std::chrono::duration_cast<std::chrono::microseconds>(msec) / blocks_needed; + u32bit counter = 1; while(key_len) { size_t T_size = std::min<size_t>(mac->output_length(), key_len); - mac->update(salt, salt_size); + mac->update(salt, salt_len); mac->update_be(counter); mac->final(&U[0]); xor_buf(T, &U[0], T_size); - for(size_t j = 1; j != iterations; ++j) + if(iterations == 0) + { + /* + If no iterations set, run the first block to calibrate based + on how long hashing takes on whatever machine we're running on. + */ + + const auto start = std::chrono::high_resolution_clock::now(); + + iterations = 1; // the first iteration we did above + + while(true) + { + mac->update(U); + mac->final(&U[0]); + xor_buf(T, &U[0], T_size); + iterations++; + + /* + Only break on relatively 'even' iterations. For one it + avoids confusion, and likely some broken implementations + break on getting completely randomly distributed values + */ + if(iterations % 8192 == 0) + { + auto time_taken = std::chrono::high_resolution_clock::now() - start; + auto usec_taken = std::chrono::duration_cast<std::chrono::microseconds>(time_taken); + if(usec_taken > usec_per_block) + break; + } + } + } + else { - mac->update(U); - mac->final(&U[0]); - xor_buf(T, &U[0], T_size); + for(size_t i = 1; i != iterations; ++i) + { + mac->update(U); + mac->final(&U[0]); + xor_buf(T, &U[0], T_size); + } } key_len -= T_size; @@ -62,7 +105,7 @@ OctetString PKCS5_PBKDF2::derive_key(size_t key_len, ++counter; } - return key; + return std::make_pair(iterations, key); } } diff --git a/src/pbkdf/pbkdf2/pbkdf2.h b/src/pbkdf/pbkdf2/pbkdf2.h index 26392bdad..8bc271fcf 100644 --- a/src/pbkdf/pbkdf2/pbkdf2.h +++ b/src/pbkdf/pbkdf2/pbkdf2.h @@ -1,6 +1,6 @@ /* * PBKDF2 -* (C) 1999-2007 Jack Lloyd +* (C) 1999-2007,2012 Jack Lloyd * * Distributed under the terms of the Botan license */ @@ -19,20 +19,22 @@ namespace Botan { class BOTAN_DLL PKCS5_PBKDF2 : public PBKDF { public: - std::string name() const + std::string name() const override { return "PBKDF2(" + mac->name() + ")"; } - PBKDF* clone() const + PBKDF* clone() const override { return new PKCS5_PBKDF2(mac->clone()); } - OctetString derive_key(size_t output_len, - const std::string& passphrase, - const byte salt[], size_t salt_len, - size_t iterations) const; + std::pair<size_t, OctetString> + key_derivation(size_t output_len, + const std::string& passphrase, + const byte salt[], size_t salt_len, + size_t iterations, + std::chrono::milliseconds msec) const override; /** * Create a PKCS #5 instance using the specified message auth code diff --git a/src/pbkdf/pgps2k/info.txt b/src/pbkdf/pgps2k/info.txt deleted file mode 100644 index 8be9c79f8..000000000 --- a/src/pbkdf/pgps2k/info.txt +++ /dev/null @@ -1,5 +0,0 @@ -define PGPS2K - -<requires> -hash -</requires> diff --git a/src/pbkdf/pgps2k/pgp_s2k.cpp b/src/pbkdf/pgps2k/pgp_s2k.cpp deleted file mode 100644 index 6f6de58e2..000000000 --- a/src/pbkdf/pgps2k/pgp_s2k.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* -* OpenPGP S2K -* (C) 1999-2007 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#include <botan/pgp_s2k.h> - -namespace Botan { - -/* -* Derive a key using the OpenPGP S2K algorithm -*/ -OctetString OpenPGP_S2K::derive_key(size_t key_len, - const std::string& passphrase, - const byte salt_buf[], size_t salt_size, - size_t iterations) const - { - secure_vector<byte> key(key_len), hash_buf; - - size_t pass = 0, generated = 0, - total_size = passphrase.size() + salt_size; - size_t to_hash = std::max(iterations, total_size); - - hash->clear(); - while(key_len > generated) - { - for(size_t j = 0; j != pass; ++j) - hash->update(0); - - size_t left = to_hash; - while(left >= total_size) - { - hash->update(salt_buf, salt_size); - hash->update(passphrase); - left -= total_size; - } - if(left <= salt_size) - hash->update(salt_buf, left); - else - { - hash->update(salt_buf, salt_size); - left -= salt_size; - hash->update(reinterpret_cast<const byte*>(passphrase.data()), left); - } - - hash_buf = hash->final(); - buffer_insert(key, generated, &hash_buf[0], hash->output_length()); - generated += hash->output_length(); - ++pass; - } - - return key; - } - -} diff --git a/src/pbkdf/pgps2k/pgp_s2k.h b/src/pbkdf/pgps2k/pgp_s2k.h deleted file mode 100644 index 7620a6c84..000000000 --- a/src/pbkdf/pgps2k/pgp_s2k.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -* OpenPGP PBKDF -* (C) 1999-2007 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -#ifndef BOTAN_OPENPGP_S2K_H__ -#define BOTAN_OPENPGP_S2K_H__ - -#include <botan/pbkdf.h> -#include <botan/hash.h> - -namespace Botan { - -/** -* OpenPGP's S2K -*/ -class BOTAN_DLL OpenPGP_S2K : public PBKDF - { - public: - /** - * @param hash_in the hash function to use - */ - OpenPGP_S2K(HashFunction* hash_in) : hash(hash_in) {} - - ~OpenPGP_S2K() { delete hash; } - - std::string name() const - { - return "OpenPGP-S2K(" + hash->name() + ")"; - } - - PBKDF* clone() const - { - return new OpenPGP_S2K(hash->clone()); - } - - OctetString derive_key(size_t output_len, - const std::string& passphrase, - const byte salt[], size_t salt_len, - size_t iterations) const; - private: - HashFunction* hash; - }; - -} - -#endif diff --git a/src/pubkey/pkcs8.cpp b/src/pubkey/pkcs8.cpp index baf6d1250..23c021fdb 100644 --- a/src/pubkey/pkcs8.cpp +++ b/src/pubkey/pkcs8.cpp @@ -90,16 +90,12 @@ secure_vector<byte> PKCS8_decode( if(is_encrypted) { - DataSource_Memory params(pbe_alg_id.parameters); - std::unique_ptr<PBE> pbe(get_pbe(pbe_alg_id.oid, params)); - std::pair<bool, std::string> pass = get_passphrase(); if(pass.first == false) break; - pbe->set_key(pass.second); - Pipe decryptor(pbe.release()); + Pipe decryptor(get_pbe(pbe_alg_id.oid, pbe_alg_id.parameters, pass.second)); decryptor.process_msg(key_data); key = decryptor.read_all(); @@ -155,17 +151,19 @@ std::string PEM_encode(const Private_Key& key) /* * BER encode a PKCS #8 private key, encrypted */ -secure_vector<byte> BER_encode(const Private_Key& key, - RandomNumberGenerator& rng, - const std::string& pass, - const std::string& pbe_algo) +std::vector<byte> BER_encode(const Private_Key& key, + RandomNumberGenerator& rng, + const std::string& pass, + std::chrono::milliseconds msec, + const std::string& pbe_algo) { const std::string DEFAULT_PBE = "PBE-PKCS5v20(SHA-1,AES-256/CBC)"; - std::unique_ptr<PBE> pbe(get_pbe(((pbe_algo != "") ? pbe_algo : DEFAULT_PBE))); - - pbe->new_params(rng); - pbe->set_key(pass); + std::unique_ptr<PBE> pbe( + get_pbe(((pbe_algo != "") ? pbe_algo : DEFAULT_PBE), + pass, + msec, + rng)); AlgorithmIdentifier pbe_algid(pbe->get_oid(), pbe->encode_params()); @@ -177,7 +175,7 @@ secure_vector<byte> BER_encode(const Private_Key& key, .encode(pbe_algid) .encode(key_encrytor.read_all(), OCTET_STRING) .end_cons() - .get_contents(); + .get_contents_unlocked(); } /* @@ -186,12 +184,13 @@ secure_vector<byte> BER_encode(const Private_Key& key, std::string PEM_encode(const Private_Key& key, RandomNumberGenerator& rng, const std::string& pass, + std::chrono::milliseconds msec, const std::string& pbe_algo) { if(pass == "") return PEM_encode(key); - return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, pbe_algo), + return PEM_Code::encode(PKCS8::BER_encode(key, rng, pass, msec, pbe_algo), "ENCRYPTED PRIVATE KEY"); } diff --git a/src/pubkey/pkcs8.h b/src/pubkey/pkcs8.h index fae1633a8..04de723a6 100644 --- a/src/pubkey/pkcs8.h +++ b/src/pubkey/pkcs8.h @@ -10,6 +10,7 @@ #include <botan/x509_key.h> #include <functional> +#include <chrono> namespace Botan { @@ -46,15 +47,18 @@ BOTAN_DLL std::string PEM_encode(const Private_Key& key); * @param key the key to encode * @param rng the rng to use * @param pass the password to use for encryption +* @param msec number of milliseconds to run the password derivation * @param pbe_algo the name of the desired password-based encryption algorithm; if empty ("") a reasonable (portable/secure) default will be chosen. * @return encrypted key in binary BER form */ -BOTAN_DLL secure_vector<byte> BER_encode(const Private_Key& key, - RandomNumberGenerator& rng, - const std::string& pass, - const std::string& pbe_algo = ""); +BOTAN_DLL std::vector<byte> +BER_encode(const Private_Key& key, + RandomNumberGenerator& rng, + const std::string& pass, + std::chrono::milliseconds msec = std::chrono::milliseconds(200), + const std::string& pbe_algo = ""); /** * Get a string containing a PEM encoded private key, encrypting it with a @@ -62,62 +66,18 @@ BOTAN_DLL secure_vector<byte> BER_encode(const Private_Key& key, * @param key the key to encode * @param rng the rng to use * @param pass the password to use for encryption +* @param msec number of milliseconds to run the password derivation * @param pbe_algo the name of the desired password-based encryption algorithm; if empty ("") a reasonable (portable/secure) default will be chosen. * @return encrypted key in PEM form */ -BOTAN_DLL std::string PEM_encode(const Private_Key& key, - RandomNumberGenerator& rng, - const std::string& pass, - const std::string& pbe_algo = ""); - - -/** -* Encode a private key into a pipe. -* @deprecated Use PEM_encode or BER_encode instead -* -* @param key the private key to encode -* @param pipe the pipe to feed the encoded key into -* @param encoding the encoding type to use -*/ -BOTAN_DEPRECATED("Use PEM_encode or BER_encode") -inline void encode(const Private_Key& key, - Pipe& pipe, - X509_Encoding encoding = PEM) - { - if(encoding == PEM) - pipe.write(PKCS8::PEM_encode(key)); - else - pipe.write(PKCS8::BER_encode(key)); - } - -/** -* Encode and encrypt a private key into a pipe. -* @deprecated Use PEM_encode or BER_encode instead -* -* @param key the private key to encode -* @param pipe the pipe to feed the encoded key into -* @param pass the password to use for encryption -* @param rng the rng to use -* @param pbe_algo the name of the desired password-based encryption - algorithm; if empty ("") a reasonable (portable/secure) - default will be chosen. -* @param encoding the encoding type to use -*/ -BOTAN_DEPRECATED("Use PEM_encode or BER_encode") -inline void encrypt_key(const Private_Key& key, - Pipe& pipe, - RandomNumberGenerator& rng, - const std::string& pass, - const std::string& pbe_algo = "", - X509_Encoding encoding = PEM) - { - if(encoding == PEM) - pipe.write(PKCS8::PEM_encode(key, rng, pass, pbe_algo)); - else - pipe.write(PKCS8::BER_encode(key, rng, pass, pbe_algo)); - } +BOTAN_DLL std::string +PEM_encode(const Private_Key& key, + RandomNumberGenerator& rng, + const std::string& pass, + std::chrono::milliseconds msec = std::chrono::milliseconds(200), + const std::string& pbe_algo = ""); /** * Load a key from a data source. |