1 module ddata.crypto.decrypt; 2 3 import std.format: format; 4 import std.conv: to; 5 6 import deimos.openssl.evp; 7 8 import ddata.common; 9 import ddata.crypto.algorithm; 10 11 /** 12 Decrypts the data using a key with the specified algorithm 13 14 See_Also: 15 `ddata.crypto.encrypt` 16 */ 17 public string decrypt(const ubyte[] data, const string key, Algorithm algorithm = Algorithm.aes128) @safe { 18 final switch (algorithm) { 19 case Algorithm.aes128: 20 return decrypt(data, cast(const ubyte[])key, () @trusted { return EVP_aes_128_cbc(); }() ).idup; 21 } 22 } 23 24 /// Ditto 25 public string decrypt(const string data, const string key, Algorithm algorithm = Algorithm.aes128) @safe { 26 final switch (algorithm) { 27 case Algorithm.aes128: 28 return decrypt(cast(immutable ubyte[])data, cast(const ubyte[])key, () @trusted { return EVP_aes_128_cbc(); }() ).idup; 29 } 30 } 31 32 private string decrypt(const ubyte[] data, const ubyte[] key, const(EVP_CIPHER)* cipher) @trusted { 33 const iv = data[0 .. 16]; 34 const payload = data[16 .. $]; 35 36 auto ctx = EVP_CIPHER_CTX_new(); 37 if (ctx is null) { 38 throw new Exception("Failed to create EVP cipher context - %s".format(getLastError)); 39 } 40 scope(exit) EVP_CIPHER_CTX_free(ctx); 41 42 if (!EVP_DecryptInit(ctx, cipher, key.ptr, cast(const(ubyte)*)iv.ptr)) { 43 throw new Exception("Failed to initialize evp context - %s".format(getLastError)); 44 } 45 46 ubyte[] buffer = new ubyte[payload.length]; 47 int updateLength = void; 48 if (!EVP_DecryptUpdate(ctx, buffer.ptr, &updateLength, payload.ptr, cast(int)payload.length)) { 49 throw new Exception("Failed to update evp context - %s".format(getLastError)); 50 } 51 52 int finalLength = void; 53 if (!EVP_DecryptFinal(ctx, &buffer.ptr[updateLength], &finalLength)) { 54 throw new Exception("Failed to finalize evp context - %s".format(getLastError)); 55 } 56 57 return cast(string)buffer[0 .. updateLength + finalLength]; 58 } 59 60 @("decryption should handle failure gracefully") 61 unittest { 62 import std.exception: collectExceptionMsg; 63 import std.algorithm: canFind; 64 import ddata.crypto: encrypt; 65 66 auto msg = (cast(ubyte[])"76d3beec63f0dc9204c3a102d2d3db86200d57cf") 67 .decrypt("some-key", Algorithm.aes128) 68 .collectExceptionMsg; 69 assert(msg.canFind("Failed to finalize evp contex")); 70 71 const password = "some random password"; 72 const message = "this is a call to all you people how is this even happening"; 73 const encryptedMessage = encrypt(message, password); 74 const decryptedMessage = decrypt(encryptedMessage, password); 75 assert(message == decryptedMessage); 76 } 77 78 79 @("Should be able to base64 an encryption and then decrypt") 80 unittest { 81 import std.base64: Base64; 82 import ddata.crypto: encrypt; 83 const str = "0aecba4fe377338b94746e203b4718c4cfdb7629"; 84 const password = "password"; 85 const encrypted = str.encrypt(password, Algorithm.aes128); 86 const base64Encoded = Base64.encode(encrypted); 87 const base64Decoded = Base64.decode(base64Encoded); 88 const decrypted = base64Decoded.decrypt(password, Algorithm.aes128); 89 assert(str == decrypted); 90 }