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.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Javax.Crypto;
using Javax.Crypto.Spec;
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Generators;
@@ -33,5 +35,25 @@ namespace Bit.Droid.Services
generator.Init(password, salt, iterations); generator.Init(password, salt, iterations);
return ((KeyParameter)generator.GenerateDerivedMacParameters(keySize)).GetKey(); 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[]> HashAsync(byte[] value, CryptoHashAlgorithm algorithm);
Task<byte[]> HmacAsync(byte[] value, byte[] key, CryptoHashAlgorithm algorithm); Task<byte[]> HmacAsync(byte[] value, byte[] key, CryptoHashAlgorithm algorithm);
Task<bool> CompareAsync(byte[] a, byte[] b); Task<bool> CompareAsync(byte[] a, byte[] b);
Task<byte[]> AesEncryptAsync(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); Task<byte[]> AesDecryptAsync(byte[] data, byte[] iv, byte[] key, AesMode mode);
Task<byte[]> RsaEncryptAsync(byte[] data, byte[] publicKey, CryptoHashAlgorithm algorithm); Task<byte[]> RsaEncryptAsync(byte[] data, byte[] publicKey, CryptoHashAlgorithm algorithm);
Task<byte[]> RsaDecryptAsync(byte[] data, byte[] privateKey, CryptoHashAlgorithm algorithm); Task<byte[]> RsaDecryptAsync(byte[] data, byte[] privateKey, CryptoHashAlgorithm algorithm);
Task<byte[]> RsaExtractPublicKeyAsync(byte[] privateKey); Task<byte[]> RsaExtractPublicKeyAsync(byte[] privateKey);

View File

@@ -5,5 +5,7 @@ namespace Bit.Core.Abstractions
public interface ICryptoPrimitiveService public interface ICryptoPrimitiveService
{ {
byte[] Pbkdf2(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations); 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_OaepSha256_B64 = 3,
Rsa2048_OaepSha1_B64 = 4, Rsa2048_OaepSha1_B64 = 4,
Rsa2048_OaepSha256_HmacSha256_B64 = 5, 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]; Mac = encPieces[2];
break; break;
case EncryptionType.AesCbc256_B64: case EncryptionType.AesCbc256_B64:
case EncryptionType.AesGcm256_B64:
if (encPieces.Length != 2) if (encPieces.Length != 2)
{ {
return; return;

View File

@@ -1,5 +1,6 @@
using Bit.Core.Enums; using Bit.Core.Enums;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Bit.Core.Models.Domain namespace Bit.Core.Models.Domain
@@ -30,22 +31,24 @@ namespace Bit.Core.Models.Domain
} }
Key = key; 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; EncKey = Key;
MacKey = null; 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(); EncKey = new ArraySegment<byte>(Key, 0, 16).ToArray();
MacKey = new ArraySegment<byte>(Key, 16, 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(); EncKey = new ArraySegment<byte>(Key, 0, 32).ToArray();
MacKey = new ArraySegment<byte>(Key, 32, 32).ToArray(); MacKey = new ArraySegment<byte>(Key, 32, 32).ToArray();
EncTypes.Add(EncryptionType.AesGcm256_B64);
} }
else else
{ {
@@ -69,9 +72,9 @@ namespace Bit.Core.Models.Domain
public byte[] Key { get; set; } public byte[] Key { get; set; }
public byte[] EncKey { get; set; } public byte[] EncKey { get; set; }
public byte[] MacKey { get; set; } public byte[] MacKey { get; set; }
public EncryptionType EncType { get; set; }
public string KeyB64 { get; set; } public string KeyB64 { get; set; }
public string EncKeyB64 { get; set; } public string EncKeyB64 { get; set; }
public string MacKeyB64 { 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 iv = Convert.ToBase64String(encObj.Iv);
var data = Convert.ToBase64String(encObj.Data); var data = Convert.ToBase64String(encObj.Data);
var mac = encObj.Mac != null ? Convert.ToBase64String(encObj.Mac) : null; 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) public async Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null)
@@ -491,7 +491,7 @@ namespace Bit.Core.Services
macLen = encValue.Mac.Length; macLen = encValue.Mac.Length;
} }
var encBytes = new byte[1 + encValue.Iv.Length + macLen + encValue.Data.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); Buffer.BlockCopy(encValue.Iv, 0, encBytes, 1, encValue.Iv.Length);
if (encValue.Mac != null) if (encValue.Mac != null)
{ {
@@ -561,6 +561,14 @@ namespace Bit.Core.Services
ivBytes = new ArraySegment<byte>(encBytes, 1, 16).ToArray(); ivBytes = new ArraySegment<byte>(encBytes, 1, 16).ToArray();
ctBytes = new ArraySegment<byte>(encBytes, 17, encBytes.Length - 17).ToArray(); ctBytes = new ArraySegment<byte>(encBytes, 17, encBytes.Length - 17).ToArray();
break; 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: default:
return null; return null;
} }
@@ -589,16 +597,27 @@ namespace Bit.Core.Services
{ {
var obj = new EncryptedObject var obj = new EncryptedObject
{ {
Key = await GetKeyForEncryptionAsync(key), Key = await GetKeyForEncryptionAsync(key)
Iv = await _cryptoFunctionService.RandomBytesAsync(16)
}; };
obj.Data = await _cryptoFunctionService.AesEncryptAsync(data, obj.Iv, obj.Key.EncKey); if (true)
if (obj.Key.MacKey != null)
{ {
var macData = new byte[obj.Iv.Length + obj.Data.Length]; obj.Type = EncryptionType.AesCbc256_HmacSha256_B64;
Buffer.BlockCopy(obj.Iv, 0, macData, 0, obj.Iv.Length); obj.Iv = await _cryptoFunctionService.RandomBytesAsync(16);
Buffer.BlockCopy(obj.Data, 0, macData, obj.Iv.Length, obj.Data.Length); obj.Data = await _cryptoFunctionService.AesEncryptAsync(data, obj.Iv, obj.Key.EncKey, AesMode.CBC);
obj.Mac = await _cryptoFunctionService.HmacAsync(macData, obj.Key.MacKey, CryptoHashAlgorithm.Sha256); 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; return obj;
} }
@@ -608,12 +627,15 @@ namespace Bit.Core.Services
{ {
var keyForEnc = await GetKeyForEncryptionAsync(key); var keyForEnc = await GetKeyForEncryptionAsync(key);
var theKey = ResolveLegacyKey(encType, keyForEnc); 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. // Mac required.
return null; return null;
} }
if (theKey.EncType != encType) if (!theKey.EncTypes.Contains(encType))
{ {
// encType unavailable. // encType unavailable.
return null; 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); return Encoding.UTF8.GetString(decBytes);
} }
@@ -662,12 +685,15 @@ namespace Bit.Core.Services
var keyForEnc = await GetKeyForEncryptionAsync(key); var keyForEnc = await GetKeyForEncryptionAsync(key);
var theKey = ResolveLegacyKey(encType, keyForEnc); 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. // Mac required.
return null; return null;
} }
if (theKey.EncType != encType) if (!theKey.EncTypes.Contains(encType))
{ {
// encType unavailable. // encType unavailable.
return null; 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) private async Task<byte[]> RsaDecryptAsync(string encValue)
@@ -763,7 +790,8 @@ namespace Bit.Core.Services
private SymmetricCryptoKey ResolveLegacyKey(EncryptionType encKey, SymmetricCryptoKey key) 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 // Old encrypt-then-mac scheme, make a new key
if (_legacyEtmKey == null) if (_legacyEtmKey == null)
@@ -831,6 +859,7 @@ namespace Bit.Core.Services
private class EncryptedObject private class EncryptedObject
{ {
public EncryptionType Type { get; set; }
public byte[] Iv { get; set; } public byte[] Iv { get; set; }
public byte[] Data { get; set; } public byte[] Data { get; set; }
public byte[] Mac { get; set; } public byte[] Mac { get; set; }

View File

@@ -143,16 +143,24 @@ namespace Bit.Core.Services
return true; 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); var cryptoKey = provider.CreateSymmetricKey(key);
return Task.FromResult(CryptographicEngine.Encrypt(cryptoKey, data, iv)); 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); var cryptoKey = provider.CreateSymmetricKey(key);
return Task.FromResult(CryptographicEngine.Decrypt(cryptoKey, data, iv)); return Task.FromResult(CryptographicEngine.Decrypt(cryptoKey, data, iv));
} }
@@ -286,5 +294,18 @@ namespace Bit.Core.Services
throw new ArgumentException($"Invalid hkdf algorithm type, {hkdfAlgorithm}"); 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; 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 // ref: http://opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/CommonCrypto/CommonKeyDerivation.h
[DllImport(ObjCRuntime.Constants.libSystemLibrary, EntryPoint = "CCKeyDerivationPBKDF")] [DllImport(ObjCRuntime.Constants.libSystemLibrary, EntryPoint = "CCKeyDerivationPBKDF")]
private extern static int CCKeyCerivationPBKDF(uint algorithm, IntPtr password, nuint passwordLen, 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"> <ImageAsset Include="Resources\Assets.xcassets\AppIcons.appiconset\Contents.json">
<Visible>false</Visible> <Visible>false</Visible>
</ImageAsset> </ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\Contents.json" /> <ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\Contents.json">
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo.png" /> <Visible>false</Visible>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo%402x.png" /> </ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo%403x.png" /> <ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo.png">
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo_white.png" /> <Visible>false</Visible>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo_white%402x.png" /> </ImageAsset>
<ImageAsset Include="Resources\Assets.xcassets\LaunchScreen.imageset\logo_white%403x.png" /> <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>
<ItemGroup> <ItemGroup>
<InterfaceDefinition Include="LaunchScreen.storyboard" /> <InterfaceDefinition Include="LaunchScreen.storyboard" />