bdb: Add vba_update_buc

vba_update_buc writes a BUC (boot unlock code) to NVM-RW. It will be called
by AP-RW to update a BUC.

BUG=chrome-os-partner:51907
BRANCH=tot
TEST=make runtests

Change-Id: Ic91f34b60b11ebce948bce01993ddb44519a59b8
Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/346233
diff --git a/firmware/bdb/bdb.h b/firmware/bdb/bdb.h
index 9f3b9c6..5163401 100644
--- a/firmware/bdb/bdb.h
+++ b/firmware/bdb/bdb.h
@@ -88,6 +88,9 @@
 	BDB_ERROR_NVM_STRUCT_VERSION,
 	BDB_ERROR_NVM_VBE_READ,
 	BDB_ERROR_NVM_RW_BUFFER_SMALL,
+	BDB_ERROR_DECRYPT_BUC,
+	BDB_ERROR_ENCRYPT_BUC,
+	BDB_ERROR_WRITE_BUC,
 };
 
 /*****************************************************************************/
diff --git a/firmware/bdb/bdb_api.h b/firmware/bdb/bdb_api.h
index 9979824..7ef935c 100644
--- a/firmware/bdb/bdb_api.h
+++ b/firmware/bdb/bdb_api.h
@@ -68,6 +68,15 @@
 			      uint32_t kernel_version);
 
 /**
+ * Write new boot unlock code to NVM-RW
+ *
+ * @param ctx
+ * @param new_buc	New BUC to be written
+ * @return		BDB_SUCCESS or BDB_ERROR_*
+ */
+int vba_update_buc(struct vba_context *ctx, uint8_t *new_buc);
+
+/**
  * Get vboot register value
  *
  * Implemented by each chip
@@ -119,4 +128,28 @@
  */
 int vbe_write_nvm(enum nvm_type type, void *buf, uint32_t size);
 
+/**
+ * Encrypt data by AES-256
+ *
+ * @param msg	Message to be encrypted
+ * @param len	Length of <msg> in bytes
+ * @param key	Key used for encryption
+ * @param out	Buffer where encrypted message is stored
+ * @return	BDB_SUCCESS or BDB_ERROR_*
+ */
+int vbe_aes256_encrypt(const uint8_t *msg, uint32_t len, const uint8_t *key,
+		       uint8_t *out);
+
+/**
+ * Decrypt data by AES-256
+ *
+ * @param msg	Message to be decrypted
+ * @param len	Length of <msg> in bytes
+ * @param key	Key used for decryption
+ * @param out	Buffer where decrypted message is stored
+ * @return	BDB_SUCCESS or BDB_ERROR_*
+ */
+int vbe_aes256_decrypt(const uint8_t *msg, uint32_t len, const uint8_t *key,
+		       uint8_t *out);
+
 #endif
diff --git a/firmware/bdb/nvm.c b/firmware/bdb/nvm.c
index b5c53af..a7c56b0 100644
--- a/firmware/bdb/nvm.c
+++ b/firmware/bdb/nvm.c
@@ -229,3 +229,39 @@
 
 	return BDB_SUCCESS;
 }
+
+int vba_update_buc(struct vba_context *ctx, uint8_t *new_buc)
+{
+	struct nvmrw *nvm = &ctx->nvmrw;
+	uint8_t buc[BUC_ENC_DIGEST_SIZE];
+	int rv1, rv2;
+
+	if (nvmrw_verify(ctx->ro_secrets, nvm, sizeof(*nvm))) {
+		if (nvmrw_init(ctx))
+			return BDB_ERROR_NVM_INIT;
+	}
+
+	/* Encrypt new BUC
+	 * Note that we do not need to decide whether we should use hardware
+	 * crypto or not because this is supposed to be running in RW code. */
+	if (vbe_aes256_encrypt(new_buc, BUC_ENC_DIGEST_SIZE,
+			       ctx->rw_secrets->buc, buc))
+		return BDB_ERROR_ENCRYPT_BUC;
+
+	/* Return if new BUC is same as current one. */
+	if (!memcmp(buc, nvm->buc_enc_digest, sizeof(buc)))
+		return BDB_SUCCESS;
+
+	memcpy(nvm->buc_enc_digest, buc, sizeof(buc));
+
+	/* Increment update counter */
+	nvm->update_count++;
+
+	/* Write new BUC */
+	rv1 = nvmrw_write(ctx, NVM_TYPE_RW_PRIMARY);
+	rv2 = nvmrw_write(ctx, NVM_TYPE_RW_SECONDARY);
+	if (rv1 || rv2)
+		return BDB_ERROR_WRITE_BUC;
+
+	return BDB_SUCCESS;
+}
diff --git a/firmware/bdb/stub.c b/firmware/bdb/stub.c
index 58a0096..4af9126 100644
--- a/firmware/bdb/stub.c
+++ b/firmware/bdb/stub.c
@@ -35,3 +35,17 @@
 {
 	return BDB_ERROR_NOT_IMPLEMENTED;
 }
+
+__attribute__((weak))
+int vbe_aes256_encrypt(const uint8_t *msg, uint32_t len, const uint8_t *key,
+		       uint8_t *out)
+{
+	return BDB_ERROR_NOT_IMPLEMENTED;
+}
+
+__attribute__((weak))
+int vbe_aes256_decrypt(const uint8_t *msg, uint32_t len, const uint8_t *key,
+		       uint8_t *out)
+{
+	return BDB_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/tests/bdb_sprw_test.c b/tests/bdb_sprw_test.c
index 06739ae..75deba6 100644
--- a/tests/bdb_sprw_test.c
+++ b/tests/bdb_sprw_test.c
@@ -8,6 +8,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <openssl/aes.h>
 
 #include "2sha.h"
 #include "2hmac.h"
@@ -34,6 +35,13 @@
 	.nvm_rw = {0x00, },
 };
 
+struct bdb_rw_secrets rw_secrets = {
+	.buc = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+		0xff, 0xff},
+};
+
 static int vbe_write_nvm_failure = 0;
 
 static struct bdb_header *create_bdb(const char *key_dir,
@@ -554,6 +562,48 @@
 	verify_kernel_version(0, 0, 1, 0, BDB_SUCCESS);
 }
 
+int vbe_aes256_encrypt(const uint8_t *msg, uint32_t len, const uint8_t *key,
+		       uint8_t *out)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		out[i] = msg[i] ^ key[i % 256/8];
+
+	return BDB_SUCCESS;
+}
+
+int vbe_aes256_decrypt(const uint8_t *msg, uint32_t len, const uint8_t *key,
+		       uint8_t *out)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		out[i] = msg[i] ^ key[i % 256/8];
+
+	return BDB_SUCCESS;
+}
+
+static void test_update_buc(void)
+{
+	uint8_t new_buc[BUC_ENC_DIGEST_SIZE];
+	uint8_t enc_buc[BUC_ENC_DIGEST_SIZE];
+	struct nvmrw *nvm = (struct nvmrw *)nvmrw1;
+	struct vba_context ctx = {
+		.bdb = NULL,
+		.ro_secrets = &secrets,
+		.rw_secrets = &rw_secrets,
+	};
+
+	install_nvm(NVM_TYPE_RW_PRIMARY, 0, 1, 0);
+	install_nvm(NVM_TYPE_RW_SECONDARY, 1, 0, 0);
+
+	TEST_SUCC(vba_update_buc(&ctx, new_buc), NULL);
+	vbe_aes256_encrypt(new_buc, sizeof(new_buc), ctx.rw_secrets->buc,
+			   enc_buc);
+	TEST_SUCC(memcmp(nvm->buc_enc_digest, enc_buc, sizeof(new_buc)), NULL);
+}
+
 int main(int argc, char *argv[])
 {
 	if (argc != 2) {
@@ -566,6 +616,7 @@
 	test_nvm_read();
 	test_nvm_write();
 	test_update_kernel_version();
+	test_update_buc();
 
 	return gTestSuccess ? 0 : 255;
 }