[PS-2249] Implement Argon2 (#2293)

* Implement Argon2

* Fix incorrect argon2 type on iOS

* Switch argon2 implementation to native bindings

* Change argon2 to save iterations instead of memory as 'kdfIterations'

* Remove mistakenly added import

* Remove unused library
This commit is contained in:
Bernd Schoolmann 2023-01-20 22:03:16 +01:00 committed by GitHub
parent e0401f4098
commit 9613019d4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 54 additions and 1 deletions

1
.gitignore vendored
View file

@ -30,6 +30,7 @@ Components/
[Rr]eleases/ [Rr]eleases/
x64/ x64/
x86/ x86/
!src/lib/x86/
build/ build/
bld/ bld/
[Bb]in/ [Bb]in/

View file

@ -169,6 +169,10 @@
<GoogleServicesJson Include="google-services.json" /> <GoogleServicesJson Include="google-services.json" />
<GoogleServicesJson Include="google-services.json.enc" /> <GoogleServicesJson Include="google-services.json.enc" />
<None Include="fdroid-keystore.jks.enc" /> <None Include="fdroid-keystore.jks.enc" />
<AndroidNativeLibrary Include="lib\arm64-v8a\libargon2.so" />
<AndroidNativeLibrary Include="lib\armeabi-v7a\libargon2.so" />
<AndroidNativeLibrary Include="lib\x86\libargon2.so" />
<AndroidNativeLibrary Include="lib\x86_64\libargon2.so" />
<None Include="Properties\AndroidManifest.xml" /> <None Include="Properties\AndroidManifest.xml" />
<None Include="upload-keystore.jks.enc" /> <None Include="upload-keystore.jks.enc" />
</ItemGroup> </ItemGroup>

View file

@ -5,6 +5,8 @@ using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Parameters;
using System; using System;
using System.Runtime.InteropServices;
using Java.Lang;
namespace Bit.Droid.Services namespace Bit.Droid.Services
{ {
@ -33,5 +35,15 @@ namespace Bit.Droid.Services
generator.Init(password, salt, iterations); generator.Init(password, salt, iterations);
return ((KeyParameter)generator.GenerateDerivedMacParameters(keySize)).GetKey(); 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);
} }
} }

Binary file not shown.

Binary file not shown.

BIN
src/Android/lib/x86/libargon2.so Executable file

Binary file not shown.

Binary file not shown.

View file

@ -10,6 +10,10 @@ namespace Bit.Core.Abstractions
Task<byte[]> Pbkdf2Async(byte[] password, string salt, CryptoHashAlgorithm algorithm, int iterations); Task<byte[]> Pbkdf2Async(byte[] password, string salt, CryptoHashAlgorithm algorithm, int iterations);
Task<byte[]> Pbkdf2Async(string password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations); Task<byte[]> Pbkdf2Async(string password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations);
Task<byte[]> Pbkdf2Async(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations); Task<byte[]> Pbkdf2Async(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations);
Task<byte[]> Argon2Async(string password, string salt, int iterations, int memory, int parallelism);
Task<byte[]> Argon2Async(byte[] password, string salt, int iterations, int memory, int parallelism);
Task<byte[]> Argon2Async(string password, byte[] salt, int iterations, int memory, int parallelism);
Task<byte[]> Argon2Async(byte[] password, byte[] salt, int iterations, int memory, int parallelism);
Task<byte[]> HkdfAsync(byte[] ikm, string salt, string info, int outputByteSize, HkdfAlgorithm algorithm); Task<byte[]> HkdfAsync(byte[] ikm, string salt, string info, int outputByteSize, HkdfAlgorithm algorithm);
Task<byte[]> HkdfAsync(byte[] ikm, byte[] salt, string info, int outputByteSize, HkdfAlgorithm algorithm); Task<byte[]> HkdfAsync(byte[] ikm, byte[] salt, string info, int outputByteSize, HkdfAlgorithm algorithm);
Task<byte[]> HkdfAsync(byte[] ikm, string salt, byte[] info, int outputByteSize, HkdfAlgorithm algorithm); Task<byte[]> HkdfAsync(byte[] ikm, string salt, byte[] info, int outputByteSize, HkdfAlgorithm algorithm);

View file

@ -5,5 +5,6 @@ namespace Bit.Core.Abstractions
public interface ICryptoPrimitiveService public interface ICryptoPrimitiveService
{ {
byte[] Pbkdf2(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations); byte[] Pbkdf2(byte[] password, byte[] salt, CryptoHashAlgorithm algorithm, int iterations);
byte[] Argon2id(byte[] password, byte[] salt, int iterations, int memory, int parallelism);
} }
} }

View file

@ -2,6 +2,7 @@
{ {
public enum KdfType : byte public enum KdfType : byte
{ {
PBKDF2_SHA256 = 0 PBKDF2_SHA256 = 0,
Argon2id = 1,
} }
} }

View file

@ -406,6 +406,14 @@ namespace Bit.Core.Services
key = await _cryptoFunctionService.Pbkdf2Async(password, salt, key = await _cryptoFunctionService.Pbkdf2Async(password, salt,
CryptoHashAlgorithm.Sha256, kdfIterations.Value); 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 else
{ {
throw new Exception("Unknown kdf."); throw new Exception("Unknown kdf.");

View file

@ -44,6 +44,28 @@ namespace Bit.Core.Services
return Task.FromResult(_cryptoPrimitiveService.Pbkdf2(password, salt, algorithm, iterations)); return Task.FromResult(_cryptoPrimitiveService.Pbkdf2(password, salt, algorithm, iterations));
} }
public Task<byte[]> 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<byte[]> Argon2Async(byte[] password, string salt, int iterations, int memory, int parallelism)
{
return Argon2Async(password, Encoding.UTF8.GetBytes(salt), iterations, memory, parallelism);
}
public Task<byte[]> 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<byte[]> Argon2Async(byte[] password, byte[] salt, int iterations, int memory, int parallelism)
{
return Task.FromResult(_cryptoPrimitiveService.Argon2id(password, salt, iterations, memory, parallelism));
}
public async Task<byte[]> HkdfAsync(byte[] ikm, string salt, string info, int outputByteSize, HkdfAlgorithm algorithm) => public async Task<byte[]> HkdfAsync(byte[] ikm, string salt, string info, int outputByteSize, HkdfAlgorithm algorithm) =>
await HkdfAsync(ikm, Encoding.UTF8.GetBytes(salt), Encoding.UTF8.GetBytes(info), outputByteSize, algorithm); await HkdfAsync(ikm, Encoding.UTF8.GetBytes(salt), Encoding.UTF8.GetBytes(info), outputByteSize, algorithm);