Merge "Add encryption runner binary."
diff --git a/EncryptionRunner/Android.bp b/EncryptionRunner/Android.bp
new file mode 100644
index 0000000..b02e6de
--- /dev/null
+++ b/EncryptionRunner/Android.bp
@@ -0,0 +1,49 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_library {
+    name: "EncryptionRunner",
+    min_sdk_version: "23",
+    product_variables: {
+        pdk: {
+            enabled: false,
+        },
+    },
+    srcs: [
+        "src/**/*.java",
+    ],
+}
+
+android_test {
+    name: "EncryptionRunnerTest",
+    min_sdk_version: "23",
+    srcs: [
+        "test/**/*.java",
+    ],
+    product_variables: {
+        pdk: {
+            enabled: false,
+        },
+    },
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "EncryptionRunner",
+        "junit",
+        "truth-prebuilt",
+    ],
+}
diff --git a/EncryptionRunner/AndroidManifest.xml b/EncryptionRunner/AndroidManifest.xml
new file mode 100644
index 0000000..8d7643e
--- /dev/null
+++ b/EncryptionRunner/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        package="android.car.encryptionrunner" >
+    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.car.encryptionrunner"
+        android:label="Encryption Runner Tests" />
+</manifest>
diff --git a/EncryptionRunner/AndroidTest.xml b/EncryptionRunner/AndroidTest.xml
new file mode 100644
index 0000000..b6cbbfa
--- /dev/null
+++ b/EncryptionRunner/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Tests for EncryptionRunner.">
+    <option name="test-tag" value="EncryptionRunnerTest" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="EncryptionRunnerTest.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.car.encryptionrunner" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/DummyEncryptionRunner.java b/EncryptionRunner/src/android/car/encryptionrunner/DummyEncryptionRunner.java
new file mode 100644
index 0000000..b08c985
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/DummyEncryptionRunner.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An ecnryption runnner that doesn't actually do encryption. Useful for debugging. Do not use in
+ * production environments.
+ */
+class DummyEncryptionRunner implements EncryptionRunner {
+
+    private static final String KEY = "key";
+    private static final String INIT = "init";
+    private static final String INIT_RESPONSE = "initResponse";
+    private static final String CLIENT_RESPONSE = "clientResponse";
+    public static final String PIN = "1234";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Mode.UNKNOWN, Mode.CLIENT, Mode.SERVER})
+    private @interface Mode {
+
+        int UNKNOWN = 0;
+        int CLIENT = 1;
+        int SERVER = 2;
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({State.UNKNOWN, State.WAITING_FOR_RESPONSE, State.FINISHED})
+    private @interface State {
+
+        int UNKNOWN = 0;
+        int WAITING_FOR_RESPONSE = 1;
+        int FINISHED = 2;
+    }
+
+    @Mode
+    private int mMode;
+    @State
+    private int mState;
+
+    @Override
+    public HandshakeMessage initHandshake() {
+        mMode = Mode.CLIENT;
+        mState = State.WAITING_FOR_RESPONSE;
+        return HandshakeMessage.newBuilder()
+                .setNextMessage(INIT.getBytes())
+                .build();
+    }
+
+    @Override
+    public HandshakeMessage respondToInitRequest(byte[] initializationRequest)
+            throws HandshakeException {
+        mMode = Mode.SERVER;
+        if (!new String(initializationRequest).equals(INIT)) {
+            throw new HandshakeException("Unexpected initialization request");
+        }
+        mState = State.WAITING_FOR_RESPONSE;
+        return HandshakeMessage.newBuilder()
+                .setNextMessage(INIT_RESPONSE.getBytes())
+                .build();
+    }
+
+    @Override
+    public HandshakeMessage continueHandshake(byte[] response) throws HandshakeException {
+        if (mState != State.WAITING_FOR_RESPONSE) {
+            throw new HandshakeException("not waiting for response but got one");
+        }
+        switch(mMode) {
+            case Mode.SERVER:
+                if (!CLIENT_RESPONSE.equals(new String(response))) {
+                    throw new HandshakeException("unexpected response: " + new String(response));
+                }
+                mState = State.FINISHED;
+                return HandshakeMessage.newBuilder()
+                        .setHandshakeComplete(true)
+                        .setKey(new DummyKey())
+                        .build();
+            case Mode.CLIENT:
+                if (!INIT_RESPONSE.equals(new String(response))) {
+                    throw new HandshakeException("unexpected response: " + new String(response));
+                }
+                mState = State.FINISHED;
+                return HandshakeMessage.newBuilder()
+                        .setHandshakeComplete(true)
+                        .setKey(new DummyKey())
+                        .setNextMessage(CLIENT_RESPONSE.getBytes())
+                        .build();
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    @Override
+    public Key keyOf(byte[] serialized) {
+        return new DummyKey();
+    }
+
+    @Override
+    public String getPin() {
+        return PIN;
+    }
+
+    @Override
+    public byte[] encryptData(Key key, byte[] data) {
+        return data;
+    }
+
+    @Override
+    public byte[] decryptData(Key key, byte[] encryptedData) {
+        return encryptedData;
+    }
+
+    private class DummyKey implements Key {
+
+        @Override
+        public byte[] asBytes() {
+            return KEY.getBytes();
+        }
+    }
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunner.java b/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunner.java
new file mode 100644
index 0000000..7e7bd3e
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunner.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import android.annotation.NonNull;
+
+/**
+ * A generalized interface that allows for generating shared secrets as well as encrypting
+ * messages.
+ */
+public interface EncryptionRunner {
+
+    /**
+     * Starts an encryption handshake.
+     *
+     * @return A handshake message with information about the handshake that is started.
+     */
+    HandshakeMessage initHandshake();
+
+    /**
+     * Starts an encryption handshake where the device that is being communicated with already
+     * initiated the request.
+     *
+     * @param initializationRequest the bytes that the other device sent over.
+     * @return a handshake message with information about the handshake.
+     * @throws HandshakeException if initialization request is invalid.
+     */
+    HandshakeMessage respondToInitRequest(@NonNull byte[] initializationRequest)
+            throws HandshakeException;
+
+    /**
+     * Continues a handshake after receiving another response from the connected device.
+     *
+     * @param response the response from the other device.
+     * @return a message that can be used to continue the handshake.
+     * @throws HandshakeException if unexpected bytes in response.
+     */
+    HandshakeMessage continueHandshake(@NonNull byte[] response) throws HandshakeException;
+
+    /**
+     * De seriliazes a previously serilized key generated by an instance of this encryption runner.
+     *
+     * @param serialized the serialized bytes of the key.
+     * @return the Key object used for encryption.
+     */
+    Key keyOf(@NonNull byte[] serialized);
+
+    /**
+     * A user visible shared pin. This pin can be used to verify that both devices that are
+     * communicating have agreed to the same key and will be shown to a user.
+     *
+     * @return the user visible pin.
+     */
+    String getPin();
+
+    /**
+     * Encrypts data using an encryption key.
+     *
+     * @param key  the key used to encrypt the data.
+     * @param data the data to be encrypted
+     * @return the encrypted data.
+     */
+    byte[] encryptData(@NonNull Key key, @NonNull byte[] data);
+
+    /**
+     * Decrypts data using a specified key.
+     *
+     * @param key           The key used to decrypt the data.
+     * @param encryptedData The encrypted data.
+     * @return decrypted data.
+     */
+    byte[] decryptData(@NonNull Key key, @NonNull byte[] encryptedData);
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunnerFactory.java b/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunnerFactory.java
new file mode 100644
index 0000000..d975835
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/EncryptionRunnerFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+/**
+ * Factory that creates encryption runner.
+ */
+public class EncryptionRunnerFactory {
+
+    /**
+     * Creates a new {@link EncryptionRunner} one that doesn't actually do encryption but is useful
+     * for testing.
+     */
+    static EncryptionRunner newDummyRunner() {
+        return new DummyEncryptionRunner();
+    }
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/HandshakeException.java b/EncryptionRunner/src/android/car/encryptionrunner/HandshakeException.java
new file mode 100644
index 0000000..02c873c
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/HandshakeException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+/**
+ * Exception indicating an error during a Handshake of EncryptionRunner.
+ */
+public class HandshakeException extends Exception {
+
+    public HandshakeException(String message) {
+        super(message);
+    }
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/HandshakeMessage.java b/EncryptionRunner/src/android/car/encryptionrunner/HandshakeMessage.java
new file mode 100644
index 0000000..5286770
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/HandshakeMessage.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import android.annotation.Nullable;
+
+/**
+ * During an {@link EncryptionRunner} handshake process, these are the messages returned as part
+ * of each step.
+ */
+public class HandshakeMessage {
+
+    private final boolean mHandShakeComplete;
+    private final Key mKey;
+    private final byte[] mNextMessage;
+
+    /**
+     * @return Returns a builder for {@link HandshakeMessage}.
+     */
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Use the builder;
+     */
+    private HandshakeMessage(
+            boolean handShakeComplete,
+            @Nullable Key key,
+            @Nullable byte[] nextMessage) {
+        mHandShakeComplete = handShakeComplete;
+        mKey = key;
+        mNextMessage = nextMessage;
+    }
+
+    /**
+     * Returns the next message to send in a handshake.
+     */
+    @Nullable
+    public byte[] getNextMessage() {
+        return mNextMessage == null ? null : mNextMessage.clone();
+    }
+
+    /**
+     * Returns true if the handshake is complete.
+     */
+    public boolean isHandShakeComplete() {
+        return mHandShakeComplete;
+    }
+
+    /**
+     * Returns the encryption key that can be used to encrypt data.
+     */
+    @Nullable
+    public Key getKey() {
+        return mKey;
+    }
+
+    static class Builder {
+        boolean mHandshakeComplete;
+        Key mKey;
+        byte[] mNextMessage;
+
+        Builder setHandshakeComplete(boolean handshakeComplete) {
+            mHandshakeComplete = handshakeComplete;
+            return this;
+        }
+
+        Builder setKey(Key key) {
+            mKey = key;
+            return this;
+        }
+
+        Builder setNextMessage(byte[] nextMessage) {
+            mNextMessage = nextMessage == null ? null : nextMessage.clone();
+            return this;
+        }
+
+        HandshakeMessage build() {
+            return new HandshakeMessage(mHandshakeComplete, mKey, mNextMessage);
+        }
+    }
+}
diff --git a/EncryptionRunner/src/android/car/encryptionrunner/Key.java b/EncryptionRunner/src/android/car/encryptionrunner/Key.java
new file mode 100644
index 0000000..97dd362
--- /dev/null
+++ b/EncryptionRunner/src/android/car/encryptionrunner/Key.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import android.annotation.NonNull;
+
+/**
+ * Represents a serializable encryption key.
+ */
+public interface Key {
+    /**
+     * Returns a serialized encryption key.
+     */
+    @NonNull byte[] asBytes();
+}
diff --git a/EncryptionRunner/test/android/car/encryptionrunner/EncryptionRunnerTest.java b/EncryptionRunner/test/android/car/encryptionrunner/EncryptionRunnerTest.java
new file mode 100644
index 0000000..d08b37a
--- /dev/null
+++ b/EncryptionRunner/test/android/car/encryptionrunner/EncryptionRunnerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.encryptionrunner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class EncryptionRunnerTest {
+
+    private static final byte[] sTestData = "test data".getBytes();
+
+    @Test
+    public void happyFlow() throws Exception {
+        // This performs a handshake and then sends an "encrypted" message back and forth.
+        // Any encryption runner should be able to do this.
+        // Right now just using the dummy runner, when we have a real runner we can extract this
+        // method or just have the factory create a real runner.
+        EncryptionRunner clientRunner = EncryptionRunnerFactory.newDummyRunner();
+        EncryptionRunner serverRunner = EncryptionRunnerFactory.newDummyRunner();
+        HandshakeMessage initialClientMessage = clientRunner.initHandshake();
+
+        assertThat(initialClientMessage.isHandShakeComplete()).isFalse();
+        assertThat(initialClientMessage.getKey()).isNull();
+        assertThat(initialClientMessage.getNextMessage()).isNotNull();
+
+        HandshakeMessage initialServerMessage =
+                serverRunner.respondToInitRequest(initialClientMessage.getNextMessage());
+
+        assertThat(initialServerMessage.isHandShakeComplete()).isFalse();
+        assertThat(initialServerMessage.getKey()).isNull();
+        assertThat(initialServerMessage.getNextMessage()).isNotNull();
+
+        HandshakeMessage clientMessage =
+                clientRunner.continueHandshake(initialServerMessage.getNextMessage());
+
+        assertThat(clientMessage.isHandShakeComplete()).isTrue();
+        assertThat(clientMessage.getKey()).isNotNull();
+        assertThat(clientMessage.getNextMessage()).isNotNull();
+
+        HandshakeMessage serverMessage =
+                serverRunner.continueHandshake(clientMessage.getNextMessage());
+
+        assertThat(serverMessage.isHandShakeComplete()).isTrue();
+        assertThat(serverMessage.getKey()).isNotNull();
+        assertThat(serverMessage.getNextMessage()).isNull();
+
+        assertThat(serverRunner.decryptData(
+                serverMessage.getKey(),
+                clientRunner.encryptData(clientMessage.getKey(), sTestData))).isEqualTo(sTestData);
+        assertThat(clientRunner.decryptData(
+                clientMessage.getKey(),
+                serverRunner.encryptData(serverMessage.getKey(), sTestData))).isEqualTo(sTestData);
+    }
+
+}