aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlloyd <[email protected]>2012-05-31 18:19:43 +0000
committerlloyd <[email protected]>2012-05-31 18:19:43 +0000
commitb82642c328d98f2aaa1ac17aa0999e69e7152ae8 (patch)
treea2a181c26709bd7995d519c9148c6f0bc06f143f
parent75db296a459a9e25b112207707cc5e26a6f2b872 (diff)
Add new PBKDF interface that takes a std::chrono::milliseconds and
runs the KDF until at least that much time has passed, then returns the number of interations used. New parameter to the PKCS8 encryption routines which tells how long to run the PBKDF. Defaults to 200 milliseconds, which is short enough that it is unlikely to bother anyone but long enough to provide quite reasonable security against cracking attacks. On a Core i7-860, 200 ms with PBKDF2/SHA-1 runs about 180K to 220K iterations (compare with previous default of 10K). New PBE interface, remove new_params/set_key and require all inputs including the passphrase to be passed to the constructor. Drop the PGP S2K as it is pretty weird and not really useful outside of a full PGP implementation. Drop the deprecated PKCS8::encrypt_key and PKCS8::encode functions.
-rw-r--r--checks/validate.dat31
-rw-r--r--src/engine/core_engine/lookup_pbkdf.cpp9
-rw-r--r--src/pbe/get_pbe.cpp29
-rw-r--r--src/pbe/get_pbe.h9
-rw-r--r--src/pbe/pbe.h18
-rw-r--r--src/pbe/pbes1/pbes1.cpp155
-rw-r--r--src/pbe/pbes1/pbes1.h31
-rw-r--r--src/pbe/pbes2/pbes2.cpp142
-rw-r--r--src/pbe/pbes2/pbes2.h19
-rw-r--r--src/pbkdf/pbkdf.h44
-rw-r--r--src/pbkdf/pbkdf1/pbkdf1.cpp38
-rw-r--r--src/pbkdf/pbkdf1/pbkdf1.h10
-rw-r--r--src/pbkdf/pbkdf2/pbkdf2.cpp67
-rw-r--r--src/pbkdf/pbkdf2/pbkdf2.h16
-rw-r--r--src/pbkdf/pgps2k/info.txt5
-rw-r--r--src/pbkdf/pgps2k/pgp_s2k.cpp57
-rw-r--r--src/pbkdf/pgps2k/pgp_s2k.h49
-rw-r--r--src/pubkey/pkcs8.cpp29
-rw-r--r--src/pubkey/pkcs8.h70
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.