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);