java - ¿Implementaciones existentes para la función de derivación de clave de concatenación NIST SP 800-56A?
c++ security (2)
¿Alguien sabe de alguna implementación existente para NIST SP 800-56A Función de Derivación de Claves de Concatenación / CONCAT KDF (preferiblemente en Java)?
La función de derivación de claves está documentada en la sección 5.8.1 de la publicación del NIST: Recomendación para esquemas de establecimiento de claves par pares con Criptografía de Logaritmos Discretos
Enlace aquí: http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf
El CNG de Microsoft tiene una implementación aquí, pero si compara la función implementada por Microsoft, en comparación con los parámetros documentados en NIST SP 800-56A, no coinciden, y la implementación de Microsoft es inutilizable. También intenté implementar un programa de ejemplo en C ++, pero no pude hacer coincidir los parámetros.
¿Alguien podría intentar implementarlo o conocer alguna implementación existente?
Estoy buscando una implementación que pueda justificar por qué es precisa para las especificaciones NIST. He visto un par de implementaciones y creo que no son precisas para las especificaciones NIST (parámetros faltantes, flujo lógico no válido, etc.).
Si puede implementarlo usted mismo, siempre estoy contento de compartir mi propio código fuente para el debate. ¡Gracias! ¡Esta sería una buena contribución a la comunidad de código abierto!
EDITAR:
Gracias a @Rasmus Faber, finalmente puedo finalizar esta pregunta y espero responder la misma pregunta que todos los demás tienen como yo.
Aquí está el código que he editado basado en @Rasmus Faber y mis códigos originales:
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: Gracias por su esfuerzo. Le doy crédito completo por el código anterior. Lo que hice con el código anterior fue agregar código para realizar la validación según lo exigen las especificaciones del NIST.
Además, arreglé un error donde el keyDataLen pasado se suponía que especificara la longitud en bits, pero se trató como la longitud en bytes. Por lo tanto, la clave generada terminó siendo 8 veces más grande.
Esto se solucionó agregando una línea keyDataLen = keyDataLen/8;
en la primera línea del segundo método.
¡Les agradezco a todos por su apoyo y espero que este pedazo de código llegue muy lejos a la comunidad de código abierto!
Aquí hay una implementación rápida y sucia:
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;
}
No crea que pueda encontrarlos, luego pruebe todos en la lista de Validated Component Implementations
desde http://csrc.nist.gov/groups/STM/cavp/documents/components/componentval.html .
Solo hay un proveedor que los escribe en Java: Entrust http://www.entrust.com .
Todos ellos están validados sin el KDF :). El resto del trabajo depende de ti.