diff options
Diffstat (limited to 'src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java')
-rw-r--r-- | src/main/javatest/com/google/security/cryptauth/lib/securemessage/SecureMessageTest.java | 766 |
1 files changed, 0 insertions, 766 deletions
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; - } - } -} |