Skip to content

Commit

Permalink
Extends the API with SecretKey types in addition to raw byte[]
Browse files Browse the repository at this point in the history
This is to make the lib compatible to security framework that require
the use of the JCE to work properly (e.g. HSM front ends).

Breaks the hkdfMacFactory interface.

refs #4
  • Loading branch information
patrickfav committed Jun 13, 2019
1 parent f9be6c4 commit 0ae0c2f
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 43 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Releases

## Unreleased

* refactor HkdfMacFactory to accept `SecretKey` types instead of byte array - helps to be compatible with some security frameworks #4

### Breaking Change

The interface HkdfMacFactory changed to accept `SecretKey` and two new methods where added for creating
a secret key from a raw byte source and to return the mac length in bytes. See the default implementation
for details on how to implement this if you need a custom impl.

## v1.0.2

* add OSWAP dependency check plugin to Maven POM #3
Expand Down
61 changes: 53 additions & 8 deletions src/main/java/at/favre/lib/crypto/HKDF.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package at.favre.lib.crypto;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.nio.ByteBuffer;

/**
Expand Down Expand Up @@ -111,13 +112,27 @@ public static HKDF from(HkdfMacFactory macFactory) {
* strengthening the analytical results that back the HKDF design.
* </blockquote>
*
* @param salt optional salt value (a non-secret random value);
* @param salt optional salt value (a non-secret random value) (can be null)
* @param inputKeyingMaterial data to be extracted (IKM)
* if not provided, it is set to a array of hash length of zeros.
* @return a new byte array pseudo random key (of hash length in bytes) (PRK) which can be used to expand
* @see <a href="https://tools.ietf.org/html/rfc5869#section-2.2">RFC 5869 Section 2.2</a>
*/
public byte[] extract(byte[] salt, byte[] inputKeyingMaterial) {
return extract(macFactory.createSecretKey(salt), inputKeyingMaterial);
}

/**
* Use this if you require {@link SecretKey} types by your security framework. See also
* {@link HkdfMacFactory#createSecretKey(byte[])}.
* <p>
* See {@link #extract(byte[], byte[])} for description.
*
* @param salt optional salt value (a non-secret random value) (can be null)
* @param inputKeyingMaterial data to be extracted (IKM)
* @return a new byte array pseudo random key (of hash length in bytes) (PRK) which can be used to expand
*/
public byte[] extract(SecretKey salt, byte[] inputKeyingMaterial) {
return new Extractor(macFactory).execute(salt, inputKeyingMaterial);
}

Expand Down Expand Up @@ -148,6 +163,21 @@ public byte[] extract(byte[] salt, byte[] inputKeyingMaterial) {
* @see <a href="https://tools.ietf.org/html/rfc5869#section-2.3">RFC 5869 Section 2.3</a>
*/
public byte[] expand(byte[] pseudoRandomKey, byte[] info, int outLengthBytes) {
return expand(macFactory.createSecretKey(pseudoRandomKey), info, outLengthBytes);
}

/**
* Use this if you require {@link SecretKey} types by your security framework. See also
* {@link HkdfMacFactory#createSecretKey(byte[])}.
* <p>
* See {@link #expand(byte[], byte[], int)} for description.
*
* @param pseudoRandomKey a pseudo random key of at least hmac hash length in bytes (usually, the output from the extract step)
* @param info optional context and application specific information; may be null
* @param outLengthBytes length of output keying material in bytes
* @return new byte array of output keying material (OKM)
*/
public byte[] expand(SecretKey pseudoRandomKey, byte[] info, int outLengthBytes) {
return new Expander(macFactory).execute(pseudoRandomKey, info, outLengthBytes);
}

Expand All @@ -161,7 +191,22 @@ public byte[] expand(byte[] pseudoRandomKey, byte[] info, int outLengthBytes) {
* @return new byte array of output keying material (OKM)
*/
public byte[] extractAndExpand(byte[] saltExtract, byte[] inputKeyingMaterial, byte[] infoExpand, int outLengthByte) {
return new Expander(macFactory).execute(new Extractor(macFactory).execute(saltExtract, inputKeyingMaterial), infoExpand, outLengthByte);
return extractAndExpand(macFactory.createSecretKey(saltExtract), inputKeyingMaterial, infoExpand, outLengthByte);
}

/**
* Convenience method for extract &amp; expand in a single method
*
* @param saltExtract optional salt value (a non-secret random value);
* @param inputKeyingMaterial data to be extracted (IKM)
* @param infoExpand optional context and application specific information; may be null
* @param outLengthByte length of output keying material in bytes
* @return new byte array of output keying material (OKM)
*/
public byte[] extractAndExpand(SecretKey saltExtract, byte[] inputKeyingMaterial, byte[] infoExpand, int outLengthByte) {
return new Expander(macFactory).execute(macFactory.createSecretKey(
new Extractor(macFactory).execute(saltExtract, inputKeyingMaterial)),
infoExpand, outLengthByte);
}

/**
Expand Down Expand Up @@ -190,9 +235,9 @@ static final class Extractor {
* if not provided, it is set to a array of hash length of zeros.
* @return a new byte array pseudorandom key (of hash length in bytes) (PRK) which can be used to expand
*/
byte[] execute(byte[] salt, byte[] inputKeyingMaterial) {
if (salt == null || salt.length == 0) {
salt = new byte[macFactory.createInstance(new byte[1]).getMacLength()];
byte[] execute(SecretKey salt, byte[] inputKeyingMaterial) {
if (salt == null) {
salt = macFactory.createSecretKey(new byte[macFactory.getMacLengthBytes()]);
}

if (inputKeyingMaterial == null || inputKeyingMaterial.length <= 0) {
Expand All @@ -219,14 +264,14 @@ static final class Expander {
* @param outLengthBytes length of output keying material in bytes (must be <= 255 * mac hash length)
* @return new byte array of output keying material (OKM)
*/
byte[] execute(byte[] pseudoRandomKey, byte[] info, int outLengthBytes) {
byte[] execute(SecretKey pseudoRandomKey, byte[] info, int outLengthBytes) {

if (outLengthBytes <= 0) {
throw new IllegalArgumentException("out length bytes must be at least 1");
}

if (pseudoRandomKey == null || pseudoRandomKey.length <= 0) {
throw new IllegalArgumentException("provided pseudoRandomKey must be at least of size 1 and not null");
if (pseudoRandomKey == null) {
throw new IllegalArgumentException("provided pseudoRandomKey must not be null");
}

Mac hmacHasher = macFactory.createInstance(pseudoRandomKey);
Expand Down
45 changes: 40 additions & 5 deletions src/main/java/at/favre/lib/crypto/HkdfMacFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,22 @@ public interface HkdfMacFactory {
* @param key the key used, must not be null
* @return a new mac instance
*/
Mac createInstance(byte[] key);
Mac createInstance(SecretKey key);

/**
* Get the length of the outputted mac in bytes
*
* @return the length of mac output in bytes
*/
int getMacLengthBytes();

/**
* Creates a secret key from a byte raw key material to be used with {@link #createInstance(SecretKey)}
*
* @param rawKeyMaterial the raw key
* @return wrapped as secret key instance or null if input is null or empty
*/
SecretKey createSecretKey(byte[] rawKeyMaterial);

/**
* Default implementation
Expand Down Expand Up @@ -95,10 +110,18 @@ public Default(String macAlgorithmName, Provider provider) {
}

@Override
public Mac createInstance(byte[] key) {
public Mac createInstance(SecretKey key) {
try {
SecretKey secretKey = new SecretKeySpec(key, macAlgorithmName);
Mac mac = createMacInstance();
mac.init(key);
return mac;
} catch (Exception e) {
throw new IllegalStateException("could not make hmac hasher in hkdf", e);
}
}

private Mac createMacInstance() {
try {
Mac hmacInstance;

if (provider == null) {
Expand All @@ -107,13 +130,25 @@ public Mac createInstance(byte[] key) {
hmacInstance = Mac.getInstance(macAlgorithmName, provider);
}

hmacInstance.init(secretKey);
return hmacInstance;
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("defined mac algorithm was not found", e);
} catch (Exception e) {
throw new IllegalStateException("could not make hmac hasher in hkdf", e);
throw new IllegalStateException("could not create mac instance in hkdf", e);
}
}

@Override
public int getMacLengthBytes() {
return createMacInstance().getMacLength();
}

@Override
public SecretKey createSecretKey(byte[] rawKeyMaterial) {
if (rawKeyMaterial == null || rawKeyMaterial.length <= 0) {
return null;
}
return new SecretKeySpec(rawKeyMaterial, macAlgorithmName);
}
}
}
Loading

0 comments on commit 0ae0c2f

Please sign in to comment.