1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

Compare commits

...

1 Commits

Author SHA1 Message Date
Kyle Spearrin
02ad4adff5 support for aes gcm crypto 2021-05-05 15:02:19 -04:00
11 changed files with 148 additions and 36 deletions

View File

@@ -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;
}
}
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

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

View File

@@ -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
}
}

View File

@@ -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;

View File

@@ -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<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
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>();
}
}

View File

@@ -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
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<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,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<byte[]> 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; }

View File

@@ -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)
{
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}");
}
}
}
}

View File

@@ -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,

View File

@@ -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" />