diff options
author | lloyd <[email protected]> | 2008-09-28 21:27:57 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2008-09-28 21:27:57 +0000 |
commit | 0e0fb8fd1f0aa2906b7452ae011a2ebe2ad35389 (patch) | |
tree | 1f4e5504e3f965158225ae2f4d181aa0ca527701 /src/pbe | |
parent | 9ebc60c266c37722e83ca7482f1516fc3e8bf6d3 (diff) |
Modularize PBEs (password-based encryption schemes)
Diffstat (limited to 'src/pbe')
-rw-r--r-- | src/pbe/pbes1/pbes1.cpp | 169 | ||||
-rw-r--r-- | src/pbe/pbes1/pbes1.h | 42 | ||||
-rw-r--r-- | src/pbe/pbes2/pbes2.cpp | 230 |
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); + } + +} |