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 17package android.security.keystore; 18 19import android.annotation.CallSuper; 20import android.annotation.NonNull; 21import android.os.IBinder; 22import android.security.KeyStore; 23import android.security.KeyStoreException; 24import android.security.keymaster.KeymasterArguments; 25import android.security.keymaster.KeymasterDefs; 26import android.security.keymaster.OperationResult; 27 28import libcore.util.EmptyArray; 29 30import java.nio.ByteBuffer; 31import java.security.InvalidKeyException; 32import java.security.InvalidParameterException; 33import java.security.PrivateKey; 34import java.security.ProviderException; 35import java.security.PublicKey; 36import java.security.SecureRandom; 37import java.security.SignatureException; 38import java.security.SignatureSpi; 39 40/** 41 * Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers. 42 * 43 * @hide 44 */ 45abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi 46 implements KeyStoreCryptoOperation { 47 private final KeyStore mKeyStore; 48 49 // Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin 50 // and should be preserved after SignatureSpi.engineSign/engineVerify finishes. 51 private boolean mSigning; 52 private AndroidKeyStoreKey mKey; 53 54 /** 55 * Token referencing this operation inside keystore service. It is initialized by 56 * {@code engineInitSign}/{@code engineInitVerify} and is invalidated when 57 * {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between. 58 */ 59 private IBinder mOperationToken; 60 private long mOperationHandle; 61 private KeyStoreCryptoOperationStreamer mMessageStreamer; 62 63 /** 64 * Encountered exception which could not be immediately thrown because it was encountered inside 65 * a method that does not throw checked exception. This exception will be thrown from 66 * {@code engineSign} or {@code engineVerify}. Once such an exception is encountered, 67 * {@code engineUpdate} starts ignoring input data. 68 */ 69 private Exception mCachedException; 70 71 AndroidKeyStoreSignatureSpiBase() { 72 mKeyStore = KeyStore.getInstance(); 73 } 74 75 @Override 76 protected final void engineInitSign(PrivateKey key) throws InvalidKeyException { 77 engineInitSign(key, null); 78 } 79 80 @Override 81 protected final void engineInitSign(PrivateKey privateKey, SecureRandom random) 82 throws InvalidKeyException { 83 resetAll(); 84 85 boolean success = false; 86 try { 87 if (privateKey == null) { 88 throw new InvalidKeyException("Unsupported key: null"); 89 } 90 AndroidKeyStoreKey keystoreKey; 91 if (privateKey instanceof AndroidKeyStorePrivateKey) { 92 keystoreKey = (AndroidKeyStoreKey) privateKey; 93 } else { 94 throw new InvalidKeyException("Unsupported private key type: " + privateKey); 95 } 96 mSigning = true; 97 initKey(keystoreKey); 98 appRandom = random; 99 ensureKeystoreOperationInitialized(); 100 success = true; 101 } finally { 102 if (!success) { 103 resetAll(); 104 } 105 } 106 } 107 108 @Override 109 protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { 110 resetAll(); 111 112 boolean success = false; 113 try { 114 if (publicKey == null) { 115 throw new InvalidKeyException("Unsupported key: null"); 116 } 117 AndroidKeyStoreKey keystoreKey; 118 if (publicKey instanceof AndroidKeyStorePublicKey) { 119 keystoreKey = (AndroidKeyStorePublicKey) publicKey; 120 } else { 121 throw new InvalidKeyException("Unsupported public key type: " + publicKey); 122 } 123 mSigning = false; 124 initKey(keystoreKey); 125 appRandom = null; 126 ensureKeystoreOperationInitialized(); 127 success = true; 128 } finally { 129 if (!success) { 130 resetAll(); 131 } 132 } 133 } 134 135 /** 136 * Configures this signature instance to use the provided key. 137 * 138 * @throws InvalidKeyException if the {@code key} is not suitable. 139 */ 140 @CallSuper 141 protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException { 142 mKey = key; 143 } 144 145 /** 146 * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new 147 * cipher instance. 148 * 149 * <p>Subclasses storing additional state should override this method, reset the additional 150 * state, and then chain to superclass. 151 */ 152 @CallSuper 153 protected void resetAll() { 154 IBinder operationToken = mOperationToken; 155 if (operationToken != null) { 156 mOperationToken = null; 157 mKeyStore.abort(operationToken); 158 } 159 mSigning = false; 160 mKey = null; 161 appRandom = null; 162 mOperationToken = null; 163 mOperationHandle = 0; 164 mMessageStreamer = null; 165 mCachedException = null; 166 } 167 168 /** 169 * Resets this cipher while preserving the initialized state. This must be equivalent to 170 * rolling back the cipher's state to just after the most recent {@code engineInit} completed 171 * successfully. 172 * 173 * <p>Subclasses storing additional post-init state should override this method, reset the 174 * additional state, and then chain to superclass. 175 */ 176 @CallSuper 177 protected void resetWhilePreservingInitState() { 178 IBinder operationToken = mOperationToken; 179 if (operationToken != null) { 180 mOperationToken = null; 181 mKeyStore.abort(operationToken); 182 } 183 mOperationHandle = 0; 184 mMessageStreamer = null; 185 mCachedException = null; 186 } 187 188 private void ensureKeystoreOperationInitialized() throws InvalidKeyException { 189 if (mMessageStreamer != null) { 190 return; 191 } 192 if (mCachedException != null) { 193 return; 194 } 195 if (mKey == null) { 196 throw new IllegalStateException("Not initialized"); 197 } 198 199 KeymasterArguments keymasterInputArgs = new KeymasterArguments(); 200 addAlgorithmSpecificParametersToBegin(keymasterInputArgs); 201 202 OperationResult opResult = mKeyStore.begin( 203 mKey.getAlias(), 204 mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY, 205 true, // permit aborting this operation if keystore runs out of resources 206 keymasterInputArgs, 207 null, // no additional entropy for begin -- only finish might need some 208 mKey.getUid()); 209 if (opResult == null) { 210 throw new KeyStoreConnectException(); 211 } 212 213 // Store operation token and handle regardless of the error code returned by KeyStore to 214 // ensure that the operation gets aborted immediately if the code below throws an exception. 215 mOperationToken = opResult.token; 216 mOperationHandle = opResult.operationHandle; 217 218 // If necessary, throw an exception due to KeyStore operation having failed. 219 InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit( 220 mKeyStore, mKey, opResult.resultCode); 221 if (e != null) { 222 throw e; 223 } 224 225 if (mOperationToken == null) { 226 throw new ProviderException("Keystore returned null operation token"); 227 } 228 if (mOperationHandle == 0) { 229 throw new ProviderException("Keystore returned invalid operation handle"); 230 } 231 232 mMessageStreamer = createMainDataStreamer(mKeyStore, opResult.token); 233 } 234 235 /** 236 * Creates a streamer which sends the message to be signed/verified into the provided KeyStore 237 * 238 * <p>This implementation returns a working streamer. 239 */ 240 @NonNull 241 protected KeyStoreCryptoOperationStreamer createMainDataStreamer( 242 KeyStore keyStore, IBinder operationToken) { 243 return new KeyStoreCryptoOperationChunkedStreamer( 244 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( 245 keyStore, operationToken)); 246 } 247 248 @Override 249 public final long getOperationHandle() { 250 return mOperationHandle; 251 } 252 253 @Override 254 protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException { 255 if (mCachedException != null) { 256 throw new SignatureException(mCachedException); 257 } 258 259 try { 260 ensureKeystoreOperationInitialized(); 261 } catch (InvalidKeyException e) { 262 throw new SignatureException(e); 263 } 264 265 if (len == 0) { 266 return; 267 } 268 269 byte[] output; 270 try { 271 output = mMessageStreamer.update(b, off, len); 272 } catch (KeyStoreException e) { 273 throw new SignatureException(e); 274 } 275 276 if (output.length != 0) { 277 throw new ProviderException( 278 "Update operation unexpectedly produced output: " + output.length + " bytes"); 279 } 280 } 281 282 @Override 283 protected final void engineUpdate(byte b) throws SignatureException { 284 engineUpdate(new byte[] {b}, 0, 1); 285 } 286 287 @Override 288 protected final void engineUpdate(ByteBuffer input) { 289 byte[] b; 290 int off; 291 int len = input.remaining(); 292 if (input.hasArray()) { 293 b = input.array(); 294 off = input.arrayOffset() + input.position(); 295 input.position(input.limit()); 296 } else { 297 b = new byte[len]; 298 off = 0; 299 input.get(b); 300 } 301 302 try { 303 engineUpdate(b, off, len); 304 } catch (SignatureException e) { 305 mCachedException = e; 306 } 307 } 308 309 @Override 310 protected final int engineSign(byte[] out, int outOffset, int outLen) 311 throws SignatureException { 312 return super.engineSign(out, outOffset, outLen); 313 } 314 315 @Override 316 protected final byte[] engineSign() throws SignatureException { 317 if (mCachedException != null) { 318 throw new SignatureException(mCachedException); 319 } 320 321 byte[] signature; 322 try { 323 ensureKeystoreOperationInitialized(); 324 325 byte[] additionalEntropy = 326 KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( 327 appRandom, getAdditionalEntropyAmountForSign()); 328 signature = mMessageStreamer.doFinal( 329 EmptyArray.BYTE, 0, 0, 330 null, // no signature provided -- it'll be generated by this invocation 331 additionalEntropy); 332 } catch (InvalidKeyException | KeyStoreException e) { 333 throw new SignatureException(e); 334 } 335 336 resetWhilePreservingInitState(); 337 return signature; 338 } 339 340 @Override 341 protected final boolean engineVerify(byte[] signature) throws SignatureException { 342 if (mCachedException != null) { 343 throw new SignatureException(mCachedException); 344 } 345 346 try { 347 ensureKeystoreOperationInitialized(); 348 } catch (InvalidKeyException e) { 349 throw new SignatureException(e); 350 } 351 352 boolean verified; 353 try { 354 byte[] output = mMessageStreamer.doFinal( 355 EmptyArray.BYTE, 0, 0, 356 signature, 357 null // no additional entropy needed -- verification is deterministic 358 ); 359 if (output.length != 0) { 360 throw new ProviderException( 361 "Signature verification unexpected produced output: " + output.length 362 + " bytes"); 363 } 364 verified = true; 365 } catch (KeyStoreException e) { 366 switch (e.getErrorCode()) { 367 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED: 368 verified = false; 369 break; 370 default: 371 throw new SignatureException(e); 372 } 373 } 374 375 resetWhilePreservingInitState(); 376 return verified; 377 } 378 379 @Override 380 protected final boolean engineVerify(byte[] sigBytes, int offset, int len) 381 throws SignatureException { 382 return engineVerify(ArrayUtils.subarray(sigBytes, offset, len)); 383 } 384 385 @Deprecated 386 @Override 387 protected final Object engineGetParameter(String param) throws InvalidParameterException { 388 throw new InvalidParameterException(); 389 } 390 391 @Deprecated 392 @Override 393 protected final void engineSetParameter(String param, Object value) 394 throws InvalidParameterException { 395 throw new InvalidParameterException(); 396 } 397 398 protected final KeyStore getKeyStore() { 399 return mKeyStore; 400 } 401 402 /** 403 * Returns {@code true} if this signature is initialized for signing, {@code false} if this 404 * signature is initialized for verification. 405 */ 406 protected final boolean isSigning() { 407 return mSigning; 408 } 409 410 // The methods below need to be implemented by subclasses. 411 412 /** 413 * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's 414 * {@code finish} operation when generating a signature. 415 * 416 * <p>This value should match (or exceed) the amount of Shannon entropy of the produced 417 * signature assuming the key and the message are known. For example, for ECDSA signature this 418 * should be the size of {@code R}, whereas for the RSA signature with PKCS#1 padding this 419 * should be {@code 0}. 420 */ 421 protected abstract int getAdditionalEntropyAmountForSign(); 422 423 /** 424 * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation. 425 * 426 * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific 427 * parameters. 428 */ 429 protected abstract void addAlgorithmSpecificParametersToBegin( 430 @NonNull KeymasterArguments keymasterArgs); 431} 432