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

Commit 19e79e12 authored by Alex Klyubin's avatar Alex Klyubin Committed by Gerrit Code Review
Browse files

Merge "Add SecretKeyFactory backed by AndroidKeyStore."

parents d720dedb acc835f3
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