1 module secured.aes; 2 3 import deimos.openssl.evp; 4 5 import secured.mac; 6 import secured.random; 7 import secured.util; 8 9 @trusted public ubyte[] encrypt (ubyte[] key, ubyte[] data) 10 in 11 { 12 assert(key.length == 32, "Encryption key must be 32 bytes in length."); 13 } 14 body 15 { 16 ubyte[] output = new ubyte[data.length]; 17 18 //Generate a random IV 19 ubyte[] iv = random(16); 20 21 //Get the OpenSSL cipher context 22 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); 23 if (ctx is null) 24 throw new CryptographicException("Cannot get an OpenSSL cipher context."); 25 scope(exit) 26 if (ctx !is null) 27 EVP_CIPHER_CTX_free(ctx); 28 29 //Initialize the cipher context 30 if (EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), null, key.ptr, iv.ptr) != 1) 31 throw new CryptographicException("Cannot initialize the OpenSSL cipher context."); 32 33 //Write data to the cipher context 34 int written = 0; 35 int len = 0; 36 if (EVP_EncryptUpdate(ctx, &output[written], &len, data.ptr, cast(int)data.length) != 1) 37 throw new CryptographicException("Unable to write bytes to cipher context."); 38 written += len; 39 40 //Extract the complete ciphertext 41 if (EVP_EncryptFinal_ex(ctx, &output[written-1], &len) != 1) 42 throw new CryptographicException("Unable to extract the ciphertext from the cipher context."); 43 written += len; 44 45 //HMAC the combined cipher text 46 ubyte[] hashdata = iv ~ output; 47 ubyte[] hash = hmac(key, hashdata); 48 49 //Return the HMAC + IV + Ciphertext as a single byte array. 50 return hash ~ iv ~ output; 51 } 52 53 @trusted public bool validate (ubyte[] key, ubyte[] data) 54 in 55 { 56 assert(key.length == 32, "Encryption key must be 32 bytes in length."); 57 } 58 body 59 { 60 ubyte[] datahash = data[0..48]; 61 ubyte[] computed = hmac(key, data[48..$]); 62 63 return constantTimeEquality(datahash, computed); 64 } 65 66 @trusted public ubyte[] decrypt (ubyte[] key, ubyte[] data) 67 in 68 { 69 assert(key.length == 32, "Encryption key must be 32 bytes in length."); 70 } 71 body 72 { 73 //Validate the data 74 if (!validate(key, data)) 75 throw new CryptographicException("Cannot get an OpenSSL cipher context."); 76 77 ubyte[] iv = data[48..64]; 78 ubyte[] payload = data[64..$]; 79 ubyte[] output = new ubyte[payload.length]; 80 81 //Get the OpenSSL cipher context 82 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); 83 if (ctx is null) 84 throw new CryptographicException("Cannot get an OpenSSL cipher context."); 85 scope(exit) 86 EVP_CIPHER_CTX_free(ctx); 87 88 //Initialize the cipher context 89 if (EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), null, key.ptr, iv.ptr) != 1) 90 throw new CryptographicException("Cannot initialize the OpenSSL cipher context."); 91 92 //Write data to the cipher context 93 int written = 0; 94 int len = 0; 95 if (EVP_DecryptUpdate(ctx, &output[written], &len, payload.ptr, cast(int)payload.length) != 1) 96 throw new CryptographicException("Unable to write bytes to cipher context."); 97 written += len; 98 99 //Extract the complete plaintext 100 if (EVP_DecryptFinal_ex(ctx, &output[written-1], &len) != 1) 101 throw new CryptographicException("Unable to extract the plaintext from the cipher context."); 102 written += len; 103 104 return output; 105 } 106 107 unittest 108 { 109 import std.digest; 110 import std.stdio; 111 112 writeln("Testing Encryption (No Additional Data)"); 113 114 ubyte[32] key = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 115 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ]; 116 117 string input = "The quick brown fox jumps over the lazy dog."; 118 writeln("Encryption Input: ", input); 119 ubyte[] enc = encrypt(key, cast(ubyte[])input); 120 writeln("Encryption Output: ", toHexString!(LetterCase.lower)(enc)); 121 122 write("Testing Validation (No Additional Data): "); 123 assert(validate(key, enc)); 124 writeln("Success!"); 125 126 writeln("Testing Decryption (No Additional Data)"); 127 ubyte[] dec = decrypt(key, enc); 128 writeln("Decryption Input: ", toHexString!(LetterCase.lower)(enc)); 129 writeln("Decryption Output: ", cast(string)dec); 130 131 assert((cast(string)dec) == input); 132 }