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

Commit e7b15a3a authored by Guojing Yuan's avatar Guojing Yuan Committed by Automerger Merge Worker
Browse files

Merge "[CDM Transport] Exchange platform info to decide which Transport to...

Merge "[CDM Transport] Exchange platform info to decide which Transport to create" into udc-dev am: ca6f9059

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21676403



Change-Id: I7df150479a902454f6391854d0de37b667a1d27b
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 5c6fa8c8 ca6f9059
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVER
import static android.content.ComponentName.createRelative;
import static android.content.ComponentName.createRelative;


import static com.android.server.companion.Utils.prepareForIpc;
import static com.android.server.companion.Utils.prepareForIpc;
import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
@@ -91,7 +92,8 @@ public class SystemDataTransferProcessor {
        mAssociationStore = associationStore;
        mAssociationStore = associationStore;
        mSystemDataTransferRequestStore = systemDataTransferRequestStore;
        mSystemDataTransferRequestStore = systemDataTransferRequestStore;
        mTransportManager = transportManager;
        mTransportManager = transportManager;
        mTransportManager.setListener(this::onReceivePermissionRestore);
        mTransportManager.addListener(MESSAGE_REQUEST_PERMISSION_RESTORE,
                this::onReceivePermissionRestore);
        mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
        mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
        mExecutor = Executors.newSingleThreadExecutor();
        mExecutor = Executors.newSingleThreadExecutor();
    }
    }
+1 −1
Original line number Original line Diff line number Diff line
@@ -35,7 +35,7 @@ import java.util.function.BiConsumer;
/**
/**
 * Helper class to perform attestation verification synchronously.
 * Helper class to perform attestation verification synchronously.
 */
 */
class AttestationVerifier {
public class AttestationVerifier {
    private static final long ATTESTATION_VERIFICATION_TIMEOUT_SECONDS = 10; // 10 seconds
    private static final long ATTESTATION_VERIFICATION_TIMEOUT_SECONDS = 10; // 10 seconds
    private static final String PARAM_OWNED_BY_SYSTEM = "android.key_owned_by_system";
    private static final String PARAM_OWNED_BY_SYSTEM = "android.key_owned_by_system";


+4 −3
Original line number Original line Diff line number Diff line
@@ -110,7 +110,7 @@ public class SecureChannel {
        this(in, out, callback, null, new AttestationVerifier(context));
        this(in, out, callback, null, new AttestationVerifier(context));
    }
    }


    private SecureChannel(
    public SecureChannel(
            final InputStream in,
            final InputStream in,
            final OutputStream out,
            final OutputStream out,
            Callback callback,
            Callback callback,
@@ -381,9 +381,10 @@ public class SecureChannel {


    private void exchangeAuthentication()
    private void exchangeAuthentication()
            throws IOException, GeneralSecurityException, BadHandleException, CryptoException {
            throws IOException, GeneralSecurityException, BadHandleException, CryptoException {
        if (mVerifier == null) {
        if (mPreSharedKey != null) {
            exchangePreSharedKey();
            exchangePreSharedKey();
        } else {
        }
        if (mVerifier != null) {
            exchangeAttestation();
            exchangeAttestation();
        }
        }
    }
    }
+97 −21
Original line number Original line Diff line number Diff line
@@ -19,9 +19,9 @@ package com.android.server.companion.transport;
import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES;
import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES;


import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE;
import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PERMISSION_RESTORE;
import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PLATFORM_INFO;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal;
import android.content.Context;
import android.content.Context;
@@ -30,12 +30,17 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
import android.os.Binder;
import android.os.Build;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArray;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.LocalServices;
import com.android.server.companion.transport.Transport.Listener;


import java.io.IOException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.Future;


@@ -44,6 +49,9 @@ public class CompanionTransportManager {
    private static final String TAG = "CDM_CompanionTransportManager";
    private static final String TAG = "CDM_CompanionTransportManager";
    private static final boolean DEBUG = false;
    private static final boolean DEBUG = false;


    private static final int SECURE_CHANNEL_AVAILABLE_SDK = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
    private static final int NON_ANDROID = -1;

    private boolean mSecureTransportEnabled = true;
    private boolean mSecureTransportEnabled = true;


    private static boolean isRequest(int message) {
    private static boolean isRequest(int message) {
@@ -54,24 +62,29 @@ public class CompanionTransportManager {
        return (message & 0xFF000000) == 0x33000000;
        return (message & 0xFF000000) == 0x33000000;
    }
    }


    public interface Listener {
        void onRequestPermissionRestore(byte[] data);
    }

    private final Context mContext;
    private final Context mContext;


    @GuardedBy("mTransports")
    @GuardedBy("mTransports")
    private final SparseArray<Transport> mTransports = new SparseArray<>();
    private final SparseArray<Transport> mTransports = new SparseArray<>();


    @Nullable
    @NonNull
    private Listener mListener;
    private final Map<Integer, Listener> mListeners = new HashMap<>();

    private Transport mTempTransport;


    public CompanionTransportManager(Context context) {
    public CompanionTransportManager(Context context) {
        mContext = context;
        mContext = context;
    }
    }


    public void setListener(@NonNull Listener listener) {
    /**
        mListener = listener;
     * Add a message listener when a message is received for the message type
     */
    @GuardedBy("mTransports")
    public void addListener(int message, @NonNull Listener listener) {
        mListeners.put(message, listener);
        for (int i = 0; i < mTransports.size(); i++) {
            mTransports.valueAt(i).addListener(message, listener);
        }
    }
    }


    /**
    /**
@@ -105,15 +118,7 @@ public class CompanionTransportManager {
                detachSystemDataTransport(packageName, userId, associationId);
                detachSystemDataTransport(packageName, userId, associationId);
            }
            }


            final Transport transport;
            initializeTransport(associationId, fd);
            if (isSecureTransportEnabled(associationId)) {
                transport = new SecureTransport(associationId, fd, mContext, mListener);
            } else {
                transport = new RawTransport(associationId, fd, mContext, mListener);
            }

            transport.start();
            mTransports.put(associationId, transport);
        }
        }
    }
    }


@@ -128,13 +133,85 @@ public class CompanionTransportManager {
        }
        }
    }
    }


    @GuardedBy("mTransports")
    private void initializeTransport(int associationId, ParcelFileDescriptor fd) {
        if (!isSecureTransportEnabled()) {
            Transport transport = new RawTransport(associationId, fd, mContext);
            for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
                transport.addListener(entry.getKey(), entry.getValue());
            }
            transport.start();
            mTransports.put(associationId, transport);
            Slog.i(TAG, "RawTransport is created");
            return;
        }

        // Exchange platform info to decide which transport should be created
        mTempTransport = new RawTransport(associationId, fd, mContext);
        for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
            mTempTransport.addListener(entry.getKey(), entry.getValue());
        }
        mTempTransport.addListener(MESSAGE_REQUEST_PLATFORM_INFO, this::onPlatformInfoReceived);
        mTempTransport.start();

        int sdk = Build.VERSION.SDK_INT;
        String release = Build.VERSION.RELEASE;
        // data format: | SDK_INT (int) | release length (int) | release |
        final ByteBuffer data = ByteBuffer.allocate(4 + 4 + release.getBytes().length)
                .putInt(sdk)
                .putInt(release.getBytes().length)
                .put(release.getBytes());

        // TODO: it should check if preSharedKey is given
        mTempTransport.requestForResponse(MESSAGE_REQUEST_PLATFORM_INFO, data.array());
    }

    /**
     * Depending on the remote platform info to decide which transport should be created
     */
    @GuardedBy("mTransports")
    private void onPlatformInfoReceived(byte[] data) {
        // TODO: it should check if preSharedKey is given

        ByteBuffer buffer = ByteBuffer.wrap(data);
        int remoteSdk = buffer.getInt();
        byte[] remoteRelease = new byte[buffer.getInt()];
        buffer.get(remoteRelease);

        Slog.i(TAG, "Remote device SDK: " + remoteSdk + ", release:" + new String(remoteRelease));

        Transport transport = mTempTransport;
        mTempTransport = null;

        int sdk = Build.VERSION.SDK_INT;
        String release = Build.VERSION.RELEASE;
        if (remoteSdk == NON_ANDROID) {
            // TODO: pass in a real preSharedKey
            transport = new SecureTransport(transport.getAssociationId(), transport.getFd(),
                    mContext, null, null);
        } else if (sdk < SECURE_CHANNEL_AVAILABLE_SDK
                || remoteSdk < SECURE_CHANNEL_AVAILABLE_SDK) {
            // TODO: depending on the release version, either
            //       1) using a RawTransport for old T versions
            //       2) or an Ukey2 handshaked transport for UKey2 backported T versions
        } else {
            Slog.i(TAG, "Creating a secure channel");
            transport = new SecureTransport(transport.getAssociationId(), transport.getFd(),
                    mContext);
            for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
                transport.addListener(entry.getKey(), entry.getValue());
            }
            transport.start();
        }
        mTransports.put(transport.getAssociationId(), transport);
    }

    public Future<?> requestPermissionRestore(int associationId, byte[] data) {
    public Future<?> requestPermissionRestore(int associationId, byte[] data) {
        synchronized (mTransports) {
        synchronized (mTransports) {
            final Transport transport = mTransports.get(associationId);
            final Transport transport = mTransports.get(associationId);
            if (transport == null) {
            if (transport == null) {
                return CompletableFuture.failedFuture(new IOException("Missing transport"));
                return CompletableFuture.failedFuture(new IOException("Missing transport"));
            }
            }

            return transport.requestForResponse(MESSAGE_REQUEST_PERMISSION_RESTORE, data);
            return transport.requestForResponse(MESSAGE_REQUEST_PERMISSION_RESTORE, data);
        }
        }
    }
    }
@@ -146,10 +223,9 @@ public class CompanionTransportManager {
        this.mSecureTransportEnabled = enabled;
        this.mSecureTransportEnabled = enabled;
    }
    }


    private boolean isSecureTransportEnabled(int associationId) {
    private boolean isSecureTransportEnabled() {
        boolean enabled = !Build.IS_DEBUGGABLE || mSecureTransportEnabled;
        boolean enabled = !Build.IS_DEBUGGABLE || mSecureTransportEnabled;


        // TODO: version comparison logic
        return enabled;
        return enabled;
    }
    }
}
}
+38 −65
Original line number Original line Diff line number Diff line
@@ -16,51 +16,51 @@


package com.android.server.companion.transport;
package com.android.server.companion.transport;


import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Slog;
import android.util.Slog;


import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.util.Arrays;
import java.security.cert.CertificateException;


import javax.crypto.BadPaddingException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


/**
/**
 * This class can be used to encrypt and decrypt bytes using Android Cryptography
 * This class uses Java Cryptography to encrypt and decrypt messages
 */
 */
public class CryptoManager {
public class CryptoManager {


    private static final String TAG = "CDM_CryptoManager";
    private static final String TAG = "CDM_CryptoManager";
    private static final int SECRET_KEY_LENGTH = 32;
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding";


    private static final String KEY_STORE_ALIAS = "cdm_secret";
    private final byte[] mPreSharedKey;
    private static final String ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
    private Cipher mEncryptCipher;
    private static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
    private Cipher mDecryptCipher;
    private static final String PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
    private static final String TRANSFORMATION = ALGORITHM + "/" + BLOCK_MODE + "/" + PADDING;


    private final KeyStore mKeyStore;
    private SecretKey mSecretKey;


    public CryptoManager() {
    public CryptoManager(byte[] preSharedKey) {
        // Initialize KeyStore
        if (preSharedKey == null) {
            mPreSharedKey = Arrays.copyOf(new byte[0], SECRET_KEY_LENGTH);
        } else {
            mPreSharedKey = Arrays.copyOf(preSharedKey, SECRET_KEY_LENGTH);
        }
        mSecretKey = new SecretKeySpec(mPreSharedKey, ALGORITHM);
        try {
        try {
            mKeyStore = KeyStore.getInstance("AndroidKeyStore");
            mEncryptCipher = Cipher.getInstance(TRANSFORMATION);
            mKeyStore.load(null);
            mEncryptCipher.init(Cipher.ENCRYPT_MODE, mSecretKey);
        } catch (KeyStoreException | IOException | NoSuchAlgorithmException
            mDecryptCipher = Cipher.getInstance(TRANSFORMATION);
                 | CertificateException e) {
        } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
            throw new RuntimeException(e);
            Slog.e(TAG, e.getMessage());
        }
        }
    }
    }


@@ -69,21 +69,19 @@ public class CryptoManager {
     */
     */
    public byte[] encrypt(byte[] input) {
    public byte[] encrypt(byte[] input) {
        try {
        try {
            // Encrypt using Cipher
            if (mEncryptCipher == null) {
            Cipher encryptCipher = Cipher.getInstance(TRANSFORMATION);
                return null;
            encryptCipher.init(Cipher.ENCRYPT_MODE, getKey());
            }
            byte[] encryptedBytes = encryptCipher.doFinal(input);


            // Write to bytes
            byte[] encryptedBytes = mEncryptCipher.doFinal(input);
            ByteBuffer buffer = ByteBuffer.allocate(
            ByteBuffer buffer = ByteBuffer.allocate(
                            4 + encryptCipher.getIV().length + 4 + encryptedBytes.length)
                            4 + mEncryptCipher.getIV().length + 4 + encryptedBytes.length)
                    .putInt(encryptCipher.getIV().length)
                    .putInt(mEncryptCipher.getIV().length)
                    .put(encryptCipher.getIV())
                    .put(mEncryptCipher.getIV())
                    .putInt(encryptedBytes.length)
                    .putInt(encryptedBytes.length)
                    .put(encryptedBytes);
                    .put(encryptedBytes);
            return buffer.array();
            return buffer.array();
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
        } catch (IllegalBlockSizeException | BadPaddingException e) {
                 | IllegalBlockSizeException | BadPaddingException e) {
            Slog.e(TAG, e.getMessage());
            Slog.e(TAG, e.getMessage());
            return null;
            return null;
        }
        }
@@ -99,45 +97,20 @@ public class CryptoManager {
        byte[] encryptedBytes = new byte[buffer.getInt()];
        byte[] encryptedBytes = new byte[buffer.getInt()];
        buffer.get(encryptedBytes);
        buffer.get(encryptedBytes);
        try {
        try {
            Cipher decryptCipher = Cipher.getInstance(TRANSFORMATION);
            mDecryptCipher.init(Cipher.DECRYPT_MODE, getKey(), new IvParameterSpec(iv));
            decryptCipher.init(Cipher.DECRYPT_MODE, getKey(), new IvParameterSpec(iv));
            return mDecryptCipher.doFinal(encryptedBytes);
            return decryptCipher.doFinal(encryptedBytes);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                 | IllegalBlockSizeException | BadPaddingException e) {
                 | InvalidAlgorithmParameterException | IllegalBlockSizeException
                 | BadPaddingException e) {
            Slog.e(TAG, e.getMessage());
            Slog.e(TAG, e.getMessage());
            return null;
            return null;
        }
        }
    }
    }


    private SecretKey getKey() {
    private SecretKey getKey() {
        try {
        if (mSecretKey != null) {
            KeyStore.Entry keyEntry = mKeyStore.getEntry(KEY_STORE_ALIAS, null);
            return mSecretKey;
            if (keyEntry instanceof KeyStore.SecretKeyEntry
                    && ((KeyStore.SecretKeyEntry) keyEntry).getSecretKey() != null) {
                return ((KeyStore.SecretKeyEntry) keyEntry).getSecretKey();
            } else {
                return createKey();
            }
        } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) {
            throw new RuntimeException(e);
        }
    }

    private SecretKey createKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
            keyGenerator.init(
                    new KeyGenParameterSpec.Builder(KEY_STORE_ALIAS,
                            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                            .setBlockModes(BLOCK_MODE)
                            .setEncryptionPaddings(PADDING)
                            .setUserAuthenticationRequired(false)
                            .setRandomizedEncryptionRequired(true)
                            .build());
            return keyGenerator.generateKey();
        } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
            throw new RuntimeException(e);
        }
        }
        mSecretKey = new SecretKeySpec(mPreSharedKey, ALGORITHM);
        return mSecretKey;
    }
    }
}
}
Loading