1
0
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:
Jacob Fink
2023-07-17 21:48:50 -04:00
parent c1619536aa
commit 69d38d4d75
8 changed files with 362 additions and 284 deletions

View File

@@ -332,7 +332,7 @@ namespace Bit.App.Pages
var protectedPin = await _stateService.GetProtectedPinAsync(); var protectedPin = await _stateService.GetProtectedPinAsync();
var encKey = await _cryptoService.GetEncKeyAsync(key); var encKey = await _cryptoService.GetEncKeyAsync(key);
var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey); 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)); await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey));
} }

View File

@@ -207,7 +207,7 @@ namespace Bit.App.Pages
await _cryptoService.SetKeyAsync(key); await _cryptoService.SetKeyAsync(key);
await _cryptoService.SetPasswordHashAsync(localMasterPasswordHash); await _cryptoService.SetPasswordHashAsync(localMasterPasswordHash);
await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString); await _cryptoService.SetEncKeyAsync(encKey.Item2.EncryptedString);
await _cryptoService.SetEncPrivateKeyAsync(keys.Item2.EncryptedString); await _cryptoService.SetPrivateKeyAsync(keys.Item2.EncryptedString);
if (ResetPasswordAutoEnroll) if (ResetPasswordAutoEnroll)
{ {

View File

@@ -437,7 +437,7 @@ namespace Bit.App.Pages
var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile)); var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile));
var email = await _stateService.GetEmailAsync(); 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 key = await _cryptoService.GetKeyAsync();
var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey); var pinProtectedKey = await _cryptoService.EncryptAsync(key.Key, pinKey);

View File

@@ -16,17 +16,34 @@ namespace Bit.Core.Abstractions
Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null); Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null);
Task SetMasterKeyAsync(MasterKey masterKey, string userId = null); Task SetMasterKeyAsync(MasterKey masterKey, string userId = null);
Task<MasterKey> GetMasterKeyAsync(string userId = null); Task<MasterKey> GetMasterKeyAsync(string userId = null);
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
Task ClearMasterKeyAsync(string userId = null); Task ClearMasterKeyAsync(string userId = null);
Task SetPasswordHashAsync(string keyHash); Task SetPasswordHashAsync(string keyHash);
Task<string> GetPasswordHashAsync(); Task<string> GetPasswordHashAsync();
Task ClearPasswordHashAsync(string userId = null); Task ClearPasswordHashAsync(string userId = null);
Task<bool> CompareAndUpdatePasswordHashAsync(string masterPassword, SymmetricCryptoKey key); 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 ClearEncKeyAsync(bool memoryOnly = false, string userId = null);
Task ClearKeyAsync(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); Task ClearPinProtectedKeyAsync(string userId = null);
void ClearCache(); void ClearCache();
Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key); Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key);
@@ -36,31 +53,18 @@ namespace Bit.Core.Abstractions
Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null); Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null);
Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null); Task<EncByteArray> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null);
Task<SymmetricCryptoKey> GetEncKeyAsync(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> GetKeyAsync(string userId = null);
Task<SymmetricCryptoKey> GetOrgKeyAsync(string orgId);
Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync();
Task<byte[]> GetPrivateKeyAsync();
Task<byte[]> GetPublicKeyAsync();
Task<bool> HasEncKeyAsync(); Task<bool> HasEncKeyAsync();
Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization); Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
Task<bool> HasKeyAsync(string userId = null); Task<bool> HasKeyAsync(string userId = null);
Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key); Task<Tuple<SymmetricCryptoKey, EncString>> MakeEncKeyAsync(SymmetricCryptoKey key);
Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfConfig config); Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt, KdfConfig config);
Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfConfig config, EncString protectedKeyEs = null); Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt, KdfConfig config, EncString protectedKeyEs = null);
Task<Tuple<string, EncString>> MakeKeyPairAsync(SymmetricCryptoKey key = null); // TODO(Jake): This isn't used, delete
Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfConfig config);
Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync(); 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<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 SetEncKeyAsync(string encKey);
Task SetEncPrivateKeyAsync(string encPrivateKey);
Task SetKeyAsync(SymmetricCryptoKey key); Task SetKeyAsync(SymmetricCryptoKey key);
Task SetOrgKeysAsync(IEnumerable<ProfileOrganizationResponse> orgs);
Task ToggleKeyAsync(); Task ToggleKeyAsync();
} }
} }

View File

@@ -88,4 +88,18 @@ namespace Bit.Core.Models.Domain
: base(key, encType) : 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)
{ }
}
} }

View File

@@ -505,7 +505,7 @@ namespace Bit.Core.Services
catch { } catch { }
} }
await _cryptoService.SetEncPrivateKeyAsync(tokenResponse.PrivateKey); await _cryptoService.SetPrivateKeyAsync(tokenResponse.PrivateKey);
} }
else if (tokenResponse.KeyConnectorUrl != null) else if (tokenResponse.KeyConnectorUrl != null)
{ {

View File

@@ -24,9 +24,9 @@ namespace Bit.Core.Services
private string _passwordHash; private string _passwordHash;
private byte[] _publicKey; private byte[] _publicKey;
private byte[] _privateKey; private byte[] _privateKey;
private Dictionary<string, SymmetricCryptoKey> _orgKeys; private Dictionary<string, OrgKey> _orgKeys;
private Task<SymmetricCryptoKey> _getEncKeysTask; private Task<SymmetricCryptoKey> _getEncKeysTask;
private Task<Dictionary<string, SymmetricCryptoKey>> _getOrgKeysTask; private Task<Dictionary<string, OrgKey>> _getOrgKeysTask;
public CryptoService( public CryptoService(
IStateService stateService, IStateService stateService,
@@ -79,11 +79,38 @@ namespace Bit.Core.Services
return masterKey; 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) public async Task ClearMasterKeyAsync(string userId = null)
{ {
await _stateService.SetMasterKeyAsync(null, userId); 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) public async Task SetPasswordHashAsync(string keyHash)
{ {
_passwordHash = keyHash; _passwordHash = keyHash;
@@ -132,6 +159,292 @@ namespace Bit.Core.Services
return false; 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) public async Task SetKeyAsync(SymmetricCryptoKey key)
{ {
@@ -157,22 +470,7 @@ namespace Bit.Core.Services
_encKey = null; _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) public async Task<SymmetricCryptoKey> GetKeyAsync(string userId = null)
{ {
@@ -252,107 +550,10 @@ namespace Bit.Core.Services
return _getEncKeysTask; 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) 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) public async Task ClearPinProtectedKeyAsync(string userId = null)
{ {
@@ -417,18 +602,6 @@ namespace Bit.Core.Services
_orgKeys = null; _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() public async Task ToggleKeyAsync()
{ {
@@ -504,11 +677,12 @@ namespace Bit.Core.Services
} }
protectedKeyCs = new EncString(pinProtectedKey); protectedKeyCs = new EncString(pinProtectedKey);
} }
var pinKey = await MakePinKeyAysnc(pin, salt, config); var pinKey = await MakePinKeyAsync(pin, salt, config);
var decKey = await DecryptToBytesAsync(protectedKeyCs, pinKey); var decKey = await DecryptToBytesAsync(protectedKeyCs, pinKey);
return new SymmetricCryptoKey(decKey); return new SymmetricCryptoKey(decKey);
} }
// TODO(Jake): This isn't used, delete
public async Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync() public async Task<Tuple<EncString, SymmetricCryptoKey>> MakeShareKeyAsync()
{ {
var shareKey = await _cryptoFunctionService.RandomBytesAsync(64); var shareKey = await _cryptoFunctionService.RandomBytesAsync(64);
@@ -517,25 +691,8 @@ namespace Bit.Core.Services
return new Tuple<EncString, SymmetricCryptoKey>(encShareKey, new SymmetricCryptoKey(shareKey)); 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) 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 theKey = await GetKeyForEncryptionAsync(key);
var encKey = await _cryptoFunctionService.RandomBytesAsync(64); 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) public async Task<Tuple<SymmetricCryptoKey, EncString>> RemakeEncKeyAsync(SymmetricCryptoKey key)
{ {
var encKey = await GetEncKeyAsync(); 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) public async Task<EncString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null)
@@ -606,19 +763,6 @@ namespace Bit.Core.Services
return new EncByteArray(encBytes); 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) 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); 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 // Helpers
@@ -818,60 +932,6 @@ namespace Bit.Core.Services
return await _cryptoFunctionService.AesDecryptAsync(data, iv, theKey.EncKey); 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) private async Task<UserKey> GetUserKeyWithLegacySupport(string userId = null)
{ {
@@ -948,8 +1008,8 @@ namespace Bit.Core.Services
return phrase; return phrase;
} }
private async Task<Tuple<SymmetricCryptoKey, EncString>> BuildEncKeyAsync(SymmetricCryptoKey key, private async Task<Tuple<T, EncString>> BuildProtectedSymmetricKey<T>(SymmetricCryptoKey key,
byte[] encKey) byte[] encKey) where T : SymmetricCryptoKey
{ {
EncString encKeyEnc = null; EncString encKeyEnc = null;
if (key.Key.Length == 32) if (key.Key.Length == 32)
@@ -965,7 +1025,7 @@ namespace Bit.Core.Services
{ {
throw new Exception("Invalid key size."); 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 private class EncryptedObject

View File

@@ -328,7 +328,7 @@ namespace Bit.Core.Services
return; return;
} }
await _cryptoService.SetEncKeyAsync(response.Key); await _cryptoService.SetEncKeyAsync(response.Key);
await _cryptoService.SetEncPrivateKeyAsync(response.PrivateKey); await _cryptoService.SetPrivateKeyAsync(response.PrivateKey);
await _cryptoService.SetOrgKeysAsync(response.Organizations); await _cryptoService.SetOrgKeysAsync(response.Organizations);
await _stateService.SetSecurityStampAsync(response.SecurityStamp); await _stateService.SetSecurityStampAsync(response.SecurityStamp);
var organizations = response.Organizations.ToDictionary(o => o.Id, o => new OrganizationData(o)); var organizations = response.Organizations.ToDictionary(o => o.Id, o => new OrganizationData(o));