diff options
Diffstat (limited to 'src/java/com/android/ike/ikev2/SaRecord.java')
-rw-r--r-- | src/java/com/android/ike/ikev2/SaRecord.java | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/java/com/android/ike/ikev2/SaRecord.java b/src/java/com/android/ike/ikev2/SaRecord.java new file mode 100644 index 00000000..4dcddec4 --- /dev/null +++ b/src/java/com/android/ike/ikev2/SaRecord.java @@ -0,0 +1,276 @@ +/* + * 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.ike.ikev2; + +import com.android.ike.ikev2.message.IkeMessage; +import com.android.ike.ikev2.message.IkePayload; +import com.android.internal.annotations.VisibleForTesting; + +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +/** + * 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. + */ +public abstract class SaRecord { + + private static ISaRecordHelper sSaRecordHelper = new SaRecordHelper(); + + public final byte[] nonceInitiator; + public final byte[] nonceResponder; + + /** Package private */ + SaRecord(byte[] nonceInit, byte[] nonceResp) { + nonceInitiator = nonceInit; + nonceResponder = nonceResp; + } + + /** + * SaRecordHelper implements methods for constructing SaRecord. + * + * <p>Package private + */ + static class SaRecordHelper implements ISaRecordHelper { + @Override + public IkeSaRecord makeFirstIkeSaRecord(IkeMessage initRequest, IkeMessage initResponse) { + // TODO: Generate keying materials + return null; + } + + @Override + public IkeSaRecord makeNewIkeSaRecord( + IkeSaRecord oldSaRecord, IkeMessage rekeyRequest, IkeMessage rekeyResponse) { + // TODO: Generate keying materials based on old SK_d + return null; + } + + @Override + public ChildSaRecord makeChildSaRecord( + List<IkePayload> reqPayloads, List<IkePayload> respPayloads) { + // TODO: Calculate keys and build IpSecTransform. + return null; + } + } + + /** Package private */ + static void setSaRecordHelper(ISaRecordHelper helper) { + sSaRecordHelper = helper; + } + + /** IkeSaRecord represents an IKE SA. */ + public static class IkeSaRecord extends SaRecord implements Comparable<IkeSaRecord> { + + /** SPI of IKE SA initiator */ + public final long initiatorSpi; + /** SPI of IKE SA responder */ + public final long responderSpi; + /** Flag indicates if this IKE SA is locally initiated */ + public final boolean isLocalInit; + + /** Package private */ + IkeSaRecord( + long initSpi, long respSpi, boolean localInit, byte[] nonceInit, byte[] nonceResp) { + super(nonceInit, nonceResp); + initiatorSpi = initSpi; + responderSpi = respSpi; + isLocalInit = localInit; + // TODO: Impement constructor. There will be more input parameters. + } + + /** Package private */ + static IkeSaRecord makeFirstIkeSaRecord(IkeMessage initRequest, IkeMessage initResponse) { + return sSaRecordHelper.makeFirstIkeSaRecord(initRequest, initResponse); + } + + /** Package private */ + static IkeSaRecord makeNewIkeSaRecord( + IkeSaRecord oldSaRecord, IkeMessage rekeyRequest, IkeMessage rekeyResponse) { + return sSaRecordHelper.makeNewIkeSaRecord(oldSaRecord, rekeyRequest, rekeyResponse); + } + + /** Package private */ + long getRemoteSpi() { + if (isLocalInit) { + return responderSpi; + } else { + return initiatorSpi; + } + } + + /** + * 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; + } + } + + /** ChildSaRecord represents an Child SA. */ + public static class ChildSaRecord extends SaRecord implements Comparable<ChildSaRecord> { + + /** Locally generated SPI for receiving IPsec Packet. */ + public final int inboundSpi; + /** Remotely generated SPI for sending IPsec Packet. */ + public final int outboundSpi; + + /** Package private */ + ChildSaRecord(int inSpi, int outSpi, byte[] nonceInit, byte[] nonceResp) { + super(nonceInit, nonceResp); + inboundSpi = inSpi; + outboundSpi = outSpi; + // TODO: Impement constructor. Will be more input parameters. + } + + /** Package private */ + static ChildSaRecord makeChildSaRecord( + List<IkePayload> reqPayloads, List<IkePayload> respPayloads) { + return sSaRecordHelper.makeChildSaRecord(reqPayloads, respPayloads); + } + + /** + * 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; + } + } + + /** + * 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. + * @return ikeSaRecord for initial IKE SA. + */ + IkeSaRecord makeFirstIkeSaRecord(IkeMessage initRequest, IkeMessage initResponse); + + /** + * Construct new IkeSaRecord when doing rekey. + * + * @param oldSaRecord old IKE SA + * @param rekeyRequest Rekey IKE request. + * @param rekeyResponse Rekey IKE response. + * @return ikeSaRecord for new IKE SA. + */ + IkeSaRecord makeNewIkeSaRecord( + IkeSaRecord oldSaRecord, IkeMessage rekeyRequest, IkeMessage rekeyResponse); + + /** + * Construct ChildSaRecord and generate IpSecTransform pairs. + * + * @param reqPayloads payload list in request. + * @param respPayloads payload list in response. + * @return new Child SA. + */ + ChildSaRecord makeChildSaRecord( + List<IkePayload> reqPayloads, List<IkePayload> respPayloads); + } + + /** Generate SKEYSEED using negotiated PRF. */ + @VisibleForTesting + static byte[] generateSKeySeed( + String prfAlgorithm, byte[] nonceInit, byte[] nonceResp, byte[] sharedDhKey) { + try { + ByteBuffer keyBuffer = ByteBuffer.allocate(nonceInit.length + nonceResp.length); + keyBuffer.put(nonceInit).put(nonceResp); + SecretKeySpec prfKeySpec = new SecretKeySpec(keyBuffer.array(), prfAlgorithm); + + Mac prfMac = Mac.getInstance(prfAlgorithm, IkeMessage.getSecurityProvider()); + prfMac.init(prfKeySpec); + + ByteBuffer sharedKeyBuffer = ByteBuffer.wrap(sharedDhKey); + prfMac.update(sharedKeyBuffer); + + return prfMac.doFinal(); + } catch (InvalidKeyException | NoSuchAlgorithmException e) { + throw new IllegalArgumentException("Failed to generate SKEYSEED", e); + } + } + + /** + * Derives key materials using negotiated PRF. + * + * <p>prf+(K, S) outputs a pseudorandom stream by using negotiated PRF iteratively. In this way + * it can generate long enough keying material containing all the keys for this IKE/Child SA. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.13">RFC 7296 nternet Key Exchange + * Protocol Version 2 (IKEv2) 2.13. Generating Keying Material </a> + */ + @VisibleForTesting + static byte[] generateKeyMat( + String prfAlgorithm, byte[] prfKey, byte[] dataToSign, int keyMaterialLen) + throws InvalidKeyException { + try { + SecretKeySpec prfKeySpec = new SecretKeySpec(prfKey, prfAlgorithm); + Mac prfMac = Mac.getInstance(prfAlgorithm, IkeMessage.getSecurityProvider()); + + ByteBuffer keyMatBuffer = ByteBuffer.allocate(keyMaterialLen); + + byte[] previousMac = new byte[0]; + final int padLen = 1; + byte padValue = 1; + + while (keyMatBuffer.remaining() > 0) { + prfMac.init(prfKeySpec); + + ByteBuffer dataToSignBuffer = + ByteBuffer.allocate(previousMac.length + dataToSign.length + padLen); + dataToSignBuffer.put(previousMac).put(dataToSign).put(padValue); + dataToSignBuffer.rewind(); + + prfMac.update(dataToSignBuffer); + + previousMac = prfMac.doFinal(); + keyMatBuffer.put( + previousMac, 0, Math.min(previousMac.length, keyMatBuffer.remaining())); + + padValue++; + } + + return keyMatBuffer.array(); + } catch (InvalidKeyException | NoSuchAlgorithmException e) { + throw new IllegalArgumentException("Failed to generate keying material", e); + } + } +} |