Skip to content

Commit

Permalink
Update Readme
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickfav committed Nov 22, 2018
1 parent e28b5f0 commit 9ae51a0
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 11 deletions.
57 changes: 52 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<version>0.1.0</version>
<packaging>jar</packaging>

<name>Single Step KDF (NIST SP 800-56A)</name>
<description>Single Step Key Derivation Function as defined in NIST SP 800-56A, Sect 5.8.1 as well as 800-56C
<name>Single Step KDF (NIST SP 800-56C)</name>
<description>Single Step Key Derivation Function as defined in NIST SP 800-56C, Sect 5.8.1 as well as 800-56C
</description>
<url>https://github.com/patrickfav/singlestep-kdf</url>
<inceptionYear>2018</inceptionYear>
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/at/favre/lib/crypto/SingleStepKdf.java
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
Expand Down
41 changes: 38 additions & 3 deletions src/test/java/at/favre/lib/crypto/SingleStepKdfTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 9ae51a0

Please sign in to comment.