I'm working on a CryptSharp SCrypt implementation in VS2015. I need to encrypt/decrypt text files meant to be sent as email attachments. Initially I was using AES but considering that HMAC-SHA1 is outdated I opted to use SCrypt for password hashing. However, SCrypt does not expose public methods for the data encryption itself, so would it make sense to pass the SCrypt hashed password to AES, then use the latter for data encryption? Or perhaps there is a better approach?
In this scenario, I would imagine something like this, yet I would need to find a way to reliably randomize the IV...
private static Aes SetAes(string userName, string password)
{
var passBytes = Encoding.UTF8.GetBytes(password);
var saltBytes = Encoding.UTF8.GetBytes(userName);
var cost = 131072; // around 5 secs with block at 16(on Xeon 1241 v3)
var blockSize = 16; // 8 is default but might not suffice against modern GPUs(?)
var parallel = 1;
var maxThreads = (int?)null;
byte[] derivedKey = new byte[32]; // 256 bits
SCrypt.ComputeKey(passBytes, saltBytes, cost, blockSize, parallel, maxThreads, derivedKey);
Aes aes = new AesManaged();
aes.Padding = PaddingMode.PKCS7;
aes.Key = derivedKey;
byte[] IV = new byte[16];
Array.Copy(derivedKey, IV, 16); // how to reliably randomize the IV?
aes.IV = IV;
return aes;
}
Then for file encryption:
internal static void EncryptText(string text, string userName, string password, string file)
{
// omitting argument checks for readability
using (Aes aes = SetAes(userName, password))
{
using (FileStream fileStream = new FileStream(file, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
{
using (CryptoStream cryptoStream = new CryptoStream(fileStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(cryptoStream, text); // I'm using a class to wrap the text for serialization, not shown here for readability
}
}
}
}
Even though it seems to work, I'm not sure it makes sense, so thank you very much for any insight.
EDIT:
Following vcsjones recommendations, the SetAes function would rather look like this, if I understand correctly:
private static Aes SetAes(string userName, string password, byte[] IV = null)
{
var passBytes = Encoding.UTF8.GetBytes(password);
var saltBytes = Encoding.UTF8.GetBytes(userName);
var cost = 131072;
var blockSize = 16;
var parallel = 1;
var maxThreads = (int?)null;
byte[] derivedKey = new byte[32];
SCrypt.ComputeKey(passBytes, saltBytes, cost, blockSize, parallel, maxThreads, derivedKey);
Aes aes = new AesManaged();
aes.Padding = PaddingMode.PKCS7;
aes.Key = derivedKey;
if (IV == null) // when encrypting, generate IV
{
RandomNumberGenerator rn = RandomNumberGenerator.Create();
rn.GetBytes(aes.IV);
}
else aes.IV = IV; // when decrypting, read IV from file and pass it to aes through IV parameter for decryption
return aes;
}