Home | History | Annotate | Download | only in keystore
      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.keystore;
     18 
     19 import android.annotation.NonNull;
     20 import android.os.IBinder;
     21 import android.security.KeyStore;
     22 import android.security.KeyStoreException;
     23 import android.security.keymaster.KeyCharacteristics;
     24 import android.security.keymaster.KeymasterArguments;
     25 import android.security.keymaster.KeymasterDefs;
     26 
     27 import libcore.util.EmptyArray;
     28 
     29 import java.io.ByteArrayOutputStream;
     30 import java.security.InvalidKeyException;
     31 import java.security.SignatureSpi;
     32 
     33 /**
     34  * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
     35  *
     36  * @hide
     37  */
     38 abstract 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, key.getUid(), keyCharacteristics);
    159         if (errorCode != KeyStore.NO_ERROR) {
    160             throw getKeyStore().getInvalidKeyException(key.getAlias(), key.getUid(), 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