Merge "Add support for mocked RPMB" am: be9de1ab86
am: 8477031b7c

Change-Id: I66cc9ede9eba8220e960bdb8442cf80ef3e19a0b
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c61f7d0..5f56408 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -46,6 +46,8 @@
         return MMC_RPMB;
     } else if (!strcmp(dev_type_name, "virt")) {
         return VIRT_RPMB;
+    } else if (!strcmp(dev_type_name, "sock")) {
+        return SOCK_RPMB;
     } else {
         return UNKNOWN_RPMB;
     }
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 29827e2..0bd9e68 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -21,6 +21,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 #include <unistd.h>
 
 #include <linux/major.h>
@@ -192,7 +194,7 @@
             msg->result = STORAGE_ERR_GENERIC;
             goto err_response;
         }
-    } else if (dev_type == VIRT_RPMB) {
+    } else if ((dev_type == VIRT_RPMB) || (dev_type == SOCK_RPMB)) {
         size_t payload_size = req->reliable_write_size + req->write_size;
         rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);
         if (rc < 0) {
@@ -234,12 +236,33 @@
     int rc;
     dev_type = open_dev_type;
 
-    rc = open(rpmb_devname, O_RDWR, 0);
-    if (rc < 0) {
-        ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
-        return rc;
+    if (dev_type != SOCK_RPMB) {
+        rc = open(rpmb_devname, O_RDWR, 0);
+        if (rc < 0) {
+            ALOGE("unable (%d) to open rpmb device '%s': %s\n", errno, rpmb_devname, strerror(errno));
+            return rc;
+        }
+        rpmb_fd = rc;
+    } else {
+        struct sockaddr_un unaddr;
+        struct sockaddr *addr = (struct sockaddr *)&unaddr;
+        rc = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (rc < 0) {
+            ALOGE("unable (%d) to create socket: %s\n", errno, strerror(errno));
+            return rc;
+        }
+        rpmb_fd = rc;
+
+        memset(&unaddr, 0, sizeof(unaddr));
+        unaddr.sun_family = AF_UNIX;
+        // TODO if it overflowed, bail rather than connecting?
+        strncpy(unaddr.sun_path, rpmb_devname, sizeof(unaddr.sun_path)-1);
+        rc = connect(rpmb_fd, addr, sizeof(unaddr));
+        if (rc < 0) {
+            ALOGE("unable (%d) to connect to rpmb socket '%s': %s\n", errno, rpmb_devname, strerror(errno));
+            return rc;
+        }
     }
-    rpmb_fd = rc;
     return 0;
 }
 
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index 4c330c9..09af3c5 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,7 +18,7 @@
 #include <stdint.h>
 #include <trusty/interface/storage.h>
 
-enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB };
+enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB, SOCK_RPMB };
 
 int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
 int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
diff --git a/trusty/utils/rpmb_dev/Android.bp b/trusty/utils/rpmb_dev/Android.bp
new file mode 100644
index 0000000..e923e82
--- /dev/null
+++ b/trusty/utils/rpmb_dev/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+cc_binary {
+    name: "rpmb_dev",
+    vendor: true,
+
+    srcs: [
+        "rpmb_dev.c",
+    ],
+    shared_libs: [
+        "libc",
+        "liblog",
+        "libcrypto",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    init_rc: [
+        "rpmb_dev.rc",
+    ],
+}
diff --git a/trusty/utils/rpmb_dev/rpmb.h b/trusty/utils/rpmb_dev/rpmb.h
new file mode 100644
index 0000000..ab7e8d8
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef __RPMB_H__
+#define __RPMB_H__
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct rpmb_key {
+    uint8_t byte[32];
+};
+
+struct rpmb_state;
+
+#define RPMB_BUF_SIZE 256
+
+/* provides */
+int rpmb_init(struct rpmb_state** statep,
+              void* mmc_handle,
+              const struct rpmb_key* key);
+void rpmb_uninit(struct rpmb_state* statep);
+int rpmb_read(struct rpmb_state* state,
+              void* buf,
+              uint16_t addr,
+              uint16_t count);
+/* count must be 1 or 2, addr must be aligned */
+int rpmb_write(struct rpmb_state* state,
+               const void* buf,
+               uint16_t addr,
+               uint16_t count,
+               bool sync);
+
+/* needs */
+int rpmb_send(void* mmc_handle,
+              void* reliable_write_buf,
+              size_t reliable_write_size,
+              void* write_buf,
+              size_t write_buf_size,
+              void* read_buf,
+              size_t read_buf_size,
+              bool sync);
+
+#endif
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
new file mode 100644
index 0000000..af97eba
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define LOG_TAG "rpmb_mock"
+
+#include "rpmb_protocol.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <log/log.h>
+#include <openssl/hmac.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+/* verbose is an int for getopt */
+static int verbose = false;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+
+HMAC_CTX* HMAC_CTX_new(void) {
+    HMAC_CTX* ctx = malloc(sizeof(*ctx));
+    if (ctx != NULL) {
+        HMAC_CTX_init(ctx);
+    }
+    return ctx;
+}
+
+void HMAC_CTX_free(HMAC_CTX* ctx) {
+    if (ctx != NULL) {
+        HMAC_CTX_cleanup(ctx);
+        free(ctx);
+    }
+}
+
+#endif
+
+#define MAX_WRITE_COUNTER (0xffffffff)
+
+struct rpmb_data_header {
+    uint32_t write_counter;
+    uint16_t max_block;
+    uint8_t pad1;
+    uint8_t key_programmed;
+    struct rpmb_key key;
+    uint8_t pad[512 - 4 - 2 - 1 - 1 - sizeof(struct rpmb_key)];
+};
+
+#define MAX_PACKET_COUNT (8)
+
+struct rpmb_dev_state {
+    struct rpmb_data_header header;
+    struct rpmb_packet cmd[MAX_PACKET_COUNT];
+    struct rpmb_packet res[MAX_PACKET_COUNT];
+    uint16_t cmd_count;
+    uint16_t res_count;
+    int data_fd;
+};
+
+/* TODO: move to common location */
+static int rpmb_mac(struct rpmb_key key, struct rpmb_packet* packet, size_t packet_count,
+                    struct rpmb_key* mac) {
+    size_t i;
+    int hmac_ret;
+    unsigned int md_len;
+    HMAC_CTX* hmac_ctx;
+
+    hmac_ctx = HMAC_CTX_new();
+    hmac_ret = HMAC_Init_ex(hmac_ctx, &key, sizeof(key), EVP_sha256(), NULL);
+    if (!hmac_ret) {
+        ALOGE("HMAC_Init_ex failed\n");
+        goto err;
+    }
+    for (i = 0; i < packet_count; i++) {
+        hmac_ret = HMAC_Update(hmac_ctx, packet[i].data, 284);
+        if (!hmac_ret) {
+            ALOGE("HMAC_Update failed\n");
+            goto err;
+        }
+    }
+    hmac_ret = HMAC_Final(hmac_ctx, mac->byte, &md_len);
+    if (md_len != sizeof(mac->byte)) {
+        ALOGE("bad md_len %d != %zd\n", md_len, sizeof(mac->byte));
+        exit(1);
+    }
+    if (!hmac_ret) {
+        ALOGE("HMAC_Final failed\n");
+        goto err;
+    }
+
+err:
+    HMAC_CTX_free(hmac_ctx);
+    return hmac_ret ? 0 : -1;
+}
+
+static int rpmb_file_seek(struct rpmb_dev_state* s, uint16_t addr) {
+    int ret;
+    int pos = addr * RPMB_PACKET_DATA_SIZE + sizeof(s->header);
+    ret = lseek(s->data_fd, pos, SEEK_SET);
+    if (ret != pos) {
+        ALOGE("rpmb_dev: seek to %d failed, got %d\n", pos, ret);
+        return -1;
+    }
+    return 0;
+}
+
+static uint16_t rpmb_dev_program_key(struct rpmb_dev_state* s) {
+    int ret;
+
+    if (s->header.key_programmed) {
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    s->header.key = s->cmd[0].key_mac;
+    s->header.key_programmed = 1;
+
+    ret = lseek(s->data_fd, 0, SEEK_SET);
+    if (ret) {
+        ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    ret = write(s->data_fd, &s->header, sizeof(s->header));
+    if (ret != sizeof(s->header)) {
+        ALOGE("rpmb_dev: Failed to write rpmb key: %d, %s\n", ret, strerror(errno));
+
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_get_counter(struct rpmb_dev_state* s) {
+    s->res[0].write_counter = rpmb_u32(s->header.write_counter);
+
+    return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_data_write(struct rpmb_dev_state* s) {
+    uint16_t addr = rpmb_get_u16(s->cmd[0].address);
+    uint16_t block_count = s->cmd_count;
+    uint32_t write_counter;
+    int ret;
+
+    if (s->header.write_counter == MAX_WRITE_COUNTER) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Write counter expired\n");
+        }
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    write_counter = rpmb_get_u32(s->cmd[0].write_counter);
+    if (s->header.write_counter != write_counter) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Invalid write counter %u. Expected: %u\n", write_counter,
+                  s->header.write_counter);
+        }
+        return RPMB_RES_COUNT_FAILURE;
+    }
+
+    ret = rpmb_file_seek(s, addr);
+    if (ret) {
+        ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    for (int i = 0; i < block_count; i++) {
+        ret = write(s->data_fd, s->cmd[i].data, RPMB_PACKET_DATA_SIZE);
+        if (ret != RPMB_PACKET_DATA_SIZE) {
+            ALOGE("rpmb_dev: Failed to write rpmb data file: %d, %s\n", ret, strerror(errno));
+            return RPMB_RES_WRITE_FAILURE;
+        }
+    }
+
+    s->header.write_counter++;
+
+    ret = lseek(s->data_fd, 0, SEEK_SET);
+    if (ret) {
+        ALOGE("rpmb_dev: Failed to seek rpmb data file\n");
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    ret = write(s->data_fd, &s->header.write_counter, sizeof(s->header.write_counter));
+    if (ret != sizeof(s->header.write_counter)) {
+        ALOGE("rpmb_dev: Failed to write rpmb write counter: %d, %s\n", ret, strerror(errno));
+
+        return RPMB_RES_WRITE_FAILURE;
+    }
+
+    s->res[0].write_counter = rpmb_u32(s->header.write_counter);
+    return RPMB_RES_OK;
+}
+
+static uint16_t rpmb_dev_data_read(struct rpmb_dev_state* s) {
+    uint16_t addr;
+    uint16_t block_count;
+    int ret;
+
+    addr = rpmb_get_u16(s->cmd[0].address);
+    block_count = s->res_count;
+
+    rpmb_file_seek(s, addr);
+
+    for (int i = 0; i < block_count; i++) {
+        ret = read(s->data_fd, s->res[i].data, RPMB_PACKET_DATA_SIZE);
+        if (ret != 0 && ret != RPMB_PACKET_DATA_SIZE) {
+            ALOGE("rpmb_dev: Failed to read rpmb data file: %d, %s\n", ret, strerror(errno));
+            return RPMB_RES_READ_FAILURE;
+        }
+    }
+
+    return RPMB_RES_OK;
+}
+
+struct rpmb_dev_cmd {
+    uint16_t (*func)(struct rpmb_dev_state* s);
+    uint16_t resp;
+    bool key_mac_is_key;
+    bool check_mac;
+    bool check_result_read;
+    bool check_key_programmed;
+    bool check_addr;
+    bool multi_packet_cmd;
+    bool multi_packet_res;
+    bool res_mac;
+};
+
+static struct rpmb_dev_cmd rpmb_dev_cmd_table[] = {
+        [RPMB_REQ_PROGRAM_KEY] =
+                {
+                        .func = rpmb_dev_program_key,
+                        .resp = RPMB_RESP_PROGRAM_KEY,
+                        .key_mac_is_key = true,
+                        .check_result_read = true,
+                },
+        [RPMB_REQ_GET_COUNTER] =
+                {
+                        .func = rpmb_dev_get_counter,
+                        .resp = RPMB_RESP_GET_COUNTER,
+                        .check_key_programmed = true,
+                        .res_mac = true,
+                },
+        [RPMB_REQ_DATA_WRITE] =
+                {
+                        .func = rpmb_dev_data_write,
+                        .resp = RPMB_RESP_DATA_WRITE,
+                        .check_mac = true,
+                        .check_result_read = true,
+                        .check_key_programmed = true,
+                        .check_addr = true,
+                        .multi_packet_cmd = true,
+                        .res_mac = true,
+                },
+        [RPMB_REQ_DATA_READ] =
+                {
+                        .func = rpmb_dev_data_read,
+                        .resp = RPMB_RESP_DATA_READ,
+                        .check_addr = true,
+                        .multi_packet_res = true,
+                        .res_mac = true,
+                },
+};
+
+#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
+
+static void rpmb_dev_process_cmd(struct rpmb_dev_state* s) {
+    assert(s->cmd_count > 0);
+    assert(s->res_count > 0);
+    uint16_t req_resp = rpmb_get_u16(s->cmd[0].req_resp);
+    uint16_t addr = rpmb_get_u16(s->cmd[0].address);
+    uint16_t sub_req;
+    uint16_t cmd_index = req_resp < countof(rpmb_dev_cmd_table) ? req_resp : 0;
+    struct rpmb_dev_cmd* cmd = &rpmb_dev_cmd_table[cmd_index];
+    uint16_t result = RPMB_RES_GENERAL_FAILURE;
+    struct rpmb_key mac;
+    uint16_t block_count = 0;
+
+    if (cmd->check_result_read) {
+        sub_req = rpmb_get_u16(s->cmd[s->cmd_count - 1].req_resp);
+        if (sub_req != RPMB_REQ_RESULT_READ) {
+            if (verbose) {
+                ALOGE("rpmb_dev: Request %d, missing result read request, got %d, cmd_count %d\n",
+                      req_resp, sub_req, s->cmd_count);
+            }
+            goto err;
+        }
+        assert(s->cmd_count > 1);
+        s->cmd_count--;
+    }
+
+    if (cmd->check_mac) {
+        if (rpmb_mac(s->header.key, s->cmd, s->cmd_count, &mac) != 0) {
+            ALOGE("rpmb_dev: failed to caclulate mac\n");
+            goto err;
+        }
+    } else if (cmd->key_mac_is_key) {
+        mac = s->cmd[s->cmd_count - 1].key_mac;
+    } else {
+        memset(mac.byte, 0, sizeof(mac.byte));
+    }
+
+    if (memcmp(&mac, s->cmd[s->cmd_count - 1].key_mac.byte, sizeof(mac))) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, invalid MAC, cmd_count %d\n", req_resp, s->cmd_count);
+        }
+        if (cmd->check_mac) {
+            result = RPMB_RES_AUTH_FAILURE;
+        }
+        goto err;
+    }
+
+    if (cmd->multi_packet_cmd) {
+        block_count = s->cmd_count;
+    }
+    if (cmd->multi_packet_res) {
+        block_count = s->res_count;
+    }
+
+    if (cmd->check_addr && (addr + block_count > s->header.max_block + 1)) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, invalid addr: 0x%x count 0x%x, Out of bounds. Max addr "
+                  "0x%x\n",
+                  req_resp, addr, block_count, s->header.max_block + 1);
+        }
+        result = RPMB_RES_ADDR_FAILURE;
+        goto err;
+    }
+    if (!cmd->check_addr && addr) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, invalid addr: 0x%x != 0\n", req_resp, addr);
+        }
+        goto err;
+    }
+
+    for (int i = 1; i < s->cmd_count; i++) {
+        sub_req = rpmb_get_u16(s->cmd[i].req_resp);
+        if (sub_req != req_resp) {
+            if (verbose) {
+                ALOGE("rpmb_dev: Request %d, sub-request mismatch, %d, at %d\n", req_resp, i,
+                      sub_req);
+            }
+            goto err;
+        }
+    }
+    if (!cmd->multi_packet_cmd && s->cmd_count != 1) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, bad cmd count %d, expected 1\n", req_resp, s->cmd_count);
+        }
+        goto err;
+    }
+    if (!cmd->multi_packet_res && s->res_count != 1) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, bad res count %d, expected 1\n", req_resp, s->res_count);
+        }
+        goto err;
+    }
+
+    if (cmd->check_key_programmed && !s->header.key_programmed) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Request %d, key is not programmed\n", req_resp);
+        }
+        s->res[0].result = rpmb_u16(RPMB_RES_NO_AUTH_KEY);
+        return;
+    }
+
+    if (!cmd->func) {
+        if (verbose) {
+            ALOGE("rpmb_dev: Unsupported request: %d\n", req_resp);
+        }
+        goto err;
+    }
+
+    result = cmd->func(s);
+
+err:
+    if (s->header.write_counter == MAX_WRITE_COUNTER) {
+        result |= RPMB_RES_WRITE_COUNTER_EXPIRED;
+    }
+
+    for (int i = 0; i < s->res_count; i++) {
+        s->res[i].nonce = s->cmd[0].nonce;
+        s->res[i].address = rpmb_u16(addr);
+        s->res[i].block_count = rpmb_u16(block_count);
+        s->res[i].result = rpmb_u16(result);
+        s->res[i].req_resp = rpmb_u16(cmd->resp);
+    }
+    if (cmd->res_mac) {
+        rpmb_mac(s->header.key, s->res, s->res_count, &s->res[s->res_count - 1].key_mac);
+    }
+}
+
+/*
+ * Receives data until one of the following is true:
+ * - The buffer is full (return will be len)
+ * - The connection closed (return > 0, < len)
+ * - An error occurred (return will be the negative error code from recv)
+ */
+ssize_t recv_until(int sock, void* dest_in, size_t len) {
+    size_t bytes_recvd = 0;
+    char* dest = dest_in;
+    while (bytes_recvd < len) {
+        ssize_t ret = recv(sock, dest, len - bytes_recvd, 0);
+        if (ret < 0) {
+            return ret;
+        }
+        dest += ret;
+        bytes_recvd += ret;
+        if (ret == 0) {
+            break;
+        }
+    }
+    return bytes_recvd;
+}
+
+/*
+ * Handles an incoming connection to the rpmb daemon.
+ * Returns 0 if the client disconnects without violating the protocol.
+ * Returns a negative value if we terminated the connection abnormally.
+ *
+ * Arguments:
+ *   conn_sock - an fd to send/recv on
+ *   s - an initialized rpmb device
+ */
+int handle_conn(struct rpmb_dev_state* s, int conn_sock) {
+    int ret;
+
+    while (true) {
+        memset(s->res, 0, sizeof(s->res));
+        ret = recv_until(conn_sock, &s->res_count, sizeof(s->res_count));
+
+        /*
+         * Disconnected while not in the middle of anything.
+         */
+        if (ret <= 0) {
+            return 0;
+        }
+
+        if (s->res_count > MAX_PACKET_COUNT) {
+            ALOGE("rpmb_dev: Receive count too large: %d\n", s->res_count);
+            return -1;
+        }
+        if (s->res_count <= 0) {
+            ALOGE("rpmb_dev: Receive count too small: %d\n", s->res_count);
+            return -1;
+        }
+
+        ret = recv_until(conn_sock, &s->cmd_count, sizeof(s->cmd_count));
+        if (ret != sizeof(s->cmd_count)) {
+            ALOGE("rpmb_dev: Failed to read cmd_count");
+            return -1;
+        }
+
+        if (s->cmd_count == 0) {
+            ALOGE("rpmb_dev: Must contain at least one command\n");
+            return -1;
+        }
+
+        if (s->cmd_count > MAX_PACKET_COUNT) {
+            ALOGE("rpmb_dev: Command count is too large\n");
+            return -1;
+        }
+
+        size_t cmd_size = s->cmd_count * sizeof(s->cmd[0]);
+        ret = recv_until(conn_sock, s->cmd, cmd_size);
+        if (ret != (int)cmd_size) {
+            ALOGE("rpmb_dev: Failed to read command: "
+                  "cmd_size: %zu ret: %d, %s\n",
+                  cmd_size, ret, strerror(errno));
+            return -1;
+        }
+
+        rpmb_dev_process_cmd(s);
+
+        size_t resp_size = sizeof(s->res[0]) * s->res_count;
+        ret = send(conn_sock, s->res, resp_size, 0);
+        if (ret != (int)resp_size) {
+            ALOGE("rpmb_dev: Failed to send response: %d, %s\n", ret, strerror(errno));
+            return -1;
+        }
+    }
+}
+
+void usage(const char* argv0) {
+    fprintf(stderr, "Usage: %s [-d|--dev] <datafile> [--sock] <socket_path>\n", argv0);
+    fprintf(stderr, "or:    %s [-d|--dev] <datafile> [--size <size>] [--key key]\n", argv0);
+}
+
+int main(int argc, char** argv) {
+    struct rpmb_dev_state s;
+    int ret;
+    int cmdres_sock;
+    struct sockaddr_un cmdres_sockaddr;
+    const char* data_file_name = NULL;
+    const char* socket_path = NULL;
+    int open_flags;
+    int init = false;
+
+    struct option long_options[] = {{"size", required_argument, 0, 0},
+                                    {"key", required_argument, 0, 0},
+                                    {"sock", required_argument, 0, 0},
+                                    {"dev", required_argument, 0, 'd'},
+                                    {"init", no_argument, &init, true},
+                                    {"verbose", no_argument, &verbose, true},
+                                    {0, 0, 0, 0}};
+
+    memset(&s.header, 0, sizeof(s.header));
+
+    while (1) {
+        int c;
+        int option_index = 0;
+        c = getopt_long(argc, argv, "d:", long_options, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+            /* long args */
+            case 0:
+                switch (option_index) {
+                    /* size */
+                    case 0:
+                        s.header.max_block = atoi(optarg) - 1;
+                        break;
+                    /* key */
+                    case 1:
+                        for (size_t i = 0; i < sizeof(s.header.key.byte); i++) {
+                            if (!optarg) {
+                                break;
+                            }
+                            s.header.key.byte[i] = strtol(optarg, &optarg, 16);
+                            s.header.key_programmed = 1;
+                        }
+                        break;
+                    /* sock */
+                    case 2:
+                        socket_path = optarg;
+                        break;
+                }
+                break;
+            /* dev */
+            case 'd':
+                data_file_name = optarg;
+                break;
+            default:
+                usage(argv[0]);
+                return EXIT_FAILURE;
+        }
+    }
+
+    /*
+     * We always need a data file, and at exactly one of --init or --sock
+     * must be specified.
+     */
+    if (!data_file_name || (!init == !socket_path)) {
+        usage(argv[0]);
+        return EXIT_FAILURE;
+    }
+
+    /*
+     * If the file is already initialized, exit early.
+     */
+    if (init && !access(data_file_name, F_OK)) {
+        return EXIT_SUCCESS;
+    }
+
+    open_flags = O_RDWR;
+    if (init) {
+        open_flags |= O_CREAT | O_TRUNC;
+    }
+    s.data_fd = open(data_file_name, open_flags, S_IWUSR | S_IRUSR);
+    if (s.data_fd < 0) {
+        ALOGE("rpmb_dev: Failed to open rpmb data file, %s: %s\n", data_file_name, strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    if (init) {
+        /* Create new rpmb data file */
+        if (s.header.max_block == 0) {
+            s.header.max_block = 512 - 1;
+        }
+        ret = write(s.data_fd, &s.header, sizeof(s.header));
+        if (ret != sizeof(s.header)) {
+            ALOGE("rpmb_dev: Failed to write rpmb data file: %d, %s\n", ret, strerror(errno));
+            return EXIT_FAILURE;
+        }
+        return EXIT_SUCCESS;
+    }
+
+    ret = read(s.data_fd, &s.header, sizeof(s.header));
+    if (ret != sizeof(s.header)) {
+        ALOGE("rpmb_dev: Failed to read rpmb data file: %d, %s\n", ret, strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    cmdres_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (cmdres_sock < 0) {
+        ALOGE("rpmb_dev: Failed to create command/response socket: %s\n", strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    cmdres_sockaddr.sun_family = AF_UNIX;
+    strncpy(cmdres_sockaddr.sun_path, socket_path, sizeof(cmdres_sockaddr.sun_path));
+
+    ret = bind(cmdres_sock, (struct sockaddr*)&cmdres_sockaddr, sizeof(struct sockaddr_un));
+    if (ret < 0) {
+        ALOGE("rpmb_dev: Failed to bind command/response socket: %s: %s\n", socket_path,
+              strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    ret = listen(cmdres_sock, 1);
+    if (ret < 0) {
+        ALOGE("rpmb_dev: Failed to listen on command/response socket: %s\n", strerror(errno));
+        return EXIT_FAILURE;
+    }
+
+    while (true) {
+        int conn_sock = accept(cmdres_sock, NULL, NULL);
+        if (conn_sock < 0) {
+            ALOGE("rpmb_dev: Could not accept connection: %s\n", strerror(errno));
+            return EXIT_FAILURE;
+        }
+        ret = handle_conn(&s, conn_sock);
+        close(conn_sock);
+        if (ret) {
+            ALOGE("rpmb_dev: Connection terminated: %d", ret);
+        }
+    }
+}
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.rc b/trusty/utils/rpmb_dev/rpmb_dev.rc
new file mode 100644
index 0000000..9f60e81
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_dev.rc
@@ -0,0 +1,29 @@
+# RPMB Mock
+on post-fs-data
+    mkdir /data/vendor/ss
+    chown root system /data/vendor/ss
+    chmod 0770 /data/vendor/ss
+    rm /data/vendor/ss/rpmb_sock
+    start rpmb_mock_init
+    start rpmb_mock
+
+    # Storage proxy
+    start storageproxyd
+
+service storageproxyd /vendor/bin/storageproxyd -d /dev/trusty-ipc-dev0 \
+        -r /data/vendor/ss/rpmb_sock -p /data/vendor/ss -t sock
+    class main
+    disabled
+    user root
+
+service rpmb_mock_init /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --init --key "ea df 64 44 ea 65 5d 1c 87 27 d4 20 71 0d 53 42 dd 73 a3 38 63 e1 d7 94 c3 72 a6 ea e0 64 64 e6" --size 2048
+    disabled
+    user system
+    group system
+    oneshot
+
+service rpmb_mock /vendor/bin/rpmb_dev --dev /data/vendor/ss/RPMB_DATA --sock /data/vendor/ss/rpmb_sock
+    class main
+    disabled
+    user system
+    group system
diff --git a/trusty/utils/rpmb_dev/rpmb_protocol.h b/trusty/utils/rpmb_dev/rpmb_protocol.h
new file mode 100644
index 0000000..bfcb806
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_protocol.h
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "rpmb.h" /* For struct rpmb_key */
+
+#define MMC_READ_MULTIPLE_BLOCK 18
+#define MMC_WRITE_MULTIPLE_BLOCK 25
+#define MMC_RELIABLE_WRITE_FLAG (1 << 31)
+
+#define MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_CRC (1 << 2)
+#define MMC_RSP_OPCODE (1 << 4)
+#define MMC_CMD_ADTC (1 << 5)
+#define MMC_RSP_SPI_S1 (1 << 7)
+#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
+
+struct rpmb_nonce {
+    uint8_t byte[16];
+};
+
+struct rpmb_u16 {
+    uint8_t byte[2];
+};
+
+struct rpmb_u32 {
+    uint8_t byte[4];
+};
+
+#define RPMB_PACKET_DATA_SIZE (256)
+
+struct rpmb_packet {
+    uint8_t pad[196];
+    struct rpmb_key key_mac;
+    uint8_t data[RPMB_PACKET_DATA_SIZE];
+    struct rpmb_nonce nonce;
+    struct rpmb_u32 write_counter;
+    struct rpmb_u16 address;
+    struct rpmb_u16 block_count;
+    struct rpmb_u16 result;
+    struct rpmb_u16 req_resp;
+};
+
+enum rpmb_request {
+    RPMB_REQ_PROGRAM_KEY = 0x0001,
+    RPMB_REQ_GET_COUNTER = 0x0002,
+    RPMB_REQ_DATA_WRITE = 0x0003,
+    RPMB_REQ_DATA_READ = 0x0004,
+    RPMB_REQ_RESULT_READ = 0x0005,
+};
+
+enum rpmb_response {
+    RPMB_RESP_PROGRAM_KEY = 0x0100,
+    RPMB_RESP_GET_COUNTER = 0x0200,
+    RPMB_RESP_DATA_WRITE = 0x0300,
+    RPMB_RESP_DATA_READ = 0x0400,
+};
+
+enum rpmb_result {
+    RPMB_RES_OK = 0x0000,
+    RPMB_RES_GENERAL_FAILURE = 0x0001,
+    RPMB_RES_AUTH_FAILURE = 0x0002,
+    RPMB_RES_COUNT_FAILURE = 0x0003,
+    RPMB_RES_ADDR_FAILURE = 0x0004,
+    RPMB_RES_WRITE_FAILURE = 0x0005,
+    RPMB_RES_READ_FAILURE = 0x0006,
+    RPMB_RES_NO_AUTH_KEY = 0x0007,
+
+    RPMB_RES_WRITE_COUNTER_EXPIRED = 0x0080,
+};
+
+static inline struct rpmb_u16 rpmb_u16(uint16_t val) {
+    struct rpmb_u16 ret = {{
+            (uint8_t)(val >> 8),
+            (uint8_t)(val >> 0),
+    }};
+    return ret;
+}
+
+static inline struct rpmb_u32 rpmb_u32(uint32_t val) {
+    struct rpmb_u32 ret = {{
+            (uint8_t)(val >> 24),
+            (uint8_t)(val >> 16),
+            (uint8_t)(val >> 8),
+            (uint8_t)(val >> 0),
+    }};
+    return ret;
+}
+
+static inline uint16_t rpmb_get_u16(struct rpmb_u16 u16) {
+    size_t i;
+    uint16_t val;
+
+    val = 0;
+    for (i = 0; i < sizeof(u16.byte); i++)
+        val = val << 8 | u16.byte[i];
+
+    return val;
+}
+
+static inline uint32_t rpmb_get_u32(struct rpmb_u32 u32) {
+    size_t i;
+    uint32_t val;
+
+    val = 0;
+    for (i = 0; i < sizeof(u32.byte); i++)
+        val = val << 8 | u32.byte[i];
+
+    return val;
+}