diff --git a/CHANGES.md b/CHANGES.md index 758ddf6f7..cfa0188e1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -14,6 +14,7 @@ * Valid values are supported for enum characteristics instead of min and max values * Supported valid states for Thermostat, SecuritySystem, HeaterCooler and HumidifierDehumidifier [#108] [#120](https://github.com/hap-java/HAP-Java/pull/120) * Support for FilterMaintenance. Can be used as a linked service for an Air Purifier [#124](https://github.com/hap-java/HAP-Java/pull/124) +* Update crypto libs [#130](https://github.com/hap-java/HAP-Java/pull/130) # HAP-Java 1.1.5 diff --git a/pom.xml b/pom.xml index 5faf6eafc..d4f47b432 100644 --- a/pom.xml +++ b/pom.xml @@ -100,37 +100,31 @@ <dependency> <groupId>com.nimbusds</groupId> <artifactId>srp6a</artifactId> - <version>1.5.2</version> + <version>2.1.0</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> - <version>1.51</version> + <version>1.67</version> </dependency> <dependency> <groupId>net.vrallev.ecc</groupId> <artifactId>ecc-25519-java</artifactId> - <version>1.0.1</version> - </dependency> - - <dependency> - <groupId>org.zeromq</groupId> - <artifactId>curve25519-java</artifactId> - <version>0.1.0</version> + <version>1.0.3</version> </dependency> <dependency> <groupId>javax.json</groupId> <artifactId>javax.json-api</artifactId> - <version>1.0</version> + <version>1.1.4</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.json</artifactId> - <version>1.0.4</version> + <version>1.1.4</version> </dependency> <dependency> diff --git a/src/main/java/io/github/hapjava/server/impl/HomekitUtils.java b/src/main/java/io/github/hapjava/server/impl/HomekitUtils.java index 9b5b2b8ee..20a697ff9 100644 --- a/src/main/java/io/github/hapjava/server/impl/HomekitUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/HomekitUtils.java @@ -2,7 +2,6 @@ import com.nimbusds.srp6.SRP6Routines; import java.math.BigInteger; -import java.security.InvalidAlgorithmParameterException; import java.security.SecureRandom; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -14,11 +13,12 @@ public class HomekitUtils { private static volatile SecureRandom secureRandom; public static BigInteger generateSalt() { - return new BigInteger(SRP6Routines.generateRandomSalt(16)); + return new BigInteger(new SRP6Routines().generateRandomSalt(16)); } - public static byte[] generateKey() throws InvalidAlgorithmParameterException { - EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("ed25519-sha-512"); + public static byte[] generateKey() { + EdDSAParameterSpec spec = + EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512); byte[] seed = new byte[spec.getCurve().getField().getb() / 8]; getSecureRandom().nextBytes(seed); return seed; diff --git a/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java index 4c74a469d..d616d2e0f 100644 --- a/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java +++ b/src/main/java/io/github/hapjava/server/impl/crypto/ChachaDecoder.java @@ -5,8 +5,6 @@ import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.crypto.tls.AlertDescription; -import org.bouncycastle.crypto.tls.TlsFatalAlert; import org.bouncycastle.util.Arrays; public class ChachaDecoder { @@ -28,7 +26,7 @@ public byte[] decodeCiphertext(byte[] receivedMAC, byte[] additionalData, byte[] byte[] calculatedMAC = PolyKeyCreator.create(macKey, additionalData, ciphertext); if (!Arrays.constantTimeAreEqual(calculatedMAC, receivedMAC)) { - throw new TlsFatalAlert(AlertDescription.bad_record_mac); + throw new IOException("received an incorrect MAC"); } byte[] output = new byte[ciphertext.length]; diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/ByteUtils.java b/src/main/java/io/github/hapjava/server/impl/pairing/ByteUtils.java index 6516e2e29..e65beec3c 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/ByteUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ByteUtils.java @@ -22,7 +22,7 @@ public static byte[] joinBytes(byte[]... piece) { return ret; } - public static byte[] toByteArray(BigInteger i) { + public static byte[] toUnsignedByteArray(BigInteger i) { byte[] array = i.toByteArray(); if (array[0] == 0) { array = Arrays.copyOfRange(array, 1, array.length); diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/ClientEvidenceRoutineImpl.java b/src/main/java/io/github/hapjava/server/impl/pairing/ClientEvidenceRoutineImpl.java index 16c514707..87ddf3907 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/ClientEvidenceRoutineImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ClientEvidenceRoutineImpl.java @@ -8,9 +8,7 @@ class ClientEvidenceRoutineImpl implements ClientEvidenceRoutine { - public ClientEvidenceRoutineImpl() { - // TODO Auto-generated constructor stub - } + public ClientEvidenceRoutineImpl() {} /** * Calculates M1 according to the following formula: @@ -27,10 +25,10 @@ public BigInteger computeClientEvidence( } catch (NoSuchAlgorithmException e) { throw new RuntimeException("Could not locate requested algorithm", e); } - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(cryptoParams.N)); + digest.update(BigIntegerUtils.bigIntegerToBytes(cryptoParams.N)); byte[] hN = digest.digest(); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(cryptoParams.g)); + digest.update(BigIntegerUtils.bigIntegerToBytes(cryptoParams.g)); byte[] hg = digest.digest(); byte[] hNhg = xor(hN, hg); @@ -38,14 +36,14 @@ public BigInteger computeClientEvidence( digest.update(ctx.userID.getBytes(StandardCharsets.UTF_8)); byte[] hu = digest.digest(); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.S)); + digest.update(BigIntegerUtils.bigIntegerToBytes(ctx.S)); byte[] hS = digest.digest(); digest.update(hNhg); digest.update(hu); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.s)); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.A)); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.B)); + digest.update(BigIntegerUtils.bigIntegerToBytes(ctx.s)); + digest.update(BigIntegerUtils.bigIntegerToBytes(ctx.A)); + digest.update(BigIntegerUtils.bigIntegerToBytes(ctx.B)); digest.update(hS); BigInteger ret = new BigInteger(1, digest.digest()); return ret; diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java index 3841d9fb7..116fced13 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/HomekitSRP6ServerSession.java @@ -8,6 +8,7 @@ import com.nimbusds.srp6.SRP6Session; import com.nimbusds.srp6.URoutineContext; import java.math.BigInteger; +import java.security.MessageDigest; /** * This is a slightly modified version of the SRP6ServerSession class included with nimbus. The only @@ -74,6 +75,8 @@ public static enum State { /** The current SRP-6a auth state. */ private State state; + private MessageDigest digest; + /** * Creates a new server-side SRP-6a authentication session and sets its state to {@link * State#INIT}. @@ -92,7 +95,7 @@ public HomekitSRP6ServerSession(final SRP6CryptoParams config, final int timeout this.config = config; - digest = config.getMessageDigestInstance(); + this.digest = config.getMessageDigestInstance(); if (digest == null) throw new IllegalArgumentException("Unsupported hash algorithm 'H': " + config.H); @@ -151,13 +154,13 @@ public BigInteger step1(final String userID, final BigInteger s, final BigIntege throw new IllegalStateException("State violation: Session must be in INIT state"); // Generate server private and public values - k = SRP6Routines.computeK(digest, config.N, config.g); + k = new SRP6Routines().computeK(digest, config.N, config.g); digest.reset(); b = HomekitSRP6Routines.generatePrivateValue(config.N, random); digest.reset(); - B = SRP6Routines.computePublicServerValue(config.N, config.g, k, v, b); + B = new SRP6Routines().computePublicServerValue(config.N, config.g, k, v, b); state = State.STEP_1; @@ -234,7 +237,7 @@ public BigInteger step2(final BigInteger A, final BigInteger M1) throws SRP6Exce if (hasTimedOut()) throw new SRP6Exception("Session timeout", SRP6Exception.CauseType.TIMEOUT); // Check A validity - if (!SRP6Routines.isValidPublicValue(config.N, A)) + if (!new SRP6Routines().isValidPublicValue(config.N, A)) throw new SRP6Exception( "Bad client public value 'A'", SRP6Exception.CauseType.BAD_PUBLIC_VALUE); @@ -246,11 +249,11 @@ public BigInteger step2(final BigInteger A, final BigInteger M1) throws SRP6Exce URoutineContext hashedKeysContext = new URoutineContext(A, B); u = hashedKeysRoutine.computeU(config, hashedKeysContext); } else { - u = SRP6Routines.computeU(digest, config.N, A, B); + u = new SRP6Routines().computeU(digest, config.N, A, B); digest.reset(); } - S = SRP6Routines.computeSessionKey(config.N, v, u, A, b); + S = new SRP6Routines().computeSessionKey(config.N, v, u, A, b); // Compute the own client evidence message 'M1' BigInteger computedM1; @@ -262,7 +265,7 @@ public BigInteger step2(final BigInteger A, final BigInteger M1) throws SRP6Exce computedM1 = clientEvidenceRoutine.computeClientEvidence(config, ctx); } else { // With default routine - computedM1 = SRP6Routines.computeClientEvidence(digest, A, B, S); + computedM1 = new SRP6Routines().computeClientEvidence(digest, A, B, S); digest.reset(); } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java b/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java index af6a6e01f..ee4b772f9 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/PairingManager.java @@ -29,7 +29,7 @@ public HttpResponse handle(HttpRequest httpRequest) throws Exception { if (req.getStage() == Stage.ONE) { logger.trace("Starting pair for " + registry.getLabel()); srpHandler = new SrpHandler(authInfo.getPin(), authInfo.getSalt()); - return srpHandler.handle(req); + return srpHandler.step1(); } else if (req.getStage() == Stage.TWO) { logger.trace("Entering second stage of pair for " + registry.getLabel()); if (srpHandler == null) { @@ -37,7 +37,7 @@ public HttpResponse handle(HttpRequest httpRequest) throws Exception { return new UnauthorizedResponse(); } else { try { - return srpHandler.handle(req); + return srpHandler.step2((PairSetupRequest.Stage2Request) req); } catch (Exception e) { srpHandler = null; // You don't get to try again - need a new key logger.warn("Exception encountered while processing pairing request", e); diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/ServerEvidenceRoutineImpl.java b/src/main/java/io/github/hapjava/server/impl/pairing/ServerEvidenceRoutineImpl.java index 7cf7b3164..6c032ce59 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/ServerEvidenceRoutineImpl.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/ServerEvidenceRoutineImpl.java @@ -1,5 +1,6 @@ package io.github.hapjava.server.impl.pairing; +import com.nimbusds.srp6.BigIntegerUtils; import com.nimbusds.srp6.SRP6CryptoParams; import com.nimbusds.srp6.SRP6ServerEvidenceContext; import com.nimbusds.srp6.ServerEvidenceRoutine; @@ -20,10 +21,10 @@ public BigInteger computeServerEvidence( throw new RuntimeException("Could not locate requested algorithm", e); } - byte[] hS = digest.digest(SrpHandler.bigIntegerToUnsignedByteArray(ctx.S)); + byte[] hS = digest.digest(BigIntegerUtils.bigIntegerToBytes(ctx.S)); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.A)); - digest.update(SrpHandler.bigIntegerToUnsignedByteArray(ctx.M1)); + digest.update(BigIntegerUtils.bigIntegerToBytes(ctx.A)); + digest.update(BigIntegerUtils.bigIntegerToBytes(ctx.M1)); digest.update(hS); return new BigInteger(1, digest.digest()); diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java b/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java index e02ccdb90..92d2098fa 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/SrpHandler.java @@ -6,20 +6,15 @@ import io.github.hapjava.server.impl.pairing.PairSetupRequest.Stage2Request; import io.github.hapjava.server.impl.pairing.TypeLengthValueUtils.Encoder; import io.github.hapjava.server.impl.responses.ConflictResponse; -import io.github.hapjava.server.impl.responses.NotFoundResponse; import java.math.BigInteger; import java.security.MessageDigest; -import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class SrpHandler { - // Precomputed safe 3072 bit prime 'N'. Origin RFC 5054, appendix A. - private static final BigInteger N_3072 = - new BigInteger( - "5809605995369958062791915965639201402176612226902900533702900882779736177890990861472094774477339581147373410185646378328043729800750470098210924487866935059164371588168047540943981644516632755067501626434556398193186628990071248660819361205119793693985433297036118232914410171876807536457391277857011849897410207519105333355801121109356897459426271845471397952675959440793493071628394122780510124618488232602464649876850458861245784240929258426287699705312584509625419513463605155428017165714465363094021609290561084025893662561222573202082865797821865270991145082200656978177192827024538990239969175546190770645685893438011714430426409338676314743571154537142031573004276428701433036381801705308659830751190352946025482059931306571004727362479688415574702596946457770284148435989129632853918392117997472632693078113129886487399347796982772784615865232621289656944284216824611318709764535152507354116344703769998514148343807"); - private static final BigInteger G = BigInteger.valueOf(5); + private static final int BIT_SIZE = 3072; + private static final String HASH_ALG_H = "SHA-512"; private static final String IDENTIFIER = "Pair-Setup"; private static final Logger logger = LoggerFactory.getLogger(SrpHandler.class); @@ -30,7 +25,7 @@ class SrpHandler { private final String pin; public SrpHandler(String pin, BigInteger salt) { - config = new SRP6CryptoParams(N_3072, G, "SHA-512"); + config = SRP6CryptoParams.getInstance(BIT_SIZE, HASH_ALG_H); session = new HomekitSRP6ServerSession(config); session.setClientEvidenceRoutine(new ClientEvidenceRoutineImpl()); session.setServerEvidenceRoutine(new ServerEvidenceRoutineImpl()); @@ -38,20 +33,7 @@ public SrpHandler(String pin, BigInteger salt) { 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 { + HttpResponse step1() throws Exception { if (session.getState() != State.INIT) { logger.warn("Session is not in state INIT when receiving step1"); return new ConflictResponse(); @@ -68,7 +50,7 @@ private HttpResponse step1() throws Exception { return new PairingResponse(encoder.toByteArray()); } - private HttpResponse step2(Stage2Request request) throws Exception { + HttpResponse step2(Stage2Request request) throws Exception { if (session.getState() != State.STEP_1) { logger.warn("Session is not in state Stage 1 when receiving step2"); return new ConflictResponse(); @@ -80,18 +62,10 @@ private HttpResponse step2(Stage2Request request) throws Exception { return new PairingResponse(encoder.toByteArray()); } - public byte[] getK() { + byte[] getK() { MessageDigest digest = session.getCryptoParams().getMessageDigestInstance(); - BigInteger S = session.getSessionKey(false); - byte[] sBytes = bigIntegerToUnsignedByteArray(S); + BigInteger S = session.getSessionKey(); + byte[] sBytes = BigIntegerUtils.bigIntegerToBytes(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; - } } diff --git a/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java b/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java index 396829d34..bf76f53fb 100644 --- a/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java +++ b/src/main/java/io/github/hapjava/server/impl/pairing/TypeLengthValueUtils.java @@ -1,5 +1,6 @@ package io.github.hapjava.server.impl.pairing; +import com.nimbusds.srp6.BigIntegerUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -38,7 +39,7 @@ private Encoder() { } public void add(MessageType type, BigInteger i) throws IOException { - add(type, ByteUtils.toByteArray(i)); + add(type, BigIntegerUtils.bigIntegerToBytes(i)); } public void add(MessageType type, short b) {