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

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

am 9c0f257f: am 19e79e12: Merge "Add SecretKeyFactory backed by AndroidKeyStore."

* commit '9c0f257f':
  Add SecretKeyFactory backed by AndroidKeyStore.
parents a54f2b16 9c0f257f
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -40,6 +40,10 @@ public class AndroidKeyStoreProvider extends Provider {
        put("KeyGenerator.AES", KeyStoreKeyGeneratorSpi.AES.class.getName());
        put("KeyGenerator.HmacSHA256", KeyStoreKeyGeneratorSpi.HmacSHA256.class.getName());

        // java.security.SecretKeyFactory
        put("SecretKeyFactory.AES", KeyStoreSecretKeyFactorySpi.class.getName());
        put("SecretKeyFactory.HmacSHA256", KeyStoreSecretKeyFactorySpi.class.getName());

        // javax.crypto.Mac
        putMacImpl("HmacSHA256", KeyStoreHmacSpi.HmacSHA256.class.getName());

+1 −1
Original line number Diff line number Diff line
@@ -146,7 +146,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
    }

    /**
     * Gets the set of purposes for which the key can be used to the provided set of purposes.
     * Gets the set of purposes for which the key can be used.
     *
     * @return set of purposes or {@code null} if the key can be used for any purpose.
     */
+52 −0
Original line number Diff line number Diff line
package android.security;

import android.annotation.IntDef;
import android.security.keymaster.KeymasterDefs;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Characteristics of {@code AndroidKeyStore} keys.
 *
 * @hide
 */
public abstract class KeyStoreKeyCharacteristics {
    private KeyStoreKeyCharacteristics() {}

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({Origin.GENERATED_INSIDE_TEE, Origin.GENERATED_OUTSIDE_OF_TEE, Origin.IMPORTED})
    public @interface OriginEnum {}

    /**
     * Origin of the key.
     */
    public static abstract class Origin {
        private Origin() {}

        /** Key was generated inside a TEE. */
        public static final int GENERATED_INSIDE_TEE = 1;

        /** Key was generated outside of a TEE. */
        public static final int GENERATED_OUTSIDE_OF_TEE = 2;

        /** Key was imported. */
        public static final int IMPORTED = 0;

        /**
         * @hide
         */
        public static @OriginEnum int fromKeymaster(int origin) {
            switch (origin) {
                case KeymasterDefs.KM_ORIGIN_HARDWARE:
                    return GENERATED_INSIDE_TEE;
                case KeymasterDefs.KM_ORIGIN_SOFTWARE:
                    return GENERATED_OUTSIDE_OF_TEE;
                case KeymasterDefs.KM_ORIGIN_IMPORTED:
                    return IMPORTED;
                default:
                    throw new IllegalArgumentException("Unknown origin: " + origin);
            }
        }
    }
}
+210 −0
Original line number Diff line number Diff line
package android.security;

import java.security.spec.KeySpec;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * Information about a key from the <a href="{@docRoot}training/articles/keystore.html">Android
 * KeyStore</a>.
 *
 * @hide
 */
public class KeyStoreKeySpec implements KeySpec {
    private final String mKeystoreAlias;
    private final int mKeySize;
    private final @KeyStoreKeyCharacteristics.OriginEnum int mOrigin;
    private final Date mKeyValidityStart;
    private final Date mKeyValidityForOriginationEnd;
    private final Date mKeyValidityForConsumptionEnd;
    private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
    private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
    private final Integer mMinSecondsBetweenOperations;
    private final Integer mMaxUsesPerBoot;
    private final Set<Integer> mUserAuthenticators;
    private final Set<Integer> mTeeBackedUserAuthenticators;
    private final Integer mUserAuthenticationValidityDurationSeconds;


    /**
     * @hide
     */
    KeyStoreKeySpec(String keystoreKeyAlias,
            @KeyStoreKeyCharacteristics.OriginEnum int origin,
            int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd,
            Date keyValidityForConsumptionEnd,
            @KeyStoreKeyConstraints.PurposeEnum int purposes,
            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
            @KeyStoreKeyConstraints.DigestEnum Integer digest,
            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
            Integer minSecondsBetweenOperations,
            Integer maxUsesPerBoot,
            Set<Integer> userAuthenticators,
            Set<Integer> teeBackedUserAuthenticators,
            Integer userAuthenticationValidityDurationSeconds) {
        mKeystoreAlias = keystoreKeyAlias;
        mOrigin = origin;
        mKeySize = keySize;
        mKeyValidityStart = keyValidityStart;
        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
        mPurposes = purposes;
        mAlgorithm = algorithm;
        mPadding = padding;
        mDigest = digest;
        mBlockMode = blockMode;
        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
        mMaxUsesPerBoot = maxUsesPerBoot;
        mUserAuthenticators = (userAuthenticators != null)
                ? new HashSet<Integer>(userAuthenticators)
                : Collections.<Integer>emptySet();
        mTeeBackedUserAuthenticators = (teeBackedUserAuthenticators != null)
                ? new HashSet<Integer>(teeBackedUserAuthenticators)
                : Collections.<Integer>emptySet();
        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
    }

    /**
     * Gets the entry alias under which the key is stored in the {@code AndroidKeyStore}.
     */
    public String getKeystoreAlias() {
        return mKeystoreAlias;
    }

    /**
     * Gets the origin of the key.
     */
    public @KeyStoreKeyCharacteristics.OriginEnum int getOrigin() {
        return mOrigin;
    }

    /**
     * Gets the key's size in bits.
     */
    public int 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.
     */
    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.
     */
    public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
        return mPurposes;
    }

    /**
     * Gets the algorithm of the key.
     */
    public @KeyStoreKeyConstraints.AlgorithmEnum int getAlgorithm() {
        return mAlgorithm;
    }

    /**
     * Gets the only block mode with which the key can be used.
     *
     * @return block mode or {@code null} if the block mode is not restricted.
     */
    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
        return mBlockMode;
    }

    /**
     * Gets the only padding mode with which the key can be used.
     *
     * @return padding mode or {@code null} if the padding mode is not restricted.
     */
    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
        return mPadding;
    }

    /**
     * Gets the only digest algorithm with which the key can be used.
     *
     * @return digest algorithm or {@code null} if the digest algorithm is not restricted.
     */
    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
        return mDigest;
    }

    /**
     * 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.
     */
    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.
     */
    public Integer getMaxUsesPerBoot() {
        return mMaxUsesPerBoot;
    }

    /**
     * Gets the user authenticators which protect access to the 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.
     */
    public Set<Integer> getUserAuthenticators() {
        return new HashSet<Integer>(mUserAuthenticators);
    }

    /**
     * Gets the TEE-backed user authenticators which protect access to the key. This is a subset of
     * the user authentications returned by {@link #getUserAuthenticators()}.
     */
    public Set<Integer> getTeeBackedUserAuthenticators() {
        return new HashSet<Integer>(mTeeBackedUserAuthenticators);
    }

    /**
     * Gets the duration of time (seconds) for which the 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.
     */
    public Integer getUserAuthenticationValidityDurationSeconds() {
        return mUserAuthenticationValidityDurationSeconds;
    }
}
+140 −0
Original line number Diff line number Diff line
package android.security;

import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;

import java.security.InvalidKeyException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Collections;
import java.util.Set;

import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactorySpi;
import javax.crypto.spec.SecretKeySpec;

/**
 * {@link SecretKeyFactorySpi} backed by Android KeyStore.
 *
 * @hide
 */
public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {

    private final KeyStore mKeyStore = KeyStore.getInstance();

    @Override
    protected KeySpec engineGetKeySpec(SecretKey key,
            @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
        if (keySpecClass == null) {
            throw new InvalidKeySpecException("keySpecClass == null");
        }
        if (!(key instanceof KeyStoreSecretKey)) {
            throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " +
                    ((key != null) ? key.getClass().getName() : "null"));
        }
        if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) {
            throw new InvalidKeySpecException(
                    "Key material export of Android KeyStore keys is not supported");
        }
        if (!KeyStoreKeySpec.class.equals(keySpecClass)) {
            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
        }
        String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias();
        String entryAlias;
        if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
            entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
        } else {
            throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
        }

        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
        int errorCode =
                mKeyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
        if (errorCode != KeyStore.NO_ERROR) {
            throw new InvalidKeySpecException("Failed to obtain information about key."
                    + " Keystore error: " + errorCode);
        }

        @KeyStoreKeyCharacteristics.OriginEnum Integer origin;
        int keySize;
        @KeyStoreKeyConstraints.PurposeEnum int purposes;
        @KeyStoreKeyConstraints.AlgorithmEnum int algorithm;
        @KeyStoreKeyConstraints.PaddingEnum Integer padding;
        @KeyStoreKeyConstraints.DigestEnum Integer digest;
        @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode;
        try {
            origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN);
            if (origin == null) {
                throw new InvalidKeySpecException("Key origin not available");
            }
            origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster(origin);
            Integer keySizeInteger =
                    KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_KEY_SIZE);
            if (keySizeInteger == null) {
                throw new InvalidKeySpecException("Key size not available");
            }
            keySize = keySizeInteger;
            purposes = KeyStoreKeyConstraints.Purpose.allFromKeymaster(
                    KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PURPOSE));
            Integer alg = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ALGORITHM);
            if (alg == null) {
                throw new InvalidKeySpecException("Key algorithm not available");
            }
            algorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(alg);
            padding = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING);
            if (padding != null) {
                padding = KeyStoreKeyConstraints.Padding.fromKeymaster(padding);
            }
            digest = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST);
            if (digest != null) {
                digest = KeyStoreKeyConstraints.Digest.fromKeymaster(digest);
            }
            blockMode = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE);
            if (blockMode != null) {
                blockMode = KeyStoreKeyConstraints.BlockMode.fromKeymaster(blockMode);
            }
        } catch (IllegalArgumentException e) {
            throw new InvalidKeySpecException("Unsupported key characteristic", e);
        }

        // TODO: Read user authentication IDs once the Keymaster API has stabilized
        Set<Integer> userAuthenticators = Collections.emptySet();
        Set<Integer> teeBackedUserAuthenticators = Collections.emptySet();
//        Set<Integer> userAuthenticators = new HashSet<Integer>(
//                getInts(keyCharacteristics, KeymasterDefs.KM_TAG_USER_AUTH_ID));
//        Set<Integer> teeBackedUserAuthenticators = new HashSet<Integer>(
//                keyCharacteristics.hwEnforced.getInts(KeymasterDefs.KM_TAG_USER_AUTH_ID));

        return new KeyStoreKeySpec(entryAlias,
                origin,
                keySize,
                KeymasterUtils.getDate(keyCharacteristics, KeymasterDefs.KM_TAG_ACTIVE_DATETIME),
                KeymasterUtils.getDate(keyCharacteristics,
                        KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME),
                KeymasterUtils.getDate(keyCharacteristics,
                        KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME),
                purposes,
                algorithm,
                padding,
                digest,
                blockMode,
                KeymasterUtils.getInt(keyCharacteristics,
                        KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS),
                KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT),
                userAuthenticators,
                teeBackedUserAuthenticators,
                KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT));
    }

    @Override
    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
        throw new UnsupportedOperationException(
                "Key import into Android KeyStore is not supported");
    }

    @Override
    protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
        throw new UnsupportedOperationException(
                "Key import into Android KeyStore is not supported");
    }
}
Loading