diff --git a/README.md b/README.md
index a0946ed..1912ea7 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Single Step KDF (NIST SP 800-56A)
+# Single Step KDF (NIST SP 800-56C)
-This is an implementation of the single-step key derivation function as described in [NIST SP 800-56A revision 1, chapter 4](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Cr1.pdf). It is an unopinionated approach towards the subject, allowing all 3 options (message digest, hmac and kmac) as H function and leaving open the exact format of the `fixedInfo` parameter.
+This is an implementation of the single-step key derivation function as described in [NIST SP 800-56C revision 1, chapter 4](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Cr1.pdf). It is an unopinionated approach towards the subject, allowing all 3 options (message digest, hmac and kmac) as H function and leaving open the exact format of the `fixedInfo` parameter.
[![Download](https://api.bintray.com/packages/patrickfav/maven/singlestep-kdf/images/download.svg)](https://bintray.com/patrickfav/maven/singlestep-kdf/_latestVersion)
[![Build Status](https://travis-ci.org/patrickfav/singlestep-kdf.svg?branch=master)](https://travis-ci.org/patrickfav/singlestep-kdf)
@@ -34,18 +34,42 @@ SecretKey secretKey = new SecretKeySpec(keyMaterial, "AES");
### Full Example
+NIST 800-56C specifies the KDF with an H-function which may be a [hash](https://en.wikipedia.org/wiki/Cryptographic_hash_function), [HMAC](https://en.wikipedia.org/wiki/HMAC) or [KMAC](https://keccak.team/index.html).
+
### Using with Message Digest (Option 1)
+Using Option 1, ie. `H(x) = hash(x)`, where hash is an approved hash function. Note that when you use this option, the salt parameter is not supported. If you want to incorporate a salt just include it into the `fixedInfo` parameter. **If you have no specific reason for choosing Option 1, I would always prefer Option 2 (HMAC) over this one.**
+
```java
-TBD
+// a shared secret provided by your protocol
+byte[] sharedSecret = Bytes.random(16).array();
+// fixed info to bind the key to the context
+byte[] fixedInfo = "macKey".getBytes();
+// salt is not directly supported with message digest, you may include it in fixedInfo
+byte[] keyMaterial = SingleStepKdf.fromSha256().derive(sharedSecret, 32, fixedInfo);
+SecretKey secretKey = new SecretKeySpec(keyMaterial, "AES");
```
### Using with HMAC (Option 2)
+Using Option 2, ie. `H(x) = HMAC-hash(salt, x)`, where HMAC-hash is an implementation of the HMAC algorithm (as defined in FIPS 198) employing an approved hash function. A salt which serves as the HMAC key, and x (the input to H) is a bit string that serves as the HMAC "message". This implemention can use any `Mac` implementation. If no salt is provided, an empty array is internally initialized.
+```java
+byte[] keyMaterial = SingleStepKdf.fromHmacSha256().derive(sharedSecret, 32, salt, fixedInfo);
+ SecretKey secretKey = new SecretKeySpec(keyMaterial, "AES");
+```
+
+### Using with KMAC (Option 3)
+
+KMAC is a MAC using [SHA-3/Keccak](https://en.wikipedia.org/wiki/SHA-3). Unlike SHA-1 and SHA-2, [Keccak](http://keccak.noekeon.org/) does not have the [length-extension weakness](https://en.wikipedia.org/wiki/Length_extension_attack), hence does not need the HMAC nested construction. Instead, MAC computation can be performed by simply prepending the message with the key. Java has a SHA-3 implementation [since version 9](https://openjdk.java.net/jeps/287). This implementation supports Java 7, so no default implementation is present for KMAC. It is probably quite easy to implement it yourself using either the `HFunctionFactory.MacFactory` or implementing yourself with `HFunction`.
### Using custom Message Digest / HMAC implementation
+Default implementation exists for the h-function which can be used for any `MessageDigest` or `Mac`. It is also possible to implement it from scratch by using the `HFunction` interface. A factory is used to generate instances. Thes can be used like this:
+
```java
-TBD
+// create instance with sha1 as backing hash function
+SingleStepKdf sha1Kdf = SingleStepKdf.from(new HFunctionFactory.Default.DigestFactory("SHA-1"));
+// creat instance with HMAC-SHA1 as backing h function
+SingleStepKdf hmacSha1Kdf = SingleStepKdf.from(new HFunctionFactory.Default.DigestFactory("HmacSHA1"));
```
## Download
@@ -75,7 +99,25 @@ Add to your `build.gradle` module dependencies:
## Description
-TBD
+The following is summarized and shorted version of [NIST 800-56C Rev1](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Cr1.pdf):
+
+Single Step KDF specifies a family of approved key-derivation functions (KDFs) that are executed in a single step; The input to each specified KDF includes the shared secret generated during the execution of a key-establishment scheme specified in [SP 800-56A](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf) or [SP 800-56B](https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-56br1.pdf), an indication of the desired bit length of the keying material to be output, and, perhaps, other information (as determined by the particular implementation of the key-establishment scheme and/or key-derivation function).
+
+Implementations of these one-step KDFs depend upon the choice of an auxiliary function H, which can be either:
+
+ 1. an approved hash function, denoted as hash, as defined in [FIPS 180](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.180-4.pdf) or [FIPS 202](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf);
+ 2. HMAC with an approved hash function, hash, denoted as HMAC-hash, and defined in [FIPS 198](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf); or
+ 3. a KMAC variant, as defined in [SP 800-185](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf). H shall be chosen in accordance with the selection requirements specified in [800-56C Rev1/Section 7](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Cr1.pdf).
+
+When an approved MAC algorithm (HMAC or KMAC) is used to define the auxiliary function H, it is permitted to use a known salt value as the MAC key. In such cases, it is assumed that the MAC algorithm will satisfy the following property (for each of its supported security strengths):
+
+Given knowledge of the key `k`, and (perhaps) partial knowledge of a message `x` that includes an unknown substring `z`, it must be computationally infeasible to predict the (as-yet-unseen) value of `MAC(k, x, …)` with a probability of success that is a significant improvement over simply guessing either the MAC value or the value of `z`, even if one has already seen the values of `MAC(kj, xj, …)` for a feasible number of other `(kj, xj)` pairs, where each key `kj` is known and each (partially known) message `xj` includes the same unknown substring `z`, provided that none of the `(kj, xj)` pairs is identical to `(k, x)`.
+
+This property is consistent with the use of the MAC algorithm as the specification of a family of pseudorandom functions defined on the appropriate message space and indexed by the choice of MAC key. Under Option 2 and Option 3 of the KDF specification below, the auxiliary function H is a particular selection from such a family.
+
+### Two Step Key Derivation Function
+
+NIST 800-56C Rev1 also describes a two step kdf with a extract and expand phase. The most prominent implementation of it is [HKDF (RFC5869)](https://tools.ietf.org/html/rfc5869). A java implementation of it can be [found here](https://github.com/patrickfav/hkdf).
## Digital Signatures
@@ -122,6 +164,11 @@ Use maven (3.1+) to create a jar including all dependencies
* Java 7
* Maven
+## Relevant Libraries
+
+* [BCyrpt Password Hash Function (Java)](https://github.com/patrickfav/bcrypt)
+* [HKDF [RFC5869] Two-Step KDF (Java)](https://tools.ietf.org/html/rfc5869)
+
# License
Copyright 2018 Patrick Favre-Bulle
diff --git a/pom.xml b/pom.xml
index 3a91b07..4fd71f4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,8 +9,8 @@
0.1.0
jar
- Single Step KDF (NIST SP 800-56A)
- Single Step Key Derivation Function as defined in NIST SP 800-56A, Sect 5.8.1 as well as 800-56C
+ Single Step KDF (NIST SP 800-56C)
+ Single Step Key Derivation Function as defined in NIST SP 800-56C, Sect 5.8.1 as well as 800-56C
https://github.com/patrickfav/singlestep-kdf
2018
diff --git a/src/main/java/at/favre/lib/crypto/SingleStepKdf.java b/src/main/java/at/favre/lib/crypto/SingleStepKdf.java
index 8781fdb..ca3af49 100644
--- a/src/main/java/at/favre/lib/crypto/SingleStepKdf.java
+++ b/src/main/java/at/favre/lib/crypto/SingleStepKdf.java
@@ -171,6 +171,7 @@ public byte[] derive(byte[] sharedSecretZ,
* conveyed in OtherInput. See [SP 800-56A] and [SP 800-56B] for more detailed
* recommendations concerning the format and content of FixedInfo.
* @return derived keying material (to use as secret key)
+ * @throws IllegalArgumentException if salt is used with message digest as H function
*/
public byte[] derive(byte[] sharedSecretZ,
int outLengthBytes, byte[] salt,
@@ -190,7 +191,7 @@ public byte[] derive(byte[] sharedSecretZ,
digest.init(salt);
} else if (salt != null) {
// fail fast so caller is not under the impression a salt is used if it is just discarded
- throw new IllegalArgumentException("used h-function does not require a salt and none should be provided");
+ throw new IllegalArgumentException("Used h-function does not require a salt and none should be provided. You may include it in the fixedInfo parameter.");
}
ByteBuffer buffer = ByteBuffer.allocate(outLengthBytes);
diff --git a/src/test/java/at/favre/lib/crypto/SingleStepKdfTest.java b/src/test/java/at/favre/lib/crypto/SingleStepKdfTest.java
index a4a8f95..806e86f 100644
--- a/src/test/java/at/favre/lib/crypto/SingleStepKdfTest.java
+++ b/src/test/java/at/favre/lib/crypto/SingleStepKdfTest.java
@@ -17,13 +17,48 @@ public void quickstart() {
byte[] sharedSecret = Bytes.random(16).array();
// a salt; if you don't have access to a salt use SingleStepKdf.fromSha256() or similar
byte[] salt = Bytes.random(16).array();
- // other info to bind the key to the context, see the NIST spec for more detail
- byte[] otherInfo = "macKey".getBytes();
- byte[] keyMaterial = SingleStepKdf.fromHmacSha256().derive(sharedSecret, 32, salt, otherInfo);
+ // fixed info to bind the key to the context, see the NIST spec for more detail (previously otherInfo)
+ byte[] fixedInfo = "macKey".getBytes();
+ byte[] keyMaterial = SingleStepKdf.fromHmacSha256().derive(sharedSecret, 32, salt, fixedInfo);
SecretKey secretKey = new SecretKeySpec(keyMaterial, "AES");
assertNotNull(secretKey);
}
+ @Test
+ public void quickstartMessageDigest() {
+ // a shared secret provided by your protocol
+ byte[] sharedSecret = Bytes.random(16).array();
+ // fixed info to bind the key to the context
+ byte[] fixedInfo = "macKey".getBytes();
+ // salt is not directly supported with message digest, you may include it in fixedInfo
+ byte[] keyMaterial = SingleStepKdf.fromSha256().derive(sharedSecret, 32, fixedInfo);
+ SecretKey secretKey = new SecretKeySpec(keyMaterial, "AES");
+ assertNotNull(secretKey);
+ }
+
+ @Test
+ public void quickstartHmac() {
+ byte[] salt = Bytes.random(16).array();
+ // a shared secret provided by your protocol
+ byte[] sharedSecret = Bytes.random(16).array();
+ // fixed info to bind the key to the context
+ byte[] fixedInfo = "macKey".getBytes();
+ // salt is not directly supported with message digest, you may include it in fixedInfo
+ byte[] keyMaterial = SingleStepKdf.fromHmacSha256().derive(sharedSecret, 32, salt, fixedInfo);
+ SecretKey secretKey = new SecretKeySpec(keyMaterial, "AES");
+ assertNotNull(secretKey);
+ }
+
+ @Test
+ public void quickstartCustomImpl() {
+ // create instance with sha1 as backing hash function
+ SingleStepKdf sha1Kdf = SingleStepKdf.from(new HFunctionFactory.Default.DigestFactory("SHA-1"));
+ // creat instance with HMAC-SHA1 as backing h function
+ SingleStepKdf hmacSha1Kdf = SingleStepKdf.from(new HFunctionFactory.Default.DigestFactory("HmacSHA1"));
+
+
+ }
+
@Test
public void testIllegalOutLength() {
SingleStepKdf kdf = SingleStepKdf.fromSha256();