diff --git a/src/App/Models/CipherString.cs b/src/App/Models/CipherString.cs index 8cdc1d3ed..c770c33e9 100644 --- a/src/App/Models/CipherString.cs +++ b/src/App/Models/CipherString.cs @@ -18,7 +18,7 @@ namespace Bit.App.Models EncryptedString = encryptedString; } - public CipherString(string initializationVector, string cipherText) + public CipherString(string initializationVector, string cipherText, string mac = null) { if(string.IsNullOrWhiteSpace(initializationVector)) { @@ -31,13 +31,32 @@ namespace Bit.App.Models } EncryptedString = string.Format("{0}|{1}", initializationVector, cipherText); + + if(!string.IsNullOrWhiteSpace(mac)) + { + EncryptedString = string.Format("{0}|{1}", EncryptedString, mac); + } } public string EncryptedString { get; private set; } - public string InitializationVector { get { return EncryptedString?.Split('|')[0]; } } - public string CipherText { get { return EncryptedString?.Split('|')[1]; } } - public byte[] InitializationVectorBytes { get { return Convert.FromBase64String(InitializationVector); } } - public byte[] CipherTextBytes { get { return Convert.FromBase64String(CipherText); } } + public string InitializationVector => EncryptedString?.Split('|')[0] ?? null; + public string CipherText => EncryptedString?.Split('|')[1] ?? null; + public string Mac + { + get + { + var pieces = EncryptedString?.Split('|') ?? new string[0]; + if(pieces.Length > 2) + { + return pieces[2]; + } + + return null; + } + } + public byte[] InitializationVectorBytes => Convert.FromBase64String(InitializationVector); + public byte[] CipherTextBytes => Convert.FromBase64String(CipherText); + public byte[] MacBytes => Mac == null ? null : Convert.FromBase64String(Mac); public string Decrypt() { diff --git a/src/App/Services/CryptoService.cs b/src/App/Services/CryptoService.cs index f71f741d8..da0458124 100644 --- a/src/App/Services/CryptoService.cs +++ b/src/App/Services/CryptoService.cs @@ -106,6 +106,9 @@ namespace Bit.App.Services } } + public byte[] EncKey => Key?.Take(16).ToArray(); + public byte[] MacKey => Key?.Skip(16).Take(16).ToArray(); + public CipherString Encrypt(string plaintextValue) { if(Key == null) @@ -121,10 +124,14 @@ namespace Bit.App.Services var plaintextBytes = Encoding.UTF8.GetBytes(plaintextValue); var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); - var cryptoKey = provider.CreateSymmetricKey(Key); + // TODO: Turn on whenever ready to support encrypt-then-mac + var cryptoKey = provider.CreateSymmetricKey(false ? EncKey : Key); var iv = WinRTCrypto.CryptographicBuffer.GenerateRandom(provider.BlockLength); var encryptedBytes = WinRTCrypto.CryptographicEngine.Encrypt(cryptoKey, plaintextBytes, iv); - return new CipherString(Convert.ToBase64String(iv), Convert.ToBase64String(encryptedBytes)); + // TODO: Turn on whenever ready to support encrypt-then-mac + var mac = false ? ComputeMac(encryptedBytes, iv) : null; + + return new CipherString(Convert.ToBase64String(iv), Convert.ToBase64String(encryptedBytes), mac); } public string Decrypt(CipherString encyptedValue) @@ -141,9 +148,17 @@ namespace Bit.App.Services throw new ArgumentNullException(nameof(encyptedValue)); } + if(encyptedValue.Mac != null) + { + var computedMac = ComputeMac(encyptedValue.CipherTextBytes, encyptedValue.InitializationVectorBytes); + if(computedMac != encyptedValue.Mac) + { + throw new InvalidOperationException("MAC failed."); + } + } var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); - var cryptoKey = provider.CreateSymmetricKey(Key); + var cryptoKey = provider.CreateSymmetricKey(encyptedValue.Mac != null ? MacKey : Key); var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, encyptedValue.CipherTextBytes, encyptedValue.InitializationVectorBytes); return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length).TrimEnd('\0'); @@ -155,6 +170,30 @@ namespace Bit.App.Services } } + private string ComputeMac(byte[] ctBytes, byte[] ivBytes) + { + if(MacKey == null) + { + throw new ArgumentNullException(nameof(MacKey)); + } + + if(ctBytes == null) + { + throw new ArgumentNullException(nameof(ctBytes)); + } + + if(ivBytes == null) + { + throw new ArgumentNullException(nameof(ivBytes)); + } + + var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256); + var hasher = algorithm.CreateHash(MacKey); + hasher.Append(ivBytes.Concat(ctBytes).ToArray()); + var mac = hasher.GetValueAndReset(); + return Convert.ToBase64String(mac); + } + public byte[] MakeKeyFromPassword(string password, string salt) { if(password == null)