using Bit.App.Enums; using Bit.App.Models; using PCLCrypto; using System; using System.Collections.Generic; using System.Linq; namespace Bit.App.Utilities { public static class Crypto { public static CipherString AesCbcEncrypt(byte[] plainBytes, SymmetricCryptoKey key) { if(key == null) { throw new ArgumentNullException(nameof(key)); } if(plainBytes == null) { throw new ArgumentNullException(nameof(plainBytes)); } var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); var cryptoKey = provider.CreateSymmetricKey(key.EncKey); var iv = RandomBytes(provider.BlockLength); var encryptedBytes = WinRTCrypto.CryptographicEngine.Encrypt(cryptoKey, plainBytes, iv); var mac = key.MacKey != null ? ComputeMacBase64(encryptedBytes, iv, key.MacKey) : null; return new CipherString(key.EncryptionType, Convert.ToBase64String(iv), Convert.ToBase64String(encryptedBytes), mac); } public static byte[] AesCbcDecrypt(CipherString encyptedValue, SymmetricCryptoKey key) { if(encyptedValue == null) { throw new ArgumentNullException(nameof(encyptedValue)); } return AesCbcDecrypt(encyptedValue.EncryptionType, encyptedValue.CipherTextBytes, encyptedValue.InitializationVectorBytes, encyptedValue.MacBytes, key); } public static byte[] AesCbcDecrypt(EncryptionType type, byte[] ct, byte[] iv, byte[] mac, SymmetricCryptoKey key) { if(key == null) { throw new ArgumentNullException(nameof(key)); } if(ct == null) { throw new ArgumentNullException(nameof(ct)); } if(iv == null) { throw new ArgumentNullException(nameof(iv)); } if(key.EncryptionType != type) { throw new InvalidOperationException(nameof(type)); } if(key.MacKey != null && mac != null) { var computedMacBytes = ComputeMac(ct, iv, key.MacKey); if(!MacsEqual(key.MacKey, computedMacBytes, mac)) { throw new InvalidOperationException("MAC failed."); } } var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); var cryptoKey = provider.CreateSymmetricKey(key.EncKey); var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, ct, iv); return decryptedBytes; } public static byte[] RandomBytes(int length) { return WinRTCrypto.CryptographicBuffer.GenerateRandom(length); } public static string ComputeMacBase64(byte[] ctBytes, byte[] ivBytes, byte[] macKey) { var mac = ComputeMac(ctBytes, ivBytes, macKey); return Convert.ToBase64String(mac); } public static byte[] ComputeMac(byte[] ctBytes, byte[] ivBytes, byte[] macKey) { if(ctBytes == null) { throw new ArgumentNullException(nameof(ctBytes)); } if(ivBytes == null) { throw new ArgumentNullException(nameof(ivBytes)); } return ComputeMac(ivBytes.Concat(ctBytes), macKey); } public static byte[] ComputeMac(IEnumerable dataBytes, byte[] macKey) { if(macKey == null) { throw new ArgumentNullException(nameof(macKey)); } if(dataBytes == null) { throw new ArgumentNullException(nameof(dataBytes)); } var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256); var hasher = algorithm.CreateHash(macKey); hasher.Append(dataBytes.ToArray()); var mac = hasher.GetValueAndReset(); return mac; } // Safely compare two MACs in a way that protects against timing attacks (Double HMAC Verification). // ref: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/ public static bool MacsEqual(byte[] macKey, byte[] mac1, byte[] mac2) { var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256); var hasher = algorithm.CreateHash(macKey); hasher.Append(mac1); mac1 = hasher.GetValueAndReset(); hasher.Append(mac2); mac2 = hasher.GetValueAndReset(); if(mac1.Length != mac2.Length) { return false; } for(int i = 0; i < mac2.Length; i++) { if(mac1[i] != mac2[i]) { return false; } } return true; } // ref: https://github.com/mirthas/totp-net/blob/master/TOTP/Totp.cs public static string Totp(string b32Key) { var key = Base32.FromBase32(b32Key); if(key == null || key.Length == 0) { return null; } var now = Helpers.EpocUtcNow() / 1000; var sec = now / 30; var secBytes = BitConverter.GetBytes(sec); if(BitConverter.IsLittleEndian) { Array.Reverse(secBytes, 0, secBytes.Length); } var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha1); var hasher = algorithm.CreateHash(key); hasher.Append(secBytes); var hash = hasher.GetValueAndReset(); var offset = (hash[hash.Length - 1] & 0xf); var i = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff); var code = i % (int)Math.Pow(10, 6); return code.ToString().PadLeft(6, '0'); } } }