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

Commit f853f649 authored by Alex Klyubin's avatar Alex Klyubin
Browse files

Require IND-CPA by default for new AndroidKeyStore keys.

Bug: 18088752
Change-Id: I01e44b7155df4326b5c9d83dda57f889c1f23ec7
parent c0d6b7cb
Loading
Loading
Loading
Loading
+16 −5
Original line number Original line Diff line number Diff line
@@ -512,12 +512,23 @@ public class AndroidKeyStore extends KeyStoreSpi {
            }
            }
        }
        }


        int purposes = params.getPurposes();
        @KeyStoreKeyConstraints.PurposeEnum int purposes = params.getPurposes();
        @KeyStoreKeyConstraints.BlockModeEnum int blockModes = params.getBlockModes();
        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
                && (params.isRandomizedEncryptionRequired())) {
            @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes =
                    blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES;
            if (incompatibleBlockModes != 0) {
                throw new KeyStoreException("Randomized encryption (IND-CPA) required but may be"
                        + " violated by block mode(s): "
                        + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes)
                        + ". See KeyStoreParameter documentation.");
            }
        }
        for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
        for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
        }
        }
        for (int keymasterBlockMode :
        for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) {
            KeyStoreKeyConstraints.BlockMode.allToKeymaster(params.getBlockModes())) {
            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
        }
        }
        for (int keymasterPadding :
        for (int keymasterPadding :
@@ -549,8 +560,8 @@ public class AndroidKeyStore extends KeyStoreSpi {
        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);


        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
                || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
                && (!params.isRandomizedEncryptionRequired())) {
            // Permit caller-specified IV. This is needed for the Cipher abstraction.
            // Permit caller-provided IV when encrypting with this key
            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
        }
        }


+56 −0
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.text.TextUtils;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Date;
import java.util.Date;


import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKey;


@@ -51,6 +52,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
    private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
    private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
    private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
    private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
    private final boolean mRandomizedEncryptionRequired;
    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
    private final int mUserAuthenticationValidityDurationSeconds;
    private final int mUserAuthenticationValidityDurationSeconds;


@@ -65,6 +67,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
            @KeyStoreKeyConstraints.PurposeEnum int purposes,
            @KeyStoreKeyConstraints.PurposeEnum int purposes,
            @KeyStoreKeyConstraints.PaddingEnum int paddings,
            @KeyStoreKeyConstraints.PaddingEnum int paddings,
            @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
            @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
            boolean randomizedEncryptionRequired,
            @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
            @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
            int userAuthenticationValidityDurationSeconds) {
            int userAuthenticationValidityDurationSeconds) {
        if (context == null) {
        if (context == null) {
@@ -87,6 +90,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
        mPurposes = purposes;
        mPurposes = purposes;
        mPaddings = paddings;
        mPaddings = paddings;
        mBlockModes = blockModes;
        mBlockModes = blockModes;
        mRandomizedEncryptionRequired = randomizedEncryptionRequired;
        mUserAuthenticators = userAuthenticators;
        mUserAuthenticators = userAuthenticators;
        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
    }
    }
@@ -168,6 +172,19 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
        return mBlockModes;
        return mBlockModes;
    }
    }


    /**
     * Returns {@code true} if encryption using this key must be sufficiently randomized to produce
     * different ciphertexts for the same plaintext every time. The formal cryptographic property
     * being required is <em>indistinguishability under chosen-plaintext attack ({@code
     * IND-CPA})</em>. This property is important because it mitigates several classes of
     * weaknesses due to which ciphertext may leak information about plaintext. For example, if a
     * given plaintext always produces the same ciphertext, an attacker may see the repeated
     * ciphertexts and be able to deduce something about the plaintext.
     */
    public boolean isRandomizedEncryptionRequired() {
        return mRandomizedEncryptionRequired;
    }

    /**
    /**
     * Gets the set of user authenticators which protect access to this key. The key can only be
     * Gets the set of user authenticators which protect access to this key. The key can only be
     * used iff the user has authenticated to at least one of these user authenticators.
     * used iff the user has authenticated to at least one of these user authenticators.
@@ -207,6 +224,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
        private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
        private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
        private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
        private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
        private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
        private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
        private boolean mRandomizedEncryptionRequired = true;
        private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
        private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
        private int mUserAuthenticationValidityDurationSeconds = -1;
        private int mUserAuthenticationValidityDurationSeconds = -1;


@@ -345,6 +363,43 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
            return this;
            return this;
        }
        }


        /**
         * Sets whether encryption using this key must be sufficiently randomized to produce
         * different ciphertexts for the same plaintext every time. The formal cryptographic
         * property being required is <em>indistinguishability under chosen-plaintext attack
         * ({@code IND-CPA})</em>. This property is important because it mitigates several classes
         * of weaknesses due to which ciphertext may leak information about plaintext. For example,
         * if a given plaintext always produces the same ciphertext, an attacker may see the
         * repeated ciphertexts and be able to deduce something about the plaintext.
         *
         * <p>By default, {@code IND-CPA} is required.
         *
         * <p>When {@code IND-CPA} is required:
         * <ul>
         * <li>block modes which do not offer {@code IND-CPA}, such as {@code ECB}, are prohibited;
         * </li>
         * <li>in block modes which use an IV, such as {@code CBC}, {@code CTR}, and {@code GCM},
         * caller-provided IVs are rejected when encrypting, to ensure that only random IVs are
         * used.</li>
         *
         * <p>Before disabling this requirement, consider the following approaches instead:
         * <ul>
         * <li>If you are generating a random IV for encryption and then initializing a {@code}
         * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV
         * instead. This will occur if the {@code Cipher} is initialized for encryption without an
         * IV. The IV can then be queried via {@link Cipher#getIV()}.</li>
         * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully
         * random, such as the name of the file being encrypted, or transaction ID, or password,
         * or a device identifier), consider changing your design to use a random IV which will then
         * be provided in addition to the ciphertext to the entities which need to decrypt the
         * ciphertext.</li>
         * </ul>
         */
        public Builder setRandomizedEncryptionRequired(boolean required) {
            mRandomizedEncryptionRequired = required;
            return this;
        }

        /**
        /**
         * Sets the user authenticators which protect access to this key. The key can only be used
         * Sets the user authenticators which protect access to this key. The key can only be used
         * iff the user has authenticated to at least one of these user authenticators.
         * iff the user has authenticated to at least one of these user authenticators.
@@ -394,6 +449,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
                    mPurposes,
                    mPurposes,
                    mPaddings,
                    mPaddings,
                    mBlockModes,
                    mBlockModes,
                    mRandomizedEncryptionRequired,
                    mUserAuthenticators,
                    mUserAuthenticators,
                    mUserAuthenticationValidityDurationSeconds);
                    mUserAuthenticationValidityDurationSeconds);
        }
        }
+69 −2
Original line number Original line Diff line number Diff line
@@ -86,6 +86,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {


    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;


    private final boolean mRandomizedEncryptionRequired;

    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;


    private final int mUserAuthenticationValidityDurationSeconds;
    private final int mUserAuthenticationValidityDurationSeconds;
@@ -132,6 +134,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
            @KeyStoreKeyConstraints.DigestEnum int digests,
            @KeyStoreKeyConstraints.DigestEnum int digests,
            @KeyStoreKeyConstraints.PaddingEnum int paddings,
            @KeyStoreKeyConstraints.PaddingEnum int paddings,
            @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
            @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
            boolean randomizedEncryptionRequired,
            @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
            @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
            int userAuthenticationValidityDurationSeconds) {
            int userAuthenticationValidityDurationSeconds) {
        if (context == null) {
        if (context == null) {
@@ -171,6 +174,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
        mDigests = digests;
        mDigests = digests;
        mPaddings = paddings;
        mPaddings = paddings;
        mBlockModes = blockModes;
        mBlockModes = blockModes;
        mRandomizedEncryptionRequired = randomizedEncryptionRequired;
        mUserAuthenticators = userAuthenticators;
        mUserAuthenticators = userAuthenticators;
        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
    }
    }
@@ -182,8 +186,26 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
    public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
    public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
            AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
            AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
            Date startDate, Date endDate, int flags) {
            Date startDate, Date endDate, int flags) {
        this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate,
        this(context,
                endDate, flags, startDate, endDate, endDate, 0, 0, 0, 0, 0, -1);
                keyStoreAlias,
                keyType,
                keySize,
                spec,
                subjectDN,
                serialNumber,
                startDate,
                endDate,
                flags,
                startDate,
                endDate,
                endDate,
                0,
                0,
                0,
                0,
                true,
                0,
                -1);
    }
    }


    /**
    /**
@@ -342,6 +364,21 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
        return mBlockModes;
        return mBlockModes;
    }
    }


    /**
     * Returns {@code true} if encryption using this key must be sufficiently randomized to produce
     * different ciphertexts for the same plaintext every time. The formal cryptographic property
     * being required is <em>indistinguishability under chosen-plaintext attack ({@code
     * IND-CPA})</em>. This property is important because it mitigates several classes of
     * weaknesses due to which ciphertext may leak information about plaintext.  For example, if a
     * given plaintext always produces the same ciphertext, an attacker may see the repeated
     * ciphertexts and be able to deduce something about the plaintext.
     *
     * @hide
     */
    public boolean isRandomizedEncryptionRequired() {
        return mRandomizedEncryptionRequired;
    }

    /**
    /**
     * Gets the set of user authenticators which protect access to the private key. The key can only
     * Gets the set of user authenticators which protect access to the private key. The key can only
     * be used iff the user has authenticated to at least one of these user authenticators.
     * be used iff the user has authenticated to at least one of these user authenticators.
@@ -429,6 +466,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {


        private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
        private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;


        private boolean mRandomizedEncryptionRequired = true;

        private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
        private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;


        private int mUserAuthenticationValidityDurationSeconds = -1;
        private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -669,6 +708,33 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
            return this;
            return this;
        }
        }


        /**
         * Sets whether encryption using this key must be sufficiently randomized to produce
         * different ciphertexts for the same plaintext every time. The formal cryptographic
         * property being required is <em>indistinguishability under chosen-plaintext attack
         * ({@code IND-CPA})</em>. This property is important because it mitigates several classes
         * of weaknesses due to which ciphertext may leak information about plaintext. For example,
         * if a given plaintext always produces the same ciphertext, an attacker may see the
         * repeated ciphertexts and be able to deduce something about the plaintext.
         *
         * <p>By default, {@code IND-CPA} is required.
         *
         * <p>When {@code IND-CPA} is required, encryption/decryption transformations which do not
         * offer {@code IND-CPA}, such as RSA without padding, are prohibited.
         *
         * <p>Before disabling this requirement, consider the following approaches instead:
         * <ul>
         * <li>If you are using RSA encryption without padding, consider switching to padding
         * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
         * </ul>
         *
         * @hide
         */
        public Builder setRandomizedEncryptionRequired(boolean required) {
            mRandomizedEncryptionRequired = required;
            return this;
        }

        /**
        /**
         * Sets the user authenticators which protect access to this key. The key can only be used
         * Sets the user authenticators which protect access to this key. The key can only be used
         * iff the user has authenticated to at least one of these user authenticators.
         * iff the user has authenticated to at least one of these user authenticators.
@@ -736,6 +802,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec {
                    mDigests,
                    mDigests,
                    mPaddings,
                    mPaddings,
                    mBlockModes,
                    mBlockModes,
                    mRandomizedEncryptionRequired,
                    mUserAuthenticators,
                    mUserAuthenticators,
                    mUserAuthenticationValidityDurationSeconds);
                    mUserAuthenticationValidityDurationSeconds);
        }
        }
+26 −0
Original line number Original line Diff line number Diff line
@@ -492,6 +492,14 @@ public abstract class KeyStoreKeyConstraints {
        /** Galois/Counter Mode (GCM) block mode. */
        /** Galois/Counter Mode (GCM) block mode. */
        public static final int GCM = 1 << 3;
        public static final int GCM = 1 << 3;


        /**
         * Set of block modes compatible with IND-CPA if used correctly.
         *
         * @hide
         */
        public static final @BlockModeEnum int IND_CPA_COMPATIBLE_MODES =
                CBC | CTR | GCM;

        /**
        /**
         * @hide
         * @hide
         */
         */
@@ -568,6 +576,24 @@ public abstract class KeyStoreKeyConstraints {
            }
            }
        }
        }


        /**
         * @hide
         */
        public static String allToString(@BlockModeEnum int modes) {
            StringBuilder result = new StringBuilder("[");
            boolean firstValue = true;
            for (@BlockModeEnum int mode : getSetFlags(modes)) {
                if (firstValue) {
                    firstValue = false;
                } else {
                    result.append(", ");
                }
                result.append(toString(mode));
            }
            result.append(']');
            return result.toString();
        }

        /**
        /**
         * @hide
         * @hide
         */
         */
+18 −5
Original line number Original line Diff line number Diff line
@@ -109,13 +109,26 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
        }
        }
        int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
        int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
        int purposes = spec.getPurposes();
        @KeyStoreKeyConstraints.PurposeEnum int purposes = spec.getPurposes();
        @KeyStoreKeyConstraints.BlockModeEnum int blockModes = spec.getBlockModes();
        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
                && (spec.isRandomizedEncryptionRequired())) {
            @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes =
                    blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES;
            if (incompatibleBlockModes != 0) {
                throw new IllegalStateException(
                        "Randomized encryption (IND-CPA) required but may be violated by block"
                        + " mode(s): "
                        + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes)
                        + ". See KeyGeneratorSpec documentation.");
            }
        }

        for (int keymasterPurpose :
        for (int keymasterPurpose :
            KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
            KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
        }
        }
        for (int keymasterBlockMode :
        for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) {
            KeyStoreKeyConstraints.BlockMode.allToKeymaster(spec.getBlockModes())) {
            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
        }
        }
        for (int keymasterPadding :
        for (int keymasterPadding :
@@ -144,8 +157,8 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
                ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
                ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));


        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
            || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
                && (!spec.isRandomizedEncryptionRequired())) {
            // Permit caller-specified IV. This is needed due to the Cipher abstraction.
            // Permit caller-provided IV when encrypting with this key
            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
        }
        }


Loading