Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.net;
     17 
     18 import android.annotation.NonNull;
     19 import android.annotation.StringDef;
     20 import android.os.Build;
     21 import android.os.Parcel;
     22 import android.os.Parcelable;
     23 
     24 import com.android.internal.annotations.VisibleForTesting;
     25 import com.android.internal.util.HexDump;
     26 
     27 import java.lang.annotation.Retention;
     28 import java.lang.annotation.RetentionPolicy;
     29 import java.util.Arrays;
     30 
     31 /**
     32  * This class represents a single algorithm that can be used by an {@link IpSecTransform}.
     33  *
     34  * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
     35  * Internet Protocol</a>
     36  */
     37 public final class IpSecAlgorithm implements Parcelable {
     38     private static final String TAG = "IpSecAlgorithm";
     39 
     40     /**
     41      * Null cipher.
     42      *
     43      * @hide
     44      */
     45     public static final String CRYPT_NULL = "ecb(cipher_null)";
     46 
     47     /**
     48      * AES-CBC Encryption/Ciphering Algorithm.
     49      *
     50      * <p>Valid lengths for this key are {128, 192, 256}.
     51      */
     52     public static final String CRYPT_AES_CBC = "cbc(aes)";
     53 
     54     /**
     55      * MD5 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in
     56      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
     57      *
     58      * <p>Keys for this algorithm must be 128 bits in length.
     59      *
     60      * <p>Valid truncation lengths are multiples of 8 bits from 96 to 128.
     61      */
     62     public static final String AUTH_HMAC_MD5 = "hmac(md5)";
     63 
     64     /**
     65      * SHA1 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in
     66      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
     67      *
     68      * <p>Keys for this algorithm must be 160 bits in length.
     69      *
     70      * <p>Valid truncation lengths are multiples of 8 bits from 96 to 160.
     71      */
     72     public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
     73 
     74     /**
     75      * SHA256 HMAC Authentication/Integrity Algorithm.
     76      *
     77      * <p>Keys for this algorithm must be 256 bits in length.
     78      *
     79      * <p>Valid truncation lengths are multiples of 8 bits from 96 to 256.
     80      */
     81     public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
     82 
     83     /**
     84      * SHA384 HMAC Authentication/Integrity Algorithm.
     85      *
     86      * <p>Keys for this algorithm must be 384 bits in length.
     87      *
     88      * <p>Valid truncation lengths are multiples of 8 bits from 192 to 384.
     89      */
     90     public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
     91 
     92     /**
     93      * SHA512 HMAC Authentication/Integrity Algorithm.
     94      *
     95      * <p>Keys for this algorithm must be 512 bits in length.
     96      *
     97      * <p>Valid truncation lengths are multiples of 8 bits from 256 to 512.
     98      */
     99     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
    100 
    101     /**
    102      * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
    103      *
    104      * <p>Valid lengths for keying material are {160, 224, 288}.
    105      *
    106      * <p>As per <a href="https://tools.ietf.org/html/rfc4106#section-8.1">RFC4106 (Section
    107      * 8.1)</a>, keying material consists of a 128, 192, or 256 bit AES key followed by a 32-bit
    108      * salt. RFC compliance requires that the salt must be unique per invocation with the same key.
    109      *
    110      * <p>Valid ICV (truncation) lengths are {64, 96, 128}.
    111      */
    112     public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
    113 
    114     /** @hide */
    115     @StringDef({
    116         CRYPT_AES_CBC,
    117         AUTH_HMAC_MD5,
    118         AUTH_HMAC_SHA1,
    119         AUTH_HMAC_SHA256,
    120         AUTH_HMAC_SHA384,
    121         AUTH_HMAC_SHA512,
    122         AUTH_CRYPT_AES_GCM
    123     })
    124     @Retention(RetentionPolicy.SOURCE)
    125     public @interface AlgorithmName {}
    126 
    127     private final String mName;
    128     private final byte[] mKey;
    129     private final int mTruncLenBits;
    130 
    131     /**
    132      * Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are
    133      * defined as constants in this class.
    134      *
    135      * <p>For algorithms that produce an integrity check value, the truncation length is a required
    136      * parameter. See {@link #IpSecAlgorithm(String algorithm, byte[] key, int truncLenBits)}
    137      *
    138      * @param algorithm name of the algorithm.
    139      * @param key key padded to a multiple of 8 bits.
    140      */
    141     public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
    142         this(algorithm, key, 0);
    143     }
    144 
    145     /**
    146      * Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are
    147      * defined as constants in this class.
    148      *
    149      * <p>This constructor only supports algorithms that use a truncation length. i.e.
    150      * Authentication and Authenticated Encryption algorithms.
    151      *
    152      * @param algorithm name of the algorithm.
    153      * @param key key padded to a multiple of 8 bits.
    154      * @param truncLenBits number of bits of output hash to use.
    155      */
    156     public IpSecAlgorithm(
    157             @NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
    158         mName = algorithm;
    159         mKey = key.clone();
    160         mTruncLenBits = truncLenBits;
    161         checkValidOrThrow(mName, mKey.length * 8, mTruncLenBits);
    162     }
    163 
    164     /** Get the algorithm name */
    165     @NonNull
    166     public String getName() {
    167         return mName;
    168     }
    169 
    170     /** Get the key for this algorithm */
    171     @NonNull
    172     public byte[] getKey() {
    173         return mKey.clone();
    174     }
    175 
    176     /** Get the truncation length of this algorithm, in bits */
    177     public int getTruncationLengthBits() {
    178         return mTruncLenBits;
    179     }
    180 
    181     /* Parcelable Implementation */
    182     public int describeContents() {
    183         return 0;
    184     }
    185 
    186     /** Write to parcel */
    187     public void writeToParcel(Parcel out, int flags) {
    188         out.writeString(mName);
    189         out.writeByteArray(mKey);
    190         out.writeInt(mTruncLenBits);
    191     }
    192 
    193     /** Parcelable Creator */
    194     public static final Parcelable.Creator<IpSecAlgorithm> CREATOR =
    195             new Parcelable.Creator<IpSecAlgorithm>() {
    196                 public IpSecAlgorithm createFromParcel(Parcel in) {
    197                     final String name = in.readString();
    198                     final byte[] key = in.createByteArray();
    199                     final int truncLenBits = in.readInt();
    200 
    201                     return new IpSecAlgorithm(name, key, truncLenBits);
    202                 }
    203 
    204                 public IpSecAlgorithm[] newArray(int size) {
    205                     return new IpSecAlgorithm[size];
    206                 }
    207             };
    208 
    209     private static void checkValidOrThrow(String name, int keyLen, int truncLen) {
    210         boolean isValidLen = true;
    211         boolean isValidTruncLen = true;
    212 
    213         switch(name) {
    214             case CRYPT_AES_CBC:
    215                 isValidLen = keyLen == 128 || keyLen == 192 || keyLen == 256;
    216                 break;
    217             case AUTH_HMAC_MD5:
    218                 isValidLen = keyLen == 128;
    219                 isValidTruncLen = truncLen >= 96 && truncLen <= 128;
    220                 break;
    221             case AUTH_HMAC_SHA1:
    222                 isValidLen = keyLen == 160;
    223                 isValidTruncLen = truncLen >= 96 && truncLen <= 160;
    224                 break;
    225             case AUTH_HMAC_SHA256:
    226                 isValidLen = keyLen == 256;
    227                 isValidTruncLen = truncLen >= 96 && truncLen <= 256;
    228                 break;
    229             case AUTH_HMAC_SHA384:
    230                 isValidLen = keyLen == 384;
    231                 isValidTruncLen = truncLen >= 192 && truncLen <= 384;
    232                 break;
    233             case AUTH_HMAC_SHA512:
    234                 isValidLen = keyLen == 512;
    235                 isValidTruncLen = truncLen >= 256 && truncLen <= 512;
    236                 break;
    237             case AUTH_CRYPT_AES_GCM:
    238                 // The keying material for GCM is a key plus a 32-bit salt
    239                 isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
    240                 isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
    241                 break;
    242             default:
    243                 throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
    244         }
    245 
    246         if (!isValidLen) {
    247             throw new IllegalArgumentException("Invalid key material keyLength: " + keyLen);
    248         }
    249         if (!isValidTruncLen) {
    250             throw new IllegalArgumentException("Invalid truncation keyLength: " + truncLen);
    251         }
    252     }
    253 
    254     /** @hide */
    255     public boolean isAuthentication() {
    256         switch (getName()) {
    257             // Fallthrough
    258             case AUTH_HMAC_MD5:
    259             case AUTH_HMAC_SHA1:
    260             case AUTH_HMAC_SHA256:
    261             case AUTH_HMAC_SHA384:
    262             case AUTH_HMAC_SHA512:
    263                 return true;
    264             default:
    265                 return false;
    266         }
    267     }
    268 
    269     /** @hide */
    270     public boolean isEncryption() {
    271         return getName().equals(CRYPT_AES_CBC);
    272     }
    273 
    274     /** @hide */
    275     public boolean isAead() {
    276         return getName().equals(AUTH_CRYPT_AES_GCM);
    277     }
    278 
    279     // Because encryption keys are sensitive and userdebug builds are used by large user pools
    280     // such as beta testers, we only allow sensitive info such as keys on eng builds.
    281     private static boolean isUnsafeBuild() {
    282         return Build.IS_DEBUGGABLE && Build.IS_ENG;
    283     }
    284 
    285     @Override
    286     @NonNull
    287     public String toString() {
    288         return new StringBuilder()
    289                 .append("{mName=")
    290                 .append(mName)
    291                 .append(", mKey=")
    292                 .append(isUnsafeBuild() ? HexDump.toHexString(mKey) : "<hidden>")
    293                 .append(", mTruncLenBits=")
    294                 .append(mTruncLenBits)
    295                 .append("}")
    296                 .toString();
    297     }
    298 
    299     /** @hide */
    300     @VisibleForTesting
    301     public static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) {
    302         if (lhs == null || rhs == null) return (lhs == rhs);
    303         return (lhs.mName.equals(rhs.mName)
    304                 && Arrays.equals(lhs.mKey, rhs.mKey)
    305                 && lhs.mTruncLenBits == rhs.mTruncLenBits);
    306     }
    307 };
    308