diff options
author | Benedict Wong <benedictwong@google.com> | 2020-03-21 02:41:04 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2020-03-21 02:41:04 +0000 |
commit | 52ef0fc7e7f0a7d4c727da263367f075101fa8c5 (patch) | |
tree | 429985fdeeb709ffce7f3f48b9bcac77d2c1abc7 | |
parent | daf36f5e091f24a8ed9baf10a4fc865a96382ac1 (diff) | |
parent | eabb42802bf82dc691f7a327df53089fe3d9678b (diff) | |
download | ike-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
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); |