aboutsummaryrefslogtreecommitdiffstats
path: root/src/pbe
diff options
context:
space:
mode:
authorlloyd <[email protected]>2008-09-28 21:27:57 +0000
committerlloyd <[email protected]>2008-09-28 21:27:57 +0000
commit0e0fb8fd1f0aa2906b7452ae011a2ebe2ad35389 (patch)
tree1f4e5504e3f965158225ae2f4d181aa0ca527701 /src/pbe
parent9ebc60c266c37722e83ca7482f1516fc3e8bf6d3 (diff)
Modularize PBEs (password-based encryption schemes)
Diffstat (limited to 'src/pbe')
-rw-r--r--src/pbe/pbes1/pbes1.cpp169
-rw-r--r--src/pbe/pbes1/pbes1.h42
-rw-r--r--src/pbe/pbes2/pbes2.cpp230
3 files changed, 441 insertions, 0 deletions
diff --git a/src/pbe/pbes1/pbes1.cpp b/src/pbe/pbes1/pbes1.cpp
new file mode 100644
index 000000000..84b34eed6
--- /dev/null
+++ b/src/pbe/pbes1/pbes1.cpp
@@ -0,0 +1,169 @@
+/*************************************************
+* PKCS #5 PBES1 Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/pbe_pkcs.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/lookup.h>
+#include <botan/libstate.h>
+#include <algorithm>
+#include <memory>
+
+namespace Botan {
+
+/*************************************************
+* Encrypt some bytes using PBES1 *
+*************************************************/
+void PBE_PKCS5v15::write(const byte input[], u32bit length)
+ {
+ while(length)
+ {
+ u32bit put = std::min(DEFAULT_BUFFERSIZE, length);
+ pipe.write(input, length);
+ flush_pipe(true);
+ length -= put;
+ }
+ }
+
+/*************************************************
+* Start encrypting with PBES1 *
+*************************************************/
+void PBE_PKCS5v15::start_msg()
+ {
+ pipe.append(get_cipher(cipher, key, iv, direction));
+ pipe.start_msg();
+ if(pipe.message_count() > 1)
+ pipe.set_default_msg(pipe.default_msg() + 1);
+ }
+
+/*************************************************
+* Finish encrypting with PBES1 *
+*************************************************/
+void PBE_PKCS5v15::end_msg()
+ {
+ pipe.end_msg();
+ flush_pipe(false);
+ pipe.reset();
+ }
+
+/*************************************************
+* Flush the pipe *
+*************************************************/
+void PBE_PKCS5v15::flush_pipe(bool safe_to_skip)
+ {
+ if(safe_to_skip && pipe.remaining() < 64)
+ return;
+
+ SecureVector<byte> buffer(DEFAULT_BUFFERSIZE);
+ while(pipe.remaining())
+ {
+ u32bit got = pipe.read(buffer, buffer.size());
+ send(buffer, got);
+ }
+ }
+
+/*************************************************
+* Set the passphrase to use *
+*************************************************/
+void PBE_PKCS5v15::set_key(const std::string& passphrase)
+ {
+ std::auto_ptr<S2K> pbkdf(get_s2k("PBKDF1(" + digest + ")"));
+ pbkdf->set_iterations(iterations);
+ pbkdf->change_salt(salt, salt.size());
+ SymmetricKey key_and_iv = pbkdf->derive_key(16, passphrase);
+
+ key.set(key_and_iv.begin(), 8);
+ iv.set(key_and_iv.begin() + 8, 8);
+ }
+
+/*************************************************
+* Create a new set of PBES1 parameters *
+*************************************************/
+void PBE_PKCS5v15::new_params(RandomNumberGenerator& rng)
+ {
+ iterations = 2048;
+ salt.create(8);
+ rng.randomize(salt, salt.size());
+ }
+
+/*************************************************
+* Encode PKCS#5 PBES1 parameters *
+*************************************************/
+MemoryVector<byte> PBE_PKCS5v15::encode_params() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(salt, OCTET_STRING)
+ .encode(iterations)
+ .end_cons()
+ .get_contents();
+ }
+
+/*************************************************
+* 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");
+ if(cipher == "DES/CBC" && digest == "MD2")
+ return (base_pbes1_oid + 1);
+ else if(cipher == "DES/CBC" && digest == "MD5")
+ return (base_pbes1_oid + 3);
+ else if(cipher == "DES/CBC" && digest == "SHA-160")
+ return (base_pbes1_oid + 10);
+ else if(cipher == "RC2/CBC" && digest == "MD2")
+ return (base_pbes1_oid + 4);
+ else if(cipher == "RC2/CBC" && digest == "MD5")
+ return (base_pbes1_oid + 6);
+ else if(cipher == "RC2/CBC" && digest == "SHA-160")
+ return (base_pbes1_oid + 11);
+ else
+ throw Internal_Error("PBE-PKCS5 v1.5: get_oid() has run out of options");
+ }
+
+/*************************************************
+* PKCS#5 v1.5 PBE Constructor *
+*************************************************/
+PBE_PKCS5v15::PBE_PKCS5v15(const std::string& d_algo,
+ const std::string& c_algo, Cipher_Dir dir) :
+ direction(dir),
+ digest(global_state().deref_alias(d_algo)),
+ cipher(c_algo)
+ {
+ std::vector<std::string> cipher_spec = split_on(c_algo, '/');
+ if(cipher_spec.size() != 2)
+ throw Invalid_Argument("PBE-PKCS5 v1.5: Invalid cipher spec " + c_algo);
+ const std::string cipher_algo = global_state().deref_alias(cipher_spec[0]);
+ const std::string cipher_mode = cipher_spec[1];
+
+ if(!have_block_cipher(cipher_algo))
+ throw Algorithm_Not_Found(cipher_algo);
+ if(!have_hash(digest))
+ throw Algorithm_Not_Found(digest);
+
+ if((cipher_algo != "DES" && cipher_algo != "RC2") || (cipher_mode != "CBC"))
+ throw Invalid_Argument("PBE-PKCS5 v1.5: Invalid cipher " + cipher);
+ if(digest != "MD2" && digest != "MD5" && digest != "SHA-160")
+ throw Invalid_Argument("PBE-PKCS5 v1.5: Invalid digest " + digest);
+ }
+
+}
diff --git a/src/pbe/pbes1/pbes1.h b/src/pbe/pbes1/pbes1.h
new file mode 100644
index 000000000..89d611b4e
--- /dev/null
+++ b/src/pbe/pbes1/pbes1.h
@@ -0,0 +1,42 @@
+/*************************************************
+* PKCS #5 v1.5 PBE Header File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#ifndef BOTAN_PBE_PKCS_V15_H__
+#define BOTAN_PBE_PKCS_V15_H__
+
+#include <botan/pbe.h>
+#include <botan/pipe.h>
+#include <botan/enums.h>
+
+namespace Botan {
+
+/*************************************************
+* PKCS#5 v1.5 PBE *
+*************************************************/
+class BOTAN_DLL PBE_PKCS5v15 : public PBE
+ {
+ public:
+ void write(const byte[], u32bit);
+ void start_msg();
+ void end_msg();
+ PBE_PKCS5v15(const std::string&, const std::string&, Cipher_Dir);
+ private:
+ void set_key(const std::string&);
+ void new_params(RandomNumberGenerator& rng);
+ MemoryVector<byte> encode_params() const;
+ void decode_params(DataSource&);
+ OID get_oid() const;
+
+ void flush_pipe(bool);
+ const Cipher_Dir direction;
+ const std::string digest, cipher;
+ SecureVector<byte> salt, key, iv;
+ u32bit iterations;
+ Pipe pipe;
+ };
+
+}
+
+#endif
diff --git a/src/pbe/pbes2/pbes2.cpp b/src/pbe/pbes2/pbes2.cpp
new file mode 100644
index 000000000..d3533f14f
--- /dev/null
+++ b/src/pbe/pbes2/pbes2.cpp
@@ -0,0 +1,230 @@
+/*************************************************
+* PKCS #5 PBES2 Source File *
+* (C) 1999-2007 Jack Lloyd *
+*************************************************/
+
+#include <botan/pbe_pkcs.h>
+#include <botan/libstate.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/parsing.h>
+#include <botan/lookup.h>
+#include <botan/asn1_obj.h>
+#include <botan/oids.h>
+#include <algorithm>
+#include <memory>
+
+namespace Botan {
+
+/*************************************************
+* Encrypt some bytes using PBES2 *
+*************************************************/
+void PBE_PKCS5v20::write(const byte input[], u32bit length)
+ {
+ while(length)
+ {
+ u32bit put = std::min(DEFAULT_BUFFERSIZE, length);
+ pipe.write(input, length);
+ flush_pipe(true);
+ length -= put;
+ }
+ }
+
+/*************************************************
+* Start encrypting with PBES2 *
+*************************************************/
+void PBE_PKCS5v20::start_msg()
+ {
+ pipe.append(get_cipher(cipher, key, iv, direction));
+ pipe.start_msg();
+ if(pipe.message_count() > 1)
+ pipe.set_default_msg(pipe.default_msg() + 1);
+ }
+
+/*************************************************
+* Finish encrypting with PBES2 *
+*************************************************/
+void PBE_PKCS5v20::end_msg()
+ {
+ pipe.end_msg();
+ flush_pipe(false);
+ pipe.reset();
+ }
+
+/*************************************************
+* Flush the pipe *
+*************************************************/
+void PBE_PKCS5v20::flush_pipe(bool safe_to_skip)
+ {
+ if(safe_to_skip && pipe.remaining() < 64)
+ return;
+
+ SecureVector<byte> buffer(DEFAULT_BUFFERSIZE);
+ while(pipe.remaining())
+ {
+ u32bit got = pipe.read(buffer, buffer.size());
+ send(buffer, got);
+ }
+ }
+
+/*************************************************
+* Set the passphrase to use *
+*************************************************/
+void PBE_PKCS5v20::set_key(const std::string& passphrase)
+ {
+ std::auto_ptr<S2K> pbkdf(get_s2k("PBKDF2(" + digest + ")"));
+ pbkdf->set_iterations(iterations);
+ pbkdf->change_salt(salt, salt.size());
+ key = pbkdf->derive_key(key_length, passphrase).bits_of();
+ }
+
+/*************************************************
+* Create a new set of PBES2 parameters *
+*************************************************/
+void PBE_PKCS5v20::new_params(RandomNumberGenerator& rng)
+ {
+ iterations = 2048;
+ key_length = max_keylength_of(cipher_algo);
+
+ salt.create(8);
+ rng.randomize(salt, salt.size());
+
+ iv.create(block_size_of(cipher_algo));
+ rng.randomize(iv, iv.size());
+ }
+
+/*************************************************
+* Encode PKCS#5 PBES2 parameters *
+*************************************************/
+MemoryVector<byte> PBE_PKCS5v20::encode_params() const
+ {
+ return DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(
+ AlgorithmIdentifier("PKCS5.PBKDF2",
+ DER_Encoder()
+ .start_cons(SEQUENCE)
+ .encode(salt, OCTET_STRING)
+ .encode(iterations)
+ .encode(key_length)
+ .end_cons()
+ .get_contents()
+ )
+ )
+ .encode(
+ AlgorithmIdentifier(cipher,
+ DER_Encoder()
+ .encode(iv, OCTET_STRING)
+ .get_contents()
+ )
+ )
+ .end_cons()
+ .get_contents();
+ }
+
+/*************************************************
+* Decode PKCS#5 PBES2 parameters *
+*************************************************/
+void PBE_PKCS5v20::decode_params(DataSource& source)
+ {
+ AlgorithmIdentifier kdf_algo, enc_algo;
+
+ BER_Decoder(source)
+ .start_cons(SEQUENCE)
+ .decode(kdf_algo)
+ .decode(enc_algo)
+ .verify_end()
+ .end_cons();
+
+ if(kdf_algo.oid == OIDS::lookup("PKCS5.PBKDF2"))
+ {
+ digest = "SHA-160";
+
+ BER_Decoder(kdf_algo.parameters)
+ .start_cons(SEQUENCE)
+ .decode(salt, OCTET_STRING)
+ .decode(iterations)
+ .decode_optional(key_length, INTEGER, UNIVERSAL)
+ .verify_end()
+ .end_cons();
+ }
+ else
+ throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " +
+ kdf_algo.oid.as_string());
+
+ cipher = OIDS::lookup(enc_algo.oid);
+ 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);
+ cipher_algo = global_state().deref_alias(cipher_spec[0]);
+
+ if(!known_cipher(cipher_algo) || cipher_spec[1] != "CBC")
+ throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " +
+ cipher);
+
+ BER_Decoder(enc_algo.parameters).decode(iv, OCTET_STRING).verify_end();
+
+ if(key_length == 0)
+ key_length = max_keylength_of(cipher_algo);
+
+ 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) const
+ {
+ if(algo == "AES-128" || algo == "AES-192" || algo == "AES-256")
+ return true;
+ if(algo == "DES" || algo == "TripleDES")
+ return true;
+ return false;
+ }
+
+/*************************************************
+* PKCS#5 v2.0 PBE Constructor *
+*************************************************/
+PBE_PKCS5v20::PBE_PKCS5v20(const std::string& d_algo,
+ const std::string& c_algo) :
+ direction(ENCRYPTION),
+ digest(global_state().deref_alias(d_algo)),
+ cipher(c_algo)
+ {
+ std::vector<std::string> cipher_spec = split_on(cipher, '/');
+ if(cipher_spec.size() != 2)
+ throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid cipher spec " + cipher);
+ cipher_algo = global_state().deref_alias(cipher_spec[0]);
+ const std::string cipher_mode = cipher_spec[1];
+
+ if(!have_block_cipher(cipher_algo))
+ throw Algorithm_Not_Found(cipher_algo);
+ if(!have_hash(digest))
+ throw Algorithm_Not_Found(digest);
+
+ if(!known_cipher(cipher_algo))
+ throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid cipher " + cipher);
+ if(cipher_mode != "CBC")
+ throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid cipher " + cipher);
+ if(digest != "SHA-160")
+ throw Invalid_Argument("PBE-PKCS5 v2.0: Invalid digest " + digest);
+ }
+
+/*************************************************
+* PKCS#5 v2.0 PBE Constructor *
+*************************************************/
+PBE_PKCS5v20::PBE_PKCS5v20(DataSource& params) : direction(DECRYPTION)
+ {
+ decode_params(params);
+ }
+
+}