mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
[PM-2713] continue organizing crypto service
This commit is contained in:
@@ -332,7 +332,7 @@ namespace Bit.App.Pages
|
||||
var protectedPin = await _stateService.GetProtectedPinAsync();
|
||||
var encKey = await _cryptoService.GetEncKeyAsync(key);
|
||||
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey);
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig);
|
||||
var pinKey = await _cryptoService.MakePinKeyAsync(decPin, _email, kdfConfig);
|
||||
await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
|
||||
}
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Bit.App.Pages
|
||||
await _cryptoService.SetKeyAsync(key);
|
||||
await _cryptoService.SetPasswordHashAsync(localMasterPasswordHash);
|
||||
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
|
||||
await _cryptoService.SetEncPrivateKeyAsync(keys.Item2.EncryptedString);
|
||||
await _cryptoService.SetPrivateKeyAsync(keys.Item2.EncryptedString);
|
||||
|
||||
if (ResetPasswordAutoEnroll)
|
||||
{
|
||||
|
||||
@@ -437,7 +437,7 @@ namespace Bit.App.Pages
|
||||
|
||||
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
var pinKey = await _cryptoService.MakePinKeyAysnc(pin, email, kdfConfig);
|
||||
var pinKey = await _cryptoService.MakePinKeyAsync(pin, email, kdfConfig);
|
||||
var key = await _cryptoService.GetKeyAsync();
|
||||
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);
|
||||
|
||||
|
||||
@@ -16,17 +16,34 @@ namespace Bit.Core.Abstractions
|
||||
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
|
||||
Task SetMasterKeyAsync(MasterKey masterKey, string userId = null);
|
||||
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
||||
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
|
||||
Task ClearMasterKeyAsync(string userId = null);
|
||||
Task SetPasswordHashAsync(string keyHash);
|
||||
Task<string> GetPasswordHashAsync();
|
||||
Task ClearPasswordHashAsync(string userId = null);
|
||||
Task<bool> CompareAndUpdatePasswordHashAsync(string masterPassword, SymmetricCryptoKey key);
|
||||
Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs);
|
||||
Task<OrgKey> GetOrgKeyAsync(string orgId);
|
||||
Task<Dictionary<string, OrgKey>> GetOrgKeysAsync();
|
||||
Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
|
||||
Task<byte[]> GetPublicKeyAsync();
|
||||
Task SetPrivateKeyAsync(string encPrivateKey);
|
||||
Task<byte[]> GetPrivateKeyAsync();
|
||||
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
|
||||
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
||||
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
|
||||
Task<PinKey> MakePinKeyAsync(string pin, string salt, KdfConfig config);
|
||||
// Task<UserKey> DecryptUserKeyWithPin(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null);
|
||||
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
||||
// TODO(Jake): This isn't used, delete?
|
||||
Task ClearKeysAsync(string userId = null);
|
||||
Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null);
|
||||
Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null);
|
||||
Task<int> RandomNumberAsync(int min, int max);
|
||||
Task<string> RandomStringAsync(int length);
|
||||
|
||||
Task ClearEncKeyAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearKeyAsync(string userId = null);
|
||||
Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearKeysAsync(string userId = null);
|
||||
Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null);
|
||||
Task ClearPinProtectedKeyAsync(string userId = null);
|
||||
void ClearCache();
|
||||
Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key);
|
||||
@@ -36,31 +53,18 @@ namespace Bit.Core.Abstractions
|
||||
Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null);
|
||||
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
|
||||
Task<SymmetricCryptoKey> GetEncKeyAsync(SymmetricCryptoKey key = null);
|
||||
Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null);
|
||||
Task<SymmetricCryptoKey> GetKeyAsync(string userId = null);
|
||||
Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId);
|
||||
Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync();
|
||||
Task<byte[]> GetPrivateKeyAsync();
|
||||
Task<byte[]> GetPublicKeyAsync();
|
||||
Task<bool> HasEncKeyAsync();
|
||||
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
||||
Task<bool> HasKeyAsync(string userId = null);
|
||||
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
|
||||
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfConfig config);
|
||||
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfConfig config, EncString protectedKeyEs = null);
|
||||
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null);
|
||||
Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfConfig config);
|
||||
// TODO(Jake): This isn't used, delete
|
||||
Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync();
|
||||
Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial);
|
||||
Task<int> RandomNumberAsync(int min, int max);
|
||||
Task<string> RandomStringAsync(int length);
|
||||
Task<Tuple<SymmetricCryptoKey, EncString>> RemakeEncKeyAsync(SymmetricCryptoKey key);
|
||||
Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null);
|
||||
Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null);
|
||||
Task SetEncKeyAsync(string encKey);
|
||||
Task SetEncPrivateKeyAsync(string encPrivateKey);
|
||||
Task SetKeyAsync(SymmetricCryptoKey key);
|
||||
Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs);
|
||||
Task ToggleKeyAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,4 +88,18 @@ namespace Bit.Core.Models.Domain
|
||||
: base(key, encType)
|
||||
{ }
|
||||
}
|
||||
|
||||
public class PinKey : SymmetricCryptoKey
|
||||
{
|
||||
public PinKey(byte[] key, EncryptionType? encType = null)
|
||||
: base(key, encType)
|
||||
{ }
|
||||
}
|
||||
|
||||
public class OrgKey : SymmetricCryptoKey
|
||||
{
|
||||
public OrgKey(byte[] key, EncryptionType? encType = null)
|
||||
: base(key, encType)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,7 +505,7 @@ namespace Bit.Core.Services
|
||||
catch { }
|
||||
}
|
||||
|
||||
await _cryptoService.SetEncPrivateKeyAsync(tokenResponse.PrivateKey);
|
||||
await _cryptoService.SetPrivateKeyAsync(tokenResponse.PrivateKey);
|
||||
}
|
||||
else if (tokenResponse.KeyConnectorUrl != null)
|
||||
{
|
||||
|
||||
@@ -24,9 +24,9 @@ namespace Bit.Core.Services
|
||||
private string _passwordHash;
|
||||
private byte[] _publicKey;
|
||||
private byte[] _privateKey;
|
||||
private Dictionary<string, SymmetricCryptoKey> _orgKeys;
|
||||
private Dictionary<string, OrgKey> _orgKeys;
|
||||
private Task<SymmetricCryptoKey> _getEncKeysTask;
|
||||
private Task<Dictionary<string, SymmetricCryptoKey>> _getOrgKeysTask;
|
||||
private Task<Dictionary<string, OrgKey>> _getOrgKeysTask;
|
||||
|
||||
public CryptoService(
|
||||
IStateService stateService,
|
||||
@@ -79,11 +79,38 @@ namespace Bit.Core.Services
|
||||
return masterKey;
|
||||
}
|
||||
|
||||
public async Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig)
|
||||
{
|
||||
return await MakeKeyAsync(password, email, kdfConfig) as MasterKey;
|
||||
}
|
||||
|
||||
public async Task ClearMasterKeyAsync(string userId = null)
|
||||
{
|
||||
await _stateService.SetMasterKeyAsync(null, userId);
|
||||
}
|
||||
|
||||
public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKey(UserKey key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new Exception("No key provided");
|
||||
}
|
||||
|
||||
var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||
return await BuildProtectedSymmetricKey<SymmetricCryptoKey>(key, newSymKey);
|
||||
}
|
||||
|
||||
public async Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKey(OrgKey key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new Exception("No key provided");
|
||||
}
|
||||
|
||||
var newSymKey = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||
return await BuildProtectedSymmetricKey<SymmetricCryptoKey>(key, newSymKey);
|
||||
}
|
||||
|
||||
public async Task SetPasswordHashAsync(string keyHash)
|
||||
{
|
||||
_passwordHash = keyHash;
|
||||
@@ -132,6 +159,292 @@ namespace Bit.Core.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs)
|
||||
{
|
||||
var orgKeys = orgs.ToDictionary(org => org.Id, org => org.Key);
|
||||
_orgKeys = null;
|
||||
await _stateService.SetOrgKeysEncryptedAsync(orgKeys);
|
||||
}
|
||||
|
||||
public async Task<OrgKey> GetOrgKeyAsync(string orgId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(orgId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var orgKeys = await GetOrgKeysAsync();
|
||||
if (orgKeys == null || !orgKeys.ContainsKey(orgId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return orgKeys[orgId];
|
||||
}
|
||||
|
||||
public Task<Dictionary<string, OrgKey>> GetOrgKeysAsync()
|
||||
{
|
||||
if (_orgKeys != null && _orgKeys.Count > 0)
|
||||
{
|
||||
return Task.FromResult(_orgKeys);
|
||||
}
|
||||
if (_getOrgKeysTask != null && !_getOrgKeysTask.IsCompleted && !_getOrgKeysTask.IsFaulted)
|
||||
{
|
||||
return _getOrgKeysTask;
|
||||
}
|
||||
async Task<Dictionary<string, OrgKey>> doTask()
|
||||
{
|
||||
try
|
||||
{
|
||||
var encOrgKeys = await _stateService.GetOrgKeysEncryptedAsync();
|
||||
if (encOrgKeys == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var orgKeys = new Dictionary<string, OrgKey>();
|
||||
var setKey = false;
|
||||
foreach (var org in encOrgKeys)
|
||||
{
|
||||
var decValue = await RsaDecryptAsync(org.Value);
|
||||
orgKeys.Add(org.Key, new OrgKey(decValue));
|
||||
setKey = true;
|
||||
}
|
||||
|
||||
if (setKey)
|
||||
{
|
||||
_orgKeys = orgKeys;
|
||||
}
|
||||
return _orgKeys;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_getOrgKeysTask = null;
|
||||
}
|
||||
}
|
||||
_getOrgKeysTask = doTask();
|
||||
return _getOrgKeysTask;
|
||||
}
|
||||
|
||||
|
||||
public async Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null)
|
||||
{
|
||||
_orgKeys = null;
|
||||
if (!memoryOnly)
|
||||
{
|
||||
await _stateService.SetOrgKeysEncryptedAsync(null, userId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<byte[]> GetPublicKeyAsync()
|
||||
{
|
||||
if (_publicKey != null)
|
||||
{
|
||||
return _publicKey;
|
||||
}
|
||||
var privateKey = await GetPrivateKeyAsync();
|
||||
if (privateKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
_publicKey = await _cryptoFunctionService.RsaExtractPublicKeyAsync(privateKey);
|
||||
return _publicKey;
|
||||
}
|
||||
|
||||
public async Task SetPrivateKeyAsync(string encPrivateKey)
|
||||
{
|
||||
if (encPrivateKey == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await _stateService.SetPrivateKeyEncryptedAsync(encPrivateKey);
|
||||
_privateKey = null;
|
||||
}
|
||||
|
||||
public async Task<byte[]> GetPrivateKeyAsync()
|
||||
{
|
||||
if (_privateKey != null)
|
||||
{
|
||||
return _privateKey;
|
||||
}
|
||||
var encPrivateKey = await _stateService.GetPrivateKeyEncryptedAsync();
|
||||
if (encPrivateKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
_privateKey = await DecryptToBytesAsync(new EncString(encPrivateKey), null);
|
||||
return _privateKey;
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null)
|
||||
{
|
||||
if (publicKey == null)
|
||||
{
|
||||
publicKey = await GetPublicKeyAsync();
|
||||
}
|
||||
if (publicKey == null)
|
||||
{
|
||||
throw new Exception("No public key available.");
|
||||
}
|
||||
var keyFingerprint = await _cryptoFunctionService.HashAsync(publicKey, CryptoHashAlgorithm.Sha256);
|
||||
var userFingerprint = await _cryptoFunctionService.HkdfExpandAsync(keyFingerprint, Encoding.UTF8.GetBytes(userId), 32, HkdfAlgorithm.Sha256);
|
||||
return HashPhrase(userFingerprint);
|
||||
}
|
||||
|
||||
public async Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null)
|
||||
{
|
||||
var keyPair = await _cryptoFunctionService.RsaGenerateKeyPairAsync(2048);
|
||||
var publicB64 = Convert.ToBase64String(keyPair.Item1);
|
||||
var privateEnc = await EncryptAsync(keyPair.Item2, key);
|
||||
return new Tuple<string, EncString>(publicB64, privateEnc);
|
||||
}
|
||||
|
||||
public async Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null)
|
||||
{
|
||||
_publicKey = _privateKey = null;
|
||||
if (!memoryOnly)
|
||||
{
|
||||
await _stateService.SetPrivateKeyEncryptedAsync(null, userId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<PinKey> MakePinKeyAsync(string pin, string salt, KdfConfig config)
|
||||
{
|
||||
var pinKey = await MakeKeyAsync(pin, salt, config);
|
||||
return await StretchKeyAsync(pinKey) as PinKey;
|
||||
}
|
||||
|
||||
// public async Task<UserKey> DecryptUserKeyWithPin(string pin, string salt, KdfConfig kdfConfig, EncString pinProtectedUserKey = null)
|
||||
// {
|
||||
// pinProtectedUserKey ??= await _stateService.GetUserKeyPinAsync();
|
||||
// pinProtectedUserKey ??= await _stateService.GetUserKeyPinEphemeralAsync();
|
||||
// if (pinProtectedUserKey == null)
|
||||
// {
|
||||
// throw new Exception("No PIN protected user key found.");
|
||||
// }
|
||||
// var pinKey = await MakePinKeyAsync(pin, salt, kdfConfig);
|
||||
// var userKey = await DecryptToBytesAsync(pinProtectedUserKey, pinKey);
|
||||
// return new UserKey(userKey);
|
||||
// }
|
||||
|
||||
public async Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial)
|
||||
{
|
||||
var sendKey = await _cryptoFunctionService.HkdfAsync(keyMaterial, "bitwarden-send", "send", 64, HkdfAlgorithm.Sha256);
|
||||
return new SymmetricCryptoKey(sendKey);
|
||||
}
|
||||
|
||||
// TODO(Jake): This isn't used, delete?
|
||||
public async Task ClearKeysAsync(string userId = null)
|
||||
{
|
||||
await Task.WhenAll(new Task[]
|
||||
{
|
||||
ClearUserKeyAsync(userId),
|
||||
ClearPasswordHashAsync(userId),
|
||||
ClearOrgKeysAsync(false, userId),
|
||||
ClearKeyPairAsync(false, userId),
|
||||
// TODO(Jake): replace with ClearPinKeys
|
||||
ClearPinProtectedKeyAsync(userId)
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null)
|
||||
{
|
||||
if (publicKey == null)
|
||||
{
|
||||
publicKey = await GetPublicKeyAsync();
|
||||
}
|
||||
if (publicKey == null)
|
||||
{
|
||||
throw new Exception("Public key unavailable.");
|
||||
}
|
||||
var encBytes = await _cryptoFunctionService.RsaEncryptAsync(data, publicKey, CryptoHashAlgorithm.Sha1);
|
||||
return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Convert.ToBase64String(encBytes));
|
||||
}
|
||||
|
||||
public async Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null)
|
||||
{
|
||||
var headerPieces = encValue.Split('.');
|
||||
EncryptionType? encType = null;
|
||||
string[] encPieces = null;
|
||||
|
||||
if (headerPieces.Length == 1)
|
||||
{
|
||||
encType = EncryptionType.Rsa2048_OaepSha256_B64;
|
||||
encPieces = new string[] { headerPieces[0] };
|
||||
}
|
||||
else if (headerPieces.Length == 2 && Enum.TryParse(headerPieces[0], out EncryptionType type))
|
||||
{
|
||||
encType = type;
|
||||
encPieces = headerPieces[1].Split('|');
|
||||
}
|
||||
|
||||
if (!encType.HasValue)
|
||||
{
|
||||
throw new Exception("encType unavailable.");
|
||||
}
|
||||
if (encPieces == null || encPieces.Length == 0)
|
||||
{
|
||||
throw new Exception("encPieces unavailable.");
|
||||
}
|
||||
|
||||
var data = Convert.FromBase64String(encPieces[0]);
|
||||
|
||||
if (privateKey is null)
|
||||
{
|
||||
privateKey = await GetPrivateKeyAsync();
|
||||
}
|
||||
|
||||
if (privateKey == null)
|
||||
{
|
||||
throw new Exception("No private key.");
|
||||
}
|
||||
|
||||
var alg = CryptoHashAlgorithm.Sha1;
|
||||
switch (encType.Value)
|
||||
{
|
||||
case EncryptionType.Rsa2048_OaepSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
|
||||
alg = CryptoHashAlgorithm.Sha256;
|
||||
break;
|
||||
case EncryptionType.Rsa2048_OaepSha1_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
|
||||
break;
|
||||
default:
|
||||
throw new Exception("encType unavailable.");
|
||||
}
|
||||
|
||||
return await _cryptoFunctionService.RsaDecryptAsync(data, privateKey, alg);
|
||||
}
|
||||
|
||||
public async Task<int> RandomNumberAsync(int min, int max)
|
||||
{
|
||||
// Make max inclusive
|
||||
max = max + 1;
|
||||
|
||||
var diff = (long)max - min;
|
||||
var upperBound = uint.MaxValue / diff * diff;
|
||||
uint ui;
|
||||
do
|
||||
{
|
||||
ui = await _cryptoFunctionService.RandomNumberAsync();
|
||||
} while (ui >= upperBound);
|
||||
return (int)(min + (ui % diff));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes random string with length <paramref name="length"/> based on the charset <see cref="RANDOM_STRING_CHARSET"/>
|
||||
/// </summary>
|
||||
public async Task<string> RandomStringAsync(int length)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var randomCharIndex = await RandomNumberAsync(0, RANDOM_STRING_CHARSET.Length - 1);
|
||||
sb.Append(RANDOM_STRING_CHARSET[randomCharIndex]);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task SetKeyAsync(SymmetricCryptoKey key)
|
||||
{
|
||||
@@ -157,22 +470,7 @@ namespace Bit.Core.Services
|
||||
_encKey = null;
|
||||
}
|
||||
|
||||
public async Task SetEncPrivateKeyAsync(string encPrivateKey)
|
||||
{
|
||||
if (encPrivateKey == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await _stateService.SetPrivateKeyEncryptedAsync(encPrivateKey);
|
||||
_privateKey = null;
|
||||
}
|
||||
|
||||
public async Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs)
|
||||
{
|
||||
var orgKeys = orgs.ToDictionary(org => org.Id, org => org.Key);
|
||||
_orgKeys = null;
|
||||
await _stateService.SetOrgKeysEncryptedAsync(orgKeys);
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> GetKeyAsync(string userId = null)
|
||||
{
|
||||
@@ -252,107 +550,10 @@ namespace Bit.Core.Services
|
||||
return _getEncKeysTask;
|
||||
}
|
||||
|
||||
public async Task<byte[]> GetPublicKeyAsync()
|
||||
{
|
||||
if (_publicKey != null)
|
||||
{
|
||||
return _publicKey;
|
||||
}
|
||||
var privateKey = await GetPrivateKeyAsync();
|
||||
if (privateKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
_publicKey = await _cryptoFunctionService.RsaExtractPublicKeyAsync(privateKey);
|
||||
return _publicKey;
|
||||
}
|
||||
|
||||
public async Task<byte[]> GetPrivateKeyAsync()
|
||||
{
|
||||
if (_privateKey != null)
|
||||
{
|
||||
return _privateKey;
|
||||
}
|
||||
var encPrivateKey = await _stateService.GetPrivateKeyEncryptedAsync();
|
||||
if (encPrivateKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
_privateKey = await DecryptToBytesAsync(new EncString(encPrivateKey), null);
|
||||
return _privateKey;
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetFingerprintAsync(string userId, byte[] publicKey = null)
|
||||
{
|
||||
if (publicKey == null)
|
||||
{
|
||||
publicKey = await GetPublicKeyAsync();
|
||||
}
|
||||
if (publicKey == null)
|
||||
{
|
||||
throw new Exception("No public key available.");
|
||||
}
|
||||
var keyFingerprint = await _cryptoFunctionService.HashAsync(publicKey, CryptoHashAlgorithm.Sha256);
|
||||
var userFingerprint = await _cryptoFunctionService.HkdfExpandAsync(keyFingerprint, Encoding.UTF8.GetBytes(userId), 32, HkdfAlgorithm.Sha256);
|
||||
return HashPhrase(userFingerprint);
|
||||
}
|
||||
|
||||
public Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync()
|
||||
{
|
||||
if (_orgKeys != null && _orgKeys.Count > 0)
|
||||
{
|
||||
return Task.FromResult(_orgKeys);
|
||||
}
|
||||
if (_getOrgKeysTask != null && !_getOrgKeysTask.IsCompleted && !_getOrgKeysTask.IsFaulted)
|
||||
{
|
||||
return _getOrgKeysTask;
|
||||
}
|
||||
async Task<Dictionary<string, SymmetricCryptoKey>> doTask()
|
||||
{
|
||||
try
|
||||
{
|
||||
var encOrgKeys = await _stateService.GetOrgKeysEncryptedAsync();
|
||||
if (encOrgKeys == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var orgKeys = new Dictionary<string, SymmetricCryptoKey>();
|
||||
var setKey = false;
|
||||
foreach (var org in encOrgKeys)
|
||||
{
|
||||
var decValue = await RsaDecryptAsync(org.Value);
|
||||
orgKeys.Add(org.Key, new SymmetricCryptoKey(decValue));
|
||||
setKey = true;
|
||||
}
|
||||
|
||||
if (setKey)
|
||||
{
|
||||
_orgKeys = orgKeys;
|
||||
}
|
||||
return _orgKeys;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_getOrgKeysTask = null;
|
||||
}
|
||||
}
|
||||
_getOrgKeysTask = doTask();
|
||||
return _getOrgKeysTask;
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(orgId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var orgKeys = await GetOrgKeysAsync();
|
||||
if (orgKeys == null || !orgKeys.ContainsKey(orgId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return orgKeys[orgId];
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> HasKeyAsync(string userId = null)
|
||||
@@ -384,23 +585,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClearKeyPairAsync(bool memoryOnly = false, string userId = null)
|
||||
{
|
||||
_publicKey = _privateKey = null;
|
||||
if (!memoryOnly)
|
||||
{
|
||||
await _stateService.SetPrivateKeyEncryptedAsync(null, userId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClearOrgKeysAsync(bool memoryOnly = false, string userId = null)
|
||||
{
|
||||
_orgKeys = null;
|
||||
if (!memoryOnly)
|
||||
{
|
||||
await _stateService.SetOrgKeysEncryptedAsync(null, userId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClearPinProtectedKeyAsync(string userId = null)
|
||||
{
|
||||
@@ -417,18 +602,6 @@ namespace Bit.Core.Services
|
||||
_orgKeys = null;
|
||||
}
|
||||
|
||||
public async Task ClearKeysAsync(string userId = null)
|
||||
{
|
||||
await Task.WhenAll(new Task[]
|
||||
{
|
||||
ClearKeyAsync(userId),
|
||||
ClearPasswordHashAsync(userId),
|
||||
ClearOrgKeysAsync(false, userId),
|
||||
ClearEncKeyAsync(false, userId),
|
||||
ClearKeyPairAsync(false, userId),
|
||||
ClearPinProtectedKeyAsync(userId)
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ToggleKeyAsync()
|
||||
{
|
||||
@@ -504,11 +677,12 @@ namespace Bit.Core.Services
|
||||
}
|
||||
protectedKeyCs = new EncString(pinProtectedKey);
|
||||
}
|
||||
var pinKey = await MakePinKeyAysnc(pin, salt, config);
|
||||
var pinKey = await MakePinKeyAsync(pin, salt, config);
|
||||
var decKey = await DecryptToBytesAsync(protectedKeyCs, pinKey);
|
||||
return new SymmetricCryptoKey(decKey);
|
||||
}
|
||||
|
||||
// TODO(Jake): This isn't used, delete
|
||||
public async Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync()
|
||||
{
|
||||
var shareKey = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||
@@ -517,25 +691,8 @@ namespace Bit.Core.Services
|
||||
return new Tuple<EncString, SymmetricCryptoKey>(encShareKey, new SymmetricCryptoKey(shareKey));
|
||||
}
|
||||
|
||||
public async Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null)
|
||||
{
|
||||
var keyPair = await _cryptoFunctionService.RsaGenerateKeyPairAsync(2048);
|
||||
var publicB64 = Convert.ToBase64String(keyPair.Item1);
|
||||
var privateEnc = await EncryptAsync(keyPair.Item2, key);
|
||||
return new Tuple<string, EncString>(publicB64, privateEnc);
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfConfig config)
|
||||
{
|
||||
var pinKey = await MakeKeyAsync(pin, salt, config);
|
||||
return await StretchKeyAsync(pinKey);
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> MakeSendKeyAsync(byte[] keyMaterial)
|
||||
{
|
||||
var sendKey = await _cryptoFunctionService.HkdfAsync(keyMaterial, "bitwarden-send", "send", 64, HkdfAlgorithm.Sha256);
|
||||
return new SymmetricCryptoKey(sendKey);
|
||||
}
|
||||
|
||||
public async Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization)
|
||||
{
|
||||
@@ -556,13 +713,13 @@ namespace Bit.Core.Services
|
||||
{
|
||||
var theKey = await GetKeyForEncryptionAsync(key);
|
||||
var encKey = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||
return await BuildEncKeyAsync(theKey, encKey);
|
||||
return await BuildProtectedSymmetricKey<SymmetricCryptoKey>(theKey, encKey);
|
||||
}
|
||||
|
||||
public async Task<Tuple<SymmetricCryptoKey, EncString>> RemakeEncKeyAsync(SymmetricCryptoKey key)
|
||||
{
|
||||
var encKey = await GetEncKeyAsync();
|
||||
return await BuildEncKeyAsync(key, encKey.Key);
|
||||
return await BuildProtectedSymmetricKey<SymmetricCryptoKey>(key, encKey.Key);
|
||||
}
|
||||
|
||||
public async Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null)
|
||||
@@ -606,19 +763,6 @@ namespace Bit.Core.Services
|
||||
return new EncByteArray(encBytes);
|
||||
}
|
||||
|
||||
public async Task<EncString> RsaEncryptAsync(byte[] data, byte[] publicKey = null)
|
||||
{
|
||||
if (publicKey == null)
|
||||
{
|
||||
publicKey = await GetPublicKeyAsync();
|
||||
}
|
||||
if (publicKey == null)
|
||||
{
|
||||
throw new Exception("Public key unavailable.");
|
||||
}
|
||||
var encBytes = await _cryptoFunctionService.RsaEncryptAsync(data, publicKey, CryptoHashAlgorithm.Sha1);
|
||||
return new EncString(EncryptionType.Rsa2048_OaepSha1_B64, Convert.ToBase64String(encBytes));
|
||||
}
|
||||
|
||||
public async Task<byte[]> DecryptToBytesAsync(EncString encString, SymmetricCryptoKey key = null)
|
||||
{
|
||||
@@ -673,36 +817,6 @@ namespace Bit.Core.Services
|
||||
return await AesDecryptToBytesAsync(encType, ctBytes, ivBytes, macBytes, key);
|
||||
}
|
||||
|
||||
public async Task<int> RandomNumberAsync(int min, int max)
|
||||
{
|
||||
// Make max inclusive
|
||||
max = max + 1;
|
||||
|
||||
var diff = (long)max - min;
|
||||
var upperBound = uint.MaxValue / diff * diff;
|
||||
uint ui;
|
||||
do
|
||||
{
|
||||
ui = await _cryptoFunctionService.RandomNumberAsync();
|
||||
} while (ui >= upperBound);
|
||||
return (int)(min + (ui % diff));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes random string with length <paramref name="length"/> based on the charset <see cref="RANDOM_STRING_CHARSET"/>
|
||||
/// </summary>
|
||||
public async Task<string> RandomStringAsync(int length)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var randomCharIndex = await RandomNumberAsync(0, RANDOM_STRING_CHARSET.Length - 1);
|
||||
sb.Append(RANDOM_STRING_CHARSET[randomCharIndex]);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
@@ -818,60 +932,6 @@ namespace Bit.Core.Services
|
||||
return await _cryptoFunctionService.AesDecryptAsync(data, iv, theKey.EncKey);
|
||||
}
|
||||
|
||||
public async Task<byte[]> RsaDecryptAsync(string encValue, byte[] privateKey = null)
|
||||
{
|
||||
var headerPieces = encValue.Split('.');
|
||||
EncryptionType? encType = null;
|
||||
string[] encPieces = null;
|
||||
|
||||
if (headerPieces.Length == 1)
|
||||
{
|
||||
encType = EncryptionType.Rsa2048_OaepSha256_B64;
|
||||
encPieces = new string[] { headerPieces[0] };
|
||||
}
|
||||
else if (headerPieces.Length == 2 && Enum.TryParse(headerPieces[0], out EncryptionType type))
|
||||
{
|
||||
encType = type;
|
||||
encPieces = headerPieces[1].Split('|');
|
||||
}
|
||||
|
||||
if (!encType.HasValue)
|
||||
{
|
||||
throw new Exception("encType unavailable.");
|
||||
}
|
||||
if (encPieces == null || encPieces.Length == 0)
|
||||
{
|
||||
throw new Exception("encPieces unavailable.");
|
||||
}
|
||||
|
||||
var data = Convert.FromBase64String(encPieces[0]);
|
||||
|
||||
if (privateKey is null)
|
||||
{
|
||||
privateKey = await GetPrivateKeyAsync();
|
||||
}
|
||||
|
||||
if (privateKey == null)
|
||||
{
|
||||
throw new Exception("No private key.");
|
||||
}
|
||||
|
||||
var alg = CryptoHashAlgorithm.Sha1;
|
||||
switch (encType.Value)
|
||||
{
|
||||
case EncryptionType.Rsa2048_OaepSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64:
|
||||
alg = CryptoHashAlgorithm.Sha256;
|
||||
break;
|
||||
case EncryptionType.Rsa2048_OaepSha1_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64:
|
||||
break;
|
||||
default:
|
||||
throw new Exception("encType unavailable.");
|
||||
}
|
||||
|
||||
return await _cryptoFunctionService.RsaDecryptAsync(data, privateKey, alg);
|
||||
}
|
||||
|
||||
private async Task<UserKey> GetUserKeyWithLegacySupport(string userId = null)
|
||||
{
|
||||
@@ -948,8 +1008,8 @@ namespace Bit.Core.Services
|
||||
return phrase;
|
||||
}
|
||||
|
||||
private async Task<Tuple<SymmetricCryptoKey, EncString>> BuildEncKeyAsync(SymmetricCryptoKey key,
|
||||
byte[] encKey)
|
||||
private async Task<Tuple<T, EncString>> BuildProtectedSymmetricKey<T>(SymmetricCryptoKey key,
|
||||
byte[] encKey) where T : SymmetricCryptoKey
|
||||
{
|
||||
EncString encKeyEnc = null;
|
||||
if (key.Key.Length == 32)
|
||||
@@ -965,7 +1025,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
throw new Exception("Invalid key size.");
|
||||
}
|
||||
return new Tuple<SymmetricCryptoKey, EncString>(new SymmetricCryptoKey(encKey), encKeyEnc);
|
||||
return new Tuple<T, EncString>(new SymmetricCryptoKey(encKey) as T, encKeyEnc);
|
||||
}
|
||||
|
||||
private class EncryptedObject
|
||||
|
||||
@@ -328,7 +328,7 @@ namespace Bit.Core.Services
|
||||
return;
|
||||
}
|
||||
await _cryptoService.SetEncKeyAsync(response.Key);
|
||||
await _cryptoService.SetEncPrivateKeyAsync(response.PrivateKey);
|
||||
await _cryptoService.SetPrivateKeyAsync(response.PrivateKey);
|
||||
await _cryptoService.SetOrgKeysAsync(response.Organizations);
|
||||
await _stateService.SetSecurityStampAsync(response.SecurityStamp);
|
||||
var organizations = response.Organizations.ToDictionary(o => o.Id, o => new OrganizationData(o));
|
||||
|
||||
Reference in New Issue
Block a user