AndroidKeyStoreSignatureSpiBase.java revision a72b55195c23fc06d1600efe8f6aac85290c7f8f
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 KeyStoreCryptoOperationChunkedStreamer 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 ); 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 = new KeyStoreCryptoOperationChunkedStreamer( 233 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream( 234 mKeyStore, opResult.token)); 235 } 236 237 @Override 238 public final long getOperationHandle() { 239 return mOperationHandle; 240 } 241 242 @Override 243 protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException { 244 if (mCachedException != null) { 245 throw new SignatureException(mCachedException); 246 } 247 248 try { 249 ensureKeystoreOperationInitialized(); 250 } catch (InvalidKeyException e) { 251 throw new SignatureException(e); 252 } 253 254 if (len == 0) { 255 return; 256 } 257 258 byte[] output; 259 try { 260 output = mMessageStreamer.update(b, off, len); 261 } catch (KeyStoreException e) { 262 throw new SignatureException(e); 263 } 264 265 if (output.length != 0) { 266 throw new ProviderException( 267 "Update operation unexpectedly produced output: " + output.length + " bytes"); 268 } 269 } 270 271 @Override 272 protected final void engineUpdate(byte b) throws SignatureException { 273 engineUpdate(new byte[] {b}, 0, 1); 274 } 275 276 @Override 277 protected final void engineUpdate(ByteBuffer input) { 278 byte[] b; 279 int off; 280 int len = input.remaining(); 281 if (input.hasArray()) { 282 b = input.array(); 283 off = input.arrayOffset() + input.position(); 284 input.position(input.limit()); 285 } else { 286 b = new byte[len]; 287 off = 0; 288 input.get(b); 289 } 290 291 try { 292 engineUpdate(b, off, len); 293 } catch (SignatureException e) { 294 mCachedException = e; 295 } 296 } 297 298 @Override 299 protected final int engineSign(byte[] out, int outOffset, int outLen) 300 throws SignatureException { 301 return super.engineSign(out, outOffset, outLen); 302 } 303 304 @Override 305 protected final byte[] engineSign() throws SignatureException { 306 if (mCachedException != null) { 307 throw new SignatureException(mCachedException); 308 } 309 310 byte[] signature; 311 try { 312 ensureKeystoreOperationInitialized(); 313 314 byte[] additionalEntropy = 315 KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng( 316 appRandom, getAdditionalEntropyAmountForSign()); 317 signature = mMessageStreamer.doFinal(EmptyArray.BYTE, 0, 0, additionalEntropy); 318 } catch (InvalidKeyException | KeyStoreException e) { 319 throw new SignatureException(e); 320 } 321 322 resetWhilePreservingInitState(); 323 return signature; 324 } 325 326 @Override 327 protected final boolean engineVerify(byte[] signature) throws SignatureException { 328 if (mCachedException != null) { 329 throw new SignatureException(mCachedException); 330 } 331 332 boolean result; 333 try { 334 ensureKeystoreOperationInitialized(); 335 mMessageStreamer.flush(); 336 OperationResult opResult = mKeyStore.finish(mOperationToken, null, signature); 337 if (opResult == null) { 338 throw new KeyStoreConnectException(); 339 } 340 switch (opResult.resultCode) { 341 case KeyStore.NO_ERROR: 342 result = true; 343 break; 344 case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED: 345 result = false; 346 break; 347 default: 348 throw new SignatureException( 349 KeyStore.getKeyStoreException(opResult.resultCode)); 350 } 351 } catch (InvalidKeyException | KeyStoreException e) { 352 throw new SignatureException(e); 353 } 354 355 resetWhilePreservingInitState(); 356 return result; 357 } 358 359 @Override 360 protected final boolean engineVerify(byte[] sigBytes, int offset, int len) 361 throws SignatureException { 362 return engineVerify(ArrayUtils.subarray(sigBytes, offset, len)); 363 } 364 365 @Deprecated 366 @Override 367 protected final Object engineGetParameter(String param) throws InvalidParameterException { 368 throw new InvalidParameterException(); 369 } 370 371 @Deprecated 372 @Override 373 protected final void engineSetParameter(String param, Object value) 374 throws InvalidParameterException { 375 throw new InvalidParameterException(); 376 } 377 378 protected final KeyStore getKeyStore() { 379 return mKeyStore; 380 } 381 382 /** 383 * Returns {@code true} if this signature is initialized for signing, {@code false} if this 384 * signature is initialized for verification. 385 */ 386 protected final boolean isSigning() { 387 return mSigning; 388 } 389 390 // The methods below need to be implemented by subclasses. 391 392 /** 393 * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's 394 * {@code finish} operation when generating a signature. 395 * 396 * <p>This value should match (or exceed) the amount of Shannon entropy of the produced 397 * signature assuming the key and the message are known. For example, for ECDSA signature this 398 * should be the size of {@code R}, whereas for the RSA signature with PKCS#1 padding this 399 * should be {@code 0}. 400 */ 401 protected abstract int getAdditionalEntropyAmountForSign(); 402 403 /** 404 * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation. 405 * 406 * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific 407 * parameters. 408 */ 409 protected abstract void addAlgorithmSpecificParametersToBegin( 410 @NonNull KeymasterArguments keymasterArgs); 411} 412