diff --git a/.gitignore b/.gitignore
index 107fad1df..a006ce4bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ Components/
[Rr]eleases/
x64/
x86/
+!src/lib/x86/
build/
bld/
[Bb]in/
diff --git a/src/Android/Android.csproj b/src/Android/Android.csproj
index 8d01e1091..c285cad13 100644
--- a/src/Android/Android.csproj
+++ b/src/Android/Android.csproj
@@ -169,6 +169,10 @@
+
+
+
+
diff --git a/src/Android/Services/CryptoPrimitiveService.cs b/src/Android/Services/CryptoPrimitiveService.cs
index bdb19cfa5..6ea22224a 100644
--- a/src/Android/Services/CryptoPrimitiveService.cs
+++ b/src/Android/Services/CryptoPrimitiveService.cs
@@ -5,6 +5,8 @@ using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using System;
+using System.Runtime.InteropServices;
+using Java.Lang;
namespace Bit.Droid.Services
{
@@ -33,5 +35,15 @@ namespace Bit.Droid.Services
generator.Init(password, salt, iterations);
return ((KeyParameter)generator.GenerateDerivedMacParameters(keySize)).GetKey();
}
+ public byte[] Argon2id(byte[] password, byte[] salt, int iterations, int memory, int parallelism)
+ {
+ JavaSystem.LoadLibrary("argon2");
+ var hash = new byte[32];
+ argon2id_hash_raw(iterations, memory, parallelism, password, password.Length, password, salt.Length, hash, 32);
+ return hash.ToArray();
+ }
+
+ [DllImport("argon2", EntryPoint = "argon2id_hash_raw")]
+ internal static extern int argon2id_hash_raw(int timeCost, int memoryCost, int parallelism, byte[] pwd, int pwdlen, byte[] salt, int saltlen, ref byte hash, int hashlen);
}
}
diff --git a/src/Android/lib/arm64-v8a/libargon2.so b/src/Android/lib/arm64-v8a/libargon2.so
new file mode 100755
index 000000000..da072ae3c
Binary files /dev/null and b/src/Android/lib/arm64-v8a/libargon2.so differ
diff --git a/src/Android/lib/armeabi-v7a/libargon2.so b/src/Android/lib/armeabi-v7a/libargon2.so
new file mode 100755
index 000000000..ebb445931
Binary files /dev/null and b/src/Android/lib/armeabi-v7a/libargon2.so differ
diff --git a/src/Android/lib/x86/libargon2.so b/src/Android/lib/x86/libargon2.so
new file mode 100755
index 000000000..01c269702
Binary files /dev/null and b/src/Android/lib/x86/libargon2.so differ
diff --git a/src/Android/lib/x86_64/libargon2.so b/src/Android/lib/x86_64/libargon2.so
new file mode 100755
index 000000000..8e379f601
Binary files /dev/null and b/src/Android/lib/x86_64/libargon2.so differ
diff --git a/src/Core/Abstractions/ICryptoFunctionService.cs b/src/Core/Abstractions/ICryptoFunctionService.cs
index 14f8fdf6c..39b6ba6a1 100644
--- a/src/Core/Abstractions/ICryptoFunctionService.cs
+++ b/src/Core/Abstractions/ICryptoFunctionService.cs
@@ -10,6 +10,10 @@ namespace Bit.Core.Abstractions
Task Pbkdf2Async(byte[] password, string salt, CryptoHashAlgorithm algorithm, int iterations);
Task Pbkdf2Async(string password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations);
Task Pbkdf2Async(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations);
+ Task Argon2Async(string password, string salt, int iterations, int memory, int parallelism);
+ Task Argon2Async(byte[] password, string salt, int iterations, int memory, int parallelism);
+ Task Argon2Async(string password, byte[] salt, int iterations, int memory, int parallelism);
+ Task Argon2Async(byte[] password, byte[] salt, int iterations, int memory, int parallelism);
Task HkdfAsync(byte[] ikm, string salt, string info, int outputByteSize, HkdfAlgorithm algorithm);
Task HkdfAsync(byte[] ikm, byte[] salt, string info, int outputByteSize, HkdfAlgorithm algorithm);
Task HkdfAsync(byte[] ikm, string salt, byte[] info, int outputByteSize, HkdfAlgorithm algorithm);
diff --git a/src/Core/Abstractions/ICryptoPrimitiveService.cs b/src/Core/Abstractions/ICryptoPrimitiveService.cs
index 897c337c4..cf29f01de 100644
--- a/src/Core/Abstractions/ICryptoPrimitiveService.cs
+++ b/src/Core/Abstractions/ICryptoPrimitiveService.cs
@@ -5,5 +5,6 @@ namespace Bit.Core.Abstractions
public interface ICryptoPrimitiveService
{
byte[] Pbkdf2(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations);
+ byte[] Argon2id(byte[] password, byte[] salt, int iterations, int memory, int parallelism);
}
}
diff --git a/src/Core/Enums/KdfType.cs b/src/Core/Enums/KdfType.cs
index 1c845846a..86cdf849c 100644
--- a/src/Core/Enums/KdfType.cs
+++ b/src/Core/Enums/KdfType.cs
@@ -2,6 +2,7 @@
{
public enum KdfType : byte
{
- PBKDF2_SHA256 = 0
+ PBKDF2_SHA256 = 0,
+ Argon2id = 1,
}
}
diff --git a/src/Core/Services/CryptoService.cs b/src/Core/Services/CryptoService.cs
index 96977ca5a..433424ed8 100644
--- a/src/Core/Services/CryptoService.cs
+++ b/src/Core/Services/CryptoService.cs
@@ -406,6 +406,14 @@ namespace Bit.Core.Services
key = await _cryptoFunctionService.Pbkdf2Async(password, salt,
CryptoHashAlgorithm.Sha256, kdfIterations.Value);
}
+ else if (kdf == KdfType.Argon2id)
+ {
+ var iterations = kdfIterations.Value;
+ const int parallelism = 1;
+ const int memory = 1024 * 16; // 16 MiB
+
+ key = await _cryptoFunctionService.Argon2Async(password, salt, iterations, memory, parallelism);
+ }
else
{
throw new Exception("Unknown kdf.");
diff --git a/src/Core/Services/PclCryptoFunctionService.cs b/src/Core/Services/PclCryptoFunctionService.cs
index fa8e8693d..50c555065 100644
--- a/src/Core/Services/PclCryptoFunctionService.cs
+++ b/src/Core/Services/PclCryptoFunctionService.cs
@@ -44,6 +44,28 @@ namespace Bit.Core.Services
return Task.FromResult(_cryptoPrimitiveService.Pbkdf2(password, salt, algorithm, iterations));
}
+ public Task Argon2Async(string password, string salt, int iterations, int memory, int parallelism)
+ {
+ password = NormalizePassword(password);
+ return Argon2Async(Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytes(salt), iterations, memory, parallelism);
+ }
+
+ public Task Argon2Async(byte[] password, string salt, int iterations, int memory, int parallelism)
+ {
+ return Argon2Async(password, Encoding.UTF8.GetBytes(salt), iterations, memory, parallelism);
+ }
+
+ public Task Argon2Async(string password, byte[] salt, int iterations, int memory, int parallelism)
+ {
+ password = NormalizePassword(password);
+ return Argon2Async(Encoding.UTF8.GetBytes(password), salt, iterations, memory, parallelism);
+ }
+
+ public Task Argon2Async(byte[] password, byte[] salt, int iterations, int memory, int parallelism)
+ {
+ return Task.FromResult(_cryptoPrimitiveService.Argon2id(password, salt, iterations, memory, parallelism));
+ }
+
public async Task HkdfAsync(byte[] ikm, string salt, string info, int outputByteSize, HkdfAlgorithm algorithm) =>
await HkdfAsync(ikm, Encoding.UTF8.GetBytes(salt), Encoding.UTF8.GetBytes(info), outputByteSize, algorithm);