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 */ IpSecAlgorithm(@onNull @lgorithmName String algorithm, @NonNull byte[] key)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 */ IpSecAlgorithm( @onNull @lgorithmName String algorithm, @NonNull byte[] key, int truncLenBits)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 getName()166 public String getName() { 167 return mName; 168 } 169 170 /** Get the key for this algorithm */ 171 @NonNull getKey()172 public byte[] getKey() { 173 return mKey.clone(); 174 } 175 176 /** Get the truncation length of this algorithm, in bits */ getTruncationLengthBits()177 public int getTruncationLengthBits() { 178 return mTruncLenBits; 179 } 180 181 /* Parcelable Implementation */ describeContents()182 public int describeContents() { 183 return 0; 184 } 185 186 /** Write to parcel */ writeToParcel(Parcel out, int flags)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 @android.annotation.NonNull 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 checkValidOrThrow(String name, int keyLen, int truncLen)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 */ isAuthentication()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 */ isEncryption()270 public boolean isEncryption() { 271 return getName().equals(CRYPT_AES_CBC); 272 } 273 274 /** @hide */ isAead()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. isUnsafeBuild()281 private static boolean isUnsafeBuild() { 282 return Build.IS_DEBUGGABLE && Build.IS_ENG; 283 } 284 285 @Override 286 @NonNull toString()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 equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs)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