IpSecAlgorithm.java revision 9be845c5cd73a975b19182e720e15cce55810103
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 */ 16package android.net; 17 18import android.annotation.NonNull; 19import android.annotation.StringDef; 20import android.os.Build; 21import android.os.Parcel; 22import android.os.Parcelable; 23 24import com.android.internal.annotations.VisibleForTesting; 25import com.android.internal.util.HexDump; 26 27import java.lang.annotation.Retention; 28import java.lang.annotation.RetentionPolicy; 29import 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 */ 37public 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 * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128. 60 */ 61 public static final String AUTH_HMAC_MD5 = "hmac(md5)"; 62 63 /** 64 * SHA1 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in 65 * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b> 66 * 67 * <p>Keys for this algorithm must be 160 bits in length. 68 * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160. 69 */ 70 public static final String AUTH_HMAC_SHA1 = "hmac(sha1)"; 71 72 /** 73 * SHA256 HMAC Authentication/Integrity Algorithm. 74 * 75 * <p>Keys for this algorithm must be 256 bits in length. 76 * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256. 77 */ 78 public static final String AUTH_HMAC_SHA256 = "hmac(sha256)"; 79 80 /** 81 * SHA384 HMAC Authentication/Integrity Algorithm. 82 * 83 * <p>Keys for this algorithm must be 384 bits in length. 84 * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384. 85 */ 86 public static final String AUTH_HMAC_SHA384 = "hmac(sha384)"; 87 88 /** 89 * SHA512 HMAC Authentication/Integrity Algorithm. 90 * 91 * <p>Keys for this algorithm must be 512 bits in length. 92 * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512. 93 */ 94 public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; 95 96 /** 97 * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm. 98 * 99 * <p>Valid lengths for keying material are {160, 224, 288}. 100 * 101 * <p>As per <a href="https://tools.ietf.org/html/rfc4106#section-8.1">RFC4106 (Section 102 * 8.1)</a>, keying material consists of a 128, 192, or 256 bit AES key followed by a 32-bit 103 * salt. RFC compliance requires that the salt must be unique per invocation with the same key. 104 * 105 * <p>Valid ICV (truncation) lengths are {64, 96, 128}. 106 */ 107 public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))"; 108 109 /** @hide */ 110 @StringDef({ 111 CRYPT_AES_CBC, 112 AUTH_HMAC_MD5, 113 AUTH_HMAC_SHA1, 114 AUTH_HMAC_SHA256, 115 AUTH_HMAC_SHA512, 116 AUTH_CRYPT_AES_GCM 117 }) 118 @Retention(RetentionPolicy.SOURCE) 119 public @interface AlgorithmName {} 120 121 private final String mName; 122 private final byte[] mKey; 123 private final int mTruncLenBits; 124 125 /** 126 * Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are 127 * defined as constants in this class. 128 * 129 * @param algorithm name of the algorithm. 130 * @param key key padded to a multiple of 8 bits. 131 */ 132 public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key) { 133 this(algorithm, key, key.length * 8); 134 } 135 136 /** 137 * Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are 138 * defined as constants in this class. 139 * 140 * <p>This constructor only supports algorithms that use a truncation length. i.e. 141 * Authentication and Authenticated Encryption algorithms. 142 * 143 * @param algorithm name of the algorithm. 144 * @param key key padded to a multiple of 8 bits. 145 * @param truncLenBits number of bits of output hash to use. 146 */ 147 public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) { 148 mName = algorithm; 149 mKey = key.clone(); 150 mTruncLenBits = truncLenBits; 151 checkValidOrThrow(mName, mKey.length * 8, mTruncLenBits); 152 } 153 154 /** Get the algorithm name */ 155 public String getName() { 156 return mName; 157 } 158 159 /** Get the key for this algorithm */ 160 public byte[] getKey() { 161 return mKey.clone(); 162 } 163 164 /** Get the truncation length of this algorithm, in bits */ 165 public int getTruncationLengthBits() { 166 return mTruncLenBits; 167 } 168 169 /* Parcelable Implementation */ 170 public int describeContents() { 171 return 0; 172 } 173 174 /** Write to parcel */ 175 public void writeToParcel(Parcel out, int flags) { 176 out.writeString(mName); 177 out.writeByteArray(mKey); 178 out.writeInt(mTruncLenBits); 179 } 180 181 /** Parcelable Creator */ 182 public static final Parcelable.Creator<IpSecAlgorithm> CREATOR = 183 new Parcelable.Creator<IpSecAlgorithm>() { 184 public IpSecAlgorithm createFromParcel(Parcel in) { 185 final String name = in.readString(); 186 final byte[] key = in.createByteArray(); 187 final int truncLenBits = in.readInt(); 188 189 return new IpSecAlgorithm(name, key, truncLenBits); 190 } 191 192 public IpSecAlgorithm[] newArray(int size) { 193 return new IpSecAlgorithm[size]; 194 } 195 }; 196 197 private static void checkValidOrThrow(String name, int keyLen, int truncLen) { 198 boolean isValidLen = true; 199 boolean isValidTruncLen = true; 200 201 switch(name) { 202 case CRYPT_AES_CBC: 203 isValidLen = keyLen == 128 || keyLen == 192 || keyLen == 256; 204 break; 205 case AUTH_HMAC_MD5: 206 isValidLen = keyLen == 128; 207 isValidTruncLen = truncLen >= 96 && truncLen <= 128; 208 break; 209 case AUTH_HMAC_SHA1: 210 isValidLen = keyLen == 160; 211 isValidTruncLen = truncLen >= 96 && truncLen <= 160; 212 break; 213 case AUTH_HMAC_SHA256: 214 isValidLen = keyLen == 256; 215 isValidTruncLen = truncLen >= 96 && truncLen <= 256; 216 break; 217 case AUTH_HMAC_SHA384: 218 isValidLen = keyLen == 384; 219 isValidTruncLen = truncLen >= 192 && truncLen <= 384; 220 break; 221 case AUTH_HMAC_SHA512: 222 isValidLen = keyLen == 512; 223 isValidTruncLen = truncLen >= 256 && truncLen <= 512; 224 break; 225 case AUTH_CRYPT_AES_GCM: 226 // The keying material for GCM is a key plus a 32-bit salt 227 isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32; 228 break; 229 default: 230 throw new IllegalArgumentException("Couldn't find an algorithm: " + name); 231 } 232 233 if (!isValidLen) { 234 throw new IllegalArgumentException("Invalid key material keyLength: " + keyLen); 235 } 236 if (!isValidTruncLen) { 237 throw new IllegalArgumentException("Invalid truncation keyLength: " + truncLen); 238 } 239 } 240 241 /** @hide */ 242 public boolean isAuthentication() { 243 switch (getName()) { 244 // Fallthrough 245 case AUTH_HMAC_MD5: 246 case AUTH_HMAC_SHA1: 247 case AUTH_HMAC_SHA256: 248 case AUTH_HMAC_SHA384: 249 case AUTH_HMAC_SHA512: 250 return true; 251 default: 252 return false; 253 } 254 } 255 256 /** @hide */ 257 public boolean isEncryption() { 258 return getName().equals(CRYPT_AES_CBC); 259 } 260 261 /** @hide */ 262 public boolean isAead() { 263 return getName().equals(AUTH_CRYPT_AES_GCM); 264 } 265 266 // Because encryption keys are sensitive and userdebug builds are used by large user pools 267 // such as beta testers, we only allow sensitive info such as keys on eng builds. 268 private static boolean isUnsafeBuild() { 269 return Build.IS_DEBUGGABLE && Build.IS_ENG; 270 } 271 272 @Override 273 public String toString() { 274 return new StringBuilder() 275 .append("{mName=") 276 .append(mName) 277 .append(", mKey=") 278 .append(isUnsafeBuild() ? HexDump.toHexString(mKey) : "<hidden>") 279 .append(", mTruncLenBits=") 280 .append(mTruncLenBits) 281 .append("}") 282 .toString(); 283 } 284 285 /** @hide */ 286 @VisibleForTesting 287 public static boolean equals(IpSecAlgorithm lhs, IpSecAlgorithm rhs) { 288 if (lhs == null || rhs == null) return (lhs == rhs); 289 return (lhs.mName.equals(rhs.mName) 290 && Arrays.equals(lhs.mKey, rhs.mKey) 291 && lhs.mTruncLenBits == rhs.mTruncLenBits); 292 } 293}; 294