package com.beowulfe.hap.impl.pairing;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.beowulfe.hap.impl.http.HttpResponse;
import com.beowulfe.hap.impl.pairing.HomekitSRP6ServerSession.State;
import com.beowulfe.hap.impl.pairing.PairSetupRequest.Stage2Request;
import com.beowulfe.hap.impl.pairing.TypeLengthValueUtils.Encoder;
import com.beowulfe.hap.impl.responses.ConflictResponse;
import com.beowulfe.hap.impl.responses.NotFoundResponse;
import com.nimbusds.srp6.*;
class SrpHandler {
// Precomputed safe 3072 bit prime 'N'. Origin RFC 5054, appendix A.
private final static BigInteger N_3072 = new BigInteger("5809605995369958062791915965639201402176612226902900533702900882779736177890990861472094774477339581147373410185646378328043729800750470098210924487866935059164371588168047540943981644516632755067501626434556398193186628990071248660819361205119793693985433297036118232914410171876807536457391277857011849897410207519105333355801121109356897459426271845471397952675959440793493071628394122780510124618488232602464649876850458861245784240929258426287699705312584509625419513463605155428017165714465363094021609290561084025893662561222573202082865797821865270991145082200656978177192827024538990239969175546190770645685893438011714430426409338676314743571154537142031573004276428701433036381801705308659830751190352946025482059931306571004727362479688415574702596946457770284148435989129632853918392117997472632693078113129886487399347796982772784615865232621289656944284216824611318709764535152507354116344703769998514148343807");
private final static BigInteger G = BigInteger.valueOf(5);
private final static String IDENTIFIER = "Pair-Setup";
private final static Logger logger = LoggerFactory.getLogger(SrpHandler.class);
private final BigInteger salt;
private final HomekitSRP6ServerSession session;
private final SRP6CryptoParams config;
private final String pin;
public SrpHandler(String pin, BigInteger salt) {
config = new SRP6CryptoParams(N_3072, G, "SHA-512");
session = new HomekitSRP6ServerSession(config);
session.setClientEvidenceRoutine(new ClientEvidenceRoutineImpl());
session.setServerEvidenceRoutine(new ServerEvidenceRoutineImpl());
this.pin = pin;
this.salt = salt;
}
public HttpResponse handle(PairSetupRequest request) throws Exception {
switch(request.getStage()) {
case ONE:
return step1();
case TWO:
return step2((Stage2Request) request);
default:
return new NotFoundResponse();
}
}
private HttpResponse step1() throws Exception {
if (session.getState() != State.INIT) {
logger.error("Session is not in state INIT when receiving step1");
return new ConflictResponse();
}
SRP6VerifierGenerator verifierGenerator = new SRP6VerifierGenerator(config);
verifierGenerator.setXRoutine(new XRoutineWithUserIdentity());
BigInteger verifier = verifierGenerator.generateVerifier(salt, IDENTIFIER, pin);
Encoder encoder = TypeLengthValueUtils.getEncoder();
encoder.add(MessageType.STATE, (short) 0x02);
encoder.add(MessageType.SALT, salt);
encoder.add(MessageType.PUBLIC_KEY, session.step1(IDENTIFIER, salt, verifier));
return new PairingResponse(encoder.toByteArray());
}
private HttpResponse step2(Stage2Request request) throws Exception {
if (session.getState() != State.STEP_1) {
logger.error("Session is not in state Stage 1 when receiving step2");
return new ConflictResponse();
}
BigInteger m2 = session.step2(request.getA(), request.getM1());
Encoder encoder = TypeLengthValueUtils.getEncoder();
encoder.add(MessageType.STATE, (short) 0x04);
encoder.add(MessageType.PROOF, m2);
return new PairingResponse(encoder.toByteArray());
}
public byte[] getK() {
MessageDigest digest = session.getCryptoParams().getMessageDigestInstance();
BigInteger S = session.getSessionKey(false);
byte[] sBytes = bigIntegerToUnsignedByteArray(S);
return digest.digest(sBytes);
}
public static byte[] bigIntegerToUnsignedByteArray(BigInteger i) {
byte[] array = i.toByteArray();
if (array[0] == 0) {
array = Arrays.copyOfRange(array, 1, array.length);
}
return array;
}
}