mirror of
https://github.com/bitwarden/android.git
synced 2024-12-24 18:08:26 +03:00
refactor for enc type header and cryptokey
This commit is contained in:
parent
0ebfe85d8e
commit
e7f3b115a4
11 changed files with 211 additions and 85 deletions
|
@ -14,6 +14,6 @@ namespace Bit.App.Abstractions
|
||||||
|
|
||||||
void LogOut();
|
void LogOut();
|
||||||
Task<FullLoginResult> TokenPostAsync(string email, string masterPassword);
|
Task<FullLoginResult> TokenPostAsync(string email, string masterPassword);
|
||||||
Task<LoginResult> TokenPostTwoFactorAsync(string token, string email, string masterPasswordHash, byte[] key);
|
Task<LoginResult> TokenPostTwoFactorAsync(string token, string email, string masterPasswordHash, CryptoKey key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,15 @@ namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
public interface ICryptoService
|
public interface ICryptoService
|
||||||
{
|
{
|
||||||
string Base64Key { get; }
|
CryptoKey Key { get; set; }
|
||||||
byte[] Key { get; set; }
|
CryptoKey PreviousKey { get; }
|
||||||
byte[] PreviousKey { get; }
|
|
||||||
bool KeyChanged { get; }
|
bool KeyChanged { get; }
|
||||||
|
|
||||||
string Decrypt(CipherString encyptedValue);
|
string Decrypt(CipherString encyptedValue, CryptoKey key = null);
|
||||||
CipherString Encrypt(string plaintextValue);
|
CipherString Encrypt(string plaintextValue, CryptoKey key = null);
|
||||||
byte[] MakeKeyFromPassword(string password, string salt);
|
CryptoKey MakeKeyFromPassword(string password, string salt);
|
||||||
string MakeKeyFromPasswordBase64(string password, string salt);
|
string MakeKeyFromPasswordBase64(string password, string salt);
|
||||||
byte[] HashPassword(byte[] key, string password);
|
byte[] HashPassword(CryptoKey key, string password);
|
||||||
string HashPasswordBase64(byte[] key, string password);
|
string HashPasswordBase64(CryptoKey key, string password);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -80,6 +80,7 @@
|
||||||
<Compile Include="Controls\FormEntryCell.cs" />
|
<Compile Include="Controls\FormEntryCell.cs" />
|
||||||
<Compile Include="Controls\PinControl.cs" />
|
<Compile Include="Controls\PinControl.cs" />
|
||||||
<Compile Include="Controls\VaultListViewCell.cs" />
|
<Compile Include="Controls\VaultListViewCell.cs" />
|
||||||
|
<Compile Include="Enums\EncryptionType.cs" />
|
||||||
<Compile Include="Enums\LockType.cs" />
|
<Compile Include="Enums\LockType.cs" />
|
||||||
<Compile Include="Enums\CipherType.cs" />
|
<Compile Include="Enums\CipherType.cs" />
|
||||||
<Compile Include="Enums\PushType.cs" />
|
<Compile Include="Enums\PushType.cs" />
|
||||||
|
@ -108,6 +109,7 @@
|
||||||
<Compile Include="Models\Api\LoginDataModel.cs" />
|
<Compile Include="Models\Api\LoginDataModel.cs" />
|
||||||
<Compile Include="Models\Cipher.cs" />
|
<Compile Include="Models\Cipher.cs" />
|
||||||
<Compile Include="Models\CipherString.cs" />
|
<Compile Include="Models\CipherString.cs" />
|
||||||
|
<Compile Include="Models\CryptoKey.cs" />
|
||||||
<Compile Include="Models\Data\SettingsData.cs" />
|
<Compile Include="Models\Data\SettingsData.cs" />
|
||||||
<Compile Include="Models\Data\FolderData.cs" />
|
<Compile Include="Models\Data\FolderData.cs" />
|
||||||
<Compile Include="Abstractions\IDataObject.cs" />
|
<Compile Include="Abstractions\IDataObject.cs" />
|
||||||
|
|
10
src/App/Enums/EncryptionType.cs
Normal file
10
src/App/Enums/EncryptionType.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Bit.App.Enums
|
||||||
|
{
|
||||||
|
public enum EncryptionType : byte
|
||||||
|
{
|
||||||
|
AesCbc256_B64 = 0,
|
||||||
|
AesCbc128_HmacSha256_B64 = 1,
|
||||||
|
AesCbc256_HmacSha256_B64 = 2,
|
||||||
|
RsaOaep_Sha256_B64 = 3
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using XLabs.Ioc;
|
using XLabs.Ioc;
|
||||||
|
using Bit.App.Enums;
|
||||||
|
|
||||||
namespace Bit.App.Models
|
namespace Bit.App.Models
|
||||||
{
|
{
|
||||||
|
@ -15,10 +16,58 @@ namespace Bit.App.Models
|
||||||
throw new ArgumentException(nameof(encryptedString));
|
throw new ArgumentException(nameof(encryptedString));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var headerPieces = encryptedString.Split('.');
|
||||||
|
string[] encPieces;
|
||||||
|
if(headerPieces.Length == 2 && Enum.TryParse(headerPieces[0], out EncryptionType encType))
|
||||||
|
{
|
||||||
|
EncryptionType = encType;
|
||||||
|
encPieces = headerPieces[1].Split('|');
|
||||||
|
}
|
||||||
|
else if(headerPieces.Length == 1)
|
||||||
|
{
|
||||||
|
encPieces = headerPieces[0].Split('|');
|
||||||
|
EncryptionType = encPieces.Length == 3 ? EncryptionType.AesCbc128_HmacSha256_B64 : EncryptionType.AesCbc256_B64;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Malformed header.");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(EncryptionType)
|
||||||
|
{
|
||||||
|
case EncryptionType.AesCbc256_B64:
|
||||||
|
if(encPieces.Length != 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Malformed encPieces.");
|
||||||
|
}
|
||||||
|
InitializationVector = encPieces[0];
|
||||||
|
CipherText = encPieces[1];
|
||||||
|
break;
|
||||||
|
case EncryptionType.AesCbc128_HmacSha256_B64:
|
||||||
|
case EncryptionType.AesCbc256_HmacSha256_B64:
|
||||||
|
if(encPieces.Length != 3)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Malformed encPieces.");
|
||||||
|
}
|
||||||
|
InitializationVector = encPieces[0];
|
||||||
|
CipherText = encPieces[1];
|
||||||
|
Mac = encPieces[2];
|
||||||
|
break;
|
||||||
|
case EncryptionType.RsaOaep_Sha256_B64:
|
||||||
|
if(encPieces.Length != 1)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Malformed encPieces.");
|
||||||
|
}
|
||||||
|
CipherText = encPieces[0];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("Unknown encType.");
|
||||||
|
}
|
||||||
|
|
||||||
EncryptedString = encryptedString;
|
EncryptedString = encryptedString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CipherString(string initializationVector, string cipherText, string mac = null)
|
public CipherString(EncryptionType encryptionType, string initializationVector, string cipherText, string mac = null)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(initializationVector))
|
if(string.IsNullOrWhiteSpace(initializationVector))
|
||||||
{
|
{
|
||||||
|
@ -30,30 +79,24 @@ namespace Bit.App.Models
|
||||||
throw new ArgumentNullException(nameof(cipherText));
|
throw new ArgumentNullException(nameof(cipherText));
|
||||||
}
|
}
|
||||||
|
|
||||||
EncryptedString = string.Format("{0}|{1}", initializationVector, cipherText);
|
EncryptionType = encryptionType;
|
||||||
|
EncryptedString = string.Format("{0}.{1}|{2}", (byte)encryptionType, initializationVector, cipherText);
|
||||||
|
|
||||||
if(!string.IsNullOrWhiteSpace(mac))
|
if(!string.IsNullOrWhiteSpace(mac))
|
||||||
{
|
{
|
||||||
EncryptedString = string.Format("{0}|{1}", EncryptedString, mac);
|
EncryptedString = string.Format("{0}|{1}", EncryptedString, mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CipherText = cipherText;
|
||||||
|
InitializationVector = initializationVector;
|
||||||
|
Mac = mac;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EncryptionType EncryptionType { get; private set; }
|
||||||
public string EncryptedString { get; private set; }
|
public string EncryptedString { get; private set; }
|
||||||
public string InitializationVector => EncryptedString?.Split('|')[0] ?? null;
|
public string InitializationVector { get; private set; }
|
||||||
public string CipherText => EncryptedString?.Split('|')[1] ?? null;
|
public string CipherText { get; private set; }
|
||||||
public string Mac
|
public string Mac { get; private set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var pieces = EncryptedString?.Split('|') ?? new string[0];
|
|
||||||
if(pieces.Length > 2)
|
|
||||||
{
|
|
||||||
return pieces[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public byte[] InitializationVectorBytes => Convert.FromBase64String(InitializationVector);
|
public byte[] InitializationVectorBytes => Convert.FromBase64String(InitializationVector);
|
||||||
public byte[] CipherTextBytes => Convert.FromBase64String(CipherText);
|
public byte[] CipherTextBytes => Convert.FromBase64String(CipherText);
|
||||||
public byte[] MacBytes => Mac == null ? null : Convert.FromBase64String(Mac);
|
public byte[] MacBytes => Mac == null ? null : Convert.FromBase64String(Mac);
|
||||||
|
|
62
src/App/Models/CryptoKey.cs
Normal file
62
src/App/Models/CryptoKey.cs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
using Bit.App.Enums;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Bit.App.Models
|
||||||
|
{
|
||||||
|
public class CryptoKey
|
||||||
|
{
|
||||||
|
public CryptoKey(byte[] rawBytes, EncryptionType? encType = null)
|
||||||
|
{
|
||||||
|
if(rawBytes == null || rawBytes.Length == 0)
|
||||||
|
{
|
||||||
|
throw new Exception("Must provide keyBytes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(encType == null)
|
||||||
|
{
|
||||||
|
if(rawBytes.Length == 32)
|
||||||
|
{
|
||||||
|
encType = EncryptionType.AesCbc256_B64;
|
||||||
|
}
|
||||||
|
else if(rawBytes.Length == 64)
|
||||||
|
{
|
||||||
|
encType = EncryptionType.AesCbc256_HmacSha256_B64;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Unable to determine encType.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptionType = encType.Value;
|
||||||
|
Key = rawBytes;
|
||||||
|
|
||||||
|
if(EncryptionType == EncryptionType.AesCbc256_B64 && Key.Length == 32)
|
||||||
|
{
|
||||||
|
EncKey = Key;
|
||||||
|
MacKey = null;
|
||||||
|
}
|
||||||
|
else if(EncryptionType == EncryptionType.AesCbc128_HmacSha256_B64 && Key.Length == 32)
|
||||||
|
{
|
||||||
|
EncKey = Key.Take(16).ToArray();
|
||||||
|
MacKey = Key.Skip(16).Take(16).ToArray();
|
||||||
|
}
|
||||||
|
else if(EncryptionType == EncryptionType.AesCbc256_HmacSha256_B64 && Key.Length == 64)
|
||||||
|
{
|
||||||
|
EncKey = Key.Take(32).ToArray();
|
||||||
|
MacKey = Key.Skip(32).Take(32).ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Unsupported encType/key length.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Key { get; set; }
|
||||||
|
public string B64Key => Convert.ToBase64String(Key);
|
||||||
|
public byte[] EncKey { get; set; }
|
||||||
|
public byte[] MacKey { get; set; }
|
||||||
|
public EncryptionType EncryptionType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@
|
||||||
public class FullLoginResult : LoginResult
|
public class FullLoginResult : LoginResult
|
||||||
{
|
{
|
||||||
public bool TwoFactorRequired { get; set; }
|
public bool TwoFactorRequired { get; set; }
|
||||||
public byte[] Key { get; set; }
|
public CryptoKey Key { get; set; }
|
||||||
public string MasterPasswordHash { get; set; }
|
public string MasterPasswordHash { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, _authService.Email);
|
var key = _cryptoService.MakeKeyFromPassword(PasswordCell.Entry.Text, _authService.Email);
|
||||||
if(key.SequenceEqual(_cryptoService.Key))
|
if(key.Key.SequenceEqual(_cryptoService.Key.Key))
|
||||||
{
|
{
|
||||||
_settings.AddOrUpdateValue(Constants.Locked, false);
|
_settings.AddOrUpdateValue(Constants.Locked, false);
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
|
|
|
@ -7,6 +7,7 @@ using XLabs.Ioc;
|
||||||
using Acr.UserDialogs;
|
using Acr.UserDialogs;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using PushNotification.Plugin.Abstractions;
|
using PushNotification.Plugin.Abstractions;
|
||||||
|
using Bit.App.Models;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
|
@ -19,9 +20,9 @@ namespace Bit.App.Pages
|
||||||
private IPushNotification _pushNotification;
|
private IPushNotification _pushNotification;
|
||||||
private readonly string _email;
|
private readonly string _email;
|
||||||
private readonly string _masterPasswordHash;
|
private readonly string _masterPasswordHash;
|
||||||
private readonly byte[] _key;
|
private readonly CryptoKey _key;
|
||||||
|
|
||||||
public LoginTwoFactorPage(string email, string masterPasswordHash, byte[] key)
|
public LoginTwoFactorPage(string email, string masterPasswordHash, CryptoKey key)
|
||||||
: base(updateActivity: false)
|
: base(updateActivity: false)
|
||||||
{
|
{
|
||||||
_email = email;
|
_email = email;
|
||||||
|
|
|
@ -6,8 +6,6 @@ using Bit.App.Models.Api;
|
||||||
using Plugin.Settings.Abstractions;
|
using Plugin.Settings.Abstractions;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Xamarin.Forms;
|
|
||||||
using PushNotification.Plugin.Abstractions;
|
|
||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
|
@ -237,7 +235,7 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LoginResult> TokenPostTwoFactorAsync(string token, string email, string masterPasswordHash,
|
public async Task<LoginResult> TokenPostTwoFactorAsync(string token, string email, string masterPasswordHash,
|
||||||
byte[] key)
|
CryptoKey key)
|
||||||
{
|
{
|
||||||
var result = new LoginResult();
|
var result = new LoginResult();
|
||||||
|
|
||||||
|
@ -263,7 +261,7 @@ namespace Bit.App.Services
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessLoginSuccess(byte[] key, TokenResponse response)
|
private void ProcessLoginSuccess(CryptoKey key, TokenResponse response)
|
||||||
{
|
{
|
||||||
_cryptoService.Key = key;
|
_cryptoService.Key = key;
|
||||||
_tokenService.Token = response.AccessToken;
|
_tokenService.Token = response.AccessToken;
|
||||||
|
|
|
@ -5,7 +5,6 @@ using Bit.App.Abstractions;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using PCLCrypto;
|
using PCLCrypto;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Xamarin.Forms;
|
|
||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
|
@ -17,8 +16,9 @@ namespace Bit.App.Services
|
||||||
|
|
||||||
private readonly ISecureStorageService _secureStorage;
|
private readonly ISecureStorageService _secureStorage;
|
||||||
private readonly IKeyDerivationService _keyDerivationService;
|
private readonly IKeyDerivationService _keyDerivationService;
|
||||||
private byte[] _key;
|
private CryptoKey _key;
|
||||||
private byte[] _previousKey;
|
private CryptoKey _legacyEtmKey;
|
||||||
|
private CryptoKey _previousKey;
|
||||||
|
|
||||||
public CryptoService(
|
public CryptoService(
|
||||||
ISecureStorageService secureStorage,
|
ISecureStorageService secureStorage,
|
||||||
|
@ -28,13 +28,13 @@ namespace Bit.App.Services
|
||||||
_keyDerivationService = keyDerivationService;
|
_keyDerivationService = keyDerivationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] Key
|
public CryptoKey Key
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if(_key == null)
|
if(_key == null)
|
||||||
{
|
{
|
||||||
_key = _secureStorage.Retrieve(KeyKey);
|
_key = new CryptoKey(_secureStorage.Retrieve(KeyKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
return _key;
|
return _key;
|
||||||
|
@ -43,37 +43,25 @@ namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
if(value != null)
|
if(value != null)
|
||||||
{
|
{
|
||||||
_secureStorage.Store(KeyKey, value);
|
_secureStorage.Store(KeyKey, value.Key);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PreviousKey = _key;
|
PreviousKey = _key;
|
||||||
_secureStorage.Delete(KeyKey);
|
_secureStorage.Delete(KeyKey);
|
||||||
_key = null;
|
_key = null;
|
||||||
|
_legacyEtmKey = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Base64Key
|
public CryptoKey PreviousKey
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if(Key == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Convert.ToBase64String(Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] PreviousKey
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if(_previousKey == null)
|
if(_previousKey == null)
|
||||||
{
|
{
|
||||||
_previousKey = _secureStorage.Retrieve(PreviousKeyKey);
|
_previousKey = new CryptoKey(_secureStorage.Retrieve(PreviousKeyKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
return _previousKey;
|
return _previousKey;
|
||||||
|
@ -82,7 +70,7 @@ namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
if(value != null)
|
if(value != null)
|
||||||
{
|
{
|
||||||
_secureStorage.Store(PreviousKeyKey, value);
|
_secureStorage.Store(PreviousKeyKey, value.Key);
|
||||||
_previousKey = value;
|
_previousKey = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,18 +90,20 @@ namespace Bit.App.Services
|
||||||
return Key != null;
|
return Key != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !PreviousKey.SequenceEqual(Key);
|
return !PreviousKey.Key.SequenceEqual(Key.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] EncKey => Key?.Take(16).ToArray();
|
public CipherString Encrypt(string plaintextValue, CryptoKey key = null)
|
||||||
public byte[] MacKey => Key?.Skip(16).Take(16).ToArray();
|
{
|
||||||
|
if(key == null)
|
||||||
|
{
|
||||||
|
key = Key;
|
||||||
|
}
|
||||||
|
|
||||||
public CipherString Encrypt(string plaintextValue)
|
if(key == null)
|
||||||
{
|
{
|
||||||
if(Key == null)
|
throw new ArgumentNullException(nameof(key));
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(Key));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(plaintextValue == null)
|
if(plaintextValue == null)
|
||||||
|
@ -124,23 +114,26 @@ namespace Bit.App.Services
|
||||||
var plaintextBytes = Encoding.UTF8.GetBytes(plaintextValue);
|
var plaintextBytes = Encoding.UTF8.GetBytes(plaintextValue);
|
||||||
|
|
||||||
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
|
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
|
||||||
// TODO: Turn on whenever ready to support encrypt-then-mac
|
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
|
||||||
var cryptoKey = provider.CreateSymmetricKey(true ? EncKey : Key);
|
|
||||||
var iv = WinRTCrypto.CryptographicBuffer.GenerateRandom(provider.BlockLength);
|
var iv = WinRTCrypto.CryptographicBuffer.GenerateRandom(provider.BlockLength);
|
||||||
var encryptedBytes = WinRTCrypto.CryptographicEngine.Encrypt(cryptoKey, plaintextBytes, iv);
|
var encryptedBytes = WinRTCrypto.CryptographicEngine.Encrypt(cryptoKey, plaintextBytes, iv);
|
||||||
// TODO: Turn on whenever ready to support encrypt-then-mac
|
var mac = key.MacKey != null ? ComputeMac(encryptedBytes, iv, key.MacKey) : null;
|
||||||
var mac = true ? ComputeMac(encryptedBytes, iv) : null;
|
|
||||||
|
|
||||||
return new CipherString(Convert.ToBase64String(iv), Convert.ToBase64String(encryptedBytes), mac);
|
return new CipherString(key.EncryptionType, Convert.ToBase64String(iv), Convert.ToBase64String(encryptedBytes), mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Decrypt(CipherString encyptedValue)
|
public string Decrypt(CipherString encyptedValue, CryptoKey key = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(Key == null)
|
if(key == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(Key));
|
key = Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(encyptedValue == null)
|
if(encyptedValue == null)
|
||||||
|
@ -148,9 +141,27 @@ namespace Bit.App.Services
|
||||||
throw new ArgumentNullException(nameof(encyptedValue));
|
throw new ArgumentNullException(nameof(encyptedValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(encyptedValue.Mac != null)
|
if(encyptedValue.EncryptionType == Enums.EncryptionType.AesCbc128_HmacSha256_B64 &&
|
||||||
|
key.EncryptionType == Enums.EncryptionType.AesCbc256_B64)
|
||||||
{
|
{
|
||||||
var computedMac = ComputeMac(encyptedValue.CipherTextBytes, encyptedValue.InitializationVectorBytes);
|
// Old encrypt-then-mac scheme, swap out the key
|
||||||
|
if(_legacyEtmKey == null)
|
||||||
|
{
|
||||||
|
_legacyEtmKey = new CryptoKey(key.Key, Enums.EncryptionType.AesCbc128_HmacSha256_B64);
|
||||||
|
}
|
||||||
|
|
||||||
|
key = _legacyEtmKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(encyptedValue.EncryptionType != key.EncryptionType)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("encType unavailable.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(key.MacKey != null && !string.IsNullOrWhiteSpace(encyptedValue.Mac))
|
||||||
|
{
|
||||||
|
var computedMac = ComputeMac(encyptedValue.CipherTextBytes,
|
||||||
|
encyptedValue.InitializationVectorBytes, key.MacKey);
|
||||||
if(computedMac != encyptedValue.Mac)
|
if(computedMac != encyptedValue.Mac)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("MAC failed.");
|
throw new InvalidOperationException("MAC failed.");
|
||||||
|
@ -158,7 +169,7 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
|
var provider = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
|
||||||
var cryptoKey = provider.CreateSymmetricKey(encyptedValue.Mac != null ? EncKey : Key);
|
var cryptoKey = provider.CreateSymmetricKey(key.EncKey);
|
||||||
var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, encyptedValue.CipherTextBytes,
|
var decryptedBytes = WinRTCrypto.CryptographicEngine.Decrypt(cryptoKey, encyptedValue.CipherTextBytes,
|
||||||
encyptedValue.InitializationVectorBytes);
|
encyptedValue.InitializationVectorBytes);
|
||||||
return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length).TrimEnd('\0');
|
return Encoding.UTF8.GetString(decryptedBytes, 0, decryptedBytes.Length).TrimEnd('\0');
|
||||||
|
@ -170,11 +181,11 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ComputeMac(byte[] ctBytes, byte[] ivBytes)
|
private string ComputeMac(byte[] ctBytes, byte[] ivBytes, byte[] macKey)
|
||||||
{
|
{
|
||||||
if(MacKey == null)
|
if(macKey == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(MacKey));
|
throw new ArgumentNullException(nameof(macKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ctBytes == null)
|
if(ctBytes == null)
|
||||||
|
@ -188,13 +199,13 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
|
var algorithm = WinRTCrypto.MacAlgorithmProvider.OpenAlgorithm(MacAlgorithm.HmacSha256);
|
||||||
var hasher = algorithm.CreateHash(MacKey);
|
var hasher = algorithm.CreateHash(macKey);
|
||||||
hasher.Append(ivBytes.Concat(ctBytes).ToArray());
|
hasher.Append(ivBytes.Concat(ctBytes).ToArray());
|
||||||
var mac = hasher.GetValueAndReset();
|
var mac = hasher.GetValueAndReset();
|
||||||
return Convert.ToBase64String(mac);
|
return Convert.ToBase64String(mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] MakeKeyFromPassword(string password, string salt)
|
public CryptoKey MakeKeyFromPassword(string password, string salt)
|
||||||
{
|
{
|
||||||
if(password == null)
|
if(password == null)
|
||||||
{
|
{
|
||||||
|
@ -209,17 +220,17 @@ namespace Bit.App.Services
|
||||||
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
||||||
var saltBytes = Encoding.UTF8.GetBytes(salt);
|
var saltBytes = Encoding.UTF8.GetBytes(salt);
|
||||||
|
|
||||||
var key = _keyDerivationService.DeriveKey(passwordBytes, saltBytes, 5000);
|
var keyBytes = _keyDerivationService.DeriveKey(passwordBytes, saltBytes, 5000);
|
||||||
return key;
|
return new CryptoKey(keyBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string MakeKeyFromPasswordBase64(string password, string salt)
|
public string MakeKeyFromPasswordBase64(string password, string salt)
|
||||||
{
|
{
|
||||||
var key = MakeKeyFromPassword(password, salt);
|
var key = MakeKeyFromPassword(password, salt);
|
||||||
return Convert.ToBase64String(key);
|
return Convert.ToBase64String(key.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] HashPassword(byte[] key, string password)
|
public byte[] HashPassword(CryptoKey key, string password)
|
||||||
{
|
{
|
||||||
if(key == null)
|
if(key == null)
|
||||||
{
|
{
|
||||||
|
@ -232,11 +243,11 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
||||||
var hash = _keyDerivationService.DeriveKey(key, passwordBytes, 1);
|
var hash = _keyDerivationService.DeriveKey(key.Key, passwordBytes, 1);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string HashPasswordBase64(byte[] key, string password)
|
public string HashPasswordBase64(CryptoKey key, string password)
|
||||||
{
|
{
|
||||||
var hash = HashPassword(key, password);
|
var hash = HashPassword(key, password);
|
||||||
return Convert.ToBase64String(hash);
|
return Convert.ToBase64String(hash);
|
||||||
|
|
Loading…
Reference in a new issue