1 /** 2 * Cryptobox Message Routines 3 * 4 * Copyright: 5 * (C) 2009,2013 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.constructs.cryptobox_psk; 12 13 import botan.constants; 14 static if (BOTAN_HAS_CRYPTOBOX): 15 16 import botan.rng.rng; 17 import botan.algo_base.symkey; 18 import botan.filters.pipe; 19 import botan.libstate.lookup; 20 import botan.mac.mac; 21 import botan.utils.mem_ops; 22 import botan.utils.loadstor; 23 import botan.utils.types; 24 /** 25 * This namespace holds various high-level crypto functions 26 */ 27 struct CryptoBox { 28 29 /** 30 * Encrypt a message using a shared secret key 31 * Params: 32 * input = the input data 33 * input_len = the length of input in bytes 34 * master_key = the key used to encrypt the message 35 * rng = a ref to a random number generator, such as AutoSeededRNG 36 */ 37 static Vector!ubyte encrypt(const(ubyte)* input, size_t input_len, 38 in SymmetricKey master_key, 39 RandomNumberGenerator rng) 40 { 41 Unique!KDF kdf = getKdf(CRYPTOBOX_KDF); 42 43 const SecureVector!ubyte cipher_key_salt = rng.randomVec(KEY_KDF_SALT_LENGTH); 44 45 const SecureVector!ubyte mac_key_salt = rng.randomVec(KEY_KDF_SALT_LENGTH); 46 47 SymmetricKey cipher_key = kdf.deriveKey(CIPHER_KEY_LENGTH, master_key.bitsOf(), cipher_key_salt); 48 49 SymmetricKey mac_key = kdf.deriveKey(MAC_KEY_LENGTH, master_key.bitsOf(), mac_key_salt); 50 51 InitializationVector cipher_iv = InitializationVector(rng, 16); 52 53 Unique!MessageAuthenticationCode mac = retrieveMac(CRYPTOBOX_MAC).clone(); 54 mac.setKey(mac_key); 55 56 Pipe pipe = Pipe(getCipher(CRYPTOBOX_CIPHER, cipher_key, cipher_iv, ENCRYPTION)); 57 pipe.processMsg(input, input_len); 58 SecureVector!ubyte ctext = pipe.readAll(0); 59 60 SecureVector!ubyte output = SecureVector!ubyte(MAGIC_LENGTH); 61 storeBigEndian(CRYPTOBOX_MAGIC, output.ptr); 62 output ~= cipher_key_salt[]; 63 output ~= mac_key_salt[]; 64 auto vec = cipher_iv.bitsOf(); 65 output ~= vec[]; 66 output ~= ctext[]; 67 68 mac.update(output); 69 70 output ~= mac.finished(); 71 return output.unlock(); 72 } 73 74 /** 75 * Encrypt a message using a shared secret key 76 * Params: 77 * input = the input data 78 * input_len = the length of input in bytes 79 * master_key = the key used to encrypt the message 80 */ 81 static SecureVector!ubyte decrypt(const(ubyte)* input, size_t input_len, in SymmetricKey master_key) 82 { 83 __gshared immutable size_t MIN_CTEXT_SIZE = 16; // due to using CBC with padding 84 85 __gshared immutable size_t MIN_POSSIBLE_LENGTH = MAGIC_LENGTH + 2 * KEY_KDF_SALT_LENGTH + CIPHER_IV_LENGTH + 86 MIN_CTEXT_SIZE + MAC_OUTPUT_LENGTH; 87 88 if (input_len < MIN_POSSIBLE_LENGTH) 89 throw new DecodingError("Encrypted input too short to be valid"); 90 91 if (loadBigEndian!uint(input, 0) != CRYPTOBOX_MAGIC) 92 throw new DecodingError("Unknown header value in cryptobox"); 93 94 Unique!KDF kdf = getKdf(CRYPTOBOX_KDF); 95 96 const(ubyte)* cipher_key_salt = &input[MAGIC_LENGTH]; 97 98 const(ubyte)* mac_key_salt = &input[MAGIC_LENGTH + KEY_KDF_SALT_LENGTH]; 99 100 SymmetricKey mac_key = kdf.deriveKey(MAC_KEY_LENGTH, 101 master_key.bitsOf(), 102 mac_key_salt, 103 KEY_KDF_SALT_LENGTH); 104 105 Unique!MessageAuthenticationCode mac = retrieveMac(CRYPTOBOX_MAC).clone(); 106 mac.setKey(mac_key); 107 108 mac.update(input, input_len - MAC_OUTPUT_LENGTH); 109 SecureVector!ubyte computed_mac = mac.finished(); 110 111 if (!sameMem(&input[input_len - MAC_OUTPUT_LENGTH], computed_mac.ptr, computed_mac.length)) 112 throw new DecodingError("MAC verification failed"); 113 114 SymmetricKey cipher_key = kdf.deriveKey(CIPHER_KEY_LENGTH, master_key.bitsOf(), cipher_key_salt, KEY_KDF_SALT_LENGTH); 115 116 InitializationVector cipher_iv = InitializationVector(&input[MAGIC_LENGTH+2*KEY_KDF_SALT_LENGTH], CIPHER_IV_LENGTH); 117 118 const size_t CTEXT_OFFSET = MAGIC_LENGTH + 2 * KEY_KDF_SALT_LENGTH + CIPHER_IV_LENGTH; 119 120 Pipe pipe = Pipe(getCipher(CRYPTOBOX_CIPHER, cipher_key, cipher_iv, DECRYPTION)); 121 pipe.processMsg(&input[CTEXT_OFFSET], 122 input_len - (MAC_OUTPUT_LENGTH + CTEXT_OFFSET)); 123 return pipe.readAll(); 124 } 125 126 } 127 128 private: 129 130 __gshared immutable uint CRYPTOBOX_MAGIC = 0x571B0E4F; 131 __gshared immutable string CRYPTOBOX_CIPHER = "AES-256/CBC"; 132 __gshared immutable string CRYPTOBOX_MAC = "HMAC(SHA-256)"; 133 __gshared immutable string CRYPTOBOX_KDF = "KDF2(SHA-256)"; 134 135 __gshared immutable size_t MAGIC_LENGTH = 4; 136 __gshared immutable size_t KEY_KDF_SALT_LENGTH = 10; 137 __gshared immutable size_t MAC_KEY_LENGTH = 32; 138 __gshared immutable size_t CIPHER_KEY_LENGTH = 32; 139 __gshared immutable size_t CIPHER_IV_LENGTH = 16; 140 __gshared immutable size_t MAC_OUTPUT_LENGTH = 32;