package im.actor.runtime.crypto.primitives.kdf;
import im.actor.runtime.crypto.Digest;
import im.actor.runtime.crypto.primitives.hmac.HMAC;
import im.actor.runtime.crypto.primitives.util.ByteStrings;
// Disabling Bounds checks for speeding up calculations
/*-[
#define J2OBJC_DISABLE_ARRAY_BOUND_CHECKS 1
]-*/
/**
* HKDF implementation based on RFC 5869: https://tools.ietf.org/html/rfc5869
*
* @author Steve Kite (steve@actor.im)
*/
public class HKDF {
private Digest baseDigest;
public HKDF(Digest baseDigest) {
this.baseDigest = baseDigest;
}
public byte[] deriveSecrets(byte[] keyMaterial, int outputLength) {
return deriveSecrets(keyMaterial, new byte[0], outputLength);
}
public byte[] deriveSecrets(byte[] keyMaterial, byte[] info, int outputLength) {
return deriveSecrets(keyMaterial, new byte[baseDigest.getDigestSize()], info, outputLength);
}
public byte[] deriveSecrets(byte[] keyMaterial, byte[] salt, byte[] info, int outputLength) {
byte[] prk = hkdfExtract(salt, keyMaterial);
return hkdfExpand(prk, info, outputLength);
}
byte[] hkdfExtract(byte[] keyMaterial, byte[] salt) {
HMAC hmac = new HMAC(salt, baseDigest);
hmac.reset();
hmac.update(keyMaterial, 0, keyMaterial.length);
byte[] res = new byte[baseDigest.getDigestSize()];
hmac.doFinal(res, 0);
return res;
}
byte[] hkdfExpand(byte[] prk, byte[] info, int outputSize) {
byte[] res = new byte[outputSize];
HMAC hmac = new HMAC(prk, baseDigest);
hmac.reset();
byte[] prevHash = new byte[0];
int offset = 0;
int index = 0;
byte[] indexB = new byte[1];
while (offset < res.length) {
hmac.reset();
hmac.update(prevHash, 0, prevHash.length);
hmac.update(info, 0, info.length);
indexB[0] = (byte) index;
hmac.update(indexB, 0, 1);
byte[] result = new byte[baseDigest.getDigestSize()];
hmac.doFinal(result, 0);
int digestSize = baseDigest.getDigestSize();
int blockLength = Math.min(outputSize - offset, digestSize);
ByteStrings.write(res, offset, result, 0, blockLength);
prevHash = result;
offset += digestSize;
index++;
}
return res;
}
}