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; } }