AndroidKeyStoreECDSASignatureSpi.java revision d23dc502b0a1952887d4453cba98aa2e3d2f5009
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.NonNull; 20import android.os.IBinder; 21import android.security.KeyStore; 22import android.security.KeyStoreException; 23import android.security.keymaster.KeyCharacteristics; 24import android.security.keymaster.KeymasterArguments; 25import android.security.keymaster.KeymasterDefs; 26 27import libcore.util.EmptyArray; 28 29import java.io.ByteArrayOutputStream; 30import java.security.InvalidKeyException; 31import java.security.SignatureSpi; 32 33/** 34 * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures. 35 * 36 * @hide 37 */ 38abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase { 39 40 public final static class NONE extends AndroidKeyStoreECDSASignatureSpi { 41 public NONE() { 42 super(KeymasterDefs.KM_DIGEST_NONE); 43 } 44 45 @Override 46 protected KeyStoreCryptoOperationStreamer createMainDataStreamer(KeyStore keyStore, 47 IBinder operationToken) { 48 return new TruncateToFieldSizeMessageStreamer( 49 super.createMainDataStreamer(keyStore, operationToken), 50 getGroupSizeBits()); 51 } 52 53 /** 54 * Streamer which buffers all input, then truncates it to field size, and then sends it into 55 * KeyStore via the provided delegate streamer. 56 */ 57 private static class TruncateToFieldSizeMessageStreamer 58 implements KeyStoreCryptoOperationStreamer { 59 60 private final KeyStoreCryptoOperationStreamer mDelegate; 61 private final int mGroupSizeBits; 62 private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream(); 63 private long mConsumedInputSizeBytes; 64 65 private TruncateToFieldSizeMessageStreamer( 66 KeyStoreCryptoOperationStreamer delegate, 67 int groupSizeBits) { 68 mDelegate = delegate; 69 mGroupSizeBits = groupSizeBits; 70 } 71 72 @Override 73 public byte[] update(byte[] input, int inputOffset, int inputLength) 74 throws KeyStoreException { 75 if (inputLength > 0) { 76 mInputBuffer.write(input, inputOffset, inputLength); 77 mConsumedInputSizeBytes += inputLength; 78 } 79 return EmptyArray.BYTE; 80 } 81 82 @Override 83 public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature, 84 byte[] additionalEntropy) throws KeyStoreException { 85 if (inputLength > 0) { 86 mConsumedInputSizeBytes += inputLength; 87 mInputBuffer.write(input, inputOffset, inputLength); 88 } 89 90 byte[] bufferedInput = mInputBuffer.toByteArray(); 91 mInputBuffer.reset(); 92 // Truncate input at field size (bytes) 93 return mDelegate.doFinal(bufferedInput, 94 0, 95 Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)), 96 signature, additionalEntropy); 97 } 98 99 @Override 100 public long getConsumedInputSizeBytes() { 101 return mConsumedInputSizeBytes; 102 } 103 104 @Override 105 public long getProducedOutputSizeBytes() { 106 return mDelegate.getProducedOutputSizeBytes(); 107 } 108 } 109 } 110 111 public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi { 112 public SHA1() { 113 super(KeymasterDefs.KM_DIGEST_SHA1); 114 } 115 } 116 117 public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi { 118 public SHA224() { 119 super(KeymasterDefs.KM_DIGEST_SHA_2_224); 120 } 121 } 122 123 public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi { 124 public SHA256() { 125 super(KeymasterDefs.KM_DIGEST_SHA_2_256); 126 } 127 } 128 129 public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi { 130 public SHA384() { 131 super(KeymasterDefs.KM_DIGEST_SHA_2_384); 132 } 133 } 134 135 public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi { 136 public SHA512() { 137 super(KeymasterDefs.KM_DIGEST_SHA_2_512); 138 } 139 } 140 141 private final int mKeymasterDigest; 142 143 private int mGroupSizeBits = -1; 144 145 AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) { 146 mKeymasterDigest = keymasterDigest; 147 } 148 149 @Override 150 protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException { 151 if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) { 152 throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm() 153 + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported"); 154 } 155 156 KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); 157 int errorCode = getKeyStore().getKeyCharacteristics( 158 key.getAlias(), null, null, keyCharacteristics); 159 if (errorCode != KeyStore.NO_ERROR) { 160 throw getKeyStore().getInvalidKeyException(key.getAlias(), errorCode); 161 } 162 long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1); 163 if (keySizeBits == -1) { 164 throw new InvalidKeyException("Size of key not known"); 165 } else if (keySizeBits > Integer.MAX_VALUE) { 166 throw new InvalidKeyException("Key too large: " + keySizeBits + " bits"); 167 } 168 mGroupSizeBits = (int) keySizeBits; 169 170 super.initKey(key); 171 } 172 173 @Override 174 protected final void resetAll() { 175 mGroupSizeBits = -1; 176 super.resetAll(); 177 } 178 179 @Override 180 protected final void resetWhilePreservingInitState() { 181 super.resetWhilePreservingInitState(); 182 } 183 184 @Override 185 protected final void addAlgorithmSpecificParametersToBegin( 186 @NonNull KeymasterArguments keymasterArgs) { 187 keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC); 188 keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest); 189 } 190 191 @Override 192 protected final int getAdditionalEntropyAmountForSign() { 193 return (mGroupSizeBits + 7) / 8; 194 } 195 196 protected final int getGroupSizeBits() { 197 if (mGroupSizeBits == -1) { 198 throw new IllegalStateException("Not initialized"); 199 } 200 return mGroupSizeBits; 201 } 202} 203