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.annotation.NonNull; 20 import android.hardware.security.keymint.KeyParameter; 21 import android.security.KeyStoreException; 22 import android.security.KeyStoreOperation; 23 import android.security.keymaster.KeymasterDefs; 24 import android.security.keystore.KeyProperties; 25 import android.system.keystore2.Authorization; 26 27 import libcore.util.EmptyArray; 28 29 import java.io.ByteArrayOutputStream; 30 import java.security.InvalidKeyException; 31 import java.security.SignatureSpi; 32 import java.security.spec.NamedParameterSpec; 33 import java.util.Arrays; 34 import java.util.List; 35 import java.util.Set; 36 37 /** 38 * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures. 39 * 40 * @hide 41 */ 42 abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase { 43 private static final Set<String> ACCEPTED_SIGNING_SCHEMES = Set.of( 44 KeyProperties.KEY_ALGORITHM_EC.toLowerCase(), 45 NamedParameterSpec.ED25519.getName().toLowerCase(), 46 "eddsa"); 47 48 public final static class NONE extends AndroidKeyStoreECDSASignatureSpi { NONE()49 public NONE() { 50 super(KeymasterDefs.KM_DIGEST_NONE); 51 } 52 53 @Override getAlgorithm()54 protected String getAlgorithm() { 55 return "NONEwithECDSA"; 56 } 57 58 @Override createMainDataStreamer( KeyStoreOperation operation)59 protected KeyStoreCryptoOperationStreamer createMainDataStreamer( 60 KeyStoreOperation operation) { 61 return new TruncateToFieldSizeMessageStreamer( 62 super.createMainDataStreamer(operation), 63 getGroupSizeBits()); 64 } 65 66 /** 67 * Streamer which buffers all input, then truncates it to field size, and then sends it into 68 * KeyStore via the provided delegate streamer. 69 */ 70 private static class TruncateToFieldSizeMessageStreamer 71 implements KeyStoreCryptoOperationStreamer { 72 73 private final KeyStoreCryptoOperationStreamer mDelegate; 74 private final int mGroupSizeBits; 75 private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream(); 76 private long mConsumedInputSizeBytes; 77 TruncateToFieldSizeMessageStreamer( KeyStoreCryptoOperationStreamer delegate, int groupSizeBits)78 private TruncateToFieldSizeMessageStreamer( 79 KeyStoreCryptoOperationStreamer delegate, 80 int groupSizeBits) { 81 mDelegate = delegate; 82 mGroupSizeBits = groupSizeBits; 83 } 84 85 @Override update(byte[] input, int inputOffset, int inputLength)86 public byte[] update(byte[] input, int inputOffset, int inputLength) 87 throws KeyStoreException { 88 if (inputLength > 0) { 89 mInputBuffer.write(input, inputOffset, inputLength); 90 mConsumedInputSizeBytes += inputLength; 91 } 92 return EmptyArray.BYTE; 93 } 94 95 @Override doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature)96 public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature) 97 throws KeyStoreException { 98 if (inputLength > 0) { 99 mConsumedInputSizeBytes += inputLength; 100 mInputBuffer.write(input, inputOffset, inputLength); 101 } 102 103 byte[] bufferedInput = mInputBuffer.toByteArray(); 104 mInputBuffer.reset(); 105 // Truncate input at field size (bytes) 106 return mDelegate.doFinal(bufferedInput, 107 0, 108 Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)), 109 signature); 110 } 111 112 @Override getConsumedInputSizeBytes()113 public long getConsumedInputSizeBytes() { 114 return mConsumedInputSizeBytes; 115 } 116 117 @Override getProducedOutputSizeBytes()118 public long getProducedOutputSizeBytes() { 119 return mDelegate.getProducedOutputSizeBytes(); 120 } 121 } 122 } 123 124 public static final class Ed25519 extends AndroidKeyStoreECDSASignatureSpi { Ed25519()125 public Ed25519() { 126 // Ed25519 uses an internal digest system. 127 super(KeymasterDefs.KM_DIGEST_NONE); 128 } 129 130 @Override getAlgorithm()131 protected String getAlgorithm() { 132 return NamedParameterSpec.ED25519.getName(); 133 } 134 } 135 136 public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi { SHA1()137 public SHA1() { 138 super(KeymasterDefs.KM_DIGEST_SHA1); 139 } 140 @Override getAlgorithm()141 protected String getAlgorithm() { 142 return "SHA1withECDSA"; 143 } 144 } 145 146 public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi { SHA224()147 public SHA224() { 148 super(KeymasterDefs.KM_DIGEST_SHA_2_224); 149 } 150 @Override getAlgorithm()151 protected String getAlgorithm() { 152 return "SHA224withECDSA"; 153 } 154 } 155 156 public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi { SHA256()157 public SHA256() { 158 super(KeymasterDefs.KM_DIGEST_SHA_2_256); 159 } 160 @Override getAlgorithm()161 protected String getAlgorithm() { 162 return "SHA256withECDSA"; 163 } 164 } 165 166 public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi { SHA384()167 public SHA384() { 168 super(KeymasterDefs.KM_DIGEST_SHA_2_384); 169 } 170 @Override getAlgorithm()171 protected String getAlgorithm() { 172 return "SHA384withECDSA"; 173 } 174 } 175 176 public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi { SHA512()177 public SHA512() { 178 super(KeymasterDefs.KM_DIGEST_SHA_2_512); 179 } 180 @Override getAlgorithm()181 protected String getAlgorithm() { 182 return "SHA512withECDSA"; 183 } 184 } 185 186 private final int mKeymasterDigest; 187 188 private int mGroupSizeBits = -1; 189 AndroidKeyStoreECDSASignatureSpi(int keymasterDigest)190 AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) { 191 mKeymasterDigest = keymasterDigest; 192 } 193 194 @Override initKey(AndroidKeyStoreKey key)195 protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException { 196 if (!ACCEPTED_SIGNING_SCHEMES.contains(key.getAlgorithm().toLowerCase())) { 197 throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm() 198 + ". Only " + Arrays.toString(ACCEPTED_SIGNING_SCHEMES.stream().toArray()) 199 + " supported"); 200 } 201 202 long keySizeBits = -1; 203 for (Authorization a : key.getAuthorizations()) { 204 if (a.keyParameter.tag == KeymasterDefs.KM_TAG_KEY_SIZE) { 205 keySizeBits = KeyStore2ParameterUtils.getUnsignedInt(a); 206 break; 207 } else if (a.keyParameter.tag == KeymasterDefs.KM_TAG_EC_CURVE) { 208 keySizeBits = KeyProperties.EcCurve.fromKeymasterCurve( 209 a.keyParameter.value.getEcCurve()); 210 break; 211 } 212 } 213 214 if (keySizeBits == -1) { 215 throw new InvalidKeyException("Size of key not known"); 216 } else if (keySizeBits > Integer.MAX_VALUE) { 217 throw new InvalidKeyException("Key too large: " + keySizeBits + " bits"); 218 } 219 mGroupSizeBits = (int) keySizeBits; 220 221 super.initKey(key); 222 } 223 224 @Override resetAll()225 protected final void resetAll() { 226 mGroupSizeBits = -1; 227 super.resetAll(); 228 } 229 230 @Override resetWhilePreservingInitState()231 protected final void resetWhilePreservingInitState() { 232 super.resetWhilePreservingInitState(); 233 } 234 235 @Override addAlgorithmSpecificParametersToBegin( @onNull List<KeyParameter> parameters)236 protected final void addAlgorithmSpecificParametersToBegin( 237 @NonNull List<KeyParameter> parameters) { 238 parameters.add(KeyStore2ParameterUtils.makeEnum( 239 KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC 240 )); 241 parameters.add(KeyStore2ParameterUtils.makeEnum( 242 KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest 243 )); 244 } 245 246 @Override getAdditionalEntropyAmountForSign()247 protected final int getAdditionalEntropyAmountForSign() { 248 return (mGroupSizeBits + 7) / 8; 249 } 250 getGroupSizeBits()251 protected final int getGroupSizeBits() { 252 if (mGroupSizeBits == -1) { 253 throw new IllegalStateException("Not initialized"); 254 } 255 return mGroupSizeBits; 256 } 257 } 258