Botan  1.11.15
src/lib/misc/cryptobox/cryptobox.cpp
Go to the documentation of this file.
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 }