diff options
author | lloyd <[email protected]> | 2015-02-05 07:44:25 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2015-02-05 07:44:25 +0000 |
commit | cb0f83ae63c4555cbdd0607e3a5f6e9260c0d19c (patch) | |
tree | 4981027a25fa8074177b97c3a3ca7431f3337deb /src/lib/misc | |
parent | 2c14faf0aa1cfe0f8d70af1938dcad5b4d6d3b59 (diff) |
Clean up root dir, remove some unneeded dependencies
Diffstat (limited to 'src/lib/misc')
32 files changed, 2384 insertions, 0 deletions
diff --git a/src/lib/misc/aont/info.txt b/src/lib/misc/aont/info.txt new file mode 100644 index 000000000..05426fba9 --- /dev/null +++ b/src/lib/misc/aont/info.txt @@ -0,0 +1,7 @@ +define PACKAGE_TRANSFORM 20131128 + +<requires> +ctr +rng +filters +</requires> diff --git a/src/lib/misc/aont/package.cpp b/src/lib/misc/aont/package.cpp new file mode 100644 index 000000000..731cae408 --- /dev/null +++ b/src/lib/misc/aont/package.cpp @@ -0,0 +1,120 @@ +/* +* Rivest's Package Tranform +* +* (C) 2009 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/package.h> +#include <botan/filters.h> +#include <botan/ctr.h> +#include <botan/get_byte.h> +#include <botan/internal/xor_buf.h> + +namespace Botan { + +void aont_package(RandomNumberGenerator& rng, + BlockCipher* cipher, + const byte input[], size_t input_len, + byte output[]) + { + const size_t BLOCK_SIZE = cipher->block_size(); + + if(!cipher->valid_keylength(BLOCK_SIZE)) + throw Invalid_Argument("AONT::package: Invalid cipher"); + + // The all-zero string which is used both as the CTR IV and as K0 + const std::string all_zeros(BLOCK_SIZE*2, '0'); + + SymmetricKey package_key(rng, BLOCK_SIZE); + + Pipe pipe(new StreamCipher_Filter(new CTR_BE(cipher), package_key)); + + pipe.process_msg(input, input_len); + pipe.read(output, pipe.remaining()); + + // Set K0 (the all zero key) + cipher->set_key(SymmetricKey(all_zeros)); + + secure_vector<byte> buf(BLOCK_SIZE); + + const size_t blocks = + (input_len + BLOCK_SIZE - 1) / BLOCK_SIZE; + + byte* final_block = output + input_len; + clear_mem(final_block, BLOCK_SIZE); + + // XOR the hash blocks into the final block + for(size_t i = 0; i != blocks; ++i) + { + const size_t left = std::min<size_t>(BLOCK_SIZE, + input_len - BLOCK_SIZE * i); + + zeroise(buf); + copy_mem(&buf[0], output + (BLOCK_SIZE * i), left); + + for(size_t j = 0; j != sizeof(i); ++j) + buf[BLOCK_SIZE - 1 - j] ^= get_byte(sizeof(i)-1-j, i); + + cipher->encrypt(&buf[0]); + + xor_buf(&final_block[0], &buf[0], BLOCK_SIZE); + } + + // XOR the random package key into the final block + xor_buf(&final_block[0], package_key.begin(), BLOCK_SIZE); + } + +void aont_unpackage(BlockCipher* cipher, + const byte input[], size_t input_len, + byte output[]) + { + const size_t BLOCK_SIZE = cipher->block_size(); + + if(!cipher->valid_keylength(BLOCK_SIZE)) + throw Invalid_Argument("AONT::unpackage: Invalid cipher"); + + if(input_len < BLOCK_SIZE) + throw Invalid_Argument("AONT::unpackage: Input too short"); + + // The all-zero string which is used both as the CTR IV and as K0 + const std::string all_zeros(BLOCK_SIZE*2, '0'); + + cipher->set_key(SymmetricKey(all_zeros)); + + secure_vector<byte> package_key(BLOCK_SIZE); + secure_vector<byte> buf(BLOCK_SIZE); + + // Copy the package key (masked with the block hashes) + copy_mem(&package_key[0], + input + (input_len - BLOCK_SIZE), + BLOCK_SIZE); + + const size_t blocks = ((input_len - 1) / BLOCK_SIZE); + + // XOR the blocks into the package key bits + for(size_t i = 0; i != blocks; ++i) + { + const size_t left = std::min<size_t>(BLOCK_SIZE, + input_len - BLOCK_SIZE * (i+1)); + + zeroise(buf); + copy_mem(&buf[0], input + (BLOCK_SIZE * i), left); + + for(size_t j = 0; j != sizeof(i); ++j) + buf[BLOCK_SIZE - 1 - j] ^= get_byte(sizeof(i)-1-j, i); + + cipher->encrypt(&buf[0]); + + xor_buf(&package_key[0], &buf[0], BLOCK_SIZE); + } + + Pipe pipe(new StreamCipher_Filter(new CTR_BE(cipher), package_key)); + + pipe.process_msg(input, input_len - BLOCK_SIZE); + + pipe.read(output, pipe.remaining()); + } + +} diff --git a/src/lib/misc/aont/package.h b/src/lib/misc/aont/package.h new file mode 100644 index 000000000..76e679490 --- /dev/null +++ b/src/lib/misc/aont/package.h @@ -0,0 +1,44 @@ +/* +* Rivest's Package Tranform +* (C) 2009 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AONT_PACKAGE_TRANSFORM_H__ +#define BOTAN_AONT_PACKAGE_TRANSFORM_H__ + +#include <botan/block_cipher.h> +#include <botan/rng.h> + +namespace Botan { + +/** +* Rivest's Package Tranform +* @param rng the random number generator to use +* @param cipher the block cipher to use +* @param input the input data buffer +* @param input_len the length of the input data in bytes +* @param output the output data buffer (must be at least +* input_len + cipher->BLOCK_SIZE bytes long) +*/ +void BOTAN_DLL aont_package(RandomNumberGenerator& rng, + BlockCipher* cipher, + const byte input[], size_t input_len, + byte output[]); + +/** +* Rivest's Package Tranform (Inversion) +* @param cipher the block cipher to use +* @param input the input data buffer +* @param input_len the length of the input data in bytes +* @param output the output data buffer (must be at least +* input_len - cipher->BLOCK_SIZE bytes long) +*/ +void BOTAN_DLL aont_unpackage(BlockCipher* cipher, + const byte input[], size_t input_len, + byte output[]); + +} + +#endif diff --git a/src/lib/misc/cryptobox/cryptobox.cpp b/src/lib/misc/cryptobox/cryptobox.cpp new file mode 100644 index 000000000..fb210bc0b --- /dev/null +++ b/src/lib/misc/cryptobox/cryptobox.cpp @@ -0,0 +1,163 @@ +/* +* Cryptobox Message Routines +* (C) 2009 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/cryptobox.h> +#include <botan/filters.h> +#include <botan/pipe.h> +#include <botan/lookup.h> +#include <botan/sha2_64.h> +#include <botan/hmac.h> +#include <botan/pbkdf2.h> +#include <botan/pem.h> +#include <botan/get_byte.h> +#include <botan/mem_ops.h> + +namespace Botan { + +namespace CryptoBox { + +namespace { + +/* +First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits +for later use as flags, etc if needed +*/ +const u32bit CRYPTOBOX_VERSION_CODE = 0xEFC22400; + +const size_t VERSION_CODE_LEN = 4; +const size_t CIPHER_KEY_LEN = 32; +const size_t CIPHER_IV_LEN = 16; +const size_t MAC_KEY_LEN = 32; +const size_t MAC_OUTPUT_LEN = 20; +const size_t PBKDF_SALT_LEN = 10; +const size_t PBKDF_ITERATIONS = 8 * 1024; + +const size_t PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + CIPHER_IV_LEN + MAC_KEY_LEN; + +} + +std::string encrypt(const byte input[], size_t input_len, + const std::string& passphrase, + RandomNumberGenerator& rng) + { + secure_vector<byte> pbkdf_salt(PBKDF_SALT_LEN); + rng.randomize(&pbkdf_salt[0], pbkdf_salt.size()); + + PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512)); + + OctetString master_key = pbkdf.derive_key( + PBKDF_OUTPUT_LEN, + passphrase, + &pbkdf_salt[0], + pbkdf_salt.size(), + PBKDF_ITERATIONS); + + const byte* mk = master_key.begin(); + + SymmetricKey cipher_key(&mk[0], CIPHER_KEY_LEN); + SymmetricKey mac_key(&mk[CIPHER_KEY_LEN], MAC_KEY_LEN); + InitializationVector iv(&mk[CIPHER_KEY_LEN + MAC_KEY_LEN], CIPHER_IV_LEN); + + Pipe pipe(get_cipher("Serpent/CTR-BE", cipher_key, iv, ENCRYPTION), + new Fork( + nullptr, + new MAC_Filter(new HMAC(new SHA_512), + mac_key, MAC_OUTPUT_LEN))); + + pipe.process_msg(input, input_len); + + /* + Output format is: + version # (4 bytes) + salt (10 bytes) + mac (20 bytes) + ciphertext + */ + const size_t ciphertext_len = pipe.remaining(0); + + std::vector<byte> out_buf(VERSION_CODE_LEN + + PBKDF_SALT_LEN + + MAC_OUTPUT_LEN + + ciphertext_len); + + for(size_t i = 0; i != VERSION_CODE_LEN; ++i) + out_buf[i] = get_byte(i, CRYPTOBOX_VERSION_CODE); + + copy_mem(&out_buf[VERSION_CODE_LEN], &pbkdf_salt[0], PBKDF_SALT_LEN); + + pipe.read(&out_buf[VERSION_CODE_LEN + PBKDF_SALT_LEN], MAC_OUTPUT_LEN, 1); + pipe.read(&out_buf[VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN], + ciphertext_len, 0); + + return PEM_Code::encode(out_buf, "BOTAN CRYPTOBOX MESSAGE"); + } + +std::string decrypt(const byte input[], size_t input_len, + const std::string& passphrase) + { + DataSource_Memory input_src(input, input_len); + secure_vector<byte> ciphertext = + PEM_Code::decode_check_label(input_src, + "BOTAN CRYPTOBOX MESSAGE"); + + if(ciphertext.size() < (VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN)) + throw Decoding_Error("Invalid CryptoBox input"); + + for(size_t i = 0; i != VERSION_CODE_LEN; ++i) + if(ciphertext[i] != get_byte(i, CRYPTOBOX_VERSION_CODE)) + throw Decoding_Error("Bad CryptoBox version"); + + const byte* pbkdf_salt = &ciphertext[VERSION_CODE_LEN]; + + PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512)); + + OctetString master_key = pbkdf.derive_key( + PBKDF_OUTPUT_LEN, + passphrase, + pbkdf_salt, + PBKDF_SALT_LEN, + PBKDF_ITERATIONS); + + const byte* mk = master_key.begin(); + + SymmetricKey cipher_key(&mk[0], CIPHER_KEY_LEN); + SymmetricKey mac_key(&mk[CIPHER_KEY_LEN], MAC_KEY_LEN); + InitializationVector iv(&mk[CIPHER_KEY_LEN + MAC_KEY_LEN], CIPHER_IV_LEN); + + Pipe pipe(new Fork( + get_cipher("Serpent/CTR-BE", cipher_key, iv, DECRYPTION), + new MAC_Filter(new HMAC(new SHA_512), + mac_key, MAC_OUTPUT_LEN))); + + const size_t ciphertext_offset = + VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN; + + pipe.process_msg(&ciphertext[ciphertext_offset], + ciphertext.size() - ciphertext_offset); + + byte computed_mac[MAC_OUTPUT_LEN]; + pipe.read(computed_mac, MAC_OUTPUT_LEN, 1); + + if(!same_mem(computed_mac, + &ciphertext[VERSION_CODE_LEN + PBKDF_SALT_LEN], + MAC_OUTPUT_LEN)) + throw Decoding_Error("CryptoBox integrity failure"); + + return pipe.read_all_as_string(0); + } + +std::string decrypt(const std::string& input, + const std::string& passphrase) + { + return decrypt(reinterpret_cast<const byte*>(&input[0]), + input.size(), + passphrase); + } + +} + +} diff --git a/src/lib/misc/cryptobox/cryptobox.h b/src/lib/misc/cryptobox/cryptobox.h new file mode 100644 index 000000000..27dc55a68 --- /dev/null +++ b/src/lib/misc/cryptobox/cryptobox.h @@ -0,0 +1,55 @@ +/* +* Cryptobox Message Routines +* (C) 2009 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CRYPTOBOX_H__ +#define BOTAN_CRYPTOBOX_H__ + +#include <string> +#include <botan/rng.h> +#include <botan/symkey.h> + +namespace Botan { + +/** +* This namespace holds various high-level crypto functions +*/ +namespace CryptoBox { + +/** +* Encrypt a message using a passphrase +* @param input the input data +* @param input_len the length of input in bytes +* @param passphrase the passphrase used to encrypt the message +* @param rng a ref to a random number generator, such as AutoSeeded_RNG +*/ +BOTAN_DLL std::string encrypt(const byte input[], size_t input_len, + const std::string& passphrase, + RandomNumberGenerator& rng); + + +/** +* Decrypt a message encrypted with CryptoBox::encrypt +* @param input the input data +* @param input_len the length of input in bytes +* @param passphrase the passphrase used to encrypt the message +*/ +BOTAN_DLL std::string decrypt(const byte input[], size_t input_len, + const std::string& passphrase); + +/** +* Decrypt a message encrypted with CryptoBox::encrypt +* @param input the input data +* @param passphrase the passphrase used to encrypt the message +*/ +BOTAN_DLL std::string decrypt(const std::string& input, + const std::string& passphrase); + +} + +} + +#endif diff --git a/src/lib/misc/cryptobox/info.txt b/src/lib/misc/cryptobox/info.txt new file mode 100644 index 000000000..b7bf6e4e8 --- /dev/null +++ b/src/lib/misc/cryptobox/info.txt @@ -0,0 +1,13 @@ +define CRYPTO_BOX 20131128 + +<requires> +filters +ctr +hmac +rng +serpent +sha2_64 +base64 +pbkdf2 +pem +</requires> diff --git a/src/lib/misc/fpe_fe1/fpe_fe1.cpp b/src/lib/misc/fpe_fe1/fpe_fe1.cpp new file mode 100644 index 000000000..c0a29519a --- /dev/null +++ b/src/lib/misc/fpe_fe1/fpe_fe1.cpp @@ -0,0 +1,188 @@ +/* +* Format Preserving Encryption (FE1 scheme) +* (C) 2009 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/fpe_fe1.h> +#include <botan/numthry.h> +#include <botan/hmac.h> +#include <botan/sha2_32.h> +#include <stdexcept> + +namespace Botan { + +namespace FPE { + +namespace { + +// Normally FPE is for SSNs, CC#s, etc, nothing too big +const size_t MAX_N_BYTES = 128/8; + +/* +* Factor n into a and b which are as close together as possible. +* Assumes n is composed mostly of small factors which is the case for +* typical uses of FPE (typically, n is a power of 10) +* +* Want a >= b since the safe number of rounds is 2+log_a(b); if a >= b +* then this is always 3 +*/ +void factor(BigInt n, BigInt& a, BigInt& b) + { + a = 1; + b = 1; + + size_t n_low_zero = low_zero_bits(n); + + a <<= (n_low_zero / 2); + b <<= n_low_zero - (n_low_zero / 2); + n >>= n_low_zero; + + for(size_t i = 0; i != PRIME_TABLE_SIZE; ++i) + { + while(n % PRIMES[i] == 0) + { + a *= PRIMES[i]; + if(a > b) + std::swap(a, b); + n /= PRIMES[i]; + } + } + + if(a > b) + std::swap(a, b); + a *= n; + if(a < b) + std::swap(a, b); + + if(a <= 1 || b <= 1) + throw std::runtime_error("Could not factor n for use in FPE"); + } + +/* +* According to a paper by Rogaway, Bellare, etc, the min safe number +* of rounds to use for FPE is 2+log_a(b). If a >= b then log_a(b) <= 1 +* so 3 rounds is safe. The FPE factorization routine should always +* return a >= b, so just confirm that and return 3. +*/ +size_t rounds(const BigInt& a, const BigInt& b) + { + if(a < b) + throw std::logic_error("FPE rounds: a < b"); + return 3; + } + +/* +* A simple round function based on HMAC(SHA-256) +*/ +class FPE_Encryptor + { + public: + FPE_Encryptor(const SymmetricKey& key, + const BigInt& n, + const std::vector<byte>& tweak); + + BigInt operator()(size_t i, const BigInt& R); + + private: + std::unique_ptr<MessageAuthenticationCode> mac; + std::vector<byte> mac_n_t; + }; + +FPE_Encryptor::FPE_Encryptor(const SymmetricKey& key, + const BigInt& n, + const std::vector<byte>& tweak) + { + mac.reset(new HMAC(new SHA_256)); + mac->set_key(key); + + std::vector<byte> n_bin = BigInt::encode(n); + + if(n_bin.size() > MAX_N_BYTES) + throw std::runtime_error("N is too large for FPE encryption"); + + mac->update_be(static_cast<u32bit>(n_bin.size())); + mac->update(&n_bin[0], n_bin.size()); + + mac->update_be(static_cast<u32bit>(tweak.size())); + mac->update(&tweak[0], tweak.size()); + + mac_n_t = unlock(mac->final()); + } + +BigInt FPE_Encryptor::operator()(size_t round_no, const BigInt& R) + { + secure_vector<byte> r_bin = BigInt::encode_locked(R); + + mac->update(mac_n_t); + mac->update_be(static_cast<u32bit>(round_no)); + + mac->update_be(static_cast<u32bit>(r_bin.size())); + mac->update(&r_bin[0], r_bin.size()); + + secure_vector<byte> X = mac->final(); + return BigInt(&X[0], X.size()); + } + +} + +/* +* Generic Z_n FPE encryption, FE1 scheme +*/ +BigInt fe1_encrypt(const BigInt& n, const BigInt& X0, + const SymmetricKey& key, + const std::vector<byte>& tweak) + { + FPE_Encryptor F(key, n, tweak); + + BigInt a, b; + factor(n, a, b); + + const size_t r = rounds(a, b); + + BigInt X = X0; + + for(size_t i = 0; i != r; ++i) + { + BigInt L = X / b; + BigInt R = X % b; + + BigInt W = (L + F(i, R)) % a; + X = a * R + W; + } + + return X; + } + +/* +* Generic Z_n FPE decryption, FD1 scheme +*/ +BigInt fe1_decrypt(const BigInt& n, const BigInt& X0, + const SymmetricKey& key, + const std::vector<byte>& tweak) + { + FPE_Encryptor F(key, n, tweak); + + BigInt a, b; + factor(n, a, b); + + const size_t r = rounds(a, b); + + BigInt X = X0; + + for(size_t i = 0; i != r; ++i) + { + BigInt W = X % a; + BigInt R = X / a; + + BigInt L = (W - F(r-i-1, R)) % a; + X = b * L + R; + } + + return X; + } + +} + +} diff --git a/src/lib/misc/fpe_fe1/fpe_fe1.h b/src/lib/misc/fpe_fe1/fpe_fe1.h new file mode 100644 index 000000000..a1cae9917 --- /dev/null +++ b/src/lib/misc/fpe_fe1/fpe_fe1.h @@ -0,0 +1,48 @@ +/* +* Format Preserving Encryption (FE1 scheme) +* (C) 2009 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_FPE_FE1_H__ +#define BOTAN_FPE_FE1_H__ + +#include <botan/bigint.h> +#include <botan/symkey.h> + +namespace Botan { + +namespace FPE { + +/** +* Format Preserving Encryption using the scheme FE1 from the paper +* "Format-Preserving Encryption" by Bellare, Rogaway, et al +* (http://eprint.iacr.org/2009/251) +* +* Encrypt X from and onto the group Z_n using key and tweak +* @param n the modulus +* @param X the plaintext as a BigInt +* @param key a random key +* @param tweak will modify the ciphertext (think of as an IV) +*/ +BigInt BOTAN_DLL fe1_encrypt(const BigInt& n, const BigInt& X, + const SymmetricKey& key, + const std::vector<byte>& tweak); + +/** +* Decrypt X from and onto the group Z_n using key and tweak +* @param n the modulus +* @param X the ciphertext as a BigInt +* @param key is the key used for encryption +* @param tweak the same tweak used for encryption +*/ +BigInt BOTAN_DLL fe1_decrypt(const BigInt& n, const BigInt& X, + const SymmetricKey& key, + const std::vector<byte>& tweak); + +} + +} + +#endif diff --git a/src/lib/misc/fpe_fe1/info.txt b/src/lib/misc/fpe_fe1/info.txt new file mode 100644 index 000000000..42264e54e --- /dev/null +++ b/src/lib/misc/fpe_fe1/info.txt @@ -0,0 +1,7 @@ +define FPE_FE1 20131128 + +<requires> +hmac +sha2_32 +bigint +</requires> diff --git a/src/lib/misc/hkdf/hkdf.cpp b/src/lib/misc/hkdf/hkdf.cpp new file mode 100644 index 000000000..49fa7e256 --- /dev/null +++ b/src/lib/misc/hkdf/hkdf.cpp @@ -0,0 +1,62 @@ +/* +* HKDF +* (C) 2013 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/hkdf.h> + +namespace Botan { + +std::string HKDF::name() const + { + return "HKDF(" + m_prf->name() + ")"; + } + +void HKDF::clear() + { + m_extractor->clear(); + m_prf->clear(); + } + +void HKDF::start_extract(const byte salt[], size_t salt_len) + { + m_extractor->set_key(salt, salt_len); + } + +void HKDF::extract(const byte input[], size_t input_len) + { + m_extractor->update(input, input_len); + } + +void HKDF::finish_extract() + { + m_prf->set_key(m_extractor->final()); + } + +void HKDF::expand(byte output[], size_t output_len, + const byte info[], size_t info_len) + { + if(output_len > m_prf->output_length() * 255) + throw std::invalid_argument("HKDF requested output too large"); + + byte counter = 1; + + secure_vector<byte> T; + + while(output_len) + { + m_prf->update(T); + m_prf->update(info, info_len); + m_prf->update(counter++); + T = m_prf->final(); + + const size_t to_write = std::min(T.size(), output_len); + copy_mem(&output[0], &T[0], to_write); + output += to_write; + output_len -= to_write; + } + } + +} diff --git a/src/lib/misc/hkdf/hkdf.h b/src/lib/misc/hkdf/hkdf.h new file mode 100644 index 000000000..6bc68796b --- /dev/null +++ b/src/lib/misc/hkdf/hkdf.h @@ -0,0 +1,50 @@ +/* +* HKDF +* (C) 2013 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_HKDF_H__ +#define BOTAN_HKDF_H__ + +#include <botan/mac.h> +#include <botan/hash.h> + +namespace Botan { + +/** +* HKDF, see @rfc 5869 for details +*/ +class BOTAN_DLL HKDF + { + public: + HKDF(MessageAuthenticationCode* extractor, + MessageAuthenticationCode* prf) : + m_extractor(extractor), m_prf(prf) {} + + HKDF(MessageAuthenticationCode* prf) : + m_extractor(prf), m_prf(m_extractor->clone()) {} + + void start_extract(const byte salt[], size_t salt_len); + void extract(const byte input[], size_t input_len); + void finish_extract(); + + /** + * Only call after extract + * @param output_len must be less than 256*hashlen + */ + void expand(byte output[], size_t output_len, + const byte info[], size_t info_len); + + std::string name() const; + + void clear(); + private: + std::unique_ptr<MessageAuthenticationCode> m_extractor; + std::unique_ptr<MessageAuthenticationCode> m_prf; + }; + +} + +#endif diff --git a/src/lib/misc/hkdf/info.txt b/src/lib/misc/hkdf/info.txt new file mode 100644 index 000000000..7389e5bb1 --- /dev/null +++ b/src/lib/misc/hkdf/info.txt @@ -0,0 +1 @@ +define HKDF 20131128 diff --git a/src/lib/misc/openpgp/info.txt b/src/lib/misc/openpgp/info.txt new file mode 100644 index 000000000..72467ee72 --- /dev/null +++ b/src/lib/misc/openpgp/info.txt @@ -0,0 +1,8 @@ +define OPENPGP_CODEC 20131128 + +load_on auto + +<requires> +crc24 +filters +</requires> diff --git a/src/lib/misc/openpgp/openpgp.cpp b/src/lib/misc/openpgp/openpgp.cpp new file mode 100644 index 000000000..3a464d906 --- /dev/null +++ b/src/lib/misc/openpgp/openpgp.cpp @@ -0,0 +1,196 @@ +/* +* OpenPGP Codec +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/openpgp.h> +#include <botan/filters.h> +#include <botan/basefilt.h> +#include <botan/charset.h> +#include <botan/crc24.h> + +namespace Botan { + +/* +* OpenPGP Base64 encoding +*/ +std::string PGP_encode( + const byte input[], size_t length, + const std::string& label, + const std::map<std::string, std::string>& headers) + { + const std::string PGP_HEADER = "-----BEGIN PGP " + label + "-----\n"; + const std::string PGP_TRAILER = "-----END PGP " + label + "-----\n"; + const size_t PGP_WIDTH = 64; + + std::string pgp_encoded = PGP_HEADER; + + if(headers.find("Version") != headers.end()) + pgp_encoded += "Version: " + headers.find("Version")->second + '\n'; + + std::map<std::string, std::string>::const_iterator i = headers.begin(); + while(i != headers.end()) + { + if(i->first != "Version") + pgp_encoded += i->first + ": " + i->second + '\n'; + ++i; + } + pgp_encoded += '\n'; + + Pipe pipe(new Fork( + new Base64_Encoder(true, PGP_WIDTH), + new Chain(new Hash_Filter(new CRC24), new Base64_Encoder) + ) + ); + + pipe.process_msg(input, length); + + pgp_encoded += pipe.read_all_as_string(0); + pgp_encoded += '=' + pipe.read_all_as_string(1) + '\n'; + pgp_encoded += PGP_TRAILER; + + return pgp_encoded; + } + +/* +* OpenPGP Base64 encoding +*/ +std::string PGP_encode(const byte input[], size_t length, + const std::string& type) + { + std::map<std::string, std::string> empty; + return PGP_encode(input, length, type, empty); + } + +/* +* OpenPGP Base64 decoding +*/ +secure_vector<byte> PGP_decode(DataSource& source, + std::string& label, + std::map<std::string, std::string>& headers) + { + const size_t RANDOM_CHAR_LIMIT = 5; + + const std::string PGP_HEADER1 = "-----BEGIN PGP "; + const std::string PGP_HEADER2 = "-----"; + size_t position = 0; + + while(position != PGP_HEADER1.length()) + { + byte b; + if(!source.read_byte(b)) + throw Decoding_Error("PGP: No PGP header found"); + if(b == PGP_HEADER1[position]) + ++position; + else if(position >= RANDOM_CHAR_LIMIT) + throw Decoding_Error("PGP: Malformed PGP header"); + else + position = 0; + } + position = 0; + while(position != PGP_HEADER2.length()) + { + byte b; + if(!source.read_byte(b)) + throw Decoding_Error("PGP: No PGP header found"); + if(b == PGP_HEADER2[position]) + ++position; + else if(position) + throw Decoding_Error("PGP: Malformed PGP header"); + + if(position == 0) + label += static_cast<char>(b); + } + + headers.clear(); + bool end_of_headers = false; + while(!end_of_headers) + { + std::string this_header; + byte b = 0; + while(b != '\n') + { + if(!source.read_byte(b)) + throw Decoding_Error("PGP: Bad armor header"); + if(b != '\n') + this_header += static_cast<char>(b); + } + + end_of_headers = true; + for(size_t j = 0; j != this_header.length(); ++j) + if(!Charset::is_space(this_header[j])) + end_of_headers = false; + + if(!end_of_headers) + { + std::string::size_type pos = this_header.find(": "); + if(pos == std::string::npos) + throw Decoding_Error("OpenPGP: Bad headers"); + + std::string key = this_header.substr(0, pos); + std::string value = this_header.substr(pos + 2, std::string::npos); + headers[key] = value; + } + } + + Pipe base64(new Base64_Decoder, + new Fork(nullptr, + new Chain(new Hash_Filter(new CRC24), + new Base64_Encoder) + ) + ); + base64.start_msg(); + + const std::string PGP_TRAILER = "-----END PGP " + label + "-----"; + position = 0; + bool newline_seen = 0; + std::string crc; + while(position != PGP_TRAILER.length()) + { + byte b; + if(!source.read_byte(b)) + throw Decoding_Error("PGP: No PGP trailer found"); + if(b == PGP_TRAILER[position]) + ++position; + else if(position) + throw Decoding_Error("PGP: Malformed PGP trailer"); + + if(b == '=' && newline_seen) + { + while(b != '\n') + { + if(!source.read_byte(b)) + throw Decoding_Error("PGP: Bad CRC tail"); + if(b != '\n') + crc += static_cast<char>(b); + } + } + else if(b == '\n') + newline_seen = true; + else if(position == 0) + { + base64.write(b); + newline_seen = false; + } + } + base64.end_msg(); + + if(crc != "" && crc != base64.read_all_as_string(1)) + throw Decoding_Error("PGP: Corrupt CRC"); + + return base64.read_all(); + } + +/* +* OpenPGP Base64 decoding +*/ +secure_vector<byte> PGP_decode(DataSource& source, std::string& label) + { + std::map<std::string, std::string> ignored; + return PGP_decode(source, label, ignored); + } + +} + diff --git a/src/lib/misc/openpgp/openpgp.h b/src/lib/misc/openpgp/openpgp.h new file mode 100644 index 000000000..538b31342 --- /dev/null +++ b/src/lib/misc/openpgp/openpgp.h @@ -0,0 +1,61 @@ +/* +* OpenPGP Codec +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_OPENPGP_CODEC_H__ +#define BOTAN_OPENPGP_CODEC_H__ + +#include <botan/data_src.h> +#include <string> +#include <map> + +namespace Botan { + +/** +* @param input the input data +* @param length length of input in bytes +* @param label the human-readable label +* @param headers a set of key/value pairs included in the header +*/ +BOTAN_DLL std::string PGP_encode( + const byte input[], + size_t length, + const std::string& label, + const std::map<std::string, std::string>& headers); + +/** +* @param input the input data +* @param length length of input in bytes +* @param label the human-readable label +*/ +BOTAN_DLL std::string PGP_encode( + const byte input[], + size_t length, + const std::string& label); + +/** +* @param source the input source +* @param label is set to the human-readable label +* @param headers is set to any headers +* @return decoded output as raw binary +*/ +BOTAN_DLL secure_vector<byte> PGP_decode( + DataSource& source, + std::string& label, + std::map<std::string, std::string>& headers); + +/** +* @param source the input source +* @param label is set to the human-readable label +* @return decoded output as raw binary +*/ +BOTAN_DLL secure_vector<byte> PGP_decode( + DataSource& source, + std::string& label); + +} + +#endif diff --git a/src/lib/misc/pbes2/info.txt b/src/lib/misc/pbes2/info.txt new file mode 100644 index 000000000..ed88ac3eb --- /dev/null +++ b/src/lib/misc/pbes2/info.txt @@ -0,0 +1,9 @@ +define PKCS5_PBES2 20141119 + +<requires> +asn1 +cbc +hmac +oid_lookup +pbkdf2 +</requires> diff --git a/src/lib/misc/pbes2/pbes2.cpp b/src/lib/misc/pbes2/pbes2.cpp new file mode 100644 index 000000000..17f14170d --- /dev/null +++ b/src/lib/misc/pbes2/pbes2.cpp @@ -0,0 +1,172 @@ +/* +* PKCS #5 PBES2 +* (C) 1999-2008,2014 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/pbes2.h> +#include <botan/internal/algo_registry.h> +#include <botan/cipher_mode.h> +#include <botan/pbkdf2.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/parsing.h> +#include <botan/alg_id.h> +#include <botan/oids.h> +#include <botan/rng.h> +#include <algorithm> + +namespace Botan { + +namespace { + +/* +* Encode PKCS#5 PBES2 parameters +*/ +std::vector<byte> encode_pbes2_params(const std::string& cipher, + const std::string& prf, + const secure_vector<byte>& salt, + const secure_vector<byte>& iv, + size_t iterations, + size_t key_length) + { + return DER_Encoder() + .start_cons(SEQUENCE) + .encode( + AlgorithmIdentifier("PKCS5.PBKDF2", + DER_Encoder() + .start_cons(SEQUENCE) + .encode(salt, OCTET_STRING) + .encode(iterations) + .encode(key_length) + .encode_if( + prf != "HMAC(SHA-160)", + AlgorithmIdentifier(prf, AlgorithmIdentifier::USE_NULL_PARAM)) + .end_cons() + .get_contents_unlocked() + ) + ) + .encode( + AlgorithmIdentifier(cipher, + DER_Encoder().encode(iv, OCTET_STRING).get_contents_unlocked() + ) + ) + .end_cons() + .get_contents_unlocked(); + } + +} + +/* +* PKCS#5 v2.0 PBE Constructor +*/ +std::pair<AlgorithmIdentifier, std::vector<byte>> +pbes2_encrypt(const secure_vector<byte>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + const std::string& cipher, + const std::string& digest, + RandomNumberGenerator& rng) + { + const std::string prf = "HMAC(" + digest + ")"; + + const 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); + + const secure_vector<byte> salt = rng.random_vec(12); + + if(cipher_spec[1] != "CBC" && cipher_spec[1] != "GCM") + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + std::unique_ptr<Keyed_Transform> enc(get_cipher_mode(cipher, ENCRYPTION)); + + PKCS5_PBKDF2 pbkdf(Algo_Registry<MessageAuthenticationCode>::global_registry().make(prf)); + + const size_t key_length = enc->key_spec().maximum_keylength(); + size_t iterations = 0; + + secure_vector<byte> iv = rng.random_vec(enc->default_nonce_length()); + + enc->set_key(pbkdf.derive_key(key_length, passphrase, &salt[0], salt.size(), + msec, iterations).bits_of()); + + enc->start(iv); + secure_vector<byte> buf = key_bits; + enc->finish(buf); + + AlgorithmIdentifier id( + OIDS::lookup("PBE-PKCS5v20"), + encode_pbes2_params(cipher, prf, salt, iv, iterations, key_length)); + + return std::make_pair(id, unlock(buf)); + } + +secure_vector<byte> +pbes2_decrypt(const secure_vector<byte>& key_bits, + const std::string& passphrase, + const std::vector<byte>& params) + { + AlgorithmIdentifier kdf_algo, enc_algo; + + BER_Decoder(params) + .start_cons(SEQUENCE) + .decode(kdf_algo) + .decode(enc_algo) + .verify_end() + .end_cons(); + + AlgorithmIdentifier prf_algo; + + if(kdf_algo.oid != OIDS::lookup("PKCS5.PBKDF2")) + throw Decoding_Error("PBE-PKCS5 v2.0: Unknown KDF algorithm " + + kdf_algo.oid.as_string()); + + secure_vector<byte> salt; + size_t iterations = 0, key_length = 0; + + BER_Decoder(kdf_algo.parameters) + .start_cons(SEQUENCE) + .decode(salt, OCTET_STRING) + .decode(iterations) + .decode_optional(key_length, INTEGER, UNIVERSAL) + .decode_optional(prf_algo, SEQUENCE, CONSTRUCTED, + AlgorithmIdentifier("HMAC(SHA-160)", + AlgorithmIdentifier::USE_NULL_PARAM)) + .verify_end() + .end_cons(); + + const std::string cipher = OIDS::lookup(enc_algo.oid); + const 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); + if(cipher_spec[1] != "CBC" && cipher_spec[1] != "GCM") + throw Decoding_Error("PBE-PKCS5 v2.0: Don't know param format for " + cipher); + + if(salt.size() < 8) + throw Decoding_Error("PBE-PKCS5 v2.0: Encoded salt is too small"); + + secure_vector<byte> iv; + BER_Decoder(enc_algo.parameters).decode(iv, OCTET_STRING).verify_end(); + + const std::string prf = OIDS::lookup(prf_algo.oid); + PKCS5_PBKDF2 pbkdf(Algo_Registry<MessageAuthenticationCode>::global_registry().make(prf)); + + std::unique_ptr<Keyed_Transform> dec(get_cipher_mode(cipher, DECRYPTION)); + + if(key_length == 0) + key_length = dec->key_spec().maximum_keylength(); + + dec->set_key(pbkdf.derive_key(key_length, passphrase, &salt[0], salt.size(), + iterations).bits_of()); + + dec->start(iv); + + secure_vector<byte> buf = key_bits; + dec->finish(buf); + + return buf; + } + +} diff --git a/src/lib/misc/pbes2/pbes2.h b/src/lib/misc/pbes2/pbes2.h new file mode 100644 index 000000000..90aa4f84b --- /dev/null +++ b/src/lib/misc/pbes2/pbes2.h @@ -0,0 +1,47 @@ +/* +* PKCS #5 v2.0 PBE +* (C) 1999-2007,2014 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PBE_PKCS_v20_H__ +#define BOTAN_PBE_PKCS_v20_H__ + +#include <botan/secmem.h> +#include <botan/transform.h> +#include <botan/alg_id.h> +#include <chrono> + +namespace Botan { + +/** +* Encrypt with PBES2 from PKCS #5 v2.0 +* @param passphrase the passphrase to use for encryption +* @param msec how many milliseconds 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<byte>> +BOTAN_DLL pbes2_encrypt(const secure_vector<byte>& key_bits, + const std::string& passphrase, + std::chrono::milliseconds msec, + 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 +* @param params the PBES2 parameters +*/ +secure_vector<byte> +BOTAN_DLL pbes2_decrypt(const secure_vector<byte>& key_bits, + const std::string& passphrase, + const std::vector<byte>& params); + +} + +#endif diff --git a/src/lib/misc/pem/info.txt b/src/lib/misc/pem/info.txt new file mode 100644 index 000000000..008240adc --- /dev/null +++ b/src/lib/misc/pem/info.txt @@ -0,0 +1,6 @@ +define PEM_CODEC 20131128 + +<requires> +base64 +filters +</requires> diff --git a/src/lib/misc/pem/pem.cpp b/src/lib/misc/pem/pem.cpp new file mode 100644 index 000000000..9b57c1531 --- /dev/null +++ b/src/lib/misc/pem/pem.cpp @@ -0,0 +1,147 @@ +/* +* PEM Encoding/Decoding +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/pem.h> +#include <botan/filters.h> +#include <botan/parsing.h> + +namespace Botan { + +namespace PEM_Code { + +/* +* PEM encode BER/DER-encoded objects +*/ +std::string encode(const byte der[], size_t length, const std::string& label, + size_t width) + { + const std::string PEM_HEADER = "-----BEGIN " + label + "-----\n"; + const std::string PEM_TRAILER = "-----END " + label + "-----\n"; + + Pipe pipe(new Base64_Encoder(true, width)); + pipe.process_msg(der, length); + return (PEM_HEADER + pipe.read_all_as_string() + PEM_TRAILER); + } + +/* +* Decode PEM down to raw BER/DER +*/ +secure_vector<byte> decode_check_label(DataSource& source, + const std::string& label_want) + { + std::string label_got; + secure_vector<byte> ber = decode(source, label_got); + if(label_got != label_want) + throw Decoding_Error("PEM: Label mismatch, wanted " + label_want + + ", got " + label_got); + return ber; + } + +/* +* Decode PEM down to raw BER/DER +*/ +secure_vector<byte> decode(DataSource& source, std::string& label) + { + const size_t RANDOM_CHAR_LIMIT = 8; + + const std::string PEM_HEADER1 = "-----BEGIN "; + const std::string PEM_HEADER2 = "-----"; + size_t position = 0; + + while(position != PEM_HEADER1.length()) + { + byte b; + if(!source.read_byte(b)) + throw Decoding_Error("PEM: No PEM header found"); + if(b == PEM_HEADER1[position]) + ++position; + else if(position >= RANDOM_CHAR_LIMIT) + throw Decoding_Error("PEM: Malformed PEM header"); + else + position = 0; + } + position = 0; + while(position != PEM_HEADER2.length()) + { + byte b; + if(!source.read_byte(b)) + throw Decoding_Error("PEM: No PEM header found"); + if(b == PEM_HEADER2[position]) + ++position; + else if(position) + throw Decoding_Error("PEM: Malformed PEM header"); + + if(position == 0) + label += static_cast<char>(b); + } + + Pipe base64(new Base64_Decoder); + base64.start_msg(); + + const std::string PEM_TRAILER = "-----END " + label + "-----"; + position = 0; + while(position != PEM_TRAILER.length()) + { + byte b; + if(!source.read_byte(b)) + throw Decoding_Error("PEM: No PEM trailer found"); + if(b == PEM_TRAILER[position]) + ++position; + else if(position) + throw Decoding_Error("PEM: Malformed PEM trailer"); + + if(position == 0) + base64.write(b); + } + base64.end_msg(); + return base64.read_all(); + } + +secure_vector<byte> decode_check_label(const std::string& pem, + const std::string& label_want) + { + DataSource_Memory src(pem); + return decode_check_label(src, label_want); + } + +secure_vector<byte> decode(const std::string& pem, std::string& label) + { + DataSource_Memory src(pem); + return decode(src, label); + } + +/* +* Search for a PEM signature +*/ +bool matches(DataSource& source, const std::string& extra, + size_t search_range) + { + const std::string PEM_HEADER = "-----BEGIN " + extra; + + secure_vector<byte> search_buf(search_range); + size_t got = source.peek(&search_buf[0], search_buf.size(), 0); + + if(got < PEM_HEADER.length()) + return false; + + size_t index = 0; + + for(size_t j = 0; j != got; ++j) + { + if(search_buf[j] == PEM_HEADER[index]) + ++index; + else + index = 0; + if(index == PEM_HEADER.size()) + return true; + } + return false; + } + +} + +} diff --git a/src/lib/misc/pem/pem.h b/src/lib/misc/pem/pem.h new file mode 100644 index 000000000..3b3fec32e --- /dev/null +++ b/src/lib/misc/pem/pem.h @@ -0,0 +1,90 @@ +/* +* PEM Encoding/Decoding +* (C) 1999-2007 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_PEM_H__ +#define BOTAN_PEM_H__ + +#include <botan/data_src.h> + +namespace Botan { + +namespace PEM_Code { + +/** +* Encode some binary data in PEM format +*/ +BOTAN_DLL std::string encode(const byte data[], + size_t data_len, + const std::string& label, + size_t line_width = 64); + +/** +* Encode some binary data in PEM format +*/ +inline std::string encode(const std::vector<byte>& data, + const std::string& label, + size_t line_width = 64) + { + return encode(&data[0], data.size(), label, line_width); + } + +/** +* Encode some binary data in PEM format +*/ +inline std::string encode(const secure_vector<byte>& data, + const std::string& label, + size_t line_width = 64) + { + return encode(&data[0], data.size(), label, line_width); + } + +/** +* Decode PEM data +* @param pem a datasource containing PEM encoded data +* @param label is set to the PEM label found for later inspection +*/ +BOTAN_DLL secure_vector<byte> decode(DataSource& pem, + std::string& label); + +/** +* Decode PEM data +* @param pem a string containing PEM encoded data +* @param label is set to the PEM label found for later inspection +*/ +BOTAN_DLL secure_vector<byte> decode(const std::string& pem, + std::string& label); + +/** +* Decode PEM data +* @param pem a datasource containing PEM encoded data +* @param label is what we expect the label to be +*/ +BOTAN_DLL secure_vector<byte> decode_check_label( + DataSource& pem, + const std::string& label); + +/** +* Decode PEM data +* @param pem a string containing PEM encoded data +* @param label is what we expect the label to be +*/ +BOTAN_DLL secure_vector<byte> decode_check_label( + const std::string& pem, + const std::string& label); + +/** +* Heuristic test for PEM data. +*/ +BOTAN_DLL bool matches(DataSource& source, + const std::string& extra = "", + size_t search_range = 4096); + +} + +} + +#endif diff --git a/src/lib/misc/rfc3394/info.txt b/src/lib/misc/rfc3394/info.txt new file mode 100644 index 000000000..8cd5989ca --- /dev/null +++ b/src/lib/misc/rfc3394/info.txt @@ -0,0 +1,5 @@ +define RFC3394_KEYWRAP 20131128 + +<requires> +aes +</requires> diff --git a/src/lib/misc/rfc3394/rfc3394.cpp b/src/lib/misc/rfc3394/rfc3394.cpp new file mode 100644 index 000000000..422f2a2dd --- /dev/null +++ b/src/lib/misc/rfc3394/rfc3394.cpp @@ -0,0 +1,119 @@ +/* +* AES Key Wrap (RFC 3394) +* (C) 2011 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/rfc3394.h> +#include <botan/internal/algo_registry.h> +#include <botan/block_cipher.h> +#include <botan/loadstor.h> +#include <botan/exceptn.h> +#include <botan/internal/xor_buf.h> + +namespace Botan { + +namespace { + +BlockCipher* make_aes(size_t keylength) + { + auto& block_ciphers = Algo_Registry<BlockCipher>::global_registry(); + if(keylength == 16) + return block_ciphers.make("AES-128"); + else if(keylength == 24) + return block_ciphers.make("AES-192"); + else if(keylength == 32) + return block_ciphers.make("AES-256"); + else + throw std::invalid_argument("Bad KEK length for NIST keywrap"); + } + +} + +secure_vector<byte> rfc3394_keywrap(const secure_vector<byte>& key, + const SymmetricKey& kek) + { + if(key.size() % 8 != 0) + throw std::invalid_argument("Bad input key size for NIST key wrap"); + + std::unique_ptr<BlockCipher> aes(make_aes(kek.length())); + aes->set_key(kek); + + const size_t n = key.size() / 8; + + secure_vector<byte> R((n + 1) * 8); + secure_vector<byte> A(16); + + for(size_t i = 0; i != 8; ++i) + A[i] = 0xA6; + + copy_mem(&R[8], &key[0], key.size()); + + for(size_t j = 0; j <= 5; ++j) + { + for(size_t i = 1; i <= n; ++i) + { + const u32bit t = (n * j) + i; + + copy_mem(&A[8], &R[8*i], 8); + + aes->encrypt(&A[0]); + copy_mem(&R[8*i], &A[8], 8); + + byte t_buf[4] = { 0 }; + store_be(t, t_buf); + xor_buf(&A[4], &t_buf[0], 4); + } + } + + copy_mem(&R[0], &A[0], 8); + + return R; + } + +secure_vector<byte> rfc3394_keyunwrap(const secure_vector<byte>& key, + const SymmetricKey& kek) + { + if(key.size() < 16 || key.size() % 8 != 0) + throw std::invalid_argument("Bad input key size for NIST key unwrap"); + + std::unique_ptr<BlockCipher> aes(make_aes(kek.length())); + aes->set_key(kek); + + const size_t n = (key.size() - 8) / 8; + + secure_vector<byte> R(n * 8); + secure_vector<byte> A(16); + + for(size_t i = 0; i != 8; ++i) + A[i] = key[i]; + + copy_mem(&R[0], &key[8], key.size() - 8); + + for(size_t j = 0; j <= 5; ++j) + { + for(size_t i = n; i != 0; --i) + { + const u32bit t = (5 - j) * n + i; + + byte t_buf[4] = { 0 }; + store_be(t, t_buf); + + xor_buf(&A[4], &t_buf[0], 4); + + copy_mem(&A[8], &R[8*(i-1)], 8); + + aes->decrypt(&A[0]); + + copy_mem(&R[8*(i-1)], &A[8], 8); + } + } + + if(load_be<u64bit>(&A[0], 0) != 0xA6A6A6A6A6A6A6A6) + throw Integrity_Failure("NIST key unwrap failed"); + + return R; + } + +} diff --git a/src/lib/misc/rfc3394/rfc3394.h b/src/lib/misc/rfc3394/rfc3394.h new file mode 100644 index 000000000..fab6bc3cb --- /dev/null +++ b/src/lib/misc/rfc3394/rfc3394.h @@ -0,0 +1,40 @@ +/* +* AES Key Wrap (RFC 3394) +* (C) 2011 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AES_KEY_WRAP_H__ +#define BOTAN_AES_KEY_WRAP_H__ + +#include <botan/symkey.h> + +namespace Botan { + +/** +* Encrypt a key under a key encryption key using the algorithm +* described in RFC 3394 +* +* @param key the plaintext key to encrypt +* @param kek the key encryption key +* @return key encrypted under kek +*/ +secure_vector<byte> BOTAN_DLL rfc3394_keywrap(const secure_vector<byte>& key, + const SymmetricKey& kek); + +/** +* Decrypt a key under a key encryption key using the algorithm +* described in RFC 3394 +* +* @param key the encrypted key to decrypt +* @param kek the key encryption key +* @param af an algorithm factory +* @return key decrypted under kek +*/ +secure_vector<byte> BOTAN_DLL rfc3394_keyunwrap(const secure_vector<byte>& key, + const SymmetricKey& kek); + +} + +#endif diff --git a/src/lib/misc/srp6/info.txt b/src/lib/misc/srp6/info.txt new file mode 100644 index 000000000..5b07264b4 --- /dev/null +++ b/src/lib/misc/srp6/info.txt @@ -0,0 +1,6 @@ +define SRP6 20131128 + +<requires> +bigint +dl_group +</requires> diff --git a/src/lib/misc/srp6/srp6.cpp b/src/lib/misc/srp6/srp6.cpp new file mode 100644 index 000000000..d3f7338bd --- /dev/null +++ b/src/lib/misc/srp6/srp6.cpp @@ -0,0 +1,157 @@ +/* +* SRP-6a (RFC 5054 compatatible) +* (C) 2011,2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/srp6.h> +#include <botan/dl_group.h> +#include <botan/numthry.h> +#include <botan/lookup.h> + +namespace Botan { + +namespace { + +BigInt hash_seq(const std::string& hash_id, + size_t pad_to, + const BigInt& in1, + const BigInt& in2) + { + std::unique_ptr<HashFunction> hash_fn(get_hash(hash_id)); + + hash_fn->update(BigInt::encode_1363(in1, pad_to)); + hash_fn->update(BigInt::encode_1363(in2, pad_to)); + + return BigInt::decode(hash_fn->final()); + } + +BigInt compute_x(const std::string& hash_id, + const std::string& identifier, + const std::string& password, + const std::vector<byte>& salt) + { + std::unique_ptr<HashFunction> hash_fn(get_hash(hash_id)); + + hash_fn->update(identifier); + hash_fn->update(":"); + hash_fn->update(password); + + secure_vector<byte> inner_h = hash_fn->final(); + + hash_fn->update(salt); + hash_fn->update(inner_h); + + secure_vector<byte> outer_h = hash_fn->final(); + + return BigInt::decode(outer_h); + } + +} + +std::string srp6_group_identifier(const BigInt& N, const BigInt& g) + { + /* + This function assumes that only one 'standard' SRP parameter set has + been defined for a particular bitsize. As of this writing that is the case. + */ + try + { + const std::string group_name = "modp/srp/" + std::to_string(N.bits()); + + DL_Group group(group_name); + + if(group.get_p() == N && group.get_g() == g) + return group_name; + + throw std::runtime_error("Unknown SRP params"); + } + catch(...) + { + throw Invalid_Argument("Bad SRP group parameters"); + } + } + +std::pair<BigInt, SymmetricKey> +srp6_client_agree(const std::string& identifier, + const std::string& password, + const std::string& group_id, + const std::string& hash_id, + const std::vector<byte>& salt, + const BigInt& B, + RandomNumberGenerator& rng) + { + DL_Group group(group_id); + const BigInt& g = group.get_g(); + const BigInt& p = group.get_p(); + + const size_t p_bytes = group.get_p().bytes(); + + if(B <= 0 || B >= p) + throw std::runtime_error("Invalid SRP parameter from server"); + + BigInt k = hash_seq(hash_id, p_bytes, p, g); + + BigInt a(rng, 256); + + BigInt A = power_mod(g, a, p); + + BigInt u = hash_seq(hash_id, p_bytes, A, B); + + const BigInt x = compute_x(hash_id, identifier, password, salt); + + BigInt S = power_mod((B - (k * power_mod(g, x, p))) % p, (a + (u * x)), p); + + SymmetricKey Sk(BigInt::encode_1363(S, p_bytes)); + + return std::make_pair(A, Sk); + } + +BigInt generate_srp6_verifier(const std::string& identifier, + const std::string& password, + const std::vector<byte>& salt, + const std::string& group_id, + const std::string& hash_id) + { + const BigInt x = compute_x(hash_id, identifier, password, salt); + + DL_Group group(group_id); + return power_mod(group.get_g(), x, group.get_p()); + } + +BigInt SRP6_Server_Session::step1(const BigInt& v, + const std::string& group_id, + const std::string& hash_id, + RandomNumberGenerator& rng) + { + DL_Group group(group_id); + const BigInt& g = group.get_g(); + const BigInt& p = group.get_p(); + + m_p_bytes = p.bytes(); + m_v = v; + m_b = BigInt(rng, 256); + m_p = p; + m_hash_id = hash_id; + + const BigInt k = hash_seq(hash_id, m_p_bytes, p, g); + + m_B = (v*k + power_mod(g, m_b, p)) % p; + + return m_B; + } + +SymmetricKey SRP6_Server_Session::step2(const BigInt& A) + { + if(A <= 0 || A >= m_p) + throw std::runtime_error("Invalid SRP parameter from client"); + + const BigInt u = hash_seq(m_hash_id, m_p_bytes, A, m_B); + + const BigInt S = power_mod(A * power_mod(m_v, u, m_p), m_b, m_p); + + return BigInt::encode_1363(S, m_p_bytes); + } + +} diff --git a/src/lib/misc/srp6/srp6.h b/src/lib/misc/srp6/srp6.h new file mode 100644 index 000000000..3eb21b742 --- /dev/null +++ b/src/lib/misc/srp6/srp6.h @@ -0,0 +1,97 @@ +/* +* SRP-6a (RFC 5054 compatatible) +* (C) 2011,2012 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_RFC5054_SRP6_H__ +#define BOTAN_RFC5054_SRP6_H__ + +#include <botan/bigint.h> +#include <botan/hash.h> +#include <botan/rng.h> +#include <botan/symkey.h> +#include <string> + +namespace Botan { + +/** +* SRP6a Client side +* @param username the username we are attempting login for +* @param password the password we are attempting to use +* @param group_id specifies the shared SRP group +* @param hash_id specifies a secure hash function +* @param salt is the salt value sent by the server +* @param B is the server's public value +* @param rng is a random number generator +* +* @return (A,K) the client public key and the shared secret key +*/ +std::pair<BigInt,SymmetricKey> +BOTAN_DLL srp6_client_agree(const std::string& username, + const std::string& password, + const std::string& group_id, + const std::string& hash_id, + const std::vector<byte>& salt, + const BigInt& B, + RandomNumberGenerator& rng); + +/** +* Generate a new SRP-6 verifier +* @param identifier a username or other client identifier +* @param password the secret used to authenticate user +* @param salt a randomly chosen value, at least 128 bits long +* @param group_id specifies the shared SRP group +* @param hash_id specifies a secure hash function +*/ +BigInt BOTAN_DLL generate_srp6_verifier(const std::string& identifier, + const std::string& password, + const std::vector<byte>& salt, + const std::string& group_id, + const std::string& hash_id); + +/** +* Return the group id for this SRP param set, or else thrown an +* exception +* @param N the group modulus +* @param g the group generator +* @return group identifier +*/ +std::string BOTAN_DLL srp6_group_identifier(const BigInt& N, const BigInt& g); + +/** +* Represents a SRP-6a server session +*/ +class BOTAN_DLL SRP6_Server_Session + { + public: + /** + * Server side step 1 + * @param v the verification value saved from client registration + * @param group_id the SRP group id + * @param hash_id the SRP hash in use + * @param rng a random number generator + * @return SRP-6 B value + */ + BigInt step1(const BigInt& v, + const std::string& group_id, + const std::string& hash_id, + RandomNumberGenerator& rng); + + /** + * Server side step 2 + * @param A the client's value + * @return shared symmetric key + */ + SymmetricKey step2(const BigInt& A); + + private: + std::string m_hash_id; + BigInt m_B, m_b, m_v, m_S, m_p; + size_t m_p_bytes; + }; + +} + +#endif diff --git a/src/lib/misc/srp6/srp6_files.cpp b/src/lib/misc/srp6/srp6_files.cpp new file mode 100644 index 000000000..50f51fa75 --- /dev/null +++ b/src/lib/misc/srp6/srp6_files.cpp @@ -0,0 +1,69 @@ +/* +* SRP-6a File Handling +* (C) 2011 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/srp6_files.h> +#include <botan/parsing.h> +#include <botan/base64.h> +#include <fstream> + +namespace Botan { + +SRP6_Authenticator_File::SRP6_Authenticator_File(const std::string& filename) + { + std::ifstream in(filename.c_str()); + + if(!in) + return; // no entries + + while(in.good()) + { + std::string line; + std::getline(in, line); + + std::vector<std::string> parts = split_on(line, ':'); + + if(parts.size() != 4) + throw Decoding_Error("Invalid line in SRP authenticator file"); + + std::string username = parts[0]; + BigInt v = BigInt::decode(base64_decode(parts[1])); + std::vector<byte> salt = unlock(base64_decode(parts[2])); + BigInt group_id_idx = BigInt::decode(base64_decode(parts[3])); + + std::string group_id; + + if(group_id_idx == 1) + group_id = "modp/srp/1024"; + else if(group_id_idx == 2) + group_id = "modp/srp/1536"; + else if(group_id_idx == 3) + group_id = "modp/srp/2048"; + else + continue; // unknown group, ignored + + entries[username] = SRP6_Data(v, salt, group_id); + } + } + +bool SRP6_Authenticator_File::lookup_user(const std::string& username, + BigInt& v, + std::vector<byte>& salt, + std::string& group_id) const + { + std::map<std::string, SRP6_Data>::const_iterator i = entries.find(username); + + if(i == entries.end()) + return false; + + v = i->second.v; + salt = i->second.salt; + group_id = i->second.group_id; + + return true; + } + +} diff --git a/src/lib/misc/srp6/srp6_files.h b/src/lib/misc/srp6/srp6_files.h new file mode 100644 index 000000000..45c3b0bfe --- /dev/null +++ b/src/lib/misc/srp6/srp6_files.h @@ -0,0 +1,53 @@ +/* +* SRP-6a File Handling +* (C) 2011 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SRP6A_FILES_H__ +#define BOTAN_SRP6A_FILES_H__ + +#include <botan/bigint.h> +#include <string> +#include <map> + +namespace Botan { + +/** +* A GnuTLS compatible SRP6 authenticator file +*/ +class BOTAN_DLL SRP6_Authenticator_File + { + public: + /** + * @param filename will be opened and processed as a SRP + * authenticator file + */ + SRP6_Authenticator_File(const std::string& filename); + + bool lookup_user(const std::string& username, + BigInt& v, + std::vector<byte>& salt, + std::string& group_id) const; + private: + struct SRP6_Data + { + SRP6_Data() {} + + SRP6_Data(const BigInt& v, + const std::vector<byte>& salt, + const std::string& group_id) : + v(v), salt(salt), group_id(group_id) {} + + BigInt v; + std::vector<byte> salt; + std::string group_id; + }; + + std::map<std::string, SRP6_Data> entries; + }; + +} + +#endif diff --git a/src/lib/misc/tss/info.txt b/src/lib/misc/tss/info.txt new file mode 100644 index 000000000..c4fed288d --- /dev/null +++ b/src/lib/misc/tss/info.txt @@ -0,0 +1,7 @@ +define THRESHOLD_SECRET_SHARING 20131128 + +<requires> +rng +filters +hex +</requires> diff --git a/src/lib/misc/tss/tss.cpp b/src/lib/misc/tss/tss.cpp new file mode 100644 index 000000000..c021bff7b --- /dev/null +++ b/src/lib/misc/tss/tss.cpp @@ -0,0 +1,261 @@ +/* +* RTSS (threshold secret sharing) +* (C) 2009 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/tss.h> +#include <botan/loadstor.h> +#include <botan/pipe.h> +#include <botan/hex.h> +#include <botan/sha2_32.h> +#include <botan/sha160.h> + +namespace Botan { + +namespace { + +/** +Table for GF(2^8) arithmetic (exponentials) +*/ +const byte RTSS_EXP[256] = { +0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, +0x96, 0xA1, 0xF8, 0x13, 0x35, 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73, +0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, 0xE5, +0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, +0x90, 0xAB, 0xE6, 0x31, 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, +0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, 0x4C, 0xD4, +0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18, +0x28, 0x78, 0x88, 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, +0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, 0xB5, 0xC4, 0x57, +0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, +0x61, 0xA3, 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, +0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, 0xFB, 0x16, 0x3A, 0x4E, +0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, +0x41, 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED, +0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, 0x9F, 0xBA, 0xD5, 0x64, 0xAC, +0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80, +0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, +0xB1, 0xC8, 0x43, 0xC5, 0x54, 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, +0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, 0x45, +0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, +0xC6, 0x51, 0xF3, 0x0E, 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D, +0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, 0x39, 0x4B, +0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, +0x52, 0xF6, 0x01 }; + +/** +Table for GF(2^8) arithmetic (logarithms) +*/ +const byte RTSS_LOG[] = { +0x90, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, +0x68, 0x33, 0xEE, 0xDF, 0x03, 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D, +0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, 0x7D, +0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, +0x9A, 0xC9, 0x09, 0x78, 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, +0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, 0x96, 0x8F, +0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40, +0x46, 0x83, 0x38, 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, +0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, 0x7E, 0x6E, 0x48, +0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, +0x3D, 0xBA, 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, +0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, 0xAF, 0x58, 0xA8, 0x50, +0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, +0xE8, 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB, +0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, 0x7F, 0x0C, 0xF6, 0x6F, 0x17, +0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7, +0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, +0x6C, 0xAA, 0x55, 0x29, 0x9D, 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, +0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, 0x53, +0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, +0x56, 0xF2, 0xD3, 0xAB, 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E, +0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, 0x67, 0x4A, +0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, +0xF7, 0x70, 0x07 }; + +byte gfp_mul(byte x, byte y) + { + if(x == 0 || y == 0) + return 0; + return RTSS_EXP[(RTSS_LOG[x] + RTSS_LOG[y]) % 255]; + } + +byte rtss_hash_id(const std::string& hash_name) + { + if(hash_name == "SHA-160") + return 1; + else if(hash_name == "SHA-256") + return 2; + else + throw Invalid_Argument("RTSS only supports SHA-1 and SHA-256"); + } + +HashFunction* get_rtss_hash_by_id(byte id) + { + if(id == 1) + return new SHA_160; + else if(id == 2) + return new SHA_256; + else + throw Decoding_Error("Bad RTSS hash identifier"); + } + +} + +RTSS_Share::RTSS_Share(const std::string& hex_input) + { + contents = hex_decode_locked(hex_input); + } + +byte RTSS_Share::share_id() const + { + if(!initialized()) + throw Invalid_State("RTSS_Share::share_id not initialized"); + + return contents[20]; + } + +std::string RTSS_Share::to_string() const + { + return hex_encode(&contents[0], contents.size()); + } + +std::vector<RTSS_Share> +RTSS_Share::split(byte M, byte N, + const byte S[], u16bit S_len, + const byte identifier[16], + RandomNumberGenerator& rng) + { + if(M == 0 || N == 0 || M > N) + throw Encoding_Error("RTSS_Share::split: M == 0 or N == 0 or M > N"); + + SHA_256 hash; // always use SHA-256 when generating shares + + std::vector<RTSS_Share> shares(N); + + // Create RTSS header in each share + for(byte i = 0; i != N; ++i) + { + shares[i].contents += std::make_pair(identifier, 16); + shares[i].contents += rtss_hash_id(hash.name()); + shares[i].contents += M; + shares[i].contents += get_byte(0, S_len); + shares[i].contents += get_byte(1, S_len); + } + + // Choose sequential values for X starting from 1 + for(byte i = 0; i != N; ++i) + shares[i].contents.push_back(i+1); + + // secret = S || H(S) + secure_vector<byte> secret(S, S + S_len); + secret += hash.process(S, S_len); + + for(size_t i = 0; i != secret.size(); ++i) + { + std::vector<byte> coefficients(M-1); + rng.randomize(&coefficients[0], coefficients.size()); + + for(byte j = 0; j != N; ++j) + { + const byte X = j + 1; + + byte sum = secret[i]; + byte X_i = X; + + for(size_t k = 0; k != coefficients.size(); ++k) + { + sum ^= gfp_mul(X_i, coefficients[k]); + X_i = gfp_mul(X_i, X); + } + + shares[j].contents.push_back(sum); + } + } + + return shares; + } + +secure_vector<byte> +RTSS_Share::reconstruct(const std::vector<RTSS_Share>& shares) + { + const size_t RTSS_HEADER_SIZE = 20; + + for(size_t i = 0; i != shares.size(); ++i) + { + if(shares[i].size() != shares[0].size()) + throw Decoding_Error("Different sized RTSS shares detected"); + if(shares[i].share_id() == 0) + throw Decoding_Error("Invalid (id = 0) RTSS share detected"); + if(shares[i].size() < RTSS_HEADER_SIZE) + throw Decoding_Error("Missing or malformed RTSS header"); + + if(!same_mem(&shares[0].contents[0], + &shares[i].contents[0], RTSS_HEADER_SIZE)) + throw Decoding_Error("Different RTSS headers detected"); + } + + if(shares.size() < shares[0].contents[17]) + throw Decoding_Error("Insufficient shares to do TSS reconstruction"); + + u16bit secret_len = make_u16bit(shares[0].contents[18], + shares[0].contents[19]); + + byte hash_id = shares[0].contents[16]; + + std::unique_ptr<HashFunction> hash(get_rtss_hash_by_id(hash_id)); + + if(shares[0].size() != secret_len + hash->output_length() + RTSS_HEADER_SIZE + 1) + throw Decoding_Error("Bad RTSS length field in header"); + + std::vector<byte> V(shares.size()); + secure_vector<byte> secret; + + for(size_t i = RTSS_HEADER_SIZE + 1; i != shares[0].size(); ++i) + { + for(size_t j = 0; j != V.size(); ++j) + V[j] = shares[j].contents[i]; + + byte r = 0; + for(size_t k = 0; k != shares.size(); ++k) + { + // L_i function: + byte r2 = 1; + for(size_t l = 0; l != shares.size(); ++l) + { + if(k == l) + continue; + + byte share_k = shares[k].share_id(); + byte share_l = shares[l].share_id(); + + if(share_k == share_l) + throw Decoding_Error("Duplicate shares found in RTSS recovery"); + + byte div = RTSS_EXP[(255 + + RTSS_LOG[share_l] - + RTSS_LOG[share_k ^ share_l]) % 255]; + + r2 = gfp_mul(r2, div); + } + + r ^= gfp_mul(V[k], r2); + } + secret.push_back(r); + } + + if(secret.size() != secret_len + hash->output_length()) + throw Decoding_Error("Bad length in RTSS output"); + + hash->update(&secret[0], secret_len); + secure_vector<byte> hash_check = hash->final(); + + if(!same_mem(&hash_check[0], + &secret[secret_len], hash->output_length())) + throw Decoding_Error("RTSS hash check failed"); + + return secure_vector<byte>(&secret[0], &secret[secret_len]); + } + +} diff --git a/src/lib/misc/tss/tss.h b/src/lib/misc/tss/tss.h new file mode 100644 index 000000000..09a5dbe19 --- /dev/null +++ b/src/lib/misc/tss/tss.h @@ -0,0 +1,76 @@ +/* +* RTSS (threshold secret sharing) +* (C) 2009 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_RTSS_H__ +#define BOTAN_RTSS_H__ + +#include <botan/secmem.h> +#include <botan/hash.h> +#include <botan/rng.h> +#include <vector> + +namespace Botan { + +/** +* A split secret, using the format from draft-mcgrew-tss-03 +*/ +class BOTAN_DLL RTSS_Share + { + public: + /** + * @param M the number of shares needed to reconstruct + * @param N the number of shares generated + * @param secret the secret to split + * @param secret_len the length of the secret + * @param identifier the 16 byte share identifier + * @param rng the random number generator to use + */ + static std::vector<RTSS_Share> + split(byte M, byte N, + const byte secret[], u16bit secret_len, + const byte identifier[16], + RandomNumberGenerator& rng); + + /** + * @param shares the list of shares + */ + static secure_vector<byte> + reconstruct(const std::vector<RTSS_Share>& shares); + + RTSS_Share() {} + + /** + * @param hex_input the share encoded in hexadecimal + */ + RTSS_Share(const std::string& hex_input); + + /** + * @return hex representation + */ + std::string to_string() const; + + /** + * @return share identifier + */ + byte share_id() const; + + /** + * @return size of this share in bytes + */ + size_t size() const { return contents.size(); } + + /** + * @return if this TSS share was initialized or not + */ + bool initialized() const { return (contents.size() > 0); } + private: + secure_vector<byte> contents; + }; + +} + +#endif |