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);