aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/android/internal/net/ipsec/ike/SaRecord.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/android/internal/net/ipsec/ike/SaRecord.java')
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/SaRecord.java1098
1 files changed, 1098 insertions, 0 deletions
diff --git a/src/java/com/android/internal/net/ipsec/ike/SaRecord.java b/src/java/com/android/internal/net/ipsec/ike/SaRecord.java
new file mode 100644
index 00000000..035af175
--- /dev/null
+++ b/src/java/com/android/internal/net/ipsec/ike/SaRecord.java
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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.android.internal.net.ipsec.ike;
+
+import static android.net.ipsec.ike.IkeManager.getIkeLog;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.IpSecManager;
+import android.net.IpSecManager.ResourceUnavailableException;
+import android.net.IpSecManager.SecurityParameterIndex;
+import android.net.IpSecManager.SpiUnavailableException;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.IpSecTransform;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.ChildLocalRequest;
+import com.android.internal.net.ipsec.ike.IkeLocalRequestScheduler.LocalRequest;
+import com.android.internal.net.ipsec.ike.IkeSessionStateMachine.IkeSecurityParameterIndex;
+import com.android.internal.net.ipsec.ike.crypto.IkeCipher;
+import com.android.internal.net.ipsec.ike.crypto.IkeMacIntegrity;
+import com.android.internal.net.ipsec.ike.crypto.IkeMacPrf;
+import com.android.internal.net.ipsec.ike.message.IkeKePayload;
+import com.android.internal.net.ipsec.ike.message.IkeMessage;
+import com.android.internal.net.ipsec.ike.message.IkeMessage.DecodeResultPartial;
+import com.android.internal.net.ipsec.ike.message.IkeNoncePayload;
+import com.android.internal.net.ipsec.ike.message.IkePayload;
+
+import dalvik.system.CloseGuard;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * SaRecord represents common information of an IKE SA and a Child SA.
+ *
+ * <p>When doing rekey, there can be multiple SAs in the same IkeSessionStateMachine or
+ * ChildSessionStateMachine, where they use same cryptographic algorithms but with different keys.
+ * We store cryptographic algorithms and unchanged SA configurations in IkeSessionOptions or
+ * ChildSessionOptions and store changed information including keys, SPIs, and nonces in SaRecord.
+ *
+ * <p>All keys are named by the key type plus the source of the traffic this key is protecting. For
+ * example, "mSkAi" represents the integrity key that protects traffic from the SA initiator to the
+ * SA responder.
+ *
+ * <p>Except for keys, all other paramters (SPIs, nonces and messages) are named by the creator. For
+ * example, "initSPI" represents a SPI that is created by the SA initiator.
+ */
+public abstract class SaRecord implements AutoCloseable {
+ private static ISaRecordHelper sSaRecordHelper = new SaRecordHelper();
+ private static IIpSecTransformHelper sIpSecTransformHelper = new IpSecTransformHelper();
+
+ /** Flag indicates if this SA is locally initiated */
+ public final boolean isLocalInit;
+
+ public final byte[] nonceInitiator;
+ public final byte[] nonceResponder;
+
+ private final byte[] mSkAi;
+ private final byte[] mSkAr;
+ private final byte[] mSkEi;
+ private final byte[] mSkEr;
+
+ private final LocalRequest mFutureRekeyEvent;
+
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ /** Package private */
+ SaRecord(
+ boolean localInit,
+ byte[] nonceInit,
+ byte[] nonceResp,
+ byte[] skAi,
+ byte[] skAr,
+ byte[] skEi,
+ byte[] skEr,
+ LocalRequest futureRekeyEvent) {
+ isLocalInit = localInit;
+ nonceInitiator = nonceInit;
+ nonceResponder = nonceResp;
+
+ mSkAi = skAi;
+ mSkAr = skAr;
+ mSkEi = skEi;
+ mSkEr = skEr;
+
+ logKey("SK_ai", skAi);
+ logKey("SK_ar", skAr);
+ logKey("SK_ei", skEi);
+ logKey("SK_er", skEr);
+
+ mFutureRekeyEvent = futureRekeyEvent;
+
+ mCloseGuard.open("close");
+ }
+
+ private void logKey(String type, byte[] key) {
+ getIkeLog().d(getTag(), type + ": " + getIkeLog().pii(key));
+ }
+
+ protected abstract String getTag();
+
+ /**
+ * Get the integrity key for calculate integrity checksum for an outbound packet.
+ *
+ * @return the integrity key in a byte array, which will be empty if integrity algorithm is not
+ * used in this SA.
+ */
+ public byte[] getOutboundIntegrityKey() {
+ return isLocalInit ? mSkAi : mSkAr;
+ }
+
+ /**
+ * Get the integrity key to authenticate an inbound packet.
+ *
+ * @return the integrity key in a byte array, which will be empty if integrity algorithm is not
+ * used in this SA.
+ */
+ public byte[] getInboundIntegrityKey() {
+ return isLocalInit ? mSkAr : mSkAi;
+ }
+
+ /**
+ * Get the encryption key for protecting an outbound packet.
+ *
+ * @return the encryption key in a byte array.
+ */
+ public byte[] getOutboundEncryptionKey() {
+ return isLocalInit ? mSkEi : mSkEr;
+ }
+
+ /**
+ * Get the decryption key for an inbound packet.
+ *
+ * @return the decryption key in a byte array.
+ */
+ public byte[] getInboundDecryptionKey() {
+ return isLocalInit ? mSkEr : mSkEi;
+ }
+
+ /** Check that the SaRecord was closed properly. */
+ @Override
+ protected void finalize() throws Throwable {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+
+ @Override
+ public void close() {
+ mFutureRekeyEvent.cancel();
+ }
+
+ /** Package private */
+ LocalRequest getFutureRekeyEvent() {
+ return mFutureRekeyEvent;
+ }
+
+ /** Package private */
+ @VisibleForTesting
+ static void setSaRecordHelper(ISaRecordHelper helper) {
+ sSaRecordHelper = helper;
+ }
+
+ /** Package private */
+ @VisibleForTesting
+ static void setIpSecTransformHelper(IIpSecTransformHelper helper) {
+ sIpSecTransformHelper = helper;
+ }
+
+ /**
+ * SaRecordHelper implements methods for constructing SaRecord.
+ *
+ * <p>Package private
+ */
+ static class SaRecordHelper implements ISaRecordHelper {
+ @Override
+ public IkeSaRecord makeFirstIkeSaRecord(
+ IkeMessage initRequest,
+ IkeMessage initResponse,
+ IkeSaRecordConfig ikeSaRecordConfig)
+ throws GeneralSecurityException {
+ // Extract nonces
+ byte[] nonceInit =
+ initRequest.getPayloadForType(
+ IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class)
+ .nonceData;
+ byte[] nonceResp =
+ initResponse.getPayloadForType(
+ IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class)
+ .nonceData;
+
+ // Get SKEYSEED
+ byte[] sharedDhKey = getSharedKey(initRequest, initResponse);
+ byte[] sKeySeed =
+ ikeSaRecordConfig.prf.generateSKeySeed(nonceInit, nonceResp, sharedDhKey);
+
+ return makeIkeSaRecord(sKeySeed, nonceInit, nonceResp, ikeSaRecordConfig);
+ }
+
+ @Override
+ public IkeSaRecord makeRekeyedIkeSaRecord(
+ IkeSaRecord oldSaRecord,
+ IkeMacPrf oldPrf,
+ IkeMessage rekeyRequest,
+ IkeMessage rekeyResponse,
+ IkeSaRecordConfig ikeSaRecordConfig)
+ throws GeneralSecurityException {
+ // Extract nonces
+ byte[] nonceInit =
+ rekeyRequest.getPayloadForType(
+ IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class)
+ .nonceData;
+ byte[] nonceResp =
+ rekeyResponse.getPayloadForType(
+ IkePayload.PAYLOAD_TYPE_NONCE, IkeNoncePayload.class)
+ .nonceData;
+
+ // Get SKEYSEED
+ IkeMessage localMsg = ikeSaRecordConfig.isLocalInit ? rekeyRequest : rekeyResponse;
+ IkeMessage remoteMsg = ikeSaRecordConfig.isLocalInit ? rekeyResponse : rekeyRequest;
+
+ byte[] sharedDhKey = getSharedKey(localMsg, remoteMsg);
+ byte[] sKeySeed =
+ oldPrf.generateRekeyedSKeySeed(
+ oldSaRecord.mSkD, nonceInit, nonceResp, sharedDhKey);
+
+ return makeIkeSaRecord(sKeySeed, nonceInit, nonceResp, ikeSaRecordConfig);
+ }
+
+ private byte[] getSharedKey(IkeMessage keLocalMessage, IkeMessage keRemoteMessage)
+ throws GeneralSecurityException {
+ IkeKePayload keLocalPayload =
+ keLocalMessage.getPayloadForType(
+ IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class);
+ IkeKePayload keRemotePayload =
+ keRemoteMessage.getPayloadForType(
+ IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class);
+
+ return IkeKePayload.getSharedKey(
+ keLocalPayload.localPrivateKey, keRemotePayload.keyExchangeData);
+ }
+
+ /**
+ * Package private method for calculating keys and construct IkeSaRecord.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.13">RFC 7296, Internet Key
+ * Exchange Protocol Version 2 (IKEv2), Generating Keying Material</a>
+ */
+ @VisibleForTesting
+ IkeSaRecord makeIkeSaRecord(
+ byte[] sKeySeed,
+ byte[] nonceInit,
+ byte[] nonceResp,
+ IkeSaRecordConfig ikeSaRecordConfig) {
+ // Build data to sign for generating the keying material.
+ ByteBuffer bufferToSign =
+ ByteBuffer.allocate(
+ nonceInit.length + nonceResp.length + 2 * IkePayload.SPI_LEN_IKE);
+
+ IkeSecurityParameterIndex initSpi = ikeSaRecordConfig.initSpi;
+ IkeSecurityParameterIndex respSpi = ikeSaRecordConfig.respSpi;
+ IkeMacPrf prf = ikeSaRecordConfig.prf;
+ int integrityKeyLength = ikeSaRecordConfig.integrityKeyLength;
+ int encryptionKeyLength = ikeSaRecordConfig.encryptionKeyLength;
+
+ bufferToSign
+ .put(nonceInit)
+ .put(nonceResp)
+ .putLong(initSpi.getSpi())
+ .putLong(respSpi.getSpi());
+
+ // Get length of the keying material according to RFC 7296, 2.13 and 2.14. The length of
+ // SK_D is always equal to the length of PRF key.
+ int skDLength = prf.getKeyLength();
+ int keyMaterialLen =
+ skDLength
+ + 2 * integrityKeyLength
+ + 2 * encryptionKeyLength
+ + 2 * prf.getKeyLength();
+ byte[] keyMat = prf.generateKeyMat(sKeySeed, bufferToSign.array(), keyMaterialLen);
+
+ // Extract keys.
+ byte[] skD = new byte[skDLength];
+ byte[] skAi = new byte[integrityKeyLength];
+ byte[] skAr = new byte[integrityKeyLength];
+ byte[] skEi = new byte[encryptionKeyLength];
+ byte[] skEr = new byte[encryptionKeyLength];
+ byte[] skPi = new byte[prf.getKeyLength()];
+ byte[] skPr = new byte[prf.getKeyLength()];
+
+ ByteBuffer keyMatBuffer = ByteBuffer.wrap(keyMat);
+ keyMatBuffer.get(skD).get(skAi).get(skAr).get(skEi).get(skEr).get(skPi).get(skPr);
+ return new IkeSaRecord(
+ initSpi,
+ respSpi,
+ ikeSaRecordConfig.isLocalInit,
+ nonceInit,
+ nonceResp,
+ skD,
+ skAi,
+ skAr,
+ skEi,
+ skEr,
+ skPi,
+ skPr,
+ ikeSaRecordConfig.futureRekeyEvent);
+ }
+
+ @Override
+ public ChildSaRecord makeChildSaRecord(
+ List<IkePayload> reqPayloads,
+ List<IkePayload> respPayloads,
+ ChildSaRecordConfig childSaRecordConfig)
+ throws GeneralSecurityException, ResourceUnavailableException,
+ SpiUnavailableException, IOException {
+ // Extract nonces. Encoding/Decoding of payload list guarantees that there is only one
+ // nonce payload in the reqPayloads and respPayloads lists
+ byte[] nonceInit =
+ IkePayload.getPayloadForTypeInProvidedList(
+ IkePayload.PAYLOAD_TYPE_NONCE,
+ IkeNoncePayload.class,
+ reqPayloads)
+ .nonceData;
+ byte[] nonceResp =
+ IkePayload.getPayloadForTypeInProvidedList(
+ IkePayload.PAYLOAD_TYPE_NONCE,
+ IkeNoncePayload.class,
+ respPayloads)
+ .nonceData;
+
+ // Check if KE Payload exists and get DH shared key. Encoding/Decoding of payload list
+ // guarantees that there is either no KE payload in the reqPayloads and respPayloads
+ // lists, or only one KE payload in each list.
+ byte[] sharedDhKey = new byte[0];
+ IkeKePayload keInitPayload =
+ IkePayload.getPayloadForTypeInProvidedList(
+ IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class, reqPayloads);
+ if (keInitPayload != null) {
+ IkeKePayload keRespPayload =
+ IkePayload.getPayloadForTypeInProvidedList(
+ IkePayload.PAYLOAD_TYPE_KE, IkeKePayload.class, respPayloads);
+ sharedDhKey =
+ IkeKePayload.getSharedKey(
+ keInitPayload.localPrivateKey, keRespPayload.keyExchangeData);
+ }
+
+ return makeChildSaRecord(sharedDhKey, nonceInit, nonceResp, childSaRecordConfig);
+ }
+ /**
+ * Package private method for calculating keys, build IpSecTransforms and construct
+ * ChildSaRecord.
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.17">RFC 7296, Internet Key
+ * Exchange Protocol Version 2 (IKEv2), Generating Keying Material for Child SAs</a>
+ */
+ @VisibleForTesting
+ ChildSaRecord makeChildSaRecord(
+ byte[] sharedKey,
+ byte[] nonceInit,
+ byte[] nonceResp,
+ ChildSaRecordConfig childSaRecordConfig)
+ throws ResourceUnavailableException, SpiUnavailableException, IOException {
+ // Build data to sign for generating the keying material.
+ ByteBuffer bufferToSign =
+ ByteBuffer.allocate(sharedKey.length + nonceInit.length + nonceResp.length);
+ bufferToSign.put(sharedKey).put(nonceInit).put(nonceResp);
+
+ // Get length of the keying material according to RFC 7296, 2.17.
+ int encryptionKeyLength = childSaRecordConfig.encryptionAlgo.getKeyLength();
+ int integrityKeyLength =
+ childSaRecordConfig.hasIntegrityAlgo
+ ? childSaRecordConfig.integrityAlgo.getKeyLength()
+ : 0;
+ int keyMaterialLen = 2 * encryptionKeyLength + 2 * integrityKeyLength;
+ byte[] keyMat =
+ childSaRecordConfig.ikePrf.generateKeyMat(
+ childSaRecordConfig.skD, bufferToSign.array(), keyMaterialLen);
+
+ // Extract keys according to the order that keys carrying data from initiator to
+ // responder are taken before keys for the other direction and encryption keys are taken
+ // before integrity keys.
+ byte[] skEi = new byte[encryptionKeyLength];
+ byte[] skAi = new byte[integrityKeyLength];
+ byte[] skEr = new byte[encryptionKeyLength];
+ byte[] skAr = new byte[integrityKeyLength];
+
+ ByteBuffer keyMatBuffer = ByteBuffer.wrap(keyMat);
+ keyMatBuffer.get(skEi).get(skAi).get(skEr).get(skAr);
+
+ // IpSecTransform for traffic from the initiator
+ IpSecTransform initTransform = null;
+ // IpSecTransform for traffic from the responder
+ IpSecTransform respTransform = null;
+ try {
+ // Build IpSecTransform
+ initTransform =
+ sIpSecTransformHelper.makeIpSecTransform(
+ childSaRecordConfig.context,
+ childSaRecordConfig.initAddress /*source address*/,
+ childSaRecordConfig.udpEncapSocket,
+ childSaRecordConfig.respSpi /*destination SPI*/,
+ childSaRecordConfig.integrityAlgo,
+ childSaRecordConfig.encryptionAlgo,
+ skAi,
+ skEi,
+ childSaRecordConfig.isTransport);
+ respTransform =
+ sIpSecTransformHelper.makeIpSecTransform(
+ childSaRecordConfig.context,
+ childSaRecordConfig.respAddress /*source address*/,
+ childSaRecordConfig.udpEncapSocket,
+ childSaRecordConfig.initSpi /*destination SPI*/,
+ childSaRecordConfig.integrityAlgo,
+ childSaRecordConfig.encryptionAlgo,
+ skAr,
+ skEr,
+ childSaRecordConfig.isTransport);
+
+ int initSpi = childSaRecordConfig.initSpi.getSpi();
+ int respSpi = childSaRecordConfig.respSpi.getSpi();
+
+ boolean isLocalInit = childSaRecordConfig.isLocalInit;
+ int inSpi = isLocalInit ? initSpi : respSpi;
+ int outSpi = isLocalInit ? respSpi : initSpi;
+ IpSecTransform inTransform = isLocalInit ? respTransform : initTransform;
+ IpSecTransform outTransform = isLocalInit ? initTransform : respTransform;
+
+ return new ChildSaRecord(
+ inSpi,
+ outSpi,
+ isLocalInit,
+ nonceInit,
+ nonceResp,
+ skAi,
+ skAr,
+ skEi,
+ skEr,
+ inTransform,
+ outTransform,
+ childSaRecordConfig.futureRekeyEvent);
+
+ } catch (Exception e) {
+ if (initTransform != null) initTransform.close();
+ if (respTransform != null) respTransform.close();
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * IpSecTransformHelper implements the IIpSecTransformHelper interface for constructing {@link
+ * IpSecTransform}}.
+ *
+ * <p>Package private
+ */
+ static class IpSecTransformHelper implements IIpSecTransformHelper {
+ private static final String TAG = "IpSecTransformHelper";
+
+ @Override
+ public IpSecTransform makeIpSecTransform(
+ Context context,
+ InetAddress sourceAddress,
+ UdpEncapsulationSocket udpEncapSocket,
+ IpSecManager.SecurityParameterIndex spi,
+ @Nullable IkeMacIntegrity integrityAlgo,
+ IkeCipher encryptionAlgo,
+ byte[] integrityKey,
+ byte[] encryptionKey,
+ boolean isTransport)
+ throws ResourceUnavailableException, SpiUnavailableException, IOException {
+ IpSecTransform.Builder builder = new IpSecTransform.Builder(context);
+
+ if (encryptionAlgo.isAead()) {
+ builder.setAuthenticatedEncryption(
+ encryptionAlgo.buildIpSecAlgorithmWithKey(encryptionKey));
+ } else {
+ builder.setEncryption(encryptionAlgo.buildIpSecAlgorithmWithKey(encryptionKey));
+ builder.setAuthentication(integrityAlgo.buildIpSecAlgorithmWithKey(integrityKey));
+ }
+
+ if (udpEncapSocket != null && sourceAddress instanceof Inet6Address) {
+ getIkeLog().wtf(TAG, "Kernel does not support UDP encapsulation for IPv6 SAs");
+ }
+ if (udpEncapSocket != null && sourceAddress instanceof Inet4Address) {
+ builder.setIpv4Encapsulation(udpEncapSocket, IkeSocket.IKE_SERVER_PORT);
+ }
+
+ if (isTransport) {
+ return builder.buildTransportModeTransform(sourceAddress, spi);
+ } else {
+ return builder.buildTunnelModeTransform(sourceAddress, spi);
+ }
+ }
+ }
+
+ /** Package private class to group parameters for building a ChildSaRecord. */
+ @VisibleForTesting
+ static final class ChildSaRecordConfig {
+ public final Context context;
+ public final SecurityParameterIndex initSpi;
+ public final SecurityParameterIndex respSpi;
+ public final InetAddress initAddress;
+ public final InetAddress respAddress;
+ @Nullable public final UdpEncapsulationSocket udpEncapSocket;
+ public final IkeMacPrf ikePrf;
+ @Nullable public final IkeMacIntegrity integrityAlgo;
+ public final IkeCipher encryptionAlgo;
+ public final byte[] skD;
+ public final boolean isTransport;
+ public final boolean isLocalInit;
+ public final boolean hasIntegrityAlgo;
+ public final ChildLocalRequest futureRekeyEvent;
+
+ ChildSaRecordConfig(
+ Context context,
+ SecurityParameterIndex initSpi,
+ SecurityParameterIndex respSpi,
+ InetAddress localAddress,
+ InetAddress remoteAddress,
+ @Nullable UdpEncapsulationSocket udpEncapSocket,
+ IkeMacPrf ikePrf,
+ @Nullable IkeMacIntegrity integrityAlgo,
+ IkeCipher encryptionAlgo,
+ byte[] skD,
+ boolean isTransport,
+ boolean isLocalInit,
+ ChildLocalRequest futureRekeyEvent) {
+ this.context = context;
+ this.initSpi = initSpi;
+ this.respSpi = respSpi;
+ this.initAddress = isLocalInit ? localAddress : remoteAddress;
+ this.respAddress = isLocalInit ? remoteAddress : localAddress;
+ this.udpEncapSocket = udpEncapSocket;
+ this.ikePrf = ikePrf;
+ this.integrityAlgo = integrityAlgo;
+ this.encryptionAlgo = encryptionAlgo;
+ this.skD = skD;
+ this.isTransport = isTransport;
+ this.isLocalInit = isLocalInit;
+ hasIntegrityAlgo = (integrityAlgo != null);
+ this.futureRekeyEvent = futureRekeyEvent;
+ }
+ }
+
+ /** IkeSaRecord represents an IKE SA. */
+ public static class IkeSaRecord extends SaRecord implements Comparable<IkeSaRecord> {
+ private static final String TAG = "IkeSaRecord";
+
+ /** SPI of IKE SA initiator */
+ private final IkeSecurityParameterIndex mInitiatorSpiResource;
+ /** SPI of IKE SA responder */
+ private final IkeSecurityParameterIndex mResponderSpiResource;
+
+ private final byte[] mSkD;
+ private final byte[] mSkPi;
+ private final byte[] mSkPr;
+
+ private int mLocalRequestMessageId;
+ private int mRemoteRequestMessageId;
+
+ private DecodeResultPartial mCollectedReqFragments;
+ private DecodeResultPartial mCollectedRespFragments;
+
+ private byte[] mLastRecivedReqFirstPacket;
+ private List<byte[]> mLastSentRespAllPackets;
+
+ /** Package private */
+ IkeSaRecord(
+ IkeSecurityParameterIndex initSpi,
+ IkeSecurityParameterIndex respSpi,
+ boolean localInit,
+ byte[] nonceInit,
+ byte[] nonceResp,
+ byte[] skD,
+ byte[] skAi,
+ byte[] skAr,
+ byte[] skEi,
+ byte[] skEr,
+ byte[] skPi,
+ byte[] skPr,
+ LocalRequest futureRekeyEvent) {
+ super(localInit, nonceInit, nonceResp, skAi, skAr, skEi, skEr, futureRekeyEvent);
+
+ mInitiatorSpiResource = initSpi;
+ mResponderSpiResource = respSpi;
+
+ mSkD = skD;
+ mSkPi = skPi;
+ mSkPr = skPr;
+
+ mLocalRequestMessageId = 0;
+ mRemoteRequestMessageId = 0;
+
+ mCollectedReqFragments = null;
+ mCollectedRespFragments = null;
+
+ logKey("SK_d", skD);
+ logKey("SK_pi", skPi);
+ logKey("SK_pr", skPr);
+ }
+
+ /**
+ * Package private interface for IkeSessionStateMachien to construct an IkeSaRecord
+ * instance.
+ */
+ static IkeSaRecord makeFirstIkeSaRecord(
+ IkeMessage initRequest,
+ IkeMessage initResponse,
+ IkeSecurityParameterIndex initSpi,
+ IkeSecurityParameterIndex respSpi,
+ IkeMacPrf prf,
+ int integrityKeyLength,
+ int encryptionKeyLength,
+ LocalRequest futureRekeyEvent)
+ throws GeneralSecurityException {
+ return sSaRecordHelper.makeFirstIkeSaRecord(
+ initRequest,
+ initResponse,
+ new IkeSaRecordConfig(
+ initSpi,
+ respSpi,
+ prf,
+ integrityKeyLength,
+ encryptionKeyLength,
+ true /*isLocalInit*/,
+ futureRekeyEvent));
+ }
+
+ /** Package private */
+ static IkeSaRecord makeRekeyedIkeSaRecord(
+ IkeSaRecord oldSaRecord,
+ IkeMacPrf oldPrf,
+ IkeMessage rekeyRequest,
+ IkeMessage rekeyResponse,
+ IkeSecurityParameterIndex initSpi,
+ IkeSecurityParameterIndex respSpi,
+ IkeMacPrf prf,
+ int integrityKeyLength,
+ int encryptionKeyLength,
+ boolean isLocalInit,
+ LocalRequest futureRekeyEvent)
+ throws GeneralSecurityException {
+ return sSaRecordHelper.makeRekeyedIkeSaRecord(
+ oldSaRecord,
+ oldPrf,
+ rekeyRequest,
+ rekeyResponse,
+ new IkeSaRecordConfig(
+ initSpi,
+ respSpi,
+ prf,
+ integrityKeyLength,
+ encryptionKeyLength,
+ isLocalInit,
+ futureRekeyEvent));
+ }
+
+ private void logKey(String type, byte[] key) {
+ getIkeLog().d(TAG, type + ": " + getIkeLog().pii(key));
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+
+ /** Package private */
+ long getInitiatorSpi() {
+ return mInitiatorSpiResource.getSpi();
+ }
+
+ /** Package private */
+ long getResponderSpi() {
+ return mResponderSpiResource.getSpi();
+ }
+
+ /** Package private */
+ long getLocalSpi() {
+ return isLocalInit ? mInitiatorSpiResource.getSpi() : mResponderSpiResource.getSpi();
+ }
+
+ /** Package private */
+ long getRemoteSpi() {
+ return isLocalInit ? mResponderSpiResource.getSpi() : mInitiatorSpiResource.getSpi();
+ }
+
+ /** Package private */
+ byte[] getSkD() {
+ return mSkD;
+ }
+
+ /**
+ * Get the PRF key of IKE initiator for building an outbound Auth Payload.
+ *
+ * @return the PRF key in a byte array.
+ */
+ public byte[] getSkPi() {
+ return mSkPi;
+ }
+
+ /**
+ * Get the PRF key of IKE responder for validating an inbound Auth Payload.
+ *
+ * @return the PRF key in a byte array.
+ */
+ public byte[] getSkPr() {
+ return mSkPr;
+ }
+
+ /**
+ * Compare with a specific IkeSaRecord
+ *
+ * @param record IkeSaRecord to be compared.
+ * @return a negative integer if input IkeSaRecord contains lowest nonce; a positive integer
+ * if this IkeSaRecord has lowest nonce; return zero if lowest nonces of two
+ * IkeSaRecords match.
+ */
+ public int compareTo(IkeSaRecord record) {
+ // TODO: Implement it b/122924815.
+ return 1;
+ }
+
+ /**
+ * Get current message ID for the local requesting window.
+ *
+ * <p>Called for building an outbound request or for validating the message ID of an inbound
+ * response.
+ *
+ * @return the local request message ID.
+ */
+ public int getLocalRequestMessageId() {
+ return mLocalRequestMessageId;
+ }
+
+ /**
+ * Get current message ID for the remote requesting window.
+ *
+ * <p>Called for validating the message ID of an inbound request. If the message ID of the
+ * inbound request is smaller than the current remote message ID by one, it means the
+ * message is a retransmitted request.
+ *
+ * @return the remote request message ID
+ */
+ public int getRemoteRequestMessageId() {
+ return mRemoteRequestMessageId;
+ }
+
+ /**
+ * Increment the local request message ID by one.
+ *
+ * <p>It should be called when IKE library has received an authenticated and protected
+ * response with the correct local request message ID.
+ */
+ public void incrementLocalRequestMessageId() {
+ mLocalRequestMessageId++;
+ }
+
+ /**
+ * Increment the remote request message ID by one.
+ *
+ * <p>It should be called when IKE library has received an authenticated and protected
+ * request with the correct remote request message ID.
+ */
+ public void incrementRemoteRequestMessageId() {
+ mRemoteRequestMessageId++;
+ }
+
+ /** Return all collected IKE fragments that have been collected. */
+ public DecodeResultPartial getCollectedFragments(boolean isResp) {
+ return isResp ? mCollectedRespFragments : mCollectedReqFragments;
+ }
+
+ /**
+ * Update collected IKE fragments when receiving new IKE fragment.
+ *
+ * <p>TODO: b/140264067 Investigate if we need to support reassembling timeout. It is safe
+ * to do not support it because as an initiator, we will re-transmit the request anyway. As
+ * a responder, caching these fragments until getting a complete message won't affect
+ * anything.
+ */
+ public void updateCollectedFragments(
+ DecodeResultPartial updatedFragments, boolean isResp) {
+ if (isResp) {
+ mCollectedRespFragments = updatedFragments;
+ } else {
+ mCollectedReqFragments = updatedFragments;
+ }
+ }
+
+ /** Reset collected IKE fragemnts */
+ public void resetCollectedFragments(boolean isResp) {
+ updateCollectedFragments(null, isResp);
+ }
+
+ /** Update first packet of last received request. */
+ public void updateLastReceivedReqFirstPacket(byte[] reqPacket) {
+ mLastRecivedReqFirstPacket = reqPacket;
+ }
+
+ /** Update all packets of last sent response. */
+ public void updateLastSentRespAllPackets(List<byte[]> respPacketList) {
+ mLastSentRespAllPackets = respPacketList;
+ }
+
+ /** Returns if received IKE packet is the first packet of a re-transmistted request. */
+ public boolean isRetransmittedRequest(byte[] request) {
+ return Arrays.equals(mLastRecivedReqFirstPacket, request);
+ }
+
+ /** Get all encoded packets of last sent response. */
+ public List<byte[]> getLastSentRespAllPackets() {
+ return mLastSentRespAllPackets;
+ }
+
+ /** Release IKE SPI resource. */
+ @Override
+ public void close() {
+ super.close();
+ mInitiatorSpiResource.close();
+ mResponderSpiResource.close();
+ }
+ }
+
+ /** Package private class that groups parameters to construct an IkeSaRecord instance. */
+ @VisibleForTesting
+ static class IkeSaRecordConfig {
+ public final IkeSecurityParameterIndex initSpi;
+ public final IkeSecurityParameterIndex respSpi;
+ public final IkeMacPrf prf;
+ public final int integrityKeyLength;
+ public final int encryptionKeyLength;
+ public final boolean isLocalInit;
+ public final LocalRequest futureRekeyEvent;
+
+ IkeSaRecordConfig(
+ IkeSecurityParameterIndex initSpi,
+ IkeSecurityParameterIndex respSpi,
+ IkeMacPrf prf,
+ int integrityKeyLength,
+ int encryptionKeyLength,
+ boolean isLocalInit,
+ LocalRequest futureRekeyEvent) {
+ this.initSpi = initSpi;
+ this.respSpi = respSpi;
+ this.prf = prf;
+ this.integrityKeyLength = integrityKeyLength;
+ this.encryptionKeyLength = encryptionKeyLength;
+ this.isLocalInit = isLocalInit;
+ this.futureRekeyEvent = futureRekeyEvent;
+ }
+ }
+
+ /** ChildSaRecord represents an Child SA. */
+ public static class ChildSaRecord extends SaRecord implements Comparable<ChildSaRecord> {
+ private static final String TAG = "ChildSaRecord";
+
+ /** Locally generated SPI for receiving IPsec Packet. */
+ private final int mInboundSpi;
+ /** Remotely generated SPI for sending IPsec Packet. */
+ private final int mOutboundSpi;
+
+ /** IPsec Transform applied to traffic towards the host. */
+ private final IpSecTransform mInboundTransform;
+ /** IPsec Transform applied to traffic from the host. */
+ private final IpSecTransform mOutboundTransform;
+
+ /** Package private */
+ ChildSaRecord(
+ int inSpi,
+ int outSpi,
+ boolean localInit,
+ byte[] nonceInit,
+ byte[] nonceResp,
+ byte[] skAi,
+ byte[] skAr,
+ byte[] skEi,
+ byte[] skEr,
+ IpSecTransform inTransform,
+ IpSecTransform outTransform,
+ ChildLocalRequest futureRekeyEvent) {
+ super(localInit, nonceInit, nonceResp, skAi, skAr, skEi, skEr, futureRekeyEvent);
+
+ mInboundSpi = inSpi;
+ mOutboundSpi = outSpi;
+ mInboundTransform = inTransform;
+ mOutboundTransform = outTransform;
+ }
+
+ /**
+ * Package private interface for ChildSessionStateMachine to construct a ChildSaRecord
+ * instance.
+ */
+ static ChildSaRecord makeChildSaRecord(
+ Context context,
+ List<IkePayload> reqPayloads,
+ List<IkePayload> respPayloads,
+ SecurityParameterIndex initSpi,
+ SecurityParameterIndex respSpi,
+ InetAddress localAddress,
+ InetAddress remoteAddress,
+ @Nullable UdpEncapsulationSocket udpEncapSocket,
+ IkeMacPrf prf,
+ @Nullable IkeMacIntegrity integrityAlgo,
+ IkeCipher encryptionAlgo,
+ byte[] skD,
+ boolean isTransport,
+ boolean isLocalInit,
+ ChildLocalRequest futureRekeyEvent)
+ throws GeneralSecurityException, ResourceUnavailableException,
+ SpiUnavailableException, IOException {
+ return sSaRecordHelper.makeChildSaRecord(
+ reqPayloads,
+ respPayloads,
+ new ChildSaRecordConfig(
+ context,
+ initSpi,
+ respSpi,
+ localAddress,
+ remoteAddress,
+ udpEncapSocket,
+ prf,
+ integrityAlgo,
+ encryptionAlgo,
+ skD,
+ isTransport,
+ isLocalInit,
+ futureRekeyEvent));
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+
+ /** Package private */
+ int getLocalSpi() {
+ return mInboundSpi;
+ }
+
+ /** Package private */
+ int getRemoteSpi() {
+ return mOutboundSpi;
+ }
+
+ /** Package private */
+ IpSecTransform getInboundIpSecTransform() {
+ return mInboundTransform;
+ }
+
+ /** Package private */
+ IpSecTransform getOutboundIpSecTransform() {
+ return mOutboundTransform;
+ }
+
+ /**
+ * Compare with a specific ChildSaRecord
+ *
+ * @param record ChildSaRecord to be compared.
+ * @return a negative integer if input ChildSaRecord contains lowest nonce; a positive
+ * integer if this ChildSaRecord has lowest nonce; return zero if lowest nonces of two
+ * ChildSaRecord match.
+ */
+ public int compareTo(ChildSaRecord record) {
+ // TODO: Implement it b/122924815
+ return 1;
+ }
+
+ /** Release IpSecTransform pair. */
+ @Override
+ public void close() {
+ super.close();
+ mInboundTransform.close();
+ mOutboundTransform.close();
+ }
+ }
+
+ /**
+ * ISaRecordHelper provides a package private interface for constructing SaRecord.
+ *
+ * <p>ISaRecordHelper exists so that the interface is injectable for testing.
+ */
+ interface ISaRecordHelper {
+ /**
+ * Construct IkeSaRecord as results of IKE initial exchange.
+ *
+ * @param initRequest IKE_INIT request.
+ * @param initResponse IKE_INIT request.
+ * @param ikeSaRecordConfig that contains IKE SPI resources and negotiated algorithm
+ * information for constructing an IkeSaRecord instance.
+ * @return ikeSaRecord for initial IKE SA.
+ * @throws GeneralSecurityException if the DH public key in the response is invalid.
+ */
+ IkeSaRecord makeFirstIkeSaRecord(
+ IkeMessage initRequest,
+ IkeMessage initResponse,
+ IkeSaRecordConfig ikeSaRecordConfig)
+ throws GeneralSecurityException;
+
+ /**
+ * Construct new IkeSaRecord when doing rekey.
+ *
+ * @param oldSaRecord old IKE SA
+ * @param oldPrf the PRF function from the old SA
+ * @param rekeyRequest Rekey IKE request.
+ * @param rekeyResponse Rekey IKE response.
+ * @param ikeSaRecordConfig that contains IKE SPI resources and negotiated algorithm
+ * information for constructing an IkeSaRecord instance.
+ * @return ikeSaRecord for new IKE SA.
+ */
+ IkeSaRecord makeRekeyedIkeSaRecord(
+ IkeSaRecord oldSaRecord,
+ IkeMacPrf oldPrf,
+ IkeMessage rekeyRequest,
+ IkeMessage rekeyResponse,
+ IkeSaRecordConfig ikeSaRecordConfig)
+ throws GeneralSecurityException;
+
+ /**
+ * Construct ChildSaRecord and generate IpSecTransform pairs.
+ *
+ * @param reqPayloads payload list in request.
+ * @param respPayloads payload list in response.
+ * @param childSaRecordConfig the grouped parameters for constructing ChildSaRecord.
+ * @return new Child SA.
+ */
+ ChildSaRecord makeChildSaRecord(
+ List<IkePayload> reqPayloads,
+ List<IkePayload> respPayloads,
+ ChildSaRecordConfig childSaRecordConfig)
+ throws GeneralSecurityException, ResourceUnavailableException,
+ SpiUnavailableException, IOException;
+ }
+
+ /**
+ * IIpSecTransformHelper provides a package private interface to construct {@link
+ * IpSecTransform}
+ *
+ * <p>IIpSecTransformHelper exists so that the interface is injectable for testing.
+ */
+ @VisibleForTesting
+ interface IIpSecTransformHelper {
+ /**
+ * Construct an instance of {@link IpSecTransform}
+ *
+ * @param context current context
+ * @param sourceAddress the source {@code InetAddress} of traffic on sockets of interfaces
+ * that will use this transform
+ * @param udpEncapSocket the UDP-Encap socket that allows IpSec traffic to pass through a
+ * NAT. Null if no NAT exists.
+ * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+ * traffic
+ * @param integrityAlgo specifying the authentication algorithm to be applied.
+ * @param encryptionAlgo specifying the encryption algorithm or authenticated encryption
+ * algorithm to be applied.
+ * @param integrityKey the negotiated authentication key to be applied.
+ * @param encryptionKey the negotiated encryption key to be applied.
+ * @param isTransport the flag indicates if a transport or a tunnel mode transform will be
+ * built.
+ * @return an instance of {@link IpSecTransform}
+ * @throws ResourceUnavailableException indicating that too many transforms are active
+ * @throws SpiUnavailableException indicating the rare case where an SPI collides with an
+ * existing transform
+ * @throws IOException indicating other errors
+ */
+ IpSecTransform makeIpSecTransform(
+ Context context,
+ InetAddress sourceAddress,
+ UdpEncapsulationSocket udpEncapSocket,
+ IpSecManager.SecurityParameterIndex spi,
+ @Nullable IkeMacIntegrity integrityAlgo,
+ IkeCipher encryptionAlgo,
+ byte[] integrityKey,
+ byte[] encryptionKey,
+ boolean isTransport)
+ throws ResourceUnavailableException, SpiUnavailableException, IOException;
+ }
+}