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 @@
+