1 /*
2 * Cryptobox Message Routines
3 * (C) 2009 Jack Lloyd
4 *
5 * Botan is released under the Simplified BSD License (see license.txt)
6 */
7 
8 #include <botan/cryptobox.h>
9 #include <botan/cipher_mode.h>
10 #include <botan/mac.h>
11 #include <botan/rng.h>
12 #include <botan/pbkdf.h>
13 #include <botan/data_src.h>
14 #include <botan/pem.h>
15 #include <botan/loadstor.h>
16 #include <botan/mem_ops.h>
17 
18 namespace Botan {
19 
20 namespace CryptoBox {
21 
22 namespace {
23 
24 /*
25 First 24 bits of SHA-256("Botan Cryptobox"), followed by 8 0 bits
26 for later use as flags, etc if needed
27 */
28 const uint32_t CRYPTOBOX_VERSION_CODE = 0xEFC22400;
29 
30 const size_t VERSION_CODE_LEN = 4;
31 const size_t CIPHER_KEY_LEN = 32;
32 const size_t CIPHER_IV_LEN = 16;
33 const size_t MAC_KEY_LEN = 32;
34 const size_t MAC_OUTPUT_LEN = 20;
35 const size_t PBKDF_SALT_LEN = 10;
36 const size_t PBKDF_ITERATIONS = 8 * 1024;
37 
38 const size_t PBKDF_OUTPUT_LEN = CIPHER_KEY_LEN + MAC_KEY_LEN + CIPHER_IV_LEN;
39 const size_t CRYPTOBOX_HEADER_LEN = VERSION_CODE_LEN + PBKDF_SALT_LEN + MAC_OUTPUT_LEN;
40 
41 }
42 
encrypt(const uint8_t input[],size_t input_len,const std::string & passphrase,RandomNumberGenerator & rng)43 std::string encrypt(const uint8_t input[], size_t input_len,
44                     const std::string& passphrase,
45                     RandomNumberGenerator& rng)
46    {
47    /*
48    Output format is:
49       version # (4 bytes)
50       salt (10 bytes)
51       mac (20 bytes)
52       ciphertext
53    */
54    secure_vector<uint8_t> out_buf(CRYPTOBOX_HEADER_LEN + input_len);
55    for(size_t i = 0; i != VERSION_CODE_LEN; ++i)
56      out_buf[i] = get_byte(i, CRYPTOBOX_VERSION_CODE);
57    rng.randomize(&out_buf[VERSION_CODE_LEN], PBKDF_SALT_LEN);
58    // space left for MAC here
59    if(input_len > 0)
60       copy_mem(&out_buf[CRYPTOBOX_HEADER_LEN], input, input_len);
61 
62    // Generate the keys and IV
63 
64    std::unique_ptr<PBKDF> pbkdf(PBKDF::create_or_throw("PBKDF2(HMAC(SHA-512))"));
65 
66    OctetString master_key = pbkdf->derive_key(
67       CIPHER_KEY_LEN + MAC_KEY_LEN + CIPHER_IV_LEN,
68       passphrase,
69       &out_buf[VERSION_CODE_LEN],
70       PBKDF_SALT_LEN,
71       PBKDF_ITERATIONS);
72 
73    const uint8_t* mk = master_key.begin();
74    const uint8_t* cipher_key = mk;
75    const uint8_t* mac_key = mk + CIPHER_KEY_LEN;
76    const uint8_t* iv = mk + CIPHER_KEY_LEN + MAC_KEY_LEN;
77 
78    // Now encrypt and authenticate
79    std::unique_ptr<Cipher_Mode> ctr = Cipher_Mode::create_or_throw("Serpent/CTR-BE", ENCRYPTION);
80    ctr->set_key(cipher_key, CIPHER_KEY_LEN);
81    ctr->start(iv, CIPHER_IV_LEN);
82    ctr->finish(out_buf, CRYPTOBOX_HEADER_LEN);
83 
84    std::unique_ptr<MessageAuthenticationCode> hmac =
85       MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
86    hmac->set_key(mac_key, MAC_KEY_LEN);
87    if(input_len > 0)
88       hmac->update(&out_buf[CRYPTOBOX_HEADER_LEN], input_len);
89 
90    // Can't write directly because of MAC truncation
91    secure_vector<uint8_t> mac = hmac->final();
92    copy_mem(&out_buf[VERSION_CODE_LEN + PBKDF_SALT_LEN], mac.data(), MAC_OUTPUT_LEN);
93 
94    return PEM_Code::encode(out_buf, "BOTAN CRYPTOBOX MESSAGE");
95    }
96 
97 secure_vector<uint8_t>
decrypt_bin(const uint8_t input[],size_t input_len,const std::string & passphrase)98 decrypt_bin(const uint8_t input[], size_t input_len,
99             const std::string& passphrase)
100    {
101    DataSource_Memory input_src(input, input_len);
102    secure_vector<uint8_t> ciphertext =
103       PEM_Code::decode_check_label(input_src,
104                                    "BOTAN CRYPTOBOX MESSAGE");
105 
106    if(ciphertext.size() < CRYPTOBOX_HEADER_LEN)
107       throw Decoding_Error("Invalid CryptoBox input");
108 
109    for(size_t i = 0; i != VERSION_CODE_LEN; ++i)
110       if(ciphertext[i] != get_byte(i, CRYPTOBOX_VERSION_CODE))
111          throw Decoding_Error("Bad CryptoBox version");
112 
113    const uint8_t* pbkdf_salt = &ciphertext[VERSION_CODE_LEN];
114    const uint8_t* box_mac = &ciphertext[VERSION_CODE_LEN + PBKDF_SALT_LEN];
115 
116    std::unique_ptr<PBKDF> pbkdf(PBKDF::create_or_throw("PBKDF2(HMAC(SHA-512))"));
117 
118    OctetString master_key = pbkdf->derive_key(
119       PBKDF_OUTPUT_LEN,
120       passphrase,
121       pbkdf_salt,
122       PBKDF_SALT_LEN,
123       PBKDF_ITERATIONS);
124 
125    const uint8_t* mk = master_key.begin();
126    const uint8_t* cipher_key = mk;
127    const uint8_t* mac_key = mk + CIPHER_KEY_LEN;
128    const uint8_t* iv = mk + CIPHER_KEY_LEN + MAC_KEY_LEN;
129 
130    // Now authenticate and decrypt
131    std::unique_ptr<MessageAuthenticationCode> hmac =
132       MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
133    hmac->set_key(mac_key, MAC_KEY_LEN);
134 
135    if(ciphertext.size() > CRYPTOBOX_HEADER_LEN)
136       {
137       hmac->update(&ciphertext[CRYPTOBOX_HEADER_LEN],
138                    ciphertext.size() - CRYPTOBOX_HEADER_LEN);
139       }
140    secure_vector<uint8_t> computed_mac = hmac->final();
141 
142    if(!constant_time_compare(computed_mac.data(), box_mac, MAC_OUTPUT_LEN))
143       throw Decoding_Error("CryptoBox integrity failure");
144 
145    std::unique_ptr<Cipher_Mode> ctr(Cipher_Mode::create_or_throw("Serpent/CTR-BE", DECRYPTION));
146    ctr->set_key(cipher_key, CIPHER_KEY_LEN);
147    ctr->start(iv, CIPHER_IV_LEN);
148    ctr->finish(ciphertext, CRYPTOBOX_HEADER_LEN);
149 
150    ciphertext.erase(ciphertext.begin(), ciphertext.begin() + CRYPTOBOX_HEADER_LEN);
151    return ciphertext;
152    }
153 
decrypt_bin(const std::string & input,const std::string & passphrase)154 secure_vector<uint8_t> decrypt_bin(const std::string& input,
155                                    const std::string& passphrase)
156    {
157    return decrypt_bin(cast_char_ptr_to_uint8(input.data()),
158                       input.size(),
159                       passphrase);
160    }
161 
decrypt(const uint8_t input[],size_t input_len,const std::string & passphrase)162 std::string decrypt(const uint8_t input[], size_t input_len,
163                     const std::string& passphrase)
164    {
165    const secure_vector<uint8_t> bin = decrypt_bin(input, input_len, passphrase);
166 
167    return std::string(cast_uint8_ptr_to_char(&bin[0]),
168                       bin.size());
169    }
170 
decrypt(const std::string & input,const std::string & passphrase)171 std::string decrypt(const std::string& input,
172                     const std::string& passphrase)
173    {
174    return decrypt(cast_char_ptr_to_uint8(input.data()),
175                   input.size(), passphrase);
176    }
177 
178 }
179 
180 }
181