mirror of
https://github.com/bitwarden/android.git
synced 2024-12-24 18:08:26 +03:00
crypto service implementation
This commit is contained in:
parent
3f5115728b
commit
f0eca137ef
6 changed files with 630 additions and 7 deletions
|
@ -21,5 +21,6 @@ namespace Bit.Core.Abstractions
|
|||
Task<byte[]> RsaExtractPublicKeyAsync(byte[] privateKey);
|
||||
Task<Tuple<byte[], byte[]>> RsaGenerateKeyPairAsync(int length);
|
||||
Task<byte[]> RandomBytesAsync(int length);
|
||||
Task<uint> RandomNumberAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
public static class Constants
|
||||
{
|
||||
public static string LockOptionKey = "lockOption";
|
||||
public static string PinProtectedKey = "pinProtectedKey";
|
||||
}
|
||||
}
|
||||
|
|
7
src/Core/Enums/KdfType.cs
Normal file
7
src/Core/Enums/KdfType.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum KdfType : byte
|
||||
{
|
||||
PBKDF2_SHA256 = 0
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ namespace Bit.Core.Models.Domain
|
|||
{
|
||||
private string _decryptedValue;
|
||||
|
||||
public CipherString(EncryptionType encryptionType, string data, string iv, string mac)
|
||||
public CipherString(EncryptionType encryptionType, string data, string iv = null, string mac = null)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(data))
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ using Bit.Core.Models.Response;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
|
@ -134,11 +135,12 @@ namespace Bit.Core.Services
|
|||
var encKeyCipher = new CipherString(encKey);
|
||||
if(encKeyCipher.EncryptionType == EncryptionType.AesCbc256_B64)
|
||||
{
|
||||
// TODO
|
||||
decEncKey = await DecryptToBytesAsync(encKeyCipher, key);
|
||||
}
|
||||
else if(encKeyCipher.EncryptionType == EncryptionType.AesCbc256_HmacSha256_B64)
|
||||
{
|
||||
// TODO
|
||||
var newKey = await StretchKeyAsync(key);
|
||||
decEncKey = await DecryptToBytesAsync(encKeyCipher, newKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -179,7 +181,7 @@ namespace Bit.Core.Services
|
|||
{
|
||||
return null;
|
||||
}
|
||||
// TODO
|
||||
_privateKey = await DecryptToBytesAsync(new CipherString(encPrivateKey), null);
|
||||
return _privateKey;
|
||||
}
|
||||
|
||||
|
@ -194,9 +196,616 @@ namespace Bit.Core.Services
|
|||
throw new Exception("No public key available.");
|
||||
}
|
||||
var keyFingerprint = await _cryptoFunctionService.HashAsync(publicKey, CryptoHashAlgorithm.Sha256);
|
||||
// TODO
|
||||
return null;
|
||||
var userFingerprint = await HkdfExpandAsync(keyFingerprint, Encoding.UTF8.GetBytes(userId), 32);
|
||||
return HashPhrase(userFingerprint);
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, SymmetricCryptoKey>> GetOrgKeysAsync()
|
||||
{
|
||||
if(_orgKeys != null && _orgKeys.Count > 0)
|
||||
{
|
||||
return _orgKeys;
|
||||
}
|
||||
var encOrgKeys = await _storageService.GetAsync<Dictionary<string, string>>(Keys_EncOrgKeys);
|
||||
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;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
var key = await GetKeyAsync();
|
||||
return key != null;
|
||||
}
|
||||
|
||||
public async Task<bool> HasEncKeyAsync()
|
||||
{
|
||||
var encKey = await _storageService.GetAsync<string>(Keys_EncKey);
|
||||
return encKey != null;
|
||||
}
|
||||
|
||||
public async Task ClearKeyAsync()
|
||||
{
|
||||
_key = _legacyEtmKey = null;
|
||||
await _secureStorageService.RemoveAsync(Keys_Key);
|
||||
}
|
||||
|
||||
public async Task ClearKeyHashAsync()
|
||||
{
|
||||
_keyHash = null;
|
||||
await _storageService.RemoveAsync(Keys_KeyHash);
|
||||
}
|
||||
|
||||
public async Task ClearEncKeyAsync(bool memoryOnly = false)
|
||||
{
|
||||
_encKey = null;
|
||||
if(!memoryOnly)
|
||||
{
|
||||
await _storageService.RemoveAsync(Keys_EncKey);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClearKeyPairAsync(bool memoryOnly = false)
|
||||
{
|
||||
_publicKey = _privateKey = null;
|
||||
if(!memoryOnly)
|
||||
{
|
||||
await _storageService.RemoveAsync(Keys_EncPrivateKey);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClearOrgKeysAsync(bool memoryOnly = false)
|
||||
{
|
||||
_orgKeys = null;
|
||||
if(!memoryOnly)
|
||||
{
|
||||
await _storageService.RemoveAsync(Keys_EncOrgKeys);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ClearPinProtectedKeyAsync()
|
||||
{
|
||||
await _storageService.RemoveAsync(Constants.PinProtectedKey);
|
||||
}
|
||||
|
||||
public async Task ClearKeysAsync()
|
||||
{
|
||||
await Task.WhenAll(new Task[]
|
||||
{
|
||||
ClearKeyAsync(),
|
||||
ClearKeyHashAsync(),
|
||||
ClearOrgKeysAsync(),
|
||||
ClearEncKeyAsync(),
|
||||
ClearKeyPairAsync(),
|
||||
ClearPinProtectedKeyAsync()
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ToggleKeyAsync()
|
||||
{
|
||||
var key = await GetKeyAsync();
|
||||
var option = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
|
||||
if(option != null || option == 0)
|
||||
{
|
||||
await ClearKeyAsync();
|
||||
_key = key;
|
||||
return;
|
||||
}
|
||||
await SetKeyAsync(key);
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> MakeKeyAsync(string password, string salt,
|
||||
KdfType? kdf, int? kdfIterations)
|
||||
{
|
||||
byte[] key = null;
|
||||
if(kdf == null || kdf == KdfType.PBKDF2_SHA256)
|
||||
{
|
||||
if(kdfIterations == null)
|
||||
{
|
||||
kdfIterations = 5000;
|
||||
}
|
||||
if(kdfIterations < 5000)
|
||||
{
|
||||
throw new Exception("PBKDF2 iteration minimum is 5000.");
|
||||
}
|
||||
key = await _cryptoFunctionService.Pbkdf2Async(password, salt,
|
||||
CryptoHashAlgorithm.Sha256, kdfIterations.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Unknown kdf.");
|
||||
}
|
||||
return new SymmetricCryptoKey(key);
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> MakeKeyFromPinAsync(string pin, string salt,
|
||||
KdfType kdf, int kdfIterations)
|
||||
{
|
||||
var pinProtectedKey = await _storageService.GetAsync<string>(Constants.PinProtectedKey);
|
||||
if(pinProtectedKey == null)
|
||||
{
|
||||
throw new Exception("No PIN protected key found.");
|
||||
}
|
||||
var protectedKeyCs = new CipherString(pinProtectedKey);
|
||||
var pinKey = await MakePinKeyAysnc(pin, salt, kdf, kdfIterations);
|
||||
var decKey = await DecryptToBytesAsync(protectedKeyCs, pinKey);
|
||||
return new SymmetricCryptoKey(decKey);
|
||||
}
|
||||
|
||||
public async Task<Tuple<CipherString, SymmetricCryptoKey>> MakeShareKeyAsync()
|
||||
{
|
||||
var shareKey = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||
var publicKey = await GetPublicKeyAsync();
|
||||
var encShareKey = await RsaEncryptAsync(shareKey, publicKey);
|
||||
return new Tuple<CipherString, SymmetricCryptoKey>(encShareKey, new SymmetricCryptoKey(shareKey));
|
||||
}
|
||||
|
||||
public async Task<Tuple<string, CipherString>> 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, CipherString>(publicB64, privateEnc);
|
||||
}
|
||||
|
||||
public async Task<SymmetricCryptoKey> MakePinKeyAysnc(string pin, string salt, KdfType kdf, int kdfIterations)
|
||||
{
|
||||
var pinKey = await MakeKeyAsync(pin, salt, kdf, kdfIterations);
|
||||
return await StretchKeyAsync(pinKey);
|
||||
}
|
||||
|
||||
public async Task<string> HashPasswordAsync(string password, SymmetricCryptoKey key)
|
||||
{
|
||||
if(key == null)
|
||||
{
|
||||
key = await GetKeyAsync();
|
||||
}
|
||||
if(password == null || key == null)
|
||||
{
|
||||
throw new Exception("Invalid parameters.");
|
||||
}
|
||||
var hash = await _cryptoFunctionService.Pbkdf2Async(key.Key, password, CryptoHashAlgorithm.Sha256, 1);
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
|
||||
public async Task<Tuple<SymmetricCryptoKey, CipherString>> MakeEncKeyAsync(SymmetricCryptoKey key)
|
||||
{
|
||||
var theKey = await GetKeyForEncryptionAsync(key);
|
||||
var encKey = await _cryptoFunctionService.RandomBytesAsync(64);
|
||||
return await BuildEncKeyAsync(theKey, encKey);
|
||||
}
|
||||
|
||||
public async Task<Tuple<SymmetricCryptoKey, CipherString>> RemakeEncKeyAsync(SymmetricCryptoKey key)
|
||||
{
|
||||
var encKey = await GetEncKeyAsync();
|
||||
return await BuildEncKeyAsync(key, encKey.Key);
|
||||
}
|
||||
|
||||
public async Task<CipherString> EncryptAsync(string plainValue, SymmetricCryptoKey key = null)
|
||||
{
|
||||
if(plainValue == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return await EncryptAsync(Encoding.UTF8.GetBytes(plainValue), key);
|
||||
}
|
||||
|
||||
public async Task<CipherString> EncryptAsync(byte[] plainValue, SymmetricCryptoKey key = null)
|
||||
{
|
||||
if(plainValue == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var encObj = await AesEncryptAsync(plainValue, key);
|
||||
var iv = Convert.ToBase64String(encObj.Iv);
|
||||
var data = Convert.ToBase64String(encObj.Data);
|
||||
var mac = encObj.Mac != null ? Convert.ToBase64String(encObj.Mac) : null;
|
||||
return new CipherString(encObj.Key.EncType, data, iv, mac);
|
||||
}
|
||||
|
||||
public async Task<byte[]> EncryptToBytesAsync(byte[] plainValue, SymmetricCryptoKey key = null)
|
||||
{
|
||||
var encValue = await AesEncryptAsync(plainValue, key);
|
||||
var macLen = 0;
|
||||
if(encValue.Mac != null)
|
||||
{
|
||||
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(encValue.Iv, 0, encBytes, 1, encValue.Iv.Length);
|
||||
if(encValue.Mac != null)
|
||||
{
|
||||
Buffer.BlockCopy(encValue.Mac, 0, encBytes, 1 + encValue.Iv.Length, encValue.Mac.Length);
|
||||
}
|
||||
Buffer.BlockCopy(encValue.Data, 0, encBytes, 1 + encValue.Iv.Length + macLen, encValue.Data.Length);
|
||||
return encBytes;
|
||||
}
|
||||
|
||||
public async Task<CipherString> 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 CipherString(EncryptionType.Rsa2048_OaepSha1_B64, Convert.ToBase64String(encBytes));
|
||||
}
|
||||
|
||||
public async Task<byte[]> DecryptToBytesAsync(CipherString cipherString, SymmetricCryptoKey key = null)
|
||||
{
|
||||
var iv = Convert.FromBase64String(cipherString.Iv);
|
||||
var data = Convert.FromBase64String(cipherString.Data);
|
||||
var mac = !string.IsNullOrWhiteSpace(cipherString.Mac) ? Convert.FromBase64String(cipherString.Mac) : null;
|
||||
return await AesDecryptToBytesAsync(cipherString.EncryptionType, data, iv, mac, key);
|
||||
}
|
||||
|
||||
public async Task<string> DecryptToUtf8Async(CipherString cipherString, SymmetricCryptoKey key = null)
|
||||
{
|
||||
return await AesDecryptToUtf8Async(cipherString.EncryptionType, cipherString.Data,
|
||||
cipherString.Iv, cipherString.Mac, key);
|
||||
}
|
||||
|
||||
public async Task<byte[]> DecryptFromBytesAsync(byte[] encBytes, SymmetricCryptoKey key)
|
||||
{
|
||||
if(encBytes == null)
|
||||
{
|
||||
throw new Exception("no encBytes.");
|
||||
}
|
||||
|
||||
var encType = (EncryptionType)encBytes[0];
|
||||
byte[] ctBytes = null;
|
||||
byte[] ivBytes = null;
|
||||
byte[] macBytes = null;
|
||||
|
||||
switch(encType)
|
||||
{
|
||||
case EncryptionType.AesCbc128_HmacSha256_B64:
|
||||
case EncryptionType.AesCbc256_HmacSha256_B64:
|
||||
if(encBytes.Length < 49) // 1 + 16 + 32 + ctLength
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ivBytes = new ArraySegment<byte>(encBytes, 1, 16).ToArray();
|
||||
macBytes = new ArraySegment<byte>(encBytes, 17, 32).ToArray();
|
||||
ctBytes = new ArraySegment<byte>(encBytes, 49, encBytes.Length - 49).ToArray();
|
||||
break;
|
||||
case EncryptionType.AesCbc256_B64:
|
||||
if(encBytes.Length < 17) // 1 + 16 + ctLength
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ivBytes = new ArraySegment<byte>(encBytes, 1, 16).ToArray();
|
||||
ctBytes = new ArraySegment<byte>(encBytes, 17, encBytes.Length - 17).ToArray();
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
private async Task<EncryptedObject> AesEncryptAsync(byte[] data, SymmetricCryptoKey key)
|
||||
{
|
||||
var obj = new EncryptedObject
|
||||
{
|
||||
Key = await GetKeyForEncryptionAsync(key),
|
||||
Iv = await _cryptoFunctionService.RandomBytesAsync(16)
|
||||
};
|
||||
obj.Data = await _cryptoFunctionService.AesEncryptAsync(data, obj.Iv, obj.Key.EncKey);
|
||||
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);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
private async Task<string> AesDecryptToUtf8Async(EncryptionType encType, string data, string iv, string mac,
|
||||
SymmetricCryptoKey key)
|
||||
{
|
||||
var keyForEnc = await GetKeyForEncryptionAsync(key);
|
||||
var theKey = ResolveLegacyKey(encType, keyForEnc);
|
||||
if(theKey.MacKey != null && mac == null)
|
||||
{
|
||||
// Mac required.
|
||||
return null;
|
||||
}
|
||||
if(theKey.EncType != encType)
|
||||
{
|
||||
// encType unavailable.
|
||||
return null;
|
||||
}
|
||||
|
||||
// "Fast params" conversion
|
||||
var encKey = theKey.EncKey;
|
||||
var dataBytes = Convert.FromBase64String(data);
|
||||
var ivBytes = Convert.FromBase64String(iv);
|
||||
|
||||
var macDataBytes = new byte[ivBytes.Length + dataBytes.Length];
|
||||
Buffer.BlockCopy(ivBytes, 0, macDataBytes, 0, ivBytes.Length);
|
||||
Buffer.BlockCopy(dataBytes, 0, macDataBytes, ivBytes.Length, dataBytes.Length);
|
||||
|
||||
byte[] macKey = null;
|
||||
if(key.MacKey != null)
|
||||
{
|
||||
macKey = key.MacKey;
|
||||
}
|
||||
byte[] macBytes = null;
|
||||
if(mac != null)
|
||||
{
|
||||
macBytes = Convert.FromBase64String(mac);
|
||||
}
|
||||
|
||||
// Compute mac
|
||||
if(macKey != null && macBytes != null)
|
||||
{
|
||||
var computedMac = await _cryptoFunctionService.HmacAsync(macDataBytes, macKey,
|
||||
CryptoHashAlgorithm.Sha256);
|
||||
var macsEqual = await _cryptoFunctionService.CompareAsync(macBytes, computedMac);
|
||||
if(!macsEqual)
|
||||
{
|
||||
// Mac failed
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var decBytes = await _cryptoFunctionService.AesDecryptAsync(dataBytes, ivBytes, encKey);
|
||||
return Encoding.UTF8.GetString(decBytes);
|
||||
}
|
||||
|
||||
private async Task<byte[]> AesDecryptToBytesAsync(EncryptionType encType, byte[] data, byte[] iv, byte[] mac,
|
||||
SymmetricCryptoKey key)
|
||||
{
|
||||
|
||||
var keyForEnc = await GetKeyForEncryptionAsync(key);
|
||||
var theKey = ResolveLegacyKey(encType, keyForEnc);
|
||||
if(theKey.MacKey != null && mac == null)
|
||||
{
|
||||
// Mac required.
|
||||
return null;
|
||||
}
|
||||
if(theKey.EncType != encType)
|
||||
{
|
||||
// encType unavailable.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Compute mac
|
||||
if(theKey.MacKey != null && mac != null)
|
||||
{
|
||||
var macData = new byte[iv.Length + data.Length];
|
||||
Buffer.BlockCopy(iv, 0, macData, 0, iv.Length);
|
||||
Buffer.BlockCopy(data, 0, macData, iv.Length, data.Length);
|
||||
|
||||
var computedMac = await _cryptoFunctionService.HmacAsync(macData, theKey.MacKey,
|
||||
CryptoHashAlgorithm.Sha256);
|
||||
if(computedMac == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var macsMatch = await _cryptoFunctionService.CompareAsync(mac, computedMac);
|
||||
if(!macsMatch)
|
||||
{
|
||||
// Mac failed
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return await _cryptoFunctionService.AesDecryptAsync(data, iv, theKey.EncKey);
|
||||
}
|
||||
|
||||
private async Task<byte[]> RsaDecryptAsync(string encValue)
|
||||
{
|
||||
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]);
|
||||
var 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<SymmetricCryptoKey> GetKeyForEncryptionAsync(SymmetricCryptoKey key = null)
|
||||
{
|
||||
if(key != null)
|
||||
{
|
||||
return key;
|
||||
}
|
||||
var encKey = await GetEncKeyAsync();
|
||||
if(encKey != null)
|
||||
{
|
||||
return encKey;
|
||||
}
|
||||
return await GetKeyAsync();
|
||||
}
|
||||
|
||||
private SymmetricCryptoKey ResolveLegacyKey(EncryptionType encKey, SymmetricCryptoKey key)
|
||||
{
|
||||
if(encKey == EncryptionType.AesCbc128_HmacSha256_B64 && key.EncType == EncryptionType.AesCbc256_B64)
|
||||
{
|
||||
// Old encrypt-then-mac scheme, make a new key
|
||||
if(_legacyEtmKey == null)
|
||||
{
|
||||
_legacyEtmKey = new SymmetricCryptoKey(key.Key, EncryptionType.AesCbc128_HmacSha256_B64);
|
||||
}
|
||||
return _legacyEtmKey;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private async Task<SymmetricCryptoKey> StretchKeyAsync(SymmetricCryptoKey key)
|
||||
{
|
||||
var newKey = new byte[64];
|
||||
var enc = await HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("enc"), 32);
|
||||
Buffer.BlockCopy(enc, 0, newKey, 0, 32);
|
||||
var mac = await HkdfExpandAsync(key.Key, Encoding.UTF8.GetBytes("mac"), 32);
|
||||
Buffer.BlockCopy(mac, 0, newKey, 32, 32);
|
||||
return new SymmetricCryptoKey(newKey);
|
||||
}
|
||||
|
||||
// ref: https://tools.ietf.org/html/rfc5869
|
||||
private async Task<byte[]> HkdfExpandAsync(byte[] prk, byte[] info, int size)
|
||||
{
|
||||
var hashLen = 32; // sha256
|
||||
var okm = new byte[size];
|
||||
var previousT = new byte[0];
|
||||
var n = (int)Math.Ceiling((double)size / hashLen);
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
var t = new byte[previousT.Length + info.Length + 1];
|
||||
previousT.CopyTo(t, 0);
|
||||
info.CopyTo(t, previousT.Length);
|
||||
t[t.Length - 1] = (byte)(i + 1);
|
||||
previousT = await _cryptoFunctionService.HmacAsync(t, prk, CryptoHashAlgorithm.Sha256);
|
||||
previousT.CopyTo(okm, i * hashLen);
|
||||
}
|
||||
return okm;
|
||||
}
|
||||
|
||||
private List<string> HashPhrase(byte[] hash, int minimumEntropy = 64)
|
||||
{
|
||||
// TODO: word list
|
||||
var EEFLongWordList = new string[] { };
|
||||
|
||||
var entropyPerWord = Math.Log(EEFLongWordList.Length) / Math.Log(2);
|
||||
var numWords = (int)Math.Ceiling(minimumEntropy / entropyPerWord);
|
||||
|
||||
var entropyAvailable = hash.Length * 4;
|
||||
if(numWords * entropyPerWord > entropyAvailable)
|
||||
{
|
||||
throw new Exception("Output entropy of hash function is too small");
|
||||
}
|
||||
|
||||
var phrase = new List<string>();
|
||||
// TODO: big int from array
|
||||
var hashNumber = 123; // bigInt.fromArray(hash, 256);
|
||||
while(numWords-- > 0)
|
||||
{
|
||||
// var remainder = hashNumber.mod(EEFLongWordList.Length);
|
||||
// hashNumber = hashNumber.divide(EEFLongWordList.Length);
|
||||
// phrase.Add(EEFLongWordList[remainder]);
|
||||
}
|
||||
return phrase;
|
||||
}
|
||||
|
||||
private async Task<Tuple<SymmetricCryptoKey, CipherString>> BuildEncKeyAsync(SymmetricCryptoKey key,
|
||||
byte[] encKey)
|
||||
{
|
||||
CipherString encKeyEnc = null;
|
||||
if(key.Key.Length == 32)
|
||||
{
|
||||
var newKey = await StretchKeyAsync(key);
|
||||
encKeyEnc = await EncryptAsync(encKey, newKey);
|
||||
}
|
||||
else if(key.Key.Length == 64)
|
||||
{
|
||||
encKeyEnc = await EncryptAsync(encKey, key);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Invalid key size.");
|
||||
}
|
||||
return new Tuple<SymmetricCryptoKey, CipherString>(new SymmetricCryptoKey(encKey), encKeyEnc);
|
||||
}
|
||||
|
||||
private class EncryptedObject
|
||||
{
|
||||
public byte[] Iv { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
public byte[] Mac { get; set; }
|
||||
public SymmetricCryptoKey Key { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,6 +142,11 @@ namespace Bit.Core.Services
|
|||
return Task.FromResult(CryptographicBuffer.GenerateRandom(length));
|
||||
}
|
||||
|
||||
public Task<uint> RandomNumberAsync()
|
||||
{
|
||||
return Task.FromResult(CryptographicBuffer.GenerateRandomNumber());
|
||||
}
|
||||
|
||||
private HashAlgorithm ToHashAlgorithm(CryptoHashAlgorithm algorithm)
|
||||
{
|
||||
switch(algorithm)
|
||||
|
|
Loading…
Reference in a new issue