Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2c500236 authored by Alex Klyubin's avatar Alex Klyubin Committed by Android (Google) Code Review
Browse files

Merge "RSA encrypt with private key in Android Keystore." into mnc-dev

parents 5b688e84 5552c89f
Loading
Loading
Loading
Loading
+24 −4
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
    // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
    // doFinal finishes.
    private boolean mEncrypting;
    private int mKeymasterPurposeOverride = -1;
    private AndroidKeyStoreKey mKey;
    private SecureRandom mRng;

@@ -165,6 +166,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
            mKeyStore.abort(operationToken);
        }
        mEncrypting = false;
        mKeymasterPurposeOverride = -1;
        mKey = null;
        mRng = null;
        mOperationToken = null;
@@ -210,9 +212,16 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
        byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
                mRng, getAdditionalEntropyAmountForBegin());

        int purpose;
        if (mKeymasterPurposeOverride != -1) {
            purpose = mKeymasterPurposeOverride;
        } else {
            purpose = mEncrypting
                    ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT;
        }
        OperationResult opResult = mKeyStore.begin(
                mKey.getAlias(),
                mEncrypting ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT,
                purpose,
                true, // permit aborting this operation if keystore runs out of resources
                keymasterInputArgs,
                additionalEntropy);
@@ -346,11 +355,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
        } catch (KeyStoreException e) {
            switch (e.getErrorCode()) {
                case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
                    throw new IllegalBlockSizeException();
                    throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
                case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT:
                    throw new BadPaddingException();
                    throw (BadPaddingException) new BadPaddingException().initCause(e);
                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
                    throw new AEADBadTagException();
                    throw (AEADBadTagException) new AEADBadTagException().initCause(e);
                default:
                    throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
            }
@@ -436,6 +445,17 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
        mKey = key;
    }

    /**
     * Overrides the default purpose/type of the crypto operation.
     */
    protected final void setKeymasterPurposeOverride(int keymasterPurpose) {
        mKeymasterPurposeOverride = keymasterPurpose;
    }

    protected final int getKeymasterPurposeOverride() {
        return mKeymasterPurposeOverride;
    }

    /**
     * Returns {@code true} if this cipher is initialized for encryption, {@code false} if this
     * cipher is initialized for decryption.
+61 −8
Original line number Diff line number Diff line
@@ -59,6 +59,13 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
            super(KeymasterDefs.KM_PAD_NONE);
        }

        @Override
        protected boolean isEncryptingUsingPrivateKeyPermitted() {
            // RSA encryption with no padding using private key is is a way to implement raw RSA
            // signatures. We have to support this.
            return true;
        }

        @Override
        protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}

@@ -152,8 +159,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
                            paddedInput.length - bufferedInput.length,
                            bufferedInput.length);
                } else {
                    // No need to pad input
                    paddedInput = bufferedInput;
                    // RI throws BadPaddingException in this scenario. INVALID_ARGUMENT below will
                    // be translated into BadPaddingException.
                    throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_ARGUMENT,
                            "Message size (" + bufferedInput.length + " bytes) must be smaller than"
                            + " modulus (" + mModulusSizeBytes + " bytes)");
                }
                return mDelegate.doFinal(paddedInput, 0, paddedInput.length);
            }
@@ -412,14 +422,46 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
        }

        if (keystoreKey instanceof PrivateKey) {
            if ((opmode != Cipher.DECRYPT_MODE) && (opmode != Cipher.UNWRAP_MODE)) {
                throw new InvalidKeyException("Private key cannot be used with opmode: " + opmode
                        + ". Only DECRYPT_MODE and UNWRAP_MODE supported");
            // Private key
            switch (opmode) {
                case Cipher.DECRYPT_MODE:
                case Cipher.UNWRAP_MODE:
                    // Permitted
                    break;
                case Cipher.ENCRYPT_MODE:
                    if (!isEncryptingUsingPrivateKeyPermitted()) {
                        throw new InvalidKeyException(
                                "RSA private keys cannot be used with Cipher.ENCRYPT_MODE"
                                + ". Only RSA public keys supported for this mode");
                    }
                    // JCA doesn't provide a way to generate raw RSA signatures (with arbitrary
                    // padding). Thus, encrypting with private key is used instead.
                    setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
                    break;
                case Cipher.WRAP_MODE:
                    throw new InvalidKeyException(
                            "RSA private keys cannot be used with Cipher.WRAP_MODE"
                            + ". Only RSA public keys supported for this mode");
                    // break;
                default:
                    throw new InvalidKeyException(
                            "RSA private keys cannot be used with opmode: " + opmode);
            }
        } else {
            if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.WRAP_MODE)) {
                throw new InvalidKeyException("Public key cannot be used with opmode: " + opmode
                        + ". Only ENCRYPT_MODE and WRAP_MODE supported");
            // Public key
            switch (opmode) {
                case Cipher.ENCRYPT_MODE:
                case Cipher.WRAP_MODE:
                    // Permitted
                    return;
                case Cipher.DECRYPT_MODE:
                case Cipher.UNWRAP_MODE:
                    throw new InvalidKeyException("RSA public keys cannot be used with opmode: "
                            + opmode + ". Only RSA private keys supported for this opmode.");
                    // break;
                default:
                    throw new InvalidKeyException(
                            "RSA public keys cannot be used with opmode: " + opmode);
            }
        }

@@ -438,6 +480,10 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
        setKey(keystoreKey);
    }

    protected boolean isEncryptingUsingPrivateKeyPermitted() {
        return false;
    }

    @Override
    protected final void resetAll() {
        mModulusSizeBytes = -1;
@@ -454,6 +500,13 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
            @NonNull KeymasterArguments keymasterArgs) {
        keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
        keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
        int purposeOverride = getKeymasterPurposeOverride();
        if ((purposeOverride != -1)
                && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN)
                || (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) {
            // Keymaster sign/verify requires digest to be specified. For raw sign/verify it's NONE.
            keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
        }
    }

    @Override