blob: e671e8c0ebab6ab08d566243c73ae7f34748cf65 [file] [log] [blame]
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.security.cryptauth.lib.securegcm;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.security.SignatureException;
import java.util.Arrays;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Base class for Android compatible tests for {@link D2DConnectionContext} subclasses.
* Note: We would use a Parameterized test runner to test different versions, but this
* functionality is not supported by Android tests.
*/
@RunWith(JUnit4.class)
public class D2DConnectionContextTest {
private static final String PING = "ping";
private static final String PONG = "pong";
// Key is: "initiator_encode_key_for_aes_256"
private static final SecretKey INITIATOR_ENCODE_KEY = new SecretKeySpec(
new byte[] {
(byte) 0x69, (byte) 0x6e, (byte) 0x69, (byte) 0x74, (byte) 0x69, (byte) 0x61, (byte) 0x74,
(byte) 0x6f, (byte) 0x72, (byte) 0x5f, (byte) 0x65, (byte) 0x6e, (byte) 0x63, (byte) 0x6f,
(byte) 0x64, (byte) 0x65, (byte) 0x5f, (byte) 0x6b, (byte) 0x65, (byte) 0x79, (byte) 0x5f,
(byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x5f, (byte) 0x61, (byte) 0x65, (byte) 0x73,
(byte) 0x5f, (byte) 0x32, (byte) 0x35, (byte) 0x36
},
"AES");
// Key is: "initiator_decode_key_for_aes_256"
private static final SecretKey INITIATOR_DECODE_KEY = new SecretKeySpec(
new byte[] {
(byte) 0x69, (byte) 0x6e, (byte) 0x69, (byte) 0x74, (byte) 0x69, (byte) 0x61, (byte) 0x74,
(byte) 0x6f, (byte) 0x72, (byte) 0x5f, (byte) 0x64, (byte) 0x65, (byte) 0x63, (byte) 0x6f,
(byte) 0x64, (byte) 0x65, (byte) 0x5f, (byte) 0x6b, (byte) 0x65, (byte) 0x79, (byte) 0x5f,
(byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x5f, (byte) 0x61, (byte) 0x65, (byte) 0x73,
(byte) 0x5f, (byte) 0x32, (byte) 0x35, (byte) 0x36
},
"AES");
private D2DConnectionContext initiatorCtx;
private D2DConnectionContext responderCtx;
@Before
public void setUp() throws Exception {
KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
}
protected void testPeerToPeerProtocol(int protocolVersion) throws Exception {
if (KeyEncoding.isLegacyCryptoRequired()) {
// this means we're running on an old SDK, which doesn't support the
// necessary crypto. Let's not test anything in this case.
return;
}
initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
// (send message to responder)
// responder
String messageStr = responderCtx.decodeMessageFromPeerAsString(pingMessage);
assertEquals(PING, messageStr);
byte[] pongMessage = responderCtx.encodeMessageToPeer(PONG);
// (send message to initiator)
// initiator
messageStr = initiatorCtx.decodeMessageFromPeerAsString(pongMessage);
assertEquals(PONG, messageStr);
// let's make sure there is actually some crypto involved.
pingMessage = initiatorCtx.encodeMessageToPeer("can you see this?");
pingMessage[2] = (byte) (pingMessage[2] + 1); // twiddle with the message
try {
responderCtx.decodeMessageFromPeerAsString(pingMessage);
fail("expected exception, but didn't get it");
} catch (SignatureException expected) {
assertTrue(expected.getMessage().contains("failed verification"));
}
// Try and replay the previous encoded message to the initiator (replays should not work).
try {
initiatorCtx.decodeMessageFromPeerAsString(pongMessage);
fail("expected exception, but didn't get it");
} catch (SignatureException expected) {
assertTrue(expected.getMessage().contains("sequence"));
}
assertEquals(protocolVersion, initiatorCtx.getProtocolVersion());
assertEquals(protocolVersion, responderCtx.getProtocolVersion());
}
@Test
public void testPeerToPeerProtocol_V0() throws Exception {
testPeerToPeerProtocol(D2DConnectionContextV0.PROTOCOL_VERSION);
}
@Test
public void testPeerToPeerProtocol_V1() throws Exception {
testPeerToPeerProtocol(D2DConnectionContextV1.PROTOCOL_VERSION);
}
protected void testResponderSendsFirst(int protocolVersion) throws Exception {
if (KeyEncoding.isLegacyCryptoRequired()) {
// this means we're running on an old SDK, which doesn't support the
// necessary crypto. Let's not test anything in this case.
return;
}
initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
byte[] pongMessage = responderCtx.encodeMessageToPeer(PONG);
assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
pongMessage = responderCtx.encodeMessageToPeer(PONG);
assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
// for good measure, if the initiator now responds, it should also work:
byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
pingMessage = initiatorCtx.encodeMessageToPeer(PING);
assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
pingMessage = initiatorCtx.encodeMessageToPeer(PING);
assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
}
@Test
public void testResponderSendsFirst_V0() throws Exception {
testResponderSendsFirst(D2DConnectionContextV0.PROTOCOL_VERSION);
}
@Test
public void testResponderSendsFirst_V1() throws Exception {
testResponderSendsFirst(D2DConnectionContextV1.PROTOCOL_VERSION);
}
protected void testAssymmetricFlows(int protocolVersion) throws Exception {
if (KeyEncoding.isLegacyCryptoRequired()) {
// this means we're running on an old SDK, which doesn't support the
// necessary crypto. Let's not test anything in this case.
return;
}
initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
// Let's test that this still works if one side sends a few messages in a row.
byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
pingMessage = initiatorCtx.encodeMessageToPeer(PING);
assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
pingMessage = initiatorCtx.encodeMessageToPeer(PING);
assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
byte[] pongMessage = responderCtx.encodeMessageToPeer(PONG);
assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
pongMessage = responderCtx.encodeMessageToPeer(PONG);
assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
}
@Test
public void testAssymmetricFlows_V0() throws Exception {
testAssymmetricFlows(D2DConnectionContextV0.PROTOCOL_VERSION);
}
@Test
public void testAssymmetricFlows_V1() throws Exception {
testAssymmetricFlows(D2DConnectionContextV1.PROTOCOL_VERSION);
}
public void testErrorWhenResponderResendsMessage(int protocolVersion) throws Exception {
if (KeyEncoding.isLegacyCryptoRequired()) {
// this means we're running on an old SDK, which doesn't support the
// necessary crypto. Let's not test anything in this case.
return;
}
initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
byte[] pongMessage = responderCtx.encodeMessageToPeer(PONG);
assertEquals(PONG, initiatorCtx.decodeMessageFromPeerAsString(pongMessage));
try {
// send pongMessage again to the initiator
initiatorCtx.decodeMessageFromPeerAsString(pongMessage);
fail("expected exception, but didn't get it");
} catch (SignatureException expected) {
assertTrue(expected.getMessage().contains("sequence"));
}
}
@Test
public void testErrorWhenResponderResendsMessage_V0() throws Exception {
testErrorWhenResponderResendsMessage(D2DConnectionContextV0.PROTOCOL_VERSION);
}
@Test
public void testErrorWhenResponderResendsMessage_V1() throws Exception {
testErrorWhenResponderResendsMessage(D2DConnectionContextV1.PROTOCOL_VERSION);
}
protected void testErrorWhenResponderEchoesInitiatorMessage(
int protocolVersion) throws Exception {
if (KeyEncoding.isLegacyCryptoRequired()) {
return;
}
initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
try {
initiatorCtx.decodeMessageFromPeerAsString(pingMessage);
fail("expected exception, but didn't get it");
} catch (SignatureException expected) {
}
}
@Test
public void testErrorWhenResponderEchoesInitiatorMessage_V0() throws Exception {
testErrorWhenResponderEchoesInitiatorMessage(D2DConnectionContextV0.PROTOCOL_VERSION);
}
@Test
public void testErrorWhenResponderEchoesInitiatorMessage_V1() throws Exception {
testErrorWhenResponderEchoesInitiatorMessage(D2DConnectionContextV1.PROTOCOL_VERSION);
}
@Test
public void testErrorUsingV1InitiatorWithV0Responder() throws SignatureException {
if (KeyEncoding.isLegacyCryptoRequired()) {
// this means we're running on an old SDK, which doesn't support the
// necessary crypto. Let's not test anything in this case.
return;
}
initiatorCtx = new D2DConnectionContextV1(INITIATOR_ENCODE_KEY, INITIATOR_DECODE_KEY, 1, 1);
responderCtx = new D2DConnectionContextV0(INITIATOR_DECODE_KEY, 1);
// Decoding the responder's message should succeed, because the decode key and sequence numbers
// match.
initiatorCtx.decodeMessageFromPeer(responderCtx.encodeMessageToPeer(PING));
// Responder fails to decodes initiator's encoded message because keys do not match.
try {
responderCtx.decodeMessageFromPeer(initiatorCtx.encodeMessageToPeer(PONG));
fail("Expected verification to fail.");
} catch (SignatureException e) {
// Exception expected.
}
}
@Test
public void testErrorWithV0InitiatorV1Responder() throws SignatureException {
if (KeyEncoding.isLegacyCryptoRequired()) {
// this means we're running on an old SDK, which doesn't support the
// necessary crypto. Let's not test anything in this case.
return;
}
initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, 1);
responderCtx = new D2DConnectionContextV1(INITIATOR_DECODE_KEY, INITIATOR_ENCODE_KEY, 1, 1);
// Decoding the initiator's message should succeed, because the decode key and sequence numbers
// match.
responderCtx.decodeMessageFromPeer(initiatorCtx.encodeMessageToPeer(PING));
// Initiator fails to decodes responder's encoded message because keys do not match.
try {
initiatorCtx.decodeMessageFromPeer(responderCtx.encodeMessageToPeer(PONG));
fail("Expected verification to fail.");
} catch (SignatureException e) {
// Exception expected.
}
}
protected void testSessionUnique(int protocolVersion) throws Exception {
// Should be the same (we set them up with the same key and sequence number)
initiatorCtx = createConnectionContext(protocolVersion, true /** isInitiator */);
responderCtx = createConnectionContext(protocolVersion, false /** isInitiator */);
Assert.assertArrayEquals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique());
// Change just the key (should not match)
SecretKey wrongKey = new SecretKeySpec("wrong".getBytes("UTF8"), "AES");
responderCtx = createConnectionContext(protocolVersion, false, wrongKey, wrongKey, 0, 1);
assertFalse(Arrays.equals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique()));
// Change just the sequence number (should still match)
responderCtx = createConnectionContext(
protocolVersion, false, INITIATOR_ENCODE_KEY, INITIATOR_DECODE_KEY, 2, 2);
Assert.assertArrayEquals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique());
}
@Test
public void testSessionUnique_V0() throws Exception {
testSessionUnique(D2DConnectionContextV0.PROTOCOL_VERSION);
}
@Test
public void testSessionUnique_V1() throws Exception {
testSessionUnique(D2DConnectionContextV1.PROTOCOL_VERSION);
}
@Test
public void testSessionUniqueValues_V0() throws Exception {
// The key and the session unique value should match ones in the equivalent test in
// @link {cs/Nearby/D2DCrypto/Tests/D2DConnectionContextTest.m}
byte[] key =
new byte[] {
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
(byte) 0x08, (byte) 0x09, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c, (byte) 0x0d, (byte) 0x0e,
(byte) 0x0f, (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15,
(byte) 0x16, (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1a, (byte) 0x1b, (byte) 0x1c,
(byte) 0x1d, (byte) 0x1e, (byte) 0x1f, (byte) 0x20
};
byte[] sessionUnique =
new byte[] {
(byte) 0x70, (byte) 0x7a, (byte) 0x17, (byte) 0x27, (byte) 0xa3, (byte) 0x0e, (byte) 0x68,
(byte) 0x63, (byte) 0x38, (byte) 0xdf, (byte) 0x72, (byte) 0x62, (byte) 0xf4, (byte) 0xb0,
(byte) 0x41, (byte) 0xac, (byte) 0x75, (byte) 0x8b, (byte) 0xca, (byte) 0x3b, (byte) 0x11,
(byte) 0xd4, (byte) 0x09, (byte) 0x64, (byte) 0x96, (byte) 0x54, (byte) 0xb4, (byte) 0x9b,
(byte) 0x43, (byte) 0xe6, (byte) 0x9b, (byte) 0xce
};
SecretKey secretKey = new SecretKeySpec(key, "AES");
D2DConnectionContext context = new D2DConnectionContextV0(secretKey, 1);
Assert.assertArrayEquals(context.getSessionUnique(), sessionUnique);
}
@Test
public void testSessionUniqueValues_V1_Initiator() throws Exception {
// The key and the session unique value should match ones in the equivalent test in
// @link {cs/Nearby/D2DCrypto/Tests/D2DConnectionContextTest.m}
byte[] sessionUnique =
new byte[] {
(byte) 0x91, (byte) 0xc7, (byte) 0xc9, (byte) 0x26, (byte) 0x2c, (byte) 0x17, (byte) 0x8a,
(byte) 0xa0, (byte) 0x36, (byte) 0x9f, (byte) 0xf2, (byte) 0x05, (byte) 0x20, (byte) 0x98,
(byte) 0x38, (byte) 0x53, (byte) 0xa5, (byte) 0x46, (byte) 0xab, (byte) 0x3a, (byte) 0x21,
(byte) 0x3b, (byte) 0x76, (byte) 0x58, (byte) 0x59, (byte) 0x4e, (byte) 0xe7, (byte) 0xe3,
(byte) 0xc1, (byte) 0x69, (byte) 0x87, (byte) 0xfa
};
D2DConnectionContext initiatorContext = new D2DConnectionContextV1(
INITIATOR_ENCODE_KEY, INITIATOR_DECODE_KEY, 0, 1);
D2DConnectionContext responderContext = new D2DConnectionContextV1(
INITIATOR_DECODE_KEY, INITIATOR_ENCODE_KEY, 1, 0);
// Both the initiator and responder must be the same.
Assert.assertArrayEquals(initiatorContext.getSessionUnique(), sessionUnique);
Assert.assertArrayEquals(responderContext.getSessionUnique(), sessionUnique);
}
@Test
public void testSaveSessionV0() throws Exception {
D2DConnectionContext initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, 1);
D2DConnectionContext responderCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, 1);
// Save the state
byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
byte[] responderSavedSessionState = responderCtx.saveSession();
// Try to rebuild the context
initiatorCtx = D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
responderCtx = D2DConnectionContext.fromSavedSession(responderSavedSessionState);
// Sanity check
assertEquals(1, initiatorCtx.getSequenceNumberForDecoding());
assertEquals(1, responderCtx.getSequenceNumberForDecoding());
Assert.assertArrayEquals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique());
// Make sure they can still talk to one another
assertEquals(PING,
responderCtx.decodeMessageFromPeerAsString(initiatorCtx.encodeMessageToPeer(PING)));
assertEquals(PONG,
initiatorCtx.decodeMessageFromPeerAsString(responderCtx.encodeMessageToPeer(PONG)));
}
@Test
public void testSaveSessionV0_negativeSeqNumber() throws Exception {
D2DConnectionContext initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, -5);
// Save the state
byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
// Try to rebuild the context
initiatorCtx = D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
// Sanity check
assertEquals(-5, initiatorCtx.getSequenceNumberForDecoding());
}
@Test
public void testSaveSessionV0_shortKey() throws Exception {
D2DConnectionContext initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, -5);
// Save the state
byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
// Try to rebuild the context
try {
D2DConnectionContext.fromSavedSession(Arrays.copyOf(initiatorSavedSessionState,
initiatorSavedSessionState.length - 1));
fail("Expected failure as key is too short");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testSaveSession_unknownProtocolVersion() throws Exception {
D2DConnectionContext initiatorCtx = new D2DConnectionContextV0(INITIATOR_ENCODE_KEY, -5);
// Save the state
byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
// Mess with the protocol version
initiatorSavedSessionState[0] = (byte) 0xff;
// Try to rebuild the context
try {
D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
fail("Expected failure as 0xff is not a valid protocol version");
} catch (IllegalArgumentException e) {
// expected
}
// Mess with the protocol version in the other direction
initiatorSavedSessionState[0] = 2;
// Try to rebuild the context
try {
D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
fail("Expected failure as 2 is not a valid protocol version");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testSaveSessionV1() throws Exception {
D2DConnectionContext initiatorCtx = new D2DConnectionContextV1(INITIATOR_ENCODE_KEY,
INITIATOR_DECODE_KEY, 0, 1);
D2DConnectionContext responderCtx = new D2DConnectionContextV1(INITIATOR_DECODE_KEY,
INITIATOR_ENCODE_KEY, 1, 0);
// Save the state
byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
byte[] responderSavedSessionState = responderCtx.saveSession();
// Try to rebuild the context
initiatorCtx = D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
responderCtx = D2DConnectionContext.fromSavedSession(responderSavedSessionState);
// Sanity check
assertEquals(1, initiatorCtx.getSequenceNumberForDecoding());
assertEquals(0, initiatorCtx.getSequenceNumberForEncoding());
assertEquals(0, responderCtx.getSequenceNumberForDecoding());
assertEquals(1, responderCtx.getSequenceNumberForEncoding());
Assert.assertArrayEquals(initiatorCtx.getSessionUnique(), responderCtx.getSessionUnique());
// Make sure they can still talk to one another
assertEquals(PING,
responderCtx.decodeMessageFromPeerAsString(initiatorCtx.encodeMessageToPeer(PING)));
assertEquals(PONG,
initiatorCtx.decodeMessageFromPeerAsString(responderCtx.encodeMessageToPeer(PONG)));
}
@Test
public void testSaveSessionV1_negativeSeqNumbers() throws Exception {
D2DConnectionContext initiatorCtx = new D2DConnectionContextV1(INITIATOR_ENCODE_KEY,
INITIATOR_DECODE_KEY, -8, -10);
// Save the state
byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
// Try to rebuild the context
initiatorCtx = D2DConnectionContext.fromSavedSession(initiatorSavedSessionState);
// Sanity check
assertEquals(-10, initiatorCtx.getSequenceNumberForDecoding());
assertEquals(-8, initiatorCtx.getSequenceNumberForEncoding());
}
@Test
public void testSaveSessionV1_tooShort() throws Exception {
D2DConnectionContext initiatorCtx = new D2DConnectionContextV1(INITIATOR_ENCODE_KEY,
INITIATOR_DECODE_KEY, -8, -10);
// Save the state
byte[] initiatorSavedSessionState = initiatorCtx.saveSession();
// Try to rebuild the context
try {
D2DConnectionContext.fromSavedSession(
Arrays.copyOf(initiatorSavedSessionState, initiatorSavedSessionState.length - 1));
fail("Expected error as saved session is too short");
} catch (IllegalArgumentException e) {
// expected
}
// Sanity check
assertEquals(-10, initiatorCtx.getSequenceNumberForDecoding());
assertEquals(-8, initiatorCtx.getSequenceNumberForEncoding());
}
D2DConnectionContext createConnectionContext(int protocolVersion, boolean isInitiator) {
return createConnectionContext(
protocolVersion, isInitiator, INITIATOR_ENCODE_KEY, INITIATOR_DECODE_KEY, 0, 1);
}
D2DConnectionContext createConnectionContext(
int protocolVersion, boolean isInitiator,
SecretKey initiatorEncodeKey, SecretKey initiatorDecodeKey,
int initiatorSequenceNumber, int responderSequenceNumber) {
if (protocolVersion == D2DConnectionContextV0.PROTOCOL_VERSION) {
return new D2DConnectionContextV0(initiatorEncodeKey, responderSequenceNumber);
} else if (protocolVersion == D2DConnectionContextV1.PROTOCOL_VERSION) {
return isInitiator
? new D2DConnectionContextV1(
initiatorEncodeKey, initiatorDecodeKey,
initiatorSequenceNumber, responderSequenceNumber)
: new D2DConnectionContextV1(
initiatorDecodeKey, initiatorEncodeKey,
responderSequenceNumber, initiatorSequenceNumber);
} else {
throw new IllegalArgumentException("Unknown version: " + protocolVersion);
}
}
}