RFR: Proposed HKDF API (JDK-8145255)

Jamil Nimeh jamil.j.nimeh at oracle.com
Fri Apr 15 21:33:03 UTC 2016


Hi Mike, thanks for your comments and suggestions, I need to digest some 
of this but I have some follow-up questions to start:

On 04/15/2016 12:54 PM, Michael StJohns wrote:
> Hi Jamil -
>
> I need to look at this a bit more, but I think its probably 
> incorrect.  Basically, KDFs should be able to provide multiple keys 
> from a single call, not a single key as you've described here.
Would that mean that a second call to Kdf.generateKey() with no prior 
reinitialization would create a new key of whatever output length it was 
initialized with, assuming the end state from the previous call?  In 
other words, the output from generateKey1 and generateKey2 would yield 
the same bytes as it would if a single call was made for the combined 
length and then segmented after the fact?
> They may also may need to provide non-key cryptographic material such 
> as IV's.  The TLS1.2 KDF (PRF) is an example of this.
>
> There's this other problem that you're outputting unwrapped data - 
> which means you can never define this to run as a hardware module as 
> the outputs are byte arrays.
The output from SunJCE's implementation (the only one we're doing right 
off the bat) would start as a byte array internally from 
HKDFKeyGenerator, true.  But SunJCE's implementation is supposed to be 
native and not assuming outside hardware, AFAIK.  If we had to interact 
with an HSM, I would think we'd need to define an HKDFKeyGenerator class 
within the SunPKCS11 provider, or some other provider capable of 
speaking to said HSM.

My PKCS#11 is a bit rusty, but if the underlying provider had its own 
HDKF mechanism then I would guess we could call into C_DeriveKey using 
that mech and the template could mark the resulting key as sensitive or 
sensitive/non-extractable.  If it didn't have an HKDF mechanism (OASIS 
hasn't defined an HKDF mechanism yet, have they?) then we could 
implement HKDF in terms of HMAC calls, but that doesn't solve the 
concern you have, since IIRC C_Sign calls would be used and those return 
byte arrays which would expose the key as bytes until a C_CreateObject 
could be called.

Assuming though that SunPKCS11 could talk to an HSM that had an HKDF 
mech in their PKCS#11 library, and we added HKDF support to that 
provider, the output from KeyGenerator.generateKey() is a SecretKey, one 
that would I believe be a wrapper around an object handle rather than 
holding the actual key bytes (I'd have to go look to be sure). I think 
that's why calls like Key.getEncoded() are not guaranteed to return 
encoded data...in some cases like a PKCS#11 sensitive key we wouldn't be 
allowed access to it.
>
> So I'd generalize this more as: (This is a single pass design - I 
> haven't gone back and tweaked the obvious mistakes - not enough time 
> right now).
Are you suggesting that your KDF solution below is accessed through the 
KeyGenerator API and the implementation would derive from 
KeyGeneratorSpi?  I ask because two of the forms of init you provide 
below are not part of the API.

Or are you thinking of it as standalone or derived from another API?  
Some of the calls make it look like it's intended to be a KeyGenerator 
and that's why I ask.
>
> Kdf.getInstance(String algName);
>
> Kdf.init (AlgorithmParameterSpec kdfSpec);  // only one key produced, 
> well defined by the algName, no parameters required.
> Kdf.init (AlgorithmParameterSpec kdfSpec, KDFAlgorithmParameterSpec 
> keyspec);
> Kdf.init (AlgorithmParameterSpec kdfSpec, KDFAlgorithmParameterSpec[] 
> keyspecs);
>
> Key xxx = Kdf.generateKey();
> Key[] xxx = Kdf.generateKeys();
Why do we need an array form for generateKeys?  Is that just a 
convenience?  Is it inspired by the TLS-style key/mac/IV derivation?
>
> public interface KDFAlgorithmParameterSpec extends 
> AlgorithmParameterSpec {
>     public KDFParameters[] getParams();
> }
>
> public class SecretKeyAlgorithmSpec implements 
> KDFAlgorithmParameterSpec  {
>     public SecretKeyAlgorithmSpec (String algorithm, int size, 
> KDFParameters ... params );
>     public int getSize();
>     public String getAlgorithm();
>     public KDFParameters[] getParams();
> }
>
> public interface KDFParameters{};
>
> KDFParameters is a marker interface to deal with mixin parameters that 
> are specific to the key material.
>
> I'd define HKDF as the instance type for the Kdf.getInstance().
>
> I'd define HKDFAlgorithmParameterSpec  and place the info on which PRF 
> function to use in that spec along with any other instance specific 
> stuff.  This is also where you put the context and label mixin data.
>
>
> I'd then define the "HKDFExtract" and "HKDFExpand" as two separate 
> KDFs.  The first produces a single Key of instance type MasterKey 
> (which is a sub type of SecretKey) which is a well known length based 
> on the provided PRF.  The second produces whatever you ask for based 
> on the key spec's you provide.
>
> This generalizes well to other KDFs including SP800-108.
I'll have to spend a little time digesting what you put down here. 
Interesting stuff.

--Jamil
>
> Mike
>
>
>
>
>
>
>
>
> On 4/15/2016 2:06 PM, Jamil Nimeh wrote:
>> Hello all,
>>
>> We are looking to add HKDF support as a KeyGenerator into OpenJDK 9.  
>> This will be available for general-purpose use. I've documented the 
>> proposed API below.
>>
>> RFE: https://bugs.openjdk.java.net/browse/JDK-8145255
>> Proof-of-concept implementation: 
>> http://cr.openjdk.java.net/~jnimeh/reviews/8145255/webrev.1/
>>
>> A set of new standard algorithm names would be created that define 
>> the HMAC algorithm used with HKDF: HkdfSHA224, HkdfSHA256, HkdfSHA384 
>> and HkdfSHA512.  We can at a later date include HKDF variants that 
>> use other supported HMAC algorithms.
>>
>> Instantiation:
>> --------------
>> In order to do HKDF derivations, a KeyGenerator of the appropriate 
>> type must be obtained.  This is done using one of the 
>> KeyGenerator.getInstance methods, specifying the underlying HMAC type 
>> using one of the names listed above.
>>
>> Initialization:
>> ---------------
>> The resulting HKDF KeyGenerator, in order to be compliant with the 
>> KeyGenerator API, may be used to generate a key without any further 
>> initialization.  In this case, the Extract-then-Expand operation will 
>> be performed using null salt and application-specific information, 
>> and a random input key.  The resulting output key will be sized to 
>> the output length of the underlying HMAC.  This can be used as a way 
>> to obtain a random key.
>>
>> The HKDF KeyGenerator supports all five flavors of the init method.  
>> The resulting behavior differs between each flavor however.
>>
>> KeyGenerator.init(SecureRandom):
>> --------------------------------
>> If this version of the init method is used, the KeyGenerator will 
>> behave similarly to the default initialization, with the exception 
>> that the caller may provide their own SecureRandom source for the 
>> input key.  A null value is allowed, in which case the implementation 
>> will obtain the default SecureRandom implementation for generating 
>> the input key.
>>
>> KeyGenerator.init(int);
>> KeyGenerator.init(int, SecureRandom);
>> -------------------------------------
>> These two versions of the KeyGenerator allow the caller to provide 
>> the resulting key length and, in the second case provide a 
>> SecureRandom source.  The caller must provide a non-negative length 
>> value in bytes.  A value of zero is allowed and returns a key of the 
>> same length as the underlying HMAC.  In both forms the 
>> Extract-then-Expand operation will be performed with null salt and 
>> application-specific information, and a random input key.  If a 
>> SecureRandom value is provided, its behavior is similar to 
>> KeyGenerator.init(SecureRandom).
>>
>> KeyGenerator.init(AlgorithmParameterSpec);
>> KeyGenerator.init(AlgorithmParameterSpec, SecureRandom);
>> --------------------------------------------------------
>> These versions of the init method allow the caller to customize the 
>> input parameters to the HKDF generator as well as select the HKDF 
>> function to perform.
>>
>> Users desiring a specific HKDF function would initialize it using one 
>> of three new AlgorithmParameterSpec classes: 
>> HkdfExtractParameterSpec, HkdfExpandParameterSpec, or 
>> HkdfParameterSpec.  These three parameter spec classes are used to 
>> initialize the HKDF key generator to perform the HKDF-Expand, 
>> HKDF-Extract or a combination HKDF-Extract-then-Expand operation, 
>> respectively.
>>
>> The init(AlgorithmParameterSpec, SecureRandom) ignores the 
>> SecureRandom parameter, and requires that input key material (IKM) or 
>> a pseudorandom key (PRK) is provided.
>>
>> Key Generation:
>> ---------------
>> Once initialized (default or via one of the init methods) a key is 
>> generated by calling KeyGenerator.generateKey().
>>
>>
>> The Specification:
>> ------------------
>> Three new AlgorithmParameterSpec classes will be created to 
>> initialize HKDF KeyGenerator objects:
>> HkdfParameterSpec: For performing the Extract-then-Expand operation
>> HkdfExtractParameterSpec: For performingthe HKDF-Extract operation
>> HkdfExpandParameterSpec: For performing the HKDF-Expand operation
>>
>>
>> /**
>>  * Parameters for the Extract operation of the HMAC-based 
>> Extract-and-Expand
>>  * Key Derivation Function (HKDF). The HKDF function is defined in
>>  * <a href="http://tools.ietf.org/html/rfc5869">RFC 5869</a>.
>>  * This class is used to initialize KeyGenerators in the HKDF family of
>>  * generators, specifically for the HKDF Extract function.
>>  * <p>
>>  * Here is an example of how an HkdfExtractParameterSpec would be 
>> used to
>>  * initialize an HKDF KeyGenerator:
>>  * <pre>
>>  *     byte[] salt;
>>  *     SecretKey inputKey;
>>  *     SecretKey resultingPRK;
>>  *
>>  *     // salt and inputKey values populated with data
>>  *     ...
>>  *
>>  *     // Get an instance of the HKDF KeyGenerator
>>  *     hkdfGen = KeyGenerator.getInstance("HkdfSHA256");
>>  *
>>  *     // Create the spec object and use it to initialize the generator.
>>  *     hkdfGen.init(new HkdfExtractParameterSpec(salt, inputKey));
>>  *
>>  *     // Generate the PRK
>>  *     resultingPRK = hkdfGen.generateKey();
>>  * </pre>
>>  *
>>  * @since 9
>>  */
>> public final class HkdfExtractParameterSpec implements 
>> AlgorithmParameterSpec {
>>
>>     /**
>>      * Constructs a new HkdfExtractParameterSpec with the given salt 
>> value
>>      * and key material
>>      *
>>      * @param salt the salt value, or {@code null} if not specified.  
>> The
>>      *      contents of the array are copied to protect against 
>> subsequent
>>      *      modification.
>>      * @param inputKey the Input Keying Material (IKM).
>>      *
>>      * @throws NullPointerException if {@code inputKey} is {@code null}.
>>      */
>>     public HkdfExtractParameterSpec(byte[] salt, SecretKey inputKey);
>>
>>     /**
>>      * Returns the Input Keying Material (IKM).
>>      *
>>      * @return the Input Keying Material.
>>      */
>>     public SecretKey getIKM();
>>
>>     /**
>>      * Returns the salt value.
>>      *
>>      * @return a copy of the salt value or {@code null} if no salt 
>> was provided.
>>      */
>>     public byte[] getSalt();
>> }
>>
>> HkdfExpandParameterSpec:
>> ------------------------
>> /**
>>  * Parameters for the Expand operation of the HMAC-based 
>> Extract-and-Expand
>>  * Key Derivation Function (HKDF). The HKDF function is defined in
>>  * <a href="http://tools.ietf.org/html/rfc5869">RFC 5869</a>.
>>  * <p>
>>  * Here is an example of how an HkdfExpandParameterSpec would be used to
>>  * initialize an HKDF KeyGenerator:
>>  * <pre>
>>  *     byte[] info;
>>  *     SecretKey pseudoRandomKey;
>>  *     SecretKey resultingKey;
>>  *
>>  *     // pseudoRandomKey and context info values populated with data
>>  *     ...
>>  *
>>  *     // Get an instance of the HKDF KeyGenerator
>>  *     hkdfGen = KeyGenerator.getInstance("HkdfSHA256");
>>  *
>>  *     // Create the spec object and use it to initialize the generator.
>>  *     // Ask for a 64-byte key to be output.
>>  *     hkdfGen.init(new HkdfExpandParameterSpec(pseudoRandomKey, 
>> info, 64));
>>  *
>>  *     // Generate the key
>>  *     resultingKey = hkdfGen.generateKey();
>>  * </pre>
>>  *
>>  * @since 9
>>  */
>> public final class HkdfExpandParameterSpec implements 
>> AlgorithmParameterSpec {
>>
>>     /**
>>      * Constructs a new HkdfExpandParameterSpec.
>>      *
>>      * @param prk the pseudorandom key used for HKDF-Expand.
>>      * @param info optional context and application specific 
>> information or
>>      *      {@code null} if no info data is provided.  The contents 
>> of the
>>      *      array are copied to protect against subsequent modification.
>>      * @param outLen the length in bytes of the output data
>>      *
>>      * @throws NullPointerException if {@code prk} is {@code null}.
>>      * @throws IllegalArgumentException if {@code outLen} is a
>>      *      non-positive value.
>>      */
>>     public HkdfExpandParameterSpec(SecretKey prk, byte[] info, int 
>> outLen);
>>
>>     /**
>>      * Returns a {@link SecretKey} object containing the pseudorandom 
>> key (PRK).
>>      *
>>      * @return a {@link SecretKey} object containing the pseudorandom 
>> key.
>>      */
>>     public SecretKey getPRK();
>>
>>     /**
>>      * Returns a copy of the context and application specific 
>> information.
>>      *
>>      * @return a copy of the context and application specific 
>> information.
>>      *      This may be {@code null} or empty if no specific 
>> information was
>>      *      provided.
>>      */
>>     public byte[] getInfo();
>>
>>     /**
>>      * Returns the length in bytes of the output key to be produced.
>>      *
>>      * @return the length in bytes of the output key to be produced.
>>      */
>>     public int getOutputLength();
>> }
>>
>>
>> HkdfParameterSpec:
>> ------------------
>>
>> /**
>>  * Parameters for the combined Extract-then-Expand operation of the 
>> HMAC-based
>>  * Extract-and-Expand Key Derivation Function (HKDF). The HKDF function
>>  * is defined in <a href="http://tools.ietf.org/html/rfc5869">RFC 
>> 5869</a>.
>>  * <p>
>>  * Here is an example of how an HkdfParameterSpec would be used to 
>> initialize
>>  * an HKDF KeyGenerator:
>>  * <pre>
>>  *     byte[] salt;
>>  *     byte[] info;
>>  *     SecretKey inputKey;
>>  *     SecretKey resultingKey;
>>  *
>>  *     // salt, info and inputKey values populated with data
>>  *     ...
>>  *
>>  *     // Get an instance of the HKDF KeyGenerator
>>  *     hkdfGen = KeyGenerator.getInstance("HkdfSHA256");
>>  *
>>  *     // Create the spec object and use it to initialize the generator.
>>  *     // Ask for a 64-byte key to be output.
>>  *     hkdfGen.init(new HkdfParameterSpec(inputKey, salt, info, 64));
>>  *
>>  *     // Generate the key
>>  *     resultingKey = hkdfGen.generateKey();
>>  * </pre>
>>  *
>>  * @since 9
>>  */
>> public final class HkdfParameterSpec implements AlgorithmParameterSpec {
>>
>>     /**
>>      * Constructs a new HkdfParameterSpec.
>>      *
>>      * @param inputKey the input keying material used for the
>>      *      HKDF-Extract-then-Expand operation.
>>      * @param salt the salt value, or {@code null} if not specified.  
>> The
>>      *      contents of the array are copied to protect against 
>> subsequent
>>      *      modification.
>>      * @param info optional context and application specific 
>> information or
>>      *      {@code null} if no info data is provided.  The contents 
>> of the
>>      *      array are copied to protect against subsequent modification.
>>      * @param outLen the length in bytes of the output data
>>      *
>>      * @throws NullPointerException if {@code inputKey} is {@code null}.
>>      * @throws IllegalArgumentException if {@code outLen} is a
>>      *      non-positive value.
>>      */
>>     public HkdfParameterSpec(SecretKey inputKey, byte[] salt, byte[] 
>> info,
>>             int outLen);
>>
>>     /**
>>      * Returns the Input Keying Material (IKM).
>>      *
>>      * @return the Input Keying Material.
>>      */
>>     public SecretKey getIKM();
>>
>>     /**
>>      * Returns the salt value.
>>      *
>>      * @return a copy of the salt value or {@code null} if no salt 
>> was provided.
>>      */
>>     public byte[] getSalt();
>>
>>     /**
>>      * Returns a copy of the context and application specific 
>> information.
>>      *
>>      * @return a copy of the context and application specific 
>> information.
>>      *      This may be {@code null} or empty if no specific 
>> information was
>>      *      provided.
>>      */
>>     public byte[] getInfo();
>>
>>     /**
>>      * Returns the length in bytes of the output key to be produced.
>>      *
>>      * @return the length in bytes of the output key to be produced.
>>      */
>>     public int getOutputLength();
>> }
>



More information about the security-dev mailing list