Skip to content

Commit 2cdf7be

Browse files
committed
"Wedge" handshakes after falling back
1 parent b1af66e commit 2cdf7be

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

src/main/java/com/eatthepath/noise/NoiseHandshake.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public class NoiseHandshake {
136136

137137
private int currentMessagePattern = 0;
138138
private boolean hasSplit = false;
139+
private boolean hasFallenBack = false;
139140

140141
private final CipherState cipherState;
141142
private final NoiseHash noiseHash;
@@ -478,6 +479,10 @@ public boolean isOneWayHandshake() {
478479
* @see #isDone()
479480
*/
480481
public boolean isExpectingRead() {
482+
if (hasFallenBack) {
483+
return false;
484+
}
485+
481486
if (currentMessagePattern < handshakePattern.getHandshakeMessagePatterns().length) {
482487
return handshakePattern.getHandshakeMessagePatterns()[currentMessagePattern].sender() != role;
483488
}
@@ -497,6 +502,10 @@ public boolean isExpectingRead() {
497502
* @see #isDone()
498503
*/
499504
public boolean isExpectingWrite() {
505+
if (hasFallenBack) {
506+
return false;
507+
}
508+
500509
if (currentMessagePattern < handshakePattern.getHandshakeMessagePatterns().length) {
501510
return handshakePattern.getHandshakeMessagePatterns()[currentMessagePattern].sender() == role;
502511
}
@@ -514,6 +523,10 @@ public boolean isExpectingWrite() {
514523
* @see #isExpectingWrite()
515524
*/
516525
public boolean isDone() {
526+
if (hasFallenBack) {
527+
return false;
528+
}
529+
517530
return currentMessagePattern == handshakePattern.getHandshakeMessagePatterns().length;
518531
}
519532

@@ -1246,7 +1259,6 @@ private void handleMixKeyToken(final HandshakePattern.Token token) {
12461259
* @see HandshakePattern#isFallbackPattern()
12471260
*/
12481261
public NoiseHandshake fallbackTo(final String handshakePatternName) throws NoSuchPatternException {
1249-
// TODO Self-destruct after falling back
12501262
return fallbackTo(handshakePatternName, null);
12511263
}
12521264

@@ -1271,6 +1283,10 @@ public NoiseHandshake fallbackTo(final String handshakePatternName) throws NoSuc
12711283
* @see HandshakePattern#isFallbackPattern()
12721284
*/
12731285
public NoiseHandshake fallbackTo(final String handshakePatternName, @Nullable final List<byte[]> preSharedKeys) throws NoSuchPatternException {
1286+
if (hasFallenBack) {
1287+
throw new IllegalStateException("Handshake has already fallen back to another pattern");
1288+
}
1289+
12741290
final HandshakePattern fallbackPattern = HandshakePattern.getInstance(handshakePatternName);
12751291

12761292
if (!fallbackPattern.isFallbackPattern()) {
@@ -1313,6 +1329,8 @@ public NoiseHandshake fallbackTo(final String handshakePatternName, @Nullable fi
13131329
fallbackRemoteEphemeralPublicKey = null;
13141330
}
13151331

1332+
hasFallenBack = true;
1333+
13161334
return new NoiseHandshake(role,
13171335
fallbackPattern,
13181336
keyAgreement,

src/test/java/com/eatthepath/noise/NoiseHandshakeTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import com.eatthepath.noise.component.NoiseKeyAgreement;
44
import org.junit.jupiter.api.Test;
55

6+
import javax.crypto.AEADBadTagException;
67
import javax.crypto.ShortBufferException;
78
import java.nio.ByteBuffer;
9+
import java.security.KeyPair;
810
import java.security.NoSuchAlgorithmException;
11+
import java.security.PublicKey;
912

1013
import static org.junit.jupiter.api.Assertions.*;
1114

@@ -130,4 +133,37 @@ void readMessageShortBuffer() throws NoSuchAlgorithmException {
130133
assertThrows(ShortBufferException.class, () ->
131134
handshake.readMessage(ByteBuffer.wrap(message), ByteBuffer.allocate(payloadLength - 1)));
132135
}
136+
137+
@Test
138+
void repeatedFallback() throws NoSuchAlgorithmException {
139+
final NoiseKeyAgreement keyAgreement = NoiseKeyAgreement.getInstance("25519");
140+
141+
final KeyPair initiatorStaticKeyPair = keyAgreement.generateKeyPair();
142+
final PublicKey staleRemoteStaticPublicKey = keyAgreement.generateKeyPair().getPublic();
143+
final KeyPair currentResponderStaticKeyPair = keyAgreement.generateKeyPair();
144+
145+
final byte[] initiatorStaticKeyMessage;
146+
{
147+
final NoiseHandshake ikInitiatorHandshake =
148+
NoiseHandshakeBuilder.forIKInitiator(initiatorStaticKeyPair, staleRemoteStaticPublicKey)
149+
.setComponentsFromProtocolName("Noise_IK_25519_AESGCM_SHA256")
150+
.build();
151+
152+
initiatorStaticKeyMessage = ikInitiatorHandshake.writeMessage((byte[]) null);
153+
}
154+
155+
final NoiseHandshake ikResponderHandshake =
156+
NoiseHandshakeBuilder.forIKResponder(currentResponderStaticKeyPair)
157+
.setComponentsFromProtocolName("Noise_IK_25519_AESGCM_SHA256")
158+
.build();
159+
160+
assertThrows(AEADBadTagException.class, () -> ikResponderHandshake.readMessage(initiatorStaticKeyMessage));
161+
162+
assertDoesNotThrow(() -> ikResponderHandshake.fallbackTo("XXfallback"));
163+
assertThrows(IllegalStateException.class, () -> ikResponderHandshake.fallbackTo("XXfallback"));
164+
165+
assertFalse(ikResponderHandshake.isExpectingRead());
166+
assertFalse(ikResponderHandshake.isExpectingWrite());
167+
assertFalse(ikResponderHandshake.isDone());
168+
}
133169
}

0 commit comments

Comments
 (0)