Hardware keyguard message interface

Simple message serialization interface for passing
keyguard objects through the HAL.

Change-Id: I3c1f83976edd88b0f5cb7fea3f33ba620b5f2057
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..8a28334
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+###
+# libkeyguard_messages contains just the code necessary to communicate with a
+# GoogleKeyguard implementation, e.g. one running in TrustZone.
+##
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libkeyguard_messages
+LOCAL_SRC_FILES:= keyguard_messages.cpp
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/include
+LOCAL_CFLAGS = -Wall -Werror
+LOCAL_MODULE_TAGS := optional
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_SHARED_LIBRARY)
diff --git a/include/keyguard/keyguard_messages.h b/include/keyguard/keyguard_messages.h
new file mode 100644
index 0000000..9907527
--- /dev/null
+++ b/include/keyguard/keyguard_messages.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 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 KEYGUARD_MESSAGES_H_
+#define KEYGUARD_MESSAGES_H_
+
+#include <memory>
+#include <stdint.h>
+
+namespace keyguard {
+
+typedef enum {
+    KG_ERROR_OK = 0,
+    KG_ERROR_INVALID = 1,
+} keyguard_error_t;
+
+typedef struct {
+    std::unique_ptr<uint8_t> buffer;
+    size_t length;
+} SizedBuffer;
+
+class KeyguardMessage {
+public:
+    KeyguardMessage() : error_(KG_ERROR_OK) {}
+    KeyguardMessage(keyguard_error_t error) : error_(error) {}
+    virtual ~KeyguardMessage() {}
+
+    size_t GetSerializedSize() const;
+    uint8_t *Serialize() const;
+    keyguard_error_t Deserialize(const uint8_t *payload, const uint8_t *end);
+    keyguard_error_t GetError() const { return error_; }
+
+protected:
+    virtual size_t nonErrorSerializedSize() const { return 0; } ;
+    virtual void nonErrorSerialize(uint8_t *buffer) const { }
+    virtual keyguard_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) {
+        return KG_ERROR_OK;
+    }
+
+    keyguard_error_t error_;
+};
+
+class VerifyRequest : public KeyguardMessage {
+public:
+    VerifyRequest(
+            SizedBuffer *enrolled_password_handle,
+            SizedBuffer *provided_password_payload);
+    VerifyRequest();
+    ~VerifyRequest();
+
+    const SizedBuffer *GetPasswordHandle() const { return &password_handle_; }
+    const SizedBuffer *GetProvidedPassword() const { return &provided_password_; }
+
+protected:
+    virtual size_t nonErrorSerializedSize() const;
+    virtual void nonErrorSerialize(uint8_t *buffer) const;
+    virtual keyguard_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end);
+
+private:
+    SizedBuffer password_handle_;
+    SizedBuffer provided_password_;
+};
+
+class VerifyResponse : public KeyguardMessage {
+public:
+    VerifyResponse(SizedBuffer *verification_token);
+    VerifyResponse();
+    ~VerifyResponse();
+
+    const SizedBuffer *GetVerificationToken() const { return &verification_token_; }
+
+protected:
+    virtual size_t nonErrorSerializedSize() const;
+    virtual void nonErrorSerialize(uint8_t *buffer) const;
+    virtual keyguard_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end);
+
+private:
+    SizedBuffer verification_token_;
+};
+
+class EnrollRequest : public KeyguardMessage {
+public:
+    EnrollRequest(SizedBuffer *provided_password);
+    EnrollRequest();
+    ~EnrollRequest();
+
+    const SizedBuffer *GetProvidedPassword() const { return &provided_password_; }
+
+protected:
+    virtual size_t nonErrorSerializedSize() const;
+    virtual void nonErrorSerialize(uint8_t *buffer) const;
+    virtual keyguard_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end);
+private:
+    SizedBuffer provided_password_;
+};
+
+class EnrollResponse : public KeyguardMessage {
+public:
+    EnrollResponse(SizedBuffer *enrolled_password_handle);
+    EnrollResponse();
+    ~EnrollResponse();
+
+    const SizedBuffer *GetEnrolledPasswordHandle() const { return &enrolled_password_handle_; }
+
+protected:
+    virtual size_t nonErrorSerializedSize() const;
+    virtual void nonErrorSerialize(uint8_t *buffer) const;
+    virtual keyguard_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end);
+
+private:
+   SizedBuffer enrolled_password_handle_;
+};
+}
+
+#endif // KEYGUARD_MESSAGES_H_
diff --git a/keyguard_messages.cpp b/keyguard_messages.cpp
new file mode 100644
index 0000000..0c92e78
--- /dev/null
+++ b/keyguard_messages.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright 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.
+ *
+ *
+ * TODO(anmorales): figure out a reasonable max buffer size
+ */
+
+#include <keyguard/keyguard_messages.h>
+
+#include <string.h>
+
+namespace keyguard {
+
+/**
+ * Variant of memset() that uses GCC-specific pragmas to disable optimizations, so effect is not
+ * optimized away.  This is important because we often need to wipe blocks of sensitive data from
+ * memory.  As an additional convenience, this implementation avoids writing to NULL pointers.
+ */
+#ifdef __clang__
+#define OPTNONE __attribute__((optnone))
+#else  // not __clang__
+#define OPTNONE __attribute__((optimize("O0")))
+#endif  // not __clang__
+inline OPTNONE void* memset_s(void* s, int c, size_t n) {
+    if (!s)
+        return s;
+    return memset(s, c, n);
+}
+#undef OPTNONE
+
+static inline size_t buffer_size(const SizedBuffer &buf) {
+    return sizeof(uint32_t) + buf.length;
+}
+
+static inline void append_to_buffer(uint8_t **buffer, const SizedBuffer *to_append) {
+    memcpy(*buffer, &to_append->length, sizeof(to_append->length));
+    *buffer += sizeof(to_append->length);
+    memcpy(*buffer, to_append->buffer.get(), to_append->length);
+    *buffer += to_append->length;
+}
+
+static inline keyguard_error_t read_from_buffer(const uint8_t **buffer, const uint8_t *end,
+        SizedBuffer *target) {
+    if (*buffer + sizeof(target->length) >= end) return KG_ERROR_INVALID;
+
+    memcpy(&target->length, *buffer, sizeof(target->length));
+    *buffer += sizeof(target->length);
+    const uint8_t *buffer_end = *buffer + target->length;
+    if (buffer_end > end || buffer_end <= *buffer) return KG_ERROR_INVALID;
+
+    target->buffer.reset(new uint8_t[target->length]);
+    memcpy(target->buffer.get(), *buffer, target->length);
+    *buffer += target->length;
+    return KG_ERROR_OK;
+}
+
+size_t KeyguardMessage::GetSerializedSize() const {
+    if (error_ == KG_ERROR_OK) {
+        return sizeof(uint32_t) + nonErrorSerializedSize();
+    } else {
+        return sizeof(uint32_t);
+    }
+}
+
+uint8_t *KeyguardMessage::Serialize() const {
+    if (error_ != KG_ERROR_OK) {
+        uint32_t *error_buf = new uint32_t;
+        *error_buf = static_cast<uint32_t>(error_);
+        return reinterpret_cast<uint8_t *>(error_buf);
+    } else {
+        uint8_t *buf = new uint8_t[sizeof(uint32_t) + nonErrorSerializedSize()];
+        uint32_t error_value = static_cast<uint32_t>(error_);
+        memcpy(buf, &error_value, sizeof(uint32_t));
+        nonErrorSerialize(buf + sizeof(uint32_t));
+        return buf;
+    }
+}
+
+keyguard_error_t KeyguardMessage::Deserialize(const uint8_t *payload, const uint8_t *end) {
+    uint32_t error_value;
+    if (payload + sizeof(uint32_t) > end) return KG_ERROR_INVALID;
+    memcpy(&error_value, payload, sizeof(uint32_t));
+    error_ = static_cast<keyguard_error_t>(error_value);
+    if (error_ == KG_ERROR_OK) {
+        error_ = nonErrorDeserialize(payload + sizeof(uint32_t), end);
+    }
+
+    return error_;
+}
+
+
+VerifyRequest::VerifyRequest(SizedBuffer *enrolled_password_handle,
+        SizedBuffer *provided_password_payload) {
+    password_handle_.buffer = std::move(enrolled_password_handle->buffer);
+    password_handle_.length = enrolled_password_handle->length;
+    provided_password_.buffer = std::move(provided_password_payload->buffer);
+    provided_password_.length = provided_password_payload->length;
+}
+
+VerifyRequest::VerifyRequest() {
+    memset_s(&password_handle_, 0, sizeof(password_handle_));
+    memset_s(&provided_password_, 0, sizeof(provided_password_));
+}
+
+VerifyRequest::~VerifyRequest() {
+    if (password_handle_.buffer) {
+        password_handle_.buffer.reset();
+    }
+
+    if (provided_password_.buffer) {
+        memset_s(provided_password_.buffer.get(), 0, provided_password_.length);
+        provided_password_.buffer.reset();
+    }
+}
+
+size_t VerifyRequest::nonErrorSerializedSize() const {
+    return buffer_size(password_handle_) + buffer_size(provided_password_);
+}
+
+void VerifyRequest::nonErrorSerialize(uint8_t *buffer) const {
+    append_to_buffer(&buffer, &password_handle_);
+    append_to_buffer(&buffer, &provided_password_);
+}
+
+keyguard_error_t VerifyRequest::nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) {
+    keyguard_error_t error = KG_ERROR_OK;
+
+    if (password_handle_.buffer) {
+        password_handle_.buffer.reset();
+    }
+
+    if (provided_password_.buffer) {
+        memset_s(provided_password_.buffer.get(), 0, provided_password_.length);
+        provided_password_.buffer.reset();
+    }
+
+    error = read_from_buffer(&payload, end, &password_handle_);
+    if (error != KG_ERROR_OK) return error;
+
+    return read_from_buffer(&payload, end, &provided_password_);
+
+}
+
+VerifyResponse::VerifyResponse(SizedBuffer *verification_token) {
+    verification_token_.buffer = std::move(verification_token->buffer);
+    verification_token_.length = verification_token->length;
+}
+
+VerifyResponse::VerifyResponse() {
+    memset_s(&verification_token_, 0, sizeof(verification_token_));
+};
+
+VerifyResponse::~VerifyResponse() {
+    if (verification_token_.length > 0) {
+        verification_token_.buffer.reset();
+    }
+}
+
+size_t VerifyResponse::nonErrorSerializedSize() const {
+    return buffer_size(verification_token_);
+}
+
+void VerifyResponse::nonErrorSerialize(uint8_t *buffer) const {
+    append_to_buffer(&buffer, &verification_token_);
+}
+
+keyguard_error_t VerifyResponse::nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) {
+    if (verification_token_.buffer) {
+        verification_token_.buffer.reset();
+    }
+
+    return read_from_buffer(&payload, end, &verification_token_);
+}
+
+EnrollRequest::EnrollRequest(SizedBuffer *provided_password) {
+    provided_password_.buffer = std::move(provided_password->buffer);
+    provided_password_.length = provided_password->length;
+}
+
+EnrollRequest::EnrollRequest() {
+    memset_s(&provided_password_, 0, sizeof(provided_password_));
+}
+
+EnrollRequest::~EnrollRequest() {
+    if (provided_password_.buffer) {
+        memset_s(provided_password_.buffer.get(), 0, provided_password_.length);
+        provided_password_.buffer.reset();
+    }
+}
+
+size_t EnrollRequest::nonErrorSerializedSize() const {
+   return buffer_size(provided_password_);
+}
+
+void EnrollRequest::nonErrorSerialize(uint8_t *buffer) const {
+    append_to_buffer(&buffer, &provided_password_);
+}
+
+keyguard_error_t EnrollRequest::nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) {
+    if (provided_password_.buffer) {
+        memset_s(provided_password_.buffer.get(), 0, provided_password_.length);
+        provided_password_.buffer.reset();
+    }
+
+    return read_from_buffer(&payload, end, &provided_password_);
+}
+
+EnrollResponse::EnrollResponse(SizedBuffer *enrolled_password_handle) {
+    enrolled_password_handle_.buffer = std::move(enrolled_password_handle->buffer);
+    enrolled_password_handle_.length = enrolled_password_handle->length;
+}
+
+EnrollResponse::EnrollResponse() {
+    memset_s(&enrolled_password_handle_, 0, sizeof(enrolled_password_handle_));
+}
+
+EnrollResponse::~EnrollResponse() {
+    if (enrolled_password_handle_.buffer) {
+        enrolled_password_handle_.buffer.reset();
+    }
+}
+
+size_t EnrollResponse::nonErrorSerializedSize() const {
+    return buffer_size(enrolled_password_handle_);
+}
+
+void EnrollResponse::nonErrorSerialize(uint8_t *buffer) const {
+    append_to_buffer(&buffer, &enrolled_password_handle_);
+}
+
+keyguard_error_t EnrollResponse::nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) {
+    if (enrolled_password_handle_.buffer) {
+        enrolled_password_handle_.buffer.reset();
+    }
+
+    return read_from_buffer(&payload, end, &enrolled_password_handle_);
+}
+
+};
+
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..60fc50f
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := keyguard-unit-tests
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_SHARED_LIBRARIES += libkeyguard_messages
+LOCAL_SRC_FILES := keyguard_messages_test.cpp
+include $(BUILD_NATIVE_TEST)
diff --git a/tests/keyguard_messages_test.cpp b/tests/keyguard_messages_test.cpp
new file mode 100644
index 0000000..77fbb99
--- /dev/null
+++ b/tests/keyguard_messages_test.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <keyguard/keyguard_messages.h>
+
+using ::keyguard::SizedBuffer;
+using ::testing::Test;
+using ::keyguard::EnrollRequest;
+using ::keyguard::EnrollResponse;
+using ::keyguard::VerifyRequest;
+using ::keyguard::VerifyResponse;
+using std::cout;
+using std::endl;
+
+static SizedBuffer *make_buffer(size_t size) {
+    SizedBuffer *result = new SizedBuffer;
+    result->length = size;
+    uint8_t *buffer = new uint8_t[size];
+    srand(size);
+
+    for (size_t i = 0; i < size; i++) {
+        buffer[i] = rand();
+    }
+
+    result->buffer.reset(buffer);
+    return result;
+}
+
+TEST(RoundTripTest, EnrollRequest) {
+    const size_t password_size = 512;
+    SizedBuffer *provided_password = make_buffer(password_size);
+    const SizedBuffer *deserialized_password;
+    // create request, serialize, deserialize, and validate
+    EnrollRequest req(provided_password);
+    uint8_t *serialized_req = req.Serialize();
+    EnrollRequest deserialized_req;
+    deserialized_req.Deserialize(serialized_req, serialized_req + req.GetSerializedSize());
+    delete[] serialized_req;
+
+    ASSERT_EQ(keyguard::keyguard_error_t::KG_ERROR_OK,
+            deserialized_req.GetError());
+
+    deserialized_password = deserialized_req.GetProvidedPassword();
+    ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
+    ASSERT_EQ(0, memcmp(req.GetProvidedPassword()->buffer.get(), deserialized_password->buffer.get(), password_size));
+}
+
+TEST(RoundTripTest, EnrollResponse) {
+    const size_t password_size = 512;
+    SizedBuffer *enrolled_password = make_buffer(password_size);
+    const SizedBuffer *deserialized_password;
+    // create request, serialize, deserialize, and validate
+    EnrollResponse req(enrolled_password);
+    uint8_t *serialized_req = req.Serialize();
+    EnrollResponse deserialized_req;
+    deserialized_req.Deserialize(serialized_req, serialized_req + req.GetSerializedSize());
+    delete[] serialized_req;
+
+    ASSERT_EQ(keyguard::keyguard_error_t::KG_ERROR_OK,
+            deserialized_req.GetError());
+
+    deserialized_password = deserialized_req.GetEnrolledPasswordHandle();
+    ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
+    ASSERT_EQ(0, memcmp(req.GetEnrolledPasswordHandle()->buffer.get(), deserialized_password->buffer.get(), password_size));
+}
+
+TEST(RoundTripTest, VerifyRequest) {
+    const size_t password_size = 1;
+    SizedBuffer *provided_password = make_buffer(password_size),
+          *password_handle = make_buffer(password_size);
+    const SizedBuffer *deserialized_password;
+    // create request, serialize, deserialize, and validate
+    VerifyRequest req(password_handle, provided_password);
+    uint8_t *serialized_req = req.Serialize();
+    VerifyRequest deserialized_req;
+    deserialized_req.Deserialize(serialized_req, serialized_req + req.GetSerializedSize());
+
+    ASSERT_EQ(keyguard::keyguard_error_t::KG_ERROR_OK,
+            deserialized_req.GetError());
+
+    deserialized_password = deserialized_req.GetProvidedPassword();
+    ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
+    ASSERT_EQ(0, memcmp(req.GetProvidedPassword()->buffer.get(), deserialized_password->buffer.get(), password_size));
+
+    deserialized_password = deserialized_req.GetPasswordHandle();
+    ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
+    ASSERT_EQ(0, memcmp(req.GetPasswordHandle()->buffer.get(), deserialized_password->buffer.get(), password_size));
+}
+
+TEST(RoundTripTest, VerifyResponse) {
+    const size_t password_size = 512;
+    SizedBuffer *verification_token = make_buffer(password_size);
+    const SizedBuffer *deserialized_password;
+    // create request, serialize, deserialize, and validate
+    VerifyResponse req(verification_token);
+    uint8_t *serialized_req = req.Serialize();
+    VerifyResponse deserialized_req;
+    deserialized_req.Deserialize(serialized_req, serialized_req + req.GetSerializedSize());
+    delete[] serialized_req;
+
+    ASSERT_EQ(keyguard::keyguard_error_t::KG_ERROR_OK,
+            deserialized_req.GetError());
+
+    deserialized_password = deserialized_req.GetVerificationToken();
+    ASSERT_EQ((uint32_t) password_size, deserialized_password->length);
+    ASSERT_EQ(0, memcmp(req.GetVerificationToken()->buffer.get(), deserialized_password->buffer.get(), password_size));
+}
+
+uint8_t msgbuf[] = {
+    220, 88,  183, 255, 71,  1,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   173, 0,   0,   0,   228, 174, 98,  187, 191, 135, 253, 200, 51,  230, 114, 247, 151, 109,
+    237, 79,  87,  32,  94,  5,   204, 46,  154, 30,  91,  6,   103, 148, 254, 129, 65,  171, 228,
+    167, 224, 163, 9,   15,  206, 90,  58,  11,  205, 55,  211, 33,  87,  178, 149, 91,  28,  236,
+    218, 112, 231, 34,  82,  82,  134, 103, 137, 115, 27,  156, 102, 159, 220, 226, 89,  42,  25,
+    37,  9,   84,  239, 76,  161, 198, 72,  167, 163, 39,  91,  148, 191, 17,  191, 87,  169, 179,
+    136, 10,  194, 154, 4,   40,  107, 109, 61,  161, 20,  176, 247, 13,  214, 106, 229, 45,  17,
+    5,   60,  189, 64,  39,  166, 208, 14,  57,  25,  140, 148, 25,  177, 246, 189, 43,  181, 88,
+    204, 29,  126, 224, 100, 143, 93,  60,  57,  249, 55,  0,   87,  83,  227, 224, 166, 59,  214,
+    81,  144, 129, 58,  6,   57,  46,  254, 232, 41,  220, 209, 230, 167, 138, 158, 94,  180, 125,
+    247, 26,  162, 116, 238, 202, 187, 100, 65,  13,  180, 44,  245, 159, 83,  161, 176, 58,  72,
+    236, 109, 105, 160, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   11,  0,   0,   0,   98,  0,   0,   0,   1,   0,   0,   32,  2,   0,   0,   0,   1,   0,
+    0,   32,  3,   0,   0,   0,   2,   0,   0,   16,  1,   0,   0,   0,   3,   0,   0,   48,  0,
+    1,   0,   0,   200, 0,   0,   80,  3,   0,   0,   0,   0,   0,   0,   0,   244, 1,   0,   112,
+    1,   246, 1,   0,   112, 1,   189, 2,   0,   96,  144, 178, 236, 250, 255, 255, 255, 255, 145,
+    1,   0,   96,  144, 226, 33,  60,  222, 2,   0,   0,   189, 2,   0,   96,  0,   0,   0,   0,
+    0,   0,   0,   0,   190, 2,   0,   16,  1,   0,   0,   0,   12,  0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   110, 0,   0,   0,   0,   0,   0,   0,   11,  0,
+    0,   0,   98,  0,   0,   0,   1,   0,   0,   32,  2,   0,   0,   0,   1,   0,   0,   32,  3,
+    0,   0,   0,   2,   0,   0,   16,  1,   0,   0,   0,   3,   0,   0,   48,  0,   1,   0,   0,
+    200, 0,   0,   80,  3,   0,   0,   0,   0,   0,   0,   0,   244, 1,   0,   112, 1,   246, 1,
+    0,   112, 1,   189, 2,   0,   96,  144, 178, 236, 250, 255, 255, 255, 255, 145, 1,   0,   96,
+    144, 226, 33,  60,  222, 2,   0,   0,   189, 2,   0,   96,  0,   0,   0,   0,   0,   0,   0,
+    0,   190, 2,   0,   16,  1,   0,   0,   0,
+};
+
+
+/*
+ * These tests don't have any assertions or expectations. They just try to parse garbage, to see if
+ * the result will be a crash.  This is especially informative when run under Valgrind memcheck.
+ */
+
+template <typename Message> void parse_garbage() {
+    Message msg;
+    size_t array_length = sizeof(msgbuf) / sizeof(msgbuf[0]);
+    const uint8_t* end = msgbuf + array_length;
+    for (size_t i = 0; i < array_length; ++i) {
+        const uint8_t* begin = msgbuf + i;
+        const uint8_t* p = begin;
+        msg.Deserialize(p, end);
+    }
+}
+
+#define GARBAGE_TEST(Message)                                                                      \
+    TEST(GarbageTest, Message) { parse_garbage<Message>(); }
+
+GARBAGE_TEST(VerifyRequest);
+GARBAGE_TEST(VerifyResponse);
+GARBAGE_TEST(EnrollRequest);
+GARBAGE_TEST(EnrollResponse);