diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java index b51dc972a..37be4fe8c 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/crypto/TransportCrypto.java @@ -1,6 +1,7 @@ package org.briarproject.bramble.api.crypto; import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.transport.StaticTransportKeys; import org.briarproject.bramble.api.transport.TransportKeys; /** @@ -25,6 +26,23 @@ public interface TransportCrypto { */ TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod); + /** + * Derives static transport keys for the given transport in the given time + * period from the given root key. + * + * @param alice whether the keys are for use by Alice or Bob. + */ + StaticTransportKeys deriveStaticTransportKeys(TransportId t, + SecretKey rootKey, boolean alice, long timePeriod); + + /** + * Updates the given static transport keys to the given time period. If + * the keys are for the given period or any later period they are not + * updated. + */ + StaticTransportKeys updateTransportKeys(StaticTransportKeys k, + long timePeriod); + /** * Encodes the pseudo-random tag that is used to recognise a stream. */ diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/StaticTransportKeys.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/StaticTransportKeys.java new file mode 100644 index 000000000..22f85d976 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/StaticTransportKeys.java @@ -0,0 +1,31 @@ +package org.briarproject.bramble.api.transport; + +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.TransportId; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +public class StaticTransportKeys extends TransportKeys { + + private final SecretKey rootKey; + private final boolean alice; + + public StaticTransportKeys(TransportId transportId, IncomingKeys inPrev, + IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr, + SecretKey rootKey, boolean alice) { + super(transportId, inPrev, inCurr, inNext, outCurr); + this.rootKey = rootKey; + this.alice = alice; + } + + public SecretKey getRootKey() { + return rootKey; + } + + public boolean isAlice() { + return alice; + } +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportConstants.java index cf5502a5f..2f58af225 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/TransportConstants.java @@ -108,4 +108,27 @@ public interface TransportConstants { */ String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE"; + /** + * Label for deriving Alice's static tag key from the root key. + */ + String ALICE_STATIC_TAG_LABEL = + "org.briarproject.bramble.transport/ALICE_STATIC_TAG_KEY"; + + /** + * Label for deriving Bob's static tag key from the root key. + */ + String BOB_STATIC_TAG_LABEL = + "org.briarproject.bramble.transport/BOB_STATIC_TAG_KEY"; + + /** + * Label for deriving Alice's static header key from the root key. + */ + String ALICE_STATIC_HEADER_LABEL = + "org.briarproject.bramble.transport/ALICE_STATIC_HEADER_KEY"; + + /** + * Label for deriving Bob's static header key from the root key. + */ + String BOB_STATIC_HEADER_LABEL = + "org.briarproject.bramble.transport/BOB_STATIC_HEADER_KEY"; } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java index 003a2ab63..a05b2487b 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/crypto/TransportCryptoImpl.java @@ -6,17 +6,21 @@ import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys; +import org.briarproject.bramble.api.transport.StaticTransportKeys; import org.briarproject.bramble.api.transport.TransportKeys; -import org.briarproject.bramble.util.ByteUtils; -import org.briarproject.bramble.util.StringUtils; import org.spongycastle.crypto.Digest; import org.spongycastle.crypto.digests.Blake2bDigest; import javax.inject.Inject; +import static java.lang.System.arraycopy; import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL; +import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_STATIC_HEADER_LABEL; +import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_STATIC_TAG_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL; +import static org.briarproject.bramble.api.transport.TransportConstants.BOB_STATIC_HEADER_LABEL; +import static org.briarproject.bramble.api.transport.TransportConstants.BOB_STATIC_TAG_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; @@ -24,6 +28,9 @@ import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; +import static org.briarproject.bramble.util.ByteUtils.writeUint16; +import static org.briarproject.bramble.util.ByteUtils.writeUint64; +import static org.briarproject.bramble.util.StringUtils.toUtf8; class TransportCryptoImpl implements TransportCrypto { @@ -90,24 +97,111 @@ class TransportCryptoImpl implements TransportCrypto { private SecretKey rotateKey(SecretKey k, long timePeriod) { byte[] period = new byte[INT_64_BYTES]; - ByteUtils.writeUint64(timePeriod, period, 0); + writeUint64(timePeriod, period, 0); return crypto.deriveKey(ROTATE_LABEL, k, period); } private SecretKey deriveTagKey(SecretKey master, TransportId t, boolean alice) { String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL; - byte[] id = StringUtils.toUtf8(t.getString()); + byte[] id = toUtf8(t.getString()); return crypto.deriveKey(label, master, id); } private SecretKey deriveHeaderKey(SecretKey master, TransportId t, boolean alice) { String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL; - byte[] id = StringUtils.toUtf8(t.getString()); + byte[] id = toUtf8(t.getString()); return crypto.deriveKey(label, master, id); } + @Override + public StaticTransportKeys deriveStaticTransportKeys(TransportId t, + SecretKey rootKey, boolean alice, long timePeriod) { + if (timePeriod < 1) throw new IllegalArgumentException(); + IncomingKeys inPrev = deriveStaticIncomingKeys(t, rootKey, alice, + timePeriod - 1); + IncomingKeys inCurr = deriveStaticIncomingKeys(t, rootKey, alice, + timePeriod); + IncomingKeys inNext = deriveStaticIncomingKeys(t, rootKey, alice, + timePeriod + 1); + OutgoingKeys outCurr = deriveStaticOutgoingKeys(t, rootKey, alice, + timePeriod); + return new StaticTransportKeys(t, inPrev, inCurr, inNext, outCurr, + rootKey, alice); + } + + private IncomingKeys deriveStaticIncomingKeys(TransportId t, + SecretKey rootKey, boolean alice, long timePeriod) { + SecretKey tag = deriveStaticTagKey(t, rootKey, !alice, timePeriod); + SecretKey header = deriveStaticHeaderKey(t, rootKey, !alice, + timePeriod); + return new IncomingKeys(tag, header, timePeriod); + } + + private OutgoingKeys deriveStaticOutgoingKeys(TransportId t, + SecretKey rootKey, boolean alice, long timePeriod) { + SecretKey tag = deriveStaticTagKey(t, rootKey, alice, timePeriod); + SecretKey header = deriveStaticHeaderKey(t, rootKey, alice, timePeriod); + return new OutgoingKeys(tag, header, timePeriod, true); + } + + private SecretKey deriveStaticTagKey(TransportId t, SecretKey root, + boolean alice, long timePeriod) { + String label = alice ? ALICE_STATIC_TAG_LABEL : BOB_STATIC_TAG_LABEL; + byte[] id = toUtf8(t.getString()); + byte[] period = new byte[INT_64_BYTES]; + writeUint64(timePeriod, period, 0); + return crypto.deriveKey(label, root, id, period); + } + + private SecretKey deriveStaticHeaderKey(TransportId t, SecretKey root, + boolean alice, long timePeriod) { + String label = + alice ? ALICE_STATIC_HEADER_LABEL : BOB_STATIC_HEADER_LABEL; + byte[] id = toUtf8(t.getString()); + byte[] period = new byte[INT_64_BYTES]; + writeUint64(timePeriod, period, 0); + return crypto.deriveKey(label, root, id, period); + } + + @Override + public StaticTransportKeys updateTransportKeys(StaticTransportKeys k, + long timePeriod) { + long elapsed = timePeriod - k.getTimePeriod(); + TransportId t = k.getTransportId(); + SecretKey rootKey = k.getRootKey(); + boolean alice = k.isAlice(); + if (elapsed <= 0) { + // The keys are for the given period or later - don't update them + return k; + } else if (elapsed == 1) { + // The keys are one period old - shift by one period + IncomingKeys inPrev = k.getCurrentIncomingKeys(); + IncomingKeys inCurr = k.getNextIncomingKeys(); + IncomingKeys inNext = deriveStaticIncomingKeys(t, rootKey, alice, + timePeriod + 1); + OutgoingKeys outCurr = deriveStaticOutgoingKeys(t, rootKey, alice, + timePeriod); + return new StaticTransportKeys(t, inPrev, inCurr, inNext, outCurr, + rootKey, alice); + } else if (elapsed == 2) { + // The keys are two periods old - shift by two periods + IncomingKeys inPrev = k.getNextIncomingKeys(); + IncomingKeys inCurr = deriveStaticIncomingKeys(t, rootKey, alice, + timePeriod); + IncomingKeys inNext = deriveStaticIncomingKeys(t, rootKey, alice, + timePeriod + 1); + OutgoingKeys outCurr = deriveStaticOutgoingKeys(t, rootKey, alice, + timePeriod); + return new StaticTransportKeys(t, inPrev, inCurr, inNext, outCurr, + rootKey, alice); + } else { + // The keys are more than two periods old - derive fresh keys + return deriveStaticTransportKeys(t, rootKey, alice, timePeriod); + } + } + @Override public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion, long streamNumber) { @@ -124,14 +218,14 @@ class TransportCryptoImpl implements TransportCrypto { // The input is the protocol version as a 16-bit integer, followed by // the stream number as a 64-bit integer byte[] protocolVersionBytes = new byte[INT_16_BYTES]; - ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0); + writeUint16(protocolVersion, protocolVersionBytes, 0); prf.update(protocolVersionBytes, 0, protocolVersionBytes.length); byte[] streamNumberBytes = new byte[INT_64_BYTES]; - ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0); + writeUint64(streamNumber, streamNumberBytes, 0); prf.update(streamNumberBytes, 0, streamNumberBytes.length); byte[] mac = new byte[macLength]; prf.doFinal(mac, 0); // The output is the first TAG_LENGTH bytes of the MAC - System.arraycopy(mac, 0, tag, 0, TAG_LENGTH); + arraycopy(mac, 0, tag, 0, TAG_LENGTH); } } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTest.java index 45c68c818..41bbc11db 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/crypto/KeyDerivationTest.java @@ -20,6 +20,7 @@ import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; public class KeyDerivationTest extends BrambleTestCase { @@ -137,7 +138,7 @@ public class KeyDerivationTest extends BrambleTestCase { @Test public void testTransportIdAffectsOutput() { TransportId transportId1 = getTransportId(); - assertFalse(transportId.getString().equals(transportId1.getString())); + assertNotEquals(transportId.getString(), transportId1.getString()); TransportKeys k = transportCrypto.deriveTransportKeys(transportId, master, 123, true, true); TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,