diff options
author | evitayan <evitayan@google.com> | 2019-03-05 10:35:43 -0800 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-03-05 10:35:43 -0800 |
commit | edef32aba243271eb3188e2330ea54baf246d941 (patch) | |
tree | 021e21e84664af1008cdcf5505eaf7a7c8a6311d | |
parent | fbe41f418387d06a7b5533012984626fae27e303 (diff) | |
parent | 04177d35228000474011c6fb875ba66e0dbf574c (diff) | |
download | ike-edef32aba243271eb3188e2330ea54baf246d941.tar.gz |
Support building and validating SaProposal am: 1d3f038662 am: 37a409a167
am: 04177d3522
Change-Id: I9329de615e3a86c6b6f07923b9859e3b71caf251
3 files changed, 355 insertions, 17 deletions
diff --git a/src/java/com/android/ike/ikev2/SaProposal.java b/src/java/com/android/ike/ikev2/SaProposal.java index 232ffd7f..e5b258e7 100644 --- a/src/java/com/android/ike/ikev2/SaProposal.java +++ b/src/java/com/android/ike/ikev2/SaProposal.java @@ -209,24 +209,89 @@ public final class SaProposal { ERROR_TAG + "Encryption algorithm must be proposed."); } - return (EncryptionTransform[]) mProposedEncryptAlgos.toArray(); + return mProposedEncryptAlgos.toArray( + new EncryptionTransform[mProposedEncryptAlgos.size()]); } private PrfTransform[] buildPrfsOrThrow() { - // TODO: Validate that PRF must be proposed for IKE SA and PRF must not be - // proposed for Child SA. - throw new UnsupportedOperationException("Cannot validate user proposed algorithm."); + if (mIsIkeProposal == mProposedPrfs.isEmpty()) { + throw new IllegalArgumentException( + ERROR_TAG + "Invalid PRF configuration for this SA Proposal."); + } + + return mProposedPrfs.toArray(new PrfTransform[mProposedPrfs.size()]); + } + + private IntegrityTransform[] buildIntegAlgosForIkeOrThrow() { + // When building IKE SA Proposal with normal-mode ciphers, mProposedIntegrityAlgos must + // not be empty and must not have INTEGRITY_ALGORITHM_NONE. When building IKE SA + // Proposal with combined-mode ciphers, mProposedIntegrityAlgos must be either empty or + // only have INTEGRITY_ALGORITHM_NONE. + if (mProposedIntegrityAlgos.isEmpty() && !mHasAead) { + throw new IllegalArgumentException( + ERROR_TAG + + "Integrity algorithm " + + "must be proposed with normal ciphers in IKE proposal."); + } + + for (IntegrityTransform transform : mProposedIntegrityAlgos) { + if ((transform.id == INTEGRITY_ALGORITHM_NONE) != mHasAead) { + throw new IllegalArgumentException( + ERROR_TAG + + "Invalid integrity algorithm configuration" + + " for this SA Proposal"); + } + } + + return mProposedIntegrityAlgos.toArray( + new IntegrityTransform[mProposedIntegrityAlgos.size()]); } - private IntegrityTransform[] buildIntegAlgosOrThrow() { - // TODO: Validate proposed integrity algorithms according to existence of AEAD. - throw new UnsupportedOperationException("Cannot validate user proposed algorithm."); + private IntegrityTransform[] buildIntegAlgosForChildOrThrow() { + // When building Child SA Proposal with normal-mode ciphers, there is no contraint on + // integrity algorithm. When building Child SA Proposal with combined-mode ciphers, + // mProposedIntegrityAlgos must be either empty or only have INTEGRITY_ALGORITHM_NONE. + for (IntegrityTransform transform : mProposedIntegrityAlgos) { + if (transform.id != INTEGRITY_ALGORITHM_NONE && mHasAead) { + throw new IllegalArgumentException( + ERROR_TAG + + "Only INTEGRITY_ALGORITHM_NONE can be" + + " proposed with combined-mode ciphers in any proposal."); + } + } + + return mProposedIntegrityAlgos.toArray( + new IntegrityTransform[mProposedIntegrityAlgos.size()]); } - private DhGroupTransform[] buildDhGroupsOrThrow() { - // TODO: Validate proposed DH groups according to the usage of SaProposal (for - // IKE SA, for first Child SA or for addtional Child SA) - throw new UnsupportedOperationException("Cannot validate user proposed algorithm."); + private DhGroupTransform[] buildDhGroupsForIkeOrThrow() { + if (mProposedDhGroups.isEmpty()) { + throw new IllegalArgumentException( + ERROR_TAG + "DH group must be proposed in IKE SA proposal."); + } + + for (DhGroupTransform transform : mProposedDhGroups) { + if (transform.id == DH_GROUP_NONE) { + throw new IllegalArgumentException( + ERROR_TAG + + "None-value DH group must not" + + " be proposed in IKE SA proposal"); + } + } + + return mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]); + } + + private DhGroupTransform[] buildDhGroupsForChildOrThrow() { + for (DhGroupTransform transform : mProposedDhGroups) { + if (transform.id != DH_GROUP_NONE && mIsFirstChild) { + throw new IllegalArgumentException( + ERROR_TAG + + "Only DH_GROUP_NONE can be" + + " proposed in first Child SA proposal."); + } + } + return mProposedDhGroups.toArray(new DhGroupTransform[mProposedDhGroups.size()]); } /** Returns a new Builder for a IKE SA Proposal. */ @@ -338,13 +403,17 @@ public final class SaProposal { * * @return SaProposal the validated SaProposal. * @throws IllegalArgumentException if SaProposal is invalid. - * */ + */ public SaProposal buildOrThrow() { EncryptionTransform[] encryptionTransforms = buildEncryptAlgosOrThrow(); PrfTransform[] prfTransforms = buildPrfsOrThrow(); - IntegrityTransform[] integrityTransforms = buildIntegAlgosOrThrow(); - DhGroupTransform[] dhGroupTransforms = buildDhGroupsOrThrow(); + IntegrityTransform[] integrityTransforms = + mIsIkeProposal + ? buildIntegAlgosForIkeOrThrow() + : buildIntegAlgosForChildOrThrow(); + DhGroupTransform[] dhGroupTransforms = + mIsIkeProposal ? buildDhGroupsForIkeOrThrow() : buildDhGroupsForChildOrThrow(); // IKE library only supports negotiating ESP Child SA. int protocol = mIsIkeProposal ? IkePayload.PROTOCOL_ID_IKE : IkePayload.PROTOCOL_ID_ESP; diff --git a/tests/iketests/src/java/com/android/ike/ikev2/SaProposalTest.java b/tests/iketests/src/java/com/android/ike/ikev2/SaProposalTest.java index 522d44bc..4b0656da 100644 --- a/tests/iketests/src/java/com/android/ike/ikev2/SaProposalTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/SaProposalTest.java @@ -16,19 +16,129 @@ package com.android.ike.ikev2; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.android.ike.ikev2.SaProposal.Builder; +import com.android.ike.ikev2.message.IkePayload; +import com.android.ike.ikev2.message.IkeSaPayload.DhGroupTransform; +import com.android.ike.ikev2.message.IkeSaPayload.EncryptionTransform; +import com.android.ike.ikev2.message.IkeSaPayload.IntegrityTransform; +import com.android.ike.ikev2.message.IkeSaPayload.PrfTransform; import org.junit.Test; public final class SaProposalTest { + private final EncryptionTransform mEncryption3DesTransform; + private final EncryptionTransform mEncryptionAesGcm8Transform; + private final IntegrityTransform mIntegrityHmacSha1Transform; + private final IntegrityTransform mIntegrityNoneTransform; + private final PrfTransform mPrfAes128XCbcTransform; + private final DhGroupTransform mDhGroup1024Transform; + + public SaProposalTest() { + mEncryption3DesTransform = new EncryptionTransform(SaProposal.ENCRYPTION_ALGORITHM_3DES); + mEncryptionAesGcm8Transform = + new EncryptionTransform( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, SaProposal.KEY_LEN_AES_128); + mIntegrityHmacSha1Transform = + new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96); + mIntegrityNoneTransform = new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_NONE); + mPrfAes128XCbcTransform = new PrfTransform(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC); + mDhGroup1024Transform = new DhGroupTransform(SaProposal.DH_GROUP_1024_BIT_MODP); + } + + @Test + public void testBuildIkeSaProposalWithNormalModeCipher() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); + SaProposal proposal = + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) + .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) + .buildOrThrow(); + + assertEquals(IkePayload.PROTOCOL_ID_IKE, proposal.mProtocolId); + assertArrayEquals( + new EncryptionTransform[] {mEncryption3DesTransform}, + proposal.mEncryptionAlgorithms); + assertArrayEquals( + new IntegrityTransform[] {mIntegrityHmacSha1Transform}, + proposal.mIntegrityAlgorithms); + assertArrayEquals( + new PrfTransform[] {mPrfAes128XCbcTransform}, proposal.mPseudorandomFunctions); + assertArrayEquals(new DhGroupTransform[] {mDhGroup1024Transform}, proposal.mDhGroups); + } + + @Test + public void testBuildIkeSaProposalWithCombinedModeCipher() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); + SaProposal proposal = + builder.addEncryptionAlgorithm( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, + SaProposal.KEY_LEN_AES_128) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) + .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) + .buildOrThrow(); + + assertEquals(IkePayload.PROTOCOL_ID_IKE, proposal.mProtocolId); + assertArrayEquals( + new EncryptionTransform[] {mEncryptionAesGcm8Transform}, + proposal.mEncryptionAlgorithms); + assertArrayEquals( + new PrfTransform[] {mPrfAes128XCbcTransform}, proposal.mPseudorandomFunctions); + assertArrayEquals(new DhGroupTransform[] {mDhGroup1024Transform}, proposal.mDhGroups); + assertTrue(proposal.mIntegrityAlgorithms.length == 0); + } + + @Test + public void testBuildFirstChildSaProposalWithCombinedCipher() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(true); + SaProposal proposal = + builder.addEncryptionAlgorithm( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, + SaProposal.KEY_LEN_AES_128) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE) + .buildOrThrow(); + + assertEquals(IkePayload.PROTOCOL_ID_ESP, proposal.mProtocolId); + assertArrayEquals( + new EncryptionTransform[] {mEncryptionAesGcm8Transform}, + proposal.mEncryptionAlgorithms); + assertArrayEquals( + new IntegrityTransform[] {mIntegrityNoneTransform}, proposal.mIntegrityAlgorithms); + assertTrue(proposal.mPseudorandomFunctions.length == 0); + assertTrue(proposal.mDhGroups.length == 0); + } + + @Test + public void testBuildAdditionalChildSaProposalWithNormalCipher() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); + + SaProposal proposal = + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE) + .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) + .buildOrThrow(); + + assertEquals(IkePayload.PROTOCOL_ID_ESP, proposal.mProtocolId); + assertArrayEquals( + new EncryptionTransform[] {mEncryption3DesTransform}, + proposal.mEncryptionAlgorithms); + assertArrayEquals( + new IntegrityTransform[] {mIntegrityNoneTransform}, proposal.mIntegrityAlgorithms); + assertArrayEquals(new DhGroupTransform[] {mDhGroup1024Transform}, proposal.mDhGroups); + assertTrue(proposal.mPseudorandomFunctions.length == 0); + } + @Test public void testBuildEncryptAlgosWithNoAlgorithm() throws Exception { Builder builder = Builder.newIkeSaProposalBuilder(); try { builder.buildOrThrow(); - fail("Encryption algorithm is not provided."); + fail("Expected to fail when no encryption algorithm is proposed."); } catch (IllegalArgumentException expected) { } @@ -39,7 +149,7 @@ public final class SaProposalTest { Builder builder = Builder.newIkeSaProposalBuilder(); try { builder.addEncryptionAlgorithm(-1); - fail("Encryption algorithm is not recognized."); + fail("Expected to fail when unrecognized encryption algorithm is proposed."); } catch (IllegalArgumentException expected) { } @@ -51,7 +161,139 @@ public final class SaProposalTest { try { builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12); - fail("Expect failure when normal and combined-mode ciphers are proposed together."); + fail( + "Expected to fail when " + + "normal and combined-mode ciphers are proposed together."); + } catch (IllegalArgumentException expected) { + + } + } + + @Test + public void testBuildIkeProposalWithoutPrf() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES).buildOrThrow(); + fail("Expected to fail when PRF is not provided in IKE SA proposal."); + } catch (IllegalArgumentException expected) { + + } + } + + @Test + public void testBuildChildProposalWithPrf() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1) + .buildOrThrow(); + + fail("Expected to fail when PRF is provided in Child SA proposal."); + } catch (IllegalArgumentException expected) { + + } + } + + // Test throwing exception when building IKE SA Proposal with AEAD and not-none integrity + // algorithm. + @Test + public void testBuildAeadWithIntegrityAlgo() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) + .buildOrThrow(); + + fail("Expected to fail when not-none integrity algorithm is proposed with AEAD"); + } catch (IllegalArgumentException expected) { + + } + } + + // Test throwing exception when building IKE SA Proposal with normal mode cipher and without + // integrity algorithm. + @Test + public void testBuildIkeProposalNormalCipherWithoutIntegrityAlgo() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1) + .buildOrThrow(); + + fail( + "Expected to fail when" + + " no integrity algorithm is proposed with non-combined cipher"); + } catch (IllegalArgumentException expected) { + + } + } + + // Test throwing exception when building IKE SA Proposal with normal mode cipher and none-value + // integrity algorithm. + @Test + public void testBuildIkeProposalNormalCipherWithNoneValueIntegrityAlgo() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(false); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) + .buildOrThrow(); + + fail( + "Expected to fail when none-value integrity algorithm is proposed" + + " with non-combined cipher"); + } catch (IllegalArgumentException expected) { + + } + } + + @Test + public void testBuildIkeProposalWithoutDhGroup() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) + .buildOrThrow(); + + fail("Expected to fail when no DH Group is proposed in IKE SA proposal."); + } catch (IllegalArgumentException expected) { + + } + } + + @Test + public void testBuildIkeProposalWithNoneValueDhGroup() throws Exception { + Builder builder = Builder.newIkeSaProposalBuilder(); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) + .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC) + .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) + .addDhGroup(SaProposal.DH_GROUP_NONE) + .buildOrThrow(); + + fail("Expected to fail when none-value DH Group is proposed in IKE SA proposal."); + } catch (IllegalArgumentException expected) { + + } + } + + // Test throwing exception when building first Child SA Proposal with not-none-value DH Group. + @Test + public void testBuildFirstChildProposalWithNotNoneValueDhGroup() throws Exception { + Builder builder = Builder.newChildSaProposalBuilder(true); + try { + builder.addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES) + .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) + .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP) + .buildOrThrow(); + + fail( + "Expected to fail when" + + " not-none-value DH Group is proposed in first Child SA proposal."); } catch (IllegalArgumentException expected) { } diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java index d2db6db9..cfde10e5 100644 --- a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeSaPayloadTest.java @@ -18,6 +18,7 @@ package com.android.ike.ikev2.message; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; @@ -383,6 +384,32 @@ public final class IkeSaPayloadTest { } @Test + public void testTransformEquals() throws Exception { + EncryptionTransform mEncrAesGcm8Key128TransformLeft = + new EncryptionTransform( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, SaProposal.KEY_LEN_AES_128); + EncryptionTransform mEncrAesGcm8Key128TransformRight = + new EncryptionTransform( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, SaProposal.KEY_LEN_AES_128); + + assertEquals(mEncrAesGcm8Key128TransformLeft, mEncrAesGcm8Key128TransformRight); + + EncryptionTransform mEncrAesGcm8Key192TransformLeft = + new EncryptionTransform( + SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8, SaProposal.KEY_LEN_AES_192); + + assertNotEquals(mEncrAesGcm8Key128TransformLeft, mEncrAesGcm8Key192TransformLeft); + + IntegrityTransform mIntegHmacSha1TransformLeft = + new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96); + IntegrityTransform mIntegHmacSha1TransformRight = + new IntegrityTransform(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96); + + assertNotEquals(mEncrAesGcm8Key128TransformLeft, mIntegHmacSha1TransformLeft); + assertEquals(mIntegHmacSha1TransformLeft, mIntegHmacSha1TransformRight); + } + + @Test public void testDecodeSingleProposal() throws Exception { byte[] inputPacket = TestUtils.hexStringToByteArray(PROPOSAL_RAW_PACKET); ByteBuffer inputBuffer = ByteBuffer.wrap(inputPacket); |