
io.github.hapjava.server.impl.pairing.FinalPairHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hap Show documentation
Show all versions of hap Show documentation
Homekit Accessory Protocol for Java
package io.github.hapjava.server.impl.pairing;
import io.github.hapjava.server.HomekitAuthInfo;
import io.github.hapjava.server.impl.crypto.ChachaDecoder;
import io.github.hapjava.server.impl.crypto.ChachaEncoder;
import io.github.hapjava.server.impl.crypto.EdsaSigner;
import io.github.hapjava.server.impl.crypto.EdsaVerifier;
import io.github.hapjava.server.impl.http.HttpResponse;
import io.github.hapjava.server.impl.pairing.PairSetupRequest.Stage3Request;
import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.DecodeResult;
import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.Encoder;
import java.nio.charset.StandardCharsets;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
import org.bouncycastle.crypto.params.HKDFParameters;
class FinalPairHandler {
private final byte[] k;
private final HomekitAuthInfo authInfo;
private byte[] hkdf_enc_key;
public FinalPairHandler(byte[] k, HomekitAuthInfo authInfo) {
this.k = k;
this.authInfo = authInfo;
}
public HttpResponse handle(PairSetupRequest req) throws Exception {
HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest());
hkdf.init(
new HKDFParameters(
k,
"Pair-Setup-Encrypt-Salt".getBytes(StandardCharsets.UTF_8),
"Pair-Setup-Encrypt-Info".getBytes(StandardCharsets.UTF_8)));
byte[] okm = hkdf_enc_key = new byte[32];
hkdf.generateBytes(okm, 0, 32);
return decrypt((Stage3Request) req, okm);
}
private HttpResponse decrypt(Stage3Request req, byte[] key) throws Exception {
ChachaDecoder chacha = new ChachaDecoder(key, "PS-Msg05".getBytes(StandardCharsets.UTF_8));
byte[] plaintext = chacha.decodeCiphertext(req.getAuthTagData(), req.getMessageData());
DecodeResult d = TypeLengthValueUtils.decode(plaintext);
byte[] username = d.getBytes(MessageType.USERNAME);
byte[] ltpk = d.getBytes(MessageType.PUBLIC_KEY);
byte[] proof = d.getBytes(MessageType.SIGNATURE);
return createUser(username, ltpk, proof);
}
private HttpResponse createUser(byte[] username, byte[] ltpk, byte[] proof) throws Exception {
HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest());
hkdf.init(
new HKDFParameters(
k,
"Pair-Setup-Controller-Sign-Salt".getBytes(StandardCharsets.UTF_8),
"Pair-Setup-Controller-Sign-Info".getBytes(StandardCharsets.UTF_8)));
byte[] okm = new byte[32];
hkdf.generateBytes(okm, 0, 32);
byte[] completeData = ByteUtils.joinBytes(okm, username, ltpk);
if (!new EdsaVerifier(ltpk).verify(completeData, proof)) {
throw new Exception("Invalid signature");
}
authInfo.createUser(authInfo.getMac() + new String(username, StandardCharsets.UTF_8), ltpk);
return createResponse();
}
private HttpResponse createResponse() throws Exception {
HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA512Digest());
hkdf.init(
new HKDFParameters(
k,
"Pair-Setup-Accessory-Sign-Salt".getBytes(StandardCharsets.UTF_8),
"Pair-Setup-Accessory-Sign-Info".getBytes(StandardCharsets.UTF_8)));
byte[] okm = new byte[32];
hkdf.generateBytes(okm, 0, 32);
EdsaSigner signer = new EdsaSigner(authInfo.getPrivateKey());
byte[] material =
ByteUtils.joinBytes(
okm, authInfo.getMac().getBytes(StandardCharsets.UTF_8), signer.getPublicKey());
byte[] proof = signer.sign(material);
Encoder encoder = TypeLengthValueUtils.getEncoder();
encoder.add(MessageType.USERNAME, authInfo.getMac().getBytes(StandardCharsets.UTF_8));
encoder.add(MessageType.PUBLIC_KEY, signer.getPublicKey());
encoder.add(MessageType.SIGNATURE, proof);
byte[] plaintext = encoder.toByteArray();
ChachaEncoder chacha =
new ChachaEncoder(hkdf_enc_key, "PS-Msg06".getBytes(StandardCharsets.UTF_8));
byte[] ciphertext = chacha.encodeCiphertext(plaintext);
encoder = TypeLengthValueUtils.getEncoder();
encoder.add(MessageType.STATE, (short) 6);
encoder.add(MessageType.ENCRYPTED_DATA, ciphertext);
return new PairingResponse(encoder.toByteArray());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy