aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-03-29 10:04:26 -0400
committerJack Lloyd <[email protected]>2017-03-29 10:04:26 -0400
commit8a312886fc3b6286b0bee67ad806ff9047256903 (patch)
tree5312b33d1ad57f0a62015dfb4c7bea5b9dc0c550 /src/lib
parent484b4ef68120cc34a738837b913c6cfe1be3928e (diff)
parent8bbec86f8984b52b5d0cce8cd1309563d2b294cc (diff)
Merge GH #899 Add ability to specify iterations when encrypting a private key
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/ffi/ffi.cpp82
-rw-r--r--src/lib/ffi/ffi.h27
-rw-r--r--src/lib/pubkey/pbes2/pbes2.cpp79
-rw-r--r--src/lib/pubkey/pbes2/pbes2.h37
-rw-r--r--src/lib/pubkey/pkcs8.cpp86
-rw-r--r--src/lib/pubkey/pkcs8.h89
6 files changed, 382 insertions, 18 deletions
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp
index 75b10502c..8eb984c55 100644
--- a/src/lib/ffi/ffi.cpp
+++ b/src/lib/ffi/ffi.cpp
@@ -1374,19 +1374,93 @@ int botan_privkey_export_encrypted(botan_privkey_t key,
uint8_t out[], size_t* out_len,
botan_rng_t rng_obj,
const char* pass,
- const char* pbe,
+ const char* /*ignored - pbe*/,
uint32_t flags)
{
+ return botan_privkey_export_encrypted_pbkdf_iter(key, out, out_len, rng_obj, pass, 100000, nullptr, nullptr, flags);
+ }
+
+int botan_privkey_export_encrypted_pbkdf_msec(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng_obj,
+ const char* pass,
+ uint32_t pbkdf_msec,
+ size_t* pbkdf_iters_out,
+ const char* maybe_cipher,
+ const char* maybe_pbkdf_hash,
+ uint32_t flags)
+ {
+ return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
+ const std::chrono::milliseconds pbkdf_time(pbkdf_msec);
+ Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+
+ std::string cipher;
+ if(maybe_cipher)
+ {
+ cipher = maybe_cipher;
+ }
+
+ std::string pbkdf_hash;
+ if(maybe_pbkdf_hash)
+ {
+ pbkdf_hash = maybe_pbkdf_hash;
+ }
+
+ if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
+ {
+ return write_vec_output(out, out_len,
+ Botan::PKCS8::BER_encode_encrypted_pbkdf_msec(k, rng, pass, pbkdf_time, pbkdf_iters_out, cipher, pbkdf_hash));
+ }
+ else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
+ {
+ return write_str_output(out, out_len,
+ Botan::PKCS8::PEM_encode_encrypted_pbkdf_msec(k, rng, pass, pbkdf_time, pbkdf_iters_out, cipher, pbkdf_hash));
+ }
+ else
+ {
+ return -2;
+ }
+ });
+ }
+
+int botan_privkey_export_encrypted_pbkdf_iter(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng_obj,
+ const char* pass,
+ size_t pbkdf_iter,
+ const char* maybe_cipher,
+ const char* maybe_pbkdf_hash,
+ uint32_t flags)
+ {
return BOTAN_FFI_DO(Botan::Private_Key, key, k, {
- auto pbkdf_time = std::chrono::milliseconds(300);
Botan::RandomNumberGenerator& rng = safe_get(rng_obj);
+ std::string cipher;
+ if(maybe_cipher)
+ {
+ cipher = maybe_cipher;
+ }
+
+ std::string pbkdf_hash;
+ if(maybe_pbkdf_hash)
+ {
+ pbkdf_hash = maybe_pbkdf_hash;
+ }
+
if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_DER)
- return write_vec_output(out, out_len, Botan::PKCS8::BER_encode(k, rng, pass, pbkdf_time, pbe));
+ {
+ return write_vec_output(out, out_len,
+ Botan::PKCS8::BER_encode_encrypted_pbkdf_iter(k, rng, pass, pbkdf_iter, cipher, pbkdf_hash));
+ }
else if(flags == BOTAN_PRIVKEY_EXPORT_FLAG_PEM)
- return write_str_output(out, out_len, Botan::PKCS8::PEM_encode(k, rng, pass, pbkdf_time, pbe));
+ {
+ return write_str_output(out, out_len,
+ Botan::PKCS8::PEM_encode_encrypted_pbkdf_iter(k, rng, pass, pbkdf_iter, cipher, pbkdf_hash));
+ }
else
+ {
return -2;
+ }
});
}
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
index 2cb8d38bd..661368c18 100644
--- a/src/lib/ffi/ffi.h
+++ b/src/lib/ffi/ffi.h
@@ -597,6 +597,7 @@ BOTAN_DLL int botan_privkey_export(botan_privkey_t key,
/*
* Set encryption_algo to NULL or "" to have the library choose a default (recommended)
*/
+BOTAN_DEPRECATED("Use botan_privkey_export_encrypted_pbkdf_{msec,iter}")
BOTAN_DLL int botan_privkey_export_encrypted(botan_privkey_t key,
uint8_t out[], size_t* out_len,
botan_rng_t rng,
@@ -604,6 +605,32 @@ BOTAN_DLL int botan_privkey_export_encrypted(botan_privkey_t key,
const char* encryption_algo,
uint32_t flags);
+/*
+* Export a private key, running PBKDF for specified amount of time
+* @param key the private key to export
+*/
+BOTAN_DLL int botan_privkey_export_encrypted_pbkdf_msec(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng,
+ const char* passphrase,
+ uint32_t pbkdf_msec_runtime,
+ size_t* pbkdf_iterations_out,
+ const char* cipher_algo,
+ const char* pbkdf_algo,
+ uint32_t flags);
+
+/*
+* Export a private key using the specified number of iterations.
+*/
+BOTAN_DLL int botan_privkey_export_encrypted_pbkdf_iter(botan_privkey_t key,
+ uint8_t out[], size_t* out_len,
+ botan_rng_t rng,
+ const char* passphrase,
+ size_t pbkdf_iterations,
+ const char* cipher_algo,
+ const char* pbkdf_algo,
+ uint32_t flags);
+
typedef struct botan_pubkey_struct* botan_pubkey_t;
BOTAN_DLL int botan_pubkey_load(botan_pubkey_t* key, const uint8_t bits[], size_t len);
diff --git a/src/lib/pubkey/pbes2/pbes2.cpp b/src/lib/pubkey/pbes2/pbes2.cpp
index 01bab76bb..b0a7f336d 100644
--- a/src/lib/pubkey/pbes2/pbes2.cpp
+++ b/src/lib/pubkey/pbes2/pbes2.cpp
@@ -55,18 +55,17 @@ std::vector<uint8_t> encode_pbes2_params(const std::string& cipher,
.get_contents_unlocked();
}
-}
-
/*
-* PKCS#5 v2.0 PBE Constructor
+* PKCS#5 v2.0 PBE Encryption
*/
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)
+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& digest,
+ RandomNumberGenerator& rng)
{
const std::string prf = "HMAC(" + digest + ")";
@@ -87,12 +86,22 @@ pbes2_encrypt(const secure_vector<uint8_t>& key_bits,
std::unique_ptr<PBKDF> pbkdf(get_pbkdf("PBKDF2(" + prf + ")"));
const size_t key_length = enc->key_spec().maximum_keylength();
- size_t iterations = 0;
+
secure_vector<uint8_t> iv = rng.random_vec(enc->default_nonce_length());
- enc->set_key(pbkdf->derive_key(key_length, passphrase, salt.data(), salt.size(),
- msec, iterations).bits_of());
+ size_t iterations = iterations_if_msec_null;
+
+ if(msec_in_iterations_out)
+ {
+ std::chrono::milliseconds msec(*msec_in_iterations_out);
+ enc->set_key(pbkdf->derive_key(key_length, passphrase, salt.data(), salt.size(), msec, iterations).bits_of());
+ *msec_in_iterations_out = iterations;
+ }
+ else
+ {
+ enc->set_key(pbkdf->pbkdf_iterations(key_length, passphrase, salt.data(), salt.size(), iterations));
+ }
enc->start(iv);
secure_vector<uint8_t> buf = key_bits;
@@ -105,6 +114,52 @@ pbes2_encrypt(const secure_vector<uint8_t>& key_bits,
return std::make_pair(id, unlock(buf));
}
+
+}
+
+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 = 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 = 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,
diff --git a/src/lib/pubkey/pbes2/pbes2.h b/src/lib/pubkey/pbes2/pbes2.h
index e50896c6d..951ba3178 100644
--- a/src/lib/pubkey/pbes2/pbes2.h
+++ b/src/lib/pubkey/pbes2/pbes2.h
@@ -32,6 +32,43 @@ BOTAN_DLL pbes2_encrypt(const secure_vector<uint8_t>& key_bits,
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_DLL 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_DLL 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
diff --git a/src/lib/pubkey/pkcs8.cpp b/src/lib/pubkey/pkcs8.cpp
index c4294d563..010516973 100644
--- a/src/lib/pubkey/pkcs8.cpp
+++ b/src/lib/pubkey/pkcs8.cpp
@@ -175,8 +175,8 @@ std::vector<uint8_t> BER_encode(const Private_Key& key,
const auto pbe_params = choose_pbe_params(pbe_algo, key.algo_name());
const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
- pbes2_encrypt(PKCS8::BER_encode(key), pass, msec,
- pbe_params.first, pbe_params.second, rng);
+ pbes2_encrypt_msec(PKCS8::BER_encode(key), pass, msec, nullptr,
+ pbe_params.first, pbe_params.second, rng);
return DER_Encoder()
.start_cons(SEQUENCE)
@@ -202,6 +202,88 @@ std::string PEM_encode(const Private_Key& key,
"ENCRYPTED PRIVATE KEY");
}
+/*
+* BER encode a PKCS #8 private key, encrypted
+*/
+std::vector<uint8_t> BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ size_t pbkdf_iterations,
+ const std::string& cipher,
+ const std::string& pbkdf_hash)
+ {
+ const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
+ pbes2_encrypt_iter(key.private_key_info(),
+ pass, pbkdf_iterations,
+ cipher.empty() ? "AES-256/CBC" : cipher,
+ pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
+ rng);
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(pbe_info.first)
+ .encode(pbe_info.second, OCTET_STRING)
+ .end_cons()
+ .get_contents_unlocked();
+ }
+
+/*
+* PEM encode a PKCS #8 private key, encrypted
+*/
+std::string PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ size_t pbkdf_iterations,
+ const std::string& cipher,
+ const std::string& pbkdf_hash)
+ {
+ return PEM_Code::encode(
+ PKCS8::BER_encode_encrypted_pbkdf_iter(key, rng, pass, pbkdf_iterations, cipher, pbkdf_hash),
+ "ENCRYPTED PRIVATE KEY");
+ }
+
+/*
+* BER encode a PKCS #8 private key, encrypted
+*/
+std::vector<uint8_t> BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds pbkdf_msec,
+ size_t* pbkdf_iterations,
+ const std::string& cipher,
+ const std::string& pbkdf_hash)
+ {
+ const std::pair<AlgorithmIdentifier, std::vector<uint8_t>> pbe_info =
+ pbes2_encrypt_msec(key.private_key_info(), pass,
+ pbkdf_msec, pbkdf_iterations,
+ cipher.empty() ? "AES-256/CBC" : cipher,
+ pbkdf_hash.empty() ? "SHA-256" : pbkdf_hash,
+ rng);
+
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(pbe_info.first)
+ .encode(pbe_info.second, OCTET_STRING)
+ .end_cons()
+ .get_contents_unlocked();
+ }
+
+/*
+* PEM encode a PKCS #8 private key, encrypted
+*/
+std::string PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds pbkdf_msec,
+ size_t* pbkdf_iterations,
+ const std::string& cipher,
+ const std::string& pbkdf_hash)
+ {
+ return PEM_Code::encode(
+ PKCS8::BER_encode_encrypted_pbkdf_msec(key, rng, pass, pbkdf_msec, pbkdf_iterations, cipher, pbkdf_hash),
+ "ENCRYPTED PRIVATE KEY");
+ }
+
namespace {
/*
diff --git a/src/lib/pubkey/pkcs8.h b/src/lib/pubkey/pkcs8.h
index 309ca2798..014ce8714 100644
--- a/src/lib/pubkey/pkcs8.h
+++ b/src/lib/pubkey/pkcs8.h
@@ -80,6 +80,95 @@ PEM_encode(const Private_Key& key,
const std::string& pbe_algo = "");
/**
+* Encrypt a key using PKCS #8 encryption and a fixed iteration count
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_iter number of interations to run PBKDF2
+* @param cipher if non-empty specifies the cipher to use. CBC and GCM modes
+* are supported, for example "AES-128/CBC", "AES-256/GCM", "Serpent/CBC".
+* If empty a suitable default is chosen.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+* For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in binary BER form
+*/
+BOTAN_DLL std::vector<uint8_t>
+BER_encode_encrypted_pbkdf_iter(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ size_t pbkdf_iter,
+ const std::string& cipher = "",
+ const std::string& pbkdf_hash = "");
+
+/**
+* Get a string containing a PEM encoded private key, encrypting it with a
+* password.
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_iter number of iterations to run PBKDF
+* @param cipher if non-empty specifies the cipher to use. CBC and GCM modes
+* are supported, for example "AES-128/CBC", "AES-256/GCM", "Serpent/CBC".
+* If empty a suitable default is chosen.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+* For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in PEM form
+*/
+BOTAN_DLL std::string
+PEM_encode_encrypted_pbkdf_iter(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ size_t pbkdf_iter,
+ const std::string& cipher = "",
+ const std::string& pbkdf_hash = "");
+
+/**
+* Encrypt a key using PKCS #8 encryption and a variable iteration count
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_msec how long to run PBKDF2
+* @param pbkdf_iterations if non-null, set to the number of iterations used
+* @param cipher if non-empty specifies the cipher to use. CBC and GCM modes
+* are supported, for example "AES-128/CBC", "AES-256/GCM", "Serpent/CBC".
+* If empty a suitable default is chosen.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+* For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in binary BER form
+*/
+BOTAN_DLL std::vector<uint8_t>
+BER_encode_encrypted_pbkdf_msec(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds pbkdf_msec,
+ size_t* pbkdf_iterations,
+ const std::string& cipher = "",
+ const std::string& pbkdf_hash = "");
+
+/**
+* Get a string containing a PEM encoded private key, encrypting it with a
+* password.
+* @param key the key to encode
+* @param rng the rng to use
+* @param pass the password to use for encryption
+* @param pbkdf_iter number of iterations to run PBKDF
+* @param cipher if non-empty specifies the cipher to use. CBC and GCM modes
+* are supported, for example "AES-128/CBC", "AES-256/GCM", "Serpent/CBC".
+* If empty a suitable default is chosen.
+* @param pbkdf_hash if non-empty specifies the PBKDF hash function to use.
+* For example "SHA-256" or "SHA-384". If empty a suitable default is chosen.
+* @return encrypted key in PEM form
+*/
+BOTAN_DLL std::string
+PEM_encode_encrypted_pbkdf_msec(const Private_Key& key,
+ RandomNumberGenerator& rng,
+ const std::string& pass,
+ std::chrono::milliseconds pbkdf_msec,
+ size_t* pbkdf_iterations,
+ const std::string& cipher = "",
+ const std::string& pbkdf_hash = "");
+
+/**
* Load an encrypted key from a data source.
* @param source the data source providing the encoded key
* @param rng ignored for compatability