Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/argon2 contexts secret and associated data #86

Merged
merged 4 commits into from
Apr 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,71 @@ public interface Argon2Advanced extends Argon2 {
*/
HashResult hashAdvanced(int iterations, int memory, int parallelism, byte[] password, byte[] salt, int hashLength, Argon2Version version);

/**
* Hashes a password, using the given salt, secret and associated data.
*
* @param iterations Number of iterations
* @param memory Sets memory usage to x kibibytes
* @param parallelism Number of threads and compute lanes
* @param password Password to hash
* @param charset Charset of the password
* @param salt Salt
* @param secret Secret (sometimes referred as Pepper)
* @param associatedData Associated Data
* @return Hashed password in raw bytes.
*/
byte[] rawHashAdvanced(int iterations, int memory, int parallelism, char[] password, Charset charset, byte[] salt, byte[] secret, byte[] associatedData);

/**
* Advanced version of hash, let the caller specify addition parameters such as hash length, salt, secret and associated data.
* Return both the encoded and the raw hash.
*
* @param iterations Number of iterations
* @param memory Sets memory usage to x kibibytes
* @param parallelism Number of threads and compute lanes
* @param password Password to hash
* @param salt Salt
* @param secret Secret (sometimes referred as Pepper)
* @param associatedData Associated Data
* @param hashLength Length of the returned hash in bytes.
* @param version Argon2 version
* @return Hashed password in raw bytes.
*/
byte[] rawHashAdvanced(int iterations, int memory, int parallelism, byte[] password, byte[] salt, byte[] secret, byte[] associatedData, int hashLength, Argon2Version version);

/**
* Verifies a password against a hash.
*
* @param iterations Number of iterations
* @param memory Sets memory usage to x kibibytes
* @param parallelism Number of threads and compute lanes
* @param password Password to hash
* @param charset Charset of the password
* @param salt Salt
* @param secret Secret (sometimes referred as Pepper)
* @param associatedData Associated Data
* @param rawHash Raw Hash bytes.
* @return True if the password matches the hash, false otherwise.
*/
boolean verifyAdvanced(int iterations, int memory, int parallelism, char[] password, Charset charset, byte[] salt, byte[] secret, byte[] associatedData, byte[] rawHash);

/**
* Verifies a password against a hash.
*
* @param iterations Number of iterations
* @param memory Sets memory usage to x kibibytes
* @param parallelism Number of threads and compute lanes
* @param password Password to hash
* @param salt Salt
* @param secret Secret (sometimes referred as Pepper)
* @param associatedData Associated Data
* @param hashLength Length of the returned hash in bytes.
* @param version Argon2 version
* @param rawHash Raw Hash bytes.
* @return True if the password matches the hash, false otherwise.
*/
boolean verifyAdvanced(int iterations, int memory, int parallelism, byte[] password, byte[] salt, byte[] secret, byte[] associatedData, int hashLength, Argon2Version version, byte[] rawHash);

/**
* Generates salt with the default length.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
package de.mkammerer.argon2;

import de.mkammerer.argon2.jna.Argon2_version;

/**
* Version of the Argon2 algorithm.
*/
public enum Argon2Version {
V10(0x10),
V13(0x13);
V13(0x13),
DEFAULT_VERSION(V13.version);

private final int version;
private final Argon2_version jnaType;

private final int jnaVersion;
Argon2Version(int version) {
this.version = version;
this.jnaType = new Argon2_version(version);
}

Argon2Version(int jnaVersion) {
this.jnaVersion = jnaVersion;
public Argon2_version getJnaType() {
return jnaType;
}

public int getJnaVersion() {
return jnaVersion;
public int getVersion() {
return version;
}
}
11 changes: 11 additions & 0 deletions argon2-jvm-nolibs/src/main/java/de/mkammerer/argon2/Argon2d.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.mkammerer.argon2;

import de.mkammerer.argon2.jna.Argon2Library;
import de.mkammerer.argon2.jna.Argon2_context;
import de.mkammerer.argon2.jna.JnaUint32;
import de.mkammerer.argon2.jna.Size_t;

Expand Down Expand Up @@ -45,4 +46,14 @@ salt, new Size_t(salt.length), hash, new Size_t(hash.length)
protected int callLibraryVerify(byte[] encoded, byte[] pwd) {
return Argon2Library.INSTANCE.argon2d_verify(encoded, pwd, new Size_t(pwd.length));
}

@Override
protected int callLibraryContext(Argon2_context.ByReference context) {
return Argon2Library.INSTANCE.argon2d_ctx(context);
}

@Override
protected int callLibraryVerifyContext(Argon2_context.ByReference context, byte[] rawHash) {
return Argon2Library.INSTANCE.argon2d_verify_ctx(context, rawHash);
}
}
11 changes: 11 additions & 0 deletions argon2-jvm-nolibs/src/main/java/de/mkammerer/argon2/Argon2i.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.mkammerer.argon2;

import de.mkammerer.argon2.jna.Argon2Library;
import de.mkammerer.argon2.jna.Argon2_context;
import de.mkammerer.argon2.jna.JnaUint32;
import de.mkammerer.argon2.jna.Size_t;

Expand Down Expand Up @@ -45,4 +46,14 @@ salt, new Size_t(salt.length), hash, new Size_t(hash.length)
protected int callLibraryVerify(byte[] encoded, byte[] pwd) {
return Argon2Library.INSTANCE.argon2i_verify(encoded, pwd, new Size_t(pwd.length));
}

@Override
protected int callLibraryContext(Argon2_context.ByReference context) {
return Argon2Library.INSTANCE.argon2i_ctx(context);
}

@Override
protected int callLibraryVerifyContext(Argon2_context.ByReference context, byte[] rawHash) {
return Argon2Library.INSTANCE.argon2i_verify_ctx(context, rawHash);
}
}
11 changes: 11 additions & 0 deletions argon2-jvm-nolibs/src/main/java/de/mkammerer/argon2/Argon2id.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.mkammerer.argon2;

import de.mkammerer.argon2.jna.Argon2Library;
import de.mkammerer.argon2.jna.Argon2_context;
import de.mkammerer.argon2.jna.JnaUint32;
import de.mkammerer.argon2.jna.Size_t;

Expand Down Expand Up @@ -45,4 +46,14 @@ salt, new Size_t(salt.length), hash, new Size_t(hash.length)
protected int callLibraryVerify(byte[] encoded, byte[] pwd) {
return Argon2Library.INSTANCE.argon2id_verify(encoded, pwd, new Size_t(pwd.length));
}

@Override
protected int callLibraryContext(Argon2_context.ByReference context) {
return Argon2Library.INSTANCE.argon2id_ctx(context);
}

@Override
protected int callLibraryVerifyContext(Argon2_context.ByReference context, byte[] rawHash) {
return Argon2Library.INSTANCE.argon2id_verify_ctx(context, rawHash);
}
}
146 changes: 144 additions & 2 deletions argon2-jvm-nolibs/src/main/java/de/mkammerer/argon2/BaseArgon2.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package de.mkammerer.argon2;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import de.mkammerer.argon2.jna.Argon2Library;
import de.mkammerer.argon2.jna.Argon2_context;
import de.mkammerer.argon2.jna.JnaUint32;
import de.mkammerer.argon2.jna.Size_t;

Expand Down Expand Up @@ -226,13 +229,50 @@ public HashResult hashAdvanced(int iterations, int memory, int parallelism, byte

int result = Argon2Library.INSTANCE.argon2_hash(
jnaIterations, jnaMemory, jnaParallelism, password, new Size_t(password.length), salt, new Size_t(salt.length),
hash, new Size_t(hash.length), encoded, new Size_t(encoded.length), getType().getJnaType(), new JnaUint32(version.getJnaVersion())
hash, new Size_t(hash.length), encoded, new Size_t(encoded.length), getType().getJnaType(), version.getJnaType()
);
checkResult(result);

return new HashResult(hash, Native.toString(encoded, ASCII));
}

@Override
public byte[] rawHashAdvanced(int iterations, int memory, int parallelism, char[] password, Charset charset, byte[] salt, byte[] secret, byte[] associatedData) {
byte[] pwd = toByteArray(password, charset);
return rawHashAdvanced(iterations, memory, parallelism, pwd, salt, secret, associatedData, defaultHashLength, Argon2Version.DEFAULT_VERSION);
}

@Override
public byte[] rawHashAdvanced(int iterations, int memory, int parallelism, byte[] password, byte[] salt, byte[] secret, byte[] associatedData, int hashLength, Argon2Version version) {
if (hashLength <= 0) throw new IllegalArgumentException("hashLength must be greater than zero");
Argon2_context.ByReference context = buildContextReference(iterations, memory, parallelism,
hashLength, password, salt, secret, associatedData, version);

int result = callLibraryContext(context);
wipeMemory(context);
checkResult(result);

return context.out.getByteArray(0, hashLength);
}

@Override
public boolean verifyAdvanced(int iterations, int memory, int parallelism, char[] password, Charset charset, byte[] salt, byte[] secret, byte[] associatedData, byte[] rawHash) {
byte[] pwd = toByteArray(password, charset);
return verifyAdvanced(iterations, memory, parallelism, pwd, salt, secret, associatedData, defaultHashLength, Argon2Version.DEFAULT_VERSION, rawHash);
}

@Override
public boolean verifyAdvanced(int iterations, int memory, int parallelism, byte[] password, byte[] salt, byte[] secret, byte[] associatedData, int hashLength, Argon2Version version, byte[] rawHash) {
if (hashLength <= 0) throw new IllegalArgumentException("hashLength must be greater than zero");
Argon2_context.ByReference context = buildContextReference(iterations, memory, parallelism,
hashLength, password, salt, secret, associatedData, version);

int result = callLibraryVerifyContext(context, rawHash);
wipeMemory(context);

return result == Argon2Library.ARGON2_OK;
}

@Override
public byte[] generateSalt() {
return generateSalt(defaultSaltLength);
Expand Down Expand Up @@ -297,6 +337,23 @@ protected int getDefaultHashLength() {
*/
protected abstract int callLibraryVerify(byte[] encoded, byte[] pwd);

/**
* Is called when the ctx hash function of the native library should be called.
*
* @param context Pointer to one Argon2 context.
* @return Return code.
*/
protected abstract int callLibraryContext(Argon2_context.ByReference context);

/**
* Is called when the ctx verify function of the native library should be called.
*
* @param context Pointer to one Argon2 context.
* @param rawHash Raw hash.
* @return Return code.
*/
protected abstract int callLibraryVerifyContext(Argon2_context.ByReference context, byte[] rawHash);

private String hashBytes(int iterations, int memory, int parallelism, byte[] pwd) {
byte[] salt = generateSalt();
return hashBytes(iterations, memory, parallelism, pwd, salt);
Expand Down Expand Up @@ -352,7 +409,7 @@ private boolean verifyBytes(String hash, byte[] pwd) {
* @param charset Charset of the password
* @return UTF-8 encoded byte array
*/
private byte[] toByteArray(char[] chars, Charset charset) {
private static byte[] toByteArray(char[] chars, Charset charset) {
assert chars != null;

CharBuffer charBuffer = CharBuffer.wrap(chars);
Expand All @@ -362,4 +419,89 @@ private byte[] toByteArray(char[] chars, Charset charset) {
Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
return bytes;
}

/**
* Builds a {@link Argon2_context} by the specified arguments.
*
* @param iterations Iterations.
* @param memory Memory.
* @param parallelism Parallelism.
* @param hashLength Hash length.
* @param password Password.
* @param salt Salt.
* @param secret Secret (nullable).
* @param associatedData Associated Data (nullable).
* @param version Version (nullable).
* @return {@link Argon2_context}
*/
private static Argon2_context.ByReference buildContextReference(int iterations, int memory, int parallelism, int hashLength, byte[] password, byte[] salt, byte[] secret, byte[] associatedData, Argon2Version version) {
Argon2_context.ByReference context = new Argon2_context.ByReference();

context.out = new Memory(hashLength);
context.outlen = new JnaUint32(hashLength);

context.pwd = new Memory(password.length);
context.pwd.write(0, password, 0, password.length);
context.pwdlen = new JnaUint32(password.length);

context.salt = new Memory(salt.length);
context.salt.write(0, salt, 0, salt.length);
context.saltlen = new JnaUint32(salt.length);

if (secret != null) {
context.secret = new Memory(secret.length);
context.secret.write(0, secret, 0, secret.length);
context.secretlen = new JnaUint32(secret.length);
} else {
context.secret = Pointer.NULL;
context.secretlen = new JnaUint32(0);
}

if (associatedData != null) {
context.ad = new Memory(associatedData.length);
context.ad.write(0, associatedData, 0, associatedData.length);
context.adlen = new JnaUint32(associatedData.length);
} else {
context.ad = Pointer.NULL;
context.adlen = new JnaUint32(0);
}

context.t_cost = new JnaUint32(iterations);
context.m_cost = new JnaUint32(memory);

/*
lanes and threads properties are set similar to the argon2.h c library function int argon_hash(...)
see: https://github.com/P-H-C/phc-winner-argon2/blob/master/include/argon2.h
*/
context.lanes = new JnaUint32(parallelism);
phxql marked this conversation as resolved.
Show resolved Hide resolved
context.threads = new JnaUint32(parallelism);

context.version = version != null ? version.getJnaType() : Argon2Version.DEFAULT_VERSION.getJnaType();

context.allocate_cbk = Pointer.NULL;
context.free_cbk = Pointer.NULL;

context.flags = new JnaUint32(0);

return context;
}

/**
* Wipes the confidential data from a previously created context except for
* the {@link Argon2_context#out} field as this stores the hash.
*
* @param context {@link Argon2_context}
*/
private static void wipeMemory(Argon2_context context) {
context.pwd.clear(context.pwdlen.longValue());
context.salt.clear(context.saltlen.longValue());

if (context.secretlen.longValue() != 0 && context.secret != Pointer.NULL) {
context.secret.clear(context.secretlen.longValue());
}

if (context.adlen.longValue() != 0 && context.ad != Pointer.NULL) {
context.ad.clear(context.adlen.longValue());
}
}
}
Loading