Existing implementations for NIST SP 800-56A Concatenation/Single-Step Key Derivation Function?

c++, cryptoapi, cryptography, java, security

Solution

Here is a quick and dirty implementation:

    public byte[] concatKDF(String hashAlg, byte[] z, int keyDataLen, byte[] algorithmID, byte[] partyUInfo, byte[] partyVInfo, byte[] suppPubInfo, byte[] suppPrivInfo) throws NoSuchAlgorithmException
{
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        baos.write(algorithmID);
        baos.write(partyUInfo);
        baos.write(partyVInfo);
        baos.write(suppPubInfo);
        baos.write(suppPrivInfo);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

    byte[] otherInfo = baos.toByteArray();
    return concatKDF(hashAlg, z, keyDataLen, otherInfo);
}

public byte[] concatKDF(String hashAlg, byte[] z, int keyDataLen, byte[] otherInfo) throws NoSuchAlgorithmException
{
    byte[] key = new byte[keyDataLen];
    MessageDigest md = MessageDigest.getInstance(hashAlg);
    int hashLen = md.getDigestLength(); 
    int reps = keyDataLen / hashLen;
    for(int i=1;i<=reps;i++){
        md.reset();
        md.update(intToFourBytes(i));
        md.update(z);
        md.update(otherInfo);
        byte[] hash = md.digest();
        if(i<reps){
            System.arraycopy(hash, 0, key, hashLen*(i-1), hashLen);
        }else{
            if(keyDataLen % hashLen == 0){
                System.arraycopy(hash, 0, key, hashLen*(i-1), hashLen);
            }else{
                System.arraycopy(hash, 0, key, hashLen*(i-1), keyDataLen % hashLen);
            }
        }
    }
    return key;
}

public byte[] intToFourBytes(int i){
    byte[] res = new byte[4];
    res[0] = (byte) (i >>> 24);
    res[1] = (byte) ((i >>> 16) & 0xFF);
    res[2] = (byte) ((i >>> 8) & 0xFF);
    res[3] = (byte) (i & 0xFF);
    return res;
}

Problem

Does anyone know of any existing implementations for NIST SP 800-56A Concatenation Key Derivation Function / CONCAT KDF (preferably in Java)? The key derivation function is documented in section 5.8.1 of NIST's publication: Recommendation for Pair-Wise Key Establishment Schemes Using Discrete Logarithm Cryptography Link here: http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf Microsoft's CNG has an implementation here but if you compare the function implemented by Microsoft, as compared to the parameters documented in NIST SP 800-56A, they do not tally, and the implementation by Microsoft is unusable. I have attempted to implement a sample program in C++ as well but I couldn't match the parameters. Would anyone be able to attempt to implement it or know of any existing implementations? I am looking for an implementation that is able to justify why it is accurate to the NIST specifications. I have seen a couple implementations out there and I feel that they are not accurate to the NIST specifications (missing parameters, invalid logic flow etc). If you can implement it yourself, I am always glad to share my own source code for debate. Thanks! This would be a good contribution to the open source community! EDIT: Thanks to @Rasmus Faber, I can finally bring this question to a close, and hope to answer the same question that everyone else has as me. Here's the code that I have edited based on @Rasmus Faber and my original codes: ConcatKeyDerivationFunction.java ``` import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * * Implementation of Concatenation Key Derivation Function<br/> * http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf * */ public class ConcatKeyDerivationFunction { private static final long MAX_HASH_INPUTLEN = Long.MAX_VALUE; private static final long UNSIGNED_INT_MAX_VALUE = 4294967295L; private static MessageDigest md; public ConcatKeyDerivationFunction(String hashAlg) throws NoSuchAlgorithmException { md = MessageDigest.getInstance(hashAlg); } public byte[] concatKDF(byte[] z, int keyDataLen, byte[] algorithmID, byte[] partyUInfo, byte[] partyVInfo, byte[] suppPubInfo, byte[] suppPrivInfo) { int hashLen = md.getDigestLength() * 8; if (keyDataLen % 8 != 0) { throw new IllegalArgumentException("keydatalen should be a multiple of 8"); } if (keyDataLen > (long) hashLen * UNSIGNED_INT_MAX_VALUE) { throw new IllegalArgumentException("keydatalen is too large"); } if (algorithmID == null || partyUInfo == null || partyVInfo == null) { throw new NullPointerException("Required parameter is null"); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { baos.write(algorithmID); baos.write(partyUInfo); baos.write(partyVInfo); if (suppPubInfo != null) { baos.write(suppPubInfo); } if (suppPrivInfo != null) { baos.write(suppPrivInfo); } } catch (IOException e) { throw new RuntimeException(e); } byte[] otherInfo = baos.toByteArray(); return concatKDF(z, keyDataLen, otherInfo); } private byte[] concatKDF(byte[] z, int keyDataLen, byte[] otherInfo) { keyDataLen = keyDataLen / 8; byte[] key = new byte[keyDataLen]; int hashLen = md.getDigestLength(); int reps = keyDataLen / hashLen; if (reps > UNSIGNED_INT_MAX_VALUE) { throw new IllegalArgumentException("Key derivation failed"); } int counter = 1; byte[] counterInBytes = intToFourBytes(counter); if ((counterInBytes.length + z.length + otherInfo.length) * 8 > MAX_HASH_INPUTLEN) { throw new IllegalArgumentException("Key derivation failed"); } for (int i = 0; i <= reps; i++) { md.reset(); md.update(intToFourBytes(i + 1)); md.update(z); md.update(otherInfo); byte[] hash = md.digest(); if (i < reps) { System.arraycopy(hash, 0, key, hashLen * i, hashLen); } else { System.arraycopy(hash, 0, key, hashLen * i, keyDataLen % hashLen); } } return key; } private byte[] intToFourBytes(int i) { byte[] res = new byte[4]; res[0] = (byte) (i >>> 24); res[1] = (byte) ((i >>> 16) & 0xFF); res[2] = (byte) ((i >>> 8) & 0xFF); res[3] = (byte) (i & 0xFF); return res; } } ``` @Rasmus Faber: Thank you for your effort. I give you full credit for the above code. What I have done with the code above was to add in code to perform validation as required by the NIST specifications. Also, I fixed a bug where the keyDataLen passed in was meant to specify the length in bits, but it was treated as the length in bytes. Hence, the key generated ended up being 8 times larger. This was fixed by adding a line `keyDataLen = keyDataLen/8;` in the first line of the second method. I thank everyone for their support and hope this piece of code will go a long way to the open source community!

Original source