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

Commit cd671e38 authored by Eric Biggers's avatar Eric Biggers Committed by Android (Google) Code Review
Browse files

Merge "Update synthetic password terminology to match new design doc"

parents 0b9444cb c37987fe
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -170,7 +170,7 @@ public class LockPatternUtils {
    public static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
    public static final String SYNTHETIC_PASSWORD_KEY_PREFIX = "synthetic_password_";

    public static final String SYNTHETIC_PASSWORD_HANDLE_KEY = "sp-handle";
    public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle";
    public static final String PASSWORD_HISTORY_DELIMITER = ",";

    @UnsupportedAppUsage
+6 −6
Original line number Diff line number Diff line
@@ -40,8 +40,8 @@ import java.util.List;
import java.util.Objects;

/**
 * A class representing a lockscreen credential. It can be either an empty password, a pattern
 * or a password (or PIN).
 * A class representing a lockscreen credential, also called a Lock Screen Knowledge Factor (LSKF).
 * It can be a PIN, pattern, password, or none (a.k.a. empty).
 *
 * <p> As required by some security certification, the framework tries its best to
 * remove copies of the lockscreen credential bytes from memory. In this regard, this class
@@ -52,10 +52,10 @@ import java.util.Objects;
 *     // Process the credential in some way
 * }
 * </pre>
 * With this construct, we can guarantee that there will be no copies of the password left in
 * memory when the credential goes out of scope. This should help mitigate certain class of
 * attacks where the attcker gains read-only access to full device memory (cold boot attack,
 * unsecured software/hardware memory dumping interfaces such as JTAG).
 * With this construct, we can guarantee that there will be no copies of the credential left in
 * memory when the object goes out of scope. This should help mitigate certain class of attacks
 * where the attacker gains read-only access to full device memory (cold boot attack, unsecured
 * software/hardware memory dumping interfaces such as JTAG).
 */
public class LockscreenCredential implements Parcelable, AutoCloseable {

+149 −174

File changed.

Preview size limit exceeded, changes collapsed.

+13 −12
Original line number Diff line number Diff line
@@ -380,29 +380,30 @@ class LockSettingsStorage {
        }
    }

    public void writeSyntheticPasswordState(int userId, long handle, String name, byte[] data) {
    public void writeSyntheticPasswordState(int userId, long protectorId, String name,
            byte[] data) {
        ensureSyntheticPasswordDirectoryForUser(userId);
        writeFile(getSyntheticPasswordStateFileForUser(userId, handle, name), data);
        writeFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name), data);
    }

    public byte[] readSyntheticPasswordState(int userId, long handle, String name) {
        return readFile(getSyntheticPasswordStateFileForUser(userId, handle, name));
    public byte[] readSyntheticPasswordState(int userId, long protectorId, String name) {
        return readFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name));
    }

    public void deleteSyntheticPasswordState(int userId, long handle, String name) {
        deleteFile(getSyntheticPasswordStateFileForUser(userId, handle, name));
    public void deleteSyntheticPasswordState(int userId, long protectorId, String name) {
        deleteFile(getSyntheticPasswordStateFileForUser(userId, protectorId, name));
    }

    public Map<Integer, List<Long>> listSyntheticPasswordHandlesForAllUsers(String stateName) {
    public Map<Integer, List<Long>> listSyntheticPasswordProtectorsForAllUsers(String stateName) {
        Map<Integer, List<Long>> result = new ArrayMap<>();
        final UserManager um = UserManager.get(mContext);
        for (UserInfo user : um.getUsers()) {
            result.put(user.id, listSyntheticPasswordHandlesForUser(stateName, user.id));
            result.put(user.id, listSyntheticPasswordProtectorsForUser(stateName, user.id));
        }
        return result;
    }

    public List<Long> listSyntheticPasswordHandlesForUser(String stateName, int userId) {
    public List<Long> listSyntheticPasswordProtectorsForUser(String stateName, int userId) {
        File baseDir = getSyntheticPasswordDirectoryForUser(userId);
        List<Long> result = new ArrayList<>();
        File[] files = baseDir.listFiles();
@@ -415,7 +416,7 @@ class LockSettingsStorage {
                try {
                    result.add(Long.parseUnsignedLong(parts[0], 16));
                } catch (NumberFormatException e) {
                    Slog.e(TAG, "Failed to parse handle " + parts[0]);
                    Slog.e(TAG, "Failed to parse protector ID " + parts[0]);
                }
            }
        }
@@ -435,8 +436,8 @@ class LockSettingsStorage {
        }
    }

    private File getSyntheticPasswordStateFileForUser(int userId, long handle, String name) {
        String fileName = formatSimple("%016x.%s", handle, name);
    private File getSyntheticPasswordStateFileForUser(int userId, long protectorId, String name) {
        String fileName = formatSimple("%016x.%s", protectorId, name);
        return new File(getSyntheticPasswordDirectoryForUser(userId), fileName);
    }

+34 −16
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ public class SyntheticPasswordCrypto {
    private static final int PROFILE_KEY_IV_SIZE = 12;
    private static final int DEFAULT_TAG_LENGTH_BITS = 128;
    private static final int AES_KEY_LENGTH = 32; // 256-bit AES key
    private static final byte[] APPLICATION_ID_PERSONALIZATION = "application-id".getBytes();
    private static final byte[] PROTECTOR_SECRET_PERSONALIZATION = "application-id".getBytes();
    // Time between the user credential is verified with GK and the decryption of synthetic password
    // under the auth-bound key. This should always happen one after the other, but give it 15
    // seconds just to be sure.
@@ -127,15 +127,19 @@ public class SyntheticPasswordCrypto {
        }
    }

    public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] applicationId) {
    /**
     * Decrypt a legacy SP blob which did the Keystore and software encryption layers in the wrong
     * order.
     */
    public static byte[] decryptBlobV1(String keyAlias, byte[] blob, byte[] protectorSecret) {
        try {
            KeyStore keyStore = getKeyStore();
            SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
            if (decryptionKey == null) {
            SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null);
            if (keyStoreKey == null) {
                throw new IllegalStateException("SP key is missing: " + keyAlias);
            }
            byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob);
            return decrypt(decryptionKey, intermediate);
            byte[] intermediate = decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, blob);
            return decrypt(keyStoreKey, intermediate);
        } catch (Exception e) {
            Slog.e(TAG, "Failed to decrypt V1 blob", e);
            throw new IllegalStateException("Failed to decrypt blob", e);
@@ -157,16 +161,19 @@ public class SyntheticPasswordCrypto {
        return keyStore;
    }

    public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) {
    /**
     * Decrypts an SP blob that was created by {@link #createBlob}.
     */
    public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] protectorSecret) {
        try {
            final KeyStore keyStore = getKeyStore();

            SecretKey decryptionKey = (SecretKey) keyStore.getKey(keyAlias, null);
            if (decryptionKey == null) {
            SecretKey keyStoreKey = (SecretKey) keyStore.getKey(keyAlias, null);
            if (keyStoreKey == null) {
                throw new IllegalStateException("SP key is missing: " + keyAlias);
            }
            byte[] intermediate = decrypt(decryptionKey, blob);
            return decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, intermediate);
            byte[] intermediate = decrypt(keyStoreKey, blob);
            return decrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, intermediate);
        } catch (CertificateException | IOException | BadPaddingException
                | IllegalBlockSizeException
                | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
@@ -177,11 +184,22 @@ public class SyntheticPasswordCrypto {
        }
    }

    public static byte[] createBlob(String keyAlias, byte[] data, byte[] applicationId, long sid) {
    /**
     * Creates a new SP blob by encrypting the given data.  Two encryption layers are applied: an
     * inner layer using a hash of protectorSecret as the key, and an outer layer using a new
     * Keystore key with the given alias and optionally bound to a SID.
     *
     * The reason we use a layer of software encryption, instead of using protectorSecret as the
     * applicationId of the Keystore key, is to work around buggy KeyMint implementations that don't
     * cryptographically bind the applicationId to the key.  The Keystore layer has to be the outer
     * layer, so that LSKF verification is ratelimited by Gatekeeper when Weaver is unavailable.
     */
    public static byte[] createBlob(String keyAlias, byte[] data, byte[] protectorSecret,
            long sid) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
            keyGenerator.init(AES_KEY_LENGTH * 8, new SecureRandom());
            SecretKey secretKey = keyGenerator.generateKey();
            SecretKey keyStoreKey = keyGenerator.generateKey();
            final KeyStore keyStore = getKeyStore();
            KeyProtection.Builder builder = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
@@ -194,10 +212,10 @@ public class SyntheticPasswordCrypto {
            }

            keyStore.setEntry(keyAlias,
                    new KeyStore.SecretKeyEntry(secretKey),
                    new KeyStore.SecretKeyEntry(keyStoreKey),
                    builder.build());
            byte[] intermediate = encrypt(applicationId, APPLICATION_ID_PERSONALIZATION, data);
            return encrypt(secretKey, intermediate);
            byte[] intermediate = encrypt(protectorSecret, PROTECTOR_SECRET_PERSONALIZATION, data);
            return encrypt(keyStoreKey, intermediate);
        } catch (CertificateException | IOException | BadPaddingException
                | IllegalBlockSizeException
                | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
Loading