diff --git a/CHANGELOG b/CHANGELOG index ca50831..5fd130b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,11 +1,14 @@ # Releases +## v0.4.0 + +* change api from `extract(ikm,salt)` to a more RFC compliant `extract(salt,ikm)` (also `extractAndExpand()`) + ## v0.3.0 * refactor 'expand' byte handling to use byte buffer * rename package to `at.favre.lib.crypto` - ## v0.2.1 initial version \ No newline at end of file diff --git a/pom.xml b/pom.xml index 49e664b..cf605bf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ at.favre.lib hkdf - 0.3.0 + 0.4.0 jar HKDF-RFC5869 diff --git a/src/main/java/at/favre/lib/crypto/HKDF.java b/src/main/java/at/favre/lib/crypto/HKDF.java index 756950d..d45e4f1 100644 --- a/src/main/java/at/favre/lib/crypto/HKDF.java +++ b/src/main/java/at/favre/lib/crypto/HKDF.java @@ -20,7 +20,7 @@ *

* Simple Example: *

- *     byte[] pseudoRandomKey = HKDF.fromHmacSha256().extract(lowEntropyInput, null);
+ *     byte[] pseudoRandomKey = HKDF.fromHmacSha256().extract(null, lowEntropyInput);
  *     byte[] outputKeyingMaterial = HKDF.fromHmacSha256().expand(pseudoRandomKey, null, 64);
  * 
* @@ -95,14 +95,14 @@ public static HKDF from(HkdfMacFactory macFactory) { * strengthening the analytical results that back the HKDF design. * * - * @param inputKeyingMaterial data to be extracted (IKM) * @param salt optional salt value (a non-secret random value); + * @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 RFC 5869 Section 2.2 */ - public byte[] extract(byte[] inputKeyingMaterial, byte[] salt) { - return new Extractor(macFactory).execute(inputKeyingMaterial, salt); + public byte[] extract(byte[] salt, byte[] inputKeyingMaterial) { + return new Extractor(macFactory).execute(salt, inputKeyingMaterial); } /** @@ -138,14 +138,14 @@ public byte[] expand(byte[] pseudoRandomKey, byte[] info, int outLengthBytes) { /** * Convenience method for extract & expand in a single method * - * @param inputKeyingMaterial data to be extracted (IKM) * @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(byte[] inputKeyingMaterial, byte[] saltExtract, byte[] infoExpand, int outLengthByte) { - return new Expander(macFactory).execute(new Extractor(macFactory).execute(inputKeyingMaterial, saltExtract), infoExpand, outLengthByte); + public byte[] extractAndExpand(byte[] saltExtract, byte[] inputKeyingMaterial, byte[] infoExpand, int outLengthByte) { + return new Expander(macFactory).execute(new Extractor(macFactory).execute(saltExtract, inputKeyingMaterial), infoExpand, outLengthByte); } /** @@ -169,12 +169,12 @@ static final class Extractor { /** * Step 1 of RFC 5869 * - * @param inputKeyingMaterial data to be extracted (IKM) * @param salt optional salt value (a non-secret random value); + * @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 pseudorandom key (of hash length in bytes) (PRK) which can be used to expand */ - byte[] execute(byte[] inputKeyingMaterial, byte[] salt) { + byte[] execute(byte[] salt, byte[] inputKeyingMaterial) { if (salt == null || salt.length == 0) { salt = new byte[macFactory.createInstance(new byte[1]).getMacLength()]; } diff --git a/src/test/java/at/favre/lib/crypto/HKDFBenchmarkTest.java b/src/test/java/at/favre/lib/crypto/HKDFBenchmarkTest.java index 368c6c6..18266d0 100644 --- a/src/test/java/at/favre/lib/crypto/HKDFBenchmarkTest.java +++ b/src/test/java/at/favre/lib/crypto/HKDFBenchmarkTest.java @@ -34,7 +34,7 @@ public void benchmark() throws Exception { //benchmark different input sizes for (int j = 1024; j < 1024 * 1024; j += 1024) { input = RandomUtils.nextBytes(j); - byte[] pseudoRandomKey = HKDF.fromHmacSha256().extract(input, new byte[]{0x62, 0x58, (byte) 0x84, 0x2C}); + byte[] pseudoRandomKey = HKDF.fromHmacSha256().extract(new byte[]{0x62, 0x58, (byte) 0x84, 0x2C}, input); byte[] outputKeyingMaterial = HKDF.fromHmacSha256().expand(pseudoRandomKey, null, 32); assertTrue(outputKeyingMaterial.length > 0); count++; @@ -43,7 +43,7 @@ public void benchmark() throws Exception { //benchmark different input sizes for (int j = 16; j < 255 * 64; j += 16) { input = RandomUtils.nextBytes(128); - byte[] pseudoRandomKey = HKDF.fromHmacSha512().extract(input, new byte[]{0x62, 0x58, (byte) 0x84, 0x2C}); + byte[] pseudoRandomKey = HKDF.fromHmacSha512().extract(new byte[]{0x62, 0x58, (byte) 0x84, 0x2C}, input); byte[] outputKeyingMaterial = HKDF.fromHmacSha512().expand(pseudoRandomKey, null, j); assertTrue(outputKeyingMaterial.length > 0); count++; diff --git a/src/test/java/at/favre/lib/crypto/HKDFTest.java b/src/test/java/at/favre/lib/crypto/HKDFTest.java index 2416c0a..2ed56d3 100644 --- a/src/test/java/at/favre/lib/crypto/HKDFTest.java +++ b/src/test/java/at/favre/lib/crypto/HKDFTest.java @@ -27,7 +27,7 @@ public void setUp() throws Exception { public void quickStarTest() throws Exception { byte[] lowEntropyInput = new byte[]{0x62, 0x58, (byte) 0x84, 0x2C}; - byte[] pseudoRandomKey = HKDF.fromHmacSha256().extract(lowEntropyInput, null); + byte[] pseudoRandomKey = HKDF.fromHmacSha256().extract(null, lowEntropyInput); byte[] outputKeyingMaterial = HKDF.fromHmacSha256().expand(pseudoRandomKey, null, 64); assertEquals(64, outputKeyingMaterial.length); @@ -44,7 +44,7 @@ public void simpleUseCase() throws Exception { HKDF hkdf = HKDF.fromHmacSha256(); //extract the "raw" data to create output with concentrated entropy - byte[] pseudoRandomKey = hkdf.extract(userInput.getBytes(StandardCharsets.UTF_8), staticSalt32Byte); + byte[] pseudoRandomKey = hkdf.extract(staticSalt32Byte, userInput.getBytes(StandardCharsets.UTF_8)); //create expanded bytes for e.g. AES secret key and IV byte[] expandedAesKey = hkdf.expand(pseudoRandomKey, "aes-key".getBytes(StandardCharsets.UTF_8), 16); @@ -73,7 +73,7 @@ public void customHmac() throws Exception { HKDF hkdfMd5 = HKDF.from(new HkdfMacFactory.Default("HmacMD5", 16, Security.getProvider("SunJCE"))); byte[] lowEntropyInput = new byte[]{0x62, 0x58, (byte) 0x84, 0x2C}; - byte[] outputKeyingMaterial = hkdfMd5.extractAndExpand(lowEntropyInput, null, null, 32); + byte[] outputKeyingMaterial = hkdfMd5.extractAndExpand(null, lowEntropyInput, null, 32); assertEquals(32, outputKeyingMaterial.length); } @@ -87,16 +87,16 @@ public void checkLength() throws Exception { for (int i : counts) { ikm = RandomUtils.nextBytes(i); salt = RandomUtils.nextBytes(i * 2); - checkLength(HKDF.fromHmacSha256().extract(ikm, salt), 32); - checkLength(HKDF.fromHmacSha256().extract(ikm, null), 32); - checkLength(HKDF.fromHmacSha256().extract(ikm, new byte[0]), 32); - checkLength(HKDF.fromHmacSha512().extract(ikm, salt), 64); - checkLength(HKDF.fromHmacSha512().extract(ikm, null), 64); - checkLength(HKDF.from(HkdfMacFactory.Default.hmacSha1()).extract(ikm, salt), 20); + checkLength(HKDF.fromHmacSha256().extract(salt, ikm), 32); + checkLength(HKDF.fromHmacSha256().extract(null, ikm), 32); + checkLength(HKDF.fromHmacSha256().extract(new byte[0], ikm), 32); + checkLength(HKDF.fromHmacSha512().extract(salt, ikm), 64); + checkLength(HKDF.fromHmacSha512().extract(null, ikm), 64); + checkLength(HKDF.from(HkdfMacFactory.Default.hmacSha1()).extract(salt, ikm), 20); checkLength(HKDF.from(new HkdfMacFactory.Default("HmacMD5", 16)).extract(ikm, salt), 16); - assertFalse(Arrays.equals(HKDF.fromHmacSha256().extract(ikm, salt), HKDF.fromHmacSha512().extract(ikm, salt))); - assertFalse(Arrays.equals(HKDF.fromHmacSha256().extract(ikm, salt), HKDF.from(HkdfMacFactory.Default.hmacSha1()).extract(ikm, salt))); + assertFalse(Arrays.equals(HKDF.fromHmacSha256().extract(salt, ikm), HKDF.fromHmacSha512().extract(salt, ikm))); + assertFalse(Arrays.equals(HKDF.fromHmacSha256().extract(salt, ikm), HKDF.from(HkdfMacFactory.Default.hmacSha1()).extract(salt, ikm))); } } @@ -107,13 +107,13 @@ private void checkLength(byte[] prk, int refOutLength) { @Test public void testExtractFailures() throws Exception { try { - HKDF.fromHmacSha256().extract(null, RandomUtils.nextBytes(10)); + HKDF.fromHmacSha256().extract(RandomUtils.nextBytes(10), null); fail(); } catch (Exception ignored) { } try { - HKDF.fromHmacSha512().extract(new byte[0], null); + HKDF.fromHmacSha512().extract(null, new byte[0]); fail(); } catch (Exception ignored) { } @@ -174,24 +174,24 @@ public void testExpandFailures() throws Exception { @Test public void extractAndExpand() throws Exception { - checkLength(HKDF.from(HkdfMacFactory.Default.hmacSha1()).extractAndExpand(RandomUtils.nextBytes(16), RandomUtils.nextBytes(20), null, 80), 80); - checkLength(HKDF.fromHmacSha256().extractAndExpand(RandomUtils.nextBytes(16), RandomUtils.nextBytes(32), null, 80), 80); - checkLength(HKDF.fromHmacSha512().extractAndExpand(RandomUtils.nextBytes(250), RandomUtils.nextBytes(64), null, 80), 80); + checkLength(HKDF.from(HkdfMacFactory.Default.hmacSha1()).extractAndExpand(RandomUtils.nextBytes(20), RandomUtils.nextBytes(16), null, 80), 80); + checkLength(HKDF.fromHmacSha256().extractAndExpand(RandomUtils.nextBytes(32), RandomUtils.nextBytes(16), null, 80), 80); + checkLength(HKDF.fromHmacSha512().extractAndExpand(RandomUtils.nextBytes(64), RandomUtils.nextBytes(250), null, 80), 80); } @Test public void testLongInputExpand() throws Exception { byte[] longInput = RandomUtils.nextBytes(1024 * 1024); //1 MiB - checkLength(HKDF.fromHmacSha256().extract(longInput, null), 32); + checkLength(HKDF.fromHmacSha256().extract(null, longInput), 32); } @Test public void testLongOutputExtract() throws Exception { int outLengthSha512 = 255 * 64; - checkLength(HKDF.fromHmacSha512().expand(HKDF.fromHmacSha512().extract(new byte[16], null), null, outLengthSha512), outLengthSha512); + checkLength(HKDF.fromHmacSha512().expand(HKDF.fromHmacSha512().extract(null, new byte[16]), null, outLengthSha512), outLengthSha512); int outLengthSha256 = 255 * 32; - checkLength(HKDF.fromHmacSha256().expand(HKDF.fromHmacSha256().extract(new byte[16], null), null, outLengthSha256), outLengthSha256); + checkLength(HKDF.fromHmacSha256().expand(HKDF.fromHmacSha256().extract(null, new byte[16]), null, outLengthSha256), outLengthSha256); } @Test @@ -214,7 +214,7 @@ public void run() { byte[] ikm = RandomUtils.nextBytes(r.nextInt(12) + 12); byte[] salt = RandomUtils.nextBytes(r.nextInt(32)); - byte[] prk = hkdf.extract(ikm, salt); + byte[] prk = hkdf.extract(salt, ikm); assertTrue(hkdf.getMacFactory().createInstance(new byte[1]).getMacLength() == prk.length); diff --git a/src/test/java/at/favre/lib/crypto/RFC5869TestCases.java b/src/test/java/at/favre/lib/crypto/RFC5869TestCases.java index 82195c1..2aefc26 100644 --- a/src/test/java/at/favre/lib/crypto/RFC5869TestCases.java +++ b/src/test/java/at/favre/lib/crypto/RFC5869TestCases.java @@ -148,8 +148,8 @@ private void checkStep2Sha1(String prk, String info, int l, String okm) throws E } private void checkStep1(HkdfMacFactory macFactory, String ikm, String salt, String prk) throws Exception { - byte[] currentPrk = HKDF.from(macFactory).extract(Hex.decodeHex(ikm.toCharArray()), - Hex.decodeHex(salt.toCharArray())); + byte[] currentPrk = HKDF.from(macFactory).extract(Hex.decodeHex(salt.toCharArray()), + Hex.decodeHex(ikm.toCharArray())); assertArrayEquals(Hex.decodeHex(prk.toCharArray()), currentPrk); }