aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenedict Wong <benedictwong@google.com>2020-03-21 02:41:04 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-03-21 02:41:04 +0000
commit52ef0fc7e7f0a7d4c727da263367f075101fa8c5 (patch)
tree429985fdeeb709ffce7f3f48b9bcac77d2c1abc7
parentdaf36f5e091f24a8ed9baf10a4fc865a96382ac1 (diff)
parenteabb42802bf82dc691f7a327df53089fe3d9678b (diff)
downloadike-52ef0fc7e7f0a7d4c727da263367f075101fa8c5.tar.gz
Merge changes I607a5f16,Ie41f82c2,Iaa2f550c,I3043a57e
* changes: Send IKE Notify Payload for SIGNATURE_HASH_ALGORITHMS Add support for sending Digital Signature Authentication Use system trusted root certificates if not provided by caller Implement encoding of X509 certificates and digital signatures
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java49
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayload.java71
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayload.java24
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayload.java20
-rw-r--r--src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java19
-rw-r--r--tests/iketests/assets/key/end-cert-key-b.key28
-rw-r--r--tests/iketests/assets/key/end-cert-key-small.key10
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java120
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayloadTest.java35
-rw-r--r--tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayloadTest.java20
10 files changed, 333 insertions, 63 deletions
diff --git a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
index 3de08c37..4f17b693 100644
--- a/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
+++ b/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachine.java
@@ -53,6 +53,7 @@ import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
+import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig;
import android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig;
import android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
import android.net.ipsec.ike.exceptions.IkeException;
@@ -121,12 +122,14 @@ import java.lang.annotation.RetentionPolicy;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -2510,6 +2513,17 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
new IkeNotifyPayload(
IkeNotifyPayload.NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED));
+ ByteBuffer signatureHashAlgoTypes =
+ ByteBuffer.allocate(
+ IkeAuthDigitalSignPayload.ALL_SIGNATURE_ALGO_TYPES.length * 2);
+ for (short type : IkeAuthDigitalSignPayload.ALL_SIGNATURE_ALGO_TYPES) {
+ signatureHashAlgoTypes.putShort(type);
+ }
+ payloadList.add(
+ new IkeNotifyPayload(
+ IkeNotifyPayload.NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS,
+ signatureHashAlgoTypes.array()));
+
// TODO: Add Notification Payloads according to user configurations.
// Build IKE header
@@ -2560,10 +2574,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
respKePayload = (IkeKePayload) payload;
break;
case IkePayload.PAYLOAD_TYPE_CERT_REQUEST:
- throw new UnsupportedOperationException(
- "Do not support handling Cert Request Payload.");
- // TODO: Handle it when using certificate based authentication. Otherwise,
- // ignore it.
+ // Certificates unconditionally sent (only) for Digital Signature Auth
+ break;
case IkePayload.PAYLOAD_TYPE_NONCE:
hasNoncePayload = true;
mIkeRespNoncePayload = (IkeNoncePayload) payload;
@@ -2951,9 +2963,28 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
payloadList.add(pskPayload);
break;
case IkeSessionParams.IKE_AUTH_METHOD_PUB_KEY_SIGNATURE:
- // TODO: Support authentication based on public key signature.
- throw new UnsupportedOperationException(
- "Do not support public-key based authentication.");
+ IkeAuthDigitalSignLocalConfig localAuthConfig =
+ (IkeAuthDigitalSignLocalConfig) mIkeSessionParams.getLocalAuthConfig();
+
+ // Add certificates to list
+ payloadList.add(
+ new IkeCertX509CertPayload(localAuthConfig.getClientEndCertificate()));
+ for (X509Certificate intermediateCert : localAuthConfig.mIntermediateCerts) {
+ payloadList.add(new IkeCertX509CertPayload(intermediateCert));
+ }
+
+ IkeAuthDigitalSignPayload digitalSignaturePayload =
+ new IkeAuthDigitalSignPayload(
+ IkeAuthDigitalSignPayload.SIGNATURE_ALGO_RSA_SHA2_512,
+ localAuthConfig.mPrivateKey,
+ mIkeInitRequestBytes,
+ mCurrentIkeSaRecord.nonceResponder,
+ mInitIdPayload.getEncodedPayloadBody(),
+ mIkePrf,
+ mCurrentIkeSaRecord.getSkPi());
+ payloadList.add(digitalSignaturePayload);
+
+ break;
case IkeSessionParams.IKE_AUTH_METHOD_EAP:
// Do not include AUTH payload when using EAP.
break;
@@ -3135,8 +3166,8 @@ public class IkeSessionStateMachine extends AbstractSessionStateMachine {
"The remote/server failed to provide a end certificate");
}
- Set<TrustAnchor> trustAnchorSet = new HashSet<>();
- trustAnchorSet.add(trustAnchor);
+ Set<TrustAnchor> trustAnchorSet =
+ trustAnchor == null ? null : Collections.singleton(trustAnchor);
IkeCertPayload.validateCertificates(
endCert, certList, null /*crlList*/, trustAnchorSet);
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayload.java
index 61c45694..956e9bee 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayload.java
@@ -41,7 +41,7 @@ import java.util.Arrays;
* signature authentication method.
*
* <p>If AUTH_METHOD_RSA_DIGITAL_SIGN is used, then the hash algorithm is SHA1. If
- * AUTH_METHOD_GENERIC_DIGITAL_SIGN is used, the signature algorihtm and hash algorithm are
+ * AUTH_METHOD_GENERIC_DIGITAL_SIGN is used, the signature algorithm and hash algorithm are
* extracted from authentication data.
*
* @see <a href="https://tools.ietf.org/html/rfc7296#section-3.8">RFC 7296, Internet Key Exchange
@@ -51,6 +51,8 @@ import java.util.Arrays;
*/
public class IkeAuthDigitalSignPayload extends IkeAuthPayload {
private static final String KEY_ALGO_NAME = "RSA";
+ private static final byte SIGNATURE_ALGO_ASN1_BYTES_LEN = (byte) 15;
+ private static final byte SIGNATURE_ALGO_ASN1_BYTES_LEN_LEN = (byte) 1;
// Byte arrays of DER encoded identifier ASN.1 objects that indicates the algorithm used to
// generate the signature, extracted from
@@ -95,12 +97,25 @@ public class IkeAuthDigitalSignPayload extends IkeAuthPayload {
@VisibleForTesting
@interface SignatureAlgo {}
- @VisibleForTesting static final String SIGNATURE_ALGO_RSA_SHA1 = "SHA1withRSA";
- @VisibleForTesting static final String SIGNATURE_ALGO_RSA_SHA2_256 = "SHA256withRSA";
- @VisibleForTesting static final String SIGNATURE_ALGO_RSA_SHA2_384 = "SHA384withRSA";
- @VisibleForTesting static final String SIGNATURE_ALGO_RSA_SHA2_512 = "SHA512withRSA";
+ public static final String SIGNATURE_ALGO_RSA_SHA1 = "SHA1withRSA";
+ public static final String SIGNATURE_ALGO_RSA_SHA2_256 = "SHA256withRSA";
+ public static final String SIGNATURE_ALGO_RSA_SHA2_384 = "SHA384withRSA";
+ public static final String SIGNATURE_ALGO_RSA_SHA2_512 = "SHA512withRSA";
- public final String signatureAlgoAndHash;
+ // IKEv2 types for hash algorithms.
+ public static final short HASH_ALGORITHM_RSA_SHA1 = 1;
+ public static final short HASH_ALGORITHM_RSA_SHA2_256 = 2;
+ public static final short HASH_ALGORITHM_RSA_SHA2_384 = 3;
+ public static final short HASH_ALGORITHM_RSA_SHA2_512 = 4;
+ public static final short[] ALL_SIGNATURE_ALGO_TYPES =
+ new short[] {
+ HASH_ALGORITHM_RSA_SHA1,
+ HASH_ALGORITHM_RSA_SHA2_256,
+ HASH_ALGORITHM_RSA_SHA2_384,
+ HASH_ALGORITHM_RSA_SHA2_512
+ };
+
+ public final String signatureAndHashAlgos;
public final byte[] signature;
protected IkeAuthDigitalSignPayload(
@@ -109,7 +124,7 @@ public class IkeAuthDigitalSignPayload extends IkeAuthPayload {
super(critical, authMethod);
switch (authMethod) {
case AUTH_METHOD_RSA_DIGITAL_SIGN:
- signatureAlgoAndHash = SIGNATURE_ALGO_RSA_SHA1;
+ signatureAndHashAlgos = SIGNATURE_ALGO_RSA_SHA1;
signature = authData;
break;
case AUTH_METHOD_GENERIC_DIGITAL_SIGN:
@@ -119,7 +134,7 @@ public class IkeAuthDigitalSignPayload extends IkeAuthPayload {
int signAlgoLen = Byte.toUnsignedInt(inputBuffer.get());
byte[] signAlgoBytes = new byte[signAlgoLen];
inputBuffer.get(signAlgoBytes);
- signatureAlgoAndHash = bytesToJavaStandardSignAlgoName(signAlgoBytes);
+ signatureAndHashAlgos = bytesToJavaStandardSignAlgoName(signAlgoBytes);
// Get signature.
signature = new byte[authData.length - SIGNATURE_ALGO_ASN1_LEN_LEN - signAlgoLen];
@@ -169,7 +184,7 @@ public class IkeAuthDigitalSignPayload extends IkeAuthPayload {
signGen.update(dataToSignBytes);
signature = signGen.sign();
- signatureAlgoAndHash = signatureAlgoName;
+ signatureAndHashAlgos = signatureAlgoName;
} catch (SignatureException | InvalidKeyException e) {
throw new IllegalArgumentException("Signature generation failed", e);
} catch (NoSuchAlgorithmException e) {
@@ -181,6 +196,21 @@ public class IkeAuthDigitalSignPayload extends IkeAuthPayload {
}
}
+ private byte[] javaStandardSignAlgoNameToAsn1Bytes(String javaSignatureAndHashAlgo) {
+ switch (javaSignatureAndHashAlgo) {
+ case SIGNATURE_ALGO_RSA_SHA1:
+ return PKI_ALGO_ID_DER_BYTES_RSA_SHA1;
+ case SIGNATURE_ALGO_RSA_SHA2_256:
+ return PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256;
+ case SIGNATURE_ALGO_RSA_SHA2_384:
+ return PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384;
+ case SIGNATURE_ALGO_RSA_SHA2_512:
+ return PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512;
+ default:
+ throw new IllegalArgumentException("Impossible! We used an unsupported algo");
+ }
+ }
+
private String bytesToJavaStandardSignAlgoName(byte[] signAlgoBytes)
throws AuthenticationFailedException {
if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA1, signAlgoBytes)) {
@@ -225,7 +255,7 @@ public class IkeAuthDigitalSignPayload extends IkeAuthPayload {
getSignedOctets(ikeInitBytes, nonce, idPayloadBodyBytes, ikePrf, prfKeyBytes);
try {
- Signature signValidator = Signature.getInstance(signatureAlgoAndHash);
+ Signature signValidator = Signature.getInstance(signatureAndHashAlgos);
signValidator.initVerify(certificate);
signValidator.update(dataToSignBytes);
@@ -236,24 +266,27 @@ public class IkeAuthDigitalSignPayload extends IkeAuthPayload {
throw new AuthenticationFailedException(e);
} catch (NoSuchAlgorithmException e) {
throw new ProviderException(
- "Security Provider does not support " + signatureAlgoAndHash);
+ "Security Provider does not support " + signatureAndHashAlgos);
}
}
- // TODO: Add methods for generating signature.
-
@Override
protected void encodeAuthDataToByteBuffer(ByteBuffer byteBuffer) {
- // TODO: Implement it.
- throw new UnsupportedOperationException(
- "It is not supported to encode a " + getTypeString());
+ if (authMethod == AUTH_METHOD_GENERIC_DIGITAL_SIGN) {
+ byteBuffer.put(SIGNATURE_ALGO_ASN1_BYTES_LEN);
+ byteBuffer.put(javaStandardSignAlgoNameToAsn1Bytes(signatureAndHashAlgos));
+ }
+ byteBuffer.put(signature);
}
@Override
protected int getAuthDataLength() {
- // TODO: Implement it.
- throw new UnsupportedOperationException(
- "It is not supported to get payload length of " + getTypeString());
+ if (authMethod == AUTH_METHOD_GENERIC_DIGITAL_SIGN) {
+ return SIGNATURE_ALGO_ASN1_BYTES_LEN_LEN
+ + SIGNATURE_ALGO_ASN1_BYTES_LEN
+ + signature.length;
+ }
+ return signature.length;
}
@Override
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayload.java
index cd743978..d75607ed 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertPayload.java
@@ -53,7 +53,7 @@ import javax.net.ssl.X509TrustManager;
*/
public abstract class IkeCertPayload extends IkePayload {
// Length of certificate encoding type field in octets.
- private static final int CERT_ENCODING_LEN = 1;
+ protected static final int CERT_ENCODING_LEN = 1;
private static final String KEY_STORE_TYPE_PKCS12 = "PKCS12";
private static final String CERT_PATH_ALGO_PKIX = "PKIX";
@@ -132,14 +132,20 @@ public abstract class IkeCertPayload extends IkePayload {
try {
// TODO: b/122676944 Support CRL checking
- // Create a new keyStore with all trusted anchors
- KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_PKCS12);
- keyStore.load(null);
- for (TrustAnchor t : trustAnchorSet) {
- X509Certificate trustedCert = t.getTrustedCert();
- String alias =
- trustedCert.getSubjectX500Principal().getName() + trustedCert.hashCode();
- keyStore.setCertificateEntry(alias, trustedCert);
+ // By default, use system-trusted CAs
+ KeyStore keyStore = null;
+
+ // But if a specific trust anchor is specified, use that instead
+ if (trustAnchorSet != null && !trustAnchorSet.isEmpty()) {
+ keyStore = KeyStore.getInstance(KEY_STORE_TYPE_PKCS12);
+ keyStore.load(null);
+ for (TrustAnchor t : trustAnchorSet) {
+ X509Certificate trustedCert = t.getTrustedCert();
+ String alias =
+ trustedCert.getSubjectX500Principal().getName()
+ + trustedCert.hashCode();
+ keyStore.setCertificateEntry(alias, trustedCert);
+ }
}
// Build X509TrustManager with all keystore
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayload.java
index e30db070..4a26f7e9 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayload.java
@@ -23,6 +23,7 @@ import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedExcepti
import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -75,9 +76,14 @@ public final class IkeCertX509CertPayload extends IkeCertPayload {
*/
@Override
protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) {
- // TODO: Implement it.
- throw new UnsupportedOperationException(
- "It is not supported to encode a " + getTypeString());
+ encodePayloadHeaderToByteBuffer(nextPayload, getPayloadLength(), byteBuffer);
+
+ byteBuffer.put((byte) certEncodingType);
+ try {
+ byteBuffer.put(certificate.getEncoded());
+ } catch (CertificateEncodingException e) {
+ // No certificate to encode.
+ }
}
/**
@@ -87,9 +93,11 @@ public final class IkeCertX509CertPayload extends IkeCertPayload {
*/
@Override
protected int getPayloadLength() {
- // TODO: Implement it.
- throw new UnsupportedOperationException(
- "It is not supported to get payload length of " + getTypeString());
+ try {
+ return GENERIC_HEADER_LENGTH + CERT_ENCODING_LEN + certificate.getEncoded().length;
+ } catch (CertificateEncodingException e) {
+ return GENERIC_HEADER_LENGTH + CERT_ENCODING_LEN;
+ }
}
/**
diff --git a/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java b/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java
index a15b7776..b9087777 100644
--- a/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java
+++ b/src/java/com/android/internal/net/ipsec/ike/message/IkeNotifyPayload.java
@@ -83,7 +83,9 @@ public final class IkeNotifyPayload extends IkeInformationalPayload {
NOTIFY_TYPE_USE_TRANSPORT_MODE,
NOTIFY_TYPE_REKEY_SA,
NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED,
- NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION
+ NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION,
+ NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED,
+ NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS
})
public @interface NotifyType {}
@@ -123,12 +125,21 @@ public final class IkeNotifyPayload extends IkeInformationalPayload {
* being negotiated. Only allowed in the request/response for negotiating a Child SA.
*/
public static final int NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED = 16394;
- /** Indicates that the sender supports IKE fragmentation. */
- public static final int NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED = 16430;
/** Indicates that the sender prefers to use only eap based authentication */
public static final int NOTIFY_TYPE_EAP_ONLY_AUTHENTICATION = 16417;
+ /** Indicates that the sender supports IKE fragmentation. */
+ public static final int NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED = 16430;
+
+ /**
+ * Indicates that the sender supports GENERIC_DIGITAL_SIGNATURE authentication payloads.
+ *
+ * <p>See RFC 7427 - Signature Authentication in the Internet Key Exchange Version 2 (IKEv2) for
+ * more details
+ */
+ public static final int NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS = 16431;
+
private static final int NOTIFY_HEADER_LEN = 4;
private static final int ERROR_NOTIFY_TYPE_MAX = 16383;
@@ -195,6 +206,8 @@ public final class IkeNotifyPayload extends IkeInformationalPayload {
NOTIFY_TYPE_ESP_TFC_PADDING_NOT_SUPPORTED, "ESP TCP Padding not supported");
NOTIFY_TYPE_TO_STRING.put(
NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED, "Fragmentation supported");
+ NOTIFY_TYPE_TO_STRING.put(
+ NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS, "Generic Digital Signatures supported");
}
public final int protocolId;
diff --git a/tests/iketests/assets/key/end-cert-key-b.key b/tests/iketests/assets/key/end-cert-key-b.key
new file mode 100644
index 00000000..b03f379c
--- /dev/null
+++ b/tests/iketests/assets/key/end-cert-key-b.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDGaHKmiwz92EU+
++YemKqT5slHbJ/DkB5aGymeO4gkHVff9MUJFOYsuxDxUZFCdIpiUBgnavlemBhDT
+2vgNgBsfAvahJi2S0zOprHbYmVHvdzNkfgPdRO0qTsJ0u1zU08VdgWaUn24T9MPW
+le9fCP291iny1W6WzZG/6EE22AzidPUWrIymnWyvm3Ysh3H5akNHg8LwLscTF67D
+jA4FjqZani4StaoMUF9A0jd3PCnJJG2e4xUpJflByPpn5uoqa0CACdmp2gt6Cfiw
+FsQsMVbVBg2qn1CB0k70/1HaUjwtm8px3bIHzMnb4krUR+H0SvOGoWdFzUHNuwHE
+okFumlldAgMBAAECggEAGebOzjZ+8Vtc3DYf4wMphb4leN0uUdvlEcm2BULgTN3d
+7GbphjdXQo60auFXCNHYZhKiZ1/L+iJL62xoT6g2t4YKg8qDzpQrPW5aFuLFKI7r
+iFCkg9bDY2+OwUXTKPxQhWl8O9DTF7XaJIHEaH1jACJyWCpML/xm7Pe5c8XMFiVV
+yiSpXdUFArILAlm4uH3jzmxDbfIApaZ3hrW9s0uhkXJNcVKPXBsi2fyaa6lhAyEe
+swn7Jg92uBL7EVRsZeXqi0vVo8LJ5+eQLxoy1aQNIOYKKjblvKNwOsUIeXlDhdzc
+uJ942RvNn3hgH270AOJBE0R1Fy4GIPsIlSpYSGzTPQKBgQDjoEofLK2MmrZbbXMa
+ru1ERYE8WskGL4wYyfgtDpTS+5mkMgnnAwOVFjD69CPctSXXgWbBsKYCIBiCTJEg
+QJWpKEx/Oc94sEPJNPlP2FR9ul0fqZ6lPMRb5h2dWRIC0w414FSQ2qyHhmJRPm0W
+bS87yHbzspWkkBvfpbfr7ML+AwKBgQDfI8mAqUqfXpm17a+AtMghaxzZ61HT7e9F
+CA0/shsutbRpX7pz9Az6Xd9AeUlfWFTn1ibk3nHWxSkW147Jp4nWD+0exBam2ths
+0gavllojF42PNuE2erV0e7BjCGzJmjcCaA70iSK7D1BKvRwSMpyXeL3Vwkzy+edq
+zfgqE+fdHwKBgQCofn5flTLiYaObCemqVtJLNyfzBUDI02lg8CBFOgXwJ04TvPaX
+iiOcuFCJcy8YsP7SB3eI6Ln52MhLq2HB/gzKqon1o1VwgQmsZwolkHz6lHladrR4
+nAK+BBjn4Ib0kEZ0mxn+H+CWcWNVwF//udK87icfPZKHviTj6Ee83AV7AQKBgDuR
+Gx6Mks4ee9wUGlC6cd5x8J6/8fUoREJiVt8VFaqPeRzUIDsKAFjprEpS1mJseyX0
+hid8tlkGAuUFICn23pXVZJBc+ksKitHp8zz2XxOVGwYLOW3Irh3tUvXs7tWWpG6x
+BqUC4tGa0e1RtGerOSA6bDrfBpmqWrupRkE9VR0zAoGAYPg6Zc3h+cycFVXBqscC
+ziJmS+hMD2rPu+sVODv6V1n3DmH/b7B/Qk5cgZYhU1dsR8FtgA0Au9rDAnjnFDMZ
+RtaFsa91DBCpWlLxejMO1en0Mxg+zYapjdAQ1kLBu0jZDoPlClyquf8O2C2YCuBI
+28maIjZq+cEvG79y32Ga65Y=
+-----END PRIVATE KEY-----
diff --git a/tests/iketests/assets/key/end-cert-key-small.key b/tests/iketests/assets/key/end-cert-key-small.key
new file mode 100644
index 00000000..48530465
--- /dev/null
+++ b/tests/iketests/assets/key/end-cert-key-small.key
@@ -0,0 +1,10 @@
+-----BEGIN PRIVATE KEY-----
+MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAphl0fstit/XcelkX
+2iKoosGsZ2U5rU+mYWR/9RJIY+qR3OYrBeqqtdvjPOu2fNjHEtsO//dCRvdxVWdx
+20ADPQIDAQABAkApsawXg/Bk4zeUErc1D4wrRtiDH9rJkXvfaL3iA9PeGIU2j2ci
+WwqbJY6HhGSJiNAKMVHRMRAtoaBeX80DOqH1AiEA0pWj6kItG2zTww09sqc6ymTc
+1aknFaGuXZY+RO0MTF8CIQDJ68uKQ8LV6labUnnmLPUnIYfVPY8XTNpvkwazeTuV
+4wIgDy60u637vI9zEQwCV8AQ0AjHlyvz4m5euOadJLEGgvcCIQCc9BpsySsjmFnl
+tgBm+L8+wYOSL52QYP7SB5kH3M6CPQIgQQIVYRKf44G9agh09utnaiw11FCwRr0M
+EKsdiVmkw+o=
+-----END PRIVATE KEY-----
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
index 22e62ca5..f44e3a59 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/IkeSessionStateMachineTest.java
@@ -39,6 +39,7 @@ import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY
import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED;
import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP;
import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP;
+import static com.android.internal.net.ipsec.ike.message.IkeNotifyPayload.NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_AUTH;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_NOTIFY;
import static com.android.internal.net.ipsec.ike.message.IkePayload.PAYLOAD_TYPE_SA;
@@ -162,6 +163,7 @@ import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.security.GeneralSecurityException;
+import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.LinkedList;
@@ -216,8 +218,10 @@ public final class IkeSessionStateMachineTest {
private static final String NAT_DETECTION_SOURCE_PAYLOAD_HEX_STRING =
"2900001c00004004e54f73b7d83f6beb881eab2051d8663f421d10b0";
private static final String NAT_DETECTION_DESTINATION_PAYLOAD_HEX_STRING =
- "2b00001c00004005d915368ca036004cb578ae3e3fb268509aeab190";
+ "2900001c00004005d915368ca036004cb578ae3e3fb268509aeab190";
private static final String FRAGMENTATION_SUPPORTED_PAYLOAD_HEX_STRING = "290000080000402e";
+ private static final String SIGNATURE_HASH_SUPPORTED_PAYLOAD_HEX_STRING =
+ "2b00000F0000402f0001000200030004";
private static final String DELETE_IKE_PAYLOAD_HEX_STRING = "0000000801000000";
private static final String NOTIFY_REKEY_IKE_PAYLOAD_HEX_STRING = "2100000800004009";
private static final String ID_PAYLOAD_INITIATOR_HEX_STRING =
@@ -326,6 +330,8 @@ public final class IkeSessionStateMachineTest {
private X509Certificate mRootCertificate;
private X509Certificate mServerEndCertificate;
+ private PrivateKey mUserPrivateKey;
+ private X509Certificate mUserEndCert;
private ArgumentCaptor<IkeMessage> mIkeMessageCaptor =
ArgumentCaptor.forClass(IkeMessage.class);
@@ -665,6 +671,8 @@ public final class IkeSessionStateMachineTest {
mRootCertificate = CertUtils.createCertFromPemFile("self-signed-ca-a.pem");
mServerEndCertificate = CertUtils.createCertFromPemFile("end-cert-a.pem");
+ mUserEndCert = CertUtils.createCertFromPemFile("end-cert-b.pem");
+ mUserPrivateKey = CertUtils.createRsaPrivateKeyFromKeyFile("end-cert-key-b.key");
mPsk = TestUtils.hexStringToByteArray(PSK_HEX_STRING);
@@ -798,6 +806,12 @@ public final class IkeSessionStateMachineTest {
.build();
}
+ private IkeSessionParams buildIkeSessionParamsDigitalSignature() throws Exception {
+ return buildIkeSessionParamsCommon()
+ .setAuthDigitalSignature(mRootCertificate, mUserEndCert, mUserPrivateKey)
+ .build();
+ }
+
private ChildSessionParams buildChildSessionParams() throws Exception {
ChildSaProposal saProposal =
new ChildSaProposal.Builder()
@@ -824,6 +838,7 @@ public final class IkeSessionStateMachineTest {
payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NOTIFY);
payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NOTIFY);
payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NOTIFY);
+ payloadTypeList.add(IkePayload.PAYLOAD_TYPE_NOTIFY);
payloadTypeList.add(IkePayload.PAYLOAD_TYPE_VENDOR);
payloadHexStringList.add(IKE_SA_PAYLOAD_HEX_STRING);
@@ -832,6 +847,7 @@ public final class IkeSessionStateMachineTest {
payloadHexStringList.add(NAT_DETECTION_SOURCE_PAYLOAD_HEX_STRING);
payloadHexStringList.add(NAT_DETECTION_DESTINATION_PAYLOAD_HEX_STRING);
payloadHexStringList.add(FRAGMENTATION_SUPPORTED_PAYLOAD_HEX_STRING);
+ payloadHexStringList.add(SIGNATURE_HASH_SUPPORTED_PAYLOAD_HEX_STRING);
payloadHexStringList.add(VENDOR_ID_PAYLOAD_HEX_STRING);
// In each test assign different IKE responder SPI in IKE INIT response to avoid remote SPI
@@ -1271,6 +1287,7 @@ public final class IkeSessionStateMachineTest {
assertTrue(isNotifyExist(payloadList, NOTIFY_TYPE_NAT_DETECTION_SOURCE_IP));
assertTrue(isNotifyExist(payloadList, NOTIFY_TYPE_NAT_DETECTION_DESTINATION_IP));
assertTrue(isNotifyExist(payloadList, NOTIFY_TYPE_IKEV2_FRAGMENTATION_SUPPORTED));
+ assertTrue(isNotifyExist(payloadList, NOTIFY_TYPE_SIGNATURE_HASH_ALGORITHMS));
verify(mSpyIkeUdpEncapSocket)
.registerIke(eq(mSpyCurrentIkeSaRecord.getLocalSpi()), eq(mIkeSessionStateMachine));
@@ -2001,6 +2018,35 @@ public final class IkeSessionStateMachineTest {
return ikeAuthReqMessage;
}
+ private void verifyDigitalSignatureAuthentication(
+ IkeAuthDigitalSignPayload spyAuthPayload,
+ X509Certificate certificate,
+ IkeIdPayload respIdPayload,
+ List<IkePayload> authRelatedPayloads,
+ boolean hasChildPayloads,
+ boolean hasConfigPayloadInResp)
+ throws Exception {
+ IkeMessage ikeAuthReqMessage =
+ verifyAuthenticationCommonAndGetIkeMessage(
+ respIdPayload,
+ authRelatedPayloads,
+ hasChildPayloads,
+ hasConfigPayloadInResp);
+
+ verify(spyAuthPayload)
+ .verifyInboundSignature(
+ certificate,
+ mIkeSessionStateMachine.mIkeInitRequestBytes,
+ mSpyCurrentIkeSaRecord.nonceInitiator,
+ respIdPayload.getEncodedPayloadBody(),
+ mIkeSessionStateMachine.mIkePrf,
+ mSpyCurrentIkeSaRecord.getSkPr());
+
+ assertNotNull(
+ ikeAuthReqMessage.getPayloadForType(
+ IkePayload.PAYLOAD_TYPE_AUTH, IkeAuthDigitalSignPayload.class));
+ }
+
private void verifySharedKeyAuthentication(
IkeAuthPskPayload spyAuthPayload,
IkeIdPayload respIdPayload,
@@ -2008,6 +2054,34 @@ public final class IkeSessionStateMachineTest {
boolean hasChildPayloads,
boolean hasConfigPayloadInResp)
throws Exception {
+ IkeMessage ikeAuthReqMessage =
+ verifyAuthenticationCommonAndGetIkeMessage(
+ respIdPayload,
+ authRelatedPayloads,
+ hasChildPayloads,
+ hasConfigPayloadInResp);
+
+ // Validate authentication is done. Cannot use matchers because IkeAuthPskPayload is final.
+ verify(spyAuthPayload)
+ .verifyInboundSignature(
+ mPsk,
+ mIkeSessionStateMachine.mIkeInitRequestBytes,
+ mSpyCurrentIkeSaRecord.nonceInitiator,
+ respIdPayload.getEncodedPayloadBody(),
+ mIkeSessionStateMachine.mIkePrf,
+ mSpyCurrentIkeSaRecord.getSkPr());
+
+ assertNotNull(
+ ikeAuthReqMessage.getPayloadForType(
+ IkePayload.PAYLOAD_TYPE_AUTH, IkeAuthPskPayload.class));
+ }
+
+ private IkeMessage verifyAuthenticationCommonAndGetIkeMessage(
+ IkeIdPayload respIdPayload,
+ List<IkePayload> authRelatedPayloads,
+ boolean hasChildPayloads,
+ boolean hasConfigPayloadInResp)
+ throws Exception {
// Send IKE AUTH response to IKE state machine
ReceivedIkePacket authResp = makeIkeAuthRespWithChildPayloads(authRelatedPayloads);
mIkeSessionStateMachine.sendMessage(
@@ -2021,9 +2095,6 @@ public final class IkeSessionStateMachineTest {
} else {
ikeAuthReqMessage = verifyAuthReqAndGetMsg();
}
- assertNotNull(
- ikeAuthReqMessage.getPayloadForType(
- IkePayload.PAYLOAD_TYPE_AUTH, IkeAuthPskPayload.class));
// Validate that there is no EAP only notify payload
List<IkeNotifyPayload> notifyPayloads = ikeAuthReqMessage.getPayloadListForType(
@@ -2034,16 +2105,6 @@ public final class IkeSessionStateMachineTest {
verifyIncrementLocaReqMsgId();
verifyDecodeEncryptedMessage(mSpyCurrentIkeSaRecord, authResp);
- // Validate authentication is done. Cannot use matchers because IkeAuthPskPayload is final.
- verify(spyAuthPayload)
- .verifyInboundSignature(
- mPsk,
- mIkeSessionStateMachine.mIkeInitRequestBytes,
- mSpyCurrentIkeSaRecord.nonceInitiator,
- respIdPayload.getEncodedPayloadBody(),
- mIkeSessionStateMachine.mIkePrf,
- mSpyCurrentIkeSaRecord.getSkPr());
-
// Validate that user has been notified
verify(mSpyUserCbExecutor).execute(any(Runnable.class));
@@ -2138,6 +2199,8 @@ public final class IkeSessionStateMachineTest {
mLooper.dispatchAll();
assertTrue(
mIkeSessionStateMachine.getCurrentState() instanceof IkeSessionStateMachine.Idle);
+
+ return ikeAuthReqMessage;
}
private IkeAuthPskPayload makeSpyRespPskPayload() throws Exception {
@@ -2180,6 +2243,35 @@ public final class IkeSessionStateMachineTest {
}
@Test
+ public void testCreateIkeLocalIkeAuthDigitalSignature() throws Exception {
+ // Quit and restart IKE Session with Digital Signature Auth params
+ mIkeSessionStateMachine.quitNow();
+ reset(mMockChildSessionFactoryHelper);
+ setupChildStateMachineFactory(mMockChildSessionStateMachine);
+ mIkeSessionStateMachine = makeAndStartIkeSession(buildIkeSessionParamsDigitalSignature());
+ mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth);
+
+ // Build IKE AUTH response with Digital Signature Auth, ID-Responder and config payloads.
+ List<IkePayload> authRelatedPayloads = new LinkedList<>();
+ IkeAuthDigitalSignPayload spyAuthPayload = makeSpyDigitalSignAuthPayload();
+ authRelatedPayloads.add(spyAuthPayload);
+ authRelatedPayloads.add(new IkeCertX509CertPayload(mServerEndCertificate));
+
+ IkeIdPayload respIdPayload = makeRespIdPayload();
+ authRelatedPayloads.add(respIdPayload);
+ authRelatedPayloads.add(makeConfigPayload());
+
+ verifyDigitalSignatureAuthentication(
+ spyAuthPayload,
+ mServerEndCertificate,
+ respIdPayload,
+ authRelatedPayloads,
+ true /*hasChildPayloads*/,
+ true /*hasConfigPayloadInResp*/);
+ verifyRetransmissionStopped();
+ }
+
+ @Test
public void testCreateIkeLocalIkeAuthPsk() throws Exception {
mockIkeInitAndTransitionToIkeAuth(mIkeSessionStateMachine.mCreateIkeLocalIkeAuth);
verifyRetransmissionStarted();
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayloadTest.java
index a5837ffe..2940f7bc 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayloadTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeAuthDigitalSignPayloadTest.java
@@ -34,17 +34,21 @@ import com.android.internal.net.ipsec.ike.testutils.CertUtils;
import org.junit.Before;
import org.junit.Test;
+import java.nio.ByteBuffer;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
public final class IkeAuthDigitalSignPayloadTest {
// TODO: Build a RSA_SHA1 signature and add tests for it.
- // RSA_SHA2_256
+ // AuthMethod 14 (Generic DS) with RSA_SHA2_256
private static final String AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING =
"0e0000000f300d06092a864886f70d01010b05006f76af4150d653c5d4136b9f"
+ "69d905849bf075c563e6d14ccda42361ec3e7d12c72e2dece5711ea1d952f7b8"
+ "e12c5d982aa4efdaeac36a02b222aa96242cc424";
+
+ private static final String GENERIC_PAYLOAD_HEADER = "00000058"; // 88B length
+
private static final String SIGNATURE =
"6f76af4150d653c5d4136b9f69d905849bf075c563e6d14ccda42361ec3e7d12"
+ "c72e2dece5711ea1d952f7b8e12c5d982aa4efdaeac36a02b222aa96242cc424";
@@ -65,6 +69,8 @@ public final class IkeAuthDigitalSignPayloadTest {
+ "c5078355436f3347376ff2900001c0000400545bc3f2113770de91c769094f1bd"
+ "614534e765ea290000080000402e290000100000402f000100020003000400000"
+ "00800004014";
+
+ private static final int NEXT_PAYLOAD_TYPE = IkePayload.PAYLOAD_TYPE_NO_NEXT;
private static final String NONCE_INIT_HEX_STRING =
"a5dded450b5ffd2670f37954367fce28279a085c830a03358b10b0872c0578f9";
private static final String ID_RESP_PAYLOAD_BODY_HEX_STRING = "01000000c0a82b8a";
@@ -94,11 +100,34 @@ public final class IkeAuthDigitalSignPayloadTest {
assertTrue(payload instanceof IkeAuthDigitalSignPayload);
IkeAuthDigitalSignPayload dsPayload = (IkeAuthDigitalSignPayload) payload;
- assertEquals(SIGNATURE_ALGO_RSA_SHA2_256, dsPayload.signatureAlgoAndHash);
+ assertEquals(SIGNATURE_ALGO_RSA_SHA2_256, dsPayload.signatureAndHashAlgos);
assertArrayEquals(dsPayload.signature, TestUtils.hexStringToByteArray(SIGNATURE));
}
@Test
+ public void testSignAndEncode() throws Exception {
+ PrivateKey key = CertUtils.createRsaPrivateKeyFromKeyFile("end-cert-key-a.key");
+
+ IkeAuthDigitalSignPayload authPayload =
+ new IkeAuthDigitalSignPayload(
+ SIGNATURE_ALGO_RSA_SHA2_256,
+ key,
+ IKE_INIT_RESP_REQUEST,
+ NONCE_INIT_RESP,
+ ID_RESP_PAYLOAD_BODY,
+ mIkeHmacSha1Prf,
+ PRF_RESP_KEY);
+
+ ByteBuffer buffer = ByteBuffer.allocate(authPayload.getPayloadLength());
+ authPayload.encodeToByteBuffer(NEXT_PAYLOAD_TYPE, buffer);
+
+ byte[] expected =
+ TestUtils.hexStringToByteArray(
+ GENERIC_PAYLOAD_HEADER + AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING);
+ assertArrayEquals(expected, buffer.array());
+ }
+
+ @Test
public void testVerifyInboundSignature() throws Exception {
byte[] inputPacket =
TestUtils.hexStringToByteArray(AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING);
@@ -153,7 +182,7 @@ public final class IkeAuthDigitalSignPayloadTest {
mIkeHmacSha1Prf,
PRF_RESP_KEY);
- assertEquals(SIGNATURE_ALGO_RSA_SHA2_256, authPayload.signatureAlgoAndHash);
+ assertEquals(SIGNATURE_ALGO_RSA_SHA2_256, authPayload.signatureAndHashAlgos);
assertArrayEquals(authPayload.signature, TestUtils.hexStringToByteArray(SIGNATURE));
}
}
diff --git a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayloadTest.java b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayloadTest.java
index 61f268cf..865cf450 100644
--- a/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayloadTest.java
+++ b/tests/iketests/src/java/com/android/internal/net/ipsec/ike/message/IkeCertX509CertPayloadTest.java
@@ -16,6 +16,7 @@
package com.android.internal.net.ipsec.ike.message;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -26,12 +27,17 @@ import com.android.internal.net.ipsec.ike.exceptions.AuthenticationFailedExcepti
import org.junit.Test;
import java.io.ByteArrayInputStream;
+import java.nio.ByteBuffer;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Base64;
public final class IkeCertX509CertPayloadTest {
+ private static final int NEXT_PAYLOAD_TYPE = IkePayload.PAYLOAD_TYPE_NO_NEXT;
+
+ // Generic header - length: 4B (Generic header) + 1B (Cert encoding) 1061B (Cert length)
+ private static final String CERT_PAYLOAD_HEADER_HEX_STRING = "0000042a";
private static final String CERT_PAYLOAD_BODY_HEX_STRING =
"043082042130820209a003020102020827a2b30cdd5043ab300d06092a864886"
+ "f70d01010c05003035310b3009060355040613024e5a310e300c060355040a13"
@@ -95,6 +101,20 @@ public final class IkeCertX509CertPayloadTest {
private static final int CERTIFICATE_OFFSET = 1;
@Test
+ public void testEncodeX509Certificate() throws Exception {
+ X509Certificate cert = pemStringToCertificate(CLIENT_END_CERTIFICATE);
+ IkeCertX509CertPayload certPayload = new IkeCertX509CertPayload(cert);
+
+ ByteBuffer buffer = ByteBuffer.allocate(certPayload.getPayloadLength());
+ certPayload.encodeToByteBuffer(NEXT_PAYLOAD_TYPE, buffer);
+
+ byte[] expected =
+ TestUtils.hexStringToByteArray(
+ CERT_PAYLOAD_HEADER_HEX_STRING + CERT_PAYLOAD_BODY_HEX_STRING);
+ assertArrayEquals(expected, buffer.array());
+ }
+
+ @Test
public void testDecodeX509Certificate() throws Exception {
byte[] inputPacket = TestUtils.hexStringToByteArray(CERT_PAYLOAD_BODY_HEX_STRING);
IkeCertPayload certPayload = IkeCertPayload.getIkeCertPayload(false, inputPacket);