diff options
Diffstat (limited to 'src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java')
-rw-r--r-- | src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java | 403 |
1 files changed, 403 insertions, 0 deletions
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 new file mode 100644 index 0000000..285b259 --- /dev/null +++ b/src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageSimpleTestVectorTest.java @@ -0,0 +1,403 @@ +// 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); + } + } +} |