00001
00002
00003
00004
00005
00006
00007
00008 #include <botan/cryptobox.h>
00009 #include <botan/filters.h>
00010 #include <botan/pipe.h>
00011 #include <botan/lookup.h>
00012 #include <botan/sha2_64.h>
00013 #include <botan/hmac.h>
00014 #include <botan/pbkdf2.h>
00015 #include <botan/pem.h>
00016 #include <botan/get_byte.h>
00017 #include <botan/mem_ops.h>
00018
00019 namespace Botan {
00020
00021 namespace CryptoBox {
00022
00023 namespace {
00024
00025
00026
00027
00028
00029 const u32bit CRYPTOBOX_VERSION_CODE = 0xEFC22400;
00030
00031 const u32bit VERSION_CODE_LEN = 4;
00032 const u32bit CIPHER_KEY_LEN = 32;
00033 const u32bit CIPHER_IV_LEN = 16;
00034 const u32bit MAC_KEY_LEN = 32;
00035 const u32bit MAC_OUTPUT_LEN = 20;
00036 const u32bit PBKDF_SALT_LEN = 10;
00037 const u32bit PBKDF_ITERATIONS = 8 * 1024;
00038
00039 const u32bit PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + CIPHER_IV_LEN + MAC_KEY_LEN;
00040
00041 }
00042
00043 std::string encrypt(const byte input[], u32bit input_len,
00044 const std::string& passphrase,
00045 RandomNumberGenerator& rng)
00046 {
00047 SecureVector<byte> pbkdf_salt(PBKDF_SALT_LEN);
00048 rng.randomize(pbkdf_salt.begin(), pbkdf_salt.size());
00049
00050 PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512));
00051
00052 OctetString mk = pbkdf.derive_key(PBKDF_OUTPUT_LEN, passphrase,
00053 &pbkdf_salt[0], pbkdf_salt.size(),
00054 PBKDF_ITERATIONS);
00055
00056 SymmetricKey cipher_key(mk.begin(), CIPHER_KEY_LEN);
00057 SymmetricKey mac_key(mk.begin() + CIPHER_KEY_LEN, MAC_KEY_LEN);
00058 InitializationVector iv(mk.begin() + CIPHER_KEY_LEN + MAC_KEY_LEN,
00059 CIPHER_IV_LEN);
00060
00061 Pipe pipe(get_cipher("Serpent/CTR-BE", cipher_key, iv, ENCRYPTION),
00062 new Fork(
00063 0,
00064 new MAC_Filter(new HMAC(new SHA_512),
00065 mac_key, MAC_OUTPUT_LEN)));
00066
00067 pipe.process_msg(input, input_len);
00068
00069
00070
00071
00072
00073
00074
00075
00076 u32bit ciphertext_len = pipe.remaining(0);
00077
00078 SecureVector<byte> out_buf;
00079
00080 for(u32bit i = 0; i != VERSION_CODE_LEN; ++i)
00081 out_buf.append(get_byte(i, CRYPTOBOX_VERSION_CODE));
00082
00083 out_buf.append(pbkdf_salt.begin(), pbkdf_salt.size());
00084
00085 out_buf.grow_to(out_buf.size() + MAC_OUTPUT_LEN + ciphertext_len);
00086 pipe.read(out_buf + VERSION_CODE_LEN + PBKDF_SALT_LEN, MAC_OUTPUT_LEN, 1);
00087 pipe.read(out_buf + VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN,
00088 ciphertext_len, 0);
00089
00090 return PEM_Code::encode(out_buf.begin(), out_buf.size(),
00091 "BOTAN CRYPTOBOX MESSAGE");
00092 }
00093
00094 std::string decrypt(const byte input[], u32bit input_len,
00095 const std::string& passphrase)
00096 {
00097 DataSource_Memory input_src(input, input_len);
00098 SecureVector<byte> ciphertext =
00099 PEM_Code::decode_check_label(input_src,
00100 "BOTAN CRYPTOBOX MESSAGE");
00101
00102 if(ciphertext.size() < (VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN))
00103 throw Decoding_Error("Invalid CryptoBox input");
00104
00105 for(u32bit i = 0; i != VERSION_CODE_LEN; ++i)
00106 if(ciphertext[i] != get_byte(i, CRYPTOBOX_VERSION_CODE))
00107 throw Decoding_Error("Bad CryptoBox version");
00108
00109 SecureVector<byte> pbkdf_salt(ciphertext + VERSION_CODE_LEN, PBKDF_SALT_LEN);
00110
00111 PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512));
00112
00113 OctetString mk = pbkdf.derive_key(PBKDF_OUTPUT_LEN, passphrase,
00114 &pbkdf_salt[0], pbkdf_salt.size(),
00115 PBKDF_ITERATIONS);
00116
00117 SymmetricKey cipher_key(mk.begin(), CIPHER_KEY_LEN);
00118 SymmetricKey mac_key(mk.begin() + CIPHER_KEY_LEN, MAC_KEY_LEN);
00119 InitializationVector iv(mk.begin() + CIPHER_KEY_LEN + MAC_KEY_LEN,
00120 CIPHER_IV_LEN);
00121
00122 Pipe pipe(new Fork(
00123 get_cipher("Serpent/CTR-BE", cipher_key, iv, DECRYPTION),
00124 new MAC_Filter(new HMAC(new SHA_512),
00125 mac_key, MAC_OUTPUT_LEN)));
00126
00127 const u32bit ciphertext_offset =
00128 VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN;
00129
00130 pipe.process_msg(ciphertext + ciphertext_offset,
00131 ciphertext.size() - ciphertext_offset);
00132
00133 byte computed_mac[MAC_OUTPUT_LEN];
00134 pipe.read(computed_mac, MAC_OUTPUT_LEN, 1);
00135
00136 if(!same_mem(computed_mac, ciphertext + VERSION_CODE_LEN + PBKDF_SALT_LEN,
00137 MAC_OUTPUT_LEN))
00138 throw Decoding_Error("CryptoBox integrity failure");
00139
00140 return pipe.read_all_as_string(0);
00141 }
00142
00143 }
00144
00145 }