diff options
author | lloyd <[email protected]> | 2009-11-17 07:19:37 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2009-11-17 07:19:37 +0000 |
commit | e00b46cf9c1dcb364ebb7d5968d6ff9dcd600c4e (patch) | |
tree | 8ffb8d032f06bbcb7ab376c7469751a25b556dca /src/constructs | |
parent | a98a9ff5f95bd4dca9c1eda11e27e712c869cd66 (diff) |
Move most code that relies heavily on Filters into src/filters.
Remove support for (unused) modset settings.
Move tss, fpe, cryptobox, and aont to new dir constructs
Diffstat (limited to 'src/constructs')
-rw-r--r-- | src/constructs/aont/info.txt | 15 | ||||
-rw-r--r-- | src/constructs/aont/package.cpp | 120 | ||||
-rw-r--r-- | src/constructs/aont/package.h | 45 | ||||
-rw-r--r-- | src/constructs/cryptobox/cryptobox.cpp | 145 | ||||
-rw-r--r-- | src/constructs/cryptobox/cryptobox.h | 42 | ||||
-rw-r--r-- | src/constructs/cryptobox/info.txt | 20 | ||||
-rw-r--r-- | src/constructs/fpe/fpe.cpp | 195 | ||||
-rw-r--r-- | src/constructs/fpe/fpe.h | 32 | ||||
-rw-r--r-- | src/constructs/fpe/info.txt | 13 | ||||
-rw-r--r-- | src/constructs/tss/info.txt | 8 | ||||
-rw-r--r-- | src/constructs/tss/tss.cpp | 263 | ||||
-rw-r--r-- | src/constructs/tss/tss.h | 55 |
12 files changed, 953 insertions, 0 deletions
diff --git a/src/constructs/aont/info.txt b/src/constructs/aont/info.txt new file mode 100644 index 000000000..533b70eb5 --- /dev/null +++ b/src/constructs/aont/info.txt @@ -0,0 +1,15 @@ +define PACKAGE_TRANSFORM + +load_on auto + +<add> +package.cpp +package.h +</add> + +<requires> +block +ctr +rng +filters +</requires> diff --git a/src/constructs/aont/package.cpp b/src/constructs/aont/package.cpp new file mode 100644 index 000000000..37bad46c8 --- /dev/null +++ b/src/constructs/aont/package.cpp @@ -0,0 +1,120 @@ +/* +* Rivest's Package Tranform +* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/package.h> +#include <botan/filters.h> +#include <botan/ctr.h> +#include <botan/loadstor.h> +#include <botan/xor_buf.h> + +namespace Botan { + +namespace AllOrNothingTransform { + +void package(RandomNumberGenerator& rng, + BlockCipher* cipher, + const byte input[], u32bit input_len, + byte output[]) + { + if(!cipher->valid_keylength(cipher->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(cipher->BLOCK_SIZE*2, '0'); + + SymmetricKey package_key(rng, cipher->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)); + + SecureVector<byte> buf(cipher->BLOCK_SIZE); + + const u32bit blocks = + (input_len + cipher->BLOCK_SIZE - 1) / cipher->BLOCK_SIZE; + + byte* final_block = output + input_len; + clear_mem(final_block, cipher->BLOCK_SIZE); + + // XOR the hash blocks into the final block + for(u32bit i = 0; i != blocks; ++i) + { + u32bit left = std::min<u32bit>(cipher->BLOCK_SIZE, + input_len - cipher->BLOCK_SIZE * i); + + buf.clear(); + copy_mem(&buf[0], output + cipher->BLOCK_SIZE * i, left); + + for(u32bit j = 0; j != 4; ++j) + buf[cipher->BLOCK_SIZE - 1 - j] ^= get_byte(3-j, i); + + cipher->encrypt(buf); + + xor_buf(final_block, buf, cipher->BLOCK_SIZE); + } + + // XOR the random package key into the final block + xor_buf(final_block, package_key.begin(), cipher->BLOCK_SIZE); + } + +void unpackage(BlockCipher* cipher, + const byte input[], u32bit input_len, + byte output[]) + { + if(!cipher->valid_keylength(cipher->BLOCK_SIZE)) + throw Invalid_Argument("AONT::unpackage: Invalid cipher"); + + if(input_len < cipher->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(cipher->BLOCK_SIZE*2, '0'); + + cipher->set_key(SymmetricKey(all_zeros)); + + SecureVector<byte> package_key(cipher->BLOCK_SIZE); + SecureVector<byte> buf(cipher->BLOCK_SIZE); + + // Copy the package key (masked with the block hashes) + copy_mem(&package_key[0], + input + (input_len - cipher->BLOCK_SIZE), + cipher->BLOCK_SIZE); + + const u32bit blocks = ((input_len - 1) / cipher->BLOCK_SIZE); + + // XOR the blocks into the package key bits + for(u32bit i = 0; i != blocks; ++i) + { + u32bit left = std::min<u32bit>(cipher->BLOCK_SIZE, + input_len - cipher->BLOCK_SIZE * (i+1)); + + buf.clear(); + copy_mem(&buf[0], input + cipher->BLOCK_SIZE * i, left); + + for(u32bit j = 0; j != 4; ++j) + buf[cipher->BLOCK_SIZE - 1 - j] ^= get_byte(3-j, i); + + cipher->encrypt(buf); + + xor_buf(&package_key[0], buf, cipher->BLOCK_SIZE); + } + + Pipe pipe(new StreamCipher_Filter(new CTR_BE(cipher), package_key)); + + pipe.process_msg(input, input_len - cipher->BLOCK_SIZE); + + pipe.read(output, pipe.remaining()); + } + +} + +} diff --git a/src/constructs/aont/package.h b/src/constructs/aont/package.h new file mode 100644 index 000000000..35d2a23fc --- /dev/null +++ b/src/constructs/aont/package.h @@ -0,0 +1,45 @@ +/* +* Rivest's Package Tranform +* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/block_cipher.h> +#include <botan/rng.h> + +namespace Botan { + +namespace AllOrNothingTransform { + +/** +* Rivest's Package Tranform +* @arg rng the random number generator to use +* @arg cipher the block cipher to use +* @arg input the input data buffer +* @arg input_len the length of the input data in bytes +* @arg output the output data buffer (must be at least +* input_len + cipher->BLOCK_SIZE bytes long) +*/ +void package(RandomNumberGenerator& rng, + BlockCipher* cipher, + const byte input[], u32bit input_len, + byte output[]); + +/** +* Rivest's Package Tranform (Inversion) +* @arg rng the random number generator to use +* @arg cipher the block cipher to use +* @arg input the input data buffer +* @arg input_len the length of the input data in bytes +* @arg output the output data buffer (must be at least +* input_len - cipher->BLOCK_SIZE bytes long) +*/ +void unpackage(BlockCipher* cipher, + const byte input[], u32bit input_len, + byte output[]); + +} + +} diff --git a/src/constructs/cryptobox/cryptobox.cpp b/src/constructs/cryptobox/cryptobox.cpp new file mode 100644 index 000000000..ba7553c55 --- /dev/null +++ b/src/constructs/cryptobox/cryptobox.cpp @@ -0,0 +1,145 @@ +/* +* Cryptobox Message Routines +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#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/loadstor.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 u32bit VERSION_CODE_LEN = 4; +const u32bit CIPHER_KEY_LEN = 32; +const u32bit CIPHER_IV_LEN = 16; +const u32bit MAC_KEY_LEN = 32; +const u32bit MAC_OUTPUT_LEN = 20; +const u32bit PBKDF_SALT_LEN = 10; +const u32bit PBKDF_ITERATIONS = 8 * 1024; + +const u32bit PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + CIPHER_IV_LEN + MAC_KEY_LEN; + +} + +std::string encrypt(const byte input[], u32bit input_len, + const std::string& passphrase, + RandomNumberGenerator& rng) + { + SecureVector<byte> pbkdf_salt(PBKDF_SALT_LEN); + rng.randomize(pbkdf_salt.begin(), pbkdf_salt.size()); + + PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512)); + pbkdf.change_salt(pbkdf_salt.begin(), pbkdf_salt.size()); + pbkdf.set_iterations(PBKDF_ITERATIONS); + + OctetString mk = pbkdf.derive_key(PBKDF_OUTPUT_LEN, passphrase); + + SymmetricKey cipher_key(mk.begin(), CIPHER_KEY_LEN); + SymmetricKey mac_key(mk.begin() + CIPHER_KEY_LEN, MAC_KEY_LEN); + InitializationVector iv(mk.begin() + CIPHER_KEY_LEN + MAC_KEY_LEN, + CIPHER_IV_LEN); + + Pipe pipe(get_cipher("Serpent/CTR-BE", cipher_key, iv, ENCRYPTION), + new Fork( + 0, + 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 + */ + u32bit ciphertext_len = pipe.remaining(0); + + SecureVector<byte> out_buf; + + for(u32bit i = 0; i != VERSION_CODE_LEN; ++i) + out_buf.append(get_byte(i, CRYPTOBOX_VERSION_CODE)); + + out_buf.append(pbkdf_salt.begin(), pbkdf_salt.size()); + + out_buf.grow_to(out_buf.size() + MAC_OUTPUT_LEN + ciphertext_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.begin(), out_buf.size(), + "BOTAN CRYPTOBOX MESSAGE"); + } + +std::string decrypt(const byte input[], u32bit input_len, + const std::string& passphrase) + { + DataSource_Memory input_src(input, input_len); + SecureVector<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(u32bit i = 0; i != VERSION_CODE_LEN; ++i) + if(ciphertext[i] != get_byte(i, CRYPTOBOX_VERSION_CODE)) + throw Decoding_Error("Bad CryptoBox version"); + + SecureVector<byte> pbkdf_salt(ciphertext + VERSION_CODE_LEN, PBKDF_SALT_LEN); + + PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512)); + pbkdf.change_salt(pbkdf_salt.begin(), pbkdf_salt.size()); + pbkdf.set_iterations(PBKDF_ITERATIONS); + + OctetString mk = pbkdf.derive_key(PBKDF_OUTPUT_LEN, passphrase); + + SymmetricKey cipher_key(mk.begin(), CIPHER_KEY_LEN); + SymmetricKey mac_key(mk.begin() + CIPHER_KEY_LEN, MAC_KEY_LEN); + InitializationVector iv(mk.begin() + CIPHER_KEY_LEN + MAC_KEY_LEN, + CIPHER_IV_LEN); + + Pipe pipe(new Fork( + get_cipher("Serpent/CTR-BE", cipher_key, iv, ENCRYPTION), + new MAC_Filter(new HMAC(new SHA_512), + mac_key, MAC_OUTPUT_LEN))); + + const u32bit 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 Integrity_Failure("CryptoBox integrity failure"); + + return pipe.read_all_as_string(0); + } + +} + +} diff --git a/src/constructs/cryptobox/cryptobox.h b/src/constructs/cryptobox/cryptobox.h new file mode 100644 index 000000000..a30cb244a --- /dev/null +++ b/src/constructs/cryptobox/cryptobox.h @@ -0,0 +1,42 @@ +/* +* Cryptobox Message Routines +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_CRYPTOBOX_H__ +#define BOTAN_CRYPTOBOX_H__ + +#include <string> +#include <botan/rng.h> + +namespace Botan { + +namespace CryptoBox { + +/** +* Encrypt a message +* @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[], u32bit 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[], u32bit input_len, + const std::string& passphrase); + +} + +} + +#endif diff --git a/src/constructs/cryptobox/info.txt b/src/constructs/cryptobox/info.txt new file mode 100644 index 000000000..0780f55aa --- /dev/null +++ b/src/constructs/cryptobox/info.txt @@ -0,0 +1,20 @@ +load_on auto + +define CRYPTO_BOX + +<add> +cryptobox.h +cryptobox.cpp +</add> + +<requires> +filters +ctr +hmac +rng +serpent +sha2 +base64 +pbkdf2 +pem +</requires> diff --git a/src/constructs/fpe/fpe.cpp b/src/constructs/fpe/fpe.cpp new file mode 100644 index 000000000..86e56625d --- /dev/null +++ b/src/constructs/fpe/fpe.cpp @@ -0,0 +1,195 @@ +/* +* Format Preserving Encryption using the scheme FE1 from the paper +* "Format-Preserving Encryption" by Bellare, Rogaway, et al +* (http://eprint.iacr.org/2009/251) +* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/fpe.h> +#include <botan/numthry.h> +#include <botan/hmac.h> +#include <botan/sha2_32.h> +#include <botan/loadstor.h> +#include <stdexcept> + +namespace Botan { + +namespace { + +// Normally FPE is for SSNs, CC#s, etc, nothing too big +const u32bit 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; + + u32bit 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(u32bit 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. +*/ +u32bit 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 MemoryRegion<byte>& tweak); + + ~FPE_Encryptor() { delete mac; } + + BigInt operator()(u32bit i, const BigInt& R); + + private: + MessageAuthenticationCode* mac; + SecureVector<byte> mac_n_t; + }; + +FPE_Encryptor::FPE_Encryptor(const SymmetricKey& key, + const BigInt& n, + const MemoryRegion<byte>& tweak) + { + mac = new HMAC(new SHA_256); + mac->set_key(key); + + SecureVector<byte> n_bin = BigInt::encode(n); + + if(n_bin.size() > MAX_N_BYTES) + throw std::runtime_error("N is too large for FPE encryption"); + + for(u32bit i = 0; i != 4; ++i) + mac->update(get_byte(i, n_bin.size())); + mac->update(&n_bin[0], n_bin.size()); + + for(u32bit i = 0; i != 4; ++i) + mac->update(get_byte(i, tweak.size())); + mac->update(&tweak[0], tweak.size()); + + mac_n_t = mac->final(); + } + +BigInt FPE_Encryptor::operator()(u32bit round_no, const BigInt& R) + { + mac->update(mac_n_t); + + for(u32bit i = 0; i != 4; ++i) + mac->update(get_byte(i, round_no)); + + SecureVector<byte> r_bin = BigInt::encode(R); + + for(u32bit i = 0; i != 4; ++i) + mac->update(get_byte(i, r_bin.size())); + mac->update(&r_bin[0], r_bin.size()); + + SecureVector<byte> X = mac->final(); + return BigInt(&X[0], X.size()); + } + +} + +/** +* Generic Z_n FPE encryption, FE1 scheme +*/ +BigInt fpe_encrypt(const BigInt& n, const BigInt& X0, + const SymmetricKey& key, + const MemoryRegion<byte>& tweak) + { + FPE_Encryptor F(key, n, tweak); + + BigInt a, b; + factor(n, a, b); + + const u32bit r = rounds(a, b); + + BigInt X = X0; + + for(u32bit 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 fpe_decrypt(const BigInt& n, const BigInt& X0, + const SymmetricKey& key, + const MemoryRegion<byte>& tweak) + { + FPE_Encryptor F(key, n, tweak); + + BigInt a, b; + factor(n, a, b); + + const u32bit r = rounds(a, b); + + BigInt X = X0; + + for(u32bit 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/constructs/fpe/fpe.h b/src/constructs/fpe/fpe.h new file mode 100644 index 000000000..fba1652d3 --- /dev/null +++ b/src/constructs/fpe/fpe.h @@ -0,0 +1,32 @@ +/* +* Format Preserving Encryption +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_FORMAT_PRESERVING_ENCRYPTION_H__ +#define BOTAN_FORMAT_PRESERVING_ENCRYPTION_H__ + +#include <botan/bigint.h> +#include <botan/symkey.h> + +namespace Botan { + +/* +* Encrypt X from and onto the group Z_n using key and tweak +*/ +BigInt fpe_encrypt(const BigInt& n, const BigInt& X, + const SymmetricKey& key, + const MemoryRegion<byte>& tweak); + +/* +* Decrypt X from and onto the group Z_n using key and tweak +*/ +BigInt fpe_decrypt(const BigInt& n, const BigInt& X, + const SymmetricKey& key, + const MemoryRegion<byte>& tweak); + +} + +#endif diff --git a/src/constructs/fpe/info.txt b/src/constructs/fpe/info.txt new file mode 100644 index 000000000..f7a8c4ae4 --- /dev/null +++ b/src/constructs/fpe/info.txt @@ -0,0 +1,13 @@ +define FORMAT_PRESERVING_ENCRYPTION + +load_on auto + +<add> +fpe.cpp +fpe.h +</add> + +<requires> +cbc_mac +bigint +</requires> diff --git a/src/constructs/tss/info.txt b/src/constructs/tss/info.txt new file mode 100644 index 000000000..1d7f793ea --- /dev/null +++ b/src/constructs/tss/info.txt @@ -0,0 +1,8 @@ +define THRESHOLD_SECRET_SHARING + +<requires> +hash +rng +filters +hex +</requires> diff --git a/src/constructs/tss/tss.cpp b/src/constructs/tss/tss.cpp new file mode 100644 index 000000000..0782a27d1 --- /dev/null +++ b/src/constructs/tss/tss.cpp @@ -0,0 +1,263 @@ +/* +* RTSS (threshold secret sharing) +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#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> +#include <memory> + +namespace Botan { + +namespace { + +/** +Table for GF(2^8) arithmetic (exponentials) +*/ +const byte 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 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 EXP[(LOG[x] + 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) + { + Pipe pipe(new Hex_Decoder); + pipe.process_msg(hex_input); + contents = pipe.read_all(); + } + +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 + { + Pipe pipe(new Hex_Encoder); + pipe.process_msg(contents); + return pipe.read_all_as_string(); + } + +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.append(identifier, 16); + shares[i].contents.append(rtss_hash_id(hash.name())); + shares[i].contents.append(M); + shares[i].contents.append(get_byte(0, S_len)); + shares[i].contents.append(get_byte(1, S_len)); + } + + // Choose sequential values for X starting from 1 + for(byte i = 0; i != N; ++i) + shares[i].contents.append(i+1); + + // secret = S || H(S) + SecureVector<byte> secret(S, S_len); + secret.append(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.append(sum); + } + } + + return shares; + } + +SecureVector<byte> +RTSS_Share::reconstruct(const std::vector<RTSS_Share>& shares) + { + const u32bit 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.begin(), + shares[i].contents.begin(), 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::auto_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()); + SecureVector<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 = EXP[(255 + LOG[share_l] - LOG[share_k ^ share_l]) % 255]; + + r2 = gfp_mul(r2, div); + } + + r ^= gfp_mul(V[k], r2); + } + secret.append(r); + } + + if(secret.size() != secret_len + hash->OUTPUT_LENGTH) + throw Decoding_Error("Bad length in RTSS output"); + + hash->update(secret, secret_len); + SecureVector<byte> hash_check = hash->final(); + + if(!same_mem(hash_check.begin(), secret + secret_len, hash->OUTPUT_LENGTH)) + throw Decoding_Error("RTSS hash check failed"); + + return SecureVector<byte>(secret, secret_len); + } + +} diff --git a/src/constructs/tss/tss.h b/src/constructs/tss/tss.h new file mode 100644 index 000000000..6e623c193 --- /dev/null +++ b/src/constructs/tss/tss.h @@ -0,0 +1,55 @@ +/* +* RTSS (threshold secret sharing) +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_RTSS_H__ +#define BOTAN_RTSS_H__ + +#include <botan/secmem.h> +#include <botan/hash.h> +#include <botan/rng.h> +#include <vector> + +namespace Botan { + +class RTSS_Share + { + public: + /** + * @arg M the number of shares needed to reconstruct + * @arg N the number of shares generated + * @arg secret the secret to split + * @arg secret_len the length of the secret + * @arg identifier the 16 byte share identifier + * @arg 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); + + /** + * @arg shares the list of shares + */ + static SecureVector<byte> + reconstruct(const std::vector<RTSS_Share>& shares); + + RTSS_Share() {} + RTSS_Share(const std::string&); + + std::string to_string() const; + byte share_id() const; + + u32bit size() const { return contents.size(); } + bool initialized() const { return contents.size(); } + private: + SecureVector<byte> contents; + }; + +} + +#endif |