diff --git a/build.gradle b/build.gradle index 6fb646e..af5a7b6 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,10 @@ dependencies { implementation ('org.bouncycastle:bcprov-jdk15on:1.64') { exclude group: 'org.hamcrest', module: 'hamcrest-core' } + implementation ('de.mkammerer:argon2-jvm:2.7') { + exclude group: 'org.hamcrest', module: 'hamcrest-core' + exclude group: 'junit', module: 'junit' + } implementation ('ch.qos.logback:logback-classic:1.2.3') { exclude group: 'org.hamcrest', module: 'hamcrest-core' } diff --git a/src/main/java/com/sparrowwallet/drongo/crypto/Argon2KeyDeriver.java b/src/main/java/com/sparrowwallet/drongo/crypto/Argon2KeyDeriver.java index e2d4bd5..297d064 100644 --- a/src/main/java/com/sparrowwallet/drongo/crypto/Argon2KeyDeriver.java +++ b/src/main/java/com/sparrowwallet/drongo/crypto/Argon2KeyDeriver.java @@ -1,9 +1,35 @@ package com.sparrowwallet.drongo.crypto; +import de.mkammerer.argon2.Argon2Advanced; +import de.mkammerer.argon2.Argon2Factory; + +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; + public class Argon2KeyDeriver implements KeyDeriver { + private static final int SALT_LENGTH = 16; + private static final int HASH_LENGTH = 32; + private static final int ITERATIONS = 10; + private static final int MEMORY = 256 * 1024; + private static final int PARALLELISM = 4; + + private final byte[] salt; + + public Argon2KeyDeriver() { + SecureRandom secureRandom = new SecureRandom(); + salt = new byte[SALT_LENGTH]; + secureRandom.nextBytes(salt); + } + + public Argon2KeyDeriver(byte[] salt) { + this.salt = salt; + } + @Override public Key deriveKey(String password) throws KeyCrypterException { - return null; + Argon2Advanced argon2 = Argon2Factory.createAdvanced(Argon2Factory.Argon2Types.ARGON2id, SALT_LENGTH, HASH_LENGTH); + byte[] hash = argon2.rawHash(ITERATIONS, MEMORY, PARALLELISM, password.getBytes(StandardCharsets.UTF_8), salt); + return new Key(hash, salt, getDeriverType()); } @Override diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 3f38cad..5e9c842 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,5 +1,6 @@ open module com.sparrowwallet.drongo { requires org.bouncycastle.provider; + requires de.mkammerer.argon2; requires slf4j.api; exports com.sparrowwallet.drongo; exports com.sparrowwallet.drongo.psbt; diff --git a/src/test/java/com/sparrowwallet/drongo/crypto/Argon2KeyDeriverTest.java b/src/test/java/com/sparrowwallet/drongo/crypto/Argon2KeyDeriverTest.java new file mode 100644 index 0000000..ac1b724 --- /dev/null +++ b/src/test/java/com/sparrowwallet/drongo/crypto/Argon2KeyDeriverTest.java @@ -0,0 +1,52 @@ +package com.sparrowwallet.drongo.crypto; + +import de.mkammerer.argon2.Argon2; +import de.mkammerer.argon2.Argon2Factory; +import de.mkammerer.argon2.Argon2Helper; +import org.junit.Assert; +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; + +public class Argon2KeyDeriverTest { + @Test + public void testArgon2() { + String password = "thisisapassword"; + + Argon2KeyDeriver keyDeriver = new Argon2KeyDeriver(); + Key key = keyDeriver.deriveKey(password); + + KeyCrypter keyCrypter = new AESKeyCrypter(); + + String message = "absent essay fox snake vast pumpkin height crouch silent bulb excuse razor"; + byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8); + + byte[] iv = new byte[16]; + SecureRandom secureRandom = new SecureRandom(); + secureRandom.nextBytes(iv); + + EncryptedData encrypted = keyCrypter.encrypt(messageBytes, iv, key); + + //Decrypt + + Argon2KeyDeriver keyDeriver2 = new Argon2KeyDeriver(encrypted.getKeySalt()); + Key key2 = keyDeriver2.deriveKey(password); + + byte[] decrypted = keyCrypter.decrypt(encrypted, key2); + String decryptedMessage = new String(decrypted, StandardCharsets.UTF_8); + + Assert.assertEquals(message, decryptedMessage); + } + +// @Test +// public void findIterations() { +// Argon2 argon2 = Argon2Factory.create(); +// // 1000 = The hash call must take at most 1000 ms +// // 65536 = Memory cost +// // 1 = parallelism +// int iterations = Argon2Helper.findIterations(argon2, 500, 256*1024, 4); +// +// System.out.println("Optimal number of iterations: " + iterations); +// } +}