diff options
author | evitayan <evitayan@google.com> | 2019-01-02 17:06:31 -0800 |
---|---|---|
committer | evitayan <evitayan@google.com> | 2019-02-08 16:01:41 -0800 |
commit | 1b4e61671dc3f0a2165c2eb2d64e7186428fdfd5 (patch) | |
tree | 378edef04405ed13abef1c0878480dc6fdb9c2d8 | |
parent | 7f541ae9f07c8222ea52fbb095f9292d6c4a72cc (diff) | |
download | ike-1b4e61671dc3f0a2165c2eb2d64e7186428fdfd5.tar.gz |
Decode auth payload that uses digital signature
This commit:
- Create IkeAuthDigitalSignPayload for payload that uses digital
signature
- Decode auth payload that uses RSA signature for IKE auth
- Decode auth payload that uses generic digital signature for
IKE auth
Bug: 122688641
Test: FrameworksIkeTests IkeAuthDigitalSignPayloadTest
HexUtilsTest
Change-Id: Ie2363ee848d33511eb3e6bb94e955d996b0d22d1
6 files changed, 239 insertions, 9 deletions
diff --git a/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java b/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java new file mode 100644 index 00000000..12fb5f66 --- /dev/null +++ b/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayload.java @@ -0,0 +1,158 @@ +/* + * 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.message; + +import android.annotation.StringDef; + +import com.android.ike.ikev2.exceptions.AuthenticationFailedException; +import com.android.ike.ikev2.exceptions.IkeException; +import com.android.ike.ikev2.message.IkeAuthPayload.AuthMethod; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.ByteBuffer; +import java.util.Arrays; + +/** + * IkeAuthDigitalSignPayload represents Authentication Payload using a specific or generic digital + * 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 + * extracted from authentication data. + * + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.8">RFC 7296, Internet Key Exchange + * Protocol Version 2 (IKEv2). + * @see <a href="https://tools.ietf.org/html/rfc7427">RFC 7427, Signature Authentication in the + * Internet Key Exchange Version 2 (IKEv2) + */ +public class IkeAuthDigitalSignPayload extends IkeAuthPayload { + + // Byte arrays of DER encoded identifier ASN.1 objects that indicates the algorithm used to + // generate the signature, extracted from + // <a href="https://tools.ietf.org/html/rfc7427#appendix-A"> RFC 7427. There is no need to + // understand the encoding process. They are just constants to indicate the algorithm type. + private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA1 = { + (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, + (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, + (byte) 0x05, (byte) 0x05, (byte) 0x00 + }; + private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256 = { + (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, + (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, + (byte) 0x0b, (byte) 0x05, (byte) 0x00 + }; + private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384 = { + (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, + (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, + (byte) 0x0c, (byte) 0x05, (byte) 0x00 + }; + private static final byte[] PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512 = { + (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, + (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, + (byte) 0x0d, (byte) 0x05, (byte) 0x00 + }; + + // Length of ASN.1 object length field. + private static final int SIGNATURE_ALGO_ASN1_LEN_LEN = 1; + + // Currently we only support RSA for signature algorithm. + @Retention(RetentionPolicy.SOURCE) + @StringDef({ + SIGNATURE_ALGO_RSA_SHA1, + SIGNATURE_ALGO_RSA_SHA2_256, + SIGNATURE_ALGO_RSA_SHA2_384, + SIGNATURE_ALGO_RSA_SHA2_512 + }) + public @interface SignatureAlgo {} + + 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"; + // TODO: Allow users to configure authentication method using @SignatureAlgo + + public final String signatureAlgoAndHash; + public final byte[] signature; + + protected IkeAuthDigitalSignPayload( + boolean critical, @AuthMethod int authMethod, byte[] authData) throws IkeException { + super(critical, authMethod); + switch (authMethod) { + case AUTH_METHOD_RSA_DIGITAL_SIGN: + signatureAlgoAndHash = SIGNATURE_ALGO_RSA_SHA1; + signature = authData; + break; + case AUTH_METHOD_GENERIC_DIGITAL_SIGN: + ByteBuffer inputBuffer = ByteBuffer.wrap(authData); + + // Get signature algorithm. + int signAlgoLen = Byte.toUnsignedInt(inputBuffer.get()); + byte[] signAlgoBytes = new byte[signAlgoLen]; + inputBuffer.get(signAlgoBytes); + signatureAlgoAndHash = bytesToSignAlgoName(signAlgoBytes); + + // Get signature. + signature = new byte[authData.length - SIGNATURE_ALGO_ASN1_LEN_LEN - signAlgoLen]; + inputBuffer.get(signature); + break; + default: + // Won't hit here. + throw new IllegalArgumentException("Unrecognized authentication method."); + } + } + + private String bytesToSignAlgoName(byte[] signAlgoBytes) throws AuthenticationFailedException { + if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA1, signAlgoBytes)) { + return SIGNATURE_ALGO_RSA_SHA1; + } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_256, signAlgoBytes)) { + return SIGNATURE_ALGO_RSA_SHA2_256; + } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_384, signAlgoBytes)) { + return SIGNATURE_ALGO_RSA_SHA2_384; + } else if (Arrays.equals(PKI_ALGO_ID_DER_BYTES_RSA_SHA2_512, signAlgoBytes)) { + return SIGNATURE_ALGO_RSA_SHA2_512; + } else { + throw new AuthenticationFailedException( + "Unrecognized ASN.1 objects for Signature algorithm and Hash"); + } + } + + // TODO: Add methods for generating and validating signature. + + @Override + protected void encodeToByteBuffer(@PayloadType int nextPayload, ByteBuffer byteBuffer) { + // TODO: Implement it. + throw new UnsupportedOperationException( + "It is not supported to encode a " + getTypeString()); + } + + @Override + protected int getPayloadLength() { + // TODO: Implement it. + throw new UnsupportedOperationException( + "It is not supported to get payload length of " + getTypeString()); + } + + @Override + public String getTypeString() { + return "Authentication-Digital-Signature Payload"; + } +} diff --git a/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java b/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java index 251235fc..beb2bdc6 100644 --- a/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeAuthPayload.java @@ -81,24 +81,30 @@ public abstract class IkeAuthPayload extends IkePayload { // TODO: Handle RSA and generic signature-based authentication. case AUTH_METHOD_PRE_SHARED_KEY: return new IkeAuthPskPayload(critical, authData); + case AUTH_METHOD_RSA_DIGITAL_SIGN: + return new IkeAuthDigitalSignPayload( + critical, AUTH_METHOD_RSA_DIGITAL_SIGN, authData); + case AUTH_METHOD_GENERIC_DIGITAL_SIGN: + return new IkeAuthDigitalSignPayload( + critical, AUTH_METHOD_GENERIC_DIGITAL_SIGN, authData); default: // TODO: Throw AuthenticationFailedException throw new UnsupportedOperationException("Unsupported authentication method"); } } - // Sign value with PRF when building outbound packet or verifying inbound packet. It is called + // Sign data with PRF when building outbound packet or verifying inbound packet. It is called // for calculating signature over ID payload for all types of authentication and also for // calculating signature over PSK for PSK authentication. - protected static byte[] signWithPrf(Mac prfMac, byte[] prfKeyBytes, byte[] value) + protected static byte[] signWithPrf(Mac prfMac, byte[] prfKeyBytes, byte[] dataToSign) throws InvalidKeyException { SecretKeySpec prfKey = new SecretKeySpec(prfKeyBytes, prfMac.getAlgorithm()); prfMac.init(prfKey); - ByteBuffer valueBuffer = ByteBuffer.wrap(value); + ByteBuffer dataBuffer = ByteBuffer.wrap(dataToSign); // Calculate MAC. - prfMac.update(valueBuffer); + prfMac.update(dataBuffer); return prfMac.doFinal(); } diff --git a/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java b/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java index 92c931a0..63f45dd5 100644 --- a/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java +++ b/src/java/com/android/ike/ikev2/message/IkeAuthPskPayload.java @@ -47,4 +47,9 @@ public final class IkeAuthPskPayload extends IkeAuthPayload { throw new UnsupportedOperationException( "It is not supported to get payload length of " + getTypeString()); } + + @Override + public String getTypeString() { + return "Authentication-PSK Payload"; + } } diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayloadTest.java new file mode 100644 index 00000000..a2c4d1f0 --- /dev/null +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeAuthDigitalSignPayloadTest.java @@ -0,0 +1,50 @@ +/* + * 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.message; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public final class IkeAuthDigitalSignPayloadTest { + + private static final String AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING = + "0e0000000f300d06092a864886f70d01010b05007b2f4456878b1344e803f094" + + "159a59361bc639071b69de41915452c478b77a46ce4a2c96ddc7ba2c18d08406" + + "50ce51c77124605423a2f75d8ed4b5a1ec5944c3396221a39e25def09abe5c9f" + + "6d9cd70e8f6254d4c835015256c9d6c26f0c6d31ac96a2ed802ccb16e48e7ff3" + + "daf736221b18c2a972130a69edb197a505a312882baed95d38a47bf6784533f2" + + "ffee671d742b5ae463216e46ef970ee6a335ffb3fc9c170a680fb802bb950cb0" + + "5601339be8869a73f8f85254d792b6e91697d8893ccd34b5fb6aad6268c4ab0f" + + "9ead7b3f8a4a255e1b2eabfa3da0de284f3954cf49271918dd2d2db95c8e7812" + + "9aea77e5761ac5683a0b5af300ceb52f5e8d8168"; + // TODO: Build a RSA_SHA1 signature and add tests for it. + + @Test + public void testDecodeGenericDigitalSignPayload() throws Exception { + byte[] inputPacket = + TestUtils.hexStringToByteArray(AUTH_PAYLOAD_BODY_GENERIC_DIGITAL_SIGN_HEX_STRING); + IkeAuthPayload payload = IkeAuthPayload.getIkeAuthPayload(false, inputPacket); + + assertTrue(payload instanceof IkeAuthDigitalSignPayload); + IkeAuthDigitalSignPayload dsPayload = (IkeAuthDigitalSignPayload) payload; + assertEquals( + IkeAuthDigitalSignPayload.SIGNATURE_ALGO_RSA_SHA2_256, + dsPayload.signatureAlgoAndHash); + } +} diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeNoncePayloadTest.java b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeNoncePayloadTest.java index a15507c5..f4e4a984 100644 --- a/tests/iketests/src/java/com/android/ike/ikev2/message/IkeNoncePayloadTest.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/IkeNoncePayloadTest.java @@ -41,7 +41,7 @@ public final class IkeNoncePayloadTest { payload.encodeToByteBuffer(NEXT_PAYLOAD_TYPE, byteBuffer); byte[] expectedNoncePayload = - TestUtils.hexStringToByteArray(NONCE_PAYLOAD_RAW_HEX_STRING); + TestUtils.hexStringToByteArray(NONCE_PAYLOAD_RAW_HEX_STRING); assertArrayEquals(expectedNoncePayload, byteBuffer.array()); } } diff --git a/tests/iketests/src/java/com/android/ike/ikev2/message/TestUtils.java b/tests/iketests/src/java/com/android/ike/ikev2/message/TestUtils.java index d1c82ddd..e77e799d 100644 --- a/tests/iketests/src/java/com/android/ike/ikev2/message/TestUtils.java +++ b/tests/iketests/src/java/com/android/ike/ikev2/message/TestUtils.java @@ -16,15 +16,26 @@ package com.android.ike.ikev2.message; +/** TestUtils provides utility methods for parsing Hex String */ public final class TestUtils { - static byte[] hexStringToByteArray(String s) { - int len = s.length(); + /** + * Converts the unsigned Hex String to a byte array. + * + * @param hexString hex representation of an unsigned value. + * @return the converted byte array. + * @throws IllegalArgumentException when length of Hex String is an odd number. + */ + public static byte[] hexStringToByteArray(String hexString) throws IllegalArgumentException { + int len = hexString.length(); + if (len % 2 != 0) { + throw new IllegalArgumentException("Invalid Hex String"); + } byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) - ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i + 1), 16)); + ((Character.digit(hexString.charAt(i), 16) << 4) + + Character.digit(hexString.charAt(i + 1), 16)); } return data; } |