Browse Source

support for aes gcm crypto

aesgcm
Kyle Spearrin 5 years ago
parent
commit
02ad4adff5
  1. 22
      src/Android/Services/CryptoPrimitiveService.cs
  2. 4
      src/Core/Abstractions/ICryptoFunctionService.cs
  3. 2
      src/Core/Abstractions/ICryptoPrimitiveService.cs
  4. 8
      src/Core/Enums/AesMode.cs
  5. 3
      src/Core/Enums/EncryptionType.cs
  6. 1
      src/Core/Models/Domain/EncString.cs
  7. 13
      src/Core/Models/Domain/SymmetricCryptoKey.cs
  8. 53
      src/Core/Services/CryptoService.cs
  9. 29
      src/Core/Services/PclCryptoFunctionService.cs
  10. 11
      src/iOS.Core/Services/CryptoPrimitiveService.cs
  11. 28
      src/iOS/iOS.csproj

22
src/Android/Services/CryptoPrimitiveService.cs

@ -1,5 +1,7 @@ @@ -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 @@ -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;
}
}
}

4
src/Core/Abstractions/ICryptoFunctionService.cs

@ -20,8 +20,8 @@ namespace Bit.Core.Abstractions @@ -20,8 +20,8 @@ namespace Bit.Core.Abstractions
Task<byte[]> HashAsync(byte[] value, CryptoHashAlgorithm algorithm);
Task<byte[]> HmacAsync(byte[] value, byte[] key, CryptoHashAlgorithm algorithm);
Task<bool> CompareAsync(byte[] a, byte[] b);
Task<byte[]> AesEncryptAsync(byte[] data, byte[] iv, byte[] key);
Task<byte[]> AesDecryptAsync(byte[] data, byte[] iv, byte[] key);
Task<byte[]> AesEncryptAsync(byte[] data, byte[] iv, byte[] key, AesMode mode);
Task<byte[]> AesDecryptAsync(byte[] data, byte[] iv, byte[] key, AesMode mode);
Task<byte[]> RsaEncryptAsync(byte[] data, byte[] publicKey, CryptoHashAlgorithm algorithm);
Task<byte[]> RsaDecryptAsync(byte[] data, byte[] privateKey, CryptoHashAlgorithm algorithm);
Task<byte[]> RsaExtractPublicKeyAsync(byte[] privateKey);

2
src/Core/Abstractions/ICryptoPrimitiveService.cs

@ -5,5 +5,7 @@ namespace Bit.Core.Abstractions @@ -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);
}
}

8
src/Core/Enums/AesMode.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace Bit.Core.Enums
{
public enum AesMode : byte
{
CBC = 0,
GCM = 1,
}
}

3
src/Core/Enums/EncryptionType.cs

@ -8,6 +8,7 @@ @@ -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
}
}

1
src/Core/Models/Domain/EncString.cs

@ -73,6 +73,7 @@ namespace Bit.Core.Models.Domain @@ -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;

13
src/Core/Models/Domain/SymmetricCryptoKey.cs

@ -1,5 +1,6 @@ @@ -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 @@ -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<byte>(Key, 0, 16).ToArray();
MacKey = new ArraySegment<byte>(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<byte>(Key, 0, 32).ToArray();
MacKey = new ArraySegment<byte>(Key, 32, 32).ToArray();
EncTypes.Add(EncryptionType.AesGcm256_B64);
}
else
{
@ -69,9 +72,9 @@ namespace Bit.Core.Models.Domain @@ -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<EncryptionType> EncTypes { get; set; } = new HashSet<EncryptionType>();
}
}

53
src/Core/Services/CryptoService.cs

@ -479,7 +479,7 @@ namespace Bit.Core.Services @@ -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<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null)
@ -491,7 +491,7 @@ namespace Bit.Core.Services @@ -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 @@ -561,6 +561,14 @@ namespace Bit.Core.Services
ivBytes = new ArraySegment<byte>(encBytes, 1, 16).ToArray();
ctBytes = new ArraySegment<byte>(encBytes, 17, encBytes.Length - 17).ToArray();
break;
case EncryptionType.AesGcm256_B64:
if (encBytes.Length < 13) // 1 + 12 + ctLength
{
return null;
}
ivBytes = new ArraySegment<byte>(encBytes, 1, 12).ToArray();
ctBytes = new ArraySegment<byte>(encBytes, 13, encBytes.Length - 13).ToArray();
break;
default:
return null;
}
@ -589,10 +597,13 @@ namespace Bit.Core.Services @@ -589,10 +597,13 @@ 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 (true)
{
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];
@ -600,6 +611,14 @@ namespace Bit.Core.Services @@ -600,6 +611,14 @@ namespace Bit.Core.Services
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 @@ -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 @@ -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 @@ -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 @@ -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<byte[]> RsaDecryptAsync(string encValue)
@ -763,7 +790,8 @@ namespace Bit.Core.Services @@ -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 @@ -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; }

29
src/Core/Services/PclCryptoFunctionService.cs

@ -143,16 +143,24 @@ namespace Bit.Core.Services @@ -143,16 +143,24 @@ namespace Bit.Core.Services
return true;
}
public Task<byte[]> AesEncryptAsync(byte[] data, byte[] iv, byte[] key)
public Task<byte[]> 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<byte[]> AesDecryptAsync(byte[] data, byte[] iv, byte[] key)
public Task<byte[]> AesDecryptAsync(byte[] data, byte[] iv, byte[] key, AesMode mode)
{
if (mode == AesMode.GCM)
{
var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
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 @@ -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}");
}
}
}
}

11
src/iOS.Core/Services/CryptoPrimitiveService.cs

@ -39,6 +39,17 @@ namespace Bit.iOS.Core.Services @@ -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,

28
src/iOS/iOS.csproj

@ -144,13 +144,27 @@ @@ -144,13 +144,27 @@
<ImageAsset Include="Resources\Assets.xcassets\AppIcons.appiconset\Contents.json">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\Contents.json" />
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo.png" />
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo%402x.png" />
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo%403x.png" />
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo_white.png" />
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo_white%402x.png" />
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo_white%403x.png" />
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\Contents.json">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo%402x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo%403x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo_white.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo_white%402x.png">
<Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo_white%403x.png">
<Visible>false</Visible>
</ImageAsset>
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="LaunchScreen.storyboard" />

Loading…
Cancel
Save