diff options
| -rw-r--r-- | core/java/android/net/IpSecAlgorithm.java | 72 | ||||
| -rw-r--r-- | tests/net/java/android/net/IpSecAlgorithmTest.java | 115 | ||||
| -rw-r--r-- | tests/net/java/android/net/IpSecConfigTest.java | 4 |
3 files changed, 167 insertions, 24 deletions
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java index d6e62cf1f88d..f82627b942c3 100644 --- a/core/java/android/net/IpSecAlgorithm.java +++ b/core/java/android/net/IpSecAlgorithm.java @@ -21,6 +21,7 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; import java.lang.annotation.Retention; @@ -34,6 +35,8 @@ import java.util.Arrays; * Internet Protocol</a> */ public final class IpSecAlgorithm implements Parcelable { + private static final String TAG = "IpSecAlgorithm"; + /** * AES-CBC Encryption/Ciphering Algorithm. * @@ -45,6 +48,7 @@ public final class IpSecAlgorithm implements Parcelable { * MD5 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b> * + * <p>Keys for this algorithm must be 128 bits in length. * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128. */ public static final String AUTH_HMAC_MD5 = "hmac(md5)"; @@ -53,6 +57,7 @@ public final class IpSecAlgorithm implements Parcelable { * SHA1 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b> * + * <p>Keys for this algorithm must be 160 bits in length. * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160. */ public static final String AUTH_HMAC_SHA1 = "hmac(sha1)"; @@ -60,6 +65,7 @@ public final class IpSecAlgorithm implements Parcelable { /** * SHA256 HMAC Authentication/Integrity Algorithm. * + * <p>Keys for this algorithm must be 256 bits in length. * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256. */ public static final String AUTH_HMAC_SHA256 = "hmac(sha256)"; @@ -67,6 +73,7 @@ public final class IpSecAlgorithm implements Parcelable { /** * SHA384 HMAC Authentication/Integrity Algorithm. * + * <p>Keys for this algorithm must be 384 bits in length. * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384. */ public static final String AUTH_HMAC_SHA384 = "hmac(sha384)"; @@ -74,6 +81,7 @@ public final class IpSecAlgorithm implements Parcelable { /** * SHA512 HMAC Authentication/Integrity Algorithm. * + * <p>Keys for this algorithm must be 512 bits in length. * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512. */ public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; @@ -130,12 +138,10 @@ public final class IpSecAlgorithm implements Parcelable { * @param truncLenBits number of bits of output hash to use. */ public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) { - if (!isTruncationLengthValid(algorithm, truncLenBits)) { - throw new IllegalArgumentException("Unknown algorithm or invalid length"); - } mName = algorithm; mKey = key.clone(); - mTruncLenBits = Math.min(truncLenBits, key.length * 8); + mTruncLenBits = truncLenBits; + checkValidOrThrow(mName, mKey.length * 8, mTruncLenBits); } /** Get the algorithm name */ @@ -169,7 +175,11 @@ public final class IpSecAlgorithm implements Parcelable { public static final Parcelable.Creator<IpSecAlgorithm> CREATOR = new Parcelable.Creator<IpSecAlgorithm>() { public IpSecAlgorithm createFromParcel(Parcel in) { - return new IpSecAlgorithm(in); + final String name = in.readString(); + final byte[] key = in.createByteArray(); + final int truncLenBits = in.readInt(); + + return new IpSecAlgorithm(name, key, truncLenBits); } public IpSecAlgorithm[] newArray(int size) { @@ -177,30 +187,47 @@ public final class IpSecAlgorithm implements Parcelable { } }; - private IpSecAlgorithm(Parcel in) { - mName = in.readString(); - mKey = in.createByteArray(); - mTruncLenBits = in.readInt(); - } + private static void checkValidOrThrow(String name, int keyLen, int truncLen) { + boolean isValidLen = true; + boolean isValidTruncLen = true; - private static boolean isTruncationLengthValid(String algo, int truncLenBits) { - switch (algo) { + switch(name) { case CRYPT_AES_CBC: - return (truncLenBits == 128 || truncLenBits == 192 || truncLenBits == 256); + isValidLen = keyLen == 128 || keyLen == 192 || keyLen == 256; + break; case AUTH_HMAC_MD5: - return (truncLenBits >= 96 && truncLenBits <= 128); + isValidLen = keyLen == 128; + isValidTruncLen = truncLen >= 96 && truncLen <= 128; + break; case AUTH_HMAC_SHA1: - return (truncLenBits >= 96 && truncLenBits <= 160); + isValidLen = keyLen == 160; + isValidTruncLen = truncLen >= 96 && truncLen <= 160; + break; case AUTH_HMAC_SHA256: - return (truncLenBits >= 96 && truncLenBits <= 256); + isValidLen = keyLen == 256; + isValidTruncLen = truncLen >= 96 && truncLen <= 256; + break; case AUTH_HMAC_SHA384: - return (truncLenBits >= 192 && truncLenBits <= 384); + isValidLen = keyLen == 384; + isValidTruncLen = truncLen >= 192 && truncLen <= 384; + break; case AUTH_HMAC_SHA512: - return (truncLenBits >= 256 && truncLenBits <= 512); + isValidLen = keyLen == 512; + isValidTruncLen = truncLen >= 256 && truncLen <= 512; + break; case AUTH_CRYPT_AES_GCM: - return (truncLenBits == 64 || truncLenBits == 96 || truncLenBits == 128); + // The keying material for GCM is a key plus a 32-bit salt + isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32; + break; default: - return false; + throw new IllegalArgumentException("Couldn't find an algorithm: " + name); + } + + if (!isValidLen) { + throw new IllegalArgumentException("Invalid key material keyLength: " + keyLen); + } + if (!isValidTruncLen) { + throw new IllegalArgumentException("Invalid truncation keyLength: " + truncLen); } } @@ -217,8 +244,9 @@ public final class IpSecAlgorithm implements Parcelable { .toString(); } - /** package */ - static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) { + /** @hide */ + @VisibleForTesting + public static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) { if (lhs == null || rhs == null) return (lhs == rhs); return (lhs.mName.equals(rhs.mName) && Arrays.equals(lhs.mKey, rhs.mKey) diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java new file mode 100644 index 000000000000..6bdfdc6db2d1 --- /dev/null +++ b/tests/net/java/android/net/IpSecAlgorithmTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 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 android.net; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.os.Parcel; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import java.util.Arrays; +import java.util.Random; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link IpSecAlgorithm}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class IpSecAlgorithmTest { + + private static final byte[] KEY_MATERIAL; + + static { + KEY_MATERIAL = new byte[128]; + new Random().nextBytes(KEY_MATERIAL); + }; + + @Test + public void testDefaultTruncLen() throws Exception { + IpSecAlgorithm explicit = + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8), 256); + IpSecAlgorithm implicit = + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8)); + assertTrue( + "Default Truncation Length Incorrect, Explicit: " + + explicit + + "implicit: " + + implicit, + IpSecAlgorithm.equals(explicit, implicit)); + } + + @Test + public void testTruncLenValidation() throws Exception { + for (int truncLen : new int[] {256, 512}) { + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA512, + Arrays.copyOf(KEY_MATERIAL, 512 / 8), + truncLen); + } + + for (int truncLen : new int[] {255, 513}) { + try { + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA512, + Arrays.copyOf(KEY_MATERIAL, 512 / 8), + truncLen); + fail("Invalid truncation length not validated"); + } catch (IllegalArgumentException pass) { + } + } + } + + @Test + public void testLenValidation() throws Exception { + for (int len : new int[] {128, 192, 256}) { + new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, len / 8)); + } + try { + new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 384 / 8)); + fail("Invalid key length not validated"); + } catch (IllegalArgumentException pass) { + } + } + + @Test + public void testAlgoNameValidation() throws Exception { + try { + new IpSecAlgorithm("rot13", Arrays.copyOf(KEY_MATERIAL, 128 / 8)); + fail("Invalid algorithm name not validated"); + } catch (IllegalArgumentException pass) { + } + } + + @Test + public void testParcelUnparcel() throws Exception { + IpSecAlgorithm init = + new IpSecAlgorithm( + IpSecAlgorithm.AUTH_HMAC_SHA512, Arrays.copyOf(KEY_MATERIAL, 512 / 8), 256); + + Parcel p = Parcel.obtain(); + p.setDataPosition(0); + init.writeToParcel(p, 0); + + p.setDataPosition(0); + IpSecAlgorithm fin = IpSecAlgorithm.CREATOR.createFromParcel(p); + assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin)); + p.recycle(); + } +} diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java index 1b4bef5d2f43..efc01f2ace6f 100644 --- a/tests/net/java/android/net/IpSecConfigTest.java +++ b/tests/net/java/android/net/IpSecConfigTest.java @@ -71,7 +71,7 @@ public class IpSecConfigTest { c.setAuthentication( IpSecTransform.DIRECTION_OUT, new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA1, + IpSecAlgorithm.AUTH_HMAC_MD5, new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0})); c.setSpiResourceId(IpSecTransform.DIRECTION_OUT, 1984); c.setEncryption( @@ -82,7 +82,7 @@ public class IpSecConfigTest { c.setAuthentication( IpSecTransform.DIRECTION_IN, new IpSecAlgorithm( - IpSecAlgorithm.AUTH_HMAC_SHA1, + IpSecAlgorithm.AUTH_HMAC_MD5, new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 1})); c.setSpiResourceId(IpSecTransform.DIRECTION_IN, 99); assertParcelingIsLossless(c); |