diff options
-rw-r--r-- | Android.mk | 8 | ||||
-rw-r--r-- | include/keyguard/keyguard.h | 91 | ||||
-rw-r--r-- | include/keyguard/keyguard_messages.h | 63 | ||||
-rw-r--r-- | include/keyguard/keyguard_utils.h | 57 | ||||
-rw-r--r-- | keyguard.cpp | 92 | ||||
-rw-r--r-- | keyguard_messages.cpp | 63 | ||||
-rw-r--r-- | tests/Android.mk | 6 | ||||
-rw-r--r-- | tests/keyguard_messages_test.cpp | 16 | ||||
-rw-r--r-- | tests/keyguard_test.cpp | 128 |
9 files changed, 482 insertions, 42 deletions
@@ -15,12 +15,14 @@ LOCAL_PATH := $(call my-dir) ### -# libkeyguard_messages contains just the code necessary to communicate with a +# libkeyguard 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_MODULE:= libkeyguard +LOCAL_SRC_FILES := \ + keyguard_messages.cpp \ + google_keyguard.cpp LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include LOCAL_CFLAGS = -Wall -Werror diff --git a/include/keyguard/keyguard.h b/include/keyguard/keyguard.h new file mode 100644 index 0000000..cae9d05 --- /dev/null +++ b/include/keyguard/keyguard.h @@ -0,0 +1,91 @@ +/* + * 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 GOOGLE_KEYGUARD_H_ +#define GOOGLE_KEYGUARD_H_ + +#include <memory> +#include <stdint.h> + +#include "keyguard_messages.h" + +namespace keyguard { + +/** + * Data format for an authentication record used to prove + * successful password verification. Consumed by KeyStore + * and keymaster to determine CryptoObject availability. + */ +struct __attribute__ ((__packed__)) AuthToken { + const uint8_t auth_token_tag = 0x01; + uint32_t auth_token_size; + const uint8_t user_id_tag = 0x02; + uint32_t user_id; + const uint8_t authenticator_id_tag = 0x03; + const uint32_t authenticator_id = 0; + const uint8_t timestamp_tag = 0x04; + uint64_t timestamp; + const uint8_t hmac_tag = 0x06; + uint8_t hmac[16]; +}; + +/** + * Base class for keyguard implementations. Provides all functionality except + * the ability to create/access keys and compute signatures. These are left up + * to the platform-specific implementation. + */ +class GoogleKeyguard { +public: + GoogleKeyguard() {} + virtual ~GoogleKeyguard(); + + void Enroll(const EnrollRequest &request, EnrollResponse *response); + void Verify(const VerifyRequest &request, VerifyResponse *response); + +protected: + /** + * Generates a signed attestation of an authentication event. + * The format is consistent with that of AuthToken above. + */ + std::unique_ptr<uint8_t> MintAuthToken(uint32_t user_id, size_t *length); + + // The following methods are intended to be implemented by concrete subclasses + + /** + * Retrieves the key used by GoogleKeyguard::MintAuthToken to sign the payload + * of the AuthToken. This is not cached as is may have changed due to an event such + * as a password change. + */ + virtual std::unique_ptr<uint8_t[]> GetAuthTokenKey() const = 0; + + /** + * Uses platform-specific routines to compute a signature on the provided message. + * Returns a pointer to the signature, as well as the length in signature_length if + * it is not NULL. + */ + virtual std::unique_ptr<uint8_t> ComputeSignature(const uint8_t key[], + const uint8_t *message, const size_t length, size_t *signature_length) const = 0; + + /** + * The key used to sign and verify password data. This is different from the AuthTokenKey. + * It can be cached in this member variable as Keyguard is its only consumer. It should at + * no point leave Keyguard for any reason. + */ + std::unique_ptr<uint8_t[]> password_key_; +}; +} + +#endif // GOOGLE_KEYGUARD_H_ diff --git a/include/keyguard/keyguard_messages.h b/include/keyguard/keyguard_messages.h index 9907527..59c9f04 100644 --- a/include/keyguard/keyguard_messages.h +++ b/include/keyguard/keyguard_messages.h @@ -19,6 +19,10 @@ #include <memory> #include <stdint.h> +#include "google_keyguard_utils.h" +/** + * Message serialization objects for communicating with the hardware keyguard. + */ namespace keyguard { typedef enum { @@ -31,36 +35,86 @@ typedef struct { size_t length; } SizedBuffer; +/* + * Abstract base class of all message objects. Handles serialization of common + * elements like the error and user ID. Delegates specialized serialization + * to protected pure virtual functions implemented by subclasses. + */ class KeyguardMessage { public: KeyguardMessage() : error_(KG_ERROR_OK) {} KeyguardMessage(keyguard_error_t error) : error_(error) {} virtual ~KeyguardMessage() {} + /** + * Returns serialized size in bytes of the current state of the + * object. + */ size_t GetSerializedSize() const; + /** + * Converts the object into its serialized representation. + * The returned buffer's ownwership is tranferred to the caller. + * TODO: make this return a unique_ptr so that this is clear + */ uint8_t *Serialize() const; + + /** + * Inflates the object from its serial representation. + */ keyguard_error_t Deserialize(const uint8_t *payload, const uint8_t *end); + keyguard_error_t GetError() const { return error_; } + void SetError(const keyguard_error_t error) { error_ = error; } + uint32_t GetUserId() const { return user_id_; } protected: + /** + * The following methods are intended to be implemented by subclasses. + * They are hooks to serialize the elements specific to each particular + * specialization. + */ + + /** + * Returns the size of serializing only the elements specific to the + * current sublclass. + */ virtual size_t nonErrorSerializedSize() const { return 0; } ; + /** + * Takes a pointer to a buffer prepared by Serialize and writes + * the subclass specific data into it. The size of the buffer is exaclty + * that returned by nonErrorSerializedSize() in bytes. + */ virtual void nonErrorSerialize(uint8_t *buffer) const { } + + /** + * Deserializes subclass specific data from payload without reading past end. + */ virtual keyguard_error_t nonErrorDeserialize(const uint8_t *payload, const uint8_t *end) { return KG_ERROR_OK; } keyguard_error_t error_; + uint32_t user_id_; }; class VerifyRequest : public KeyguardMessage { public: VerifyRequest( + uint32_t user_id, SizedBuffer *enrolled_password_handle, SizedBuffer *provided_password_payload); VerifyRequest(); ~VerifyRequest(); + /** + * The currently enrolled password handle returned by Enroll. + */ const SizedBuffer *GetPasswordHandle() const { return &password_handle_; } + + /** + * The password provided by the user to be verified against the password handle + * above. + */ const SizedBuffer *GetProvidedPassword() const { return &provided_password_; } protected: @@ -75,10 +129,11 @@ private: class VerifyResponse : public KeyguardMessage { public: - VerifyResponse(SizedBuffer *verification_token); + VerifyResponse(uint32_t user_id, SizedBuffer *verification_token); VerifyResponse(); ~VerifyResponse(); + void SetVerificationToken(SizedBuffer *verification_token); const SizedBuffer *GetVerificationToken() const { return &verification_token_; } protected: @@ -92,10 +147,11 @@ private: class EnrollRequest : public KeyguardMessage { public: - EnrollRequest(SizedBuffer *provided_password); + EnrollRequest(uint32_t user_id, SizedBuffer *provided_password); EnrollRequest(); ~EnrollRequest(); + const SizedBuffer *GetProvidedPassword() const { return &provided_password_; } protected: @@ -108,10 +164,11 @@ private: class EnrollResponse : public KeyguardMessage { public: - EnrollResponse(SizedBuffer *enrolled_password_handle); + EnrollResponse(uint32_t user_id, SizedBuffer *enrolled_password_handle); EnrollResponse(); ~EnrollResponse(); + void SetEnrolledPasswordHandle(SizedBuffer *password_handle); const SizedBuffer *GetEnrolledPasswordHandle() const { return &enrolled_password_handle_; } protected: diff --git a/include/keyguard/keyguard_utils.h b/include/keyguard/keyguard_utils.h new file mode 100644 index 0000000..fd00248 --- /dev/null +++ b/include/keyguard/keyguard_utils.h @@ -0,0 +1,57 @@ +/* + * 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 GOOGLE_KEYGUARD_UTILS_H_ +#define GOOGLE_KEYGUARD_UTILS_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 + +/** + * Return the number of elements in array \p a. + */ +template <typename T, size_t N> inline size_t array_length(const T (&)[N]) { + return N; +} + +static int memcmp_s(const void* p1, const void* p2, size_t length) { + const uint8_t* s1 = static_cast<const uint8_t*>(p1); + const uint8_t* s2 = static_cast<const uint8_t*>(p2); + uint8_t result = 0; + while (length-- > 0) + result |= *s1++ ^ *s2++; + return result == 0 ? 0 : 1; +} + +}; +#endif //GOOGLE_KEYGUARD_UTILS_H_ diff --git a/keyguard.cpp b/keyguard.cpp new file mode 100644 index 0000000..95cfc73 --- /dev/null +++ b/keyguard.cpp @@ -0,0 +1,92 @@ +/* + * 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. + */ +#include <sys/time.h> + +#include <keyguard/google_keyguard.h> + +namespace keyguard { + +GoogleKeyguard::~GoogleKeyguard() { + if (password_key_) { + memset_s(password_key_.get(), 0, sizeof(password_key_.get()) / sizeof(password_key_[0])); + } +} + +void GoogleKeyguard::Enroll(const EnrollRequest &request, EnrollResponse *response) { + if (response == NULL) return; + + SizedBuffer enrolled_password; + const SizedBuffer *provided_password = request.GetProvidedPassword(); + if (provided_password == NULL || !provided_password->buffer) { + response->SetError(KG_ERROR_INVALID); + return; + } + enrolled_password.buffer = ComputeSignature(password_key_.get(), + provided_password->buffer.get(), provided_password->length, &enrolled_password.length); + response->SetEnrolledPasswordHandle(&enrolled_password); +} + +void GoogleKeyguard::Verify(const VerifyRequest &request, VerifyResponse *response) { + if (response == NULL) return; + + const SizedBuffer *enrolled_password = request.GetPasswordHandle(); + const SizedBuffer *provided_password = request.GetProvidedPassword(); + + + if (provided_password == NULL || !provided_password->buffer + || enrolled_password == NULL || !enrolled_password->buffer) { + response->SetError(KG_ERROR_INVALID); + return; + } + + SizedBuffer signed_provided_password; + signed_provided_password.buffer = ComputeSignature(password_key_.get(), + provided_password->buffer.get(), provided_password->length, + &signed_provided_password.length); + if (memcmp_s(enrolled_password->buffer.get(), signed_provided_password.buffer.get(), + enrolled_password->length) == 0) { + // Signature matches + SizedBuffer auth_token; + auth_token.buffer = MintAuthToken(request.GetUserId(), &auth_token.length); + response->SetVerificationToken(&auth_token); + } else { + response->SetError(KG_ERROR_INVALID); + } +} + +std::unique_ptr<uint8_t> GoogleKeyguard::MintAuthToken(uint32_t user_id, size_t *length) { + AuthToken *auth_token = new AuthToken; + SizedBuffer serialized_auth_token; + + struct timeval time; + gettimeofday(&time, NULL); + + auth_token->auth_token_size = sizeof(AuthToken) - + sizeof(auth_token->auth_token_tag) - sizeof(auth_token->auth_token_size); + auth_token->user_id = user_id; + auth_token->timestamp = static_cast<uint64_t>(time.tv_sec); + + size_t hash_len = (size_t)((uint8_t *)&auth_token->hmac_tag - (uint8_t *)auth_token); + size_t signature_len; + std::unique_ptr<uint8_t> signature = ComputeSignature(GetAuthTokenKey().get(), + reinterpret_cast<uint8_t *>(auth_token), hash_len, &signature_len); + + memcpy(&auth_token->hmac, signature.get(), sizeof(auth_token->hmac)); + if (length != NULL) *length = sizeof(AuthToken); + std::unique_ptr<uint8_t> result(reinterpret_cast<uint8_t *>(auth_token)); + return result; +} +} diff --git a/keyguard_messages.cpp b/keyguard_messages.cpp index 0c92e78..2b550c4 100644 --- a/keyguard_messages.cpp +++ b/keyguard_messages.cpp @@ -13,34 +13,20 @@ * 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. + * Methods for serializing/deserializing SizedBuffers */ -#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) { + +static inline size_t serialized_buffer_size(const SizedBuffer &buf) { return sizeof(uint32_t) + buf.length; } @@ -66,9 +52,10 @@ static inline keyguard_error_t read_from_buffer(const uint8_t **buffer, const ui return KG_ERROR_OK; } + size_t KeyguardMessage::GetSerializedSize() const { if (error_ == KG_ERROR_OK) { - return sizeof(uint32_t) + nonErrorSerializedSize(); + return 2 * sizeof(uint32_t) + nonErrorSerializedSize(); } else { return sizeof(uint32_t); } @@ -80,10 +67,11 @@ uint8_t *KeyguardMessage::Serialize() const { *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()]; + uint8_t *buf = new uint8_t[2*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)); + memcpy(buf + sizeof(uint32_t), &user_id_, sizeof(user_id_)); + nonErrorSerialize(buf + 2*sizeof(uint32_t)); return buf; } } @@ -93,7 +81,10 @@ keyguard_error_t KeyguardMessage::Deserialize(const uint8_t *payload, const uint 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); + payload += sizeof(uint32_t); if (error_ == KG_ERROR_OK) { + if (payload == end) return KG_ERROR_INVALID; + user_id_ = *((uint32_t *) payload); error_ = nonErrorDeserialize(payload + sizeof(uint32_t), end); } @@ -101,8 +92,9 @@ keyguard_error_t KeyguardMessage::Deserialize(const uint8_t *payload, const uint } -VerifyRequest::VerifyRequest(SizedBuffer *enrolled_password_handle, +VerifyRequest::VerifyRequest(uint32_t user_id, SizedBuffer *enrolled_password_handle, SizedBuffer *provided_password_payload) { + user_id_ = user_id; 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); @@ -126,7 +118,7 @@ VerifyRequest::~VerifyRequest() { } size_t VerifyRequest::nonErrorSerializedSize() const { - return buffer_size(password_handle_) + buffer_size(provided_password_); + return serialized_buffer_size(password_handle_) + serialized_buffer_size(provided_password_); } void VerifyRequest::nonErrorSerialize(uint8_t *buffer) const { @@ -153,7 +145,8 @@ keyguard_error_t VerifyRequest::nonErrorDeserialize(const uint8_t *payload, cons } -VerifyResponse::VerifyResponse(SizedBuffer *verification_token) { +VerifyResponse::VerifyResponse(uint32_t user_id, SizedBuffer *verification_token) { + user_id_ = user_id; verification_token_.buffer = std::move(verification_token->buffer); verification_token_.length = verification_token->length; } @@ -168,8 +161,13 @@ VerifyResponse::~VerifyResponse() { } } +void VerifyResponse::SetVerificationToken(SizedBuffer *verification_token) { + verification_token_.buffer = std::move(verification_token->buffer); + verification_token_.length = verification_token->length; +} + size_t VerifyResponse::nonErrorSerializedSize() const { - return buffer_size(verification_token_); + return serialized_buffer_size(verification_token_); } void VerifyResponse::nonErrorSerialize(uint8_t *buffer) const { @@ -184,7 +182,8 @@ keyguard_error_t VerifyResponse::nonErrorDeserialize(const uint8_t *payload, con return read_from_buffer(&payload, end, &verification_token_); } -EnrollRequest::EnrollRequest(SizedBuffer *provided_password) { +EnrollRequest::EnrollRequest(uint32_t user_id, SizedBuffer *provided_password) { + user_id_ = user_id; provided_password_.buffer = std::move(provided_password->buffer); provided_password_.length = provided_password->length; } @@ -201,7 +200,7 @@ EnrollRequest::~EnrollRequest() { } size_t EnrollRequest::nonErrorSerializedSize() const { - return buffer_size(provided_password_); + return serialized_buffer_size(provided_password_); } void EnrollRequest::nonErrorSerialize(uint8_t *buffer) const { @@ -217,7 +216,8 @@ keyguard_error_t EnrollRequest::nonErrorDeserialize(const uint8_t *payload, cons return read_from_buffer(&payload, end, &provided_password_); } -EnrollResponse::EnrollResponse(SizedBuffer *enrolled_password_handle) { +EnrollResponse::EnrollResponse(uint32_t user_id, SizedBuffer *enrolled_password_handle) { + user_id_ = user_id; enrolled_password_handle_.buffer = std::move(enrolled_password_handle->buffer); enrolled_password_handle_.length = enrolled_password_handle->length; } @@ -232,8 +232,13 @@ EnrollResponse::~EnrollResponse() { } } +void EnrollResponse::SetEnrolledPasswordHandle(SizedBuffer *enrolled_password_handle) { + enrolled_password_handle_.buffer = std::move(enrolled_password_handle->buffer); + enrolled_password_handle_.length = enrolled_password_handle->length; +} + size_t EnrollResponse::nonErrorSerializedSize() const { - return buffer_size(enrolled_password_handle_); + return serialized_buffer_size(enrolled_password_handle_); } void EnrollResponse::nonErrorSerialize(uint8_t *buffer) const { diff --git a/tests/Android.mk b/tests/Android.mk index 60fc50f..7b2f1f3 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -20,6 +20,8 @@ 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 +LOCAL_SHARED_LIBRARIES += libkeyguard +LOCAL_SRC_FILES := \ + keyguard_messages_test.cpp \ + keyguard_test.cpp include $(BUILD_NATIVE_TEST) diff --git a/tests/keyguard_messages_test.cpp b/tests/keyguard_messages_test.cpp index 77fbb99..dc579f7 100644 --- a/tests/keyguard_messages_test.cpp +++ b/tests/keyguard_messages_test.cpp @@ -30,6 +30,8 @@ using ::keyguard::VerifyResponse; using std::cout; using std::endl; +static const uint32_t USER_ID = 3857; + static SizedBuffer *make_buffer(size_t size) { SizedBuffer *result = new SizedBuffer; result->length = size; @@ -49,7 +51,7 @@ TEST(RoundTripTest, EnrollRequest) { SizedBuffer *provided_password = make_buffer(password_size); const SizedBuffer *deserialized_password; // create request, serialize, deserialize, and validate - EnrollRequest req(provided_password); + EnrollRequest req(USER_ID, provided_password); uint8_t *serialized_req = req.Serialize(); EnrollRequest deserialized_req; deserialized_req.Deserialize(serialized_req, serialized_req + req.GetSerializedSize()); @@ -59,6 +61,7 @@ TEST(RoundTripTest, EnrollRequest) { deserialized_req.GetError()); deserialized_password = deserialized_req.GetProvidedPassword(); + ASSERT_EQ(USER_ID, deserialized_req.GetUserId()); ASSERT_EQ((uint32_t) password_size, deserialized_password->length); ASSERT_EQ(0, memcmp(req.GetProvidedPassword()->buffer.get(), deserialized_password->buffer.get(), password_size)); } @@ -68,7 +71,7 @@ TEST(RoundTripTest, EnrollResponse) { SizedBuffer *enrolled_password = make_buffer(password_size); const SizedBuffer *deserialized_password; // create request, serialize, deserialize, and validate - EnrollResponse req(enrolled_password); + EnrollResponse req(USER_ID, enrolled_password); uint8_t *serialized_req = req.Serialize(); EnrollResponse deserialized_req; deserialized_req.Deserialize(serialized_req, serialized_req + req.GetSerializedSize()); @@ -78,17 +81,18 @@ TEST(RoundTripTest, EnrollResponse) { deserialized_req.GetError()); deserialized_password = deserialized_req.GetEnrolledPasswordHandle(); + ASSERT_EQ(USER_ID, deserialized_req.GetUserId()); 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; + const size_t password_size = 512; 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); + VerifyRequest req(USER_ID, password_handle, provided_password); uint8_t *serialized_req = req.Serialize(); VerifyRequest deserialized_req; deserialized_req.Deserialize(serialized_req, serialized_req + req.GetSerializedSize()); @@ -96,6 +100,7 @@ TEST(RoundTripTest, VerifyRequest) { ASSERT_EQ(keyguard::keyguard_error_t::KG_ERROR_OK, deserialized_req.GetError()); + ASSERT_EQ(USER_ID, deserialized_req.GetUserId()); 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)); @@ -110,7 +115,7 @@ TEST(RoundTripTest, VerifyResponse) { SizedBuffer *verification_token = make_buffer(password_size); const SizedBuffer *deserialized_password; // create request, serialize, deserialize, and validate - VerifyResponse req(verification_token); + VerifyResponse req(USER_ID, verification_token); uint8_t *serialized_req = req.Serialize(); VerifyResponse deserialized_req; deserialized_req.Deserialize(serialized_req, serialized_req + req.GetSerializedSize()); @@ -119,6 +124,7 @@ TEST(RoundTripTest, VerifyResponse) { ASSERT_EQ(keyguard::keyguard_error_t::KG_ERROR_OK, deserialized_req.GetError()); + ASSERT_EQ(USER_ID, deserialized_req.GetUserId()); 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)); diff --git a/tests/keyguard_test.cpp b/tests/keyguard_test.cpp new file mode 100644 index 0000000..8b733df --- /dev/null +++ b/tests/keyguard_test.cpp @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#include <gtest/gtest.h> + +#include <keyguard/google_keyguard.h> + +using ::keyguard::SizedBuffer; +using ::testing::Test; +using ::keyguard::EnrollRequest; +using ::keyguard::EnrollResponse; +using ::keyguard::VerifyRequest; +using ::keyguard::VerifyResponse; +using ::keyguard::GoogleKeyguard; +using ::keyguard::AuthToken; + +class FakeKeyguard : public GoogleKeyguard { +public: + FakeKeyguard() { + password_key_ = std::unique_ptr<uint8_t[]>(new uint8_t[16] { + 2, 34, 23, 43, 52, 25, 234, 22, 65, 24, 90, + 48, 5, 52, 62, 12 }); + } + +private: + std::unique_ptr<uint8_t[]> GetAuthTokenKey() const { + return std::unique_ptr<uint8_t[]>(new uint8_t[16] { + 2, 34, 23, 43, 52, 25, 234, 22, 65, 24, 90, + 48, 5, 52, 62, 12 }); + } + + std::unique_ptr<uint8_t> ComputeSignature(const uint8_t key[], + const uint8_t *message, const size_t length, size_t *signature_length) const { + const size_t signature_size = 16; + uint8_t *signature = new uint8_t[signature_size]; + memset(signature, 0, signature_size); + size_t len = length >= signature_size ? signature_size : length; + memcpy(signature, message, len); + if (signature_length != NULL) *signature_length = len; + return std::unique_ptr<uint8_t>(signature); + } +}; + +TEST(KeyguardTest, EnrollSuccess) { + FakeKeyguard keyguard; + SizedBuffer password; + EnrollResponse response; + + password.buffer = std::unique_ptr<uint8_t>(new uint8_t[16]); + password.length = 16; + memset(password.buffer.get(), 0, 16); + EnrollRequest request(0, &password); + + keyguard.Enroll(request, &response); + + ASSERT_EQ(::keyguard::keyguard_error_t::KG_ERROR_OK, response.GetError()); + ASSERT_EQ((size_t) 16, response.GetEnrolledPasswordHandle()->length); +} + +TEST(KeyguardTest, EnrollBogusData) { + FakeKeyguard keyguard; + SizedBuffer password; + EnrollResponse response; + + EnrollRequest request(0, &password); + + keyguard.Enroll(request, &response); + + ASSERT_EQ(::keyguard::keyguard_error_t::KG_ERROR_INVALID, response.GetError()); +} + +TEST(KeyguardTest, VerifySuccess) { + FakeKeyguard keyguard; + SizedBuffer provided_password; + SizedBuffer password_handle; + + provided_password.buffer = std::unique_ptr<uint8_t>(new uint8_t[16]); + provided_password.length = 16; + memset(provided_password.buffer.get(), 0, 16); + + password_handle.buffer = std::unique_ptr<uint8_t>(new uint8_t[16]); + password_handle.length = 16; + memset(password_handle.buffer.get(), 0, 16); + + VerifyRequest request(0, &password_handle, &provided_password); + VerifyResponse response; + + keyguard.Verify(request, &response); + + ASSERT_EQ(::keyguard::keyguard_error_t::KG_ERROR_OK, response.GetError()); + + AuthToken *auth_token = + reinterpret_cast<AuthToken *>(response.GetVerificationToken()->buffer.get()); + + ASSERT_EQ((uint8_t) 1, auth_token->auth_token_tag); + ASSERT_EQ((uint8_t) 2, auth_token->user_id_tag); + ASSERT_EQ((uint8_t) 3, auth_token->authenticator_id_tag); + ASSERT_EQ((uint8_t) 4, auth_token->timestamp_tag); + + ASSERT_EQ((uint32_t)0, auth_token->user_id); + ASSERT_EQ((uint32_t)0, auth_token->authenticator_id); +} + +TEST(KeyguardTest, VerifyBogusData) { + FakeKeyguard keyguard; + SizedBuffer provided_password; + SizedBuffer password_handle; + VerifyResponse response; + + VerifyRequest request(0, &provided_password, &password_handle); + + keyguard.Verify(request, &response); + + ASSERT_EQ(::keyguard::keyguard_error_t::KG_ERROR_INVALID, response.GetError()); +} |