summaryrefslogtreecommitdiff
path: root/src/main/javatest/com/google/security/cryptauth
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/javatest/com/google/security/cryptauth')
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextTest.java568
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshakeTest.java432
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ed25519Test.java195
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOpsTest.java134
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/KeyEncodingTest.java189
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/TransportCryptoOpsTest.java110
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2CppCompatibilityTest.java124
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2HandshakeTest.java818
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java342
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/CryptoOpsTest.java172
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/NullsGoogle3Test.java42
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtilTest.java412
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java403
-rw-r--r--src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java766
14 files changed, 0 insertions, 4707 deletions
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextTest.java
deleted file mode 100644
index e671e8c..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DConnectionContextTest.java
+++ /dev/null
@@ -1,568 +0,0 @@
-// 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);
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshakeTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshakeTest.java
deleted file mode 100644
index 4de794a..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/D2DDiffieHellmanKeyExchangeHandshakeTest.java
+++ /dev/null
@@ -1,432 +0,0 @@
-// 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 com.google.security.cryptauth.lib.securegcm.DeviceToDeviceMessagesProto.InitiatorHello;
-import com.google.security.cryptauth.lib.securegcm.DeviceToDeviceMessagesProto.ResponderHello;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.Payload;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.PayloadType;
-import com.google.security.cryptauth.lib.securemessage.PublicKeyProtoUtil;
-import java.nio.charset.Charset;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import javax.crypto.SecretKey;
-import junit.framework.TestCase;
-import org.junit.Assert;
-
-/**
- * Android compatible tests for the {@link D2DDiffieHellmanKeyExchangeHandshake} class.
- */
-public class D2DDiffieHellmanKeyExchangeHandshakeTest extends TestCase {
-
- private static final byte[] RESPONDER_HELLO_MESSAGE =
- "first payload".getBytes(Charset.forName("UTF-8"));
-
- private static final String PING = "ping";
-
- @Override
- protected void setUp() throws Exception {
- KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
- super.setUp();
- }
-
- public void testHandshakeWithPayload() 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;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- assertFalse(initiatorHandshakeContext.canSendPayloadInHandshakeMessage());
- assertFalse(initiatorHandshakeContext.isHandshakeComplete());
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- assertFalse(initiatorHandshakeContext.isHandshakeComplete());
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- byte[] payload = responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- assertEquals(0, payload.length);
- assertTrue(responderHandshakeContext.canSendPayloadInHandshakeMessage());
- assertFalse(responderHandshakeContext.isHandshakeComplete());
- byte[] responderHelloAndPayload = responderHandshakeContext.getNextHandshakeMessage(
- RESPONDER_HELLO_MESSAGE);
- assertTrue(responderHandshakeContext.isHandshakeComplete());
- D2DConnectionContext responderCtx = responderHandshakeContext.toConnectionContext();
- // (send responderHelloAndPayload to initiator)
-
- // initiator
- byte[] messageFromPayload =
- initiatorHandshakeContext.parseHandshakeMessage(responderHelloAndPayload);
- Assert.assertArrayEquals(RESPONDER_HELLO_MESSAGE, messageFromPayload);
- assertTrue(initiatorHandshakeContext.isHandshakeComplete());
- D2DConnectionContextV1 initiatorCtx =
- (D2DConnectionContextV1) initiatorHandshakeContext.toConnectionContext();
-
- // Test that that initiator and responder contexts are initialized correctly.
- checkInitializedConnectionContexts(initiatorCtx, responderCtx);
- }
-
- public void testHandshakeWithoutPayload() 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;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload = responderHandshakeContext.getNextHandshakeMessage();
- assertTrue(responderHandshakeContext.isHandshakeComplete());
- D2DConnectionContext responderCtx = responderHandshakeContext.toConnectionContext();
- // (send responderHelloAndPayload to initiator)
-
- // initiator
- byte[] messageFromPayload =
- initiatorHandshakeContext.parseHandshakeMessage(responderHelloAndPayload);
- assertEquals(0, messageFromPayload.length);
- assertTrue(initiatorHandshakeContext.isHandshakeComplete());
- D2DConnectionContext initiatorCtx = initiatorHandshakeContext.toConnectionContext();
-
- // Test that that initiator and responder contexts are initialized correctly.
- checkInitializedConnectionContexts(initiatorCtx, responderCtx);
- }
-
- public void testErrorWhenInitiatorOrResponderSendTwice() 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;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- try {
- initiatorHandshakeContext.getNextHandshakeMessage();
- fail("Expected error as initiator has no more initiator messages to send");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("Cannot get next message"));
- }
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- responderHandshakeContext.getNextHandshakeMessage();
- try {
- responderHandshakeContext.getNextHandshakeMessage();
- fail("Expected error as initiator has no more responder messages to send");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("Cannot get"));
- }
- }
-
- public void testInitiatorOrResponderFailOnEmptyMessage() 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;
- }
-
- D2DHandshakeContext handshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- try {
- handshakeContext.parseHandshakeMessage(null);
- fail("Expected to crash on null message");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("short"));
- }
- try {
- handshakeContext.parseHandshakeMessage(new byte[0]);
- fail("Expected to crash on empty message");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("short"));
- }
- }
-
- public void testPrematureConversionToConnection() 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;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- try {
- initiatorHandshakeContext.toConnectionContext();
- fail("Expected to crash: initiator hasn't done anything to deserve full connection");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("not complete"));
- }
-
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- try {
- initiatorHandshakeContext.toConnectionContext();
- fail("Expected to crash: initiator hasn't yet received responder's key");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("not complete"));
- }
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- try {
- initiatorHandshakeContext.toConnectionContext();
- fail("Expected to crash: responder hasn't yet send their key");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("not complete"));
- }
- }
-
- public void testCannotReuseHandshakeContext() 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;
- }
-
- // initiator:
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
- // (send initiatorHello to responder)
-
- // responder:
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload = responderHandshakeContext.getNextHandshakeMessage();
- D2DConnectionContext responderCtx = responderHandshakeContext.toConnectionContext();
- // (send responderHelloAndPayload to initiator)
-
- // initiator
- initiatorHandshakeContext.parseHandshakeMessage(responderHelloAndPayload);
- D2DConnectionContext initiatorCtx = initiatorHandshakeContext.toConnectionContext();
-
- // Test that that initiator and responder contexts are initialized correctly.
- checkInitializedConnectionContexts(initiatorCtx, responderCtx);
-
- // Try to get another full context
- try {
- initiatorHandshakeContext.toConnectionContext();
- fail("Expected crash: initiator context has already been used");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("used"));
- }
- try {
- responderHandshakeContext.toConnectionContext();
- fail("Expected crash: responder context has already been used");
- } catch (HandshakeException expected) {
- assertTrue(expected.getMessage().contains("used"));
- }
- }
-
- public void testErrorWhenInitiatorEchosResponderHello() 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;
- }
-
- // Initiator echoing back responder's first packet:
- D2DDiffieHellmanKeyExchangeHandshake partialInitiatorContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = partialInitiatorContext.getNextHandshakeMessage();
-
- D2DDiffieHellmanKeyExchangeHandshake partialResponderCtx =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- partialResponderCtx.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload =
- partialResponderCtx.getNextHandshakeMessage(RESPONDER_HELLO_MESSAGE);
- D2DConnectionContext responderCtx = partialResponderCtx.toConnectionContext();
-
- try {
- // initiator sends responderHelloAndPayload to responder
- responderCtx.decodeMessageFromPeerAsString(responderHelloAndPayload);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- assertTrue(expected.getMessage().contains("Signature failed verification"));
- }
- }
-
- public void testErrorWhenInitiatorResendsMessage() 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;
- }
-
- // Initiator repeating the same packet twice
- D2DDiffieHellmanKeyExchangeHandshake partialInitiatorContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = partialInitiatorContext.getNextHandshakeMessage();
-
- D2DDiffieHellmanKeyExchangeHandshake partialResponderCtx =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- partialResponderCtx.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload =
- partialResponderCtx.getNextHandshakeMessage(RESPONDER_HELLO_MESSAGE);
- D2DConnectionContext responderCtx = partialResponderCtx.toConnectionContext();
-
- partialInitiatorContext.parseHandshakeMessage(responderHelloAndPayload);
- D2DConnectionContext initiatorCtx = partialInitiatorContext.toConnectionContext();
-
- byte[] pingMessage = initiatorCtx.encodeMessageToPeer(PING);
- assertEquals(PING, responderCtx.decodeMessageFromPeerAsString(pingMessage));
-
- try {
- // send pingMessage to responder again
- responderCtx.decodeMessageFromPeerAsString(pingMessage);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- assertTrue(expected.getMessage().contains("sequence"));
- }
- }
-
- public void testErrorWhenResponderResendsFirstMessage() 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;
- }
-
- D2DDiffieHellmanKeyExchangeHandshake partialInitiatorContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = partialInitiatorContext.getNextHandshakeMessage();
-
- D2DDiffieHellmanKeyExchangeHandshake partialResponderCtx =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- partialResponderCtx.parseHandshakeMessage(initiatorHello);
- byte[] responderHelloAndPayload =
- partialResponderCtx.getNextHandshakeMessage(RESPONDER_HELLO_MESSAGE);
-
- partialInitiatorContext.parseHandshakeMessage(responderHelloAndPayload);
- D2DConnectionContext initiatorCtx = partialInitiatorContext.toConnectionContext();
-
- try {
- // Send the responderHelloAndPayload again. This time, the initiator will
- // process it as a normal message.
- initiatorCtx.decodeMessageFromPeerAsString(responderHelloAndPayload);
- fail("expected exception, but didn't get it");
- } catch (SignatureException expected) {
- assertTrue(expected.getMessage().contains("wrong message type"));
- }
- }
-
- public void testHandshakeWithInitiatorV1AndResponderV0() 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;
- }
-
- // Initialize initiator side.
- D2DHandshakeContext initiatorHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forInitiator();
- byte[] initiatorHello = initiatorHandshakeContext.getNextHandshakeMessage();
-
- // Set up keys used by the responder.
- PublicKey initiatorPublicKey = PublicKeyProtoUtil.parsePublicKey(
- InitiatorHello.parseFrom(initiatorHello).getPublicDhKey());
- KeyPair responderKeyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- SecretKey sharedKey =
- EnrollmentCryptoOps.doKeyAgreement(responderKeyPair.getPrivate(), initiatorPublicKey);
-
- // Construct a responder hello message without the version field, whose payload is encrypted
- // with the shared key.
- byte[] responderHello = D2DCryptoOps.signcryptPayload(
- new Payload(
- PayloadType.DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD,
- D2DConnectionContext.createDeviceToDeviceMessage(new byte[] {}, 1).toByteArray()),
- sharedKey,
- ResponderHello.newBuilder()
- .setPublicDhKey(
- PublicKeyProtoUtil.encodePublicKey(responderKeyPair.getPublic()))
- .build().toByteArray());
-
- // Handle V0 responder hello message.
- initiatorHandshakeContext.parseHandshakeMessage(responderHello);
- D2DConnectionContext initiatorCtx = initiatorHandshakeContext.toConnectionContext();
-
- assertEquals(D2DConnectionContextV0.PROTOCOL_VERSION, initiatorCtx.getProtocolVersion());
- assertEquals(1, initiatorCtx.getSequenceNumberForEncoding());
- assertEquals(1, initiatorCtx.getSequenceNumberForDecoding());
- }
-
- public void testHandshakeWithInitiatorV0AndResponderV1() 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;
- }
-
- // Construct an initiator hello message without the version field.
- byte[] initiatorHello = InitiatorHello.newBuilder()
- .setPublicDhKey(PublicKeyProtoUtil.encodePublicKey(
- PublicKeyProtoUtil.generateEcP256KeyPair().getPublic()))
- .build()
- .toByteArray();
-
- // Handle V0 initiator hello message.
- D2DHandshakeContext responderHandshakeContext =
- D2DDiffieHellmanKeyExchangeHandshake.forResponder();
- responderHandshakeContext.parseHandshakeMessage(initiatorHello);
- responderHandshakeContext.getNextHandshakeMessage();
- D2DConnectionContext responderCtx = responderHandshakeContext.toConnectionContext();
-
- assertEquals(D2DConnectionContextV0.PROTOCOL_VERSION, responderCtx.getProtocolVersion());
- assertEquals(1, responderCtx.getSequenceNumberForEncoding());
- assertEquals(1, responderCtx.getSequenceNumberForDecoding());
- }
-
- private void checkInitializedConnectionContexts(
- D2DConnectionContext initiatorCtx, D2DConnectionContext responderCtx) {
- assertNotNull(initiatorCtx);
- assertNotNull(responderCtx);
- assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, initiatorCtx.getProtocolVersion());
- assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, responderCtx.getProtocolVersion());
- assertEquals(initiatorCtx.getEncodeKey(), responderCtx.getDecodeKey());
- assertEquals(initiatorCtx.getDecodeKey(), responderCtx.getEncodeKey());
- assertEquals(0, initiatorCtx.getSequenceNumberForEncoding());
- assertEquals(1, initiatorCtx.getSequenceNumberForDecoding());
- assertEquals(1, responderCtx.getSequenceNumberForEncoding());
- assertEquals(0, responderCtx.getSequenceNumberForDecoding());
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ed25519Test.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ed25519Test.java
deleted file mode 100644
index 6ae95d8..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ed25519Test.java
+++ /dev/null
@@ -1,195 +0,0 @@
-// 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.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertThat;
-
-import com.google.security.cryptauth.lib.securegcm.Ed25519.Ed25519Exception;
-import java.math.BigInteger;
-import junit.framework.TestCase;
-
-/**
- * Android compatible tests for the {@link Ed25519} class.
- */
-public class Ed25519Test extends TestCase {
-
- // Points on the curve
- private static final int HEX_RADIX = 16;
- private static final BigInteger[] KM = new BigInteger[] {
- new BigInteger("1981FB43F103290ECF9772022DB8B19BFAF389057ED91E8486EB368763435925", HEX_RADIX),
- new BigInteger("A714C34F3B588AAC92FD2587884A20964FD351A1F147D5C4BBF5C2F37A77C36", HEX_RADIX)};
- private static final BigInteger[] KN = new BigInteger[] {
- new BigInteger("201A184F47D9A7973891D148E3D1C864D8084547131C2C1CEFB7EEBD26C63567", HEX_RADIX),
- new BigInteger("6DA2D3B18EC4F9AA3B08E39C997CD8BF6E9948FFD4FEFFECAF8DD0B3D648B7E8", HEX_RADIX)};
-
- // Curve prime P
- private static final BigInteger P =
- new BigInteger("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED", HEX_RADIX);
-
- // Test vectors obtain by multiplying KM by k by manually using the official implementation
- // see: http://ed25519.cr.yp.to/python/ed25519.py
- // k = 2
- private static final BigInteger[] KM_2 = new BigInteger[] {
- new BigInteger("718079972e63c2d62caf0ee93ec6f00337ceaff4e283181c04c4082b1d5e1ecf", HEX_RADIX),
- new BigInteger("143d18d393a8058c8614335bf36bf59364cc7c451db74726b322ce9d0b826d51", HEX_RADIX)
- };
- // k = 3
- private static final BigInteger[] KM_3 = new BigInteger[] {
- new BigInteger("39DA3C92EFC0577586B4D58F4A5C0BF65A6CC8F6BF358F38D70B2E6C28A31E8E", HEX_RADIX),
- new BigInteger("6D194F054B3FC2BE217F6A360BBEC747D2937FCEBD74B67FC3B20ED638ADD670", HEX_RADIX)
- };
- // k = 317698
- private static final BigInteger[] KM_317698 = new BigInteger[] {
- new BigInteger("7945D0ADEB568B16495476E81ADF281F4515439AE835914FBF6CEEAFEB9CD7E8", HEX_RADIX),
- new BigInteger("3631503DCDEBC0BF9BB1FFC3984A8CB52A34FFC2E77E9C19FD896DC6EE64A530", HEX_RADIX)
- };
- // k = P
- private static final BigInteger[] KM_HUGE = new BigInteger[] {
- new BigInteger("530162B05F440E00E219DFD3188524821C860C41FD87B9AC6AF2A283FDD585A1", HEX_RADIX),
- new BigInteger("48385A7D2BB858F3DB7F72E7CDFE218B9CA84DDA8BD64C3775AA43551D974F60", HEX_RADIX)
- };
- // k = P + 10000
- private static final BigInteger[] KM_XRAHUGE = new BigInteger[] {
- new BigInteger("16377E9F5EE2C0F4C70E17AC298EF670700A7CB186EEB0DA10CDD59635000AF8", HEX_RADIX),
- new BigInteger("5BD7921EEE662ACBAC3A96D8B6039D2356F154859FAF41FD2F0D99DF06CD2EAE", HEX_RADIX)
- };
-
- // Helpful constants
- private static final BigInteger ONE = BigInteger.ONE;
- private static final BigInteger ZERO = BigInteger.ZERO;
-
- // Identity element of the group (the zero) in affine and extended representations
- private static final BigInteger[] ID = new BigInteger[] {ZERO, ONE};
- private static final BigInteger[] ID_EX = new BigInteger[] {ZERO, ONE, ONE, ZERO};
-
- public void testValidPoints() 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;
- }
-
- // We've got a couple of valid points
- Ed25519.validateAffinePoint(KM);
- Ed25519.validateAffinePoint(KN);
-
- // And a bunch of invalid ones
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {ZERO, ONE});
- fail("Validate point not catching zero x coordinates");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("positive"));
- }
-
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {ONE, ZERO});
- fail("Validate point not catching zero y coordinates");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("positive"));
- }
-
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {new BigInteger("-1"), ONE});
- fail("Validate point not catching negative x coordinates");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("positive"));
- }
-
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {ONE, new BigInteger("-1")});
- fail("Validate point not catching negative y coordinates");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("positive"));
- }
-
- try {
- Ed25519.validateAffinePoint(new BigInteger[] {ONE, ONE});
- fail("Validate point not catching points that are not on curve");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("expected curve"));
- }
- }
-
- public void testAffineExtendedConversion() throws Exception {
- BigInteger[] km1 = Ed25519.toAffine(Ed25519.toExtended(KM));
- BigInteger[] kn1 = Ed25519.toAffine(Ed25519.toExtended(KN));
-
- assertArrayEquals(KM, km1);
- assertArrayEquals(KN, kn1);
-
- assertArrayEquals(ID, Ed25519.toAffine(ID_EX));
- assertArrayEquals(ID_EX, Ed25519.toExtended(ID));
- }
-
- public void testRepresentationCheck() throws Exception {
- Ed25519.checkPointIsInAffineRepresentation(KM);
- Ed25519.checkPointIsInExtendedRepresentation(ID_EX);
-
- try {
- Ed25519.checkPointIsInExtendedRepresentation(KM);
- fail("Point is not really in extended representation, expected failure");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("not in extended"));
- }
-
- try {
- Ed25519.checkPointIsInAffineRepresentation(Ed25519.toExtended(KM));
- fail("Point is not really in affine representation, expected failure");
- } catch (Ed25519Exception e) {
- assertThat(e.getMessage(), containsString("not in affine"));
- }
- }
-
- public void testAddSubtractExtendedPoints() throws Exception {
- // Adding/subtracting identity to/from itself should yield the identity point
- assertArrayEquals(ID, Ed25519.addAffinePoints(ID, ID));
- assertArrayEquals(ID, Ed25519.subtractAffinePoints(ID, ID));
-
- // In fact adding/subtracting the identity point to/from any point should yield that point
- assertArrayEquals(KM, Ed25519.addAffinePoints(KM, ID));
- assertArrayEquals(KM, Ed25519.subtractAffinePoints(KM, ID));
-
- // Subtracting a point from itself should yield the identity element
- assertArrayEquals(ID, Ed25519.subtractAffinePoints(KM, KM));
- assertArrayEquals(ID, Ed25519.subtractAffinePoints(KN, KN));
-
- // Adding and subtracting should yield the same point
- assertArrayEquals(KM, Ed25519.subtractAffinePoints(Ed25519.addAffinePoints(KM, KN), KN));
- assertArrayEquals(KN, Ed25519.subtractAffinePoints(Ed25519.addAffinePoints(KN, KM), KM));
- }
-
- public void testScalarMultiplyExtendedPoints() throws Exception {
- // A point times one is the point itself
- assertArrayEquals(KM, Ed25519.scalarMultiplyAffinePoint(KM, ONE));
- assertArrayEquals(KN, Ed25519.scalarMultiplyAffinePoint(KN, ONE));
-
- // A point times zero is the identity point
- assertArrayEquals(ID, Ed25519.scalarMultiplyAffinePoint(KM, ZERO));
- assertArrayEquals(ID, Ed25519.scalarMultiplyAffinePoint(KN, ZERO));
-
- // The identity times a scalar is the identity
- assertArrayEquals(ID, Ed25519.scalarMultiplyAffinePoint(ID, BigInteger.valueOf(317698)));
-
- // Use test vectors
- assertArrayEquals(KM_2, Ed25519.scalarMultiplyAffinePoint(KM, BigInteger.valueOf(2)));
- assertArrayEquals(KM_3, Ed25519.scalarMultiplyAffinePoint(KM, BigInteger.valueOf(3)));
- assertArrayEquals(KM_317698, Ed25519.scalarMultiplyAffinePoint(KM, BigInteger.valueOf(317698)));
- assertArrayEquals(KM_HUGE, Ed25519.scalarMultiplyAffinePoint(KM, P));
- assertArrayEquals(KM_XRAHUGE,
- Ed25519.scalarMultiplyAffinePoint(KM, P.add(BigInteger.valueOf(10000))));
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOpsTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOpsTest.java
deleted file mode 100644
index 4437045..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/EnrollmentCryptoOpsTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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 com.google.protobuf.ByteString;
-import com.google.security.cryptauth.lib.securegcm.SecureGcmProto.GcmDeviceInfo;
-import com.google.security.cryptauth.lib.securemessage.PublicKeyProtoUtil;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.Arrays;
-import javax.crypto.SecretKey;
-import junit.framework.TestCase;
-
-/**
- * Android compatible tests for the {@link EnrollmentCryptoOps} class.
- */
-public class EnrollmentCryptoOpsTest extends TestCase {
-
- private static final long DEVICE_ID = 1234567890L;
- private static final byte[] GCM_REGISTRATION_ID = { -0x80, 0, -0x80, 0, -0x80, 0 };
- private static final String DEVICE_MODEL = "TEST DEVICE";
- private static final String LOCALE = "en";
- private static final byte[] SESSION_ID = { 5, 5, 4, 4, 3, 3, 2, 2, 1, 1 };
- private static final String OAUTH_TOKEN = "1/23456etc";
-
- @Override
- protected void setUp() throws Exception {
- KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
- assertEquals(
- PublicKeyProtoUtil.isLegacyCryptoRequired(), KeyEncoding.isLegacyCryptoRequired());
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- KeyEncoding.setSimulateLegacyCrypto(false);
- super.tearDown();
- }
-
-
- public void testSimulatedEnrollment() throws Exception {
- boolean isLegacy = KeyEncoding.isLegacyCryptoRequired();
- // Step 1: Server generates an ephemeral DH key pair, saves the private key, and sends
- // the public key to the client as server_ephemeral_key.
- KeyPair serverEphemeralKeyPair =
- EnrollmentCryptoOps.generateEnrollmentKeyAgreementKeyPair(isLegacy);
- byte[] savedServerPrivateKey =
- KeyEncoding.encodeKeyAgreementPrivateKey(serverEphemeralKeyPair.getPrivate());
- byte[] serverEphemeralKey = KeyEncoding.encodeKeyAgreementPublicKey(
- serverEphemeralKeyPair.getPublic());
-
- // Step 2a: Client generates an ephemeral DH key pair, and completes the DH key exchange
- // to derive the master key.
- KeyPair clientEphemeralKeyPair =
- EnrollmentCryptoOps.generateEnrollmentKeyAgreementKeyPair(isLegacy);
- byte[] clientEphemeralKey = KeyEncoding.encodeKeyAgreementPublicKey(
- clientEphemeralKeyPair.getPublic());
- SecretKey clientMasterKey = EnrollmentCryptoOps.doKeyAgreement(
- clientEphemeralKeyPair.getPrivate(),
- KeyEncoding.parseKeyAgreementPublicKey(serverEphemeralKey));
-
- // Step 2b: Client generates its user key pair, and fills in a GcmDeviceInfo message containing
- // the enrollment request (which includes the user public key).
- KeyPair userKeyPair = isLegacy ? PublicKeyProtoUtil.generateRSA2048KeyPair()
- : PublicKeyProtoUtil.generateEcP256KeyPair();
- GcmDeviceInfo clientInfo = createGcmDeviceInfo(userKeyPair.getPublic(), clientMasterKey);
-
- // Step 2c: Client signcrypts the enrollment request to the server, using a combination of the
- // master key and its user signing key.
- byte[] enrollmentMessage = EnrollmentCryptoOps.encryptEnrollmentMessage(
- clientInfo, clientMasterKey, userKeyPair.getPrivate());
-
-
- // Step 3a: Server receives the client's DH public key and completes the key exchange using
- // the saved DH private key.
- SecretKey serverMasterKey = EnrollmentCryptoOps.doKeyAgreement(
- KeyEncoding.parseKeyAgreementPrivateKey(savedServerPrivateKey, isLegacy),
- KeyEncoding.parseKeyAgreementPublicKey(clientEphemeralKey));
-
- // Step 3b: Server uses the exchanged master key to de-signcrypt the enrollment request
- // (which also provides the user public key in the clear).
- GcmDeviceInfo serverInfo = EnrollmentCryptoOps.decryptEnrollmentMessage(
- enrollmentMessage, serverMasterKey, isLegacy);
-
- // Verify that the server sees the client's original enrollment request
- assertTrue(Arrays.equals(clientInfo.toByteArray(), serverInfo.toByteArray()));
-
- // Confirm that the server can recover a valid user PublicKey from the enrollment
- PublicKey serverUserPublicKey = KeyEncoding.parseUserPublicKey(
- serverInfo.getUserPublicKey().toByteArray());
- assertTrue(serverUserPublicKey.equals(userKeyPair.getPublic()));
- }
-
- public void testSimulatedEnrollmentWithForcedLegacy() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // We already test with legacy in this case
- return;
- }
- KeyEncoding.setSimulateLegacyCrypto(true);
- testSimulatedEnrollment();
- }
-
- private GcmDeviceInfo createGcmDeviceInfo(PublicKey userPublicKey, SecretKey masterKey) {
- // One possible method of generating a key handle:
- GenericPublicKey encodedUserPublicKey = PublicKeyProtoUtil.encodePublicKey(userPublicKey);
- byte[] keyHandle = EnrollmentCryptoOps.sha256(encodedUserPublicKey.toByteArray());
-
- return GcmDeviceInfo.newBuilder()
- .setAndroidDeviceId(DEVICE_ID)
- .setGcmRegistrationId(ByteString.copyFrom(GCM_REGISTRATION_ID))
- .setDeviceMasterKeyHash(
- ByteString.copyFrom(EnrollmentCryptoOps.getMasterKeyHash(masterKey)))
- .setUserPublicKey(ByteString.copyFrom(KeyEncoding.encodeUserPublicKey(userPublicKey)))
- .setDeviceModel(DEVICE_MODEL)
- .setLocale(LOCALE)
- .setKeyHandle(ByteString.copyFrom(keyHandle))
- .setEnrollmentSessionId(ByteString.copyFrom(SESSION_ID))
- .setOauthToken(OAUTH_TOKEN)
- .build();
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/KeyEncodingTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/KeyEncodingTest.java
deleted file mode 100644
index 7012eae..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/KeyEncodingTest.java
+++ /dev/null
@@ -1,189 +0,0 @@
-// 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 com.google.security.cryptauth.lib.securemessage.PublicKeyProtoUtil;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.Provider;
-import java.security.PublicKey;
-import java.security.Security;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import javax.crypto.interfaces.DHPrivateKey;
-import javax.crypto.interfaces.DHPublicKey;
-import junit.framework.TestCase;
-
-/**
- * Android compatible tests for the {@link KeyEncoding} class.
- */
-public class KeyEncodingTest extends TestCase {
- private static final byte[] RAW_KEY_BYTES = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2};
-
- private Boolean isLegacy;
- private KeyPair userKeyPair;
-
- @Override
- protected void setUp() throws Exception {
- installSunEcSecurityProviderIfNecessary();
- isLegacy = PublicKeyProtoUtil.isLegacyCryptoRequired();
- setUserKeyPair();
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- KeyEncoding.setSimulateLegacyCrypto(false);
- isLegacy = PublicKeyProtoUtil.isLegacyCryptoRequired();
- super.tearDown();
- }
-
- private void setUserKeyPair() {
- userKeyPair = isLegacy ? PublicKeyProtoUtil.generateRSA2048KeyPair()
- : PublicKeyProtoUtil.generateEcP256KeyPair();
- }
-
- public void testSimulateLegacyCrypto() {
- if (isLegacy) {
- return; // Nothing to test if we are already stuck in a legacy platform
- }
- assertFalse(KeyEncoding.isLegacyCryptoRequired());
- KeyEncoding.setSimulateLegacyCrypto(true);
- assertTrue(KeyEncoding.isLegacyCryptoRequired());
- }
-
- public void testMasterKeyEncoding() {
- // Require that master keys are encoded/decoded as raw byte arrays
- assertTrue(Arrays.equals(
- RAW_KEY_BYTES,
- KeyEncoding.encodeMasterKey(KeyEncoding.parseMasterKey(RAW_KEY_BYTES))));
- }
-
- public void testUserPublicKeyEncoding() throws InvalidKeySpecException {
- PublicKey pk = userKeyPair.getPublic();
- byte[] encodedPk = KeyEncoding.encodeUserPublicKey(pk);
- PublicKey decodedPk = KeyEncoding.parseUserPublicKey(encodedPk);
- assertKeysEqual(pk, decodedPk);
- }
-
- public void testUserPrivateKeyEncoding() throws InvalidKeySpecException {
- PrivateKey sk = userKeyPair.getPrivate();
- byte[] encodedSk = KeyEncoding.encodeUserPrivateKey(sk);
- PrivateKey decodedSk = KeyEncoding.parseUserPrivateKey(encodedSk, isLegacy);
- assertKeysEqual(sk, decodedSk);
- }
-
- public void testKeyAgreementPublicKeyEncoding() throws InvalidKeySpecException {
- KeyPair clientKeyPair = EnrollmentCryptoOps.generateEnrollmentKeyAgreementKeyPair(isLegacy);
- PublicKey pk = clientKeyPair.getPublic();
- byte[] encodedPk = KeyEncoding.encodeKeyAgreementPublicKey(pk);
- PublicKey decodedPk = KeyEncoding.parseKeyAgreementPublicKey(encodedPk);
- assertKeysEqual(pk, decodedPk);
- }
-
- public void testKeyAgreementPrivateKeyEncoding() throws InvalidKeySpecException {
- KeyPair clientKeyPair = EnrollmentCryptoOps.generateEnrollmentKeyAgreementKeyPair(isLegacy);
- PrivateKey sk = clientKeyPair.getPrivate();
- byte[] encodedSk = KeyEncoding.encodeKeyAgreementPrivateKey(sk);
- PrivateKey decodedSk = KeyEncoding.parseKeyAgreementPrivateKey(encodedSk, isLegacy);
- assertKeysEqual(sk, decodedSk);
- }
-
- public void testEncodingsWithForcedLegacy() throws InvalidKeySpecException {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // We already test with legacy in this case
- return;
- }
- KeyEncoding.setSimulateLegacyCrypto(true);
- isLegacy = true;
- setUserKeyPair();
- testUserPublicKeyEncoding();
- testUserPrivateKeyEncoding();
- testKeyAgreementPublicKeyEncoding();
- testKeyAgreementPrivateKeyEncoding();
- }
-
- public void testSigningPublicKeyEncoding() throws InvalidKeySpecException {
- KeyPair keyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- PublicKey pk = keyPair.getPublic();
- byte[] encodedPk = KeyEncoding.encodeSigningPublicKey(pk);
- PublicKey decodedPk = KeyEncoding.parseSigningPublicKey(encodedPk);
- assertKeysEqual(pk, decodedPk);
- }
-
- public void testSigningPrivateKeyEncoding() throws InvalidKeySpecException {
- KeyPair keyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- PrivateKey sk = keyPair.getPrivate();
- byte[] encodedSk = KeyEncoding.encodeSigningPrivateKey(sk);
- PrivateKey decodedSk = KeyEncoding.parseSigningPrivateKey(encodedSk);
- assertKeysEqual(sk, decodedSk);
- }
-
- public void testDeviceSyncPublicKeyEncoding() throws InvalidKeySpecException {
- KeyPair keyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- PublicKey pk = keyPair.getPublic();
- byte[] encodedPk = KeyEncoding.encodeDeviceSyncGroupPublicKey(pk);
- PublicKey decodedPk = KeyEncoding.parseDeviceSyncGroupPublicKey(encodedPk);
- assertKeysEqual(pk, decodedPk);
- }
-
- void assertKeysEqual(Key a, Key b) {
- if ((a instanceof ECPublicKey)
- || (a instanceof ECPrivateKey)
- || (a instanceof RSAPublicKey)
- || (a instanceof RSAPrivateKey)) {
- assertNotNull(a.getEncoded());
- assertTrue(Arrays.equals(a.getEncoded(), b.getEncoded()));
- }
- if (a instanceof DHPublicKey) {
- DHPublicKey ya = (DHPublicKey) a;
- DHPublicKey yb = (DHPublicKey) b;
- assertEquals(ya.getY(), yb.getY());
- assertEquals(ya.getParams().getG(), yb.getParams().getG());
- assertEquals(ya.getParams().getP(), yb.getParams().getP());
- }
- if (a instanceof DHPrivateKey) {
- DHPrivateKey xa = (DHPrivateKey) a;
- DHPrivateKey xb = (DHPrivateKey) b;
- assertEquals(xa.getX(), xb.getX());
- assertEquals(xa.getParams().getG(), xb.getParams().getG());
- assertEquals(xa.getParams().getP(), xb.getParams().getP());
- }
- }
-
- /**
- * Registers the SunEC security provider if no EC security providers are currently registered.
- */
- // TODO(shabsi): Remove this method when b/7891565 is fixed
- static void installSunEcSecurityProviderIfNecessary() {
- if (Security.getProviders("KeyPairGenerator.EC") == null) {
- try {
- Class<?> providerClass = Class.forName("sun.security.ec.SunEC");
- Security.addProvider((Provider) providerClass.newInstance());
- } catch (Exception e) {
- // SunEC is not available, nothing we can do
- }
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/TransportCryptoOpsTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/TransportCryptoOpsTest.java
deleted file mode 100644
index 9e45c0a..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/TransportCryptoOpsTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-// 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 com.google.security.cryptauth.lib.securegcm.SecureGcmProto.Tickle;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.Payload;
-import com.google.security.cryptauth.lib.securegcm.TransportCryptoOps.PayloadType;
-import com.google.security.cryptauth.lib.securemessage.PublicKeyProtoUtil;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.Arrays;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import junit.framework.TestCase;
-
-/**
- * Android compatible tests for the {@link TransportCryptoOps} class.
- */
-public class TransportCryptoOpsTest extends TestCase {
- private static final byte[] KEY_BYTES = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2
- };
- private static final byte[] KEY_HANDLE = { 9 };
-
- private SecretKey masterKey;
-
- @Override
- protected void setUp() throws Exception {
- KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
- masterKey = new SecretKeySpec(KEY_BYTES, "AES");
- super.setUp();
- }
-
- public void testServerMessage() throws Exception {
- long tickleExpiry = 12345L;
- Tickle tickle = Tickle.newBuilder()
- .setExpiryTime(tickleExpiry)
- .build();
-
- // Simulate sending a message
- byte[] signcryptedMessage = TransportCryptoOps.signcryptServerMessage(
- new Payload(PayloadType.TICKLE, tickle.toByteArray()),
- masterKey,
- KEY_HANDLE);
-
- // Simulate the process of receiving the message
- assertTrue(Arrays.equals(KEY_HANDLE, TransportCryptoOps.getKeyHandleFor(signcryptedMessage)));
- Payload received = TransportCryptoOps.verifydecryptServerMessage(signcryptedMessage, masterKey);
- assertEquals(PayloadType.TICKLE, received.getPayloadType());
- Tickle receivedTickle = Tickle.parseFrom(received.getMessage());
- assertEquals(tickleExpiry, receivedTickle.getExpiryTime());
- }
-
- public void testClientMessage() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- return; // This test isn't for legacy crypto
- }
- KeyPair userKeyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- doTestClientMessageWith(userKeyPair);
- }
-
- public void testClientMessageWithLegacyCrypto() throws Exception {
- KeyPair userKeyPair = PublicKeyProtoUtil.generateRSA2048KeyPair();
- doTestClientMessageWith(userKeyPair);
- }
-
- private void doTestClientMessageWith(KeyPair userKeyPair) throws Exception {
- PublicKey userPublicKey = userKeyPair.getPublic();
- // Will use a Tickle for the test message, even though that would normally
- // only be sent from the server to the client
- long tickleExpiry = 12345L;
- Tickle tickle = Tickle.newBuilder()
- .setExpiryTime(tickleExpiry)
- .build();
-
- // Simulate sending a message
- byte[] signcryptedMessage = TransportCryptoOps.signcryptClientMessage(
- new Payload(PayloadType.TICKLE, tickle.toByteArray()),
- userKeyPair,
- masterKey);
-
- // Simulate the process of receiving the message
- byte[] encodedUserPublicKey = TransportCryptoOps.getEncodedUserPublicKeyFor(signcryptedMessage);
- assertTrue(Arrays.equals(KeyEncoding.encodeUserPublicKey(userPublicKey), encodedUserPublicKey));
- userPublicKey = KeyEncoding.parseUserPublicKey(encodedUserPublicKey);
- // At this point the server would have looked up the masterKey for this userPublicKey
-
- Payload received = TransportCryptoOps.verifydecryptClientMessage(
- signcryptedMessage, userPublicKey, masterKey);
-
- assertEquals(PayloadType.TICKLE, received.getPayloadType());
- Tickle receivedTickle = Tickle.parseFrom(received.getMessage());
- assertEquals(tickleExpiry, receivedTickle.getExpiryTime());
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2CppCompatibilityTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2CppCompatibilityTest.java
deleted file mode 100644
index db319e0..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2CppCompatibilityTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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 com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.HandshakeCipher;
-import com.google.security.cryptauth.lib.securegcm.Ukey2ShellCppWrapper.Mode;
-import java.util.Arrays;
-import junit.framework.TestCase;
-
-/**
- * Tests the compatibility between the Java and C++ implementations of the UKEY2 protocol. This
- * integration test executes and talks to a compiled binary exposing the C++ implementation (wrapped
- * by {@link Ukey2ShellCppWrapper}).
- *
- * <p>The C++ implementation is located in //security/cryptauth/lib/securegcm.
- */
-public class Ukey2CppCompatibilityTest extends TestCase {
- private static final int VERIFICATION_STRING_LENGTH = 32;
-
- private static final byte[] sPayload1 = "payload to encrypt1".getBytes();
- private static final byte[] sPayload2 = "payload to encrypt2".getBytes();
-
- /** Tests full handshake with C++ client and Java server. */
- public void testCppClientJavaServer() throws Exception {
- Ukey2ShellCppWrapper cppUkey2Shell =
- new Ukey2ShellCppWrapper(Mode.INITIATOR, VERIFICATION_STRING_LENGTH);
- cppUkey2Shell.startShell();
- Ukey2Handshake javaUkey2Handshake = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
-
- // ClientInit:
- byte[] clientInit = cppUkey2Shell.readHandshakeMessage();
- javaUkey2Handshake.parseHandshakeMessage(clientInit);
-
- // ServerInit:
- byte[] serverInit = javaUkey2Handshake.getNextHandshakeMessage();
- cppUkey2Shell.writeHandshakeMessage(serverInit);
-
- // ClientFinished:
- byte[] clientFinished = cppUkey2Shell.readHandshakeMessage();
- javaUkey2Handshake.parseHandshakeMessage(clientFinished);
-
- // Verification String:
- cppUkey2Shell.confirmAuthString(
- javaUkey2Handshake.getVerificationString(VERIFICATION_STRING_LENGTH));
- javaUkey2Handshake.verifyHandshake();
-
- // Secure channel:
- D2DConnectionContext javaSecureContext = javaUkey2Handshake.toConnectionContext();
-
- // ukey2_shell encodes data:
- byte[] encodedData = cppUkey2Shell.sendEncryptCommand(sPayload1);
- byte[] decodedData = javaSecureContext.decodeMessageFromPeer(encodedData);
- assertTrue(Arrays.equals(sPayload1, decodedData));
-
- // ukey2_shell decodes data:
- encodedData = javaSecureContext.encodeMessageToPeer(sPayload2);
- decodedData = cppUkey2Shell.sendDecryptCommand(encodedData);
- assertTrue(Arrays.equals(sPayload2, decodedData));
-
- // ukey2_shell session unique:
- byte[] localSessionUnique = javaSecureContext.getSessionUnique();
- byte[] remoteSessionUnique = cppUkey2Shell.sendSessionUniqueCommand();
- assertTrue(Arrays.equals(localSessionUnique, remoteSessionUnique));
-
- cppUkey2Shell.stopShell();
- }
-
- /** Tests full handshake with C++ server and Java client. */
- public void testCppServerJavaClient() throws Exception {
- Ukey2ShellCppWrapper cppUkey2Shell =
- new Ukey2ShellCppWrapper(Mode.RESPONDER, VERIFICATION_STRING_LENGTH);
- cppUkey2Shell.startShell();
- Ukey2Handshake javaUkey2Handshake = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
-
- // ClientInit:
- byte[] clientInit = javaUkey2Handshake.getNextHandshakeMessage();
- cppUkey2Shell.writeHandshakeMessage(clientInit);
-
- // ServerInit:
- byte[] serverInit = cppUkey2Shell.readHandshakeMessage();
- javaUkey2Handshake.parseHandshakeMessage(serverInit);
-
- // ClientFinished:
- byte[] clientFinished = javaUkey2Handshake.getNextHandshakeMessage();
- cppUkey2Shell.writeHandshakeMessage(clientFinished);
-
- // Verification String:
- cppUkey2Shell.confirmAuthString(
- javaUkey2Handshake.getVerificationString(VERIFICATION_STRING_LENGTH));
- javaUkey2Handshake.verifyHandshake();
-
- // Secure channel:
- D2DConnectionContext javaSecureContext = javaUkey2Handshake.toConnectionContext();
-
- // ukey2_shell encodes data:
- byte[] encodedData = cppUkey2Shell.sendEncryptCommand(sPayload1);
- byte[] decodedData = javaSecureContext.decodeMessageFromPeer(encodedData);
- assertTrue(Arrays.equals(sPayload1, decodedData));
-
- // ukey2_shell decodes data:
- encodedData = javaSecureContext.encodeMessageToPeer(sPayload2);
- decodedData = cppUkey2Shell.sendDecryptCommand(encodedData);
- assertTrue(Arrays.equals(sPayload2, decodedData));
-
- // ukey2_shell session unique:
- byte[] localSessionUnique = javaSecureContext.getSessionUnique();
- byte[] remoteSessionUnique = cppUkey2Shell.sendSessionUniqueCommand();
- assertTrue(Arrays.equals(localSessionUnique, remoteSessionUnique));
-
- cppUkey2Shell.stopShell();
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2HandshakeTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2HandshakeTest.java
deleted file mode 100644
index 49e6f30..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2HandshakeTest.java
+++ /dev/null
@@ -1,818 +0,0 @@
-// 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 com.google.protobuf.ByteString;
-import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.AlertException;
-import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.HandshakeCipher;
-import com.google.security.cryptauth.lib.securegcm.Ukey2Handshake.State;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ClientFinished;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ClientInit;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ClientInit.CipherCommitment;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2Message;
-import com.google.security.cryptauth.lib.securegcm.UkeyProto.Ukey2ServerInit;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import junit.framework.TestCase;
-import org.junit.Assert;
-
-/**
- * Android compatible tests for the {@link Ukey2Handshake} class.
- */
-public class Ukey2HandshakeTest extends TestCase {
-
- private static final int MAX_AUTH_STRING_LENGTH = 32;
-
- @Override
- protected void setUp() throws Exception {
- KeyEncodingTest.installSunEcSecurityProviderIfNecessary();
- super.setUp();
- }
-
- /**
- * Tests correct use
- */
- public void testHandshake() 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;
- }
-
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage;
-
- assertEquals(State.IN_PROGRESS, client.getHandshakeState());
- assertEquals(State.IN_PROGRESS, server.getHandshakeState());
-
- // Message 1 (Client Init)
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- assertEquals(State.IN_PROGRESS, client.getHandshakeState());
- assertEquals(State.IN_PROGRESS, server.getHandshakeState());
-
- // Message 2 (Server Init)
- handshakeMessage = server.getNextHandshakeMessage();
- client.parseHandshakeMessage(handshakeMessage);
- assertEquals(State.IN_PROGRESS, client.getHandshakeState());
- assertEquals(State.IN_PROGRESS, server.getHandshakeState());
-
- // Message 3 (Client Finish)
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- assertEquals(State.VERIFICATION_NEEDED, client.getHandshakeState());
- assertEquals(State.VERIFICATION_NEEDED, server.getHandshakeState());
-
- // Get the auth string
- byte[] clientAuthString = client.getVerificationString(MAX_AUTH_STRING_LENGTH);
- byte[] serverAuthString = server.getVerificationString(MAX_AUTH_STRING_LENGTH);
- Assert.assertArrayEquals(clientAuthString, serverAuthString);
- assertEquals(State.VERIFICATION_IN_PROGRESS, client.getHandshakeState());
- assertEquals(State.VERIFICATION_IN_PROGRESS, server.getHandshakeState());
-
- // Verify the auth string
- client.verifyHandshake();
- server.verifyHandshake();
- assertEquals(State.FINISHED, client.getHandshakeState());
- assertEquals(State.FINISHED, server.getHandshakeState());
-
- // Make a context
- D2DConnectionContext clientContext = client.toConnectionContext();
- D2DConnectionContext serverContext = server.toConnectionContext();
- assertContextsCompatible(clientContext, serverContext);
- assertEquals(State.ALREADY_USED, client.getHandshakeState());
- assertEquals(State.ALREADY_USED, server.getHandshakeState());
- }
-
- /**
- * Verify enums for ciphers match the proto values
- */
- public void testCipherEnumValuesCorrect() {
- assertEquals(
- "You added a cipher, but forgot to change the test", 1, HandshakeCipher.values().length);
-
- assertEquals(UkeyProto.Ukey2HandshakeCipher.P256_SHA512,
- HandshakeCipher.P256_SHA512.getValue());
- }
-
- /**
- * Tests incorrect use by callers (client and servers accidentally sending the wrong message at
- * the wrong time)
- */
- public void testHandshakeClientAndServerSendRepeatedOutOfOrderMessages() 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;
- }
-
- // Client sends ClientInit (again) instead of ClientFinished
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- server.getNextHandshakeMessage(); // do this to avoid illegal state
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Expected Alert for client sending ClientInit twice");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
-
- // Server sends ClientInit back to client instead of ServerInit
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Expected Alert for server sending ClientInit back to client");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
-
- // Clients sends ServerInit back to client instead of ClientFinished
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Expected Alert for client sending ServerInit back to server");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
- }
-
- /**
- * Tests that verification codes are different for different handshake runs. Also tests a full
- * on-path attack.
- */
- public void testVerificationCodeUniqueToSession() 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;
- }
-
- // Client 1 and Server 1
- Ukey2Handshake client1 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server1 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client1.getNextHandshakeMessage();
- server1.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server1.getNextHandshakeMessage();
- client1.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client1.getNextHandshakeMessage();
- server1.parseHandshakeMessage(handshakeMessage);
- byte[] client1AuthString = client1.getVerificationString(MAX_AUTH_STRING_LENGTH);
- byte[] server1AuthString = server1.getVerificationString(MAX_AUTH_STRING_LENGTH);
- Assert.assertArrayEquals(client1AuthString, server1AuthString);
-
- // Client 2 and Server 2
- Ukey2Handshake client2 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server2 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client2.getNextHandshakeMessage();
- server2.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server2.getNextHandshakeMessage();
- client2.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client2.getNextHandshakeMessage();
- server2.parseHandshakeMessage(handshakeMessage);
- byte[] client2AuthString = client2.getVerificationString(MAX_AUTH_STRING_LENGTH);
- byte[] server2AuthString = server2.getVerificationString(MAX_AUTH_STRING_LENGTH);
- Assert.assertArrayEquals(client2AuthString, server2AuthString);
-
- // Make sure the verification strings differ
- assertFalse(Arrays.equals(client1AuthString, client2AuthString));
- }
-
- /**
- * Test an attack where the adversary swaps out the public key in the final message (i.e.,
- * commitment doesn't match public key)
- */
- public void testPublicKeyDoesntMatchCommitment() 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;
- }
-
- // Run handshake as usual, but stop before sending client finished
- Ukey2Handshake client1 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server1 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client1.getNextHandshakeMessage();
- server1.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server1.getNextHandshakeMessage();
-
- // Run another handshake and get the final client finished
- Ukey2Handshake client2 = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server2 = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client2.getNextHandshakeMessage();
- server2.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server2.getNextHandshakeMessage();
- client2.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client2.getNextHandshakeMessage();
-
- // Now use the client finished from second handshake in first handshake (simulates where an
- // attacker switches out the last message).
- try {
- server1.parseHandshakeMessage(handshakeMessage);
- fail("Expected server to catch mismatched ClientFinished");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server1.getHandshakeState());
-
- // Make sure caller can't actually do anything with the server now that an error has occurred
- try {
- server1.getVerificationString(MAX_AUTH_STRING_LENGTH);
- fail("Server allows operations post error");
- } catch (IllegalStateException e) {
- // success
- }
- try {
- server1.verifyHandshake();
- fail("Server allows operations post error");
- } catch (IllegalStateException e) {
- // success
- }
- try {
- server1.toConnectionContext();
- fail("Server allows operations post error");
- } catch (IllegalStateException e) {
- // success
- }
- }
-
- /**
- * Test commitment having unsupported version
- */
- public void testClientInitUnsupportedVersion() 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;
- }
-
- // Get ClientInit and modify the version to be too big
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ClientInit.Builder clientInit =
- Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData()));
- clientInit.setVersion(Ukey2Handshake.VERSION + 1);
- message.setMessageData(ByteString.copyFrom(clientInit.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch unsupported version (too big) in ClientInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
-
- // Get ClientInit and modify the version to be too big
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
-
- message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- clientInit = Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData()));
- clientInit.setVersion(0 /* minimum version is 1 */);
- message.setMessageData(ByteString.copyFrom(clientInit.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch unsupported version (too small) in ClientInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
- }
-
- /**
- * Tests that server catches wrong number of random bytes in ClientInit
- */
- public void testWrongNonceLengthInClientInit() 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;
- }
-
- // Get ClientInit and modify the nonce
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ClientInit.Builder clientInit =
- Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData()));
- clientInit.setRandom(
- ByteString.copyFrom(
- Arrays.copyOf(
- clientInit.getRandom().toByteArray(),
- 31 /* as per go/ukey2, nonces must be 32 bytes long */)));
- message.setMessageData(ByteString.copyFrom(clientInit.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch nonce being too short in ClientInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
- }
-
- /**
- * Test that server catches missing commitment in ClientInit message
- */
- public void testServerCatchesMissingCommitmentInClientInit() 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;
- }
-
- // Get ClientInit and modify the commitment
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ClientInit clientInit =
- Ukey2ClientInit.newBuilder(Ukey2ClientInit.parseFrom(message.getMessageData()))
- .build();
- Ukey2ClientInit.Builder badClientInit = Ukey2ClientInit.newBuilder()
- .setVersion(clientInit.getVersion())
- .setRandom(clientInit.getRandom());
- for (CipherCommitment commitment : clientInit.getCipherCommitmentsList()) {
- badClientInit.addCipherCommitments(commitment);
- }
-
- message.setMessageData(ByteString.copyFrom(badClientInit.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch missing commitment in ClientInit");
- } catch (AlertException e) {
- // success
- }
- }
-
- /**
- * Test that client catches invalid version in ServerInit
- */
- public void testServerInitUnsupportedVersion() 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;
- }
-
- // Get ServerInit and modify the version to be too big
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ServerInit serverInit =
- Ukey2ServerInit.newBuilder(Ukey2ServerInit.parseFrom(message.getMessageData()))
- .setVersion(Ukey2Handshake.VERSION + 1)
- .build();
- message.setMessageData(ByteString.copyFrom(serverInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch unsupported version (too big) in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
-
- // Get ServerInit and modify the version to be too big
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
-
- message = Ukey2Message.newBuilder(Ukey2Message.parseFrom(handshakeMessage));
- serverInit =
- Ukey2ServerInit.newBuilder(Ukey2ServerInit.parseFrom(message.getMessageData()))
- .setVersion(0 /* minimum version is 1 */)
- .build();
- message.setMessageData(ByteString.copyFrom(serverInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch unsupported version (too small) in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
- }
-
- /**
- * Tests that client catches wrong number of random bytes in ServerInit
- */
- public void testWrongNonceLengthInServerInit() 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;
- }
-
- // Get ServerInit and modify the nonce
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
-
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ServerInit.Builder serverInitBuilder =
- Ukey2ServerInit.newBuilder(Ukey2ServerInit.parseFrom(message.getMessageData()));
- Ukey2ServerInit serverInit = serverInitBuilder.setRandom(ByteString.copyFrom(Arrays.copyOf(
- serverInitBuilder.getRandom().toByteArray(),
- 31 /* as per go/ukey2, nonces must be 32 bytes long */)))
- .build();
- message.setMessageData(ByteString.copyFrom(serverInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch nonce being too short in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
- }
-
- /**
- * Test that client catches missing or incorrect handshake cipher in serverInit
- */
- public void testMissingOrIncorrectHandshakeCipherInServerInit() 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;
- }
-
- // Get ServerInit
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ServerInit serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- // remove handshake cipher
- Ukey2ServerInit badServerInit = Ukey2ServerInit.newBuilder()
- .setPublicKey(serverInit.getPublicKey())
- .setRandom(serverInit.getRandom())
- .setVersion(serverInit.getVersion())
- .build();
-
- message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch missing handshake cipher in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
-
- // Get ServerInit
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- message = Ukey2Message.newBuilder(Ukey2Message.parseFrom(handshakeMessage));
- serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- // put in a bad handshake cipher
- badServerInit = Ukey2ServerInit.newBuilder()
- .setPublicKey(serverInit.getPublicKey())
- .setRandom(serverInit.getRandom())
- .setVersion(serverInit.getVersion())
- .setHandshakeCipher(UkeyProto.Ukey2HandshakeCipher.RESERVED)
- .build();
-
- message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch bad handshake cipher in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
- }
-
- /**
- * Test that client catches missing or incorrect public key in serverInit
- */
- public void testMissingOrIncorrectPublicKeyInServerInit() 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;
- }
-
- // Get ServerInit
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- Ukey2ServerInit serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- // remove public key
- Ukey2ServerInit badServerInit = Ukey2ServerInit.newBuilder()
- .setRandom(serverInit.getRandom())
- .setVersion(serverInit.getVersion())
- .setHandshakeCipher(serverInit.getHandshakeCipher())
- .build();
-
- message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch missing public key in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
-
- // Get ServerInit
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
- serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- // put in a bad public key
- badServerInit = Ukey2ServerInit.newBuilder()
- .setPublicKey(ByteString.copyFrom(new byte[] {42, 12, 1}))
- .setRandom(serverInit.getRandom())
- .setVersion(serverInit.getVersion())
- .setHandshakeCipher(serverInit.getHandshakeCipher())
- .build();
-
- message.setMessageData(ByteString.copyFrom(badServerInit.toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- client.parseHandshakeMessage(handshakeMessage);
- fail("Client did not catch bad public key in ServerInit");
- } catch (AlertException e) {
- // success
- }
- assertEquals(State.ERROR, client.getHandshakeState());
- }
-
- /**
- * Test that client catches missing or incorrect public key in clientFinished
- */
- public void testMissingOrIncorrectPublicKeyInClientFinished() 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;
- }
-
- // Get ClientFinished
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- client.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client.getNextHandshakeMessage();
- Ukey2Message.Builder message = Ukey2Message.newBuilder(
- Ukey2Message.parseFrom(handshakeMessage));
-
- // remove public key
- Ukey2ClientFinished.Builder badClientFinished = Ukey2ClientFinished.newBuilder();
-
- message.setMessageData(ByteString.copyFrom(badClientFinished.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch missing public key in ClientFinished");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
-
- // Get ClientFinished
- client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- client.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client.getNextHandshakeMessage();
- message = Ukey2Message.newBuilder(Ukey2Message.parseFrom(handshakeMessage));
-
- // remove public key
- badClientFinished = Ukey2ClientFinished.newBuilder()
- .setPublicKey(ByteString.copyFrom(new byte[] {42, 12, 1}));
-
- message.setMessageData(ByteString.copyFrom(badClientFinished.build().toByteArray()));
- handshakeMessage = message.build().toByteArray();
-
- try {
- server.parseHandshakeMessage(handshakeMessage);
- fail("Server did not catch bad public key in ClientFinished");
- } catch (HandshakeException e) {
- // success
- }
- assertEquals(State.ERROR, server.getHandshakeState());
- }
-
- /**
- * Tests that items (nonces, commitments, public keys) that should be random are at least
- * different on every run.
- */
- public void testRandomItemsDifferentOnEveryRun() 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;
- }
-
- int numberOfRuns = 50;
-
- // Search for collisions
- Set<Integer> commitments = new HashSet<>(numberOfRuns);
- Set<Integer> clientNonces = new HashSet<>(numberOfRuns);
- Set<Integer> serverNonces = new HashSet<>(numberOfRuns);
- Set<Integer> serverPublicKeys = new HashSet<>(numberOfRuns);
- Set<Integer> clientPublicKeys = new HashSet<>(numberOfRuns);
-
- for (int i = 0; i < numberOfRuns; i++) {
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- Ukey2Message message = Ukey2Message.parseFrom(handshakeMessage);
- Ukey2ClientInit clientInit = Ukey2ClientInit.parseFrom(message.getMessageData());
-
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- message = Ukey2Message.parseFrom(handshakeMessage);
- Ukey2ServerInit serverInit = Ukey2ServerInit.parseFrom(message.getMessageData());
-
- client.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client.getNextHandshakeMessage();
- message = Ukey2Message.parseFrom(handshakeMessage);
- Ukey2ClientFinished clientFinished = Ukey2ClientFinished.parseFrom(message.getMessageData());
-
- // Clean up to save some memory (b/32054837)
- client = null;
- server = null;
- handshakeMessage = null;
- message = null;
- System.gc();
-
- // ClientInit randomness
- Integer nonceHash = Integer.valueOf(Arrays.hashCode(clientInit.getRandom().toByteArray()));
- if (clientNonces.contains(nonceHash) || serverNonces.contains(nonceHash)) {
- fail("Nonce in ClientINit has repeated!");
- }
- clientNonces.add(nonceHash);
-
- Integer commitmentHash = 0;
- for (CipherCommitment commitement : clientInit.getCipherCommitmentsList()) {
- commitmentHash += Arrays.hashCode(commitement.toByteArray());
- }
- if (commitments.contains(nonceHash)) {
- fail("Commitment has repeated!");
- }
- commitments.add(commitmentHash);
-
- // ServerInit randomness
- nonceHash = Integer.valueOf(Arrays.hashCode(serverInit.getRandom().toByteArray()));
- if (serverNonces.contains(nonceHash) || clientNonces.contains(nonceHash)) {
- fail("Nonce in ServerInit repeated!");
- }
- serverNonces.add(nonceHash);
-
- Integer publicKeyHash =
- Integer.valueOf(Arrays.hashCode(serverInit.getPublicKey().toByteArray()));
- if (serverPublicKeys.contains(publicKeyHash) || clientPublicKeys.contains(publicKeyHash)) {
- fail("Public Key in ServerInit repeated!");
- }
- serverPublicKeys.add(publicKeyHash);
-
- // Client Finished randomness
- publicKeyHash = Integer.valueOf(Arrays.hashCode(clientFinished.getPublicKey().toByteArray()));
- if (serverPublicKeys.contains(publicKeyHash) || clientPublicKeys.contains(publicKeyHash)) {
- fail("Public Key in ClientFinished repeated!");
- }
- clientPublicKeys.add(publicKeyHash);
- }
- }
-
- /**
- * Tests that {@link Ukey2Handshake#getVerificationString(int)} enforces sane verification string
- * lengths.
- */
- public void testGetVerificationEnforcesSaneLengths() 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;
- }
-
- // Run the protocol
- Ukey2Handshake client = Ukey2Handshake.forInitiator(HandshakeCipher.P256_SHA512);
- Ukey2Handshake server = Ukey2Handshake.forResponder(HandshakeCipher.P256_SHA512);
- byte[] handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = server.getNextHandshakeMessage();
- client.parseHandshakeMessage(handshakeMessage);
- handshakeMessage = client.getNextHandshakeMessage();
- server.parseHandshakeMessage(handshakeMessage);
-
- // Try to get too short verification string
- try {
- client.getVerificationString(0);
- fail("Too short verification string allowed");
- } catch (IllegalArgumentException e) {
- // success
- }
-
- // Try to get too long verification string
- try {
- server.getVerificationString(MAX_AUTH_STRING_LENGTH + 1);
- fail("Too long verification string allowed");
- } catch (IllegalArgumentException e) {
- // success
- }
- }
-
- /**
- * Asserts that the given client and server contexts are compatible
- */
- private void assertContextsCompatible(
- D2DConnectionContext clientContext, D2DConnectionContext serverContext) {
- assertNotNull(clientContext);
- assertNotNull(serverContext);
- assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, clientContext.getProtocolVersion());
- assertEquals(D2DConnectionContextV1.PROTOCOL_VERSION, serverContext.getProtocolVersion());
- assertEquals(clientContext.getEncodeKey(), serverContext.getDecodeKey());
- assertEquals(clientContext.getDecodeKey(), serverContext.getEncodeKey());
- assertFalse(clientContext.getEncodeKey().equals(clientContext.getDecodeKey()));
- assertEquals(0, clientContext.getSequenceNumberForEncoding());
- assertEquals(0, clientContext.getSequenceNumberForDecoding());
- assertEquals(0, serverContext.getSequenceNumberForEncoding());
- assertEquals(0, serverContext.getSequenceNumberForDecoding());
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java b/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java
deleted file mode 100644
index 2b73653..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securegcm/Ukey2ShellCppWrapper.java
+++ /dev/null
@@ -1,342 +0,0 @@
-// 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 com.google.common.io.BaseEncoding;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.ProcessBuilder.Redirect;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import javax.annotation.Nullable;
-
-/**
- * A wrapper to execute and interact with the //security/cryptauth/lib/securegcm:ukey2_shell binary.
- *
- * <p>This binary is a shell over the C++ implementation of the UKEY2 protocol, so this wrapper is
- * used to test compatibility between the C++ and Java implementations.
- *
- * <p>The ukey2_shell is invoked as follows:
- *
- * <pre>{@code
- * ukey2_shell --mode=<mode> --verification_string_length=<length>
- * }</pre>
- *
- * where {@code mode={initiator, responder}} and {@code verification_string_length} is a positive
- * integer.
- */
-public class Ukey2ShellCppWrapper {
- // The path the the ukey2_shell binary.
- private static final String BINARY_PATH = "build/src/main/cpp/src/securegcm/ukey2_shell";
-
- // The time to wait before timing out a read or write operation to the shell.
- @SuppressWarnings("GoodTime") // TODO(b/147378611): store a java.time.Duration instead
- private static final long IO_TIMEOUT_MILLIS = 5000;
-
- public enum Mode {
- INITIATOR,
- RESPONDER
- }
-
- private final Mode mode;
- private final int verificationStringLength;
- private final ExecutorService executorService;
-
- @Nullable private Process shellProcess;
- private boolean secureContextEstablished;
-
- /**
- * @param mode The mode to run the shell in (initiator or responder).
- * @param verificationStringLength The length of the verification string used in the handshake.
- */
- public Ukey2ShellCppWrapper(Mode mode, int verificationStringLength) {
- this.mode = mode;
- this.verificationStringLength = verificationStringLength;
- this.executorService = Executors.newSingleThreadExecutor();
- }
-
- /**
- * Begins execution of the ukey2_shell binary.
- *
- * @throws IOException
- */
- public void startShell() throws IOException {
- if (shellProcess != null) {
- throw new IllegalStateException("Shell already started.");
- }
-
- String modeArg = "--mode=" + getModeString();
- String verificationStringLengthArg = "--verification_string_length=" + verificationStringLength;
-
- final ProcessBuilder builder =
- new ProcessBuilder(BINARY_PATH, modeArg, verificationStringLengthArg);
-
- // Merge the shell's stderr with the stderr of the current process.
- builder.redirectError(Redirect.INHERIT);
-
- shellProcess = builder.start();
- }
-
- /**
- * Stops execution of the ukey2_shell binary.
- *
- * @throws IOException
- */
- public void stopShell() {
- if (shellProcess == null) {
- throw new IllegalStateException("Shell not started.");
- }
- shellProcess.destroy();
- }
-
- /**
- * @return the handshake message read from the shell.
- * @throws IOException
- */
- public byte[] readHandshakeMessage() throws IOException {
- return readFrameWithTimeout();
- }
-
- /**
- * Sends the handshake message to the shell.
- *
- * @param message
- * @throws IOException
- */
- public void writeHandshakeMessage(byte[] message) throws IOException {
- writeFrameWithTimeout(message);
- }
-
- /**
- * Reads the auth string from the shell and compares it with {@code authString}. If verification
- * succeeds, then write "ok" back as a confirmation.
- *
- * @param authString the auth string to compare to.
- * @throws IOException
- */
- public void confirmAuthString(byte[] authString) throws IOException {
- byte[] shellAuthString = readFrameWithTimeout();
- if (!Arrays.equals(authString, shellAuthString)) {
- throw new IOException(
- String.format(
- "Unable to verify auth string: 0x%s != 0x%s",
- BaseEncoding.base16().encode(authString),
- BaseEncoding.base16().encode(shellAuthString)));
- }
- writeFrameWithTimeout("ok".getBytes());
- secureContextEstablished = true;
- }
-
- /**
- * Sends {@code payload} to be encrypted by the shell. This function can only be called after a
- * handshake is performed and a secure context established.
- *
- * @param payload the data to be encrypted.
- * @return the encrypted message returned by the shell.
- * @throws IOException
- */
- public byte[] sendEncryptCommand(byte[] payload) throws IOException {
- writeFrameWithTimeout(createExpression("encrypt", payload));
- return readFrameWithTimeout();
- }
-
- /**
- * Sends {@code message} to be decrypted by the shell. This function can only be called after a
- * handshake is performed and a secure context established.
- *
- * @param message the data to be decrypted.
- * @return the decrypted payload returned by the shell.
- * @throws IOException
- */
- public byte[] sendDecryptCommand(byte[] message) throws IOException {
- writeFrameWithTimeout(createExpression("decrypt", message));
- return readFrameWithTimeout();
- }
-
- /**
- * Requests the session unique value from the shell. This function can only be called after a
- * handshake is performed and a secure context established.
- *
- * @return the session unique value returned by the shell.
- * @throws IOException
- */
- public byte[] sendSessionUniqueCommand() throws IOException {
- writeFrameWithTimeout(createExpression("session_unique", null));
- return readFrameWithTimeout();
- }
-
- /**
- * Reads a frame from the shell's stdout with a timeout.
- *
- * @return The contents of the frame.
- * @throws IOException
- */
- private byte[] readFrameWithTimeout() throws IOException {
- Future<byte[]> future =
- executorService.submit(
- new Callable<byte[]>() {
- @Override
- public byte[] call() throws Exception {
- return readFrame();
- }
- });
-
- try {
- return future.get(IO_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Writes a frame to the shell's stdin with a timeout.
- *
- * @param contents the contents of the frame.
- * @throws IOException
- */
- private void writeFrameWithTimeout(final byte[] contents) throws IOException {
- Future<?> future =
- executorService.submit(
- new Runnable() {
- @Override
- public void run() {
- try {
- writeFrame(contents);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- });
-
- try {
- future.get(IO_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- throw new IOException(e);
- }
- }
-
- /**
- * Reads a frame from the shell's stdout, which has the format:
- *
- * <pre>{@code
- * +---------------------+-----------------+
- * | 4-bytes | |length| bytes |
- * +---------------------+-----------------+
- * | (unsigned) length | contents |
- * +---------------------+-----------------+
- * }</pre>
- *
- * @return the contents that were read
- * @throws IOException
- */
- private byte[] readFrame() throws IOException {
- if (shellProcess == null) {
- throw new IllegalStateException("Shell not started.");
- }
-
- InputStream inputStream = shellProcess.getInputStream();
- byte[] lengthBytes = new byte[4];
- if (inputStream.read(lengthBytes) != lengthBytes.length) {
- throw new IOException("Failed to read length.");
- }
-
- int length = ByteBuffer.wrap(lengthBytes).order(ByteOrder.BIG_ENDIAN).getInt();
- if (length < 0) {
- throw new IOException("Length too large: " + Arrays.toString(lengthBytes));
- }
-
- byte[] contents = new byte[length];
- int bytesRead = inputStream.read(contents);
- if (bytesRead != length) {
- throw new IOException("Failed to read entire contents: " + bytesRead + " != " + length);
- }
-
- return contents;
- }
-
- /**
- * Writes a frame to the shell's stdin, which has the format:
- *
- * <pre>{@code
- * +---------------------+-----------------+
- * | 4-bytes | |length| bytes |
- * +---------------------+-----------------+
- * | (unsigned) length | contents |
- * +---------------------+-----------------+
- * }</pre>
- *
- * @param contents the contents to send.
- * @throws IOException
- */
- private void writeFrame(byte[] contents) throws IOException {
- if (shellProcess == null) {
- throw new IllegalStateException("Shell not started.");
- }
-
- // The length is big-endian encoded, network byte order.
- long length = contents.length;
- byte[] lengthBytes = new byte[4];
- lengthBytes[0] = (byte) (length >> 32 & 0xFF);
- lengthBytes[1] = (byte) (length >> 16 & 0xFF);
- lengthBytes[2] = (byte) (length >> 8 & 0xFF);
- lengthBytes[3] = (byte) (length >> 0 & 0xFF);
-
- OutputStream outputStream = shellProcess.getOutputStream();
- outputStream.write(lengthBytes);
- outputStream.write(contents);
- outputStream.flush();
- }
-
- /**
- * Creates an expression to be processed when a secure connection is established, after the
- * handshake is done.
- *
- * @param command The command to send.
- * @param argument The argument of the command. Can be null.
- * @return the expression that can be sent to the shell.
- * @throws IOException.
- */
- private byte[] createExpression(String command, @Nullable byte[] argument) throws IOException {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- outputStream.write(command.getBytes());
- outputStream.write(" ".getBytes());
- if (argument != null) {
- outputStream.write(argument);
- }
- return outputStream.toByteArray();
- }
-
- /** @return the mode string to use in the argument to start the ukey2_shell process. */
- private String getModeString() {
- switch (mode) {
- case INITIATOR:
- return "initiator";
- case RESPONDER:
- return "responder";
- default:
- throw new IllegalArgumentException("Uknown mode " + mode);
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/CryptoOpsTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/CryptoOpsTest.java
deleted file mode 100644
index 65fa094..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/CryptoOpsTest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-// 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.securemessage;
-
-import static org.junit.Assert.assertThrows;
-
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.EncType;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.SigType;
-import java.util.Arrays;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import junit.framework.TestCase;
-
-/**
- * Unit tests for the CryptoOps class
- */
-public class CryptoOpsTest extends TestCase {
-
- /** HKDF Test Case 1 IKM from RFC 5869 */
- private static final byte[] HKDF_CASE1_IKM = {
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
- 0x0b, 0x0b
- };
-
- /** HKDF Test Case 1 salt from RFC 5869 */
- private static final byte[] HKDF_CASE1_SALT = {
- 0x00, 0x01, 0x02, 0x03, 0x04,
- 0x05, 0x06, 0x07, 0x08, 0x09,
- 0x0a, 0x0b, 0x0c
- };
-
- /** HKDF Test Case 1 info from RFC 5869 */
- private static final byte[] HKDF_CASE1_INFO = {
- (byte) 0xf0, (byte) 0xf1, (byte) 0xf2, (byte) 0xf3, (byte) 0xf4,
- (byte) 0xf5, (byte) 0xf6, (byte) 0xf7, (byte) 0xf8, (byte) 0xf9
- };
-
- /** First 32 bytes of HKDF Test Case 1 OKM (output) from RFC 5869 */
- private static final byte[] HKDF_CASE1_OKM = {
- (byte) 0x3c, (byte) 0xb2, (byte) 0x5f, (byte) 0x25, (byte) 0xfa,
- (byte) 0xac, (byte) 0xd5, (byte) 0x7a, (byte) 0x90, (byte) 0x43,
- (byte) 0x4f, (byte) 0x64, (byte) 0xd0, (byte) 0x36, (byte) 0x2f,
- (byte) 0x2a, (byte) 0x2d, (byte) 0x2d, (byte) 0x0a, (byte) 0x90,
- (byte) 0xcf, (byte) 0x1a, (byte) 0x5a, (byte) 0x4c, (byte) 0x5d,
- (byte) 0xb0, (byte) 0x2d, (byte) 0x56, (byte) 0xec, (byte) 0xc4,
- (byte) 0xc5, (byte) 0xbf, (byte) 0x34, (byte) 0x00, (byte) 0x72,
- (byte) 0x08, (byte) 0xd5, (byte) 0xb8, (byte) 0x87, (byte) 0x18,
- (byte) 0x58, (byte) 0x65
- };
-
- private SecretKey aesKey1;
- private SecretKey aesKey2;
-
- @Override
- protected void setUp() throws Exception {
- KeyGenerator aesKeygen = KeyGenerator.getInstance("AES");
- aesKeygen.init(256);
- aesKey1 = aesKeygen.generateKey();
- aesKey2 = aesKeygen.generateKey();
- super.setUp();
- }
-
- public void testNoPurposeConflicts() {
- // Ensure that signature algorithms and encryption algorithms are not given identical purposes
- // (this prevents confusion of derived keys).
- for (SigType sigType : SigType.values()) {
- for (EncType encType : EncType.values()) {
- assertFalse(CryptoOps.getPurpose(sigType).equals(CryptoOps.getPurpose(encType)));
- }
- }
- }
-
- public void testDeriveAes256KeyFor() throws Exception {
- // Test that deriving with the same key and purpose twice is deterministic
- assertTrue(Arrays.equals(CryptoOps.deriveAes256KeyFor(aesKey1, "A").getEncoded(),
- CryptoOps.deriveAes256KeyFor(aesKey1, "A").getEncoded()));
- // Test that derived keys with different purposes differ
- assertFalse(Arrays.equals(CryptoOps.deriveAes256KeyFor(aesKey1, "A").getEncoded(),
- CryptoOps.deriveAes256KeyFor(aesKey1, "B").getEncoded()));
- // Test that derived keys with the same purpose but different master keys differ
- assertFalse(Arrays.equals(CryptoOps.deriveAes256KeyFor(aesKey1, "A").getEncoded(),
- CryptoOps.deriveAes256KeyFor(aesKey2, "A").getEncoded()));
- }
-
- public void testHkdf() throws Exception {
- SecretKey inputKey = new SecretKeySpec(HKDF_CASE1_IKM, "AES");
- byte[] result = CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO);
- byte[] expectedResult = Arrays.copyOf(HKDF_CASE1_OKM, 32);
- assertTrue(Arrays.equals(result, expectedResult));
- }
-
- public void testHkdfLongOutput() throws Exception {
- SecretKey inputKey = new SecretKeySpec(HKDF_CASE1_IKM, "AES");
- byte[] result = CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO, 42);
- byte[] expectedResult = Arrays.copyOf(HKDF_CASE1_OKM, 42);
- assertTrue(Arrays.equals(result, expectedResult));
- }
-
- public void testHkdfShortOutput() throws Exception {
- SecretKey inputKey = new SecretKeySpec(HKDF_CASE1_IKM, "AES");
- byte[] result = CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO, 12);
- byte[] expectedResult = Arrays.copyOf(HKDF_CASE1_OKM, 12);
- assertTrue(Arrays.equals(result, expectedResult));
- }
-
- public void testHkdfInvalidLengths() throws Exception {
- SecretKey inputKey = new SecretKeySpec(HKDF_CASE1_IKM, "AES");
-
- // Negative length
- assertThrows(
- IllegalArgumentException.class,
- () -> CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO, -5));
-
- // Too long, would be more than 256 blocks
- assertThrows(
- IllegalArgumentException.class,
- () -> CryptoOps.hkdf(inputKey, HKDF_CASE1_SALT, HKDF_CASE1_INFO, 32 * 256 + 1));
- }
-
- public void testConcat() {
- byte[] a = { 1, 2, 3, 4};
- byte[] b = { 5 , 6 };
- byte[] expectedResult = { 1, 2, 3, 4, 5, 6 };
- byte[] result = CryptoOps.concat(a, b);
- assertEquals(a.length + b.length, result.length);
- assertTrue(Arrays.equals(expectedResult, result));
-
- byte[] empty = { };
- assertEquals(0, CryptoOps.concat(empty, empty).length);
- assertTrue(Arrays.equals(a, CryptoOps.concat(a, empty)));
- assertTrue(Arrays.equals(a, CryptoOps.concat(empty, a)));
-
- assertEquals(0, CryptoOps.concat(null, null).length);
- assertTrue(Arrays.equals(a, CryptoOps.concat(a, null)));
- assertTrue(Arrays.equals(a, CryptoOps.concat(null, a)));
- }
-
- public void testSubarray() {
- byte[] in = { 1, 2, 3, 4, 5, 6, 7 };
- assertTrue(Arrays.equals(in, CryptoOps.subarray(in, 0, in.length)));
- assertEquals(0, CryptoOps.subarray(in, 0, 0).length);
- byte[] expectedResult1 = { 1 };
- assertTrue(Arrays.equals(expectedResult1, CryptoOps.subarray(in, 0, 1)));
- byte[] expectedResult34 = { 3, 4 };
- assertTrue(Arrays.equals(expectedResult34, CryptoOps.subarray(in, 2, 4)));
- assertThrows(IndexOutOfBoundsException.class, () -> CryptoOps.subarray(in, 0, in.length + 1));
- assertThrows(IndexOutOfBoundsException.class, () -> CryptoOps.subarray(in, -1, in.length));
- assertThrows(
- IndexOutOfBoundsException.class, () -> CryptoOps.subarray(in, in.length, in.length));
- assertThrows(
- IndexOutOfBoundsException.class,
- () -> CryptoOps.subarray(in, Integer.MIN_VALUE, in.length));
- assertThrows(
- IndexOutOfBoundsException.class, () -> CryptoOps.subarray(in, 1, Integer.MIN_VALUE));
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/NullsGoogle3Test.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/NullsGoogle3Test.java
deleted file mode 100644
index c28d2f9..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/NullsGoogle3Test.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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.securemessage;
-
-import com.google.common.testing.NullPointerTester;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SecureMessage;
-import junit.framework.TestCase;
-
-/**
- * Non-portable Google3-based test to check null pointer behavior.
- */
-public class NullsGoogle3Test extends TestCase {
-
- /**
- * We test all of the classes in one place to avoid a proliferation of similar test cases,
- * noting that {@link NullPointerTester} emits the name of the class where the breakge occurs.
- */
- public void testNulls() {
- final NullPointerTester tester = new NullPointerTester();
- tester.testAllPublicStaticMethods(CryptoOps.class);
- tester.testAllPublicStaticMethods(PublicKeyProtoUtil.class);
-
- tester.setDefault(SecureMessage.class, SecureMessage.getDefaultInstance());
- tester.testAllPublicStaticMethods(SecureMessageParser.class);
-
- tester.testAllPublicStaticMethods(SecureMessageBuilder.class);
- tester.testAllPublicConstructors(SecureMessageBuilder.class);
- tester.testAllPublicInstanceMethods(new SecureMessageBuilder());
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtilTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtilTest.java
deleted file mode 100644
index 8581622..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/PublicKeyProtoUtilTest.java
+++ /dev/null
@@ -1,412 +0,0 @@
-// 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.securemessage;
-
-import com.google.common.io.BaseEncoding;
-import com.google.protobuf.ByteString;
-import com.google.security.annotations.SuppressInsecureCipherModeCheckerNoReview;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.DhPublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.EcP256PublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SimpleRsaPublicKey;
-import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import javax.crypto.KeyAgreement;
-import javax.crypto.interfaces.DHPrivateKey;
-import javax.crypto.interfaces.DHPublicKey;
-import junit.framework.TestCase;
-
-/** Tests for the PublicKeyProtoUtil class. */
-public class PublicKeyProtoUtilTest extends TestCase {
-
- private static final byte[] ZERO_BYTE = {0};
- private PublicKey ecPublicKey;
- private PublicKey rsaPublicKey;
-
- /**
- * Diffie Hellman {@link PublicKey}s require special treatment, so we store them specifically as a
- * {@link DHPublicKey} to minimize casting.
- */
- private DHPublicKey dhPublicKey;
-
- @Override
- public void setUp() {
- if (!isAndroidOsWithoutEcSupport()) {
- ecPublicKey = PublicKeyProtoUtil.generateEcP256KeyPair().getPublic();
- }
- rsaPublicKey = PublicKeyProtoUtil.generateRSA2048KeyPair().getPublic();
- dhPublicKey = (DHPublicKey) PublicKeyProtoUtil.generateDh2048KeyPair().getPublic();
- }
-
- public void testPublicKeyProtoSpecificEncodeParse() throws Exception {
- if (!isAndroidOsWithoutEcSupport()) {
- assertEquals(
- ecPublicKey,
- PublicKeyProtoUtil.parseEcPublicKey(PublicKeyProtoUtil.encodeEcPublicKey(ecPublicKey)));
- }
-
- assertEquals(
- rsaPublicKey,
- PublicKeyProtoUtil.parseRsa2048PublicKey(
- PublicKeyProtoUtil.encodeRsa2048PublicKey(rsaPublicKey)));
-
- // DHPublicKey objects don't seem to properly implement equals(), so we have to test that
- // the individual y and p values match (it is safe to assume g = 2 is used if p is correct).
- DHPublicKey parsedDHPublicKey =
- PublicKeyProtoUtil.parseDh2048PublicKey(
- PublicKeyProtoUtil.encodeDh2048PublicKey(dhPublicKey));
- assertEquals(dhPublicKey.getY(), parsedDHPublicKey.getY());
- assertEquals(dhPublicKey.getParams().getP(), parsedDHPublicKey.getParams().getP());
- assertEquals(dhPublicKey.getParams().getG(), parsedDHPublicKey.getParams().getG());
- }
-
- public void testPublicKeyProtoGenericEncodeParse() throws Exception {
- if (!isAndroidOsWithoutEcSupport()) {
- assertEquals(
- ecPublicKey,
- PublicKeyProtoUtil.parsePublicKey(
- PublicKeyProtoUtil.encodePaddedEcPublicKey(ecPublicKey)));
- assertEquals(
- ecPublicKey,
- PublicKeyProtoUtil.parsePublicKey(PublicKeyProtoUtil.encodePublicKey(ecPublicKey)));
- }
-
- assertEquals(
- rsaPublicKey,
- PublicKeyProtoUtil.parsePublicKey(PublicKeyProtoUtil.encodePublicKey(rsaPublicKey)));
-
- // See above explanation for why we treat DHPublicKey objects differently.
- DHPublicKey parsedDHPublicKey =
- PublicKeyProtoUtil.parseDh2048PublicKey(
- PublicKeyProtoUtil.encodeDh2048PublicKey(dhPublicKey));
- assertEquals(dhPublicKey.getY(), parsedDHPublicKey.getY());
- assertEquals(dhPublicKey.getParams().getP(), parsedDHPublicKey.getParams().getP());
- assertEquals(dhPublicKey.getParams().getG(), parsedDHPublicKey.getParams().getG());
- }
-
- public void testPaddedECPublicKeyEncodeHasPaddedNullByte() throws Exception {
- if (isAndroidOsWithoutEcSupport()) {
- return;
- }
-
- // Key where the x coordinate is 33 bytes, y coordinate is 32 bytes
- ECPublicKey maxXByteLengthKey =
- buildEcPublicKey(
- BaseEncoding.base64().decode("AM730WQL7ZAmvyAJX4euNdr3+nAIueGlYYGXE6p732h6"),
- BaseEncoding.base64().decode("JEnmaDpKn0fH4/0kKGb97qUSwI2uT+ta0GLe3V7REfk="));
- // Key where both coordinates are 33 bytes
- ECPublicKey maxByteLengthKey =
- buildEcPublicKey(
- BaseEncoding.base64().decode("AOg9TQCxFfVdXv7lO/6UVDyiPsu8XDkEWQIPUfqX6UHP"),
- BaseEncoding.base64().decode("AP/RW8uVyu6QImpbza51CqG1mtBTh5c9pjv9CUwOuB7E"));
- // Key where both coordinates are 32 bytes
- ECPublicKey notMaxByteLengthKey =
- buildEcPublicKey(
- BaseEncoding.base64().decode("M35bxV8HKr0e8v7f4zuXgw6TYFawvikFdI71u9S1ONI="),
- BaseEncoding.base64().decode("OXR+xCpD8AR0VR8TeBXA00eIr3rWE6sV6KrOM6MoWsc="));
- GenericPublicKey encodedMaxXByteLengthKey =
- PublicKeyProtoUtil.encodePublicKey(maxXByteLengthKey);
- GenericPublicKey paddedEncodedMaxXByteLengthKey =
- PublicKeyProtoUtil.encodePaddedEcPublicKey(maxXByteLengthKey);
- GenericPublicKey encodedMaxByteLengthKey = PublicKeyProtoUtil.encodePublicKey(maxByteLengthKey);
- GenericPublicKey paddedEncodedMaxByteLengthKey =
- PublicKeyProtoUtil.encodePaddedEcPublicKey(maxByteLengthKey);
- GenericPublicKey encodedNotMaxByteLengthKey =
- PublicKeyProtoUtil.encodePublicKey(notMaxByteLengthKey);
- GenericPublicKey paddedEncodedNotMaxByteLengthKey =
- PublicKeyProtoUtil.encodePaddedEcPublicKey(notMaxByteLengthKey);
-
- assertEquals(maxXByteLengthKey, PublicKeyProtoUtil.parsePublicKey(encodedMaxXByteLengthKey));
- assertEquals(
- maxXByteLengthKey, PublicKeyProtoUtil.parsePublicKey(paddedEncodedMaxXByteLengthKey));
- assertEquals(maxByteLengthKey, PublicKeyProtoUtil.parsePublicKey(encodedMaxByteLengthKey));
- assertEquals(
- maxByteLengthKey, PublicKeyProtoUtil.parsePublicKey(paddedEncodedMaxByteLengthKey));
- assertEquals(
- notMaxByteLengthKey, PublicKeyProtoUtil.parsePublicKey(paddedEncodedNotMaxByteLengthKey));
- assertEquals(
- notMaxByteLengthKey, PublicKeyProtoUtil.parsePublicKey(encodedNotMaxByteLengthKey));
-
- assertEquals(33, paddedEncodedMaxXByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(33, paddedEncodedMaxXByteLengthKey.getEcP256PublicKey().getY().size());
- assertEquals(0, paddedEncodedMaxXByteLengthKey.getEcP256PublicKey().getX().byteAt(0));
- assertEquals(0, paddedEncodedMaxXByteLengthKey.getEcP256PublicKey().getY().byteAt(0));
- assertEquals(33, encodedMaxXByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(32, encodedMaxXByteLengthKey.getEcP256PublicKey().getY().size());
-
- assertEquals(33, paddedEncodedMaxByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(33, paddedEncodedMaxByteLengthKey.getEcP256PublicKey().getY().size());
- assertEquals(0, paddedEncodedMaxByteLengthKey.getEcP256PublicKey().getX().byteAt(0));
- assertEquals(0, paddedEncodedMaxByteLengthKey.getEcP256PublicKey().getY().byteAt(0));
- assertEquals(33, encodedMaxByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(33, encodedMaxByteLengthKey.getEcP256PublicKey().getY().size());
-
- assertEquals(32, encodedNotMaxByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(32, encodedNotMaxByteLengthKey.getEcP256PublicKey().getY().size());
- assertEquals(0, paddedEncodedNotMaxByteLengthKey.getEcP256PublicKey().getX().byteAt(0));
- assertEquals(0, paddedEncodedNotMaxByteLengthKey.getEcP256PublicKey().getY().byteAt(0));
- assertEquals(33, paddedEncodedNotMaxByteLengthKey.getEcP256PublicKey().getX().size());
- assertEquals(33, paddedEncodedNotMaxByteLengthKey.getEcP256PublicKey().getY().size());
- }
-
- @SuppressInsecureCipherModeCheckerNoReview
- public void testWrongPublicKeyType() throws Exception {
- KeyPairGenerator dsaGen = KeyPairGenerator.getInstance("DSA");
- dsaGen.initialize(512);
- PublicKey pk = dsaGen.generateKeyPair().getPublic();
-
- if (!isAndroidOsWithoutEcSupport()) {
- // Try to encode it as EC
- try {
- PublicKeyProtoUtil.encodeEcPublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- try {
- PublicKeyProtoUtil.encodePaddedEcPublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- }
-
- // Try to encode it as RSA
- try {
- PublicKeyProtoUtil.encodeRsa2048PublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- // Try to encode it as DH
- try {
- PublicKeyProtoUtil.encodeDh2048PublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
-
- // Try to encode it as Generic
- try {
- PublicKeyProtoUtil.encodePublicKey(pk);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- }
-
- public void testEcPublicKeyProtoInvalidEncoding() throws Exception {
- if (isAndroidOsWithoutEcSupport()) {
- return;
- }
-
- EcP256PublicKey validProto = PublicKeyProtoUtil.encodeEcPublicKey(ecPublicKey);
- EcP256PublicKey.Builder invalidProto = EcP256PublicKey.newBuilder(validProto);
-
- // Mess up the X coordinate by repeating it twice
- byte[] newX =
- CryptoOps.concat(validProto.getX().toByteArray(), validProto.getX().toByteArray());
- checkParsingFailsFor(invalidProto.setX(ByteString.copyFrom(newX)).build());
-
- // Mess up the Y coordinate by erasing it
- invalidProto = EcP256PublicKey.newBuilder(validProto);
- checkParsingFailsFor(invalidProto.setY(ByteString.EMPTY).build());
-
- // Pick a point that is likely not on the curve by copying X over Y
- invalidProto = EcP256PublicKey.newBuilder(validProto);
- checkParsingFailsFor(invalidProto.setY(validProto.getX()).build());
-
- // Try the point (0, 0)
- invalidProto = EcP256PublicKey.newBuilder(validProto);
- checkParsingFailsFor(
- invalidProto
- .setX(ByteString.copyFrom(ZERO_BYTE))
- .setY(ByteString.copyFrom(ZERO_BYTE))
- .build());
- }
-
- private void checkParsingFailsFor(EcP256PublicKey invalid) {
- try {
- // Should fail to decode
- PublicKeyProtoUtil.parseEcPublicKey(invalid);
- fail();
- } catch (InvalidKeySpecException expected) {
- }
- }
-
- public void testSimpleRsaPublicKeyProtoInvalidEncoding() throws Exception {
- SimpleRsaPublicKey validProto = PublicKeyProtoUtil.encodeRsa2048PublicKey(rsaPublicKey);
- SimpleRsaPublicKey.Builder invalidProto;
-
- // Double the number of bits in the modulus
- invalidProto = SimpleRsaPublicKey.newBuilder(validProto);
- byte[] newN =
- CryptoOps.concat(validProto.getN().toByteArray(), validProto.getN().toByteArray());
- checkParsingFailsFor(invalidProto.setN(ByteString.copyFrom(newN)).build());
-
- // Set the modulus to 0
- invalidProto = SimpleRsaPublicKey.newBuilder(validProto);
- checkParsingFailsFor(invalidProto.setN(ByteString.copyFrom(ZERO_BYTE)).build());
-
- // Set the modulus to 65537 (way too small)
- invalidProto = SimpleRsaPublicKey.newBuilder(validProto);
- checkParsingFailsFor(
- invalidProto.setN(ByteString.copyFrom(BigInteger.valueOf(65537).toByteArray())).build());
- }
-
- private static void checkParsingFailsFor(SimpleRsaPublicKey invalid) {
- try {
- // Should fail to decode
- PublicKeyProtoUtil.parseRsa2048PublicKey(invalid);
- fail();
- } catch (InvalidKeySpecException expected) {
- }
- }
-
- public void testSimpleDhPublicKeyProtoInvalidEncoding() throws Exception {
- DhPublicKey validProto = PublicKeyProtoUtil.encodeDh2048PublicKey(dhPublicKey);
- DhPublicKey.Builder invalidProto;
-
- // Double the number of bits in the public element encoding
- invalidProto = DhPublicKey.newBuilder(validProto);
- byte[] newY =
- CryptoOps.concat(validProto.getY().toByteArray(), validProto.getY().toByteArray());
- checkParsingFailsFor(invalidProto.setY(ByteString.copyFrom(newY)).build());
-
- // Set the public element to 0
- invalidProto = DhPublicKey.newBuilder(validProto);
- checkParsingFailsFor(invalidProto.setY(ByteString.copyFrom(ZERO_BYTE)).build());
- }
-
- private static void checkParsingFailsFor(DhPublicKey invalid) {
- try {
- // Should fail to decode
- PublicKeyProtoUtil.parseDh2048PublicKey(invalid);
- fail();
- } catch (InvalidKeySpecException expected) {
- }
- }
-
- public void testDhKeyAgreementWorks() throws Exception {
- int minExpectedSecretLength = (PublicKeyProtoUtil.DH_P.bitLength() / 8) - 4;
-
- KeyPair clientKeyPair = PublicKeyProtoUtil.generateDh2048KeyPair();
- KeyPair serverKeyPair = PublicKeyProtoUtil.generateDh2048KeyPair();
- BigInteger clientY = ((DHPublicKey) clientKeyPair.getPublic()).getY();
- BigInteger serverY = ((DHPublicKey) serverKeyPair.getPublic()).getY();
- assertFalse(clientY.equals(serverY)); // DHPublicKeys should not be equal
-
- // Run client side of the key exchange
- byte[] clientSecret = doDhAgreement(clientKeyPair.getPrivate(), serverKeyPair.getPublic());
- assert (clientSecret.length >= minExpectedSecretLength);
-
- // Run the server side of the key exchange
- byte[] serverSecret = doDhAgreement(serverKeyPair.getPrivate(), clientKeyPair.getPublic());
- assert (serverSecret.length >= minExpectedSecretLength);
-
- assertTrue(Arrays.equals(clientSecret, serverSecret));
- }
-
- public void testDh2048PrivateKeyEncoding() throws Exception {
- KeyPair testPair = PublicKeyProtoUtil.generateDh2048KeyPair();
- DHPrivateKey sk = (DHPrivateKey) testPair.getPrivate();
- DHPrivateKey skParsed =
- PublicKeyProtoUtil.parseDh2048PrivateKey(PublicKeyProtoUtil.encodeDh2048PrivateKey(sk));
- assertEquals(sk.getX(), skParsed.getX());
- assertEquals(sk.getParams().getP(), skParsed.getParams().getP());
- assertEquals(sk.getParams().getG(), skParsed.getParams().getG());
- }
-
- public void testParseEcPublicKeyOnLegacyPlatform() {
- if (!PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- return; // This test only runs on legacy platforms
- }
- byte[] pointBytes = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
- 1, 2
- };
-
- try {
- PublicKeyProtoUtil.parseEcPublicKey(
- EcP256PublicKey.newBuilder()
- .setX(ByteString.copyFrom(pointBytes))
- .setY(ByteString.copyFrom(pointBytes))
- .build());
- fail();
- } catch (InvalidKeySpecException expected) {
- // Should get this specific exception when EC doesn't work
- }
- }
-
- public void testIsLegacyCryptoRequired() {
- assertEquals(isAndroidOsWithoutEcSupport(), PublicKeyProtoUtil.isLegacyCryptoRequired());
- }
-
- /** @return true if running on an Android OS that doesn't support Elliptic Curve algorithms */
- public static boolean isAndroidOsWithoutEcSupport() {
- try {
- Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("android.os.Build$VERSION");
- int sdkVersion = clazz.getField("SDK_INT").getInt(null);
- if (sdkVersion < PublicKeyProtoUtil.ANDROID_HONEYCOMB_SDK_INT) {
- return true;
- }
- } catch (ClassNotFoundException e) {
- // Not running on Android
- return false;
- } catch (SecurityException e) {
- throw new AssertionError(e);
- } catch (NoSuchFieldException e) {
- throw new AssertionError(e);
- } catch (IllegalArgumentException e) {
- throw new AssertionError(e);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- }
- return false;
- }
-
- @SuppressInsecureCipherModeCheckerNoReview
- private static byte[] doDhAgreement(PrivateKey secretKey, PublicKey peerKey) throws Exception {
- KeyAgreement agreement = KeyAgreement.getInstance("DH");
- agreement.init(secretKey);
- agreement.doPhase(peerKey, true);
- return agreement.generateSecret();
- }
-
- private static ECPublicKey buildEcPublicKey(byte[] encodedX, byte[] encodedY) throws Exception {
- try {
- BigInteger wX = new BigInteger(encodedX);
- BigInteger wY = new BigInteger(encodedY);
- return (ECPublicKey)
- KeyFactory.getInstance("EC")
- .generatePublic(
- new ECPublicKeySpec(
- new ECPoint(wX, wY),
- ((ECPublicKey) PublicKeyProtoUtil.generateEcP256KeyPair().getPublic())
- .getParams()));
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java
deleted file mode 100644
index 285b259..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java
+++ /dev/null
@@ -1,403 +0,0 @@
-// 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.securemessage;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.EncType;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.SigType;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.Header;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.HeaderAndBody;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SecureMessage;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.Arrays;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import junit.framework.TestCase;
-
-/**
- * Tests the library against some very basic test vectors, to help ensure wire-format
- * compatibility is not broken.
- */
-public class SecureMessageSimpleTestVectorTest extends TestCase {
-
- private static final KeyFactory EC_KEY_FACTORY;
- static {
- try {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- EC_KEY_FACTORY = null;
- } else {
- EC_KEY_FACTORY = KeyFactory.getInstance("EC");
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private static final byte[] TEST_ASSOCIATED_DATA = {
- 11, 22, 33, 44, 55
- };
- private static final byte[] TEST_METADATA = {
- 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28
- };
- private static final byte[] TEST_VKID = {
- 0, 0, 1
- };
- private static final byte[] TEST_DKID = {
- -1, -1, 0,
- };
- private static final byte[] TEST_MESSAGE = {
- 0, 99, 1, 98, 2, 97, 3, 96, 4, 95, 5, 94, 6, 93, 7, 92, 8, 91, 9, 90
- };
-
- // The following fields are initialized below, in a static block that contains auto-generated test
- // vectors. Initialization can't just be done inline due to code that throws checked exceptions.
- private static final PublicKey TEST_EC_PUBLIC_KEY;
- private static final PrivateKey TEST_EC_PRIVATE_KEY;
- private static final SecretKey TEST_KEY1;
- private static final SecretKey TEST_KEY2;
- private static final byte[] TEST_VECTOR_ECDSA_ONLY;
- private static final byte[] TEST_VECTOR_ECDSA_AND_AES;
- private static final byte[] TEST_VECTOR_HMAC_AND_AES_SAME_KEYS;
- private static final byte[] TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS;
-
- public void testEcdsaOnly() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // On older Android platforms we can't run this test.
- return;
- }
- SecureMessage testVector = SecureMessage.parseFrom(TEST_VECTOR_ECDSA_ONLY);
- Header unverifiedHeader = SecureMessageParser.getUnverifiedHeader(testVector);
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignedCleartextMessage(
- testVector, TEST_EC_PUBLIC_KEY, SigType.ECDSA_P256_SHA256, TEST_ASSOCIATED_DATA);
- assertTrue(Arrays.equals(
- unverifiedHeader.toByteArray(),
- headerAndBody.getHeader().toByteArray()));
- assertTrue(Arrays.equals(TEST_MESSAGE, headerAndBody.getBody().toByteArray()));
- assertEquals(TEST_ASSOCIATED_DATA.length, unverifiedHeader.getAssociatedDataLength());
- assertTrue(Arrays.equals(TEST_METADATA, unverifiedHeader.getPublicMetadata().toByteArray()));
- assertTrue(Arrays.equals(TEST_VKID, unverifiedHeader.getVerificationKeyId().toByteArray()));
- assertFalse(unverifiedHeader.hasDecryptionKeyId());
- }
-
- public void testEcdsaAndAes() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // On older Android platforms we can't run this test.
- return;
- }
- SecureMessage testVector = SecureMessage.parseFrom(TEST_VECTOR_ECDSA_AND_AES);
- Header unverifiedHeader = SecureMessageParser.getUnverifiedHeader(testVector);
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignCryptedMessage(
- testVector,
- TEST_EC_PUBLIC_KEY,
- SigType.ECDSA_P256_SHA256,
- TEST_KEY1,
- EncType.AES_256_CBC,
- TEST_ASSOCIATED_DATA);
- assertTrue(Arrays.equals(
- unverifiedHeader.toByteArray(),
- headerAndBody.getHeader().toByteArray()));
- assertTrue(Arrays.equals(TEST_MESSAGE, headerAndBody.getBody().toByteArray()));
- assertEquals(TEST_ASSOCIATED_DATA.length, unverifiedHeader.getAssociatedDataLength());
- assertTrue(Arrays.equals(TEST_METADATA, unverifiedHeader.getPublicMetadata().toByteArray()));
- assertTrue(Arrays.equals(TEST_VKID, unverifiedHeader.getVerificationKeyId().toByteArray()));
- assertTrue(Arrays.equals(TEST_DKID, unverifiedHeader.getDecryptionKeyId().toByteArray()));
- }
-
- public void testHmacAndAesSameKeys() throws Exception {
- SecureMessage testVector = SecureMessage.parseFrom(TEST_VECTOR_HMAC_AND_AES_SAME_KEYS);
- Header unverifiedHeader = SecureMessageParser.getUnverifiedHeader(testVector);
-
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignCryptedMessage(
- testVector,
- TEST_KEY1,
- SigType.HMAC_SHA256,
- TEST_KEY1,
- EncType.AES_256_CBC,
- TEST_ASSOCIATED_DATA);
- assertTrue(Arrays.equals(
- unverifiedHeader.toByteArray(),
- headerAndBody.getHeader().toByteArray()));
- assertTrue(Arrays.equals(TEST_MESSAGE, headerAndBody.getBody().toByteArray()));
- assertEquals(TEST_ASSOCIATED_DATA.length, unverifiedHeader.getAssociatedDataLength());
- assertTrue(Arrays.equals(TEST_METADATA, unverifiedHeader.getPublicMetadata().toByteArray()));
- assertTrue(Arrays.equals(TEST_VKID, unverifiedHeader.getVerificationKeyId().toByteArray()));
- assertTrue(Arrays.equals(TEST_DKID, unverifiedHeader.getDecryptionKeyId().toByteArray()));
- }
-
- public void testHmacAndAesDifferentKeys() throws Exception {
- SecureMessage testVector = SecureMessage.parseFrom(TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS);
- Header unverifiedHeader = SecureMessageParser.getUnverifiedHeader(testVector);
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignCryptedMessage(
- testVector,
- TEST_KEY1,
- SigType.HMAC_SHA256,
- TEST_KEY2,
- EncType.AES_256_CBC,
- TEST_ASSOCIATED_DATA);
- assertTrue(Arrays.equals(
- unverifiedHeader.toByteArray(),
- headerAndBody.getHeader().toByteArray()));
- assertTrue(Arrays.equals(TEST_MESSAGE, headerAndBody.getBody().toByteArray()));
- assertEquals(TEST_ASSOCIATED_DATA.length, unverifiedHeader.getAssociatedDataLength());
- assertTrue(Arrays.equals(TEST_METADATA, unverifiedHeader.getPublicMetadata().toByteArray()));
- assertTrue(Arrays.equals(TEST_VKID, unverifiedHeader.getVerificationKeyId().toByteArray()));
- assertTrue(Arrays.equals(TEST_DKID, unverifiedHeader.getDecryptionKeyId().toByteArray()));
- }
-
- /**
- * This code emits the test vectors to {@code System.out}. It will not generate fresh test
- * vectors unless an existing test vector is set to {@code null}, but it contains all of the code
- * used to the generate the test vector values. Ideally, existing test vectors should never be
- * regenerated, but having this code available should make it easier to add new test vectors.
- */
- public void testGenerateTestVectorsPseudoTest() throws Exception {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- // On older Android platforms we can't run this test.
- return;
- }
- System.out.printf(" static {\n try {\n");
- String indent = " ";
- PublicKey testEcPublicKey = TEST_EC_PUBLIC_KEY;
- PrivateKey testEcPrivateKey = TEST_EC_PRIVATE_KEY;
- if (testEcPublicKey == null) {
- KeyPair testEcKeyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- testEcPublicKey = testEcKeyPair.getPublic();
- testEcPrivateKey = testEcKeyPair.getPrivate();
- }
- System.out.printf("%s%s = parsePublicKey(new byte[] %s);\n",
- indent,
- "TEST_EC_PUBLIC_KEY",
- byteArrayToJavaCode(indent, encodePublicKey(testEcPublicKey)));
- System.out.printf("%s%s = parseEcPrivateKey(new byte[] %s);\n",
- indent,
- "TEST_EC_PRIVATE_KEY",
- byteArrayToJavaCode(indent, encodeEcPrivateKey(testEcPrivateKey)));
-
- SecretKey testKey1 = TEST_KEY1;
- if (testKey1 == null) {
- testKey1 = makeAesKey();
- }
- System.out.printf("%s%s = new SecretKeySpec(new byte[] %s, \"AES\");\n",
- indent,
- "TEST_KEY1",
- byteArrayToJavaCode(indent, testKey1.getEncoded()));
-
- SecretKey testKey2 = TEST_KEY2;
- if (testKey2 == null) {
- testKey2 = makeAesKey();
- }
- System.out.printf("%s%s = new SecretKeySpec(new byte[] %s, \"AES\");\n",
- indent,
- "TEST_KEY2",
- byteArrayToJavaCode(indent, testKey2.getEncoded()));
-
- byte[] testVectorEcdsaOnly = TEST_VECTOR_ECDSA_ONLY;
- if (testVectorEcdsaOnly == null) {
- testVectorEcdsaOnly = new SecureMessageBuilder()
- .setAssociatedData(TEST_ASSOCIATED_DATA)
- .setPublicMetadata(TEST_METADATA)
- .setVerificationKeyId(TEST_VKID)
- .buildSignedCleartextMessage(
- testEcPrivateKey, SigType.ECDSA_P256_SHA256, TEST_MESSAGE).toByteArray();
- }
- printInitializerFor(indent, "TEST_VECTOR_ECDSA_ONLY", testVectorEcdsaOnly);
-
- byte[] testVectorEcdsaAndAes = TEST_VECTOR_ECDSA_AND_AES;
- if (testVectorEcdsaAndAes == null) {
- testVectorEcdsaAndAes = new SecureMessageBuilder()
- .setAssociatedData(TEST_ASSOCIATED_DATA)
- .setDecryptionKeyId(TEST_DKID)
- .setPublicMetadata(TEST_METADATA)
- .setVerificationKeyId(TEST_VKID)
- .buildSignCryptedMessage(
- testEcPrivateKey,
- SigType.ECDSA_P256_SHA256,
- testKey1,
- EncType.AES_256_CBC,
- TEST_MESSAGE).toByteArray();
- }
- printInitializerFor(indent, "TEST_VECTOR_ECDSA_AND_AES", testVectorEcdsaAndAes);
-
- byte[] testVectorHmacAndAesSameKeys = TEST_VECTOR_HMAC_AND_AES_SAME_KEYS;
- if (testVectorHmacAndAesSameKeys == null) {
- testVectorHmacAndAesSameKeys = new SecureMessageBuilder()
- .setAssociatedData(TEST_ASSOCIATED_DATA)
- .setDecryptionKeyId(TEST_DKID)
- .setPublicMetadata(TEST_METADATA)
- .setVerificationKeyId(TEST_VKID)
- .buildSignCryptedMessage(
- testKey1,
- SigType.HMAC_SHA256,
- testKey1,
- EncType.AES_256_CBC,
- TEST_MESSAGE).toByteArray();
- }
- printInitializerFor(indent, "TEST_VECTOR_HMAC_AND_AES_SAME_KEYS", testVectorHmacAndAesSameKeys);
-
- byte[] testVectorHmacAndAesDifferentKeys = TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS;
- if (testVectorHmacAndAesDifferentKeys == null) {
- testVectorHmacAndAesDifferentKeys = new SecureMessageBuilder()
- .setAssociatedData(TEST_ASSOCIATED_DATA)
- .setDecryptionKeyId(TEST_DKID)
- .setPublicMetadata(TEST_METADATA)
- .setVerificationKeyId(TEST_VKID)
- .buildSignCryptedMessage(
- testKey1,
- SigType.HMAC_SHA256,
- testKey2,
- EncType.AES_256_CBC,
- TEST_MESSAGE).toByteArray();
- }
- printInitializerFor(
- indent, "TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS", testVectorHmacAndAesDifferentKeys);
-
- System.out.printf(
- " } catch (Exception e) {\n throw new RuntimeException(e);\n }\n }\n");
- }
-
- private SecretKey makeAesKey() throws NoSuchAlgorithmException {
- KeyGenerator aesKeygen = KeyGenerator.getInstance("AES");
- aesKeygen.init(256);
- return aesKeygen.generateKey();
- }
-
- private void printInitializerFor(String indent, String name, byte[] value) {
- System.out.printf("%s%s = new byte[] %s;\n",
- indent,
- name,
- byteArrayToJavaCode(indent, value));
- }
-
- private static String byteArrayToJavaCode(String lineIndent, byte[] array) {
- String newline = "\n" + lineIndent + " ";
- String unwrappedArray = Arrays.toString(array).replace("[", "").replace("]", "");
- int wrapAfter = 16;
- int count = wrapAfter;
- StringBuilder result = new StringBuilder("{");
- for (String entry : unwrappedArray.split(" ")) {
- if (++count > wrapAfter) {
- result.append(newline);
- count = 0;
- } else {
- result.append(" ");
- }
- result.append(entry);
- }
- result.append(" }");
- return result.toString();
- }
-
- private static byte[] encodePublicKey(PublicKey pk) {
- return PublicKeyProtoUtil.encodePublicKey(pk).toByteArray();
- }
-
- private static PublicKey parsePublicKey(byte[] encodedPk)
- throws InvalidKeySpecException, InvalidProtocolBufferException {
- GenericPublicKey gpk = GenericPublicKey.parseFrom(encodedPk);
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()
- && gpk.getType() == SecureMessageProto.PublicKeyType.EC_P256) {
- return null;
- }
- return PublicKeyProtoUtil.parsePublicKey(gpk);
- }
-
- private static byte[] encodeEcPrivateKey(PrivateKey sk) {
- return sk.getEncoded();
- }
-
- private static PrivateKey parseEcPrivateKey(byte[] sk) throws InvalidKeySpecException {
- if (PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- return null;
- }
- return EC_KEY_FACTORY.generatePrivate(new PKCS8EncodedKeySpec(sk));
- }
-
- // The following block of code was automatically generated by cut and pasting the output of the
- // generateTestVectorsPseudoTest, which should reliably emit this same test vectors. Please
- // DO NOT DELETE any of these existing test vectors unless you _really_ know what you are doing.
- //
- // --- AUTO GENERATED CODE BEGINS HERE ---
- static {
- try {
- TEST_EC_PUBLIC_KEY = parsePublicKey(new byte[] {
- 8, 1, 18, 70, 10, 33, 0, -109, 9, 5, 8, -89, -3, -68, -86, -19, 17,
- -126, -11, -95, 35, 101, 102, -57, -84, -118, 73, 83, 66, -62, -49, -91, 71, -19,
- 52, 123, 113, 119, 45, 18, 33, 0, -65, -19, 83, -66, -12, 62, 102, -67, 116,
- 64, 42, 55, -84, -101, 90, -106, 113, -89, -30, 57, -112, 96, -99, -126, 14, 83,
- 41, 95, -24, -114, 23, -5 });
- TEST_EC_PRIVATE_KEY = parseEcPrivateKey(new byte[] {
- 48, 65, 2, 1, 0, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6,
- 8, 42, -122, 72, -50, 61, 3, 1, 7, 4, 39, 48, 37, 2, 1, 1, 4,
- 32, 26, -82, -61, -86, -59, -8, 2, -62, -17, -20, 122, 3, 85, -102, -76, 81,
- 51, 39, -9, 12, 99, -117, 127, 19, 121, 109, -31, -49, 110, 121, 76, -107 });
- TEST_KEY1 = new SecretKeySpec(new byte[] {
- -89, 105, 62, -41, -75, 78, 70, 110, -62, -58, -80, -81, -99, -62, 39, 38, 37,
- -7, -112, -83, 81, 23, 125, -72, -100, 103, -34, -23, -68, 21, -46, -104 }, "AES");
- TEST_KEY2 = new SecretKeySpec(new byte[] {
- -6, 48, 107, 61, -99, -89, 111, 33, 70, 54, -13, 111, 81, -120, 50, 89, -119,
- -113, -114, 63, 12, -68, 40, 42, -77, -58, -49, 18, 69, 91, -20, -65 }, "AES");
- TEST_VECTOR_ECDSA_ONLY = new byte[] {
- 10, 56, 10, 32, 8, 2, 16, 1, 26, 3, 0, 0, 1, 50, 19, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
- 56, 5, 18, 20, 0, 99, 1, 98, 2, 97, 3, 96, 4, 95, 5, 94, 6,
- 93, 7, 92, 8, 91, 9, 90, 18, 72, 48, 70, 2, 33, 0, -79, 59, 50,
- 21, 54, 61, -92, 77, -34, -77, -45, -105, 107, -28, -19, 91, -78, 120, 68, 33,
- 11, -76, -1, 50, 64, -127, -78, 6, 108, 115, -13, 126, 2, 33, 0, -72, -44,
- 52, 93, 105, 109, -127, -111, 11, 33, -111, 97, -114, 9, 117, -68, -45, 64, 63,
- 43, 60, -44, -89, -107, -59, -45, 56, 100, -66, -40, 46, -60 };
- TEST_VECTOR_ECDSA_AND_AES = new byte[] {
- 10, 107, 10, 55, 8, 2, 16, 2, 26, 3, 0, 0, 1, 34, 3, -1, -1,
- 0, 42, 16, -86, 16, 55, -8, -85, -47, -77, -36, -127, 44, -10, -44, -63, 115,
- -111, 26, 50, 19, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 27, 28, 56, 5, 18, 48, -110, 23, -67, 122, -118, 96, -4,
- 32, -113, -104, -107, -16, 76, 37, -61, -67, -63, 90, 38, 96, -47, -105, 56, -34,
- 50, -30, 82, 25, 100, 36, 69, 50, 68, 60, 38, 96, -108, -49, -73, -10, -62,
- -76, -45, -105, -86, 93, 28, 34, 18, 70, 48, 68, 2, 33, 0, -87, -103, 11,
- -70, 34, 33, -41, 90, -83, -74, 19, -13, 127, -43, -116, -32, 88, -13, 125, -122,
- 56, -21, 79, 47, 101, 89, -80, -43, 102, 92, 4, -15, 2, 31, 109, -69, 35,
- 21, 44, -27, -77, 32, 17, -90, -68, 113, 55, -24, -122, 40, 81, 51, 0, -84,
- -29, -12, -26, 73, 105, -32, 116, -28, 84, -116, -117 };
- TEST_VECTOR_HMAC_AND_AES_SAME_KEYS = new byte[] {
- 10, 91, 10, 55, 8, 1, 16, 2, 26, 3, 0, 0, 1, 34, 3, -1, -1,
- 0, 42, 16, -110, 48, 67, 67, -31, 24, -42, 13, -44, -109, 6, 113, 34, -70,
- 121, 6, 50, 19, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 27, 28, 56, 5, 18, 32, -44, -102, -16, 123, 113, -75, 88,
- -33, 118, 25, 60, -65, 109, 26, -70, -123, 58, -114, 126, 8, 106, -28, 65, -38,
- -4, 68, -78, -91, 49, -13, 22, -122, 18, 32, 20, -120, -113, -76, 85, -35, -53,
- 37, -18, 66, -38, 32, 10, 30, 89, 112, -39, -27, 24, 93, -36, -100, -127, -79,
- 94, -7, -19, -41, -47, -29, 1, 12 };
- TEST_VECTOR_HMAC_AND_AES_DIFFERENT_KEYS = new byte[] {
- 10, 107, 10, 55, 8, 1, 16, 2, 26, 3, 0, 0, 1, 34, 3, -1, -1,
- 0, 42, 16, -96, -7, 39, 79, -37, 40, 1, -30, 97, 0, 123, -7, -124, -75,
- -127, -18, 50, 19, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 27, 28, 56, 5, 18, 48, 90, 40, -48, -113, 84, -32, 47,
- 98, 54, -128, 127, 115, 32, 87, -86, 4, -26, 99, 9, -88, 13, 77, 127, 114,
- -48, -117, -94, 96, -86, -105, -123, 11, 116, -69, -83, -110, 3, -10, 0, -34, 72,
- 10, -58, 3, -119, -94, 23, -114, 18, 32, -25, -126, 95, 125, -110, -62, -36, -78,
- 97, 72, -54, -114, 97, -68, -46, 107, 53, 55, -57, 88, 127, -20, -23, 80, -9,
- -91, 115, 42, 24, 49, -76, -111 };
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-}
diff --git a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java
deleted file mode 100644
index 40e5091..0000000
--- a/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java
+++ /dev/null
@@ -1,766 +0,0 @@
-// 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.securemessage;
-
-import com.google.protobuf.ByteString;
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.UninitializedMessageException;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.EncType;
-import com.google.security.cryptauth.lib.securemessage.CryptoOps.SigType;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.EcP256PublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.Header;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.HeaderAndBody;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SecureMessage;
-import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.SimpleRsaPublicKey;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.SignatureException;
-import java.security.spec.InvalidKeySpecException;
-import java.util.Arrays;
-import java.util.List;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import junit.framework.TestCase;
-
-/**
- * Tests for the SecureMessageBuilder and SecureMessageParser classes.
- */
-public class SecureMessageTest extends TestCase {
- // Not to be used when generating cross-platform test vectors (due to default charset encoding)
- public static final byte[] TEST_MESSAGE =
- "Testing 1 2 3... Testing 1 2 3... Testing 1 2 3...".getBytes();
-
- private static final byte[] TEST_KEY_ID =
- { 0, 1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
- // Not to be used when generating cross-platform test vectors (due to default charset encoding)
- private static final byte[] TEST_METADATA = "Some protocol metadata string goes here".getBytes();
- private static final byte[] TEST_ASSOCIATED_DATA = {
- 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 11 };
- private static final byte[] ZERO_BYTE = { 0 };
- private static final byte[] EMPTY_BYTES = { };
-
- private static final List<byte[]> MESSAGE_VALUES = Arrays.asList(
- EMPTY_BYTES,
- TEST_MESSAGE
- );
-
- private static final List<byte[]> KEY_ID_VALUES = Arrays.asList(
- null,
- EMPTY_BYTES,
- TEST_KEY_ID);
-
- private static final List<byte[]> METADATA_VALUES = Arrays.asList(
- null,
- EMPTY_BYTES,
- TEST_METADATA
- );
-
- private static final List<byte[]> ASSOCIATED_DATA_VALUES = Arrays.asList(
- null,
- ZERO_BYTE,
- TEST_ASSOCIATED_DATA);
-
- private byte[] message;
- private byte[] metadata;
- private byte[] verificationKeyId;
- private byte[] decryptionKeyId;
- private byte[] associatedData;
- private PublicKey ecPublicKey;
- private PrivateKey ecPrivateKey;
- private PublicKey rsaPublicKey;
- private PrivateKey rsaPrivateKey;
- private SecretKey aesEncryptionKey;
- private SecretKey hmacKey;
- private SecureMessageBuilder secureMessageBuilder = new SecureMessageBuilder();
- private SecureRandom rng = new SecureRandom();
-
- @Override
- public void setUp() {
- message = TEST_MESSAGE;
- metadata = null;
- verificationKeyId = null;
- decryptionKeyId = null;
- associatedData = null;
- if (!PublicKeyProtoUtil.isLegacyCryptoRequired()) {
- KeyPair ecKeyPair = PublicKeyProtoUtil.generateEcP256KeyPair();
- ecPublicKey = ecKeyPair.getPublic();
- ecPrivateKey = ecKeyPair.getPrivate();
- }
- KeyPair rsaKeyPair = PublicKeyProtoUtil.generateRSA2048KeyPair();
- rsaPublicKey = rsaKeyPair.getPublic();
- rsaPrivateKey = rsaKeyPair.getPrivate();
- try {
- aesEncryptionKey = makeAesKey();
- hmacKey = makeAesKey();
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- fail();
- }
- secureMessageBuilder.reset();
- }
-
- private SecureMessage sign(SigType sigType) throws NoSuchAlgorithmException, InvalidKeyException {
- return getPreconfiguredBuilder().buildSignedCleartextMessage(
- getSigningKeyFor(sigType), sigType, message);
- }
-
- private SecureMessage signCrypt(SigType sigType, EncType encType)
- throws NoSuchAlgorithmException, InvalidKeyException {
- return getPreconfiguredBuilder().buildSignCryptedMessage(
- getSigningKeyFor(sigType), sigType, aesEncryptionKey, encType, message);
- }
-
- private void verify(SecureMessage signed, SigType sigType)
- throws NoSuchAlgorithmException, InvalidKeyException, SignatureException,
- InvalidProtocolBufferException {
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignedCleartextMessage(
- signed,
- getVerificationKeyFor(sigType),
- sigType,
- associatedData);
- consistencyCheck(signed, headerAndBody, sigType, EncType.NONE);
- }
-
- private void verifyDecrypt(SecureMessage encryptedAndSigned, SigType sigType, EncType encType)
- throws InvalidProtocolBufferException, InvalidKeyException, NoSuchAlgorithmException,
- SignatureException {
- HeaderAndBody headerAndBody = SecureMessageParser.parseSignCryptedMessage(
- encryptedAndSigned,
- getVerificationKeyFor(sigType),
- sigType,
- aesEncryptionKey,
- encType,
- associatedData);
- consistencyCheck(encryptedAndSigned, headerAndBody, sigType, encType);
- }
-
- // A collection of different kinds of "alterations" that can be made to SecureMessage protos.
- enum Alteration {
- DECRYPTION_KEY_ID,
- ENCTYPE,
- HEADER_AND_BODY_PROTO,
- MESSAGE,
- METADATA,
- RESIGNCRYPTION_ATTACK,
- SIGTYPE,
- VERIFICATION_KEY_ID,
- ASSOCIATED_DATA_LENGTH,
- }
-
- private void doSignAndVerify(SigType sigType) throws Exception {
- System.out.println("BEGIN_TEST -- Testing SigType: " + sigType + " with:");
- System.out.println("VerificationKeyId: " + Arrays.toString(verificationKeyId));
- System.out.println("Metadata: " + Arrays.toString(metadata));
- System.out.println("AssociatedData: " + Arrays.toString(associatedData));
- System.out.println("Message: " + Arrays.toString(message));
- // Positive test cases
- SecureMessage signed = sign(sigType);
- verify(signed, sigType);
-
- // Negative test cases
- for (Alteration altType : getAlterationsToTest()) {
- System.out.println("Testing alteration: " + altType.toString());
- SecureMessage modified = modifyMessage(signed, altType);
- try {
- verify(modified, sigType);
- fail(altType.toString());
- } catch (SignatureException e) {
- // We expect this
- }
- }
-
- // Try verifying with the wrong associated data
- if ((associatedData == null) || (associatedData.length == 0)) {
- associatedData = ZERO_BYTE;
- } else {
- associatedData = null;
- }
- try {
- verify(signed, sigType);
- fail("Expected verification to fail due to incorrect associatedData");
- } catch (SignatureException e) {
- // We expect this
- }
-
- System.out.println("PASS_TEST -- Testing SigType: " + sigType);
- }
-
- private List<Alteration> getAlterationsToTest() {
- if (isRunningInAndroid()) {
- // Android is very slow. Only try one alteration attack, intead of all of them.
- int randomAlteration = Math.abs(rng.nextInt()) % Alteration.values().length;
- return Arrays.asList(Alteration.values()[randomAlteration]);
- } else {
- // Just try all of them
- return Arrays.asList(Alteration.values());
- }
- }
-
- private void doSignCryptAndVerifyDecrypt(SigType sigType) throws Exception {
- // For now, EncType is always AES_256_CBC
- EncType encType = EncType.AES_256_CBC;
- System.out.println("BEGIN_TEST -- Testing SigType: " + sigType
- + " EncType: " + encType + " with:");
- System.out.println("DecryptionKeyId: " + Arrays.toString(decryptionKeyId));
- System.out.println("VerificationKeyId: " + Arrays.toString(verificationKeyId));
- System.out.println("Metadata: " + Arrays.toString(metadata));
- System.out.println("AssociatedData: " + Arrays.toString(associatedData));
- System.out.println("Message: " + Arrays.toString(message));
- SecureMessage encryptedAndSigned = null;
- encryptedAndSigned = signCrypt(sigType, encType);
- verifyDecrypt(encryptedAndSigned, sigType, encType);
-
- // Negative test cases
- for (Alteration altType : getAlterationsToTest()) {
- if (skipAlterationTestFor(altType, sigType)) {
- System.out.println("Skipping alteration test: " + altType.toString());
- continue;
- }
-
- System.out.println("Testing alteration: " + altType.toString());
- SecureMessage modified = modifyMessage(encryptedAndSigned, altType);
- try {
- verifyDecrypt(modified, sigType, encType);
- fail();
- } catch (SignatureException e) {
- // We expect this
- }
- }
- System.out.println("PASS_TEST -- Testing SigType: " + sigType + " EncType: " + encType);
- }
-
- private boolean skipAlterationTestFor(Alteration altType, SigType sigType) {
- // The RESIGNCRYPTION_ATTACK may be allowed to succeed iff the same symmetric key
- // is being reused for both signature and encryption.
- return (altType == Alteration.RESIGNCRYPTION_ATTACK)
- // Intentionally testing equality of object address here
- && (getVerificationKeyFor(sigType) == aesEncryptionKey);
- }
-
- private SecureMessage modifyMessage(SecureMessage original, Alteration altType) throws Exception {
- ByteString bogus = ByteString.copyFromUtf8("BOGUS");
- HeaderAndBody origHAB = HeaderAndBody.parseFrom(original.getHeaderAndBody());
- HeaderAndBody.Builder newHAB = HeaderAndBody.newBuilder(origHAB);
- Header.Builder newHeader = Header.newBuilder(origHAB.getHeader());
- Header origHeader = origHAB.getHeader();
- SecureMessage.Builder result = SecureMessage.newBuilder(original);
- switch (altType) {
- case DECRYPTION_KEY_ID:
- if (origHeader.hasDecryptionKeyId()) {
- newHeader.clearDecryptionKeyId();
- } else {
- newHeader.setDecryptionKeyId(ByteString.copyFrom(TEST_KEY_ID));
- }
- break;
- case ENCTYPE:
- if (origHeader.getEncryptionScheme() == SecureMessageProto.EncScheme.NONE) {
- newHeader.setEncryptionScheme(SecureMessageProto.EncScheme.AES_256_CBC);
- } else {
- newHeader.setEncryptionScheme(SecureMessageProto.EncScheme.NONE);
- }
- break;
- case HEADER_AND_BODY_PROTO:
- // Substitute a junk byte string instead of the HeeaderAndBody proto message
- return result.setHeaderAndBody(bogus).build();
- case MESSAGE:
- byte[] origBody = origHAB.getBody().toByteArray();
- if (origBody.length > 0) {
- // Lop off trailing byte of the body
- byte[] truncatedBody = CryptoOps.subarray(origBody, 0, origBody.length - 1);
- newHAB.setBody(ByteString.copyFrom(truncatedBody));
- } else {
- newHAB.setBody(bogus);
- }
- break;
- case METADATA:
- if (origHeader.hasPublicMetadata()) {
- newHeader.clearPublicMetadata();
- } else {
- newHeader.setPublicMetadata(bogus);
- }
- break;
- case RESIGNCRYPTION_ATTACK:
- // Simulate stripping a signature, and re-signing a message to see if it will be decrypted.
- newHeader
- .setVerificationKeyId(bogus)
- // In case original was cleartext
- .setEncryptionScheme(SecureMessageProto.EncScheme.AES_256_CBC);
-
- // Now that we've mildly changed the header, compute a new signature for it.
- newHAB.setHeader(newHeader.build());
- byte[] headerAndBodyBytes = newHAB.build().toByteArray();
- result.setHeaderAndBody(ByteString.copyFrom(headerAndBodyBytes));
- SigType sigType = SigType.valueOf(origHeader.getSignatureScheme());
- // Note that in all cases where this attack applies, the associatedData is not normally
- // used directly inside the signature (but rather inside the inner ciphertext).
- result.setSignature(ByteString.copyFrom(CryptoOps.sign(
- sigType, getSigningKeyFor(sigType), rng, headerAndBodyBytes)));
- return result.build();
- case SIGTYPE:
- if (origHeader.getSignatureScheme() == SecureMessageProto.SigScheme.ECDSA_P256_SHA256) {
- newHeader
- .setSignatureScheme(SecureMessageProto.SigScheme.HMAC_SHA256);
- } else {
- newHeader
- .setSignatureScheme(SecureMessageProto.SigScheme.ECDSA_P256_SHA256);
- }
- break;
- case VERIFICATION_KEY_ID:
- if (origHeader.hasVerificationKeyId()) {
- newHeader.clearVerificationKeyId();
- } else {
- newHeader.setVerificationKeyId(
- ByteString.copyFrom(TEST_KEY_ID));
- }
- break;
- case ASSOCIATED_DATA_LENGTH:
- int adLength = origHeader.getAssociatedDataLength();
- switch (adLength) {
- case 0:
- newHeader.setAssociatedDataLength(1);
- break;
- case 1:
- newHeader.setAssociatedDataLength(0);
- break;
- default:
- newHeader.setAssociatedDataLength(adLength - 1);
- }
- break;
- default:
- fail("Forgot to implement an alteration attack: " + altType);
- break;
- }
- // Set the header.
- newHAB.setHeader(newHeader.build());
-
- return result.setHeaderAndBody(ByteString.copyFrom(newHAB.build().toByteArray()))
- .build();
- }
-
- public void testEcDsaSignedOnly() throws Exception {
- doTestSignedOnly(SigType.ECDSA_P256_SHA256);
- }
-
- public void testRsaSignedOnly() throws Exception {
- doTestSignedOnly(SigType.RSA2048_SHA256);
- }
-
- public void testHmacSignedOnly() throws Exception {
- doTestSignedOnly(SigType.HMAC_SHA256);
- }
-
- private void doTestSignedOnly(SigType sigType) throws Exception {
- if (isUnsupported(sigType)) {
- return;
- }
-
- // decryptionKeyId must be left null for signature-only operation
- for (byte[] vkId : KEY_ID_VALUES) {
- verificationKeyId = vkId;
- for (byte[] md : METADATA_VALUES) {
- metadata = md;
- for (byte[] ad : ASSOCIATED_DATA_VALUES) {
- associatedData = ad;
- for (byte[] msg : MESSAGE_VALUES) {
- message = msg;
- doSignAndVerify(sigType);
- }
- }
- }
- }
-
- // Test that use of a DecryptionKeyId is not allowed for signature-only
- try {
- decryptionKeyId = TEST_KEY_ID; // Should trigger a failure
- doSignAndVerify(sigType);
- fail();
- } catch (IllegalStateException expected) {
- }
-}
-
- public void testEncryptedAndMACed() throws Exception {
- for (byte[] dkId : KEY_ID_VALUES) {
- decryptionKeyId = dkId;
- for (byte[] vkId : KEY_ID_VALUES) {
- verificationKeyId = vkId;
- for (byte[] md : METADATA_VALUES) {
- metadata = md;
- for (byte[] ad : ASSOCIATED_DATA_VALUES) {
- associatedData = ad;
- for (byte[] msg : MESSAGE_VALUES) {
- message = msg;
- doSignCryptAndVerifyDecrypt(SigType.HMAC_SHA256);
- }
- }
- }
- }
- }
- }
-
- public void testEncryptedAndMACedWithSameKey() throws Exception {
- hmacKey = aesEncryptionKey; // Re-use the same key for both
- testEncryptedAndMACed();
- }
-
- public void testEncryptedAndEcdsaSigned() throws Exception {
- doTestEncryptedAndSigned(SigType.ECDSA_P256_SHA256);
- }
-
- public void testEncryptedAndRsaSigned() throws Exception {
- doTestEncryptedAndSigned(SigType.RSA2048_SHA256);
- }
-
- public void doTestEncryptedAndSigned(SigType sigType) throws Exception {
- if (isUnsupported(sigType)) {
- return; // EC operations aren't supported on older Android releases
- }
-
- for (byte[] dkId : KEY_ID_VALUES) {
- decryptionKeyId = dkId;
- for (byte[] vkId : KEY_ID_VALUES) {
- verificationKeyId = vkId;
- if ((verificationKeyId == null) && sigType.isPublicKeyScheme()) {
- continue; // Null verificationKeyId is not allowed with public key signcryption
- }
- for (byte[] md : METADATA_VALUES) {
- metadata = md;
- for (byte[] ad : ASSOCIATED_DATA_VALUES) {
- associatedData = ad;
- for (byte[] msg : MESSAGE_VALUES) {
- message = msg;
- doSignCryptAndVerifyDecrypt(sigType);
- }
- }
- }
- }
- }
-
- // Verify that a missing verificationKeyId is not allowed here
- try {
- verificationKeyId = null; // Should trigger a failure
- signCrypt(sigType, EncType.AES_256_CBC);
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testSignCryptionRequiresEncryption() throws Exception {
- try {
- signCrypt(SigType.RSA2048_SHA256, EncType.NONE);
- } catch (IllegalArgumentException expected) {
- }
- }
-
- public void testAssociatedData() throws Exception {
- // How much extra room might the encoding of AssociatedDataLength take up?
- int maxAssociatedDataOverheadBytes = 4;
- // How many bytes might normally vary in the encoding length for SecureMessages generated with
- // fresh randomness but identical contents (e.g., due to MSBs being 0)
- int maxJitter = 2;
- verificationKeyId = TEST_KEY_ID; // So that public key signcryption will work
- message = TEST_MESSAGE;
-
- for (SigType sigType : SigType.values()) {
- if (isUnsupported(sigType)) {
- continue;
- }
- associatedData = null;
- SecureMessage signed = sign(sigType);
- int signedLength = signed.toByteArray().length;
- associatedData = EMPTY_BYTES;
- // Check that EMPTY_BYTES is equivalent to null associated data under verification
- verify(signed, sigType);
- // We already tested that incorrect associated data fails elsewhere in negative test cases
- associatedData = TEST_ASSOCIATED_DATA;
- SecureMessage signedWithAssociatedData = sign(sigType);
- int signedWithAssociatedDataLength = signedWithAssociatedData.toByteArray().length;
- String logInfo = "Testing associated data overhead for signature using: " + sigType
- + " signedLength=" + signedLength
- + " signedWithAssociatedDataLength=" + signedWithAssociatedDataLength;
- System.out.println(logInfo);
- assertTrue(logInfo,
- signedWithAssociatedData.toByteArray().length
- <= signed.toByteArray().length + maxAssociatedDataOverheadBytes + maxJitter);
- }
-
- for (SigType sigType : SigType.values()) {
- if (isUnsupported(sigType)) {
- continue;
- }
- associatedData = null;
- SecureMessage signCrypted = signCrypt(sigType, EncType.AES_256_CBC);
- int signCryptedLength = signCrypted.toByteArray().length;
- // Check that EMPTY_BYTES is equivalent to null associated data under verification
- associatedData = EMPTY_BYTES;
- verifyDecrypt(signCrypted, sigType, EncType.AES_256_CBC);
- // We already tested that incorrect associated data fails elsewhere in negative test cases
- associatedData = TEST_ASSOCIATED_DATA;
- SecureMessage signCryptedWithAssociatedData = signCrypt(sigType, EncType.AES_256_CBC);
- int signCryptedWithAssociatedDataLength = signCryptedWithAssociatedData.toByteArray().length;
- String logInfo = "Testing associated data overhead for signcryption using: " + sigType
- + " signCryptedLength=" + signCryptedLength
- + " signCryptedWithAssociatedDataLength=" + signCryptedWithAssociatedDataLength;
- System.out.println(logInfo);
- assertTrue(logInfo,
- signCryptedWithAssociatedData.toByteArray().length
- <= signCrypted.toByteArray().length + maxAssociatedDataOverheadBytes + maxJitter);
- }
- }
-
- public void testEncryptedAndEcdsaSignedUsingPublicKeyProto() throws Exception {
- if (isUnsupported(SigType.ECDSA_P256_SHA256)) {
- return;
- }
-
- // Safest usage of SignCryption is to set the VerificationKeyId to an actual representation of
- // the verification key.
- verificationKeyId = PublicKeyProtoUtil.encodeEcPublicKey(ecPublicKey).toByteArray();
- SecureMessage encryptedAndSigned = signCrypt(SigType.ECDSA_P256_SHA256, EncType.AES_256_CBC);
-
- // Simulate extracting the verification key ID from the SecureMessage (non-standard usage)
- ecPublicKey =
- PublicKeyProtoUtil.parseEcPublicKey(
- EcP256PublicKey.parseFrom(
- SecureMessageParser.getUnverifiedHeader(encryptedAndSigned)
- .getVerificationKeyId()));
-
- // Note that this verification uses the encoded/decoded ecPublicKey value
- verifyDecrypt(encryptedAndSigned, SigType.ECDSA_P256_SHA256, EncType.AES_256_CBC);
- }
-
- public void testEncryptedAndRsaSignedUsingPublicKeyProto() throws Exception {
- // Safest usage of SignCryption is to set the VerificationKeyId to an actual representation of
- // the verification key.
- verificationKeyId = PublicKeyProtoUtil.encodeRsa2048PublicKey(rsaPublicKey).toByteArray();
- SecureMessage encryptedAndSigned = signCrypt(SigType.RSA2048_SHA256, EncType.AES_256_CBC);
-
- // Simulate extracting the verification key ID from the SecureMessage (non-standard usage)
- rsaPublicKey =
- PublicKeyProtoUtil.parseRsa2048PublicKey(
- SimpleRsaPublicKey.parseFrom(
- SecureMessageParser.getUnverifiedHeader(encryptedAndSigned)
- .getVerificationKeyId()));
-
- // Note that this verification uses the encoded/decoded SimpleRsaPublicKey value
- verifyDecrypt(encryptedAndSigned, SigType.RSA2048_SHA256, EncType.AES_256_CBC);
- }
-
- // TODO(shabsi): The test was only corrupting header but wasn't setting the body. With protolite,
- // not setting a required field causes problems. Modify the SecureMessageParser test and
- // enable/remove this test.
- /*
- public void testCorruptUnverifiedHeader() throws Exception {
- // Create a sample message
- SecureMessage original = signCrypt(SigType.HMAC_SHA256, EncType.AES_256_CBC);
- HeaderAndBody originalHAB = HeaderAndBody.parseFrom(original.getHeaderAndBody().toByteArray());
- for (CorruptHeaderType corruptionType : CorruptHeaderType.values()) {
- // Mess with the HeaderAndBody field
- HeaderAndBody.Builder corruptHAB = HeaderAndBody.newBuilder(originalHAB);
- try {
- corruptHeaderWith(corruptionType, corruptHAB);
- // Construct the corrupted message using the modified HeaderAndBody
- SecureMessage.Builder corrupt = SecureMessage.newBuilder(original);
- corrupt.setHeaderAndBody(ByteString.copyFrom(corruptHAB.build().toByteArray())).build();
- SecureMessageParser.getUnverifiedHeader(corrupt.build());
- fail("Corrupt header type " + corruptionType + " parsed without error");
- } catch (InvalidProtocolBufferException expected) {
- }
- }
- }
- */
-
- public void testParseEmptyMessage() throws Exception {
- byte[] bogusData = new byte[0];
-
- try {
- SecureMessageParser.parseSignedCleartextMessage(
- SecureMessage.parseFrom(bogusData),
- aesEncryptionKey,
- SigType.HMAC_SHA256);
- fail("Empty message verified without error");
- } catch (SignatureException | UninitializedMessageException
- | InvalidProtocolBufferException expected) {
- }
- }
-
- public void testParseKeyInvalidInputs() throws Exception {
- GenericPublicKey[] badKeys = new GenericPublicKey[] {
- GenericPublicKey.newBuilder().setType(SecureMessageProto.PublicKeyType.EC_P256).build(),
- GenericPublicKey.newBuilder().setType(SecureMessageProto.PublicKeyType.RSA2048).build(),
- GenericPublicKey.newBuilder().setType(SecureMessageProto.PublicKeyType.DH2048_MODP).build(),
- };
- for (int i = 0; i < badKeys.length; i++) {
- GenericPublicKey key = badKeys[i];
- try {
- PublicKeyProtoUtil.parsePublicKey(key);
- fail(String.format("%sth key was parsed without exceptions", i));
- } catch (InvalidKeySpecException expected) {
- }
- }
- }
-
- enum CorruptHeaderType {
- EMPTY,
- // TODO(shabsi): Remove these test cases and modify code in SecureMessageParser appropriately.
- // UNSET,
- // JUNK,
- }
-
- private void corruptHeaderWith(CorruptHeaderType corruptionType,
- HeaderAndBody.Builder protoToModify) {
- switch (corruptionType) {
- case EMPTY:
- protoToModify.setHeader(Header.getDefaultInstance());
- break;
- /*
- case JUNK:
- Header.Builder junk = Header.newBuilder();
- junk.setDecryptionKeyId(ByteString.copyFromUtf8("fooooo"));
- junk.setIv(ByteString.copyFromUtf8("bar"));
- // Don't set signature scheme.
- junk.setVerificationKeyId(ByteString.copyFromUtf8("bazzzzz"));
- protoToModify.setHeader(junk.build());
- break;
- case UNSET:
- protoToModify.clearHeader();
- break;
- */
- default:
- throw new RuntimeException("Broken test code");
- }
- }
-
- private void consistencyCheck(
- SecureMessage secmsg, HeaderAndBody headerAndBody, SigType sigType, EncType encType)
- throws InvalidProtocolBufferException {
- Header header = SecureMessageParser.getUnverifiedHeader(secmsg);
- checkHeader(header, sigType, encType); // Checks that the "unverified header" looks right
- checkHeaderAndBody(header, headerAndBody); // Matches header vs. the "verified" headerAndBody
- }
-
- private Header checkHeader(Header header, SigType sigType, EncType encType) {
- assertEquals(sigType.getSigScheme(), header.getSignatureScheme());
- assertEquals(encType.getEncScheme(), header.getEncryptionScheme());
- checkKeyIdsAndMetadata(verificationKeyId, decryptionKeyId, metadata, associatedData, header);
- return header;
- }
-
- private void checkHeaderAndBody(Header header, HeaderAndBody headerAndBody) {
- assertTrue(header.equals(headerAndBody.getHeader()));
- assertTrue(Arrays.equals(message, headerAndBody.getBody().toByteArray()));
- }
-
- private void checkKeyIdsAndMetadata(byte[] verificationKeyId, byte[] decryptionKeyId,
- byte[] metadata, byte[] associatedData, Header header) {
- if (verificationKeyId == null) {
- assertFalse(header.hasVerificationKeyId());
- } else {
- assertTrue(Arrays.equals(verificationKeyId, header.getVerificationKeyId().toByteArray()));
- }
- if (decryptionKeyId == null) {
- assertFalse(header.hasDecryptionKeyId());
- } else {
- assertTrue(Arrays.equals(decryptionKeyId, header.getDecryptionKeyId().toByteArray()));
- }
- if (metadata == null) {
- assertFalse(header.hasPublicMetadata());
- } else {
- assertTrue(Arrays.equals(metadata, header.getPublicMetadata().toByteArray()));
- }
- if (associatedData == null) {
- assertFalse(header.hasAssociatedDataLength());
- } else {
- assertEquals(associatedData.length, header.getAssociatedDataLength());
- }
- }
-
- private SecretKey makeAesKey() throws NoSuchAlgorithmException {
- KeyGenerator aesKeygen = KeyGenerator.getInstance("AES");
- aesKeygen.init(256);
- return aesKeygen.generateKey();
- }
-
- private Key getSigningKeyFor(SigType sigType) {
- if (sigType == SigType.ECDSA_P256_SHA256) {
- return ecPrivateKey;
- }
- if (sigType == SigType.RSA2048_SHA256) {
- return rsaPrivateKey;
- }
- if (sigType == SigType.HMAC_SHA256) {
- return hmacKey;
- }
- return null; // This should not happen
- }
-
- private Key getVerificationKeyFor(SigType sigType) {
- try {
- if (sigType == SigType.ECDSA_P256_SHA256) {
- return PublicKeyProtoUtil.parseEcPublicKey(
- PublicKeyProtoUtil.encodeEcPublicKey(ecPublicKey));
- }
- if (sigType == SigType.RSA2048_SHA256) {
- return PublicKeyProtoUtil.parseRsa2048PublicKey(
- PublicKeyProtoUtil.encodeRsa2048PublicKey(rsaPublicKey));
- }
- } catch (InvalidKeySpecException e) {
- throw new AssertionError(e);
- }
-
- assertFalse(sigType.isPublicKeyScheme());
- // For symmetric key schemes
- return getSigningKeyFor(sigType);
- }
-
- private SecureMessageBuilder getPreconfiguredBuilder() {
- // Re-use a single instance of SecureMessageBuilder for efficiency.
- SecureMessageBuilder builder = secureMessageBuilder.reset();
- if (verificationKeyId != null) {
- builder.setVerificationKeyId(verificationKeyId);
- }
- if (decryptionKeyId != null) {
- builder.setDecryptionKeyId(decryptionKeyId);
- }
- if (metadata != null) {
- builder.setPublicMetadata(metadata);
- }
- if (associatedData != null) {
- builder.setAssociatedData(associatedData);
- }
- return builder;
- }
-
- private static boolean isUnsupported(SigType sigType) {
- // EC operations aren't supported on older Android releases
- return PublicKeyProtoUtil.isLegacyCryptoRequired()
- && (sigType == SigType.ECDSA_P256_SHA256);
- }
-
- private static boolean isRunningInAndroid() {
- try {
- ClassLoader.getSystemClassLoader().loadClass("android.os.Build$VERSION");
- return true;
- } catch (ClassNotFoundException e) {
- // Not running on Android
- return false;
- }
- }
-}