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.annotation.Nullable;
     21 import android.security.KeyStore;
     22 import android.security.keymaster.KeyCharacteristics;
     23 import android.security.keymaster.KeymasterArguments;
     24 import android.security.keymaster.KeymasterDefs;
     25 
     26 import java.security.AlgorithmParameters;
     27 import java.security.InvalidAlgorithmParameterException;
     28 import java.security.InvalidKeyException;
     29 import java.security.Key;
     30 import java.security.NoSuchAlgorithmException;
     31 import java.security.PrivateKey;
     32 import java.security.ProviderException;
     33 import java.security.spec.AlgorithmParameterSpec;
     34 import java.security.spec.InvalidParameterSpecException;
     35 import java.security.spec.MGF1ParameterSpec;
     36 
     37 import javax.crypto.Cipher;
     38 import javax.crypto.CipherSpi;
     39 import javax.crypto.spec.OAEPParameterSpec;
     40 import javax.crypto.spec.PSource;
     41 
     42 /**
     43  * Base class for {@link CipherSpi} providing Android KeyStore backed RSA encryption/decryption.
     44  *
     45  * @hide
     46  */
     47 abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase {
     48 
     49     /**
     50      * Raw RSA cipher without any padding.
     51      */
     52     public static final class NoPadding extends AndroidKeyStoreRSACipherSpi {
     53         public NoPadding() {
     54             super(KeymasterDefs.KM_PAD_NONE);
     55         }
     56 
     57         @Override
     58         protected boolean adjustConfigForEncryptingWithPrivateKey() {
     59             // RSA encryption with no padding using private key is a way to implement raw RSA
     60             // signatures which JCA does not expose via Signature. We thus have to support this.
     61             setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
     62             return true;
     63         }
     64 
     65         @Override
     66         protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
     67 
     68         @Override
     69         protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
     70                 throws InvalidAlgorithmParameterException {
     71             if (params != null) {
     72                 throw new InvalidAlgorithmParameterException(
     73                         "Unexpected parameters: " + params + ". No parameters supported");
     74             }
     75         }
     76 
     77         @Override
     78         protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
     79                 throws InvalidAlgorithmParameterException {
     80 
     81             if (params != null) {
     82                 throw new InvalidAlgorithmParameterException(
     83                         "Unexpected parameters: " + params + ". No parameters supported");
     84             }
     85         }
     86 
     87         @Override
     88         protected AlgorithmParameters engineGetParameters() {
     89             return null;
     90         }
     91 
     92         @Override
     93         protected final int getAdditionalEntropyAmountForBegin() {
     94             return 0;
     95         }
     96 
     97         @Override
     98         protected final int getAdditionalEntropyAmountForFinish() {
     99             return 0;
    100         }
    101     }
    102 
    103     /**
    104      * RSA cipher with PKCS#1 v1.5 encryption padding.
    105      */
    106     public static final class PKCS1Padding extends AndroidKeyStoreRSACipherSpi {
    107         public PKCS1Padding() {
    108             super(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
    109         }
    110 
    111         @Override
    112         protected boolean adjustConfigForEncryptingWithPrivateKey() {
    113             // RSA encryption with PCKS#1 padding using private key is a way to implement RSA
    114             // signatures with PKCS#1 padding. We have to support this for legacy reasons.
    115             setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
    116             setKeymasterPaddingOverride(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
    117             return true;
    118         }
    119 
    120         @Override
    121         protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
    122 
    123         @Override
    124         protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
    125                 throws InvalidAlgorithmParameterException {
    126             if (params != null) {
    127                 throw new InvalidAlgorithmParameterException(
    128                         "Unexpected parameters: " + params + ". No parameters supported");
    129             }
    130         }
    131 
    132         @Override
    133         protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
    134                 throws InvalidAlgorithmParameterException {
    135 
    136             if (params != null) {
    137                 throw new InvalidAlgorithmParameterException(
    138                         "Unexpected parameters: " + params + ". No parameters supported");
    139             }
    140         }
    141 
    142         @Override
    143         protected AlgorithmParameters engineGetParameters() {
    144             return null;
    145         }
    146 
    147         @Override
    148         protected final int getAdditionalEntropyAmountForBegin() {
    149             return 0;
    150         }
    151 
    152         @Override
    153         protected final int getAdditionalEntropyAmountForFinish() {
    154             return (isEncrypting()) ? getModulusSizeBytes() : 0;
    155         }
    156     }
    157 
    158     /**
    159      * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF.
    160      */
    161     abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi {
    162 
    163         private static final String MGF_ALGORITGM_MGF1 = "MGF1";
    164 
    165         private int mKeymasterDigest = -1;
    166         private int mDigestOutputSizeBytes;
    167 
    168         OAEPWithMGF1Padding(int keymasterDigest) {
    169             super(KeymasterDefs.KM_PAD_RSA_OAEP);
    170             mKeymasterDigest = keymasterDigest;
    171             mDigestOutputSizeBytes =
    172                     (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
    173         }
    174 
    175         @Override
    176         protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {}
    177 
    178         @Override
    179         protected final void initAlgorithmSpecificParameters(
    180                 @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException {
    181             if (params == null) {
    182                 return;
    183             }
    184 
    185             if (!(params instanceof OAEPParameterSpec)) {
    186                 throw new InvalidAlgorithmParameterException(
    187                         "Unsupported parameter spec: " + params
    188                         + ". Only OAEPParameterSpec supported");
    189             }
    190             OAEPParameterSpec spec = (OAEPParameterSpec) params;
    191             if (!MGF_ALGORITGM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) {
    192                 throw new InvalidAlgorithmParameterException(
    193                         "Unsupported MGF: " + spec.getMGFAlgorithm()
    194                         + ". Only " + MGF_ALGORITGM_MGF1 + " supported");
    195             }
    196             String jcaDigest = spec.getDigestAlgorithm();
    197             int keymasterDigest;
    198             try {
    199                 keymasterDigest = KeyProperties.Digest.toKeymaster(jcaDigest);
    200             } catch (IllegalArgumentException e) {
    201                 throw new InvalidAlgorithmParameterException(
    202                         "Unsupported digest: " + jcaDigest, e);
    203             }
    204             switch (keymasterDigest) {
    205                 case KeymasterDefs.KM_DIGEST_SHA1:
    206                 case KeymasterDefs.KM_DIGEST_SHA_2_224:
    207                 case KeymasterDefs.KM_DIGEST_SHA_2_256:
    208                 case KeymasterDefs.KM_DIGEST_SHA_2_384:
    209                 case KeymasterDefs.KM_DIGEST_SHA_2_512:
    210                     // Permitted.
    211                     break;
    212                 default:
    213                     throw new InvalidAlgorithmParameterException(
    214                             "Unsupported digest: " + jcaDigest);
    215             }
    216             AlgorithmParameterSpec mgfParams = spec.getMGFParameters();
    217             if (mgfParams == null) {
    218                 throw new InvalidAlgorithmParameterException("MGF parameters must be provided");
    219             }
    220             // Check whether MGF parameters match the OAEPParameterSpec
    221             if (!(mgfParams instanceof MGF1ParameterSpec)) {
    222                 throw new InvalidAlgorithmParameterException("Unsupported MGF parameters"
    223                         + ": " + mgfParams + ". Only MGF1ParameterSpec supported");
    224             }
    225             MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) mgfParams;
    226             String mgf1JcaDigest = mgfSpec.getDigestAlgorithm();
    227             if (!KeyProperties.DIGEST_SHA1.equalsIgnoreCase(mgf1JcaDigest)) {
    228                 throw new InvalidAlgorithmParameterException(
    229                         "Unsupported MGF1 digest: " + mgf1JcaDigest
    230                         + ". Only " + KeyProperties.DIGEST_SHA1 + " supported");
    231             }
    232             PSource pSource = spec.getPSource();
    233             if (!(pSource instanceof PSource.PSpecified)) {
    234                 throw new InvalidAlgorithmParameterException(
    235                         "Unsupported source of encoding input P: " + pSource
    236                         + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
    237             }
    238             PSource.PSpecified pSourceSpecified = (PSource.PSpecified) pSource;
    239             byte[] pSourceValue = pSourceSpecified.getValue();
    240             if ((pSourceValue != null) && (pSourceValue.length > 0)) {
    241                 throw new InvalidAlgorithmParameterException(
    242                         "Unsupported source of encoding input P: " + pSource
    243                         + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
    244             }
    245             mKeymasterDigest = keymasterDigest;
    246             mDigestOutputSizeBytes =
    247                     (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
    248         }
    249 
    250         @Override
    251         protected final void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
    252                 throws InvalidAlgorithmParameterException {
    253             if (params == null) {
    254                 return;
    255             }
    256 
    257             OAEPParameterSpec spec;
    258             try {
    259                 spec = params.getParameterSpec(OAEPParameterSpec.class);
    260             } catch (InvalidParameterSpecException e) {
    261                 throw new InvalidAlgorithmParameterException("OAEP parameters required"
    262                         + ", but not found in parameters: " + params, e);
    263             }
    264             if (spec == null) {
    265                 throw new InvalidAlgorithmParameterException("OAEP parameters required"
    266                         + ", but not provided in parameters: " + params);
    267             }
    268             initAlgorithmSpecificParameters(spec);
    269         }
    270 
    271         @Override
    272         protected final AlgorithmParameters engineGetParameters() {
    273             OAEPParameterSpec spec =
    274                     new OAEPParameterSpec(
    275                             KeyProperties.Digest.fromKeymaster(mKeymasterDigest),
    276                             MGF_ALGORITGM_MGF1,
    277                             MGF1ParameterSpec.SHA1,
    278                             PSource.PSpecified.DEFAULT);
    279             try {
    280                 AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP");
    281                 params.init(spec);
    282                 return params;
    283             } catch (NoSuchAlgorithmException e) {
    284                 throw new ProviderException(
    285                         "Failed to obtain OAEP AlgorithmParameters", e);
    286             } catch (InvalidParameterSpecException e) {
    287                 throw new ProviderException(
    288                         "Failed to initialize OAEP AlgorithmParameters with an IV",
    289                         e);
    290             }
    291         }
    292 
    293         @Override
    294         protected final void addAlgorithmSpecificParametersToBegin(
    295                 KeymasterArguments keymasterArgs) {
    296             super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
    297             keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
    298         }
    299 
    300         @Override
    301         protected final void loadAlgorithmSpecificParametersFromBeginResult(
    302                 @NonNull KeymasterArguments keymasterArgs) {
    303             super.loadAlgorithmSpecificParametersFromBeginResult(keymasterArgs);
    304         }
    305 
    306         @Override
    307         protected final int getAdditionalEntropyAmountForBegin() {
    308             return 0;
    309         }
    310 
    311         @Override
    312         protected final int getAdditionalEntropyAmountForFinish() {
    313             return (isEncrypting()) ? mDigestOutputSizeBytes : 0;
    314         }
    315     }
    316 
    317     public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding {
    318         public OAEPWithSHA1AndMGF1Padding() {
    319             super(KeymasterDefs.KM_DIGEST_SHA1);
    320         }
    321     }
    322 
    323     public static class OAEPWithSHA224AndMGF1Padding extends OAEPWithMGF1Padding {
    324         public OAEPWithSHA224AndMGF1Padding() {
    325             super(KeymasterDefs.KM_DIGEST_SHA_2_224);
    326         }
    327     }
    328 
    329     public static class OAEPWithSHA256AndMGF1Padding extends OAEPWithMGF1Padding {
    330         public OAEPWithSHA256AndMGF1Padding() {
    331             super(KeymasterDefs.KM_DIGEST_SHA_2_256);
    332         }
    333     }
    334 
    335     public static class OAEPWithSHA384AndMGF1Padding extends OAEPWithMGF1Padding {
    336         public OAEPWithSHA384AndMGF1Padding() {
    337             super(KeymasterDefs.KM_DIGEST_SHA_2_384);
    338         }
    339     }
    340 
    341     public static class OAEPWithSHA512AndMGF1Padding extends OAEPWithMGF1Padding {
    342         public OAEPWithSHA512AndMGF1Padding() {
    343             super(KeymasterDefs.KM_DIGEST_SHA_2_512);
    344         }
    345     }
    346 
    347     private final int mKeymasterPadding;
    348     private int mKeymasterPaddingOverride;
    349 
    350     private int mModulusSizeBytes = -1;
    351 
    352     AndroidKeyStoreRSACipherSpi(int keymasterPadding) {
    353         mKeymasterPadding = keymasterPadding;
    354     }
    355 
    356     @Override
    357     protected final void initKey(int opmode, Key key) throws InvalidKeyException {
    358         if (key == null) {
    359             throw new InvalidKeyException("Unsupported key: null");
    360         }
    361         if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
    362             throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
    363                     + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported");
    364         }
    365         AndroidKeyStoreKey keystoreKey;
    366         if (key instanceof AndroidKeyStorePrivateKey) {
    367             keystoreKey = (AndroidKeyStoreKey) key;
    368         } else if (key instanceof AndroidKeyStorePublicKey) {
    369             keystoreKey = (AndroidKeyStoreKey) key;
    370         } else {
    371             throw new InvalidKeyException("Unsupported key type: " + key);
    372         }
    373 
    374         if (keystoreKey instanceof PrivateKey) {
    375             // Private key
    376             switch (opmode) {
    377                 case Cipher.DECRYPT_MODE:
    378                 case Cipher.UNWRAP_MODE:
    379                     // Permitted
    380                     break;
    381                 case Cipher.ENCRYPT_MODE:
    382                 case Cipher.WRAP_MODE:
    383                     if (!adjustConfigForEncryptingWithPrivateKey()) {
    384                         throw new InvalidKeyException(
    385                                 "RSA private keys cannot be used with " + opmodeToString(opmode)
    386                                 + " and padding "
    387                                 + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
    388                                 + ". Only RSA public keys supported for this mode");
    389                     }
    390                     break;
    391                 default:
    392                     throw new InvalidKeyException(
    393                             "RSA private keys cannot be used with opmode: " + opmode);
    394             }
    395         } else {
    396             // Public key
    397             switch (opmode) {
    398                 case Cipher.ENCRYPT_MODE:
    399                 case Cipher.WRAP_MODE:
    400                     // Permitted
    401                     break;
    402                 case Cipher.DECRYPT_MODE:
    403                 case Cipher.UNWRAP_MODE:
    404                     throw new InvalidKeyException(
    405                             "RSA public keys cannot be used with " + opmodeToString(opmode)
    406                             + " and padding "
    407                             + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
    408                             + ". Only RSA private keys supported for this opmode.");
    409                     // break;
    410                 default:
    411                     throw new InvalidKeyException(
    412                             "RSA public keys cannot be used with " + opmodeToString(opmode));
    413             }
    414         }
    415 
    416         KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
    417         int errorCode = getKeyStore().getKeyCharacteristics(
    418                 keystoreKey.getAlias(), null, null, keystoreKey.getUid(), keyCharacteristics);
    419         if (errorCode != KeyStore.NO_ERROR) {
    420             throw getKeyStore().getInvalidKeyException(
    421                     keystoreKey.getAlias(), keystoreKey.getUid(), errorCode);
    422         }
    423         long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
    424         if (keySizeBits == -1) {
    425             throw new InvalidKeyException("Size of key not known");
    426         } else if (keySizeBits > Integer.MAX_VALUE) {
    427             throw new InvalidKeyException("Key too large: " + keySizeBits + " bits");
    428         }
    429         mModulusSizeBytes = (int) ((keySizeBits + 7) / 8);
    430 
    431         setKey(keystoreKey);
    432     }
    433 
    434     /**
    435      * Adjusts the configuration of this cipher for encrypting using the private key.
    436      *
    437      * <p>The default implementation does nothing and refuses to adjust the configuration.
    438      *
    439      * @return {@code true} if the configuration has been adjusted, {@code false} if encrypting
    440      *         using private key is not permitted for this cipher.
    441      */
    442     protected boolean adjustConfigForEncryptingWithPrivateKey() {
    443         return false;
    444     }
    445 
    446     @Override
    447     protected final void resetAll() {
    448         mModulusSizeBytes = -1;
    449         mKeymasterPaddingOverride = -1;
    450         super.resetAll();
    451     }
    452 
    453     @Override
    454     protected final void resetWhilePreservingInitState() {
    455         super.resetWhilePreservingInitState();
    456     }
    457 
    458     @Override
    459     protected void addAlgorithmSpecificParametersToBegin(
    460             @NonNull KeymasterArguments keymasterArgs) {
    461         keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
    462         int keymasterPadding = getKeymasterPaddingOverride();
    463         if (keymasterPadding == -1) {
    464             keymasterPadding = mKeymasterPadding;
    465         }
    466         keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
    467         int purposeOverride = getKeymasterPurposeOverride();
    468         if ((purposeOverride != -1)
    469                 && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN)
    470                 || (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) {
    471             // Keymaster sign/verify requires digest to be specified. For raw sign/verify it's NONE.
    472             keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
    473         }
    474     }
    475 
    476     @Override
    477     protected void loadAlgorithmSpecificParametersFromBeginResult(
    478             @NonNull KeymasterArguments keymasterArgs) {
    479     }
    480 
    481     @Override
    482     protected final int engineGetBlockSize() {
    483         // Not a block cipher, according to the RI
    484         return 0;
    485     }
    486 
    487     @Override
    488     protected final byte[] engineGetIV() {
    489         // IV never used
    490         return null;
    491     }
    492 
    493     @Override
    494     protected final int engineGetOutputSize(int inputLen) {
    495         return getModulusSizeBytes();
    496     }
    497 
    498     protected final int getModulusSizeBytes() {
    499         if (mModulusSizeBytes == -1) {
    500             throw new IllegalStateException("Not initialized");
    501         }
    502         return mModulusSizeBytes;
    503     }
    504 
    505     /**
    506      * Overrides the default padding of the crypto operation.
    507      */
    508     protected final void setKeymasterPaddingOverride(int keymasterPadding) {
    509         mKeymasterPaddingOverride = keymasterPadding;
    510     }
    511 
    512     protected final int getKeymasterPaddingOverride() {
    513         return mKeymasterPaddingOverride;
    514     }
    515 }
    516