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

Commit 5552c89f authored by Alex Klyubin's avatar Alex Klyubin
Browse files

RSA encrypt with private key in Android Keystore.

This adds support for RSA encryption using private key and no padding.
This mode of operation is needed because JCA does not offer an RSA
Signature primitive that does not apply padding.

Bug: 18088752
Bug: 20912868
Change-Id: I0b481b4c19916f601aa270fada5eabfb12987e8d
parent 3a4656e8
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