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 output ~= cipher_iv.bitsOf()[]; 65 output ~= ctext[]; 66 67 mac.update(output); 68 69 output ~= mac.finished(); 70 return output.unlock(); 71 } 72 73 /** 74 * Encrypt a message using a shared secret key 75 * Params: 76 * input = the input data 77 * input_len = the length of input in bytes 78 * master_key = the key used to encrypt the message 79 */ 80 static SecureVector!ubyte decrypt(const(ubyte)* input, size_t input_len, in SymmetricKey master_key) 81 { 82 __gshared immutable size_t MIN_CTEXT_SIZE = 16; // due to using CBC with padding 83 84 __gshared immutable size_t MIN_POSSIBLE_LENGTH = MAGIC_LENGTH + 2 * KEY_KDF_SALT_LENGTH + CIPHER_IV_LENGTH + 85 MIN_CTEXT_SIZE + MAC_OUTPUT_LENGTH; 86 87 if (input_len < MIN_POSSIBLE_LENGTH) 88 throw new DecodingError("Encrypted input too short to be valid"); 89 90 if (loadBigEndian!uint(input, 0) != CRYPTOBOX_MAGIC) 91 throw new DecodingError("Unknown header value in cryptobox"); 92 93 Unique!KDF kdf = getKdf(CRYPTOBOX_KDF); 94 95 const(ubyte)* cipher_key_salt = &input[MAGIC_LENGTH]; 96 97 const(ubyte)* mac_key_salt = &input[MAGIC_LENGTH + KEY_KDF_SALT_LENGTH]; 98 99 SymmetricKey mac_key = kdf.deriveKey(MAC_KEY_LENGTH, 100 master_key.bitsOf(), 101 mac_key_salt, 102 KEY_KDF_SALT_LENGTH); 103 104 Unique!MessageAuthenticationCode mac = retrieveMac(CRYPTOBOX_MAC).clone(); 105 mac.setKey(mac_key); 106 107 mac.update(input, input_len - MAC_OUTPUT_LENGTH); 108 SecureVector!ubyte computed_mac = mac.finished(); 109 110 if (!sameMem(&input[input_len - MAC_OUTPUT_LENGTH], computed_mac.ptr, computed_mac.length)) 111 throw new DecodingError("MAC verification failed"); 112 113 SymmetricKey cipher_key = kdf.deriveKey(CIPHER_KEY_LENGTH, master_key.bitsOf(), cipher_key_salt, KEY_KDF_SALT_LENGTH); 114 115 InitializationVector cipher_iv = InitializationVector(&input[MAGIC_LENGTH+2*KEY_KDF_SALT_LENGTH], CIPHER_IV_LENGTH); 116 117 const size_t CTEXT_OFFSET = MAGIC_LENGTH + 2 * KEY_KDF_SALT_LENGTH + CIPHER_IV_LENGTH; 118 119 Pipe pipe = Pipe(getCipher(CRYPTOBOX_CIPHER, cipher_key, cipher_iv, DECRYPTION)); 120 pipe.processMsg(&input[CTEXT_OFFSET], 121 input_len - (MAC_OUTPUT_LENGTH + CTEXT_OFFSET)); 122 return pipe.readAll(); 123 } 124 125 } 126 127 private: 128 129 __gshared immutable uint CRYPTOBOX_MAGIC = 0x571B0E4F; 130 __gshared immutable string CRYPTOBOX_CIPHER = "AES-256/CBC"; 131 __gshared immutable string CRYPTOBOX_MAC = "HMAC(SHA-256)"; 132 __gshared immutable string CRYPTOBOX_KDF = "KDF2(SHA-256)"; 133 134 __gshared immutable size_t MAGIC_LENGTH = 4; 135 __gshared immutable size_t KEY_KDF_SALT_LENGTH = 10; 136 __gshared immutable size_t MAC_KEY_LENGTH = 32; 137 __gshared immutable size_t CIPHER_KEY_LENGTH = 32; 138 __gshared immutable size_t CIPHER_IV_LENGTH = 16; 139 __gshared immutable size_t MAC_OUTPUT_LENGTH = 32;