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

Commit d7e06104 authored by Alex Klyubin's avatar Alex Klyubin Committed by Android Git Automerger
Browse files

am 36ee836d: Merge "Symmetric key generation for AndroidKeyStore."

* commit '36ee836d':
  Symmetric key generation for AndroidKeyStore.
parents 3e89fac5 36ee836d
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.security.keymaster;

import java.util.HashMap;
import java.util.Map;

/**
 * Class tracking all the keymaster enum values needed for the binder API to keystore.
 * This must be kept in sync with hardware/libhardware/include/hardware/keymaster_defs.h
@@ -224,7 +227,53 @@ public final class KeymasterDefs {
    public static final int KM_ERROR_VERSION_MISMATCH = -101;
    public static final int KM_ERROR_UNKNOWN_ERROR = -1000;

    public static final Map<Integer, String> sErrorCodeToString = new HashMap<Integer, String>();
    static {
        sErrorCodeToString.put(KM_ERROR_OK, "OK");
        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PURPOSE, "Unsupported purpose");
        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PURPOSE, "Incompatible purpose");
        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_ALGORITHM, "Unsupported algorithm");
        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_ALGORITHM, "Incompatible algorithm");
        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_SIZE, "Unsupported key size");
        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_BLOCK_MODE, "Unsupported block mode");
        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_BLOCK_MODE, "Incompatible block mode");
        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_TAG_LENGTH,
                "Unsupported authentication tag length");
        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PADDING_MODE, "Unsupported padding mode");
        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PADDING_MODE, "Incompatible padding mode");
        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_DIGEST, "Unsupported digest");
        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_DIGEST, "Incompatible digest");
        sErrorCodeToString.put(KM_ERROR_INVALID_EXPIRATION_TIME, "Invalid expiration time");
        sErrorCodeToString.put(KM_ERROR_INVALID_USER_ID, "Invalid user ID");
        sErrorCodeToString.put(KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
                "Invalid user authorization timeout");
        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_FORMAT, "Unsupported key format");
        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_KEY_FORMAT, "Incompatible key format");
        sErrorCodeToString.put(KM_ERROR_INVALID_INPUT_LENGTH, "Invalid input length");
        sErrorCodeToString.put(KM_ERROR_KEY_NOT_YET_VALID, "Key not yet valid");
        sErrorCodeToString.put(KM_ERROR_KEY_EXPIRED, "Key expired");
        sErrorCodeToString.put(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, "Key user not authenticated");
        sErrorCodeToString.put(KM_ERROR_INVALID_OPERATION_HANDLE, "Invalid operation handle");
        sErrorCodeToString.put(KM_ERROR_VERIFICATION_FAILED, "Signature/MAC verification failed");
        sErrorCodeToString.put(KM_ERROR_TOO_MANY_OPERATIONS, "Too many operations");
        sErrorCodeToString.put(KM_ERROR_INVALID_KEY_BLOB, "Invalid key blob");
        sErrorCodeToString.put(KM_ERROR_INVALID_ARGUMENT, "Invalid argument");
        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_TAG, "Unsupported tag");
        sErrorCodeToString.put(KM_ERROR_INVALID_TAG, "Invalid tag");
        sErrorCodeToString.put(KM_ERROR_MEMORY_ALLOCATION_FAILED, "Memory allocation failed");
        sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
        sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
    }

    public static int getTagType(int tag) {
        return tag & (0xF << 28);
    }

    public static String getErrorMessage(int errorCode) {
        String result = sErrorCodeToString.get(errorCode);
        if (result != null) {
            return result;
        }
        return String.valueOf(errorCode);
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -35,5 +35,9 @@ public class AndroidKeyStoreProvider extends Provider {
        // java.security.KeyPairGenerator
        put("KeyPairGenerator.EC", AndroidKeyPairGenerator.EC.class.getName());
        put("KeyPairGenerator.RSA", AndroidKeyPairGenerator.RSA.class.getName());

        // javax.crypto.KeyGenerator
        put("KeyGenerator.AES", KeyStoreKeyGeneratorSpi.AES.class.getName());
        put("KeyGenerator.HmacSHA256", KeyStoreKeyGeneratorSpi.HmacSHA256.class.getName());
    }
}
+45 −0
Original line number Diff line number Diff line
package android.security;

/**
 * Base class for exceptions during cryptographic operations which cannot throw a suitable checked
 * exception.
 *
 * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or
 * {@code Signature}) is that they can throw a checked exception during initialization, but are not
 * permitted to throw a checked exception during operation. Because crypto operations can fail
 * for a variety of reasons after initialization, this base class provides type-safety for unchecked
 * exceptions that may be thrown in those cases.
 *
 * @hide
 */
public class CryptoOperationException extends RuntimeException {

    /**
     * Constructs a new {@code CryptoOperationException} without detail message and cause.
     */
    public CryptoOperationException() {
        super();
    }

    /**
     * Constructs a new {@code CryptoOperationException} with the provided detail message and no
     * cause.
     */
    public CryptoOperationException(String message) {
        super(message);
    }

    /**
     * Constructs a new {@code CryptoOperationException} with the provided detail message and cause.
     */
    public CryptoOperationException(String message, Throwable cause) {
        super(message, cause);
    }

    /**
     * Constructs a new {@code CryptoOperationException} with the provided cause.
     */
    public CryptoOperationException(Throwable cause) {
        super(cause);
    }
}
+471 −0
Original line number Diff line number Diff line
package android.security;

import android.content.Context;
import android.text.TextUtils;

import java.security.cert.Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

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

/**
 * {@link AlgorithmParameterSpec} for initializing a {@code KeyGenerator} that works with
 * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>.
 *
 * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API
 * using the {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up
 * some UI to ask the user to unlock or initialize the Android KeyStore facility.
 *
 * <p>After generation, the {@code keyStoreAlias} is used with the
 * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
 * interface to retrieve the {@link SecretKey} and its associated {@link Certificate} chain.
 *
 * @hide
 */
public class KeyGeneratorSpec implements AlgorithmParameterSpec {

    private final Context mContext;
    private final String mKeystoreAlias;
    private final int mFlags;
    private final Integer mKeySize;
    private final Date mKeyValidityStart;
    private final Date mKeyValidityForOriginationEnd;
    private final Date mKeyValidityForConsumptionEnd;
    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
    private final Integer mMinSecondsBetweenOperations;
    private final Integer mMaxUsesPerBoot;
    private final Set<Integer> mUserAuthenticators;
    private final Integer mUserAuthenticationValidityDurationSeconds;

    private KeyGeneratorSpec(
            Context context,
            String keyStoreAlias,
            int flags,
            Integer keySize,
            Date keyValidityStart,
            Date keyValidityForOriginationEnd,
            Date keyValidityForConsumptionEnd,
            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
            Integer minSecondsBetweenOperations,
            Integer maxUsesPerBoot,
            Set<Integer> userAuthenticators,
            Integer userAuthenticationValidityDurationSeconds) {
        if (context == null) {
            throw new IllegalArgumentException("context == null");
        } else if (TextUtils.isEmpty(keyStoreAlias)) {
            throw new IllegalArgumentException("keyStoreAlias must not be empty");
        } else if ((userAuthenticationValidityDurationSeconds != null)
                && (userAuthenticationValidityDurationSeconds < 0)) {
            throw new IllegalArgumentException(
                    "userAuthenticationValidityDurationSeconds must not be negative");
        }

        mContext = context;
        mKeystoreAlias = keyStoreAlias;
        mFlags = flags;
        mKeySize = keySize;
        mKeyValidityStart = keyValidityStart;
        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
        mPurposes = purposes;
        mPadding = padding;
        mBlockMode = blockMode;
        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
        mMaxUsesPerBoot = maxUsesPerBoot;
        mUserAuthenticators = (userAuthenticators != null)
                ? new HashSet<Integer>(userAuthenticators)
                : Collections.<Integer>emptySet();
        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
    }

    /**
     * Gets the Android context used for operations with this instance.
     */
    public Context getContext() {
        return mContext;
    }

    /**
     * Returns the alias that will be used in the {@code java.security.KeyStore} in conjunction with
     * the {@code AndroidKeyStore}.
     */
    public String getKeystoreAlias() {
        return mKeystoreAlias;
    }

    /**
     * @hide
     */
    public int getFlags() {
        return mFlags;
    }

    /**
     * Gets the requested key size or {@code null} if the default size should be used.
     */
    public Integer getKeySize() {
        return mKeySize;
    }

    /**
     * Gets the time instant before which the key is not yet valid.
     *
     * @return instant or {@code null} if not restricted.
     */
    public Date getKeyValidityStart() {
        return mKeyValidityStart;
    }

    /**
     * Gets the time instant after which the key is no long valid for decryption and verification.
     *
     * @return instant or {@code null} if not restricted.
     *
     * @hide
     */
    public Date getKeyValidityForConsumptionEnd() {
        return mKeyValidityForConsumptionEnd;
    }

    /**
     * Gets the time instant after which the key is no long valid for encryption and signing.
     *
     * @return instant or {@code null} if not restricted.
     */
    public Date getKeyValidityForOriginationEnd() {
        return mKeyValidityForOriginationEnd;
    }

    /**
     * Gets the set of purposes for which the key can be used to the provided set of purposes.
     *
     * @return set of purposes or {@code null} if the key can be used for any purpose.
     */
    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
        return mPurposes;
    }

    /**
     * Gets the padding scheme to which the key is restricted.
     *
     * @return padding scheme or {@code null} if the padding scheme is not restricted.
     */
    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
        return mPadding;
    }

    /**
     * Gets the block mode to which the key is restricted when used for encryption or decryption.
     *
     * @return block more or {@code null} if block mode is not restricted.
     *
     * @hide
     */
    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
        return mBlockMode;
    }

    /**
     * Gets the minimum number of seconds that must expire since the most recent use of the key
     * before it can be used again.
     *
     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
     *         can be used.
     *
     * @hide
     */
    public Integer getMinSecondsBetweenOperations() {
        return mMinSecondsBetweenOperations;
    }

    /**
     * Gets the number of times the key can be used without rebooting the device.
     *
     * @return maximum number of times or {@code null} if there is no restriction.
     * @hide
     */
    public Integer getMaxUsesPerBoot() {
        return mMaxUsesPerBoot;
    }

    /**
     * Gets 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.
     *
     * @return user authenticators or empty set if the key can be used without user authentication.
     *
     * @hide
     */
    public Set<Integer> getUserAuthenticators() {
        return new HashSet<Integer>(mUserAuthenticators);
    }

    /**
     * Gets the duration of time (seconds) for which this key can be used after the user
     * successfully authenticates to one of the associated user authenticators.
     *
     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
     *         is required for every use of the key.
     *
     * @hide
     */
    public Integer getUserAuthenticationValidityDurationSeconds() {
        return mUserAuthenticationValidityDurationSeconds;
    }

    /**
     * Returns {@code true} if the key must be encrypted in the {@link java.security.KeyStore}.
     */
    public boolean isEncryptionRequired() {
        return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
    }

    public static class Builder {
        private final Context mContext;
        private String mKeystoreAlias;
        private int mFlags;
        private Integer mKeySize;
        private Date mKeyValidityStart;
        private Date mKeyValidityForOriginationEnd;
        private Date mKeyValidityForConsumptionEnd;
        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
        private Integer mMinSecondsBetweenOperations;
        private Integer mMaxUsesPerBoot;
        private Set<Integer> mUserAuthenticators;
        private Integer mUserAuthenticationValidityDurationSeconds;

        /**
         * Creates a new instance of the {@code Builder} with the given {@code context}. The
         * {@code context} passed in may be used to pop up some UI to ask the user to unlock or
         * initialize the Android KeyStore facility.
         */
        public Builder(Context context) {
            if (context == null) {
                throw new NullPointerException("context == null");
            }
            mContext = context;
        }

        /**
         * Sets the alias to be used to retrieve the key later from a {@link java.security.KeyStore}
         * instance using the {@code AndroidKeyStore} provider.
         *
         * <p>The alias must be provided. There is no default.
         */
        public Builder setAlias(String alias) {
            if (alias == null) {
                throw new NullPointerException("alias == null");
            }
            mKeystoreAlias = alias;
            return this;
        }

        /**
         * Sets the size (in bits) of the key to be generated.
         *
         * <p>By default, the key size will be determines based on the key algorithm. For example,
         * for {@code HmacSHA256}, the key size will default to {@code 256}.
         */
        public Builder setKeySize(int keySize) {
            mKeySize = keySize;
            return this;
        }

        /**
         * Indicates that this key must be encrypted at rest on storage. Note that enabling this
         * will require that the user enable a strong lock screen (e.g., PIN, password) before
         * creating or using the generated key is successful.
         */
        public Builder setEncryptionRequired(boolean required) {
            if (required) {
                mFlags |= KeyStore.FLAG_ENCRYPTED;
            } else {
                mFlags &= ~KeyStore.FLAG_ENCRYPTED;
            }
            return this;
        }

        /**
         * Sets the time instant before which the key is not yet valid.
         *
         * <b>By default, the key is valid at any instant.
         *
         * @see #setKeyValidityEnd(Date)
         *
         * @hide
         */
        public Builder setKeyValidityStart(Date startDate) {
            mKeyValidityStart = startDate;
            return this;
        }

        /**
         * Sets the time instant after which the key is no longer valid.
         *
         * <b>By default, the key is valid at any instant.
         *
         * @see #setKeyValidityStart(Date)
         * @see #setKeyValidityForConsumptionEnd(Date)
         * @see #setKeyValidityForOriginationEnd(Date)
         *
         * @hide
         */
        public Builder setKeyValidityEnd(Date endDate) {
            setKeyValidityForOriginationEnd(endDate);
            setKeyValidityForConsumptionEnd(endDate);
            return this;
        }

        /**
         * Sets the time instant after which the key is no longer valid for encryption and signing.
         *
         * <b>By default, the key is valid at any instant.
         *
         * @see #setKeyValidityForConsumptionEnd(Date)
         *
         * @hide
         */
        public Builder setKeyValidityForOriginationEnd(Date endDate) {
            mKeyValidityForOriginationEnd = endDate;
            return this;
        }

        /**
         * Sets the time instant after which the key is no longer valid for decryption and
         * verification.
         *
         * <b>By default, the key is valid at any instant.
         *
         * @see #setKeyValidityForOriginationEnd(Date)
         *
         * @hide
         */
        public Builder setKeyValidityForConsumptionEnd(Date endDate) {
            mKeyValidityForConsumptionEnd = endDate;
            return this;
        }

        /**
         * Restricts the purposes for which the key can be used to the provided set of purposes.
         *
         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
         *
         * @hide
         */
        public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
            mPurposes = purposes;
            return this;
        }

        /**
         * Restricts the key to being used only with the provided padding scheme. Attempts to use
         * the key with any other padding will be rejected.
         *
         * <p>This restriction must be specified for keys which are used for encryption/decryption.
         *
         * @hide
         */
        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
            mPadding = padding;
            return this;
        }

        /**
         * Restricts the key to being used only with the provided block mode when encrypting or
         * decrypting. Attempts to use the key with any other block modes will be rejected.
         *
         * <p>This restriction must be specified for keys which are used for encryption/decryption.
         *
         * @hide
         */
        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
            mBlockMode = blockMode;
            return this;
        }

        /**
         * Sets the minimum number of seconds that must expire since the most recent use of the key
         * before it can be used again.
         *
         * <p>By default, there is no restriction on how frequently a key can be used.
         *
         * @hide
         */
        public Builder setMinSecondsBetweenOperations(int seconds) {
            mMinSecondsBetweenOperations = seconds;
            return this;
        }

        /**
         * Sets the maximum number of times a key can be used without rebooting the device.
         *
         * <p>By default, the key can be used for an unlimited number of times.
         *
         * @hide
         */
        public Builder setMaxUsesPerBoot(int count) {
            mMaxUsesPerBoot = count;
            return this;
        }

        /**
         * 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.
         *
         * <p>By default, the key can be used without user authentication.
         *
         * @param userAuthenticators user authenticators or empty list if this key can be accessed
         *        without user authentication.
         *
         * @see #setUserAuthenticationValidityDurationSeconds(int)
         *
         * @hide
         */
        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
            mUserAuthenticators =
                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
            return this;
        }

        /**
         * Sets the duration of time (seconds) for which this key can be used after the user
         * successfully authenticates to one of the associated user authenticators.
         *
         * <p>By default, the user needs to authenticate for every use of the key.
         *
         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
         *        every use of the key.
         *
         * @see #setUserAuthenticators(Set)
         *
         * @hide
         */
        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
            mUserAuthenticationValidityDurationSeconds = seconds;
            return this;
        }

        /**
         * Builds a new instance instance of {@code KeyGeneratorSpec}.
         *
         * @throws IllegalArgumentException if a required field is missing or violates a constraint.
         */
        public KeyGeneratorSpec build() {
            return new KeyGeneratorSpec(mContext, mKeystoreAlias, mFlags, mKeySize,
                    mKeyValidityStart, mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd,
                    mPurposes, mPadding, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot,
                    mUserAuthenticators, mUserAuthenticationValidityDurationSeconds);
        }
    }
}
+28 −0
Original line number Diff line number Diff line
@@ -290,6 +290,22 @@ public abstract class KeyStoreKeyConstraints {
                    throw new IllegalArgumentException("Unknown padding: " + padding);
            }
        }

        /**
         * @hide
         */
        public static String toString(@PaddingEnum int padding) {
            switch (padding) {
                case NONE:
                    return "NONE";
                case ZERO:
                    return "ZERO";
                case PKCS7:
                    return "PKCS#7";
                default:
                    throw new IllegalArgumentException("Unknown padding: " + padding);
            }
        }
    }

    @Retention(RetentionPolicy.SOURCE)
@@ -425,5 +441,17 @@ public abstract class KeyStoreKeyConstraints {
                    throw new IllegalArgumentException("Unknown block mode: " + mode);
            }
        }

        /**
         * @hide
         */
        public static String toString(@BlockModeEnum int mode) {
            switch (mode) {
                case ECB:
                    return "ECB";
                default:
                    throw new IllegalArgumentException("Unknown block mode: " + mode);
            }
        }
    }
}
Loading