package com.subgraph.orchid.crypto;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.subgraph.orchid.Tor;
public class TorRFC5869KeyDerivation {
private final static String PROTOID = "ntor-curve25519-sha256-1";
private final static String M_EXPAND = PROTOID + ":key_expand";
private final static byte[] M_EXPAND_BYTES = M_EXPAND.getBytes(Tor.getDefaultCharset());
private final byte[] seed;
public TorRFC5869KeyDerivation(byte[] seed) {
this.seed = new byte[seed.length];
System.arraycopy(seed, 0, this.seed, 0, seed.length);
}
public void deriveKeys(byte[] keyMaterialOut, byte[] verifyHashOut) {
final ByteBuffer keyData = deriveKeys(keyMaterialOut.length + verifyHashOut.length);
keyData.get(keyMaterialOut);
keyData.get(verifyHashOut);
}
public ByteBuffer deriveKeys(int length) {
int round = 1;
final ByteBuffer bb = makeBuffer(length);
byte[] macOutput = null;
while(bb.hasRemaining()) {
macOutput = expandRound(round, macOutput);
if(macOutput.length > bb.remaining()) {
bb.put(macOutput, 0, bb.remaining());
} else {
bb.put(macOutput);
}
round += 1;
}
bb.flip();
return bb;
}
private byte[] expandRound(int round, byte[] priorMac) {
final ByteBuffer bb;
if(round == 1) {
bb = makeBuffer(M_EXPAND_BYTES.length + 1);
} else {
bb = makeBuffer(M_EXPAND_BYTES.length + TorMessageDigest.TOR_DIGEST256_SIZE + 1);
bb.put(priorMac);
}
bb.put(M_EXPAND_BYTES);
bb.put((byte) round);
final Mac mac = createMacInstance();
return mac.doFinal(bb.array());
}
private ByteBuffer makeBuffer(int len) {
final byte[] bs = new byte[len];
return ByteBuffer.wrap(bs);
}
private Mac createMacInstance() {
final SecretKeySpec keyspec = new SecretKeySpec(seed, "HmacSHA256");
try {
final Mac mac = Mac.getInstance("HmacSHA256");
mac.init(keyspec);
return mac;
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Could not create HmacSHA256 instance: "+ e);
} catch (InvalidKeyException e) {
throw new IllegalStateException("Could not create HmacSHA256 instance: "+ e);
}
}
}