mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
1 Commits
vault/pm-5
...
aesgcm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02ad4adff5 |
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
Normal file
8
src/Core/Enums/AesMode.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum AesMode : byte
|
||||
{
|
||||
CBC = 0,
|
||||
GCM = 1,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user