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();