diff --git a/src/Android/Services/CryptoPrimitiveService.cs b/src/Android/Services/CryptoPrimitiveService.cs index bdb19cfa5..293a3a305 100644 --- a/src/Android/Services/CryptoPrimitiveService.cs +++ b/src/Android/Services/CryptoPrimitiveService.cs @@ -1,5 +1,7 @@ using Bit.Core.Abstractions; using Bit.Core.Enums; +using Javax.Crypto; +using Javax.Crypto.Spec; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Generators; @@ -33,5 +35,25 @@ namespace Bit.Droid.Services generator.Init(password, salt, iterations); return ((KeyParameter)generator.GenerateDerivedMacParameters(keySize)).GetKey(); } + + public byte[] AesGcmEncrypt(byte[] data, byte[] iv, byte[] key) + { + var secKey = new SecretKeySpec(key, "AES"); + var cipher = Cipher.GetInstance("AES/GCM/NoPadding"); + var gcmSpec = new GCMParameterSpec(128, iv); + cipher.Init(CipherMode.EncryptMode, secKey, gcmSpec); + var result = cipher.DoFinal(data); + return result; + } + + public byte[] AesGcmDecrypt(byte[] data, byte[] iv, byte[] key) + { + var secKey = new SecretKeySpec(key, "AES"); + var cipher = Cipher.GetInstance("AES/GCM/NoPadding"); + var gcmSpec = new GCMParameterSpec(128, iv); + cipher.Init(CipherMode.DecryptMode, secKey, gcmSpec); + var result = cipher.DoFinal(data); + return result; + } } } diff --git a/src/Core/Abstractions/ICryptoFunctionService.cs b/src/Core/Abstractions/ICryptoFunctionService.cs index 98e58bf48..266c144f6 100644 --- a/src/Core/Abstractions/ICryptoFunctionService.cs +++ b/src/Core/Abstractions/ICryptoFunctionService.cs @@ -20,8 +20,8 @@ namespace Bit.Core.Abstractions Task HashAsync(byte[] value, CryptoHashAlgorithm algorithm); Task HmacAsync(byte[] value, byte[] key, CryptoHashAlgorithm algorithm); Task CompareAsync(byte[] a, byte[] b); - Task AesEncryptAsync(byte[] data, byte[] iv, byte[] key); - Task AesDecryptAsync(byte[] data, byte[] iv, byte[] key); + Task AesEncryptAsync(byte[] data, byte[] iv, byte[] key, AesMode mode); + Task AesDecryptAsync(byte[] data, byte[] iv, byte[] key, AesMode mode); Task RsaEncryptAsync(byte[] data, byte[] publicKey, CryptoHashAlgorithm algorithm); Task RsaDecryptAsync(byte[] data, byte[] privateKey, CryptoHashAlgorithm algorithm); Task RsaExtractPublicKeyAsync(byte[] privateKey); diff --git a/src/Core/Abstractions/ICryptoPrimitiveService.cs b/src/Core/Abstractions/ICryptoPrimitiveService.cs index 897c337c4..73ef066b9 100644 --- a/src/Core/Abstractions/ICryptoPrimitiveService.cs +++ b/src/Core/Abstractions/ICryptoPrimitiveService.cs @@ -5,5 +5,7 @@ namespace Bit.Core.Abstractions public interface ICryptoPrimitiveService { byte[] Pbkdf2(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations); + byte[] AesGcmEncrypt(byte[] data, byte[] iv, byte[] key); + byte[] AesGcmDecrypt(byte[] data, byte[] iv, byte[] key); } } diff --git a/src/Core/Enums/AesMode.cs b/src/Core/Enums/AesMode.cs new file mode 100644 index 000000000..5ea01381f --- /dev/null +++ b/src/Core/Enums/AesMode.cs @@ -0,0 +1,8 @@ +namespace Bit.Core.Enums +{ + public enum AesMode : byte + { + CBC = 0, + GCM = 1, + } +} diff --git a/src/Core/Enums/EncryptionType.cs b/src/Core/Enums/EncryptionType.cs index 2b6eaf086..f87229c99 100644 --- a/src/Core/Enums/EncryptionType.cs +++ b/src/Core/Enums/EncryptionType.cs @@ -8,6 +8,7 @@ Rsa2048_OaepSha256_B64 = 3, Rsa2048_OaepSha1_B64 = 4, Rsa2048_OaepSha256_HmacSha256_B64 = 5, - Rsa2048_OaepSha1_HmacSha256_B64 = 6 + Rsa2048_OaepSha1_HmacSha256_B64 = 6, + AesGcm256_B64 = 7 } } diff --git a/src/Core/Models/Domain/EncString.cs b/src/Core/Models/Domain/EncString.cs index df4757b9e..4fe383a49 100644 --- a/src/Core/Models/Domain/EncString.cs +++ b/src/Core/Models/Domain/EncString.cs @@ -73,6 +73,7 @@ namespace Bit.Core.Models.Domain Mac = encPieces[2]; break; case EncryptionType.AesCbc256_B64: + case EncryptionType.AesGcm256_B64: if (encPieces.Length != 2) { return; diff --git a/src/Core/Models/Domain/SymmetricCryptoKey.cs b/src/Core/Models/Domain/SymmetricCryptoKey.cs index 820823b26..e4fe64f06 100644 --- a/src/Core/Models/Domain/SymmetricCryptoKey.cs +++ b/src/Core/Models/Domain/SymmetricCryptoKey.cs @@ -1,5 +1,6 @@ using Bit.Core.Enums; using System; +using System.Collections.Generic; using System.Linq; namespace Bit.Core.Models.Domain @@ -30,22 +31,24 @@ namespace Bit.Core.Models.Domain } Key = key; - EncType = encType.Value; + EncTypes.Add(encType.Value); - if (EncType == EncryptionType.AesCbc256_B64 && Key.Length == 32) + if (encType.Value == EncryptionType.AesCbc256_B64 && Key.Length == 32) { EncKey = Key; MacKey = null; + EncTypes.Add(EncryptionType.AesGcm256_B64); } - else if (EncType == EncryptionType.AesCbc128_HmacSha256_B64 && Key.Length == 32) + else if (encType.Value == EncryptionType.AesCbc128_HmacSha256_B64 && Key.Length == 32) { EncKey = new ArraySegment(Key, 0, 16).ToArray(); MacKey = new ArraySegment(Key, 16, 16).ToArray(); } - else if (EncType == EncryptionType.AesCbc256_HmacSha256_B64 && Key.Length == 64) + else if (encType.Value == EncryptionType.AesCbc256_HmacSha256_B64 && Key.Length == 64) { EncKey = new ArraySegment(Key, 0, 32).ToArray(); MacKey = new ArraySegment(Key, 32, 32).ToArray(); + EncTypes.Add(EncryptionType.AesGcm256_B64); } else { @@ -69,9 +72,9 @@ namespace Bit.Core.Models.Domain public byte[] Key { get; set; } public byte[] EncKey { get; set; } public byte[] MacKey { get; set; } - public EncryptionType EncType { get; set; } public string KeyB64 { get; set; } public string EncKeyB64 { get; set; } public string MacKeyB64 { get; set; } + public HashSet EncTypes { get; set; } = new HashSet(); } } diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs index 3c21925cc..f59f221f3 100644 --- a/src/Core/Services/CryptoService.cs +++ b/src/Core/Services/CryptoService.cs @@ -479,7 +479,7 @@ namespace Bit.Core.Services var iv = Convert.ToBase64String(encObj.Iv); var data = Convert.ToBase64String(encObj.Data); var mac = encObj.Mac != null ? Convert.ToBase64String(encObj.Mac) : null; - return new EncString(encObj.Key.EncType, data, iv, mac); + return new EncString(encObj.Type, data, iv, mac); } public async Task EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null) @@ -491,7 +491,7 @@ namespace Bit.Core.Services macLen = encValue.Mac.Length; } var encBytes = new byte[1 + encValue.Iv.Length + macLen + encValue.Data.Length]; - Buffer.BlockCopy(new byte[] { (byte)encValue.Key.EncType }, 0, encBytes, 0, 1); + Buffer.BlockCopy(new byte[] { (byte)encValue.Type }, 0, encBytes, 0, 1); Buffer.BlockCopy(encValue.Iv, 0, encBytes, 1, encValue.Iv.Length); if (encValue.Mac != null) { @@ -561,6 +561,14 @@ namespace Bit.Core.Services ivBytes = new ArraySegment(encBytes, 1, 16).ToArray(); ctBytes = new ArraySegment(encBytes, 17, encBytes.Length - 17).ToArray(); break; + case EncryptionType.AesGcm256_B64: + if (encBytes.Length < 13) // 1 + 12 + ctLength + { + return null; + } + ivBytes = new ArraySegment(encBytes, 1, 12).ToArray(); + ctBytes = new ArraySegment(encBytes, 13, encBytes.Length - 13).ToArray(); + break; default: return null; } @@ -589,16 +597,27 @@ namespace Bit.Core.Services { var obj = new EncryptedObject { - Key = await GetKeyForEncryptionAsync(key), - Iv = await _cryptoFunctionService.RandomBytesAsync(16) + Key = await GetKeyForEncryptionAsync(key) }; - obj.Data = await _cryptoFunctionService.AesEncryptAsync(data, obj.Iv, obj.Key.EncKey); - if (obj.Key.MacKey != null) + if (true) { - var macData = new byte[obj.Iv.Length + obj.Data.Length]; - Buffer.BlockCopy(obj.Iv, 0, macData, 0, obj.Iv.Length); - Buffer.BlockCopy(obj.Data, 0, macData, obj.Iv.Length, obj.Data.Length); - obj.Mac = await _cryptoFunctionService.HmacAsync(macData, obj.Key.MacKey, CryptoHashAlgorithm.Sha256); + obj.Type = EncryptionType.AesCbc256_HmacSha256_B64; + obj.Iv = await _cryptoFunctionService.RandomBytesAsync(16); + obj.Data = await _cryptoFunctionService.AesEncryptAsync(data, obj.Iv, obj.Key.EncKey, AesMode.CBC); + if (obj.Key.MacKey != null) + { + var macData = new byte[obj.Iv.Length + obj.Data.Length]; + Buffer.BlockCopy(obj.Iv, 0, macData, 0, obj.Iv.Length); + Buffer.BlockCopy(obj.Data, 0, macData, obj.Iv.Length, obj.Data.Length); + obj.Mac = await _cryptoFunctionService.HmacAsync(macData, obj.Key.MacKey, CryptoHashAlgorithm.Sha256); + } + } + else + { + // TODO: make this case live when we switch to GCM encryption + obj.Type = EncryptionType.AesGcm256_B64; + obj.Iv = await _cryptoFunctionService.RandomBytesAsync(12); + obj.Data = await _cryptoFunctionService.AesEncryptAsync(data, obj.Iv, obj.Key.EncKey, AesMode.GCM); } return obj; } @@ -608,12 +627,15 @@ namespace Bit.Core.Services { var keyForEnc = await GetKeyForEncryptionAsync(key); var theKey = ResolveLegacyKey(encType, keyForEnc); - if (theKey.MacKey != null && mac == null) + + var macType = encType == EncryptionType.AesCbc128_HmacSha256_B64 || + encType == EncryptionType.AesCbc256_HmacSha256_B64; + if (macType && theKey.MacKey != null && mac == null) { // Mac required. return null; } - if (theKey.EncType != encType) + if (!theKey.EncTypes.Contains(encType)) { // encType unavailable. return null; @@ -652,7 +674,8 @@ namespace Bit.Core.Services } } - var decBytes = await _cryptoFunctionService.AesDecryptAsync(dataBytes, ivBytes, encKey); + var decBytes = await _cryptoFunctionService.AesDecryptAsync(dataBytes, ivBytes, encKey, + encType == EncryptionType.AesGcm256_B64 ? AesMode.GCM : AesMode.CBC); return Encoding.UTF8.GetString(decBytes); } @@ -662,12 +685,15 @@ namespace Bit.Core.Services var keyForEnc = await GetKeyForEncryptionAsync(key); var theKey = ResolveLegacyKey(encType, keyForEnc); - if (theKey.MacKey != null && mac == null) + + var macType = encType == EncryptionType.AesCbc128_HmacSha256_B64 || + encType == EncryptionType.AesCbc256_HmacSha256_B64; + if (macType && theKey.MacKey != null && mac == null) { // Mac required. return null; } - if (theKey.EncType != encType) + if (!theKey.EncTypes.Contains(encType)) { // encType unavailable. return null; @@ -694,7 +720,8 @@ namespace Bit.Core.Services } } - return await _cryptoFunctionService.AesDecryptAsync(data, iv, theKey.EncKey); + return await _cryptoFunctionService.AesDecryptAsync(data, iv, theKey.EncKey, + encType == EncryptionType.AesGcm256_B64 ? AesMode.GCM : AesMode.CBC); } private async Task RsaDecryptAsync(string encValue) @@ -763,7 +790,8 @@ namespace Bit.Core.Services private SymmetricCryptoKey ResolveLegacyKey(EncryptionType encKey, SymmetricCryptoKey key) { - if (encKey == EncryptionType.AesCbc128_HmacSha256_B64 && key.EncType == EncryptionType.AesCbc256_B64) + if (encKey == EncryptionType.AesCbc128_HmacSha256_B64 && + key.EncTypes.Contains(EncryptionType.AesCbc256_B64)) { // Old encrypt-then-mac scheme, make a new key if (_legacyEtmKey == null) @@ -831,6 +859,7 @@ namespace Bit.Core.Services private class EncryptedObject { + public EncryptionType Type { get; set; } public byte[] Iv { get; set; } public byte[] Data { get; set; } public byte[] Mac { get; set; } diff --git a/src/Core/Services/PclCryptoFunctionService.cs b/src/Core/Services/PclCryptoFunctionService.cs index 8299f8218..5899a8c04 100644 --- a/src/Core/Services/PclCryptoFunctionService.cs +++ b/src/Core/Services/PclCryptoFunctionService.cs @@ -143,16 +143,24 @@ namespace Bit.Core.Services return true; } - public Task AesEncryptAsync(byte[] data, byte[] iv, byte[] key) + public Task AesEncryptAsync(byte[] data, byte[] iv, byte[] key, AesMode mode) { - var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); + if (mode == AesMode.GCM) + { + return Task.FromResult(_cryptoPrimitiveService.AesGcmEncrypt(data, iv, key)); + } + var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(AesModeToSymmetricAlgorithm(mode)); var cryptoKey = provider.CreateSymmetricKey(key); return Task.FromResult(CryptographicEngine.Encrypt(cryptoKey, data, iv)); } - public Task AesDecryptAsync(byte[] data, byte[] iv, byte[] key) + public Task AesDecryptAsync(byte[] data, byte[] iv, byte[] key, AesMode mode) { - var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); + if (mode == AesMode.GCM) + { + return Task.FromResult(_cryptoPrimitiveService.AesGcmDecrypt(data, iv, key)); + } + var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(AesModeToSymmetricAlgorithm(mode)); var cryptoKey = provider.CreateSymmetricKey(key); return Task.FromResult(CryptographicEngine.Decrypt(cryptoKey, data, iv)); } @@ -286,5 +294,18 @@ namespace Bit.Core.Services throw new ArgumentException($"Invalid hkdf algorithm type, {hkdfAlgorithm}"); } } + + private SymmetricAlgorithm AesModeToSymmetricAlgorithm(AesMode aesMode) + { + switch (aesMode) + { + case AesMode.CBC: + return SymmetricAlgorithm.AesCbcPkcs7; + case AesMode.GCM: + return SymmetricAlgorithm.AesGcm; + default: + throw new ArgumentException($"Invalid aes mode type, {aesMode}"); + } + } } } diff --git a/src/iOS.Core/Services/CryptoPrimitiveService.cs b/src/iOS.Core/Services/CryptoPrimitiveService.cs index 9d97fa647..3e3d0c3e2 100644 --- a/src/iOS.Core/Services/CryptoPrimitiveService.cs +++ b/src/iOS.Core/Services/CryptoPrimitiveService.cs @@ -39,6 +39,17 @@ namespace Bit.iOS.Core.Services return keyBytes; } + public byte[] AesGcmEncrypt(byte[] data, byte[] iv, byte[] key) + { + throw new NotImplementedException(); + + } + + public byte[] AesGcmDecrypt(byte[] data, byte[] iv, byte[] key) + { + throw new NotImplementedException(); + } + // ref: http://opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/CommonCrypto/CommonKeyDerivation.h [DllImport(ObjCRuntime.Constants.libSystemLibrary, EntryPoint = "CCKeyDerivationPBKDF")] private extern static int CCKeyCerivationPBKDF(uint algorithm, IntPtr password, nuint passwordLen, diff --git a/src/iOS/iOS.csproj b/src/iOS/iOS.csproj index 9f8b7888a..2c8a9e347 100644 --- a/src/iOS/iOS.csproj +++ b/src/iOS/iOS.csproj @@ -144,13 +144,27 @@ false - - - - - - - + + false + + + false + + + false + + + false + + + false + + + false + + + false +