diff options
author | Dheeraj Shetty <shettydheeraj@google.com> | 2020-05-01 15:44:33 -0700 |
---|---|---|
committer | Dheeraj Shetty <shettydheeraj@google.com> | 2020-05-04 04:36:59 +0000 |
commit | a630efd48373a956972433d0c857aa008d12a76d (patch) | |
tree | 1b24bb30c068e99568fa33d1b124b42c2f17c48e | |
parent | 5c216abbb4945a9c591e95b86dea2b9dbe4503e0 (diff) | |
download | ike-a630efd48373a956972433d0c857aa008d12a76d.tar.gz |
Use dh group from KE payload for remote rekey
Test: atest FrameworksIkeTest
Bug: 138965374
Change-Id: I7765379fd58ab3640db47f7fa6fd7e2149dc0f85
Merged-In: I7765379fd58ab3640db47f7fa6fd7e2149dc0f85
4 files changed, 109 insertions, 10 deletions
diff --git a/src/java/android/net/ipsec/ike/ChildSaProposal.java b/src/java/android/net/ipsec/ike/ChildSaProposal.java index bf55e8fa..7318659a 100644 --- a/src/java/android/net/ipsec/ike/ChildSaProposal.java +++ b/src/java/android/net/ipsec/ike/ChildSaProposal.java @@ -117,6 +117,15 @@ public final class ChildSaProposal extends SaProposal { && isTransformSelectedFrom(mEsns, ((ChildSaProposal) saProposal).mEsns); } + /** @hide */ + public ChildSaProposal getCopyWithAdditionalDhTransform(int dhGroup) { + return new ChildSaProposal( + getEncryptionTransforms(), + getIntegrityTransforms(), + new DhGroupTransform[] {new DhGroupTransform(dhGroup)}, + getEsnTransforms()); + } + /** * This class is used to incrementally construct a ChildSaProposal. ChildSaProposal instances * are immutable once built. diff --git a/src/java/android/net/ipsec/ike/ChildSessionParams.java b/src/java/android/net/ipsec/ike/ChildSessionParams.java index e34d1d6d..0221491c 100644 --- a/src/java/android/net/ipsec/ike/ChildSessionParams.java +++ b/src/java/android/net/ipsec/ike/ChildSessionParams.java @@ -35,6 +35,17 @@ import java.util.concurrent.TimeUnit; * <p>Note that references to negotiated configurations will be held, and the same parameters will * be reused during rekey. This includes SA Proposals, lifetimes and traffic selectors. * + * <p>IKE library will send out KE payload only if user has configured one or more DH groups. The KE + * payload in a request will use the first DH group from the first user provided SA proposal (or the + * peer selected SA proposal if it's a rekey request). The KE payload in a response will depend on + * the SA proposal negotiation result. + * + * <p>When requesting the first Child Session in IKE AUTH, IKE library will not propose any DH group + * even if user has configured it, as per RFC 7296. When rekeying this child session, IKE library + * will accept DH groups that are configured in its ChildSessionParams. If after rekeying user needs + * to have the same DH group as that of the IKE Session, then they need to explicitly set the same + * DH Group in ChildSessionParams. + * * @see {@link TunnelModeChildSessionParams} and {@link TransportModeChildSessionParams} * @hide */ @@ -238,8 +249,9 @@ public abstract class ChildSessionParams { break; case IkeTrafficSelector.TRAFFIC_SELECTOR_TYPE_IPV6_ADDR_RANGE: startAddress = InetAddresses.parseNumericAddress("::"); - endAddress = InetAddresses.parseNumericAddress( - "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + endAddress = + InetAddresses.parseNumericAddress( + "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); break; default: throw new IllegalArgumentException("Invalid Traffic Selector type: " + tsType); diff --git a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java index 72663a85..8e244946 100644 --- a/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java +++ b/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachine.java @@ -1482,9 +1482,20 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { IkePayload.getPayloadForTypeInProvidedList( PAYLOAD_TYPE_SA, IkeSaPayload.class, reqPayloads); - // TODO: Handle first child remote rekey request with KE payload. + IkeKePayload reqKePayload = + IkePayload.getPayloadForTypeInProvidedList( + PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloads); + + ChildSaProposal saProposal = mSaProposal; - byte respProposalNumber = reqSaPayload.getNegotiatedProposalNumber(mSaProposal); + // Try accepting a DH group requested during remote rekey for both first and + // additional Child Sessions even if it is different from the previously negotiated + // proposal. + if (reqKePayload != null && isKePayloadAcceptable(reqKePayload)) { + saProposal = mSaProposal.getCopyWithAdditionalDhTransform(reqKePayload.dhGroup); + } + + byte respProposalNumber = reqSaPayload.getNegotiatedProposalNumber(saProposal); respPayloads = CreateChildSaHelper.getRekeyChildCreateRespPayloads( @@ -1492,7 +1503,7 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { mIpSecSpiGenerator, mLocalAddress, respProposalNumber, - mSaProposal, + saProposal, mLocalTs, mRemoteTs, mCurrentChildSaRecord.getLocalSpi(), @@ -1519,8 +1530,8 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { switch (createChildResult.status) { case CREATE_STATUS_OK: try { - // Do not need to update the negotiated proposal and TS - // because they are not changed. + // Do not need to update TS because they are not changed. + mSaProposal = createChildResult.negotiatedProposal; ChildLocalRequest rekeyLocalRequest = makeRekeyLocalRequest(); @@ -1604,6 +1615,20 @@ public class ChildSessionStateMachine extends AbstractSessionStateMachine { } } + private boolean isKePayloadAcceptable(IkeKePayload reqKePayload) { + ChildSaProposal proposal = + mSaProposal.getCopyWithAdditionalDhTransform(reqKePayload.dhGroup); + + // Verify if this proposal is accepted by user + for (SaProposal saProposal : mChildSessionParams.getSaProposals()) { + if (proposal.isNegotiatedFrom(saProposal)) { + return true; + } + } + + return false; + } + private void handleCreationFailureAndBackToIdle(IkeProtocolException e) { loge("Received invalid Rekey Child request. Reject with error notification", e); diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java index eab7f4a6..99f6d2e7 100644 --- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java +++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/ChildSessionStateMachineTest.java @@ -77,6 +77,7 @@ import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.ChildSessionConfiguration; import android.net.ipsec.ike.ChildSessionParams; import android.net.ipsec.ike.IkeManager; +import android.net.ipsec.ike.IkeSaProposal; import android.net.ipsec.ike.IkeTrafficSelector; import android.net.ipsec.ike.SaProposal; import android.net.ipsec.ike.TunnelModeChildSessionParams; @@ -1675,7 +1676,7 @@ public final class ChildSessionStateMachineTest { @Test public void testFirstChildLocalRekey() throws Exception { - ChildSaProposal saProposal = buildSaProposalWithDhGroup(); + ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP); ChildSessionParams childSessionParams = new TunnelModeChildSessionParams.Builder() .addSaProposal(saProposal) @@ -1694,9 +1695,11 @@ public final class ChildSessionStateMachineTest { // Send Rekey-Create request mChildSessionStateMachine.rekeyChildSession(); mLooper.dispatchAll(); + assertTrue( mChildSessionStateMachine.getCurrentState() instanceof ChildSessionStateMachine.RekeyChildLocalCreate); + verifyOutboundRekeyKePayload(false /*isResp*/); } @@ -1731,12 +1734,62 @@ public final class ChildSessionStateMachineTest { mMockChildSessionSmCallback); } - private ChildSaProposal buildSaProposalWithDhGroup() { + private ChildSaProposal buildSaProposalWithDhGroup(int dhGroup) { return new ChildSaProposal.Builder() .addEncryptionAlgorithm( SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, SaProposal.KEY_LEN_AES_128) .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96) - .addDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP) + .addDhGroup(dhGroup) .build(); } + + @Test + public void testRemoteRekeyWithKePayload() throws Exception { + // Use child session params with dh group to initiate the state machine + ChildSaProposal saProposal = buildSaProposalWithDhGroup(SaProposal.DH_GROUP_2048_BIT_MODP); + ChildSessionParams childSessionParams = + new TunnelModeChildSessionParams.Builder() + .addSaProposal(saProposal) + .addInternalAddressRequest(AF_INET) + .addInternalAddressRequest(INTERNAL_ADDRESS) + .build(); + mChildSessionStateMachine = buildChildSession(childSessionParams); + mChildSessionStateMachine.setDbg(true); + mChildSessionStateMachine.start(); + + setupIdleStateMachine(); + + // Setup for new Child SA negotiation. + setUpSpiResource(LOCAL_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_IN); + setUpSpiResource(REMOTE_ADDRESS, REMOTE_INIT_NEW_CHILD_SA_SPI_OUT); + + IkeSaPayload saPayload = + IkeSaPayload.createChildSaRequestPayload( + new ChildSaProposal[] {saProposal}, mIpSecSpiGenerator, LOCAL_ADDRESS); + List<IkePayload> rekeyReqPayloads = + makeInboundRekeyChildPayloads( + REMOTE_INIT_NEW_CHILD_SA_SPI_OUT, saPayload, false /*isLocalInitRekey*/); + + rekeyReqPayloads.add( + new IkeKePayload(IkeSaProposal.DH_GROUP_2048_BIT_MODP, createMockRandomFactory())); + + when(mMockSaRecordHelper.makeChildSaRecord( + eq(rekeyReqPayloads), any(List.class), any(ChildSaRecordConfig.class))) + .thenReturn(mSpyRemoteInitNewChildSaRecord); + + assertEquals(0, mChildSessionStateMachine.mSaProposal.getDhGroups().size()); + + // Receive rekey Child request + mChildSessionStateMachine.receiveRequest( + IKE_EXCHANGE_SUBTYPE_REKEY_CHILD, EXCHANGE_TYPE_CREATE_CHILD_SA, rekeyReqPayloads); + mLooper.dispatchAll(); + + assertTrue( + mChildSessionStateMachine.getCurrentState() + instanceof ChildSessionStateMachine.RekeyChildRemoteDelete); + + verifyOutboundRekeyKePayload(true /*isResp*/); + + assertEquals(1, mChildSessionStateMachine.mSaProposal.getDhGroups().size()); + } } |