1 /* 2 * Copyright (C) 2015 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.hardware.security.keymint.KeyParameter; 20 import android.security.KeyStoreException; 21 import android.security.KeyStoreOperation; 22 import android.security.keymaster.KeymasterDefs; 23 import android.security.keystore.KeyStoreCryptoOperation; 24 25 import java.security.InvalidAlgorithmParameterException; 26 import java.security.InvalidKeyException; 27 import java.security.Key; 28 import java.security.ProviderException; 29 import java.security.spec.AlgorithmParameterSpec; 30 import java.util.ArrayList; 31 import java.util.List; 32 33 import javax.crypto.MacSpi; 34 35 /** 36 * {@link MacSpi} which provides HMAC implementations backed by Android KeyStore. 37 * 38 * @hide 39 */ 40 public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation { 41 42 private static final String TAG = "AndroidKeyStoreHmacSpi"; 43 44 public static class HmacSHA1 extends AndroidKeyStoreHmacSpi { HmacSHA1()45 public HmacSHA1() { 46 super(KeymasterDefs.KM_DIGEST_SHA1); 47 } 48 } 49 50 public static class HmacSHA224 extends AndroidKeyStoreHmacSpi { HmacSHA224()51 public HmacSHA224() { 52 super(KeymasterDefs.KM_DIGEST_SHA_2_224); 53 } 54 } 55 56 public static class HmacSHA256 extends AndroidKeyStoreHmacSpi { HmacSHA256()57 public HmacSHA256() { 58 super(KeymasterDefs.KM_DIGEST_SHA_2_256); 59 } 60 } 61 62 public static class HmacSHA384 extends AndroidKeyStoreHmacSpi { HmacSHA384()63 public HmacSHA384() { 64 super(KeymasterDefs.KM_DIGEST_SHA_2_384); 65 } 66 } 67 68 public static class HmacSHA512 extends AndroidKeyStoreHmacSpi { HmacSHA512()69 public HmacSHA512() { 70 super(KeymasterDefs.KM_DIGEST_SHA_2_512); 71 } 72 } 73 74 private final int mKeymasterDigest; 75 private final int mMacSizeBits; 76 77 // Fields below are populated by engineInit and should be preserved after engineDoFinal. 78 private AndroidKeyStoreSecretKey mKey; 79 80 // Fields below are reset when engineDoFinal succeeds. 81 private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer; 82 private KeyStoreOperation mOperation; 83 private long mOperationChallenge; 84 AndroidKeyStoreHmacSpi(int keymasterDigest)85 protected AndroidKeyStoreHmacSpi(int keymasterDigest) { 86 mKeymasterDigest = keymasterDigest; 87 mMacSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest); 88 mOperation = null; 89 mOperationChallenge = 0; 90 mKey = null; 91 mChunkedStreamer = null; 92 } 93 94 @Override engineGetMacLength()95 protected int engineGetMacLength() { 96 return (mMacSizeBits + 7) / 8; 97 } 98 99 @Override engineInit(Key key, AlgorithmParameterSpec params)100 protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, 101 InvalidAlgorithmParameterException { 102 resetAll(); 103 104 boolean success = false; 105 try { 106 init(key, params); 107 ensureKeystoreOperationInitialized(); 108 success = true; 109 } finally { 110 if (!success) { 111 resetAll(); 112 } 113 } 114 } 115 init(Key key, AlgorithmParameterSpec params)116 private void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, 117 InvalidAlgorithmParameterException { 118 if (key == null) { 119 throw new InvalidKeyException("key == null"); 120 } else if (!(key instanceof AndroidKeyStoreSecretKey)) { 121 throw new InvalidKeyException( 122 "Only Android KeyStore secret keys supported. Key: " + key); 123 } 124 mKey = (AndroidKeyStoreSecretKey) key; 125 126 if (params != null) { 127 throw new InvalidAlgorithmParameterException( 128 "Unsupported algorithm parameters: " + params); 129 } 130 131 } 132 abortOperation()133 private void abortOperation() { 134 KeyStoreCryptoOperationUtils.abortOperation(mOperation); 135 mOperation = null; 136 } 137 resetAll()138 private void resetAll() { 139 abortOperation(); 140 mOperationChallenge = 0; 141 mKey = null; 142 mChunkedStreamer = null; 143 } 144 resetWhilePreservingInitState()145 private void resetWhilePreservingInitState() { 146 abortOperation(); 147 mOperationChallenge = 0; 148 mChunkedStreamer = null; 149 } 150 151 @Override engineReset()152 protected void engineReset() { 153 resetWhilePreservingInitState(); 154 } 155 ensureKeystoreOperationInitialized()156 private void ensureKeystoreOperationInitialized() throws InvalidKeyException { 157 if (mChunkedStreamer != null) { 158 return; 159 } 160 if (mKey == null) { 161 throw new IllegalStateException("Not initialized"); 162 } 163 164 List<KeyParameter> parameters = new ArrayList<>(); 165 parameters.add(KeyStore2ParameterUtils.makeEnum( 166 KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN 167 )); 168 parameters.add(KeyStore2ParameterUtils.makeEnum( 169 KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC 170 )); 171 parameters.add(KeyStore2ParameterUtils.makeEnum( 172 KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest 173 )); 174 parameters.add(KeyStore2ParameterUtils.makeInt( 175 KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits 176 )); 177 178 try { 179 mOperation = mKey.getSecurityLevel().createOperation( 180 mKey.getKeyIdDescriptor(), 181 parameters 182 ); 183 } catch (KeyStoreException keyStoreException) { 184 // If necessary, throw an exception due to KeyStore operation having failed. 185 InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyException( 186 mKey, keyStoreException); 187 if (e != null) { 188 throw e; 189 } 190 } 191 192 // Now we check if we got an operation challenge. This indicates that user authorization 193 // is required. And if we got a challenge we check if the authorization can possibly 194 // succeed. 195 mOperationChallenge = KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge( 196 mOperation, mKey); 197 198 mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer( 199 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( 200 mOperation)); 201 } 202 203 @Override engineUpdate(byte input)204 protected void engineUpdate(byte input) { 205 engineUpdate(new byte[] {input}, 0, 1); 206 } 207 208 @Override engineUpdate(byte[] input, int offset, int len)209 protected void engineUpdate(byte[] input, int offset, int len) { 210 try { 211 ensureKeystoreOperationInitialized(); 212 } catch (InvalidKeyException e) { 213 throw new ProviderException("Failed to reinitialize MAC", e); 214 } 215 216 byte[] output; 217 try { 218 output = mChunkedStreamer.update(input, offset, len); 219 } catch (KeyStoreException e) { 220 throw new ProviderException("Keystore operation failed", e); 221 } 222 if ((output != null) && (output.length != 0)) { 223 throw new ProviderException("Update operation unexpectedly produced output"); 224 } 225 } 226 227 @Override engineDoFinal()228 protected byte[] engineDoFinal() { 229 try { 230 ensureKeystoreOperationInitialized(); 231 } catch (InvalidKeyException e) { 232 throw new ProviderException("Failed to reinitialize MAC", e); 233 } 234 235 byte[] result; 236 try { 237 result = mChunkedStreamer.doFinal( 238 null, 0, 0, 239 null); // no signature provided -- this invocation will generate one 240 } catch (KeyStoreException e) { 241 throw new ProviderException("Keystore operation failed", e); 242 } 243 244 resetWhilePreservingInitState(); 245 return result; 246 } 247 248 @Override finalize()249 public void finalize() throws Throwable { 250 try { 251 abortOperation(); 252 } finally { 253 super.finalize(); 254 } 255 } 256 257 @Override getOperationHandle()258 public long getOperationHandle() { 259 return mOperationChallenge; 260 } 261 } 262