From cb9dddc7a7749a760aa1f7c2187260d6a5c8ab61 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 28 Mar 2019 15:43:50 -0400 Subject: [PATCH] crypto function service --- src/Android/Android.csproj | 4 + src/Android/Resources/Resource.designer.cs | 16 +- .../Services/CryptoPrimitiveService.cs | 37 ++++ .../Abstractions/ICryptoFunctionService.cs | 10 ++ .../Abstractions/ICryptoPrimitiveService.cs | 9 + src/Core/Core.csproj | 1 + src/Core/Enums/CryptoHashAlgorithm.cs | 10 ++ src/Core/Services/PclCryptoFunctionService.cs | 162 ++++++++++++++++++ src/iOS/Main.cs | 9 +- src/iOS/Services/CryptoPrimitiveService.cs | 47 +++++ src/iOS/iOS.csproj | 1 + 11 files changed, 296 insertions(+), 10 deletions(-) create mode 100644 src/Android/Services/CryptoPrimitiveService.cs create mode 100644 src/Core/Abstractions/ICryptoFunctionService.cs create mode 100644 src/Core/Abstractions/ICryptoPrimitiveService.cs create mode 100644 src/Core/Enums/CryptoHashAlgorithm.cs create mode 100644 src/Core/Services/PclCryptoFunctionService.cs create mode 100644 src/iOS/Services/CryptoPrimitiveService.cs diff --git a/src/Android/Android.csproj b/src/Android/Android.csproj index ea4a17c29..52d2b0f02 100644 --- a/src/Android/Android.csproj +++ b/src/Android/Android.csproj @@ -49,6 +49,9 @@ + + 1.8.5 + 1.1.0 @@ -64,6 +67,7 @@ + diff --git a/src/Android/Resources/Resource.designer.cs b/src/Android/Resources/Resource.designer.cs index 75b1bf931..48ae4f88d 100644 --- a/src/Android/Resources/Resource.designer.cs +++ b/src/Android/Resources/Resource.designer.cs @@ -26,6 +26,8 @@ namespace Bit.Droid public static void UpdateIdValues() { + global::PCLCrypto.Resource.String.ApplicationName = global::Bit.Droid.Resource.String.ApplicationName; + global::PCLCrypto.Resource.String.Hello = global::Bit.Droid.Resource.String.Hello; global::Xamarin.Essentials.Resource.Attribute.alpha = global::Bit.Droid.Resource.Attribute.alpha; global::Xamarin.Essentials.Resource.Attribute.coordinatorLayoutStyle = global::Bit.Droid.Resource.Attribute.coordinatorLayoutStyle; global::Xamarin.Essentials.Resource.Attribute.font = global::Bit.Droid.Resource.Attribute.font; @@ -7693,13 +7695,19 @@ namespace Bit.Droid { // aapt resource value: 0x7f0b004f - public const int AutoFillServiceDescription = 2131427407; + public const int ApplicationName = 2131427407; - // aapt resource value: 0x7f0b004e - public const int AutoFillServiceSummary = 2131427406; + // aapt resource value: 0x7f0b0051 + public const int AutoFillServiceDescription = 2131427409; // aapt resource value: 0x7f0b0050 - public const int MyVault = 2131427408; + public const int AutoFillServiceSummary = 2131427408; + + // aapt resource value: 0x7f0b004e + public const int Hello = 2131427406; + + // aapt resource value: 0x7f0b0052 + public const int MyVault = 2131427410; // aapt resource value: 0x7f0b0018 public const int abc_action_bar_home_description = 2131427352; diff --git a/src/Android/Services/CryptoPrimitiveService.cs b/src/Android/Services/CryptoPrimitiveService.cs new file mode 100644 index 000000000..e00e370c3 --- /dev/null +++ b/src/Android/Services/CryptoPrimitiveService.cs @@ -0,0 +1,37 @@ +using Bit.Core.Abstractions; +using Bit.Core.Enums; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using System; + +namespace Bit.Droid.Services +{ + public class CryptoPrimitiveService : ICryptoPrimitiveService + { + public byte[] Pbkdf2(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations) + { + int keySize = 256; + IDigest digest = null; + if(algorithm == CryptoHashAlgorithm.Sha256) + { + keySize = 256; + digest = new Sha256Digest(); + } + else if(algorithm == CryptoHashAlgorithm.Sha512) + { + keySize = 512; + digest = new Sha512Digest(); + } + else + { + throw new ArgumentException("Unsupported PBKDF2 algorithm."); + } + + var generator = new Pkcs5S2ParametersGenerator(digest); + generator.Init(password, salt, iterations); + return ((KeyParameter)generator.GenerateDerivedMacParameters(keySize)).GetKey(); + } + } +} diff --git a/src/Core/Abstractions/ICryptoFunctionService.cs b/src/Core/Abstractions/ICryptoFunctionService.cs new file mode 100644 index 000000000..0aaa290cc --- /dev/null +++ b/src/Core/Abstractions/ICryptoFunctionService.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bit.Core.Abstractions +{ + public interface ICryptoFunctionService + { + } +} diff --git a/src/Core/Abstractions/ICryptoPrimitiveService.cs b/src/Core/Abstractions/ICryptoPrimitiveService.cs new file mode 100644 index 000000000..897c337c4 --- /dev/null +++ b/src/Core/Abstractions/ICryptoPrimitiveService.cs @@ -0,0 +1,9 @@ +using Bit.Core.Enums; + +namespace Bit.Core.Abstractions +{ + public interface ICryptoPrimitiveService + { + byte[] Pbkdf2(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations); + } +} diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index c0028818b..40a82c9af 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Core/Enums/CryptoHashAlgorithm.cs b/src/Core/Enums/CryptoHashAlgorithm.cs new file mode 100644 index 000000000..0c1df3101 --- /dev/null +++ b/src/Core/Enums/CryptoHashAlgorithm.cs @@ -0,0 +1,10 @@ +namespace Bit.Core.Enums +{ + public enum CryptoHashAlgorithm + { + Sha1 = 0, + Sha256 = 1, + Sha512 = 2, + Md5 = 3 + } +} diff --git a/src/Core/Services/PclCryptoFunctionService.cs b/src/Core/Services/PclCryptoFunctionService.cs new file mode 100644 index 000000000..ddf770e0e --- /dev/null +++ b/src/Core/Services/PclCryptoFunctionService.cs @@ -0,0 +1,162 @@ +using Bit.Core.Abstractions; +using Bit.Core.Enums; +using PCLCrypto; +using System; +using System.Text; +using System.Threading.Tasks; +using static PCLCrypto.WinRTCrypto; + +namespace Bit.Core.Services +{ + public class PclCryptoFunctionService + { + private readonly ICryptoPrimitiveService _cryptoPrimitiveService; + + public PclCryptoFunctionService(ICryptoPrimitiveService cryptoPrimitiveService) + { + _cryptoPrimitiveService = cryptoPrimitiveService; + } + + public Task Pbkdf2Async(string password, string salt, CryptoHashAlgorithm algorithm, int iterations) + { + return Pbkdf2Async(Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytes(salt), algorithm, iterations); + } + + public Task Pbkdf2Async(byte[] password, string salt, CryptoHashAlgorithm algorithm, int iterations) + { + return Pbkdf2Async(password, Encoding.UTF8.GetBytes(salt), algorithm, iterations); + } + + public Task Pbkdf2Async(string password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations) + { + return Pbkdf2Async(Encoding.UTF8.GetBytes(password), salt, algorithm, iterations); + } + + public Task Pbkdf2Async(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations) + { + if(algorithm != CryptoHashAlgorithm.Sha256 && algorithm != CryptoHashAlgorithm.Sha512) + { + throw new ArgumentException("Unsupported PBKDF2 algorithm."); + } + return Task.FromResult(_cryptoPrimitiveService.Pbkdf2(password, salt, algorithm, iterations)); + } + + public Task HashAsync(string value, CryptoHashAlgorithm algorithm) + { + return HashAsync(Encoding.UTF8.GetBytes(value), algorithm); + } + + public Task HashAsync(byte[] value, CryptoHashAlgorithm algorithm) + { + var provider = HashAlgorithmProvider.OpenAlgorithm(ToHashAlgorithm(algorithm)); + return Task.FromResult(provider.HashData(value)); + } + + public Task HmacAsync(byte[] value, byte[] key, CryptoHashAlgorithm algorithm) + { + var provider = MacAlgorithmProvider.OpenAlgorithm(ToMacAlgorithm(algorithm)); + var hasher = provider.CreateHash(key); + hasher.Append(value); + return Task.FromResult(hasher.GetValueAndReset()); + } + + public Task AesEncryptAsync(byte[] data, byte[] iv, byte[] key) + { + var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); + var cryptoKey = provider.CreateSymmetricKey(key); + return Task.FromResult(CryptographicEngine.Encrypt(cryptoKey, data, iv)); + } + + public Task AesDecryptAsync(byte[] data, byte[] iv, byte[] key) + { + var provider = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7); + var cryptoKey = provider.CreateSymmetricKey(key); + return Task.FromResult(CryptographicEngine.Decrypt(cryptoKey, data, iv)); + } + + public Task RsaEncryptAsync(byte[] data, byte[] publicKey, CryptoHashAlgorithm algorithm) + { + var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(ToAsymmetricAlgorithm(algorithm)); + var cryptoKey = provider.ImportPublicKey(publicKey, + CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo); + return Task.FromResult(CryptographicEngine.Encrypt(cryptoKey, data)); + } + + public Task RsaDecryptAsync(byte[] data, byte[] privateKey, CryptoHashAlgorithm algorithm) + { + var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(ToAsymmetricAlgorithm(algorithm)); + var cryptoKey = provider.ImportKeyPair(privateKey, CryptographicPrivateKeyBlobType.Pkcs8RawPrivateKeyInfo); + return Task.FromResult(CryptographicEngine.Decrypt(cryptoKey, data)); + } + + public Task RsaExtractPublicKeyAsync(byte[] privateKey) + { + // Have to specify some algorithm + var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithm.RsaOaepSha1); + var cryptoKey = provider.ImportKeyPair(privateKey, CryptographicPrivateKeyBlobType.Pkcs8RawPrivateKeyInfo); + return Task.FromResult(cryptoKey.ExportPublicKey(CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo)); + } + + public Task> RsaGenerateKeyPairAsync(int length) + { + // Have to specify some algorithm + var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithm.RsaOaepSha1); + var cryptoKey = provider.CreateKeyPair(length); + var publicKey = cryptoKey.ExportPublicKey(CryptographicPublicKeyBlobType.X509SubjectPublicKeyInfo); + var privateKey = cryptoKey.Export(CryptographicPrivateKeyBlobType.Pkcs8RawPrivateKeyInfo); + return Task.FromResult(new Tuple(publicKey, privateKey)); + } + + public Task RandomBytesAsync(int length) + { + return Task.FromResult(CryptographicBuffer.GenerateRandom(length)); + } + + private HashAlgorithm ToHashAlgorithm(CryptoHashAlgorithm algorithm) + { + switch(algorithm) + { + case CryptoHashAlgorithm.Sha1: + return HashAlgorithm.Sha1; + case CryptoHashAlgorithm.Sha256: + return HashAlgorithm.Sha256; + case CryptoHashAlgorithm.Sha512: + return HashAlgorithm.Sha512; + case CryptoHashAlgorithm.Md5: + return HashAlgorithm.Md5; + default: + throw new ArgumentException("Unsupported hash algorithm."); + } + } + + private MacAlgorithm ToMacAlgorithm(CryptoHashAlgorithm algorithm) + { + switch(algorithm) + { + case CryptoHashAlgorithm.Sha1: + return MacAlgorithm.HmacSha1; + case CryptoHashAlgorithm.Sha256: + return MacAlgorithm.HmacSha256; + case CryptoHashAlgorithm.Sha512: + return MacAlgorithm.HmacSha512; + default: + throw new ArgumentException("Unsupported mac algorithm."); + } + } + + private AsymmetricAlgorithm ToAsymmetricAlgorithm(CryptoHashAlgorithm algorithm) + { + switch(algorithm) + { + case CryptoHashAlgorithm.Sha1: + return AsymmetricAlgorithm.RsaOaepSha1; + // RsaOaepSha256 is not supported on iOS + // ref: https://github.com/AArnott/PCLCrypto/issues/124 + // case CryptoHashAlgorithm.SHA256: + // return AsymmetricAlgorithm.RsaOaepSha256; + default: + throw new ArgumentException("Unsupported asymmetric algorithm."); + } + } + } +} diff --git a/src/iOS/Main.cs b/src/iOS/Main.cs index 3d025ad0c..3d3672401 100644 --- a/src/iOS/Main.cs +++ b/src/iOS/Main.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using Foundation; -using UIKit; +using UIKit; namespace Bit.iOS { @@ -12,6 +7,8 @@ namespace Bit.iOS // This is the main entry point of the application. static void Main(string[] args) { + ObjCRuntime.Dlfcn.dlopen(ObjCRuntime.Constants.libSystemLibrary, 0); + // if you want to use a different Application Delegate class from "AppDelegate" // you can specify it here. UIApplication.Main(args, null, "AppDelegate"); diff --git a/src/iOS/Services/CryptoPrimitiveService.cs b/src/iOS/Services/CryptoPrimitiveService.cs new file mode 100644 index 000000000..aba0d993f --- /dev/null +++ b/src/iOS/Services/CryptoPrimitiveService.cs @@ -0,0 +1,47 @@ +using Bit.Core.Abstractions; +using Bit.Core.Enums; +using Foundation; +using System; +using System.Runtime.InteropServices; + +namespace Bit.iOS.Services +{ + public class CryptoPrimitiveService : ICryptoPrimitiveService + { + private const uint PBKDFAlgorithm = 2; // kCCPBKDF2 + + public byte[] Pbkdf2(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations) + { + uint keySize = 32; + uint pseudoRandomAlgorithm = 3; // kCCPRFHmacAlgSHA256 + if(algorithm == CryptoHashAlgorithm.Sha512) + { + keySize = 64; + pseudoRandomAlgorithm = 5; // kCCPRFHmacAlgSHA512 + } + else if(algorithm != CryptoHashAlgorithm.Sha256) + { + throw new ArgumentException("Unsupported PBKDF2 algorithm."); + } + + var keyData = new NSMutableData(); + keyData.Length = keySize; + + var passwordData = NSData.FromArray(password); + var saltData = NSData.FromArray(salt); + + var result = CCKeyCerivationPBKDF(PBKDFAlgorithm, passwordData.Bytes, passwordData.Length, saltData.Bytes, + saltData.Length, pseudoRandomAlgorithm, Convert.ToUInt32(iterations), keyData.MutableBytes, + keyData.Length); + + byte[] keyBytes = new byte[keyData.Length]; + Marshal.Copy(keyData.Bytes, keyBytes, 0, Convert.ToInt32(keyData.Length)); + return keyBytes; + } + + // ref: http://opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/CommonCrypto/CommonKeyDerivation.h + [DllImport(ObjCRuntime.Constants.libSystemLibrary, EntryPoint = "CCKeyDerivationPBKDF")] + private extern static int CCKeyCerivationPBKDF(uint algorithm, IntPtr password, nuint passwordLen, + IntPtr salt, nuint saltLen, uint prf, nuint rounds, IntPtr derivedKey, nuint derivedKeyLength); + } +} diff --git a/src/iOS/iOS.csproj b/src/iOS/iOS.csproj index 241705401..d166cb296 100644 --- a/src/iOS/iOS.csproj +++ b/src/iOS/iOS.csproj @@ -92,6 +92,7 @@ +