[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/
x64/
x86/
!src/lib/x86/
build/
bld/
[Bb]in/

View file

@ -169,6 +169,10 @@
<GoogleServicesJson Include="google-services.json" />
<GoogleServicesJson Include="google-services.json.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="upload-keystore.jks.enc" />
</ItemGroup>

View file

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

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(string 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, byte[] salt, string 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
{
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
{
PBKDF2_SHA256 = 0
PBKDF2_SHA256 = 0,
Argon2id = 1,
}
}

View file

@ -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.");

View file

@ -44,6 +44,28 @@ namespace Bit.Core.Services
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) =>
await HkdfAsync(ikm, Encoding.UTF8.GetBytes(salt), Encoding.UTF8.GetBytes(info), outputByteSize, algorithm);