package im.actor.crypto.primitives.prf; import im.actor.crypto.primitives.Digest; import im.actor.crypto.primitives.hmac.HMAC; import im.actor.crypto.primitives.util.ByteStrings; public class PRF { private Digest digest; public PRF(Digest digest) { this.digest = digest; } public byte[] calculate(byte[] secret, String label, byte[] seed, int length) { HMAC hmac = new HMAC(secret, digest); // PRF(secret: bytes, label: string, seed: bytes) = P_HASH(secret, bytes(label) + seed); // P_HASH(secret, seed) = HASH(secret, A(1) + seed) + HASH(secret, A(2) + seed) + HASH(secret, A(3) + seed) + ... // where A(): // A(0) = seed // A(i) = HMAC_HASH(secret, A(i-1)) byte[] rSeed = ByteStrings.merge(label.getBytes(), seed); byte[] res = new byte[length]; byte[] A = rSeed; byte[] tHash = new byte[digest.getDigestSize()]; int offset = 0; while (offset * 32 < length) { // Update A hmac.reset(); hmac.update(A, 0, A.length); hmac.doFinal(tHash, 0); A = new byte[digest.getDigestSize()]; ByteStrings.write(A, 0, tHash, 0, A.length); // Writing digest digest.reset(); digest.update(secret, 0, secret.length); digest.update(A, 0, A.length); digest.update(rSeed, 0, rSeed.length); digest.doFinal(tHash, 0); ByteStrings.write(res, offset * digest.getDigestSize(), tHash, 0, Math.min(tHash.length, res.length - offset * digest.getDigestSize())); offset++; } return res; } }