Botan
1.11.15
|
00001 /* 00002 * Cryptobox Message Routines 00003 * (C) 2009 Jack Lloyd 00004 * 00005 * Botan is released under the Simplified BSD License (see license.txt) 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 First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits 00027 for later use as flags, etc if needed 00028 */ 00029 const u32bit CRYPTOBOX_VERSION_CODE = 0xEFC22400; 00030 00031 const size_t VERSION_CODE_LEN = 4; 00032 const size_t CIPHER_KEY_LEN = 32; 00033 const size_t CIPHER_IV_LEN = 16; 00034 const size_t MAC_KEY_LEN = 32; 00035 const size_t MAC_OUTPUT_LEN = 20; 00036 const size_t PBKDF_SALT_LEN = 10; 00037 const size_t PBKDF_ITERATIONS = 8 * 1024; 00038 00039 const size_t PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + CIPHER_IV_LEN + MAC_KEY_LEN; 00040 00041 } 00042 00043 std::string encrypt(const byte input[], size_t input_len, 00044 const std::string& passphrase, 00045 RandomNumberGenerator& rng) 00046 { 00047 secure_vector<byte> pbkdf_salt(PBKDF_SALT_LEN); 00048 rng.randomize(&pbkdf_salt[0], pbkdf_salt.size()); 00049 00050 PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512)); 00051 00052 OctetString master_key = pbkdf.derive_key( 00053 PBKDF_OUTPUT_LEN, 00054 passphrase, 00055 &pbkdf_salt[0], 00056 pbkdf_salt.size(), 00057 PBKDF_ITERATIONS); 00058 00059 const byte* mk = master_key.begin(); 00060 00061 SymmetricKey cipher_key(&mk[0], CIPHER_KEY_LEN); 00062 SymmetricKey mac_key(&mk[CIPHER_KEY_LEN], MAC_KEY_LEN); 00063 InitializationVector iv(&mk[CIPHER_KEY_LEN + MAC_KEY_LEN], CIPHER_IV_LEN); 00064 00065 Pipe pipe(get_cipher("Serpent/CTR-BE", cipher_key, iv, ENCRYPTION), 00066 new Fork( 00067 nullptr, 00068 new MAC_Filter(new HMAC(new SHA_512), 00069 mac_key, MAC_OUTPUT_LEN))); 00070 00071 pipe.process_msg(input, input_len); 00072 00073 /* 00074 Output format is: 00075 version # (4 bytes) 00076 salt (10 bytes) 00077 mac (20 bytes) 00078 ciphertext 00079 */ 00080 const size_t ciphertext_len = pipe.remaining(0); 00081 00082 std::vector<byte> out_buf(VERSION_CODE_LEN + 00083 PBKDF_SALT_LEN + 00084 MAC_OUTPUT_LEN + 00085 ciphertext_len); 00086 00087 for(size_t i = 0; i != VERSION_CODE_LEN; ++i) 00088 out_buf[i] = get_byte(i, CRYPTOBOX_VERSION_CODE); 00089 00090 copy_mem(&out_buf[VERSION_CODE_LEN], &pbkdf_salt[0], PBKDF_SALT_LEN); 00091 00092 pipe.read(&out_buf[VERSION_CODE_LEN + PBKDF_SALT_LEN], MAC_OUTPUT_LEN, 1); 00093 pipe.read(&out_buf[VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN], 00094 ciphertext_len, 0); 00095 00096 return PEM_Code::encode(out_buf, "BOTAN CRYPTOBOX MESSAGE"); 00097 } 00098 00099 std::string decrypt(const byte input[], size_t input_len, 00100 const std::string& passphrase) 00101 { 00102 DataSource_Memory input_src(input, input_len); 00103 secure_vector<byte> ciphertext = 00104 PEM_Code::decode_check_label(input_src, 00105 "BOTAN CRYPTOBOX MESSAGE"); 00106 00107 if(ciphertext.size() < (VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN)) 00108 throw Decoding_Error("Invalid CryptoBox input"); 00109 00110 for(size_t i = 0; i != VERSION_CODE_LEN; ++i) 00111 if(ciphertext[i] != get_byte(i, CRYPTOBOX_VERSION_CODE)) 00112 throw Decoding_Error("Bad CryptoBox version"); 00113 00114 const byte* pbkdf_salt = &ciphertext[VERSION_CODE_LEN]; 00115 00116 PKCS5_PBKDF2 pbkdf(new HMAC(new SHA_512)); 00117 00118 OctetString master_key = pbkdf.derive_key( 00119 PBKDF_OUTPUT_LEN, 00120 passphrase, 00121 pbkdf_salt, 00122 PBKDF_SALT_LEN, 00123 PBKDF_ITERATIONS); 00124 00125 const byte* mk = master_key.begin(); 00126 00127 SymmetricKey cipher_key(&mk[0], CIPHER_KEY_LEN); 00128 SymmetricKey mac_key(&mk[CIPHER_KEY_LEN], MAC_KEY_LEN); 00129 InitializationVector iv(&mk[CIPHER_KEY_LEN + MAC_KEY_LEN], CIPHER_IV_LEN); 00130 00131 Pipe pipe(new Fork( 00132 get_cipher("Serpent/CTR-BE", cipher_key, iv, DECRYPTION), 00133 new MAC_Filter(new HMAC(new SHA_512), 00134 mac_key, MAC_OUTPUT_LEN))); 00135 00136 const size_t ciphertext_offset = 00137 VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN; 00138 00139 pipe.process_msg(&ciphertext[ciphertext_offset], 00140 ciphertext.size() - ciphertext_offset); 00141 00142 byte computed_mac[MAC_OUTPUT_LEN]; 00143 pipe.read(computed_mac, MAC_OUTPUT_LEN, 1); 00144 00145 if(!same_mem(computed_mac, 00146 &ciphertext[VERSION_CODE_LEN + PBKDF_SALT_LEN], 00147 MAC_OUTPUT_LEN)) 00148 throw Decoding_Error("CryptoBox integrity failure"); 00149 00150 return pipe.read_all_as_string(0); 00151 } 00152 00153 std::string decrypt(const std::string& input, 00154 const std::string& passphrase) 00155 { 00156 return decrypt(reinterpret_cast<const byte*>(&input[0]), 00157 input.size(), 00158 passphrase); 00159 } 00160 00161 } 00162 00163 }