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.annotation.Nullable;
21import android.security.KeyStore;
22import android.security.keymaster.KeyCharacteristics;
23import android.security.keymaster.KeymasterArguments;
24import android.security.keymaster.KeymasterDefs;
25
26import java.security.AlgorithmParameters;
27import java.security.InvalidAlgorithmParameterException;
28import java.security.InvalidKeyException;
29import java.security.Key;
30import java.security.NoSuchAlgorithmException;
31import java.security.PrivateKey;
32import java.security.ProviderException;
33import java.security.spec.AlgorithmParameterSpec;
34import java.security.spec.InvalidParameterSpecException;
35import java.security.spec.MGF1ParameterSpec;
36
37import javax.crypto.Cipher;
38import javax.crypto.CipherSpi;
39import javax.crypto.spec.OAEPParameterSpec;
40import javax.crypto.spec.PSource;
41
42/**
43 * Base class for {@link CipherSpi} providing Android KeyStore backed RSA encryption/decryption.
44 *
45 * @hide
46 */
47abstract 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