1 /* 2 * Copyright (C) 2021 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 17 package android.security.keystore2; 18 19 import android.security.keymaster.KeymasterArguments; 20 import android.security.keymaster.KeymasterDefs; 21 import android.security.keystore.KeyProperties; 22 23 import java.security.ProviderException; 24 25 /** 26 * @hide 27 */ 28 public abstract class KeymasterUtils { 29 KeymasterUtils()30 private KeymasterUtils() {} 31 32 /** @hide */ getDigestOutputSizeBits(int keymasterDigest)33 static int getDigestOutputSizeBits(int keymasterDigest) { 34 switch (keymasterDigest) { 35 case KeymasterDefs.KM_DIGEST_NONE: 36 return -1; 37 case KeymasterDefs.KM_DIGEST_MD5: 38 return 128; 39 case KeymasterDefs.KM_DIGEST_SHA1: 40 return 160; 41 case KeymasterDefs.KM_DIGEST_SHA_2_224: 42 return 224; 43 case KeymasterDefs.KM_DIGEST_SHA_2_256: 44 return 256; 45 case KeymasterDefs.KM_DIGEST_SHA_2_384: 46 return 384; 47 case KeymasterDefs.KM_DIGEST_SHA_2_512: 48 return 512; 49 default: 50 throw new IllegalArgumentException("Unknown digest: " + keymasterDigest); 51 } 52 } 53 54 /** @hide */ isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( int keymasterBlockMode)55 static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto( 56 int keymasterBlockMode) { 57 switch (keymasterBlockMode) { 58 case KeymasterDefs.KM_MODE_ECB: 59 return false; 60 case KeymasterDefs.KM_MODE_CBC: 61 case KeymasterDefs.KM_MODE_CTR: 62 case KeymasterDefs.KM_MODE_GCM: 63 return true; 64 default: 65 throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode); 66 } 67 } 68 69 /** @hide */ isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( int keymasterPadding)70 static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto( 71 int keymasterPadding) { 72 switch (keymasterPadding) { 73 case KeymasterDefs.KM_PAD_NONE: 74 return false; 75 case KeymasterDefs.KM_PAD_RSA_OAEP: 76 case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT: 77 return true; 78 default: 79 throw new IllegalArgumentException( 80 "Unsupported asymmetric encryption padding scheme: " + keymasterPadding); 81 } 82 } 83 84 /** 85 * Adds {@code KM_TAG_MIN_MAC_LENGTH} tag, if necessary, to the keymaster arguments for 86 * generating or importing a key. This tag may only be needed for symmetric keys (e.g., HMAC, 87 * AES-GCM). 88 */ addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args, int keymasterAlgorithm, int[] keymasterBlockModes, int[] keymasterDigests)89 public static void addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args, 90 int keymasterAlgorithm, 91 int[] keymasterBlockModes, 92 int[] keymasterDigests) { 93 switch (keymasterAlgorithm) { 94 case KeymasterDefs.KM_ALGORITHM_AES: 95 if (com.android.internal.util.ArrayUtils.contains( 96 keymasterBlockModes, KeymasterDefs.KM_MODE_GCM)) { 97 // AES GCM key needs the minimum length of AEAD tag specified. 98 args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, 99 AndroidKeyStoreAuthenticatedAESCipherSpi.GCM 100 .MIN_SUPPORTED_TAG_LENGTH_BITS); 101 } 102 break; 103 case KeymasterDefs.KM_ALGORITHM_HMAC: 104 // HMAC key needs the minimum length of MAC set to the output size of the associated 105 // digest. This is because we do not offer a way to generate shorter MACs and 106 // don't offer a way to verify MACs (other than by generating them). 107 if (keymasterDigests.length != 1) { 108 throw new ProviderException( 109 "Unsupported number of authorized digests for HMAC key: " 110 + keymasterDigests.length 111 + ". Exactly one digest must be authorized"); 112 } 113 int keymasterDigest = keymasterDigests[0]; 114 int digestOutputSizeBits = getDigestOutputSizeBits(keymasterDigest); 115 if (digestOutputSizeBits == -1) { 116 throw new ProviderException( 117 "HMAC key authorized for unsupported digest: " 118 + KeyProperties.Digest.fromKeymaster(keymasterDigest)); 119 } 120 args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits); 121 break; 122 } 123 } 124 } 125