Project import
diff --git a/keymaster/.clang-format b/keymaster/.clang-format new file mode 100644 index 0000000..5747e19 --- /dev/null +++ b/keymaster/.clang-format
@@ -0,0 +1,10 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +BreakBeforeBraces: Attach +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +IndentCaseLabels: false +ColumnLimit: 100 +PointerBindsToType: true +SpacesBeforeTrailingComments: 2
diff --git a/keymaster/Android.mk b/keymaster/Android.mk new file mode 100644 index 0000000..233ed95 --- /dev/null +++ b/keymaster/Android.mk
@@ -0,0 +1,180 @@ +# Copyright (C) 2014 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) + +### +# libkeymaster_messages contains just the code necessary to communicate with a +# AndroidKeymaster implementation, e.g. one running in TrustZone. +## +include $(CLEAR_VARS) +LOCAL_MODULE:= libkeymaster_messages +LOCAL_SRC_FILES:= \ + android_keymaster_messages.cpp \ + android_keymaster_utils.cpp \ + authorization_set.cpp \ + keymaster_tags.cpp \ + logger.cpp \ + serializable.cpp +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include +LOCAL_CFLAGS = -Wall -Werror -Wunused -DKEYMASTER_NAME_TAGS +LOCAL_CLANG := true +# TODO(krasin): reenable coverage flags, when the new Clang toolchain is released. +# Currently, if enabled, these flags will cause an internal error in Clang. +LOCAL_CLANG_CFLAGS += -fno-sanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp +LOCAL_MODULE_TAGS := optional +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +include $(BUILD_SHARED_LIBRARY) + +### +# libkeymaster1 contains almost everything needed for a keymaster1 +# implementation, lacking only a subclass of the (abstract) KeymasterContext +# class to provide environment-specific services and a wrapper to translate from +# the function-based keymaster HAL API to the message-based AndroidKeymaster API. +### +include $(CLEAR_VARS) +LOCAL_MODULE:= libkeymaster1 +LOCAL_SRC_FILES:= \ + aes_key.cpp \ + aes_operation.cpp \ + android_keymaster.cpp \ + android_keymaster_messages.cpp \ + android_keymaster_utils.cpp \ + asymmetric_key.cpp \ + asymmetric_key_factory.cpp \ + attestation_record.cpp \ + auth_encrypted_key_blob.cpp \ + ec_key.cpp \ + ec_key_factory.cpp \ + ecdsa_operation.cpp \ + ecies_kem.cpp \ + hkdf.cpp \ + hmac.cpp \ + hmac_key.cpp \ + hmac_operation.cpp \ + integrity_assured_key_blob.cpp \ + iso18033kdf.cpp \ + kdf.cpp \ + key.cpp \ + keymaster_enforcement.cpp \ + nist_curve_key_exchange.cpp \ + ocb.c \ + ocb_utils.cpp \ + openssl_err.cpp \ + openssl_utils.cpp \ + operation.cpp \ + operation_table.cpp \ + rsa_key.cpp \ + rsa_key_factory.cpp \ + rsa_operation.cpp \ + symmetric_key.cpp +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include +LOCAL_SHARED_LIBRARIES := libcrypto libkeymaster_messages +LOCAL_CFLAGS = -Wall -Werror -Wunused +LOCAL_CLANG := true +LOCAL_CLANG_CFLAGS += -Wno-error=unused-const-variable -Wno-error=unused-private-field +# TODO(krasin): reenable coverage flags, when the new Clang toolchain is released. +# Currently, if enabled, these flags will cause an internal error in Clang. +LOCAL_CLANG_CFLAGS += -fno-sanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp +# Ignore benigh warnings for now. +LOCAL_CLANG_CFLAGS += -Wno-error=unused-private-field +LOCAL_MODULE_TAGS := optional +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +include $(BUILD_SHARED_LIBRARY) + + +### +# libsoftkeymaster provides a software-based keymaster HAL implementation. +# This is used by keystore as a fallback for when the hardware keymaster does +# not support the request. +### +include $(CLEAR_VARS) +LOCAL_MODULE := libsoftkeymasterdevice +LOCAL_SRC_FILES := \ + ec_keymaster0_key.cpp \ + ec_keymaster1_key.cpp \ + ecdsa_keymaster1_operation.cpp \ + keymaster0_engine.cpp \ + keymaster1_engine.cpp \ + rsa_keymaster0_key.cpp \ + rsa_keymaster1_key.cpp \ + rsa_keymaster1_operation.cpp \ + soft_keymaster_context.cpp \ + soft_keymaster_device.cpp \ + soft_keymaster_logger.cpp +LOCAL_C_INCLUDES := \ + system/security/keystore \ + $(LOCAL_PATH)/include +LOCAL_CFLAGS = -Wall -Werror -Wunused +LOCAL_CLANG := true +LOCAL_CLANG_CFLAGS += -Wno-error=unused-const-variable -Wno-error=unused-private-field +# TODO(krasin): reenable coverage flags, when the new Clang toolchain is released. +# Currently, if enabled, these flags will cause an internal error in Clang. +LOCAL_CLANG_CFLAGS += -fno-sanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp +LOCAL_SHARED_LIBRARIES := libkeymaster_messages libkeymaster1 liblog libcrypto +LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +include $(BUILD_SHARED_LIBRARY) + +### +# libkeymasterfiles is an empty library that exports all of the files in keymaster as includes. +### +include $(CLEAR_VARS) +LOCAL_MODULE := libkeymasterfiles +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(LOCAL_PATH) \ + $(LOCAL_PATH)/include +LOCAL_MODULE_TAGS := optional +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +include $(BUILD_STATIC_LIBRARY) + +# Unit tests for libkeymaster +include $(CLEAR_VARS) +LOCAL_MODULE := keymaster_tests +LOCAL_SRC_FILES := \ + android_keymaster_messages_test.cpp \ + android_keymaster_test.cpp \ + android_keymaster_test_utils.cpp \ + attestation_record_test.cpp \ + authorization_set_test.cpp \ + hkdf_test.cpp \ + hmac_test.cpp \ + kdf1_test.cpp \ + kdf2_test.cpp \ + kdf_test.cpp \ + key_blob_test.cpp \ + keymaster_enforcement_test.cpp + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include +LOCAL_CFLAGS = -Wall -Werror -Wunused -DKEYMASTER_NAME_TAGS +LOCAL_CLANG := true +LOCAL_CLANG_CFLAGS += -Wno-error=unused-const-variable -Wno-error=unused-private-field +# TODO(krasin): reenable coverage flags, when the new Clang toolchain is released. +# Currently, if enabled, these flags will cause an internal error in Clang. +LOCAL_CLANG_CFLAGS += -fno-sanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp +LOCAL_MODULE_TAGS := tests +LOCAL_SHARED_LIBRARIES := \ + libsoftkeymasterdevice \ + libkeymaster_messages \ + libkeymaster1 \ + libcrypto \ + libsoftkeymaster +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +include $(BUILD_NATIVE_TEST)
diff --git a/keymaster/List.h b/keymaster/List.h new file mode 100644 index 0000000..403cd7f --- /dev/null +++ b/keymaster/List.h
@@ -0,0 +1,332 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Templated list class. Normally we'd use STL, but we don't have that. +// This class mimics STL's interfaces. +// +// Objects are copied into the list with the '=' operator or with copy- +// construction, so if the compiler's auto-generated versions won't work for +// you, define your own. +// +// The only class you want to use from here is "List". +// +#ifndef _LIBS_UTILS_LIST_H +#define _LIBS_UTILS_LIST_H + +#include <stddef.h> +#include <stdint.h> + +namespace android { + +/* + * Doubly-linked list. Instantiate with "List<MyClass> myList". + * + * Objects added to the list are copied using the assignment operator, + * so this must be defined. + */ +template<typename T> +class List +{ +protected: + /* + * One element in the list. + */ + class _Node { + public: + explicit _Node(const T& val) : mVal(val) {} + ~_Node() {} + inline T& getRef() { return mVal; } + inline const T& getRef() const { return mVal; } + inline _Node* getPrev() const { return mpPrev; } + inline _Node* getNext() const { return mpNext; } + inline void setVal(const T& val) { mVal = val; } + inline void setPrev(_Node* ptr) { mpPrev = ptr; } + inline void setNext(_Node* ptr) { mpNext = ptr; } + private: + friend class List; + friend class _ListIterator; + T mVal; + _Node* mpPrev; + _Node* mpNext; + }; + + /* + * Iterator for walking through the list. + */ + + template <typename TYPE> + struct CONST_ITERATOR { + typedef _Node const * NodePtr; + typedef const TYPE Type; + }; + + template <typename TYPE> + struct NON_CONST_ITERATOR { + typedef _Node* NodePtr; + typedef TYPE Type; + }; + + template< + typename U, + template <class> class Constness + > + class _ListIterator { + typedef _ListIterator<U, Constness> _Iter; + typedef typename Constness<U>::NodePtr _NodePtr; + typedef typename Constness<U>::Type _Type; + + explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {} + + public: + _ListIterator() {} + _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {} + ~_ListIterator() {} + + // this will handle conversions from iterator to const_iterator + // (and also all convertible iterators) + // Here, in this implementation, the iterators can be converted + // if the nodes can be converted + template<typename V> explicit + _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {} + + + /* + * Dereference operator. Used to get at the juicy insides. + */ + _Type& operator*() const { return mpNode->getRef(); } + _Type* operator->() const { return &(mpNode->getRef()); } + + /* + * Iterator comparison. + */ + inline bool operator==(const _Iter& right) const { + return mpNode == right.mpNode; } + + inline bool operator!=(const _Iter& right) const { + return mpNode != right.mpNode; } + + /* + * handle comparisons between iterator and const_iterator + */ + template<typename OTHER> + inline bool operator==(const OTHER& right) const { + return mpNode == right.mpNode; } + + template<typename OTHER> + inline bool operator!=(const OTHER& right) const { + return mpNode != right.mpNode; } + + /* + * Incr/decr, used to move through the list. + */ + inline _Iter& operator++() { // pre-increment + mpNode = mpNode->getNext(); + return *this; + } + const _Iter operator++(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getNext(); + return tmp; + } + inline _Iter& operator--() { // pre-increment + mpNode = mpNode->getPrev(); + return *this; + } + const _Iter operator--(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getPrev(); + return tmp; + } + + inline _NodePtr getNode() const { return mpNode; } + + _NodePtr mpNode; /* should be private, but older gcc fails */ + private: + friend class List; + }; + +public: + List() { + prep(); + } + List(const List<T>& src) { // copy-constructor + prep(); + insert(begin(), src.begin(), src.end()); + } + virtual ~List() { + clear(); + delete[] (unsigned char*) mpMiddle; + } + + typedef _ListIterator<T, NON_CONST_ITERATOR> iterator; + typedef _ListIterator<T, CONST_ITERATOR> const_iterator; + + List<T>& operator=(const List<T>& right); + + /* returns true if the list is empty */ + inline bool empty() const { return mpMiddle->getNext() == mpMiddle; } + + /* return #of elements in list */ + size_t size() const { + return size_t(distance(begin(), end())); + } + + /* + * Return the first element or one past the last element. The + * _Node* we're returning is converted to an "iterator" by a + * constructor in _ListIterator. + */ + inline iterator begin() { + return iterator(mpMiddle->getNext()); + } + inline const_iterator begin() const { + return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); + } + inline iterator end() { + return iterator(mpMiddle); + } + inline const_iterator end() const { + return const_iterator(const_cast<_Node const*>(mpMiddle)); + } + + /* add the object to the head or tail of the list */ + void push_front(const T& val) { insert(begin(), val); } + void push_back(const T& val) { insert(end(), val); } + + /* insert before the current node; returns iterator at new node */ + iterator insert(iterator posn, const T& val) + { + _Node* newNode = new _Node(val); // alloc & copy-construct + newNode->setNext(posn.getNode()); + newNode->setPrev(posn.getNode()->getPrev()); + posn.getNode()->getPrev()->setNext(newNode); + posn.getNode()->setPrev(newNode); + return iterator(newNode); + } + + /* insert a range of elements before the current node */ + void insert(iterator posn, const_iterator first, const_iterator last) { + for ( ; first != last; ++first) + insert(posn, *first); + } + + /* remove one entry; returns iterator at next node */ + iterator erase(iterator posn) { + _Node* pNext = posn.getNode()->getNext(); + _Node* pPrev = posn.getNode()->getPrev(); + pPrev->setNext(pNext); + pNext->setPrev(pPrev); + delete posn.getNode(); + return iterator(pNext); + } + + /* remove a range of elements */ + iterator erase(iterator first, iterator last) { + while (first != last) + erase(first++); // don't erase than incr later! + return iterator(last); + } + + /* remove all contents of the list */ + void clear() { + _Node* pCurrent = mpMiddle->getNext(); + _Node* pNext; + + while (pCurrent != mpMiddle) { + pNext = pCurrent->getNext(); + delete pCurrent; + pCurrent = pNext; + } + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * Measure the distance between two iterators. On exist, "first" + * will be equal to "last". The iterators must refer to the same + * list. + * + * FIXME: This is actually a generic iterator function. It should be a + * template function at the top-level with specializations for things like + * vector<>, which can just do pointer math). Here we limit it to + * _ListIterator of the same type but different constness. + */ + template< + typename U, + template <class> class CL, + template <class> class CR + > + ptrdiff_t distance( + _ListIterator<U, CL> first, _ListIterator<U, CR> last) const + { + ptrdiff_t count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + +private: + /* + * I want a _Node but don't need it to hold valid data. More + * to the point, I don't want T's constructor to fire, since it + * might have side-effects or require arguments. So, we do this + * slightly uncouth storage alloc. + */ + void prep() { + mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * This node plays the role of "pointer to head" and "pointer to tail". + * It sits in the middle of a circular list of nodes. The iterator + * runs around the circle until it encounters this one. + */ + _Node* mpMiddle; +}; + +/* + * Assignment operator. + * + * The simplest way to do this would be to clear out the target list and + * fill it with the source. However, we can speed things along by + * re-using existing elements. + */ +template<class T> +List<T>& List<T>::operator=(const List<T>& right) +{ + if (this == &right) + return *this; // self-assignment + iterator firstDst = begin(); + iterator lastDst = end(); + const_iterator firstSrc = right.begin(); + const_iterator lastSrc = right.end(); + while (firstSrc != lastSrc && firstDst != lastDst) + *firstDst++ = *firstSrc++; + if (firstSrc == lastSrc) // ran out of elements in source? + erase(firstDst, lastDst); // yes, erase any extras + else + insert(lastDst, firstSrc, lastSrc); // copy remaining over + return *this; +} + +}; // namespace android + +#endif // _LIBS_UTILS_LIST_H
diff --git a/keymaster/MODULE_LICENSE_APACHE2 b/keymaster/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/keymaster/MODULE_LICENSE_APACHE2
diff --git a/keymaster/Makefile b/keymaster/Makefile new file mode 100644 index 0000000..fc8a540 --- /dev/null +++ b/keymaster/Makefile
@@ -0,0 +1,381 @@ +##### +# Local unit test Makefile +# +# This makefile builds and runs the keymaster unit tests locally on the development +# machine, not on an Android device. Android.mk builds the same tests into the +# "keymaster_tests" binary for execution on-device, but this Makefile runs them locally, +# for a very fast edit/build/test development cycle. +# +# To build and run these tests, one pre-requisite must be manually installed: BoringSSL. +# This Makefile expects to find BoringSSL in a directory adjacent to $ANDROID_BUILD_TOP. +# To get and build it, first install the Ninja build tool (e.g. apt-get install +# ninja-build), then do: +# +# cd $ANDROID_BUILD_TOP/.. +# git clone https://boringssl.googlesource.com/boringssl +# cd boringssl +# mdkir build +# cd build +# cmake -GNinja .. +# ninja +# +# Then return to $ANDROID_BUILD_TOP/system/keymaster and run "make". +##### + +BASE=../.. +SUBS=system/core \ + hardware/libhardware \ + external/gtest \ + system/security/softkeymaster \ + system/security/keystore +GTEST=$(BASE)/external/gtest + +INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \ + -I $(BASE)/libnativehelper/include/nativehelper \ + -I $(GTEST) -Iinclude -I$(BASE)/../boringssl/include + +ifdef FORCE_32_BIT +ARCH_FLAGS = -m32 +endif + +ifdef USE_CLANG +CC=/usr/bin/clang +CXX=/usr/bin/clang +CXXFLAGS +=-std=c++11 -DKEYMASTER_CLANG_TEST_BUILD +CFLAGS += -DKEYMASTER_CLANG_TEST_BUILD +else +CXXFLAGS +=-std=c++0x -fprofile-arcs -ftest-coverage +CFLAGS += -fprofile-arcs -ftest-coverage +endif + +LDFLAGS += $(ARCH_FLAGS) +CPPFLAGS = $(INCLUDES) -g -O0 -MD -MP +CXXFLAGS += -Wall -Werror -Wno-unused -Winit-self -Wpointer-arith -Wunused-parameter \ + -Werror=sign-compare -Werror=return-type -fno-permissive \ + -Wno-deprecated-declarations -fno-exceptions -DKEYMASTER_NAME_TAGS $(ARCH_FLAGS) +CFLAGS += $(ARCH_FLAGS) + +# Uncomment to enable debug logging. +# CXXFLAGS += -DDEBUG + +LDLIBS=-L$(BASE)/../boringssl/build/crypto -lcrypto -lpthread -lstdc++ -lgcov + +CPPSRCS=\ + aes_key.cpp \ + aes_operation.cpp \ + android_keymaster.cpp \ + android_keymaster_messages.cpp \ + android_keymaster_messages_test.cpp \ + android_keymaster_test.cpp \ + android_keymaster_test_utils.cpp \ + android_keymaster_utils.cpp \ + asymmetric_key.cpp \ + asymmetric_key_factory.cpp \ + attestation_record.cpp \ + attestation_record_test.cpp \ + auth_encrypted_key_blob.cpp \ + authorization_set.cpp \ + authorization_set_test.cpp \ + ec_key.cpp \ + ec_key_factory.cpp \ + ec_keymaster0_key.cpp \ + ec_keymaster1_key.cpp \ + ecdsa_keymaster1_operation.cpp \ + ecdsa_operation.cpp \ + ecies_kem.cpp \ + ecies_kem_test.cpp \ + gtest_main.cpp \ + hkdf.cpp \ + hkdf_test.cpp \ + hmac.cpp \ + hmac_key.cpp \ + hmac_operation.cpp \ + hmac_test.cpp \ + integrity_assured_key_blob.cpp \ + iso18033kdf.cpp \ + kdf.cpp \ + kdf_test.cpp \ + kdf1_test.cpp \ + kdf2_test.cpp \ + key.cpp \ + key_blob_test.cpp \ + keymaster0_engine.cpp \ + keymaster1_engine.cpp \ + keymaster_enforcement.cpp \ + keymaster_enforcement_test.cpp \ + keymaster_tags.cpp \ + logger.cpp \ + nist_curve_key_exchange.cpp \ + nist_curve_key_exchange_test.cpp \ + ocb_utils.cpp \ + openssl_err.cpp \ + openssl_utils.cpp \ + operation.cpp \ + operation_table.cpp \ + rsa_key.cpp \ + rsa_key_factory.cpp \ + rsa_keymaster0_key.cpp \ + rsa_keymaster1_key.cpp \ + rsa_keymaster1_operation.cpp \ + rsa_operation.cpp \ + serializable.cpp \ + soft_keymaster_context.cpp \ + soft_keymaster_device.cpp \ + symmetric_key.cpp + +CCSRCS=$(GTEST)/src/gtest-all.cc +CSRCS=ocb.c + +OBJS=$(CPPSRCS:.cpp=.o) $(CCSRCS:.cc=.o) $(CSRCS:.c=.o) +DEPS=$(CPPSRCS:.cpp=.d) $(CCSRCS:.cc=.d) $(CSRCS:.c=.d) + +BINARIES = \ + android_keymaster_messages_test \ + android_keymaster_test \ + attestation_record_test \ + authorization_set_test \ + ecies_kem_test \ + hkdf_test \ + hmac_test \ + kdf_test \ + kdf1_test \ + kdf2_test \ + key_blob_test \ + keymaster_enforcement_test \ + nist_curve_key_exchange_test + +.PHONY: coverage memcheck massif clean run + +%.run: % + ./$< + touch $@ + +run: $(BINARIES:=.run) + +coverage: coverage.info + genhtml coverage.info --output-directory coverage + +coverage.info: run + lcov --capture --directory=. --output-file coverage.info + +%.coverage : % + $(MAKE) clean && $(MAKE) $< + ./$< + lcov --capture --directory=. --output-file coverage.info + genhtml coverage.info --output-directory coverage + +#UNINIT_OPTS=--track-origins=yes +UNINIT_OPTS=--undef-value-errors=no + +MEMCHECK_OPTS=--leak-check=full \ + --show-reachable=yes \ + --vgdb=full \ + $(UNINIT_OPTS) \ + --error-exitcode=1 \ + --suppressions=valgrind.supp \ + --gen-suppressions=all + +MASSIF_OPTS=--tool=massif \ + --stacks=yes + +%.memcheck : % + valgrind $(MEMCHECK_OPTS) ./$< && \ + touch $@ + +%.massif : % + valgrind $(MASSIF_OPTS) --massif-out-file=$@ ./$< + +memcheck: $(BINARIES:=.memcheck) + +massif: $(BINARIES:=.massif) + +GTEST_OBJS = $(GTEST)/src/gtest-all.o gtest_main.o + +hmac_test: hmac_test.o \ + android_keymaster_test_utils.o \ + android_keymaster_utils.o \ + authorization_set.o \ + hmac.o \ + keymaster_tags.o \ + logger.o \ + serializable.o \ + $(GTEST_OBJS) + +hkdf_test: hkdf_test.o \ + android_keymaster_test_utils.o \ + android_keymaster_utils.o \ + authorization_set.o \ + hkdf.o \ + hmac.o \ + kdf.o \ + keymaster_tags.o \ + logger.o \ + serializable.o \ + $(GTEST_OBJS) + +kdf_test: kdf_test.o \ + android_keymaster_utils.o \ + kdf.o \ + logger.o \ + serializable.o \ + $(GTEST_OBJS) + +kdf1_test: kdf1_test.o \ + android_keymaster_test_utils.o \ + android_keymaster_utils.o \ + authorization_set.o \ + iso18033kdf.o \ + kdf.o \ + keymaster_tags.o \ + logger.o \ + serializable.o \ + $(GTEST_OBJS) + +kdf2_test: kdf2_test.o \ + android_keymaster_test_utils.o \ + android_keymaster_utils.o \ + authorization_set.o \ + iso18033kdf.o \ + kdf.o \ + keymaster_tags.o \ + logger.o \ + serializable.o \ + $(GTEST_OBJS) + +nist_curve_key_exchange_test: nist_curve_key_exchange_test.o \ + android_keymaster_test_utils.o \ + authorization_set.o \ + keymaster_tags.o \ + logger.o \ + nist_curve_key_exchange.o \ + openssl_err.o \ + openssl_utils.o \ + serializable.o \ + $(GTEST_OBJS) + +ecies_kem_test: ecies_kem_test.o \ + android_keymaster_utils.o \ + android_keymaster_test_utils.o \ + authorization_set.o \ + ecies_kem.o \ + hkdf.o \ + hmac.o \ + kdf.o \ + keymaster_tags.o \ + logger.o \ + nist_curve_key_exchange.o \ + openssl_err.o \ + openssl_utils.o \ + serializable.o \ + $(GTEST_OBJS) + +authorization_set_test: authorization_set_test.o \ + android_keymaster_test_utils.o \ + authorization_set.o \ + keymaster_tags.o \ + logger.o \ + serializable.o \ + $(GTEST_OBJS) + +key_blob_test: key_blob_test.o \ + android_keymaster_test_utils.o \ + android_keymaster_utils.o \ + auth_encrypted_key_blob.o \ + authorization_set.o \ + integrity_assured_key_blob.o \ + keymaster_tags.o \ + logger.o \ + ocb.o \ + ocb_utils.o \ + openssl_err.o \ + serializable.o \ + $(GTEST_OBJS) + +android_keymaster_messages_test: android_keymaster_messages_test.o \ + android_keymaster_messages.o \ + android_keymaster_test_utils.o \ + android_keymaster_utils.o \ + authorization_set.o \ + keymaster_tags.o \ + logger.o \ + serializable.o \ + $(GTEST_OBJS) + +android_keymaster_test: android_keymaster_test.o \ + aes_key.o \ + aes_operation.o \ + android_keymaster.o \ + android_keymaster_messages.o \ + android_keymaster_test_utils.o \ + android_keymaster_utils.o \ + asymmetric_key.o \ + asymmetric_key_factory.o \ + attestation_record.o \ + auth_encrypted_key_blob.o \ + authorization_set.o \ + ec_key.o \ + ec_key_factory.o \ + ec_keymaster0_key.o \ + ec_keymaster1_key.o \ + ecdsa_keymaster1_operation.o \ + ecdsa_operation.o \ + hmac_key.o \ + hmac_operation.o \ + integrity_assured_key_blob.o \ + key.o \ + keymaster0_engine.o \ + keymaster1_engine.o \ + keymaster_enforcement.o \ + keymaster_tags.o \ + logger.o \ + ocb.o \ + ocb_utils.o \ + openssl_err.o \ + openssl_utils.o \ + operation.o \ + operation_table.o \ + rsa_key.o \ + rsa_key_factory.o \ + rsa_keymaster0_key.o \ + rsa_keymaster1_key.o \ + rsa_keymaster1_operation.o \ + rsa_operation.o \ + serializable.o \ + soft_keymaster_context.o \ + soft_keymaster_device.o \ + symmetric_key.o \ + $(BASE)/system/security/softkeymaster/keymaster_openssl.o \ + $(BASE)/system/security/keystore/keyblob_utils.o \ + $(GTEST_OBJS) + +keymaster_enforcement_test: keymaster_enforcement_test.o \ + android_keymaster_messages.o \ + android_keymaster_test_utils.o \ + android_keymaster_utils.o \ + authorization_set.o \ + keymaster_enforcement.o \ + keymaster_tags.o \ + logger.o \ + serializable.o \ + $(GTEST_OBJS) + +attestation_record_test: attestation_record_test.o \ + android_keymaster_test_utils.o \ + attestation_record.o \ + authorization_set.o \ + keymaster_tags.o \ + logger.o \ + openssl_err.o \ + serializable.o \ + $(GTEST_OBJS) + +$(GTEST)/src/gtest-all.o: CXXFLAGS:=$(subst -Wmissing-declarations,,$(CXXFLAGS)) + +clean: + rm -f $(OBJS) $(DEPS) $(BINARIES) \ + $(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=.massif) \ + *gcov *gcno *gcda coverage.info + rm -rf coverage + +-include $(CPPSRCS:.cpp=.d) +-include $(CCSRCS:.cc=.d)
diff --git a/keymaster/NOTICE b/keymaster/NOTICE new file mode 100644 index 0000000..34bdaf1 --- /dev/null +++ b/keymaster/NOTICE
@@ -0,0 +1,190 @@ + + Copyright (c) 2005-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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS +
diff --git a/keymaster/ae.h b/keymaster/ae.h new file mode 100644 index 0000000..864d349 --- /dev/null +++ b/keymaster/ae.h
@@ -0,0 +1,164 @@ +/* --------------------------------------------------------------------------- + * + * AEAD API 0.12 - 23-MAY-2012 + * + * This file gives an interface appropriate for many authenticated + * encryption with associated data (AEAD) implementations. It does not try + * to accommodate all possible options or limitations that an implementation + * might have -- you should consult the documentation of your chosen + * implementation to find things like RFC 5116 constants, alignment + * requirements, whether the incremental interface is supported, etc. + * + * This file is in the public domain. It is provided "as is", without + * warranty of any kind. Use at your own risk. + * + * Comments are welcome: Ted Krovetz <ted@krovetz>. + * + * ------------------------------------------------------------------------ */ + +#ifndef _AE_H_ +#define _AE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* -------------------------------------------------------------------------- + * + * Constants + * + * ----------------------------------------------------------------------- */ + +/* Return status codes: Negative return values indicate an error occurred. + * For full explanations of error values, consult the implementation's + * documentation. */ +#define AE_SUCCESS (0) /* Indicates successful completion of call */ +#define AE_INVALID (-1) /* Indicates bad tag during decryption */ +#define AE_NOT_SUPPORTED (-2) /* Indicates unsupported option requested */ + +/* Flags: When data can be processed "incrementally", these flags are used + * to indicate whether the submitted data is the last or not. */ +#define AE_FINALIZE (1) /* This is the last of data */ +#define AE_PENDING (0) /* More data of is coming */ + +/* -------------------------------------------------------------------------- + * + * AEAD opaque structure definition + * + * ----------------------------------------------------------------------- */ + +typedef struct _ae_ctx ae_ctx; + +/* -------------------------------------------------------------------------- + * + * Data Structure Routines + * + * ----------------------------------------------------------------------- */ + +ae_ctx* ae_allocate(void* misc); /* Allocate ae_ctx, set optional ptr */ +void ae_free(ae_ctx* ctx); /* Deallocate ae_ctx struct */ +int ae_clear(ae_ctx* ctx); /* Undo initialization */ +int ae_ctx_sizeof(void); /* Return sizeof(ae_ctx) */ +/* ae_allocate() allocates an ae_ctx structure, but does not initialize it. + * ae_free() deallocates an ae_ctx structure, but does not zero it. + * ae_clear() zeroes sensitive values associated with an ae_ctx structure + * and deallocates any auxiliary structures allocated during ae_init(). + * ae_ctx_sizeof() returns sizeof(ae_ctx), to aid in any static allocations. + */ + +/* -------------------------------------------------------------------------- + * + * AEAD Routines + * + * ----------------------------------------------------------------------- */ + +int ae_init(ae_ctx* ctx, const void* key, int key_len, int nonce_len, int tag_len); +/* -------------------------------------------------------------------------- + * + * Initialize an ae_ctx context structure. + * + * Parameters: + * ctx - Pointer to an ae_ctx structure to be initialized + * key - Pointer to user-supplied key + * key_len - Length of key supplied, in bytes + * nonce_len - Length of nonces to be used for this key, in bytes + * tag_len - Length of tags to be produced for this key, in bytes + * + * Returns: + * AE_SUCCESS - Success. Ctx ready for use. + * AE_NOT_SUPPORTED - An unsupported length was supplied. Ctx is untouched. + * Otherwise - Error. Check implementation documentation for codes. + * + * ----------------------------------------------------------------------- */ + +int ae_encrypt(ae_ctx* ctx, const void* nonce, const void* pt, int pt_len, const void* ad, + int ad_len, void* ct, void* tag, int final); +/* -------------------------------------------------------------------------- + * + * Encrypt plaintext; provide for authentication of ciphertext/associated data. + * + * Parameters: + * ctx - Pointer to an ae_ctx structure initialized by ae_init. + * nonce - Pointer to a nonce_len (defined in ae_init) byte nonce. + * pt - Pointer to plaintext bytes to be encrypted. + * pt_len - number of bytes pointed to by pt. + * ad - Pointer to associated data. + * ad_len - number of bytes pointed to by ad. + * ct - Pointer to buffer to receive ciphertext encryption. + * tag - Pointer to receive authentication tag; or NULL + * if tag is to be bundled into the ciphertext. + * final - Non-zero if this call completes the plaintext being encrypted. + * + * If nonce!=NULL then a message is being initiated. If final!=0 + * then a message is being finalized. If final==0 or nonce==NULL + * then the incremental interface is being used. If nonce!=NULL and + * ad_len<0, then use same ad as last message. + * + * Returns: + * non-negative - Number of bytes written to ct. + * AE_NOT_SUPPORTED - Usage mode unsupported (eg, incremental and/or sticky). + * Otherwise - Error. Check implementation documentation for codes. + * + * ----------------------------------------------------------------------- */ + +int ae_decrypt(ae_ctx* ctx, const void* nonce, const void* ct, int ct_len, const void* ad, + int ad_len, void* pt, const void* tag, int final); +/* -------------------------------------------------------------------------- + * + * Decrypt ciphertext; provide authenticity of plaintext and associated data. + * + * Parameters: + * ctx - Pointer to an ae_ctx structure initialized by ae_init. + * nonce - Pointer to a nonce_len (defined in ae_init) byte nonce. + * ct - Pointer to ciphertext bytes to be decrypted. + * ct_len - number of bytes pointed to by ct. + * ad - Pointer to associated data. + * ad_len - number of bytes pointed to by ad. + * pt - Pointer to buffer to receive plaintext decryption. + * tag - Pointer to tag_len (defined in ae_init) bytes; or NULL + * if tag is bundled into the ciphertext. Non-NULL tag is only + * read when final is non-zero. + * final - Non-zero if this call completes the ciphertext being decrypted. + * + * If nonce!=NULL then "ct" points to the start of a ciphertext. If final!=0 + * then "in" points to the final piece of ciphertext. If final==0 or nonce== + * NULL then the incremental interface is being used. If nonce!=NULL and + * ad_len<0, then use same ad as last message. + * + * Returns: + * non-negative - Number of bytes written to pt. + * AE_INVALID - Authentication failure. + * AE_NOT_SUPPORTED - Usage mode unsupported (eg, incremental and/or sticky). + * Otherwise - Error. Check implementation documentation for codes. + * + * NOTE !!! NOTE !!! -- The ciphertext should be assumed possibly inauthentic + * until it has been completely written and it is + * verified that this routine did not return AE_INVALID. + * + * ----------------------------------------------------------------------- */ + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif + +#endif /* _AE_H_ */
diff --git a/keymaster/aes_key.cpp b/keymaster/aes_key.cpp new file mode 100644 index 0000000..6b784b7 --- /dev/null +++ b/keymaster/aes_key.cpp
@@ -0,0 +1,94 @@ +/* + * Copyright 2014 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 "aes_key.h" + +#include <assert.h> + +#include <new> + +#include <openssl/err.h> +#include <openssl/rand.h> + +#include "aes_operation.h" + +namespace keymaster { + +AesEncryptionOperationFactory encrypt_factory; +AesDecryptionOperationFactory decrypt_factory; + +OperationFactory* AesKeyFactory::GetOperationFactory(keymaster_purpose_t purpose) const { + switch (purpose) { + case KM_PURPOSE_ENCRYPT: + return &encrypt_factory; + case KM_PURPOSE_DECRYPT: + return &decrypt_factory; + default: + return nullptr; + } +} + +keymaster_error_t AesKeyFactory::LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& /* additional_params */, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const { + if (!key) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + uint32_t min_mac_length = 0; + if (hw_enforced.Contains(TAG_BLOCK_MODE, KM_MODE_GCM) || + sw_enforced.Contains(TAG_BLOCK_MODE, KM_MODE_GCM)) { + + if (!hw_enforced.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length) && + !sw_enforced.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length)) { + + LOG_E("AES-GCM key must have KM_TAG_MIN_MAC_LENGTH", 0); + return KM_ERROR_INVALID_KEY_BLOB; + } + } + + keymaster_error_t error = KM_ERROR_OK; + key->reset(new (std::nothrow) AesKey(key_material, hw_enforced, sw_enforced, &error)); + if (!key->get()) + error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return error; +} + +keymaster_error_t AesKeyFactory::validate_algorithm_specific_new_key_params( + const AuthorizationSet& key_description) const { + if (key_description.Contains(TAG_BLOCK_MODE, KM_MODE_GCM)) { + uint32_t min_tag_length; + if (!key_description.GetTagValue(TAG_MIN_MAC_LENGTH, &min_tag_length)) + return KM_ERROR_MISSING_MIN_MAC_LENGTH; + + if (min_tag_length % 8 != 0) + return KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH; + + if (min_tag_length < kMinGcmTagLength || min_tag_length > kMaxGcmTagLength) + return KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH; + } else { + // Not GCM + if (key_description.find(TAG_MIN_MAC_LENGTH) != -1) { + LOG_W("KM_TAG_MIN_MAC_LENGTH found for non AES-GCM key", 0); + return KM_ERROR_INVALID_TAG; + } + } + + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/aes_key.h b/keymaster/aes_key.h new file mode 100644 index 0000000..a4ab2f2 --- /dev/null +++ b/keymaster/aes_key.h
@@ -0,0 +1,60 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_AES_KEY_H_ +#define SYSTEM_KEYMASTER_AES_KEY_H_ + +#include <openssl/aes.h> + +#include "symmetric_key.h" + +namespace keymaster { + +const size_t kMinGcmTagLength = 12 * 8; +const size_t kMaxGcmTagLength = 16 * 8; + +class AesKeyFactory : public SymmetricKeyFactory { + public: + explicit AesKeyFactory(const KeymasterContext* context) : SymmetricKeyFactory(context) {} + + keymaster_algorithm_t registry_key() const { return KM_ALGORITHM_AES; } + + keymaster_error_t LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const override; + + OperationFactory* GetOperationFactory(keymaster_purpose_t purpose) const override; + + private: + bool key_size_supported(size_t key_size_bits) const override { + return key_size_bits == 128 || key_size_bits == 192 || key_size_bits == 256; + } + keymaster_error_t validate_algorithm_specific_new_key_params( + const AuthorizationSet& key_description) const override; +}; + +class AesKey : public SymmetricKey { + public: + AesKey(const KeymasterKeyBlob& key_material, const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, keymaster_error_t* error) + : SymmetricKey(key_material, hw_enforced, sw_enforced, error) {} +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_AES_KEY_H_
diff --git a/keymaster/aes_operation.cpp b/keymaster/aes_operation.cpp new file mode 100644 index 0000000..4c5c88c --- /dev/null +++ b/keymaster/aes_operation.cpp
@@ -0,0 +1,651 @@ +/* + * Copyright 2014 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 "aes_operation.h" + +#include <stdio.h> + +#include <new> + +#include <UniquePtr.h> + +#include <openssl/aes.h> +#include <openssl/err.h> +#include <openssl/rand.h> + +#include <keymaster/logger.h> + +#include "aes_key.h" +#include "openssl_err.h" + +namespace keymaster { + +static const size_t GCM_NONCE_SIZE = 12; + +inline bool allows_padding(keymaster_block_mode_t block_mode) { + switch (block_mode) { + case KM_MODE_CTR: + case KM_MODE_GCM: + return false; + case KM_MODE_ECB: + case KM_MODE_CBC: + return true; + } + assert(false /* Can't get here */); + return false; +} + +static keymaster_error_t GetAndValidateGcmTagLength(const AuthorizationSet& begin_params, + const AuthorizationSet& key_params, + size_t* tag_length) { + uint32_t tag_length_bits; + if (!begin_params.GetTagValue(TAG_MAC_LENGTH, &tag_length_bits)) { + return KM_ERROR_MISSING_MAC_LENGTH; + } + + uint32_t min_tag_length_bits; + if (!key_params.GetTagValue(TAG_MIN_MAC_LENGTH, &min_tag_length_bits)) { + LOG_E("AES GCM key must have KM_TAG_MIN_MAC_LENGTH", 0); + return KM_ERROR_INVALID_KEY_BLOB; + } + + if (tag_length_bits % 8 != 0 || tag_length_bits > kMaxGcmTagLength || + tag_length_bits < kMinGcmTagLength) { + return KM_ERROR_UNSUPPORTED_MAC_LENGTH; + } + + if (tag_length_bits < min_tag_length_bits) { + return KM_ERROR_INVALID_MAC_LENGTH; + } + + *tag_length = tag_length_bits / 8; + return KM_ERROR_OK; +} + +Operation* AesOperationFactory::CreateOperation(const Key& key, + const AuthorizationSet& begin_params, + keymaster_error_t* error) { + *error = KM_ERROR_OK; + const SymmetricKey* symmetric_key = static_cast<const SymmetricKey*>(&key); + + switch (symmetric_key->key_data_size()) { + case 16: + case 24: + case 32: + break; + default: + *error = KM_ERROR_UNSUPPORTED_KEY_SIZE; + return nullptr; + } + + keymaster_block_mode_t block_mode; + if (!begin_params.GetTagValue(TAG_BLOCK_MODE, &block_mode)) { + LOG_E("%d block modes specified in begin params", begin_params.GetTagCount(TAG_BLOCK_MODE)); + *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE; + return nullptr; + } else if (!supported(block_mode)) { + LOG_E("Block mode %d not supported", block_mode); + *error = KM_ERROR_UNSUPPORTED_BLOCK_MODE; + return nullptr; + } else if (!key.authorizations().Contains(TAG_BLOCK_MODE, block_mode)) { + LOG_E("Block mode %d was specified, but not authorized by key", block_mode); + *error = KM_ERROR_INCOMPATIBLE_BLOCK_MODE; + return nullptr; + } + + size_t tag_length = 0; + if (block_mode == KM_MODE_GCM) { + *error = GetAndValidateGcmTagLength(begin_params, key.authorizations(), &tag_length); + if (*error != KM_ERROR_OK) { + return nullptr; + } + } + + keymaster_padding_t padding; + if (!GetAndValidatePadding(begin_params, key, &padding, error)) { + return nullptr; + } + if (!allows_padding(block_mode) && padding != KM_PAD_NONE) { + LOG_E("Mode does not support padding", 0); + *error = KM_ERROR_INCOMPATIBLE_PADDING_MODE; + return nullptr; + } + + bool caller_nonce = key.authorizations().GetTagValue(TAG_CALLER_NONCE); + + Operation* op = nullptr; + switch (purpose()) { + case KM_PURPOSE_ENCRYPT: + op = new (std::nothrow) + AesEvpEncryptOperation(block_mode, padding, caller_nonce, tag_length, + symmetric_key->key_data(), symmetric_key->key_data_size()); + break; + case KM_PURPOSE_DECRYPT: + op = new (std::nothrow) + AesEvpDecryptOperation(block_mode, padding, tag_length, symmetric_key->key_data(), + symmetric_key->key_data_size()); + break; + default: + *error = KM_ERROR_UNSUPPORTED_PURPOSE; + return nullptr; + } + + if (!op) + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return op; +} + +static const keymaster_block_mode_t supported_block_modes[] = {KM_MODE_ECB, KM_MODE_CBC, + KM_MODE_CTR, KM_MODE_GCM}; + +const keymaster_block_mode_t* +AesOperationFactory::SupportedBlockModes(size_t* block_mode_count) const { + *block_mode_count = array_length(supported_block_modes); + return supported_block_modes; +} + +static const keymaster_padding_t supported_padding_modes[] = {KM_PAD_NONE, KM_PAD_PKCS7}; +const keymaster_padding_t* +AesOperationFactory::SupportedPaddingModes(size_t* padding_mode_count) const { + *padding_mode_count = array_length(supported_padding_modes); + return supported_padding_modes; +} + +AesEvpOperation::AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode, + keymaster_padding_t padding, bool caller_iv, size_t tag_length, + const uint8_t* key, size_t key_size) + : Operation(purpose), block_mode_(block_mode), caller_iv_(caller_iv), tag_length_(tag_length), + data_started_(false), key_size_(key_size), padding_(padding) { + memcpy(key_, key, key_size_); + EVP_CIPHER_CTX_init(&ctx_); +} + +AesEvpOperation::~AesEvpOperation() { + EVP_CIPHER_CTX_cleanup(&ctx_); + memset_s(aad_block_buf_.get(), AES_BLOCK_SIZE, 0); +} + +keymaster_error_t AesEvpOperation::Begin(const AuthorizationSet& /* input_params */, + AuthorizationSet* /* output_params */) { + if (block_mode_ == KM_MODE_GCM) { + aad_block_buf_length_ = 0; + aad_block_buf_.reset(new (std::nothrow) uint8_t[AES_BLOCK_SIZE]); + if (!aad_block_buf_.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + + return InitializeCipher(); +} + +keymaster_error_t AesEvpOperation::Update(const AuthorizationSet& additional_params, + const Buffer& input, + AuthorizationSet* /* output_params */, Buffer* output, + size_t* input_consumed) { + keymaster_error_t error; + if (block_mode_ == KM_MODE_GCM) + if (!HandleAad(additional_params, input, &error)) + return error; + + if (!InternalUpdate(input.peek_read(), input.available_read(), output, &error)) + return error; + *input_consumed = input.available_read(); + + return KM_ERROR_OK; +} + +inline bool is_bad_decrypt(unsigned long error) { + return (ERR_GET_LIB(error) == ERR_LIB_CIPHER && // + ERR_GET_REASON(error) == CIPHER_R_BAD_DECRYPT); +} + +keymaster_error_t AesEvpOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& /* signature */, + AuthorizationSet* output_params, Buffer* output) { + keymaster_error_t error; + if (!UpdateForFinish(additional_params, input, output_params, output, &error)) + return error; + + if (!output->reserve(AES_BLOCK_SIZE)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (block_mode_ == KM_MODE_GCM && aad_block_buf_length_ > 0 && !ProcessBufferedAadBlock(&error)) + return error; + + int output_written = -1; + if (!EVP_CipherFinal_ex(&ctx_, output->peek_write(), &output_written)) { + if (tag_length_ > 0) + return KM_ERROR_VERIFICATION_FAILED; + LOG_E("Error encrypting final block: %s", ERR_error_string(ERR_peek_last_error(), NULL)); + return TranslateLastOpenSslError(); + } + + assert(output_written <= AES_BLOCK_SIZE); + if (!output->advance_write(output_written)) + return KM_ERROR_UNKNOWN_ERROR; + return KM_ERROR_OK; +} + +bool AesEvpOperation::need_iv() const { + switch (block_mode_) { + case KM_MODE_CBC: + case KM_MODE_CTR: + case KM_MODE_GCM: + return true; + case KM_MODE_ECB: + return false; + default: + // Shouldn't get here. + assert(false); + return false; + } +} + +keymaster_error_t AesEvpOperation::InitializeCipher() { + const EVP_CIPHER* cipher; + switch (block_mode_) { + case KM_MODE_ECB: + switch (key_size_) { + case 16: + cipher = EVP_aes_128_ecb(); + break; + case 24: + cipher = EVP_aes_192_ecb(); + break; + case 32: + cipher = EVP_aes_256_ecb(); + break; + default: + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + break; + case KM_MODE_CBC: + switch (key_size_) { + case 16: + cipher = EVP_aes_128_cbc(); + break; + case 24: + cipher = EVP_aes_192_cbc(); + break; + case 32: + cipher = EVP_aes_256_cbc(); + break; + default: + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + break; + case KM_MODE_CTR: + switch (key_size_) { + case 16: + cipher = EVP_aes_128_ctr(); + break; + case 24: + cipher = EVP_aes_192_ctr(); + break; + case 32: + cipher = EVP_aes_256_ctr(); + break; + default: + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + break; + case KM_MODE_GCM: + switch (key_size_) { + case 16: + cipher = EVP_aes_128_gcm(); + break; + case 24: + cipher = EVP_aes_192_gcm(); + break; + case 32: + cipher = EVP_aes_256_gcm(); + break; + default: + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + break; + default: + return KM_ERROR_UNSUPPORTED_BLOCK_MODE; + } + + if (!EVP_CipherInit_ex(&ctx_, cipher, NULL /* engine */, key_, iv_.get(), evp_encrypt_mode())) + return TranslateLastOpenSslError(); + + switch (padding_) { + case KM_PAD_NONE: + EVP_CIPHER_CTX_set_padding(&ctx_, 0 /* disable padding */); + break; + case KM_PAD_PKCS7: + // This is the default for OpenSSL EVP cipher operations. + break; + default: + return KM_ERROR_UNSUPPORTED_PADDING_MODE; + } + + if (block_mode_ == KM_MODE_GCM) { + aad_block_buf_length_ = 0; + aad_block_buf_.reset(new (std::nothrow) uint8_t[AES_BLOCK_SIZE]); + if (!aad_block_buf_.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + + return KM_ERROR_OK; +} + +keymaster_error_t AesEvpOperation::GetIv(const AuthorizationSet& input_params) { + keymaster_blob_t iv_blob; + if (!input_params.GetTagValue(TAG_NONCE, &iv_blob)) { + LOG_E("No IV provided", 0); + return KM_ERROR_INVALID_ARGUMENT; + } + if (block_mode_ != KM_MODE_GCM && iv_blob.data_length != AES_BLOCK_SIZE) { + LOG_E("Expected %d-byte IV for AES operation, but got %d bytes", AES_BLOCK_SIZE, + iv_blob.data_length); + return KM_ERROR_INVALID_NONCE; + } + if (block_mode_ == KM_MODE_GCM && iv_blob.data_length != GCM_NONCE_SIZE) { + LOG_E("Expected %d-byte nonce for AES-GCM operation, but got %d bytes", GCM_NONCE_SIZE, + iv_blob.data_length); + return KM_ERROR_INVALID_NONCE; + } + iv_.reset(dup_array(iv_blob.data, iv_blob.data_length)); + if (!iv_.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + iv_length_ = iv_blob.data_length; + return KM_ERROR_OK; +} + +/* + * Process Incoming Associated Authentication Data. + * + * This method is more complex than might be expected, because the underlying library silently does + * the wrong thing when given partial AAD blocks, so we have to take care to process AAD in + * AES_BLOCK_SIZE increments, buffering (in aad_block_buf_) when given smaller amounts of data. + */ +bool AesEvpOperation::HandleAad(const AuthorizationSet& input_params, const Buffer& input, + keymaster_error_t* error) { + assert(tag_length_ > 0); + assert(error); + + keymaster_blob_t aad; + if (input_params.GetTagValue(TAG_ASSOCIATED_DATA, &aad)) { + if (data_started_) { + *error = KM_ERROR_INVALID_TAG; + return false; + } + + if (aad_block_buf_length_ > 0) { + FillBufferedAadBlock(&aad); + if (aad_block_buf_length_ == AES_BLOCK_SIZE && !ProcessBufferedAadBlock(error)) + return false; + } + + size_t blocks_to_process = aad.data_length / AES_BLOCK_SIZE; + if (blocks_to_process && !ProcessAadBlocks(aad.data, blocks_to_process, error)) + return false; + aad.data += blocks_to_process * AES_BLOCK_SIZE; + aad.data_length -= blocks_to_process * AES_BLOCK_SIZE; + + FillBufferedAadBlock(&aad); + assert(aad.data_length == 0); + } + + if (input.available_read()) { + data_started_ = true; + // Data has begun, no more AAD is allowed. Process any buffered AAD. + if (aad_block_buf_length_ > 0 && !ProcessBufferedAadBlock(error)) + return false; + } + + return true; +} + +bool AesEvpOperation::ProcessBufferedAadBlock(keymaster_error_t* error) { + int output_written; + if (EVP_CipherUpdate(&ctx_, nullptr /* out */, &output_written, aad_block_buf_.get(), + aad_block_buf_length_)) { + aad_block_buf_length_ = 0; + return true; + } + *error = TranslateLastOpenSslError(); + return false; +} + +bool AesEvpOperation::ProcessAadBlocks(const uint8_t* data, size_t blocks, + keymaster_error_t* error) { + int output_written; + if (EVP_CipherUpdate(&ctx_, nullptr /* out */, &output_written, data, blocks * AES_BLOCK_SIZE)) + return true; + *error = TranslateLastOpenSslError(); + return false; +} + +inline size_t min(size_t a, size_t b) { + return (a < b) ? a : b; +} + +void AesEvpOperation::FillBufferedAadBlock(keymaster_blob_t* aad) { + size_t to_buffer = min(AES_BLOCK_SIZE - aad_block_buf_length_, aad->data_length); + memcpy(aad_block_buf_.get() + aad_block_buf_length_, aad->data, to_buffer); + aad->data += to_buffer; + aad->data_length -= to_buffer; + aad_block_buf_length_ += to_buffer; +} + +bool AesEvpOperation::InternalUpdate(const uint8_t* input, size_t input_length, Buffer* output, + keymaster_error_t* error) { + assert(output); + assert(error); + + if (!input_length) + return true; + + if (!output->reserve(input_length + AES_BLOCK_SIZE)) { + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return false; + } + + int output_written = -1; + if (!EVP_CipherUpdate(&ctx_, output->peek_write(), &output_written, input, input_length)) { + *error = TranslateLastOpenSslError(); + return false; + } + return output->advance_write(output_written); +} + +bool AesEvpOperation::UpdateForFinish(const AuthorizationSet& additional_params, + const Buffer& input, AuthorizationSet* output_params, + Buffer* output, keymaster_error_t* error) { + if (input.available_read() || !additional_params.empty()) { + size_t input_consumed; + *error = Update(additional_params, input, output_params, output, &input_consumed); + if (*error != KM_ERROR_OK) + return false; + if (input_consumed != input.available_read()) { + *error = KM_ERROR_INVALID_INPUT_LENGTH; + return false; + } + } + + return true; +} + +keymaster_error_t AesEvpEncryptOperation::Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) { + if (!output_params) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + if (need_iv()) { + keymaster_error_t error = KM_ERROR_OK; + if (input_params.find(TAG_NONCE) == -1) + error = GenerateIv(); + else if (caller_iv_) + error = GetIv(input_params); + else + error = KM_ERROR_CALLER_NONCE_PROHIBITED; + + if (error == KM_ERROR_OK) + output_params->push_back(TAG_NONCE, iv_.get(), iv_length_); + else + return error; + } + + return AesEvpOperation::Begin(input_params, output_params); +} + +keymaster_error_t AesEvpEncryptOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& signature, + AuthorizationSet* output_params, Buffer* output) { + if (!output->reserve(input.available_read() + AES_BLOCK_SIZE + tag_length_)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + keymaster_error_t error = + AesEvpOperation::Finish(additional_params, input, signature, output_params, output); + if (error != KM_ERROR_OK) + return error; + + if (tag_length_ > 0) { + if (!output->reserve(tag_length_)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (!EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_GET_TAG, tag_length_, output->peek_write())) + return TranslateLastOpenSslError(); + if (!output->advance_write(tag_length_)) + return KM_ERROR_UNKNOWN_ERROR; + } + + return KM_ERROR_OK; +} + +keymaster_error_t AesEvpEncryptOperation::GenerateIv() { + iv_length_ = (block_mode_ == KM_MODE_GCM) ? GCM_NONCE_SIZE : AES_BLOCK_SIZE; + iv_.reset(new (std::nothrow) uint8_t[iv_length_]); + if (!iv_.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + if (RAND_bytes(iv_.get(), iv_length_) != 1) + return TranslateLastOpenSslError(); + return KM_ERROR_OK; +} + +keymaster_error_t AesEvpDecryptOperation::Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) { + if (need_iv()) { + keymaster_error_t error = GetIv(input_params); + if (error != KM_ERROR_OK) + return error; + } + + if (tag_length_ > 0) { + tag_buf_length_ = 0; + tag_buf_.reset(new (std::nothrow) uint8_t[tag_length_]); + if (!tag_buf_.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + + return AesEvpOperation::Begin(input_params, output_params); +} + +keymaster_error_t AesEvpDecryptOperation::Update(const AuthorizationSet& additional_params, + const Buffer& input, + AuthorizationSet* /* output_params */, + Buffer* output, size_t* input_consumed) { + if (!output || !input_consumed) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + // Barring error, we'll consume it all. + *input_consumed = input.available_read(); + + keymaster_error_t error; + if (block_mode_ == KM_MODE_GCM) { + if (!HandleAad(additional_params, input, &error)) + return error; + return ProcessAllButTagLengthBytes(input, output); + } + + if (!InternalUpdate(input.peek_read(), input.available_read(), output, &error)) + return error; + return KM_ERROR_OK; +} + +keymaster_error_t AesEvpDecryptOperation::ProcessAllButTagLengthBytes(const Buffer& input, + Buffer* output) { + if (input.available_read() <= tag_buf_unused()) { + BufferCandidateTagData(input.peek_read(), input.available_read()); + return KM_ERROR_OK; + } + + const size_t data_available = tag_buf_length_ + input.available_read(); + + const size_t to_process = data_available - tag_length_; + const size_t to_process_from_tag_buf = min(to_process, tag_buf_length_); + const size_t to_process_from_input = to_process - to_process_from_tag_buf; + + if (!output->reserve(to_process + AES_BLOCK_SIZE)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + keymaster_error_t error; + if (!ProcessTagBufContentsAsData(to_process_from_tag_buf, output, &error)) + return error; + + if (!InternalUpdate(input.peek_read(), to_process_from_input, output, &error)) + return error; + + BufferCandidateTagData(input.peek_read() + to_process_from_input, + input.available_read() - to_process_from_input); + assert(tag_buf_unused() == 0); + + return KM_ERROR_OK; +} + +bool AesEvpDecryptOperation::ProcessTagBufContentsAsData(size_t to_process, Buffer* output, + keymaster_error_t* error) { + assert(to_process <= tag_buf_length_); + if (!InternalUpdate(tag_buf_.get(), to_process, output, error)) + return false; + if (to_process < tag_buf_length_) + memmove(tag_buf_.get(), tag_buf_.get() + to_process, tag_buf_length_ - to_process); + tag_buf_length_ -= to_process; + return true; +} + +void AesEvpDecryptOperation::BufferCandidateTagData(const uint8_t* data, size_t data_length) { + assert(data_length <= tag_length_ - tag_buf_length_); + memcpy(tag_buf_.get() + tag_buf_length_, data, data_length); + tag_buf_length_ += data_length; +} + +keymaster_error_t AesEvpDecryptOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& signature, + AuthorizationSet* output_params, Buffer* output) { + keymaster_error_t error; + if (!UpdateForFinish(additional_params, input, output_params, output, &error)) + return error; + + if (tag_buf_length_ < tag_length_) + return KM_ERROR_INVALID_INPUT_LENGTH; + else if (tag_length_ > 0 && + !EVP_CIPHER_CTX_ctrl(&ctx_, EVP_CTRL_GCM_SET_TAG, tag_length_, tag_buf_.get())) + return TranslateLastOpenSslError(); + + AuthorizationSet empty_params; + Buffer empty_input; + return AesEvpOperation::Finish(empty_params, empty_input, signature, output_params, output); +} + +keymaster_error_t AesEvpOperation::Abort() { + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/aes_operation.h b/keymaster/aes_operation.h new file mode 100644 index 0000000..1a296bb --- /dev/null +++ b/keymaster/aes_operation.h
@@ -0,0 +1,158 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_AES_OPERATION_H_ +#define SYSTEM_KEYMASTER_AES_OPERATION_H_ + +#include <openssl/evp.h> + +#include "ocb_utils.h" +#include "operation.h" + +namespace keymaster { + +/** + * Abstract base for AES operation factories. This class does all of the work to create + * AES operations. + */ +class AesOperationFactory : public OperationFactory { + public: + KeyType registry_key() const override { return KeyType(KM_ALGORITHM_AES, purpose()); } + + Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params, + keymaster_error_t* error) override; + const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const override; + const keymaster_padding_t* SupportedPaddingModes(size_t* padding_count) const override; + + virtual keymaster_purpose_t purpose() const = 0; +}; + +/** + * Concrete factory for AES encryption operations. + */ +class AesEncryptionOperationFactory : public AesOperationFactory { + keymaster_purpose_t purpose() const override { return KM_PURPOSE_ENCRYPT; } +}; + +/** + * Concrete factory for AES decryption operations. + */ +class AesDecryptionOperationFactory : public AesOperationFactory { + keymaster_purpose_t purpose() const override { return KM_PURPOSE_DECRYPT; } +}; + +static const size_t MAX_EVP_KEY_SIZE = 32; + +class AesEvpOperation : public Operation { + public: + AesEvpOperation(keymaster_purpose_t purpose, keymaster_block_mode_t block_mode, + keymaster_padding_t padding, bool caller_iv, size_t tag_length, + const uint8_t* key, size_t key_size); + ~AesEvpOperation(); + + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override; + keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, + size_t* input_consumed) override; + keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override; + keymaster_error_t Abort() override; + + virtual int evp_encrypt_mode() = 0; + + protected: + bool need_iv() const; + keymaster_error_t InitializeCipher(); + keymaster_error_t GetIv(const AuthorizationSet& input_params); + bool HandleAad(const AuthorizationSet& input_params, const Buffer& input, + keymaster_error_t* error); + bool ProcessAadBlocks(const uint8_t* data, size_t blocks, keymaster_error_t* error); + void FillBufferedAadBlock(keymaster_blob_t* aad); + bool ProcessBufferedAadBlock(keymaster_error_t* error); + bool InternalUpdate(const uint8_t* input, size_t input_length, Buffer* output, + keymaster_error_t* error); + bool UpdateForFinish(const AuthorizationSet& additional_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, keymaster_error_t* error); + + const keymaster_block_mode_t block_mode_; + EVP_CIPHER_CTX ctx_; + UniquePtr<uint8_t[]> iv_; + size_t iv_length_; + const bool caller_iv_; + size_t tag_length_; + UniquePtr<uint8_t[]> aad_block_buf_; + size_t aad_block_buf_length_; + + private: + bool data_started_; + const size_t key_size_; + const keymaster_padding_t padding_; + uint8_t key_[MAX_EVP_KEY_SIZE]; +}; + +class AesEvpEncryptOperation : public AesEvpOperation { + public: + AesEvpEncryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding, + bool caller_iv, size_t tag_length, const uint8_t* key, size_t key_size) + : AesEvpOperation(KM_PURPOSE_ENCRYPT, block_mode, padding, caller_iv, tag_length, key, + key_size) {} + + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override; + keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override; + + int evp_encrypt_mode() override { return 1; } + + private: + keymaster_error_t GenerateIv(); +}; + +class AesEvpDecryptOperation : public AesEvpOperation { + public: + AesEvpDecryptOperation(keymaster_block_mode_t block_mode, keymaster_padding_t padding, + size_t tag_length, const uint8_t* key, size_t key_size) + : AesEvpOperation(KM_PURPOSE_DECRYPT, block_mode, padding, + false /* caller_iv -- don't care */, tag_length, key, key_size) {} + + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override; + keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, + size_t* input_consumed) override; + keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override; + + int evp_encrypt_mode() override { return 0; } + + private: + size_t tag_buf_unused() { return tag_length_ - tag_buf_length_; } + + keymaster_error_t ProcessAllButTagLengthBytes(const Buffer& input, Buffer* output); + bool ProcessTagBufContentsAsData(size_t to_process, Buffer* output, keymaster_error_t* error); + void BufferCandidateTagData(const uint8_t* data, size_t data_length); + + UniquePtr<uint8_t[]> tag_buf_; + size_t tag_buf_length_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_AES_OPERATION_H_
diff --git a/keymaster/android_keymaster.cpp b/keymaster/android_keymaster.cpp new file mode 100644 index 0000000..3a53394 --- /dev/null +++ b/keymaster/android_keymaster.cpp
@@ -0,0 +1,440 @@ +/* + * Copyright 2014 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 <keymaster/android_keymaster.h> + +#include <assert.h> +#include <string.h> + +#include <cstddef> + +#include <openssl/rand.h> +#include <openssl/x509.h> + +#include <UniquePtr.h> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/key_factory.h> +#include <keymaster/keymaster_context.h> + +#include "ae.h" +#include "key.h" +#include "openssl_err.h" +#include "operation.h" +#include "operation_table.h" + +namespace keymaster { + +const uint8_t MAJOR_VER = 1; +const uint8_t MINOR_VER = 1; +const uint8_t SUBMINOR_VER = 0; + +AndroidKeymaster::AndroidKeymaster(KeymasterContext* context, size_t operation_table_size) + : context_(context), operation_table_(new OperationTable(operation_table_size)) {} + +AndroidKeymaster::~AndroidKeymaster() {} + +struct AE_CTX_Delete { + void operator()(ae_ctx* ctx) const { ae_free(ctx); } +}; +typedef UniquePtr<ae_ctx, AE_CTX_Delete> Unique_ae_ctx; + +// TODO(swillden): Unify support analysis. Right now, we have per-keytype methods that determine if +// specific modes, padding, etc. are supported for that key type, and AndroidKeymaster also has +// methods that return the same information. They'll get out of sync. Best to put the knowledge in +// the keytypes and provide some mechanism for AndroidKeymaster to query the keytypes for the +// information. + +template <typename T> +bool check_supported(const KeymasterContext& context, keymaster_algorithm_t algorithm, + SupportedResponse<T>* response) { + if (context.GetKeyFactory(algorithm) == NULL) { + response->error = KM_ERROR_UNSUPPORTED_ALGORITHM; + return false; + } + return true; +} + +void AndroidKeymaster::GetVersion(const GetVersionRequest&, GetVersionResponse* rsp) { + if (rsp == NULL) + return; + + rsp->major_ver = MAJOR_VER; + rsp->minor_ver = MINOR_VER; + rsp->subminor_ver = SUBMINOR_VER; + rsp->error = KM_ERROR_OK; +} + +void AndroidKeymaster::SupportedAlgorithms(const SupportedAlgorithmsRequest& /* request */, + SupportedAlgorithmsResponse* response) { + if (response == NULL) + return; + + response->error = KM_ERROR_OK; + + size_t algorithm_count = 0; + const keymaster_algorithm_t* algorithms = context_->GetSupportedAlgorithms(&algorithm_count); + if (algorithm_count == 0) + return; + response->results_length = algorithm_count; + response->results = dup_array(algorithms, algorithm_count); + if (!response->results) + response->error = KM_ERROR_MEMORY_ALLOCATION_FAILED; +} + +template <typename T> +void GetSupported(const KeymasterContext& context, keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + const T* (OperationFactory::*get_supported_method)(size_t* count) const, + SupportedResponse<T>* response) { + if (response == NULL || !check_supported(context, algorithm, response)) + return; + + const OperationFactory* factory = context.GetOperationFactory(algorithm, purpose); + if (!factory) { + response->error = KM_ERROR_UNSUPPORTED_PURPOSE; + return; + } + + size_t count; + const T* supported = (factory->*get_supported_method)(&count); + response->SetResults(supported, count); +} + +void AndroidKeymaster::SupportedBlockModes(const SupportedBlockModesRequest& request, + SupportedBlockModesResponse* response) { + GetSupported(*context_, request.algorithm, request.purpose, + &OperationFactory::SupportedBlockModes, response); +} + +void AndroidKeymaster::SupportedPaddingModes(const SupportedPaddingModesRequest& request, + SupportedPaddingModesResponse* response) { + GetSupported(*context_, request.algorithm, request.purpose, + &OperationFactory::SupportedPaddingModes, response); +} + +void AndroidKeymaster::SupportedDigests(const SupportedDigestsRequest& request, + SupportedDigestsResponse* response) { + GetSupported(*context_, request.algorithm, request.purpose, &OperationFactory::SupportedDigests, + response); +} + +void AndroidKeymaster::SupportedImportFormats(const SupportedImportFormatsRequest& request, + SupportedImportFormatsResponse* response) { + if (response == NULL || !check_supported(*context_, request.algorithm, response)) + return; + + size_t count; + const keymaster_key_format_t* formats = + context_->GetKeyFactory(request.algorithm)->SupportedImportFormats(&count); + response->SetResults(formats, count); +} + +void AndroidKeymaster::SupportedExportFormats(const SupportedExportFormatsRequest& request, + SupportedExportFormatsResponse* response) { + if (response == NULL || !check_supported(*context_, request.algorithm, response)) + return; + + size_t count; + const keymaster_key_format_t* formats = + context_->GetKeyFactory(request.algorithm)->SupportedExportFormats(&count); + response->SetResults(formats, count); +} + +void AndroidKeymaster::AddRngEntropy(const AddEntropyRequest& request, + AddEntropyResponse* response) { + response->error = context_->AddRngEntropy(request.random_data.peek_read(), + request.random_data.available_read()); +} + +void AndroidKeymaster::GenerateKey(const GenerateKeyRequest& request, + GenerateKeyResponse* response) { + if (response == NULL) + return; + + keymaster_algorithm_t algorithm; + KeyFactory* factory = 0; + UniquePtr<Key> key; + if (!request.key_description.GetTagValue(TAG_ALGORITHM, &algorithm) || + !(factory = context_->GetKeyFactory(algorithm))) + response->error = KM_ERROR_UNSUPPORTED_ALGORITHM; + else { + KeymasterKeyBlob key_blob; + response->enforced.Clear(); + response->unenforced.Clear(); + response->error = factory->GenerateKey(request.key_description, &key_blob, + &response->enforced, &response->unenforced); + if (response->error == KM_ERROR_OK) + response->key_blob = key_blob.release(); + } +} + +void AndroidKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request, + GetKeyCharacteristicsResponse* response) { + if (response == NULL) + return; + + KeymasterKeyBlob key_material; + response->error = + context_->ParseKeyBlob(KeymasterKeyBlob(request.key_blob), request.additional_params, + &key_material, &response->enforced, &response->unenforced); + if (response->error != KM_ERROR_OK) + return; +} + +static KeyFactory* GetKeyFactory(const KeymasterContext& context, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + keymaster_algorithm_t* algorithm, keymaster_error_t* error) { + *error = KM_ERROR_UNSUPPORTED_ALGORITHM; + if (!hw_enforced.GetTagValue(TAG_ALGORITHM, algorithm) && + !sw_enforced.GetTagValue(TAG_ALGORITHM, algorithm)) + return nullptr; + KeyFactory* factory = context.GetKeyFactory(*algorithm); + if (factory) + *error = KM_ERROR_OK; + return factory; +} + +void AndroidKeymaster::BeginOperation(const BeginOperationRequest& request, + BeginOperationResponse* response) { + if (response == NULL) + return; + response->op_handle = 0; + + AuthorizationSet hw_enforced; + AuthorizationSet sw_enforced; + const KeyFactory* key_factory; + UniquePtr<Key> key; + response->error = LoadKey(request.key_blob, request.additional_params, &hw_enforced, + &sw_enforced, &key_factory, &key); + if (response->error != KM_ERROR_OK) + return; + + response->error = KM_ERROR_UNKNOWN_ERROR; + keymaster_algorithm_t key_algorithm; + if (!key->authorizations().GetTagValue(TAG_ALGORITHM, &key_algorithm)) + return; + + response->error = KM_ERROR_UNSUPPORTED_PURPOSE; + OperationFactory* factory = key_factory->GetOperationFactory(request.purpose); + if (!factory) + return; + + UniquePtr<Operation> operation( + factory->CreateOperation(*key, request.additional_params, &response->error)); + if (operation.get() == NULL) + return; + + if (context_->enforcement_policy()) { + km_id_t key_id; + response->error = KM_ERROR_UNKNOWN_ERROR; + if (!context_->enforcement_policy()->CreateKeyId(request.key_blob, &key_id)) + return; + operation->set_key_id(key_id); + response->error = context_->enforcement_policy()->AuthorizeOperation( + request.purpose, key_id, key->authorizations(), request.additional_params, + 0 /* op_handle */, true /* is_begin_operation */); + if (response->error != KM_ERROR_OK) + return; + } + + response->output_params.Clear(); + response->error = operation->Begin(request.additional_params, &response->output_params); + if (response->error != KM_ERROR_OK) + return; + + operation->SetAuthorizations(key->authorizations()); + response->error = operation_table_->Add(operation.release(), &response->op_handle); +} + +void AndroidKeymaster::UpdateOperation(const UpdateOperationRequest& request, + UpdateOperationResponse* response) { + if (response == NULL) + return; + + response->error = KM_ERROR_INVALID_OPERATION_HANDLE; + Operation* operation = operation_table_->Find(request.op_handle); + if (operation == NULL) + return; + + if (context_->enforcement_policy()) { + response->error = context_->enforcement_policy()->AuthorizeOperation( + operation->purpose(), operation->key_id(), operation->authorizations(), + request.additional_params, request.op_handle, false /* is_begin_operation */); + if (response->error != KM_ERROR_OK) { + operation_table_->Delete(request.op_handle); + return; + } + } + + response->error = + operation->Update(request.additional_params, request.input, &response->output_params, + &response->output, &response->input_consumed); + if (response->error != KM_ERROR_OK) { + // Any error invalidates the operation. + operation_table_->Delete(request.op_handle); + } +} + +void AndroidKeymaster::FinishOperation(const FinishOperationRequest& request, + FinishOperationResponse* response) { + if (response == NULL) + return; + + response->error = KM_ERROR_INVALID_OPERATION_HANDLE; + Operation* operation = operation_table_->Find(request.op_handle); + if (operation == NULL) + return; + + if (context_->enforcement_policy()) { + response->error = context_->enforcement_policy()->AuthorizeOperation( + operation->purpose(), operation->key_id(), operation->authorizations(), + request.additional_params, request.op_handle, false /* is_begin_operation */); + if (response->error != KM_ERROR_OK) { + operation_table_->Delete(request.op_handle); + return; + } + } + + response->error = operation->Finish(request.additional_params, request.input, request.signature, + &response->output_params, &response->output); + operation_table_->Delete(request.op_handle); +} + +void AndroidKeymaster::AbortOperation(const AbortOperationRequest& request, + AbortOperationResponse* response) { + if (!response) + return; + + Operation* operation = operation_table_->Find(request.op_handle); + if (!operation) { + response->error = KM_ERROR_INVALID_OPERATION_HANDLE; + return; + } + + response->error = operation->Abort(); + operation_table_->Delete(request.op_handle); +} + +void AndroidKeymaster::ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response) { + if (response == NULL) + return; + + AuthorizationSet hw_enforced; + AuthorizationSet sw_enforced; + KeymasterKeyBlob key_material; + response->error = + context_->ParseKeyBlob(KeymasterKeyBlob(request.key_blob), request.additional_params, + &key_material, &hw_enforced, &sw_enforced); + if (response->error != KM_ERROR_OK) + return; + + keymaster_algorithm_t algorithm; + KeyFactory* key_factory = + GetKeyFactory(*context_, hw_enforced, sw_enforced, &algorithm, &response->error); + if (!key_factory) + return; + + UniquePtr<Key> key; + response->error = key_factory->LoadKey(key_material, request.additional_params, hw_enforced, + sw_enforced, &key); + if (response->error != KM_ERROR_OK) + return; + + UniquePtr<uint8_t[]> out_key; + size_t size; + response->error = key->formatted_key_material(request.key_format, &out_key, &size); + if (response->error == KM_ERROR_OK) { + response->key_data = out_key.release(); + response->key_data_length = size; + } +} + +void AndroidKeymaster::AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response) { + if (!response) + return; + + AuthorizationSet tee_enforced; + AuthorizationSet sw_enforced; + const KeyFactory* key_factory; + UniquePtr<Key> key; + response->error = LoadKey(request.key_blob, request.attest_params, &tee_enforced, &sw_enforced, + &key_factory, &key); + if (response->error != KM_ERROR_OK) + return; + + response->error = key->GenerateAttestation(*context_, request.attest_params, tee_enforced, + sw_enforced, &response->certificate_chain); +} + +void AndroidKeymaster::ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response) { + if (response == NULL) + return; + + keymaster_algorithm_t algorithm; + KeyFactory* factory = 0; + UniquePtr<Key> key; + if (!request.key_description.GetTagValue(TAG_ALGORITHM, &algorithm) || + !(factory = context_->GetKeyFactory(algorithm))) + response->error = KM_ERROR_UNSUPPORTED_ALGORITHM; + else { + keymaster_key_blob_t key_material = {request.key_data, request.key_data_length}; + KeymasterKeyBlob key_blob; + response->error = factory->ImportKey(request.key_description, request.key_format, + KeymasterKeyBlob(key_material), &key_blob, + &response->enforced, &response->unenforced); + if (response->error == KM_ERROR_OK) + response->key_blob = key_blob.release(); + } +} + +void AndroidKeymaster::DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response) { + if (!response) + return; + response->error = context_->DeleteKey(KeymasterKeyBlob(request.key_blob)); +} + +void AndroidKeymaster::DeleteAllKeys(const DeleteAllKeysRequest&, DeleteAllKeysResponse* response) { + if (!response) + return; + response->error = context_->DeleteAllKeys(); +} + +bool AndroidKeymaster::has_operation(keymaster_operation_handle_t op_handle) const { + return operation_table_->Find(op_handle) != nullptr; +} + +keymaster_error_t AndroidKeymaster::LoadKey(const keymaster_key_blob_t& key_blob, + const AuthorizationSet& additional_params, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced, + const KeyFactory** factory, UniquePtr<Key>* key) { + KeymasterKeyBlob key_material; + keymaster_error_t error = context_->ParseKeyBlob(KeymasterKeyBlob(key_blob), additional_params, + &key_material, hw_enforced, sw_enforced); + if (error != KM_ERROR_OK) + return error; + + keymaster_algorithm_t algorithm; + *factory = GetKeyFactory(*context_, *hw_enforced, *sw_enforced, &algorithm, &error); + if (error != KM_ERROR_OK) + return error; + + return (*factory)->LoadKey(key_material, additional_params, *hw_enforced, *sw_enforced, key); +} + +} // namespace keymaster
diff --git a/keymaster/android_keymaster_messages.cpp b/keymaster/android_keymaster_messages.cpp new file mode 100644 index 0000000..1b8f36e --- /dev/null +++ b/keymaster/android_keymaster_messages.cpp
@@ -0,0 +1,526 @@ +/* + * Copyright 2014 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 <keymaster/android_keymaster_messages.h> +#include <keymaster/android_keymaster_utils.h> + +namespace keymaster { + +/* + * Helper functions for working with key blobs. + */ + +static void set_key_blob(keymaster_key_blob_t* key_blob, const void* key_material, size_t length) { + delete[] key_blob->key_material; + key_blob->key_material = dup_buffer(key_material, length); + key_blob->key_material_size = length; +} + +static size_t key_blob_size(const keymaster_key_blob_t& key_blob) { + return sizeof(uint32_t) /* key size */ + key_blob.key_material_size; +} + +static uint8_t* serialize_key_blob(const keymaster_key_blob_t& key_blob, uint8_t* buf, + const uint8_t* end) { + return append_size_and_data_to_buf(buf, end, key_blob.key_material, key_blob.key_material_size); +} + +static bool deserialize_key_blob(keymaster_key_blob_t* key_blob, const uint8_t** buf_ptr, + const uint8_t* end) { + delete[] key_blob->key_material; + key_blob->key_material = 0; + UniquePtr<uint8_t[]> deserialized_key_material; + if (!copy_size_and_data_from_buf(buf_ptr, end, &key_blob->key_material_size, + &deserialized_key_material)) + return false; + key_blob->key_material = deserialized_key_material.release(); + return true; +} + +size_t KeymasterResponse::SerializedSize() const { + if (error != KM_ERROR_OK) + return sizeof(int32_t); + else + return sizeof(int32_t) + NonErrorSerializedSize(); +} + +uint8_t* KeymasterResponse::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = append_uint32_to_buf(buf, end, static_cast<uint32_t>(error)); + if (error == KM_ERROR_OK) + buf = NonErrorSerialize(buf, end); + return buf; +} + +bool KeymasterResponse::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + if (!copy_uint32_from_buf(buf_ptr, end, &error)) + return false; + if (error != KM_ERROR_OK) + return true; + return NonErrorDeserialize(buf_ptr, end); +} + +GenerateKeyResponse::~GenerateKeyResponse() { + delete[] key_blob.key_material; +} + +size_t GenerateKeyResponse::NonErrorSerializedSize() const { + return key_blob_size(key_blob) + enforced.SerializedSize() + unenforced.SerializedSize(); +} + +uint8_t* GenerateKeyResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + buf = serialize_key_blob(key_blob, buf, end); + buf = enforced.Serialize(buf, end); + return unenforced.Serialize(buf, end); +} + +bool GenerateKeyResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return deserialize_key_blob(&key_blob, buf_ptr, end) && enforced.Deserialize(buf_ptr, end) && + unenforced.Deserialize(buf_ptr, end); +} + +GetKeyCharacteristicsRequest::~GetKeyCharacteristicsRequest() { + delete[] key_blob.key_material; +} + +void GetKeyCharacteristicsRequest::SetKeyMaterial(const void* key_material, size_t length) { + set_key_blob(&key_blob, key_material, length); +} + +size_t GetKeyCharacteristicsRequest::SerializedSize() const { + return key_blob_size(key_blob) + additional_params.SerializedSize(); +} + +uint8_t* GetKeyCharacteristicsRequest::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = serialize_key_blob(key_blob, buf, end); + return additional_params.Serialize(buf, end); +} + +bool GetKeyCharacteristicsRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return deserialize_key_blob(&key_blob, buf_ptr, end) && + additional_params.Deserialize(buf_ptr, end); +} + +size_t GetKeyCharacteristicsResponse::NonErrorSerializedSize() const { + return enforced.SerializedSize() + unenforced.SerializedSize(); +} + +uint8_t* GetKeyCharacteristicsResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + buf = enforced.Serialize(buf, end); + return unenforced.Serialize(buf, end); +} + +bool GetKeyCharacteristicsResponse::NonErrorDeserialize(const uint8_t** buf_ptr, + const uint8_t* end) { + return enforced.Deserialize(buf_ptr, end) && unenforced.Deserialize(buf_ptr, end); +} + +void BeginOperationRequest::SetKeyMaterial(const void* key_material, size_t length) { + set_key_blob(&key_blob, key_material, length); +} + +size_t BeginOperationRequest::SerializedSize() const { + return sizeof(uint32_t) /* purpose */ + key_blob_size(key_blob) + + additional_params.SerializedSize(); +} + +uint8_t* BeginOperationRequest::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = append_uint32_to_buf(buf, end, purpose); + buf = serialize_key_blob(key_blob, buf, end); + return additional_params.Serialize(buf, end); +} + +bool BeginOperationRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return copy_uint32_from_buf(buf_ptr, end, &purpose) && + deserialize_key_blob(&key_blob, buf_ptr, end) && + additional_params.Deserialize(buf_ptr, end); +} + +size_t BeginOperationResponse::NonErrorSerializedSize() const { + if (message_version == 0) + return sizeof(op_handle); + else + return sizeof(op_handle) + output_params.SerializedSize(); +} + +uint8_t* BeginOperationResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + buf = append_uint64_to_buf(buf, end, op_handle); + if (message_version > 0) + buf = output_params.Serialize(buf, end); + return buf; +} + +bool BeginOperationResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + bool retval = copy_uint64_from_buf(buf_ptr, end, &op_handle); + if (retval && message_version > 0) + retval = output_params.Deserialize(buf_ptr, end); + return retval; +} + +size_t UpdateOperationRequest::SerializedSize() const { + if (message_version == 0) + return sizeof(op_handle) + input.SerializedSize(); + else + return sizeof(op_handle) + input.SerializedSize() + additional_params.SerializedSize(); +} + +uint8_t* UpdateOperationRequest::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = append_uint64_to_buf(buf, end, op_handle); + buf = input.Serialize(buf, end); + if (message_version > 0) + buf = additional_params.Serialize(buf, end); + return buf; +} + +bool UpdateOperationRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + bool retval = copy_uint64_from_buf(buf_ptr, end, &op_handle) && input.Deserialize(buf_ptr, end); + if (retval && message_version > 0) + retval = additional_params.Deserialize(buf_ptr, end); + return retval; +} + +size_t UpdateOperationResponse::NonErrorSerializedSize() const { + size_t size = 0; + switch (message_version) { + case 3: + case 2: + size += output_params.SerializedSize(); + ; /* falls through */ + case 1: + size += sizeof(uint32_t); + ; /* falls through */ + case 0: + size += output.SerializedSize(); + break; + + default: + assert(false); + } + + return size; +} + +uint8_t* UpdateOperationResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + buf = output.Serialize(buf, end); + if (message_version > 0) + buf = append_uint32_to_buf(buf, end, input_consumed); + if (message_version > 1) + buf = output_params.Serialize(buf, end); + return buf; +} + +bool UpdateOperationResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + bool retval = output.Deserialize(buf_ptr, end); + if (retval && message_version > 0) + retval = copy_uint32_from_buf(buf_ptr, end, &input_consumed); + if (retval && message_version > 1) + retval = output_params.Deserialize(buf_ptr, end); + return retval; +} + +size_t FinishOperationRequest::SerializedSize() const { + size_t size = 0; + switch (message_version) { + case 3: + size += input.SerializedSize(); + ; /* falls through */ + case 2: + case 1: + size += additional_params.SerializedSize(); + ; /* falls through */ + case 0: + size += sizeof(op_handle) + signature.SerializedSize(); + break; + + default: + assert(false); // Should never get here. + } + + return size; +} + +uint8_t* FinishOperationRequest::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = append_uint64_to_buf(buf, end, op_handle); + buf = signature.Serialize(buf, end); + if (message_version > 0) + buf = additional_params.Serialize(buf, end); + if (message_version > 2) + buf = input.Serialize(buf, end); + return buf; +} + +bool FinishOperationRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + bool retval = + copy_uint64_from_buf(buf_ptr, end, &op_handle) && signature.Deserialize(buf_ptr, end); + if (retval && message_version > 0) + retval = additional_params.Deserialize(buf_ptr, end); + if (retval && message_version > 2) + retval = input.Deserialize(buf_ptr, end); + return retval; +} + +size_t FinishOperationResponse::NonErrorSerializedSize() const { + if (message_version < 2) + return output.SerializedSize(); + else + return output.SerializedSize() + output_params.SerializedSize(); +} + +uint8_t* FinishOperationResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + buf = output.Serialize(buf, end); + if (message_version > 1) + buf = output_params.Serialize(buf, end); + return buf; +} + +bool FinishOperationResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + bool retval = output.Deserialize(buf_ptr, end); + if (retval && message_version > 1) + retval = output_params.Deserialize(buf_ptr, end); + return retval; +} + +size_t AddEntropyRequest::SerializedSize() const { + return random_data.SerializedSize(); +} + +uint8_t* AddEntropyRequest::Serialize(uint8_t* buf, const uint8_t* end) const { + return random_data.Serialize(buf, end); +} + +bool AddEntropyRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return random_data.Deserialize(buf_ptr, end); +} + +void ImportKeyRequest::SetKeyMaterial(const void* key_material, size_t length) { + delete[] key_data; + key_data = dup_buffer(key_material, length); + key_data_length = length; +} + +size_t ImportKeyRequest::SerializedSize() const { + return key_description.SerializedSize() + sizeof(uint32_t) /* key_format */ + + sizeof(uint32_t) /* key_data_length */ + key_data_length; +} + +uint8_t* ImportKeyRequest::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = key_description.Serialize(buf, end); + buf = append_uint32_to_buf(buf, end, key_format); + return append_size_and_data_to_buf(buf, end, key_data, key_data_length); +} + +bool ImportKeyRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + delete[] key_data; + key_data = NULL; + UniquePtr<uint8_t[]> deserialized_key_material; + if (!key_description.Deserialize(buf_ptr, end) || + !copy_uint32_from_buf(buf_ptr, end, &key_format) || + !copy_size_and_data_from_buf(buf_ptr, end, &key_data_length, &deserialized_key_material)) + return false; + key_data = deserialized_key_material.release(); + return true; +} + +void ImportKeyResponse::SetKeyMaterial(const void* key_material, size_t length) { + set_key_blob(&key_blob, key_material, length); +} + +size_t ImportKeyResponse::NonErrorSerializedSize() const { + return key_blob_size(key_blob) + enforced.SerializedSize() + unenforced.SerializedSize(); +} + +uint8_t* ImportKeyResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + buf = serialize_key_blob(key_blob, buf, end); + buf = enforced.Serialize(buf, end); + return unenforced.Serialize(buf, end); +} + +bool ImportKeyResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return deserialize_key_blob(&key_blob, buf_ptr, end) && enforced.Deserialize(buf_ptr, end) && + unenforced.Deserialize(buf_ptr, end); +} + +void ExportKeyRequest::SetKeyMaterial(const void* key_material, size_t length) { + set_key_blob(&key_blob, key_material, length); +} + +size_t ExportKeyRequest::SerializedSize() const { + return additional_params.SerializedSize() + sizeof(uint32_t) /* key_format */ + + key_blob_size(key_blob); +} + +uint8_t* ExportKeyRequest::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = additional_params.Serialize(buf, end); + buf = append_uint32_to_buf(buf, end, key_format); + return serialize_key_blob(key_blob, buf, end); +} + +bool ExportKeyRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return additional_params.Deserialize(buf_ptr, end) && + copy_uint32_from_buf(buf_ptr, end, &key_format) && + deserialize_key_blob(&key_blob, buf_ptr, end); +} + +void ExportKeyResponse::SetKeyMaterial(const void* key_material, size_t length) { + delete[] key_data; + key_data = dup_buffer(key_material, length); + key_data_length = length; +} + +size_t ExportKeyResponse::NonErrorSerializedSize() const { + return sizeof(uint32_t) /* key_data_length */ + key_data_length; +} + +uint8_t* ExportKeyResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + return append_size_and_data_to_buf(buf, end, key_data, key_data_length); +} + +bool ExportKeyResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + delete[] key_data; + key_data = NULL; + UniquePtr<uint8_t[]> deserialized_key_material; + if (!copy_size_and_data_from_buf(buf_ptr, end, &key_data_length, &deserialized_key_material)) + return false; + key_data = deserialized_key_material.release(); + return true; +} + +void DeleteKeyRequest::SetKeyMaterial(const void* key_material, size_t length) { + set_key_blob(&key_blob, key_material, length); +} + +size_t DeleteKeyRequest::SerializedSize() const { + return key_blob_size(key_blob); +} + +uint8_t* DeleteKeyRequest::Serialize(uint8_t* buf, const uint8_t* end) const { + return serialize_key_blob(key_blob, buf, end); +} + +bool DeleteKeyRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return deserialize_key_blob(&key_blob, buf_ptr, end); +} + +size_t GetVersionResponse::NonErrorSerializedSize() const { + return sizeof(major_ver) + sizeof(minor_ver) + sizeof(subminor_ver); +} + +uint8_t* GetVersionResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + if (buf + NonErrorSerializedSize() <= end) { + *buf++ = major_ver; + *buf++ = minor_ver; + *buf++ = subminor_ver; + } else { + buf += NonErrorSerializedSize(); + } + return buf; +} + +bool GetVersionResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + if (*buf_ptr + NonErrorSerializedSize() > end) + return false; + const uint8_t* tmp = *buf_ptr; + major_ver = *tmp++; + minor_ver = *tmp++; + subminor_ver = *tmp++; + *buf_ptr = tmp; + return true; +} + +AttestKeyRequest::~AttestKeyRequest() { + delete[] key_blob.key_material; +} + +void AttestKeyRequest::SetKeyMaterial(const void* key_material, size_t length) { + set_key_blob(&key_blob, key_material, length); +} + +size_t AttestKeyRequest::SerializedSize() const { + return key_blob_size(key_blob) + attest_params.SerializedSize(); +} + +uint8_t* AttestKeyRequest::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = serialize_key_blob(key_blob, buf, end); + return attest_params.Serialize(buf, end); +} + +bool AttestKeyRequest::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + return deserialize_key_blob(&key_blob, buf_ptr, end) && attest_params.Deserialize(buf_ptr, end); +} + +AttestKeyResponse::~AttestKeyResponse() { + for (size_t i = 0; i < certificate_chain.entry_count; ++i) + delete[] certificate_chain.entries[i].data; + delete[] certificate_chain.entries; +} + +const size_t kMaxChainEntryCount = 10; +bool AttestKeyResponse::AllocateChain(size_t entry_count) { + if (entry_count > kMaxChainEntryCount) + return false; + + if (certificate_chain.entries) { + for (size_t i = 0; i < certificate_chain.entry_count; ++i) + delete[] certificate_chain.entries[i].data; + delete[] certificate_chain.entries; + } + + certificate_chain.entry_count = entry_count; + certificate_chain.entries = new keymaster_blob_t[entry_count]; + if (!certificate_chain.entries) { + certificate_chain.entry_count = 0; + return false; + } + + memset(certificate_chain.entries, 0, sizeof(certificate_chain.entries[0]) * entry_count); + return true; +} + +size_t AttestKeyResponse::NonErrorSerializedSize() const { + size_t result = sizeof(uint32_t); /* certificate_chain.entry_count */ + for (size_t i = 0; i < certificate_chain.entry_count; ++i) { + result += sizeof(uint32_t); /* certificate_chain.entries[i].data_length */ + result += certificate_chain.entries[i].data_length; + } + return result; +} + +uint8_t* AttestKeyResponse::NonErrorSerialize(uint8_t* buf, const uint8_t* end) const { + buf = append_uint32_to_buf(buf, end, certificate_chain.entry_count); + for (size_t i = 0; i < certificate_chain.entry_count; ++i) { + buf = append_size_and_data_to_buf(buf, end, certificate_chain.entries[i].data, + certificate_chain.entries[i].data_length); + } + return buf; +} + +bool AttestKeyResponse::NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + size_t entry_count; + if (!copy_uint32_from_buf(buf_ptr, end, &entry_count) || !AllocateChain(entry_count)) + return false; + + for (size_t i = 0; i < certificate_chain.entry_count; ++i) { + UniquePtr<uint8_t[]> data; + size_t data_length; + if (!copy_size_and_data_from_buf(buf_ptr, end, &data_length, &data)) + return false; + certificate_chain.entries[i].data = data.release(); + certificate_chain.entries[i].data_length = data_length; + } + + return true; +} + +} // namespace keymaster
diff --git a/keymaster/android_keymaster_messages_test.cpp b/keymaster/android_keymaster_messages_test.cpp new file mode 100644 index 0000000..1ec5894 --- /dev/null +++ b/keymaster/android_keymaster_messages_test.cpp
@@ -0,0 +1,683 @@ +/* + * Copyright (C) 2014 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 <UniquePtr.h> + +#include <gtest/gtest.h> + +#include <keymaster/android_keymaster.h> +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/keymaster_tags.h> + +#include "android_keymaster_test_utils.h" + +namespace keymaster { +namespace test { + +/** + * Serialize and deserialize a message. + */ +template <typename Message> +Message* round_trip(int32_t ver, const Message& message, size_t expected_size) { + size_t size = message.SerializedSize(); + EXPECT_EQ(expected_size, size); + if (size == 0) + return NULL; + + UniquePtr<uint8_t[]> buf(new uint8_t[size]); + EXPECT_EQ(buf.get() + size, message.Serialize(buf.get(), buf.get() + size)); + + Message* deserialized = new Message(ver); + const uint8_t* p = buf.get(); + EXPECT_TRUE(deserialized->Deserialize(&p, p + size)); + EXPECT_EQ((ptrdiff_t)size, p - buf.get()); + return deserialized; +} + +struct EmptyKeymasterResponse : public KeymasterResponse { + explicit EmptyKeymasterResponse(int32_t ver) : KeymasterResponse(ver) {} + size_t NonErrorSerializedSize() const { return 1; } + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* /* end */) const { + *buf++ = 0; + return buf; + } + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) { + if (*buf_ptr >= end) + return false; + EXPECT_EQ(0, **buf_ptr); + (*buf_ptr)++; + return true; + } +}; + +TEST(RoundTrip, EmptyKeymasterResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + EmptyKeymasterResponse msg(ver); + msg.error = KM_ERROR_OK; + + UniquePtr<EmptyKeymasterResponse> deserialized(round_trip(ver, msg, 5)); + } +} + +TEST(RoundTrip, EmptyKeymasterResponseError) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + EmptyKeymasterResponse msg(ver); + msg.error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + + UniquePtr<EmptyKeymasterResponse> deserialized(round_trip(ver, msg, 4)); + } +} + +TEST(RoundTrip, SupportedByAlgorithmRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + SupportedByAlgorithmRequest req(ver); + req.algorithm = KM_ALGORITHM_EC; + + UniquePtr<SupportedByAlgorithmRequest> deserialized(round_trip(ver, req, 4)); + EXPECT_EQ(KM_ALGORITHM_EC, deserialized->algorithm); + } +} + +TEST(RoundTrip, SupportedByAlgorithmAndPurposeRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + SupportedByAlgorithmAndPurposeRequest req(ver); + req.algorithm = KM_ALGORITHM_EC; + req.purpose = KM_PURPOSE_DECRYPT; + + UniquePtr<SupportedByAlgorithmAndPurposeRequest> deserialized(round_trip(ver, req, 8)); + EXPECT_EQ(KM_ALGORITHM_EC, deserialized->algorithm); + EXPECT_EQ(KM_PURPOSE_DECRYPT, deserialized->purpose); + } +} + +TEST(RoundTrip, SupportedResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + SupportedResponse<keymaster_digest_t> rsp(ver); + keymaster_digest_t digests[] = {KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1}; + rsp.error = KM_ERROR_OK; + rsp.SetResults(digests); + + UniquePtr<SupportedResponse<keymaster_digest_t>> deserialized(round_trip(ver, rsp, 20)); + EXPECT_EQ(array_length(digests), deserialized->results_length); + EXPECT_EQ(0, memcmp(deserialized->results, digests, array_size(digests))); + } +} + +static keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), + Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), + Authorization(TAG_USER_ID, 7), + Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD), + Authorization(TAG_APPLICATION_ID, "app_id", 6), + Authorization(TAG_AUTH_TIMEOUT, 300), +}; +uint8_t TEST_DATA[] = "a key blob"; + +TEST(RoundTrip, GenerateKeyRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + GenerateKeyRequest req(ver); + req.key_description.Reinitialize(params, array_length(params)); + UniquePtr<GenerateKeyRequest> deserialized(round_trip(ver, req, 78)); + EXPECT_EQ(deserialized->key_description, req.key_description); + } +} + +TEST(RoundTrip, GenerateKeyResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + GenerateKeyResponse rsp(ver); + rsp.error = KM_ERROR_OK; + rsp.key_blob.key_material = dup_array(TEST_DATA); + rsp.key_blob.key_material_size = array_length(TEST_DATA); + rsp.enforced.Reinitialize(params, array_length(params)); + + UniquePtr<GenerateKeyResponse> deserialized(round_trip(ver, rsp, 109)); + EXPECT_EQ(KM_ERROR_OK, deserialized->error); + EXPECT_EQ(deserialized->enforced, rsp.enforced); + EXPECT_EQ(deserialized->unenforced, rsp.unenforced); + } +} + +TEST(RoundTrip, GenerateKeyResponseTestError) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + GenerateKeyResponse rsp(ver); + rsp.error = KM_ERROR_UNSUPPORTED_ALGORITHM; + rsp.key_blob.key_material = dup_array(TEST_DATA); + rsp.key_blob.key_material_size = array_length(TEST_DATA); + rsp.enforced.Reinitialize(params, array_length(params)); + + UniquePtr<GenerateKeyResponse> deserialized(round_trip(ver, rsp, 4)); + EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM, deserialized->error); + EXPECT_EQ(0U, deserialized->enforced.size()); + EXPECT_EQ(0U, deserialized->unenforced.size()); + EXPECT_EQ(0U, deserialized->key_blob.key_material_size); + } +} + +TEST(RoundTrip, GetKeyCharacteristicsRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + GetKeyCharacteristicsRequest req(ver); + req.additional_params.Reinitialize(params, array_length(params)); + req.SetKeyMaterial("foo", 3); + + UniquePtr<GetKeyCharacteristicsRequest> deserialized(round_trip(ver, req, 85)); + EXPECT_EQ(7U, deserialized->additional_params.size()); + EXPECT_EQ(3U, deserialized->key_blob.key_material_size); + EXPECT_EQ(0, memcmp(deserialized->key_blob.key_material, "foo", 3)); + } +} + +TEST(RoundTrip, GetKeyCharacteristicsResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + GetKeyCharacteristicsResponse msg(ver); + msg.error = KM_ERROR_OK; + msg.enforced.Reinitialize(params, array_length(params)); + msg.unenforced.Reinitialize(params, array_length(params)); + + UniquePtr<GetKeyCharacteristicsResponse> deserialized(round_trip(ver, msg, 160)); + EXPECT_EQ(msg.enforced, deserialized->enforced); + EXPECT_EQ(msg.unenforced, deserialized->unenforced); + } +} + +TEST(RoundTrip, BeginOperationRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + BeginOperationRequest msg(ver); + msg.purpose = KM_PURPOSE_SIGN; + msg.SetKeyMaterial("foo", 3); + msg.additional_params.Reinitialize(params, array_length(params)); + + UniquePtr<BeginOperationRequest> deserialized(round_trip(ver, msg, 89)); + EXPECT_EQ(KM_PURPOSE_SIGN, deserialized->purpose); + EXPECT_EQ(3U, deserialized->key_blob.key_material_size); + EXPECT_EQ(0, memcmp(deserialized->key_blob.key_material, "foo", 3)); + EXPECT_EQ(msg.additional_params, deserialized->additional_params); + } +} + +TEST(RoundTrip, BeginOperationResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + BeginOperationResponse msg(ver); + msg.error = KM_ERROR_OK; + msg.op_handle = 0xDEADBEEF; + msg.output_params.push_back(Authorization(TAG_NONCE, "foo", 3)); + + UniquePtr<BeginOperationResponse> deserialized; + switch (ver) { + case 0: + deserialized.reset(round_trip(ver, msg, 12)); + break; + case 1: + case 2: + case 3: + deserialized.reset(round_trip(ver, msg, 39)); + break; + default: + FAIL(); + } + + EXPECT_EQ(KM_ERROR_OK, deserialized->error); + EXPECT_EQ(0xDEADBEEF, deserialized->op_handle); + + switch (ver) { + case 0: + EXPECT_EQ(0U, deserialized->output_params.size()); + break; + case 1: + case 2: + case 3: + EXPECT_EQ(msg.output_params, deserialized->output_params); + break; + default: + FAIL(); + } + } +} + +TEST(RoundTrip, BeginOperationResponseError) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + BeginOperationResponse msg(ver); + msg.error = KM_ERROR_INVALID_OPERATION_HANDLE; + msg.op_handle = 0xDEADBEEF; + + UniquePtr<BeginOperationResponse> deserialized(round_trip(ver, msg, 4)); + EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, deserialized->error); + } +} + +TEST(RoundTrip, UpdateOperationRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + UpdateOperationRequest msg(ver); + msg.op_handle = 0xDEADBEEF; + msg.input.Reinitialize("foo", 3); + + UniquePtr<UpdateOperationRequest> deserialized; + switch (ver) { + case 0: + deserialized.reset(round_trip(ver, msg, 15)); + break; + case 1: + case 2: + case 3: + deserialized.reset(round_trip(ver, msg, 27)); + break; + default: + FAIL(); + } + EXPECT_EQ(3U, deserialized->input.available_read()); + EXPECT_EQ(0, memcmp(deserialized->input.peek_read(), "foo", 3)); + } +} + +TEST(RoundTrip, UpdateOperationResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + UpdateOperationResponse msg(ver); + msg.error = KM_ERROR_OK; + msg.output.Reinitialize("foo", 3); + msg.input_consumed = 99; + msg.output_params.push_back(TAG_APPLICATION_ID, "bar", 3); + + UniquePtr<UpdateOperationResponse> deserialized; + switch (ver) { + case 0: + deserialized.reset(round_trip(ver, msg, 11)); + break; + case 1: + deserialized.reset(round_trip(ver, msg, 15)); + break; + case 2: + case 3: + deserialized.reset(round_trip(ver, msg, 42)); + break; + default: + FAIL(); + } + EXPECT_EQ(KM_ERROR_OK, deserialized->error); + EXPECT_EQ(3U, deserialized->output.available_read()); + EXPECT_EQ(0, memcmp(deserialized->output.peek_read(), "foo", 3)); + + switch (ver) { + case 0: + EXPECT_EQ(0U, deserialized->input_consumed); + break; + case 1: + EXPECT_EQ(99U, deserialized->input_consumed); + break; + case 2: + case 3: + EXPECT_EQ(99U, deserialized->input_consumed); + EXPECT_EQ(1U, deserialized->output_params.size()); + break; + default: + FAIL(); + } + } +} + +TEST(RoundTrip, FinishOperationRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + FinishOperationRequest msg(ver); + msg.op_handle = 0xDEADBEEF; + msg.signature.Reinitialize("bar", 3); + msg.input.Reinitialize("baz", 3); + + UniquePtr<FinishOperationRequest> deserialized; + switch (ver) { + case 0: + deserialized.reset(round_trip(ver, msg, 15)); + break; + case 1: + case 2: + deserialized.reset(round_trip(ver, msg, 27)); + break; + case 3: + deserialized.reset(round_trip(ver, msg, 34)); + break; + default: + FAIL(); + } + EXPECT_EQ(0xDEADBEEF, deserialized->op_handle); + EXPECT_EQ(3U, deserialized->signature.available_read()); + EXPECT_EQ(0, memcmp(deserialized->signature.peek_read(), "bar", 3)); + } +} + +TEST(Round_Trip, FinishOperationResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + FinishOperationResponse msg(ver); + msg.error = KM_ERROR_OK; + msg.output.Reinitialize("foo", 3); + + UniquePtr<FinishOperationResponse> deserialized; + switch (ver) { + case 0: + case 1: + deserialized.reset(round_trip(ver, msg, 11)); + break; + case 2: + case 3: + deserialized.reset(round_trip(ver, msg, 23)); + break; + default: + FAIL(); + } + EXPECT_EQ(msg.error, deserialized->error); + EXPECT_EQ(msg.output.available_read(), deserialized->output.available_read()); + EXPECT_EQ(0, memcmp(msg.output.peek_read(), deserialized->output.peek_read(), + msg.output.available_read())); + } +} + +TEST(RoundTrip, ImportKeyRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + ImportKeyRequest msg(ver); + msg.key_description.Reinitialize(params, array_length(params)); + msg.key_format = KM_KEY_FORMAT_X509; + msg.SetKeyMaterial("foo", 3); + + UniquePtr<ImportKeyRequest> deserialized(round_trip(ver, msg, 89)); + EXPECT_EQ(msg.key_description, deserialized->key_description); + EXPECT_EQ(msg.key_format, deserialized->key_format); + EXPECT_EQ(msg.key_data_length, deserialized->key_data_length); + EXPECT_EQ(0, memcmp(msg.key_data, deserialized->key_data, msg.key_data_length)); + } +} + +TEST(RoundTrip, ImportKeyResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + ImportKeyResponse msg(ver); + msg.error = KM_ERROR_OK; + msg.SetKeyMaterial("foo", 3); + msg.enforced.Reinitialize(params, array_length(params)); + msg.unenforced.Reinitialize(params, array_length(params)); + + UniquePtr<ImportKeyResponse> deserialized(round_trip(ver, msg, 167)); + EXPECT_EQ(msg.error, deserialized->error); + EXPECT_EQ(msg.key_blob.key_material_size, deserialized->key_blob.key_material_size); + EXPECT_EQ(0, memcmp(msg.key_blob.key_material, deserialized->key_blob.key_material, + msg.key_blob.key_material_size)); + EXPECT_EQ(msg.enforced, deserialized->enforced); + EXPECT_EQ(msg.unenforced, deserialized->unenforced); + } +} + +TEST(RoundTrip, ExportKeyRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + ExportKeyRequest msg(ver); + msg.additional_params.Reinitialize(params, array_length(params)); + msg.key_format = KM_KEY_FORMAT_X509; + msg.SetKeyMaterial("foo", 3); + + UniquePtr<ExportKeyRequest> deserialized(round_trip(ver, msg, 89)); + EXPECT_EQ(msg.additional_params, deserialized->additional_params); + EXPECT_EQ(msg.key_format, deserialized->key_format); + EXPECT_EQ(3U, deserialized->key_blob.key_material_size); + EXPECT_EQ(0, memcmp("foo", deserialized->key_blob.key_material, 3)); + } +} + +TEST(RoundTrip, ExportKeyResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + ExportKeyResponse msg(ver); + msg.error = KM_ERROR_OK; + msg.SetKeyMaterial("foo", 3); + + UniquePtr<ExportKeyResponse> deserialized(round_trip(ver, msg, 11)); + EXPECT_EQ(3U, deserialized->key_data_length); + EXPECT_EQ(0, memcmp("foo", deserialized->key_data, 3)); + } +} + +TEST(RoundTrip, DeleteKeyRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + DeleteKeyRequest msg(ver); + msg.SetKeyMaterial("foo", 3); + + UniquePtr<DeleteKeyRequest> deserialized(round_trip(ver, msg, 7)); + EXPECT_EQ(3U, deserialized->key_blob.key_material_size); + EXPECT_EQ(0, memcmp("foo", deserialized->key_blob.key_material, 3)); + } +} + +TEST(RoundTrip, DeleteKeyResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + DeleteKeyResponse msg(ver); + UniquePtr<DeleteKeyResponse> deserialized(round_trip(ver, msg, 4)); + } +} + +TEST(RoundTrip, DeleteAllKeysRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + DeleteAllKeysRequest msg(ver); + UniquePtr<DeleteAllKeysRequest> deserialized(round_trip(ver, msg, 0)); + } +} + +TEST(RoundTrip, DeleteAllKeysResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + DeleteAllKeysResponse msg(ver); + UniquePtr<DeleteAllKeysResponse> deserialized(round_trip(ver, msg, 4)); + } +} + +TEST(RoundTrip, GetVersionRequest) { + GetVersionRequest msg; + + size_t size = msg.SerializedSize(); + ASSERT_EQ(0U, size); + + UniquePtr<uint8_t[]> buf(new uint8_t[size]); + EXPECT_EQ(buf.get() + size, msg.Serialize(buf.get(), buf.get() + size)); + + GetVersionRequest deserialized; + const uint8_t* p = buf.get(); + EXPECT_TRUE(deserialized.Deserialize(&p, p + size)); + EXPECT_EQ((ptrdiff_t)size, p - buf.get()); +} + +TEST(RoundTrip, GetVersionResponse) { + GetVersionResponse msg; + msg.error = KM_ERROR_OK; + msg.major_ver = 9; + msg.minor_ver = 98; + msg.subminor_ver = 38; + + size_t size = msg.SerializedSize(); + ASSERT_EQ(7U, size); + + UniquePtr<uint8_t[]> buf(new uint8_t[size]); + EXPECT_EQ(buf.get() + size, msg.Serialize(buf.get(), buf.get() + size)); + + GetVersionResponse deserialized; + const uint8_t* p = buf.get(); + EXPECT_TRUE(deserialized.Deserialize(&p, p + size)); + EXPECT_EQ((ptrdiff_t)size, p - buf.get()); + EXPECT_EQ(9U, msg.major_ver); + EXPECT_EQ(98U, msg.minor_ver); + EXPECT_EQ(38U, msg.subminor_ver); +} + +TEST(RoundTrip, AddEntropyRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + AddEntropyRequest msg(ver); + msg.random_data.Reinitialize("foo", 3); + + UniquePtr<AddEntropyRequest> deserialized(round_trip(ver, msg, 7)); + EXPECT_EQ(3U, deserialized->random_data.available_read()); + EXPECT_EQ(0, memcmp("foo", deserialized->random_data.peek_read(), 3)); + } +} + +TEST(RoundTrip, AddEntropyResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + AddEntropyResponse msg(ver); + UniquePtr<AddEntropyResponse> deserialized(round_trip(ver, msg, 4)); + } +} + +TEST(RoundTrip, AbortOperationRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + AbortOperationRequest msg(ver); + UniquePtr<AbortOperationRequest> deserialized(round_trip(ver, msg, 8)); + } +} + +TEST(RoundTrip, AbortOperationResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + AbortOperationResponse msg(ver); + UniquePtr<AbortOperationResponse> deserialized(round_trip(ver, msg, 4)); + } +} + +TEST(RoundTrip, AttestKeyRequest) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + AttestKeyRequest msg(ver); + msg.SetKeyMaterial("foo", 3); + msg.attest_params.Reinitialize(params, array_length(params)); + + UniquePtr<AttestKeyRequest> deserialized(round_trip(ver, msg, 85)); + EXPECT_EQ(3U, deserialized->key_blob.key_material_size); + EXPECT_EQ(0, memcmp("foo", deserialized->key_blob.key_material, 3)); + EXPECT_EQ(msg.attest_params, deserialized->attest_params); + } +} + +TEST(RoundTrip, AttestKeyResponse) { + for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + AttestKeyResponse msg(ver); + msg.error = KM_ERROR_OK; + EXPECT_TRUE(msg.AllocateChain(3)); + msg.certificate_chain.entries[0] = {dup_buffer("foo", 3), 3}; + msg.certificate_chain.entries[1] = {dup_buffer("bar", 3), 3}; + msg.certificate_chain.entries[2] = {dup_buffer("baz", 3), 3}; + + UniquePtr<AttestKeyResponse> deserialized(round_trip(ver, msg, 29)); + keymaster_cert_chain_t* chain = &deserialized->certificate_chain; + + EXPECT_NE(nullptr, chain->entries); + EXPECT_EQ(3U, chain->entry_count); + EXPECT_EQ(3U, chain->entries[0].data_length); + EXPECT_EQ(0, memcmp("foo", chain->entries[0].data, 3)); + EXPECT_EQ(3U, chain->entries[1].data_length); + EXPECT_EQ(0, memcmp("bar", chain->entries[1].data, 3)); + EXPECT_EQ(3U, chain->entries[2].data_length); + EXPECT_EQ(0, memcmp("baz", chain->entries[2].data, 3)); + } +} + +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() { + for (int32_t ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) { + Message msg(ver); + const uint8_t* end = msgbuf + array_length(msgbuf); + for (size_t i = 0; i < array_length(msgbuf); ++i) { + const uint8_t* begin = msgbuf + i; + const uint8_t* p = begin; + msg.Deserialize(&p, end); + } + } + + time_t now = time(NULL); + std::cout << "Seeding rand() with " << now << " for fuzz test." << std::endl; + srand(now); + + // Fill large buffer with random bytes. + const int kBufSize = 10000; + UniquePtr<uint8_t[]> buf(new uint8_t[kBufSize]); + for (size_t i = 0; i < kBufSize; ++i) + buf[i] = static_cast<uint8_t>(rand()); + + for (uint32_t ver = 0; ver < MAX_MESSAGE_VERSION; ++ver) { + Message msg(ver); + const uint8_t* end = buf.get() + kBufSize; + for (size_t i = 0; i < kBufSize; ++i) { + const uint8_t* begin = buf.get() + i; + const uint8_t* p = begin; + msg.Deserialize(&p, end); + } + } +} + +#define GARBAGE_TEST(Message) \ + TEST(GarbageTest, Message) { parse_garbage<Message>(); } + +GARBAGE_TEST(AbortOperationRequest); +GARBAGE_TEST(AbortOperationResponse); +GARBAGE_TEST(AddEntropyRequest); +GARBAGE_TEST(AddEntropyResponse); +GARBAGE_TEST(BeginOperationRequest); +GARBAGE_TEST(BeginOperationResponse); +GARBAGE_TEST(DeleteAllKeysRequest); +GARBAGE_TEST(DeleteAllKeysResponse); +GARBAGE_TEST(DeleteKeyRequest); +GARBAGE_TEST(DeleteKeyResponse); +GARBAGE_TEST(ExportKeyRequest); +GARBAGE_TEST(ExportKeyResponse); +GARBAGE_TEST(FinishOperationRequest); +GARBAGE_TEST(FinishOperationResponse); +GARBAGE_TEST(GenerateKeyRequest); +GARBAGE_TEST(GenerateKeyResponse); +GARBAGE_TEST(GetKeyCharacteristicsRequest); +GARBAGE_TEST(GetKeyCharacteristicsResponse); +GARBAGE_TEST(ImportKeyRequest); +GARBAGE_TEST(ImportKeyResponse); +GARBAGE_TEST(SupportedByAlgorithmAndPurposeRequest) +GARBAGE_TEST(SupportedByAlgorithmRequest) +GARBAGE_TEST(UpdateOperationRequest); +GARBAGE_TEST(UpdateOperationResponse); +GARBAGE_TEST(AttestKeyRequest); +GARBAGE_TEST(AttestKeyResponse); + +// The macro doesn't work on this one. +TEST(GarbageTest, SupportedResponse) { + parse_garbage<SupportedResponse<keymaster_digest_t>>(); +} + +} // namespace test + +} // namespace keymaster
diff --git a/keymaster/android_keymaster_test.cpp b/keymaster/android_keymaster_test.cpp new file mode 100644 index 0000000..ddc9783 --- /dev/null +++ b/keymaster/android_keymaster_test.cpp
@@ -0,0 +1,3599 @@ +/* + * Copyright (C) 2014 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 <fstream> +#include <string> +#include <vector> + +#include <openssl/evp.h> +#include <openssl/x509.h> + +#include <hardware/keymaster0.h> +#include <keymaster/key_factory.h> +#include <keymaster/soft_keymaster_context.h> +#include <keymaster/soft_keymaster_device.h> +#include <keymaster/softkeymaster.h> + +#include "android_keymaster_test_utils.h" +#include "attestation_record.h" +#include "keymaster0_engine.h" +#include "openssl_utils.h" + +using std::ifstream; +using std::istreambuf_iterator; +using std::ofstream; +using std::string; +using std::unique_ptr; +using std::vector; + +extern "C" { +int __android_log_print(int prio, const char* tag, const char* fmt); +int __android_log_print(int prio, const char* tag, const char* fmt) { + (void)prio, (void)tag, (void)fmt; + return 0; +} +} // extern "C" + +namespace keymaster { +namespace test { + +StdoutLogger logger; + +template <typename T> vector<T> make_vector(const T* array, size_t len) { + return vector<T>(array, array + len); +} + +/** + * KeymasterEnforcement class for use in testing. It's permissive in the sense that it doesn't + * check cryptoperiods, but restrictive in the sense that the clock never advances (so rate-limited + * keys will only work once). + */ +class TestKeymasterEnforcement : public KeymasterEnforcement { + public: + TestKeymasterEnforcement() : KeymasterEnforcement(3, 3) {} + + virtual bool activation_date_valid(uint64_t /* activation_date */) const { return true; } + virtual bool expiration_date_passed(uint64_t /* expiration_date */) const { return false; } + virtual bool auth_token_timed_out(const hw_auth_token_t& /* token */, + uint32_t /* timeout */) const { + return false; + } + virtual uint32_t get_current_time() const { return 0; } + virtual bool ValidateTokenSignature(const hw_auth_token_t& /* token */) const { return true; } +}; + +/** + * Variant of SoftKeymasterContext that provides a TestKeymasterEnforcement. + */ +class TestKeymasterContext : public SoftKeymasterContext { + public: + TestKeymasterContext() {} + explicit TestKeymasterContext(const string& root_of_trust) : SoftKeymasterContext(root_of_trust) {} + + KeymasterEnforcement* enforcement_policy() override { return &test_policy_; } + + private: + TestKeymasterEnforcement test_policy_; +}; + +/** + * Test instance creator that builds a pure software keymaster1 implementations. + */ +class SoftKeymasterTestInstanceCreator : public Keymaster2TestInstanceCreator { + public: + keymaster2_device_t* CreateDevice() const override { + std::cerr << "Creating software-only device" << std::endl; + SoftKeymasterDevice* device = new SoftKeymasterDevice(new TestKeymasterContext); + return device->keymaster2_device(); + } + + bool algorithm_in_km0_hardware(keymaster_algorithm_t) const override { return false; } + int keymaster0_calls() const override { return 0; } +}; + +/** + * Test instance creator that builds keymaster1 instances which wrap a faked hardware keymaster0 + * instance, with or without EC support. + */ +class Keymaster0AdapterTestInstanceCreator : public Keymaster2TestInstanceCreator { + public: + explicit Keymaster0AdapterTestInstanceCreator(bool support_ec) : support_ec_(support_ec) {} + + keymaster2_device_t* CreateDevice() const { + std::cerr << "Creating keymaster0-backed device (with ec: " << std::boolalpha << support_ec_ + << ")." << std::endl; + hw_device_t* softkeymaster_device; + EXPECT_EQ(0, openssl_open(&softkeymaster_module.common, KEYSTORE_KEYMASTER, + &softkeymaster_device)); + // Make the software device pretend to be hardware + keymaster0_device_t* keymaster0_device = + reinterpret_cast<keymaster0_device_t*>(softkeymaster_device); + keymaster0_device->flags &= ~KEYMASTER_SOFTWARE_ONLY; + + if (!support_ec_) { + // Make the software device pretend not to support EC + keymaster0_device->flags &= ~KEYMASTER_SUPPORTS_EC; + } + + counting_keymaster0_device_ = new Keymaster0CountingWrapper(keymaster0_device); + + SoftKeymasterDevice* keymaster = new SoftKeymasterDevice(new TestKeymasterContext); + keymaster->SetHardwareDevice(counting_keymaster0_device_); + return keymaster->keymaster2_device(); + } + + bool algorithm_in_km0_hardware(keymaster_algorithm_t algorithm) const override { + switch (algorithm) { + case KM_ALGORITHM_RSA: + return true; + case KM_ALGORITHM_EC: + return support_ec_; + default: + return false; + } + } + int keymaster0_calls() const override { return counting_keymaster0_device_->count(); } + + private: + mutable Keymaster0CountingWrapper* counting_keymaster0_device_; + bool support_ec_; +}; + +/** + * Test instance creator that builds a SoftKeymasterDevice which wraps a fake hardware keymaster1 + * instance, with minimal digest support. + */ +class Sha256OnlyKeymaster1TestInstanceCreator : public Keymaster2TestInstanceCreator { + keymaster2_device_t* CreateDevice() const { + std::cerr << "Creating keymaster1-backed device that supports only SHA256"; + + // fake_device doesn't leak because device (below) takes ownership of it. + keymaster1_device_t* fake_device = make_device_sha256_only( + (new SoftKeymasterDevice(new TestKeymasterContext("PseudoHW")))->keymaster_device()); + + // device doesn't leak; it's cleaned up by device->keymaster_device()->common.close(). + SoftKeymasterDevice* device = new SoftKeymasterDevice(new TestKeymasterContext); + device->SetHardwareDevice(fake_device); + + return device->keymaster2_device(); + } + + bool algorithm_in_km0_hardware(keymaster_algorithm_t) const override { return false; } + int keymaster0_calls() const override { return 0; } + int minimal_digest_set() const override { return true; } +}; + +static auto test_params = testing::Values( + InstanceCreatorPtr(new SoftKeymasterTestInstanceCreator), + InstanceCreatorPtr(new Keymaster0AdapterTestInstanceCreator(true /* support_ec */)), + InstanceCreatorPtr(new Keymaster0AdapterTestInstanceCreator(false /* support_ec */)), + InstanceCreatorPtr(new Sha256OnlyKeymaster1TestInstanceCreator)); + +class NewKeyGeneration : public Keymaster2Test { + protected: + void CheckBaseParams() { + AuthorizationSet auths = sw_enforced(); + EXPECT_GT(auths.SerializedSize(), 12U); + + EXPECT_TRUE(contains(auths, TAG_PURPOSE, KM_PURPOSE_SIGN)); + EXPECT_TRUE(contains(auths, TAG_PURPOSE, KM_PURPOSE_VERIFY)); + EXPECT_TRUE(contains(auths, TAG_USER_ID, 7)); + EXPECT_TRUE(contains(auths, TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)); + EXPECT_TRUE(contains(auths, TAG_AUTH_TIMEOUT, 300)); + + // Verify that App ID, App data and ROT are NOT included. + EXPECT_FALSE(contains(auths, TAG_ROOT_OF_TRUST)); + EXPECT_FALSE(contains(auths, TAG_APPLICATION_ID)); + EXPECT_FALSE(contains(auths, TAG_APPLICATION_DATA)); + + // Just for giggles, check that some unexpected tags/values are NOT present. + EXPECT_FALSE(contains(auths, TAG_PURPOSE, KM_PURPOSE_ENCRYPT)); + EXPECT_FALSE(contains(auths, TAG_PURPOSE, KM_PURPOSE_DECRYPT)); + EXPECT_FALSE(contains(auths, TAG_AUTH_TIMEOUT, 301)); + + // Now check that unspecified, defaulted tags are correct. + EXPECT_TRUE(contains(auths, KM_TAG_CREATION_DATETIME)); + } +}; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, NewKeyGeneration, test_params); + +TEST_P(NewKeyGeneration, Rsa) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + CheckBaseParams(); + + // Check specified tags are all present, and in the right set. + AuthorizationSet crypto_params; + AuthorizationSet non_crypto_params; + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) { + EXPECT_NE(0U, hw_enforced().size()); + EXPECT_NE(0U, sw_enforced().size()); + crypto_params.push_back(hw_enforced()); + non_crypto_params.push_back(sw_enforced()); + } else { + EXPECT_EQ(0U, hw_enforced().size()); + EXPECT_NE(0U, sw_enforced().size()); + crypto_params.push_back(sw_enforced()); + } + + EXPECT_TRUE(contains(crypto_params, TAG_ALGORITHM, KM_ALGORITHM_RSA)); + EXPECT_FALSE(contains(non_crypto_params, TAG_ALGORITHM, KM_ALGORITHM_RSA)); + EXPECT_TRUE(contains(crypto_params, TAG_KEY_SIZE, 256)); + EXPECT_FALSE(contains(non_crypto_params, TAG_KEY_SIZE, 256)); + EXPECT_TRUE(contains(crypto_params, TAG_RSA_PUBLIC_EXPONENT, 3)); + EXPECT_FALSE(contains(non_crypto_params, TAG_RSA_PUBLIC_EXPONENT, 3)); + + EXPECT_EQ(KM_ERROR_OK, DeleteKey()); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, RsaDefaultSize) { + ASSERT_EQ(KM_ERROR_UNSUPPORTED_KEY_SIZE, + GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3) + .SigningKey())); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, Ecdsa) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE))); + CheckBaseParams(); + + // Check specified tags are all present, and in the right set. + AuthorizationSet crypto_params; + AuthorizationSet non_crypto_params; + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) { + EXPECT_NE(0U, hw_enforced().size()); + EXPECT_NE(0U, sw_enforced().size()); + crypto_params.push_back(hw_enforced()); + non_crypto_params.push_back(sw_enforced()); + } else { + EXPECT_EQ(0U, hw_enforced().size()); + EXPECT_NE(0U, sw_enforced().size()); + crypto_params.push_back(sw_enforced()); + } + + EXPECT_TRUE(contains(crypto_params, TAG_ALGORITHM, KM_ALGORITHM_EC)); + EXPECT_FALSE(contains(non_crypto_params, TAG_ALGORITHM, KM_ALGORITHM_EC)); + EXPECT_TRUE(contains(crypto_params, TAG_KEY_SIZE, 224)); + EXPECT_FALSE(contains(non_crypto_params, TAG_KEY_SIZE, 224)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(1, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, EcdsaDefaultSize) { + ASSERT_EQ(KM_ERROR_UNSUPPORTED_KEY_SIZE, + GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC) + .SigningKey() + .Digest(KM_DIGEST_NONE))); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, EcdsaInvalidSize) { + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + ASSERT_EQ( + KM_ERROR_UNKNOWN_ERROR, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(190).Digest(KM_DIGEST_NONE))); + else + ASSERT_EQ( + KM_ERROR_UNSUPPORTED_KEY_SIZE, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(190).Digest(KM_DIGEST_NONE))); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(1, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, EcdsaAllValidSizes) { + size_t valid_sizes[] = {224, 256, 384, 521}; + for (size_t size : valid_sizes) { + EXPECT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(size).Digest( + KM_DIGEST_NONE))) + << "Failed to generate size: " << size; + } + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, HmacSha256) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 256))); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, HmacMultipleDigests) { + ASSERT_EQ(KM_ERROR_UNSUPPORTED_DIGEST, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA1) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, HmacDigestNone) { + ASSERT_EQ(KM_ERROR_UNSUPPORTED_DIGEST, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, HmacSha256TooShortMacLength) { + ASSERT_EQ(KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 48))); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, HmacSha256NonIntegralOctetMacLength) { + ASSERT_EQ(KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 130))); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(NewKeyGeneration, HmacSha256TooLongMacLength) { + ASSERT_EQ(KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH, + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 384))); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +typedef Keymaster2Test GetKeyCharacteristics; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, GetKeyCharacteristics, test_params); + +TEST_P(GetKeyCharacteristics, SimpleRsa) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + AuthorizationSet original(sw_enforced()); + + ASSERT_EQ(KM_ERROR_OK, GetCharacteristics()); + EXPECT_EQ(original, sw_enforced()); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(1, GetParam()->keymaster0_calls()); +} + +typedef Keymaster2Test SigningOperationsTest; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, SigningOperationsTest, test_params); + +TEST_P(SigningOperationsTest, RsaSuccess) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + string message = "12345678901234567890123456789012"; + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaPssSha256Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_RSA_PSS))); + // Use large message, which won't work without digesting. + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + string message = "12345678901234567890123456789012"; + string signature; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN); + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PADDING_MODE, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaPkcs1Sha256Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN))); + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaPkcs1NoDigestSuccess) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN))); + string message(53, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_RSA_PKCS1_1_5_SIGN); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaPkcs1NoDigestTooLarge) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN))); + string message(54, 'a'); + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + string signature; + EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&signature)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaPssSha256TooSmallKey) { + // Key must be at least 10 bytes larger than hash, to provide eight bytes of random salt, so + // verify that nine bytes larger than hash won't work. + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256 + 9 * 8, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_RSA_PSS))); + string message(1024, 'a'); + string signature; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PSS); + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_DIGEST, BeginOperation(KM_PURPOSE_SIGN, begin_params)); +} + +TEST_P(SigningOperationsTest, RsaNoPaddingHugeData) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN))); + string message(64 * 1024, 'a'); + string signature; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN); + ASSERT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, UpdateOperation(message, &result, &input_consumed)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaAbort) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + ASSERT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + EXPECT_EQ(KM_ERROR_OK, AbortOperation()); + // Another abort should fail + EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, AbortOperation()); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaUnsupportedPadding) { + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_SHA_2_256 /* supported digest */) + .Padding(KM_PAD_PKCS7)); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + ASSERT_EQ(KM_ERROR_UNSUPPORTED_PADDING_MODE, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaNoDigest) { + // PSS requires a digest. + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_RSA_PSS)); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PSS); + ASSERT_EQ(KM_ERROR_INCOMPATIBLE_DIGEST, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaNoPadding) { + // Padding must be specified + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaKey(256, 3).SigningKey().Digest( + KM_DIGEST_NONE))); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + ASSERT_EQ(KM_ERROR_UNSUPPORTED_PADDING_MODE, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaTooShortMessage) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + string message = "1234567890123456789012345678901"; + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaSignWithEncryptionKey) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + ASSERT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, RsaSignTooLargeMessage) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + string message(256 / 8, static_cast<char>(0xff)); + string signature; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + ASSERT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + string result; + size_t input_consumed; + ASSERT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + ASSERT_EQ(message.size(), input_consumed); + string output; + ASSERT_EQ(KM_ERROR_INVALID_ARGUMENT, FinishOperation(&output)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, EcdsaSuccess) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE))); + string message(224 / 8, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, EcdsaSha256Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest( + KM_DIGEST_SHA_2_256))); + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, EcdsaSha384Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest( + KM_DIGEST_SHA_2_384))); + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_384); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, EcdsaNoPaddingHugeData) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE))); + string message(64 * 1024, 'a'); + string signature; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + ASSERT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params)); + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, EcdsaAllSizesAndHashes) { + vector<int> key_sizes = {224, 256, 384, 521}; + vector<keymaster_digest_t> digests = { + KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, KM_DIGEST_SHA_2_256, + KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512, + }; + + for (int key_size : key_sizes) { + for (keymaster_digest_t digest : digests) { + ASSERT_EQ( + KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(key_size).Digest(digest))); + + string message(1024, 'a'); + string signature; + if (digest == KM_DIGEST_NONE) + message.resize(key_size / 8); + SignMessage(message, &signature, digest); + } + } + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(digests.size() * key_sizes.size() * 3, + static_cast<size_t>(GetParam()->keymaster0_calls())); +} + +TEST_P(SigningOperationsTest, AesEcbSign) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().AesEncryptionKey(128).Authorization( + TAG_BLOCK_MODE, KM_MODE_ECB))); + ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_SIGN)); + ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_VERIFY)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacSha1Success) { + if (GetParam()->minimal_digest_set()) + // Can't emulate other digests for HMAC. + return; + + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA1) + .Authorization(TAG_MIN_MAC_LENGTH, 160)); + string message = "12345678901234567890123456789012"; + string signature; + MacMessage(message, &signature, 160); + ASSERT_EQ(20U, signature.size()); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacSha224Success) { + if (GetParam()->minimal_digest_set()) + // Can't emulate other digests for HMAC. + return; + + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_224) + .Authorization(TAG_MIN_MAC_LENGTH, 160))); + string message = "12345678901234567890123456789012"; + string signature; + MacMessage(message, &signature, 224); + ASSERT_EQ(28U, signature.size()); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacSha256Success) { + if (GetParam()->minimal_digest_set()) + // Can't emulate other digests for HMAC. + return; + + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 256))); + string message = "12345678901234567890123456789012"; + string signature; + MacMessage(message, &signature, 256); + ASSERT_EQ(32U, signature.size()); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacSha384Success) { + if (GetParam()->minimal_digest_set()) + // Can't emulate other digests for HMAC. + return; + + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_384) + .Authorization(TAG_MIN_MAC_LENGTH, 384))); + + string message = "12345678901234567890123456789012"; + string signature; + MacMessage(message, &signature, 384); + ASSERT_EQ(48U, signature.size()); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacSha512Success) { + if (GetParam()->minimal_digest_set()) + // Can't emulate other digests for HMAC. + return; + + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_512) + .Authorization(TAG_MIN_MAC_LENGTH, 384))); + string message = "12345678901234567890123456789012"; + string signature; + MacMessage(message, &signature, 512); + ASSERT_EQ(64U, signature.size()); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacLengthInKey) { + // TODO(swillden): unified API should generate an error on key generation. + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string message = "12345678901234567890123456789012"; + string signature; + MacMessage(message, &signature, 160); + ASSERT_EQ(20U, signature.size()); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacRfc4231TestCase1) { + uint8_t key_data[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + }; + string message = "Hi There"; + uint8_t sha_224_expected[] = { + 0x89, 0x6f, 0xb1, 0x12, 0x8a, 0xbb, 0xdf, 0x19, 0x68, 0x32, 0x10, 0x7c, 0xd4, 0x9d, + 0xf3, 0x3f, 0x47, 0xb4, 0xb1, 0x16, 0x99, 0x12, 0xba, 0x4f, 0x53, 0x68, 0x4b, 0x22, + }; + uint8_t sha_256_expected[] = { + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, + 0xce, 0xaf, 0x0b, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, + 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7, + }; + uint8_t sha_384_expected[] = { + 0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62, 0x6b, 0x08, 0x25, 0xf4, + 0xab, 0x46, 0x90, 0x7f, 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6, + 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c, 0xfa, 0xea, 0x9e, 0xa9, + 0x07, 0x6e, 0xde, 0x7f, 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6, + }; + uint8_t sha_512_expected[] = { + 0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d, 0x4f, 0xf0, 0xb4, 0x24, 0x1a, + 0x1d, 0x6c, 0xb0, 0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78, 0x7a, 0xd0, + 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde, 0xda, 0xa8, 0x33, 0xb7, 0xd6, 0xb8, 0xa7, + 0x02, 0x03, 0x8b, 0x27, 0x4e, 0xae, 0xa3, 0xf4, 0xe4, 0xbe, 0x9d, 0x91, 0x4e, + 0xeb, 0x61, 0xf1, 0x70, 0x2e, 0x69, 0x6c, 0x20, 0x3a, 0x12, 0x68, 0x54, + }; + + string key = make_string(key_data); + + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + if (!GetParam()->minimal_digest_set()) { + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected)); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacRfc4231TestCase2) { + string key = "Jefe"; + string message = "what do ya want for nothing?"; + uint8_t sha_224_expected[] = { + 0xa3, 0x0e, 0x01, 0x09, 0x8b, 0xc6, 0xdb, 0xbf, 0x45, 0x69, 0x0f, 0x3a, 0x7e, 0x9e, + 0x6d, 0x0f, 0x8b, 0xbe, 0xa2, 0xa3, 0x9e, 0x61, 0x48, 0x00, 0x8f, 0xd0, 0x5e, 0x44, + }; + uint8_t sha_256_expected[] = { + 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, + 0x26, 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, + 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43, + }; + uint8_t sha_384_expected[] = { + 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31, 0x61, 0x7f, 0x78, 0xd2, + 0xb5, 0x8a, 0x6b, 0x1b, 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47, + 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e, 0x8e, 0x22, 0x40, 0xca, + 0x5e, 0x69, 0xe2, 0xc7, 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49, + }; + uint8_t sha_512_expected[] = { + 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 0xe3, 0x95, 0xfb, 0xe7, 0x3b, + 0x56, 0xe0, 0xa3, 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, 0x10, 0x27, + 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54, 0x97, 0x58, 0xbf, 0x75, 0xc0, 0x5a, 0x99, + 0x4a, 0x6d, 0x03, 0x4f, 0x65, 0xf8, 0xf0, 0xe6, 0xfd, 0xca, 0xea, 0xb1, 0xa3, + 0x4d, 0x4a, 0x6b, 0x4b, 0x63, 0x6e, 0x07, 0x0a, 0x38, 0xbc, 0xe7, 0x37, + }; + + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + if (!GetParam()->minimal_digest_set()) { + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected)); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacRfc4231TestCase3) { + string key(20, 0xaa); + string message(50, 0xdd); + uint8_t sha_224_expected[] = { + 0x7f, 0xb3, 0xcb, 0x35, 0x88, 0xc6, 0xc1, 0xf6, 0xff, 0xa9, 0x69, 0x4d, 0x7d, 0x6a, + 0xd2, 0x64, 0x93, 0x65, 0xb0, 0xc1, 0xf6, 0x5d, 0x69, 0xd1, 0xec, 0x83, 0x33, 0xea, + }; + uint8_t sha_256_expected[] = { + 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, + 0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, + 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe, + }; + uint8_t sha_384_expected[] = { + 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, 0x0a, 0xa2, 0xac, 0xe0, + 0x14, 0xc8, 0xa8, 0x6f, 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb, + 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, 0x2a, 0x5a, 0xb3, 0x9d, + 0xc1, 0x38, 0x14, 0xb9, 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27, + }; + uint8_t sha_512_expected[] = { + 0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 0xef, 0xb0, 0xf0, 0x75, 0x6c, + 0x89, 0x0b, 0xe9, 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 0x55, 0xf8, + 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22, + 0xc8, 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, 0xb9, 0x46, 0xa3, 0x37, + 0xbe, 0xe8, 0x94, 0x26, 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb, + }; + + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + if (!GetParam()->minimal_digest_set()) { + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected)); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacRfc4231TestCase4) { + uint8_t key_data[25] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + }; + string key = make_string(key_data); + string message(50, 0xcd); + uint8_t sha_224_expected[] = { + 0x6c, 0x11, 0x50, 0x68, 0x74, 0x01, 0x3c, 0xac, 0x6a, 0x2a, 0xbc, 0x1b, 0xb3, 0x82, + 0x62, 0x7c, 0xec, 0x6a, 0x90, 0xd8, 0x6e, 0xfc, 0x01, 0x2d, 0xe7, 0xaf, 0xec, 0x5a, + }; + uint8_t sha_256_expected[] = { + 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81, + 0x98, 0x99, 0xf2, 0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, + 0xf8, 0x07, 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b, + }; + uint8_t sha_384_expected[] = { + 0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85, 0x19, 0x33, 0xab, 0x62, + 0x90, 0xaf, 0x6c, 0xa7, 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c, + 0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e, 0x68, 0x01, 0xdd, 0x23, + 0xc4, 0xa7, 0xd6, 0x79, 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb, + }; + uint8_t sha_512_expected[] = { + 0xb0, 0xba, 0x46, 0x56, 0x37, 0x45, 0x8c, 0x69, 0x90, 0xe5, 0xa8, 0xc5, 0xf6, + 0x1d, 0x4a, 0xf7, 0xe5, 0x76, 0xd9, 0x7f, 0xf9, 0x4b, 0x87, 0x2d, 0xe7, 0x6f, + 0x80, 0x50, 0x36, 0x1e, 0xe3, 0xdb, 0xa9, 0x1c, 0xa5, 0xc1, 0x1a, 0xa2, 0x5e, + 0xb4, 0xd6, 0x79, 0x27, 0x5c, 0xc5, 0x78, 0x80, 0x63, 0xa5, 0xf1, 0x97, 0x41, + 0x12, 0x0c, 0x4f, 0x2d, 0xe2, 0xad, 0xeb, 0xeb, 0x10, 0xa2, 0x98, 0xdd, + }; + + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + if (!GetParam()->minimal_digest_set()) { + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected)); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacRfc4231TestCase5) { + string key(20, 0x0c); + string message = "Test With Truncation"; + + uint8_t sha_224_expected[] = { + 0x0e, 0x2a, 0xea, 0x68, 0xa9, 0x0c, 0x8d, 0x37, + 0xc9, 0x88, 0xbc, 0xdb, 0x9f, 0xca, 0x6f, 0xa8, + }; + uint8_t sha_256_expected[] = { + 0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10, 0x0e, 0xe0, + 0x6e, 0x0c, 0x79, 0x6c, 0x29, 0x55, 0x55, 0x2b, + }; + uint8_t sha_384_expected[] = { + 0x3a, 0xbf, 0x34, 0xc3, 0x50, 0x3b, 0x2a, 0x23, + 0xa4, 0x6e, 0xfc, 0x61, 0x9b, 0xae, 0xf8, 0x97, + }; + uint8_t sha_512_expected[] = { + 0x41, 0x5f, 0xad, 0x62, 0x71, 0x58, 0x0a, 0x53, + 0x1d, 0x41, 0x79, 0xbc, 0x89, 0x1d, 0x87, 0xa6, + }; + + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + if (!GetParam()->minimal_digest_set()) { + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected)); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacRfc4231TestCase6) { + string key(131, 0xaa); + string message = "Test Using Larger Than Block-Size Key - Hash Key First"; + + uint8_t sha_224_expected[] = { + 0x95, 0xe9, 0xa0, 0xdb, 0x96, 0x20, 0x95, 0xad, 0xae, 0xbe, 0x9b, 0x2d, 0x6f, 0x0d, + 0xbc, 0xe2, 0xd4, 0x99, 0xf1, 0x12, 0xf2, 0xd2, 0xb7, 0x27, 0x3f, 0xa6, 0x87, 0x0e, + }; + uint8_t sha_256_expected[] = { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, + 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, + 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54, + }; + uint8_t sha_384_expected[] = { + 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, 0x88, 0xd2, 0xc6, 0x3a, + 0x04, 0x1b, 0xc5, 0xb4, 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f, + 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, 0x0c, 0x2e, 0xf6, 0xab, + 0x40, 0x30, 0xfe, 0x82, 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52, + }; + uint8_t sha_512_expected[] = { + 0x80, 0xb2, 0x42, 0x63, 0xc7, 0xc1, 0xa3, 0xeb, 0xb7, 0x14, 0x93, 0xc1, 0xdd, + 0x7b, 0xe8, 0xb4, 0x9b, 0x46, 0xd1, 0xf4, 0x1b, 0x4a, 0xee, 0xc1, 0x12, 0x1b, + 0x01, 0x37, 0x83, 0xf8, 0xf3, 0x52, 0x6b, 0x56, 0xd0, 0x37, 0xe0, 0x5f, 0x25, + 0x98, 0xbd, 0x0f, 0xd2, 0x21, 0x5d, 0x6a, 0x1e, 0x52, 0x95, 0xe6, 0x4f, 0x73, + 0xf6, 0x3f, 0x0a, 0xec, 0x8b, 0x91, 0x5a, 0x98, 0x5d, 0x78, 0x65, 0x98, + }; + + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + if (!GetParam()->minimal_digest_set()) { + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected)); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacRfc4231TestCase7) { + string key(131, 0xaa); + string message = "This is a test using a larger than block-size key and a larger than " + "block-size data. The key needs to be hashed before being used by the HMAC " + "algorithm."; + + uint8_t sha_224_expected[] = { + 0x3a, 0x85, 0x41, 0x66, 0xac, 0x5d, 0x9f, 0x02, 0x3f, 0x54, 0xd5, 0x17, 0xd0, 0xb3, + 0x9d, 0xbd, 0x94, 0x67, 0x70, 0xdb, 0x9c, 0x2b, 0x95, 0xc9, 0xf6, 0xf5, 0x65, 0xd1, + }; + uint8_t sha_256_expected[] = { + 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, + 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, + 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2, + }; + uint8_t sha_384_expected[] = { + 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, 0x35, 0x1e, 0x2f, 0x25, + 0x4e, 0x8f, 0xd3, 0x2c, 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a, + 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, 0xa6, 0x78, 0xcc, 0x31, + 0xe7, 0x99, 0x17, 0x6d, 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e, + }; + uint8_t sha_512_expected[] = { + 0xe3, 0x7b, 0x6a, 0x77, 0x5d, 0xc8, 0x7d, 0xba, 0xa4, 0xdf, 0xa9, 0xf9, 0x6e, + 0x5e, 0x3f, 0xfd, 0xde, 0xbd, 0x71, 0xf8, 0x86, 0x72, 0x89, 0x86, 0x5d, 0xf5, + 0xa3, 0x2d, 0x20, 0xcd, 0xc9, 0x44, 0xb6, 0x02, 0x2c, 0xac, 0x3c, 0x49, 0x82, + 0xb1, 0x0d, 0x5e, 0xeb, 0x55, 0xc3, 0xe4, 0xde, 0x15, 0x13, 0x46, 0x76, 0xfb, + 0x6d, 0xe0, 0x44, 0x60, 0x65, 0xc9, 0x74, 0x40, 0xfa, 0x8c, 0x6a, 0x58, + }; + + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected)); + if (!GetParam()->minimal_digest_set()) { + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected)); + CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected)); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacSha256TooLargeMacLength) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 256))); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_MAC_LENGTH, 264); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + ASSERT_EQ(KM_ERROR_UNSUPPORTED_MAC_LENGTH, + BeginOperation(KM_PURPOSE_SIGN, begin_params, nullptr /* output_params */)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(SigningOperationsTest, HmacSha256TooSmallMacLength) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_MAC_LENGTH, 120); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + ASSERT_EQ(KM_ERROR_INVALID_MAC_LENGTH, + BeginOperation(KM_PURPOSE_SIGN, begin_params, nullptr /* output_params */)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +// TODO(swillden): Add more verification failure tests. + +typedef Keymaster2Test VerificationOperationsTest; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, VerificationOperationsTest, test_params); + +TEST_P(VerificationOperationsTest, RsaSuccess) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + string message = "12345678901234567890123456789012"; + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + VerifyMessage(message, signature, KM_DIGEST_NONE, KM_PAD_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, RsaPssSha256Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_RSA_PSS))); + // Use large message, which won't work without digesting. + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS); + VerifyMessage(message, signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, RsaPssSha224Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_224) + .Padding(KM_PAD_RSA_PSS))); + // Use large message, which won't work without digesting. + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_224, KM_PAD_RSA_PSS); + VerifyMessage(message, signature, KM_DIGEST_SHA_2_224, KM_PAD_RSA_PSS); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); + + // Verify with OpenSSL. + string pubkey; + EXPECT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &pubkey)); + + const uint8_t* p = reinterpret_cast<const uint8_t*>(pubkey.data()); + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey( + d2i_PUBKEY(nullptr /* alloc new */, &p, pubkey.size())); + ASSERT_TRUE(pkey.get()); + + EVP_MD_CTX digest_ctx; + EVP_MD_CTX_init(&digest_ctx); + EVP_PKEY_CTX* pkey_ctx; + EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, EVP_sha224(), nullptr /* engine */, + pkey.get())); + EXPECT_EQ(1, EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)); + EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(), message.size())); + EXPECT_EQ(1, + EVP_DigestVerifyFinal(&digest_ctx, reinterpret_cast<const uint8_t*>(signature.data()), + signature.size())); + EVP_MD_CTX_cleanup(&digest_ctx); +} + +TEST_P(VerificationOperationsTest, RsaPssSha256CorruptSignature) { + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_RSA_PSS)); + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS); + ++signature[signature.size() / 2]; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PSS); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, RsaPssSha256CorruptInput) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_RSA_PSS))); + // Use large message, which won't work without digesting. + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS); + ++message[message.size() / 2]; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PSS); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, RsaPkcs1Sha256Success) { + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)); + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN); + VerifyMessage(message, signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, RsaPks1Sha224Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_224) + .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN))); + // Use large message, which won't work without digesting. + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_224, KM_PAD_RSA_PKCS1_1_5_SIGN); + VerifyMessage(message, signature, KM_DIGEST_SHA_2_224, KM_PAD_RSA_PKCS1_1_5_SIGN); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); + + // Verify with OpenSSL. + string pubkey; + EXPECT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &pubkey)); + + const uint8_t* p = reinterpret_cast<const uint8_t*>(pubkey.data()); + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey( + d2i_PUBKEY(nullptr /* alloc new */, &p, pubkey.size())); + ASSERT_TRUE(pkey.get()); + + EVP_MD_CTX digest_ctx; + EVP_MD_CTX_init(&digest_ctx); + EVP_PKEY_CTX* pkey_ctx; + EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, EVP_sha224(), nullptr /* engine */, + pkey.get())); + EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(), message.size())); + EXPECT_EQ(1, + EVP_DigestVerifyFinal(&digest_ctx, reinterpret_cast<const uint8_t*>(signature.data()), + signature.size())); + EVP_MD_CTX_cleanup(&digest_ctx); +} + +TEST_P(VerificationOperationsTest, RsaPkcs1Sha256CorruptSignature) { + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)); + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN); + ++signature[signature.size() / 2]; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, RsaPkcs1Sha256CorruptInput) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN))); + // Use large message, which won't work without digesting. + string message(1024, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN); + ++message[message.size() / 2]; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, RsaAllDigestAndPadCombinations) { + vector<keymaster_digest_t> digests = { + KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, + KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512, + }; + + vector<keymaster_padding_t> padding_modes{ + KM_PAD_NONE, KM_PAD_RSA_PKCS1_1_5_SIGN, KM_PAD_RSA_PSS, + }; + + int trial_count = 0; + for (keymaster_padding_t padding_mode : padding_modes) { + for (keymaster_digest_t digest : digests) { + if (digest != KM_DIGEST_NONE && padding_mode == KM_PAD_NONE) + // Digesting requires padding + continue; + + // Compute key & message size that will work. + size_t key_bits = 0; + size_t message_len = 1000; + + if (digest == KM_DIGEST_NONE) { + key_bits = 256; + switch (padding_mode) { + case KM_PAD_NONE: + // Match key size. + message_len = key_bits / 8; + break; + case KM_PAD_RSA_PKCS1_1_5_SIGN: + message_len = key_bits / 8 - 11; + break; + case KM_PAD_RSA_PSS: + // PSS requires a digest. + continue; + default: + FAIL() << "Missing padding"; + break; + } + } else { + size_t digest_bits; + switch (digest) { + case KM_DIGEST_MD5: + digest_bits = 128; + break; + case KM_DIGEST_SHA1: + digest_bits = 160; + break; + case KM_DIGEST_SHA_2_224: + digest_bits = 224; + break; + case KM_DIGEST_SHA_2_256: + digest_bits = 256; + break; + case KM_DIGEST_SHA_2_384: + digest_bits = 384; + break; + case KM_DIGEST_SHA_2_512: + digest_bits = 512; + break; + default: + FAIL() << "Missing digest"; + } + + switch (padding_mode) { + case KM_PAD_RSA_PKCS1_1_5_SIGN: + key_bits = digest_bits + 8 * (11 + 19); + break; + case KM_PAD_RSA_PSS: + key_bits = digest_bits + 22 * 8; + break; + default: + FAIL() << "Missing padding"; + break; + } + } + + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(key_bits, 3) + .Digest(digest) + .Padding(padding_mode)); + string message(message_len, 'a'); + string signature; + SignMessage(message, &signature, digest, padding_mode); + VerifyMessage(message, signature, digest, padding_mode); + ++trial_count; + } + } + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(trial_count * 4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, EcdsaSuccess) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(KM_DIGEST_NONE))); + string message = "12345678901234567890123456789012"; + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE); + VerifyMessage(message, signature, KM_DIGEST_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, EcdsaTooShort) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(KM_DIGEST_NONE))); + string message = "12345678901234567890"; + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE); + VerifyMessage(message, signature, KM_DIGEST_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, EcdsaSlightlyTooLong) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(521).Digest(KM_DIGEST_NONE))); + + string message(66, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE); + VerifyMessage(message, signature, KM_DIGEST_NONE); + + // Modifying low-order bits doesn't matter, because they didn't get signed. Ugh. + message[65] ^= 7; + VerifyMessage(message, signature, KM_DIGEST_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(5, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, EcdsaSha256Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(256) + .Digest(KM_DIGEST_SHA_2_256) + .Digest(KM_DIGEST_NONE))); + string message = "12345678901234567890123456789012"; + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_256); + VerifyMessage(message, signature, KM_DIGEST_SHA_2_256); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); + + // Just for giggles, try verifying with the wrong digest. + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result)); +} + +TEST_P(VerificationOperationsTest, EcdsaSha224Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest( + KM_DIGEST_SHA_2_224))); + + string message = "12345678901234567890123456789012"; + string signature; + SignMessage(message, &signature, KM_DIGEST_SHA_2_224); + VerifyMessage(message, signature, KM_DIGEST_SHA_2_224); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); + + // Just for giggles, try verifying with the wrong digest. + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result)); +} + +TEST_P(VerificationOperationsTest, EcdsaAllDigestsAndKeySizes) { + keymaster_digest_t digests[] = { + KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, KM_DIGEST_SHA_2_256, + KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512, + }; + size_t key_sizes[] = {224, 256, 384, 521}; + + string message = "1234567890"; + string signature; + + for (auto key_size : key_sizes) { + AuthorizationSetBuilder builder; + builder.EcdsaSigningKey(key_size); + for (auto digest : digests) + builder.Digest(digest); + ASSERT_EQ(KM_ERROR_OK, GenerateKey(builder)); + + for (auto digest : digests) { + SignMessage(message, &signature, digest); + VerifyMessage(message, signature, digest); + } + } + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(static_cast<int>(array_length(key_sizes) * (1 + 3 * array_length(digests))), + GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, HmacSha1Success) { + if (GetParam()->minimal_digest_set()) + // Can't emulate missing digests for HMAC. + return; + + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA1) + .Authorization(TAG_MIN_MAC_LENGTH, 128)); + string message = "123456789012345678901234567890123456789012345678"; + string signature; + MacMessage(message, &signature, 160); + VerifyMac(message, signature); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, HmacSha224Success) { + if (GetParam()->minimal_digest_set()) + // Can't emulate missing digests for HMAC. + return; + + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_224) + .Authorization(TAG_MIN_MAC_LENGTH, 128)); + string message = "123456789012345678901234567890123456789012345678"; + string signature; + MacMessage(message, &signature, 224); + VerifyMac(message, signature); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, HmacSha256Success) { + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 128)); + string message = "123456789012345678901234567890123456789012345678"; + string signature; + MacMessage(message, &signature, 256); + VerifyMac(message, signature); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, HmacSha256TooShortMac) { + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 128)); + string message = "123456789012345678901234567890123456789012345678"; + string signature; + MacMessage(message, &signature, 256); + + // Shorten to 128 bits, should still work. + signature.resize(128 / 8); + VerifyMac(message, signature); + + // Drop one more byte. + signature.resize(signature.length() - 1); + + AuthorizationSet begin_params(client_params()); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params)); + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(KM_ERROR_INVALID_MAC_LENGTH, FinishOperation(signature, &result)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, HmacSha384Success) { + if (GetParam()->minimal_digest_set()) + // Can't emulate missing digests for HMAC. + return; + + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_384) + .Authorization(TAG_MIN_MAC_LENGTH, 128)); + string message = "123456789012345678901234567890123456789012345678"; + string signature; + MacMessage(message, &signature, 384); + VerifyMac(message, signature); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(VerificationOperationsTest, HmacSha512Success) { + if (GetParam()->minimal_digest_set()) + // Can't emulate missing digests for HMAC. + return; + + GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_512) + .Authorization(TAG_MIN_MAC_LENGTH, 128)); + string message = "123456789012345678901234567890123456789012345678"; + string signature; + MacMessage(message, &signature, 512); + VerifyMac(message, signature); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +typedef Keymaster2Test ExportKeyTest; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, ExportKeyTest, test_params); + +TEST_P(ExportKeyTest, RsaSuccess) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + string export_data; + ASSERT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &export_data)); + EXPECT_GT(export_data.length(), 0U); + + // TODO(swillden): Verify that the exported key is actually usable to verify signatures. + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(ExportKeyTest, EcdsaSuccess) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE))); + string export_data; + ASSERT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &export_data)); + EXPECT_GT(export_data.length(), 0U); + + // TODO(swillden): Verify that the exported key is actually usable to verify signatures. + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(ExportKeyTest, RsaUnsupportedKeyFormat) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + string export_data; + ASSERT_EQ(KM_ERROR_UNSUPPORTED_KEY_FORMAT, ExportKey(KM_KEY_FORMAT_PKCS8, &export_data)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(ExportKeyTest, RsaCorruptedKeyBlob) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + corrupt_key_blob(); + string export_data; + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, ExportKey(KM_KEY_FORMAT_X509, &export_data)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(ExportKeyTest, AesKeyExportFails) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().AesEncryptionKey(128))); + string export_data; + + EXPECT_EQ(KM_ERROR_UNSUPPORTED_KEY_FORMAT, ExportKey(KM_KEY_FORMAT_X509, &export_data)); + EXPECT_EQ(KM_ERROR_UNSUPPORTED_KEY_FORMAT, ExportKey(KM_KEY_FORMAT_PKCS8, &export_data)); + EXPECT_EQ(KM_ERROR_UNSUPPORTED_KEY_FORMAT, ExportKey(KM_KEY_FORMAT_RAW, &export_data)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +static string read_file(const string& file_name) { + ifstream file_stream(file_name, std::ios::binary); + istreambuf_iterator<char> file_begin(file_stream); + istreambuf_iterator<char> file_end; + return string(file_begin, file_end); +} + +typedef Keymaster2Test ImportKeyTest; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, ImportKeyTest, test_params); + +TEST_P(ImportKeyTest, RsaSuccess) { + string pk8_key = read_file("rsa_privkey_pk8.der"); + ASSERT_EQ(633U, pk8_key.size()); + + ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder() + .RsaSigningKey(1024, 65537) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE), + KM_KEY_FORMAT_PKCS8, pk8_key)); + + // Check values derived from the key. + EXPECT_TRUE(contains(GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA) ? hw_enforced() + : sw_enforced(), + TAG_ALGORITHM, KM_ALGORITHM_RSA)); + EXPECT_TRUE(contains(GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA) ? hw_enforced() + : sw_enforced(), + TAG_KEY_SIZE, 1024)); + EXPECT_TRUE(contains(GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA) ? hw_enforced() + : sw_enforced(), + TAG_RSA_PUBLIC_EXPONENT, 65537U)); + + // And values provided by AndroidKeymaster + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_TRUE(contains(hw_enforced(), TAG_ORIGIN, KM_ORIGIN_UNKNOWN)); + else + EXPECT_TRUE(contains(sw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED)); + EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME)); + + string message(1024 / 8, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + VerifyMessage(message, signature, KM_DIGEST_NONE, KM_PAD_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(ImportKeyTest, RsaKeySizeMismatch) { + string pk8_key = read_file("rsa_privkey_pk8.der"); + ASSERT_EQ(633U, pk8_key.size()); + ASSERT_EQ(KM_ERROR_IMPORT_PARAMETER_MISMATCH, + ImportKey(AuthorizationSetBuilder() + .RsaSigningKey(2048 /* Doesn't match key */, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE), + KM_KEY_FORMAT_PKCS8, pk8_key)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(ImportKeyTest, RsaPublicExponenMismatch) { + string pk8_key = read_file("rsa_privkey_pk8.der"); + ASSERT_EQ(633U, pk8_key.size()); + ASSERT_EQ(KM_ERROR_IMPORT_PARAMETER_MISMATCH, + ImportKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3 /* Doesnt' match key */) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE), + KM_KEY_FORMAT_PKCS8, pk8_key)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(ImportKeyTest, EcdsaSuccess) { + string pk8_key = read_file("ec_privkey_pk8.der"); + ASSERT_EQ(138U, pk8_key.size()); + + ASSERT_EQ(KM_ERROR_OK, + ImportKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(KM_DIGEST_NONE), + KM_KEY_FORMAT_PKCS8, pk8_key)); + + // Check values derived from the key. + EXPECT_TRUE(contains(GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC) ? hw_enforced() + : sw_enforced(), + TAG_ALGORITHM, KM_ALGORITHM_EC)); + EXPECT_TRUE(contains(GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC) ? hw_enforced() + : sw_enforced(), + TAG_KEY_SIZE, 256)); + + // And values provided by AndroidKeymaster + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_TRUE(contains(hw_enforced(), TAG_ORIGIN, KM_ORIGIN_UNKNOWN)); + else + EXPECT_TRUE(contains(sw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED)); + EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME)); + + string message(32, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE); + VerifyMessage(message, signature, KM_DIGEST_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(ImportKeyTest, EcdsaSizeSpecified) { + string pk8_key = read_file("ec_privkey_pk8.der"); + ASSERT_EQ(138U, pk8_key.size()); + + ASSERT_EQ(KM_ERROR_OK, + ImportKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(KM_DIGEST_NONE), + KM_KEY_FORMAT_PKCS8, pk8_key)); + + // Check values derived from the key. + EXPECT_TRUE(contains(GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC) ? hw_enforced() + : sw_enforced(), + TAG_ALGORITHM, KM_ALGORITHM_EC)); + EXPECT_TRUE(contains(GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC) ? hw_enforced() + : sw_enforced(), + TAG_KEY_SIZE, 256)); + + // And values provided by AndroidKeymaster + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_TRUE(contains(hw_enforced(), TAG_ORIGIN, KM_ORIGIN_UNKNOWN)); + else + EXPECT_TRUE(contains(sw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED)); + EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME)); + + string message(32, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE); + VerifyMessage(message, signature, KM_DIGEST_NONE); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(ImportKeyTest, EcdsaSizeMismatch) { + string pk8_key = read_file("ec_privkey_pk8.der"); + ASSERT_EQ(138U, pk8_key.size()); + ASSERT_EQ(KM_ERROR_IMPORT_PARAMETER_MISMATCH, + ImportKey(AuthorizationSetBuilder() + .EcdsaSigningKey(224 /* Doesn't match key */) + .Digest(KM_DIGEST_NONE), + KM_KEY_FORMAT_PKCS8, pk8_key)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(ImportKeyTest, AesKeySuccess) { + char key_data[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + string key(key_data, sizeof(key_data)); + ASSERT_EQ(KM_ERROR_OK, + ImportKey(AuthorizationSetBuilder().AesEncryptionKey(128).EcbMode().Authorization( + TAG_PADDING, KM_PAD_PKCS7), + KM_KEY_FORMAT_RAW, key)); + + EXPECT_TRUE(contains(sw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED)); + EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME)); + + string message = "Hello World!"; + string ciphertext = EncryptMessage(message, KM_MODE_ECB, KM_PAD_PKCS7); + string plaintext = DecryptMessage(ciphertext, KM_MODE_ECB, KM_PAD_PKCS7); + EXPECT_EQ(message, plaintext); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(ImportKeyTest, HmacSha256KeySuccess) { + char key_data[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + string key(key_data, sizeof(key_data)); + ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder() + .HmacKey(sizeof(key_data) * 8) + .Digest(KM_DIGEST_SHA_2_256) + .Authorization(TAG_MIN_MAC_LENGTH, 256), + KM_KEY_FORMAT_RAW, key)); + + EXPECT_TRUE(contains(sw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED)); + EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME)); + + string message = "Hello World!"; + string signature; + MacMessage(message, &signature, 256); + VerifyMac(message, signature); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +typedef Keymaster2Test EncryptionOperationsTest; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, EncryptionOperationsTest, test_params); + +TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(256, 3).Padding(KM_PAD_NONE))); + + string message = "12345678901234567890123456789012"; + string ciphertext1 = EncryptMessage(string(message), KM_PAD_NONE); + EXPECT_EQ(256U / 8, ciphertext1.size()); + + string ciphertext2 = EncryptMessage(string(message), KM_PAD_NONE); + EXPECT_EQ(256U / 8, ciphertext2.size()); + + // Unpadded RSA is deterministic + EXPECT_EQ(ciphertext1, ciphertext2); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaNoPaddingTooShort) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(256, 3).Padding(KM_PAD_NONE))); + + string message = "1"; + + string ciphertext = EncryptMessage(message, KM_PAD_NONE); + EXPECT_EQ(256U / 8, ciphertext.size()); + + string expected_plaintext = string(256 / 8 - 1, 0) + message; + string plaintext = DecryptMessage(ciphertext, KM_PAD_NONE); + + EXPECT_EQ(expected_plaintext, plaintext); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaNoPaddingTooLong) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(256, 3).Padding(KM_PAD_NONE))); + + string message = "123456789012345678901234567890123"; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, UpdateOperation(message, &result, &input_consumed)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaNoPaddingLargerThanModulus) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(256, 3).Padding(KM_PAD_NONE))); + + string exported; + ASSERT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &exported)); + + const uint8_t* p = reinterpret_cast<const uint8_t*>(exported.data()); + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey( + d2i_PUBKEY(nullptr /* alloc new */, &p, exported.size())); + unique_ptr<RSA, RSA_Delete> rsa(EVP_PKEY_get1_RSA(pkey.get())); + + size_t modulus_len = BN_num_bytes(rsa->n); + ASSERT_EQ(256U / 8, modulus_len); + unique_ptr<uint8_t[]> modulus_buf(new uint8_t[modulus_len]); + BN_bn2bin(rsa->n, modulus_buf.get()); + + // The modulus is too big to encrypt. + string message(reinterpret_cast<const char*>(modulus_buf.get()), modulus_len); + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, FinishOperation(&result)); + + // One smaller than the modulus is okay. + BN_sub(rsa->n, rsa->n, BN_value_one()); + modulus_len = BN_num_bytes(rsa->n); + ASSERT_EQ(256U / 8, modulus_len); + BN_bn2bin(rsa->n, modulus_buf.get()); + message = string(reinterpret_cast<const char*>(modulus_buf.get()), modulus_len); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&result)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaOaepSuccess) { + size_t key_size = 768; + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(key_size, 3) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_SHA_2_256))); + + string message = "Hello"; + string ciphertext1 = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP); + EXPECT_EQ(key_size / 8, ciphertext1.size()); + + string ciphertext2 = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP); + EXPECT_EQ(key_size / 8, ciphertext2.size()); + + // OAEP randomizes padding so every result should be different. + EXPECT_NE(ciphertext1, ciphertext2); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaOaepSha224Success) { + size_t key_size = 768; + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(key_size, 3) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_SHA_2_224))); + + string message = "Hello"; + string ciphertext1 = EncryptMessage(string(message), KM_DIGEST_SHA_2_224, KM_PAD_RSA_OAEP); + EXPECT_EQ(key_size / 8, ciphertext1.size()); + + string ciphertext2 = EncryptMessage(string(message), KM_DIGEST_SHA_2_224, KM_PAD_RSA_OAEP); + EXPECT_EQ(key_size / 8, ciphertext2.size()); + + // OAEP randomizes padding so every result should be different. + EXPECT_NE(ciphertext1, ciphertext2); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaOaepRoundTrip) { + size_t key_size = 768; + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(key_size, 3) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_SHA_2_256))); + string message = "Hello World!"; + string ciphertext = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP); + EXPECT_EQ(key_size / 8, ciphertext.size()); + + string plaintext = DecryptMessage(ciphertext, KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP); + EXPECT_EQ(message, plaintext); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaOaepSha224RoundTrip) { + size_t key_size = 768; + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(key_size, 3) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_SHA_2_224))); + string message = "Hello World!"; + string ciphertext = EncryptMessage(string(message), KM_DIGEST_SHA_2_224, KM_PAD_RSA_OAEP); + EXPECT_EQ(key_size / 8, ciphertext.size()); + + string plaintext = DecryptMessage(ciphertext, KM_DIGEST_SHA_2_224, KM_PAD_RSA_OAEP); + EXPECT_EQ(message, plaintext); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaOaepInvalidDigest) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(512, 3) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_NONE))); + string message = "Hello World!"; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE); + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_DIGEST, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaOaepUnauthorizedDigest) { + if (GetParam()->minimal_digest_set()) + // We don't have two supported digests, so we can't try authorizing one and using another. + return; + + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(512, 3) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_SHA_2_256))); + string message = "Hello World!"; + // Works because encryption is a public key operation. + EncryptMessage(string(message), KM_DIGEST_SHA1, KM_PAD_RSA_OAEP); + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA1); + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_DIGEST, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaOaepDecryptWithWrongDigest) { + if (GetParam()->minimal_digest_set()) + // We don't have two supported digests, so we can't try encrypting with one and decrypting + // with another. + return; + + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(768, 3) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_SHA_2_256) + .Digest(KM_DIGEST_SHA_2_384))); + string message = "Hello World!"; + string ciphertext = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP); + + string result; + size_t input_consumed; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_384); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &result, &input_consumed)); + EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(&result)); + EXPECT_EQ(0U, result.size()); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaOaepTooLarge) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(512, 3) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_SHA1))); + string message = "12345678901234567890123"; + string result; + size_t input_consumed; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA1); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&result)); + EXPECT_EQ(0U, result.size()); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaOaepCorruptedDecrypt) { + size_t key_size = 768; + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(768, 3) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_SHA_2_256))); + string message = "Hello World!"; + string ciphertext = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP); + EXPECT_EQ(key_size / 8, ciphertext.size()); + + // Corrupt the ciphertext + ciphertext[key_size / 8 / 2]++; + + string result; + size_t input_consumed; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP); + begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &result, &input_consumed)); + EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(&result)); + EXPECT_EQ(0U, result.size()); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaPkcs1Success) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(512, 3).Padding( + KM_PAD_RSA_PKCS1_1_5_ENCRYPT))); + string message = "Hello World!"; + string ciphertext1 = EncryptMessage(message, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + EXPECT_EQ(512U / 8, ciphertext1.size()); + + string ciphertext2 = EncryptMessage(message, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + EXPECT_EQ(512U / 8, ciphertext2.size()); + + // PKCS1 v1.5 randomizes padding so every result should be different. + EXPECT_NE(ciphertext1, ciphertext2); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaPkcs1RoundTrip) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(512, 3).Padding( + KM_PAD_RSA_PKCS1_1_5_ENCRYPT))); + string message = "Hello World!"; + string ciphertext = EncryptMessage(message, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + EXPECT_EQ(512U / 8, ciphertext.size()); + + string plaintext = DecryptMessage(ciphertext, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + EXPECT_EQ(message, plaintext); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaRoundTripAllCombinations) { + size_t key_size = 2048; + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(key_size, 3) + .Padding(KM_PAD_RSA_PKCS1_1_5_ENCRYPT) + .Padding(KM_PAD_RSA_OAEP) + .Digest(KM_DIGEST_NONE) + .Digest(KM_DIGEST_MD5) + .Digest(KM_DIGEST_SHA1) + .Digest(KM_DIGEST_SHA_2_224) + .Digest(KM_DIGEST_SHA_2_256) + .Digest(KM_DIGEST_SHA_2_384) + .Digest(KM_DIGEST_SHA_2_512))); + + string message = "Hello World!"; + + keymaster_padding_t padding_modes[] = {KM_PAD_RSA_OAEP, KM_PAD_RSA_PKCS1_1_5_ENCRYPT}; + keymaster_digest_t digests[] = { + KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, + KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512, + }; + + for (auto padding : padding_modes) + for (auto digest : digests) { + if (padding == KM_PAD_RSA_OAEP && digest == KM_DIGEST_NONE) + // OAEP requires a digest. + continue; + + string ciphertext = EncryptMessage(message, digest, padding); + EXPECT_EQ(key_size / 8, ciphertext.size()); + + string plaintext = DecryptMessage(ciphertext, digest, padding); + EXPECT_EQ(message, plaintext); + } + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(40, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaPkcs1TooLarge) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(512, 3).Padding( + KM_PAD_RSA_PKCS1_1_5_ENCRYPT))); + string message = "123456789012345678901234567890123456789012345678901234"; + string result; + size_t input_consumed; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&result)); + EXPECT_EQ(0U, result.size()); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaPkcs1CorruptedDecrypt) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(512, 3).Padding( + KM_PAD_RSA_PKCS1_1_5_ENCRYPT))); + string message = "Hello World!"; + string ciphertext = EncryptMessage(string(message), KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + EXPECT_EQ(512U / 8, ciphertext.size()); + + // Corrupt the ciphertext + ciphertext[512 / 8 / 2]++; + + string result; + size_t input_consumed; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &result, &input_consumed)); + EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(&result)); + EXPECT_EQ(0U, result.size()); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(4, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, RsaEncryptWithSigningKey) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().RsaSigningKey(256, 3).Padding(KM_PAD_NONE))); + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + ASSERT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) + EXPECT_EQ(2, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, EcdsaEncrypt) { + ASSERT_EQ(KM_ERROR_OK, + GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE))); + ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_ENCRYPT)); + ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_DECRYPT)); + + if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) + EXPECT_EQ(3, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, HmacEncrypt) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .HmacKey(128) + .Digest(KM_DIGEST_SHA_2_256) + .Padding(KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_ENCRYPT)); + ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_DECRYPT)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesEcbRoundTripSuccess) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB) + .Padding(KM_PAD_NONE))); + // Two-block message. + string message = "12345678901234567890123456789012"; + string ciphertext1 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE); + EXPECT_EQ(message.size(), ciphertext1.size()); + + string ciphertext2 = EncryptMessage(string(message), KM_MODE_ECB, KM_PAD_NONE); + EXPECT_EQ(message.size(), ciphertext2.size()); + + // ECB is deterministic. + EXPECT_EQ(ciphertext1, ciphertext2); + + string plaintext = DecryptMessage(ciphertext1, KM_MODE_ECB, KM_PAD_NONE); + EXPECT_EQ(message, plaintext); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesEcbNotAuthorized) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC) + .Padding(KM_PAD_NONE))); + // Two-block message. + string message = "12345678901234567890123456789012"; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_BLOCK_MODE, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesEcbNoPaddingWrongInputSize) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB) + .Padding(KM_PAD_NONE))); + // Message is slightly shorter than two blocks. + string message = "1234567890123456789012345678901"; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + string ciphertext; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &ciphertext, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&ciphertext)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesEcbPkcs7Padding) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB) + .Authorization(TAG_PADDING, KM_PAD_PKCS7))); + + // Try various message lengths; all should work. + for (size_t i = 0; i < 32; ++i) { + string message(i, 'a'); + string ciphertext = EncryptMessage(message, KM_MODE_ECB, KM_PAD_PKCS7); + EXPECT_EQ(i + 16 - (i % 16), ciphertext.size()); + string plaintext = DecryptMessage(ciphertext, KM_MODE_ECB, KM_PAD_PKCS7); + EXPECT_EQ(message, plaintext); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesEcbNoPaddingKeyWithPkcs7Padding) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB) + .Authorization(TAG_PADDING, KM_PAD_NONE))); + + // Try various message lengths; all should fail. + for (size_t i = 0; i < 32; ++i) { + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB); + begin_params.push_back(TAG_PADDING, KM_PAD_PKCS7); + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PADDING_MODE, + BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB) + .Authorization(TAG_PADDING, KM_PAD_PKCS7))); + + string message = "a"; + string ciphertext = EncryptMessage(message, KM_MODE_ECB, KM_PAD_PKCS7); + EXPECT_EQ(16U, ciphertext.size()); + EXPECT_NE(ciphertext, message); + ++ciphertext[ciphertext.size() / 2]; + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB); + begin_params.push_back(TAG_PADDING, KM_PAD_PKCS7); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + string plaintext; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &plaintext, &input_consumed)); + EXPECT_EQ(ciphertext.size(), input_consumed); + EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, FinishOperation(&plaintext)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesCtrRoundTripSuccess) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR) + .Padding(KM_PAD_NONE))); + string message = "123"; + string iv1; + string ciphertext1 = EncryptMessage(message, KM_MODE_CTR, KM_PAD_NONE, &iv1); + EXPECT_EQ(message.size(), ciphertext1.size()); + EXPECT_EQ(16U, iv1.size()); + + string iv2; + string ciphertext2 = EncryptMessage(message, KM_MODE_CTR, KM_PAD_NONE, &iv2); + EXPECT_EQ(message.size(), ciphertext2.size()); + EXPECT_EQ(16U, iv2.size()); + + // IVs should be random, so ciphertexts should differ. + EXPECT_NE(iv1, iv2); + EXPECT_NE(ciphertext1, ciphertext2); + + string plaintext = DecryptMessage(ciphertext1, KM_MODE_CTR, KM_PAD_NONE, iv1); + EXPECT_EQ(message, plaintext); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesCtrIncremental) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR) + .Padding(KM_PAD_NONE))); + + int increment = 15; + string message(239, 'a'); + AuthorizationSet input_params(client_params()); + input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR); + input_params.push_back(TAG_PADDING, KM_PAD_NONE); + AuthorizationSet output_params; + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, input_params, &output_params)); + + string ciphertext; + size_t input_consumed; + for (size_t i = 0; i < message.size(); i += increment) + EXPECT_EQ(KM_ERROR_OK, + UpdateOperation(message.substr(i, increment), &ciphertext, &input_consumed)); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + EXPECT_EQ(message.size(), ciphertext.size()); + + // Move TAG_NONCE into input_params + input_params.Reinitialize(output_params); + input_params.push_back(client_params()); + input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR); + input_params.push_back(TAG_PADDING, KM_PAD_NONE); + output_params.Clear(); + + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, input_params, &output_params)); + string plaintext; + for (size_t i = 0; i < ciphertext.size(); i += increment) + EXPECT_EQ(KM_ERROR_OK, + UpdateOperation(ciphertext.substr(i, increment), &plaintext, &input_consumed)); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext)); + EXPECT_EQ(ciphertext.size(), plaintext.size()); + EXPECT_EQ(message, plaintext); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +struct AesCtrSp80038aTestVector { + const char* key; + const char* nonce; + const char* plaintext; + const char* ciphertext; +}; + +// These test vectors are taken from +// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section F.5. +static const AesCtrSp80038aTestVector kAesCtrSp80038aTestVectors[] = { + // AES-128 + { + "2b7e151628aed2a6abf7158809cf4f3c", "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", + "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff" + "5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee", + }, + // AES-192 + { + "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", + "1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e94" + "1e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050", + }, + // AES-256 + { + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710", + "601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5" + "2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6", + }, +}; + +TEST_P(EncryptionOperationsTest, AesCtrSp80038aTestVector) { + for (size_t i = 0; i < 3; i++) { + const AesCtrSp80038aTestVector& test(kAesCtrSp80038aTestVectors[i]); + const string key = hex2str(test.key); + const string nonce = hex2str(test.nonce); + const string plaintext = hex2str(test.plaintext); + const string ciphertext = hex2str(test.ciphertext); + CheckAesCtrTestVector(key, nonce, plaintext, ciphertext); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesCtrInvalidPaddingMode) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR) + .Authorization(TAG_PADDING, KM_PAD_PKCS7))); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PADDING_MODE, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesCtrInvalidCallerNonce) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR) + .Authorization(TAG_CALLER_NONCE) + .Padding(KM_PAD_NONE))); + + AuthorizationSet input_params(client_params()); + input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR); + input_params.push_back(TAG_PADDING, KM_PAD_NONE); + input_params.push_back(TAG_NONCE, "123", 3); + EXPECT_EQ(KM_ERROR_INVALID_NONCE, BeginOperation(KM_PURPOSE_ENCRYPT, input_params)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesCbcRoundTripSuccess) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC) + .Padding(KM_PAD_NONE))); + // Two-block message. + string message = "12345678901234567890123456789012"; + string iv1; + string ciphertext1 = EncryptMessage(message, KM_MODE_CBC, KM_PAD_NONE, &iv1); + EXPECT_EQ(message.size(), ciphertext1.size()); + + string iv2; + string ciphertext2 = EncryptMessage(message, KM_MODE_CBC, KM_PAD_NONE, &iv2); + EXPECT_EQ(message.size(), ciphertext2.size()); + + // IVs should be random, so ciphertexts should differ. + EXPECT_NE(iv1, iv2); + EXPECT_NE(ciphertext1, ciphertext2); + + string plaintext = DecryptMessage(ciphertext1, KM_MODE_CBC, KM_PAD_NONE, iv1); + EXPECT_EQ(message, plaintext); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesCallerNonce) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC) + .Authorization(TAG_CALLER_NONCE) + .Padding(KM_PAD_NONE))); + string message = "12345678901234567890123456789012"; + string iv1; + // Don't specify nonce, should get a random one. + string ciphertext1 = EncryptMessage(message, KM_MODE_CBC, KM_PAD_NONE, &iv1); + EXPECT_EQ(message.size(), ciphertext1.size()); + EXPECT_EQ(16U, iv1.size()); + + string plaintext = DecryptMessage(ciphertext1, KM_MODE_CBC, KM_PAD_NONE, iv1); + EXPECT_EQ(message, plaintext); + + // Now specify a nonce, should also work. + AuthorizationSet input_params(client_params()); + AuthorizationSet update_params; + AuthorizationSet output_params; + input_params.push_back(TAG_NONCE, "abcdefghijklmnop", 16); + input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC); + input_params.push_back(TAG_PADDING, KM_PAD_NONE); + string ciphertext2 = + ProcessMessage(KM_PURPOSE_ENCRYPT, message, input_params, update_params, &output_params); + + // Decrypt with correct nonce. + plaintext = ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext2, input_params, update_params, + &output_params); + EXPECT_EQ(message, plaintext); + + // Now try with wrong nonce. + input_params.Reinitialize(client_params()); + input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC); + input_params.push_back(TAG_PADDING, KM_PAD_NONE); + input_params.push_back(TAG_NONCE, "aaaaaaaaaaaaaaaa", 16); + plaintext = ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext2, input_params, update_params, + &output_params); + EXPECT_NE(message, plaintext); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesCallerNonceProhibited) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC) + .Padding(KM_PAD_NONE))); + + string message = "12345678901234567890123456789012"; + string iv1; + // Don't specify nonce, should get a random one. + string ciphertext1 = EncryptMessage(message, KM_MODE_CBC, KM_PAD_NONE, &iv1); + EXPECT_EQ(message.size(), ciphertext1.size()); + EXPECT_EQ(16U, iv1.size()); + + string plaintext = DecryptMessage(ciphertext1, KM_MODE_CBC, KM_PAD_NONE, iv1); + EXPECT_EQ(message, plaintext); + + // Now specify a nonce, should fail. + AuthorizationSet input_params(client_params()); + AuthorizationSet update_params; + AuthorizationSet output_params; + input_params.push_back(TAG_NONCE, "abcdefghijklmnop", 16); + input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC); + input_params.push_back(TAG_PADDING, KM_PAD_NONE); + + EXPECT_EQ(KM_ERROR_CALLER_NONCE_PROHIBITED, + BeginOperation(KM_PURPOSE_ENCRYPT, input_params, &output_params)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesCbcIncrementalNoPadding) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC) + .Padding(KM_PAD_NONE))); + + int increment = 15; + string message(240, 'a'); + AuthorizationSet input_params(client_params()); + input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC); + input_params.push_back(TAG_PADDING, KM_PAD_NONE); + AuthorizationSet output_params; + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, input_params, &output_params)); + + string ciphertext; + size_t input_consumed; + for (size_t i = 0; i < message.size(); i += increment) + EXPECT_EQ(KM_ERROR_OK, + UpdateOperation(message.substr(i, increment), &ciphertext, &input_consumed)); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + EXPECT_EQ(message.size(), ciphertext.size()); + + // Move TAG_NONCE into input_params + input_params.Reinitialize(output_params); + input_params.push_back(client_params()); + input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC); + input_params.push_back(TAG_PADDING, KM_PAD_NONE); + output_params.Clear(); + + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, input_params, &output_params)); + string plaintext; + for (size_t i = 0; i < ciphertext.size(); i += increment) + EXPECT_EQ(KM_ERROR_OK, + UpdateOperation(ciphertext.substr(i, increment), &plaintext, &input_consumed)); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext)); + EXPECT_EQ(ciphertext.size(), plaintext.size()); + EXPECT_EQ(message, plaintext); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesCbcPkcs7Padding) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC) + .Authorization(TAG_PADDING, KM_PAD_PKCS7))); + + // Try various message lengths; all should work. + for (size_t i = 0; i < 32; ++i) { + string message(i, 'a'); + string iv; + string ciphertext = EncryptMessage(message, KM_MODE_CBC, KM_PAD_PKCS7, &iv); + EXPECT_EQ(i + 16 - (i % 16), ciphertext.size()); + string plaintext = DecryptMessage(ciphertext, KM_MODE_CBC, KM_PAD_PKCS7, iv); + EXPECT_EQ(message, plaintext); + } + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmRoundTripSuccess) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string aad = "foobar"; + string message = "123456789012345678901234567890123456"; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 128); + + AuthorizationSet update_params; + update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size()); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params)); + string ciphertext; + size_t input_consumed; + AuthorizationSet update_out_params; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext, + &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + + // Grab nonce + EXPECT_NE(-1, begin_out_params.find(TAG_NONCE)); + begin_params.push_back(begin_out_params); + + // Decrypt. + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + string plaintext; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params, + &plaintext, &input_consumed)); + EXPECT_EQ(ciphertext.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext)); + + EXPECT_EQ(message, plaintext); + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmTooShortTag) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string aad = "foobar"; + string message = "123456789012345678901234567890123456"; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 96); + + AuthorizationSet update_params; + update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size()); + + AuthorizationSet begin_out_params; + EXPECT_EQ(KM_ERROR_INVALID_MAC_LENGTH, + BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmTooShortTagOnDecrypt) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string aad = "foobar"; + string message = "123456789012345678901234567890123456"; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 128); + + AuthorizationSet update_params; + update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size()); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params)); + string ciphertext; + size_t input_consumed; + AuthorizationSet update_out_params; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext, + &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + + // Grab nonce + EXPECT_NE(-1, begin_out_params.find(TAG_NONCE)); + begin_params.Reinitialize(client_params()); + begin_params.push_back(begin_out_params); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 96); + + // Decrypt. + EXPECT_EQ(KM_ERROR_INVALID_MAC_LENGTH, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmCorruptKey) { + uint8_t nonce[] = { + 0xb7, 0x94, 0x37, 0xae, 0x08, 0xff, 0x35, 0x5d, 0x7d, 0x8a, 0x4d, 0x0f, + }; + uint8_t ciphertext[] = { + 0xb3, 0xf6, 0x79, 0x9e, 0x8f, 0x93, 0x26, 0xf2, 0xdf, 0x1e, 0x80, 0xfc, 0xd2, 0xcb, 0x16, + 0xd7, 0x8c, 0x9d, 0xc7, 0xcc, 0x14, 0xbb, 0x67, 0x78, 0x62, 0xdc, 0x6c, 0x63, 0x9b, 0x3a, + 0x63, 0x38, 0xd2, 0x4b, 0x31, 0x2d, 0x39, 0x89, 0xe5, 0x92, 0x0b, 0x5d, 0xbf, 0xc9, 0x76, + 0x76, 0x5e, 0xfb, 0xfe, 0x57, 0xbb, 0x38, 0x59, 0x40, 0xa7, 0xa4, 0x3b, 0xdf, 0x05, 0xbd, + 0xda, 0xe3, 0xc9, 0xd6, 0xa2, 0xfb, 0xbd, 0xfc, 0xc0, 0xcb, 0xa0, + }; + string ciphertext_str(reinterpret_cast<char*>(ciphertext), sizeof(ciphertext)); + + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 128); + begin_params.push_back(TAG_NONCE, nonce, sizeof(nonce)); + + string plaintext; + size_t input_consumed; + + // Import correct key and decrypt + uint8_t good_key[] = { + 0xba, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d, + 0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb, + }; + string good_key_str(reinterpret_cast<char*>(good_key), sizeof(good_key)); + ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_CALLER_NONCE) + .Authorization(TAG_MIN_MAC_LENGTH, 128), + KM_KEY_FORMAT_RAW, good_key_str)); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext_str, &plaintext, &input_consumed)); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext)); + + // Import bad key and decrypt + uint8_t bad_key[] = { + 0xbb, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d, + 0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb, + }; + string bad_key_str(reinterpret_cast<char*>(bad_key), sizeof(bad_key)); + ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128), + KM_KEY_FORMAT_RAW, bad_key_str)); + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext_str, &plaintext, &input_consumed)); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmAadNoData) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string aad = "123456789012345678"; + string empty_message; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 128); + + AuthorizationSet update_params; + update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size()); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params)); + string ciphertext; + size_t input_consumed; + AuthorizationSet update_out_params; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, empty_message, &update_out_params, + &ciphertext, &input_consumed)); + EXPECT_EQ(0U, input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + + // Grab nonce + EXPECT_NE(-1, begin_out_params.find(TAG_NONCE)); + begin_params.push_back(begin_out_params); + + // Decrypt. + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + string plaintext; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params, + &plaintext, &input_consumed)); + EXPECT_EQ(ciphertext.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext)); + + EXPECT_EQ(empty_message, plaintext); + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmIncremental) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 128); + + AuthorizationSet update_params; + update_params.push_back(TAG_ASSOCIATED_DATA, "b", 1); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params)); + string ciphertext; + size_t input_consumed; + AuthorizationSet update_out_params; + + // Send AAD, incrementally + for (int i = 0; i < 1000; ++i) { + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &ciphertext, + &input_consumed)); + EXPECT_EQ(0U, input_consumed); + EXPECT_EQ(0U, ciphertext.size()); + } + + // Now send data, incrementally, no data. + AuthorizationSet empty_params; + for (int i = 0; i < 1000; ++i) { + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(empty_params, "a", &update_out_params, &ciphertext, + &input_consumed)); + EXPECT_EQ(1U, input_consumed); + } + EXPECT_EQ(1000U, ciphertext.size()); + + // And finish. + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + EXPECT_EQ(1016U, ciphertext.size()); + + // Grab nonce + EXPECT_NE(-1, begin_out_params.find(TAG_NONCE)); + begin_params.push_back(begin_out_params); + + // Decrypt. + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + string plaintext; + + // Send AAD, incrementally, no data + for (int i = 0; i < 1000; ++i) { + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &plaintext, + &input_consumed)); + EXPECT_EQ(0U, input_consumed); + EXPECT_EQ(0U, plaintext.size()); + } + + // Now send data, incrementally. + for (size_t i = 0; i < ciphertext.length(); ++i) { + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(empty_params, string(ciphertext.data() + i, 1), + &update_out_params, &plaintext, &input_consumed)); + EXPECT_EQ(1U, input_consumed); + } + EXPECT_EQ(1000U, plaintext.size()); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmMultiPartAad) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string message = "123456789012345678901234567890123456"; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 128); + AuthorizationSet begin_out_params; + + AuthorizationSet update_params; + update_params.push_back(TAG_ASSOCIATED_DATA, "foo", 3); + + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params)); + + // No data, AAD only. + string ciphertext; + size_t input_consumed; + AuthorizationSet update_out_params; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "" /* message */, &update_out_params, + &ciphertext, &input_consumed)); + EXPECT_EQ(0U, input_consumed); + + // AAD and data. + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext, + &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + + // Grab nonce. + EXPECT_NE(-1, begin_out_params.find(TAG_NONCE)); + begin_params.push_back(begin_out_params); + + // Decrypt + update_params.Clear(); + update_params.push_back(TAG_ASSOCIATED_DATA, "foofoo", 6); + + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params)); + string plaintext; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params, + &plaintext, &input_consumed)); + EXPECT_EQ(ciphertext.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext)); + + EXPECT_EQ(message, plaintext); + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmBadAad) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string message = "12345678901234567890123456789012"; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 128); + + AuthorizationSet update_params; + update_params.push_back(TAG_ASSOCIATED_DATA, "foobar", 6); + + AuthorizationSet finish_params; + AuthorizationSet finish_out_params; + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params)); + AuthorizationSet update_out_params; + string ciphertext; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext, + &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + + // Grab nonce + EXPECT_NE(-1, begin_out_params.find(TAG_NONCE)); + begin_params.push_back(begin_out_params); + + update_params.Clear(); + update_params.push_back(TAG_ASSOCIATED_DATA, "barfoo" /* Wrong AAD */, 6); + + // Decrypt. + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params)); + string plaintext; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params, + &plaintext, &input_consumed)); + EXPECT_EQ(ciphertext.size(), input_consumed); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmWrongNonce) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string message = "12345678901234567890123456789012"; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 128); + + AuthorizationSet update_params; + update_params.push_back(TAG_ASSOCIATED_DATA, "foobar", 6); + + // Encrypt + AuthorizationSet begin_out_params; + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params)); + AuthorizationSet update_out_params; + string ciphertext; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext, + &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + + begin_params.push_back(TAG_NONCE, "123456789012", 12); + + // Decrypt + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params)); + string plaintext; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params, + &plaintext, &input_consumed)); + EXPECT_EQ(ciphertext.size(), input_consumed); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext)); + + // With wrong nonce, should have gotten garbage plaintext. + EXPECT_NE(message, plaintext); + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(EncryptionOperationsTest, AesGcmCorruptTag) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MIN_MAC_LENGTH, 128))); + string aad = "foobar"; + string message = "123456789012345678901234567890123456"; + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + begin_params.push_back(TAG_MAC_LENGTH, 128); + AuthorizationSet begin_out_params; + + AuthorizationSet update_params; + update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size()); + + // Encrypt + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params)); + AuthorizationSet update_out_params; + string ciphertext; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext, + &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext)); + + // Corrupt tag + (*ciphertext.rbegin())++; + + // Grab nonce. + EXPECT_NE(-1, begin_out_params.find(TAG_NONCE)); + begin_params.push_back(begin_out_params); + + // Decrypt. + EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params)); + string plaintext; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params, + &plaintext, &input_consumed)); + EXPECT_EQ(ciphertext.size(), input_consumed); + EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext)); + + EXPECT_EQ(message, plaintext); + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +typedef Keymaster2Test MaxOperationsTest; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, MaxOperationsTest, test_params); + +TEST_P(MaxOperationsTest, TestLimit) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .EcbMode() + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MAX_USES_PER_BOOT, 3))); + + string message = "1234567890123456"; + string ciphertext1 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE); + string ciphertext2 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE); + string ciphertext3 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE); + + // Fourth time should fail. + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + EXPECT_EQ(KM_ERROR_KEY_MAX_OPS_EXCEEDED, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(MaxOperationsTest, TestAbort) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .AesEncryptionKey(128) + .EcbMode() + .Authorization(TAG_PADDING, KM_PAD_NONE) + .Authorization(TAG_MAX_USES_PER_BOOT, 3))); + + string message = "1234567890123456"; + string ciphertext1 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE); + string ciphertext2 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE); + string ciphertext3 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE); + + // Fourth time should fail. + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + EXPECT_EQ(KM_ERROR_KEY_MAX_OPS_EXCEEDED, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +typedef Keymaster2Test AddEntropyTest; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, AddEntropyTest, test_params); + +TEST_P(AddEntropyTest, AddEntropy) { + // There's no obvious way to test that entropy is actually added, but we can test that the API + // doesn't blow up or return an error. + EXPECT_EQ(KM_ERROR_OK, + device()->add_rng_entropy(device(), reinterpret_cast<const uint8_t*>("foo"), 3)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +typedef Keymaster2Test Keymaster0AdapterTest; +INSTANTIATE_TEST_CASE_P( + AndroidKeymasterTest, Keymaster0AdapterTest, + ::testing::Values( + InstanceCreatorPtr(new Keymaster0AdapterTestInstanceCreator(true /* support_ec */)), + InstanceCreatorPtr(new Keymaster0AdapterTestInstanceCreator(false /* support_ec */)))); + +TEST_P(Keymaster0AdapterTest, OldSoftwareKeymaster1RsaBlob) { + // Load and use an old-style Keymaster1 software key blob. These blobs contain OCB-encrypted + // key data. + string km1_sw = read_file("km1_sw_rsa_512.blob"); + EXPECT_EQ(486U, km1_sw.length()); + + uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km1_sw.length())); + memcpy(key_data, km1_sw.data(), km1_sw.length()); + set_key_blob(key_data, km1_sw.length()); + + string message(64, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(Keymaster0AdapterTest, UnversionedSoftwareKeymaster1RsaBlob) { + // Load and use an old-style Keymaster1 software key blob, without the version byte. These + // blobs contain OCB-encrypted key data. + string km1_sw = read_file("km1_sw_rsa_512_unversioned.blob"); + EXPECT_EQ(477U, km1_sw.length()); + + uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km1_sw.length())); + memcpy(key_data, km1_sw.data(), km1_sw.length()); + set_key_blob(key_data, km1_sw.length()); + + string message(64, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(Keymaster0AdapterTest, OldSoftwareKeymaster1EcdsaBlob) { + // Load and use an old-style Keymaster1 software key blob. These blobs contain OCB-encrypted + // key data. + string km1_sw = read_file("km1_sw_ecdsa_256.blob"); + EXPECT_EQ(270U, km1_sw.length()); + + uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km1_sw.length())); + memcpy(key_data, km1_sw.data(), km1_sw.length()); + set_key_blob(key_data, km1_sw.length()); + + string message(32, static_cast<char>(0xFF)); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +struct Malloc_Delete { + void operator()(void* p) { free(p); } +}; + +TEST_P(Keymaster0AdapterTest, OldSoftwareKeymaster0RsaBlob) { + // Load and use an old softkeymaster blob. These blobs contain PKCS#8 key data. + string km0_sw = read_file("km0_sw_rsa_512.blob"); + EXPECT_EQ(333U, km0_sw.length()); + + uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km0_sw.length())); + memcpy(key_data, km0_sw.data(), km0_sw.length()); + set_key_blob(key_data, km0_sw.length()); + + string message(64, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(Keymaster0AdapterTest, OldSwKeymaster0RsaBlobGetCharacteristics) { + // Load and use an old softkeymaster blob. These blobs contain PKCS#8 key data. + string km0_sw = read_file("km0_sw_rsa_512.blob"); + EXPECT_EQ(333U, km0_sw.length()); + + uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km0_sw.length())); + memcpy(key_data, km0_sw.data(), km0_sw.length()); + set_key_blob(key_data, km0_sw.length()); + + EXPECT_EQ(KM_ERROR_OK, GetCharacteristics()); + EXPECT_TRUE(contains(sw_enforced(), TAG_ALGORITHM, KM_ALGORITHM_RSA)); + EXPECT_TRUE(contains(sw_enforced(), TAG_KEY_SIZE, 512)); + EXPECT_TRUE(contains(sw_enforced(), TAG_RSA_PUBLIC_EXPONENT, 3)); + EXPECT_TRUE(contains(sw_enforced(), TAG_DIGEST, KM_DIGEST_NONE)); + EXPECT_TRUE(contains(sw_enforced(), TAG_PADDING, KM_PAD_NONE)); + EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_SIGN)); + EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_VERIFY)); + EXPECT_TRUE(sw_enforced().GetTagValue(TAG_ALL_USERS)); + EXPECT_TRUE(sw_enforced().GetTagValue(TAG_NO_AUTH_REQUIRED)); + + EXPECT_EQ(0, GetParam()->keymaster0_calls()); +} + +TEST_P(Keymaster0AdapterTest, OldHwKeymaster0RsaBlob) { + // Load and use an old softkeymaster blob. These blobs contain PKCS#8 key data. + string km0_sw = read_file("km0_sw_rsa_512.blob"); + EXPECT_EQ(333U, km0_sw.length()); + + // The keymaster0 wrapper swaps the old softkeymaster leading 'P' for a 'Q' to make the key not + // be recognized as a software key. Do the same here to pretend this is a hardware key. + EXPECT_EQ('P', km0_sw[0]); + km0_sw[0] = 'Q'; + + uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km0_sw.length())); + memcpy(key_data, km0_sw.data(), km0_sw.length()); + set_key_blob(key_data, km0_sw.length()); + + string message(64, 'a'); + string signature; + SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE); + VerifyMessage(message, signature, KM_DIGEST_NONE, KM_PAD_NONE); + + EXPECT_EQ(5, GetParam()->keymaster0_calls()); +} + +TEST_P(Keymaster0AdapterTest, OldHwKeymaster0RsaBlobGetCharacteristics) { + // Load and use an old softkeymaster blob. These blobs contain PKCS#8 key data. + string km0_sw = read_file("km0_sw_rsa_512.blob"); + EXPECT_EQ(333U, km0_sw.length()); + + // The keymaster0 wrapper swaps the old softkeymaster leading 'P' for a 'Q' to make the key not + // be recognized as a software key. Do the same here to pretend this is a hardware key. + EXPECT_EQ('P', km0_sw[0]); + km0_sw[0] = 'Q'; + + uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km0_sw.length())); + memcpy(key_data, km0_sw.data(), km0_sw.length()); + set_key_blob(key_data, km0_sw.length()); + + EXPECT_EQ(KM_ERROR_OK, GetCharacteristics()); + EXPECT_TRUE(contains(hw_enforced(), TAG_ALGORITHM, KM_ALGORITHM_RSA)); + EXPECT_TRUE(contains(hw_enforced(), TAG_KEY_SIZE, 512)); + EXPECT_TRUE(contains(hw_enforced(), TAG_RSA_PUBLIC_EXPONENT, 3)); + EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_NONE)); + EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_MD5)); + EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA1)); + EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA_2_224)); + EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA_2_256)); + EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA_2_384)); + EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA_2_512)); + EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_NONE)); + EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT)); + EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN)); + EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_RSA_OAEP)); + EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_RSA_PSS)); + EXPECT_EQ(15U, hw_enforced().size()); + + EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_SIGN)); + EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_VERIFY)); + EXPECT_TRUE(sw_enforced().GetTagValue(TAG_ALL_USERS)); + EXPECT_TRUE(sw_enforced().GetTagValue(TAG_NO_AUTH_REQUIRED)); + + EXPECT_FALSE(contains(sw_enforced(), TAG_ALGORITHM, KM_ALGORITHM_RSA)); + EXPECT_FALSE(contains(sw_enforced(), TAG_KEY_SIZE, 512)); + EXPECT_FALSE(contains(sw_enforced(), TAG_RSA_PUBLIC_EXPONENT, 3)); + EXPECT_FALSE(contains(sw_enforced(), TAG_DIGEST, KM_DIGEST_NONE)); + EXPECT_FALSE(contains(sw_enforced(), TAG_PADDING, KM_PAD_NONE)); + + EXPECT_EQ(1, GetParam()->keymaster0_calls()); +} + +typedef Keymaster2Test AttestationTest; +INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, AttestationTest, test_params); + +static X509* parse_cert_blob(const keymaster_blob_t& blob) { + const uint8_t* p = blob.data; + return d2i_X509(nullptr, &p, blob.data_length); +} + +static bool verify_chain(const keymaster_cert_chain_t& chain) { + for (size_t i = 0; i < chain.entry_count - 1; ++i) { + keymaster_blob_t& key_cert_blob = chain.entries[i]; + keymaster_blob_t& signing_cert_blob = chain.entries[i + 1]; + + X509_Ptr key_cert(parse_cert_blob(key_cert_blob)); + X509_Ptr signing_cert(parse_cert_blob(signing_cert_blob)); + EXPECT_TRUE(!!key_cert.get() && !!signing_cert.get()); + if (!key_cert.get() || !signing_cert.get()) + return false; + + EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get())); + EXPECT_TRUE(!!signing_pubkey.get()); + if (!signing_pubkey.get()) + return false; + + EXPECT_EQ(1, X509_verify(key_cert.get(), signing_pubkey.get())) + << "Verification of certificate " << i << " failed"; + } + + return true; +} + +// Extract attestation record from cert. Returned object is still part of cert; don't free it +// separately. +static ASN1_OCTET_STRING* get_attestation_record(X509* certificate) { + ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */)); + EXPECT_TRUE(!!oid.get()); + if (!oid.get()) + return nullptr; + + int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */); + EXPECT_NE(-1, location); + if (location == -1) + return nullptr; + + X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location); + EXPECT_TRUE(!!attest_rec_ext); + if (!attest_rec_ext) + return nullptr; + + ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext); + EXPECT_TRUE(!!attest_rec); + return attest_rec; +} + +static bool verify_attestation_record(AuthorizationSet expected_sw_enforced, + AuthorizationSet expected_tee_enforced, + const keymaster_blob_t& attestation_cert) { + + X509_Ptr cert(parse_cert_blob(attestation_cert)); + EXPECT_TRUE(!!cert.get()); + if (!cert.get()) + return false; + + ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get()); + EXPECT_TRUE(!!attest_rec); + if (!attest_rec) + return false; + + AuthorizationSet att_sw_enforced; + AuthorizationSet att_tee_enforced; + EXPECT_EQ(KM_ERROR_OK, parse_attestation_record(attest_rec->data, attest_rec->length, + &att_sw_enforced, &att_tee_enforced)); + + // Add TAG_USER_ID to the attestation sw-enforced list, because user IDs are not included in + // attestations, since they're meaningless off-device. + uint32_t user_id; + if (expected_sw_enforced.GetTagValue(TAG_USER_ID, &user_id)) + att_sw_enforced.push_back(TAG_USER_ID, user_id); + if (expected_tee_enforced.GetTagValue(TAG_USER_ID, &user_id)) + att_tee_enforced.push_back(TAG_USER_ID, user_id); + + att_sw_enforced.Sort(); + expected_sw_enforced.Sort(); + EXPECT_EQ(expected_sw_enforced, att_sw_enforced); + + att_tee_enforced.Sort(); + expected_tee_enforced.Sort(); + EXPECT_EQ(expected_tee_enforced, att_tee_enforced); + + return true; +} + +TEST_P(AttestationTest, RsaSignedWithRsa) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + + keymaster_cert_chain_t cert_chain; + EXPECT_EQ(KM_ERROR_OK, AttestKey(KM_ALGORITHM_RSA, &cert_chain)); + EXPECT_EQ(3U, cert_chain.entry_count); + EXPECT_TRUE(verify_chain(cert_chain)); + EXPECT_TRUE(verify_attestation_record(sw_enforced(), hw_enforced(), cert_chain.entries[0])); + + keymaster_free_cert_chain(&cert_chain); +} + +TEST_P(AttestationTest, RsaSignedWithEc) { + ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(256, 3) + .Digest(KM_DIGEST_NONE) + .Padding(KM_PAD_NONE))); + + keymaster_cert_chain_t cert_chain; + EXPECT_EQ(KM_ERROR_OK, AttestKey(KM_ALGORITHM_EC, &cert_chain)); + EXPECT_EQ(3U, cert_chain.entry_count); + EXPECT_TRUE(verify_chain(cert_chain)); + EXPECT_TRUE(verify_attestation_record(sw_enforced(), hw_enforced(), cert_chain.entries[0])); + + keymaster_free_cert_chain(&cert_chain); +} + +TEST(SoftKeymasterWrapperTest, CheckKeymaster2Device) { + // Make a good fake device, and wrap it. + SoftKeymasterDevice* good_fake(new SoftKeymasterDevice(new TestKeymasterContext)); + + // Wrap it and check it. + SoftKeymasterDevice* good_fake_wrapper(new SoftKeymasterDevice(new TestKeymasterContext)); + good_fake_wrapper->SetHardwareDevice(good_fake->keymaster_device()); + EXPECT_TRUE(good_fake_wrapper->Keymaster1DeviceIsGood()); + + // Close and clean up wrapper and wrapped + good_fake_wrapper->keymaster_device()->common.close(good_fake_wrapper->hw_device()); + + // Make a "bad" (doesn't support all digests) device; + keymaster1_device_t* sha256_only_fake = make_device_sha256_only( + (new SoftKeymasterDevice(new TestKeymasterContext("256")))->keymaster_device()); + + // Wrap it and check it. + SoftKeymasterDevice* sha256_only_fake_wrapper( + (new SoftKeymasterDevice(new TestKeymasterContext))); + sha256_only_fake_wrapper->SetHardwareDevice(sha256_only_fake); + EXPECT_FALSE(sha256_only_fake_wrapper->Keymaster1DeviceIsGood()); + + // Close and clean up wrapper and wrapped + sha256_only_fake_wrapper->keymaster_device()->common.close( + sha256_only_fake_wrapper->hw_device()); +} + +} // namespace test +} // namespace keymaster
diff --git a/keymaster/android_keymaster_test_utils.cpp b/keymaster/android_keymaster_test_utils.cpp new file mode 100644 index 0000000..346421a --- /dev/null +++ b/keymaster/android_keymaster_test_utils.cpp
@@ -0,0 +1,890 @@ +/* + * Copyright 2014 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 "android_keymaster_test_utils.h" + +#include <algorithm> + +#include <openssl/rand.h> + +#include <keymaster/android_keymaster_messages.h> +#include <keymaster/android_keymaster_utils.h> + +using std::copy_if; +using std::find_if; +using std::is_permutation; +using std::ostream; +using std::string; +using std::vector; + +#ifndef KEYMASTER_NAME_TAGS +#error Keymaster test code requires that KEYMASTER_NAME_TAGS is defined +#endif + +std::ostream& operator<<(std::ostream& os, const keymaster_key_param_t& param) { + os << "Tag: " << keymaster::StringifyTag(param.tag); + switch (keymaster_tag_get_type(param.tag)) { + case KM_INVALID: + os << " Invalid"; + break; + case KM_UINT_REP: + os << " (Rep)"; + /* Falls through */ + case KM_UINT: + os << " Int: " << param.integer; + break; + case KM_ENUM_REP: + os << " (Rep)"; + /* Falls through */ + case KM_ENUM: + os << " Enum: " << param.enumerated; + break; + case KM_ULONG_REP: + os << " (Rep)"; + /* Falls through */ + case KM_ULONG: + os << " Long: " << param.long_integer; + break; + case KM_DATE: + os << " Date: " << param.date_time; + break; + case KM_BOOL: + os << " Bool: " << param.boolean; + break; + case KM_BIGNUM: + os << " Bignum: "; + if (!param.blob.data) + os << "(null)"; + else + for (size_t i = 0; i < param.blob.data_length; ++i) + os << std::hex << std::setw(2) << static_cast<int>(param.blob.data[i]) << std::dec; + break; + case KM_BYTES: + os << " Bytes: "; + if (!param.blob.data) + os << "(null)"; + else + for (size_t i = 0; i < param.blob.data_length; ++i) + os << std::hex << std::setw(2) << static_cast<int>(param.blob.data[i]) << std::dec; + break; + } + return os; +} + +bool operator==(const keymaster_key_param_t& a, const keymaster_key_param_t& b) { + if (a.tag != b.tag) { + return false; + } + + switch (keymaster_tag_get_type(a.tag)) { + case KM_INVALID: + return true; + case KM_UINT_REP: + case KM_UINT: + return a.integer == b.integer; + case KM_ENUM_REP: + case KM_ENUM: + return a.enumerated == b.enumerated; + case KM_ULONG: + case KM_ULONG_REP: + return a.long_integer == b.long_integer; + case KM_DATE: + return a.date_time == b.date_time; + case KM_BOOL: + return a.boolean == b.boolean; + case KM_BIGNUM: + case KM_BYTES: + if ((a.blob.data == NULL || b.blob.data == NULL) && a.blob.data != b.blob.data) + return false; + return a.blob.data_length == b.blob.data_length && + (memcmp(a.blob.data, b.blob.data, a.blob.data_length) == 0); + } + + return false; +} + +static char hex_value[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9' + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +string hex2str(string a) { + string b; + size_t num = a.size() / 2; + b.resize(num); + for (size_t i = 0; i < num; i++) { + b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]); + } + return b; +} + +namespace keymaster { + +bool operator==(const AuthorizationSet& a, const AuthorizationSet& b) { + if (a.size() != b.size()) + return false; + + for (size_t i = 0; i < a.size(); ++i) + if (!(a[i] == b[i])) + return false; + return true; +} + +bool operator!=(const AuthorizationSet& a, const AuthorizationSet& b) { + return !(a == b); +} + +std::ostream& operator<<(std::ostream& os, const AuthorizationSet& set) { + if (set.size() == 0) + os << "(Empty)" << std::endl; + else { + os << "\n"; + for (size_t i = 0; i < set.size(); ++i) + os << set[i] << std::endl; + } + return os; +} + +namespace test { + +Keymaster2Test::Keymaster2Test() : op_handle_(OP_HANDLE_SENTINEL) { + memset(&characteristics_, 0, sizeof(characteristics_)); + blob_.key_material = nullptr; + RAND_seed("foobar", 6); + blob_.key_material = 0; + device_ = GetParam()->CreateDevice(); +} + +Keymaster2Test::~Keymaster2Test() { + FreeCharacteristics(); + FreeKeyBlob(); + device_->common.close(reinterpret_cast<hw_device_t*>(device_)); +} + +keymaster2_device_t* Keymaster2Test::device() { + return device_; +} + +keymaster_error_t Keymaster2Test::GenerateKey(const AuthorizationSetBuilder& builder) { + AuthorizationSet params(builder.build()); + params.push_back(UserAuthParams()); + params.push_back(ClientParams()); + + FreeKeyBlob(); + FreeCharacteristics(); + return device()->generate_key(device(), ¶ms, &blob_, &characteristics_); +} + +keymaster_error_t Keymaster2Test::DeleteKey() { + return device()->delete_key(device(), &blob_); +} + +keymaster_error_t Keymaster2Test::ImportKey(const AuthorizationSetBuilder& builder, + keymaster_key_format_t format, + const string& key_material) { + AuthorizationSet params(builder.build()); + params.push_back(UserAuthParams()); + params.push_back(ClientParams()); + + FreeKeyBlob(); + FreeCharacteristics(); + keymaster_blob_t key = {reinterpret_cast<const uint8_t*>(key_material.c_str()), + key_material.length()}; + return device()->import_key(device(), ¶ms, format, &key, &blob_, &characteristics_); +} + +AuthorizationSet Keymaster2Test::UserAuthParams() { + AuthorizationSet set; + set.push_back(TAG_USER_ID, 7); + set.push_back(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD); + set.push_back(TAG_AUTH_TIMEOUT, 300); + return set; +} + +AuthorizationSet Keymaster2Test::ClientParams() { + AuthorizationSet set; + set.push_back(TAG_APPLICATION_ID, "app_id", 6); + return set; +} + +keymaster_error_t Keymaster2Test::BeginOperation(keymaster_purpose_t purpose) { + AuthorizationSet in_params(client_params()); + keymaster_key_param_set_t out_params; + keymaster_error_t error = + device()->begin(device(), purpose, &blob_, &in_params, &out_params, &op_handle_); + EXPECT_EQ(0U, out_params.length); + EXPECT_TRUE(out_params.params == nullptr); + return error; +} + +keymaster_error_t Keymaster2Test::BeginOperation(keymaster_purpose_t purpose, + const AuthorizationSet& input_set, + AuthorizationSet* output_set) { + keymaster_key_param_set_t out_params; + keymaster_error_t error = + device()->begin(device(), purpose, &blob_, &input_set, &out_params, &op_handle_); + if (error == KM_ERROR_OK) { + if (output_set) { + output_set->Reinitialize(out_params); + } else { + EXPECT_EQ(0U, out_params.length); + EXPECT_TRUE(out_params.params == nullptr); + } + keymaster_free_param_set(&out_params); + } + return error; +} + +keymaster_error_t Keymaster2Test::UpdateOperation(const string& message, string* output, + size_t* input_consumed) { + EXPECT_NE(op_handle_, OP_HANDLE_SENTINEL); + keymaster_blob_t input = {reinterpret_cast<const uint8_t*>(message.c_str()), message.length()}; + keymaster_blob_t out_tmp; + keymaster_key_param_set_t out_params; + keymaster_error_t error = device()->update(device(), op_handle_, nullptr /* params */, &input, + input_consumed, &out_params, &out_tmp); + if (error == KM_ERROR_OK && out_tmp.data) + output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length); + free(const_cast<uint8_t*>(out_tmp.data)); + return error; +} + +keymaster_error_t Keymaster2Test::UpdateOperation(const AuthorizationSet& additional_params, + const string& message, + AuthorizationSet* output_params, string* output, + size_t* input_consumed) { + EXPECT_NE(op_handle_, OP_HANDLE_SENTINEL); + keymaster_blob_t input = {reinterpret_cast<const uint8_t*>(message.c_str()), message.length()}; + keymaster_blob_t out_tmp; + keymaster_key_param_set_t out_params; + keymaster_error_t error = device()->update(device(), op_handle_, &additional_params, &input, + input_consumed, &out_params, &out_tmp); + if (error == KM_ERROR_OK && out_tmp.data) + output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length); + free((void*)out_tmp.data); + if (output_params) + output_params->Reinitialize(out_params); + keymaster_free_param_set(&out_params); + return error; +} + +keymaster_error_t Keymaster2Test::FinishOperation(string* output) { + return FinishOperation("", output); +} + +keymaster_error_t Keymaster2Test::FinishOperation(const string& signature, string* output) { + AuthorizationSet additional_params; + AuthorizationSet output_params; + return FinishOperation(additional_params, signature, &output_params, output); +} + +keymaster_error_t Keymaster2Test::FinishOperation(const AuthorizationSet& additional_params, + const string& signature, + AuthorizationSet* output_params, string* output) { + keymaster_blob_t sig = {reinterpret_cast<const uint8_t*>(signature.c_str()), + signature.length()}; + keymaster_blob_t out_tmp; + keymaster_key_param_set_t out_params; + keymaster_error_t error = device()->finish(device(), op_handle_, &additional_params, + nullptr /* input */, &sig, &out_params, &out_tmp); + if (error != KM_ERROR_OK) { + EXPECT_TRUE(out_tmp.data == nullptr); + EXPECT_TRUE(out_params.params == nullptr); + return error; + } + + if (out_tmp.data) + output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length); + free((void*)out_tmp.data); + if (output_params) + output_params->Reinitialize(out_params); + keymaster_free_param_set(&out_params); + return error; +} + +keymaster_error_t Keymaster2Test::AbortOperation() { + return device()->abort(device(), op_handle_); +} + +keymaster_error_t Keymaster2Test::AttestKey(keymaster_algorithm_t algorithm, + keymaster_cert_chain_t* cert_chain) { + AuthorizationSet attest_params( + AuthorizationSetBuilder().Authorization(TAG_ALGORITHM, algorithm)); + attest_params.push_back(UserAuthParams()); + attest_params.push_back(ClientParams()); + return device()->attest_key(device(), &blob_, &attest_params, cert_chain); +} + +string Keymaster2Test::ProcessMessage(keymaster_purpose_t purpose, const string& message) { + EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, client_params(), NULL /* output_params */)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(&result)); + return result; +} + +string Keymaster2Test::ProcessMessage(keymaster_purpose_t purpose, const string& message, + const AuthorizationSet& begin_params, + const AuthorizationSet& update_params, + AuthorizationSet* begin_out_params) { + EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, begin_params, begin_out_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, nullptr /* output_params */, + &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(update_params, "", &result)); + return result; +} + +string Keymaster2Test::ProcessMessage(keymaster_purpose_t purpose, const string& message, + const string& signature, const AuthorizationSet& begin_params, + const AuthorizationSet& update_params, + AuthorizationSet* output_params) { + EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, begin_params, output_params)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, nullptr /* output_params */, + &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(update_params, signature, &result)); + return result; +} + +string Keymaster2Test::ProcessMessage(keymaster_purpose_t purpose, const string& message, + const string& signature) { + EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, client_params(), NULL /* output_params */)); + + string result; + size_t input_consumed; + EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed)); + EXPECT_EQ(message.size(), input_consumed); + EXPECT_EQ(KM_ERROR_OK, FinishOperation(signature, &result)); + return result; +} + +void Keymaster2Test::SignMessage(const string& message, string* signature, + keymaster_digest_t digest) { + SCOPED_TRACE("SignMessage"); + AuthorizationSet input_params(AuthorizationSet(client_params_, array_length(client_params_))); + input_params.push_back(TAG_DIGEST, digest); + AuthorizationSet update_params; + AuthorizationSet output_params; + *signature = + ProcessMessage(KM_PURPOSE_SIGN, message, input_params, update_params, &output_params); + EXPECT_GT(signature->size(), 0U); +} + +void Keymaster2Test::SignMessage(const string& message, string* signature, + keymaster_digest_t digest, keymaster_padding_t padding) { + SCOPED_TRACE("SignMessage"); + AuthorizationSet input_params(AuthorizationSet(client_params_, array_length(client_params_))); + input_params.push_back(TAG_DIGEST, digest); + input_params.push_back(TAG_PADDING, padding); + AuthorizationSet update_params; + AuthorizationSet output_params; + *signature = + ProcessMessage(KM_PURPOSE_SIGN, message, input_params, update_params, &output_params); + EXPECT_GT(signature->size(), 0U); +} + +void Keymaster2Test::MacMessage(const string& message, string* signature, size_t mac_length) { + SCOPED_TRACE("SignMessage"); + AuthorizationSet input_params(AuthorizationSet(client_params_, array_length(client_params_))); + input_params.push_back(TAG_MAC_LENGTH, mac_length); + AuthorizationSet update_params; + AuthorizationSet output_params; + *signature = + ProcessMessage(KM_PURPOSE_SIGN, message, input_params, update_params, &output_params); + EXPECT_GT(signature->size(), 0U); +} + +void Keymaster2Test::VerifyMessage(const string& message, const string& signature, + keymaster_digest_t digest) { + SCOPED_TRACE("VerifyMessage"); + AuthorizationSet input_params(client_params()); + input_params.push_back(TAG_DIGEST, digest); + AuthorizationSet update_params; + AuthorizationSet output_params; + ProcessMessage(KM_PURPOSE_VERIFY, message, signature, input_params, update_params, + &output_params); +} + +void Keymaster2Test::VerifyMessage(const string& message, const string& signature, + keymaster_digest_t digest, keymaster_padding_t padding) { + SCOPED_TRACE("VerifyMessage"); + AuthorizationSet input_params(client_params()); + input_params.push_back(TAG_DIGEST, digest); + input_params.push_back(TAG_PADDING, padding); + AuthorizationSet update_params; + AuthorizationSet output_params; + ProcessMessage(KM_PURPOSE_VERIFY, message, signature, input_params, update_params, + &output_params); +} + +void Keymaster2Test::VerifyMac(const string& message, const string& signature) { + SCOPED_TRACE("VerifyMac"); + ProcessMessage(KM_PURPOSE_VERIFY, message, signature); +} + +string Keymaster2Test::EncryptMessage(const string& message, keymaster_padding_t padding, + string* generated_nonce) { + SCOPED_TRACE("EncryptMessage"); + AuthorizationSet begin_params(client_params()), output_params; + begin_params.push_back(TAG_PADDING, padding); + AuthorizationSet update_params; + string ciphertext = + ProcessMessage(KM_PURPOSE_ENCRYPT, message, begin_params, update_params, &output_params); + if (generated_nonce) { + keymaster_blob_t nonce_blob; + EXPECT_TRUE(output_params.GetTagValue(TAG_NONCE, &nonce_blob)); + *generated_nonce = make_string(nonce_blob.data, nonce_blob.data_length); + } else { + EXPECT_EQ(-1, output_params.find(TAG_NONCE)); + } + return ciphertext; +} + +string Keymaster2Test::EncryptMessage(const string& message, keymaster_digest_t digest, + keymaster_padding_t padding, string* generated_nonce) { + AuthorizationSet update_params; + return EncryptMessage(update_params, message, digest, padding, generated_nonce); +} + +string Keymaster2Test::EncryptMessage(const string& message, keymaster_block_mode_t block_mode, + keymaster_padding_t padding, string* generated_nonce) { + AuthorizationSet update_params; + return EncryptMessage(update_params, message, block_mode, padding, generated_nonce); +} + +string Keymaster2Test::EncryptMessage(const AuthorizationSet& update_params, const string& message, + keymaster_digest_t digest, keymaster_padding_t padding, + string* generated_nonce) { + SCOPED_TRACE("EncryptMessage"); + AuthorizationSet begin_params(client_params()), output_params; + begin_params.push_back(TAG_PADDING, padding); + begin_params.push_back(TAG_DIGEST, digest); + string ciphertext = + ProcessMessage(KM_PURPOSE_ENCRYPT, message, begin_params, update_params, &output_params); + if (generated_nonce) { + keymaster_blob_t nonce_blob; + EXPECT_TRUE(output_params.GetTagValue(TAG_NONCE, &nonce_blob)); + *generated_nonce = make_string(nonce_blob.data, nonce_blob.data_length); + } else { + EXPECT_EQ(-1, output_params.find(TAG_NONCE)); + } + return ciphertext; +} + +string Keymaster2Test::EncryptMessage(const AuthorizationSet& update_params, const string& message, + keymaster_block_mode_t block_mode, + keymaster_padding_t padding, string* generated_nonce) { + SCOPED_TRACE("EncryptMessage"); + AuthorizationSet begin_params(client_params()), output_params; + begin_params.push_back(TAG_PADDING, padding); + begin_params.push_back(TAG_BLOCK_MODE, block_mode); + string ciphertext = + ProcessMessage(KM_PURPOSE_ENCRYPT, message, begin_params, update_params, &output_params); + if (generated_nonce) { + keymaster_blob_t nonce_blob; + EXPECT_TRUE(output_params.GetTagValue(TAG_NONCE, &nonce_blob)); + *generated_nonce = make_string(nonce_blob.data, nonce_blob.data_length); + } else { + EXPECT_EQ(-1, output_params.find(TAG_NONCE)); + } + return ciphertext; +} + +string Keymaster2Test::EncryptMessageWithParams(const string& message, + const AuthorizationSet& begin_params, + const AuthorizationSet& update_params, + AuthorizationSet* output_params) { + SCOPED_TRACE("EncryptMessageWithParams"); + return ProcessMessage(KM_PURPOSE_ENCRYPT, message, begin_params, update_params, output_params); +} + +string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_padding_t padding) { + SCOPED_TRACE("DecryptMessage"); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, padding); + AuthorizationSet update_params; + return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params); +} + +string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_digest_t digest, + keymaster_padding_t padding) { + SCOPED_TRACE("DecryptMessage"); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, padding); + begin_params.push_back(TAG_DIGEST, digest); + AuthorizationSet update_params; + return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params); +} + +string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_block_mode_t block_mode, + keymaster_padding_t padding) { + SCOPED_TRACE("DecryptMessage"); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, padding); + begin_params.push_back(TAG_BLOCK_MODE, block_mode); + AuthorizationSet update_params; + return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params); +} + +string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_digest_t digest, + keymaster_padding_t padding, const string& nonce) { + SCOPED_TRACE("DecryptMessage"); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, padding); + begin_params.push_back(TAG_DIGEST, digest); + begin_params.push_back(TAG_NONCE, nonce.data(), nonce.size()); + AuthorizationSet update_params; + return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params); +} + +string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_block_mode_t block_mode, + keymaster_padding_t padding, const string& nonce) { + SCOPED_TRACE("DecryptMessage"); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, padding); + begin_params.push_back(TAG_BLOCK_MODE, block_mode); + begin_params.push_back(TAG_NONCE, nonce.data(), nonce.size()); + AuthorizationSet update_params; + return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params); +} + +string Keymaster2Test::DecryptMessage(const AuthorizationSet& update_params, + const string& ciphertext, keymaster_digest_t digest, + keymaster_padding_t padding, const string& nonce) { + SCOPED_TRACE("DecryptMessage"); + AuthorizationSet begin_params(client_params()); + begin_params.push_back(TAG_PADDING, padding); + begin_params.push_back(TAG_DIGEST, digest); + begin_params.push_back(TAG_NONCE, nonce.data(), nonce.size()); + return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params); +} + +keymaster_error_t Keymaster2Test::GetCharacteristics() { + FreeCharacteristics(); + return device()->get_key_characteristics(device(), &blob_, &client_id_, NULL /* app_data */, + &characteristics_); +} + +keymaster_error_t Keymaster2Test::ExportKey(keymaster_key_format_t format, string* export_data) { + keymaster_blob_t export_tmp; + keymaster_error_t error = device()->export_key(device(), format, &blob_, &client_id_, + NULL /* app_data */, &export_tmp); + + if (error != KM_ERROR_OK) + return error; + + *export_data = string(reinterpret_cast<const char*>(export_tmp.data), export_tmp.data_length); + free((void*)export_tmp.data); + return error; +} + +void Keymaster2Test::CheckHmacTestVector(const string& key, const string& message, keymaster_digest_t digest, + string expected_mac) { + ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder() + .HmacKey(key.size() * 8) + .Authorization(TAG_MIN_MAC_LENGTH, expected_mac.size() * 8) + .Digest(digest), + KM_KEY_FORMAT_RAW, key)); + string signature; + MacMessage(message, &signature, expected_mac.size() * 8); + EXPECT_EQ(expected_mac, signature) << "Test vector didn't match for digest " << (int)digest; +} + +void Keymaster2Test::CheckAesCtrTestVector(const string& key, const string& nonce, + const string& message, + const string& expected_ciphertext) { + ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder() + .AesEncryptionKey(key.size() * 8) + .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR) + .Authorization(TAG_CALLER_NONCE) + .Padding(KM_PAD_NONE), + KM_KEY_FORMAT_RAW, key)); + + AuthorizationSet begin_params(client_params()), update_params, output_params; + begin_params.push_back(TAG_NONCE, nonce.data(), nonce.size()); + begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR); + begin_params.push_back(TAG_PADDING, KM_PAD_NONE); + string ciphertext = + EncryptMessageWithParams(message, begin_params, update_params, &output_params); + EXPECT_EQ(expected_ciphertext, ciphertext); +} + +AuthorizationSet Keymaster2Test::hw_enforced() { + return AuthorizationSet(characteristics_.hw_enforced); +} + +AuthorizationSet Keymaster2Test::sw_enforced() { + return AuthorizationSet(characteristics_.sw_enforced); +} + +void Keymaster2Test::FreeCharacteristics() { + keymaster_free_characteristics(&characteristics_); +} + +void Keymaster2Test::FreeKeyBlob() { + free(const_cast<uint8_t*>(blob_.key_material)); + blob_.key_material = NULL; +} + +void Keymaster2Test::corrupt_key_blob() { + assert(blob_.key_material); + uint8_t* tmp = const_cast<uint8_t*>(blob_.key_material); + ++tmp[blob_.key_material_size / 2]; +} + +class Sha256OnlyWrapper { + public: + explicit Sha256OnlyWrapper(const keymaster1_device_t* wrapped_device) : wrapped_device_(wrapped_device) { + + new_module = *wrapped_device_->common.module; + new_module_name = std::string("SHA 256-only ") + wrapped_device_->common.module->name; + new_module.name = new_module_name.c_str(); + + memset(&device_, 0, sizeof(device_)); + device_.common.module = &new_module; + + device_.common.close = close_device; + device_.get_supported_algorithms = get_supported_algorithms; + device_.get_supported_block_modes = get_supported_block_modes; + device_.get_supported_padding_modes = get_supported_padding_modes; + device_.get_supported_digests = get_supported_digests; + device_.get_supported_import_formats = get_supported_import_formats; + device_.get_supported_export_formats = get_supported_export_formats; + device_.add_rng_entropy = add_rng_entropy; + device_.generate_key = generate_key; + device_.get_key_characteristics = get_key_characteristics; + device_.import_key = import_key; + device_.export_key = export_key; + device_.begin = begin; + device_.update = update; + device_.finish = finish; + device_.abort = abort; + } + + keymaster1_device_t* keymaster_device() { return &device_; } + + static bool is_supported(keymaster_digest_t digest) { + return digest == KM_DIGEST_NONE || digest == KM_DIGEST_SHA_2_256; + } + + static bool all_digests_supported(const keymaster_key_param_set_t* params) { + for (size_t i = 0; i < params->length; ++i) + if (params->params[i].tag == TAG_DIGEST) + if (!is_supported(static_cast<keymaster_digest_t>(params->params[i].enumerated))) + return false; + return true; + } + + static const keymaster_key_param_t* + get_algorithm_param(const keymaster_key_param_set_t* params) { + keymaster_key_param_t* end = params->params + params->length; + auto alg_ptr = std::find_if(params->params, end, [](keymaster_key_param_t& p) { + return p.tag == KM_TAG_ALGORITHM; + }); + if (alg_ptr == end) + return nullptr; + return alg_ptr; + } + + static int close_device(hw_device_t* dev) { + Sha256OnlyWrapper* wrapper = reinterpret_cast<Sha256OnlyWrapper*>(dev); + const keymaster1_device_t* wrapped_device = wrapper->wrapped_device_; + delete wrapper; + return wrapped_device->common.close(const_cast<hw_device_t*>(&wrapped_device->common)); + } + + static const keymaster1_device_t* unwrap(const keymaster1_device_t* dev) { + return reinterpret_cast<const Sha256OnlyWrapper*>(dev)->wrapped_device_; + } + + static keymaster_error_t get_supported_algorithms(const struct keymaster1_device* dev, + keymaster_algorithm_t** algorithms, + size_t* algorithms_length) { + return unwrap(dev)->get_supported_algorithms(unwrap(dev), algorithms, algorithms_length); + } + static keymaster_error_t get_supported_block_modes(const struct keymaster1_device* dev, + keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + keymaster_block_mode_t** modes, + size_t* modes_length) { + return unwrap(dev)->get_supported_block_modes(unwrap(dev), algorithm, purpose, modes, + modes_length); + } + static keymaster_error_t get_supported_padding_modes(const struct keymaster1_device* dev, + keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + keymaster_padding_t** modes, + size_t* modes_length) { + return unwrap(dev)->get_supported_padding_modes(unwrap(dev), algorithm, purpose, modes, + modes_length); + } + + static keymaster_error_t get_supported_digests(const keymaster1_device_t* dev, + keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + keymaster_digest_t** digests, + size_t* digests_length) { + keymaster_error_t error = unwrap(dev)->get_supported_digests( + unwrap(dev), algorithm, purpose, digests, digests_length); + if (error != KM_ERROR_OK) + return error; + + std::vector<keymaster_digest_t> filtered_digests; + std::copy_if(*digests, *digests + *digests_length, std::back_inserter(filtered_digests), + [](keymaster_digest_t digest) { return is_supported(digest); }); + + free(*digests); + *digests_length = filtered_digests.size(); + *digests = reinterpret_cast<keymaster_digest_t*>( + malloc(*digests_length * sizeof(keymaster_digest_t))); + std::copy(filtered_digests.begin(), filtered_digests.end(), *digests); + + return KM_ERROR_OK; + } + + static keymaster_error_t get_supported_import_formats(const struct keymaster1_device* dev, + keymaster_algorithm_t algorithm, + keymaster_key_format_t** formats, + size_t* formats_length) { + return unwrap(dev)->get_supported_import_formats(unwrap(dev), algorithm, formats, + formats_length); + } + static keymaster_error_t get_supported_export_formats(const struct keymaster1_device* dev, + keymaster_algorithm_t algorithm, + keymaster_key_format_t** formats, + size_t* formats_length) { + return unwrap(dev)->get_supported_export_formats(unwrap(dev), algorithm, formats, + formats_length); + } + static keymaster_error_t add_rng_entropy(const struct keymaster1_device* dev, + const uint8_t* data, size_t data_length) { + return unwrap(dev)->add_rng_entropy(unwrap(dev), data, data_length); + } + + static keymaster_error_t generate_key(const keymaster1_device_t* dev, + const keymaster_key_param_set_t* params, + keymaster_key_blob_t* key_blob, + keymaster_key_characteristics_t** characteristics) { + auto alg_ptr = get_algorithm_param(params); + if (!alg_ptr) + return KM_ERROR_UNSUPPORTED_ALGORITHM; + if (alg_ptr->enumerated == KM_ALGORITHM_HMAC && !all_digests_supported(params)) + return KM_ERROR_UNSUPPORTED_DIGEST; + + return unwrap(dev)->generate_key(unwrap(dev), params, key_blob, characteristics); + } + + static keymaster_error_t + get_key_characteristics(const struct keymaster1_device* dev, + const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id, + const keymaster_blob_t* app_data, + keymaster_key_characteristics_t** characteristics) { + return unwrap(dev)->get_key_characteristics(unwrap(dev), key_blob, client_id, app_data, + characteristics); + } + + static keymaster_error_t + import_key(const keymaster1_device_t* dev, const keymaster_key_param_set_t* params, + keymaster_key_format_t key_format, const keymaster_blob_t* key_data, + keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t** characteristics) { + auto alg_ptr = get_algorithm_param(params); + if (!alg_ptr) + return KM_ERROR_UNSUPPORTED_ALGORITHM; + if (alg_ptr->enumerated == KM_ALGORITHM_HMAC && !all_digests_supported(params)) + return KM_ERROR_UNSUPPORTED_DIGEST; + + return unwrap(dev)->import_key(unwrap(dev), params, key_format, key_data, key_blob, + characteristics); + } + + static keymaster_error_t export_key(const struct keymaster1_device* dev, // + keymaster_key_format_t export_format, + const keymaster_key_blob_t* key_to_export, + const keymaster_blob_t* client_id, + const keymaster_blob_t* app_data, + keymaster_blob_t* export_data) { + return unwrap(dev)->export_key(unwrap(dev), export_format, key_to_export, client_id, + app_data, export_data); + } + + static keymaster_error_t begin(const keymaster1_device_t* dev, // + keymaster_purpose_t purpose, const keymaster_key_blob_t* key, + const keymaster_key_param_set_t* in_params, + keymaster_key_param_set_t* out_params, + keymaster_operation_handle_t* operation_handle) { + if (!all_digests_supported(in_params)) + return KM_ERROR_UNSUPPORTED_DIGEST; + return unwrap(dev)->begin(unwrap(dev), purpose, key, in_params, out_params, + operation_handle); + } + + static keymaster_error_t update(const keymaster1_device_t* dev, + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* input, size_t* input_consumed, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) { + return unwrap(dev)->update(unwrap(dev), operation_handle, in_params, input, input_consumed, + out_params, output); + } + + static keymaster_error_t finish(const struct keymaster1_device* dev, // + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* signature, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) { + return unwrap(dev)->finish(unwrap(dev), operation_handle, in_params, signature, out_params, + output); + } + + static keymaster_error_t abort(const struct keymaster1_device* dev, + keymaster_operation_handle_t operation_handle) { + return unwrap(dev)->abort(unwrap(dev), operation_handle); + } + + private: + keymaster1_device_t device_; + const keymaster1_device_t* wrapped_device_; + hw_module_t new_module; + string new_module_name; +}; + +keymaster1_device_t* make_device_sha256_only(keymaster1_device_t* device) { + return (new Sha256OnlyWrapper(device))->keymaster_device(); +} + +} // namespace test +} // namespace keymaster
diff --git a/keymaster/android_keymaster_test_utils.h b/keymaster/android_keymaster_test_utils.h new file mode 100644 index 0000000..7a4d35e --- /dev/null +++ b/keymaster/android_keymaster_test_utils.h
@@ -0,0 +1,464 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_ANDROID_KEYMASTER_TEST_UTILS_H_ +#define SYSTEM_KEYMASTER_ANDROID_KEYMASTER_TEST_UTILS_H_ + +/* + * Utilities used to help with testing. Not used in production code. + */ + +#include <stdarg.h> + +#include <algorithm> +#include <memory> +#include <ostream> +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +#include <hardware/keymaster0.h> +#include <hardware/keymaster1.h> +#include <hardware/keymaster2.h> +#include <hardware/keymaster_defs.h> +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/authorization_set.h> +#include <keymaster/logger.h> + +std::ostream& operator<<(std::ostream& os, const keymaster_key_param_t& param); +bool operator==(const keymaster_key_param_t& a, const keymaster_key_param_t& b); +std::string hex2str(std::string); + +namespace keymaster { + +bool operator==(const AuthorizationSet& a, const AuthorizationSet& b); +bool operator!=(const AuthorizationSet& a, const AuthorizationSet& b); + +std::ostream& operator<<(std::ostream& os, const AuthorizationSet& set); + +namespace test { + +template <keymaster_tag_t Tag, typename KeymasterEnum> +bool contains(const AuthorizationSet& set, TypedEnumTag<KM_ENUM, Tag, KeymasterEnum> tag, + KeymasterEnum val) { + int pos = set.find(tag); + return pos != -1 && set[pos].enumerated == val; +} + +template <keymaster_tag_t Tag, typename KeymasterEnum> +bool contains(const AuthorizationSet& set, TypedEnumTag<KM_ENUM_REP, Tag, KeymasterEnum> tag, + KeymasterEnum val) { + int pos = -1; + while ((pos = set.find(tag, pos)) != -1) + if (set[pos].enumerated == val) + return true; + return false; +} + +template <keymaster_tag_t Tag> +bool contains(const AuthorizationSet& set, TypedTag<KM_UINT, Tag> tag, uint32_t val) { + int pos = set.find(tag); + return pos != -1 && set[pos].integer == val; +} + +template <keymaster_tag_t Tag> +bool contains(const AuthorizationSet& set, TypedTag<KM_UINT_REP, Tag> tag, uint32_t val) { + int pos = -1; + while ((pos = set.find(tag, pos)) != -1) + if (set[pos].integer == val) + return true; + return false; +} + +template <keymaster_tag_t Tag> +bool contains(const AuthorizationSet& set, TypedTag<KM_ULONG, Tag> tag, uint64_t val) { + int pos = set.find(tag); + return pos != -1 && set[pos].long_integer == val; +} + +template <keymaster_tag_t Tag> +bool contains(const AuthorizationSet& set, TypedTag<KM_BYTES, Tag> tag, const std::string& val) { + int pos = set.find(tag); + return pos != -1 && + std::string(reinterpret_cast<const char*>(set[pos].blob.data), + set[pos].blob.data_length) == val; +} + +template <keymaster_tag_t Tag> +bool contains(const AuthorizationSet& set, TypedTag<KM_BIGNUM, Tag> tag, const std::string& val) { + int pos = set.find(tag); + return pos != -1 && + std::string(reinterpret_cast<const char*>(set[pos].blob.data), + set[pos].blob.data_length) == val; +} + +inline bool contains(const AuthorizationSet& set, keymaster_tag_t tag) { + return set.find(tag) != -1; +} + +class StdoutLogger : public Logger { + public: + StdoutLogger() { set_instance(this); } + + int log_msg(LogLevel level, const char* fmt, va_list args) const { + int output_len = 0; + switch (level) { + case DEBUG_LVL: + output_len = printf("DEBUG: "); + break; + case INFO_LVL: + output_len = printf("INFO: "); + break; + case WARNING_LVL: + output_len = printf("WARNING: "); + break; + case ERROR_LVL: + output_len = printf("ERROR: "); + break; + case SEVERE_LVL: + output_len = printf("SEVERE: "); + break; + } + + output_len += vprintf(fmt, args); + output_len += printf("\n"); + return output_len; + } +}; + +inline std::string make_string(const uint8_t* data, size_t length) { + return std::string(reinterpret_cast<const char*>(data), length); +} + +template <size_t N> std::string make_string(const uint8_t (&a)[N]) { + return make_string(a, N); +} + +/** + * Keymaster2TestInstance is used to parameterize Keymaster2Tests. Its main function is to create a + * keymaster2_device_t to which test calls can be directed. It also provides a place to specify + * various bits of alternative behavior, in cases where different devices are expected to behave + * differently (any such cases are a potential bug, but sometimes they may make sense). + */ +class Keymaster2TestInstanceCreator { + public: + virtual ~Keymaster2TestInstanceCreator(){}; + virtual keymaster2_device_t* CreateDevice() const = 0; + + virtual bool algorithm_in_km0_hardware(keymaster_algorithm_t algorithm) const = 0; + virtual int keymaster0_calls() const = 0; + virtual int minimal_digest_set() const { return false; } +}; + +// Use a shared_ptr because it's copyable. +typedef std::shared_ptr<Keymaster2TestInstanceCreator> InstanceCreatorPtr; + +const uint64_t OP_HANDLE_SENTINEL = 0xFFFFFFFFFFFFFFFF; +class Keymaster2Test : public testing::TestWithParam<InstanceCreatorPtr> { + protected: + Keymaster2Test(); + ~Keymaster2Test(); + + keymaster2_device_t* device(); + + keymaster_error_t GenerateKey(const AuthorizationSetBuilder& builder); + + keymaster_error_t DeleteKey(); + + keymaster_error_t ImportKey(const AuthorizationSetBuilder& builder, + keymaster_key_format_t format, const std::string& key_material); + + keymaster_error_t ExportKey(keymaster_key_format_t format, std::string* export_data); + + keymaster_error_t GetCharacteristics(); + + keymaster_error_t BeginOperation(keymaster_purpose_t purpose); + keymaster_error_t BeginOperation(keymaster_purpose_t purpose, const AuthorizationSet& input_set, + AuthorizationSet* output_set = NULL); + + keymaster_error_t UpdateOperation(const std::string& message, std::string* output, + size_t* input_consumed); + keymaster_error_t UpdateOperation(const AuthorizationSet& additional_params, + const std::string& message, AuthorizationSet* output_params, + std::string* output, size_t* input_consumed); + + keymaster_error_t FinishOperation(std::string* output); + keymaster_error_t FinishOperation(const std::string& signature, std::string* output); + keymaster_error_t FinishOperation(const AuthorizationSet& additional_params, + const std::string& signature, std::string* output) { + return FinishOperation(additional_params, signature, nullptr /* output_params */, output); + } + keymaster_error_t FinishOperation(const AuthorizationSet& additional_params, + const std::string& signature, AuthorizationSet* output_params, + std::string* output); + + keymaster_error_t AbortOperation(); + + keymaster_error_t AttestKey(keymaster_algorithm_t algorithm, keymaster_cert_chain_t* chain); + + keymaster_error_t GetVersion(uint8_t* major, uint8_t* minor, uint8_t* subminor); + + std::string ProcessMessage(keymaster_purpose_t purpose, const std::string& message); + std::string ProcessMessage(keymaster_purpose_t purpose, const std::string& message, + const AuthorizationSet& begin_params, + const AuthorizationSet& update_params, + AuthorizationSet* output_params = NULL); + std::string ProcessMessage(keymaster_purpose_t purpose, const std::string& message, + const std::string& signature, const AuthorizationSet& begin_params, + const AuthorizationSet& update_params, + AuthorizationSet* output_params = NULL); + std::string ProcessMessage(keymaster_purpose_t purpose, const std::string& message, + const std::string& signature); + + void SignMessage(const std::string& message, std::string* signature, keymaster_digest_t digest); + void SignMessage(const std::string& message, std::string* signature, keymaster_digest_t digest, + keymaster_padding_t padding); + void MacMessage(const std::string& message, std::string* signature, size_t mac_length); + + void VerifyMessage(const std::string& message, const std::string& signature, + keymaster_digest_t digest); + void VerifyMessage(const std::string& message, const std::string& signature, + keymaster_digest_t digest, keymaster_padding_t padding); + void VerifyMac(const std::string& message, const std::string& signature); + + std::string EncryptMessage(const std::string& message, keymaster_padding_t padding, + std::string* generated_nonce = NULL); + std::string EncryptMessage(const std::string& message, keymaster_digest_t digest, + keymaster_padding_t padding, std::string* generated_nonce = NULL); + std::string EncryptMessage(const std::string& message, keymaster_block_mode_t block_mode, + keymaster_padding_t padding, std::string* generated_nonce = NULL); + std::string EncryptMessage(const AuthorizationSet& update_params, const std::string& message, + keymaster_digest_t digest, keymaster_padding_t padding, + std::string* generated_nonce = NULL); + std::string EncryptMessage(const AuthorizationSet& update_params, const std::string& message, + keymaster_block_mode_t block_mode, keymaster_padding_t padding, + std::string* generated_nonce = NULL); + std::string EncryptMessageWithParams(const std::string& message, + const AuthorizationSet& begin_params, + const AuthorizationSet& update_params, + AuthorizationSet* output_params); + + std::string DecryptMessage(const std::string& ciphertext, keymaster_padding_t padding); + std::string DecryptMessage(const std::string& ciphertext, keymaster_digest_t digest, + keymaster_padding_t padding); + std::string DecryptMessage(const std::string& ciphertext, keymaster_block_mode_t block_mode, + keymaster_padding_t padding); + std::string DecryptMessage(const std::string& ciphertext, keymaster_digest_t digest, + keymaster_padding_t padding, const std::string& nonce); + std::string DecryptMessage(const std::string& ciphertext, keymaster_block_mode_t block_mode, + keymaster_padding_t padding, const std::string& nonce); + std::string DecryptMessage(const AuthorizationSet& update_params, const std::string& ciphertext, + keymaster_digest_t digest, keymaster_padding_t padding, + const std::string& nonce); + std::string DecryptMessage(const AuthorizationSet& update_params, const std::string& ciphertext, + keymaster_block_mode_t block_mode, keymaster_padding_t padding, + const std::string& nonce); + + void CheckHmacTestVector(const std::string& key, const std::string& message, keymaster_digest_t digest, + std::string expected_mac); + void CheckAesOcbTestVector(const std::string& key, const std::string& nonce, + const std::string& associated_data, const std::string& message, + const std::string& expected_ciphertext); + void CheckAesCtrTestVector(const std::string& key, const std::string& nonce, + const std::string& message, const std::string& expected_ciphertext); + AuthorizationSet UserAuthParams(); + AuthorizationSet ClientParams(); + + template <typename T> + bool ResponseContains(const std::vector<T>& expected, const T* values, size_t len) { + return expected.size() == len && + std::is_permutation(values, values + len, expected.begin()); + } + + template <typename T> bool ResponseContains(T expected, const T* values, size_t len) { + return (len == 1 && *values == expected); + } + + AuthorizationSet hw_enforced(); + AuthorizationSet sw_enforced(); + + void FreeCharacteristics(); + void FreeKeyBlob(); + + void corrupt_key_blob(); + + void set_key_blob(const uint8_t* key, size_t key_length) { + FreeKeyBlob(); + blob_.key_material = key; + blob_.key_material_size = key_length; + } + + AuthorizationSet client_params() { + return AuthorizationSet(client_params_, sizeof(client_params_) / sizeof(client_params_[0])); + } + + private: + keymaster2_device_t* device_; + keymaster_blob_t client_id_ = {.data = reinterpret_cast<const uint8_t*>("app_id"), + .data_length = 6}; + keymaster_key_param_t client_params_[1] = { + Authorization(TAG_APPLICATION_ID, client_id_.data, client_id_.data_length)}; + + uint64_t op_handle_; + + keymaster_key_blob_t blob_; + keymaster_key_characteristics_t characteristics_; +}; + +struct Keymaster0CountingWrapper : public keymaster0_device_t { + explicit Keymaster0CountingWrapper(keymaster0_device_t* device) : device_(device), counter_(0) { + common = device_->common; + common.close = counting_close_device; + client_version = device_->client_version; + flags = device_->flags; + context = this; + + generate_keypair = counting_generate_keypair; + import_keypair = counting_import_keypair; + get_keypair_public = counting_get_keypair_public; + delete_keypair = counting_delete_keypair; + delete_all = counting_delete_all; + sign_data = counting_sign_data; + verify_data = counting_verify_data; + } + + int count() { return counter_; } + + // The blobs generated by the underlying softkeymaster start with "PK#8". Tweak the prefix so + // they don't get identified as softkeymaster blobs. + static void munge_blob(uint8_t* blob, size_t blob_length) { + if (blob && blob_length > 0 && *blob == 'P') + *blob = 'Q'; // Mind your Ps and Qs! + } + + // Copy and un-modfy the blob. The caller must clean up the return value. + static uint8_t* unmunge_blob(const uint8_t* blob, size_t blob_length) { + uint8_t* dup_blob = dup_buffer(blob, blob_length); + if (dup_blob && blob_length > 0 && *dup_blob == 'Q') + *dup_blob = 'P'; + return dup_blob; + } + + static keymaster0_device_t* device(const keymaster0_device_t* dev) { + Keymaster0CountingWrapper* wrapper = + reinterpret_cast<Keymaster0CountingWrapper*>(dev->context); + return wrapper->device_; + } + + static void increment(const keymaster0_device_t* dev) { + Keymaster0CountingWrapper* wrapper = + reinterpret_cast<Keymaster0CountingWrapper*>(dev->context); + wrapper->counter_++; + } + + static int counting_close_device(hw_device_t* dev) { + keymaster0_device_t* k0_dev = reinterpret_cast<keymaster0_device_t*>(dev); + increment(k0_dev); + Keymaster0CountingWrapper* wrapper = + reinterpret_cast<Keymaster0CountingWrapper*>(k0_dev->context); + int retval = + wrapper->device_->common.close(reinterpret_cast<hw_device_t*>(wrapper->device_)); + delete wrapper; + return retval; + } + + static int counting_generate_keypair(const struct keymaster0_device* dev, + const keymaster_keypair_t key_type, const void* key_params, + uint8_t** key_blob, size_t* key_blob_length) { + increment(dev); + int result = device(dev)->generate_keypair(device(dev), key_type, key_params, key_blob, + key_blob_length); + if (result == 0) + munge_blob(*key_blob, *key_blob_length); + return result; + } + + static int counting_import_keypair(const struct keymaster0_device* dev, const uint8_t* key, + const size_t key_length, uint8_t** key_blob, + size_t* key_blob_length) { + increment(dev); + int result = + device(dev)->import_keypair(device(dev), key, key_length, key_blob, key_blob_length); + if (result == 0) + munge_blob(*key_blob, *key_blob_length); + return result; + } + + static int counting_get_keypair_public(const struct keymaster0_device* dev, + const uint8_t* key_blob, const size_t key_blob_length, + uint8_t** x509_data, size_t* x509_data_length) { + increment(dev); + std::unique_ptr<uint8_t[]> dup_blob(unmunge_blob(key_blob, key_blob_length)); + return device(dev)->get_keypair_public(device(dev), dup_blob.get(), key_blob_length, + x509_data, x509_data_length); + } + + static int counting_delete_keypair(const struct keymaster0_device* dev, const uint8_t* key_blob, + const size_t key_blob_length) { + increment(dev); + if (key_blob && key_blob_length > 0) + EXPECT_EQ('Q', *key_blob); + if (device(dev)->delete_keypair) { + std::unique_ptr<uint8_t[]> dup_blob(unmunge_blob(key_blob, key_blob_length)); + return device(dev)->delete_keypair(device(dev), dup_blob.get(), key_blob_length); + } + return 0; + } + + static int counting_delete_all(const struct keymaster0_device* dev) { + increment(dev); + if (device(dev)->delete_all) + return device(dev)->delete_all(device(dev)); + return 0; + } + + static int counting_sign_data(const struct keymaster0_device* dev, const void* signing_params, + const uint8_t* key_blob, const size_t key_blob_length, + const uint8_t* data, const size_t data_length, + uint8_t** signed_data, size_t* signed_data_length) { + increment(dev); + std::unique_ptr<uint8_t[]> dup_blob(unmunge_blob(key_blob, key_blob_length)); + return device(dev)->sign_data(device(dev), signing_params, dup_blob.get(), key_blob_length, + data, data_length, signed_data, signed_data_length); + } + + static int counting_verify_data(const struct keymaster0_device* dev, const void* signing_params, + const uint8_t* key_blob, const size_t key_blob_length, + const uint8_t* signed_data, const size_t signed_data_length, + const uint8_t* signature, const size_t signature_length) { + increment(dev); + std::unique_ptr<uint8_t[]> dup_blob(unmunge_blob(key_blob, key_blob_length)); + return device(dev)->verify_data(device(dev), signing_params, dup_blob.get(), + key_blob_length, signed_data, signed_data_length, signature, + signature_length); + } + + private: + keymaster0_device_t* device_; + int counter_; +}; + +/** + * This function takes a keymaster1_device_t and wraps it in an adapter that supports only + * KM_DIGEST_SHA_2_256. + */ +keymaster1_device_t* make_device_sha256_only(keymaster1_device_t* device); + +} // namespace test +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ANDROID_KEYMASTER_TEST_UTILS_H_
diff --git a/keymaster/android_keymaster_utils.cpp b/keymaster/android_keymaster_utils.cpp new file mode 100644 index 0000000..053e72a --- /dev/null +++ b/keymaster/android_keymaster_utils.cpp
@@ -0,0 +1,45 @@ +/* + * Copyright 2014 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 <keymaster/android_keymaster_utils.h> + +#include <new> + +namespace keymaster { + +// Keymaster never manages enormous buffers, so anything particularly large is bad data or the +// result of a bug. We arbitrarily set a 16 MiB limit. +const size_t kMaxDupBufferSize = 16 * 1024 * 1024; + +uint8_t* dup_buffer(const void* buf, size_t size) { + if (size >= kMaxDupBufferSize) + return nullptr; + uint8_t* retval = new (std::nothrow) uint8_t[size]; + if (retval) + memcpy(retval, buf, size); + return retval; +} + +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; +} + +} // namespace keymaster
diff --git a/keymaster/asymmetric_key.cpp b/keymaster/asymmetric_key.cpp new file mode 100644 index 0000000..02a7f15 --- /dev/null +++ b/keymaster/asymmetric_key.cpp
@@ -0,0 +1,245 @@ +/* + * Copyright 2014 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 "asymmetric_key.h" + +#include <new> + +#include <openssl/asn1.h> +#include <openssl/stack.h> +#include <openssl/x509.h> + +#include "attestation_record.h" +#include "openssl_err.h" +#include "openssl_utils.h" + +namespace keymaster { + +keymaster_error_t AsymmetricKey::formatted_key_material(keymaster_key_format_t format, + UniquePtr<uint8_t[]>* material, + size_t* size) const { + if (format != KM_KEY_FORMAT_X509) + return KM_ERROR_UNSUPPORTED_KEY_FORMAT; + + if (material == NULL || size == NULL) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + EVP_PKEY_Ptr pkey(EVP_PKEY_new()); + if (!InternalToEvp(pkey.get())) + return TranslateLastOpenSslError(); + + int key_data_length = i2d_PUBKEY(pkey.get(), NULL); + if (key_data_length <= 0) + return TranslateLastOpenSslError(); + + material->reset(new (std::nothrow) uint8_t[key_data_length]); + if (material->get() == NULL) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + uint8_t* tmp = material->get(); + if (i2d_PUBKEY(pkey.get(), &tmp) != key_data_length) { + material->reset(); + return TranslateLastOpenSslError(); + } + + *size = key_data_length; + return KM_ERROR_OK; +} + +static keymaster_error_t build_attestation_extension(const AuthorizationSet& tee_enforced, + const AuthorizationSet& sw_enforced, + X509_EXTENSION_Ptr* extension) { + ASN1_OBJECT_Ptr oid( + OBJ_txt2obj(kAttestionRecordOid, 1 /* accept numerical dotted string form only */)); + if (!oid.get()) + return TranslateLastOpenSslError(); + + UniquePtr<uint8_t[]> attest_bytes; + size_t attest_bytes_len; + keymaster_error_t error = + build_attestation_record(sw_enforced, tee_enforced, &attest_bytes, &attest_bytes_len); + if (error != KM_ERROR_OK) + return error; + + ASN1_OCTET_STRING_Ptr attest_str(ASN1_OCTET_STRING_new()); + if (!attest_str.get() || + !ASN1_OCTET_STRING_set(attest_str.get(), attest_bytes.get(), attest_bytes_len)) + return TranslateLastOpenSslError(); + + extension->reset( + X509_EXTENSION_create_by_OBJ(nullptr, oid.get(), 0 /* not critical */, attest_str.get())); + if (!extension->get()) + return TranslateLastOpenSslError(); + + return KM_ERROR_OK; +} + +static bool add_public_key(EVP_PKEY* key, X509* certificate, keymaster_error_t* error) { + if (!X509_set_pubkey(certificate, key)) { + *error = TranslateLastOpenSslError(); + return false; + } + return true; +} + +static bool add_attestation_extension(const AuthorizationSet& tee_enforced, + const AuthorizationSet& sw_enforced, X509* certificate, + keymaster_error_t* error) { + X509_EXTENSION_Ptr attest_extension; + *error = build_attestation_extension(tee_enforced, sw_enforced, &attest_extension); + if (*error != KM_ERROR_OK) + return false; + + if (!X509_add_ext(certificate, attest_extension.get() /* Don't release; copied */, + -1 /* insert at end */)) { + *error = TranslateLastOpenSslError(); + return false; + } + + return true; +} + +static keymaster_error_t get_certificate_blob(X509* certificate, keymaster_blob_t* blob) { + int len = i2d_X509(certificate, nullptr); + if (len < 0) + return TranslateLastOpenSslError(); + + uint8_t* data = new uint8_t[len]; + if (!data) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + uint8_t* p = data; + i2d_X509(certificate, &p); + + blob->data_length = len; + blob->data = data; + + return KM_ERROR_OK; +} + +static bool allocate_cert_chain(size_t entry_count, keymaster_cert_chain_t* chain, + keymaster_error_t* error) { + if (chain->entries) { + for (size_t i = 0; i < chain->entry_count; ++i) + delete[] chain->entries[i].data; + delete[] chain->entries; + } + + chain->entry_count = entry_count; + chain->entries = new keymaster_blob_t[entry_count]; + if (!chain->entries) { + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return false; + } + return true; +} + +// Copies the intermediate and root certificates into chain, leaving the first slot for the leaf +// certificate. +static bool copy_attestation_chain(const KeymasterContext& context, + keymaster_algorithm_t sign_algorithm, + keymaster_cert_chain_t* chain, keymaster_error_t* error) { + + UniquePtr<keymaster_cert_chain_t, CertificateChainDelete> attest_key_chain( + context.AttestationChain(sign_algorithm, error)); + if (!attest_key_chain.get()) + return false; + + if (!allocate_cert_chain(attest_key_chain->entry_count + 1, chain, error)) + return false; + + chain->entries[0].data = nullptr; // Leave empty for the leaf certificate. + chain->entries[0].data_length = 0; + + for (size_t i = 0; i < attest_key_chain->entry_count; ++i) { + chain->entries[i + 1] = attest_key_chain->entries[i]; + attest_key_chain->entries[i].data = nullptr; + } + + return true; +} + +keymaster_error_t AsymmetricKey::GenerateAttestation(const KeymasterContext& context, + const AuthorizationSet& attest_params, + const AuthorizationSet& tee_enforced, + const AuthorizationSet& sw_enforced, + keymaster_cert_chain_t* cert_chain) const { + + keymaster_algorithm_t sign_algorithm; + if (!attest_params.GetTagValue(TAG_ALGORITHM, &sign_algorithm) || + (sign_algorithm != KM_ALGORITHM_RSA && sign_algorithm != KM_ALGORITHM_EC)) + return KM_ERROR_INCOMPATIBLE_ALGORITHM; + + EVP_PKEY_Ptr pkey(EVP_PKEY_new()); + if (!InternalToEvp(pkey.get())) + return TranslateLastOpenSslError(); + + X509_Ptr certificate(X509_new()); + if (!certificate.get()) + return TranslateLastOpenSslError(); + + if (!X509_set_version(certificate.get(), 2 /* version 3, but zero-based */)) + return TranslateLastOpenSslError(); + + ASN1_INTEGER_Ptr serialNumber(ASN1_INTEGER_new()); + if (!serialNumber.get() || + !ASN1_INTEGER_set( + serialNumber.get(), + 10000 /* TODO(swillden): Figure out what should go in serial number; probably a random + * value */) || + !X509_set_serialNumber(certificate.get(), serialNumber.get() /* Don't release; copied */)) + return TranslateLastOpenSslError(); + + // TODO(swillden): Find useful values (if possible) for issuerName and subjectName. + X509_NAME_Ptr issuerName(X509_NAME_new()); + if (!issuerName.get() || + !X509_set_subject_name(certificate.get(), issuerName.get() /* Don't release; copied */)) + return TranslateLastOpenSslError(); + + X509_NAME_Ptr subjectName(X509_NAME_new()); + if (!subjectName.get() || + !X509_set_subject_name(certificate.get(), subjectName.get() /* Don't release; copied */)) + return TranslateLastOpenSslError(); + + // TODO(swillden): Use key activity and expiration dates for notBefore and notAfter. + ASN1_TIME_Ptr notBefore(ASN1_TIME_new()); + if (!notBefore.get() || !ASN1_TIME_set(notBefore.get(), 0) || + !X509_set_notBefore(certificate.get(), notBefore.get() /* Don't release; copied */)) + return TranslateLastOpenSslError(); + + ASN1_TIME_Ptr notAfter(ASN1_TIME_new()); + if (!notAfter.get() || !ASN1_TIME_set(notAfter.get(), 10000) || + !X509_set_notAfter(certificate.get(), notAfter.get() /* Don't release; copied */)) + return TranslateLastOpenSslError(); + + keymaster_error_t error = KM_ERROR_OK; + EVP_PKEY_Ptr sign_key(context.AttestationKey(sign_algorithm, &error)); + + if (!sign_key.get() || // + !add_public_key(pkey.get(), certificate.get(), &error) || + !add_attestation_extension(tee_enforced, sw_enforced, certificate.get(), &error)) + return error; + + if (!X509_sign(certificate.get(), sign_key.get(), EVP_sha256())) + return TranslateLastOpenSslError(); + + if (!copy_attestation_chain(context, sign_algorithm, cert_chain, &error)) + return error; + + return get_certificate_blob(certificate.get(), &cert_chain->entries[0]); +} + +} // namespace keymaster
diff --git a/keymaster/asymmetric_key.h b/keymaster/asymmetric_key.h new file mode 100644 index 0000000..99ee585 --- /dev/null +++ b/keymaster/asymmetric_key.h
@@ -0,0 +1,48 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_ASYMMETRIC_KEY_H +#define SYSTEM_KEYMASTER_ASYMMETRIC_KEY_H + +#include <openssl/evp.h> + +#include "key.h" + +namespace keymaster { + +class AsymmetricKey : public Key { + public: + AsymmetricKey(const AuthorizationSet& hw_enforced, const AuthorizationSet& sw_enforced, + keymaster_error_t* error) + : Key(hw_enforced, sw_enforced, error) {} + + keymaster_error_t formatted_key_material(keymaster_key_format_t format, + UniquePtr<uint8_t[]>* material, + size_t* size) const override; + + keymaster_error_t GenerateAttestation(const KeymasterContext& context, + const AuthorizationSet& attest_params, + const AuthorizationSet& tee_enforced, + const AuthorizationSet& sw_enforced, + keymaster_cert_chain_t* certificate_chain) const override; + + virtual bool InternalToEvp(EVP_PKEY* pkey) const = 0; + virtual bool EvpToInternal(const EVP_PKEY* pkey) = 0; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ASYMMETRIC_KEY_H
diff --git a/keymaster/asymmetric_key_factory.cpp b/keymaster/asymmetric_key_factory.cpp new file mode 100644 index 0000000..b06d0d8 --- /dev/null +++ b/keymaster/asymmetric_key_factory.cpp
@@ -0,0 +1,66 @@ +/* + * Copyright 2014 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 <keymaster/asymmetric_key_factory.h> + +#include <keymaster/android_keymaster_utils.h> + +#include "asymmetric_key.h" +#include "openssl_err.h" +#include "openssl_utils.h" + +namespace keymaster { + +static const keymaster_key_format_t supported_import_formats[] = {KM_KEY_FORMAT_PKCS8}; +const keymaster_key_format_t* +AsymmetricKeyFactory::SupportedImportFormats(size_t* format_count) const { + *format_count = array_length(supported_import_formats); + return supported_import_formats; +} + +static const keymaster_key_format_t supported_export_formats[] = {KM_KEY_FORMAT_X509}; +const keymaster_key_format_t* +AsymmetricKeyFactory::SupportedExportFormats(size_t* format_count) const { + *format_count = array_length(supported_export_formats); + return supported_export_formats; +} + +keymaster_error_t AsymmetricKeyFactory::LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& /* additional_params */, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const { + UniquePtr<AsymmetricKey> asymmetric_key; + keymaster_error_t error = CreateEmptyKey(hw_enforced, sw_enforced, &asymmetric_key); + if (error != KM_ERROR_OK) + return error; + + const uint8_t* tmp = key_material.key_material; + EVP_PKEY* pkey = + d2i_PrivateKey(evp_key_type(), NULL /* pkey */, &tmp, key_material.key_material_size); + if (!pkey) + return TranslateLastOpenSslError(); + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey_deleter(pkey); + + if (!asymmetric_key->EvpToInternal(pkey)) + error = TranslateLastOpenSslError(); + else + key->reset(asymmetric_key.release()); + + return error; +} + +} // namespace keymaster
diff --git a/keymaster/attestation_record.cpp b/keymaster/attestation_record.cpp new file mode 100644 index 0000000..809f7c8 --- /dev/null +++ b/keymaster/attestation_record.cpp
@@ -0,0 +1,654 @@ +/* + * Copyright 2016 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 "attestation_record.h" + +#include <assert.h> + +#include <openssl/asn1t.h> + +#include "openssl_err.h" +#include "openssl_utils.h" + +namespace keymaster { + +struct stack_st_ASN1_TYPE_Delete { + void operator()(stack_st_ASN1_TYPE* p) { sk_ASN1_TYPE_free(p); } +}; + +struct ASN1_STRING_Delete { + void operator()(ASN1_STRING* p) { ASN1_STRING_free(p); } +}; + +struct ASN1_TYPE_Delete { + void operator()(ASN1_TYPE* p) { ASN1_TYPE_free(p); } +}; + +#define ASN1_INTEGER_SET STACK_OF(ASN1_INTEGER) + +typedef struct km_root_of_trust { + ASN1_OCTET_STRING* verified_boot_key; + ASN1_INTEGER* os_version; + ASN1_INTEGER* os_patchlevel; +} KM_ROOT_OF_TRUST; + +ASN1_SEQUENCE(KM_ROOT_OF_TRUST) = { + ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_key, ASN1_OCTET_STRING), + ASN1_SIMPLE(KM_ROOT_OF_TRUST, os_version, ASN1_INTEGER), + ASN1_SIMPLE(KM_ROOT_OF_TRUST, os_patchlevel, ASN1_INTEGER), +} ASN1_SEQUENCE_END(KM_ROOT_OF_TRUST); +IMPLEMENT_ASN1_FUNCTIONS(KM_ROOT_OF_TRUST); + +typedef struct km_auth_list { + ASN1_INTEGER_SET* purpose; + ASN1_INTEGER* algorithm; + ASN1_INTEGER* key_size; + ASN1_INTEGER_SET* block_mode; + ASN1_INTEGER_SET* digest; + ASN1_INTEGER_SET* padding; + ASN1_NULL* caller_nonce; + ASN1_INTEGER* min_mac_length; + ASN1_INTEGER_SET* kdf; + ASN1_INTEGER* ec_curve; + ASN1_INTEGER* rsa_public_exponent; + ASN1_NULL* ecies_single_hash_mode; + ASN1_NULL* include_unique_id; + ASN1_INTEGER* blob_usage_requirement; + ASN1_NULL* bootloader_only; + ASN1_INTEGER* active_date_time; + ASN1_INTEGER* origination_expire_date_time; + ASN1_INTEGER* usage_expire_date_time; + ASN1_INTEGER* min_seconds_between_ops; + ASN1_INTEGER* max_uses_per_boot; + ASN1_NULL* no_auth_required; + ASN1_INTEGER* user_auth_type; + ASN1_INTEGER* auth_timeout; + ASN1_NULL* allow_while_on_body; + ASN1_NULL* all_applications; + ASN1_OCTET_STRING* application_id; + ASN1_OCTET_STRING* application_data; + ASN1_INTEGER* creation_date_time; + ASN1_INTEGER* origin; + ASN1_NULL* rollback_resistant; + KM_ROOT_OF_TRUST* root_of_trust; + ASN1_INTEGER* os_version; + ASN1_INTEGER* os_patchlevel; + ASN1_OCTET_STRING* unique_id; +} KM_AUTH_LIST; + +ASN1_SEQUENCE(KM_AUTH_LIST) = { + ASN1_IMP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.masked_tag()), + ASN1_IMP_SET_OF_OPT(KM_AUTH_LIST, block_mode, ASN1_INTEGER, TAG_BLOCK_MODE.masked_tag()), + ASN1_IMP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.masked_tag()), + ASN1_IMP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, caller_nonce, ASN1_NULL, TAG_CALLER_NONCE.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, min_mac_length, ASN1_INTEGER, TAG_MIN_MAC_LENGTH.masked_tag()), + ASN1_IMP_SET_OF_OPT(KM_AUTH_LIST, kdf, ASN1_INTEGER, TAG_KDF.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER, + TAG_RSA_PUBLIC_EXPONENT.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, ecies_single_hash_mode, ASN1_NULL, + TAG_ECIES_SINGLE_HASH_MODE.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, include_unique_id, ASN1_NULL, TAG_INCLUDE_UNIQUE_ID.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, blob_usage_requirement, ASN1_INTEGER, + TAG_BLOB_USAGE_REQUIREMENTS.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, bootloader_only, ASN1_NULL, TAG_BOOTLOADER_ONLY.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER, + TAG_ORIGINATION_EXPIRE_DATETIME.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER, + TAG_USAGE_EXPIRE_DATETIME.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, min_seconds_between_ops, ASN1_INTEGER, + TAG_MIN_SECONDS_BETWEEN_OPS.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, max_uses_per_boot, ASN1_INTEGER, TAG_MAX_USES_PER_BOOT.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL, + TAG_ALLOW_WHILE_ON_BODY.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, all_applications, ASN1_NULL, TAG_ALL_APPLICATIONS.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, application_id, ASN1_OCTET_STRING, TAG_APPLICATION_ID.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, application_data, ASN1_OCTET_STRING, + TAG_APPLICATION_DATA.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER, + TAG_CREATION_DATETIME.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, rollback_resistant, ASN1_NULL, TAG_ROLLBACK_RESISTANT.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.masked_tag()), + ASN1_IMP_OPT(KM_AUTH_LIST, unique_id, ASN1_NULL, TAG_UNIQUE_ID.masked_tag()), +} ASN1_SEQUENCE_END(KM_AUTH_LIST); +IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST); + +typedef struct km_key_description { + KM_AUTH_LIST* software_enforced; + KM_AUTH_LIST* tee_enforced; +} KM_KEY_DESCRIPTION; + +ASN1_SEQUENCE(KM_KEY_DESCRIPTION) = { + ASN1_SIMPLE(KM_KEY_DESCRIPTION, software_enforced, KM_AUTH_LIST), + ASN1_SIMPLE(KM_KEY_DESCRIPTION, tee_enforced, KM_AUTH_LIST), +} ASN1_SEQUENCE_END(KM_KEY_DESCRIPTION); +IMPLEMENT_ASN1_FUNCTIONS(KM_KEY_DESCRIPTION); + +struct KM_AUTH_LIST_Delete { + void operator()(KM_AUTH_LIST* p) { KM_AUTH_LIST_free(p); } +}; + +struct KM_KEY_DESCRIPTION_Delete { + void operator()(KM_KEY_DESCRIPTION* p) { KM_KEY_DESCRIPTION_free(p); } +}; + +static uint32_t get_uint32_value(const keymaster_key_param_t& param) { + switch (keymaster_tag_get_type(param.tag)) { + case KM_ENUM: + case KM_ENUM_REP: + return param.enumerated; + case KM_UINT: + case KM_UINT_REP: + return param.integer; + default: + assert(false); + return 0xFFFFFFFF; + } +} + +// Insert value in either the dest_integer or the dest_integer_set, whichever is provided. +static keymaster_error_t insert_integer(ASN1_INTEGER* value, ASN1_INTEGER** dest_integer, + ASN1_INTEGER_SET** dest_integer_set) { + assert((dest_integer == nullptr) ^ (dest_integer_set == nullptr)); + assert(value); + + if (dest_integer_set) { + if (!*dest_integer_set) + *dest_integer_set = sk_ASN1_INTEGER_new_null(); + if (!*dest_integer_set) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + if (!sk_ASN1_INTEGER_push(*dest_integer_set, value)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + return KM_ERROR_OK; + + } else if (dest_integer) { + if (*dest_integer) + ASN1_INTEGER_free(*dest_integer); + *dest_integer = value; + return KM_ERROR_OK; + } + + assert(false); // Should never get here. + return KM_ERROR_OK; +} + +// Put the contents of the keymaster AuthorizationSet auth_list in to the ASN.1 record structure, +// record. +static keymaster_error_t build_auth_list(const AuthorizationSet& auth_list, KM_AUTH_LIST* record) { + + assert(record); + + for (auto entry : auth_list) { + + ASN1_INTEGER_SET** integer_set = nullptr; + ASN1_INTEGER** integer_ptr = nullptr; + ASN1_OCTET_STRING** string_ptr = nullptr; + ASN1_NULL** bool_ptr = nullptr; + + switch (entry.tag) { + + /* Ignored tags */ + case KM_TAG_INVALID: + case KM_TAG_ASSOCIATED_DATA: + case KM_TAG_NONCE: + case KM_TAG_AUTH_TOKEN: + case KM_TAG_MAC_LENGTH: + case KM_TAG_ALL_USERS: + case KM_TAG_USER_ID: + case KM_TAG_USER_SECURE_ID: + case KM_TAG_EXPORTABLE: + case KM_TAG_RESET_SINCE_ID_ROTATION: + continue; + + /* Non-repeating enumerations */ + case KM_TAG_ALGORITHM: + integer_ptr = &record->algorithm; + break; + case KM_TAG_EC_CURVE: + integer_ptr = &record->ec_curve; + break; + case KM_TAG_BLOB_USAGE_REQUIREMENTS: + integer_ptr = &record->blob_usage_requirement; + break; + case KM_TAG_USER_AUTH_TYPE: + integer_ptr = &record->user_auth_type; + break; + case KM_TAG_ORIGIN: + integer_ptr = &record->origin; + break; + + /* Repeating enumerations */ + case KM_TAG_PURPOSE: + integer_set = &record->purpose; + break; + case KM_TAG_BLOCK_MODE: + integer_set = &record->block_mode; + break; + case KM_TAG_PADDING: + integer_set = &record->padding; + break; + case KM_TAG_DIGEST: + integer_set = &record->digest; + break; + case KM_TAG_KDF: + integer_set = &record->kdf; + break; + + /* Non-repeating unsigned integers */ + case KM_TAG_KEY_SIZE: + integer_ptr = &record->key_size; + break; + case KM_TAG_MIN_MAC_LENGTH: + integer_ptr = &record->min_mac_length; + break; + case KM_TAG_MIN_SECONDS_BETWEEN_OPS: + integer_ptr = &record->min_seconds_between_ops; + break; + case KM_TAG_MAX_USES_PER_BOOT: + integer_ptr = &record->max_uses_per_boot; + break; + case KM_TAG_AUTH_TIMEOUT: + integer_ptr = &record->auth_timeout; + break; + + /* Non-repeating long unsigned integers */ + case KM_TAG_RSA_PUBLIC_EXPONENT: + integer_ptr = &record->rsa_public_exponent; + break; + + /* Dates */ + case KM_TAG_ACTIVE_DATETIME: + integer_ptr = &record->active_date_time; + break; + case KM_TAG_ORIGINATION_EXPIRE_DATETIME: + integer_ptr = &record->origination_expire_date_time; + break; + case KM_TAG_USAGE_EXPIRE_DATETIME: + integer_ptr = &record->usage_expire_date_time; + break; + case KM_TAG_CREATION_DATETIME: + integer_ptr = &record->creation_date_time; + break; + + /* Booleans */ + case KM_TAG_CALLER_NONCE: + bool_ptr = &record->caller_nonce; + break; + case KM_TAG_ECIES_SINGLE_HASH_MODE: + bool_ptr = &record->ecies_single_hash_mode; + break; + case KM_TAG_BOOTLOADER_ONLY: + bool_ptr = &record->bootloader_only; + break; + case KM_TAG_NO_AUTH_REQUIRED: + bool_ptr = &record->no_auth_required; + break; + case KM_TAG_ALL_APPLICATIONS: + bool_ptr = &record->all_applications; + break; + case KM_TAG_ROLLBACK_RESISTANT: + bool_ptr = &record->rollback_resistant; + break; + case KM_TAG_INCLUDE_UNIQUE_ID: + bool_ptr = &record->include_unique_id; + break; + case KM_TAG_ALLOW_WHILE_ON_BODY: + bool_ptr = &record->allow_while_on_body; + break; + + /* Byte arrays*/ + case KM_TAG_APPLICATION_ID: + string_ptr = &record->application_id; + break; + case KM_TAG_APPLICATION_DATA: + string_ptr = &record->application_data; + break; + case KM_TAG_UNIQUE_ID: + string_ptr = &record->unique_id; + break; + + /* Root of Trust components */ + case KM_TAG_OS_VERSION: + case KM_TAG_OS_PATCHLEVEL: + case KM_TAG_ROOT_OF_TRUST: + if (!record->root_of_trust) + record->root_of_trust = KM_ROOT_OF_TRUST_new(); + if (!record->root_of_trust) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + switch (entry.tag) { + case KM_TAG_OS_VERSION: + integer_ptr = &record->root_of_trust->os_version; + break; + case KM_TAG_OS_PATCHLEVEL: + integer_ptr = &record->root_of_trust->os_patchlevel; + break; + case KM_TAG_ROOT_OF_TRUST: + string_ptr = &record->root_of_trust->verified_boot_key; + break; + default: + assert(false); // Can't get here. + } + break; + } + + keymaster_tag_type_t type = keymaster_tag_get_type(entry.tag); + switch (type) { + case KM_ENUM: + case KM_ENUM_REP: + case KM_UINT: + case KM_UINT_REP: { + assert((keymaster_tag_repeatable(entry.tag) && integer_set) || + (!keymaster_tag_repeatable(entry.tag) && integer_ptr)); + + UniquePtr<ASN1_INTEGER, ASN1_INTEGER_Delete> value(ASN1_INTEGER_new()); + if (!value.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + if (!ASN1_INTEGER_set(value.get(), get_uint32_value(entry))) + return TranslateLastOpenSslError(); + + insert_integer(value.release(), integer_ptr, integer_set); + break; + } + + case KM_ULONG: + case KM_ULONG_REP: + case KM_DATE: { + assert((keymaster_tag_repeatable(entry.tag) && integer_set) || + (!keymaster_tag_repeatable(entry.tag) && integer_ptr)); + + UniquePtr<BIGNUM, BIGNUM_Delete> exponent(BN_new()); + if (!exponent.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (type == KM_DATE) { + if (!BN_set_word(exponent.get(), entry.date_time)) + return TranslateLastOpenSslError(); + } else { + if (!BN_set_word(exponent.get(), entry.long_integer)) + return TranslateLastOpenSslError(); + } + + UniquePtr<ASN1_INTEGER, ASN1_INTEGER_Delete> value( + BN_to_ASN1_INTEGER(exponent.get(), nullptr)); + if (!value.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + insert_integer(value.release(), integer_ptr, integer_set); + break; + } + + case KM_BOOL: + assert(bool_ptr); + if (!*bool_ptr) + *bool_ptr = ASN1_NULL_new(); + if (!*bool_ptr) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + break; + + /* Byte arrays*/ + case KM_BYTES: + assert(string_ptr); + if (!*string_ptr) + *string_ptr = ASN1_OCTET_STRING_new(); + if (!*string_ptr) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + if (!ASN1_OCTET_STRING_set(*string_ptr, entry.blob.data, entry.blob.data_length)) + return TranslateLastOpenSslError(); + break; + + default: + return KM_ERROR_UNIMPLEMENTED; + } + } + + return KM_ERROR_OK; +} + +// Construct an ASN1.1 DER-encoded attestation record containing the values from sw_enforced and +// tee_enforced. +keymaster_error_t build_attestation_record(const AuthorizationSet& sw_enforced, + const AuthorizationSet& tee_enforced, + UniquePtr<uint8_t[]>* asn1_key_desc, + size_t* asn1_key_desc_len) { + assert(asn1_key_desc && asn1_key_desc_len); + + UniquePtr<KM_KEY_DESCRIPTION, KM_KEY_DESCRIPTION_Delete> key_desc(KM_KEY_DESCRIPTION_new()); + if (!key_desc.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + keymaster_error_t error; + + error = build_auth_list(sw_enforced, key_desc->software_enforced); + if (error != KM_ERROR_OK) + return error; + error = build_auth_list(tee_enforced, key_desc->tee_enforced); + if (error != KM_ERROR_OK) + return error; + + int len = i2d_KM_KEY_DESCRIPTION(key_desc.get(), nullptr); + if (len < 0) + return TranslateLastOpenSslError(); + *asn1_key_desc_len = len; + asn1_key_desc->reset(new uint8_t[*asn1_key_desc_len]); + if (!asn1_key_desc->get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + uint8_t* p = asn1_key_desc->get(); + len = i2d_KM_KEY_DESCRIPTION(key_desc.get(), &p); + if (len < 0) + return TranslateLastOpenSslError(); + + return KM_ERROR_OK; +} + +// Copy all enumerated values with the specified tag from stack to auth_list. +static bool get_repeated_enums(const stack_st_ASN1_INTEGER* stack, keymaster_tag_t tag, + AuthorizationSet* auth_list) { + assert(keymaster_tag_get_type(tag) == KM_ENUM_REP); + for (size_t i = 0; i < sk_ASN1_INTEGER_num(stack); ++i) { + if (!auth_list->push_back( + keymaster_param_enum(tag, ASN1_INTEGER_get(sk_ASN1_INTEGER_value(stack, i))))) + return false; + } + return true; +} + +// Add the specified integer tag/value pair to auth_list. +template <keymaster_tag_type_t Type, keymaster_tag_t Tag, typename KeymasterEnum> +static bool get_enum(const ASN1_INTEGER* asn1_int, TypedEnumTag<Type, Tag, KeymasterEnum> tag, + AuthorizationSet* auth_list) { + if (!asn1_int) + return true; + return auth_list->push_back(tag, static_cast<KeymasterEnum>(ASN1_INTEGER_get(asn1_int))); +} + +// Add the specified ulong tag/value pair to auth_list. +static bool get_ulong(const ASN1_INTEGER* asn1_int, keymaster_tag_t tag, + AuthorizationSet* auth_list) { + if (!asn1_int) + return true; + UniquePtr<BIGNUM, BIGNUM_Delete> bn(ASN1_INTEGER_to_BN(asn1_int, nullptr)); + if (!bn.get()) + return false; + uint64_t ulong = BN_get_word(bn.get()); + return auth_list->push_back(keymaster_param_long(tag, ulong)); +} + +// Extract the values from the specified ASN.1 record and place them in auth_list. +static keymaster_error_t extract_auth_list(const KM_AUTH_LIST* record, + AuthorizationSet* auth_list) { + // Purpose + if (!get_repeated_enums(record->purpose, TAG_PURPOSE, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Algorithm + if (!get_enum(record->algorithm, TAG_ALGORITHM, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Key size + if (record->key_size && !auth_list->push_back(TAG_KEY_SIZE, ASN1_INTEGER_get(record->key_size))) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Block mode + if (!get_repeated_enums(record->block_mode, TAG_BLOCK_MODE, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Digest + if (!get_repeated_enums(record->digest, TAG_DIGEST, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Padding + if (!get_repeated_enums(record->padding, TAG_PADDING, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Caller NONCE + if (record->caller_nonce && !auth_list->push_back(TAG_CALLER_NONCE)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Min MAC length + if (record->min_mac_length && + !auth_list->push_back(TAG_MIN_MAC_LENGTH, ASN1_INTEGER_get(record->min_mac_length))) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // EC curve + if (!get_enum(record->ec_curve, TAG_EC_CURVE, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // RSA public exponent + if (!get_ulong(record->rsa_public_exponent, TAG_RSA_PUBLIC_EXPONENT, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // ECIES single hash mode + if (record->ecies_single_hash_mode && !auth_list->push_back(TAG_ECIES_SINGLE_HASH_MODE)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Blob usage requirement + if (!get_enum(record->blob_usage_requirement, TAG_BLOB_USAGE_REQUIREMENTS, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Bootloader only + if (record->bootloader_only && !auth_list->push_back(TAG_BOOTLOADER_ONLY)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Active date time + if (!get_ulong(record->active_date_time, TAG_ACTIVE_DATETIME, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Origination expire date time + if (!get_ulong(record->origination_expire_date_time, TAG_ORIGINATION_EXPIRE_DATETIME, + auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Usage Expire date time + if (!get_ulong(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Min seconds between ops + if (record->min_seconds_between_ops && + !auth_list->push_back(TAG_MIN_SECONDS_BETWEEN_OPS, + ASN1_INTEGER_get(record->min_seconds_between_ops))) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Max uses per boot + if (record->max_uses_per_boot && + !auth_list->push_back(TAG_MAX_USES_PER_BOOT, ASN1_INTEGER_get(record->max_uses_per_boot))) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // No auth required + if (record->no_auth_required && !auth_list->push_back(TAG_NO_AUTH_REQUIRED)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // User auth type + if (!get_enum(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Auth timeout + if (record->auth_timeout && + !auth_list->push_back(TAG_AUTH_TIMEOUT, ASN1_INTEGER_get(record->auth_timeout))) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // All applications + if (record->all_applications && !auth_list->push_back(TAG_ALL_APPLICATIONS)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Application ID + if (record->application_id && + !auth_list->push_back(TAG_APPLICATION_ID, record->application_id->data, + record->application_id->length)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Application data + if (record->application_data && + !auth_list->push_back(TAG_APPLICATION_DATA, record->application_data->data, + record->application_data->length)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Creation date time + if (!get_ulong(record->creation_date_time, TAG_CREATION_DATETIME, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Origin + if (!get_enum(record->origin, TAG_ORIGIN, auth_list)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Rollback resistant + if (record->rollback_resistant && !auth_list->push_back(TAG_ROLLBACK_RESISTANT)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Root of trust + if (record->root_of_trust) { + KM_ROOT_OF_TRUST* rot = record->root_of_trust; + if (!rot->verified_boot_key || !rot->os_version || !rot->os_patchlevel) + return KM_ERROR_INVALID_KEY_BLOB; + + if (!auth_list->push_back(TAG_OS_VERSION, ASN1_INTEGER_get(rot->os_version)) || + !auth_list->push_back(TAG_OS_PATCHLEVEL, ASN1_INTEGER_get(rot->os_patchlevel)) || + !auth_list->push_back(TAG_ROOT_OF_TRUST, rot->verified_boot_key->data, + rot->verified_boot_key->length)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + + return KM_ERROR_OK; +} + +// Parse the DER-encoded attestation record, placing the results in software_enforced and +// tee_enforced. +keymaster_error_t parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len, + AuthorizationSet* software_enforced, + AuthorizationSet* tee_enforced) { + const uint8_t* p = asn1_key_desc; + UniquePtr<KM_KEY_DESCRIPTION, KM_KEY_DESCRIPTION_Delete> record( + d2i_KM_KEY_DESCRIPTION(nullptr, &p, asn1_key_desc_len)); + if (!record.get()) + return TranslateLastOpenSslError(); + + keymaster_error_t error = extract_auth_list(record->software_enforced, software_enforced); + if (error != KM_ERROR_OK) + return error; + + return extract_auth_list(record->tee_enforced, tee_enforced); +} + +} // namepace keymaster
diff --git a/keymaster/attestation_record.h b/keymaster/attestation_record.h new file mode 100644 index 0000000..3cd5dde --- /dev/null +++ b/keymaster/attestation_record.h
@@ -0,0 +1,52 @@ +/* + * Copyright 2016 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 SYSTEM_KEYMASTER_ATTESTATION_RECORD_H_ +#define SYSTEM_KEYMASTER_ATTESTATION_RECORD_H_ + +#include <hardware/keymaster_defs.h> + +#include <keymaster/authorization_set.h> + +namespace keymaster { + +/** + * The OID for Android attestation records. For the curious, it breaks down as follows: + * + * 1 = ISO + * 3 = org + * 6 = DoD (Huh? OIDs are weird.) + * 1 = IANA + * 4 = Private + * 1 = Enterprises + * 11129 = Google + * 2 = Google security + * 1 = certificate extension + * 17 = Android attestation extension. + */ +static const char kAttestionRecordOid[] = "1.3.6.1.4.1.11129.2.1.17"; + +keymaster_error_t build_attestation_record(const AuthorizationSet& software_enforced, + const AuthorizationSet& tee_enforced, + UniquePtr<uint8_t[]>* asn1_key_desc, + size_t* asn1_key_desc_len); + +keymaster_error_t parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len, + AuthorizationSet* software_enforced, + AuthorizationSet* tee_enforced); +} + +#endif // SYSTEM_KEYMASTER_ATTESTATION_RECORD_H_
diff --git a/keymaster/attestation_record_test.cpp b/keymaster/attestation_record_test.cpp new file mode 100644 index 0000000..21a7eee --- /dev/null +++ b/keymaster/attestation_record_test.cpp
@@ -0,0 +1,63 @@ +/* + * Copyright 2016 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 <fstream> + +#include <gtest/gtest.h> + +#include "android_keymaster_test_utils.h" +#include "attestation_record.h" + +namespace keymaster { +namespace test { + +TEST(AttestTest, Simple) { + AuthorizationSet hw_set(AuthorizationSetBuilder() + .RsaSigningKey(512, 3) + .Digest(KM_DIGEST_SHA_2_256) + .Digest(KM_DIGEST_SHA_2_384) + .Authorization(TAG_ROOT_OF_TRUST, "foo", 3) + .Authorization(TAG_OS_VERSION, 60000) + .Authorization(TAG_OS_PATCHLEVEL, 201512) + .Authorization(TAG_APPLICATION_ID, "bar", 3)); + AuthorizationSet sw_set(AuthorizationSetBuilder().Authorization(TAG_ACTIVE_DATETIME, 10)); + + UniquePtr<uint8_t[]> asn1; + size_t asn1_len; + EXPECT_EQ(KM_ERROR_OK, build_attestation_record(sw_set, hw_set, &asn1, &asn1_len)); + EXPECT_GT(asn1_len, 0U); + + std::ofstream output("attest.der", + std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); + if (output) + output.write(reinterpret_cast<const char*>(asn1.get()), asn1_len); + output.close(); + + AuthorizationSet parsed_hw_set; + AuthorizationSet parsed_sw_set; + EXPECT_EQ(KM_ERROR_OK, + parse_attestation_record(asn1.get(), asn1_len, &parsed_sw_set, &parsed_hw_set)); + + hw_set.Sort(); + sw_set.Sort(); + parsed_hw_set.Sort(); + parsed_sw_set.Sort(); + EXPECT_EQ(hw_set, parsed_hw_set); + EXPECT_EQ(sw_set, parsed_sw_set); +} + +} // namespace test +} // namespace keymaster
diff --git a/keymaster/auth_encrypted_key_blob.cpp b/keymaster/auth_encrypted_key_blob.cpp new file mode 100644 index 0000000..655bc12 --- /dev/null +++ b/keymaster/auth_encrypted_key_blob.cpp
@@ -0,0 +1,137 @@ +/* + * 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 "auth_encrypted_key_blob.h" + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/authorization_set.h> +#include <keymaster/logger.h> + +#include "ocb_utils.h" + +namespace keymaster { + +const uint32_t CURRENT_BLOB_VERSION = 0; + +keymaster_error_t SerializeAuthEncryptedBlob(const KeymasterKeyBlob& encrypted_key_material, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + + const Buffer& nonce, const Buffer& tag, + KeymasterKeyBlob* key_blob) { + size_t size = 1 /* version byte */ + nonce.SerializedSize() + + encrypted_key_material.SerializedSize() + tag.SerializedSize() + + hw_enforced.SerializedSize() + sw_enforced.SerializedSize(); + + if (!key_blob->Reset(size)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + uint8_t* buf = key_blob->writable_data(); + const uint8_t* end = key_blob->key_material + key_blob->key_material_size; + + *buf++ = CURRENT_BLOB_VERSION; + buf = nonce.Serialize(buf, end); + buf = encrypted_key_material.Serialize(buf, end); + buf = tag.Serialize(buf, end); + buf = hw_enforced.Serialize(buf, end); + buf = sw_enforced.Serialize(buf, end); + if (buf != key_blob->key_material + key_blob->key_material_size) + return KM_ERROR_UNKNOWN_ERROR; + + return KM_ERROR_OK; +} + +static keymaster_error_t DeserializeUnversionedBlob(const KeymasterKeyBlob& key_blob, + KeymasterKeyBlob* encrypted_key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced, Buffer* nonce, + Buffer* tag) { + const uint8_t* tmp = key_blob.key_material; + const uint8_t** buf_ptr = &tmp; + const uint8_t* end = tmp + key_blob.key_material_size; + + if (!nonce->reserve(OCB_NONCE_LENGTH) || !tag->reserve(OCB_TAG_LENGTH)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (!copy_from_buf(buf_ptr, end, nonce->peek_write(), OCB_NONCE_LENGTH) || + !encrypted_key_material->Deserialize(buf_ptr, end) || + !copy_from_buf(buf_ptr, end, tag->peek_write(), OCB_TAG_LENGTH) || + !hw_enforced->Deserialize(buf_ptr, end) || // + !sw_enforced->Deserialize(buf_ptr, end)) { + LOG_D("Failed to deserialize unversioned blob (may be a HW-backed key)", 0); + return KM_ERROR_INVALID_KEY_BLOB; + } + if (!nonce->advance_write(OCB_NONCE_LENGTH) || !tag->advance_write(OCB_TAG_LENGTH)) + return KM_ERROR_UNKNOWN_ERROR; + return KM_ERROR_OK; +} + +keymaster_error_t DeserializeAuthEncryptedBlob(const KeymasterKeyBlob& key_blob, + KeymasterKeyBlob* encrypted_key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced, Buffer* nonce, + Buffer* tag) { + if (!key_blob.key_material || key_blob.key_material_size == 0) + return KM_ERROR_INVALID_KEY_BLOB; + + const uint8_t* tmp = key_blob.key_material; + const uint8_t** buf_ptr = &tmp; + const uint8_t* end = tmp + key_blob.key_material_size; + + if (end <= *buf_ptr) + return KM_ERROR_INVALID_KEY_BLOB; + + uint8_t version = *(*buf_ptr)++; + if (version != CURRENT_BLOB_VERSION || // + !nonce->Deserialize(buf_ptr, end) || nonce->available_read() != OCB_NONCE_LENGTH || + !encrypted_key_material->Deserialize(buf_ptr, end) || // + !tag->Deserialize(buf_ptr, end) || tag->available_read() != OCB_TAG_LENGTH || + !hw_enforced->Deserialize(buf_ptr, end) || // + !sw_enforced->Deserialize(buf_ptr, end)) { + // This blob failed to parse. Either it's corrupted or it's a blob generated by an earlier + // version of keymaster using a previous blob format which did not include the version byte + // or the nonce or tag length fields. So we try to parse it as that previous version. + // + // Note that it's not really a problem if we erronously parse a corrupted blob, because + // decryption will fail the authentication check. + // + // A bigger potential problem is: What if a valid unversioned blob appears to parse + // correctly as a versioned blob? It would then be rejected during decryption, causing a + // valid key to become unusable. If this is a disk encryption key, upgrading to a keymaster + // version with the new format would destroy the user's data. + // + // What is the probability that an unversioned key could be successfully parsed as a version + // 0 key? The first 12 bytes of an unversioned key are the nonce, which, in the only + // keymaster version released with unversioned keys, is chosen randomly. In order for an + // unversioned key to parse as a version 0 key, the following must be true about the first + // five of those random bytes: + // + // 1. The first byte must be zero. This will happen with probability 1/2^8. + // + // 2. The second through fifth bytes must contain an unsigned integer value equal to + // NONCE_LENGTH. This will happen with probability 1/2^32. + // + // Based on those two checks alone, the probability of interpreting an unversioned blob as a + // version 0 blob is 1/2^40. That's small enough to be negligible, but there are additional + // checks which lower it further. + LOG_D("Failed to deserialize versioned key blob. Assuming unversioned.", 0); + return DeserializeUnversionedBlob(key_blob, encrypted_key_material, hw_enforced, + sw_enforced, nonce, tag); + } + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/auth_encrypted_key_blob.h b/keymaster/auth_encrypted_key_blob.h new file mode 100644 index 0000000..b987d77 --- /dev/null +++ b/keymaster/auth_encrypted_key_blob.h
@@ -0,0 +1,42 @@ +/* + * 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 SYSTEM_KEYMASTER_AUTH_ENCRYPTED_KEY_BLOB_H_ +#define SYSTEM_KEYMASTER_AUTH_ENCRYPTED_KEY_BLOB_H_ + +#include <hardware/keymaster_defs.h> + +namespace keymaster { + +class AuthorizationSet; +class Buffer; +struct KeymasterKeyBlob; + +keymaster_error_t SerializeAuthEncryptedBlob(const KeymasterKeyBlob& encrypted_key_material, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + const Buffer& nonce, const Buffer& tag, + KeymasterKeyBlob* key_blob); + +keymaster_error_t DeserializeAuthEncryptedBlob(const KeymasterKeyBlob& key_blob, + KeymasterKeyBlob* encrypted_key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced, Buffer* nonce, + Buffer* tag); + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_AUTH_ENCRYPTED_KEY_BLOB_H_
diff --git a/keymaster/authorization_set.cpp b/keymaster/authorization_set.cpp new file mode 100644 index 0000000..09093c9 --- /dev/null +++ b/keymaster/authorization_set.cpp
@@ -0,0 +1,616 @@ +/* + * Copyright (C) 2014 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 <keymaster/authorization_set.h> + +#include <assert.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <new> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/logger.h> + +namespace keymaster { + +static inline bool is_blob_tag(keymaster_tag_t tag) { + return (keymaster_tag_get_type(tag) == KM_BYTES || keymaster_tag_get_type(tag) == KM_BIGNUM); +} + +const size_t STARTING_ELEMS_CAPACITY = 8; + +AuthorizationSet::AuthorizationSet(AuthorizationSetBuilder& builder) { + elems_ = builder.set.elems_; + builder.set.elems_ = NULL; + + elems_size_ = builder.set.elems_size_; + builder.set.elems_size_ = 0; + + elems_capacity_ = builder.set.elems_capacity_; + builder.set.elems_capacity_ = 0; + + indirect_data_ = builder.set.indirect_data_; + builder.set.indirect_data_ = NULL; + + indirect_data_capacity_ = builder.set.indirect_data_capacity_; + builder.set.indirect_data_capacity_ = 0; + + indirect_data_size_ = builder.set.indirect_data_size_; + builder.set.indirect_data_size_ = 0; + + error_ = builder.set.error_; + builder.set.error_ = OK; +} + +AuthorizationSet::~AuthorizationSet() { + FreeData(); +} + +bool AuthorizationSet::reserve_elems(size_t count) { + if (is_valid() != OK) + return false; + + if (count >= elems_capacity_) { + keymaster_key_param_t* new_elems = new (std::nothrow) keymaster_key_param_t[count]; + if (new_elems == NULL) { + set_invalid(ALLOCATION_FAILURE); + return false; + } + memcpy(new_elems, elems_, sizeof(*elems_) * elems_size_); + delete[] elems_; + elems_ = new_elems; + elems_capacity_ = count; + } + return true; +} + +bool AuthorizationSet::reserve_indirect(size_t length) { + if (is_valid() != OK) + return false; + + if (length > indirect_data_capacity_) { + uint8_t* new_data = new (std::nothrow) uint8_t[length]; + if (new_data == NULL) { + set_invalid(ALLOCATION_FAILURE); + return false; + } + memcpy(new_data, indirect_data_, indirect_data_size_); + + // Fix up the data pointers to point into the new region. + for (size_t i = 0; i < elems_size_; ++i) { + if (is_blob_tag(elems_[i].tag)) + elems_[i].blob.data = new_data + (elems_[i].blob.data - indirect_data_); + } + delete[] indirect_data_; + indirect_data_ = new_data; + indirect_data_capacity_ = length; + } + return true; +} + +bool AuthorizationSet::Reinitialize(const keymaster_key_param_t* elems, const size_t count) { + FreeData(); + + if (elems == NULL || count == 0) { + error_ = OK; + return true; + } + + if (!reserve_elems(count)) + return false; + + if (!reserve_indirect(ComputeIndirectDataSize(elems, count))) + return false; + + memcpy(elems_, elems, sizeof(keymaster_key_param_t) * count); + elems_size_ = count; + CopyIndirectData(); + error_ = OK; + return true; +} + +void AuthorizationSet::set_invalid(Error error) { + FreeData(); + error_ = error; +} + +void AuthorizationSet::Sort() { + qsort(elems_, elems_size_, sizeof(*elems_), + reinterpret_cast<int (*)(const void*, const void*)>(keymaster_param_compare)); +} + +void AuthorizationSet::Deduplicate() { + Sort(); + + size_t invalid_count = 0; + for (size_t i = 1; i < size(); ++i) { + if (elems_[i - 1].tag == KM_TAG_INVALID) + ++invalid_count; + else if (keymaster_param_compare(elems_ + i - 1, elems_ + i) == 0) { + // Mark dups as invalid. Note that this "leaks" the data referenced by KM_BYTES and + // KM_BIGNUM entries, but those are just pointers into indirect_data_, so it will all + // get cleaned up. + elems_[i - 1].tag = KM_TAG_INVALID; + ++invalid_count; + } + } + if (size() > 0 && elems_[size() - 1].tag == KM_TAG_INVALID) + ++invalid_count; + + if (invalid_count == 0) + return; + + Sort(); + + // Since KM_TAG_INVALID == 0, all of the invalid entries are first. + elems_size_ -= invalid_count; + memmove(elems_, elems_ + invalid_count, size() * sizeof(*elems_)); +} + +void AuthorizationSet::CopyToParamSet(keymaster_key_param_set_t* set) const { + assert(set); + + set->length = size(); + set->params = + reinterpret_cast<keymaster_key_param_t*>(malloc(sizeof(keymaster_key_param_t) * size())); + + for (size_t i = 0; i < size(); ++i) { + const keymaster_key_param_t src = (*this)[i]; + keymaster_key_param_t& dst(set->params[i]); + + dst = src; + keymaster_tag_type_t type = keymaster_tag_get_type(src.tag); + if (type == KM_BIGNUM || type == KM_BYTES) { + void* tmp = malloc(src.blob.data_length); + memcpy(tmp, src.blob.data, src.blob.data_length); + dst.blob.data = reinterpret_cast<uint8_t*>(tmp); + } + } +} + +int AuthorizationSet::find(keymaster_tag_t tag, int begin) const { + if (is_valid() != OK) + return -1; + + int i = ++begin; + while (i < (int)elems_size_ && elems_[i].tag != tag) + ++i; + if (i == (int)elems_size_) + return -1; + else + return i; +} + +bool AuthorizationSet::erase(size_t index) { + if (index >= size()) + return false; + + --elems_size_; + for (size_t i = index; i < elems_size_; ++i) + elems_[i] = elems_[i + 1]; + return true; +} + +keymaster_key_param_t empty_set = {KM_TAG_INVALID, {}}; +keymaster_key_param_t& AuthorizationSet::operator[](int at) { + if (is_valid() == OK && at < (int)elems_size_) { + return elems_[at]; + } + empty_set = {KM_TAG_INVALID, {}}; + return empty_set; +} + +keymaster_key_param_t AuthorizationSet::operator[](int at) const { + if (is_valid() == OK && at < (int)elems_size_) { + return elems_[at]; + } + empty_set = {KM_TAG_INVALID, {}}; + return empty_set; +} + +bool AuthorizationSet::push_back(const keymaster_key_param_set_t& set) { + if (is_valid() != OK) + return false; + + if (!reserve_elems(elems_size_ + set.length)) + return false; + + if (!reserve_indirect(indirect_data_size_ + ComputeIndirectDataSize(set.params, set.length))) + return false; + + for (size_t i = 0; i < set.length; ++i) + if (!push_back(set.params[i])) + return false; + + return true; +} + +bool AuthorizationSet::push_back(keymaster_key_param_t elem) { + if (is_valid() != OK) + return false; + + if (elems_size_ >= elems_capacity_) + if (!reserve_elems(elems_capacity_ ? elems_capacity_ * 2 : STARTING_ELEMS_CAPACITY)) + return false; + + if (is_blob_tag(elem.tag)) { + if (indirect_data_capacity_ - indirect_data_size_ < elem.blob.data_length) + if (!reserve_indirect(2 * (indirect_data_capacity_ + elem.blob.data_length))) + return false; + + memcpy(indirect_data_ + indirect_data_size_, elem.blob.data, elem.blob.data_length); + elem.blob.data = indirect_data_ + indirect_data_size_; + indirect_data_size_ += elem.blob.data_length; + } + + elems_[elems_size_++] = elem; + return true; +} + +static size_t serialized_size(const keymaster_key_param_t& param) { + switch (keymaster_tag_get_type(param.tag)) { + case KM_INVALID: + return sizeof(uint32_t); + case KM_ENUM: + case KM_ENUM_REP: + case KM_UINT: + case KM_UINT_REP: + return sizeof(uint32_t) * 2; + case KM_ULONG: + case KM_ULONG_REP: + case KM_DATE: + return sizeof(uint32_t) + sizeof(uint64_t); + case KM_BOOL: + return sizeof(uint32_t) + 1; + case KM_BIGNUM: + case KM_BYTES: + return sizeof(uint32_t) * 3; + } + + return sizeof(uint32_t); +} + +static uint8_t* serialize(const keymaster_key_param_t& param, uint8_t* buf, const uint8_t* end, + const uint8_t* indirect_base) { + buf = append_uint32_to_buf(buf, end, param.tag); + switch (keymaster_tag_get_type(param.tag)) { + case KM_INVALID: + break; + case KM_ENUM: + case KM_ENUM_REP: + buf = append_uint32_to_buf(buf, end, param.enumerated); + break; + case KM_UINT: + case KM_UINT_REP: + buf = append_uint32_to_buf(buf, end, param.integer); + break; + case KM_ULONG: + case KM_ULONG_REP: + buf = append_uint64_to_buf(buf, end, param.long_integer); + break; + case KM_DATE: + buf = append_uint64_to_buf(buf, end, param.date_time); + break; + case KM_BOOL: + if (buf < end) + *buf = static_cast<uint8_t>(param.boolean); + buf++; + break; + case KM_BIGNUM: + case KM_BYTES: + buf = append_uint32_to_buf(buf, end, param.blob.data_length); + buf = append_uint32_to_buf(buf, end, param.blob.data - indirect_base); + break; + } + return buf; +} + +static bool deserialize(keymaster_key_param_t* param, const uint8_t** buf_ptr, const uint8_t* end, + const uint8_t* indirect_base, const uint8_t* indirect_end) { + if (!copy_uint32_from_buf(buf_ptr, end, ¶m->tag)) + return false; + + switch (keymaster_tag_get_type(param->tag)) { + case KM_INVALID: + return false; + case KM_ENUM: + case KM_ENUM_REP: + return copy_uint32_from_buf(buf_ptr, end, ¶m->enumerated); + case KM_UINT: + case KM_UINT_REP: + return copy_uint32_from_buf(buf_ptr, end, ¶m->integer); + case KM_ULONG: + case KM_ULONG_REP: + return copy_uint64_from_buf(buf_ptr, end, ¶m->long_integer); + case KM_DATE: + return copy_uint64_from_buf(buf_ptr, end, ¶m->date_time); + break; + case KM_BOOL: + if (*buf_ptr < end) { + param->boolean = static_cast<bool>(**buf_ptr); + (*buf_ptr)++; + return true; + } + return false; + + case KM_BIGNUM: + case KM_BYTES: { + uint32_t offset; + if (!copy_uint32_from_buf(buf_ptr, end, ¶m->blob.data_length) || + !copy_uint32_from_buf(buf_ptr, end, &offset)) + return false; + if (param->blob.data_length + offset < param->blob.data_length || // Overflow check + static_cast<ptrdiff_t>(offset) > indirect_end - indirect_base || + static_cast<ptrdiff_t>(offset + param->blob.data_length) > indirect_end - indirect_base) + return false; + param->blob.data = indirect_base + offset; + return true; + } + } + + return false; +} + +size_t AuthorizationSet::SerializedSizeOfElements() const { + size_t size = 0; + for (size_t i = 0; i < elems_size_; ++i) { + size += serialized_size(elems_[i]); + } + return size; +} + +size_t AuthorizationSet::SerializedSize() const { + return sizeof(uint32_t) + // Size of indirect_data_ + indirect_data_size_ + // indirect_data_ + sizeof(uint32_t) + // Number of elems_ + sizeof(uint32_t) + // Size of elems_ + SerializedSizeOfElements(); // elems_ +} + +uint8_t* AuthorizationSet::Serialize(uint8_t* buf, const uint8_t* end) const { + buf = append_size_and_data_to_buf(buf, end, indirect_data_, indirect_data_size_); + buf = append_uint32_to_buf(buf, end, elems_size_); + buf = append_uint32_to_buf(buf, end, SerializedSizeOfElements()); + for (size_t i = 0; i < elems_size_; ++i) { + buf = serialize(elems_[i], buf, end, indirect_data_); + } + return buf; +} + +bool AuthorizationSet::DeserializeIndirectData(const uint8_t** buf_ptr, const uint8_t* end) { + UniquePtr<uint8_t[]> indirect_buf; + if (!copy_size_and_data_from_buf(buf_ptr, end, &indirect_data_size_, &indirect_buf)) { + LOG_E("Malformed data found in AuthorizationSet deserialization", 0); + set_invalid(MALFORMED_DATA); + return false; + } + indirect_data_ = indirect_buf.release(); + return true; +} + +bool AuthorizationSet::DeserializeElementsData(const uint8_t** buf_ptr, const uint8_t* end) { + uint32_t elements_count; + uint32_t elements_size; + if (!copy_uint32_from_buf(buf_ptr, end, &elements_count) || + !copy_uint32_from_buf(buf_ptr, end, &elements_size)) { + LOG_E("Malformed data found in AuthorizationSet deserialization", 0); + set_invalid(MALFORMED_DATA); + return false; + } + + // Note that the following validation of elements_count is weak, but it prevents allocation of + // elems_ arrays which are clearly too large to be reasonable. + if (static_cast<ptrdiff_t>(elements_size) > end - *buf_ptr || + elements_count * sizeof(uint32_t) > elements_size || + *buf_ptr + (elements_count * sizeof(*elems_)) < *buf_ptr) { + LOG_E("Malformed data found in AuthorizationSet deserialization", 0); + set_invalid(MALFORMED_DATA); + return false; + } + + if (!reserve_elems(elements_count)) + return false; + + uint8_t* indirect_end = indirect_data_ + indirect_data_size_; + const uint8_t* elements_end = *buf_ptr + elements_size; + for (size_t i = 0; i < elements_count; ++i) { + if (!deserialize(elems_ + i, buf_ptr, elements_end, indirect_data_, indirect_end)) { + LOG_E("Malformed data found in AuthorizationSet deserialization", 0); + set_invalid(MALFORMED_DATA); + return false; + } + } + elems_size_ = elements_count; + return true; +} + +bool AuthorizationSet::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + FreeData(); + + if (!DeserializeIndirectData(buf_ptr, end) || !DeserializeElementsData(buf_ptr, end)) + return false; + + if (indirect_data_size_ != ComputeIndirectDataSize(elems_, elems_size_)) { + LOG_E("Malformed data found in AuthorizationSet deserialization", 0); + set_invalid(MALFORMED_DATA); + return false; + } + return true; +} + +void AuthorizationSet::Clear() { + memset_s(elems_, 0, elems_size_ * sizeof(keymaster_key_param_t)); + memset_s(indirect_data_, 0, indirect_data_size_); + elems_size_ = 0; + indirect_data_size_ = 0; +} + +void AuthorizationSet::FreeData() { + Clear(); + + delete[] elems_; + delete[] indirect_data_; + + elems_ = NULL; + indirect_data_ = NULL; + elems_capacity_ = 0; + indirect_data_capacity_ = 0; + error_ = OK; +} + +/* static */ +size_t AuthorizationSet::ComputeIndirectDataSize(const keymaster_key_param_t* elems, size_t count) { + size_t size = 0; + for (size_t i = 0; i < count; ++i) { + if (is_blob_tag(elems[i].tag)) { + size += elems[i].blob.data_length; + } + } + return size; +} + +void AuthorizationSet::CopyIndirectData() { + memset_s(indirect_data_, 0, indirect_data_capacity_); + + uint8_t* indirect_data_pos = indirect_data_; + for (size_t i = 0; i < elems_size_; ++i) { + assert(indirect_data_pos <= indirect_data_ + indirect_data_capacity_); + if (is_blob_tag(elems_[i].tag)) { + memcpy(indirect_data_pos, elems_[i].blob.data, elems_[i].blob.data_length); + elems_[i].blob.data = indirect_data_pos; + indirect_data_pos += elems_[i].blob.data_length; + } + } + assert(indirect_data_pos == indirect_data_ + indirect_data_capacity_); + indirect_data_size_ = indirect_data_pos - indirect_data_; +} + +size_t AuthorizationSet::GetTagCount(keymaster_tag_t tag) const { + size_t count = 0; + for (int pos = -1; (pos = find(tag, pos)) != -1;) + ++count; + return count; +} + +bool AuthorizationSet::GetTagValueEnum(keymaster_tag_t tag, uint32_t* val) const { + int pos = find(tag); + if (pos == -1) { + return false; + } + *val = elems_[pos].enumerated; + return true; +} + +bool AuthorizationSet::GetTagValueEnumRep(keymaster_tag_t tag, size_t instance, + uint32_t* val) const { + size_t count = 0; + int pos = -1; + while (count <= instance) { + pos = find(tag, pos); + if (pos == -1) { + return false; + } + ++count; + } + *val = elems_[pos].enumerated; + return true; +} + +bool AuthorizationSet::GetTagValueInt(keymaster_tag_t tag, uint32_t* val) const { + int pos = find(tag); + if (pos == -1) { + return false; + } + *val = elems_[pos].integer; + return true; +} + +bool AuthorizationSet::GetTagValueIntRep(keymaster_tag_t tag, size_t instance, + uint32_t* val) const { + size_t count = 0; + int pos = -1; + while (count <= instance) { + pos = find(tag, pos); + if (pos == -1) { + return false; + } + ++count; + } + *val = elems_[pos].integer; + return true; +} + +bool AuthorizationSet::GetTagValueLong(keymaster_tag_t tag, uint64_t* val) const { + int pos = find(tag); + if (pos == -1) { + return false; + } + *val = elems_[pos].long_integer; + return true; +} + +bool AuthorizationSet::GetTagValueLongRep(keymaster_tag_t tag, size_t instance, + uint64_t* val) const { + size_t count = 0; + int pos = -1; + while (count <= instance) { + pos = find(tag, pos); + if (pos == -1) { + return false; + } + ++count; + } + *val = elems_[pos].long_integer; + return true; +} + +bool AuthorizationSet::GetTagValueDate(keymaster_tag_t tag, uint64_t* val) const { + int pos = find(tag); + if (pos == -1) { + return false; + } + *val = elems_[pos].date_time; + return true; +} + +bool AuthorizationSet::GetTagValueBlob(keymaster_tag_t tag, keymaster_blob_t* val) const { + int pos = find(tag); + if (pos == -1) { + return false; + } + *val = elems_[pos].blob; + return true; +} + +bool AuthorizationSet::GetTagValueBool(keymaster_tag_t tag) const { + int pos = find(tag); + if (pos == -1) { + return false; + } + assert(elems_[pos].boolean); + return elems_[pos].boolean; +} + +bool AuthorizationSet::ContainsEnumValue(keymaster_tag_t tag, uint32_t value) const { + for (auto& entry : *this) + if (entry.tag == tag && entry.enumerated == value) + return true; + return false; +} + +} // namespace keymaster
diff --git a/keymaster/authorization_set_test.cpp b/keymaster/authorization_set_test.cpp new file mode 100644 index 0000000..ddc7df3 --- /dev/null +++ b/keymaster/authorization_set_test.cpp
@@ -0,0 +1,630 @@ +/* + * Copyright (C) 2014 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 <keymaster/authorization_set.h> +#include <keymaster/android_keymaster_utils.h> + +#include "android_keymaster_test_utils.h" + +namespace keymaster { + +namespace test { + +TEST(Construction, ListProvided) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_USER_ID, 7), + Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD), + Authorization(TAG_APPLICATION_ID, "my_app", 6), Authorization(TAG_KEY_SIZE, 256), + Authorization(TAG_AUTH_TIMEOUT, 300), + }; + AuthorizationSet set(params, array_length(params)); + EXPECT_EQ(8U, set.size()); +} + +TEST(Construction, Copy) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_USER_ID, 7), + Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD), + Authorization(TAG_APPLICATION_ID, "my_app", 6), Authorization(TAG_KEY_SIZE, 256), + Authorization(TAG_AUTH_TIMEOUT, 300), + }; + AuthorizationSet set(params, array_length(params)); + AuthorizationSet set2(set); + EXPECT_EQ(set, set2); +} + +TEST(Construction, NullProvided) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + }; + + AuthorizationSet set1(params, 0); + EXPECT_EQ(0U, set1.size()); + EXPECT_EQ(AuthorizationSet::OK, set1.is_valid()); + + AuthorizationSet set2(reinterpret_cast<keymaster_key_param_t*>(NULL), array_length(params)); + EXPECT_EQ(0U, set2.size()); + EXPECT_EQ(AuthorizationSet::OK, set2.is_valid()); +} + +TEST(Lookup, NonRepeated) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_KEY_SIZE, 256) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + + EXPECT_EQ(8U, set.size()); + + int pos = set.find(TAG_ALGORITHM); + ASSERT_NE(-1, pos); + EXPECT_EQ(KM_TAG_ALGORITHM, set[pos].tag); + EXPECT_EQ(KM_ALGORITHM_RSA, set[pos].enumerated); + + pos = set.find(TAG_MAC_LENGTH); + EXPECT_EQ(-1, pos); + + uint32_t int_val = 0; + EXPECT_TRUE(set.GetTagValue(TAG_USER_ID, &int_val)); + EXPECT_EQ(7U, int_val); + + keymaster_blob_t blob_val; + EXPECT_TRUE(set.GetTagValue(TAG_APPLICATION_ID, &blob_val)); + EXPECT_EQ(6U, blob_val.data_length); + EXPECT_EQ(0, memcmp(blob_val.data, "my_app", 6)); +} + +TEST(Lookup, Repeated) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_SECURE_ID, 47727) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_KEY_SIZE, 256) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + EXPECT_EQ(9U, set.size()); + + int pos = set.find(TAG_PURPOSE); + ASSERT_FALSE(pos == -1); + EXPECT_EQ(KM_TAG_PURPOSE, set[pos].tag); + EXPECT_EQ(KM_PURPOSE_SIGN, set[pos].enumerated); + + pos = set.find(TAG_PURPOSE, pos); + EXPECT_EQ(KM_TAG_PURPOSE, set[pos].tag); + EXPECT_EQ(KM_PURPOSE_VERIFY, set[pos].enumerated); + + EXPECT_EQ(-1, set.find(TAG_PURPOSE, pos)); + + pos = set.find(TAG_USER_SECURE_ID, pos); + EXPECT_EQ(KM_TAG_USER_SECURE_ID, set[pos].tag); + EXPECT_EQ(47727U, set[pos].long_integer); +} + +TEST(Lookup, Indexed) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_KEY_SIZE, 256) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + EXPECT_EQ(8U, set.size()); + + EXPECT_EQ(KM_TAG_PURPOSE, set[0].tag); + EXPECT_EQ(KM_PURPOSE_SIGN, set[0].enumerated); + + // Lookup beyond end doesn't work, just returns zeros, but doens't blow up either (verify by + // running under valgrind). + EXPECT_EQ(KM_TAG_INVALID, set[10].tag); +} + +TEST(Serialization, RoundTrip) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_KEY_SIZE, 256) + .Authorization(TAG_USER_SECURE_ID, 47727) + .Authorization(TAG_AUTH_TIMEOUT, 300) + .Authorization(TAG_ALL_USERS) + .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3) + .Authorization(TAG_ACTIVE_DATETIME, 10)); + + size_t size = set.SerializedSize(); + EXPECT_TRUE(size > 0); + + UniquePtr<uint8_t[]> buf(new uint8_t[size]); + EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size)); + AuthorizationSet deserialized(buf.get(), size); + + EXPECT_EQ(AuthorizationSet::OK, deserialized.is_valid()); + EXPECT_EQ(set, deserialized); + + int pos = deserialized.find(TAG_APPLICATION_ID); + ASSERT_NE(-1, pos); + EXPECT_EQ(KM_TAG_APPLICATION_ID, deserialized[pos].tag); + EXPECT_EQ(6U, deserialized[pos].blob.data_length); + EXPECT_EQ(0, memcmp(deserialized[pos].blob.data, "my_app", 6)); +} + +TEST(Deserialization, Deserialize) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_KEY_SIZE, 256) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + + size_t size = set.SerializedSize(); + EXPECT_TRUE(size > 0); + + UniquePtr<uint8_t[]> buf(new uint8_t[size]); + EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size)); + AuthorizationSet deserialized; + const uint8_t* p = buf.get(); + EXPECT_TRUE(deserialized.Deserialize(&p, p + size)); + EXPECT_EQ(p, buf.get() + size); + + EXPECT_EQ(AuthorizationSet::OK, deserialized.is_valid()); + + EXPECT_EQ(set.size(), deserialized.size()); + for (size_t i = 0; i < set.size(); ++i) { + EXPECT_EQ(set[i].tag, deserialized[i].tag); + } + + int pos = deserialized.find(TAG_APPLICATION_ID); + ASSERT_NE(-1, pos); + EXPECT_EQ(KM_TAG_APPLICATION_ID, deserialized[pos].tag); + EXPECT_EQ(6U, deserialized[pos].blob.data_length); + EXPECT_EQ(0, memcmp(deserialized[pos].blob.data, "my_app", 6)); +} + +TEST(Deserialization, TooShortBuffer) { + uint8_t buf[] = {0, 0, 0}; + AuthorizationSet deserialized(buf, array_length(buf)); + EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid()); + + const uint8_t* p = buf; + EXPECT_FALSE(deserialized.Deserialize(&p, p + array_length(buf))); + EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid()); +} + +TEST(Deserialization, InvalidLengthField) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_KEY_SIZE, 256) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + + size_t size = set.SerializedSize(); + EXPECT_TRUE(size > 0); + + UniquePtr<uint8_t[]> buf(new uint8_t[size]); + EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size)); + *reinterpret_cast<uint32_t*>(buf.get()) = 9; + + AuthorizationSet deserialized(buf.get(), size); + EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid()); + + const uint8_t* p = buf.get(); + EXPECT_FALSE(deserialized.Deserialize(&p, p + size)); + EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid()); +} + +static uint32_t read_uint32(const uint8_t* buf) { + uint32_t val; + memcpy(&val, buf, sizeof(val)); + return val; +} + +static void add_to_uint32(uint8_t* buf, int delta) { + uint32_t val; + memcpy(&val, buf, sizeof(val)); + val += delta; + memcpy(buf, &val, sizeof(val)); +} + +TEST(Deserialization, MalformedIndirectData) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_APPLICATION_DATA, "foo", 3)); + size_t size = set.SerializedSize(); + + UniquePtr<uint8_t[]> buf(new uint8_t[size]); + EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size)); + + // This sucks. This test, as written, requires intimate knowledge of the serialized layout of + // this particular set, which means it's brittle. But it's important to test that we handle + // broken serialized data and I can't think of a better way to write this. + // + // The contents of buf are: + // + // Bytes: Content: + // 0-3 Length of string data, which is 9. + // 4-9 "my_app" + // 10-12 "foo" + // 13-16 Number of elements, which is 2. + // 17-20 Length of elements, which is 24. + // 21-24 First tag, TAG_APPLICATION_ID + // 25-28 Length of string "my_app", 6 + // 29-32 Offset of string "my_app", 0 + // 33-36 Second tag, TAG_APPLICATION_DATA + // 37-40 Length of string "foo", 3 + // 41-44 Offset of string "foo", 6 + + // Check that stuff is where we think. + EXPECT_EQ('m', buf[4]); + EXPECT_EQ('f', buf[10]); + // Length of "my_app" + EXPECT_EQ(6U, read_uint32(buf.get() + 25)); + // Offset of "my_app" + EXPECT_EQ(0U, read_uint32(buf.get() + 29)); + // Length of "foo" + EXPECT_EQ(3U, read_uint32(buf.get() + 37)); + // Offset of "foo" + EXPECT_EQ(6U, read_uint32(buf.get() + 41)); + + // Check that deserialization works. + AuthorizationSet deserialized1(buf.get(), size); + EXPECT_EQ(AuthorizationSet::OK, deserialized1.is_valid()); + + const uint8_t* p = buf.get(); + EXPECT_TRUE(deserialized1.Deserialize(&p, p + size)); + EXPECT_EQ(AuthorizationSet::OK, deserialized1.is_valid()); + + // + // Now mess them up in various ways: + // + + // Move "foo" offset so offset + length goes off the end + add_to_uint32(buf.get() + 41, 1); + AuthorizationSet deserialized2(buf.get(), size); + EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized2.is_valid()); + add_to_uint32(buf.get() + 41, -1); + + // Shorten the "my_app" length to make a gap between the blobs. + add_to_uint32(buf.get() + 25, -1); + AuthorizationSet deserialized3(buf.get(), size); + EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized3.is_valid()); + add_to_uint32(buf.get() + 25, 1); + + // Extend the "my_app" length to make them overlap, and decrease the "foo" length to keep the + // total length the same. We don't detect this but should. + // TODO(swillden): Detect overlaps and holes that leave total size correct. + add_to_uint32(buf.get() + 25, 1); + add_to_uint32(buf.get() + 37, -1); + AuthorizationSet deserialized4(buf.get(), size); + EXPECT_EQ(AuthorizationSet::OK, deserialized4.is_valid()); +} + +TEST(Growable, SuccessfulRoundTrip) { + AuthorizationSet growable; + EXPECT_TRUE(growable.push_back(Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA))); + EXPECT_EQ(1U, growable.size()); + + EXPECT_TRUE(growable.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY))); + EXPECT_EQ(2U, growable.size()); + + EXPECT_TRUE(growable.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN))); + EXPECT_EQ(3U, growable.size()); + + EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_ID, "data", 4))); + EXPECT_EQ(4U, growable.size()); + + EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_DATA, "some more data", 14))); + EXPECT_EQ(5U, growable.size()); + + size_t serialize_size = growable.SerializedSize(); + UniquePtr<uint8_t[]> serialized(new uint8_t[serialize_size]); + EXPECT_EQ(serialized.get() + serialize_size, + growable.Serialize(serialized.get(), serialized.get() + serialize_size)); + + AuthorizationSet deserialized(serialized.get(), serialize_size); + EXPECT_EQ(growable, deserialized); +} + +TEST(Growable, InsufficientElemBuf) { + AuthorizationSet growable; + EXPECT_EQ(AuthorizationSet::OK, growable.is_valid()); + + // First insertion fits. + EXPECT_TRUE(growable.push_back(Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA))); + EXPECT_EQ(1U, growable.size()); + EXPECT_EQ(AuthorizationSet::OK, growable.is_valid()); + + // Second does too. + EXPECT_TRUE(growable.push_back(Authorization(TAG_RSA_PUBLIC_EXPONENT, 3))); + EXPECT_EQ(2U, growable.size()); +} + +TEST(Growable, InsufficientIndirectBuf) { + AuthorizationSet growable; + EXPECT_EQ(AuthorizationSet::OK, growable.is_valid()); + + EXPECT_TRUE(growable.push_back(Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA))); + EXPECT_EQ(1U, growable.size()); + EXPECT_EQ(AuthorizationSet::OK, growable.is_valid()); + + EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_ID, "1234567890", 10))); + EXPECT_EQ(2U, growable.size()); + EXPECT_EQ(AuthorizationSet::OK, growable.is_valid()); + + EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_DATA, "1", 1))); + EXPECT_EQ(3U, growable.size()); + EXPECT_EQ(AuthorizationSet::OK, growable.is_valid()); + + // Can still add another entry without indirect data. Now it's full. + EXPECT_TRUE(growable.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN))); + EXPECT_EQ(4U, growable.size()); + EXPECT_EQ(AuthorizationSet::OK, growable.is_valid()); +} + +TEST(Growable, PushBackSets) { + AuthorizationSetBuilder builder; + builder.Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_KEY_SIZE, 256) + .Authorization(TAG_AUTH_TIMEOUT, 300); + + AuthorizationSet set1(builder.build()); + AuthorizationSet set2(builder.build()); + + AuthorizationSet combined; + EXPECT_TRUE(combined.push_back(set1)); + EXPECT_TRUE(combined.push_back(set2)); + EXPECT_EQ(set1.size() + set2.size(), combined.size()); + EXPECT_EQ(12U, combined.indirect_size()); +} + +TEST(GetValue, GetInt) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + + uint32_t val; + EXPECT_TRUE(set.GetTagValue(TAG_USER_ID, &val)); + EXPECT_EQ(7U, val); + + // Find one that isn't there + EXPECT_FALSE(set.GetTagValue(TAG_KEY_SIZE, &val)); +} + +TEST(GetValue, GetLong) { + AuthorizationSet set1(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3)); + + AuthorizationSet set2(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)); + + uint64_t val; + EXPECT_TRUE(set1.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &val)); + EXPECT_EQ(3U, val); + + // Find one that isn't there + EXPECT_FALSE(set2.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &val)); +} + +TEST(GetValue, GetLongRep) { + AuthorizationSet set1(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_SECURE_ID, 8338) + .Authorization(TAG_USER_SECURE_ID, 4334) + .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3)); + + AuthorizationSet set2(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)); + + uint64_t val; + EXPECT_TRUE(set1.GetTagValue(TAG_USER_SECURE_ID, 0, &val)); + EXPECT_EQ(8338U, val); + EXPECT_TRUE(set1.GetTagValue(TAG_USER_SECURE_ID, 1, &val)); + EXPECT_EQ(4334U, val); + + // Find one that isn't there + EXPECT_FALSE(set2.GetTagValue(TAG_USER_SECURE_ID, &val)); +} + +TEST(GetValue, GetEnum) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + + keymaster_algorithm_t val; + EXPECT_TRUE(set.GetTagValue(TAG_ALGORITHM, &val)); + EXPECT_EQ(KM_ALGORITHM_RSA, val); + + // Find one that isn't there + keymaster_padding_t val2; + EXPECT_FALSE(set.GetTagValue(TAG_PADDING, &val2)); +} + +TEST(GetValue, GetEnumRep) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + + keymaster_purpose_t val; + EXPECT_TRUE(set.GetTagValue(TAG_PURPOSE, 0, &val)); + EXPECT_EQ(KM_PURPOSE_SIGN, val); + EXPECT_TRUE(set.GetTagValue(TAG_PURPOSE, 1, &val)); + EXPECT_EQ(KM_PURPOSE_VERIFY, val); + + // Find one that isn't there + EXPECT_FALSE(set.GetTagValue(TAG_PURPOSE, 2, &val)); +} + +TEST(GetValue, GetDate) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_ACTIVE_DATETIME, 10) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + + uint64_t val; + EXPECT_TRUE(set.GetTagValue(TAG_ACTIVE_DATETIME, &val)); + EXPECT_EQ(10U, val); + + // Find one that isn't there + EXPECT_FALSE(set.GetTagValue(TAG_USAGE_EXPIRE_DATETIME, &val)); +} + +TEST(GetValue, GetBlob) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD) + .Authorization(TAG_APPLICATION_ID, "my_app", 6) + .Authorization(TAG_AUTH_TIMEOUT, 300)); + + keymaster_blob_t val; + EXPECT_TRUE(set.GetTagValue(TAG_APPLICATION_ID, &val)); + EXPECT_EQ(6U, val.data_length); + EXPECT_EQ(0, memcmp(val.data, "my_app", 6)); + + // Find one that isn't there + EXPECT_FALSE(set.GetTagValue(TAG_APPLICATION_DATA, &val)); +} + +TEST(Deduplication, NoDuplicates) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_ACTIVE_DATETIME, 10) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)); + AuthorizationSet copy(set); + + EXPECT_EQ(copy, set); + set.Deduplicate(); + EXPECT_EQ(copy.size(), set.size()); + + // Sets no longer compare equal, because of ordering (ugh, maybe it should be + // AuthorizationList, not AuthorizationSet). + EXPECT_NE(copy, set); +} + +TEST(Deduplication, NoDuplicatesHasInvalid) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_ACTIVE_DATETIME, 10) + .Authorization(TAG_INVALID) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)); + AuthorizationSet copy(set); + + EXPECT_EQ(copy, set); + set.Deduplicate(); + + // Deduplicate should have removed the invalid. + EXPECT_EQ(copy.size() - 1, set.size()); + EXPECT_NE(copy, set); +} + +TEST(Deduplication, DuplicateEnum) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ACTIVE_DATETIME, 10) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)); + AuthorizationSet copy(set); + + EXPECT_EQ(copy, set); + set.Deduplicate(); + EXPECT_EQ(copy.size() - 2, set.size()); + EXPECT_NE(copy, set); +} + +TEST(Deduplication, DuplicateBlob) { + AuthorizationSet set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_ACTIVE_DATETIME, 10) + .Authorization(TAG_APPLICATION_DATA, "data", 4) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_USER_ID, 7) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_APPLICATION_DATA, "data", 4) + .Authorization(TAG_APPLICATION_DATA, "foo", 3) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)); + AuthorizationSet copy(set); + + EXPECT_EQ(copy, set); + set.Deduplicate(); + EXPECT_EQ(copy.size() - 3, set.size()); + EXPECT_NE(copy, set); + + // The real test here is that valgrind reports no leak. +} + +} // namespace test +} // namespace keymaster
diff --git a/keymaster/ec_key.cpp b/keymaster/ec_key.cpp new file mode 100644 index 0000000..64ffc7a --- /dev/null +++ b/keymaster/ec_key.cpp
@@ -0,0 +1,36 @@ +/* + * Copyright 2014 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 "ec_key.h" + +#if defined(OPENSSL_IS_BORINGSSL) +typedef size_t openssl_size_t; +#else +typedef int openssl_size_t; +#endif + +namespace keymaster { + +bool EcKey::EvpToInternal(const EVP_PKEY* pkey) { + ec_key_.reset(EVP_PKEY_get1_EC_KEY(const_cast<EVP_PKEY*>(pkey))); + return ec_key_.get() != NULL; +} + +bool EcKey::InternalToEvp(EVP_PKEY* pkey) const { + return EVP_PKEY_set1_EC_KEY(pkey, ec_key_.get()) == 1; +} + +} // namespace keymaster
diff --git a/keymaster/ec_key.h b/keymaster/ec_key.h new file mode 100644 index 0000000..8230d23 --- /dev/null +++ b/keymaster/ec_key.h
@@ -0,0 +1,51 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_EC_KEY_H_ +#define SYSTEM_KEYMASTER_EC_KEY_H_ + +#include <openssl/ec.h> + +#include "asymmetric_key.h" +#include "openssl_utils.h" + +namespace keymaster { + +class EcdsaOperationFactory; + +class EcKey : public AsymmetricKey { + public: + EcKey(const AuthorizationSet& hw_enforced, const AuthorizationSet& sw_enforced, + keymaster_error_t* error) + : AsymmetricKey(hw_enforced, sw_enforced, error) {} + + bool InternalToEvp(EVP_PKEY* pkey) const override; + bool EvpToInternal(const EVP_PKEY* pkey) override; + + EC_KEY* key() const { return ec_key_.get(); } + + protected: + EcKey(EC_KEY* ec_key, const AuthorizationSet& hw_enforced, const AuthorizationSet& sw_enforced, + keymaster_error_t* error) + : AsymmetricKey(hw_enforced, sw_enforced, error), ec_key_(ec_key) {} + + private: + UniquePtr<EC_KEY, EC_KEY_Delete> ec_key_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_EC_KEY_H_
diff --git a/keymaster/ec_key_factory.cpp b/keymaster/ec_key_factory.cpp new file mode 100644 index 0000000..14c8327 --- /dev/null +++ b/keymaster/ec_key_factory.cpp
@@ -0,0 +1,182 @@ +/* + * 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 <keymaster/ec_key_factory.h> + +#include <openssl/evp.h> + +#include <keymaster/keymaster_context.h> + +#include "ec_key.h" +#include "ecdsa_operation.h" +#include "openssl_err.h" + +namespace keymaster { + +static EcdsaSignOperationFactory sign_factory; +static EcdsaVerifyOperationFactory verify_factory; + +OperationFactory* EcKeyFactory::GetOperationFactory(keymaster_purpose_t purpose) const { + switch (purpose) { + case KM_PURPOSE_SIGN: + return &sign_factory; + case KM_PURPOSE_VERIFY: + return &verify_factory; + default: + return nullptr; + } +} + +keymaster_error_t EcKeyFactory::GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + if (!key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + AuthorizationSet authorizations(key_description); + + uint32_t key_size; + if (!authorizations.GetTagValue(TAG_KEY_SIZE, &key_size)) { + LOG_E("%s", "No key size specified for EC key generation"); + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + + UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EC_KEY_new()); + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new()); + if (ec_key.get() == NULL || pkey.get() == NULL) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + UniquePtr<EC_GROUP, EC_GROUP_Delete> group(choose_group(key_size)); + if (group.get() == NULL) { + LOG_E("Unable to get EC group for key of size %d", key_size); + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + +#if !defined(OPENSSL_IS_BORINGSSL) + EC_GROUP_set_point_conversion_form(group.get(), POINT_CONVERSION_UNCOMPRESSED); + EC_GROUP_set_asn1_flag(group.get(), OPENSSL_EC_NAMED_CURVE); +#endif + + if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 || + EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) { + return TranslateLastOpenSslError(); + } + + if (EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()) != 1) + return TranslateLastOpenSslError(); + + KeymasterKeyBlob key_material; + keymaster_error_t error = EvpKeyToKeyMaterial(pkey.get(), &key_material); + if (error != KM_ERROR_OK) + return error; + + return context_->CreateKeyBlob(authorizations, KM_ORIGIN_GENERATED, key_material, key_blob, + hw_enforced, sw_enforced); +} + +keymaster_error_t EcKeyFactory::ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + if (!output_key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + AuthorizationSet authorizations; + uint32_t key_size; + keymaster_error_t error = UpdateImportKeyDescription( + key_description, input_key_material_format, input_key_material, &authorizations, &key_size); + if (error != KM_ERROR_OK) + return error; + + return context_->CreateKeyBlob(authorizations, KM_ORIGIN_IMPORTED, input_key_material, + output_key_blob, hw_enforced, sw_enforced); +} + +keymaster_error_t EcKeyFactory::UpdateImportKeyDescription(const AuthorizationSet& key_description, + keymaster_key_format_t key_format, + const KeymasterKeyBlob& key_material, + AuthorizationSet* updated_description, + uint32_t* key_size_bits) const { + if (!updated_description || !key_size_bits) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey; + keymaster_error_t error = + KeyMaterialToEvpKey(key_format, key_material, keymaster_key_type(), &pkey); + if (error != KM_ERROR_OK) + return error; + + UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EVP_PKEY_get1_EC_KEY(pkey.get())); + if (!ec_key.get()) + return TranslateLastOpenSslError(); + + updated_description->Reinitialize(key_description); + + size_t extracted_key_size_bits; + error = ec_get_group_size(EC_KEY_get0_group(ec_key.get()), &extracted_key_size_bits); + if (error != KM_ERROR_OK) + return error; + + *key_size_bits = extracted_key_size_bits; + if (!updated_description->GetTagValue(TAG_KEY_SIZE, key_size_bits)) + updated_description->push_back(TAG_KEY_SIZE, extracted_key_size_bits); + if (*key_size_bits != extracted_key_size_bits) + return KM_ERROR_IMPORT_PARAMETER_MISMATCH; + + keymaster_algorithm_t algorithm = KM_ALGORITHM_EC; + if (!updated_description->GetTagValue(TAG_ALGORITHM, &algorithm)) + updated_description->push_back(TAG_ALGORITHM, KM_ALGORITHM_EC); + if (algorithm != KM_ALGORITHM_EC) + return KM_ERROR_IMPORT_PARAMETER_MISMATCH; + + return KM_ERROR_OK; +} + +/* static */ +EC_GROUP* EcKeyFactory::choose_group(size_t key_size_bits) { + switch (key_size_bits) { + case 224: + return EC_GROUP_new_by_curve_name(NID_secp224r1); + break; + case 256: + return EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + break; + case 384: + return EC_GROUP_new_by_curve_name(NID_secp384r1); + break; + case 521: + return EC_GROUP_new_by_curve_name(NID_secp521r1); + break; + default: + return NULL; + break; + } +} + +keymaster_error_t EcKeyFactory::CreateEmptyKey(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<AsymmetricKey>* key) const { + keymaster_error_t error; + key->reset(new (std::nothrow) EcKey(hw_enforced, sw_enforced, &error)); + if (!key->get()) + error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return error; +} + +} // namespace keymaster
diff --git a/keymaster/ec_keymaster0_key.cpp b/keymaster/ec_keymaster0_key.cpp new file mode 100644 index 0000000..705ee73 --- /dev/null +++ b/keymaster/ec_keymaster0_key.cpp
@@ -0,0 +1,122 @@ +/* + * 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 "ec_keymaster0_key.h" + +#include <memory> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/logger.h> +#include <keymaster/soft_keymaster_context.h> + +#include "keymaster0_engine.h" +#include "openssl_utils.h" + +using std::unique_ptr; + +namespace keymaster { + +EcdsaKeymaster0KeyFactory::EcdsaKeymaster0KeyFactory(const SoftKeymasterContext* context, + const Keymaster0Engine* engine) + : EcKeyFactory(context), engine_(engine) {} + +keymaster_error_t EcdsaKeymaster0KeyFactory::GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + if (!key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + if (!engine_ || !engine_->supports_ec()) + return super::GenerateKey(key_description, key_blob, hw_enforced, sw_enforced); + + uint32_t key_size; + if (!key_description.GetTagValue(TAG_KEY_SIZE, &key_size)) { + LOG_E("%s", "No key size specified for EC key generation"); + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + + KeymasterKeyBlob key_material; + if (!engine_->GenerateEcKey(key_size, &key_material)) + return KM_ERROR_UNKNOWN_ERROR; + + // These tags are hardware-enforced. Putting them in the hw_enforced set here will ensure that + // context_->CreateKeyBlob doesn't put them in sw_enforced. + hw_enforced->push_back(TAG_ALGORITHM, KM_ALGORITHM_EC); + hw_enforced->push_back(TAG_KEY_SIZE, key_size); + hw_enforced->push_back(TAG_ORIGIN, KM_ORIGIN_UNKNOWN); + + return context_->CreateKeyBlob(key_description, KM_ORIGIN_UNKNOWN, key_material, key_blob, + hw_enforced, sw_enforced); +} + +keymaster_error_t EcdsaKeymaster0KeyFactory::ImportKey( + const AuthorizationSet& key_description, keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, KeymasterKeyBlob* output_key_blob, + AuthorizationSet* hw_enforced, AuthorizationSet* sw_enforced) const { + if (!output_key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + if (!engine_ || !engine_->supports_ec()) + return super::ImportKey(key_description, input_key_material_format, input_key_material, + output_key_blob, hw_enforced, sw_enforced); + + AuthorizationSet authorizations; + uint32_t key_size; + keymaster_error_t error = UpdateImportKeyDescription( + key_description, input_key_material_format, input_key_material, &authorizations, &key_size); + if (error != KM_ERROR_OK) + return error; + + KeymasterKeyBlob imported_hw_key; + if (!engine_->ImportKey(input_key_material_format, input_key_material, &imported_hw_key)) + return KM_ERROR_UNKNOWN_ERROR; + + // These tags are hardware-enforced. Putting them in the hw_enforced set here will ensure that + // context_->CreateKeyBlob doesn't put them in sw_enforced. + hw_enforced->push_back(TAG_ALGORITHM, KM_ALGORITHM_EC); + hw_enforced->push_back(TAG_KEY_SIZE, key_size); + hw_enforced->push_back(TAG_ORIGIN, KM_ORIGIN_UNKNOWN); + + return context_->CreateKeyBlob(authorizations, KM_ORIGIN_UNKNOWN, imported_hw_key, + output_key_blob, hw_enforced, sw_enforced); +} + +keymaster_error_t EcdsaKeymaster0KeyFactory::LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const { + if (!key) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + if (sw_enforced.GetTagCount(TAG_ALGORITHM) == 1) + return super::LoadKey(key_material, additional_params, hw_enforced, sw_enforced, key); + + unique_ptr<EC_KEY, EC_KEY_Delete> ec_key(engine_->BlobToEcKey(key_material)); + if (!ec_key) + return KM_ERROR_UNKNOWN_ERROR; + + keymaster_error_t error; + key->reset(new (std::nothrow) + EcKeymaster0Key(ec_key.release(), hw_enforced, sw_enforced, &error)); + if (error != KM_ERROR_OK) + return error; + + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/ec_keymaster0_key.h b/keymaster/ec_keymaster0_key.h new file mode 100644 index 0000000..0d0af3d --- /dev/null +++ b/keymaster/ec_keymaster0_key.h
@@ -0,0 +1,70 @@ +/* + * 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 SYSTEM_KEYMASTER_EC_KEYMASTER0_KEY_H_ +#define SYSTEM_KEYMASTER_EC_KEYMASTER0_KEY_H_ + +#include <openssl/ec_key.h> + +#include <keymaster/ec_key_factory.h> + +#include "ec_key.h" + +namespace keymaster { + +class Keymaster0Engine; +class SoftKeymasterContext; + +/** + * An EcdsaKeyFactory which can delegate key generation, importing and loading operations to a + * keymaster0-backed OpenSSL engine. + */ +class EcdsaKeymaster0KeyFactory : public EcKeyFactory { + typedef EcKeyFactory super; + + public: + EcdsaKeymaster0KeyFactory(const SoftKeymasterContext* context, const Keymaster0Engine* engine); + + keymaster_error_t GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const override; + + private: + const Keymaster0Engine* engine_; +}; + +class EcKeymaster0Key : public EcKey { + public: + EcKeymaster0Key(EC_KEY* ec_key, const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, keymaster_error_t* error) + : EcKey(ec_key, hw_enforced, sw_enforced, error) {} +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_EC_KEYMASTER0_KEY_H_
diff --git a/keymaster/ec_keymaster1_key.cpp b/keymaster/ec_keymaster1_key.cpp new file mode 100644 index 0000000..5cc2539 --- /dev/null +++ b/keymaster/ec_keymaster1_key.cpp
@@ -0,0 +1,120 @@ +/* + * 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 "ec_keymaster1_key.h" + +#include <memory> + +#include <keymaster/logger.h> + +#include "ecdsa_keymaster1_operation.h" +#include "ecdsa_operation.h" + +using std::unique_ptr; + +namespace keymaster { + +EcdsaKeymaster1KeyFactory::EcdsaKeymaster1KeyFactory(const SoftKeymasterContext* context, + const Keymaster1Engine* engine) + : EcKeyFactory(context), engine_(engine), + sign_factory_(new EcdsaKeymaster1OperationFactory(KM_PURPOSE_SIGN, engine)), + // For pubkey ops we can use the normal operation factories. + verify_factory_(new EcdsaVerifyOperationFactory) {} + +static bool is_supported(uint32_t digest) { + return digest == KM_DIGEST_NONE || digest == KM_DIGEST_SHA_2_256; +} + +static void UpdateToWorkAroundUnsupportedDigests(const AuthorizationSet& key_description, + AuthorizationSet* new_description) { + bool have_unsupported_digests = false; + bool have_digest_none = false; + for (const keymaster_key_param_t& entry : key_description) { + new_description->push_back(entry); + + if (entry.tag == TAG_DIGEST) { + if (entry.enumerated == KM_DIGEST_NONE) { + have_digest_none = true; + } else if (!is_supported(entry.enumerated)) { + LOG_D("Found request for unsupported digest %u", entry.enumerated); + have_unsupported_digests = true; + } + } + } + + if (have_unsupported_digests && !have_digest_none) { + LOG_I("Adding KM_DIGEST_NONE to key authorization, to enable software digesting", 0); + new_description->push_back(TAG_DIGEST, KM_DIGEST_NONE); + } +} + +keymaster_error_t EcdsaKeymaster1KeyFactory::GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + AuthorizationSet key_params_copy; + UpdateToWorkAroundUnsupportedDigests(key_description, &key_params_copy); + return engine_->GenerateKey(key_params_copy, key_blob, hw_enforced, sw_enforced); +} + +keymaster_error_t EcdsaKeymaster1KeyFactory::ImportKey( + const AuthorizationSet& key_description, keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, KeymasterKeyBlob* output_key_blob, + AuthorizationSet* hw_enforced, AuthorizationSet* sw_enforced) const { + AuthorizationSet key_params_copy; + UpdateToWorkAroundUnsupportedDigests(key_description, &key_params_copy); + return engine_->ImportKey(key_params_copy, input_key_material_format, input_key_material, + output_key_blob, hw_enforced, sw_enforced); +} + +keymaster_error_t EcdsaKeymaster1KeyFactory::LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const { + if (!key) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + keymaster_error_t error; + unique_ptr<EC_KEY, EC_KEY_Delete> ecdsa( + engine_->BuildEcKey(key_material, additional_params, &error)); + if (!ecdsa) + return error; + + key->reset(new (std::nothrow) + EcdsaKeymaster1Key(ecdsa.release(), hw_enforced, sw_enforced, &error)); + if (!key->get()) + error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (error != KM_ERROR_OK) + return error; + + return KM_ERROR_OK; +} + +OperationFactory* +EcdsaKeymaster1KeyFactory::GetOperationFactory(keymaster_purpose_t purpose) const { + switch (purpose) { + case KM_PURPOSE_SIGN: + return sign_factory_.get(); + case KM_PURPOSE_VERIFY: + return verify_factory_.get(); + default: + return nullptr; + } +} + +} // namespace keymaster
diff --git a/keymaster/ec_keymaster1_key.h b/keymaster/ec_keymaster1_key.h new file mode 100644 index 0000000..2702b35 --- /dev/null +++ b/keymaster/ec_keymaster1_key.h
@@ -0,0 +1,79 @@ +/* + * 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 SYSTEM_KEYMASTER_EC_KEYMASTER1_KEY_H_ +#define SYSTEM_KEYMASTER_EC_KEYMASTER1_KEY_H_ + +#include <openssl/ecdsa.h> + +#include <hardware/keymaster1.h> +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/logger.h> +#include <keymaster/ec_key_factory.h> +#include <keymaster/soft_keymaster_context.h> + +#include "ec_key.h" +#include "keymaster1_engine.h" + +namespace keymaster { + +class SoftKeymasterContext; + +/** + * EcdsaKeymaster1KeyFactory is a KeyFactory that creates and loads keys which are actually backed + * by a hardware keymaster1 module, but which does not support all keymaster1 digests. During + * generation or import any unsupported digests in the key description are silently replaced with + * KM_DIGEST_NONE. + */ +class EcdsaKeymaster1KeyFactory : public EcKeyFactory { + public: + EcdsaKeymaster1KeyFactory(const SoftKeymasterContext* context, const Keymaster1Engine* engine); + + keymaster_error_t GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const override; + + OperationFactory* GetOperationFactory(keymaster_purpose_t purpose) const override; + + private: + const Keymaster1Engine* engine_; + + std::unique_ptr<OperationFactory> sign_factory_; + std::unique_ptr<OperationFactory> verify_factory_; +}; + +class EcdsaKeymaster1Key : public EcKey { + public: + EcdsaKeymaster1Key(EC_KEY* ecdsa_key, const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, keymaster_error_t* error) + : EcKey(ecdsa_key, hw_enforced, sw_enforced, error) {} +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ECDSA_KEYMASTER1_KEY_H_
diff --git a/keymaster/ec_privkey_pk8.der b/keymaster/ec_privkey_pk8.der new file mode 100644 index 0000000..a4af673 --- /dev/null +++ b/keymaster/ec_privkey_pk8.der Binary files differ
diff --git a/keymaster/ecdsa_keymaster1_operation.cpp b/keymaster/ecdsa_keymaster1_operation.cpp new file mode 100644 index 0000000..7e1a4f5 --- /dev/null +++ b/keymaster/ecdsa_keymaster1_operation.cpp
@@ -0,0 +1,139 @@ +/* + * 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 "ecdsa_keymaster1_operation.h" + +#include <memory> + +#include <keymaster/android_keymaster_utils.h> + +#include "openssl_err.h" +#include "openssl_utils.h" +#include "ec_keymaster1_key.h" + +using std::unique_ptr; + +namespace keymaster { + +keymaster_error_t EcdsaKeymaster1WrappedOperation::Begin(EVP_PKEY* ecdsa_key, + const AuthorizationSet& input_params) { + Keymaster1Engine::KeyData* key_data = engine_->GetData(ecdsa_key); + if (!key_data) + return KM_ERROR_UNKNOWN_ERROR; + + // Copy the input params and substitute KM_DIGEST_NONE for whatever was specified. Also change + // KM_PAD_ECDSA_PSS and KM_PAD_OAEP to KM_PAD_NONE, if necessary. These are the params we'll + // pass + // to the hardware module. The regular Ecdsa*Operation classes will do software digesting and + // padding where we've told the HW not to. + // + // The reason we don't change KM_PAD_ECDSA_PKCS1_1_5_SIGN or KM_PAD_ECDSA_PKCS1_1_5_ENCRYPT to + // KM_PAD_NONE is because the hardware can to those padding modes, since they don't involve + // digesting. + // + // We also cache in the key the padding value that we expect to be passed to the engine crypto + // operation. This just allows us to double-check that the correct padding value is reaching + // that layer. + AuthorizationSet begin_params(input_params); + int pos = begin_params.find(TAG_DIGEST); + if (pos == -1) + return KM_ERROR_UNSUPPORTED_DIGEST; + begin_params[pos].enumerated = KM_DIGEST_NONE; + + return engine_->device()->begin(engine_->device(), purpose_, &key_data->key_material, + &begin_params, nullptr /* out_params */, &operation_handle_); +} + +keymaster_error_t +EcdsaKeymaster1WrappedOperation::PrepareFinish(EVP_PKEY* ecdsa_key, + const AuthorizationSet& input_params) { + Keymaster1Engine::KeyData* key_data = engine_->GetData(ecdsa_key); + if (!key_data) { + LOG_E("Could not get extended key data... not a Keymaster1Engine key?", 0); + return KM_ERROR_UNKNOWN_ERROR; + } + key_data->op_handle = operation_handle_; + key_data->finish_params.Reinitialize(input_params); + + return KM_ERROR_OK; +} + +keymaster_error_t EcdsaKeymaster1WrappedOperation::Abort() { + return engine_->device()->abort(engine_->device(), operation_handle_); +} + +keymaster_error_t EcdsaKeymaster1WrappedOperation::GetError(EVP_PKEY* ecdsa_key) { + Keymaster1Engine::KeyData* key_data = engine_->GetData(ecdsa_key); + if (!key_data) + return KM_ERROR_UNKNOWN_ERROR; + return key_data->error; +} + +static EVP_PKEY* GetEvpKey(const EcdsaKeymaster1Key& key, keymaster_error_t* error) { + if (!key.key()) { + *error = KM_ERROR_UNKNOWN_ERROR; + return nullptr; + } + + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new()); + if (!key.InternalToEvp(pkey.get())) { + *error = KM_ERROR_UNKNOWN_ERROR; + return nullptr; + } + return pkey.release(); +} + +Operation* EcdsaKeymaster1OperationFactory::CreateOperation(const Key& key, + const AuthorizationSet& begin_params, + keymaster_error_t* error) { + keymaster_digest_t digest; + if (!GetAndValidateDigest(begin_params, key, &digest, error)) + return nullptr; + + const EcdsaKeymaster1Key& ecdsa_km1_key(static_cast<const EcdsaKeymaster1Key&>(key)); + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> ecdsa(GetEvpKey(ecdsa_km1_key, error)); + if (!ecdsa) + return nullptr; + + switch (purpose_) { + case KM_PURPOSE_SIGN: + return new EcdsaKeymaster1Operation<EcdsaSignOperation>(digest, ecdsa.release(), engine_); + default: + LOG_E( + "Bug: Pubkey operation requested. Those should be handled by normal ECDSA operations.", + 0); + *error = KM_ERROR_UNSUPPORTED_PURPOSE; + return nullptr; + } +} + +static const keymaster_digest_t supported_digests[] = { + KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, + KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; + +const keymaster_digest_t* +EcdsaKeymaster1OperationFactory::SupportedDigests(size_t* digest_count) const { + *digest_count = array_length(supported_digests); + return supported_digests; +} + +const keymaster_padding_t* +EcdsaKeymaster1OperationFactory::SupportedPaddingModes(size_t* padding_mode_count) const { + *padding_mode_count = 0; + return nullptr; +} + +} // namespace keymaster
diff --git a/keymaster/ecdsa_keymaster1_operation.h b/keymaster/ecdsa_keymaster1_operation.h new file mode 100644 index 0000000..6045686 --- /dev/null +++ b/keymaster/ecdsa_keymaster1_operation.h
@@ -0,0 +1,120 @@ +/* + * 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 SYSTEM_KEYMASTER_ECDSA_KEYMASTER1_OPERATION_H_ +#define SYSTEM_KEYMASTER_ECDSA_KEYMASTER1_OPERATION_H_ + +#include <openssl/evp.h> + +#include <hardware/keymaster1.h> +#include <keymaster/android_keymaster_utils.h> + +#include "ecdsa_operation.h" +#include "keymaster1_engine.h" + +namespace keymaster { + +class EcdsaKeymaster1WrappedOperation { + public: + EcdsaKeymaster1WrappedOperation(keymaster_purpose_t purpose, const Keymaster1Engine* engine) + : purpose_(purpose), operation_handle_(0), engine_(engine) {} + ~EcdsaKeymaster1WrappedOperation() { + if (operation_handle_) + Abort(); + } + + keymaster_error_t Begin(EVP_PKEY* ecdsa_key, const AuthorizationSet& input_params); + keymaster_error_t PrepareFinish(EVP_PKEY* ecdsa_key, const AuthorizationSet& input_params); + void Finish() { operation_handle_ = 0; } + keymaster_error_t Abort(); + + keymaster_error_t GetError(EVP_PKEY* ecdsa_key); + + protected: + keymaster_purpose_t purpose_; + keymaster_operation_handle_t operation_handle_; + const Keymaster1Engine* engine_; +}; + +template <typename BaseOperation> class EcdsaKeymaster1Operation : public BaseOperation { + typedef BaseOperation super; + + public: + EcdsaKeymaster1Operation(keymaster_digest_t digest, EVP_PKEY* key, + const Keymaster1Engine* engine) + : BaseOperation(digest, key), wrapped_operation_(super::purpose(), engine) { + // Shouldn't be instantiated for public key operations. + assert(super::purpose() != KM_PURPOSE_VERIFY); + assert(super::purpose() != KM_PURPOSE_ENCRYPT); + } + + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override { + keymaster_error_t error = wrapped_operation_.Begin(super::ecdsa_key_, input_params); + if (error != KM_ERROR_OK) + return error; + return super::Begin(input_params, output_params); + } + + keymaster_error_t Finish(const AuthorizationSet& input_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override { + keymaster_error_t error = wrapped_operation_.PrepareFinish(super::ecdsa_key_, input_params); + if (error != KM_ERROR_OK) + return error; + error = super::Finish(input_params, input, signature, output_params, output); + if (wrapped_operation_.GetError(super::ecdsa_key_) != KM_ERROR_OK) + error = wrapped_operation_.GetError(super::ecdsa_key_); + if (error == KM_ERROR_OK) + wrapped_operation_.Finish(); + return error; + } + + keymaster_error_t Abort() override { + keymaster_error_t error = wrapped_operation_.Abort(); + if (error != KM_ERROR_OK) + return error; + return super::Abort(); + } + + private: + EcdsaKeymaster1WrappedOperation wrapped_operation_; +}; + +/** + * Factory that produces EcdsaKeymaster1Operations. This is instantiated and + * provided by EcdsaKeymaster1KeyFactory. + */ +class EcdsaKeymaster1OperationFactory : public OperationFactory { + public: + EcdsaKeymaster1OperationFactory(keymaster_purpose_t purpose, const Keymaster1Engine* engine) + : purpose_(purpose), engine_(engine) {} + KeyType registry_key() const override { return KeyType(KM_ALGORITHM_EC, purpose_); } + + Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params, + keymaster_error_t* error) override; + + const keymaster_digest_t* SupportedDigests(size_t* digest_count) const override; + const keymaster_padding_t* SupportedPaddingModes(size_t* padding_mode_count) const override; + + private: + keymaster_purpose_t purpose_; + const Keymaster1Engine* engine_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ECDSA_KEYMASTER1_OPERATION_H_
diff --git a/keymaster/ecdsa_operation.cpp b/keymaster/ecdsa_operation.cpp new file mode 100644 index 0000000..405dcb5 --- /dev/null +++ b/keymaster/ecdsa_operation.cpp
@@ -0,0 +1,230 @@ +/* + * Copyright 2014 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 "ecdsa_operation.h" + +#include <openssl/ecdsa.h> + +#include "ec_key.h" +#include "openssl_err.h" +#include "openssl_utils.h" + +namespace keymaster { + +static const keymaster_digest_t supported_digests[] = {KM_DIGEST_NONE, KM_DIGEST_SHA1, + KM_DIGEST_SHA_2_224, KM_DIGEST_SHA_2_256, + KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; + +Operation* EcdsaOperationFactory::CreateOperation(const Key& key, + const AuthorizationSet& begin_params, + keymaster_error_t* error) { + const EcKey* ecdsa_key = static_cast<const EcKey*>(&key); + if (!ecdsa_key) { + *error = KM_ERROR_UNKNOWN_ERROR; + return nullptr; + } + + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new()); + if (!ecdsa_key->InternalToEvp(pkey.get())) { + *error = KM_ERROR_UNKNOWN_ERROR; + return nullptr; + } + + keymaster_digest_t digest; + if (!GetAndValidateDigest(begin_params, key, &digest, error)) + return nullptr; + + *error = KM_ERROR_OK; + Operation* op = InstantiateOperation(digest, pkey.release()); + if (!op) + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return op; +} + +const keymaster_digest_t* EcdsaOperationFactory::SupportedDigests(size_t* digest_count) const { + *digest_count = array_length(supported_digests); + return supported_digests; +} + +EcdsaOperation::~EcdsaOperation() { + if (ecdsa_key_ != NULL) + EVP_PKEY_free(ecdsa_key_); + EVP_MD_CTX_cleanup(&digest_ctx_); +} + +keymaster_error_t EcdsaOperation::InitDigest() { + switch (digest_) { + case KM_DIGEST_NONE: + return KM_ERROR_OK; + case KM_DIGEST_MD5: + return KM_ERROR_UNSUPPORTED_DIGEST; + case KM_DIGEST_SHA1: + digest_algorithm_ = EVP_sha1(); + return KM_ERROR_OK; + case KM_DIGEST_SHA_2_224: + digest_algorithm_ = EVP_sha224(); + return KM_ERROR_OK; + case KM_DIGEST_SHA_2_256: + digest_algorithm_ = EVP_sha256(); + return KM_ERROR_OK; + case KM_DIGEST_SHA_2_384: + digest_algorithm_ = EVP_sha384(); + return KM_ERROR_OK; + case KM_DIGEST_SHA_2_512: + digest_algorithm_ = EVP_sha512(); + return KM_ERROR_OK; + default: + return KM_ERROR_UNSUPPORTED_DIGEST; + } +} + +inline size_t min(size_t a, size_t b) { + return (a < b) ? a : b; +} + +keymaster_error_t EcdsaOperation::StoreData(const Buffer& input, size_t* input_consumed) { + if (!data_.reserve((EVP_PKEY_bits(ecdsa_key_) + 7) / 8)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (!data_.write(input.peek_read(), min(data_.available_write(), input.available_read()))) + return KM_ERROR_UNKNOWN_ERROR; + + *input_consumed = input.available_read(); + return KM_ERROR_OK; +} + +keymaster_error_t EcdsaSignOperation::Begin(const AuthorizationSet& /* input_params */, + AuthorizationSet* /* output_params */) { + keymaster_error_t error = InitDigest(); + if (error != KM_ERROR_OK) + return error; + + if (digest_ == KM_DIGEST_NONE) + return KM_ERROR_OK; + + EVP_PKEY_CTX* pkey_ctx; + if (EVP_DigestSignInit(&digest_ctx_, &pkey_ctx, digest_algorithm_, nullptr /* engine */, + ecdsa_key_) != 1) + return TranslateLastOpenSslError(); + return KM_ERROR_OK; +} + +keymaster_error_t EcdsaSignOperation::Update(const AuthorizationSet& /* additional_params */, + const Buffer& input, + AuthorizationSet* /* output_params */, + Buffer* /* output */, size_t* input_consumed) { + if (digest_ == KM_DIGEST_NONE) + return StoreData(input, input_consumed); + + if (EVP_DigestSignUpdate(&digest_ctx_, input.peek_read(), input.available_read()) != 1) + return TranslateLastOpenSslError(); + *input_consumed = input.available_read(); + return KM_ERROR_OK; +} + +keymaster_error_t EcdsaSignOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& /* signature */, + AuthorizationSet* /* output_params */, + Buffer* output) { + if (!output) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + keymaster_error_t error = UpdateForFinish(additional_params, input); + if (error != KM_ERROR_OK) + return error; + + size_t siglen; + if (digest_ == KM_DIGEST_NONE) { + UniquePtr<EC_KEY, EC_KEY_Delete> ecdsa(EVP_PKEY_get1_EC_KEY(ecdsa_key_)); + if (!ecdsa.get()) + return TranslateLastOpenSslError(); + + output->Reinitialize(ECDSA_size(ecdsa.get())); + unsigned int siglen_tmp; + if (!ECDSA_sign(0 /* type -- ignored */, data_.peek_read(), data_.available_read(), + output->peek_write(), &siglen_tmp, ecdsa.get())) + return TranslateLastOpenSslError(); + siglen = siglen_tmp; + } else { + if (EVP_DigestSignFinal(&digest_ctx_, nullptr /* signature */, &siglen) != 1) + return TranslateLastOpenSslError(); + if (!output->Reinitialize(siglen)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + if (EVP_DigestSignFinal(&digest_ctx_, output->peek_write(), &siglen) <= 0) + return TranslateLastOpenSslError(); + } + if (!output->advance_write(siglen)) + return KM_ERROR_UNKNOWN_ERROR; + return KM_ERROR_OK; +} + +keymaster_error_t EcdsaVerifyOperation::Begin(const AuthorizationSet& /* input_params */, + AuthorizationSet* /* output_params */) { + keymaster_error_t error = InitDigest(); + if (error != KM_ERROR_OK) + return error; + + if (digest_ == KM_DIGEST_NONE) + return KM_ERROR_OK; + + EVP_PKEY_CTX* pkey_ctx; + if (EVP_DigestVerifyInit(&digest_ctx_, &pkey_ctx, digest_algorithm_, nullptr /* engine */, + ecdsa_key_) != 1) + return TranslateLastOpenSslError(); + return KM_ERROR_OK; +} + +keymaster_error_t EcdsaVerifyOperation::Update(const AuthorizationSet& /* additional_params */, + const Buffer& input, + AuthorizationSet* /* output_params */, + Buffer* /* output */, size_t* input_consumed) { + if (digest_ == KM_DIGEST_NONE) + return StoreData(input, input_consumed); + + if (EVP_DigestVerifyUpdate(&digest_ctx_, input.peek_read(), input.available_read()) != 1) + return TranslateLastOpenSslError(); + *input_consumed = input.available_read(); + return KM_ERROR_OK; +} + +keymaster_error_t EcdsaVerifyOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& signature, + AuthorizationSet* /* output_params */, + Buffer* /* output */) { + keymaster_error_t error = UpdateForFinish(additional_params, input); + if (error != KM_ERROR_OK) + return error; + + if (digest_ == KM_DIGEST_NONE) { + UniquePtr<EC_KEY, EC_KEY_Delete> ecdsa(EVP_PKEY_get1_EC_KEY(ecdsa_key_)); + if (!ecdsa.get()) + return TranslateLastOpenSslError(); + + int result = + ECDSA_verify(0 /* type -- ignored */, data_.peek_read(), data_.available_read(), + signature.peek_read(), signature.available_read(), ecdsa.get()); + if (result < 0) + return TranslateLastOpenSslError(); + else if (result == 0) + return KM_ERROR_VERIFICATION_FAILED; + } else if (!EVP_DigestVerifyFinal(&digest_ctx_, signature.peek_read(), + signature.available_read())) + return KM_ERROR_VERIFICATION_FAILED; + + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/ecdsa_operation.h b/keymaster/ecdsa_operation.h new file mode 100644 index 0000000..4b95dc9 --- /dev/null +++ b/keymaster/ecdsa_operation.h
@@ -0,0 +1,107 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_ECDSA_OPERATION_H_ +#define SYSTEM_KEYMASTER_ECDSA_OPERATION_H_ + +#include <openssl/ec.h> +#include <openssl/evp.h> + +#include <UniquePtr.h> + +#include "operation.h" + +namespace keymaster { + +class EcdsaOperation : public Operation { + public: + EcdsaOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, EVP_PKEY* key) + : Operation(purpose), digest_(digest), ecdsa_key_(key) { + EVP_MD_CTX_init(&digest_ctx_); + } + ~EcdsaOperation(); + + keymaster_error_t Abort() override { return KM_ERROR_OK; } + + protected: + keymaster_error_t StoreData(const Buffer& input, size_t* input_consumed); + keymaster_error_t InitDigest(); + + keymaster_digest_t digest_; + const EVP_MD* digest_algorithm_; + EVP_PKEY* ecdsa_key_; + EVP_MD_CTX digest_ctx_; + Buffer data_; +}; + +class EcdsaSignOperation : public EcdsaOperation { + public: + EcdsaSignOperation(keymaster_digest_t digest, EVP_PKEY* key) + : EcdsaOperation(KM_PURPOSE_SIGN, digest, key) {} + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override; + keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, + size_t* input_consumed) override; + keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override; +}; + +class EcdsaVerifyOperation : public EcdsaOperation { + public: + EcdsaVerifyOperation(keymaster_digest_t digest, EVP_PKEY* key) + : EcdsaOperation(KM_PURPOSE_VERIFY, digest, key) {} + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override; + keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, + size_t* input_consumed) override; + keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override; +}; + +class EcdsaOperationFactory : public OperationFactory { + private: + KeyType registry_key() const override { return KeyType(KM_ALGORITHM_EC, purpose()); } + Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params, + keymaster_error_t* error) override; + const keymaster_digest_t* SupportedDigests(size_t* digest_count) const override; + + virtual keymaster_purpose_t purpose() const = 0; + virtual Operation* InstantiateOperation(keymaster_digest_t digest, EVP_PKEY* key) = 0; +}; + +class EcdsaSignOperationFactory : public EcdsaOperationFactory { + private: + keymaster_purpose_t purpose() const override { return KM_PURPOSE_SIGN; } + Operation* InstantiateOperation(keymaster_digest_t digest, EVP_PKEY* key) { + return new (std::nothrow) EcdsaSignOperation(digest, key); + } +}; + +class EcdsaVerifyOperationFactory : public EcdsaOperationFactory { + public: + keymaster_purpose_t purpose() const override { return KM_PURPOSE_VERIFY; } + Operation* InstantiateOperation(keymaster_digest_t digest, EVP_PKEY* key) { + return new (std::nothrow) EcdsaVerifyOperation(digest, key); + } +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ECDSA_OPERATION_H_
diff --git a/keymaster/ecies_kem.cpp b/keymaster/ecies_kem.cpp new file mode 100644 index 0000000..2e31573 --- /dev/null +++ b/keymaster/ecies_kem.cpp
@@ -0,0 +1,185 @@ +/* + * 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 "ecies_kem.h" + +#include "nist_curve_key_exchange.h" +#include "openssl_err.h" + +namespace keymaster { + +EciesKem::EciesKem(const AuthorizationSet& kem_description, keymaster_error_t* error) { + const AuthorizationSet& authorizations(kem_description); + + if (!authorizations.GetTagValue(TAG_EC_CURVE, &curve_)) { + LOG_E("%s", "EciesKem: no curve specified"); + *error = KM_ERROR_INVALID_ARGUMENT; + return; + } + + switch (curve_) { + case KM_EC_CURVE_P_224: + case KM_EC_CURVE_P_256: + case KM_EC_CURVE_P_384: + case KM_EC_CURVE_P_521: + break; + default: + LOG_E("EciesKem: curve %d is unsupported", curve_); + *error = KM_ERROR_UNSUPPORTED_EC_CURVE; + return; + } + + keymaster_kdf_t kdf; + if (!authorizations.GetTagValue(TAG_KDF, &kdf)) { + LOG_E("EciesKem: No KDF specified", 0); + *error = KM_ERROR_UNSUPPORTED_KDF; + return; + } + switch (kdf) { + case KM_KDF_RFC5869_SHA256: + kdf_.reset(new Rfc5869Sha256Kdf()); + break; + default: + LOG_E("Kdf %d is unsupported", kdf); + *error = KM_ERROR_UNSUPPORTED_KDF; + return; + } + if (!kdf_.get()) { + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return; + } + + if (!authorizations.GetTagValue(TAG_KEY_SIZE, &key_bytes_to_generate_)) { + LOG_E("%s", "EciesKem: no key length specified"); + *error = KM_ERROR_UNSUPPORTED_KEY_SIZE; + return; + } + + single_hash_mode_ = authorizations.GetTagValue(TAG_ECIES_SINGLE_HASH_MODE); + *error = KM_ERROR_OK; +} + +bool EciesKem::Encrypt(const Buffer& peer_public_value, Buffer* output_clear_key, + Buffer* output_encrypted_key) { + return Encrypt(peer_public_value.peek_read(), peer_public_value.available_read(), + output_clear_key, output_encrypted_key); +} + +// http://www.shoup.net/iso/std6.pdf, section 10.2.3. +bool EciesKem::Encrypt(const uint8_t* peer_public_value, size_t peer_public_value_len, + Buffer* output_clear_key, Buffer* output_encrypted_key) { + + key_exchange_.reset(NistCurveKeyExchange::GenerateKeyExchange(curve_)); + if (!key_exchange_.get()) { + return false; + } + + Buffer shared_secret; + if (!key_exchange_->CalculateSharedKey(peer_public_value, peer_public_value_len, + &shared_secret)) { + LOG_E("EciesKem: ECDH failed, can't obtain shared secret", 0); + return false; + } + if (!key_exchange_->public_value(output_encrypted_key)) { + LOG_E("EciesKem: Can't obtain public value", 0); + return false; + } + + Buffer z; + if (single_hash_mode_) { + // z is empty. + } else { + // z = C0 + z.Reinitialize(output_encrypted_key->peek_read(), output_encrypted_key->available_read()); + } + + Buffer actual_secret(z.available_read() + shared_secret.available_read()); + actual_secret.write(z.peek_read(), z.available_read()); + actual_secret.write(shared_secret.peek_read(), shared_secret.available_read()); + + if (!kdf_->Init(actual_secret.peek_read(), actual_secret.available_read(), nullptr /* salt */, + 0 /* salt_len */)) { + LOG_E("EciesKem: KDF failed, can't derived keys", 0); + return false; + } + output_clear_key->Reinitialize(key_bytes_to_generate_); + if (!kdf_->GenerateKey(nullptr /* info */, 0 /* info length */, output_clear_key->peek_write(), + key_bytes_to_generate_)) { + LOG_E("EciesKem: KDF failed, can't derived keys", 0); + return false; + } + output_clear_key->advance_write(key_bytes_to_generate_); + + return true; +} + +bool EciesKem::Decrypt(EC_KEY* private_key, const Buffer& encrypted_key, Buffer* output_key) { + return Decrypt(private_key, encrypted_key.peek_read(), encrypted_key.available_read(), + output_key); +} + +// http://www.shoup.net/iso/std6.pdf, section 10.2.4. +bool EciesKem::Decrypt(EC_KEY* private_key, const uint8_t* encrypted_key, size_t encrypted_key_len, + Buffer* output_key) { + + keymaster_error_t error; + key_exchange_.reset(new NistCurveKeyExchange(private_key, &error)); + if (!key_exchange_.get() || error != KM_ERROR_OK) { + return false; + } + + Buffer shared_secret; + if (!key_exchange_->CalculateSharedKey(encrypted_key, encrypted_key_len, &shared_secret)) { + LOG_E("EciesKem: ECDH failed, can't obtain shared secret", 0); + return false; + } + + Buffer public_value; + if (!key_exchange_->public_value(&public_value)) { + LOG_E("%s", "EciesKem: Can't obtain public value"); + return false; + } + + Buffer z; + if (single_hash_mode_) { + // z is empty. + } else { + // z = C0 + z.Reinitialize(public_value.peek_read(), public_value.available_read()); + } + + Buffer actual_secret(z.available_read() + shared_secret.available_read()); + actual_secret.write(z.peek_read(), z.available_read()); + actual_secret.write(shared_secret.peek_read(), shared_secret.available_read()); + + if (!kdf_->Init(actual_secret.peek_read(), actual_secret.available_read(), nullptr /* salt */, + 0 /* salt_len */)) { + LOG_E("%s", "EciesKem: KDF failed, can't derived keys"); + return false; + } + + output_key->Reinitialize(key_bytes_to_generate_); + if (!kdf_->GenerateKey(nullptr /* info */, 0 /* info_len */, output_key->peek_write(), + key_bytes_to_generate_)) { + LOG_E("%s", "EciesKem: KDF failed, can't derived keys"); + return false; + } + output_key->advance_write(key_bytes_to_generate_); + + return true; +} + +} // namespace keymaster
diff --git a/keymaster/ecies_kem.h b/keymaster/ecies_kem.h new file mode 100644 index 0000000..8c1b330 --- /dev/null +++ b/keymaster/ecies_kem.h
@@ -0,0 +1,61 @@ +/* + * 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 SYSTEM_KEYMASTER_ECIES_KEM_H_ +#define SYSTEM_KEYMASTER_ECIES_KEM_H_ + +#include "kem.h" + +#include <UniquePtr.h> +#include <openssl/ec.h> + +#include <keymaster/authorization_set.h> + +#include "hkdf.h" +#include "key_exchange.h" + +namespace keymaster { + +/** + * EciesKem is an implementation of the key encapsulation mechanism ECIES-KEM described in + * ISO 18033-2 (http://www.shoup.net/iso/std6.pdf, http://www.shoup.net/papers/iso-2_1.pdf). + */ +class EciesKem : public Kem { + public: + virtual ~EciesKem() override {} + EciesKem(const AuthorizationSet& kem_description, keymaster_error_t* error); + + /* Kem interface. */ + bool Encrypt(const Buffer& peer_public_value, Buffer* output_clear_key, + Buffer* output_encrypted_key) override; + bool Encrypt(const uint8_t* peer_public_value, size_t peer_public_value_len, + Buffer* output_clear_key, Buffer* output_encrypted_key) override; + + bool Decrypt(EC_KEY* private_key, const Buffer& encrypted_key, Buffer* output_key) override; + bool Decrypt(EC_KEY* private_key, const uint8_t* encrypted_key, size_t encrypted_key_len, + Buffer* output_key) override; + + private: + UniquePtr<KeyExchange> key_exchange_; + UniquePtr<Rfc5869Sha256Kdf> kdf_; + bool single_hash_mode_; + uint32_t key_bytes_to_generate_; + keymaster_ec_curve_t curve_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ECIES_KEM_H_
diff --git a/keymaster/ecies_kem_test.cpp b/keymaster/ecies_kem_test.cpp new file mode 100644 index 0000000..f653dc0 --- /dev/null +++ b/keymaster/ecies_kem_test.cpp
@@ -0,0 +1,73 @@ +/* + * 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 "ecies_kem.h" + +#include <gtest/gtest.h> +#include <openssl/evp.h> + +#include <hardware/keymaster_defs.h> +#include <keymaster/android_keymaster_utils.h> + +#include "android_keymaster_test_utils.h" +#include "nist_curve_key_exchange.h" + +using std::string; + +namespace keymaster { +namespace test { + +StdoutLogger logger; + +static const keymaster_ec_curve_t kEcCurves[] = {KM_EC_CURVE_P_224, KM_EC_CURVE_P_256, + KM_EC_CURVE_P_384, KM_EC_CURVE_P_521}; + +/** + * TestConsistency just tests that the basic key encapsulation hold. + */ +TEST(EciesKem, TestConsistency) { + static const uint32_t kKeyLen = 32; + for (auto& curve : kEcCurves) { + AuthorizationSet kem_description(AuthorizationSetBuilder() + .Authorization(TAG_EC_CURVE, curve) + .Authorization(TAG_KDF, KM_KDF_RFC5869_SHA256) + .Authorization(TAG_ECIES_SINGLE_HASH_MODE) + .Authorization(TAG_KEY_SIZE, kKeyLen)); + keymaster_error_t error; + EciesKem* kem = new EciesKem(kem_description, &error); + ASSERT_EQ(KM_ERROR_OK, error); + + NistCurveKeyExchange* key_exchange = NistCurveKeyExchange::GenerateKeyExchange(curve); + Buffer peer_public_value; + ASSERT_TRUE(key_exchange->public_value(&peer_public_value)); + + Buffer output_clear_key; + Buffer output_encrypted_key; + ASSERT_TRUE(kem->Encrypt(peer_public_value, &output_clear_key, &output_encrypted_key)); + ASSERT_EQ(kKeyLen, output_clear_key.available_read()); + ASSERT_EQ(peer_public_value.available_read(), output_encrypted_key.available_read()); + + Buffer decrypted_clear_key; + ASSERT_TRUE( + kem->Decrypt(key_exchange->private_key(), output_encrypted_key, &decrypted_clear_key)); + ASSERT_EQ(kKeyLen, decrypted_clear_key.available_read()); + EXPECT_EQ(0, memcmp(output_clear_key.peek_read(), decrypted_clear_key.peek_read(), + output_clear_key.available_read())); + } +} + +} // namespace test +} // namespace keymaster
diff --git a/keymaster/gtest_main.cpp b/keymaster/gtest_main.cpp new file mode 100644 index 0000000..6072749 --- /dev/null +++ b/keymaster/gtest_main.cpp
@@ -0,0 +1,34 @@ +/* + * 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 <openssl/engine.h> + +int main(int argc, char** argv) { +#if !defined(OPENSSL_IS_BORINGSSL) + ERR_load_crypto_strings(); +#endif // not OPENSSL_IS_BORINGSSL + ::testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); +#if !defined(OPENSSL_IS_BORINGSSL) + // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain. + CRYPTO_cleanup_all_ex_data(); + ERR_remove_thread_state(NULL); + ERR_free_strings(); +#endif // not OPENSSL_IS_BORINGSSL + return result; +}
diff --git a/keymaster/hkdf.cpp b/keymaster/hkdf.cpp new file mode 100644 index 0000000..9727375 --- /dev/null +++ b/keymaster/hkdf.cpp
@@ -0,0 +1,96 @@ +/* + * 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 "hkdf.h" + +#include <new> + +#include <keymaster/android_keymaster_utils.h> + +#include "hmac.h" + +namespace keymaster { + +bool Rfc5869Sha256Kdf::GenerateKey(const uint8_t* info, size_t info_len, uint8_t* output, + size_t output_len) { + if (!is_initialized_ || output == nullptr) + return false; + /** + * Step 1. Extract: PRK = HMAC-SHA256(actual_salt, secret) + * https://tools.ietf.org/html/rfc5869#section-2.2 + */ + HmacSha256 prk_hmac; + bool result; + if (salt_.get() != nullptr && salt_len_ > 0) { + result = prk_hmac.Init(salt_.get(), salt_len_); + } else { + UniquePtr<uint8_t[]> zeros(new uint8_t[digest_size_]); + if (zeros.get() == nullptr) + return false; + /* If salt is not given, digest size of zeros are used. */ + memset(zeros.get(), 0, digest_size_); + result = prk_hmac.Init(zeros.get(), digest_size_); + } + if (!result) + return false; + + UniquePtr<uint8_t[]> pseudo_random_key(new uint8_t[digest_size_]); + if (pseudo_random_key.get() == nullptr || digest_size_ != prk_hmac.DigestLength()) + return false; + result = + prk_hmac.Sign(secret_key_.get(), secret_key_len_, pseudo_random_key.get(), digest_size_); + if (!result) + return false; + + /** + * Step 2. Expand: OUTPUT = HKDF-Expand(PRK, info) + * https://tools.ietf.org/html/rfc5869#section-2.3 + */ + const size_t num_blocks = (output_len + digest_size_ - 1) / digest_size_; + if (num_blocks >= 256u) + return false; + + UniquePtr<uint8_t[]> buf(new uint8_t[digest_size_ + info_len + 1]); + UniquePtr<uint8_t[]> digest(new uint8_t[digest_size_]); + if (buf.get() == nullptr || digest.get() == nullptr) + return false; + HmacSha256 hmac; + result = hmac.Init(pseudo_random_key.get(), digest_size_); + if (!result) + return false; + + for (size_t i = 0; i < num_blocks; i++) { + size_t block_input_len = 0; + if (i != 0) { + memcpy(buf.get(), digest.get(), digest_size_); + block_input_len = digest_size_; + } + if (info != nullptr && info_len > 0) + memcpy(buf.get() + block_input_len, info, info_len); + block_input_len += info_len; + *(buf.get() + block_input_len++) = static_cast<uint8_t>(i + 1); + result = hmac.Sign(buf.get(), block_input_len, digest.get(), digest_size_); + if (!result) + return false; + size_t block_output_len = digest_size_ < output_len - i * digest_size_ + ? digest_size_ + : output_len - i * digest_size_; + memcpy(output + i * digest_size_, digest.get(), block_output_len); + } + return true; +} + +} // namespace keymaster
diff --git a/keymaster/hkdf.h b/keymaster/hkdf.h new file mode 100644 index 0000000..9de39bf --- /dev/null +++ b/keymaster/hkdf.h
@@ -0,0 +1,50 @@ +/* + * 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 SYSTEM_KEYMASTER_HKDF_H_ +#define SYSTEM_KEYMASTER_HKDF_H_ + +#include "kdf.h" + +#include <keymaster/serializable.h> + +#include <UniquePtr.h> + +namespace keymaster { + +/** + * Rfc5869Sha256Kdf implements the key derivation function specified in RFC 5869 (using SHA256) and + * outputs key material, as needed by ECIES. See https://tools.ietf.org/html/rfc5869 for details. + */ +class Rfc5869Sha256Kdf : public Kdf { + public: + ~Rfc5869Sha256Kdf() {} + bool Init(Buffer& secret, Buffer& salt) { + return Init(secret.peek_read(), secret.available_read(), salt.peek_read(), + salt.available_read()); + } + + bool Init(const uint8_t* secret, size_t secret_len, const uint8_t* salt, size_t salt_len) { + return Kdf::Init(KM_DIGEST_SHA_2_256, secret, secret_len, salt, salt_len); + } + + bool GenerateKey(const uint8_t* info, size_t info_len, uint8_t* output, + size_t output_len) override; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_HKDF_H_
diff --git a/keymaster/hkdf_test.cpp b/keymaster/hkdf_test.cpp new file mode 100644 index 0000000..3d3f092 --- /dev/null +++ b/keymaster/hkdf_test.cpp
@@ -0,0 +1,78 @@ +/* + * 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 "hkdf.h" + +#include <gtest/gtest.h> +#include <string.h> + +#include "android_keymaster_test_utils.h" + +using std::string; + +namespace keymaster { +namespace test { + +struct HkdfTest { + const char* key_hex; + const char* salt_hex; + const char* info_hex; + const char* output_hex; +}; + +// These test cases are taken from +// https://tools.ietf.org/html/rfc5869#appendix-A. +static const HkdfTest kHkdfTests[] = { + { + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c" + "2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f", + "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c" + "8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf", + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdc" + "dddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e" + "590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87", + }, + { + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "", "", + "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8", + }, +}; + +TEST(HkdfTest, Hkdf) { + for (auto& test : kHkdfTests) { + const string key = hex2str(test.key_hex); + const string salt = hex2str(test.salt_hex); + const string info = hex2str(test.info_hex); + const string expected = hex2str(test.output_hex); + size_t output_len = expected.size(); + uint8_t output[output_len]; + Rfc5869Sha256Kdf hkdf; + ASSERT_TRUE(hkdf.Init(reinterpret_cast<const uint8_t*>(key.data()), key.size(), + reinterpret_cast<const uint8_t*>(salt.data()), salt.size())); + ASSERT_TRUE(hkdf.GenerateKey(reinterpret_cast<const uint8_t*>(info.data()), info.size(), + output, output_len)); + EXPECT_EQ(0, memcmp(output, expected.data(), output_len)); + } +} + +} // namespace test +} // namespace keymaster
diff --git a/keymaster/hmac.cpp b/keymaster/hmac.cpp new file mode 100644 index 0000000..02f739c --- /dev/null +++ b/keymaster/hmac.cpp
@@ -0,0 +1,89 @@ +/* + * 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 "hmac.h" + +#include <assert.h> + +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/mem.h> +#include <openssl/sha.h> + +#include <keymaster/android_keymaster_utils.h> + +namespace keymaster { + +size_t HmacSha256::DigestLength() const { + return SHA256_DIGEST_LENGTH; +} + +bool HmacSha256::Init(const Buffer& key) { + return Init(key.peek_read(), key.available_read()); +} + +bool HmacSha256::Init(const uint8_t* key, size_t key_len) { + if (!key) + return false; + + key_len_ = key_len; + key_.reset(dup_buffer(key, key_len)); + if (!key_.get()) { + return false; + } + return true; +} + +bool HmacSha256::Sign(const Buffer& data, uint8_t* out_digest, size_t digest_len) const { + return Sign(data.peek_read(), data.available_read(), out_digest, digest_len); +} + +bool HmacSha256::Sign(const uint8_t* data, size_t data_len, uint8_t* out_digest, + size_t digest_len) const { + assert(digest_len); + + uint8_t tmp[SHA256_DIGEST_LENGTH]; + uint8_t* digest = tmp; + if (digest_len >= SHA256_DIGEST_LENGTH) + digest = out_digest; + + if (nullptr == ::HMAC(EVP_sha256(), key_.get(), key_len_, data, data_len, digest, nullptr)) { + return false; + } + if (digest_len < SHA256_DIGEST_LENGTH) + memcpy(out_digest, tmp, digest_len); + + return true; +} + +bool HmacSha256::Verify(const Buffer& data, const Buffer& digest) const { + return Verify(data.peek_read(), data.available_read(), digest.peek_read(), + digest.available_read()); +} + +bool HmacSha256::Verify(const uint8_t* data, size_t data_len, const uint8_t* digest, + size_t digest_len) const { + if (digest_len != SHA256_DIGEST_LENGTH) + return false; + + uint8_t computed_digest[SHA256_DIGEST_LENGTH]; + if (!Sign(data, data_len, computed_digest, sizeof(computed_digest))) + return false; + + return 0 == CRYPTO_memcmp(digest, computed_digest, SHA256_DIGEST_LENGTH); +} + +} // namespace keymaster
diff --git a/keymaster/hmac.h b/keymaster/hmac.h new file mode 100644 index 0000000..ebd5b70 --- /dev/null +++ b/keymaster/hmac.h
@@ -0,0 +1,60 @@ +/* + * 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 SYSTEM_KEYMASTER_HMAC_H_ +#define SYSTEM_KEYMASTER_HMAC_H_ + +#include <keymaster/serializable.h> + +namespace keymaster { + +// Only HMAC-SHA256 is supported. +class HmacSha256 { + public: + HmacSha256(){}; + + // DigestLength returns the length, in bytes, of the resulting digest. + size_t DigestLength() const; + + // Initializes this instance using |key|. Call Init only once. It returns + // false on the second of later calls. + bool Init(const uint8_t* key, size_t key_length); + bool Init(const Buffer& key); + + // Sign calculates the HMAC of |data| with the key supplied to the Init + // method. At most |digest_len| bytes of the resulting digest are written + // to |digest|. + bool Sign(const Buffer& data, uint8_t* digest, size_t digest_len) const; + bool Sign(const uint8_t* data, size_t data_len, uint8_t* digest, size_t digest_len) const; + + // Verify returns true if |digest| is a valid HMAC of |data| using the key + // supplied to Init. |digest| must be exactly |DigestLength()| bytes long. + // Use of this method is strongly recommended over using Sign() with a manual + // comparison (such as memcmp), as such comparisons may result in + // side-channel disclosures, such as timing, that undermine the cryptographic + // integrity. + bool Verify(const Buffer& data, const Buffer& digest) const; + bool Verify(const uint8_t* data, size_t data_len, const uint8_t* digest, + size_t digest_len) const; + + private: + UniquePtr<uint8_t[]> key_; + size_t key_len_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_HMAC_H_
diff --git a/keymaster/hmac_key.cpp b/keymaster/hmac_key.cpp new file mode 100644 index 0000000..40a8906 --- /dev/null +++ b/keymaster/hmac_key.cpp
@@ -0,0 +1,114 @@ +/* + * Copyright 2014 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 "hmac_key.h" + +#include <new> + +#include <openssl/err.h> +#include <openssl/rand.h> + +#include "hmac_operation.h" + +namespace keymaster { + +static HmacSignOperationFactory sign_factory; +static HmacVerifyOperationFactory verify_factory; + +OperationFactory* HmacKeyFactory::GetOperationFactory(keymaster_purpose_t purpose) const { + switch (purpose) { + case KM_PURPOSE_SIGN: + return &sign_factory; + case KM_PURPOSE_VERIFY: + return &verify_factory; + default: + return nullptr; + } +} + +keymaster_error_t HmacKeyFactory::LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& /* additional_params */, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const { + if (!key) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + uint32_t min_mac_length; + if (!hw_enforced.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length) && + !sw_enforced.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length)) { + LOG_E("HMAC key must have KM_TAG_MIN_MAC_LENGTH", 0); + return KM_ERROR_INVALID_KEY_BLOB; + } + + keymaster_error_t error; + key->reset(new (std::nothrow) HmacKey(key_material, hw_enforced, sw_enforced, &error)); + if (!key->get()) + error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return error; +} + +keymaster_error_t HmacKeyFactory::validate_algorithm_specific_new_key_params( + const AuthorizationSet& key_description) const { + uint32_t min_mac_length_bits; + if (!key_description.GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length_bits)) + return KM_ERROR_MISSING_MIN_MAC_LENGTH; + + keymaster_digest_t digest; + if (!key_description.GetTagValue(TAG_DIGEST, &digest)) { + LOG_E("%d digests specified for HMAC key", key_description.GetTagCount(TAG_DIGEST)); + return KM_ERROR_UNSUPPORTED_DIGEST; + } + + size_t hash_size_bits = 0; + switch (digest) { + case KM_DIGEST_NONE: + return KM_ERROR_UNSUPPORTED_DIGEST; + case KM_DIGEST_MD5: + hash_size_bits = 128; + break; + case KM_DIGEST_SHA1: + hash_size_bits = 160; + break; + case KM_DIGEST_SHA_2_224: + hash_size_bits = 224; + break; + case KM_DIGEST_SHA_2_256: + hash_size_bits = 256; + break; + case KM_DIGEST_SHA_2_384: + hash_size_bits = 384; + break; + case KM_DIGEST_SHA_2_512: + hash_size_bits = 512; + break; + }; + + if (hash_size_bits == 0) { + // digest was not matched + return KM_ERROR_UNSUPPORTED_DIGEST; + } + + if (min_mac_length_bits % 8 != 0 || min_mac_length_bits > hash_size_bits) + return KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH; + + if (min_mac_length_bits < kMinHmacLengthBits) + return KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH; + + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/hmac_key.h b/keymaster/hmac_key.h new file mode 100644 index 0000000..c05e6a3 --- /dev/null +++ b/keymaster/hmac_key.h
@@ -0,0 +1,56 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_HMAC_KEY_H_ +#define SYSTEM_KEYMASTER_HMAC_KEY_H_ + +#include "symmetric_key.h" + +namespace keymaster { + +const size_t kMinHmacLengthBits = 64; + +class HmacKeyFactory : public SymmetricKeyFactory { + public: + explicit HmacKeyFactory(const KeymasterContext* context) : SymmetricKeyFactory(context) {} + + keymaster_error_t LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const override; + + OperationFactory* GetOperationFactory(keymaster_purpose_t purpose) const override; + + private: + bool key_size_supported(size_t key_size_bits) const override { + return key_size_bits > 0 && key_size_bits % 8 == 00 && + key_size_bits <= 2048 /* Some RFC test cases require >1024-bit keys */; + } + keymaster_error_t validate_algorithm_specific_new_key_params( + const AuthorizationSet& key_description) const override; +}; + +class HmacKey : public SymmetricKey { + public: + HmacKey(const KeymasterKeyBlob& key_material, const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, keymaster_error_t* error) + : SymmetricKey(key_material, hw_enforced, sw_enforced, error) {} +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_HMAC_KEY_H_
diff --git a/keymaster/hmac_operation.cpp b/keymaster/hmac_operation.cpp new file mode 100644 index 0000000..7f21393 --- /dev/null +++ b/keymaster/hmac_operation.cpp
@@ -0,0 +1,197 @@ +/* + * Copyright 2014 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 "hmac_operation.h" + +#include <new> + +#include <openssl/evp.h> +#include <openssl/hmac.h> + +#include "hmac_key.h" +#include "openssl_err.h" + +#if defined(OPENSSL_IS_BORINGSSL) +#include <openssl/mem.h> +typedef size_t openssl_size_t; +#else +typedef int openssl_size_t; +#endif + +namespace keymaster { + +Operation* HmacOperationFactory::CreateOperation(const Key& key, + const AuthorizationSet& begin_params, + keymaster_error_t* error) { + uint32_t min_mac_length_bits; + if (!key.authorizations().GetTagValue(TAG_MIN_MAC_LENGTH, &min_mac_length_bits)) { + LOG_E("HMAC key must have KM_TAG_MIN_MAC_LENGTH", 0); + *error = KM_ERROR_INVALID_KEY_BLOB; + return nullptr; + } + + uint32_t mac_length_bits = UINT32_MAX; + if (begin_params.GetTagValue(TAG_MAC_LENGTH, &mac_length_bits)) { + if (purpose() == KM_PURPOSE_VERIFY) { + LOG_E("MAC length may not be specified for verify", 0); + *error = KM_ERROR_INVALID_ARGUMENT; + return nullptr; + } + } else { + if (purpose() == KM_PURPOSE_SIGN) { + *error = KM_ERROR_MISSING_MAC_LENGTH; + return nullptr; + } + } + + keymaster_digest_t digest; + if (!key.authorizations().GetTagValue(TAG_DIGEST, &digest)) { + LOG_E("%d digests found in HMAC key authorizations; must be exactly 1", + begin_params.GetTagCount(TAG_DIGEST)); + *error = KM_ERROR_INVALID_KEY_BLOB; + return nullptr; + } + + const SymmetricKey* symmetric_key = static_cast<const SymmetricKey*>(&key); + UniquePtr<HmacOperation> op(new (std::nothrow) HmacOperation( + purpose(), symmetric_key->key_data(), symmetric_key->key_data_size(), digest, + mac_length_bits / 8, min_mac_length_bits / 8)); + if (!op.get()) + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + else + *error = op->error(); + + if (*error != KM_ERROR_OK) + return nullptr; + + return op.release(); +} + +static keymaster_digest_t supported_digests[] = {KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, + KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, + KM_DIGEST_SHA_2_512}; +const keymaster_digest_t* HmacOperationFactory::SupportedDigests(size_t* digest_count) const { + *digest_count = array_length(supported_digests); + return supported_digests; +} + +HmacOperation::HmacOperation(keymaster_purpose_t purpose, const uint8_t* key_data, + size_t key_data_size, keymaster_digest_t digest, size_t mac_length, + size_t min_mac_length) + : Operation(purpose), error_(KM_ERROR_OK), mac_length_(mac_length), + min_mac_length_(min_mac_length) { + // Initialize CTX first, so dtor won't crash even if we error out later. + HMAC_CTX_init(&ctx_); + + const EVP_MD* md = nullptr; + switch (digest) { + case KM_DIGEST_NONE: + case KM_DIGEST_MD5: + error_ = KM_ERROR_UNSUPPORTED_DIGEST; + break; + case KM_DIGEST_SHA1: + md = EVP_sha1(); + break; + case KM_DIGEST_SHA_2_224: + md = EVP_sha224(); + break; + case KM_DIGEST_SHA_2_256: + md = EVP_sha256(); + break; + case KM_DIGEST_SHA_2_384: + md = EVP_sha384(); + break; + case KM_DIGEST_SHA_2_512: + md = EVP_sha512(); + break; + } + + if (md == nullptr) { + error_ = KM_ERROR_UNSUPPORTED_DIGEST; + return; + } + + if (purpose == KM_PURPOSE_SIGN) { + if (mac_length > EVP_MD_size(md) || mac_length < kMinHmacLengthBits / 8) { + error_ = KM_ERROR_UNSUPPORTED_MAC_LENGTH; + return; + } + if (mac_length < min_mac_length) { + error_ = KM_ERROR_INVALID_MAC_LENGTH; + return; + } + } + + HMAC_Init_ex(&ctx_, key_data, key_data_size, md, NULL /* engine */); +} + +HmacOperation::~HmacOperation() { + HMAC_CTX_cleanup(&ctx_); +} + +keymaster_error_t HmacOperation::Begin(const AuthorizationSet& /* input_params */, + AuthorizationSet* /* output_params */) { + return error_; +} + +keymaster_error_t HmacOperation::Update(const AuthorizationSet& /* additional_params */, + const Buffer& input, AuthorizationSet* /* output_params */, + Buffer* /* output */, size_t* input_consumed) { + if (!HMAC_Update(&ctx_, input.peek_read(), input.available_read())) + return TranslateLastOpenSslError(); + *input_consumed = input.available_read(); + return KM_ERROR_OK; +} + +keymaster_error_t HmacOperation::Abort() { + return KM_ERROR_OK; +} + +keymaster_error_t HmacOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& signature, + AuthorizationSet* /* output_params */, Buffer* output) { + keymaster_error_t error = UpdateForFinish(additional_params, input); + if (error != KM_ERROR_OK) + return error; + + uint8_t digest[EVP_MAX_MD_SIZE]; + unsigned int digest_len; + if (!HMAC_Final(&ctx_, digest, &digest_len)) + return TranslateLastOpenSslError(); + + switch (purpose()) { + case KM_PURPOSE_SIGN: + if (mac_length_ > digest_len) + return KM_ERROR_UNSUPPORTED_MAC_LENGTH; + if (!output->reserve(mac_length_) || !output->write(digest, mac_length_)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + return KM_ERROR_OK; + case KM_PURPOSE_VERIFY: { + size_t siglen = signature.available_read(); + if (siglen > digest_len || siglen < kMinHmacLengthBits / 8) + return KM_ERROR_UNSUPPORTED_MAC_LENGTH; + if (siglen < min_mac_length_) + return KM_ERROR_INVALID_MAC_LENGTH; + if (CRYPTO_memcmp(signature.peek_read(), digest, siglen) != 0) + return KM_ERROR_VERIFICATION_FAILED; + return KM_ERROR_OK; + } + default: + return KM_ERROR_UNSUPPORTED_PURPOSE; + } +} + +} // namespace keymaster
diff --git a/keymaster/hmac_operation.h b/keymaster/hmac_operation.h new file mode 100644 index 0000000..83f2e09 --- /dev/null +++ b/keymaster/hmac_operation.h
@@ -0,0 +1,77 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_HMAC_OPERATION_H_ +#define SYSTEM_KEYMASTER_HMAC_OPERATION_H_ + +#include "operation.h" + +#include <openssl/hmac.h> + +namespace keymaster { + +class HmacOperation : public Operation { + public: + HmacOperation(keymaster_purpose_t purpose, const uint8_t* key_data, size_t key_data_size, + keymaster_digest_t digest, size_t mac_length, size_t min_mac_length); + ~HmacOperation(); + + virtual keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params); + virtual keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, + size_t* input_consumed); + virtual keymaster_error_t Abort(); + virtual keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output); + + keymaster_error_t error() { return error_; } + + private: + HMAC_CTX ctx_; + keymaster_error_t error_; + const size_t mac_length_; + const size_t min_mac_length_; +}; + +/** + * Abstract base for HMAC operation factories. This class does all of the work to create + * HMAC operations. + */ +class HmacOperationFactory : public OperationFactory { + public: + virtual KeyType registry_key() const { return KeyType(KM_ALGORITHM_HMAC, purpose()); } + + virtual Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params, + keymaster_error_t* error); + + virtual const keymaster_digest_t* SupportedDigests(size_t* digest_count) const; + + virtual keymaster_purpose_t purpose() const = 0; +}; + +class HmacSignOperationFactory : public HmacOperationFactory { + keymaster_purpose_t purpose() const { return KM_PURPOSE_SIGN; } +}; + +class HmacVerifyOperationFactory : public HmacOperationFactory { + keymaster_purpose_t purpose() const { return KM_PURPOSE_VERIFY; } +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_HMAC_OPERATION_H_
diff --git a/keymaster/hmac_test.cpp b/keymaster/hmac_test.cpp new file mode 100644 index 0000000..04f9356 --- /dev/null +++ b/keymaster/hmac_test.cpp
@@ -0,0 +1,84 @@ +/* + * 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 "hmac.h" + +#include <gtest/gtest.h> +#include <string.h> + +#include "android_keymaster_test_utils.h" + +using std::string; + +namespace keymaster { + +namespace test { + +struct HmacTest { + const char* data; + const char* key; + uint8_t digest[32]; +}; + +static const HmacTest kHmacTests[] = { + { + "Test Using Larger Than Block-Size Key - Hash Key First", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, + 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, + 0x0e, 0xe3, 0x7f, 0x54, + }, + }, + { + "The test message for the MD2, MD5, and SHA-1 hashing algorithms.", + "46697265666f7820616e64205468756e64657242697264206172652061776573" + "6f6d652100", + { + 0x05, 0x75, 0x9a, 0x9e, 0x70, 0x5e, 0xe7, 0x44, 0xe2, 0x46, 0x4b, 0x92, 0x22, 0x14, + 0x22, 0xe0, 0x1b, 0x92, 0x8a, 0x0c, 0xfe, 0xf5, 0x49, 0xe9, 0xa7, 0x1b, 0x56, 0x7d, + 0x1d, 0x29, 0x40, 0x48, + }, + }, +}; + +TEST(HmacTest, SHA256) { + for (size_t i = 0; i < 2; i++) { + const HmacTest& test(kHmacTests[i]); + + HmacSha256 hmac; + const string key = hex2str(test.key); + Buffer key_buffer(key.data(), key.size()); + ASSERT_TRUE(hmac.Init(key_buffer)); + + uint8_t digest_copy[sizeof(test.digest)]; + memcpy(digest_copy, test.digest, sizeof(test.digest)); + Buffer digest_buffer(reinterpret_cast<uint8_t*>(digest_copy), sizeof(digest_copy)); + + Buffer data_buffer(test.data, strlen(test.data)); + EXPECT_TRUE(hmac.Verify(data_buffer, digest_buffer)); + + digest_copy[16] ^= 0x80; + digest_buffer.Reinitialize(reinterpret_cast<uint8_t*>(digest_copy), sizeof(digest_copy)); + EXPECT_FALSE(hmac.Verify(data_buffer, digest_buffer)); + } +} + +} // namespace test +} // namespace keymaster
diff --git a/keymaster/include/keymaster/android_keymaster.h b/keymaster/include/keymaster/android_keymaster.h new file mode 100644 index 0000000..c7ecfad --- /dev/null +++ b/keymaster/include/keymaster/android_keymaster.h
@@ -0,0 +1,95 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_ANDROID_KEYMASTER_H_ +#define SYSTEM_KEYMASTER_ANDROID_KEYMASTER_H_ + +#include <keymaster/android_keymaster_messages.h> +#include <keymaster/authorization_set.h> + +namespace keymaster { + +class Key; +class KeyFactory; +class KeymasterContext; +class OperationTable; + +/** + * This is the reference implementation of Keymaster. In addition to acting as a reference for + * other Keymaster implementers to check their assumptions against, it is used by Keystore as the + * default implementation when no secure implementation is available, and may be installed and + * executed in secure hardware as a secure implementation. + * + * Note that this class doesn't actually implement the Keymaster HAL interface, instead it + * implements an alternative API which is similar to and based upon the HAL, but uses C++ "message" + * classes which support serialization. + * + * For non-secure, pure software implementation there is a HAL translation layer that converts the + * HAL's parameters to and from the message representations, which are then passed in to this + * API. + * + * For secure implementation there is another HAL translation layer that serializes the messages to + * the TEE. In the TEE implementation there's another component which deserializes the messages, + * extracts the relevant parameters and calls this API. + */ +class AndroidKeymaster { + public: + AndroidKeymaster(KeymasterContext* context, size_t operation_table_size); + virtual ~AndroidKeymaster(); + + void GetVersion(const GetVersionRequest& request, GetVersionResponse* response); + void SupportedAlgorithms(const SupportedAlgorithmsRequest& request, + SupportedAlgorithmsResponse* response); + void SupportedBlockModes(const SupportedBlockModesRequest& request, + SupportedBlockModesResponse* response); + void SupportedPaddingModes(const SupportedPaddingModesRequest& request, + SupportedPaddingModesResponse* response); + void SupportedDigests(const SupportedDigestsRequest& request, + SupportedDigestsResponse* response); + void SupportedImportFormats(const SupportedImportFormatsRequest& request, + SupportedImportFormatsResponse* response); + void SupportedExportFormats(const SupportedExportFormatsRequest& request, + SupportedExportFormatsResponse* response); + + void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response); + void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response); + void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request, + GetKeyCharacteristicsResponse* response); + void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response); + void ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response); + void AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response); + void DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response); + void DeleteAllKeys(const DeleteAllKeysRequest& request, DeleteAllKeysResponse* response); + void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response); + void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response); + void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response); + void AbortOperation(const AbortOperationRequest& request, AbortOperationResponse* response); + + bool has_operation(keymaster_operation_handle_t op_handle) const; + + private: + keymaster_error_t LoadKey(const keymaster_key_blob_t& key_blob, + const AuthorizationSet& additional_params, + AuthorizationSet* hw_enforced, AuthorizationSet* sw_enforced, + const KeyFactory** factory, UniquePtr<Key>* key); + + UniquePtr<KeymasterContext> context_; + UniquePtr<OperationTable> operation_table_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ANDROID_KEYMASTER_H_
diff --git a/keymaster/include/keymaster/android_keymaster_messages.h b/keymaster/include/keymaster/android_keymaster_messages.h new file mode 100644 index 0000000..30d3fb7 --- /dev/null +++ b/keymaster/include/keymaster/android_keymaster_messages.h
@@ -0,0 +1,627 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_ANDROID_KEYMASTER_MESSAGES_H_ +#define SYSTEM_KEYMASTER_ANDROID_KEYMASTER_MESSAGES_H_ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/authorization_set.h> + +namespace keymaster { + +// Commands +enum AndroidKeymasterCommand { + GENERATE_KEY = 0, + BEGIN_OPERATION = 1, + UPDATE_OPERATION = 2, + FINISH_OPERATION = 3, + ABORT_OPERATION = 4, + IMPORT_KEY = 5, + EXPORT_KEY = 6, + GET_VERSION = 7, + ADD_RNG_ENTROPY = 8, + GET_SUPPORTED_ALGORITHMS = 9, + GET_SUPPORTED_BLOCK_MODES = 10, + GET_SUPPORTED_PADDING_MODES = 11, + GET_SUPPORTED_DIGESTS = 12, + GET_SUPPORTED_IMPORT_FORMATS = 13, + GET_SUPPORTED_EXPORT_FORMATS = 14, + GET_KEY_CHARACTERISTICS = 15, + ATTEST_KEY = 16, +}; + +/** + * Keymaster message versions are tied to keymaster versions. We map the keymaster + * major.minor.subminor version to a sequential "message version". + * + * Rather than encoding a version number into each message we rely on the client -- who initiates + * all requests -- to check the version of the keymaster implementation with the GET_VERSION command + * and to send only requests that the implementation can understand. This means that only the + * client side needs to manage version compatibility; the implementation can always expect/produce + * messages of its format. + * + * Because message version selection is purely a client-side issue, all messages default to using + * the latest version (MAX_MESSAGE_VERSION). Client code must take care to check versions and pass + * correct version values to message constructors. The AndroidKeymaster implementation always uses + * the default, latest. + * + * Note that this approach implies that GetVersionRequest and GetVersionResponse cannot be + * versioned. + */ +const int32_t MAX_MESSAGE_VERSION = 3; +inline int32_t MessageVersion(uint8_t major_ver, uint8_t minor_ver, uint8_t /* subminor_ver */) { + int32_t message_version = -1; + switch (major_ver) { + case 0: + // For the moment we still support version 0, though in general the plan is not to support + // non-matching major versions. + message_version = 0; + break; + case 1: + switch (minor_ver) { + case 0: + message_version = 1; + break; + case 1: + message_version = 2; + break; + } + break; + case 2: + message_version = 3; + break; + }; + return message_version; +} + +struct KeymasterMessage : public Serializable { + explicit KeymasterMessage(int32_t ver) : message_version(ver) { assert(ver >= 0); } + uint32_t message_version; +}; + +/** + * All responses include an error value, and if the error is not KM_ERROR_OK, return no additional + * data. This abstract class factors out the common serialization functionality for all of the + * responses, so we only have to implement it once. Inheritance for reuse is generally not a great + * structure, but in this case it's the cleanest option. + */ +struct KeymasterResponse : public KeymasterMessage { + explicit KeymasterResponse(int32_t ver) + : KeymasterMessage(ver), error(KM_ERROR_UNKNOWN_ERROR) {} + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + virtual size_t NonErrorSerializedSize() const = 0; + virtual uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const = 0; + virtual bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) = 0; + + keymaster_error_t error; +}; + +struct SupportedAlgorithmsRequest : public KeymasterMessage { + explicit SupportedAlgorithmsRequest(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterMessage(ver) {} + + size_t SerializedSize() const override { return 0; }; + uint8_t* Serialize(uint8_t* buf, const uint8_t* /* end */) const override { return buf; } + bool Deserialize(const uint8_t** /* buf_ptr */, const uint8_t* /* end */) override { + return true; + } +}; + +struct SupportedByAlgorithmRequest : public KeymasterMessage { + explicit SupportedByAlgorithmRequest(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterMessage(ver) {} + + size_t SerializedSize() const override { return sizeof(uint32_t); }; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override { + return append_uint32_to_buf(buf, end, algorithm); + } + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override { + return copy_uint32_from_buf(buf_ptr, end, &algorithm); + } + + keymaster_algorithm_t algorithm; +}; + +struct SupportedImportFormatsRequest : public SupportedByAlgorithmRequest { + explicit SupportedImportFormatsRequest(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedByAlgorithmRequest(ver) {} +}; + +struct SupportedExportFormatsRequest : public SupportedByAlgorithmRequest { + explicit SupportedExportFormatsRequest(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedByAlgorithmRequest(ver) {} +}; + +struct SupportedByAlgorithmAndPurposeRequest : public KeymasterMessage { + explicit SupportedByAlgorithmAndPurposeRequest(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterMessage(ver) {} + + size_t SerializedSize() const override { return sizeof(uint32_t) * 2; }; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override { + buf = append_uint32_to_buf(buf, end, algorithm); + return append_uint32_to_buf(buf, end, purpose); + } + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override { + return copy_uint32_from_buf(buf_ptr, end, &algorithm) && + copy_uint32_from_buf(buf_ptr, end, &purpose); + } + + keymaster_algorithm_t algorithm; + keymaster_purpose_t purpose; +}; + +struct SupportedBlockModesRequest : public SupportedByAlgorithmAndPurposeRequest { + explicit SupportedBlockModesRequest(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedByAlgorithmAndPurposeRequest(ver) {} +}; + +struct SupportedPaddingModesRequest : public SupportedByAlgorithmAndPurposeRequest { + explicit SupportedPaddingModesRequest(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedByAlgorithmAndPurposeRequest(ver) {} +}; + +struct SupportedDigestsRequest : public SupportedByAlgorithmAndPurposeRequest { + explicit SupportedDigestsRequest(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedByAlgorithmAndPurposeRequest(ver) {} +}; + +template <typename T> struct SupportedResponse : public KeymasterResponse { + explicit SupportedResponse(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterResponse(ver), results(nullptr), results_length(0) {} + ~SupportedResponse() { delete[] results; } + + template <size_t N> void SetResults(const T (&arr)[N]) { SetResults(arr, N); } + + void SetResults(const T* arr, size_t n) { + delete[] results; + results_length = 0; + results = dup_array(arr, n); + if (results == nullptr) { + error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + } else { + results_length = n; + error = KM_ERROR_OK; + } + } + + size_t NonErrorSerializedSize() const override { + return sizeof(uint32_t) + results_length * sizeof(uint32_t); + } + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override { + return append_uint32_array_to_buf(buf, end, results, results_length); + } + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override { + delete[] results; + results = nullptr; + UniquePtr<T[]> tmp; + if (!copy_uint32_array_from_buf(buf_ptr, end, &tmp, &results_length)) + return false; + results = tmp.release(); + return true; + } + + T* results; + size_t results_length; +}; + +struct SupportedAlgorithmsResponse : public SupportedResponse<keymaster_algorithm_t> { + explicit SupportedAlgorithmsResponse(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedResponse<keymaster_algorithm_t>(ver) {} +}; + +struct SupportedBlockModesResponse : public SupportedResponse<keymaster_block_mode_t> { + explicit SupportedBlockModesResponse(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedResponse<keymaster_block_mode_t>(ver) {} +}; + +struct SupportedPaddingModesResponse : public SupportedResponse<keymaster_padding_t> { + explicit SupportedPaddingModesResponse(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedResponse<keymaster_padding_t>(ver) {} +}; + +struct SupportedDigestsResponse : public SupportedResponse<keymaster_digest_t> { + explicit SupportedDigestsResponse(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedResponse<keymaster_digest_t>(ver) {} +}; + +struct SupportedImportFormatsResponse : public SupportedResponse<keymaster_key_format_t> { + explicit SupportedImportFormatsResponse(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedResponse<keymaster_key_format_t>(ver) {} +}; + +struct SupportedExportFormatsResponse : public SupportedResponse<keymaster_key_format_t> { + explicit SupportedExportFormatsResponse(int32_t ver = MAX_MESSAGE_VERSION) + : SupportedResponse<keymaster_key_format_t>(ver) {} +}; + +struct GenerateKeyRequest : public KeymasterMessage { + explicit GenerateKeyRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) {} + + size_t SerializedSize() const override { return key_description.SerializedSize(); } + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override { + return key_description.Serialize(buf, end); + } + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override { + return key_description.Deserialize(buf_ptr, end); + } + + AuthorizationSet key_description; +}; + +struct GenerateKeyResponse : public KeymasterResponse { + explicit GenerateKeyResponse(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterResponse(ver) { + key_blob.key_material = nullptr; + key_blob.key_material_size = 0; + } + ~GenerateKeyResponse(); + + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_key_blob_t key_blob; + AuthorizationSet enforced; + AuthorizationSet unenforced; +}; + +struct GetKeyCharacteristicsRequest : public KeymasterMessage { + explicit GetKeyCharacteristicsRequest(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterMessage(ver) { + key_blob.key_material = nullptr; + key_blob.key_material_size = 0; + } + ~GetKeyCharacteristicsRequest(); + + void SetKeyMaterial(const void* key_material, size_t length); + void SetKeyMaterial(const keymaster_key_blob_t& blob) { + SetKeyMaterial(blob.key_material, blob.key_material_size); + } + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_key_blob_t key_blob; + AuthorizationSet additional_params; +}; + +struct GetKeyCharacteristicsResponse : public KeymasterResponse { + explicit GetKeyCharacteristicsResponse(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterResponse(ver) {} + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + AuthorizationSet enforced; + AuthorizationSet unenforced; +}; + +struct BeginOperationRequest : public KeymasterMessage { + explicit BeginOperationRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) { + key_blob.key_material = nullptr; + key_blob.key_material_size = 0; + } + ~BeginOperationRequest() { delete[] key_blob.key_material; } + + void SetKeyMaterial(const void* key_material, size_t length); + void SetKeyMaterial(const keymaster_key_blob_t& blob) { + SetKeyMaterial(blob.key_material, blob.key_material_size); + } + + size_t SerializedSize() const; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_purpose_t purpose; + keymaster_key_blob_t key_blob; + AuthorizationSet additional_params; +}; + +struct BeginOperationResponse : public KeymasterResponse { + explicit BeginOperationResponse(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterResponse(ver) {} + + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_operation_handle_t op_handle; + AuthorizationSet output_params; +}; + +struct UpdateOperationRequest : public KeymasterMessage { + explicit UpdateOperationRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) {} + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_operation_handle_t op_handle; + Buffer input; + AuthorizationSet additional_params; +}; + +struct UpdateOperationResponse : public KeymasterResponse { + explicit UpdateOperationResponse(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterResponse(ver), input_consumed(0) {} + + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + Buffer output; + size_t input_consumed; + AuthorizationSet output_params; +}; + +struct FinishOperationRequest : public KeymasterMessage { + explicit FinishOperationRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) {} + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_operation_handle_t op_handle; + Buffer input; + Buffer signature; + AuthorizationSet additional_params; +}; + +struct FinishOperationResponse : public KeymasterResponse { + explicit FinishOperationResponse(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterResponse(ver) {} + + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + Buffer output; + AuthorizationSet output_params; +}; + +struct AbortOperationRequest : public KeymasterMessage { + explicit AbortOperationRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) {} + + size_t SerializedSize() const override { return sizeof(uint64_t); } + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override { + return append_uint64_to_buf(buf, end, op_handle); + } + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override { + return copy_uint64_from_buf(buf_ptr, end, &op_handle); + } + + keymaster_operation_handle_t op_handle; +}; + +struct AbortOperationResponse : public KeymasterResponse { + explicit AbortOperationResponse(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterResponse(ver) {} + + size_t NonErrorSerializedSize() const override { return 0; } + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; } + bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; } +}; + +struct AddEntropyRequest : public KeymasterMessage { + explicit AddEntropyRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) {} + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + Buffer random_data; +}; + +struct AddEntropyResponse : public KeymasterResponse { + explicit AddEntropyResponse(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterResponse(ver) {} + + size_t NonErrorSerializedSize() const override { return 0; } + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* /* end */) const override { + return buf; + } + bool NonErrorDeserialize(const uint8_t** /* buf_ptr */, const uint8_t* /* end */) override { + return true; + } +}; + +struct ImportKeyRequest : public KeymasterMessage { + explicit ImportKeyRequest(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterMessage(ver), key_data(nullptr) {} + ~ImportKeyRequest() { delete[] key_data; } + + void SetKeyMaterial(const void* key_material, size_t length); + void SetKeyMaterial(const keymaster_key_blob_t& blob) { + SetKeyMaterial(blob.key_material, blob.key_material_size); + } + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + AuthorizationSet key_description; + keymaster_key_format_t key_format; + uint8_t* key_data; + size_t key_data_length; +}; + +struct ImportKeyResponse : public KeymasterResponse { + explicit ImportKeyResponse(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterResponse(ver) { + key_blob.key_material = nullptr; + key_blob.key_material_size = 0; + } + ~ImportKeyResponse() { delete[] key_blob.key_material; } + + void SetKeyMaterial(const void* key_material, size_t length); + void SetKeyMaterial(const keymaster_key_blob_t& blob) { + SetKeyMaterial(blob.key_material, blob.key_material_size); + } + + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_key_blob_t key_blob; + AuthorizationSet enforced; + AuthorizationSet unenforced; +}; + +struct ExportKeyRequest : public KeymasterMessage { + explicit ExportKeyRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) { + key_blob.key_material = nullptr; + key_blob.key_material_size = 0; + } + ~ExportKeyRequest() { delete[] key_blob.key_material; } + + void SetKeyMaterial(const void* key_material, size_t length); + void SetKeyMaterial(const keymaster_key_blob_t& blob) { + SetKeyMaterial(blob.key_material, blob.key_material_size); + } + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + AuthorizationSet additional_params; + keymaster_key_format_t key_format; + keymaster_key_blob_t key_blob; +}; + +struct ExportKeyResponse : public KeymasterResponse { + explicit ExportKeyResponse(int32_t ver = MAX_MESSAGE_VERSION) + : KeymasterResponse(ver), key_data(nullptr) {} + ~ExportKeyResponse() { delete[] key_data; } + + void SetKeyMaterial(const void* key_material, size_t length); + void SetKeyMaterial(const keymaster_key_blob_t& blob) { + SetKeyMaterial(blob.key_material, blob.key_material_size); + } + + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + uint8_t* key_data; + size_t key_data_length; +}; + +struct DeleteKeyRequest : public KeymasterMessage { + explicit DeleteKeyRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) { + key_blob.key_material = nullptr; + key_blob.key_material_size = 0; + } + ~DeleteKeyRequest() { delete[] key_blob.key_material; } + + void SetKeyMaterial(const void* key_material, size_t length); + void SetKeyMaterial(const keymaster_key_blob_t& blob) { + SetKeyMaterial(blob.key_material, blob.key_material_size); + } + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_key_blob_t key_blob; +}; + +struct DeleteKeyResponse : public KeymasterResponse { + explicit DeleteKeyResponse(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterResponse(ver) {} + + size_t NonErrorSerializedSize() const override { return 0; } + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; } + bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; } +}; + +struct DeleteAllKeysRequest : public KeymasterMessage { + explicit DeleteAllKeysRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) {} + + size_t SerializedSize() const override { return 0; } + uint8_t* Serialize(uint8_t* buf, const uint8_t*) const override { return buf; } + bool Deserialize(const uint8_t**, const uint8_t*) override { return true; }; +}; + +struct DeleteAllKeysResponse : public KeymasterResponse { + explicit DeleteAllKeysResponse(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterResponse(ver) {} + + size_t NonErrorSerializedSize() const override { return 0; } + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; } + bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; } +}; + +struct GetVersionRequest : public KeymasterMessage { + GetVersionRequest() : KeymasterMessage(0 /* not versionable */) {} + + size_t SerializedSize() const override { return 0; } + uint8_t* Serialize(uint8_t* buf, const uint8_t*) const override { return buf; } + bool Deserialize(const uint8_t**, const uint8_t*) override { return true; }; +}; + +struct GetVersionResponse : public KeymasterResponse { + GetVersionResponse() + : KeymasterResponse(0 /* not versionable */), major_ver(0), minor_ver(0), subminor_ver(0) {} + + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + uint8_t major_ver; + uint8_t minor_ver; + uint8_t subminor_ver; +}; + +struct AttestKeyRequest : public KeymasterMessage { + explicit AttestKeyRequest(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterMessage(ver) { + key_blob.key_material = nullptr; + key_blob.key_material_size = 0; + } + ~AttestKeyRequest(); + + void SetKeyMaterial(const void* key_material, size_t length); + void SetKeyMaterial(const keymaster_key_blob_t& blob) { + SetKeyMaterial(blob.key_material, blob.key_material_size); + } + + size_t SerializedSize() const override; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_key_blob_t key_blob; + AuthorizationSet attest_params; +}; + +struct AttestKeyResponse : public KeymasterResponse { + explicit AttestKeyResponse(int32_t ver = MAX_MESSAGE_VERSION) : KeymasterResponse(ver) { + certificate_chain.entry_count = 0; + certificate_chain.entries = nullptr; + } + ~AttestKeyResponse(); + + bool AllocateChain(size_t entry_count); + + size_t NonErrorSerializedSize() const override; + uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* end) const override; + bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) override; + + keymaster_cert_chain_t certificate_chain; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ANDROID_KEYMASTER_MESSAGES_H_
diff --git a/keymaster/include/keymaster/android_keymaster_utils.h b/keymaster/include/keymaster/android_keymaster_utils.h new file mode 100644 index 0000000..c190e04 --- /dev/null +++ b/keymaster/include/keymaster/android_keymaster_utils.h
@@ -0,0 +1,330 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_ANDROID_KEYMASTER_UTILS_H_ +#define SYSTEM_KEYMASTER_ANDROID_KEYMASTER_UTILS_H_ + +#include <stdint.h> +#include <string.h> +#include <time.h> // for time_t. + +#include <UniquePtr.h> + +#include <hardware/keymaster_defs.h> +#include <keymaster/serializable.h> + +namespace keymaster { + +/** + * Convert the specified time value into "Java time", which is a signed 64-bit integer representing + * elapsed milliseconds since Jan 1, 1970. + */ +inline int64_t java_time(time_t time) { + // The exact meaning of a time_t value is implementation-dependent. If this code is ported to a + // platform that doesn't define it as "seconds since Jan 1, 1970 UTC", this function will have + // to be revised. + return time * 1000; +} + +/* + * Array Manipulation functions. This set of templated inline functions provides some nice tools + * for operating on c-style arrays. C-style arrays actually do have a defined size associated with + * them, as long as they are not allowed to decay to a pointer. These template methods exploit this + * to allow size-based array operations without explicitly specifying the size. If passed a pointer + * rather than an array, they'll fail to compile. + */ + +/** + * Return the size in bytes of the array \p a. + */ +template <typename T, size_t N> inline size_t array_size(const T (&a)[N]) { + return sizeof(a); +} + +/** + * 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; +} + +/** + * Duplicate the array \p a. The memory for the new array is allocated and the caller takes + * responsibility. + */ +template <typename T> inline T* dup_array(const T* a, size_t n) { + T* dup = new (std::nothrow) T[n]; + if (dup) + for (size_t i = 0; i < n; ++i) + dup[i] = a[i]; + return dup; +} + +/** + * Duplicate the array \p a. The memory for the new array is allocated and the caller takes + * responsibility. Note that the dup is necessarily returned as a pointer, so size is lost. Call + * array_length() on the original array to discover the size. + */ +template <typename T, size_t N> inline T* dup_array(const T (&a)[N]) { + return dup_array(a, N); +} + +/** + * Duplicate the buffer \p buf. The memory for the new buffer is allocated and the caller takes + * responsibility. + */ +uint8_t* dup_buffer(const void* buf, size_t size); + +/** + * Copy the contents of array \p arr to \p dest. + */ +template <typename T, size_t N> inline void copy_array(const T (&arr)[N], T* dest) { + for (size_t i = 0; i < N; ++i) + dest[i] = arr[i]; +} + +/** + * Search array \p a for value \p val, returning true if found. Note that this function is + * early-exit, meaning that it should not be used in contexts where timing analysis attacks could be + * a concern. + */ +template <typename T, size_t N> inline bool array_contains(const T (&a)[N], T val) { + for (size_t i = 0; i < N; ++i) { + if (a[i] == val) { + return true; + } + } + return false; +} + +/** + * 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 + +/** + * Variant of memcmp that has the same runtime regardless of whether the data matches (i.e. doesn't + * short-circuit). Not an exact equivalent to memcmp because it doesn't return <0 if p1 < p2, just + * 0 for match and non-zero for non-match. + */ +int memcmp_s(const void* p1, const void* p2, size_t length); + +/** + * Eraser clears buffers. Construct it with a buffer or object and the destructor will ensure that + * it is zeroed. + */ +class Eraser { + public: + /* Not implemented. If this gets used, we want a link error. */ + template <typename T> explicit Eraser(T* t); + + template <typename T> + explicit Eraser(T& t) : buf_(reinterpret_cast<uint8_t*>(&t)), size_(sizeof(t)) {} + + template <size_t N> explicit Eraser(uint8_t (&arr)[N]) : buf_(arr), size_(N) {} + + Eraser(void* buf, size_t size) : buf_(static_cast<uint8_t*>(buf)), size_(size) {} + ~Eraser() { memset_s(buf_, 0, size_); } + + private: + Eraser(const Eraser&); + void operator=(const Eraser&); + + uint8_t* buf_; + size_t size_; +}; + +/** + * ArrayWrapper is a trivial wrapper around a C-style array that provides begin() and end() + * methods. This is primarily to facilitate range-based iteration on arrays. It does not copy, nor + * does it take ownership; it just holds pointers. + */ +template <typename T> class ArrayWrapper { + public: + ArrayWrapper(T* array, size_t size) : begin_(array), end_(array + size) {} + + T* begin() { return begin_; } + T* end() { return end_; } + + private: + T* begin_; + T* end_; +}; +template <typename T> ArrayWrapper<T> array_range(T* begin, size_t length) { + return ArrayWrapper<T>(begin, length); +} + +/** + * Convert any unsigned integer from network to host order. We implement this here rather than + * using the functions from arpa/inet.h because the TEE doesn't have inet.h. This isn't the most + * efficient implementation, but the compiler should unroll the loop and tighten it up. + */ +template <typename T> T ntoh(T t) { + const uint8_t* byte_ptr = reinterpret_cast<const uint8_t*>(&t); + T retval = 0; + for (size_t i = 0; i < sizeof(t); ++i) { + retval <<= 8; + retval |= byte_ptr[i]; + } + return retval; +} + +/** + * Convert any unsigned integer from host to network order. We implement this here rather than + * using the functions from arpa/inet.h because the TEE doesn't have inet.h. This isn't the most + * efficient implementation, but the compiler should unroll the loop and tighten it up. + */ +template <typename T> T hton(T t) { + T retval; + uint8_t* byte_ptr = reinterpret_cast<uint8_t*>(&retval); + for (size_t i = sizeof(t); i > 0; --i) { + byte_ptr[i - 1] = t & 0xFF; + t >>= 8; + } + return retval; +} + +/** + * KeymasterKeyBlob is a very simple extension of the C struct keymaster_key_blob_t. It manages its + * own memory, which makes avoiding memory leaks much easier. + */ +struct KeymasterKeyBlob : public keymaster_key_blob_t { + KeymasterKeyBlob() { + key_material = nullptr; + key_material_size = 0; + } + + KeymasterKeyBlob(const uint8_t* data, size_t size) { + key_material_size = 0; + key_material = dup_buffer(data, size); + if (key_material) + key_material_size = size; + } + + explicit KeymasterKeyBlob(size_t size) { + key_material_size = 0; + key_material = new (std::nothrow) uint8_t[size]; + if (key_material) + key_material_size = size; + } + + explicit KeymasterKeyBlob(const keymaster_key_blob_t& blob) { + key_material_size = 0; + key_material = dup_buffer(blob.key_material, blob.key_material_size); + if (key_material) + key_material_size = blob.key_material_size; + } + + KeymasterKeyBlob(const KeymasterKeyBlob& blob) { + key_material_size = 0; + key_material = dup_buffer(blob.key_material, blob.key_material_size); + if (key_material) + key_material_size = blob.key_material_size; + } + + void operator=(const KeymasterKeyBlob& blob) { + Clear(); + key_material = dup_buffer(blob.key_material, blob.key_material_size); + key_material_size = blob.key_material_size; + } + + ~KeymasterKeyBlob() { Clear(); } + + const uint8_t* begin() const { return key_material; } + const uint8_t* end() const { return key_material + key_material_size; } + + void Clear() { + memset_s(const_cast<uint8_t*>(key_material), 0, key_material_size); + delete[] key_material; + key_material = nullptr; + key_material_size = 0; + } + + const uint8_t* Reset(size_t new_size) { + Clear(); + key_material = new (std::nothrow) uint8_t[new_size]; + if (key_material) + key_material_size = new_size; + return key_material; + } + + // The key_material in keymaster_key_blob_t is const, which is the right thing in most + // circumstances, but occasionally we do need to write into it. This method exposes a non-const + // version of the pointer. Use sparingly. + uint8_t* writable_data() { return const_cast<uint8_t*>(key_material); } + + keymaster_key_blob_t release() { + keymaster_key_blob_t tmp = {key_material, key_material_size}; + key_material = nullptr; + key_material_size = 0; + return tmp; + } + + size_t SerializedSize() const { return sizeof(uint32_t) + key_material_size; } + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const { + return append_size_and_data_to_buf(buf, end, key_material, key_material_size); + } + + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + Clear(); + UniquePtr<uint8_t[]> tmp; + if (!copy_size_and_data_from_buf(buf_ptr, end, &key_material_size, &tmp)) { + key_material = nullptr; + key_material_size = 0; + return false; + } + key_material = tmp.release(); + return true; + } +}; + +struct Characteristics_Delete { + void operator()(keymaster_key_characteristics_t* p) { + keymaster_free_characteristics(p); + free(p); + } +}; + +struct Malloc_Delete { + void operator()(void* p) { free(p); } +}; + +struct CertificateChainDelete { + void operator()(keymaster_cert_chain_t* p) { + if (!p) + return; + for (size_t i = 0; i < p->entry_count; ++i) + delete[] p->entries[i].data; + delete[] p->entries; + delete p; + } +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ANDROID_KEYMASTER_UTILS_H_
diff --git a/keymaster/include/keymaster/asymmetric_key_factory.h b/keymaster/include/keymaster/asymmetric_key_factory.h new file mode 100644 index 0000000..afcfc1c --- /dev/null +++ b/keymaster/include/keymaster/asymmetric_key_factory.h
@@ -0,0 +1,51 @@ +/* + * 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 SYSTEM_KEYMASTER_ASYMMETRIC_KEY_FACTORY_H_ +#define SYSTEM_KEYMASTER_ASYMMETRIC_KEY_FACTORY_H_ + +#include <keymaster/key_factory.h> + +namespace keymaster { + +/** + * Abstract base for KeyFactories that handle asymmetric keys. + */ +class AsymmetricKey; +class AsymmetricKeyFactory : public KeyFactory { + public: + explicit AsymmetricKeyFactory(const KeymasterContext* context) : KeyFactory(context) {} + + keymaster_error_t LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const override; + + virtual keymaster_error_t CreateEmptyKey(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<AsymmetricKey>* key) const = 0; + + virtual keymaster_algorithm_t keymaster_key_type() const = 0; + virtual int evp_key_type() const = 0; + + virtual const keymaster_key_format_t* SupportedImportFormats(size_t* format_count) const; + virtual const keymaster_key_format_t* SupportedExportFormats(size_t* format_count) const; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ASYMMETRIC_KEY_FACTORY_H_
diff --git a/keymaster/include/keymaster/authorization_set.h b/keymaster/include/keymaster/authorization_set.h new file mode 100644 index 0000000..74daa8d --- /dev/null +++ b/keymaster/include/keymaster/authorization_set.h
@@ -0,0 +1,570 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_AUTHORIZATION_SET_H_ +#define SYSTEM_KEYMASTER_AUTHORIZATION_SET_H_ + +#include <UniquePtr.h> + +#include <hardware/keymaster_defs.h> +#include <keymaster/keymaster_tags.h> +#include <keymaster/serializable.h> + +namespace keymaster { + +class AuthorizationSetBuilder; + +/** + * An extension of the keymaster_key_param_set_t struct, which provides serialization memory + * management and methods for easy manipulation and construction. + */ +class AuthorizationSet : public Serializable, public keymaster_key_param_set_t { + public: + /** + * Construct an empty, dynamically-allocated, growable AuthorizationSet. Does not actually + * allocate any storage until elements are added, so there is no cost to creating an + * AuthorizationSet with this constructor and then reinitializing it to point at pre-allocated + * buffers, with \p Reinitialize. + */ + AuthorizationSet() + : elems_capacity_(0), indirect_data_(NULL), indirect_data_size_(0), + indirect_data_capacity_(0), error_(OK) { + elems_ = nullptr; + elems_size_ = 0; + } + + /** + * Construct an AuthorizationSet from the provided array. The AuthorizationSet copies the data + * from the provided array (and the data referenced by its embedded pointers, if any) into + * dynamically-allocated storage. If allocation of the needed storage fails, \p is_valid() will + * return ALLOCATION_FAILURE. It is the responsibility of the caller to check before using the + * set, if allocations might fail. + */ + AuthorizationSet(const keymaster_key_param_t* elems, size_t count) : indirect_data_(nullptr) { + elems_ = nullptr; + Reinitialize(elems, count); + } + + explicit AuthorizationSet(const keymaster_key_param_set_t& set) : indirect_data_(nullptr) { + elems_ = nullptr; + Reinitialize(set.params, set.length); + } + + explicit AuthorizationSet(const uint8_t* serialized_set, size_t serialized_size) + : indirect_data_(nullptr) { + elems_ = nullptr; + Deserialize(&serialized_set, serialized_set + serialized_size); + } + + /** + * Construct an AuthorizationSet from the provided builder. This extracts the data from the + * builder, rather than copying it, so after this call the builder is empty. + */ + explicit AuthorizationSet(/* NOT const */ AuthorizationSetBuilder& builder); + + // Copy constructor. + AuthorizationSet(const AuthorizationSet& set) : Serializable(), indirect_data_(nullptr) { + elems_ = nullptr; + Reinitialize(set.elems_, set.elems_size_); + } + + /** + * Clear existing authorization set data + */ + void Clear(); + + /** + * Reinitialize an AuthorizationSet as a dynamically-allocated, growable copy of the data in the + * provided array (and the data referenced by its embedded pointers, if any). If the allocation + * of the needed storage fails this method will return false and \p is_valid() will return + * ALLOCATION_FAILURE. + */ + bool Reinitialize(const keymaster_key_param_t* elems, size_t count); + + bool Reinitialize(const AuthorizationSet& set) { + return Reinitialize(set.elems_, set.elems_size_); + } + + bool Reinitialize(const keymaster_key_param_set_t& set) { + return Reinitialize(set.params, set.length); + } + + ~AuthorizationSet(); + + enum Error { + OK, + ALLOCATION_FAILURE, + MALFORMED_DATA, + }; + + Error is_valid() const { return error_; } + + /** + * Returns the size of the set. + */ + size_t size() const { return elems_size_; } + + /** + * Returns true if the set is empty. + */ + bool empty() const { return size() == 0; } + + /** + * Returns the total size of all indirect data referenced by set elements. + */ + size_t indirect_size() const { return indirect_data_size_; } + + /** + * Returns the data in the set, directly. Be careful with this. + */ + const keymaster_key_param_t* data() const { return elems_; } + + /** + * Sorts the set + */ + void Sort(); + + /** + * Sorts the set and removes duplicates (inadvertently duplicating tags is easy to do with the + * AuthorizationSetBuilder). + */ + void Deduplicate(); + + /** + * Returns the data in a keymaster_key_param_set_t, suitable for returning to C code. For C + * compatibility, the contents are malloced, not new'ed, and so must be freed with free(), or + * better yet with keymaster_free_param_set, not delete. The caller takes ownership. + */ + void CopyToParamSet(keymaster_key_param_set_t* set) const; + + /** + * Returns the offset of the next entry that matches \p tag, starting from the element after \p + * begin. If not found, returns -1. + */ + int find(keymaster_tag_t tag, int begin = -1) const; + + /** + * Removes the entry at the specified index. Returns true if successful, false if the index was + * out of bounds. + */ + bool erase(size_t index); + + /** + * Returns iterator (pointer) to beginning of elems array, to enable STL-style iteration + */ + const keymaster_key_param_t* begin() const { return elems_; } + + /** + * Returns iterator (pointer) one past end of elems array, to enable STL-style iteration + */ + const keymaster_key_param_t* end() const { return elems_ + elems_size_; } + + /** + * Returns the nth element of the set. + */ + keymaster_key_param_t& operator[](int n); + + /** + * Returns the nth element of the set. + */ + keymaster_key_param_t operator[](int n) const; + + /** + * Returns the number of \p tag entries. + */ + size_t GetTagCount(keymaster_tag_t tag) const; + + /** + * Returns true if the set contains the specified tag and value. + */ + template <keymaster_tag_t Tag, typename T> + bool Contains(TypedEnumTag<KM_ENUM_REP, Tag, T> tag, T val) const { + return ContainsEnumValue(tag, val); + } + + /** + * Returns true if the set contains the specified tag and value. + */ + template <keymaster_tag_t Tag, typename T> + bool Contains(TypedEnumTag<KM_ENUM, Tag, T> tag, T val) const { + return ContainsEnumValue(tag, val); + } + + /** + * If the specified integer-typed \p tag exists, places its value in \p val and returns true. + * If \p tag is not present, leaves \p val unmodified and returns false. + */ + template <keymaster_tag_t T> + inline bool GetTagValue(TypedTag<KM_UINT, T> tag, uint32_t* val) const { + return GetTagValueInt(tag, val); + } + + /** + * If the specified instance of the specified integer-typed \p tag exists, places its value + * in \p val and returns true. If \p tag is not present, leaves \p val unmodified and returns + * false. + */ + template <keymaster_tag_t Tag> + bool GetTagValue(TypedTag<KM_UINT_REP, Tag> tag, size_t instance, uint32_t* val) const { + return GetTagValueIntRep(tag, instance, val); + } + + /** + * If the specified long-typed \p tag exists, places its value in \p val and returns true. + * If \p tag is not present, leaves \p val unmodified and returns false. + */ + template <keymaster_tag_t T> + inline bool GetTagValue(TypedTag<KM_ULONG, T> tag, uint64_t* val) const { + return GetTagValueLong(tag, val); + } + + /** + * If the specified instance of the specified integer-typed \p tag exists, places its value + * in \p val and returns true. If \p tag is not present, leaves \p val unmodified and returns + * false. + */ + template <keymaster_tag_t Tag> + bool GetTagValue(TypedTag<KM_ULONG_REP, Tag> tag, size_t instance, uint64_t* val) const { + return GetTagValueLongRep(tag, instance, val); + } + + /** + * If the specified enumeration-typed \p tag exists, places its value in \p val and returns + * true. If \p tag is not present, leaves \p val unmodified and returns false. + */ + template <keymaster_tag_t Tag, typename T> + bool GetTagValue(TypedEnumTag<KM_ENUM, Tag, T> tag, T* val) const { + return GetTagValueEnum(tag, reinterpret_cast<uint32_t*>(val)); + } + + /** + * If the specified instance of the specified enumeration-typed \p tag exists, places its value + * in \p val and returns true. If \p tag is not present, leaves \p val unmodified and returns + * false. + */ + template <keymaster_tag_t Tag, typename T> + bool GetTagValue(TypedEnumTag<KM_ENUM_REP, Tag, T> tag, size_t instance, T* val) const { + return GetTagValueEnumRep(tag, instance, reinterpret_cast<uint32_t*>(val)); + } + + /** + * If exactly one instance of the specified enumeration-typed \p tag exists, places its value in + * \p val and returns true. If \p tag is not present or if multiple copies are present, leaves + * \p val unmodified and returns false. + */ + template <keymaster_tag_t Tag, typename T> + bool GetTagValue(TypedEnumTag<KM_ENUM_REP, Tag, T> tag, T* val) const { + if (GetTagCount(tag) != 1) + return false; + return GetTagValueEnumRep(tag, 0, reinterpret_cast<uint32_t*>(val)); + } + + /** + * If the specified date-typed \p tag exists, places its value in \p val and returns + * true. If \p tag is not present, leaves \p val unmodified and returns false. + */ + template <keymaster_tag_t Tag> + bool GetTagValue(TypedTag<KM_UINT_REP, Tag> tag, size_t instance, + typename TypedTag<KM_UINT_REP, Tag>::value_type* val) const { + return GetTagValueIntRep(tag, instance, val); + } + + /** + * If the specified bytes-typed \p tag exists, places its value in \p val and returns + * true. If \p tag is not present, leaves \p val unmodified and returns false. + */ + template <keymaster_tag_t Tag> + bool GetTagValue(TypedTag<KM_BYTES, Tag> tag, keymaster_blob_t* val) const { + return GetTagValueBlob(tag, val); + } + + /** + * If the specified bignum-typed \p tag exists, places its value in \p val and returns + * true. If \p tag is not present, leaves \p val unmodified and returns false. + */ + template <keymaster_tag_t Tag> + bool GetTagValue(TypedTag<KM_BIGNUM, Tag> tag, keymaster_blob_t* val) const { + return GetTagValueBlob(tag, val); + } + + /** + * Returns true if the specified tag is present, and therefore has the value 'true'. + */ + template <keymaster_tag_t Tag> bool GetTagValue(TypedTag<KM_BOOL, Tag> tag) const { + return GetTagValueBool(tag); + } + + /** + * If the specified \p tag exists, places its value in \p val and returns true. If \p tag is + * not present, leaves \p val unmodified and returns false. + */ + template <keymaster_tag_t Tag, keymaster_tag_type_t Type> + bool GetTagValue(TypedTag<Type, Tag> tag, typename TagValueType<Type>::value_type* val) const { + return GetTagValueLong(tag, val); + } + + bool push_back(keymaster_key_param_t elem); + + /** + * Grow the elements array to ensure it can contain \p count entries. Preserves any existing + * entries. + */ + bool reserve_elems(size_t count); + + /** + * Grow the indirect data array to ensure it can contain \p length bytes. Preserves any + * existing indirect data. + */ + bool reserve_indirect(size_t length); + + bool push_back(const keymaster_key_param_set_t& set); + + /** + * Append the tag and enumerated value to the set. + */ + template <keymaster_tag_t Tag, keymaster_tag_type_t Type, typename KeymasterEnum> + bool push_back(TypedEnumTag<Type, Tag, KeymasterEnum> tag, KeymasterEnum val) { + return push_back(Authorization(tag, val)); + } + + /** + * Append the boolean tag (value "true") to the set. + */ + template <keymaster_tag_t Tag> bool push_back(TypedTag<KM_BOOL, Tag> tag) { + return push_back(Authorization(tag)); + } + + /** + * Append the tag and byte array to the set. Copies the array into internal storage; does not + * take ownership of the passed-in array. + */ + template <keymaster_tag_t Tag> + bool push_back(TypedTag<KM_BYTES, Tag> tag, const void* bytes, size_t bytes_len) { + return push_back(keymaster_param_blob(tag, static_cast<const uint8_t*>(bytes), bytes_len)); + } + + /** + * Append the tag and blob to the set. Copies the blob contents into internal storage; does not + * take ownership of the blob's data. + */ + template <keymaster_tag_t Tag> + bool push_back(TypedTag<KM_BYTES, Tag> tag, const keymaster_blob_t& blob) { + return push_back(tag, blob.data, blob.data_length); + } + + /** + * Append the tag and bignum array to the set. Copies the array into internal storage; does not + * take ownership of the passed-in array. + */ + template <keymaster_tag_t Tag> + bool push_back(TypedTag<KM_BIGNUM, Tag> tag, const void* bytes, size_t bytes_len) { + return push_back(keymaster_param_blob(tag, static_cast<const uint8_t*>(bytes), bytes_len)); + } + + template <keymaster_tag_t Tag, keymaster_tag_type_t Type> + bool push_back(TypedTag<Type, Tag> tag, typename TypedTag<Type, Tag>::value_type val) { + return push_back(Authorization(tag, val)); + } + + template <keymaster_tag_t Tag, keymaster_tag_type_t Type> + bool push_back(TypedTag<Type, Tag> tag, const void* bytes, size_t bytes_len) { + return push_back(Authorization(tag, bytes, bytes_len)); + } + + /* Virtual methods from Serializable */ + size_t SerializedSize() const; + uint8_t* Serialize(uint8_t* serialized_set, const uint8_t* end) const; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end); + + size_t SerializedSizeOfElements() const; + + private: + // Disallow assignment + void operator=(const AuthorizationSet&); + + void FreeData(); + void set_invalid(Error err); + + static size_t ComputeIndirectDataSize(const keymaster_key_param_t* elems, size_t count); + void CopyIndirectData(); + bool CheckIndirectData(); + + bool DeserializeIndirectData(const uint8_t** buf_ptr, const uint8_t* end); + bool DeserializeElementsData(const uint8_t** buf_ptr, const uint8_t* end); + + bool GetTagValueEnum(keymaster_tag_t tag, uint32_t* val) const; + bool GetTagValueEnumRep(keymaster_tag_t tag, size_t instance, uint32_t* val) const; + bool GetTagValueInt(keymaster_tag_t tag, uint32_t* val) const; + bool GetTagValueIntRep(keymaster_tag_t tag, size_t instance, uint32_t* val) const; + bool GetTagValueLong(keymaster_tag_t tag, uint64_t* val) const; + bool GetTagValueLongRep(keymaster_tag_t tag, size_t instance, uint64_t* val) const; + bool GetTagValueDate(keymaster_tag_t tag, uint64_t* val) const; + bool GetTagValueBlob(keymaster_tag_t tag, keymaster_blob_t* val) const; + bool GetTagValueBool(keymaster_tag_t tag) const; + + bool ContainsEnumValue(keymaster_tag_t tag, uint32_t val) const; + + // Define elems_ and elems_size_ as aliases to params and length, respectivley. This is to + // avoid using the variables without the trailing underscore in the implementation. + keymaster_key_param_t*& elems_ = keymaster_key_param_set_t::params; + size_t& elems_size_ = keymaster_key_param_set_t::length; + + size_t elems_capacity_; + uint8_t* indirect_data_; + size_t indirect_data_size_; + size_t indirect_data_capacity_; + Error error_; +}; + +class AuthorizationSetBuilder { + public: + template <typename TagType, typename ValueType> + AuthorizationSetBuilder& Authorization(TagType tag, ValueType value) { + set.push_back(tag, value); + return *this; + } + + template <keymaster_tag_t Tag> + AuthorizationSetBuilder& Authorization(TypedTag<KM_BOOL, Tag> tag) { + set.push_back(tag); + return *this; + } + + template <keymaster_tag_t Tag> + AuthorizationSetBuilder& Authorization(TypedTag<KM_INVALID, Tag> tag) { + keymaster_key_param_t param; + param.tag = tag; + set.push_back(param); + return *this; + } + + template <keymaster_tag_t Tag> + AuthorizationSetBuilder& Authorization(TypedTag<KM_BYTES, Tag> tag, const uint8_t* data, + size_t data_length) { + set.push_back(tag, data, data_length); + return *this; + } + + template <keymaster_tag_t Tag> + AuthorizationSetBuilder& Authorization(TypedTag<KM_BYTES, Tag> tag, const char* data, + size_t data_length) { + return Authorization(tag, reinterpret_cast<const uint8_t*>(data), data_length); + } + + AuthorizationSetBuilder& RsaKey(uint32_t key_size, uint64_t public_exponent); + AuthorizationSetBuilder& EcdsaKey(uint32_t key_size); + AuthorizationSetBuilder& AesKey(uint32_t key_size); + AuthorizationSetBuilder& HmacKey(uint32_t key_size); + + AuthorizationSetBuilder& RsaSigningKey(uint32_t key_size, uint64_t public_exponent); + AuthorizationSetBuilder& RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent); + AuthorizationSetBuilder& EcdsaSigningKey(uint32_t key_size); + AuthorizationSetBuilder& AesEncryptionKey(uint32_t key_size); + + AuthorizationSetBuilder& SigningKey(); + AuthorizationSetBuilder& EncryptionKey(); + AuthorizationSetBuilder& NoDigestOrPadding(); + AuthorizationSetBuilder& EcbMode(); + + AuthorizationSetBuilder& Digest(keymaster_digest_t digest) { + return Authorization(TAG_DIGEST, digest); + } + + AuthorizationSetBuilder& Padding(keymaster_padding_t padding) { + return Authorization(TAG_PADDING, padding); + } + + AuthorizationSetBuilder& Deduplicate() { + set.Deduplicate(); + return *this; + } + + AuthorizationSet build() const { return set; } + + private: + friend AuthorizationSet; + AuthorizationSet set; +}; + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::RsaKey(uint32_t key_size, + uint64_t public_exponent) { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA); + Authorization(TAG_KEY_SIZE, key_size); + Authorization(TAG_RSA_PUBLIC_EXPONENT, public_exponent); + return *this; +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC); + Authorization(TAG_KEY_SIZE, key_size); + return *this; +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES); + return Authorization(TAG_KEY_SIZE, key_size); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_HMAC); + Authorization(TAG_KEY_SIZE, key_size); + return SigningKey(); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::RsaSigningKey(uint32_t key_size, + uint64_t public_exponent) { + RsaKey(key_size, public_exponent); + return SigningKey(); +} + +inline AuthorizationSetBuilder& +AuthorizationSetBuilder::RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent) { + RsaKey(key_size, public_exponent); + return EncryptionKey(); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) { + EcdsaKey(key_size); + return SigningKey(); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::AesEncryptionKey(uint32_t key_size) { + AesKey(key_size); + return EncryptionKey(); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::SigningKey() { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN); + return Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() { + Authorization(TAG_PURPOSE, KM_PURPOSE_ENCRYPT); + return Authorization(TAG_PURPOSE, KM_PURPOSE_DECRYPT); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() { + Authorization(TAG_DIGEST, KM_DIGEST_NONE); + return Authorization(TAG_PADDING, KM_PAD_NONE); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() { + return Authorization(TAG_BLOCK_MODE, KM_MODE_ECB); +} + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KEY_AUTHORIZATION_SET_H_
diff --git a/keymaster/include/keymaster/ec_key_factory.h b/keymaster/include/keymaster/ec_key_factory.h new file mode 100644 index 0000000..aaeb548 --- /dev/null +++ b/keymaster/include/keymaster/ec_key_factory.h
@@ -0,0 +1,60 @@ +/* + * 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 SYSTEM_KEYMASTER_EC_KEY_FACTORY_H_ +#define SYSTEM_KEYMASTER_EC_KEY_FACTORY_H_ + +#include <openssl/ec.h> +#include <openssl/evp.h> + +#include <keymaster/asymmetric_key_factory.h> + +namespace keymaster { + +class EcKeyFactory : public AsymmetricKeyFactory { + public: + explicit EcKeyFactory(const KeymasterContext* context) : AsymmetricKeyFactory(context) {} + + keymaster_algorithm_t keymaster_key_type() const override { return KM_ALGORITHM_EC; } + int evp_key_type() const override { return EVP_PKEY_EC; } + + keymaster_error_t GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + keymaster_error_t ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t CreateEmptyKey(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<AsymmetricKey>* key) const override; + + keymaster_error_t UpdateImportKeyDescription(const AuthorizationSet& key_description, + keymaster_key_format_t key_format, + const KeymasterKeyBlob& key_material, + AuthorizationSet* updated_description, + uint32_t* key_size) const; + + OperationFactory* GetOperationFactory(keymaster_purpose_t purpose) const override; + + static EC_GROUP* choose_group(size_t key_size_bits); +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_EC_KEY_FACTORY_H_
diff --git a/keymaster/include/keymaster/key_factory.h b/keymaster/include/keymaster/key_factory.h new file mode 100644 index 0000000..18f69c4 --- /dev/null +++ b/keymaster/include/keymaster/key_factory.h
@@ -0,0 +1,69 @@ +/* + * 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 SYSTEM_KEYMASTER_KEY_FACTORY_H_ +#define SYSTEM_KEYMASTER_KEY_FACTORY_H_ + +#include <hardware/keymaster_defs.h> +#include <keymaster/authorization_set.h> + +namespace keymaster { + +class Key; +class KeymasterContext; +class OperationFactory; +struct KeymasterKeyBlob; + +/** + * KeyFactory is a abstraction that encapsulats the knowledge of how to build and parse a specifiec + * subclass of Key. + */ +class KeyFactory { + public: + explicit KeyFactory(const KeymasterContext* context) : context_(context) {} + virtual ~KeyFactory() {} + + // Factory methods. + virtual keymaster_error_t GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const = 0; + + virtual keymaster_error_t ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const = 0; + + virtual keymaster_error_t LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const = 0; + + virtual OperationFactory* GetOperationFactory(keymaster_purpose_t purpose) const = 0; + + // Informational methods. + virtual const keymaster_key_format_t* SupportedImportFormats(size_t* format_count) const = 0; + virtual const keymaster_key_format_t* SupportedExportFormats(size_t* format_count) const = 0; + + protected: + const KeymasterContext* context_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KEY_FACTORY_H_
diff --git a/keymaster/include/keymaster/keymaster_context.h b/keymaster/include/keymaster/keymaster_context.h new file mode 100644 index 0000000..c9802e4 --- /dev/null +++ b/keymaster/include/keymaster/keymaster_context.h
@@ -0,0 +1,154 @@ +/* + * 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 SYSTEM_KEYMASTER_KEYMASTER_CONTEXT_H_ +#define SYSTEM_KEYMASTER_KEYMASTER_CONTEXT_H_ + +#include <assert.h> + +#include <openssl/evp.h> + +#include <hardware/keymaster_defs.h> +#include <keymaster/keymaster_enforcement.h> + +namespace keymaster { + +class AuthorizationSet; +class KeyFactory; +class OperationFactory; +struct KeymasterKeyBlob; + +/** + * KeymasterContext provides a singleton abstract interface that encapsulates various + * environment-dependent elements of AndroidKeymaster. + * + * AndroidKeymaster runs in multiple contexts. Primarily: + * + * - In a trusted execution environment (TEE) as a "secure hardware" implementation. In this + * context keys are wrapped with an master key that never leaves the TEE, TEE-specific routines + * are used for random number generation, all AndroidKeymaster-enforced authorizations are + * considered hardware-enforced, and there's a bootloader-provided root of trust. + * + * - In the non-secure world as a software-only implementation. In this context keys are not + * encrypted (though they are integrity-checked) because there is no place to securely store a + * key, OpenSSL is used for random number generation, no AndroidKeymaster-enforced authorizations + * are considered hardware enforced and the root of trust is a static string. + * + * - In the non-secure world as a hybrid implementation fronting a less-capable hardware + * implementation. For example, a keymaster0 hardware implementation. In this context keys are + * not encrypted by AndroidKeymaster, but some may be opaque blobs provided by the backing + * hardware, but blobs that lack the extended authorization lists of keymaster1. In addition, + * keymaster0 lacks many features of keymaster1, including modes of operation related to the + * backing keymaster0 keys. AndroidKeymaster must extend the blobs to add authorization lists, + * and must provide the missing operation mode implementations in software, which means that + * authorization lists are partially hardware-enforced (the bits that are enforced by the + * underlying keymaster0) and partially software-enforced (the rest). OpenSSL is used for number + * generation and the root of trust is a static string. + * + * More contexts are possible. + */ +class KeymasterContext { + public: + KeymasterContext() {} + virtual ~KeymasterContext(){}; + + virtual KeyFactory* GetKeyFactory(keymaster_algorithm_t algorithm) const = 0; + virtual OperationFactory* GetOperationFactory(keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose) const = 0; + virtual keymaster_algorithm_t* GetSupportedAlgorithms(size_t* algorithms_count) const = 0; + + /** + * CreateKeyBlob takes authorization sets and key material and produces a key blob and hardware + * and software authorization lists ready to be returned to the AndroidKeymaster client + * (Keystore, generally). The blob is integrity-checked and may be encrypted, depending on the + * needs of the context. + * + * This method is generally called only by KeyFactory subclassses. + */ + virtual keymaster_error_t CreateKeyBlob(const AuthorizationSet& key_description, + keymaster_key_origin_t origin, + const KeymasterKeyBlob& key_material, + KeymasterKeyBlob* blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const = 0; + + /** + * ParseKeyBlob takes a blob and extracts authorization sets and key material, returning an + * error if the blob fails integrity checking or decryption. Note that the returned key + * material may itself be an opaque blob usable only by secure hardware (in the hybrid case). + * + * This method is called by AndroidKeymaster. + */ + virtual keymaster_error_t ParseKeyBlob(const KeymasterKeyBlob& blob, + const AuthorizationSet& additional_params, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const = 0; + + /** + * Take whatever environment-specific action is appropriate (if any) to delete the specified + * key. + */ + virtual keymaster_error_t DeleteKey(const KeymasterKeyBlob& /* blob */) const { + return KM_ERROR_OK; + } + + /** + * Take whatever environment-specific action is appropriate to delete all keys. + */ + virtual keymaster_error_t DeleteAllKeys() const { return KM_ERROR_OK; } + + /** + * Adds entropy to the Cryptographic Pseudo Random Number Generator used to generate key + * material, and other cryptographic protocol elements. Note that if the underlying CPRNG + * tracks the size of its entropy pool, it should not assume that the provided data contributes + * any entropy, and it should also ensure that data provided through this interface cannot + * "poison" the CPRNG outputs, making them predictable. + */ + virtual keymaster_error_t AddRngEntropy(const uint8_t* buf, size_t length) const = 0; + + /** + * Generates \p length random bytes, placing them in \p buf. + */ + virtual keymaster_error_t GenerateRandom(uint8_t* buf, size_t length) const = 0; + + /** + * Return the enforcement policy for this context, or null if no enforcement should be done. + */ + virtual KeymasterEnforcement* enforcement_policy() = 0; + + /** + * Return the attestation signing key of the specified algorithm (KM_ALGORITHM_RSA or + * KM_ALGORITHM_EC). + */ + virtual EVP_PKEY* AttestationKey(keymaster_algorithm_t algorithm, + keymaster_error_t* error) const = 0; + + /** + * Return the certificate chain of the attestation signing key of the specified algorithm + * (KM_ALGORITHM_RSA or KM_ALGORITHM_EC). + */ + virtual keymaster_cert_chain_t* AttestationChain(keymaster_algorithm_t algorithm, + keymaster_error_t* error) const = 0; + + private: + // Uncopyable. + KeymasterContext(const KeymasterContext&); + void operator=(const KeymasterContext&); +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KEYMASTER_CONTEXT_H_
diff --git a/keymaster/include/keymaster/keymaster_enforcement.h b/keymaster/include/keymaster/keymaster_enforcement.h new file mode 100644 index 0000000..69ef5e3 --- /dev/null +++ b/keymaster/include/keymaster/keymaster_enforcement.h
@@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 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 ANDROID_LIBRARY_KEYMASTER_ENFORCEMENT_H +#define ANDROID_LIBRARY_KEYMASTER_ENFORCEMENT_H + +#include <stdio.h> + +#include <keymaster/authorization_set.h> + +namespace keymaster { + +typedef uint64_t km_id_t; + +class KeymasterEnforcementContext { + public: + virtual ~KeymasterEnforcementContext() {} + /* + * Get current time. + */ +}; + +class AccessTimeMap; +class AccessCountMap; + +class KeymasterEnforcement { + public: + /** + * Construct a KeymasterEnforcement. + */ + KeymasterEnforcement(uint32_t max_access_time_map_size, uint32_t max_access_count_map_size); + virtual ~KeymasterEnforcement(); + + /** + * Iterates through the authorization set and returns the corresponding keymaster error. Will + * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with + * the given operation params and handle. Used for encrypt, decrypt sign, and verify. + */ + keymaster_error_t AuthorizeOperation(const keymaster_purpose_t purpose, const km_id_t keyid, + const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params, + keymaster_operation_handle_t op_handle, + bool is_begin_operation); + + /** + * Iterates through the authorization set and returns the corresponding keymaster error. Will + * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with + * the given operation params. Used for encrypt, decrypt sign, and verify. + */ + keymaster_error_t AuthorizeBegin(const keymaster_purpose_t purpose, const km_id_t keyid, + const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params); + + /** + * Iterates through the authorization set and returns the corresponding keymaster error. Will + * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with + * the given operation params and handle. Used for encrypt, decrypt sign, and verify. + */ + keymaster_error_t AuthorizeUpdate(const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params, + keymaster_operation_handle_t op_handle) { + return AuthorizeUpdateOrFinish(auth_set, operation_params, op_handle); + } + + /** + * Iterates through the authorization set and returns the corresponding keymaster error. Will + * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with + * the given operation params and handle. Used for encrypt, decrypt sign, and verify. + */ + keymaster_error_t AuthorizeFinish(const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params, + keymaster_operation_handle_t op_handle) { + return AuthorizeUpdateOrFinish(auth_set, operation_params, op_handle); + } + + /** + * Creates a key ID for use in subsequent calls to AuthorizeOperation. Clients needn't use this + * method of creating key IDs, as long as they use something consistent and unique. This method + * hashes the key blob. + * + * Returns false if an error in the crypto library prevents creation of an ID. + */ + static bool CreateKeyId(const keymaster_key_blob_t& key_blob, km_id_t* keyid); + + // + // Methods that must be implemented by subclasses + // + // The time-related methods address the fact that different enforcement contexts may have + // different time-related capabilities. In particular: + // + // - They may or may not be able to check dates against real-world clocks. + // + // - They may or may not be able to check timestampls against authentication trustlets (minters + // of hw_auth_token_t structs). + // + // - They must have some time source for relative times, but may not be able to provide more + // than reliability and monotonicity. + + /* + * Returns true if the specified activation date has passed, or if activation cannot be + * enforced. + */ + virtual bool activation_date_valid(uint64_t activation_date) const = 0; + + /* + * Returns true if the specified expiration date has passed. Returns false if it has not, or if + * expiration cannot be enforced. + */ + virtual bool expiration_date_passed(uint64_t expiration_date) const = 0; + + /* + * Returns true if the specified auth_token is older than the specified timeout. + */ + virtual bool auth_token_timed_out(const hw_auth_token_t& token, uint32_t timeout) const = 0; + + /* + * Get current time in seconds from some starting point. This value is used to compute relative + * times between events. It must be monotonically increasing, and must not skip or lag. It + * need not have any relation to any external time standard (other than the duration of + * "second"). + * + * On POSIX systems, it's recommented to use clock_gettime(CLOCK_MONOTONIC, ...) to implement + * this method. + */ + virtual uint32_t get_current_time() const = 0; + + /* + * Returns true if the specified auth_token has a valid signature, or if signature validation is + * not available. + */ + virtual bool ValidateTokenSignature(const hw_auth_token_t& token) const = 0; + + private: + keymaster_error_t AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params, + keymaster_operation_handle_t op_handle); + + bool MinTimeBetweenOpsPassed(uint32_t min_time_between, const km_id_t keyid); + bool MaxUsesPerBootNotExceeded(const km_id_t keyid, uint32_t max_uses); + bool AuthTokenMatches(const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params, const uint64_t user_secure_id, + const int auth_type_index, const int auth_timeout_index, + const keymaster_operation_handle_t op_handle, + bool is_begin_operation) const; + + AccessTimeMap* access_time_map_; + AccessCountMap* access_count_map_; +}; + +}; /* namespace keymaster */ + +#endif // ANDROID_LIBRARY_KEYMASTER_ENFORCEMENT_H
diff --git a/keymaster/include/keymaster/keymaster_tags.h b/keymaster/include/keymaster/keymaster_tags.h new file mode 100644 index 0000000..dcaf0da --- /dev/null +++ b/keymaster/include/keymaster/keymaster_tags.h
@@ -0,0 +1,258 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_KEYMASTER_TAGS_H_ +#define SYSTEM_KEYMASTER_KEYMASTER_TAGS_H_ + +/** + * This header contains various definitions that make working with keymaster tags safer and easier. + * It makes use of a fair amount of template metaprogramming, which is genarally a bad idea for + * maintainability, but in this case all of the metaprogramming serves the purpose of making it + * impossible to make certain classes of mistakes when operating on keymaster authorizations. For + * example, it's an error to create a keymaster_param_t with tag == KM_TAG_PURPOSE and then to + * assign KM_ALGORITHM_RSA to the enumerated element of its union, but because "enumerated" is a + * uint32_t, there's no way for the compiler, ordinarily, to diagnose it. Also, generic functions + * to manipulate authorizations of multiple types can't be written, because they need to know which + * union parameter to modify. + * + * The machinery in this header solves these problems. The core elements are two templated classes, + * TypedTag and TypedEnumTag. These classes are templated on a tag type and a tag value, and in the + * case of TypedEnumTag, an enumeration type as well. Specializations are created for each + * keymaster tag, associating the tag type with the tag, and an instance of each specialization is + * created, and named the same as the keymaster tag, but with the KM_ prefix omitted. Because the + * classes include a conversion operator to keymaster_tag_t, they can be used anywhere a + * keymaster_tag_t is expected. + * + * They also define a "value_type" typedef, which specifies the type of values associated with that + * particular tag. This enables template functions to be written that check that the correct + * parameter type is used for a given tag, and that use the correct union entry for the tag type. A + * very useful example is the overloaded "Authorization" function defined below, which takes tag and + * value arguments and correctly constructs a keyamster_param_t struct. + * + * Because the classes have no data members and all of their methods are inline, they have ZERO + * run-time cost in and of themselves. The one way in which they can create code bloat is when + * template functions using them are expanded multiple times. The standard method of creating + * trivial, inlined template functions which call non-templated functions which are compact but not + * type-safe, allows the program to have both the type-safety of the templates and the compactness + * of the non-templated functions, at the same time. + */ + +#include <hardware/hw_auth_token.h> +#include <hardware/keymaster_defs.h> + +namespace keymaster { + +// The following create the numeric values that KM_TAG_PADDING and KM_TAG_DIGEST used to have. We +// need these old values to be able to support old keys that use them. +static const keymaster_tag_t KM_TAG_DIGEST_OLD = static_cast<keymaster_tag_t>(KM_ENUM | 5); +static const keymaster_tag_t KM_TAG_PADDING_OLD = static_cast<keymaster_tag_t>(KM_ENUM | 7); + +// Until we have C++11, fake std::static_assert. +template <bool b> struct StaticAssert {}; +template <> struct StaticAssert<true> { + static void check() {} +}; + +// An unusable type that we can associate with tag types that don't have a simple value type. +// That will prevent the associated type from being used inadvertently. +class Void { + Void(); + ~Void(); +}; + +/** + * A template that defines the association between non-enumerated tag types and their value + * types. For each tag type we define a specialized struct that contains a typedef "value_type". + */ +template <keymaster_tag_type_t tag_type> struct TagValueType {}; +template <> struct TagValueType<KM_ULONG> { typedef uint64_t value_type; }; +template <> struct TagValueType<KM_ULONG_REP> { typedef uint64_t value_type; }; +template <> struct TagValueType<KM_DATE> { typedef uint64_t value_type; }; +template <> struct TagValueType<KM_UINT> { typedef uint32_t value_type; }; +template <> struct TagValueType<KM_UINT_REP> { typedef uint32_t value_type; }; +template <> struct TagValueType<KM_INVALID> { typedef Void value_type; }; +template <> struct TagValueType<KM_BOOL> { typedef bool value_type; }; +template <> struct TagValueType<KM_BYTES> { typedef keymaster_blob_t value_type; }; +template <> struct TagValueType<KM_BIGNUM> { typedef keymaster_blob_t value_type; }; + +/** + * TypedTag is a templatized version of keymaster_tag_t, which provides compile-time checking of + * keymaster tag types. Instances are convertible to keymaster_tag_t, so they can be used wherever + * keymaster_tag_t is expected, and because they encode the tag type it's possible to create + * function overloadings that only operate on tags with a particular type. + */ +template <keymaster_tag_type_t tag_type, keymaster_tag_t tag> class TypedTag { + public: + typedef typename TagValueType<tag_type>::value_type value_type; + + inline TypedTag() { + // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type + // 'tag_type'. Attempting to instantiate a tag with the wrong type will result in a compile + // error (no match for template specialization StaticAssert<false>), with no run-time cost. + StaticAssert<(tag & tag_type) == tag_type>::check(); + StaticAssert<(tag_type != KM_ENUM) && (tag_type != KM_ENUM_REP)>::check(); + } + inline operator keymaster_tag_t() { return tag; } + inline long masked_tag() { return static_cast<long>(keymaster_tag_mask_type(tag)); } +}; + +template <keymaster_tag_type_t tag_type, keymaster_tag_t tag, typename KeymasterEnum> +class TypedEnumTag { + public: + typedef KeymasterEnum value_type; + + inline TypedEnumTag() { + // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type + // 'tag_type'. Attempting to instantiate a tag with the wrong type will result in a compile + // error (no match for template specialization StaticAssert<false>), with no run-time cost. + StaticAssert<(tag & tag_type) == tag_type>::check(); + StaticAssert<(tag_type == KM_ENUM) || (tag_type == KM_ENUM_REP)>::check(); + } + inline operator keymaster_tag_t() { return tag; } + inline long masked_tag() { return static_cast<long>(keymaster_tag_mask_type(tag)); } +}; + +#ifdef KEYMASTER_NAME_TAGS +const char* StringifyTag(keymaster_tag_t tag); +#endif + +// DECLARE_KEYMASTER_TAG is used to declare TypedTag instances for each non-enum keymaster tag. +#define DECLARE_KEYMASTER_TAG(type, name) extern TypedTag<type, KM_##name> name + +DECLARE_KEYMASTER_TAG(KM_INVALID, TAG_INVALID); +DECLARE_KEYMASTER_TAG(KM_UINT, TAG_KEY_SIZE); +DECLARE_KEYMASTER_TAG(KM_UINT, TAG_MAC_LENGTH); +DECLARE_KEYMASTER_TAG(KM_BOOL, TAG_CALLER_NONCE); +DECLARE_KEYMASTER_TAG(KM_UINT, TAG_MIN_MAC_LENGTH); +DECLARE_KEYMASTER_TAG(KM_ULONG, TAG_RSA_PUBLIC_EXPONENT); +DECLARE_KEYMASTER_TAG(KM_BOOL, TAG_ECIES_SINGLE_HASH_MODE); +DECLARE_KEYMASTER_TAG(KM_BOOL, TAG_INCLUDE_UNIQUE_ID); +DECLARE_KEYMASTER_TAG(KM_DATE, TAG_ACTIVE_DATETIME); +DECLARE_KEYMASTER_TAG(KM_DATE, TAG_ORIGINATION_EXPIRE_DATETIME); +DECLARE_KEYMASTER_TAG(KM_DATE, TAG_USAGE_EXPIRE_DATETIME); +DECLARE_KEYMASTER_TAG(KM_UINT, TAG_MIN_SECONDS_BETWEEN_OPS); +DECLARE_KEYMASTER_TAG(KM_UINT, TAG_MAX_USES_PER_BOOT); +DECLARE_KEYMASTER_TAG(KM_BOOL, TAG_ALL_USERS); +DECLARE_KEYMASTER_TAG(KM_UINT, TAG_USER_ID); +DECLARE_KEYMASTER_TAG(KM_ULONG_REP, TAG_USER_SECURE_ID); +DECLARE_KEYMASTER_TAG(KM_BOOL, TAG_NO_AUTH_REQUIRED); +DECLARE_KEYMASTER_TAG(KM_UINT, TAG_AUTH_TIMEOUT); +DECLARE_KEYMASTER_TAG(KM_BOOL, TAG_ALLOW_WHILE_ON_BODY); +DECLARE_KEYMASTER_TAG(KM_BOOL, TAG_ALL_APPLICATIONS); +DECLARE_KEYMASTER_TAG(KM_BYTES, TAG_APPLICATION_ID); +DECLARE_KEYMASTER_TAG(KM_BYTES, TAG_APPLICATION_DATA); +DECLARE_KEYMASTER_TAG(KM_DATE, TAG_CREATION_DATETIME); +DECLARE_KEYMASTER_TAG(KM_BOOL, TAG_ROLLBACK_RESISTANT); +DECLARE_KEYMASTER_TAG(KM_BYTES, TAG_ROOT_OF_TRUST); +DECLARE_KEYMASTER_TAG(KM_BYTES, TAG_ASSOCIATED_DATA); +DECLARE_KEYMASTER_TAG(KM_BYTES, TAG_NONCE); +DECLARE_KEYMASTER_TAG(KM_BYTES, TAG_AUTH_TOKEN); +DECLARE_KEYMASTER_TAG(KM_BOOL, TAG_BOOTLOADER_ONLY); +DECLARE_KEYMASTER_TAG(KM_UINT, TAG_OS_VERSION); +DECLARE_KEYMASTER_TAG(KM_UINT, TAG_OS_PATCHLEVEL); +DECLARE_KEYMASTER_TAG(KM_BYTES, TAG_UNIQUE_ID); + +// DECLARE_KEYMASTER_ENUM_TAG is used to declare TypedEnumTag instances for each enum keymaster tag. +#define DECLARE_KEYMASTER_ENUM_TAG(type, name, enumtype) \ + extern TypedEnumTag<type, KM_##name, enumtype> name + +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_PURPOSE, keymaster_purpose_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_ALGORITHM, keymaster_algorithm_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_BLOCK_MODE, keymaster_block_mode_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_DIGEST, keymaster_digest_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_DIGEST_OLD, keymaster_digest_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_PADDING, keymaster_padding_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_PADDING_OLD, keymaster_padding_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_BLOB_USAGE_REQUIREMENTS, + keymaster_key_blob_usage_requirements_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_ORIGIN, keymaster_key_origin_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_USER_AUTH_TYPE, hw_authenticator_type_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_KDF, keymaster_kdf_t); +DECLARE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_EC_CURVE, keymaster_ec_curve_t); + +// +// Overloaded function "Authorization" to create keymaster_key_param_t objects for all of tags. +// + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_BOOL, Tag> tag) { + return keymaster_param_bool(tag); +} + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_UINT, Tag> tag, uint32_t value) { + return keymaster_param_int(tag, value); +} + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_UINT_REP, Tag> tag, uint32_t value) { + return keymaster_param_int(tag, value); +} + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_ULONG, Tag> tag, uint64_t value) { + return keymaster_param_long(tag, value); +} + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_ULONG_REP, Tag> tag, uint64_t value) { + return keymaster_param_long(tag, value); +} + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_DATE, Tag> tag, uint64_t value) { + return keymaster_param_date(tag, value); +} + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_BYTES, Tag> tag, const void* bytes, + size_t bytes_len) { + return keymaster_param_blob(tag, reinterpret_cast<const uint8_t*>(bytes), bytes_len); +} + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_BYTES, Tag> tag, + const keymaster_blob_t& blob) { + return keymaster_param_blob(tag, blob.data, blob.data_length); +} + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_BIGNUM, Tag> tag, const void* bytes, + size_t bytes_len) { + return keymaster_param_blob(tag, reinterpret_cast<const uint8_t*>(bytes), bytes_len); +} + +template <keymaster_tag_t Tag> +inline keymaster_key_param_t Authorization(TypedTag<KM_BIGNUM, Tag> tag, + const keymaster_blob_t& blob) { + return keymaster_param_blob(tag, blob.data, blob.data_length); +} + +template <keymaster_tag_t Tag, typename KeymasterEnum> +inline keymaster_key_param_t Authorization(TypedEnumTag<KM_ENUM, Tag, KeymasterEnum> tag, + KeymasterEnum value) { + return keymaster_param_enum(tag, value); +} + +template <keymaster_tag_t Tag, typename KeymasterEnum> +inline keymaster_key_param_t Authorization(TypedEnumTag<KM_ENUM_REP, Tag, KeymasterEnum> tag, + KeymasterEnum value) { + return keymaster_param_enum(tag, value); +} + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KEYMASTER_TAGS_H_
diff --git a/keymaster/include/keymaster/logger.h b/keymaster/include/keymaster/logger.h new file mode 100644 index 0000000..a4fdea3 --- /dev/null +++ b/keymaster/include/keymaster/logger.h
@@ -0,0 +1,70 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_LOGGER_H_ +#define SYSTEM_KEYMASTER_LOGGER_H_ + +#include <stdarg.h> + +namespace keymaster { + +class Logger { + public: + Logger() {} + virtual ~Logger() {} + + enum LogLevel { + DEBUG_LVL, // Messages used only for debugging + INFO_LVL, // Informational messages; something is unusual but not wrong + WARNING_LVL, // There's an indication of trouble, but it may be okay. + ERROR_LVL, // A problem has occurred, but processing can continue + SEVERE_LVL, // A severe problem has occurred; likely indicates a defect. + }; + + virtual int log_msg(LogLevel level, const char* fmt, va_list args) const = 0; + + static int Log(LogLevel level, const char* fmt, va_list args); + static int Log(LogLevel level, const char* fmt, ...); + static int Debug(const char* fmt, ...); + static int Info(const char* fmt, ...); + static int Warning(const char* fmt, ...); + static int Error(const char* fmt, ...); + static int Severe(const char* fmt, ...); + + protected: + static void set_instance(Logger* logger) { instance_ = logger; } + + private: + // Disallow copying. + Logger(const Logger&); + void operator=(const Logger&); + + static Logger* instance_; +}; + +#define STR(x) #x +#define STRINGIFY(x) STR(x) +#define FILE_LINE __FILE__ ", Line " STRINGIFY(__LINE__) ": " + +#define LOG_D(fmt, ...) Logger::Debug(FILE_LINE fmt, __VA_ARGS__) +#define LOG_I(fmt, ...) Logger::Info(FILE_LINE fmt, __VA_ARGS__) +#define LOG_W(fmt, ...) Logger::Warning(FILE_LINE fmt, __VA_ARGS__) +#define LOG_E(fmt, ...) Logger::Error(FILE_LINE fmt, __VA_ARGS__) +#define LOG_S(fmt, ...) Logger::Severe(FILE_LINE fmt, __VA_ARGS__) + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_LOGGER_H_
diff --git a/keymaster/include/keymaster/rsa_key_factory.h b/keymaster/include/keymaster/rsa_key_factory.h new file mode 100644 index 0000000..f651652 --- /dev/null +++ b/keymaster/include/keymaster/rsa_key_factory.h
@@ -0,0 +1,60 @@ +/* + * 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 SYSTEM_KEYMASTER_RSA_KEY_FACTORY_H_ +#define SYSTEM_KEYMASTER_RSA_KEY_FACTORY_H_ + +#include <openssl/evp.h> +#include <openssl/rsa.h> + +#include <keymaster/asymmetric_key_factory.h> + +namespace keymaster { + +class RsaKeyFactory : public AsymmetricKeyFactory { + public: + explicit RsaKeyFactory(const KeymasterContext* context) : AsymmetricKeyFactory(context) {} + + keymaster_error_t GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + keymaster_error_t ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t CreateEmptyKey(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<AsymmetricKey>* key) const override; + + OperationFactory* GetOperationFactory(keymaster_purpose_t purpose) const override; + + keymaster_algorithm_t keymaster_key_type() const override { return KM_ALGORITHM_RSA; } + int evp_key_type() const override { return EVP_PKEY_RSA; } + + protected: + keymaster_error_t UpdateImportKeyDescription(const AuthorizationSet& key_description, + keymaster_key_format_t import_key_format, + const KeymasterKeyBlob& import_key_material, + AuthorizationSet* updated_description, + uint64_t* public_exponent, + uint32_t* key_size) const; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_RSA_KEY_FACTORY_H_
diff --git a/keymaster/include/keymaster/serializable.h b/keymaster/include/keymaster/serializable.h new file mode 100644 index 0000000..8748c55 --- /dev/null +++ b/keymaster/include/keymaster/serializable.h
@@ -0,0 +1,253 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_SERIALIZABLE_H_ +#define SYSTEM_KEYMASTER_SERIALIZABLE_H_ + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <cstddef> +#include <new> + +#include <UniquePtr.h> + +namespace keymaster { + +class Serializable { + public: + Serializable() {} + virtual ~Serializable() {} + + /** + * Return the size of the serialized representation of this object. + */ + virtual size_t SerializedSize() const = 0; + + /** + * Serialize this object into the provided buffer. Returns a pointer to the byte after the last + * written. Will not write past \p end, which should point to \p buf + size of the buffer + * (i.e. one past the end of the buffer). + */ + virtual uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const = 0; + + /** + * Deserialize from the provided buffer, copying the data into newly-allocated storage. Returns + * true if successful, and advances *buf past the bytes read. + */ + virtual bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) = 0; + + private: + // Disallow copying and assignment. + Serializable(const Serializable&); + void operator=(const Serializable&); +}; + +/* + * Utility functions for writing Serialize() methods + */ + +/** + * Append a byte array to a buffer. Note that by itself this function isn't very useful, because it + * provides no indication in the serialized buffer of what the array size is. For writing arrays, + * see \p append_size_and_data_to_buf(). + * + * Returns a pointer to the first byte after the data written. + */ +uint8_t* append_to_buf(uint8_t* buf, const uint8_t* end, const void* data, size_t data_len); + +/** + * Append some type of value convertible to a uint32_t to a buffer. This is primarily used for + * writing enumerated values, and uint32_ts. + * + * Returns a pointer to the first byte after the data written. + */ +template <typename T> +inline uint8_t* append_uint32_to_buf(uint8_t* buf, const uint8_t* end, T value) { + uint32_t val = static_cast<uint32_t>(value); + return append_to_buf(buf, end, &val, sizeof(val)); +} + +/** + * Append a uint64_t to a buffer. Returns a pointer to the first byte after the data written. + */ +inline uint8_t* append_uint64_to_buf(uint8_t* buf, const uint8_t* end, uint64_t value) { + return append_to_buf(buf, end, &value, sizeof(value)); +} + +/** + * Appends a byte array to a buffer, prefixing it with a 32-bit size field. Returns a pointer to + * the first byte after the data written. + * + * See copy_size_and_data_from_buf(). + */ +inline uint8_t* append_size_and_data_to_buf(uint8_t* buf, const uint8_t* end, const void* data, + size_t data_len) { + buf = append_uint32_to_buf(buf, end, data_len); + return append_to_buf(buf, end, data, data_len); +} + +/** + * Appends an array of values that are convertible to uint32_t as uint32ts to a buffer, prefixing a + * count so deserialization knows how many values to read. + * + * See copy_uint32_array_from_buf(). + */ +template <typename T> +inline uint8_t* append_uint32_array_to_buf(uint8_t* buf, const uint8_t* end, const T* data, + size_t count) { + // Check for overflow + if (count >= (UINT32_MAX / sizeof(uint32_t)) || buf + count * sizeof(uint32_t) < buf) + return buf; + buf = append_uint32_to_buf(buf, end, count); + for (size_t i = 0; i < count; ++i) + buf = append_uint32_to_buf(buf, end, static_cast<uint32_t>(data[i])); + return buf; +} + +/* + * Utility functions for writing Deserialize() methods. + */ + +/** + * Copy \p size bytes from \p *buf_ptr into \p dest. If there are fewer than \p size bytes to read, + * returns false. Advances *buf_ptr to the next byte to be read. + */ +bool copy_from_buf(const uint8_t** buf_ptr, const uint8_t* end, void* dest, size_t size); + +/** + * Extracts a uint32_t size from *buf_ptr, placing it in \p *size, and then reads *size bytes from + * *buf_ptr, placing them in newly-allocated storage in *dest. If there aren't enough bytes in + * *buf_ptr, returns false. Advances \p *buf_ptr to the next byte to be read. + * + * See \p append_size_and_data_to_buf(). + */ +bool copy_size_and_data_from_buf(const uint8_t** buf_ptr, const uint8_t* end, size_t* size, + UniquePtr<uint8_t[]>* dest); + +/** + * Copies a value convertible from uint32_t from \p *buf_ptr. Returns false if there are less than + * four bytes remaining in \p *buf_ptr. Advances \p *buf_ptr to the next byte to be read. + */ +template <typename T> +inline bool copy_uint32_from_buf(const uint8_t** buf_ptr, const uint8_t* end, T* value) { + uint32_t val; + if (!copy_from_buf(buf_ptr, end, &val, sizeof(val))) + return false; + *value = static_cast<T>(val); + return true; +} + +/** + * Copies a uint64_t from \p *buf_ptr. Returns false if there are less than eight bytes remaining + * in \p *buf_ptr. Advances \p *buf_ptr to the next byte to be read. + */ +inline bool copy_uint64_from_buf(const uint8_t** buf_ptr, const uint8_t* end, uint64_t* value) { + return copy_from_buf(buf_ptr, end, value, sizeof(*value)); +} + +/** + * Copies an array of values convertible to uint32_t from \p *buf_ptr, first reading a count of + * values to read. The count is returned in \p *count and the values returned in newly-allocated + * storage at *data. Returns false if there are insufficient bytes at \p *buf_ptr. Advances \p + * *buf_ptr to the next byte to be read. + */ +template <typename T> +inline bool copy_uint32_array_from_buf(const uint8_t** buf_ptr, const uint8_t* end, + UniquePtr<T[]>* data, size_t* count) { + if (!copy_uint32_from_buf(buf_ptr, end, count)) + return false; + + const uint8_t* array_end = *buf_ptr + *count * sizeof(uint32_t); + if (*count >= UINT32_MAX / sizeof(uint32_t) || array_end < *buf_ptr || array_end > end) + return false; + + data->reset(new (std::nothrow) T[*count]); + if (!data->get()) + return false; + for (size_t i = 0; i < *count; ++i) + if (!copy_uint32_from_buf(buf_ptr, end, &(*data)[i])) + return false; + return true; +} + +/** + * A simple buffer that supports reading and writing. Manages its own memory. + */ +class Buffer : public Serializable { + public: + Buffer() : buffer_(NULL), buffer_size_(0), read_position_(0), write_position_(0) {} + explicit Buffer(size_t size) : buffer_(NULL) { Reinitialize(size); } + Buffer(const void* buf, size_t size) : buffer_(NULL) { Reinitialize(buf, size); } + + // Grow the buffer so that at least \p size bytes can be written. + bool reserve(size_t size); + + bool Reinitialize(size_t size); + bool Reinitialize(const void* buf, size_t size); + + // Reinitialize with a copy of the provided buffer's readable data. + bool Reinitialize(const Buffer& buffer) { + return Reinitialize(buffer.peek_read(), buffer.available_read()); + } + + const uint8_t* begin() const { return peek_read(); } + const uint8_t* end() const { return peek_read() + available_read(); } + + void Clear(); + + size_t available_write() const; + size_t available_read() const; + size_t buffer_size() const { return buffer_size_; } + + bool write(const uint8_t* src, size_t write_length); + bool read(uint8_t* dest, size_t read_length); + const uint8_t* peek_read() const { return buffer_.get() + read_position_; } + bool advance_read(int distance) { + if (static_cast<size_t>(read_position_ + distance) <= write_position_) { + read_position_ += distance; + return true; + } + return false; + } + uint8_t* peek_write() { return buffer_.get() + write_position_; } + bool advance_write(int distance) { + if (static_cast<size_t>(write_position_ + distance) <= buffer_size_) { + write_position_ += distance; + return true; + } + return false; + } + + size_t SerializedSize() const; + uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const; + bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end); + + private: + // Disallow copy construction and assignment. + void operator=(const Buffer& other); + Buffer(const Buffer&); + + UniquePtr<uint8_t[]> buffer_; + size_t buffer_size_; + size_t read_position_; + size_t write_position_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_SERIALIZABLE_H_
diff --git a/keymaster/include/keymaster/soft_keymaster_context.h b/keymaster/include/keymaster/soft_keymaster_context.h new file mode 100644 index 0000000..2091d6f --- /dev/null +++ b/keymaster/include/keymaster/soft_keymaster_context.h
@@ -0,0 +1,115 @@ +/* + * 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 SYSTEM_KEYMASTER_SOFT_KEYMASTER_CONTEXT_H_ +#define SYSTEM_KEYMASTER_SOFT_KEYMASTER_CONTEXT_H_ + +#include <memory> +#include <string> + +#include <openssl/evp.h> + +#include <hardware/keymaster0.h> +#include <hardware/keymaster1.h> +#include <keymaster/keymaster_context.h> + +namespace keymaster { + +class SoftKeymasterKeyRegistrations; +class Keymaster0Engine; +class Keymaster1Engine; + +/** + * SoftKeymasterContext provides the context for a non-secure implementation of AndroidKeymaster. + */ +class SoftKeymasterContext : public KeymasterContext { + public: + explicit SoftKeymasterContext(const std::string& root_of_trust = "SW"); + ~SoftKeymasterContext() override; + + /** + * Use the specified HW keymaster0 device for the operations it supports. Takes ownership of + * the specified device (will call keymaster0_device->common.close()); + */ + keymaster_error_t SetHardwareDevice(keymaster0_device_t* keymaster0_device); + + /** + * Use the specified HW keymaster1 device for performing undigested RSA and EC operations after + * digesting has been done in software. Takes ownership of the specified device (will call + * keymaster1_device->common.close()); + */ + keymaster_error_t SetHardwareDevice(keymaster1_device_t* keymaster1_device); + + KeyFactory* GetKeyFactory(keymaster_algorithm_t algorithm) const override; + OperationFactory* GetOperationFactory(keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose) const override; + keymaster_algorithm_t* GetSupportedAlgorithms(size_t* algorithms_count) const override; + keymaster_error_t CreateKeyBlob(const AuthorizationSet& auths, keymaster_key_origin_t origin, + const KeymasterKeyBlob& key_material, KeymasterKeyBlob* blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t ParseKeyBlob(const KeymasterKeyBlob& blob, + const AuthorizationSet& additional_params, + KeymasterKeyBlob* key_material, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + keymaster_error_t DeleteKey(const KeymasterKeyBlob& blob) const override; + keymaster_error_t DeleteAllKeys() const override; + keymaster_error_t AddRngEntropy(const uint8_t* buf, size_t length) const override; + keymaster_error_t GenerateRandom(uint8_t* buf, size_t length) const override; + + EVP_PKEY* AttestationKey(keymaster_algorithm_t algorithm, + keymaster_error_t* error) const override; + keymaster_cert_chain_t* AttestationChain(keymaster_algorithm_t algorithm, + keymaster_error_t* error) const override; + + KeymasterEnforcement* enforcement_policy() override { + // SoftKeymaster does no enforcement; it's all done by Keystore. + return nullptr; + } + + private: + keymaster_error_t ParseOldSoftkeymasterBlob(const KeymasterKeyBlob& blob, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const; + keymaster_error_t ParseKeymaster1HwBlob(const KeymasterKeyBlob& blob, + const AuthorizationSet& additional_params, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const; + keymaster_error_t ParseKeymaster0HwBlob(const KeymasterKeyBlob& blob, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const; + keymaster_error_t FakeKeyAuthorizations(EVP_PKEY* pubkey, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const; + keymaster_error_t BuildHiddenAuthorizations(const AuthorizationSet& input_set, + AuthorizationSet* hidden) const; + + std::unique_ptr<Keymaster0Engine> km0_engine_; + std::unique_ptr<Keymaster1Engine> km1_engine_; + std::unique_ptr<KeyFactory> rsa_factory_; + std::unique_ptr<KeyFactory> ec_factory_; + std::unique_ptr<KeyFactory> aes_factory_; + std::unique_ptr<KeyFactory> hmac_factory_; + keymaster1_device* km1_dev_; + const std::string root_of_trust_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_SOFT_KEYMASTER_CONTEXT_H_
diff --git a/keymaster/include/keymaster/soft_keymaster_device.h b/keymaster/include/keymaster/soft_keymaster_device.h new file mode 100644 index 0000000..4a734f1 --- /dev/null +++ b/keymaster/include/keymaster/soft_keymaster_device.h
@@ -0,0 +1,245 @@ +/* + * 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 SYSTEM_KEYMASTER_SOFT_KEYMASTER_DEVICE_H_ +#define SYSTEM_KEYMASTER_SOFT_KEYMASTER_DEVICE_H_ + +#include <cstdlib> +#include <map> +#include <vector> + +#include <hardware/keymaster0.h> +#include <hardware/keymaster1.h> +#include <hardware/keymaster2.h> + +#include <keymaster/android_keymaster.h> +#include <keymaster/soft_keymaster_context.h> + +#include <UniquePtr.h> + +namespace keymaster { + +class AuthorizationSet; + +/** + * Keymaster1 device implementation. + * + * This is a hybrid software/hardware implementation which wraps a keymaster0_device_t, forwarding + * RSA operations to secure hardware and doing everything else in software. + * + * IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t + * and keymaster_device. This means it must remain a standard layout class (no virtual functions and + * no data members which aren't standard layout), and device_ must be the first data member. + * Assertions in the constructor validate compliance with those constraints. + */ +class SoftKeymasterDevice { + public: + SoftKeymasterDevice(); + + // Public only for testing. + explicit SoftKeymasterDevice(SoftKeymasterContext* context); + + /** + * Set SoftKeymasterDevice to wrap the speicified HW keymaster0 device. Takes ownership of the + * specified device (will call keymaster0_device->common.close()); + */ + keymaster_error_t SetHardwareDevice(keymaster0_device_t* keymaster0_device); + + /** + * Set SoftKeymasterDevice to wrap specified HW keymaster1 device. Takes ownership of the + * specified device (will call keymaster1_device->common.close()); + */ + keymaster_error_t SetHardwareDevice(keymaster1_device_t* keymaster1_device); + + /** + * Returns true if a keymaster1_device_t has been set as the hardware device, and if that + * hardware device should be used directly. + */ + bool Keymaster1DeviceIsGood(); + + hw_device_t* hw_device(); + keymaster1_device_t* keymaster_device(); + keymaster2_device_t* keymaster2_device(); + + // Public only for testing + void GetVersion(const GetVersionRequest& req, GetVersionResponse* rsp) { + impl_->GetVersion(req, rsp); + } + + typedef std::pair<keymaster_algorithm_t, keymaster_purpose_t> AlgPurposePair; + typedef std::map<AlgPurposePair, std::vector<keymaster_digest_t>> DigestMap; + + private: + void initialize_device_struct(uint32_t flags); + bool FindUnsupportedDigest(keymaster_algorithm_t algorithm, keymaster_purpose_t purpose, + const AuthorizationSet& params, + keymaster_digest_t* unsupported) const; + bool RequiresSoftwareDigesting(keymaster_algorithm_t algorithm, keymaster_purpose_t purpose, + const AuthorizationSet& params) const; + bool KeyRequiresSoftwareDigesting(const AuthorizationSet& key_description) const; + + static void StoreDefaultNewKeyParams(keymaster_algorithm_t algorithm, + AuthorizationSet* auth_set); + static keymaster_error_t GetPkcs8KeyAlgorithm(const uint8_t* key, size_t key_length, + keymaster_algorithm_t* algorithm); + + static int close_device(hw_device_t* dev); + + /* + * These static methods are the functions referenced through the function pointers in + * keymaster_device. + */ + + // Keymaster1 methods + static keymaster_error_t get_supported_algorithms(const keymaster1_device_t* dev, + keymaster_algorithm_t** algorithms, + size_t* algorithms_length); + static keymaster_error_t get_supported_block_modes(const keymaster1_device_t* dev, + keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + keymaster_block_mode_t** modes, + size_t* modes_length); + static keymaster_error_t get_supported_padding_modes(const keymaster1_device_t* dev, + keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + keymaster_padding_t** modes, + size_t* modes_length); + static keymaster_error_t get_supported_digests(const keymaster1_device_t* dev, + keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + keymaster_digest_t** digests, + size_t* digests_length); + static keymaster_error_t get_supported_import_formats(const keymaster1_device_t* dev, + keymaster_algorithm_t algorithm, + keymaster_key_format_t** formats, + size_t* formats_length); + static keymaster_error_t get_supported_export_formats(const keymaster1_device_t* dev, + keymaster_algorithm_t algorithm, + keymaster_key_format_t** formats, + size_t* formats_length); + static keymaster_error_t add_rng_entropy(const keymaster1_device_t* dev, const uint8_t* data, + size_t data_length); + static keymaster_error_t generate_key(const keymaster1_device_t* dev, + const keymaster_key_param_set_t* params, + keymaster_key_blob_t* key_blob, + keymaster_key_characteristics_t** characteristics); + static keymaster_error_t get_key_characteristics(const keymaster1_device_t* dev, + const keymaster_key_blob_t* key_blob, + const keymaster_blob_t* client_id, + const keymaster_blob_t* app_data, + keymaster_key_characteristics_t** character); + static keymaster_error_t import_key(const keymaster1_device_t* dev, // + const keymaster_key_param_set_t* params, + keymaster_key_format_t key_format, + const keymaster_blob_t* key_data, + keymaster_key_blob_t* key_blob, + keymaster_key_characteristics_t** characteristics); + static keymaster_error_t export_key(const keymaster1_device_t* dev, // + keymaster_key_format_t export_format, + const keymaster_key_blob_t* key_to_export, + const keymaster_blob_t* client_id, + const keymaster_blob_t* app_data, + keymaster_blob_t* export_data); + static keymaster_error_t delete_key(const keymaster1_device_t* dev, + const keymaster_key_blob_t* key); + static keymaster_error_t delete_all_keys(const keymaster1_device_t* dev); + static keymaster_error_t begin(const keymaster1_device_t* dev, keymaster_purpose_t purpose, + const keymaster_key_blob_t* key, + const keymaster_key_param_set_t* in_params, + keymaster_key_param_set_t* out_params, + keymaster_operation_handle_t* operation_handle); + static keymaster_error_t update(const keymaster1_device_t* dev, // + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* input, size_t* input_consumed, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output); + static keymaster_error_t finish(const keymaster1_device_t* dev, // + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* signature, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output); + static keymaster_error_t abort(const keymaster1_device_t* dev, + keymaster_operation_handle_t operation_handle); + + // Keymaster2 methods + static keymaster_error_t add_rng_entropy(const keymaster2_device_t* dev, const uint8_t* data, + size_t data_length); + static keymaster_error_t generate_key(const keymaster2_device_t* dev, + const keymaster_key_param_set_t* params, + keymaster_key_blob_t* key_blob, + keymaster_key_characteristics_t* characteristics); + static keymaster_error_t get_key_characteristics(const keymaster2_device_t* dev, + const keymaster_key_blob_t* key_blob, + const keymaster_blob_t* client_id, + const keymaster_blob_t* app_data, + keymaster_key_characteristics_t* character); + static keymaster_error_t import_key(const keymaster2_device_t* dev, // + const keymaster_key_param_set_t* params, + keymaster_key_format_t key_format, + const keymaster_blob_t* key_data, + keymaster_key_blob_t* key_blob, + keymaster_key_characteristics_t* characteristics); + static keymaster_error_t export_key(const keymaster2_device_t* dev, // + keymaster_key_format_t export_format, + const keymaster_key_blob_t* key_to_export, + const keymaster_blob_t* client_id, + const keymaster_blob_t* app_data, + keymaster_blob_t* export_data); + static keymaster_error_t attest_key(const keymaster2_device_t* dev, + const keymaster_key_blob_t* key_to_attest, + const keymaster_key_param_set_t* attest_params, + keymaster_cert_chain_t* cert_chain); + static keymaster_error_t delete_key(const keymaster2_device_t* dev, + const keymaster_key_blob_t* key); + static keymaster_error_t delete_all_keys(const keymaster2_device_t* dev); + static keymaster_error_t begin(const keymaster2_device_t* dev, keymaster_purpose_t purpose, + const keymaster_key_blob_t* key, + const keymaster_key_param_set_t* in_params, + keymaster_key_param_set_t* out_params, + keymaster_operation_handle_t* operation_handle); + static keymaster_error_t update(const keymaster2_device_t* dev, // + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* input, size_t* input_consumed, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output); + static keymaster_error_t finish(const keymaster2_device_t* dev, // + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* input, + const keymaster_blob_t* signature, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output); + static keymaster_error_t abort(const keymaster2_device_t* dev, + keymaster_operation_handle_t operation_handle); + + keymaster1_device_t km1_device_; + keymaster2_device_t km2_device_; + + keymaster0_device_t* wrapped_km0_device_; + keymaster1_device_t* wrapped_km1_device_; + DigestMap km1_device_digests_; + SoftKeymasterContext* context_; + UniquePtr<AndroidKeymaster> impl_; + std::string module_name_; + hw_module_t updated_module_; +}; + +} // namespace keymaster + +#endif // EXTERNAL_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_
diff --git a/keymaster/include/keymaster/soft_keymaster_logger.h b/keymaster/include/keymaster/soft_keymaster_logger.h new file mode 100644 index 0000000..3be83b8 --- /dev/null +++ b/keymaster/include/keymaster/soft_keymaster_logger.h
@@ -0,0 +1,34 @@ +/* + * 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 SYSTEM_KEYMASTER_SOFT_KEYMASTER_LOGGER_H_ +#define SYSTEM_KEYMASTER_SOFT_KEYMASTER_LOGGER_H_ + +#include <keymaster/logger.h> + +namespace keymaster { + +class SoftKeymasterLogger : public Logger +{ +public: + SoftKeymasterLogger() { set_instance(this); } + + virtual int log_msg(LogLevel level, const char* fmt, va_list args) const; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_SOFT_KEYMASTER_LOGGER_H_
diff --git a/keymaster/integrity_assured_key_blob.cpp b/keymaster/integrity_assured_key_blob.cpp new file mode 100644 index 0000000..5c317a3 --- /dev/null +++ b/keymaster/integrity_assured_key_blob.cpp
@@ -0,0 +1,149 @@ +/* + * 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 "integrity_assured_key_blob.h" + +#include <assert.h> + +#include <new> + +#include <openssl/hmac.h> +#include <openssl/mem.h> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/authorization_set.h> + +#include "openssl_err.h" + +namespace keymaster { + +static const uint8_t BLOB_VERSION = 0; +static const size_t HMAC_SIZE = 8; +static const char HMAC_KEY[] = "IntegrityAssuredBlob0"; + +inline size_t min(size_t a, size_t b) { + if (a < b) + return a; + return b; +} + +class HmacCleanup { + public: + explicit HmacCleanup(HMAC_CTX* ctx) : ctx_(ctx) {} + ~HmacCleanup() { HMAC_CTX_cleanup(ctx_); } + + private: + HMAC_CTX* ctx_; +}; + +static keymaster_error_t ComputeHmac(const uint8_t* serialized_data, size_t serialized_data_size, + const AuthorizationSet& hidden, uint8_t hmac[HMAC_SIZE]) { + size_t hidden_bytes_size = hidden.SerializedSize(); + UniquePtr<uint8_t[]> hidden_bytes(new (std::nothrow) uint8_t[hidden_bytes_size]); + if (!hidden_bytes.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + hidden.Serialize(hidden_bytes.get(), hidden_bytes.get() + hidden_bytes_size); + + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + const EVP_MD* md = EVP_sha256(); + if (!HMAC_Init_ex(&ctx, HMAC_KEY, sizeof(HMAC_KEY), md, NULL /* engine */)) + return TranslateLastOpenSslError(); + HmacCleanup cleanup(&ctx); + + uint8_t tmp[EVP_MAX_MD_SIZE]; + unsigned tmp_len; + if (!HMAC_Update(&ctx, serialized_data, serialized_data_size) || + !HMAC_Update(&ctx, hidden_bytes.get(), hidden_bytes_size) || // + !HMAC_Final(&ctx, tmp, &tmp_len)) + return TranslateLastOpenSslError(); + + assert(tmp_len >= HMAC_SIZE); + memcpy(hmac, tmp, min(HMAC_SIZE, tmp_len)); + + return KM_ERROR_OK; +} + +keymaster_error_t SerializeIntegrityAssuredBlob(const KeymasterKeyBlob& key_material, + const AuthorizationSet& hidden, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + KeymasterKeyBlob* key_blob) { + size_t size = 1 /* version */ + // + key_material.SerializedSize() + // + hw_enforced.SerializedSize() + // + sw_enforced.SerializedSize() + // + HMAC_SIZE; + + if (!key_blob->Reset(size)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + uint8_t* p = key_blob->writable_data(); + *p++ = BLOB_VERSION; + p = key_material.Serialize(p, key_blob->end()); + p = hw_enforced.Serialize(p, key_blob->end()); + p = sw_enforced.Serialize(p, key_blob->end()); + + return ComputeHmac(key_blob->key_material, p - key_blob->key_material, hidden, p); +} + +keymaster_error_t DeserializeIntegrityAssuredBlob(const KeymasterKeyBlob& key_blob, + const AuthorizationSet& hidden, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) { + const uint8_t* p = key_blob.begin(); + const uint8_t* end = key_blob.end(); + + if (p > end || p + HMAC_SIZE > end) + return KM_ERROR_INVALID_KEY_BLOB; + + uint8_t computed_hmac[HMAC_SIZE]; + keymaster_error_t error = ComputeHmac(key_blob.begin(), key_blob.key_material_size - HMAC_SIZE, + hidden, computed_hmac); + if (error != KM_ERROR_OK) + return error; + + if (CRYPTO_memcmp(key_blob.end() - HMAC_SIZE, computed_hmac, HMAC_SIZE) != 0) + return KM_ERROR_INVALID_KEY_BLOB; + + return DeserializeIntegrityAssuredBlob_NoHmacCheck(key_blob, key_material, hw_enforced, + sw_enforced); +} + +keymaster_error_t DeserializeIntegrityAssuredBlob_NoHmacCheck(const KeymasterKeyBlob& key_blob, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) { + const uint8_t* p = key_blob.begin(); + const uint8_t* end = key_blob.end() - HMAC_SIZE; + + if (p > end) + return KM_ERROR_INVALID_KEY_BLOB; + + if (*p != BLOB_VERSION) + return KM_ERROR_INVALID_KEY_BLOB; + ++p; + + if (!key_material->Deserialize(&p, end) || // + !hw_enforced->Deserialize(&p, end) || // + !sw_enforced->Deserialize(&p, end)) + return KM_ERROR_INVALID_KEY_BLOB; + + return KM_ERROR_OK; +} + +} // namespace keymaster;
diff --git a/keymaster/integrity_assured_key_blob.h b/keymaster/integrity_assured_key_blob.h new file mode 100644 index 0000000..6239a8a --- /dev/null +++ b/keymaster/integrity_assured_key_blob.h
@@ -0,0 +1,47 @@ +/* + * 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 SYSTEM_KEYMASTER_INTEGRITY_ASSURED_KEY_BLOB_ +#define SYSTEM_KEYMASTER_INTEGRITY_ASSURED_KEY_BLOB_ + +#include <hardware/keymaster_defs.h> + +namespace keymaster { + +class AuthorizationSet; +class Buffer; +struct KeymasterKeyBlob; + +keymaster_error_t SerializeIntegrityAssuredBlob(const KeymasterKeyBlob& key_material, + const AuthorizationSet& hidden, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + KeymasterKeyBlob* key_blob); + +keymaster_error_t DeserializeIntegrityAssuredBlob(const KeymasterKeyBlob& key_blob, + const AuthorizationSet& hidden, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced); + +keymaster_error_t DeserializeIntegrityAssuredBlob_NoHmacCheck(const KeymasterKeyBlob& key_blob, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced); + +} // namespace keymaster; + +#endif // SYSTEM_KEYMASTER_INTEGRITY_ASSURED_KEY_BLOB_
diff --git a/keymaster/iso18033kdf.cpp b/keymaster/iso18033kdf.cpp new file mode 100644 index 0000000..3de0fdf --- /dev/null +++ b/keymaster/iso18033kdf.cpp
@@ -0,0 +1,85 @@ +/* + * 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 "iso18033kdf.h" +#include "openssl_utils.h" + +#include <algorithm> + +#include <openssl/evp.h> + +namespace keymaster { + +inline size_t min(size_t a, size_t b) { + return (a < b) ? a : b; +} + +bool Iso18033Kdf::GenerateKey(const uint8_t* info, size_t info_len, uint8_t* output, + size_t output_len) { + if (!is_initialized_ || output == nullptr) + return false; + + /* Check whether output length is too long as specified in ISO/IEC 18033-2. */ + if ((0xFFFFFFFFULL + start_counter_) * digest_size_ < (uint64_t)output_len) + return false; + + EVP_MD_CTX ctx; + EvpMdCtxCleaner ctxCleaner(&ctx); + EVP_MD_CTX_init(&ctx); + + size_t num_blocks = (output_len + digest_size_ - 1) / digest_size_; + UniquePtr<uint8_t[]> counter(new uint8_t[4]); + UniquePtr<uint8_t[]> digest_result(new uint8_t[digest_size_]); + if (counter.get() == nullptr || digest_result.get() == nullptr) + return false; + for (size_t block = 0; block < num_blocks; block++) { + switch (digest_type_) { + case KM_DIGEST_SHA1: + if (!EVP_DigestInit_ex(&ctx, EVP_sha1(), nullptr /* default digest */)) + return false; + break; + case KM_DIGEST_SHA_2_256: + if (!EVP_DigestInit_ex(&ctx, EVP_sha256(), nullptr /* default digest */)) + return false; + break; + default: + return false; + } + + if (!EVP_DigestUpdate(&ctx, secret_key_.get(), secret_key_len_) || + !Uint32ToBigEndianByteArray(block + start_counter_, counter.get()) || + !EVP_DigestUpdate(&ctx, counter.get(), 4)) + return false; + + if (info != nullptr && info_len > 0) { + if (!EVP_DigestUpdate(&ctx, info, info_len)) + return false; + } + + /* OpenSSL does not accept size_t parameter. */ + uint32_t uint32_digest_size_ = digest_size_; + if (!EVP_DigestFinal_ex(&ctx, digest_result.get(), &uint32_digest_size_) || + uint32_digest_size_ != digest_size_) + return false; + + size_t block_start = digest_size_ * block; + size_t block_length = min(digest_size_, output_len - block_start); + memcpy(output + block_start, digest_result.get(), block_length); + } + return true; +} + +} // namespace keymaster
diff --git a/keymaster/iso18033kdf.h b/keymaster/iso18033kdf.h new file mode 100644 index 0000000..40fd2d6 --- /dev/null +++ b/keymaster/iso18033kdf.h
@@ -0,0 +1,68 @@ +/* + * 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 SYSTEM_KEYMASTER_ISO18033KDF_H_ +#define SYSTEM_KEYMASTER_ISO18033KDF_H_ + +#include "kdf.h" + +#include <hardware/keymaster_defs.h> + +#include <keymaster/serializable.h> + +#include <UniquePtr.h> + +namespace keymaster { + +/** + * A light implementation of ISO18033KDF as defined by ISO-18033-2 (www.shoup.net/iso/std6.pdf) and + * its slightly different variant ANSI-X9-42. + */ +class Iso18033Kdf : public Kdf { + public: + ~Iso18033Kdf() {} + + bool Init(keymaster_digest_t digest_type, const uint8_t* secret, size_t secret_len) { + return Kdf::Init(digest_type, secret, secret_len, nullptr /* salt */, 0 /* salt_len */); + } + + /** + * Generates ISO18033's derived key, as defined in ISO-18033-2 and ANSI-X9-42. In ISO 18033-2, + * KDF takes a secret and outputs: + * + * hash(secret || start_counter) || hash(secret|| start_counter + 1) || ... + * + * In ANSI-X9-42, KDF takes a secret and additional info, and outputs: + * + * hash(secret || start_counter || info) || hash(secret || start_counter + 1 || info) || ... + * + * Note that the KDFs are the same if the info field is considered optional. In both cases the + * length of the output is specified by the caller, and the counter is encoded as a 4-element + * byte array. + */ + bool GenerateKey(const uint8_t* info, size_t info_len, uint8_t* output, + size_t output_len) override; + + protected: + explicit Iso18033Kdf(uint32_t start_counter) : start_counter_(start_counter) {} + + private: + uint32_t start_counter_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_ISO18033KDF_H_
diff --git a/keymaster/kdf.cpp b/keymaster/kdf.cpp new file mode 100644 index 0000000..e196a0c --- /dev/null +++ b/keymaster/kdf.cpp
@@ -0,0 +1,72 @@ +/* + * 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 "kdf.h" + +namespace keymaster { + +Kdf::Kdf() : is_initialized_(false) {} + +bool Kdf::Init(keymaster_digest_t digest_type, const uint8_t* secret, size_t secret_len, + const uint8_t* salt, size_t salt_len) { + is_initialized_ = false; + + switch (digest_type) { + case KM_DIGEST_SHA1: + digest_size_ = 20; + digest_type_ = digest_type; + break; + case KM_DIGEST_SHA_2_256: + digest_size_ = 32; + digest_type_ = digest_type; + break; + default: + return false; + } + + if (!secret || secret_len == 0) + return false; + + secret_key_len_ = secret_len; + secret_key_.reset(dup_buffer(secret, secret_len)); + if (!secret_key_.get()) + return false; + + salt_len_ = salt_len; + if (salt && salt_len > 0) { + salt_.reset(dup_buffer(salt, salt_len)); + if (!salt_.get()) + return false; + } else { + salt_.reset(); + } + + is_initialized_ = true; + return true; +} + +bool Kdf::Uint32ToBigEndianByteArray(uint32_t number, uint8_t* output) { + if (!output) + return false; + + output[0] = (number >> 24) & 0xff; + output[1] = (number >> 16) & 0xff; + output[2] = (number >> 8) & 0xff; + output[3] = (number)&0xff; + return true; +} + +} // namespace keymaster
diff --git a/keymaster/kdf.h b/keymaster/kdf.h new file mode 100644 index 0000000..b90d2bc --- /dev/null +++ b/keymaster/kdf.h
@@ -0,0 +1,53 @@ +/* + * 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 SYSTEM_KEYMASTER_KDF_H_ +#define SYSTEM_KEYMASTER_KDF_H_ + +#include <hardware/keymaster_defs.h> +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/serializable.h> + +#include <UniquePtr.h> + +namespace keymaster { + +/** + * A base class for wrapping different key derivation functions. + */ +class Kdf { + public: + virtual ~Kdf() { memset_s(secret_key_.get(), 0, secret_key_len_); }; + Kdf(); + bool Init(keymaster_digest_t digest_type, const uint8_t* secret, size_t secret_len, + const uint8_t* salt, size_t salt_len); + virtual bool GenerateKey(const uint8_t* info, size_t info_len, uint8_t* output, + size_t output_len) = 0; + + protected: + bool Uint32ToBigEndianByteArray(uint32_t number, uint8_t* output); + UniquePtr<uint8_t[]> secret_key_; + size_t secret_key_len_; + UniquePtr<uint8_t[]> salt_; + size_t salt_len_; + bool is_initialized_; + keymaster_digest_t digest_type_; + size_t digest_size_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KDF_H_
diff --git a/keymaster/kdf1.h b/keymaster/kdf1.h new file mode 100644 index 0000000..fab1ef7 --- /dev/null +++ b/keymaster/kdf1.h
@@ -0,0 +1,40 @@ +/* + * 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 SYSTEM_KEYMASTER_KDF1_H_ +#define SYSTEM_KEYMASTER_KDF1_H_ + +#include "iso18033kdf.h" + +#include <hardware/keymaster_defs.h> + +#include <keymaster/serializable.h> + +#include <UniquePtr.h> + +namespace keymaster { + +/** + * Kdf1 is instance of Iso18033Kdf when the counter starts at 0. + */ +class Kdf1 : public Iso18033Kdf { + public: + Kdf1() : Iso18033Kdf(0) {} +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KDF1_H_
diff --git a/keymaster/kdf1_test.cpp b/keymaster/kdf1_test.cpp new file mode 100644 index 0000000..9c8b0d5 --- /dev/null +++ b/keymaster/kdf1_test.cpp
@@ -0,0 +1,60 @@ +/* + * 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 "kdf1.h" +#include <gtest/gtest.h> +#include <string.h> + +#include "android_keymaster_test_utils.h" + +using std::string; + +namespace keymaster { + +namespace test { + +struct Kdf1Test { + const char* key_hex; + const char* expected_output_hex; + keymaster_digest_t digest_type; +}; + +static const Kdf1Test kKdf1Tests[] = { + {"032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d764374152e0ac009e509e7ba30cd2f1778" + "e113b64e135cf4e2292c75efe5288edfda4", + "5f8de105b5e96b2e490ddecbd147dd1def7e3b8e0e6a26eb7b956ccb8b3bdc1ca975bc57c3989e8fbad31a224655d" + "800c46954840ff32052cdf0d640562bdfadfa263cfccf3c52b29f2af4a1869959bc77f854cf15bd7a25192985a842" + "dbff8e13efee5b7e7e55bbe4d389647c686a9a9ab3fb889b2d7767d3837eea4e0a2f04", + KM_DIGEST_SHA1}}; + +TEST(Kdf1Test, Kdf1) { + for (auto& test : kKdf1Tests) { + const string key = hex2str(test.key_hex); + const string expected_output = hex2str(test.expected_output_hex); + size_t output_len = expected_output.size(); + uint8_t output[output_len]; + + Kdf1 kdf1; + ASSERT_TRUE( + kdf1.Init(test.digest_type, reinterpret_cast<const uint8_t*>(key.data()), key.size())); + ASSERT_TRUE(kdf1.GenerateKey(nullptr, 0, output, output_len)); + EXPECT_EQ(0, memcmp(output, expected_output.data(), output_len)); + } +} + +} // namespace test + +} // namespace keymaster
diff --git a/keymaster/kdf2.h b/keymaster/kdf2.h new file mode 100644 index 0000000..1df97ef --- /dev/null +++ b/keymaster/kdf2.h
@@ -0,0 +1,40 @@ +/* + * 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 SYSTEM_KEYMASTER_KDF2_H_ +#define SYSTEM_KEYMASTER_KDF2_H_ + +#include "iso18033kdf.h" + +#include <hardware/keymaster_defs.h> + +#include <keymaster/serializable.h> + +#include <UniquePtr.h> + +namespace keymaster { + +/** + * Kdf2 is instance of Iso18033Kdf when the counter starts at 1. + */ +class Kdf2 : public Iso18033Kdf { + public: + Kdf2() : Iso18033Kdf(1) {} +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KDF2_H_
diff --git a/keymaster/kdf2_test.cpp b/keymaster/kdf2_test.cpp new file mode 100644 index 0000000..29ff40a --- /dev/null +++ b/keymaster/kdf2_test.cpp
@@ -0,0 +1,86 @@ +/* + * 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 "kdf2.h" +#include <gtest/gtest.h> +#include <string.h> + +#include "android_keymaster_test_utils.h" + +using std::string; + +namespace keymaster { + +namespace test { + +struct Kdf2Test { + const char* key_hex; + const char* info_hex; + const char* expected_output_hex; + keymaster_digest_t digest_type; +}; + +static const Kdf2Test kKdf2Tests[] = { + {"032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d764374152e0ac009e509e7ba30cd2f1778" + "e113b64e135cf4e2292c75efe5288edfda4", + "", + "10a2403db42a8743cb989de86e668d168cbe6046e23ff26f741e87949a3bba1311ac179f819a3d18412e9eb45668f" + "2923c087c1299005f8d5fd42ca257bc93e8fee0c5a0d2a8aa70185401fbbd99379ec76c663e9a29d0b70f3fe261a5" + "9cdc24875a60b4aacb1319fa11c3365a8b79a44669f26fba933d012db213d7e3b16349", + KM_DIGEST_SHA_2_256}, + {"032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d764374152e0ac009e509e7ba30cd2f1778" + "e113b64e135cf4e2292c75efe5288edfda4", + "", + "0e6a26eb7b956ccb8b3bdc1ca975bc57c3989e8fbad31a224655d800c46954840ff32052cdf0d640562bdfadfa263" + "cfccf3c52b29f2af4a1869959bc77f854cf15bd7a25192985a842dbff8e13efee5b7e7e55bbe4d389647c686a9a9a" + "b3fb889b2d7767d3837eea4e0a2f04b53ca8f50fb31225c1be2d0126c8c7a4753b0807", + KM_DIGEST_SHA1}, + {"CA7C0F8C3FFA87A96E1B74AC8E6AF594347BB40A", "", "744AB703F5BC082E59185F6D049D2D367DB245C2", + KM_DIGEST_SHA1}, + {"0499B502FC8B5BAFB0F4047E731D1F9FD8CD0D8881", "", + "03C62280C894E103C680B13CD4B4AE740A5EF0C72547292F82DC6B1777F47D63BA9D1EA73" + "2DBF386", + KM_DIGEST_SHA1}, + {"5E10B967A95606853E528F04262AD18A4767C761163971391E17CB05A21668D4CE2B9F151617408042CE091958382" + "3FD346D1751FBE2341AF2EE0461B62F100FFAD4F723F70C18B38238ED183E9398C8CA517EE0CBBEFFF9C59471FE27" + "8093924089480DBC5A38E9A1A97D23038106847D0D22ECF85F49A861821199BAFCB0D74E6ACFFD7D142765EBF4C71" + "2414FE4B6AB957F4CB466B46601289BB82060428272842EE28F113CD11F39431CBFFD823254CE472E2105E49B3D7F" + "113B825076E6264585807BC46454665F27C5E4E1A4BD03470486322981FDC894CCA1E2930987C92C15A38BC42EB38" + "810E867C4432F07259EC00CDBBB0FB99E1727C706DA58DD", + "484D4143204B6579", "BC98EB018CB00EE26D1F97A15AE166912A7AC4C5", KM_DIGEST_SHA1}, + +}; + +TEST(Kdf2Test, Kdf2) { + for (auto& test : kKdf2Tests) { + const string key = hex2str(test.key_hex); + const string info = hex2str(test.info_hex); + const string expected_output = hex2str(test.expected_output_hex); + size_t output_len = expected_output.size(); + uint8_t output[output_len]; + + Kdf2 kdf2; + ASSERT_TRUE( + kdf2.Init(test.digest_type, reinterpret_cast<const uint8_t*>(key.data()), key.size())); + ASSERT_TRUE(kdf2.GenerateKey(reinterpret_cast<const uint8_t*>(info.data()), info.size(), + output, output_len)); + EXPECT_EQ(0, memcmp(output, expected_output.data(), output_len)); + } +} + +} // namespace test + +} // namespace keymaster
diff --git a/keymaster/kdf_test.cpp b/keymaster/kdf_test.cpp new file mode 100644 index 0000000..f6f4a93 --- /dev/null +++ b/keymaster/kdf_test.cpp
@@ -0,0 +1,46 @@ +/* + * 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 "kdf.h" +#include <gtest/gtest.h> + +namespace keymaster { + +namespace test { + +class ForTestAbstractKdf : public Kdf { + bool GenerateKey(const uint8_t* /* info */, size_t /* info_len */, uint8_t* /* output */, + size_t /* output_len */) { + return true; + } +}; + +TEST(KdfTest, Kdf) { + ForTestAbstractKdf kdf; + uint8_t key[128]; + uint8_t salt[128]; + ASSERT_TRUE(kdf.Init(KM_DIGEST_SHA1, key, 128, salt, 128)); + ASSERT_TRUE(kdf.Init(KM_DIGEST_SHA_2_256, key, 128, salt, 128)); + ASSERT_TRUE(kdf.Init(KM_DIGEST_SHA1, key, 128, nullptr, 0)); + ASSERT_FALSE(kdf.Init(KM_DIGEST_MD5, key, 128, salt, 128)); + ASSERT_FALSE(kdf.Init(KM_DIGEST_SHA1, nullptr, 0, salt, 128)); + ASSERT_FALSE(kdf.Init(KM_DIGEST_SHA1, nullptr, 128, salt, 128)); + ASSERT_FALSE(kdf.Init(KM_DIGEST_SHA1, key, 0, salt, 128)); +} + +} // namespace test + +} // namespace keymaster
diff --git a/keymaster/kem.h b/keymaster/kem.h new file mode 100644 index 0000000..f2fa073 --- /dev/null +++ b/keymaster/kem.h
@@ -0,0 +1,54 @@ +/* + * 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 SYSTEM_KEYMASTER_KEM_H_ +#define SYSTEM_KEYMASTER_KEM_H_ + +#include <keymaster/serializable.h> + +#include "openssl_utils.h" + +namespace keymaster { + +// Kem is an abstract class that provides an interface to a key encapsulation +// mechansim primitive. A key encapsulation mechanism works just like a +// public-key encryption scheme, except that the encryption algorithm takes no +// input other than the recipient’s public key. Instead, the encryption algorithm +// generates a pair (K, C0), where K is a bit string of some specified length, +// and C0 is an encryption of K, that is, the decryption algorithm applied to C0 +// yields K. +class Kem { + public: + virtual ~Kem(){}; + + // For a key encapsulation mechanism, the goal of encryption is to take the recipient's public + // key, and to generate a pair (K, C0), where K is a bit string of some specified length, + // and C0 is an encryption of K. + virtual bool Encrypt(const Buffer& peer_public_value, Buffer* output_clear_key, + Buffer* output_encrypted_key) = 0; + virtual bool Encrypt(const uint8_t* peer_public_value, size_t peer_public_value_len, + Buffer* output_clear_key, Buffer* output_encrypted_key) = 0; + + // Decrypt takes an encrypted key, and outputs its clear text. + // Decrypt takes ownership of \p private_key. + virtual bool Decrypt(EC_KEY* private_key, const Buffer& encrypted_key, Buffer* output_key) = 0; + virtual bool Decrypt(EC_KEY* private_key, const uint8_t* encrypted_key, + size_t encrypted_key_len, Buffer* output_key) = 0; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KEM_H_
diff --git a/keymaster/key.cpp b/keymaster/key.cpp new file mode 100644 index 0000000..eb828e1 --- /dev/null +++ b/keymaster/key.cpp
@@ -0,0 +1,37 @@ +/* + * Copyright 2014 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 <assert.h> + +#include "key.h" + +#include <openssl/x509.h> + +#include "openssl_utils.h" + +namespace keymaster { + +Key::Key(const AuthorizationSet& hw_enforced, const AuthorizationSet& sw_enforced, + keymaster_error_t* error) { + assert(error); + authorizations_.push_back(hw_enforced); + authorizations_.push_back(sw_enforced); + *error = KM_ERROR_OK; + if (authorizations_.is_valid() != AuthorizationSet::OK) + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; +} + +} // namespace keymaster
diff --git a/keymaster/key.h b/keymaster/key.h new file mode 100644 index 0000000..9fb4063 --- /dev/null +++ b/keymaster/key.h
@@ -0,0 +1,63 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_KEY_H_ +#define SYSTEM_KEYMASTER_KEY_H_ + +#include <UniquePtr.h> + +#include <hardware/keymaster_defs.h> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/authorization_set.h> +#include <keymaster/keymaster_context.h> + +namespace keymaster { + +class Key { + public: + virtual ~Key() {} + + /** + * Return a copy of raw key material, in the specified format. + */ + virtual keymaster_error_t formatted_key_material(keymaster_key_format_t format, + UniquePtr<uint8_t[]>* material, + size_t* size) const = 0; + + /** + * Generate an attestation certificate chain. + */ + virtual keymaster_error_t GenerateAttestation( + const KeymasterContext& /* context */, const AuthorizationSet& /* attest_params */, + const AuthorizationSet& /* tee_enforced */, const AuthorizationSet& /* sw_enforced */, + keymaster_cert_chain_t* /* certificate_chain */) const { + return KM_ERROR_INCOMPATIBLE_ALGORITHM; + } + + const AuthorizationSet& authorizations() const { return authorizations_; } + + protected: + Key(const AuthorizationSet& hw_enforced, const AuthorizationSet& sw_enforced, + keymaster_error_t* error); + + private: + AuthorizationSet authorizations_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KEY_H_
diff --git a/keymaster/key_blob_test.cpp b/keymaster/key_blob_test.cpp new file mode 100644 index 0000000..1e590f0 --- /dev/null +++ b/keymaster/key_blob_test.cpp
@@ -0,0 +1,362 @@ +/* + * Copyright (C) 2014 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 <algorithm> + +#include <gtest/gtest.h> + +#include <openssl/engine.h> +#include <openssl/rand.h> + +#include <keymaster/authorization_set.h> +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/keymaster_tags.h> + +#include "android_keymaster_test_utils.h" +#include "auth_encrypted_key_blob.h" +#include "integrity_assured_key_blob.h" +#include "ocb_utils.h" + +namespace keymaster { + +namespace test { + +const uint8_t master_key_data[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +const uint8_t key_data[5] = {21, 22, 23, 24, 25}; + +class KeyBlobTest : public testing::Test { + protected: + KeyBlobTest() + : master_key_(master_key_data, array_length(master_key_data)), + key_material_(key_data, array_length(key_data)) { + hw_enforced_.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA); + hw_enforced_.push_back(TAG_KEY_SIZE, 256); + hw_enforced_.push_back(TAG_BLOB_USAGE_REQUIREMENTS, KM_BLOB_STANDALONE); + hw_enforced_.push_back(TAG_MIN_SECONDS_BETWEEN_OPS, 10); + hw_enforced_.push_back(TAG_ALL_USERS); + hw_enforced_.push_back(TAG_NO_AUTH_REQUIRED); + hw_enforced_.push_back(TAG_ORIGIN, KM_ORIGIN_GENERATED); + + sw_enforced_.push_back(TAG_ACTIVE_DATETIME, 10); + sw_enforced_.push_back(TAG_ORIGINATION_EXPIRE_DATETIME, 100); + sw_enforced_.push_back(TAG_CREATION_DATETIME, 10); + + hidden_.push_back(TAG_ROOT_OF_TRUST, "foo", 3); + hidden_.push_back(TAG_APPLICATION_ID, "my_app", 6); + + nonce_.reserve(OCB_NONCE_LENGTH); + EXPECT_EQ(1, RAND_bytes(nonce_.peek_write(), OCB_NONCE_LENGTH)); + nonce_.advance_write(OCB_NONCE_LENGTH); + + tag_.reserve(OCB_TAG_LENGTH); + } + + keymaster_error_t Encrypt() { + return OcbEncryptKey(hw_enforced_, sw_enforced_, hidden_, master_key_, key_material_, + nonce_, &ciphertext_, &tag_); + } + + keymaster_error_t Decrypt() { + return OcbDecryptKey(hw_enforced_, sw_enforced_, hidden_, master_key_, ciphertext_, nonce_, + tag_, &decrypted_plaintext_); + } + + keymaster_error_t Serialize() { + return SerializeAuthEncryptedBlob(ciphertext_, hw_enforced_, sw_enforced_, nonce_, tag_, + &serialized_blob_); + } + + keymaster_error_t Deserialize() { + return DeserializeAuthEncryptedBlob(serialized_blob_, &ciphertext_, &hw_enforced_, + &sw_enforced_, &nonce_, &tag_); + } + + AuthorizationSet hw_enforced_; + AuthorizationSet sw_enforced_; + AuthorizationSet hidden_; + Buffer nonce_, tag_; + + KeymasterKeyBlob master_key_; + KeymasterKeyBlob key_material_; + KeymasterKeyBlob ciphertext_; + KeymasterKeyBlob decrypted_plaintext_; + KeymasterKeyBlob serialized_blob_; +}; + +TEST_F(KeyBlobTest, EncryptDecrypt) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + // key_data shouldn't be anywhere in the blob, ciphertext should. + EXPECT_EQ(serialized_blob_.end(), std::search(serialized_blob_.begin(), serialized_blob_.end(), + key_material_.begin(), key_material_.end())); + EXPECT_NE(serialized_blob_.end(), std::search(serialized_blob_.begin(), serialized_blob_.end(), + ciphertext_.begin(), ciphertext_.end())); + + ciphertext_.Clear(); + nonce_.Clear(); + tag_.Clear(); + AuthorizationSet hw2; + AuthorizationSet sw2; + + ASSERT_EQ(KM_ERROR_OK, DeserializeAuthEncryptedBlob(serialized_blob_, &ciphertext_, &hw2, &sw2, + &nonce_, &tag_)); + KeymasterKeyBlob plaintext; + OcbDecryptKey(hw2, sw2, hidden_, master_key_, ciphertext_, nonce_, tag_, &plaintext); + + EXPECT_EQ(hw_enforced_, hw2); + EXPECT_EQ(sw_enforced_, sw2); + + ASSERT_EQ(key_material_.key_material_size, plaintext.key_material_size); + EXPECT_EQ(0, memcmp(plaintext.begin(), key_material_.begin(), plaintext.key_material_size)); +} + +TEST_F(KeyBlobTest, WrongKeyLength) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + // Modify the key length, shouldn't be able to parse. + serialized_blob_.writable_data()[1 /* version */ + 4 /* nonce len */ + 12 /* nonce */ + 3]++; + + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Deserialize()); +} + +TEST_F(KeyBlobTest, WrongNonce) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + // Find the nonce, then modify it. + auto nonce_ptr = + std::search(serialized_blob_.begin(), serialized_blob_.end(), nonce_.begin(), nonce_.end()); + ASSERT_NE(nonce_ptr, serialized_blob_.end()); + EXPECT_EQ(serialized_blob_.end(), + std::search(nonce_ptr + 1, serialized_blob_.end(), nonce_.begin(), nonce_.end())); + (*const_cast<uint8_t*>(nonce_ptr))++; + + // Deserialization shouldn't be affected, but decryption should fail. + ASSERT_EQ(KM_ERROR_OK, Deserialize()); + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt()); +} + +TEST_F(KeyBlobTest, WrongTag) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + // Find the tag, them modify it. + auto tag_ptr = + std::search(serialized_blob_.begin(), serialized_blob_.end(), tag_.begin(), tag_.end()); + ASSERT_NE(tag_ptr, serialized_blob_.end()); + EXPECT_EQ(serialized_blob_.end(), + std::search(tag_ptr + 1, serialized_blob_.end(), tag_.begin(), tag_.end())); + (*const_cast<uint8_t*>(tag_ptr))++; + + // Deserialization shouldn't be affected, but decryption should fail. + ASSERT_EQ(KM_ERROR_OK, Deserialize()); + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt()); +} + +TEST_F(KeyBlobTest, WrongCiphertext) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + // Find the ciphertext, them modify it. + auto ciphertext_ptr = std::search(serialized_blob_.begin(), serialized_blob_.end(), + ciphertext_.begin(), ciphertext_.end()); + ASSERT_NE(ciphertext_ptr, serialized_blob_.end()); + EXPECT_EQ(serialized_blob_.end(), std::search(ciphertext_ptr + 1, serialized_blob_.end(), + ciphertext_.begin(), ciphertext_.end())); + (*const_cast<uint8_t*>(ciphertext_ptr))++; + + // Deserialization shouldn't be affected, but decryption should fail. + ASSERT_EQ(KM_ERROR_OK, Deserialize()); + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt()); +} + +TEST_F(KeyBlobTest, WrongMasterKey) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + uint8_t wrong_master_data[] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + KeymasterKeyBlob wrong_master(wrong_master_data, array_length(wrong_master_data)); + + // Decrypting with wrong master key should fail. + ASSERT_EQ(KM_ERROR_OK, Deserialize()); + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, + OcbDecryptKey(hw_enforced_, sw_enforced_, hidden_, wrong_master, ciphertext_, nonce_, + tag_, &decrypted_plaintext_)); +} + +TEST_F(KeyBlobTest, WrongHwEnforced) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + // Find enforced serialization data and modify it. + size_t hw_enforced_size = hw_enforced_.SerializedSize(); + UniquePtr<uint8_t[]> hw_enforced_data(new uint8_t[hw_enforced_size]); + hw_enforced_.Serialize(hw_enforced_data.get(), hw_enforced_data.get() + hw_enforced_size); + + auto hw_enforced_ptr = + std::search(serialized_blob_.begin(), serialized_blob_.end(), hw_enforced_data.get(), + hw_enforced_data.get() + hw_enforced_size); + ASSERT_NE(serialized_blob_.end(), hw_enforced_ptr); + EXPECT_EQ(serialized_blob_.end(), + std::search(hw_enforced_ptr + 1, serialized_blob_.end(), hw_enforced_data.get(), + hw_enforced_data.get() + hw_enforced_size)); + (*(const_cast<uint8_t*>(hw_enforced_ptr) + hw_enforced_size - 1))++; + + // Deserialization shouldn't be affected, but decryption should fail. + ASSERT_EQ(KM_ERROR_OK, Deserialize()); + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt()); +} + +TEST_F(KeyBlobTest, WrongSwEnforced) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + // Find enforced serialization data and modify it. + size_t sw_enforced_size = sw_enforced_.SerializedSize(); + UniquePtr<uint8_t[]> sw_enforced_data(new uint8_t[sw_enforced_size]); + sw_enforced_.Serialize(sw_enforced_data.get(), sw_enforced_data.get() + sw_enforced_size); + + auto sw_enforced_ptr = + std::search(serialized_blob_.begin(), serialized_blob_.end(), sw_enforced_data.get(), + sw_enforced_data.get() + sw_enforced_size); + ASSERT_NE(serialized_blob_.end(), sw_enforced_ptr); + EXPECT_EQ(serialized_blob_.end(), + std::search(sw_enforced_ptr + 1, serialized_blob_.end(), sw_enforced_data.get(), + sw_enforced_data.get() + sw_enforced_size)); + (*(const_cast<uint8_t*>(sw_enforced_ptr) + sw_enforced_size - 1))++; + + // Deserialization shouldn't be affected, but decryption should fail. + ASSERT_EQ(KM_ERROR_OK, Deserialize()); + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt()); +} + +TEST_F(KeyBlobTest, EmptyHidden) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + AuthorizationSet wrong_hidden; + + // Deserialization shouldn't be affected, but decryption should fail. + ASSERT_EQ(KM_ERROR_OK, Deserialize()); + EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, + OcbDecryptKey(hw_enforced_, sw_enforced_, wrong_hidden, master_key_, ciphertext_, + nonce_, tag_, &decrypted_plaintext_)); +} + +TEST_F(KeyBlobTest, WrongRootOfTrust) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + AuthorizationSet wrong_hidden; + wrong_hidden.push_back(TAG_ROOT_OF_TRUST, "bar", 2); + wrong_hidden.push_back(TAG_APPLICATION_ID, "my_app", 6); + + // Deserialization shouldn't be affected, but decryption should fail. + ASSERT_EQ(KM_ERROR_OK, Deserialize()); + EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, + OcbDecryptKey(hw_enforced_, sw_enforced_, wrong_hidden, master_key_, ciphertext_, + nonce_, tag_, &decrypted_plaintext_)); +} + +TEST_F(KeyBlobTest, WrongAppId) { + ASSERT_EQ(KM_ERROR_OK, Encrypt()); + ASSERT_EQ(KM_ERROR_OK, Serialize()); + + AuthorizationSet wrong_hidden; + wrong_hidden.push_back(TAG_ROOT_OF_TRUST, "foo", 3); + wrong_hidden.push_back(TAG_APPLICATION_ID, "your_app", 7); + + // Deserialization shouldn't be affected, but decryption should fail. + ASSERT_EQ(KM_ERROR_OK, Deserialize()); + EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, + OcbDecryptKey(hw_enforced_, sw_enforced_, wrong_hidden, master_key_, ciphertext_, + nonce_, tag_, &decrypted_plaintext_)); +} + +// This test is especially useful when compiled for 32-bit mode and run under valgrind. +TEST_F(KeyBlobTest, FuzzTest) { + time_t now = time(NULL); + std::cout << "Seeding rand() with " << now << " for fuzz test." << std::endl; + srand(now); + + // Fill large buffer with random bytes. + const int kBufSize = 10000; + UniquePtr<uint8_t[]> buf(new uint8_t[kBufSize]); + for (size_t i = 0; i < kBufSize; ++i) + buf[i] = static_cast<uint8_t>(rand()); + + // Try to deserialize every offset with multiple methods. + size_t deserialize_auth_encrypted_success = 0; + for (size_t i = 0; i < kBufSize; ++i) { + keymaster_key_blob_t blob = {buf.get() + i, kBufSize - i}; + KeymasterKeyBlob key_blob(blob); + + // Integrity-assured blob. + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, + DeserializeIntegrityAssuredBlob(key_blob, hidden_, &key_material_, &hw_enforced_, + &sw_enforced_)); + + // Auth-encrypted OCB blob. + keymaster_error_t error = DeserializeAuthEncryptedBlob( + key_blob, &ciphertext_, &hw_enforced_, &sw_enforced_, &nonce_, &tag_); + if (error == KM_ERROR_OK) { + // It's possible to deserialize successfully. Decryption should always fail. + ++deserialize_auth_encrypted_success; + error = OcbDecryptKey(hw_enforced_, sw_enforced_, hidden_, master_key_, ciphertext_, + nonce_, tag_, &decrypted_plaintext_); + } + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, error) + << "Somehow sucessfully parsed a blob with seed " << now << " at offset " << i; + } +} + +TEST_F(KeyBlobTest, UnderflowTest) { + uint8_t buf[0]; + keymaster_key_blob_t blob = {buf, 0}; + KeymasterKeyBlob key_blob(blob); + EXPECT_NE(nullptr, key_blob.key_material); + EXPECT_EQ(0U, key_blob.key_material_size); + + EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, + DeserializeIntegrityAssuredBlob(key_blob, hidden_, &key_material_, &hw_enforced_, + &sw_enforced_)); + + EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, + DeserializeAuthEncryptedBlob(key_blob, &ciphertext_, &hw_enforced_, &sw_enforced_, + &nonce_, &tag_)); +} + +TEST_F(KeyBlobTest, DupBufferToolarge) { + uint8_t buf[0]; + keymaster_key_blob_t blob = {buf, 0}; + blob.key_material_size = 16 * 1024 * 1024 + 1; + KeymasterKeyBlob key_blob(blob); + EXPECT_EQ(nullptr, key_blob.key_material); + EXPECT_EQ(0U, key_blob.key_material_size); + + ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, + DeserializeIntegrityAssuredBlob(key_blob, hidden_, &key_material_, &hw_enforced_, + &sw_enforced_)); + + EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, + DeserializeAuthEncryptedBlob(key_blob, &ciphertext_, &hw_enforced_, &sw_enforced_, + &nonce_, &tag_)); +} + +} // namespace test +} // namespace keymaster
diff --git a/keymaster/key_exchange.h b/keymaster/key_exchange.h new file mode 100644 index 0000000..1019e20 --- /dev/null +++ b/keymaster/key_exchange.h
@@ -0,0 +1,52 @@ +/* + * 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 SYSTEM_KEYMASTER_KEY_EXCHANGE_H_ +#define SYSTEM_KEYMASTER_KEY_EXCHANGE_H_ + +#include <openssl/ec.h> + +#include <keymaster/serializable.h> + +namespace keymaster { + +/** + * KeyExchange is an abstract class that provides an interface to a + * key-exchange primitive. + */ +class KeyExchange { + public: + virtual ~KeyExchange() {} + + /** + * CalculateSharedKey computes the shared key between the local private key + * (which is implicitly known by a KeyExchange object) and a public value + * from the peer. + */ + virtual bool CalculateSharedKey(const Buffer& peer_public_value, Buffer* shared_key) const = 0; + virtual bool CalculateSharedKey(const uint8_t* peer_public_value, size_t peer_public_value_len, + Buffer* shared_key) const = 0; + + /** + * public_value writes to |public_value| the local public key which can be + * sent to a peer in order to complete a key exchange. + */ + virtual bool public_value(Buffer* public_value) const = 0; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KEY_EXCHANGE_H_
diff --git a/keymaster/keymaster0_engine.cpp b/keymaster/keymaster0_engine.cpp new file mode 100644 index 0000000..bcd64e5 --- /dev/null +++ b/keymaster/keymaster0_engine.cpp
@@ -0,0 +1,400 @@ +/* + * 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 "keymaster0_engine.h" + +#include <assert.h> + +#include <memory> + +#define LOG_TAG "Keymaster0Engine" +#include <cutils/log.h> + +#include "keymaster/android_keymaster_utils.h" + +#include <openssl/bn.h> +#include <openssl/ec_key.h> +#include <openssl/ecdsa.h> + +#include "openssl_utils.h" + +using std::shared_ptr; +using std::unique_ptr; + +namespace keymaster { + +Keymaster0Engine* Keymaster0Engine::instance_ = nullptr; + +Keymaster0Engine::Keymaster0Engine(const keymaster0_device_t* keymaster0_device) + : keymaster0_device_(keymaster0_device), engine_(ENGINE_new()), supports_ec_(false) { + assert(!instance_); + instance_ = this; + + rsa_index_ = RSA_get_ex_new_index(0 /* argl */, NULL /* argp */, NULL /* new_func */, + keyblob_dup, keyblob_free); + ec_key_index_ = EC_KEY_get_ex_new_index(0 /* argl */, NULL /* argp */, NULL /* new_func */, + keyblob_dup, keyblob_free); + + rsa_method_.common.references = 0; + rsa_method_.common.is_static = 1; + rsa_method_.app_data = nullptr; + rsa_method_.init = nullptr; + rsa_method_.finish = nullptr; + rsa_method_.size = nullptr; + rsa_method_.sign = nullptr; + rsa_method_.verify = nullptr; + rsa_method_.encrypt = nullptr; + rsa_method_.sign_raw = nullptr; + rsa_method_.decrypt = nullptr; + rsa_method_.verify_raw = nullptr; + rsa_method_.private_transform = Keymaster0Engine::rsa_private_transform; + rsa_method_.mod_exp = nullptr; + rsa_method_.bn_mod_exp = BN_mod_exp_mont; + rsa_method_.flags = RSA_FLAG_OPAQUE; + rsa_method_.keygen = nullptr; + rsa_method_.supports_digest = nullptr; + + ENGINE_set_RSA_method(engine_, &rsa_method_, sizeof(rsa_method_)); + + if ((keymaster0_device_->flags & KEYMASTER_SUPPORTS_EC) != 0) { + supports_ec_ = true; + + ecdsa_method_.common.references = 0; + ecdsa_method_.common.is_static = 1; + ecdsa_method_.app_data = nullptr; + ecdsa_method_.init = nullptr; + ecdsa_method_.finish = nullptr; + ecdsa_method_.group_order_size = nullptr; + ecdsa_method_.sign = Keymaster0Engine::ecdsa_sign; + ecdsa_method_.verify = nullptr; + ecdsa_method_.flags = ECDSA_FLAG_OPAQUE; + + ENGINE_set_ECDSA_method(engine_, &ecdsa_method_, sizeof(ecdsa_method_)); + } +} + +Keymaster0Engine::~Keymaster0Engine() { + if (keymaster0_device_) + keymaster0_device_->common.close( + reinterpret_cast<hw_device_t*>(const_cast<keymaster0_device_t*>(keymaster0_device_))); + ENGINE_free(engine_); + instance_ = nullptr; +} + +bool Keymaster0Engine::GenerateRsaKey(uint64_t public_exponent, uint32_t public_modulus, + KeymasterKeyBlob* key_material) const { + assert(key_material); + keymaster_rsa_keygen_params_t params; + params.public_exponent = public_exponent; + params.modulus_size = public_modulus; + + uint8_t* key_blob = 0; + if (keymaster0_device_->generate_keypair(keymaster0_device_, TYPE_RSA, ¶ms, &key_blob, + &key_material->key_material_size) < 0) { + ALOGE("Error generating RSA key pair with keymaster0 device"); + return false; + } + unique_ptr<uint8_t, Malloc_Delete> key_blob_deleter(key_blob); + key_material->key_material = dup_buffer(key_blob, key_material->key_material_size); + return true; +} + +bool Keymaster0Engine::GenerateEcKey(uint32_t key_size, KeymasterKeyBlob* key_material) const { + assert(key_material); + keymaster_ec_keygen_params_t params; + params.field_size = key_size; + + uint8_t* key_blob = 0; + if (keymaster0_device_->generate_keypair(keymaster0_device_, TYPE_EC, ¶ms, &key_blob, + &key_material->key_material_size) < 0) { + ALOGE("Error generating EC key pair with keymaster0 device"); + return false; + } + unique_ptr<uint8_t, Malloc_Delete> key_blob_deleter(key_blob); + key_material->key_material = dup_buffer(key_blob, key_material->key_material_size); + return true; +} + +bool Keymaster0Engine::ImportKey(keymaster_key_format_t key_format, + const KeymasterKeyBlob& to_import, + KeymasterKeyBlob* imported_key) const { + assert(imported_key); + if (key_format != KM_KEY_FORMAT_PKCS8) + return false; + + uint8_t* key_blob = 0; + if (keymaster0_device_->import_keypair(keymaster0_device_, to_import.key_material, + to_import.key_material_size, &key_blob, + &imported_key->key_material_size) < 0) { + ALOGW("Error importing keypair with keymaster0 device"); + return false; + } + unique_ptr<uint8_t, Malloc_Delete> key_blob_deleter(key_blob); + imported_key->key_material = dup_buffer(key_blob, imported_key->key_material_size); + return true; +} + +bool Keymaster0Engine::DeleteKey(const KeymasterKeyBlob& blob) const { + if (!keymaster0_device_->delete_keypair) + return true; + return (keymaster0_device_->delete_keypair(keymaster0_device_, blob.key_material, + blob.key_material_size) == 0); +} + +bool Keymaster0Engine::DeleteAllKeys() const { + if (!keymaster0_device_->delete_all) + return true; + return (keymaster0_device_->delete_all(keymaster0_device_) == 0); +} + +static keymaster_key_blob_t* duplicate_blob(const uint8_t* key_data, size_t key_data_size) { + unique_ptr<uint8_t[]> key_material_copy(dup_buffer(key_data, key_data_size)); + if (!key_material_copy) + return nullptr; + + unique_ptr<keymaster_key_blob_t> blob_copy(new (std::nothrow) keymaster_key_blob_t); + if (!blob_copy.get()) + return nullptr; + blob_copy->key_material_size = key_data_size; + blob_copy->key_material = key_material_copy.release(); + return blob_copy.release(); +} + +inline keymaster_key_blob_t* duplicate_blob(const keymaster_key_blob_t& blob) { + return duplicate_blob(blob.key_material, blob.key_material_size); +} + +RSA* Keymaster0Engine::BlobToRsaKey(const KeymasterKeyBlob& blob) const { + // Create new RSA key (with engine methods) and insert blob + unique_ptr<RSA, RSA_Delete> rsa(RSA_new_method(engine_)); + if (!rsa) + return nullptr; + + keymaster_key_blob_t* blob_copy = duplicate_blob(blob); + if (!blob_copy->key_material || !RSA_set_ex_data(rsa.get(), rsa_index_, blob_copy)) + return nullptr; + + // Copy public key into new RSA key + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(GetKeymaster0PublicKey(blob)); + if (!pkey) + return nullptr; + unique_ptr<RSA, RSA_Delete> public_rsa(EVP_PKEY_get1_RSA(pkey.get())); + if (!public_rsa) + return nullptr; + rsa->n = BN_dup(public_rsa->n); + rsa->e = BN_dup(public_rsa->e); + if (!rsa->n || !rsa->e) + return nullptr; + + return rsa.release(); +} + +EC_KEY* Keymaster0Engine::BlobToEcKey(const KeymasterKeyBlob& blob) const { + // Create new EC key (with engine methods) and insert blob + unique_ptr<EC_KEY, EC_KEY_Delete> ec_key(EC_KEY_new_method(engine_)); + if (!ec_key) + return nullptr; + + keymaster_key_blob_t* blob_copy = duplicate_blob(blob); + if (!blob_copy->key_material || !EC_KEY_set_ex_data(ec_key.get(), ec_key_index_, blob_copy)) + return nullptr; + + // Copy public key into new EC key + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(GetKeymaster0PublicKey(blob)); + if (!pkey) + return nullptr; + + unique_ptr<EC_KEY, EC_KEY_Delete> public_ec_key(EVP_PKEY_get1_EC_KEY(pkey.get())); + if (!public_ec_key) + return nullptr; + + if (!EC_KEY_set_group(ec_key.get(), EC_KEY_get0_group(public_ec_key.get())) || + !EC_KEY_set_public_key(ec_key.get(), EC_KEY_get0_public_key(public_ec_key.get()))) + return nullptr; + + return ec_key.release(); +} + +const keymaster_key_blob_t* Keymaster0Engine::RsaKeyToBlob(const RSA* rsa) const { + return reinterpret_cast<keymaster_key_blob_t*>(RSA_get_ex_data(rsa, rsa_index_)); +} + +const keymaster_key_blob_t* Keymaster0Engine::EcKeyToBlob(const EC_KEY* ec_key) const { + return reinterpret_cast<keymaster_key_blob_t*>(EC_KEY_get_ex_data(ec_key, ec_key_index_)); +} + +/* static */ +int Keymaster0Engine::keyblob_dup(CRYPTO_EX_DATA* /* to */, const CRYPTO_EX_DATA* /* from */, + void** from_d, int /* index */, long /* argl */, + void* /* argp */) { + keymaster_key_blob_t* blob = reinterpret_cast<keymaster_key_blob_t*>(*from_d); + if (!blob) + return 1; + *from_d = duplicate_blob(*blob); + if (*from_d) + return 1; + return 0; +} + +/* static */ +void Keymaster0Engine::keyblob_free(void* /* parent */, void* ptr, CRYPTO_EX_DATA* /* data */, + int /* index*/, long /* argl */, void* /* argp */) { + keymaster_key_blob_t* blob = reinterpret_cast<keymaster_key_blob_t*>(ptr); + if (blob) { + delete[] blob->key_material; + delete blob; + } +} + +/* static */ +int Keymaster0Engine::rsa_private_transform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len) { + ALOGV("rsa_private_transform(%p, %p, %p, %u)", rsa, out, in, (unsigned)len); + + assert(instance_); + return instance_->RsaPrivateTransform(rsa, out, in, len); +} + +/* static */ +int Keymaster0Engine::ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig, + unsigned int* sig_len, EC_KEY* ec_key) { + ALOGV("ecdsa_sign(%p, %u, %p)", digest, (unsigned)digest_len, ec_key); + assert(instance_); + return instance_->EcdsaSign(digest, digest_len, sig, sig_len, ec_key); +} + +bool Keymaster0Engine::Keymaster0Sign(const void* signing_params, const keymaster_key_blob_t& blob, + const uint8_t* data, const size_t data_length, + unique_ptr<uint8_t[], Malloc_Delete>* signature, + size_t* signature_length) const { + uint8_t* signed_data; + int err = keymaster0_device_->sign_data(keymaster0_device_, signing_params, blob.key_material, + blob.key_material_size, data, data_length, &signed_data, + signature_length); + if (err < 0) { + ALOGE("Keymaster0 signing failed with error %d", err); + return false; + } + + signature->reset(signed_data); + return true; +} + +EVP_PKEY* Keymaster0Engine::GetKeymaster0PublicKey(const KeymasterKeyBlob& blob) const { + uint8_t* pub_key_data; + size_t pub_key_data_length; + int err = keymaster0_device_->get_keypair_public(keymaster0_device_, blob.key_material, + blob.key_material_size, &pub_key_data, + &pub_key_data_length); + if (err < 0) { + ALOGE("Error %d extracting public key", err); + return nullptr; + } + unique_ptr<uint8_t, Malloc_Delete> pub_key(pub_key_data); + + const uint8_t* p = pub_key_data; + return d2i_PUBKEY(nullptr /* allocate new struct */, &p, pub_key_data_length); +} + +static bool data_too_large_for_public_modulus(const uint8_t* data, size_t len, const RSA* rsa) { + unique_ptr<BIGNUM, BIGNUM_Delete> input_as_bn( + BN_bin2bn(data, len, nullptr /* allocate result */)); + return input_as_bn && BN_ucmp(input_as_bn.get(), rsa->n) >= 0; +} + +int Keymaster0Engine::RsaPrivateTransform(RSA* rsa, uint8_t* out, const uint8_t* in, + size_t len) const { + const keymaster_key_blob_t* key_blob = RsaKeyToBlob(rsa); + if (key_blob == NULL) { + ALOGE("key had no key_blob!"); + return 0; + } + + keymaster_rsa_sign_params_t sign_params = {DIGEST_NONE, PADDING_NONE}; + unique_ptr<uint8_t[], Malloc_Delete> signature; + size_t signature_length; + if (!Keymaster0Sign(&sign_params, *key_blob, in, len, &signature, &signature_length)) { + if (data_too_large_for_public_modulus(in, len, rsa)) { + ALOGE("Keymaster0 signing failed because data is too large."); + OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS); + } else { + // We don't know what error code is correct; force an "unknown error" return + OPENSSL_PUT_ERROR(USER, KM_ERROR_UNKNOWN_ERROR); + } + return 0; + } + Eraser eraser(signature.get(), signature_length); + + if (signature_length > len) { + /* The result of the RSA operation can never be larger than the size of + * the modulus so we assume that the result has extra zeros on the + * left. This provides attackers with an oracle, but there's nothing + * that we can do about it here. */ + memcpy(out, signature.get() + signature_length - len, len); + } else if (signature_length < len) { + /* If the keymaster0 implementation returns a short value we assume that + * it's because it removed leading zeros from the left side. This is + * bad because it provides attackers with an oracle but we cannot do + * anything about a broken keymaster0 implementation here. */ + memset(out, 0, len); + memcpy(out + len - signature_length, signature.get(), signature_length); + } else { + memcpy(out, signature.get(), len); + } + + ALOGV("rsa=%p keystore_rsa_priv_dec successful", rsa); + return 1; +} + +int Keymaster0Engine::EcdsaSign(const uint8_t* digest, size_t digest_len, uint8_t* sig, + unsigned int* sig_len, EC_KEY* ec_key) const { + const keymaster_key_blob_t* key_blob = EcKeyToBlob(ec_key); + if (key_blob == NULL) { + ALOGE("key had no key_blob!"); + return 0; + } + + // Truncate digest if it's too long + size_t max_input_len = (ec_group_size_bits(ec_key) + 7) / 8; + if (digest_len > max_input_len) + digest_len = max_input_len; + + keymaster_ec_sign_params_t sign_params = {DIGEST_NONE}; + unique_ptr<uint8_t[], Malloc_Delete> signature; + size_t signature_length; + if (!Keymaster0Sign(&sign_params, *key_blob, digest, digest_len, &signature, + &signature_length)) { + // We don't know what error code is correct; force an "unknown error" return + OPENSSL_PUT_ERROR(USER, KM_ERROR_UNKNOWN_ERROR); + return 0; + } + Eraser eraser(signature.get(), signature_length); + + if (signature_length == 0) { + ALOGW("No valid signature returned"); + return 0; + } else if (signature_length > ECDSA_size(ec_key)) { + ALOGW("Signature is too large"); + return 0; + } else { + memcpy(sig, signature.get(), signature_length); + *sig_len = signature_length; + } + + ALOGV("ecdsa_sign(%p, %u, %p) => success", digest, (unsigned)digest_len, ec_key); + return 1; +} + +} // namespace keymaster
diff --git a/keymaster/keymaster0_engine.h b/keymaster/keymaster0_engine.h new file mode 100644 index 0000000..aedd85c --- /dev/null +++ b/keymaster/keymaster0_engine.h
@@ -0,0 +1,103 @@ +/* + * 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 SYSTEM_KEYMASTER_KEYMASTER0_ENGINE_H_ +#define SYSTEM_KEYMASTER_KEYMASTER0_ENGINE_H_ + +#include <memory> + +#include <openssl/ec.h> +#include <openssl/engine.h> +#include <openssl/ex_data.h> +#include <openssl/rsa.h> + +#include <hardware/keymaster0.h> +#include <hardware/keymaster_defs.h> + +namespace keymaster { + +struct KeymasterKeyBlob; + +/* Keymaster0Engine is a BoringSSL ENGINE that implements RSA & EC by forwarding the requested + * operations to a keymaster0 module. */ +class Keymaster0Engine { + public: + /** + * Create a Keymaster0Engine, wrapping the provided keymaster0_device. The engine takes + * ownership of the device, and will close it during destruction. + */ + explicit Keymaster0Engine(const keymaster0_device_t* keymaster0_device); + ~Keymaster0Engine(); + + bool supports_ec() const { return supports_ec_; } + + bool GenerateRsaKey(uint64_t public_exponent, uint32_t public_modulus, + KeymasterKeyBlob* key_material) const; + bool GenerateEcKey(uint32_t key_size, KeymasterKeyBlob* key_material) const; + + bool ImportKey(keymaster_key_format_t key_format, const KeymasterKeyBlob& to_import, + KeymasterKeyBlob* imported_key_material) const; + bool DeleteKey(const KeymasterKeyBlob& blob) const; + bool DeleteAllKeys() const; + + RSA* BlobToRsaKey(const KeymasterKeyBlob& blob) const; + EC_KEY* BlobToEcKey(const KeymasterKeyBlob& blob) const; + + const keymaster_key_blob_t* RsaKeyToBlob(const RSA* rsa) const; + const keymaster_key_blob_t* EcKeyToBlob(const EC_KEY* rsa) const; + + const keymaster0_device_t* device() { return keymaster0_device_; } + + EVP_PKEY* GetKeymaster0PublicKey(const KeymasterKeyBlob& blob) const; + + private: + Keymaster0Engine(const Keymaster0Engine&); // Uncopyable + void operator=(const Keymaster0Engine&); // Unassignable + + static int keyblob_dup(CRYPTO_EX_DATA* to, const CRYPTO_EX_DATA* from, void** from_d, int index, + long argl, void* argp); + static void keyblob_free(void* parent, void* ptr, CRYPTO_EX_DATA* data, int index, long argl, + void* argp); + static int rsa_private_transform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len); + static int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig, + unsigned int* sig_len, EC_KEY* ec_key); + + struct Malloc_Delete { + void operator()(void* p) { free(p); } + }; + + bool Keymaster0Sign(const void* signing_params, const keymaster_key_blob_t& key_blob, + const uint8_t* data, const size_t data_length, + std::unique_ptr<uint8_t[], Malloc_Delete>* signature, + size_t* signature_length) const; + + int RsaPrivateTransform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len) const; + int EcdsaSign(const uint8_t* digest, size_t digest_len, uint8_t* sig, unsigned int* sig_len, + EC_KEY* ec_key) const; + + const keymaster0_device_t* keymaster0_device_; + ENGINE* const engine_; + int rsa_index_, ec_key_index_; + bool supports_ec_; + RSA_METHOD rsa_method_; + ECDSA_METHOD ecdsa_method_; + + static Keymaster0Engine* instance_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KEYMASTER0_ENGINE_H_
diff --git a/keymaster/keymaster1_engine.cpp b/keymaster/keymaster1_engine.cpp new file mode 100644 index 0000000..b20a434 --- /dev/null +++ b/keymaster/keymaster1_engine.cpp
@@ -0,0 +1,408 @@ +/* + * 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 "keymaster1_engine.h" + +#include <assert.h> + +#include <algorithm> +#include <memory> + +#define LOG_TAG "Keymaster1Engine" +#include <cutils/log.h> + +#include "keymaster/android_keymaster_utils.h" + +#include <openssl/bn.h> +#include <openssl/ec_key.h> +#include <openssl/ecdsa.h> + +#include "openssl_err.h" +#include "openssl_utils.h" + +using std::shared_ptr; +using std::unique_ptr; + +namespace keymaster { + +Keymaster1Engine* Keymaster1Engine::instance_ = nullptr; + +Keymaster1Engine::Keymaster1Engine(const keymaster1_device_t* keymaster1_device) + : keymaster1_device_(keymaster1_device), engine_(ENGINE_new()), + rsa_index_(RSA_get_ex_new_index(0 /* argl */, NULL /* argp */, NULL /* new_func */, + Keymaster1Engine::duplicate_key_data, + Keymaster1Engine::free_key_data)), + ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */, NULL /* argp */, NULL /* new_func */, + Keymaster1Engine::duplicate_key_data, + Keymaster1Engine::free_key_data)), + rsa_method_(BuildRsaMethod()), ecdsa_method_(BuildEcdsaMethod()) { + assert(rsa_index_ != -1); + assert(ec_key_index_ != -1); + assert(keymaster1_device); + assert(!instance_); + + instance_ = this; + + ENGINE_set_RSA_method(engine_.get(), &rsa_method_, sizeof(rsa_method_)); + ENGINE_set_ECDSA_method(engine_.get(), &ecdsa_method_, sizeof(ecdsa_method_)); +} + +Keymaster1Engine::~Keymaster1Engine() { + keymaster1_device_->common.close( + reinterpret_cast<hw_device_t*>(const_cast<keymaster1_device_t*>(keymaster1_device_))); + instance_ = nullptr; +} + +static void ConvertCharacteristics(keymaster_key_characteristics_t* characteristics, + AuthorizationSet* hw_enforced, AuthorizationSet* sw_enforced) { + unique_ptr<keymaster_key_characteristics_t, Characteristics_Delete> characteristics_deleter( + characteristics); + if (hw_enforced) + hw_enforced->Reinitialize(characteristics->hw_enforced); + if (sw_enforced) + sw_enforced->Reinitialize(characteristics->sw_enforced); +} + +keymaster_error_t Keymaster1Engine::GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + assert(key_blob); + + keymaster_key_characteristics_t* characteristics; + keymaster_key_blob_t blob; + keymaster_error_t error = keymaster1_device_->generate_key(keymaster1_device_, &key_description, + &blob, &characteristics); + if (error != KM_ERROR_OK) + return error; + unique_ptr<uint8_t, Malloc_Delete> blob_deleter(const_cast<uint8_t*>(blob.key_material)); + key_blob->key_material = dup_buffer(blob.key_material, blob.key_material_size); + key_blob->key_material_size = blob.key_material_size; + + ConvertCharacteristics(characteristics, hw_enforced, sw_enforced); + return error; +} + +keymaster_error_t Keymaster1Engine::ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + assert(output_key_blob); + + keymaster_key_characteristics_t* characteristics; + const keymaster_blob_t input_key = {input_key_material.key_material, + input_key_material.key_material_size}; + keymaster_key_blob_t blob; + keymaster_error_t error = keymaster1_device_->import_key(keymaster1_device_, &key_description, + input_key_material_format, &input_key, + &blob, &characteristics); + if (error != KM_ERROR_OK) + return error; + unique_ptr<uint8_t, Malloc_Delete> blob_deleter(const_cast<uint8_t*>(blob.key_material)); + output_key_blob->key_material = dup_buffer(blob.key_material, blob.key_material_size); + output_key_blob->key_material_size = blob.key_material_size; + + ConvertCharacteristics(characteristics, hw_enforced, sw_enforced); + return error; +} + +keymaster_error_t Keymaster1Engine::DeleteKey(const KeymasterKeyBlob& blob) const { + if (!keymaster1_device_->delete_key) + return KM_ERROR_OK; + return keymaster1_device_->delete_key(keymaster1_device_, &blob); +} + +keymaster_error_t Keymaster1Engine::DeleteAllKeys() const { + if (!keymaster1_device_->delete_all_keys) + return KM_ERROR_OK; + return keymaster1_device_->delete_all_keys(keymaster1_device_); +} + +RSA* Keymaster1Engine::BuildRsaKey(const KeymasterKeyBlob& blob, + const AuthorizationSet& additional_params, + keymaster_error_t* error) const { + // Create new RSA key (with engine methods) and add metadata + unique_ptr<RSA, RSA_Delete> rsa(RSA_new_method(engine_.get())); + if (!rsa) { + *error = TranslateLastOpenSslError(); + return nullptr; + } + + KeyData* key_data = new KeyData(blob, additional_params); + if (!RSA_set_ex_data(rsa.get(), rsa_index_, key_data)) { + *error = TranslateLastOpenSslError(); + delete key_data; + return nullptr; + } + + // Copy public key into new RSA key + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey( + GetKeymaster1PublicKey(key_data->key_material, key_data->begin_params, error)); + if (!pkey) { + *error = TranslateLastOpenSslError(); + return nullptr; + } + + unique_ptr<RSA, RSA_Delete> public_rsa(EVP_PKEY_get1_RSA(pkey.get())); + if (!public_rsa) { + *error = TranslateLastOpenSslError(); + return nullptr; + } + + rsa->n = BN_dup(public_rsa->n); + rsa->e = BN_dup(public_rsa->e); + if (!rsa->n || !rsa->e) { + *error = TranslateLastOpenSslError(); + return nullptr; + } + + *error = KM_ERROR_OK; + return rsa.release(); +} + +EC_KEY* Keymaster1Engine::BuildEcKey(const KeymasterKeyBlob& blob, + const AuthorizationSet& additional_params, + keymaster_error_t* error) const { + // Create new EC key (with engine methods) and insert blob + unique_ptr<EC_KEY, EC_KEY_Delete> ec_key(EC_KEY_new_method(engine_.get())); + if (!ec_key) { + *error = TranslateLastOpenSslError(); + return nullptr; + } + + KeyData* key_data = new KeyData(blob, additional_params); + if (!EC_KEY_set_ex_data(ec_key.get(), ec_key_index_, key_data)) { + *error = TranslateLastOpenSslError(); + delete key_data; + return nullptr; + } + + // Copy public key into new EC key + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey( + GetKeymaster1PublicKey(blob, additional_params, error)); + if (!pkey) { + *error = TranslateLastOpenSslError(); + return nullptr; + } + + unique_ptr<EC_KEY, EC_KEY_Delete> public_ec_key(EVP_PKEY_get1_EC_KEY(pkey.get())); + if (!public_ec_key) { + *error = TranslateLastOpenSslError(); + return nullptr; + } + + if (!EC_KEY_set_group(ec_key.get(), EC_KEY_get0_group(public_ec_key.get())) || + !EC_KEY_set_public_key(ec_key.get(), EC_KEY_get0_public_key(public_ec_key.get()))) { + *error = TranslateLastOpenSslError(); + return nullptr; + } + + *error = KM_ERROR_OK; + return ec_key.release(); +} + +Keymaster1Engine::KeyData* Keymaster1Engine::GetData(EVP_PKEY* key) const { + switch (EVP_PKEY_type(key->type)) { + case EVP_PKEY_RSA: { + unique_ptr<RSA, RSA_Delete> rsa(EVP_PKEY_get1_RSA(key)); + return GetData(rsa.get()); + } + + case EVP_PKEY_EC: { + unique_ptr<EC_KEY, EC_KEY_Delete> ec_key(EVP_PKEY_get1_EC_KEY(key)); + return GetData(ec_key.get()); + } + + default: + return nullptr; + }; +} + +Keymaster1Engine::KeyData* Keymaster1Engine::GetData(const RSA* rsa) const { + if (!rsa) + return nullptr; + return reinterpret_cast<KeyData*>(RSA_get_ex_data(rsa, rsa_index_)); +} + +Keymaster1Engine::KeyData* Keymaster1Engine::GetData(const EC_KEY* ec_key) const { + if (!ec_key) + return nullptr; + return reinterpret_cast<KeyData*>(EC_KEY_get_ex_data(ec_key, ec_key_index_)); +} + +/* static */ +int Keymaster1Engine::duplicate_key_data(CRYPTO_EX_DATA* /* to */, const CRYPTO_EX_DATA* /* from */, + void** from_d, int /* index */, long /* argl */, + void* /* argp */) { + KeyData* data = reinterpret_cast<KeyData*>(*from_d); + if (!data) + return 1; + + // Default copy ctor is good. + *from_d = new KeyData(*data); + if (*from_d) + return 1; + return 0; +} + +/* static */ +void Keymaster1Engine::free_key_data(void* /* parent */, void* ptr, CRYPTO_EX_DATA* /* data */, + int /* index*/, long /* argl */, void* /* argp */) { + delete reinterpret_cast<KeyData*>(ptr); +} + +keymaster_error_t Keymaster1Engine::Keymaster1Finish(const KeyData* key_data, + const keymaster_blob_t& input, + keymaster_blob_t* output) { + if (key_data->op_handle == 0) + return KM_ERROR_UNKNOWN_ERROR; + + size_t input_consumed; + // Note: devices are required to consume all input in a single update call for undigested + // signing operations and encryption operations. No need to loop here. + keymaster_error_t error = + device()->update(device(), key_data->op_handle, &key_data->finish_params, &input, + &input_consumed, nullptr /* out_params */, nullptr /* output */); + if (error != KM_ERROR_OK) + return error; + + return device()->finish(device(), key_data->op_handle, &key_data->finish_params, + nullptr /* signature */, nullptr /* out_params */, output); +} + +/* static */ +int Keymaster1Engine::rsa_sign_raw(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out, + const uint8_t* in, size_t in_len, int padding) { + KeyData* key_data = instance_->GetData(rsa); + if (!key_data) + return 0; + + if (padding != key_data->expected_openssl_padding) { + LOG_E("Expected sign_raw with padding %d but got padding %d", + key_data->expected_openssl_padding, padding); + return KM_ERROR_UNKNOWN_ERROR; + } + + keymaster_blob_t input = {in, in_len}; + keymaster_blob_t output; + key_data->error = instance_->Keymaster1Finish(key_data, input, &output); + if (key_data->error != KM_ERROR_OK) + return 0; + unique_ptr<uint8_t, Malloc_Delete> output_deleter(const_cast<uint8_t*>(output.data)); + + *out_len = std::min(output.data_length, max_out); + memcpy(out, output.data, *out_len); + return 1; +} + +/* static */ +int Keymaster1Engine::rsa_decrypt(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out, + const uint8_t* in, size_t in_len, int padding) { + KeyData* key_data = instance_->GetData(rsa); + if (!key_data) + return 0; + + if (padding != key_data->expected_openssl_padding) { + LOG_E("Expected sign_raw with padding %d but got padding %d", + key_data->expected_openssl_padding, padding); + return KM_ERROR_UNKNOWN_ERROR; + } + + keymaster_blob_t input = {in, in_len}; + keymaster_blob_t output; + key_data->error = instance_->Keymaster1Finish(key_data, input, &output); + if (key_data->error != KM_ERROR_OK) + return 0; + unique_ptr<uint8_t, Malloc_Delete> output_deleter(const_cast<uint8_t*>(output.data)); + + *out_len = std::min(output.data_length, max_out); + memcpy(out, output.data, *out_len); + return 1; +} + +/* static */ +int Keymaster1Engine::ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig, + unsigned int* sig_len, EC_KEY* ec_key) { + KeyData* key_data = instance_->GetData(ec_key); + if (!key_data) + return 0; + + // Truncate digest if it's too long + size_t max_input_len = (ec_group_size_bits(ec_key) + 7) / 8; + if (digest_len > max_input_len) + digest_len = max_input_len; + + keymaster_blob_t input = {digest, digest_len}; + keymaster_blob_t output; + key_data->error = instance_->Keymaster1Finish(key_data, input, &output); + if (key_data->error != KM_ERROR_OK) + return 0; + unique_ptr<uint8_t, Malloc_Delete> output_deleter(const_cast<uint8_t*>(output.data)); + + *sig_len = std::min(output.data_length, ECDSA_size(ec_key)); + memcpy(sig, output.data, *sig_len); + return 1; +} + +EVP_PKEY* Keymaster1Engine::GetKeymaster1PublicKey(const KeymasterKeyBlob& blob, + const AuthorizationSet& additional_params, + keymaster_error_t* error) const { + keymaster_blob_t client_id = {nullptr, 0}; + keymaster_blob_t app_data = {nullptr, 0}; + keymaster_blob_t* client_id_ptr = nullptr; + keymaster_blob_t* app_data_ptr = nullptr; + if (additional_params.GetTagValue(TAG_APPLICATION_ID, &client_id)) + client_id_ptr = &client_id; + if (additional_params.GetTagValue(TAG_APPLICATION_DATA, &app_data)) + app_data_ptr = &app_data; + + keymaster_blob_t export_data = {nullptr, 0}; + *error = keymaster1_device_->export_key(keymaster1_device_, KM_KEY_FORMAT_X509, &blob, + client_id_ptr, app_data_ptr, &export_data); + if (*error != KM_ERROR_OK) + return nullptr; + + unique_ptr<uint8_t, Malloc_Delete> pub_key(const_cast<uint8_t*>(export_data.data)); + + const uint8_t* p = export_data.data; + return d2i_PUBKEY(nullptr /* allocate new struct */, &p, export_data.data_length); +} + +RSA_METHOD Keymaster1Engine::BuildRsaMethod() { + RSA_METHOD method = {}; + + method.common.is_static = 1; + method.sign_raw = Keymaster1Engine::rsa_sign_raw; + method.decrypt = Keymaster1Engine::rsa_decrypt; + method.bn_mod_exp = BN_mod_exp_mont; + method.flags = RSA_FLAG_OPAQUE; + + return method; +} + +ECDSA_METHOD Keymaster1Engine::BuildEcdsaMethod() { + ECDSA_METHOD method = {}; + + method.common.is_static = 1; + method.sign = Keymaster1Engine::ecdsa_sign; + method.flags = ECDSA_FLAG_OPAQUE; + + return method; +} + +} // namespace keymaster
diff --git a/keymaster/keymaster1_engine.h b/keymaster/keymaster1_engine.h new file mode 100644 index 0000000..9e2f13e --- /dev/null +++ b/keymaster/keymaster1_engine.h
@@ -0,0 +1,123 @@ +/* + * 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 SYSTEM_KEYMASTER_KEYMASTER1_ENGINE_H_ +#define SYSTEM_KEYMASTER_KEYMASTER1_ENGINE_H_ + +#include <memory> + +#include <openssl/ec.h> +#include <openssl/engine.h> +#include <openssl/ex_data.h> +#include <openssl/rsa.h> + +#include <hardware/keymaster1.h> +#include <hardware/keymaster_defs.h> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/authorization_set.h> + +#include "openssl_utils.h" + +namespace keymaster { + +class Keymaster1Engine { + public: + /** + * Create a Keymaster1Engine, wrapping the provided keymaster1_device. The engine takes + * ownership of the device, and will close it during destruction. + */ + explicit Keymaster1Engine(const keymaster1_device_t* keymaster1_device); + ~Keymaster1Engine(); + + keymaster_error_t GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_material, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const; + + keymaster_error_t ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const; + keymaster_error_t DeleteKey(const KeymasterKeyBlob& blob) const; + keymaster_error_t DeleteAllKeys() const; + + struct KeyData { + KeyData(const KeymasterKeyBlob& blob, const AuthorizationSet& params) + : op_handle(0), begin_params(params), key_material(blob), error(KM_ERROR_OK), + expected_openssl_padding(-1) {} + + keymaster_operation_handle_t op_handle; + AuthorizationSet begin_params; + AuthorizationSet finish_params; + KeymasterKeyBlob key_material; + keymaster_error_t error; + int expected_openssl_padding; + }; + + RSA* BuildRsaKey(const KeymasterKeyBlob& blob, const AuthorizationSet& additional_params, + keymaster_error_t* error) const; + EC_KEY* BuildEcKey(const KeymasterKeyBlob& blob, const AuthorizationSet& additional_params, + keymaster_error_t* error) const; + + KeyData* GetData(EVP_PKEY* key) const; + KeyData* GetData(const RSA* rsa) const; + KeyData* GetData(const EC_KEY* rsa) const; + + const keymaster1_device_t* device() const { return keymaster1_device_; } + + EVP_PKEY* GetKeymaster1PublicKey(const KeymasterKeyBlob& blob, + const AuthorizationSet& additional_params, + keymaster_error_t* error) const; + + private: + Keymaster1Engine(const Keymaster1Engine&); // Uncopyable + void operator=(const Keymaster1Engine&); // Unassignable + + RSA_METHOD BuildRsaMethod(); + ECDSA_METHOD BuildEcdsaMethod(); + void ConfigureEngineForRsa(); + void ConfigureEngineForEcdsa(); + + keymaster_error_t Keymaster1Finish(const KeyData* key_data, const keymaster_blob_t& input, + keymaster_blob_t* output); + + static int duplicate_key_data(CRYPTO_EX_DATA* to, const CRYPTO_EX_DATA* from, void** from_d, + int index, long argl, void* argp); + static void free_key_data(void* parent, void* ptr, CRYPTO_EX_DATA* data, int index, long argl, + void* argp); + + static int rsa_sign_raw(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out, + const uint8_t* in, size_t in_len, int padding); + static int rsa_decrypt(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out, + const uint8_t* in, size_t in_len, int padding); + static int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig, + unsigned int* sig_len, EC_KEY* ec_key); + + const keymaster1_device_t* const keymaster1_device_; + const std::unique_ptr<ENGINE, ENGINE_Delete> engine_; + const int rsa_index_; + const int ec_key_index_; + + const RSA_METHOD rsa_method_; + const ECDSA_METHOD ecdsa_method_; + + static Keymaster1Engine* instance_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_KEYMASTER1_ENGINE_H_
diff --git a/keymaster/keymaster_enforcement.cpp b/keymaster/keymaster_enforcement.cpp new file mode 100644 index 0000000..7696d89 --- /dev/null +++ b/keymaster/keymaster_enforcement.cpp
@@ -0,0 +1,574 @@ +/* + * Copyright (C) 2014 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 <keymaster/keymaster_enforcement.h> + +#include <assert.h> +#include <limits.h> +#include <string.h> + +#include <openssl/evp.h> + +#include <hardware/hw_auth_token.h> +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/logger.h> + +#include "List.h" + +using android::List; + +namespace keymaster { + +class AccessTimeMap { + public: + explicit AccessTimeMap(uint32_t max_size) : max_size_(max_size) {} + + /* If the key is found, returns true and fills \p last_access_time. If not found returns + * false. */ + bool LastKeyAccessTime(km_id_t keyid, uint32_t* last_access_time) const; + + /* Updates the last key access time with the currentTime parameter. Adds the key if + * needed, returning false if key cannot be added because list is full. */ + bool UpdateKeyAccessTime(km_id_t keyid, uint32_t current_time, uint32_t timeout); + + private: + struct AccessTime { + km_id_t keyid; + uint32_t access_time; + uint32_t timeout; + }; + android::List<AccessTime> last_access_list_; + const uint32_t max_size_; +}; + +class AccessCountMap { + public: + explicit AccessCountMap(uint32_t max_size) : max_size_(max_size) {} + + /* If the key is found, returns true and fills \p count. If not found returns + * false. */ + bool KeyAccessCount(km_id_t keyid, uint32_t* count) const; + + /* Increments key access count, adding an entry if the key has never been used. Returns + * false if the list has reached maximum size. */ + bool IncrementKeyAccessCount(km_id_t keyid); + + private: + struct AccessCount { + km_id_t keyid; + uint64_t access_count; + }; + android::List<AccessCount> access_count_list_; + const uint32_t max_size_; +}; + +bool is_public_key_algorithm(const AuthorizationSet& auth_set) { + keymaster_algorithm_t algorithm; + return auth_set.GetTagValue(TAG_ALGORITHM, &algorithm) && + (algorithm == KM_ALGORITHM_RSA || algorithm == KM_ALGORITHM_EC); +} + +static keymaster_error_t authorized_purpose(const keymaster_purpose_t purpose, + const AuthorizationSet& auth_set) { + switch (purpose) { + case KM_PURPOSE_VERIFY: + case KM_PURPOSE_ENCRYPT: + case KM_PURPOSE_SIGN: + case KM_PURPOSE_DECRYPT: + if (auth_set.Contains(TAG_PURPOSE, purpose)) + return KM_ERROR_OK; + return KM_ERROR_INCOMPATIBLE_PURPOSE; + + default: + return KM_ERROR_UNSUPPORTED_PURPOSE; + } +} + +inline bool is_origination_purpose(keymaster_purpose_t purpose) { + return purpose == KM_PURPOSE_ENCRYPT || purpose == KM_PURPOSE_SIGN; +} + +inline bool is_usage_purpose(keymaster_purpose_t purpose) { + return purpose == KM_PURPOSE_DECRYPT || purpose == KM_PURPOSE_VERIFY; +} + +KeymasterEnforcement::KeymasterEnforcement(uint32_t max_access_time_map_size, + uint32_t max_access_count_map_size) + : access_time_map_(new (std::nothrow) AccessTimeMap(max_access_time_map_size)), + access_count_map_(new (std::nothrow) AccessCountMap(max_access_count_map_size)) {} + +KeymasterEnforcement::~KeymasterEnforcement() { + delete access_time_map_; + delete access_count_map_; +} + +keymaster_error_t KeymasterEnforcement::AuthorizeOperation(const keymaster_purpose_t purpose, + const km_id_t keyid, + const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params, + keymaster_operation_handle_t op_handle, + bool is_begin_operation) { + if (is_public_key_algorithm(auth_set)) { + switch (purpose) { + case KM_PURPOSE_ENCRYPT: + case KM_PURPOSE_VERIFY: + /* Public key operations are always authorized. */ + return KM_ERROR_OK; + + case KM_PURPOSE_DECRYPT: + case KM_PURPOSE_SIGN: + case KM_PURPOSE_DERIVE_KEY: + break; + }; + }; + + if (is_begin_operation) + return AuthorizeBegin(purpose, keyid, auth_set, operation_params); + else + return AuthorizeUpdateOrFinish(auth_set, operation_params, op_handle); +} + +// For update and finish the only thing to check is user authentication, and then only if it's not +// timeout-based. +keymaster_error_t +KeymasterEnforcement::AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params, + keymaster_operation_handle_t op_handle) { + int auth_type_index = -1; + for (size_t pos = 0; pos < auth_set.size(); ++pos) { + switch (auth_set[pos].tag) { + case KM_TAG_NO_AUTH_REQUIRED: + case KM_TAG_AUTH_TIMEOUT: + // If no auth is required or if auth is timeout-based, we have nothing to check. + return KM_ERROR_OK; + + case KM_TAG_USER_AUTH_TYPE: + auth_type_index = pos; + break; + + default: + break; + } + } + + // Note that at this point we should be able to assume that authentication is required, because + // authentication is required if KM_TAG_NO_AUTH_REQUIRED is absent. However, there are legacy + // keys which have no authentication-related tags, so we assume that absence is equivalent to + // presence of KM_TAG_NO_AUTH_REQUIRED. + // + // So, if we found KM_TAG_USER_AUTH_TYPE or if we find KM_TAG_USER_SECURE_ID then authentication + // is required. If we find neither, then we assume authentication is not required and return + // success. + bool authentication_required = (auth_type_index != -1); + for (auto& param : auth_set) { + if (param.tag == KM_TAG_USER_SECURE_ID) { + authentication_required = true; + int auth_timeout_index = -1; + if (AuthTokenMatches(auth_set, operation_params, param.long_integer, auth_type_index, + auth_timeout_index, op_handle, false /* is_begin_operation */)) + return KM_ERROR_OK; + } + } + + if (authentication_required) + return KM_ERROR_KEY_USER_NOT_AUTHENTICATED; + + return KM_ERROR_OK; +} + +keymaster_error_t KeymasterEnforcement::AuthorizeBegin(const keymaster_purpose_t purpose, + const km_id_t keyid, + const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params) { + // Find some entries that may be needed to handle KM_TAG_USER_SECURE_ID + int auth_timeout_index = -1; + int auth_type_index = -1; + int no_auth_required_index = -1; + for (size_t pos = 0; pos < auth_set.size(); ++pos) { + switch (auth_set[pos].tag) { + case KM_TAG_AUTH_TIMEOUT: + auth_timeout_index = pos; + break; + case KM_TAG_USER_AUTH_TYPE: + auth_type_index = pos; + break; + case KM_TAG_NO_AUTH_REQUIRED: + no_auth_required_index = pos; + break; + default: + break; + } + } + + keymaster_error_t error = authorized_purpose(purpose, auth_set); + if (error != KM_ERROR_OK) + return error; + + // If successful, and if key has a min time between ops, this will be set to the time limit + uint32_t min_ops_timeout = UINT32_MAX; + + bool update_access_count = false; + bool caller_nonce_authorized_by_key = false; + bool authentication_required = false; + bool auth_token_matched = false; + + for (auto& param : auth_set) { + + // KM_TAG_PADDING_OLD and KM_TAG_DIGEST_OLD aren't actually members of the enum, so we can't + // switch on them. There's nothing to validate for them, though, so just ignore them. + if (param.tag == KM_TAG_PADDING_OLD || param.tag == KM_TAG_DIGEST_OLD) + continue; + + switch (param.tag) { + + case KM_TAG_ACTIVE_DATETIME: + if (!activation_date_valid(param.date_time)) + return KM_ERROR_KEY_NOT_YET_VALID; + break; + + case KM_TAG_ORIGINATION_EXPIRE_DATETIME: + if (is_origination_purpose(purpose) && expiration_date_passed(param.date_time)) + return KM_ERROR_KEY_EXPIRED; + break; + + case KM_TAG_USAGE_EXPIRE_DATETIME: + if (is_usage_purpose(purpose) && expiration_date_passed(param.date_time)) + return KM_ERROR_KEY_EXPIRED; + break; + + case KM_TAG_MIN_SECONDS_BETWEEN_OPS: + min_ops_timeout = param.integer; + if (!MinTimeBetweenOpsPassed(min_ops_timeout, keyid)) + return KM_ERROR_KEY_RATE_LIMIT_EXCEEDED; + break; + + case KM_TAG_MAX_USES_PER_BOOT: + update_access_count = true; + if (!MaxUsesPerBootNotExceeded(keyid, param.integer)) + return KM_ERROR_KEY_MAX_OPS_EXCEEDED; + break; + + case KM_TAG_USER_SECURE_ID: + if (no_auth_required_index != -1) { + // Key has both KM_TAG_USER_SECURE_ID and KM_TAG_NO_AUTH_REQUIRED + return KM_ERROR_INVALID_KEY_BLOB; + } + + if (auth_timeout_index != -1) { + authentication_required = true; + if (AuthTokenMatches(auth_set, operation_params, param.long_integer, + auth_type_index, auth_timeout_index, 0 /* op_handle */, + true /* is_begin_operation */)) + auth_token_matched = true; + } + break; + + case KM_TAG_CALLER_NONCE: + caller_nonce_authorized_by_key = true; + break; + + /* Tags should never be in key auths. */ + case KM_TAG_INVALID: + case KM_TAG_AUTH_TOKEN: + case KM_TAG_ROOT_OF_TRUST: + case KM_TAG_APPLICATION_DATA: + return KM_ERROR_INVALID_KEY_BLOB; + + /* Tags used for cryptographic parameters in keygen. Nothing to enforce. */ + case KM_TAG_PURPOSE: + case KM_TAG_ALGORITHM: + case KM_TAG_KEY_SIZE: + case KM_TAG_BLOCK_MODE: + case KM_TAG_DIGEST: + case KM_TAG_MAC_LENGTH: + case KM_TAG_PADDING: + case KM_TAG_NONCE: + case KM_TAG_MIN_MAC_LENGTH: + case KM_TAG_KDF: + case KM_TAG_EC_CURVE: + + /* Tags not used for operations. */ + case KM_TAG_BLOB_USAGE_REQUIREMENTS: + case KM_TAG_EXPORTABLE: + + /* Algorithm specific parameters not used for access control. */ + case KM_TAG_RSA_PUBLIC_EXPONENT: + case KM_TAG_ECIES_SINGLE_HASH_MODE: + + /* Informational tags. */ + case KM_TAG_CREATION_DATETIME: + case KM_TAG_ORIGIN: + case KM_TAG_ROLLBACK_RESISTANT: + + /* Tags handled when KM_TAG_USER_SECURE_ID is handled */ + case KM_TAG_NO_AUTH_REQUIRED: + case KM_TAG_USER_AUTH_TYPE: + case KM_TAG_AUTH_TIMEOUT: + + /* Tag to provide data to operations. */ + case KM_TAG_ASSOCIATED_DATA: + + /* Tags that are implicitly verified by secure side */ + case KM_TAG_ALL_APPLICATIONS: + case KM_TAG_APPLICATION_ID: + case KM_TAG_OS_VERSION: + case KM_TAG_OS_PATCHLEVEL: + + /* Ignored pending removal */ + case KM_TAG_USER_ID: + case KM_TAG_ALL_USERS: + + /* TODO(swillden): Handle these */ + case KM_TAG_INCLUDE_UNIQUE_ID: + case KM_TAG_UNIQUE_ID: + case KM_TAG_RESET_SINCE_ID_ROTATION: + case KM_TAG_ALLOW_WHILE_ON_BODY: + break; + + case KM_TAG_BOOTLOADER_ONLY: + return KM_ERROR_INVALID_KEY_BLOB; + } + } + + if (authentication_required && !auth_token_matched) { + LOG_E("Auth required but no matching auth token found", 0); + return KM_ERROR_KEY_USER_NOT_AUTHENTICATED; + } + + if (!caller_nonce_authorized_by_key && is_origination_purpose(purpose) && + operation_params.find(KM_TAG_NONCE) != -1) + return KM_ERROR_CALLER_NONCE_PROHIBITED; + + if (min_ops_timeout != UINT32_MAX) { + if (!access_time_map_) { + LOG_S("Rate-limited keys table not allocated. Rate-limited keys disabled", 0); + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + + if (!access_time_map_->UpdateKeyAccessTime(keyid, get_current_time(), min_ops_timeout)) { + LOG_E("Rate-limited keys table full. Entries will time out.", 0); + return KM_ERROR_TOO_MANY_OPERATIONS; + } + } + + if (update_access_count) { + if (!access_count_map_) { + LOG_S("Usage-count limited keys tabel not allocated. Count-limited keys disabled", 0); + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + + if (!access_count_map_->IncrementKeyAccessCount(keyid)) { + LOG_E("Usage count-limited keys table full, until reboot.", 0); + return KM_ERROR_TOO_MANY_OPERATIONS; + } + } + + return KM_ERROR_OK; +} + +class EvpMdCtx { + public: + EvpMdCtx() { EVP_MD_CTX_init(&ctx_); } + ~EvpMdCtx() { EVP_MD_CTX_cleanup(&ctx_); } + + EVP_MD_CTX* get() { return &ctx_; } + + private: + EVP_MD_CTX ctx_; +}; + +/* static */ +bool KeymasterEnforcement::CreateKeyId(const keymaster_key_blob_t& key_blob, km_id_t* keyid) { + EvpMdCtx ctx; + + uint8_t hash[EVP_MAX_MD_SIZE]; + unsigned int hash_len; + if (EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr /* ENGINE */) && + EVP_DigestUpdate(ctx.get(), key_blob.key_material, key_blob.key_material_size) && + EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) { + assert(hash_len >= sizeof(*keyid)); + memcpy(keyid, hash, sizeof(*keyid)); + return true; + } + + return false; +} + +bool KeymasterEnforcement::MinTimeBetweenOpsPassed(uint32_t min_time_between, const km_id_t keyid) { + if (!access_time_map_) + return false; + + uint32_t last_access_time; + if (!access_time_map_->LastKeyAccessTime(keyid, &last_access_time)) + return true; + return min_time_between <= static_cast<int64_t>(get_current_time()) - last_access_time; +} + +bool KeymasterEnforcement::MaxUsesPerBootNotExceeded(const km_id_t keyid, uint32_t max_uses) { + if (!access_count_map_) + return false; + + uint32_t key_access_count; + if (!access_count_map_->KeyAccessCount(keyid, &key_access_count)) + return true; + return key_access_count < max_uses; +} + +bool KeymasterEnforcement::AuthTokenMatches(const AuthorizationSet& auth_set, + const AuthorizationSet& operation_params, + const uint64_t user_secure_id, + const int auth_type_index, const int auth_timeout_index, + const keymaster_operation_handle_t op_handle, + bool is_begin_operation) const { + assert(auth_type_index < static_cast<int>(auth_set.size())); + assert(auth_timeout_index < static_cast<int>(auth_set.size())); + + keymaster_blob_t auth_token_blob; + if (!operation_params.GetTagValue(TAG_AUTH_TOKEN, &auth_token_blob)) { + LOG_E("Authentication required, but auth token not provided", 0); + return false; + } + + if (auth_token_blob.data_length != sizeof(hw_auth_token_t)) { + LOG_E("Bug: Auth token is the wrong size (%d expected, %d found)", sizeof(hw_auth_token_t), + auth_token_blob.data_length); + return false; + } + + hw_auth_token_t auth_token; + memcpy(&auth_token, auth_token_blob.data, sizeof(hw_auth_token_t)); + if (auth_token.version != HW_AUTH_TOKEN_VERSION) { + LOG_E("Bug: Auth token is the version %d (or is not an auth token). Expected %d", + auth_token.version, HW_AUTH_TOKEN_VERSION); + return false; + } + + if (!ValidateTokenSignature(auth_token)) { + LOG_E("Auth token signature invalid", 0); + return false; + } + + if (auth_timeout_index == -1 && op_handle && op_handle != auth_token.challenge) { + LOG_E("Auth token has the challenge %llu, need %llu", auth_token.challenge, op_handle); + return false; + } + + if (user_secure_id != auth_token.user_id && user_secure_id != auth_token.authenticator_id) { + LOG_I("Auth token SIDs %llu and %llu do not match key SID %llu", auth_token.user_id, + auth_token.authenticator_id, user_secure_id); + return false; + } + + if (auth_type_index < 0 || auth_type_index > static_cast<int>(auth_set.size())) { + LOG_E("Auth required but no auth type found", 0); + return false; + } + + assert(auth_set[auth_type_index].tag == KM_TAG_USER_AUTH_TYPE); + if (auth_set[auth_type_index].tag != KM_TAG_USER_AUTH_TYPE) + return false; + + uint32_t key_auth_type_mask = auth_set[auth_type_index].integer; + uint32_t token_auth_type = ntoh(auth_token.authenticator_type); + if ((key_auth_type_mask & token_auth_type) == 0) { + LOG_E("Key requires match of auth type mask 0%uo, but token contained 0%uo", + key_auth_type_mask, token_auth_type); + return false; + } + + if (auth_timeout_index != -1 && is_begin_operation) { + assert(auth_set[auth_timeout_index].tag == KM_TAG_AUTH_TIMEOUT); + if (auth_set[auth_timeout_index].tag != KM_TAG_AUTH_TIMEOUT) + return false; + + if (auth_token_timed_out(auth_token, auth_set[auth_timeout_index].integer)) { + LOG_E("Auth token has timed out", 0); + return false; + } + } + + // Survived the whole gauntlet. We have authentage! + return true; +} + +bool AccessTimeMap::LastKeyAccessTime(km_id_t keyid, uint32_t* last_access_time) const { + for (auto& entry : last_access_list_) + if (entry.keyid == keyid) { + *last_access_time = entry.access_time; + return true; + } + return false; +} + +bool AccessTimeMap::UpdateKeyAccessTime(km_id_t keyid, uint32_t current_time, uint32_t timeout) { + List<AccessTime>::iterator iter; + for (iter = last_access_list_.begin(); iter != last_access_list_.end();) { + if (iter->keyid == keyid) { + iter->access_time = current_time; + return true; + } + + // Expire entry if possible. + assert(current_time >= iter->access_time); + if (current_time - iter->access_time >= iter->timeout) + iter = last_access_list_.erase(iter); + else + ++iter; + } + + if (last_access_list_.size() >= max_size_) + return false; + + AccessTime new_entry; + new_entry.keyid = keyid; + new_entry.access_time = current_time; + new_entry.timeout = timeout; + last_access_list_.push_front(new_entry); + return true; +} + +bool AccessCountMap::KeyAccessCount(km_id_t keyid, uint32_t* count) const { + for (auto& entry : access_count_list_) + if (entry.keyid == keyid) { + *count = entry.access_count; + return true; + } + return false; +} + +bool AccessCountMap::IncrementKeyAccessCount(km_id_t keyid) { + for (auto& entry : access_count_list_) + if (entry.keyid == keyid) { + // Note that the 'if' below will always be true because KM_TAG_MAX_USES_PER_BOOT is a + // uint32_t, and as soon as entry.access_count reaches the specified maximum value + // operation requests will be rejected and access_count won't be incremented any more. + // And, besides, UINT64_MAX is huge. But we ensure that it doesn't wrap anyway, out of + // an abundance of caution. + if (entry.access_count < UINT64_MAX) + ++entry.access_count; + return true; + } + + if (access_count_list_.size() >= max_size_) + return false; + + AccessCount new_entry; + new_entry.keyid = keyid; + new_entry.access_count = 1; + access_count_list_.push_front(new_entry); + return true; +} +}; /* namespace keymaster */
diff --git a/keymaster/keymaster_enforcement_test.cpp b/keymaster/keymaster_enforcement_test.cpp new file mode 100644 index 0000000..3874744 --- /dev/null +++ b/keymaster/keymaster_enforcement_test.cpp
@@ -0,0 +1,872 @@ +/* + * Copyright (C) 2014 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 <errno.h> +#include <stdio.h> +#include <time.h> + +#include <keymaster/android_keymaster.h> +#include <keymaster/authorization_set.h> +#include <keymaster/keymaster_enforcement.h> + +#include "android_keymaster_test_utils.h" + +namespace keymaster { +namespace test { + +class TestKeymasterEnforcement : public KeymasterEnforcement { + public: + TestKeymasterEnforcement() + : KeymasterEnforcement(3, 3), current_time_(10000), report_token_valid_(true) {} + + keymaster_error_t AuthorizeOperation(const keymaster_purpose_t purpose, const km_id_t keyid, + const AuthorizationSet& auth_set) { + AuthorizationSet empty_set; + return KeymasterEnforcement::AuthorizeOperation( + purpose, keyid, auth_set, empty_set, 0 /* op_handle */, true /* is_begin_operation */); + } + using KeymasterEnforcement::AuthorizeOperation; + + uint32_t get_current_time() const override { return current_time_; } + bool activation_date_valid(uint64_t activation_date) const override { + // Convert java date to time_t, non-portably. + time_t activation_time = activation_date / 1000; + return difftime(time(NULL), activation_time) >= 0; + } + bool expiration_date_passed(uint64_t expiration_date) const override { + // Convert jave date to time_t, non-portably. + time_t expiration_time = expiration_date / 1000; + return difftime(time(NULL), expiration_time) > 0; + } + bool auth_token_timed_out(const hw_auth_token_t& token, uint32_t timeout) const { + return current_time_ > ntoh(token.timestamp) + timeout; + } + bool ValidateTokenSignature(const hw_auth_token_t&) const override { + return report_token_valid_; + } + + void tick(unsigned seconds = 1) { current_time_ += seconds; } + uint32_t current_time() { return current_time_; } + void set_report_token_valid(bool report_token_valid) { + report_token_valid_ = report_token_valid; + } + + private: + uint32_t current_time_; + bool report_token_valid_; +}; + +class KeymasterBaseTest : public ::testing::Test { + protected: + KeymasterBaseTest() { + past_time = 0; + + time_t t = time(NULL); + future_tm = localtime(&t); + future_tm->tm_year += 1; + future_time = static_cast<uint64_t>(mktime(future_tm)) * 1000; + sign_param = Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN); + } + virtual ~KeymasterBaseTest() {} + + TestKeymasterEnforcement kmen; + + tm past_tm; + tm* future_tm; + uint64_t past_time; + uint64_t future_time; + static const km_id_t key_id = 0xa; + static const uid_t uid = 0xf; + keymaster_key_param_t sign_param; +}; + +TEST_F(KeymasterBaseTest, TestValidKeyPeriodNoTags) { + keymaster_key_param_t params[] = { + sign_param, + }; + AuthorizationSet single_auth_set(params, array_length(params)); + + keymaster_error_t kmer = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, single_auth_set); + ASSERT_EQ(KM_ERROR_OK, kmer); +} + +TEST_F(KeymasterBaseTest, TestInvalidActiveTime) { + keymaster_key_param_t params[] = { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), + Authorization(TAG_NO_AUTH_REQUIRED), Authorization(TAG_ACTIVE_DATETIME, future_time), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + ASSERT_EQ(KM_ERROR_KEY_NOT_YET_VALID, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + + // Pubkey ops allowed. + ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestValidActiveTime) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_ACTIVE_DATETIME, past_time), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + keymaster_error_t kmer_valid_time = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set); + ASSERT_EQ(KM_ERROR_OK, kmer_valid_time); +} + +TEST_F(KeymasterBaseTest, TestInvalidOriginationExpireTime) { + keymaster_key_param_t params[] = { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), + Authorization(TAG_ORIGINATION_EXPIRE_DATETIME, past_time), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + ASSERT_EQ(KM_ERROR_KEY_EXPIRED, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + + // Pubkey ops allowed. + ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestInvalidOriginationExpireTimeOnUsgae) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + Authorization(TAG_ORIGINATION_EXPIRE_DATETIME, past_time), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + keymaster_error_t kmer_invalid_origination = + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set); + ASSERT_EQ(KM_ERROR_OK, kmer_invalid_origination); +} + +TEST_F(KeymasterBaseTest, TestValidOriginationExpireTime) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), + Authorization(TAG_ORIGINATION_EXPIRE_DATETIME, future_time), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + keymaster_error_t kmer_valid_origination = + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set); + ASSERT_EQ(KM_ERROR_OK, kmer_valid_origination); +} + +TEST_F(KeymasterBaseTest, TestInvalidUsageExpireTime) { + keymaster_key_param_t params[] = { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES), + Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + Authorization(TAG_USAGE_EXPIRE_DATETIME, past_time), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + keymaster_error_t kmer_invalid_origination = + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set); + ASSERT_EQ(KM_ERROR_KEY_EXPIRED, kmer_invalid_origination); +} + +TEST_F(KeymasterBaseTest, TestInvalidPubkeyUsageExpireTime) { + keymaster_key_param_t params[] = { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), + Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + Authorization(TAG_USAGE_EXPIRE_DATETIME, past_time), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + keymaster_error_t kmer_invalid_origination = + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set); + // Pubkey ops allowed. + ASSERT_EQ(KM_ERROR_OK, kmer_invalid_origination); +} + +TEST_F(KeymasterBaseTest, TestInvalidUsageExpireTimeOnOrigination) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), + Authorization(TAG_USAGE_EXPIRE_DATETIME, past_time), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + keymaster_error_t kmer_invalid_origination = + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set); + ASSERT_EQ(KM_ERROR_OK, kmer_invalid_origination); +} + +TEST_F(KeymasterBaseTest, TestValidUsageExpireTime) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + Authorization(TAG_USAGE_EXPIRE_DATETIME, future_time), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + keymaster_error_t kmer_valid_usage = + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set); + ASSERT_EQ(KM_ERROR_OK, kmer_valid_usage); +} + +TEST_F(KeymasterBaseTest, TestValidSingleUseAccesses) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + keymaster_error_t kmer1 = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set); + keymaster_error_t kmer2 = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set); + + ASSERT_EQ(KM_ERROR_OK, kmer1); + ASSERT_EQ(KM_ERROR_OK, kmer2); +} + +TEST_F(KeymasterBaseTest, TestInvalidMaxOps) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), + Authorization(TAG_MAX_USES_PER_BOOT, 4), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + ASSERT_EQ(KM_ERROR_KEY_MAX_OPS_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + // Pubkey ops allowed. + ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestOverFlowMaxOpsTable) { + keymaster_key_param_t params[] = { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), + Authorization(TAG_MAX_USES_PER_BOOT, 2), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 2 /* key_id */, auth_set)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 3 /* key_id */, auth_set)); + + // Key 4 should fail, because table is full. + EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 4 /* key_id */, auth_set)); + + // Key 1 still works, because it's already in the table and hasn't reached max. + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set)); + + // Key 1 no longer works, because it's reached max. + EXPECT_EQ(KM_ERROR_KEY_MAX_OPS_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set)); + + // Key 4 should fail, because table is (still and forever) full. + EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 4 /* key_id */, auth_set)); + + // Pubkey ops allowed. + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set)); + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 4 /* key_id */, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestInvalidTimeBetweenOps) { + keymaster_key_param_t params[] = { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), + Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, 10), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + keymaster_error_t kmer1 = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set); + keymaster_error_t kmer2 = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set); + keymaster_error_t kmer3 = kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set); + + ASSERT_EQ(KM_ERROR_OK, kmer1); + kmen.tick(2); + ASSERT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, kmer2); + + // Allowed because it's a pubkey op. + ASSERT_EQ(KM_ERROR_OK, kmer3); +} + +TEST_F(KeymasterBaseTest, TestValidTimeBetweenOps) { + keymaster_key_param_t params[] = { + Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, 2), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set)); + kmen.tick(); + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + kmen.tick(); + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestOptTimeoutTableOverflow) { + keymaster_key_param_t params[] = { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES), + Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, 4), + Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set)); + + kmen.tick(); + + // Key 1 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set)); + + kmen.tick(); + + // Key 1 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set)); + // Key 2 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set)); + + kmen.tick(); + + // Key 1 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set)); + // Key 2 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set)); + // Key 3 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set)); + // Key 4 fails because the table is full + EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 4 /* key_id */, auth_set)); + + kmen.tick(); + + // Key 4 succeeds because key 1 expired. + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 4 /* key_id */, auth_set)); + + // Key 1 fails because the table is full... and key 1 is no longer in it. + EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set)); + // Key 2 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set)); + // Key 3 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set)); + + kmen.tick(); + + // Key 1 succeeds because key 2 expired + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set)); + // Key 2 fails because the table is full... and key 2 is no longer in it. + EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set)); + // Key 3 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set)); + // Key 4 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 4 /* key_id */, auth_set)); + + kmen.tick(4); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set)); + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set)); + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestPubkeyOptTimeoutTableOverflow) { + keymaster_key_param_t params[] = { + Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), + Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, 4), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), + }; + + AuthorizationSet auth_set(params, array_length(params)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set)); + + kmen.tick(); + + // Key 1 fails because it's too soon + EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set)); + // Too soo, but pubkey ops allowed. + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestInvalidPurpose) { + keymaster_purpose_t invalidPurpose1 = static_cast<keymaster_purpose_t>(-1); + keymaster_purpose_t invalidPurpose2 = static_cast<keymaster_purpose_t>(4); + + AuthorizationSet auth_set( + AuthorizationSetBuilder().Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)); + + EXPECT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, + kmen.AuthorizeOperation(invalidPurpose1, key_id, auth_set)); + EXPECT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, + kmen.AuthorizeOperation(invalidPurpose2, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestIncompatiblePurposeSymmetricKey) { + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set)); + + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE, + kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, auth_set)); + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE, + kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestIncompatiblePurposeAssymmetricKey) { + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set)); + + // This one is allowed because it's a pubkey op. + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, auth_set)); + EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE, + kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestInvalidCallerNonce) { + AuthorizationSet no_caller_nonce(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_ENCRYPT) + .Authorization(TAG_PURPOSE, KM_PURPOSE_DECRYPT) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES)); + AuthorizationSet caller_nonce(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_ENCRYPT) + .Authorization(TAG_PURPOSE, KM_PURPOSE_DECRYPT) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_HMAC) + .Authorization(TAG_CALLER_NONCE)); + AuthorizationSet begin_params(AuthorizationSetBuilder().Authorization(TAG_NONCE, "foo", 3)); + + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, caller_nonce, begin_params, + 0 /* challenge */, true /* is_begin_operation */)); + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, caller_nonce, begin_params, + 0 /* challenge */, true /* is_begin_operation */)); + EXPECT_EQ(KM_ERROR_CALLER_NONCE_PROHIBITED, + kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, no_caller_nonce, begin_params, + 0 /* challenge */, true /* is_begin_operation */)); + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, no_caller_nonce, begin_params, + 0 /* challenge */, true /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestBootloaderOnly) { + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_BOOTLOADER_ONLY)); + EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); + + // Pubkey ops allowed. + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestInvalidTag) { + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_INVALID) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestAuthPerOpSuccess) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = 0; + + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token))); + + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge, + false /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestAuthPerOpInvalidTokenSignature) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = 0; + + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC) + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token))); + + kmen.set_report_token_valid(false); + EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge, + false /* is_begin_operation */)); + // Pubkey ops allowed. + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params, + token.challenge, false /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestAuthPerOpWrongChallenge) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = 0; + + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token))); + + EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, + token.challenge + 1 /* doesn't match token */, + false /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestAuthPerOpNoAuthType) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = 0; + + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token))); + + EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge, + false /* is_begin_operation */)); + // Pubkey ops allowed. + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params, + token.challenge, false /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestAuthPerOpWrongAuthType) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = 0; + + AuthorizationSet auth_set( + AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_FINGERPRINT /* doesn't match token */) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token))); + + EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge, + false /* is_begin_operation */)); + // Pubkey ops allowed. + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params, + token.challenge, false /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestAuthPerOpWrongSid) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = 0; + + AuthorizationSet auth_set( + AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_SECURE_ID, token.user_id + 1 /* doesn't match token */) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token))); + + EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge, + false /* is_begin_operation */)); + // Pubkey op allowed. + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params, + token.challenge, false /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestAuthPerOpSuccessAlternateSid) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 10; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = 0; + + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_USER_SECURE_ID, token.authenticator_id) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token))); + + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge, + false /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestAuthPerOpMissingToken) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = 0; + + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + + // During begin we can skip the auth token + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, + token.challenge, true /* is_begin_operation */)); + // Afterwards we must have authentication + EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge, + false /* is_begin_operation */)); + // Pubkey ops allowed + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params, + token.challenge, false /* is_begin_operation */)); + + auth_set.Reinitialize(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES) + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN) + .build()); + + EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params, + token.challenge, false /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestAuthAndNoAuth) { + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_USER_SECURE_ID, 1) + .Authorization(TAG_NO_AUTH_REQUIRED) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set)); +} + +TEST_F(KeymasterBaseTest, TestTimedAuthSuccess) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = hton(kmen.current_time()); + + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_AUTH_TIMEOUT, 1) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token))); + + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, + 0 /* irrelevant */, false /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestTimedAuthTimedOut) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = hton(static_cast<uint64_t>(kmen.current_time())); + + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_AUTH_TIMEOUT, 1) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token))); + + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, + 0 /* irrelevant */, false /* is_begin_operation */)); + + kmen.tick(1); + + // token still good + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, + 0 /* irrelevant */, false /* is_begin_operation */)); + + kmen.tick(1); + + // token expired, not allowed during begin. + EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, + 0 /* irrelevant */, true /* is_begin_operation */)); + + // token expired, afterwards it's okay. + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, + 0 /* irrelevant */, false /* is_begin_operation */)); + + // Pubkey ops allowed. + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params, + 0 /* irrelevant */, true /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestTimedAuthMissingToken) { + hw_auth_token_t token; + memset(&token, 0, sizeof(token)); + token.version = HW_AUTH_TOKEN_VERSION; + token.challenge = 99; + token.user_id = 9; + token.authenticator_id = 0; + token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD)); + token.timestamp = hton(static_cast<uint64_t>(kmen.current_time())); + + AuthorizationSet auth_set(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) + .Authorization(TAG_USER_SECURE_ID, token.user_id) + .Authorization(TAG_AUTH_TIMEOUT, 1) + .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY) + .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)); + + AuthorizationSet op_params; + + // Unlike auth-per-op, must have the auth token during begin. + EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge, + true /* is_begin_operation */)); + + // Later we don't check (though begin would fail, so there wouldn't be a later). + EXPECT_EQ(KM_ERROR_OK, + kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge, + false /* is_begin_operation */)); + + // Pubkey ops allowed. + EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params, + token.challenge, true /* is_begin_operation */)); +} + +TEST_F(KeymasterBaseTest, TestCreateKeyId) { + keymaster_key_blob_t blob = {reinterpret_cast<const uint8_t*>("foobar"), 6}; + + km_id_t key_id = 0; + EXPECT_TRUE(KeymasterEnforcement::CreateKeyId(blob, &key_id)); + EXPECT_NE(0U, key_id); +} + +}; /* namespace test */ +}; /* namespace keymaster */
diff --git a/keymaster/keymaster_tags.cpp b/keymaster/keymaster_tags.cpp new file mode 100644 index 0000000..ce7156f --- /dev/null +++ b/keymaster/keymaster_tags.cpp
@@ -0,0 +1,171 @@ +/* + * Copyright 2014 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 <keymaster/keymaster_tags.h> + +namespace keymaster { + +#ifdef KEYMASTER_NAME_TAGS +const char* StringifyTag(keymaster_tag_t tag) { + switch (tag) { + case KM_TAG_INVALID: + return "KM_TAG_INVALID"; + case KM_TAG_PURPOSE: + return "KM_TAG_PURPOSE"; + case KM_TAG_ALGORITHM: + return "KM_TAG_ALGORITHM"; + case KM_TAG_KEY_SIZE: + return "KM_TAG_KEY_SIZE"; + case KM_TAG_BLOCK_MODE: + return "KM_TAG_BLOCK_MODE"; + case KM_TAG_DIGEST: + return "KM_TAG_DIGEST"; + case KM_TAG_PADDING: + return "KM_TAG_PADDING"; + case KM_TAG_CALLER_NONCE: + return "KM_TAG_CALLER_NONCE"; + case KM_TAG_MIN_MAC_LENGTH: + return "KM_TAG_MIN_MAC_LENGTH"; + case KM_TAG_RSA_PUBLIC_EXPONENT: + return "KM_TAG_RSA_PUBLIC_EXPONENT"; + case KM_TAG_BLOB_USAGE_REQUIREMENTS: + return "KM_TAG_BLOB_USAGE_REQUIREMENTS"; + case KM_TAG_BOOTLOADER_ONLY: + return "KM_TAG_BOOTLOADER_ONLY"; + case KM_TAG_ACTIVE_DATETIME: + return "KM_TAG_ACTIVE_DATETIME"; + case KM_TAG_ORIGINATION_EXPIRE_DATETIME: + return "KM_TAG_ORIGINATION_EXPIRE_DATETIME"; + case KM_TAG_USAGE_EXPIRE_DATETIME: + return "KM_TAG_USAGE_EXPIRE_DATETIME"; + case KM_TAG_MIN_SECONDS_BETWEEN_OPS: + return "KM_TAG_MIN_SECONDS_BETWEEN_OPS"; + case KM_TAG_MAX_USES_PER_BOOT: + return "KM_TAG_MAX_USES_PER_BOOT"; + case KM_TAG_ALL_USERS: + return "KM_TAG_ALL_USERS"; + case KM_TAG_USER_ID: + return "KM_TAG_USER_ID"; + case KM_TAG_USER_SECURE_ID: + return "KM_TAG_USER_SECURE_ID"; + case KM_TAG_NO_AUTH_REQUIRED: + return "KM_TAG_NO_AUTH_REQUIRED"; + case KM_TAG_USER_AUTH_TYPE: + return "KM_TAG_USER_AUTH_TYPE"; + case KM_TAG_AUTH_TIMEOUT: + return "KM_TAG_AUTH_TIMEOUT"; + case KM_TAG_ALL_APPLICATIONS: + return "KM_TAG_ALL_APPLICATIONS"; + case KM_TAG_APPLICATION_ID: + return "KM_TAG_APPLICATION_ID"; + case KM_TAG_APPLICATION_DATA: + return "KM_TAG_APPLICATION_DATA"; + case KM_TAG_CREATION_DATETIME: + return "KM_TAG_CREATION_DATETIME"; + case KM_TAG_ORIGIN: + return "KM_TAG_ORIGIN"; + case KM_TAG_ROLLBACK_RESISTANT: + return "KM_TAG_ROLLBACK_RESISTANT"; + case KM_TAG_ROOT_OF_TRUST: + return "KM_TAG_ROOT_OF_TRUST"; + case KM_TAG_ASSOCIATED_DATA: + return "KM_TAG_ASSOCIATED_DATA"; + case KM_TAG_NONCE: + return "KM_TAG_NONCE"; + case KM_TAG_AUTH_TOKEN: + return "KM_TAG_AUTH_TOKEN"; + case KM_TAG_MAC_LENGTH: + return "KM_TAG_MAC_LENGTH"; + case KM_TAG_KDF: + return "KM_TAG_KDF"; + case KM_TAG_EC_CURVE: + return "KM_TAG_EC_CURVE"; + case KM_TAG_ECIES_SINGLE_HASH_MODE: + return "KM_TAG_ECIES_SINGLE_HASH_MODE"; + case KM_TAG_OS_VERSION: + return "KM_TAG_OS_VERSION"; + case KM_TAG_OS_PATCHLEVEL: + return "KM_TAG_OS_PATCHLEVEL"; + case KM_TAG_EXPORTABLE: + return "KM_TAG_EXPORTABLE"; + case KM_TAG_UNIQUE_ID: + return "KM_TAG_UNIQUE_ID"; + case KM_TAG_INCLUDE_UNIQUE_ID: + return "KM_TAG_INCLUDE_UNIQUE_ID"; + case KM_TAG_RESET_SINCE_ID_ROTATION: + return "KM_TAG_RESET_SINCE_ID_ROTATION"; + case KM_TAG_ALLOW_WHILE_ON_BODY: + return "KM_TAG_ALLOW_WHILE_ON_BODY"; + } + return "<Unknown>"; +} +#endif // KEYMASTER_NAME_TAGS + +// DEFINE_KEYMASTER_TAG is used to create TypedTag instances for each non-enum keymaster tag. +#define DEFINE_KEYMASTER_TAG(type, name) TypedTag<type, KM_##name> name + +DEFINE_KEYMASTER_TAG(KM_INVALID, TAG_INVALID); +DEFINE_KEYMASTER_TAG(KM_UINT, TAG_KEY_SIZE); +DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MAC_LENGTH); +DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_CALLER_NONCE); +DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MIN_MAC_LENGTH); +DEFINE_KEYMASTER_TAG(KM_ULONG, TAG_RSA_PUBLIC_EXPONENT); +DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ECIES_SINGLE_HASH_MODE); +DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_INCLUDE_UNIQUE_ID); +DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ACTIVE_DATETIME); +DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ORIGINATION_EXPIRE_DATETIME); +DEFINE_KEYMASTER_TAG(KM_DATE, TAG_USAGE_EXPIRE_DATETIME); +DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MIN_SECONDS_BETWEEN_OPS); +DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MAX_USES_PER_BOOT); +DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ALL_USERS); +DEFINE_KEYMASTER_TAG(KM_UINT, TAG_USER_ID); +DEFINE_KEYMASTER_TAG(KM_ULONG_REP, TAG_USER_SECURE_ID); +DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_NO_AUTH_REQUIRED); +DEFINE_KEYMASTER_TAG(KM_UINT, TAG_AUTH_TIMEOUT); +DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ALLOW_WHILE_ON_BODY); +DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ALL_APPLICATIONS); +DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_APPLICATION_ID); +DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_APPLICATION_DATA); +DEFINE_KEYMASTER_TAG(KM_DATE, TAG_CREATION_DATETIME); +DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ROLLBACK_RESISTANT); +DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_ROOT_OF_TRUST); +DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_ASSOCIATED_DATA); +DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_NONCE); +DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_AUTH_TOKEN); +DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_BOOTLOADER_ONLY); +DEFINE_KEYMASTER_TAG(KM_UINT, TAG_OS_VERSION); +DEFINE_KEYMASTER_TAG(KM_UINT, TAG_OS_PATCHLEVEL); +DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_UNIQUE_ID); + +// DEFINE_KEYMASTER_ENUM_TAG is used to create TypedEnumTag instances for each enum keymaster tag. + +#define DEFINE_KEYMASTER_ENUM_TAG(type, name, enumtype) TypedEnumTag<type, KM_##name, enumtype> name + +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_PURPOSE, keymaster_purpose_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_ALGORITHM, keymaster_algorithm_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_BLOCK_MODE, keymaster_block_mode_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_DIGEST, keymaster_digest_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_DIGEST_OLD, keymaster_digest_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_PADDING, keymaster_padding_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_PADDING_OLD, keymaster_padding_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_BLOB_USAGE_REQUIREMENTS, + keymaster_key_blob_usage_requirements_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_ORIGIN, keymaster_key_origin_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_USER_AUTH_TYPE, hw_authenticator_type_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_KDF, keymaster_kdf_t); +DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_EC_CURVE, keymaster_ec_curve_t); + +} // namespace keymaster
diff --git a/keymaster/km0_sw_rsa_512.blob b/keymaster/km0_sw_rsa_512.blob new file mode 100644 index 0000000..7fee81c --- /dev/null +++ b/keymaster/km0_sw_rsa_512.blob Binary files differ
diff --git a/keymaster/km1_sw_ecdsa_256.blob b/keymaster/km1_sw_ecdsa_256.blob new file mode 100644 index 0000000..5b9a6f4 --- /dev/null +++ b/keymaster/km1_sw_ecdsa_256.blob Binary files differ
diff --git a/keymaster/km1_sw_rsa_512.blob b/keymaster/km1_sw_rsa_512.blob new file mode 100644 index 0000000..db1730a --- /dev/null +++ b/keymaster/km1_sw_rsa_512.blob Binary files differ
diff --git a/keymaster/km1_sw_rsa_512_unversioned.blob b/keymaster/km1_sw_rsa_512_unversioned.blob new file mode 100644 index 0000000..e4f6a11 --- /dev/null +++ b/keymaster/km1_sw_rsa_512_unversioned.blob Binary files differ
diff --git a/keymaster/logger.cpp b/keymaster/logger.cpp new file mode 100644 index 0000000..f86a68f --- /dev/null +++ b/keymaster/logger.cpp
@@ -0,0 +1,82 @@ +/* + * Copyright 2014 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 <keymaster/logger.h> + +namespace keymaster { + +Logger* Logger::instance_ = 0; + +/* static */ +int Logger::Log(LogLevel level, const char* fmt, va_list args) { + if (!instance_) + return 0; + return instance_->log_msg(level, fmt, args); +} + +/* static */ +int Logger::Log(LogLevel level, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int result = Log(level, fmt, args); + va_end(args); + return result; +} + +/* static */ +int Logger::Debug(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int result = Log(DEBUG_LVL, fmt, args); + va_end(args); + return result; +} + +/* static */ +int Logger::Info(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int result = Log(INFO_LVL, fmt, args); + va_end(args); + return result; +} +/* static */ +int Logger::Warning(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int result = Log(WARNING_LVL, fmt, args); + va_end(args); + return result; +} +/* static */ +int Logger::Error(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int result = Log(ERROR_LVL, fmt, args); + va_end(args); + return result; +} +/* static */ +int Logger::Severe(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int result = Log(SEVERE_LVL, fmt, args); + va_end(args); + return result; +} + + +} // namespace keymaster
diff --git a/keymaster/nist_curve_key_exchange.cpp b/keymaster/nist_curve_key_exchange.cpp new file mode 100644 index 0000000..0dd3a54 --- /dev/null +++ b/keymaster/nist_curve_key_exchange.cpp
@@ -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. + */ + +#include "nist_curve_key_exchange.h" + +#include <openssl/ec.h> +#include <openssl/ecdh.h> +#include <openssl/err.h> +#include <openssl/evp.h> + +#include "openssl_err.h" + +namespace keymaster { + +NistCurveKeyExchange::NistCurveKeyExchange(EC_KEY* private_key, keymaster_error_t* error) + : private_key_(private_key) { + if (!private_key_.get() || !EC_KEY_check_key(private_key_.get())) { + *error = KM_ERROR_INVALID_ARGUMENT; + return; + } + *error = ExtractPublicKey(); +} + +/* static */ +NistCurveKeyExchange* NistCurveKeyExchange::GenerateKeyExchange(keymaster_ec_curve_t curve) { + int curve_name; + switch (curve) { + case KM_EC_CURVE_P_224: + curve_name = NID_secp224r1; + break; + case KM_EC_CURVE_P_256: + curve_name = NID_X9_62_prime256v1; + break; + case KM_EC_CURVE_P_384: + curve_name = NID_secp384r1; + break; + case KM_EC_CURVE_P_521: + curve_name = NID_secp521r1; + break; + default: + LOG_E("Not a NIST curve: %d", curve); + return nullptr; + } + + UniquePtr<EC_KEY, EC_KEY_Delete> key(EC_KEY_new_by_curve_name(curve_name)); + if (!key.get() || !EC_KEY_generate_key(key.get())) { + return nullptr; + } + keymaster_error_t error; + NistCurveKeyExchange* key_exchange = new NistCurveKeyExchange(key.release(), &error); + if (error != KM_ERROR_OK) { + return nullptr; + } + return key_exchange; +} + +keymaster_error_t NistCurveKeyExchange::ExtractPublicKey() { + const EC_GROUP* group = EC_KEY_get0_group(private_key_.get()); + size_t field_len_bits; + keymaster_error_t error = ec_get_group_size(group, &field_len_bits); + if (error != KM_ERROR_OK) + return error; + + shared_secret_len_ = (field_len_bits + 7) / 8; + public_key_len_ = 1 + 2 * shared_secret_len_; + public_key_.reset(new uint8_t[public_key_len_]); + if (EC_POINT_point2oct(group, EC_KEY_get0_public_key(private_key_.get()), + POINT_CONVERSION_UNCOMPRESSED, public_key_.get(), public_key_len_, + nullptr /* ctx */) != public_key_len_) { + return TranslateLastOpenSslError(); + } + return KM_ERROR_OK; +} + +bool NistCurveKeyExchange::CalculateSharedKey(const Buffer& peer_public_value, + Buffer* out_result) const { + + return CalculateSharedKey(peer_public_value.peek_read(), peer_public_value.available_read(), + out_result); +} + +bool NistCurveKeyExchange::CalculateSharedKey(const uint8_t* peer_public_value, + size_t peer_public_value_len, + Buffer* out_result) const { + const EC_GROUP* group = EC_KEY_get0_group(private_key_.get()); + UniquePtr<EC_POINT, EC_POINT_Delete> point(EC_POINT_new(group)); + if (!point.get() || + !EC_POINT_oct2point(/* also test if point is on curve */ + group, point.get(), peer_public_value, peer_public_value_len, + nullptr /* ctx */) || + !EC_POINT_is_on_curve(group, point.get(), nullptr /* ctx */)) { + LOG_E("Can't convert peer public value to point: %d", TranslateLastOpenSslError()); + return false; + } + + UniquePtr<uint8_t[]> result(new uint8_t[shared_secret_len_]); + if (ECDH_compute_key(result.get(), shared_secret_len_, point.get(), private_key_.get(), + nullptr /* kdf */) != static_cast<int>(shared_secret_len_)) { + LOG_E("Can't compute ECDH shared key: %d", TranslateLastOpenSslError()); + return false; + } + + out_result->Reinitialize(result.get(), shared_secret_len_); + return true; +} + +bool NistCurveKeyExchange::public_value(Buffer* public_value) const { + if (public_key_.get() != nullptr && public_key_len_ != 0) { + return public_value->Reinitialize(public_key_.get(), public_key_len_); + } + return false; +} + +} // namespace keymaster \ No newline at end of file
diff --git a/keymaster/nist_curve_key_exchange.h b/keymaster/nist_curve_key_exchange.h new file mode 100644 index 0000000..0a93882 --- /dev/null +++ b/keymaster/nist_curve_key_exchange.h
@@ -0,0 +1,72 @@ +/* + * 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 SYSTEM_KEYMASTER_NIST_CURVE_KEY_EXCHANGE_H_ +#define SYSTEM_KEYMASTER_NIST_CURVE_KEY_EXCHANGE_H_ + +#include "key_exchange.h" + +#include <keymaster/authorization_set.h> +#include <hardware/keymaster_defs.h> + +#include <UniquePtr.h> + +#include "openssl_utils.h" + +namespace keymaster { + +/** + * NistCurveKeyExchange implements a KeyExchange using elliptic-curve + * Diffie-Hellman on NIST curves: P-224, P-256, P-384 and P-521. + */ +class NistCurveKeyExchange : public KeyExchange { + public: + ~NistCurveKeyExchange() override {} + + /** + * NistCurveKeyExchange takes ownership of \p private_key. + */ + NistCurveKeyExchange(EC_KEY* private_key, keymaster_error_t* error); + + /** + * GenerateKeyExchange generates a new public/private key pair on a NIST curve and returns + * a new key exchange object. + */ + static NistCurveKeyExchange* GenerateKeyExchange(keymaster_ec_curve_t curve); + + /** + * KeyExchange interface. + */ + bool CalculateSharedKey(const uint8_t* peer_public_value, size_t peer_public_value_len, + Buffer* shared_key) const override; + bool CalculateSharedKey(const Buffer& peer_public_value, Buffer* shared_key) const override; + bool public_value(Buffer* public_value) const override; + + /* Caller takes ownership of \p private_key. */ + EC_KEY* private_key() { return private_key_.release(); } + + private: + keymaster_error_t ExtractPublicKey(); + + UniquePtr<EC_KEY, EC_KEY_Delete> private_key_; + UniquePtr<uint8_t[]> public_key_; + size_t public_key_len_; + size_t shared_secret_len_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_NIST_CURVE_KEY_EXCHANGE_H_ \ No newline at end of file
diff --git a/keymaster/nist_curve_key_exchange_test.cpp b/keymaster/nist_curve_key_exchange_test.cpp new file mode 100644 index 0000000..39ea38b --- /dev/null +++ b/keymaster/nist_curve_key_exchange_test.cpp
@@ -0,0 +1,219 @@ +/* + * 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 "nist_curve_key_exchange.h" + +#include <gtest/gtest.h> +#include <openssl/evp.h> + +#include <hardware/keymaster_defs.h> +#include <keymaster/android_keymaster_utils.h> + +#include "android_keymaster_test_utils.h" + +using std::string; + +namespace keymaster { +namespace test { + +StdoutLogger logger; + +static const keymaster_ec_curve_t kEcCurves[] = {KM_EC_CURVE_P_224, KM_EC_CURVE_P_256, + KM_EC_CURVE_P_384, KM_EC_CURVE_P_521}; + +/** + * SharedKey just tests that the basic key exchange identity holds: that both + * parties end up with the same key. + */ +TEST(NistCurveKeyExchange, SharedKey) { + for (auto& curve : kEcCurves) { + AuthorizationSet kex_description( + AuthorizationSetBuilder().Authorization(TAG_EC_CURVE, curve)); + for (size_t j = 0; j < 5; j++) { + NistCurveKeyExchange* alice_keyex = NistCurveKeyExchange::GenerateKeyExchange(curve); + NistCurveKeyExchange* bob_keyex = NistCurveKeyExchange::GenerateKeyExchange(curve); + + ASSERT_TRUE(alice_keyex != nullptr); + ASSERT_TRUE(bob_keyex != nullptr); + + Buffer alice_public_value; + ASSERT_TRUE(alice_keyex->public_value(&alice_public_value)); + Buffer bob_public_value; + ASSERT_TRUE(bob_keyex->public_value(&bob_public_value)); + + Buffer alice_shared, bob_shared; + ASSERT_TRUE(alice_keyex->CalculateSharedKey(bob_public_value, &alice_shared)); + ASSERT_TRUE(bob_keyex->CalculateSharedKey(alice_public_value, &bob_shared)); + EXPECT_EQ(alice_shared.available_read(), bob_shared.available_read()); + EXPECT_EQ(0, memcmp(alice_shared.peek_read(), bob_shared.peek_read(), + alice_shared.available_read())); + } + } +} + +/* + * This test tries a key agreement with a false public key (i.e. with + * a point not on the curve.) + * The expected result of such a protocol should be that the + * key agreement fails and returns an error. +*/ +static const char* kInvalidPublicKeys[] = { + "04" // uncompressed public key + "deadbeef7f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" + "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", +}; + +TEST(NistCurveKeyExchange, InvalidPublicKey) { + for (auto& curve : kEcCurves) { + AuthorizationSet kex_description( + AuthorizationSetBuilder().Authorization(TAG_EC_CURVE, curve)); + KeyExchange* key_exchange = NistCurveKeyExchange::GenerateKeyExchange(curve); + ASSERT_TRUE(key_exchange != nullptr); + + string peer_public_key = hex2str(kInvalidPublicKeys[0]); + Buffer computed_shared_secret; + ASSERT_FALSE(key_exchange->CalculateSharedKey( + reinterpret_cast<const uint8_t*>(peer_public_key.data()), peer_public_key.size(), + &computed_shared_secret)); + } +} + +/** + * Test that key exchange fails when peer public key is the point at infinity. + */ +TEST(NistCurveKeyExchange, TestInfinity) { + for (auto& curve : kEcCurves) { + /* Obtain the point at infinity */ + EC_GROUP* group = ec_get_group(curve); + EC_POINT* point_at_infinity = EC_POINT_new(group); + EC_POINT_set_to_infinity(group, point_at_infinity); + EXPECT_EQ(1, EC_POINT_is_on_curve(group, point_at_infinity, nullptr)); + size_t field_len_in_bits; + ec_get_group_size(group, &field_len_in_bits); + size_t field_len = (field_len_in_bits + 7) / 8; + size_t public_key_len = (field_len * 2) + 1; + uint8_t* public_key = new uint8_t[public_key_len]; + public_key_len = EC_POINT_point2oct(group, point_at_infinity, POINT_CONVERSION_UNCOMPRESSED, + public_key, public_key_len, nullptr /* ctx */); + + /* Perform the key exchange */ + AuthorizationSet kex_description( + AuthorizationSetBuilder().Authorization(TAG_EC_CURVE, curve)); + NistCurveKeyExchange* key_exchange = NistCurveKeyExchange::GenerateKeyExchange(curve); + ASSERT_TRUE(key_exchange != nullptr); + Buffer computed_shared_secret; + /* It should fail */ + ASSERT_FALSE(key_exchange->CalculateSharedKey(reinterpret_cast<const uint8_t*>(public_key), + public_key_len, &computed_shared_secret)); + + /* Explicitly test that ECDH_compute_key fails when the public key is the point at infinity + */ + UniquePtr<uint8_t[]> result(new uint8_t[field_len]); + EXPECT_EQ(-1 /* error */, ECDH_compute_key(result.get(), field_len, point_at_infinity, + key_exchange->private_key(), nullptr /* kdf */)); + } +} + +/* Test vectors for P-256, downloaded from NIST. */ +struct NistCurveTest { + const keymaster_ec_curve_t curve; + const char* peer_public_key; + const char* my_private_key; + const char* shared_secret; +}; + +static const NistCurveTest kNistCurveTests[] = { + { + KM_EC_CURVE_P_256, + "04" // uncompressed public key + "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" + "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", + // https://tools.ietf.org/html/rfc5915 + "30770201010420" // DER-encodeded EC private key header + "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534" // private key + "a00a06082a8648ce3d030107a144034200" // DER-encoded curve OID, + "04" + "ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230" + "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141", + "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b", + }, + { + KM_EC_CURVE_P_256, "04" + "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae" + "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3", + // https://tools.ietf.org/html/rfc5915 + "30770201010420" // DER-encodeded EC private key header + "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5" // private key + "a00a06082a8648ce3d030107a144034200" // DER-encoded curve OID, + "04" + "119f2f047902782ab0c9e27a54aff5eb9b964829ca99c06b02ddba95b0a3f6d0" + "8f52b726664cac366fc98ac7a012b2682cbd962e5acb544671d41b9445704d1d", + "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67", + }, + { + KM_EC_CURVE_P_256, "04" + "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed" + "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4", + // https://tools.ietf.org/html/rfc5915 + "30770201010420" // DER-encodeded EC private key header + "207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d" // private key + "a00a06082a8648ce3d030107a144034200" // DER-encoded curve OID, + "04" + "24277c33f450462dcb3d4801d57b9ced05188f16c28eda873258048cd1607e0d" + "c4789753e2b1f63b32ff014ec42cd6a69fac81dfe6d0d6fd4af372ae27c46f88", + "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024", + }, +}; + +/** + * Test that key exchange works with NIST test vectors. + */ +TEST(NistCurveKeyExchange, NistTestVectors) { + for (auto& test : kNistCurveTests) { + string private_key = hex2str(test.my_private_key); + string shared_secret = hex2str(test.shared_secret); + + const uint8_t* private_key_data = reinterpret_cast<const uint8_t*>(private_key.data()); + UniquePtr<EC_KEY, EC_KEY_Delete> ec_key( + d2i_ECPrivateKey(nullptr, &private_key_data, private_key.size())); + ASSERT_TRUE(ec_key.get() && EC_KEY_check_key(ec_key.get())); + + keymaster_error_t error; + NistCurveKeyExchange* key_exchange = new NistCurveKeyExchange(ec_key.release(), &error); + EXPECT_EQ(KM_ERROR_OK, error); + ASSERT_TRUE(key_exchange != nullptr); + + Buffer computed_shared_secret; + string peer_public_key = hex2str(test.peer_public_key); + ASSERT_TRUE(key_exchange->CalculateSharedKey( + reinterpret_cast<const uint8_t*>(peer_public_key.data()), peer_public_key.size(), + &computed_shared_secret)); + EXPECT_EQ(shared_secret.size(), computed_shared_secret.available_read()); + EXPECT_EQ(0, memcmp(shared_secret.data(), computed_shared_secret.peek_read(), + shared_secret.size())); + + for (size_t i = 0; i < peer_public_key.size(); i++) { + // randomly flip some bits in the peer public key to make it invalid + peer_public_key[i] ^= 0xff; + ASSERT_FALSE(key_exchange->CalculateSharedKey( + reinterpret_cast<const uint8_t*>(peer_public_key.data()), peer_public_key.size(), + &computed_shared_secret)); + } + } +} + +} // namespace test +} // namespace keymaster
diff --git a/keymaster/ocb.c b/keymaster/ocb.c new file mode 100644 index 0000000..461fe9d --- /dev/null +++ b/keymaster/ocb.c
@@ -0,0 +1,1480 @@ +/*------------------------------------------------------------------------ +/ OCB Version 3 Reference Code (Optimized C) Last modified 12-JUN-2013 +/------------------------------------------------------------------------- +/ Copyright (c) 2013 Ted Krovetz. +/ +/ Permission to use, copy, modify, and/or distribute this software for any +/ purpose with or without fee is hereby granted, provided that the above +/ copyright notice and this permission notice appear in all copies. +/ +/ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +/ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +/ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +/ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +/ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +/ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +/ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +/ +/ Phillip Rogaway holds patents relevant to OCB. See the following for +/ his patent grant: http://www.cs.ucdavis.edu/~rogaway/ocb/grant.htm +/ +/ Special thanks to Keegan McAllister for suggesting several good improvements +/ +/ Comments are welcome: Ted Krovetz <ted@krovetz.net> - Dedicated to Laurel K +/------------------------------------------------------------------------- */ + +/* ----------------------------------------------------------------------- */ +/* Usage notes */ +/* ----------------------------------------------------------------------- */ + +/* - When AE_PENDING is passed as the 'final' parameter of any function, +/ the length parameters must be a multiple of (BPI*16). +/ - When available, SSE or AltiVec registers are used to manipulate data. +/ So, when on machines with these facilities, all pointers passed to +/ any function should be 16-byte aligned. +/ - Plaintext and ciphertext pointers may be equal (ie, plaintext gets +/ encrypted in-place), but no other pair of pointers may be equal. +/ - This code assumes all x86 processors have SSE2 and SSSE3 instructions +/ when compiling under MSVC. If untrue, alter the #define. +/ - This code is tested for C99 and recent versions of GCC and MSVC. */ + +/* ----------------------------------------------------------------------- */ +/* User configuration options */ +/* ----------------------------------------------------------------------- */ + +/* Set the AES key length to use and length of authentication tag to produce. +/ Setting either to 0 requires the value be set at runtime via ae_init(). +/ Some optimizations occur for each when set to a fixed value. */ +#define OCB_KEY_LEN 16 /* 0, 16, 24 or 32. 0 means set in ae_init */ +#define OCB_TAG_LEN 16 /* 0 to 16. 0 means set in ae_init */ + +/* This implementation has built-in support for multiple AES APIs. Set any +/ one of the following to non-zero to specify which to use. */ +#define USE_OPENSSL_AES 1 /* http://openssl.org */ +#define USE_REFERENCE_AES 0 /* Internet search: rijndael-alg-fst.c */ +#define USE_AES_NI 0 /* Uses compiler's intrinsics */ + +/* During encryption and decryption, various "L values" are required. +/ The L values can be precomputed during initialization (requiring extra +/ space in ae_ctx), generated as needed (slightly slowing encryption and +/ decryption), or some combination of the two. L_TABLE_SZ specifies how many +/ L values to precompute. L_TABLE_SZ must be at least 3. L_TABLE_SZ*16 bytes +/ are used for L values in ae_ctx. Plaintext and ciphertexts shorter than +/ 2^L_TABLE_SZ blocks need no L values calculated dynamically. */ +#define L_TABLE_SZ 16 + +/* Set L_TABLE_SZ_IS_ENOUGH non-zero iff you know that all plaintexts +/ will be shorter than 2^(L_TABLE_SZ+4) bytes in length. This results +/ in better performance. */ +#define L_TABLE_SZ_IS_ENOUGH 1 + +/* ----------------------------------------------------------------------- */ +/* Includes and compiler specific definitions */ +/* ----------------------------------------------------------------------- */ + +#include "ae.h" +#include <stdlib.h> +#include <string.h> + +/* Define standard sized integers */ +#if defined(_MSC_VER) && (_MSC_VER < 1600) +typedef unsigned __int8 uint8_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +typedef __int64 int64_t; +#else +#include <stdint.h> +#endif + +/* Compiler-specific intrinsics and fixes: bswap64, ntz */ +#if _MSC_VER +#define inline __inline /* MSVC doesn't recognize "inline" in C */ +#define restrict __restrict /* MSVC doesn't recognize "restrict" in C */ +#define __SSE2__ (_M_IX86 || _M_AMD64 || _M_X64) /* Assume SSE2 */ +#define __SSSE3__ (_M_IX86 || _M_AMD64 || _M_X64) /* Assume SSSE3 */ +#include <intrin.h> +#pragma intrinsic(_byteswap_uint64, _BitScanForward, memcpy) +#define bswap64(x) _byteswap_uint64(x) +static inline unsigned ntz(unsigned x) { + _BitScanForward(&x, x); + return x; +} +#elif __GNUC__ +#define inline __inline__ /* No "inline" in GCC ansi C mode */ +#define restrict __restrict__ /* No "restrict" in GCC ansi C mode */ +#define bswap64(x) __builtin_bswap64(x) /* Assuming GCC 4.3+ */ +#define ntz(x) __builtin_ctz((unsigned)(x)) /* Assuming GCC 3.4+ */ +#else /* Assume some C99 features: stdint.h, inline, restrict */ +#define bswap32(x) \ + ((((x)&0xff000000u) >> 24) | (((x)&0x00ff0000u) >> 8) | (((x)&0x0000ff00u) << 8) | \ + (((x)&0x000000ffu) << 24)) + +static inline uint64_t bswap64(uint64_t x) { + union { + uint64_t u64; + uint32_t u32[2]; + } in, out; + in.u64 = x; + out.u32[0] = bswap32(in.u32[1]); + out.u32[1] = bswap32(in.u32[0]); + return out.u64; +} + +#if (L_TABLE_SZ <= 9) && (L_TABLE_SZ_IS_ENOUGH) /* < 2^13 byte texts */ +static inline unsigned ntz(unsigned x) { + static const unsigned char tz_table[] = { + 0, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2, 6, 2, 3, 2, 4, 2, 3, 2, 5, 2, + 3, 2, 4, 2, 3, 2, 7, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2, 6, 2, 3, 2, + 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2, 8, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, + 3, 2, 6, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2, 7, 2, 3, 2, 4, 2, 3, 2, + 5, 2, 3, 2, 4, 2, 3, 2, 6, 2, 3, 2, 4, 2, 3, 2, 5, 2, 3, 2, 4, 2, 3, 2}; + return tz_table[x / 4]; +} +#else /* From http://supertech.csail.mit.edu/papers/debruijn.pdf */ +static inline unsigned ntz(unsigned x) { + static const unsigned char tz_table[32] = {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, + 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, + 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; + return tz_table[((uint32_t)((x & -x) * 0x077CB531u)) >> 27]; +} +#endif +#endif + +/* ----------------------------------------------------------------------- */ +/* Define blocks and operations -- Patch if incorrect on your compiler. */ +/* ----------------------------------------------------------------------- */ + +#if __SSE2__ && !KEYMASTER_CLANG_TEST_BUILD +#include <xmmintrin.h> /* SSE instructions and _mm_malloc */ +#include <emmintrin.h> /* SSE2 instructions */ +typedef __m128i block; +#define xor_block(x, y) _mm_xor_si128(x, y) +#define zero_block() _mm_setzero_si128() +#define unequal_blocks(x, y) (_mm_movemask_epi8(_mm_cmpeq_epi8(x, y)) != 0xffff) +#if __SSSE3__ || USE_AES_NI +#include <tmmintrin.h> /* SSSE3 instructions */ +#define swap_if_le(b) \ + _mm_shuffle_epi8(b, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)) +#else +static inline block swap_if_le(block b) { + block a = _mm_shuffle_epi32(b, _MM_SHUFFLE(0, 1, 2, 3)); + a = _mm_shufflehi_epi16(a, _MM_SHUFFLE(2, 3, 0, 1)); + a = _mm_shufflelo_epi16(a, _MM_SHUFFLE(2, 3, 0, 1)); + return _mm_xor_si128(_mm_srli_epi16(a, 8), _mm_slli_epi16(a, 8)); +} +#endif +static inline block gen_offset(uint64_t KtopStr[3], unsigned bot) { + block hi = _mm_load_si128((__m128i*)(KtopStr + 0)); /* hi = B A */ + block lo = _mm_loadu_si128((__m128i*)(KtopStr + 1)); /* lo = C B */ + __m128i lshift = _mm_cvtsi32_si128(bot); + __m128i rshift = _mm_cvtsi32_si128(64 - bot); + lo = _mm_xor_si128(_mm_sll_epi64(hi, lshift), _mm_srl_epi64(lo, rshift)); +#if __SSSE3__ || USE_AES_NI + return _mm_shuffle_epi8(lo, _mm_set_epi8(8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7)); +#else + return swap_if_le(_mm_shuffle_epi32(lo, _MM_SHUFFLE(1, 0, 3, 2))); +#endif +} +static inline block double_block(block bl) { + const __m128i mask = _mm_set_epi32(135, 1, 1, 1); + __m128i tmp = _mm_srai_epi32(bl, 31); + tmp = _mm_and_si128(tmp, mask); + tmp = _mm_shuffle_epi32(tmp, _MM_SHUFFLE(2, 1, 0, 3)); + bl = _mm_slli_epi32(bl, 1); + return _mm_xor_si128(bl, tmp); +} +#elif __ALTIVEC__ +#include <altivec.h> +typedef vector unsigned block; +#define xor_block(x, y) vec_xor(x, y) +#define zero_block() vec_splat_u32(0) +#define unequal_blocks(x, y) vec_any_ne(x, y) +#define swap_if_le(b) (b) +#if __PPC64__ +block gen_offset(uint64_t KtopStr[3], unsigned bot) { + union { + uint64_t u64[2]; + block bl; + } rval; + rval.u64[0] = (KtopStr[0] << bot) | (KtopStr[1] >> (64 - bot)); + rval.u64[1] = (KtopStr[1] << bot) | (KtopStr[2] >> (64 - bot)); + return rval.bl; +} +#else +/* Special handling: Shifts are mod 32, and no 64-bit types */ +block gen_offset(uint64_t KtopStr[3], unsigned bot) { + const vector unsigned k32 = {32, 32, 32, 32}; + vector unsigned hi = *(vector unsigned*)(KtopStr + 0); + vector unsigned lo = *(vector unsigned*)(KtopStr + 2); + vector unsigned bot_vec; + if (bot < 32) { + lo = vec_sld(hi, lo, 4); + } else { + vector unsigned t = vec_sld(hi, lo, 4); + lo = vec_sld(hi, lo, 8); + hi = t; + bot = bot - 32; + } + if (bot == 0) + return hi; + *(unsigned*)&bot_vec = bot; + vector unsigned lshift = vec_splat(bot_vec, 0); + vector unsigned rshift = vec_sub(k32, lshift); + hi = vec_sl(hi, lshift); + lo = vec_sr(lo, rshift); + return vec_xor(hi, lo); +} +#endif +static inline block double_block(block b) { + const vector unsigned char mask = {135, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + const vector unsigned char perm = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}; + const vector unsigned char shift7 = vec_splat_u8(7); + const vector unsigned char shift1 = vec_splat_u8(1); + vector unsigned char c = (vector unsigned char)b; + vector unsigned char t = vec_sra(c, shift7); + t = vec_and(t, mask); + t = vec_perm(t, t, perm); + c = vec_sl(c, shift1); + return (block)vec_xor(c, t); +} +#elif __ARM_NEON__ +#include <arm_neon.h> +typedef int8x16_t block; /* Yay! Endian-neutral reads! */ +#define xor_block(x, y) veorq_s8(x, y) +#define zero_block() vdupq_n_s8(0) +static inline int unequal_blocks(block a, block b) { + int64x2_t t = veorq_s64((int64x2_t)a, (int64x2_t)b); + return (vgetq_lane_s64(t, 0) | vgetq_lane_s64(t, 1)) != 0; +} +#define swap_if_le(b) (b) /* Using endian-neutral int8x16_t */ +/* KtopStr is reg correct by 64 bits, return mem correct */ +block gen_offset(uint64_t KtopStr[3], unsigned bot) { + const union { + unsigned x; + unsigned char endian; + } little = {1}; + const int64x2_t k64 = {-64, -64}; + uint64x2_t hi = *(uint64x2_t*)(KtopStr + 0); /* hi = A B */ + uint64x2_t lo = *(uint64x2_t*)(KtopStr + 1); /* hi = B C */ + int64x2_t ls = vdupq_n_s64(bot); + int64x2_t rs = vqaddq_s64(k64, ls); + block rval = (block)veorq_u64(vshlq_u64(hi, ls), vshlq_u64(lo, rs)); + if (little.endian) + rval = vrev64q_s8(rval); + return rval; +} +static inline block double_block(block b) { + const block mask = {135, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + block tmp = vshrq_n_s8(b, 7); + tmp = vandq_s8(tmp, mask); + tmp = vextq_s8(tmp, tmp, 1); /* Rotate high byte to end */ + b = vshlq_n_s8(b, 1); + return veorq_s8(tmp, b); +} +#else +typedef struct { uint64_t l, r; } block; +static inline block xor_block(block x, block y) { + x.l ^= y.l; + x.r ^= y.r; + return x; +} +static inline block zero_block(void) { + const block t = {0, 0}; + return t; +} +#define unequal_blocks(x, y) ((((x).l ^ (y).l) | ((x).r ^ (y).r)) != 0) +static inline block swap_if_le(block b) { + const union { + unsigned x; + unsigned char endian; + } little = {1}; + if (little.endian) { + block r; + r.l = bswap64(b.l); + r.r = bswap64(b.r); + return r; + } else + return b; +} + +/* KtopStr is reg correct by 64 bits, return mem correct */ +block gen_offset(uint64_t KtopStr[3], unsigned bot) { + block rval; + if (bot != 0) { + rval.l = (KtopStr[0] << bot) | (KtopStr[1] >> (64 - bot)); + rval.r = (KtopStr[1] << bot) | (KtopStr[2] >> (64 - bot)); + } else { + rval.l = KtopStr[0]; + rval.r = KtopStr[1]; + } + return swap_if_le(rval); +} + +#if __GNUC__ && __arm__ +static inline block double_block(block b) { + __asm__("adds %1,%1,%1\n\t" + "adcs %H1,%H1,%H1\n\t" + "adcs %0,%0,%0\n\t" + "adcs %H0,%H0,%H0\n\t" + "it cs\n\t" + "eorcs %1,%1,#135" + : "+r"(b.l), "+r"(b.r) + : + : "cc"); + return b; +} +#else +static inline block double_block(block b) { + uint64_t t = (uint64_t)((int64_t)b.l >> 63); + b.l = (b.l + b.l) ^ (b.r >> 63); + b.r = (b.r + b.r) ^ (t & 135); + return b; +} +#endif + +#endif + +/* ----------------------------------------------------------------------- */ +/* AES - Code uses OpenSSL API. Other implementations get mapped to it. */ +/* ----------------------------------------------------------------------- */ + +/*---------------*/ +#if USE_OPENSSL_AES +/*---------------*/ + +#include <openssl/aes.h> /* http://openssl.org/ */ + +/* How to ECB encrypt an array of blocks, in place */ +static inline void AES_ecb_encrypt_blks(block* blks, unsigned nblks, AES_KEY* key) { + while (nblks) { + --nblks; + AES_encrypt((unsigned char*)(blks + nblks), (unsigned char*)(blks + nblks), key); + } +} + +static inline void AES_ecb_decrypt_blks(block* blks, unsigned nblks, AES_KEY* key) { + while (nblks) { + --nblks; + AES_decrypt((unsigned char*)(blks + nblks), (unsigned char*)(blks + nblks), key); + } +} + +#define BPI 4 /* Number of blocks in buffer per ECB call */ + +/*-------------------*/ +#elif USE_REFERENCE_AES +/*-------------------*/ + +#include "rijndael-alg-fst.h" /* Barreto's Public-Domain Code */ +#if (OCB_KEY_LEN == 0) +typedef struct { + uint32_t rd_key[60]; + int rounds; +} AES_KEY; +#define ROUNDS(ctx) ((ctx)->rounds) +#define AES_set_encrypt_key(x, y, z) \ + do { \ + rijndaelKeySetupEnc((z)->rd_key, x, y); \ + (z)->rounds = y / 32 + 6; \ + } while (0) +#define AES_set_decrypt_key(x, y, z) \ + do { \ + rijndaelKeySetupDec((z)->rd_key, x, y); \ + (z)->rounds = y / 32 + 6; \ + } while (0) +#else +typedef struct { uint32_t rd_key[OCB_KEY_LEN + 28]; } AES_KEY; +#define ROUNDS(ctx) (6 + OCB_KEY_LEN / 4) +#define AES_set_encrypt_key(x, y, z) rijndaelKeySetupEnc((z)->rd_key, x, y) +#define AES_set_decrypt_key(x, y, z) rijndaelKeySetupDec((z)->rd_key, x, y) +#endif +#define AES_encrypt(x, y, z) rijndaelEncrypt((z)->rd_key, ROUNDS(z), x, y) +#define AES_decrypt(x, y, z) rijndaelDecrypt((z)->rd_key, ROUNDS(z), x, y) + +static void AES_ecb_encrypt_blks(block* blks, unsigned nblks, AES_KEY* key) { + while (nblks) { + --nblks; + AES_encrypt((unsigned char*)(blks + nblks), (unsigned char*)(blks + nblks), key); + } +} + +void AES_ecb_decrypt_blks(block* blks, unsigned nblks, AES_KEY* key) { + while (nblks) { + --nblks; + AES_decrypt((unsigned char*)(blks + nblks), (unsigned char*)(blks + nblks), key); + } +} + +#define BPI 4 /* Number of blocks in buffer per ECB call */ + +/*----------*/ +#elif USE_AES_NI +/*----------*/ + +#include <wmmintrin.h> + +#if (OCB_KEY_LEN == 0) +typedef struct { + __m128i rd_key[15]; + int rounds; +} AES_KEY; +#define ROUNDS(ctx) ((ctx)->rounds) +#else +typedef struct { __m128i rd_key[7 + OCB_KEY_LEN / 4]; } AES_KEY; +#define ROUNDS(ctx) (6 + OCB_KEY_LEN / 4) +#endif + +#define EXPAND_ASSIST(v1, v2, v3, v4, shuff_const, aes_const) \ + v2 = _mm_aeskeygenassist_si128(v4, aes_const); \ + v3 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(v3), _mm_castsi128_ps(v1), 16)); \ + v1 = _mm_xor_si128(v1, v3); \ + v3 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(v3), _mm_castsi128_ps(v1), 140)); \ + v1 = _mm_xor_si128(v1, v3); \ + v2 = _mm_shuffle_epi32(v2, shuff_const); \ + v1 = _mm_xor_si128(v1, v2) + +#define EXPAND192_STEP(idx, aes_const) \ + EXPAND_ASSIST(x0, x1, x2, x3, 85, aes_const); \ + x3 = _mm_xor_si128(x3, _mm_slli_si128(x3, 4)); \ + x3 = _mm_xor_si128(x3, _mm_shuffle_epi32(x0, 255)); \ + kp[idx] = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(tmp), _mm_castsi128_ps(x0), 68)); \ + kp[idx + 1] = \ + _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(x0), _mm_castsi128_ps(x3), 78)); \ + EXPAND_ASSIST(x0, x1, x2, x3, 85, (aes_const * 2)); \ + x3 = _mm_xor_si128(x3, _mm_slli_si128(x3, 4)); \ + x3 = _mm_xor_si128(x3, _mm_shuffle_epi32(x0, 255)); \ + kp[idx + 2] = x0; \ + tmp = x3 + +static void AES_128_Key_Expansion(const unsigned char* userkey, void* key) { + __m128i x0, x1, x2; + __m128i* kp = (__m128i*)key; + kp[0] = x0 = _mm_loadu_si128((__m128i*)userkey); + x2 = _mm_setzero_si128(); + EXPAND_ASSIST(x0, x1, x2, x0, 255, 1); + kp[1] = x0; + EXPAND_ASSIST(x0, x1, x2, x0, 255, 2); + kp[2] = x0; + EXPAND_ASSIST(x0, x1, x2, x0, 255, 4); + kp[3] = x0; + EXPAND_ASSIST(x0, x1, x2, x0, 255, 8); + kp[4] = x0; + EXPAND_ASSIST(x0, x1, x2, x0, 255, 16); + kp[5] = x0; + EXPAND_ASSIST(x0, x1, x2, x0, 255, 32); + kp[6] = x0; + EXPAND_ASSIST(x0, x1, x2, x0, 255, 64); + kp[7] = x0; + EXPAND_ASSIST(x0, x1, x2, x0, 255, 128); + kp[8] = x0; + EXPAND_ASSIST(x0, x1, x2, x0, 255, 27); + kp[9] = x0; + EXPAND_ASSIST(x0, x1, x2, x0, 255, 54); + kp[10] = x0; +} + +static void AES_192_Key_Expansion(const unsigned char* userkey, void* key) { + __m128i x0, x1, x2, x3, tmp, *kp = (__m128i*)key; + kp[0] = x0 = _mm_loadu_si128((__m128i*)userkey); + tmp = x3 = _mm_loadu_si128((__m128i*)(userkey + 16)); + x2 = _mm_setzero_si128(); + EXPAND192_STEP(1, 1); + EXPAND192_STEP(4, 4); + EXPAND192_STEP(7, 16); + EXPAND192_STEP(10, 64); +} + +static void AES_256_Key_Expansion(const unsigned char* userkey, void* key) { + __m128i x0, x1, x2, x3, *kp = (__m128i*)key; + kp[0] = x0 = _mm_loadu_si128((__m128i*)userkey); + kp[1] = x3 = _mm_loadu_si128((__m128i*)(userkey + 16)); + x2 = _mm_setzero_si128(); + EXPAND_ASSIST(x0, x1, x2, x3, 255, 1); + kp[2] = x0; + EXPAND_ASSIST(x3, x1, x2, x0, 170, 1); + kp[3] = x3; + EXPAND_ASSIST(x0, x1, x2, x3, 255, 2); + kp[4] = x0; + EXPAND_ASSIST(x3, x1, x2, x0, 170, 2); + kp[5] = x3; + EXPAND_ASSIST(x0, x1, x2, x3, 255, 4); + kp[6] = x0; + EXPAND_ASSIST(x3, x1, x2, x0, 170, 4); + kp[7] = x3; + EXPAND_ASSIST(x0, x1, x2, x3, 255, 8); + kp[8] = x0; + EXPAND_ASSIST(x3, x1, x2, x0, 170, 8); + kp[9] = x3; + EXPAND_ASSIST(x0, x1, x2, x3, 255, 16); + kp[10] = x0; + EXPAND_ASSIST(x3, x1, x2, x0, 170, 16); + kp[11] = x3; + EXPAND_ASSIST(x0, x1, x2, x3, 255, 32); + kp[12] = x0; + EXPAND_ASSIST(x3, x1, x2, x0, 170, 32); + kp[13] = x3; + EXPAND_ASSIST(x0, x1, x2, x3, 255, 64); + kp[14] = x0; +} + +static int AES_set_encrypt_key(const unsigned char* userKey, const int bits, AES_KEY* key) { + if (bits == 128) { + AES_128_Key_Expansion(userKey, key); + } else if (bits == 192) { + AES_192_Key_Expansion(userKey, key); + } else if (bits == 256) { + AES_256_Key_Expansion(userKey, key); + } +#if (OCB_KEY_LEN == 0) + key->rounds = 6 + bits / 32; +#endif + return 0; +} + +static void AES_set_decrypt_key_fast(AES_KEY* dkey, const AES_KEY* ekey) { + int j = 0; + int i = ROUNDS(ekey); +#if (OCB_KEY_LEN == 0) + dkey->rounds = i; +#endif + dkey->rd_key[i--] = ekey->rd_key[j++]; + while (i) + dkey->rd_key[i--] = _mm_aesimc_si128(ekey->rd_key[j++]); + dkey->rd_key[i] = ekey->rd_key[j]; +} + +static int AES_set_decrypt_key(const unsigned char* userKey, const int bits, AES_KEY* key) { + AES_KEY temp_key; + AES_set_encrypt_key(userKey, bits, &temp_key); + AES_set_decrypt_key_fast(key, &temp_key); + return 0; +} + +static inline void AES_encrypt(const unsigned char* in, unsigned char* out, const AES_KEY* key) { + int j, rnds = ROUNDS(key); + const __m128i* sched = ((__m128i*)(key->rd_key)); + __m128i tmp = _mm_load_si128((__m128i*)in); + tmp = _mm_xor_si128(tmp, sched[0]); + for (j = 1; j < rnds; j++) + tmp = _mm_aesenc_si128(tmp, sched[j]); + tmp = _mm_aesenclast_si128(tmp, sched[j]); + _mm_store_si128((__m128i*)out, tmp); +} + +static inline void AES_decrypt(const unsigned char* in, unsigned char* out, const AES_KEY* key) { + int j, rnds = ROUNDS(key); + const __m128i* sched = ((__m128i*)(key->rd_key)); + __m128i tmp = _mm_load_si128((__m128i*)in); + tmp = _mm_xor_si128(tmp, sched[0]); + for (j = 1; j < rnds; j++) + tmp = _mm_aesdec_si128(tmp, sched[j]); + tmp = _mm_aesdeclast_si128(tmp, sched[j]); + _mm_store_si128((__m128i*)out, tmp); +} + +static inline void AES_ecb_encrypt_blks(block* blks, unsigned nblks, AES_KEY* key) { + unsigned i, j, rnds = ROUNDS(key); + const __m128i* sched = ((__m128i*)(key->rd_key)); + for (i = 0; i < nblks; ++i) + blks[i] = _mm_xor_si128(blks[i], sched[0]); + for (j = 1; j < rnds; ++j) + for (i = 0; i < nblks; ++i) + blks[i] = _mm_aesenc_si128(blks[i], sched[j]); + for (i = 0; i < nblks; ++i) + blks[i] = _mm_aesenclast_si128(blks[i], sched[j]); +} + +static inline void AES_ecb_decrypt_blks(block* blks, unsigned nblks, AES_KEY* key) { + unsigned i, j, rnds = ROUNDS(key); + const __m128i* sched = ((__m128i*)(key->rd_key)); + for (i = 0; i < nblks; ++i) + blks[i] = _mm_xor_si128(blks[i], sched[0]); + for (j = 1; j < rnds; ++j) + for (i = 0; i < nblks; ++i) + blks[i] = _mm_aesdec_si128(blks[i], sched[j]); + for (i = 0; i < nblks; ++i) + blks[i] = _mm_aesdeclast_si128(blks[i], sched[j]); +} + +#define BPI 8 /* Number of blocks in buffer per ECB call */ +/* Set to 4 for Westmere, 8 for Sandy Bridge */ + +#endif + +/* ----------------------------------------------------------------------- */ +/* Define OCB context structure. */ +/* ----------------------------------------------------------------------- */ + +/*------------------------------------------------------------------------ +/ Each item in the OCB context is stored either "memory correct" or +/ "register correct". On big-endian machines, this is identical. On +/ little-endian machines, one must choose whether the byte-string +/ is in the correct order when it resides in memory or in registers. +/ It must be register correct whenever it is to be manipulated +/ arithmetically, but must be memory correct whenever it interacts +/ with the plaintext or ciphertext. +/------------------------------------------------------------------------- */ + +struct _ae_ctx { + block offset; /* Memory correct */ + block checksum; /* Memory correct */ + block Lstar; /* Memory correct */ + block Ldollar; /* Memory correct */ + block L[L_TABLE_SZ]; /* Memory correct */ + block ad_checksum; /* Memory correct */ + block ad_offset; /* Memory correct */ + block cached_Top; /* Memory correct */ + uint64_t KtopStr[3]; /* Register correct, each item */ + uint32_t ad_blocks_processed; + uint32_t blocks_processed; + AES_KEY decrypt_key; + AES_KEY encrypt_key; +#if (OCB_TAG_LEN == 0) + unsigned tag_len; +#endif +}; + +/* ----------------------------------------------------------------------- */ +/* L table lookup (or on-the-fly generation) */ +/* ----------------------------------------------------------------------- */ + +#if L_TABLE_SZ_IS_ENOUGH +#define getL(_ctx, _tz) ((_ctx)->L[_tz]) +#else +static block getL(const ae_ctx* ctx, unsigned tz) { + if (tz < L_TABLE_SZ) + return ctx->L[tz]; + else { + unsigned i; + /* Bring L[MAX] into registers, make it register correct */ + block rval = swap_if_le(ctx->L[L_TABLE_SZ - 1]); + rval = double_block(rval); + for (i = L_TABLE_SZ; i < tz; i++) + rval = double_block(rval); + return swap_if_le(rval); /* To memory correct */ + } +} +#endif + +/* ----------------------------------------------------------------------- */ +/* Public functions */ +/* ----------------------------------------------------------------------- */ + +/* 32-bit SSE2 and Altivec systems need to be forced to allocate memory + on 16-byte alignments. (I believe all major 64-bit systems do already.) */ + +ae_ctx* ae_allocate(void* misc) { + void* p; + (void)misc; /* misc unused in this implementation */ +#if (__SSE2__ && !_M_X64 && !_M_AMD64 && !__amd64__) + p = _mm_malloc(sizeof(ae_ctx), 16); +#elif(__ALTIVEC__ && !__PPC64__) + if (posix_memalign(&p, 16, sizeof(ae_ctx)) != 0) + p = NULL; +#else + p = malloc(sizeof(ae_ctx)); +#endif + return (ae_ctx*)p; +} + +void ae_free(ae_ctx* ctx) { +#if (__SSE2__ && !_M_X64 && !_M_AMD64 && !__amd64__) + _mm_free(ctx); +#else + free(ctx); +#endif +} + +/* ----------------------------------------------------------------------- */ + +int ae_clear(ae_ctx* ctx) /* Zero ae_ctx and undo initialization */ +{ + memset(ctx, 0, sizeof(ae_ctx)); + return AE_SUCCESS; +} + +int ae_ctx_sizeof(void) { + return (int)sizeof(ae_ctx); +} + +/* ----------------------------------------------------------------------- */ + +int ae_init(ae_ctx* ctx, const void* key, int key_len, int nonce_len, int tag_len) { + unsigned i; + block tmp_blk; + + if (nonce_len != 12) + return AE_NOT_SUPPORTED; + +/* Initialize encryption & decryption keys */ +#if (OCB_KEY_LEN > 0) + key_len = OCB_KEY_LEN; +#endif + AES_set_encrypt_key((unsigned char*)key, key_len * 8, &ctx->encrypt_key); +#if USE_AES_NI + AES_set_decrypt_key_fast(&ctx->decrypt_key, &ctx->encrypt_key); +#else + AES_set_decrypt_key((unsigned char*)key, (int)(key_len * 8), &ctx->decrypt_key); +#endif + + /* Zero things that need zeroing */ + ctx->cached_Top = ctx->ad_checksum = zero_block(); + ctx->ad_blocks_processed = 0; + + /* Compute key-dependent values */ + AES_encrypt((unsigned char*)&ctx->cached_Top, (unsigned char*)&ctx->Lstar, &ctx->encrypt_key); + tmp_blk = swap_if_le(ctx->Lstar); + tmp_blk = double_block(tmp_blk); + ctx->Ldollar = swap_if_le(tmp_blk); + tmp_blk = double_block(tmp_blk); + ctx->L[0] = swap_if_le(tmp_blk); + for (i = 1; i < L_TABLE_SZ; i++) { + tmp_blk = double_block(tmp_blk); + ctx->L[i] = swap_if_le(tmp_blk); + } + +#if (OCB_TAG_LEN == 0) + ctx->tag_len = tag_len; +#else + (void)tag_len; /* Suppress var not used error */ +#endif + + return AE_SUCCESS; +} + +/* ----------------------------------------------------------------------- */ + +static block gen_offset_from_nonce(ae_ctx* ctx, const void* nonce) { + const union { + unsigned x; + unsigned char endian; + } little = {1}; + union { + uint32_t u32[4]; + uint8_t u8[16]; + block bl; + } tmp; + unsigned idx; + +/* Replace cached nonce Top if needed */ +#if (OCB_TAG_LEN > 0) + if (little.endian) + tmp.u32[0] = 0x01000000 + ((OCB_TAG_LEN * 8 % 128) << 1); + else + tmp.u32[0] = 0x00000001 + ((OCB_TAG_LEN * 8 % 128) << 25); +#else + if (little.endian) + tmp.u32[0] = 0x01000000 + ((ctx->tag_len * 8 % 128) << 1); + else + tmp.u32[0] = 0x00000001 + ((ctx->tag_len * 8 % 128) << 25); +#endif + tmp.u32[1] = ((uint32_t*)nonce)[0]; + tmp.u32[2] = ((uint32_t*)nonce)[1]; + tmp.u32[3] = ((uint32_t*)nonce)[2]; + idx = (unsigned)(tmp.u8[15] & 0x3f); /* Get low 6 bits of nonce */ + tmp.u8[15] = tmp.u8[15] & 0xc0; /* Zero low 6 bits of nonce */ + if (unequal_blocks(tmp.bl, ctx->cached_Top)) { /* Cached? */ + ctx->cached_Top = tmp.bl; /* Update cache, KtopStr */ + AES_encrypt(tmp.u8, (unsigned char*)&ctx->KtopStr, &ctx->encrypt_key); + if (little.endian) { /* Make Register Correct */ + ctx->KtopStr[0] = bswap64(ctx->KtopStr[0]); + ctx->KtopStr[1] = bswap64(ctx->KtopStr[1]); + } + ctx->KtopStr[2] = ctx->KtopStr[0] ^ (ctx->KtopStr[0] << 8) ^ (ctx->KtopStr[1] >> 56); + } + return gen_offset(ctx->KtopStr, idx); +} + +static void process_ad(ae_ctx* ctx, const void* ad, int ad_len, int final) { + union { + uint32_t u32[4]; + uint8_t u8[16]; + block bl; + } tmp; + block ad_offset, ad_checksum; + const block* adp = (block*)ad; + unsigned i, k, tz, remaining; + + ad_offset = ctx->ad_offset; + ad_checksum = ctx->ad_checksum; + i = ad_len / (BPI * 16); + if (i) { + unsigned ad_block_num = ctx->ad_blocks_processed; + do { + block ta[BPI], oa[BPI]; + ad_block_num += BPI; + tz = ntz(ad_block_num); + oa[0] = xor_block(ad_offset, ctx->L[0]); + ta[0] = xor_block(oa[0], adp[0]); + oa[1] = xor_block(oa[0], ctx->L[1]); + ta[1] = xor_block(oa[1], adp[1]); + oa[2] = xor_block(ad_offset, ctx->L[1]); + ta[2] = xor_block(oa[2], adp[2]); +#if BPI == 4 + ad_offset = xor_block(oa[2], getL(ctx, tz)); + ta[3] = xor_block(ad_offset, adp[3]); +#elif BPI == 8 + oa[3] = xor_block(oa[2], ctx->L[2]); + ta[3] = xor_block(oa[3], adp[3]); + oa[4] = xor_block(oa[1], ctx->L[2]); + ta[4] = xor_block(oa[4], adp[4]); + oa[5] = xor_block(oa[0], ctx->L[2]); + ta[5] = xor_block(oa[5], adp[5]); + oa[6] = xor_block(ad_offset, ctx->L[2]); + ta[6] = xor_block(oa[6], adp[6]); + ad_offset = xor_block(oa[6], getL(ctx, tz)); + ta[7] = xor_block(ad_offset, adp[7]); +#endif + AES_ecb_encrypt_blks(ta, BPI, &ctx->encrypt_key); + ad_checksum = xor_block(ad_checksum, ta[0]); + ad_checksum = xor_block(ad_checksum, ta[1]); + ad_checksum = xor_block(ad_checksum, ta[2]); + ad_checksum = xor_block(ad_checksum, ta[3]); +#if (BPI == 8) + ad_checksum = xor_block(ad_checksum, ta[4]); + ad_checksum = xor_block(ad_checksum, ta[5]); + ad_checksum = xor_block(ad_checksum, ta[6]); + ad_checksum = xor_block(ad_checksum, ta[7]); +#endif + adp += BPI; + } while (--i); + ctx->ad_blocks_processed = ad_block_num; + ctx->ad_offset = ad_offset; + ctx->ad_checksum = ad_checksum; + } + + if (final) { + block ta[BPI]; + + /* Process remaining associated data, compute its tag contribution */ + remaining = ((unsigned)ad_len) % (BPI * 16); + if (remaining) { + k = 0; +#if (BPI == 8) + if (remaining >= 64) { + tmp.bl = xor_block(ad_offset, ctx->L[0]); + ta[0] = xor_block(tmp.bl, adp[0]); + tmp.bl = xor_block(tmp.bl, ctx->L[1]); + ta[1] = xor_block(tmp.bl, adp[1]); + ad_offset = xor_block(ad_offset, ctx->L[1]); + ta[2] = xor_block(ad_offset, adp[2]); + ad_offset = xor_block(ad_offset, ctx->L[2]); + ta[3] = xor_block(ad_offset, adp[3]); + remaining -= 64; + k = 4; + } +#endif + if (remaining >= 32) { + ad_offset = xor_block(ad_offset, ctx->L[0]); + ta[k] = xor_block(ad_offset, adp[k]); + ad_offset = xor_block(ad_offset, getL(ctx, ntz(k + 2))); + ta[k + 1] = xor_block(ad_offset, adp[k + 1]); + remaining -= 32; + k += 2; + } + if (remaining >= 16) { + ad_offset = xor_block(ad_offset, ctx->L[0]); + ta[k] = xor_block(ad_offset, adp[k]); + remaining = remaining - 16; + ++k; + } + if (remaining) { + ad_offset = xor_block(ad_offset, ctx->Lstar); + tmp.bl = zero_block(); + memcpy(tmp.u8, adp + k, remaining); + tmp.u8[remaining] = (unsigned char)0x80u; + ta[k] = xor_block(ad_offset, tmp.bl); + ++k; + } + AES_ecb_encrypt_blks(ta, k, &ctx->encrypt_key); + switch (k) { +#if (BPI == 8) + case 8: + ad_checksum = xor_block(ad_checksum, ta[7]); + case 7: + ad_checksum = xor_block(ad_checksum, ta[6]); + case 6: + ad_checksum = xor_block(ad_checksum, ta[5]); + case 5: + ad_checksum = xor_block(ad_checksum, ta[4]); +#endif + case 4: + ad_checksum = xor_block(ad_checksum, ta[3]); + case 3: + ad_checksum = xor_block(ad_checksum, ta[2]); + case 2: + ad_checksum = xor_block(ad_checksum, ta[1]); + case 1: + ad_checksum = xor_block(ad_checksum, ta[0]); + } + ctx->ad_checksum = ad_checksum; + } + } +} + +/* ----------------------------------------------------------------------- */ + +int ae_encrypt(ae_ctx* ctx, const void* nonce, const void* pt, int pt_len, const void* ad, + int ad_len, void* ct, void* tag, int final) { + union { + uint32_t u32[4]; + uint8_t u8[16]; + block bl; + } tmp; + block offset, checksum; + unsigned i, k; + block* ctp = (block*)ct; + const block* ptp = (block*)pt; + + /* Non-null nonce means start of new message, init per-message values */ + if (nonce) { + ctx->offset = gen_offset_from_nonce(ctx, nonce); + ctx->ad_offset = ctx->checksum = zero_block(); + ctx->ad_blocks_processed = ctx->blocks_processed = 0; + if (ad_len >= 0) + ctx->ad_checksum = zero_block(); + } + + /* Process associated data */ + if (ad_len > 0) + process_ad(ctx, ad, ad_len, final); + + /* Encrypt plaintext data BPI blocks at a time */ + offset = ctx->offset; + checksum = ctx->checksum; + i = pt_len / (BPI * 16); + if (i) { + block oa[BPI]; + unsigned block_num = ctx->blocks_processed; + oa[BPI - 1] = offset; + do { + block ta[BPI]; + block_num += BPI; + oa[0] = xor_block(oa[BPI - 1], ctx->L[0]); + ta[0] = xor_block(oa[0], ptp[0]); + checksum = xor_block(checksum, ptp[0]); + oa[1] = xor_block(oa[0], ctx->L[1]); + ta[1] = xor_block(oa[1], ptp[1]); + checksum = xor_block(checksum, ptp[1]); + oa[2] = xor_block(oa[1], ctx->L[0]); + ta[2] = xor_block(oa[2], ptp[2]); + checksum = xor_block(checksum, ptp[2]); +#if BPI == 4 + oa[3] = xor_block(oa[2], getL(ctx, ntz(block_num))); + ta[3] = xor_block(oa[3], ptp[3]); + checksum = xor_block(checksum, ptp[3]); +#elif BPI == 8 + oa[3] = xor_block(oa[2], ctx->L[2]); + ta[3] = xor_block(oa[3], ptp[3]); + checksum = xor_block(checksum, ptp[3]); + oa[4] = xor_block(oa[1], ctx->L[2]); + ta[4] = xor_block(oa[4], ptp[4]); + checksum = xor_block(checksum, ptp[4]); + oa[5] = xor_block(oa[0], ctx->L[2]); + ta[5] = xor_block(oa[5], ptp[5]); + checksum = xor_block(checksum, ptp[5]); + oa[6] = xor_block(oa[7], ctx->L[2]); + ta[6] = xor_block(oa[6], ptp[6]); + checksum = xor_block(checksum, ptp[6]); + oa[7] = xor_block(oa[6], getL(ctx, ntz(block_num))); + ta[7] = xor_block(oa[7], ptp[7]); + checksum = xor_block(checksum, ptp[7]); +#endif + AES_ecb_encrypt_blks(ta, BPI, &ctx->encrypt_key); + ctp[0] = xor_block(ta[0], oa[0]); + ctp[1] = xor_block(ta[1], oa[1]); + ctp[2] = xor_block(ta[2], oa[2]); + ctp[3] = xor_block(ta[3], oa[3]); +#if (BPI == 8) + ctp[4] = xor_block(ta[4], oa[4]); + ctp[5] = xor_block(ta[5], oa[5]); + ctp[6] = xor_block(ta[6], oa[6]); + ctp[7] = xor_block(ta[7], oa[7]); +#endif + ptp += BPI; + ctp += BPI; + } while (--i); + ctx->offset = offset = oa[BPI - 1]; + ctx->blocks_processed = block_num; + ctx->checksum = checksum; + } + + if (final) { + block ta[BPI + 1], oa[BPI]; + + /* Process remaining plaintext and compute its tag contribution */ + unsigned remaining = ((unsigned)pt_len) % (BPI * 16); + k = 0; /* How many blocks in ta[] need ECBing */ + if (remaining) { +#if (BPI == 8) + if (remaining >= 64) { + oa[0] = xor_block(offset, ctx->L[0]); + ta[0] = xor_block(oa[0], ptp[0]); + checksum = xor_block(checksum, ptp[0]); + oa[1] = xor_block(oa[0], ctx->L[1]); + ta[1] = xor_block(oa[1], ptp[1]); + checksum = xor_block(checksum, ptp[1]); + oa[2] = xor_block(oa[1], ctx->L[0]); + ta[2] = xor_block(oa[2], ptp[2]); + checksum = xor_block(checksum, ptp[2]); + offset = oa[3] = xor_block(oa[2], ctx->L[2]); + ta[3] = xor_block(offset, ptp[3]); + checksum = xor_block(checksum, ptp[3]); + remaining -= 64; + k = 4; + } +#endif + if (remaining >= 32) { + oa[k] = xor_block(offset, ctx->L[0]); + ta[k] = xor_block(oa[k], ptp[k]); + checksum = xor_block(checksum, ptp[k]); + offset = oa[k + 1] = xor_block(oa[k], ctx->L[1]); + ta[k + 1] = xor_block(offset, ptp[k + 1]); + checksum = xor_block(checksum, ptp[k + 1]); + remaining -= 32; + k += 2; + } + if (remaining >= 16) { + offset = oa[k] = xor_block(offset, ctx->L[0]); + ta[k] = xor_block(offset, ptp[k]); + checksum = xor_block(checksum, ptp[k]); + remaining -= 16; + ++k; + } + if (remaining) { + tmp.bl = zero_block(); + memcpy(tmp.u8, ptp + k, remaining); + tmp.u8[remaining] = (unsigned char)0x80u; + checksum = xor_block(checksum, tmp.bl); + ta[k] = offset = xor_block(offset, ctx->Lstar); + ++k; + } + } + offset = xor_block(offset, ctx->Ldollar); /* Part of tag gen */ + ta[k] = xor_block(offset, checksum); /* Part of tag gen */ + AES_ecb_encrypt_blks(ta, k + 1, &ctx->encrypt_key); + offset = xor_block(ta[k], ctx->ad_checksum); /* Part of tag gen */ + if (remaining) { + --k; + tmp.bl = xor_block(tmp.bl, ta[k]); + memcpy(ctp + k, tmp.u8, remaining); + } + switch (k) { +#if (BPI == 8) + case 7: + ctp[6] = xor_block(ta[6], oa[6]); + case 6: + ctp[5] = xor_block(ta[5], oa[5]); + case 5: + ctp[4] = xor_block(ta[4], oa[4]); + case 4: + ctp[3] = xor_block(ta[3], oa[3]); +#endif + case 3: + ctp[2] = xor_block(ta[2], oa[2]); + case 2: + ctp[1] = xor_block(ta[1], oa[1]); + case 1: + ctp[0] = xor_block(ta[0], oa[0]); + } + + /* Tag is placed at the correct location + */ + if (tag) { +#if (OCB_TAG_LEN == 16) + *(block*)tag = offset; +#elif(OCB_TAG_LEN > 0) + memcpy((char*)tag, &offset, OCB_TAG_LEN); +#else + memcpy((char*)tag, &offset, ctx->tag_len); +#endif + } else { +#if (OCB_TAG_LEN > 0) + memcpy((char*)ct + pt_len, &offset, OCB_TAG_LEN); + pt_len += OCB_TAG_LEN; +#else + memcpy((char*)ct + pt_len, &offset, ctx->tag_len); + pt_len += ctx->tag_len; +#endif + } + } + return (int)pt_len; +} + +/* ----------------------------------------------------------------------- */ + +/* Compare two regions of memory, taking a constant amount of time for a + given buffer size -- under certain assumptions about the compiler + and machine, of course. + + Use this to avoid timing side-channel attacks. + + Returns 0 for memory regions with equal contents; non-zero otherwise. */ +static int constant_time_memcmp(const void* av, const void* bv, size_t n) { + const uint8_t* a = (const uint8_t*)av; + const uint8_t* b = (const uint8_t*)bv; + uint8_t result = 0; + size_t i; + + for (i = 0; i < n; i++) { + result |= *a ^ *b; + a++; + b++; + } + + return (int)result; +} + +int ae_decrypt(ae_ctx* ctx, const void* nonce, const void* ct, int ct_len, const void* ad, + int ad_len, void* pt, const void* tag, int final) { + union { + uint32_t u32[4]; + uint8_t u8[16]; + block bl; + } tmp; + block offset, checksum; + unsigned i, k; + block* ctp = (block*)ct; + block* ptp = (block*)pt; + + /* Reduce ct_len tag bundled in ct */ + if ((final) && (!tag)) +#if (OCB_TAG_LEN > 0) + ct_len -= OCB_TAG_LEN; +#else + ct_len -= ctx->tag_len; +#endif + + /* Non-null nonce means start of new message, init per-message values */ + if (nonce) { + ctx->offset = gen_offset_from_nonce(ctx, nonce); + ctx->ad_offset = ctx->checksum = zero_block(); + ctx->ad_blocks_processed = ctx->blocks_processed = 0; + if (ad_len >= 0) + ctx->ad_checksum = zero_block(); + } + + /* Process associated data */ + if (ad_len > 0) + process_ad(ctx, ad, ad_len, final); + + /* Encrypt plaintext data BPI blocks at a time */ + offset = ctx->offset; + checksum = ctx->checksum; + i = ct_len / (BPI * 16); + if (i) { + block oa[BPI]; + unsigned block_num = ctx->blocks_processed; + oa[BPI - 1] = offset; + do { + block ta[BPI]; + block_num += BPI; + oa[0] = xor_block(oa[BPI - 1], ctx->L[0]); + ta[0] = xor_block(oa[0], ctp[0]); + oa[1] = xor_block(oa[0], ctx->L[1]); + ta[1] = xor_block(oa[1], ctp[1]); + oa[2] = xor_block(oa[1], ctx->L[0]); + ta[2] = xor_block(oa[2], ctp[2]); +#if BPI == 4 + oa[3] = xor_block(oa[2], getL(ctx, ntz(block_num))); + ta[3] = xor_block(oa[3], ctp[3]); +#elif BPI == 8 + oa[3] = xor_block(oa[2], ctx->L[2]); + ta[3] = xor_block(oa[3], ctp[3]); + oa[4] = xor_block(oa[1], ctx->L[2]); + ta[4] = xor_block(oa[4], ctp[4]); + oa[5] = xor_block(oa[0], ctx->L[2]); + ta[5] = xor_block(oa[5], ctp[5]); + oa[6] = xor_block(oa[7], ctx->L[2]); + ta[6] = xor_block(oa[6], ctp[6]); + oa[7] = xor_block(oa[6], getL(ctx, ntz(block_num))); + ta[7] = xor_block(oa[7], ctp[7]); +#endif + AES_ecb_decrypt_blks(ta, BPI, &ctx->decrypt_key); + ptp[0] = xor_block(ta[0], oa[0]); + checksum = xor_block(checksum, ptp[0]); + ptp[1] = xor_block(ta[1], oa[1]); + checksum = xor_block(checksum, ptp[1]); + ptp[2] = xor_block(ta[2], oa[2]); + checksum = xor_block(checksum, ptp[2]); + ptp[3] = xor_block(ta[3], oa[3]); + checksum = xor_block(checksum, ptp[3]); +#if (BPI == 8) + ptp[4] = xor_block(ta[4], oa[4]); + checksum = xor_block(checksum, ptp[4]); + ptp[5] = xor_block(ta[5], oa[5]); + checksum = xor_block(checksum, ptp[5]); + ptp[6] = xor_block(ta[6], oa[6]); + checksum = xor_block(checksum, ptp[6]); + ptp[7] = xor_block(ta[7], oa[7]); + checksum = xor_block(checksum, ptp[7]); +#endif + ptp += BPI; + ctp += BPI; + } while (--i); + ctx->offset = offset = oa[BPI - 1]; + ctx->blocks_processed = block_num; + ctx->checksum = checksum; + } + + if (final) { + block ta[BPI + 1], oa[BPI]; + + /* Process remaining plaintext and compute its tag contribution */ + unsigned remaining = ((unsigned)ct_len) % (BPI * 16); + k = 0; /* How many blocks in ta[] need ECBing */ + if (remaining) { +#if (BPI == 8) + if (remaining >= 64) { + oa[0] = xor_block(offset, ctx->L[0]); + ta[0] = xor_block(oa[0], ctp[0]); + oa[1] = xor_block(oa[0], ctx->L[1]); + ta[1] = xor_block(oa[1], ctp[1]); + oa[2] = xor_block(oa[1], ctx->L[0]); + ta[2] = xor_block(oa[2], ctp[2]); + offset = oa[3] = xor_block(oa[2], ctx->L[2]); + ta[3] = xor_block(offset, ctp[3]); + remaining -= 64; + k = 4; + } +#endif + if (remaining >= 32) { + oa[k] = xor_block(offset, ctx->L[0]); + ta[k] = xor_block(oa[k], ctp[k]); + offset = oa[k + 1] = xor_block(oa[k], ctx->L[1]); + ta[k + 1] = xor_block(offset, ctp[k + 1]); + remaining -= 32; + k += 2; + } + if (remaining >= 16) { + offset = oa[k] = xor_block(offset, ctx->L[0]); + ta[k] = xor_block(offset, ctp[k]); + remaining -= 16; + ++k; + } + if (remaining) { + block pad; + offset = xor_block(offset, ctx->Lstar); + AES_encrypt((unsigned char*)&offset, tmp.u8, &ctx->encrypt_key); + pad = tmp.bl; + memcpy(tmp.u8, ctp + k, remaining); + tmp.bl = xor_block(tmp.bl, pad); + tmp.u8[remaining] = (unsigned char)0x80u; + memcpy(ptp + k, tmp.u8, remaining); + checksum = xor_block(checksum, tmp.bl); + } + } + AES_ecb_decrypt_blks(ta, k, &ctx->decrypt_key); + switch (k) { +#if (BPI == 8) + case 7: + ptp[6] = xor_block(ta[6], oa[6]); + checksum = xor_block(checksum, ptp[6]); + case 6: + ptp[5] = xor_block(ta[5], oa[5]); + checksum = xor_block(checksum, ptp[5]); + case 5: + ptp[4] = xor_block(ta[4], oa[4]); + checksum = xor_block(checksum, ptp[4]); + case 4: + ptp[3] = xor_block(ta[3], oa[3]); + checksum = xor_block(checksum, ptp[3]); +#endif + case 3: + ptp[2] = xor_block(ta[2], oa[2]); + checksum = xor_block(checksum, ptp[2]); + case 2: + ptp[1] = xor_block(ta[1], oa[1]); + checksum = xor_block(checksum, ptp[1]); + case 1: + ptp[0] = xor_block(ta[0], oa[0]); + checksum = xor_block(checksum, ptp[0]); + } + + /* Calculate expected tag */ + offset = xor_block(offset, ctx->Ldollar); + tmp.bl = xor_block(offset, checksum); + AES_encrypt(tmp.u8, tmp.u8, &ctx->encrypt_key); + tmp.bl = xor_block(tmp.bl, ctx->ad_checksum); /* Full tag */ + + /* Compare with proposed tag, change ct_len if invalid */ + if ((OCB_TAG_LEN == 16) && tag) { + if (unequal_blocks(tmp.bl, *(block*)tag)) + ct_len = AE_INVALID; + } else { +#if (OCB_TAG_LEN > 0) + int len = OCB_TAG_LEN; +#else + int len = ctx->tag_len; +#endif + if (tag) { + if (constant_time_memcmp(tag, tmp.u8, len) != 0) + ct_len = AE_INVALID; + } else { + if (constant_time_memcmp((char*)ct + ct_len, tmp.u8, len) != 0) + ct_len = AE_INVALID; + } + } + } + return ct_len; +} + +/* ----------------------------------------------------------------------- */ +/* Simple test program */ +/* ----------------------------------------------------------------------- */ + +#if 0 + +#include <stdio.h> +#include <time.h> + +#if __GNUC__ +#define ALIGN(n) __attribute__((aligned(n))) +#elif _MSC_VER +#define ALIGN(n) __declspec(align(n)) +#else /* Not GNU/Microsoft: delete alignment uses. */ +#define ALIGN(n) +#endif + +static void pbuf(void *p, unsigned len, const void *s) +{ + unsigned i; + if (s) + printf("%s", (char *)s); + for (i = 0; i < len; i++) + printf("%02X", (unsigned)(((unsigned char *)p)[i])); + printf("\n"); +} + +static void vectors(ae_ctx *ctx, int len) +{ + ALIGN(16) char pt[128]; + ALIGN(16) char ct[144]; + ALIGN(16) char nonce[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + int i; + for (i=0; i < 128; i++) pt[i] = i; + i = ae_encrypt(ctx,nonce,pt,len,pt,len,ct,NULL,AE_FINALIZE); + printf("P=%d,A=%d: ",len,len); pbuf(ct, i, NULL); + i = ae_encrypt(ctx,nonce,pt,0,pt,len,ct,NULL,AE_FINALIZE); + printf("P=%d,A=%d: ",0,len); pbuf(ct, i, NULL); + i = ae_encrypt(ctx,nonce,pt,len,pt,0,ct,NULL,AE_FINALIZE); + printf("P=%d,A=%d: ",len,0); pbuf(ct, i, NULL); +} + +void validate() +{ + ALIGN(16) char pt[1024]; + ALIGN(16) char ct[1024]; + ALIGN(16) char tag[16]; + ALIGN(16) char nonce[12] = {0,}; + ALIGN(16) char key[32] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31}; + ae_ctx ctx; + char *val_buf, *next; + int i, len; + + val_buf = (char *)malloc(22400 + 16); + next = val_buf = (char *)(((size_t)val_buf + 16) & ~((size_t)15)); + + if (0) { + ae_init(&ctx, key, 16, 12, 16); + /* pbuf(&ctx, sizeof(ctx), "CTX: "); */ + vectors(&ctx,0); + vectors(&ctx,8); + vectors(&ctx,16); + vectors(&ctx,24); + vectors(&ctx,32); + vectors(&ctx,40); + } + + memset(key,0,32); + memset(pt,0,128); + ae_init(&ctx, key, OCB_KEY_LEN, 12, OCB_TAG_LEN); + + /* RFC Vector test */ + for (i = 0; i < 128; i++) { + int first = ((i/3)/(BPI*16))*(BPI*16); + int second = first; + int third = i - (first + second); + + nonce[11] = i; + + if (0) { + ae_encrypt(&ctx,nonce,pt,i,pt,i,ct,NULL,AE_FINALIZE); + memcpy(next,ct,(size_t)i+OCB_TAG_LEN); + next = next+i+OCB_TAG_LEN; + + ae_encrypt(&ctx,nonce,pt,i,pt,0,ct,NULL,AE_FINALIZE); + memcpy(next,ct,(size_t)i+OCB_TAG_LEN); + next = next+i+OCB_TAG_LEN; + + ae_encrypt(&ctx,nonce,pt,0,pt,i,ct,NULL,AE_FINALIZE); + memcpy(next,ct,OCB_TAG_LEN); + next = next+OCB_TAG_LEN; + } else { + ae_encrypt(&ctx,nonce,pt,first,pt,first,ct,NULL,AE_PENDING); + ae_encrypt(&ctx,NULL,pt+first,second,pt+first,second,ct+first,NULL,AE_PENDING); + ae_encrypt(&ctx,NULL,pt+first+second,third,pt+first+second,third,ct+first+second,NULL,AE_FINALIZE); + memcpy(next,ct,(size_t)i+OCB_TAG_LEN); + next = next+i+OCB_TAG_LEN; + + ae_encrypt(&ctx,nonce,pt,first,pt,0,ct,NULL,AE_PENDING); + ae_encrypt(&ctx,NULL,pt+first,second,pt,0,ct+first,NULL,AE_PENDING); + ae_encrypt(&ctx,NULL,pt+first+second,third,pt,0,ct+first+second,NULL,AE_FINALIZE); + memcpy(next,ct,(size_t)i+OCB_TAG_LEN); + next = next+i+OCB_TAG_LEN; + + ae_encrypt(&ctx,nonce,pt,0,pt,first,ct,NULL,AE_PENDING); + ae_encrypt(&ctx,NULL,pt,0,pt+first,second,ct,NULL,AE_PENDING); + ae_encrypt(&ctx,NULL,pt,0,pt+first+second,third,ct,NULL,AE_FINALIZE); + memcpy(next,ct,OCB_TAG_LEN); + next = next+OCB_TAG_LEN; + } + + } + nonce[11] = 0; + ae_encrypt(&ctx,nonce,NULL,0,val_buf,next-val_buf,ct,tag,AE_FINALIZE); + pbuf(tag,OCB_TAG_LEN,0); + + + /* Encrypt/Decrypt test */ + for (i = 0; i < 128; i++) { + int first = ((i/3)/(BPI*16))*(BPI*16); + int second = first; + int third = i - (first + second); + + nonce[11] = i%128; + + if (1) { + len = ae_encrypt(&ctx,nonce,val_buf,i,val_buf,i,ct,tag,AE_FINALIZE); + len = ae_encrypt(&ctx,nonce,val_buf,i,val_buf,-1,ct,tag,AE_FINALIZE); + len = ae_decrypt(&ctx,nonce,ct,len,val_buf,-1,pt,tag,AE_FINALIZE); + if (len == -1) { printf("Authentication error: %d\n", i); return; } + if (len != i) { printf("Length error: %d\n", i); return; } + if (memcmp(val_buf,pt,i)) { printf("Decrypt error: %d\n", i); return; } + } else { + len = ae_encrypt(&ctx,nonce,val_buf,i,val_buf,i,ct,NULL,AE_FINALIZE); + ae_decrypt(&ctx,nonce,ct,first,val_buf,first,pt,NULL,AE_PENDING); + ae_decrypt(&ctx,NULL,ct+first,second,val_buf+first,second,pt+first,NULL,AE_PENDING); + len = ae_decrypt(&ctx,NULL,ct+first+second,len-(first+second),val_buf+first+second,third,pt+first+second,NULL,AE_FINALIZE); + if (len == -1) { printf("Authentication error: %d\n", i); return; } + if (memcmp(val_buf,pt,i)) { printf("Decrypt error: %d\n", i); return; } + } + + } + printf("Decrypt: PASS\n"); +} + +int main() +{ + validate(); + return 0; +} +#endif + +#if USE_AES_NI +char infoString[] = "OCB3 (AES-NI)"; +#elif USE_REFERENCE_AES +char infoString[] = "OCB3 (Reference)"; +#elif USE_OPENSSL_AES +char infoString[] = "OCB3 (OpenSSL)"; +#endif
diff --git a/keymaster/ocb_utils.cpp b/keymaster/ocb_utils.cpp new file mode 100644 index 0000000..7038da0 --- /dev/null +++ b/keymaster/ocb_utils.cpp
@@ -0,0 +1,194 @@ +/* + * 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 "ocb_utils.h" + +#include <assert.h> + +#include <new> + +#include <openssl/aes.h> +#include <openssl/sha.h> + +#include <hardware/keymaster_defs.h> +#include <keymaster/authorization_set.h> +#include <keymaster/android_keymaster_utils.h> + +#include "openssl_err.h" + +namespace keymaster { + +class AeCtx { + public: + AeCtx() : ctx_(ae_allocate(NULL)) {} + ~AeCtx() { + ae_clear(ctx_); + ae_free(ctx_); + } + + ae_ctx* get() { return ctx_; } + + private: + ae_ctx* ctx_; +}; + +static keymaster_error_t BuildDerivationData(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + const AuthorizationSet& hidden, + UniquePtr<uint8_t[]>* derivation_data, + size_t* derivation_data_length) { + *derivation_data_length = + hidden.SerializedSize() + hw_enforced.SerializedSize() + sw_enforced.SerializedSize(); + derivation_data->reset(new (std::nothrow) uint8_t[*derivation_data_length]); + if (!derivation_data->get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + uint8_t* buf = derivation_data->get(); + uint8_t* end = derivation_data->get() + *derivation_data_length; + buf = hidden.Serialize(buf, end); + buf = hw_enforced.Serialize(buf, end); + buf = sw_enforced.Serialize(buf, end); + + return KM_ERROR_OK; +} + +static keymaster_error_t InitializeKeyWrappingContext(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + const AuthorizationSet& hidden, + const KeymasterKeyBlob& master_key, + AeCtx* ctx) { + size_t derivation_data_length; + UniquePtr<uint8_t[]> derivation_data; + keymaster_error_t error = BuildDerivationData(hw_enforced, sw_enforced, hidden, + &derivation_data, &derivation_data_length); + if (error != KM_ERROR_OK) + return error; + + SHA256_CTX sha256_ctx; + UniquePtr<uint8_t[]> hash_buf(new (std::nothrow) uint8_t[SHA256_DIGEST_LENGTH]); + if (!hash_buf.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + Eraser hash_eraser(hash_buf.get(), SHA256_DIGEST_LENGTH); + UniquePtr<uint8_t[]> derived_key(new (std::nothrow) uint8_t[AES_BLOCK_SIZE]); + if (!derived_key.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + Eraser derived_key_eraser(derived_key.get(), AES_BLOCK_SIZE); + + if (!ctx->get() || !hash_buf.get() || !derived_key.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + // Hash derivation data. + Eraser sha256_ctx_eraser(sha256_ctx); + SHA256_Init(&sha256_ctx); + SHA256_Update(&sha256_ctx, derivation_data.get(), derivation_data_length); + SHA256_Final(hash_buf.get(), &sha256_ctx); + + // Encrypt hash with master key to build derived key. + AES_KEY aes_key; + Eraser aes_key_eraser(AES_KEY); + if (0 != + AES_set_encrypt_key(master_key.key_material, master_key.key_material_size * 8, &aes_key)) + return TranslateLastOpenSslError(); + + AES_encrypt(hash_buf.get(), derived_key.get(), &aes_key); + + // Set up AES OCB context using derived key. + if (ae_init(ctx->get(), derived_key.get(), AES_BLOCK_SIZE /* key length */, OCB_NONCE_LENGTH, + OCB_TAG_LENGTH) != AE_SUCCESS) { + memset_s(ctx->get(), 0, ae_ctx_sizeof()); + return KM_ERROR_UNKNOWN_ERROR; + } + + return KM_ERROR_OK; +} + +keymaster_error_t OcbEncryptKey(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, const AuthorizationSet& hidden, + const KeymasterKeyBlob& master_key, + const KeymasterKeyBlob& plaintext, const Buffer& nonce, + KeymasterKeyBlob* ciphertext, Buffer* tag) { + assert(ciphertext && tag); + + if (nonce.available_read() != OCB_NONCE_LENGTH) + return KM_ERROR_INVALID_ARGUMENT; + + AeCtx ctx; + if (!ctx.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + keymaster_error_t error = + InitializeKeyWrappingContext(hw_enforced, sw_enforced, hidden, master_key, &ctx); + if (error != KM_ERROR_OK) + return error; + + if (!ciphertext->Reset(plaintext.key_material_size)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + int ae_err = ae_encrypt(ctx.get(), nonce.peek_read(), plaintext.key_material, + plaintext.key_material_size, NULL /* additional data */, + 0 /* additional data length */, ciphertext->writable_data(), + tag->peek_write(), 1 /* final */); + if (ae_err < 0) { + LOG_E("Error %d while encrypting key", ae_err); + return KM_ERROR_UNKNOWN_ERROR; + } + if (!tag->advance_write(OCB_TAG_LENGTH)) + return KM_ERROR_UNKNOWN_ERROR; + assert(ae_err == static_cast<int>(plaintext.key_material_size)); + return KM_ERROR_OK; +} + +keymaster_error_t OcbDecryptKey(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, const AuthorizationSet& hidden, + const KeymasterKeyBlob& master_key, + const KeymasterKeyBlob& ciphertext, const Buffer& nonce, + const Buffer& tag, KeymasterKeyBlob* plaintext) { + assert(plaintext); + + if (nonce.available_read() != OCB_NONCE_LENGTH || tag.available_read() != OCB_TAG_LENGTH) + return KM_ERROR_INVALID_ARGUMENT; + + AeCtx ctx; + if (!ctx.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + keymaster_error_t error = + InitializeKeyWrappingContext(hw_enforced, sw_enforced, hidden, master_key, &ctx); + if (error != KM_ERROR_OK) + return error; + + if (!plaintext->Reset(ciphertext.key_material_size)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + int ae_err = ae_decrypt(ctx.get(), nonce.peek_read(), ciphertext.key_material, + ciphertext.key_material_size, NULL /* additional data */, + 0 /* additional data length */, plaintext->writable_data(), + tag.peek_read(), 1 /* final */); + if (ae_err == AE_INVALID) { + // Authentication failed! Decryption probably succeeded(ish), but we don't want to return + // any data when the authentication fails, so clear it. + plaintext->Clear(); + LOG_E("Failed to validate authentication tag during key decryption", 0); + return KM_ERROR_INVALID_KEY_BLOB; + } else if (ae_err < 0) { + LOG_E("Failed to decrypt key, error: %d", ae_err); + return KM_ERROR_UNKNOWN_ERROR; + } + assert(ae_err == static_cast<int>(ciphertext.key_material_size)); + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/ocb_utils.h b/keymaster/ocb_utils.h new file mode 100644 index 0000000..bae1e08 --- /dev/null +++ b/keymaster/ocb_utils.h
@@ -0,0 +1,48 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_OCB_UTILS_H_ +#define SYSTEM_KEYMASTER_OCB_UTILS_H_ + +#include "ae.h" + +#include <hardware/keymaster_defs.h> + +#include <keymaster/serializable.h> + +namespace keymaster { + +class AuthorizationSet; +struct KeymasterKeyBlob; + +static const int OCB_NONCE_LENGTH = 12; +static const int OCB_TAG_LENGTH = 16; + +keymaster_error_t OcbEncryptKey(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, const AuthorizationSet& hidden, + const KeymasterKeyBlob& master_key, + const KeymasterKeyBlob& plaintext, const Buffer& nonce, + KeymasterKeyBlob* ciphertext, Buffer* tag); + +keymaster_error_t OcbDecryptKey(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, const AuthorizationSet& hidden, + const KeymasterKeyBlob& master_key, + const KeymasterKeyBlob& ciphertext, const Buffer& nonce, + const Buffer& tag, KeymasterKeyBlob* plaintext); + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_OCB_UTILS_H_
diff --git a/keymaster/openssl_err.cpp b/keymaster/openssl_err.cpp new file mode 100644 index 0000000..078b8e3 --- /dev/null +++ b/keymaster/openssl_err.cpp
@@ -0,0 +1,248 @@ +/* + * Copyright (C) 2014 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 "openssl_err.h" + +#include <openssl/err.h> +#include <openssl/evp.h> + +#if defined(OPENSSL_IS_BORINGSSL) +#include <openssl/asn1.h> +#include <openssl/cipher.h> +#include <openssl/pkcs8.h> +#include <openssl/x509v3.h> +#endif + +#include <hardware/keymaster_defs.h> +#include <keymaster/logger.h> + +namespace keymaster { + +static keymaster_error_t TranslateEvpError(int reason); +#if defined(OPENSSL_IS_BORINGSSL) +static keymaster_error_t TranslateASN1Error(int reason); +static keymaster_error_t TranslateCipherError(int reason); +static keymaster_error_t TranslatePKCS8Error(int reason); +static keymaster_error_t TranslateX509v3Error(int reason); +static keymaster_error_t TranslateRsaError(int reason); +#endif + +keymaster_error_t TranslateLastOpenSslError(bool log_message) { + unsigned long error = ERR_peek_last_error(); + + if (log_message) { + LOG_D("%s", ERR_error_string(error, NULL)); + } + + int reason = ERR_GET_REASON(error); + + /* Handle global error reasons */ + switch (reason) { + case ERR_R_MALLOC_FAILURE: + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + case ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED: + case ERR_R_PASSED_NULL_PARAMETER: + case ERR_R_INTERNAL_ERROR: + case ERR_R_OVERFLOW: + return KM_ERROR_UNKNOWN_ERROR; + default: + break; + } + + switch (ERR_GET_LIB(error)) { + case ERR_LIB_USER: + return static_cast<keymaster_error_t>(reason); + case ERR_LIB_EVP: + return TranslateEvpError(reason); +#if defined(OPENSSL_IS_BORINGSSL) + case ERR_LIB_ASN1: + return TranslateASN1Error(reason); + case ERR_LIB_CIPHER: + return TranslateCipherError(reason); + case ERR_LIB_PKCS8: + return TranslatePKCS8Error(reason); + case ERR_LIB_X509V3: + return TranslateX509v3Error(reason); + case ERR_LIB_RSA: + return TranslateRsaError(reason); +#else + case ERR_LIB_ASN1: + LOG_E("ASN.1 parsing error %d", reason); + return KM_ERROR_INVALID_ARGUMENT; +#endif + } + + LOG_E("Openssl error %d, %d", ERR_GET_LIB(error), reason); + return KM_ERROR_UNKNOWN_ERROR; +} + +#if defined(OPENSSL_IS_BORINGSSL) + +keymaster_error_t TranslatePKCS8Error(int reason) { + switch (reason) { + case PKCS8_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM: + case PKCS8_R_UNKNOWN_CIPHER: + return KM_ERROR_UNSUPPORTED_ALGORITHM; + + case PKCS8_R_PRIVATE_KEY_ENCODE_ERROR: + case PKCS8_R_PRIVATE_KEY_DECODE_ERROR: + return KM_ERROR_INVALID_KEY_BLOB; + + case PKCS8_R_ENCODE_ERROR: + return KM_ERROR_INVALID_ARGUMENT; + + default: + return KM_ERROR_UNKNOWN_ERROR; + } +} + +keymaster_error_t TranslateCipherError(int reason) { + switch (reason) { + case CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: + case CIPHER_R_WRONG_FINAL_BLOCK_LENGTH: + return KM_ERROR_INVALID_INPUT_LENGTH; + + case CIPHER_R_UNSUPPORTED_KEY_SIZE: + case CIPHER_R_BAD_KEY_LENGTH: + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + + case CIPHER_R_BAD_DECRYPT: + return KM_ERROR_INVALID_ARGUMENT; + + case CIPHER_R_INVALID_KEY_LENGTH: + return KM_ERROR_INVALID_KEY_BLOB; + + default: + return KM_ERROR_UNKNOWN_ERROR; + } +} + +keymaster_error_t TranslateASN1Error(int reason) { + switch (reason) { +#if !defined(OPENSSL_IS_BORINGSSL) + case ASN1_R_UNSUPPORTED_CIPHER: + return KM_ERROR_UNSUPPORTED_ALGORITHM; + + case ASN1_R_ERROR_LOADING_SECTION: + return KM_ERROR_INVALID_KEY_BLOB; +#endif + + case ASN1_R_ENCODE_ERROR: + return KM_ERROR_INVALID_ARGUMENT; + + default: + return KM_ERROR_UNKNOWN_ERROR; + } +} + +keymaster_error_t TranslateX509v3Error(int reason) { + switch (reason) { + case X509V3_R_UNKNOWN_OPTION: + return KM_ERROR_UNSUPPORTED_ALGORITHM; + + default: + return KM_ERROR_UNKNOWN_ERROR; + } +} + +keymaster_error_t TranslateRsaError(int reason) { + switch (reason) { + case RSA_R_KEY_SIZE_TOO_SMALL: + LOG_W("RSA key is too small to use with selected padding/digest", 0); + return KM_ERROR_INCOMPATIBLE_PADDING_MODE; + case RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE: + case RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE: + return KM_ERROR_INVALID_INPUT_LENGTH; + case RSA_R_DATA_TOO_LARGE_FOR_MODULUS: + return KM_ERROR_INVALID_ARGUMENT; + default: + return KM_ERROR_UNKNOWN_ERROR; + }; +} + +#endif // OPENSSL_IS_BORINGSSL + +keymaster_error_t TranslateEvpError(int reason) { + switch (reason) { + +#if !defined(OPENSSL_IS_BORINGSSL) + case EVP_R_UNKNOWN_DIGEST: + return KM_ERROR_UNSUPPORTED_DIGEST; + + case EVP_R_UNSUPPORTED_PRF: + case EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM: + case EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION: + case EVP_R_UNSUPPORTED_SALT_TYPE: + case EVP_R_UNKNOWN_PBE_ALGORITHM: + case EVP_R_UNSUPORTED_NUMBER_OF_ROUNDS: + case EVP_R_UNSUPPORTED_CIPHER: + case EVP_R_PKCS8_UNKNOWN_BROKEN_TYPE: + case EVP_R_UNKNOWN_CIPHER: +#endif + case EVP_R_UNSUPPORTED_ALGORITHM: + case EVP_R_OPERATON_NOT_INITIALIZED: + case EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE: + return KM_ERROR_UNSUPPORTED_ALGORITHM; + +#if !defined(OPENSSL_IS_BORINGSSL) + case EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: + case EVP_R_WRONG_FINAL_BLOCK_LENGTH: + return KM_ERROR_INVALID_INPUT_LENGTH; + + case EVP_R_UNSUPPORTED_KEYLENGTH: + case EVP_R_BAD_KEY_LENGTH: + return KM_ERROR_UNSUPPORTED_KEY_SIZE; +#endif + +#if !defined(OPENSSL_IS_BORINGSSL) + case EVP_R_BAD_BLOCK_LENGTH: + case EVP_R_BN_DECODE_ERROR: + case EVP_R_BN_PUBKEY_ERROR: + case EVP_R_CIPHER_PARAMETER_ERROR: + case EVP_R_ERROR_LOADING_SECTION: + case EVP_R_EXPECTING_A_DH_KEY: + case EVP_R_EXPECTING_A_ECDSA_KEY: + case EVP_R_EXPECTING_A_EC_KEY: + case EVP_R_INVALID_DIGEST: + case EVP_R_INVALID_KEY_LENGTH: + case EVP_R_NO_DSA_PARAMETERS: + case EVP_R_PRIVATE_KEY_DECODE_ERROR: + case EVP_R_PRIVATE_KEY_ENCODE_ERROR: + case EVP_R_PUBLIC_KEY_NOT_RSA: + case EVP_R_WRONG_PUBLIC_KEY_TYPE: +#endif + case EVP_R_BUFFER_TOO_SMALL: + case EVP_R_EXPECTING_AN_RSA_KEY: + case EVP_R_EXPECTING_A_DSA_KEY: + case EVP_R_MISSING_PARAMETERS: + return KM_ERROR_INVALID_KEY_BLOB; + +#if !defined(OPENSSL_IS_BORINGSSL) + case EVP_R_BAD_DECRYPT: + case EVP_R_ENCODE_ERROR: +#endif + case EVP_R_DIFFERENT_PARAMETERS: + case EVP_R_DECODE_ERROR: + return KM_ERROR_INVALID_ARGUMENT; + + case EVP_R_DIFFERENT_KEY_TYPES: + return KM_ERROR_INCOMPATIBLE_ALGORITHM; + } + + return KM_ERROR_UNKNOWN_ERROR; +} + +} // namespace keymaster
diff --git a/keymaster/openssl_err.h b/keymaster/openssl_err.h new file mode 100644 index 0000000..8f97ef5 --- /dev/null +++ b/keymaster/openssl_err.h
@@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 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 SYSTEM_KEYMASTER_OPENSSL_ERR_H_ +#define SYSTEM_KEYMASTER_OPENSSL_ERR_H_ + +#include <hardware/keymaster_defs.h> +#include <keymaster/logger.h> + +namespace keymaster { + +/** + * Translate the last OpenSSL error to a keymaster error. Does not remove the error from the queue. + */ +keymaster_error_t TranslateLastOpenSslError(bool log_message = true); + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_OPENSSL_ERR_H_
diff --git a/keymaster/openssl_utils.cpp b/keymaster/openssl_utils.cpp new file mode 100644 index 0000000..5d2cb1c --- /dev/null +++ b/keymaster/openssl_utils.cpp
@@ -0,0 +1,142 @@ +/* + * 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 "openssl_utils.h" + +#include <keymaster/android_keymaster_utils.h> + +#include "openssl_err.h" + +namespace keymaster { + +keymaster_error_t ec_get_group_size(const EC_GROUP* group, size_t* key_size_bits) { + switch (EC_GROUP_get_curve_name(group)) { + case NID_secp224r1: + *key_size_bits = 224; + break; + case NID_X9_62_prime256v1: + *key_size_bits = 256; + break; + case NID_secp384r1: + *key_size_bits = 384; + break; + case NID_secp521r1: + *key_size_bits = 521; + break; + default: + return KM_ERROR_UNSUPPORTED_EC_FIELD; + } + return KM_ERROR_OK; +} + +EC_GROUP* ec_get_group(keymaster_ec_curve_t curve) { + switch (curve) { + case KM_EC_CURVE_P_224: + return EC_GROUP_new_by_curve_name(NID_secp224r1); + break; + case KM_EC_CURVE_P_256: + return EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + break; + case KM_EC_CURVE_P_384: + return EC_GROUP_new_by_curve_name(NID_secp384r1); + break; + case KM_EC_CURVE_P_521: + return EC_GROUP_new_by_curve_name(NID_secp521r1); + break; + default: + return nullptr; + break; + } +} + +void convert_bn_to_blob(BIGNUM* bn, keymaster_blob_t* blob) { + blob->data_length = BN_num_bytes(bn); + blob->data = new uint8_t[blob->data_length]; + BN_bn2bin(bn, const_cast<uint8_t*>(blob->data)); +} + +static int convert_to_evp(keymaster_algorithm_t algorithm) { + switch (algorithm) { + case KM_ALGORITHM_RSA: + return EVP_PKEY_RSA; + case KM_ALGORITHM_EC: + return EVP_PKEY_EC; + default: + return -1; + }; +} + +keymaster_error_t convert_pkcs8_blob_to_evp(const uint8_t* key_data, size_t key_length, + keymaster_algorithm_t expected_algorithm, + UniquePtr<EVP_PKEY, EVP_PKEY_Delete>* pkey) { + if (key_data == NULL || key_length <= 0) + return KM_ERROR_INVALID_KEY_BLOB; + + UniquePtr<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_Delete> pkcs8( + d2i_PKCS8_PRIV_KEY_INFO(NULL, &key_data, key_length)); + if (pkcs8.get() == NULL) + return TranslateLastOpenSslError(true /* log_message */); + + pkey->reset(EVP_PKCS82PKEY(pkcs8.get())); + if (!pkey->get()) + return TranslateLastOpenSslError(true /* log_message */); + + if (EVP_PKEY_type((*pkey)->type) != convert_to_evp(expected_algorithm)) { + LOG_E("EVP key algorithm was %d, not the expected %d", EVP_PKEY_type((*pkey)->type), + convert_to_evp(expected_algorithm)); + return KM_ERROR_INVALID_KEY_BLOB; + } + + return KM_ERROR_OK; +} + +keymaster_error_t KeyMaterialToEvpKey(keymaster_key_format_t key_format, + const KeymasterKeyBlob& key_material, + keymaster_algorithm_t expected_algorithm, + UniquePtr<EVP_PKEY, EVP_PKEY_Delete>* pkey) { + if (key_format != KM_KEY_FORMAT_PKCS8) + return KM_ERROR_UNSUPPORTED_KEY_FORMAT; + + return convert_pkcs8_blob_to_evp(key_material.key_material, key_material.key_material_size, + expected_algorithm, pkey); +} + +keymaster_error_t EvpKeyToKeyMaterial(const EVP_PKEY* pkey, KeymasterKeyBlob* key_blob) { + int key_data_size = i2d_PrivateKey(pkey, NULL /* key_data*/); + if (key_data_size <= 0) + return TranslateLastOpenSslError(); + + if (!key_blob->Reset(key_data_size)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + uint8_t* tmp = key_blob->writable_data(); + i2d_PrivateKey(pkey, &tmp); + + return KM_ERROR_OK; +} + +size_t ec_group_size_bits(EC_KEY* ec_key) { + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + UniquePtr<BN_CTX, BN_CTX_Delete> bn_ctx(BN_CTX_new()); + UniquePtr<BIGNUM, BIGNUM_Delete> order(BN_new()); + if (!EC_GROUP_get_order(group, order.get(), bn_ctx.get())) { + LOG_E("Failed to get EC group order", 0); + return 0; + } + return BN_num_bits(order.get()); +} + +} // namespace keymaster
diff --git a/keymaster/openssl_utils.h b/keymaster/openssl_utils.h new file mode 100644 index 0000000..034b129 --- /dev/null +++ b/keymaster/openssl_utils.h
@@ -0,0 +1,99 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_OPENSSL_UTILS_H_ +#define SYSTEM_KEYMASTER_OPENSSL_UTILS_H_ + +#include <openssl/bn.h> +#include <openssl/ec.h> +#include <openssl/engine.h> +#include <openssl/evp.h> +#include <openssl/rsa.h> +#include <openssl/x509.h> + +#include <UniquePtr.h> + +#include <hardware/keymaster_defs.h> + +namespace keymaster { + +struct KeymasterKeyBlob; + +class EvpMdCtxCleaner { + public: + explicit EvpMdCtxCleaner(EVP_MD_CTX* ctx) : ctx_(ctx) {} + ~EvpMdCtxCleaner() { EVP_MD_CTX_cleanup(ctx_); } + + private: + EVP_MD_CTX* ctx_; +}; + +template <typename T, void (*FreeFunc)(T*)> struct OpenSslObjectDeleter { + void operator()(T* p) { FreeFunc(p); } +}; + +#define DEFINE_OPENSSL_OBJECT_POINTER(name) \ + typedef OpenSslObjectDeleter<name, name##_free> name##_Delete; \ + typedef UniquePtr<name, name##_Delete> name##_Ptr; + +DEFINE_OPENSSL_OBJECT_POINTER(ASN1_INTEGER) +DEFINE_OPENSSL_OBJECT_POINTER(ASN1_OBJECT) +DEFINE_OPENSSL_OBJECT_POINTER(ASN1_OCTET_STRING) +DEFINE_OPENSSL_OBJECT_POINTER(ASN1_TIME) +DEFINE_OPENSSL_OBJECT_POINTER(BN_CTX); +DEFINE_OPENSSL_OBJECT_POINTER(EC_GROUP); +DEFINE_OPENSSL_OBJECT_POINTER(EC_KEY); +DEFINE_OPENSSL_OBJECT_POINTER(EC_POINT); +DEFINE_OPENSSL_OBJECT_POINTER(ENGINE); +DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY); +DEFINE_OPENSSL_OBJECT_POINTER(PKCS8_PRIV_KEY_INFO); +DEFINE_OPENSSL_OBJECT_POINTER(RSA); +DEFINE_OPENSSL_OBJECT_POINTER(X509) +DEFINE_OPENSSL_OBJECT_POINTER(X509_EXTENSION) +DEFINE_OPENSSL_OBJECT_POINTER(X509_NAME) + +typedef OpenSslObjectDeleter<BIGNUM, BN_free> BIGNUM_Delete; +typedef UniquePtr<BIGNUM, BIGNUM_Delete> BIGNUM_Ptr; + +keymaster_error_t ec_get_group_size(const EC_GROUP* group, size_t* key_size_bits); +EC_GROUP* ec_get_group(keymaster_ec_curve_t curve); + +/** + * Many OpenSSL APIs take ownership of an argument on success but don't free the argument on + * failure. This means we need to tell our scoped pointers when we've transferred ownership, without + * triggering a warning by not using the result of release(). + */ +template <typename T, typename Delete_T> +inline void release_because_ownership_transferred(UniquePtr<T, Delete_T>& p) { + T* val __attribute__((unused)) = p.release(); +} + +keymaster_error_t convert_pkcs8_blob_to_evp(const uint8_t* key_data, size_t key_length, + keymaster_algorithm_t expected_algorithm, + UniquePtr<EVP_PKEY, EVP_PKEY_Delete>* pkey); + +keymaster_error_t KeyMaterialToEvpKey(keymaster_key_format_t key_format, + const KeymasterKeyBlob& key_material, + keymaster_algorithm_t expected_algorithm, + UniquePtr<EVP_PKEY, EVP_PKEY_Delete>* evp_pkey); + +keymaster_error_t EvpKeyToKeyMaterial(const EVP_PKEY* evp_pkey, KeymasterKeyBlob* key_blob); + +size_t ec_group_size_bits(EC_KEY* ec_key); + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_OPENSSL_UTILS_H_
diff --git a/keymaster/operation.cpp b/keymaster/operation.cpp new file mode 100644 index 0000000..410c9aa --- /dev/null +++ b/keymaster/operation.cpp
@@ -0,0 +1,155 @@ +/* + * Copyright 2014 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 "operation.h" + +#include <keymaster/authorization_set.h> + +#include "key.h" + +namespace keymaster { + +bool OperationFactory::supported(keymaster_padding_t padding) const { + size_t padding_count; + const keymaster_padding_t* supported_paddings = SupportedPaddingModes(&padding_count); + for (size_t i = 0; i < padding_count; ++i) + if (padding == supported_paddings[i]) + return true; + return false; +} + +bool OperationFactory::supported(keymaster_block_mode_t block_mode) const { + size_t block_mode_count; + const keymaster_block_mode_t* supported_block_modes = SupportedBlockModes(&block_mode_count); + for (size_t i = 0; i < block_mode_count; ++i) + if (block_mode == supported_block_modes[i]) + return true; + return false; +} + +bool OperationFactory::supported(keymaster_digest_t digest) const { + size_t digest_count; + const keymaster_digest_t* supported_digests = SupportedDigests(&digest_count); + for (size_t i = 0; i < digest_count; ++i) + if (digest == supported_digests[i]) + return true; + return false; +} + +inline bool is_public_key_algorithm(keymaster_algorithm_t algorithm) { + switch (algorithm) { + case KM_ALGORITHM_HMAC: + case KM_ALGORITHM_AES: + return false; + case KM_ALGORITHM_RSA: + case KM_ALGORITHM_EC: + return true; + } + + // Unreachable. + assert(false); + return false; +} + +bool OperationFactory::is_public_key_operation() const { + KeyType key_type = registry_key(); + + if (!is_public_key_algorithm(key_type.algorithm)) + return false; + + switch (key_type.purpose) { + case KM_PURPOSE_VERIFY: + case KM_PURPOSE_ENCRYPT: + return true; + case KM_PURPOSE_SIGN: + case KM_PURPOSE_DECRYPT: + case KM_PURPOSE_DERIVE_KEY: + return false; + }; + + // Unreachable. + assert(false); + return false; +} + +bool OperationFactory::GetAndValidatePadding(const AuthorizationSet& begin_params, const Key& key, + keymaster_padding_t* padding, + keymaster_error_t* error) const { + *error = KM_ERROR_UNSUPPORTED_PADDING_MODE; + if (!begin_params.GetTagValue(TAG_PADDING, padding)) { + LOG_E("%d padding modes specified in begin params", begin_params.GetTagCount(TAG_PADDING)); + return false; + } else if (!supported(*padding)) { + LOG_E("Padding mode %d not supported", *padding); + return false; + } else if ( + // If it's a public key operation, all padding modes are authorized. + !is_public_key_operation() && + // Otherwise the key needs to authorize the specific mode. + !key.authorizations().Contains(TAG_PADDING, *padding) && + !key.authorizations().Contains(TAG_PADDING_OLD, *padding)) { + LOG_E("Padding mode %d was specified, but not authorized by key", *padding); + *error = KM_ERROR_INCOMPATIBLE_PADDING_MODE; + return false; + } + + *error = KM_ERROR_OK; + return true; +} + +bool OperationFactory::GetAndValidateDigest(const AuthorizationSet& begin_params, const Key& key, + keymaster_digest_t* digest, + keymaster_error_t* error) const { + *error = KM_ERROR_UNSUPPORTED_DIGEST; + if (!begin_params.GetTagValue(TAG_DIGEST, digest)) { + LOG_E("%d digests specified in begin params", begin_params.GetTagCount(TAG_DIGEST)); + return false; + } else if (!supported(*digest)) { + LOG_E("Digest %d not supported", *digest); + return false; + } else if ( + // If it's a public key operation, all digests are authorized. + !is_public_key_operation() && + // Otherwise the key needs to authorize the specific digest. + !key.authorizations().Contains(TAG_DIGEST, *digest) && + !key.authorizations().Contains(TAG_DIGEST_OLD, *digest)) { + LOG_E("Digest %d was specified, but not authorized by key", *digest); + *error = KM_ERROR_INCOMPATIBLE_DIGEST; + return false; + } + *error = KM_ERROR_OK; + return true; +} + +keymaster_error_t Operation::UpdateForFinish(const AuthorizationSet& input_params, + const Buffer& input) { + if (!input_params.empty() || input.available_read()) { + size_t input_consumed; + Buffer output; + AuthorizationSet output_params; + keymaster_error_t error = + Update(input_params, input, &output_params, &output, &input_consumed); + if (error != KM_ERROR_OK) + return error; + assert(input_consumed == input.available_read()); + assert(output_params.empty()); + assert(output.available_read() == 0); + } + + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/operation.h b/keymaster/operation.h new file mode 100644 index 0000000..aadc406 --- /dev/null +++ b/keymaster/operation.h
@@ -0,0 +1,126 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_OPERATION_H_ +#define SYSTEM_KEYMASTER_OPERATION_H_ + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> + +#include <hardware/keymaster_defs.h> +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/authorization_set.h> +#include <keymaster/logger.h> + +namespace keymaster { + +class AuthorizationSet; +class Key; +class Operation; + +class OperationFactory { + public: + virtual ~OperationFactory() {} + + // Required for registry + struct KeyType { + KeyType(keymaster_algorithm_t alg, keymaster_purpose_t purp) + : algorithm(alg), purpose(purp) {} + + keymaster_algorithm_t algorithm; + keymaster_purpose_t purpose; + + bool operator==(const KeyType& rhs) const { + return algorithm == rhs.algorithm && purpose == rhs.purpose; + } + }; + virtual KeyType registry_key() const = 0; + + // Factory methods + virtual Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params, + keymaster_error_t* error) = 0; + + // Informational methods. The returned arrays reference static memory and must not be + // deallocated or modified. + virtual const keymaster_padding_t* SupportedPaddingModes(size_t* padding_count) const { + *padding_count = 0; + return NULL; + } + virtual const keymaster_block_mode_t* SupportedBlockModes(size_t* block_mode_count) const { + *block_mode_count = 0; + return NULL; + } + virtual const keymaster_digest_t* SupportedDigests(size_t* digest_count) const { + *digest_count = 0; + return NULL; + } + + // Convenience methods + bool supported(keymaster_padding_t padding) const; + bool supported(keymaster_block_mode_t padding) const; + bool supported(keymaster_digest_t padding) const; + + bool is_public_key_operation() const; + + bool GetAndValidatePadding(const AuthorizationSet& begin_params, const Key& key, + keymaster_padding_t* padding, keymaster_error_t* error) const; + bool GetAndValidateDigest(const AuthorizationSet& begin_params, const Key& key, + keymaster_digest_t* digest, keymaster_error_t* error) const; +}; + +/** + * Abstract base for all cryptographic operations. + */ +class Operation { + public: + explicit Operation(keymaster_purpose_t purpose) : purpose_(purpose) {} + virtual ~Operation() {} + + keymaster_purpose_t purpose() const { return purpose_; } + + void set_key_id(uint64_t key_id) { key_id_ = key_id; } + uint64_t key_id() const { return key_id_; } + + void SetAuthorizations(const AuthorizationSet& auths) { + key_auths_.Reinitialize(auths.data(), auths.size()); + } + const AuthorizationSet authorizations() { return key_auths_; } + + virtual keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) = 0; + virtual keymaster_error_t Update(const AuthorizationSet& input_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, + size_t* input_consumed) = 0; + virtual keymaster_error_t Finish(const AuthorizationSet& input_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) = 0; + virtual keymaster_error_t Abort() = 0; + +protected: + // Helper function for implementing Finish() methods that need to call Update() to process + // input, but don't expect any output. + keymaster_error_t UpdateForFinish(const AuthorizationSet& input_params, const Buffer& input); + + private: + const keymaster_purpose_t purpose_; + AuthorizationSet key_auths_; + uint64_t key_id_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_OPERATION_H_
diff --git a/keymaster/operation_table.cpp b/keymaster/operation_table.cpp new file mode 100644 index 0000000..d30885e --- /dev/null +++ b/keymaster/operation_table.cpp
@@ -0,0 +1,90 @@ +/* + * 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 "operation_table.h" + +#include <new> + +#include <openssl/rand.h> + +#include "openssl_err.h" +#include "operation.h" + +namespace keymaster { + +OperationTable::Entry::~Entry() { + delete operation; + operation = NULL; + handle = 0; +} + +keymaster_error_t OperationTable::Add(Operation* operation, + keymaster_operation_handle_t* op_handle) { + if (!table_.get()) { + table_.reset(new (std::nothrow) Entry[table_size_]); + if (!table_.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + + UniquePtr<Operation> op(operation); + if (RAND_bytes(reinterpret_cast<uint8_t*>(op_handle), sizeof(*op_handle)) != 1) + return TranslateLastOpenSslError(); + if (*op_handle == 0) { + // Statistically this is vanishingly unlikely, which means if it ever happens in practice, + // it indicates a broken RNG. + return KM_ERROR_UNKNOWN_ERROR; + } + + for (size_t i = 0; i < table_size_; ++i) { + if (table_[i].operation == NULL) { + table_[i].operation = op.release(); + table_[i].handle = *op_handle; + return KM_ERROR_OK; + } + } + return KM_ERROR_TOO_MANY_OPERATIONS; +} + +Operation* OperationTable::Find(keymaster_operation_handle_t op_handle) { + if (op_handle == 0) + return NULL; + + if (!table_.get()) + return NULL; + + for (size_t i = 0; i < table_size_; ++i) { + if (table_[i].handle == op_handle) + return table_[i].operation; + } + return NULL; +} + +bool OperationTable::Delete(keymaster_operation_handle_t op_handle) { + if (!table_.get()) + return false; + + for (size_t i = 0; i < table_size_; ++i) { + if (table_[i].handle == op_handle) { + delete table_[i].operation; + table_[i].operation = NULL; + table_[i].handle = 0; + return true; + } + } + return false; +} + +} // namespace keymaster
diff --git a/keymaster/operation_table.h b/keymaster/operation_table.h new file mode 100644 index 0000000..c840ad9 --- /dev/null +++ b/keymaster/operation_table.h
@@ -0,0 +1,53 @@ +/* + * 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 SYSTEM_KEYMASTER_OPERATION_TABLE_H +#define SYSTEM_KEYMASTER_OPERATION_TABLE_H + +#include <UniquePtr.h> + +#include <hardware/keymaster_defs.h> + +namespace keymaster { + +class Operation; + +class OperationTable { + public: + explicit OperationTable(size_t table_size) : table_size_(table_size) {} + + struct Entry { + Entry() { + handle = 0; + operation = NULL; + }; + ~Entry(); + keymaster_operation_handle_t handle; + Operation* operation; + }; + + keymaster_error_t Add(Operation* operation, keymaster_operation_handle_t* op_handle); + Operation* Find(keymaster_operation_handle_t op_handle); + bool Delete(keymaster_operation_handle_t); + + private: + UniquePtr<Entry[]> table_; + size_t table_size_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_OPERATION_TABLE_H
diff --git a/keymaster/rsa_key.cpp b/keymaster/rsa_key.cpp new file mode 100644 index 0000000..4231953 --- /dev/null +++ b/keymaster/rsa_key.cpp
@@ -0,0 +1,70 @@ +/* + * Copyright 2014 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 "rsa_key.h" + +#include <keymaster/keymaster_context.h> + +#include "openssl_err.h" +#include "openssl_utils.h" +#include "rsa_operation.h" + +namespace keymaster { + +bool RsaKey::EvpToInternal(const EVP_PKEY* pkey) { + rsa_key_.reset(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(pkey))); + return rsa_key_.get() != NULL; +} + +bool RsaKey::InternalToEvp(EVP_PKEY* pkey) const { + return EVP_PKEY_set1_RSA(pkey, rsa_key_.get()) == 1; +} + +bool RsaKey::SupportedMode(keymaster_purpose_t purpose, keymaster_padding_t padding) { + switch (purpose) { + case KM_PURPOSE_SIGN: + case KM_PURPOSE_VERIFY: + return padding == KM_PAD_NONE || padding == KM_PAD_RSA_PSS || + padding == KM_PAD_RSA_PKCS1_1_5_SIGN; + + case KM_PURPOSE_ENCRYPT: + case KM_PURPOSE_DECRYPT: + return padding == KM_PAD_RSA_OAEP || padding == KM_PAD_RSA_PKCS1_1_5_ENCRYPT; + + case KM_PURPOSE_DERIVE_KEY: + return false; + }; + return false; +} + +bool RsaKey::SupportedMode(keymaster_purpose_t purpose, keymaster_digest_t digest) { + switch (purpose) { + case KM_PURPOSE_SIGN: + case KM_PURPOSE_VERIFY: + return digest == KM_DIGEST_NONE || digest == KM_DIGEST_SHA_2_256; + + case KM_PURPOSE_ENCRYPT: + case KM_PURPOSE_DECRYPT: + /* Don't care */ + break; + + case KM_PURPOSE_DERIVE_KEY: + return false; + }; + return true; +} + +} // namespace keymaster
diff --git a/keymaster/rsa_key.h b/keymaster/rsa_key.h new file mode 100644 index 0000000..502f28a --- /dev/null +++ b/keymaster/rsa_key.h
@@ -0,0 +1,55 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_RSA_KEY_H_ +#define SYSTEM_KEYMASTER_RSA_KEY_H_ + +#include <openssl/rsa.h> + +#include "asymmetric_key.h" + +namespace keymaster { + +class RsaKey : public AsymmetricKey { + public: + RsaKey(const AuthorizationSet& hw_enforced, const AuthorizationSet& sw_enforced, + keymaster_error_t* error) + : AsymmetricKey(hw_enforced, sw_enforced, error) {} + + bool InternalToEvp(EVP_PKEY* pkey) const override; + bool EvpToInternal(const EVP_PKEY* pkey) override; + + bool SupportedMode(keymaster_purpose_t purpose, keymaster_padding_t padding); + bool SupportedMode(keymaster_purpose_t purpose, keymaster_digest_t digest); + + struct RSA_Delete { + void operator()(RSA* p) { RSA_free(p); } + }; + + RSA* key() const { return rsa_key_.get(); } + + protected: + RsaKey(RSA* rsa, const AuthorizationSet& hw_enforced, const AuthorizationSet& sw_enforced, + keymaster_error_t* error) + : AsymmetricKey(hw_enforced, sw_enforced, error), rsa_key_(rsa) {} + + private: + UniquePtr<RSA, RSA_Delete> rsa_key_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_RSA_KEY_H_
diff --git a/keymaster/rsa_key_factory.cpp b/keymaster/rsa_key_factory.cpp new file mode 100644 index 0000000..e6c3f8c --- /dev/null +++ b/keymaster/rsa_key_factory.cpp
@@ -0,0 +1,180 @@ +/* + * 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 <keymaster/rsa_key_factory.h> + +#include <new> + +#include <keymaster/keymaster_context.h> + +#include "openssl_err.h" +#include "openssl_utils.h" +#include "rsa_key.h" +#include "rsa_operation.h" + +namespace keymaster { + +const int kMaximumRsaKeySize = 16 * 1024; // 16kbits should be enough for anyone. + +static RsaSigningOperationFactory sign_factory; +static RsaVerificationOperationFactory verify_factory; +static RsaEncryptionOperationFactory encrypt_factory; +static RsaDecryptionOperationFactory decrypt_factory; + +OperationFactory* RsaKeyFactory::GetOperationFactory(keymaster_purpose_t purpose) const { + switch (purpose) { + case KM_PURPOSE_SIGN: + return &sign_factory; + case KM_PURPOSE_VERIFY: + return &verify_factory; + case KM_PURPOSE_ENCRYPT: + return &encrypt_factory; + case KM_PURPOSE_DECRYPT: + return &decrypt_factory; + default: + return nullptr; + } +} + +keymaster_error_t RsaKeyFactory::GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + if (!key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const AuthorizationSet& authorizations(key_description); + + uint64_t public_exponent; + if (!authorizations.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &public_exponent)) { + LOG_E("%s", "No public exponent specified for RSA key generation"); + return KM_ERROR_INVALID_ARGUMENT; + } + + uint32_t key_size; + if (!authorizations.GetTagValue(TAG_KEY_SIZE, &key_size)) { + LOG_E("No key size specified for RSA key generation", 0); + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + if (key_size % 8 != 0 || key_size > kMaximumRsaKeySize) { + LOG_E("Invalid key size of %u bits specified for RSA key generation", key_size); + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + + UniquePtr<BIGNUM, BIGNUM_Delete> exponent(BN_new()); + UniquePtr<RSA, RsaKey::RSA_Delete> rsa_key(RSA_new()); + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new()); + if (exponent.get() == NULL || rsa_key.get() == NULL || pkey.get() == NULL) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (!BN_set_word(exponent.get(), public_exponent) || + !RSA_generate_key_ex(rsa_key.get(), key_size, exponent.get(), NULL /* callback */)) + return TranslateLastOpenSslError(); + + if (EVP_PKEY_set1_RSA(pkey.get(), rsa_key.get()) != 1) + return TranslateLastOpenSslError(); + + KeymasterKeyBlob key_material; + keymaster_error_t error = EvpKeyToKeyMaterial(pkey.get(), &key_material); + if (error != KM_ERROR_OK) + return error; + + return context_->CreateKeyBlob(authorizations, KM_ORIGIN_GENERATED, key_material, key_blob, + hw_enforced, sw_enforced); +} + +keymaster_error_t RsaKeyFactory::ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + if (!output_key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + AuthorizationSet authorizations; + uint64_t public_exponent; + uint32_t key_size; + keymaster_error_t error = + UpdateImportKeyDescription(key_description, input_key_material_format, input_key_material, + &authorizations, &public_exponent, &key_size); + if (error != KM_ERROR_OK) + return error; + return context_->CreateKeyBlob(authorizations, KM_ORIGIN_IMPORTED, input_key_material, + output_key_blob, hw_enforced, sw_enforced); +} + +keymaster_error_t RsaKeyFactory::UpdateImportKeyDescription(const AuthorizationSet& key_description, + keymaster_key_format_t key_format, + const KeymasterKeyBlob& key_material, + AuthorizationSet* updated_description, + uint64_t* public_exponent, + uint32_t* key_size) const { + if (!updated_description || !public_exponent || !key_size) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey; + keymaster_error_t error = + KeyMaterialToEvpKey(key_format, key_material, keymaster_key_type(), &pkey); + if (error != KM_ERROR_OK) + return error; + + UniquePtr<RSA, RsaKey::RSA_Delete> rsa_key(EVP_PKEY_get1_RSA(pkey.get())); + if (!rsa_key.get()) + return TranslateLastOpenSslError(); + + updated_description->Reinitialize(key_description); + + *public_exponent = BN_get_word(rsa_key->e); + if (*public_exponent == 0xffffffffL) + return KM_ERROR_INVALID_KEY_BLOB; + if (!updated_description->GetTagValue(TAG_RSA_PUBLIC_EXPONENT, public_exponent)) + updated_description->push_back(TAG_RSA_PUBLIC_EXPONENT, *public_exponent); + if (*public_exponent != BN_get_word(rsa_key->e)) { + LOG_E("Imported public exponent (%u) does not match specified public exponent (%u)", + *public_exponent, BN_get_word(rsa_key->e)); + return KM_ERROR_IMPORT_PARAMETER_MISMATCH; + } + + *key_size = RSA_size(rsa_key.get()) * 8; + if (!updated_description->GetTagValue(TAG_KEY_SIZE, key_size)) + updated_description->push_back(TAG_KEY_SIZE, *key_size); + if (RSA_size(rsa_key.get()) * 8 != *key_size) { + LOG_E("Imported key size (%u bits) does not match specified key size (%u bits)", + RSA_size(rsa_key.get()) * 8, *key_size); + return KM_ERROR_IMPORT_PARAMETER_MISMATCH; + } + + keymaster_algorithm_t algorithm = KM_ALGORITHM_RSA; + if (!updated_description->GetTagValue(TAG_ALGORITHM, &algorithm)) + updated_description->push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA); + if (algorithm != KM_ALGORITHM_RSA) + return KM_ERROR_IMPORT_PARAMETER_MISMATCH; + + return KM_ERROR_OK; +} + +keymaster_error_t RsaKeyFactory::CreateEmptyKey(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<AsymmetricKey>* key) const { + keymaster_error_t error; + key->reset(new (std::nothrow) RsaKey(hw_enforced, sw_enforced, &error)); + if (!key->get()) + error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return error; +} + +} // namespace keymaster
diff --git a/keymaster/rsa_keymaster0_key.cpp b/keymaster/rsa_keymaster0_key.cpp new file mode 100644 index 0000000..6e5c083 --- /dev/null +++ b/keymaster/rsa_keymaster0_key.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 "rsa_keymaster0_key.h" + +#include <memory> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/logger.h> +#include <keymaster/soft_keymaster_context.h> + +#include "keymaster0_engine.h" +#include "openssl_utils.h" + +using std::unique_ptr; + +namespace keymaster { + +RsaKeymaster0KeyFactory::RsaKeymaster0KeyFactory(const SoftKeymasterContext* context, + const Keymaster0Engine* engine) + : RsaKeyFactory(context), engine_(engine) {} + +keymaster_error_t RsaKeymaster0KeyFactory::GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + if (!key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + uint64_t public_exponent; + if (!key_description.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &public_exponent)) { + LOG_E("%s", "No public exponent specified for RSA key generation"); + return KM_ERROR_INVALID_ARGUMENT; + } + + uint32_t key_size; + if (!key_description.GetTagValue(TAG_KEY_SIZE, &key_size)) { + LOG_E("%s", "No key size specified for RSA key generation"); + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + } + + KeymasterKeyBlob key_material; + if (!engine_->GenerateRsaKey(public_exponent, key_size, &key_material)) + return KM_ERROR_UNKNOWN_ERROR; + + // These tags are hardware-enforced. Putting them in the hw_enforced set here will ensure that + // context_->CreateKeyBlob doesn't put them in sw_enforced. + hw_enforced->push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA); + hw_enforced->push_back(TAG_RSA_PUBLIC_EXPONENT, public_exponent); + hw_enforced->push_back(TAG_KEY_SIZE, key_size); + hw_enforced->push_back(TAG_ORIGIN, KM_ORIGIN_UNKNOWN); + + return context_->CreateKeyBlob(key_description, KM_ORIGIN_UNKNOWN, key_material, key_blob, + hw_enforced, sw_enforced); +} + +keymaster_error_t RsaKeymaster0KeyFactory::ImportKey( + const AuthorizationSet& key_description, keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, KeymasterKeyBlob* output_key_blob, + AuthorizationSet* hw_enforced, AuthorizationSet* sw_enforced) const { + if (!output_key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + AuthorizationSet authorizations; + uint64_t public_exponent; + uint32_t key_size; + keymaster_error_t error = + UpdateImportKeyDescription(key_description, input_key_material_format, input_key_material, + &authorizations, &public_exponent, &key_size); + if (error != KM_ERROR_OK) + return error; + + KeymasterKeyBlob imported_hw_key; + if (!engine_->ImportKey(input_key_material_format, input_key_material, &imported_hw_key)) + return KM_ERROR_UNKNOWN_ERROR; + + // These tags are hardware-enforced. Putting them in the hw_enforced set here will ensure that + // context_->CreateKeyBlob doesn't put them in sw_enforced. + hw_enforced->push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA); + hw_enforced->push_back(TAG_RSA_PUBLIC_EXPONENT, public_exponent); + hw_enforced->push_back(TAG_KEY_SIZE, key_size); + hw_enforced->push_back(TAG_ORIGIN, KM_ORIGIN_UNKNOWN); + + return context_->CreateKeyBlob(authorizations, KM_ORIGIN_UNKNOWN, imported_hw_key, + output_key_blob, hw_enforced, sw_enforced); +} + +keymaster_error_t RsaKeymaster0KeyFactory::LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const { + if (!key) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + if (sw_enforced.GetTagCount(TAG_ALGORITHM) == 1) + return super::LoadKey(key_material, additional_params, hw_enforced, sw_enforced, key); + + unique_ptr<RSA, RSA_Delete> rsa(engine_->BlobToRsaKey(key_material)); + if (!rsa) + return KM_ERROR_UNKNOWN_ERROR; + + keymaster_error_t error; + key->reset(new (std::nothrow) + RsaKeymaster0Key(rsa.release(), hw_enforced, sw_enforced, &error)); + if (!key->get()) + error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (error != KM_ERROR_OK) + return error; + + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/rsa_keymaster0_key.h b/keymaster/rsa_keymaster0_key.h new file mode 100644 index 0000000..261448f --- /dev/null +++ b/keymaster/rsa_keymaster0_key.h
@@ -0,0 +1,70 @@ +/* + * 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 SYSTEM_KEYMASTER_RSA_KEYMASTER0_KEY_H_ +#define SYSTEM_KEYMASTER_RSA_KEYMASTER0_KEY_H_ + +#include <openssl/rsa.h> + +#include <keymaster/rsa_key_factory.h> + +#include "rsa_key.h" + +namespace keymaster { + +class Keymaster0Engine; +class SoftKeymasterContext; + +/** + * An RsaKeyFactory which can delegate key generation, importing and loading operations to a + * keymaster0-backed OpenSSL engine. + */ +class RsaKeymaster0KeyFactory : public RsaKeyFactory { + typedef RsaKeyFactory super; + + public: + RsaKeymaster0KeyFactory(const SoftKeymasterContext* context, const Keymaster0Engine* engine); + + keymaster_error_t GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const override; + + private: + const Keymaster0Engine* engine_; +}; + +class RsaKeymaster0Key : public RsaKey { + public: + RsaKeymaster0Key(RSA* rsa_key, const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, keymaster_error_t* error) + : RsaKey(rsa_key, hw_enforced, sw_enforced, error) {} +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_RSA_KEYMASTER0_KEY_H_
diff --git a/keymaster/rsa_keymaster1_key.cpp b/keymaster/rsa_keymaster1_key.cpp new file mode 100644 index 0000000..7d8a33f --- /dev/null +++ b/keymaster/rsa_keymaster1_key.cpp
@@ -0,0 +1,144 @@ +/* + * 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 "rsa_keymaster1_key.h" + +#include <memory> + +#include <keymaster/logger.h> +#include <keymaster/soft_keymaster_context.h> + +#include "rsa_keymaster1_operation.h" + +using std::unique_ptr; + +namespace keymaster { + +RsaKeymaster1KeyFactory::RsaKeymaster1KeyFactory(const SoftKeymasterContext* context, + const Keymaster1Engine* engine) + : RsaKeyFactory(context), engine_(engine), + sign_factory_(new RsaKeymaster1OperationFactory(KM_PURPOSE_SIGN, engine)), + decrypt_factory_(new RsaKeymaster1OperationFactory(KM_PURPOSE_DECRYPT, engine)), + // For pubkey ops we can use the normal operation factories. + verify_factory_(new RsaVerificationOperationFactory), + encrypt_factory_(new RsaEncryptionOperationFactory) {} + +static bool is_supported(uint32_t digest) { + return digest == KM_DIGEST_NONE || digest == KM_DIGEST_SHA_2_256; +} + +static void UpdateToWorkAroundUnsupportedDigests(const AuthorizationSet& key_description, + AuthorizationSet* new_description) { + bool have_unsupported_digests = false; + bool have_digest_none = false; + bool have_pad_none = false; + bool have_padding_requiring_digest = false; + for (const keymaster_key_param_t& entry : key_description) { + new_description->push_back(entry); + + if (entry.tag == TAG_DIGEST) { + if (entry.enumerated == KM_DIGEST_NONE) { + have_digest_none = true; + } else if (!is_supported(entry.enumerated)) { + LOG_D("Found request for unsupported digest %u", entry.enumerated); + have_unsupported_digests = true; + } + } + + if (entry.tag == TAG_PADDING) { + switch (entry.enumerated) { + case KM_PAD_RSA_PSS: + case KM_PAD_RSA_OAEP: + have_padding_requiring_digest = true; + break; + case KM_PAD_NONE: + have_pad_none = true; + break; + } + } + } + + if (have_unsupported_digests && !have_digest_none) { + LOG_I("Adding KM_DIGEST_NONE to key authorization, to enable software digesting", 0); + new_description->push_back(TAG_DIGEST, KM_DIGEST_NONE); + } + + if (have_unsupported_digests && have_padding_requiring_digest && !have_pad_none) { + LOG_I("Adding KM_PAD_NONE to key authorization, to enable PSS or OAEP software padding", 0); + new_description->push_back(TAG_PADDING, KM_PAD_NONE); + } +} + +keymaster_error_t RsaKeymaster1KeyFactory::GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + AuthorizationSet key_params_copy; + UpdateToWorkAroundUnsupportedDigests(key_description, &key_params_copy); + return engine_->GenerateKey(key_params_copy, key_blob, hw_enforced, sw_enforced); +} + +keymaster_error_t RsaKeymaster1KeyFactory::ImportKey( + const AuthorizationSet& key_description, keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, KeymasterKeyBlob* output_key_blob, + AuthorizationSet* hw_enforced, AuthorizationSet* sw_enforced) const { + AuthorizationSet key_params_copy; + UpdateToWorkAroundUnsupportedDigests(key_description, &key_params_copy); + return engine_->ImportKey(key_params_copy, input_key_material_format, input_key_material, + output_key_blob, hw_enforced, sw_enforced); +} + +keymaster_error_t RsaKeymaster1KeyFactory::LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const { + if (!key) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + keymaster_error_t error; + unique_ptr<RSA, RSA_Delete> rsa(engine_->BuildRsaKey(key_material, additional_params, &error)); + if (!rsa) + return error; + + key->reset(new (std::nothrow) + RsaKeymaster1Key(rsa.release(), hw_enforced, sw_enforced, &error)); + if (!key->get()) + error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (error != KM_ERROR_OK) + return error; + + return KM_ERROR_OK; +} + +OperationFactory* RsaKeymaster1KeyFactory::GetOperationFactory(keymaster_purpose_t purpose) const { + switch (purpose) { + case KM_PURPOSE_SIGN: + return sign_factory_.get(); + case KM_PURPOSE_VERIFY: + return verify_factory_.get(); + case KM_PURPOSE_ENCRYPT: + return encrypt_factory_.get(); + case KM_PURPOSE_DECRYPT: + return decrypt_factory_.get(); + case KM_PURPOSE_DERIVE_KEY: + break; + } + return nullptr; +} + +} // namespace keymaster
diff --git a/keymaster/rsa_keymaster1_key.h b/keymaster/rsa_keymaster1_key.h new file mode 100644 index 0000000..dd543dd --- /dev/null +++ b/keymaster/rsa_keymaster1_key.h
@@ -0,0 +1,81 @@ +/* + * 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 SYSTEM_KEYMASTER_RSA_KEYMASTER1_KEY_H_ +#define SYSTEM_KEYMASTER_RSA_KEYMASTER1_KEY_H_ + +#include <openssl/rsa.h> + +#include <keymaster/rsa_key_factory.h> + +#include "keymaster1_engine.h" +#include "rsa_key.h" + +namespace keymaster { + +class SoftKeymasterContext; + +/** + * RsaKeymaster1KeyFactory is a KeyFactory that creates and loads keys which are actually backed by + * a hardware keymaster1 module, but which does not support all keymaster1 digests. If unsupported + * digests are found during generation or import, KM_DIGEST_NONE is added to the key description, + * then the operations handle the unsupported digests in software. + * + * If unsupported digests are requested and KM_PAD_RSA_PSS or KM_PAD_RSA_OAEP is also requested, but + * KM_PAD_NONE is not present KM_PAD_NONE will be added to the description, to allow for + * software padding as well as software digesting. + */ +class RsaKeymaster1KeyFactory : public RsaKeyFactory { + public: + RsaKeymaster1KeyFactory(const SoftKeymasterContext* context, const Keymaster1Engine* engine); + + keymaster_error_t GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + keymaster_error_t LoadKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& additional_params, + const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, + UniquePtr<Key>* key) const override; + + OperationFactory* GetOperationFactory(keymaster_purpose_t purpose) const override; + + private: + const Keymaster1Engine* engine_; + + std::unique_ptr<OperationFactory> sign_factory_; + std::unique_ptr<OperationFactory> decrypt_factory_; + std::unique_ptr<OperationFactory> verify_factory_; + std::unique_ptr<OperationFactory> encrypt_factory_; +}; + +class RsaKeymaster1Key : public RsaKey { + public: + RsaKeymaster1Key(RSA* rsa_key, const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, keymaster_error_t* error) + : RsaKey(rsa_key, hw_enforced, sw_enforced, error) {} +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_RSA_KEYMASTER1_KEY_H_
diff --git a/keymaster/rsa_keymaster1_operation.cpp b/keymaster/rsa_keymaster1_operation.cpp new file mode 100644 index 0000000..002930e --- /dev/null +++ b/keymaster/rsa_keymaster1_operation.cpp
@@ -0,0 +1,180 @@ +/* + * 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 "rsa_keymaster1_operation.h" + +#include <memory> + +#include <keymaster/android_keymaster_utils.h> + +#include "openssl_err.h" +#include "openssl_utils.h" +#include "rsa_keymaster1_key.h" + +using std::unique_ptr; + +namespace keymaster { + +keymaster_error_t RsaKeymaster1WrappedOperation::Begin(EVP_PKEY* rsa_key, + const AuthorizationSet& input_params) { + Keymaster1Engine::KeyData* key_data = engine_->GetData(rsa_key); + if (!key_data) + return KM_ERROR_UNKNOWN_ERROR; + + // Copy the input params and substitute KM_DIGEST_NONE for whatever was specified. Also change + // KM_PAD_RSA_PSS and KM_PAD_OAEP to KM_PAD_NONE, if necessary. These are the params we'll pass + // to the hardware module. The regular Rsa*Operation classes will do software digesting and + // padding where we've told the HW not to. + // + // The reason we don't change KM_PAD_RSA_PKCS1_1_5_SIGN or KM_PAD_RSA_PKCS1_1_5_ENCRYPT to + // KM_PAD_NONE is because the hardware can perform those padding modes, since they don't involve + // digesting. + // + // We also cache in the key the padding value that we expect to be passed to the engine crypto + // operation. This just allows us to double-check that the correct padding value is reaching + // that layer. + AuthorizationSet begin_params(input_params); + int pos = begin_params.find(TAG_DIGEST); + if (pos == -1) + return KM_ERROR_UNSUPPORTED_DIGEST; + begin_params[pos].enumerated = KM_DIGEST_NONE; + + pos = begin_params.find(TAG_PADDING); + if (pos == -1) + return KM_ERROR_UNSUPPORTED_PADDING_MODE; + switch (begin_params[pos].enumerated) { + + case KM_PAD_RSA_PSS: + case KM_PAD_RSA_OAEP: + key_data->expected_openssl_padding = RSA_NO_PADDING; + begin_params[pos].enumerated = KM_PAD_NONE; + break; + + case KM_PAD_RSA_PKCS1_1_5_ENCRYPT: + case KM_PAD_RSA_PKCS1_1_5_SIGN: + key_data->expected_openssl_padding = RSA_PKCS1_PADDING; + break; + } + + return engine_->device()->begin(engine_->device(), purpose_, &key_data->key_material, + &begin_params, nullptr /* out_params */, &operation_handle_); +} + +keymaster_error_t +RsaKeymaster1WrappedOperation::PrepareFinish(EVP_PKEY* rsa_key, + const AuthorizationSet& input_params) { + Keymaster1Engine::KeyData* key_data = engine_->GetData(rsa_key); + if (!key_data) { + LOG_E("Could not get extended key data... not a Keymaster1Engine key?", 0); + return KM_ERROR_UNKNOWN_ERROR; + } + key_data->op_handle = operation_handle_; + key_data->finish_params.Reinitialize(input_params); + + return KM_ERROR_OK; +} + +keymaster_error_t RsaKeymaster1WrappedOperation::Abort() { + return engine_->device()->abort(engine_->device(), operation_handle_); +} + +keymaster_error_t RsaKeymaster1WrappedOperation::GetError(EVP_PKEY* rsa_key) { + Keymaster1Engine::KeyData* key_data = engine_->GetData(rsa_key); // key_data is owned by rsa + if (!key_data) + return KM_ERROR_UNKNOWN_ERROR; + return key_data->error; +} + +static EVP_PKEY* GetEvpKey(const RsaKeymaster1Key& key, keymaster_error_t* error) { + if (!key.key()) { + *error = KM_ERROR_UNKNOWN_ERROR; + return nullptr; + } + + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new()); + if (!key.InternalToEvp(pkey.get())) { + *error = KM_ERROR_UNKNOWN_ERROR; + return nullptr; + } + return pkey.release(); +} + +Operation* RsaKeymaster1OperationFactory::CreateOperation(const Key& key, + const AuthorizationSet& begin_params, + keymaster_error_t* error) { + keymaster_digest_t digest; + if (!GetAndValidateDigest(begin_params, key, &digest, error)) + return nullptr; + + keymaster_padding_t padding; + if (!GetAndValidatePadding(begin_params, key, &padding, error)) + return nullptr; + + const RsaKeymaster1Key& rsa_km1_key(static_cast<const RsaKeymaster1Key&>(key)); + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> rsa(GetEvpKey(rsa_km1_key, error)); + if (!rsa) + return nullptr; + + switch (purpose_) { + case KM_PURPOSE_SIGN: + return new RsaKeymaster1Operation<RsaSignOperation>(digest, padding, rsa.release(), + engine_); + case KM_PURPOSE_DECRYPT: + return new RsaKeymaster1Operation<RsaDecryptOperation>(digest, padding, rsa.release(), + engine_); + default: + LOG_E("Bug: Pubkey operation requested. Those should be handled by normal RSA operations.", + 0); + *error = KM_ERROR_UNSUPPORTED_PURPOSE; + return nullptr; + } +} + +static const keymaster_digest_t supported_digests[] = { + KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, + KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; + +const keymaster_digest_t* +RsaKeymaster1OperationFactory::SupportedDigests(size_t* digest_count) const { + *digest_count = array_length(supported_digests); + return supported_digests; +} + +static const keymaster_padding_t supported_sig_padding[] = { + KM_PAD_NONE, KM_PAD_RSA_PKCS1_1_5_SIGN, KM_PAD_RSA_PSS, +}; +static const keymaster_padding_t supported_crypt_padding[] = { + KM_PAD_NONE, KM_PAD_RSA_PKCS1_1_5_ENCRYPT, KM_PAD_RSA_OAEP, +}; + +const keymaster_padding_t* +RsaKeymaster1OperationFactory::SupportedPaddingModes(size_t* padding_mode_count) const { + switch (purpose_) { + case KM_PURPOSE_SIGN: + case KM_PURPOSE_VERIFY: + *padding_mode_count = array_length(supported_sig_padding); + return supported_sig_padding; + case KM_PURPOSE_ENCRYPT: + case KM_PURPOSE_DECRYPT: + *padding_mode_count = array_length(supported_crypt_padding); + return supported_crypt_padding; + default: + *padding_mode_count = 0; + return nullptr; + } +} + +} // namespace keymaster
diff --git a/keymaster/rsa_keymaster1_operation.h b/keymaster/rsa_keymaster1_operation.h new file mode 100644 index 0000000..30123f0 --- /dev/null +++ b/keymaster/rsa_keymaster1_operation.h
@@ -0,0 +1,120 @@ +/* + * 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 SYSTEM_KEYMASTER_RSA_KEYMASTER1_OPERATION_H_ +#define SYSTEM_KEYMASTER_RSA_KEYMASTER1_OPERATION_H_ + +#include <openssl/evp.h> + +#include <hardware/keymaster1.h> +#include <keymaster/android_keymaster_utils.h> + +#include "keymaster1_engine.h" +#include "rsa_operation.h" + +namespace keymaster { + +class RsaKeymaster1WrappedOperation { + public: + RsaKeymaster1WrappedOperation(keymaster_purpose_t purpose, const Keymaster1Engine* engine) + : purpose_(purpose), operation_handle_(0), engine_(engine) {} + ~RsaKeymaster1WrappedOperation() { + if (operation_handle_) + Abort(); + } + + keymaster_error_t Begin(EVP_PKEY* rsa_key, const AuthorizationSet& input_params); + keymaster_error_t PrepareFinish(EVP_PKEY* rsa_key, const AuthorizationSet& input_params); + void Finish() { operation_handle_ = 0; } + keymaster_error_t Abort(); + + keymaster_error_t GetError(EVP_PKEY* rsa_key); + + protected: + keymaster_purpose_t purpose_; + keymaster_operation_handle_t operation_handle_; + const Keymaster1Engine* engine_; +}; + +template <typename BaseOperation> class RsaKeymaster1Operation : public BaseOperation { + typedef BaseOperation super; + + public: + RsaKeymaster1Operation(keymaster_digest_t digest, keymaster_padding_t padding, EVP_PKEY* key, + const Keymaster1Engine* engine) + : BaseOperation(digest, padding, key), wrapped_operation_(super::purpose(), engine) { + // Shouldn't be instantiated for public key operations. + assert(super::purpose() != KM_PURPOSE_VERIFY); + assert(super::purpose() != KM_PURPOSE_ENCRYPT); + } + + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override { + keymaster_error_t error = wrapped_operation_.Begin(super::rsa_key_, input_params); + if (error != KM_ERROR_OK) + return error; + return super::Begin(input_params, output_params); + } + + keymaster_error_t Finish(const AuthorizationSet& input_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override { + keymaster_error_t error = wrapped_operation_.PrepareFinish(super::rsa_key_, input_params); + if (error != KM_ERROR_OK) + return error; + error = super::Finish(input_params, input, signature, output_params, output); + if (wrapped_operation_.GetError(super::rsa_key_) != KM_ERROR_OK) + error = wrapped_operation_.GetError(super::rsa_key_); + if (error == KM_ERROR_OK) + wrapped_operation_.Finish(); + return error; + } + + keymaster_error_t Abort() override { + keymaster_error_t error = wrapped_operation_.Abort(); + if (error != KM_ERROR_OK) + return error; + return super::Abort(); + } + + private: + RsaKeymaster1WrappedOperation wrapped_operation_; +}; + +/** + * Factory that produces RsaKeymaster1Operations. This is instantiated and + * provided by RsaKeymaster1KeyFactory. + */ +class RsaKeymaster1OperationFactory : public OperationFactory { + public: + RsaKeymaster1OperationFactory(keymaster_purpose_t purpose, const Keymaster1Engine* engine) + : purpose_(purpose), engine_(engine) {} + KeyType registry_key() const override { return KeyType(KM_ALGORITHM_RSA, purpose_); } + + Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params, + keymaster_error_t* error) override; + + const keymaster_digest_t* SupportedDigests(size_t* digest_count) const override; + const keymaster_padding_t* SupportedPaddingModes(size_t* padding_mode_count) const override; + + private: + keymaster_purpose_t purpose_; + const Keymaster1Engine* engine_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_RSA_KEYMASTER1_OPERATION_H_
diff --git a/keymaster/rsa_operation.cpp b/keymaster/rsa_operation.cpp new file mode 100644 index 0000000..2046a64 --- /dev/null +++ b/keymaster/rsa_operation.cpp
@@ -0,0 +1,619 @@ +/* + * Copyright 2014 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 "rsa_operation.h" + +#include <limits.h> + +#include <new> + +#include <openssl/err.h> + +#include <keymaster/logger.h> + +#include "openssl_err.h" +#include "openssl_utils.h" +#include "rsa_key.h" + +namespace keymaster { + +const size_t kPssOverhead = 2; +const size_t kMinPssSaltSize = 20; + +// Overhead for PKCS#1 v1.5 signature padding of undigested messages. Digested messages have +// additional overhead, for the digest algorithmIdentifier required by PKCS#1. +const size_t kPkcs1UndigestedSignaturePaddingOverhead = 11; + +/* static */ +EVP_PKEY* RsaOperationFactory::GetRsaKey(const Key& key, keymaster_error_t* error) { + const RsaKey* rsa_key = static_cast<const RsaKey*>(&key); + assert(rsa_key); + if (!rsa_key || !rsa_key->key()) { + *error = KM_ERROR_UNKNOWN_ERROR; + return nullptr; + } + + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(EVP_PKEY_new()); + if (!rsa_key->InternalToEvp(pkey.get())) { + *error = KM_ERROR_UNKNOWN_ERROR; + return nullptr; + } + return pkey.release(); +} + +static const keymaster_digest_t supported_digests[] = { + KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, + KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; + +const keymaster_digest_t* RsaOperationFactory::SupportedDigests(size_t* digest_count) const { + *digest_count = array_length(supported_digests); + return supported_digests; +} + +RsaOperation* RsaOperationFactory::CreateRsaOperation(const Key& key, + const AuthorizationSet& begin_params, + keymaster_error_t* error) { + keymaster_padding_t padding; + if (!GetAndValidatePadding(begin_params, key, &padding, error)) + return nullptr; + + bool require_digest = (purpose() == KM_PURPOSE_SIGN || purpose() == KM_PURPOSE_VERIFY || + padding == KM_PAD_RSA_OAEP); + + keymaster_digest_t digest = KM_DIGEST_NONE; + if (require_digest && !GetAndValidateDigest(begin_params, key, &digest, error)) + return nullptr; + + UniquePtr<EVP_PKEY, EVP_PKEY_Delete> rsa(GetRsaKey(key, error)); + if (!rsa.get()) + return nullptr; + + RsaOperation* op = InstantiateOperation(digest, padding, rsa.release()); + if (!op) + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + return op; +} + +static const keymaster_padding_t supported_sig_padding[] = {KM_PAD_NONE, KM_PAD_RSA_PKCS1_1_5_SIGN, + KM_PAD_RSA_PSS}; +const keymaster_padding_t* +RsaDigestingOperationFactory::SupportedPaddingModes(size_t* padding_mode_count) const { + *padding_mode_count = array_length(supported_sig_padding); + return supported_sig_padding; +} + +RsaOperation* RsaCryptingOperationFactory::CreateRsaOperation(const Key& key, + const AuthorizationSet& begin_params, + keymaster_error_t* error) { + UniquePtr<RsaOperation> op(RsaOperationFactory::CreateRsaOperation(key, begin_params, error)); + if (op.get()) { + switch (op->padding()) { + case KM_PAD_NONE: + case KM_PAD_RSA_PKCS1_1_5_ENCRYPT: + if (op->digest() != KM_DIGEST_NONE) { + *error = KM_ERROR_INCOMPATIBLE_DIGEST; + return nullptr; + } + break; + + case KM_PAD_RSA_OAEP: + if (op->digest() == KM_DIGEST_NONE) { + *error = KM_ERROR_INCOMPATIBLE_DIGEST; + return nullptr; + } + break; + + default: + *error = KM_ERROR_UNSUPPORTED_PADDING_MODE; + return nullptr; + } + } + return op.release(); +} + +static const keymaster_padding_t supported_crypt_padding[] = {KM_PAD_NONE, KM_PAD_RSA_OAEP, + KM_PAD_RSA_PKCS1_1_5_ENCRYPT}; +const keymaster_padding_t* +RsaCryptingOperationFactory::SupportedPaddingModes(size_t* padding_mode_count) const { + *padding_mode_count = array_length(supported_crypt_padding); + return supported_crypt_padding; +} + +RsaOperation::~RsaOperation() { + if (rsa_key_ != NULL) + EVP_PKEY_free(rsa_key_); +} + +keymaster_error_t RsaOperation::Begin(const AuthorizationSet& /* input_params */, + AuthorizationSet* /* output_params */) { + return InitDigest(); +} + +keymaster_error_t RsaOperation::Update(const AuthorizationSet& /* additional_params */, + const Buffer& input, AuthorizationSet* /* output_params */, + Buffer* /* output */, size_t* input_consumed) { + assert(input_consumed); + switch (purpose()) { + default: + return KM_ERROR_UNIMPLEMENTED; + case KM_PURPOSE_SIGN: + case KM_PURPOSE_VERIFY: + case KM_PURPOSE_ENCRYPT: + case KM_PURPOSE_DECRYPT: + return StoreData(input, input_consumed); + } +} + +keymaster_error_t RsaOperation::StoreData(const Buffer& input, size_t* input_consumed) { + assert(input_consumed); + + if (!data_.reserve(EVP_PKEY_size(rsa_key_))) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + // If the write fails, it's because input length exceeds key size. + if (!data_.write(input.peek_read(), input.available_read())) { + LOG_E("Input too long: cannot operate on %u bytes of data with %u-byte RSA key", + input.available_read() + data_.available_read(), EVP_PKEY_size(rsa_key_)); + return KM_ERROR_INVALID_INPUT_LENGTH; + } + + *input_consumed = input.available_read(); + return KM_ERROR_OK; +} + +keymaster_error_t RsaOperation::SetRsaPaddingInEvpContext(EVP_PKEY_CTX* pkey_ctx) { + keymaster_error_t error; + int openssl_padding = GetOpensslPadding(&error); + if (error != KM_ERROR_OK) + return error; + + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, openssl_padding) <= 0) + return TranslateLastOpenSslError(); + return KM_ERROR_OK; +} + +keymaster_error_t RsaOperation::InitDigest() { + if (digest_ == KM_DIGEST_NONE) { + if (require_digest()) + return KM_ERROR_INCOMPATIBLE_DIGEST; + return KM_ERROR_OK; + } + + switch (digest_) { + case KM_DIGEST_NONE: + return KM_ERROR_OK; + case KM_DIGEST_MD5: + digest_algorithm_ = EVP_md5(); + return KM_ERROR_OK; + case KM_DIGEST_SHA1: + digest_algorithm_ = EVP_sha1(); + return KM_ERROR_OK; + case KM_DIGEST_SHA_2_224: + digest_algorithm_ = EVP_sha224(); + return KM_ERROR_OK; + case KM_DIGEST_SHA_2_256: + digest_algorithm_ = EVP_sha256(); + return KM_ERROR_OK; + case KM_DIGEST_SHA_2_384: + digest_algorithm_ = EVP_sha384(); + return KM_ERROR_OK; + case KM_DIGEST_SHA_2_512: + digest_algorithm_ = EVP_sha512(); + return KM_ERROR_OK; + default: + return KM_ERROR_UNSUPPORTED_DIGEST; + } +} + +RsaDigestingOperation::RsaDigestingOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, + keymaster_padding_t padding, EVP_PKEY* key) + : RsaOperation(purpose, digest, padding, key) { + EVP_MD_CTX_init(&digest_ctx_); +} +RsaDigestingOperation::~RsaDigestingOperation() { + EVP_MD_CTX_cleanup(&digest_ctx_); +} + +int RsaDigestingOperation::GetOpensslPadding(keymaster_error_t* error) { + *error = KM_ERROR_OK; + switch (padding_) { + case KM_PAD_NONE: + return RSA_NO_PADDING; + case KM_PAD_RSA_PKCS1_1_5_SIGN: + return RSA_PKCS1_PADDING; + case KM_PAD_RSA_PSS: + if (digest_ == KM_DIGEST_NONE) { + *error = KM_ERROR_INCOMPATIBLE_PADDING_MODE; + return -1; + } + if (EVP_MD_size(digest_algorithm_) + kPssOverhead + kMinPssSaltSize > + (size_t)EVP_PKEY_size(rsa_key_)) { + LOG_E("Input too long: %d-byte digest cannot be used with %d-byte RSA key in PSS " + "padding mode", + EVP_MD_size(digest_algorithm_), EVP_PKEY_size(rsa_key_)); + *error = KM_ERROR_INCOMPATIBLE_DIGEST; + return -1; + } + return RSA_PKCS1_PSS_PADDING; + default: + return -1; + } +} + +keymaster_error_t RsaSignOperation::Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) { + keymaster_error_t error = RsaDigestingOperation::Begin(input_params, output_params); + if (error != KM_ERROR_OK) + return error; + + if (digest_ == KM_DIGEST_NONE) + return KM_ERROR_OK; + + EVP_PKEY_CTX* pkey_ctx; + if (EVP_DigestSignInit(&digest_ctx_, &pkey_ctx, digest_algorithm_, nullptr /* engine */, + rsa_key_) != 1) + return TranslateLastOpenSslError(); + return SetRsaPaddingInEvpContext(pkey_ctx); +} + +keymaster_error_t RsaSignOperation::Update(const AuthorizationSet& additional_params, + const Buffer& input, AuthorizationSet* output_params, + Buffer* output, size_t* input_consumed) { + if (digest_ == KM_DIGEST_NONE) + // Just buffer the data. + return RsaOperation::Update(additional_params, input, output_params, output, + input_consumed); + + if (EVP_DigestSignUpdate(&digest_ctx_, input.peek_read(), input.available_read()) != 1) + return TranslateLastOpenSslError(); + *input_consumed = input.available_read(); + return KM_ERROR_OK; +} + +keymaster_error_t RsaSignOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& /* signature */, + AuthorizationSet* /* output_params */, Buffer* output) { + assert(output); + + keymaster_error_t error = UpdateForFinish(additional_params, input); + if (error != KM_ERROR_OK) + return error; + + if (digest_ == KM_DIGEST_NONE) + return SignUndigested(output); + else + return SignDigested(output); +} + +static keymaster_error_t zero_pad_left(UniquePtr<uint8_t[]>* dest, size_t padded_len, Buffer& src) { + assert(padded_len > src.available_read()); + + dest->reset(new uint8_t[padded_len]); + if (!dest->get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + size_t padding_len = padded_len - src.available_read(); + memset(dest->get(), 0, padding_len); + if (!src.read(dest->get() + padding_len, src.available_read())) + return KM_ERROR_UNKNOWN_ERROR; + + return KM_ERROR_OK; +} + +keymaster_error_t RsaSignOperation::SignUndigested(Buffer* output) { + UniquePtr<RSA, RSA_Delete> rsa(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(rsa_key_))); + if (!rsa.get()) + return TranslateLastOpenSslError(); + + if (!output->Reinitialize(RSA_size(rsa.get()))) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + size_t key_len = EVP_PKEY_size(rsa_key_); + int bytes_encrypted; + switch (padding_) { + case KM_PAD_NONE: { + const uint8_t* to_encrypt = data_.peek_read(); + UniquePtr<uint8_t[]> zero_padded; + if (data_.available_read() > key_len) { + return KM_ERROR_INVALID_INPUT_LENGTH; + } else if (data_.available_read() < key_len) { + keymaster_error_t error = zero_pad_left(&zero_padded, key_len, data_); + if (error != KM_ERROR_OK) + return error; + to_encrypt = zero_padded.get(); + } + bytes_encrypted = RSA_private_encrypt(key_len, to_encrypt, output->peek_write(), rsa.get(), + RSA_NO_PADDING); + break; + } + case KM_PAD_RSA_PKCS1_1_5_SIGN: + // Does PKCS1 padding without digesting even make sense? Dunno. We'll support it. + if (data_.available_read() + kPkcs1UndigestedSignaturePaddingOverhead > key_len) { + LOG_E("Input too long: cannot sign %u-byte message with PKCS1 padding with %u-bit key", + data_.available_read(), EVP_PKEY_size(rsa_key_) * 8); + return KM_ERROR_INVALID_INPUT_LENGTH; + } + bytes_encrypted = RSA_private_encrypt(data_.available_read(), data_.peek_read(), + output->peek_write(), rsa.get(), RSA_PKCS1_PADDING); + break; + + default: + return KM_ERROR_UNSUPPORTED_PADDING_MODE; + } + + if (bytes_encrypted <= 0) + return TranslateLastOpenSslError(); + if (!output->advance_write(bytes_encrypted)) + return KM_ERROR_UNKNOWN_ERROR; + return KM_ERROR_OK; +} + +keymaster_error_t RsaSignOperation::SignDigested(Buffer* output) { + size_t siglen; + if (EVP_DigestSignFinal(&digest_ctx_, nullptr /* signature */, &siglen) != 1) + return TranslateLastOpenSslError(); + + if (!output->Reinitialize(siglen)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (EVP_DigestSignFinal(&digest_ctx_, output->peek_write(), &siglen) <= 0) + return TranslateLastOpenSslError(); + if (!output->advance_write(siglen)) + return KM_ERROR_UNKNOWN_ERROR; + + return KM_ERROR_OK; +} + +keymaster_error_t RsaVerifyOperation::Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) { + keymaster_error_t error = RsaDigestingOperation::Begin(input_params, output_params); + if (error != KM_ERROR_OK) + return error; + + if (digest_ == KM_DIGEST_NONE) + return KM_ERROR_OK; + + EVP_PKEY_CTX* pkey_ctx; + if (EVP_DigestVerifyInit(&digest_ctx_, &pkey_ctx, digest_algorithm_, NULL, rsa_key_) != 1) + return TranslateLastOpenSslError(); + return SetRsaPaddingInEvpContext(pkey_ctx); +} + +keymaster_error_t RsaVerifyOperation::Update(const AuthorizationSet& additional_params, + const Buffer& input, AuthorizationSet* output_params, + Buffer* output, size_t* input_consumed) { + if (digest_ == KM_DIGEST_NONE) + // Just buffer the data. + return RsaOperation::Update(additional_params, input, output_params, output, + input_consumed); + + if (EVP_DigestVerifyUpdate(&digest_ctx_, input.peek_read(), input.available_read()) != 1) + return TranslateLastOpenSslError(); + *input_consumed = input.available_read(); + return KM_ERROR_OK; +} + +keymaster_error_t RsaVerifyOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& signature, + AuthorizationSet* /* output_params */, + Buffer* /* output */) { + keymaster_error_t error = UpdateForFinish(additional_params, input); + if (error != KM_ERROR_OK) + return error; + + if (digest_ == KM_DIGEST_NONE) + return VerifyUndigested(signature); + else + return VerifyDigested(signature); +} + +keymaster_error_t RsaVerifyOperation::VerifyUndigested(const Buffer& signature) { + UniquePtr<RSA, RSA_Delete> rsa(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(rsa_key_))); + if (!rsa.get()) + return KM_ERROR_UNKNOWN_ERROR; + + size_t key_len = RSA_size(rsa.get()); + int openssl_padding; + switch (padding_) { + case KM_PAD_NONE: + if (data_.available_read() > key_len) + return KM_ERROR_INVALID_INPUT_LENGTH; + if (key_len != signature.available_read()) + return KM_ERROR_VERIFICATION_FAILED; + openssl_padding = RSA_NO_PADDING; + break; + case KM_PAD_RSA_PKCS1_1_5_SIGN: + if (data_.available_read() + kPkcs1UndigestedSignaturePaddingOverhead > key_len) { + LOG_E("Input too long: cannot verify %u-byte message with PKCS1 padding && %u-bit key", + data_.available_read(), key_len * 8); + return KM_ERROR_INVALID_INPUT_LENGTH; + } + openssl_padding = RSA_PKCS1_PADDING; + break; + default: + return KM_ERROR_UNSUPPORTED_PADDING_MODE; + } + + UniquePtr<uint8_t[]> decrypted_data(new (std::nothrow) uint8_t[key_len]); + if (!decrypted_data.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + int bytes_decrypted = RSA_public_decrypt(signature.available_read(), signature.peek_read(), + decrypted_data.get(), rsa.get(), openssl_padding); + if (bytes_decrypted < 0) + return KM_ERROR_VERIFICATION_FAILED; + + const uint8_t* compare_pos = decrypted_data.get(); + size_t bytes_to_compare = bytes_decrypted; + uint8_t zero_check_result = 0; + if (padding_ == KM_PAD_NONE && data_.available_read() < bytes_to_compare) { + // If the data is short, for "unpadded" signing we zero-pad to the left. So during + // verification we should have zeros on the left of the decrypted data. Do a constant-time + // check. + const uint8_t* zero_end = compare_pos + bytes_to_compare - data_.available_read(); + while (compare_pos < zero_end) + zero_check_result |= *compare_pos++; + bytes_to_compare = data_.available_read(); + } + if (memcmp_s(compare_pos, data_.peek_read(), bytes_to_compare) != 0 || zero_check_result != 0) + return KM_ERROR_VERIFICATION_FAILED; + return KM_ERROR_OK; +} + +keymaster_error_t RsaVerifyOperation::VerifyDigested(const Buffer& signature) { + if (!EVP_DigestVerifyFinal(&digest_ctx_, signature.peek_read(), signature.available_read())) + return KM_ERROR_VERIFICATION_FAILED; + return KM_ERROR_OK; +} + +keymaster_error_t RsaCryptOperation::SetOaepDigestIfRequired(EVP_PKEY_CTX* pkey_ctx) { + if (padding() != KM_PAD_RSA_OAEP) + return KM_ERROR_OK; + + assert(digest_algorithm_ != nullptr); + if (!EVP_PKEY_CTX_set_rsa_oaep_md(pkey_ctx, digest_algorithm_)) + return TranslateLastOpenSslError(); + + // MGF1 MD is always SHA1. + if (!EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha1())) + return TranslateLastOpenSslError(); + + return KM_ERROR_OK; +} + +int RsaCryptOperation::GetOpensslPadding(keymaster_error_t* error) { + *error = KM_ERROR_OK; + switch (padding_) { + case KM_PAD_NONE: + return RSA_NO_PADDING; + case KM_PAD_RSA_PKCS1_1_5_ENCRYPT: + return RSA_PKCS1_PADDING; + case KM_PAD_RSA_OAEP: + return RSA_PKCS1_OAEP_PADDING; + default: + return -1; + } +} + +struct EVP_PKEY_CTX_Delete { + void operator()(EVP_PKEY_CTX* p) { EVP_PKEY_CTX_free(p); } +}; + +keymaster_error_t RsaEncryptOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& /* signature */, + AuthorizationSet* /* output_params */, + Buffer* output) { + if (!output) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + keymaster_error_t error = UpdateForFinish(additional_params, input); + if (error != KM_ERROR_OK) + return error; + + UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx( + EVP_PKEY_CTX_new(rsa_key_, nullptr /* engine */)); + if (!ctx.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (EVP_PKEY_encrypt_init(ctx.get()) <= 0) + return TranslateLastOpenSslError(); + + error = SetRsaPaddingInEvpContext(ctx.get()); + if (error != KM_ERROR_OK) + return error; + error = SetOaepDigestIfRequired(ctx.get()); + if (error != KM_ERROR_OK) + return error; + + size_t outlen; + if (EVP_PKEY_encrypt(ctx.get(), nullptr /* out */, &outlen, data_.peek_read(), + data_.available_read()) <= 0) + return TranslateLastOpenSslError(); + + if (!output->Reinitialize(outlen)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + const uint8_t* to_encrypt = data_.peek_read(); + size_t to_encrypt_len = data_.available_read(); + UniquePtr<uint8_t[]> zero_padded; + if (padding_ == KM_PAD_NONE && to_encrypt_len < outlen) { + keymaster_error_t error = zero_pad_left(&zero_padded, outlen, data_); + if (error != KM_ERROR_OK) + return error; + to_encrypt = zero_padded.get(); + to_encrypt_len = outlen; + } + + if (EVP_PKEY_encrypt(ctx.get(), output->peek_write(), &outlen, to_encrypt, to_encrypt_len) <= 0) + return TranslateLastOpenSslError(); + if (!output->advance_write(outlen)) + return KM_ERROR_UNKNOWN_ERROR; + + return KM_ERROR_OK; +} + +keymaster_error_t RsaDecryptOperation::Finish(const AuthorizationSet& additional_params, + const Buffer& input, const Buffer& /* signature */, + AuthorizationSet* /* output_params */, + Buffer* output) { + if (!output) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + keymaster_error_t error = UpdateForFinish(additional_params, input); + if (error != KM_ERROR_OK) + return error; + + UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx( + EVP_PKEY_CTX_new(rsa_key_, nullptr /* engine */)); + if (!ctx.get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + if (EVP_PKEY_decrypt_init(ctx.get()) <= 0) + return TranslateLastOpenSslError(); + + error = SetRsaPaddingInEvpContext(ctx.get()); + if (error != KM_ERROR_OK) + return error; + error = SetOaepDigestIfRequired(ctx.get()); + if (error != KM_ERROR_OK) + return error; + + size_t outlen; + if (EVP_PKEY_decrypt(ctx.get(), nullptr /* out */, &outlen, data_.peek_read(), + data_.available_read()) <= 0) + return TranslateLastOpenSslError(); + + if (!output->Reinitialize(outlen)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + const uint8_t* to_decrypt = data_.peek_read(); + size_t to_decrypt_len = data_.available_read(); + UniquePtr<uint8_t[]> zero_padded; + if (padding_ == KM_PAD_NONE && to_decrypt_len < outlen) { + keymaster_error_t error = zero_pad_left(&zero_padded, outlen, data_); + if (error != KM_ERROR_OK) + return error; + to_decrypt = zero_padded.get(); + to_decrypt_len = outlen; + } + + if (EVP_PKEY_decrypt(ctx.get(), output->peek_write(), &outlen, to_decrypt, to_decrypt_len) <= 0) + return TranslateLastOpenSslError(); + if (!output->advance_write(outlen)) + return KM_ERROR_UNKNOWN_ERROR; + + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/rsa_operation.h b/keymaster/rsa_operation.h new file mode 100644 index 0000000..8283f2e --- /dev/null +++ b/keymaster/rsa_operation.h
@@ -0,0 +1,261 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_RSA_OPERATION_H_ +#define SYSTEM_KEYMASTER_RSA_OPERATION_H_ + +#include <UniquePtr.h> + +#include <openssl/evp.h> +#include <openssl/rsa.h> + +#include "operation.h" + +namespace keymaster { + +/** + * Base class for all RSA operations. + * + * This class provides RSA key management, plus buffering of data for non-digesting modes. + */ +class RsaOperation : public Operation { + public: + RsaOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, + keymaster_padding_t padding, EVP_PKEY* key) + : Operation(purpose), rsa_key_(key), padding_(padding), digest_(digest), + digest_algorithm_(nullptr) {} + ~RsaOperation(); + + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override; + keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, + size_t* input_consumed) override; + keymaster_error_t Abort() override { return KM_ERROR_OK; } + + keymaster_padding_t padding() const { return padding_; } + keymaster_digest_t digest() const { return digest_; } + + protected: + virtual int GetOpensslPadding(keymaster_error_t* error) = 0; + virtual bool require_digest() const = 0; + + keymaster_error_t StoreData(const Buffer& input, size_t* input_consumed); + keymaster_error_t SetRsaPaddingInEvpContext(EVP_PKEY_CTX* pkey_ctx); + keymaster_error_t InitDigest(); + + EVP_PKEY* rsa_key_; + const keymaster_padding_t padding_; + Buffer data_; + const keymaster_digest_t digest_; + const EVP_MD* digest_algorithm_; +}; + +/** + * Base class for all digesting RSA operations. + * + * This class adds digesting support, for digesting modes. For non-digesting modes, it falls back + * on the RsaOperation input buffering. + */ +class RsaDigestingOperation : public RsaOperation { + public: + RsaDigestingOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, + keymaster_padding_t padding, EVP_PKEY* key); + ~RsaDigestingOperation(); + + protected: + int GetOpensslPadding(keymaster_error_t* error) override; + bool require_digest() const override { return padding_ == KM_PAD_RSA_PSS; } + EVP_MD_CTX digest_ctx_; +}; + +/** + * RSA private key signing operation. + */ +class RsaSignOperation : public RsaDigestingOperation { + public: + RsaSignOperation(keymaster_digest_t digest, keymaster_padding_t padding, EVP_PKEY* key) + : RsaDigestingOperation(KM_PURPOSE_SIGN, digest, padding, key) {} + + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override; + keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, + size_t* input_consumed) override; + keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override; + + private: + keymaster_error_t SignUndigested(Buffer* output); + keymaster_error_t SignDigested(Buffer* output); +}; + +/** + * RSA public key verification operation. + */ +class RsaVerifyOperation : public RsaDigestingOperation { + public: + RsaVerifyOperation(keymaster_digest_t digest, keymaster_padding_t padding, EVP_PKEY* key) + : RsaDigestingOperation(KM_PURPOSE_VERIFY, digest, padding, key) {} + + keymaster_error_t Begin(const AuthorizationSet& input_params, + AuthorizationSet* output_params) override; + keymaster_error_t Update(const AuthorizationSet& additional_params, const Buffer& input, + AuthorizationSet* output_params, Buffer* output, + size_t* input_consumed) override; + keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override; + + private: + keymaster_error_t VerifyUndigested(const Buffer& signature); + keymaster_error_t VerifyDigested(const Buffer& signature); +}; + +/** + * Base class for RSA crypting operations. + */ +class RsaCryptOperation : public RsaOperation { + public: + RsaCryptOperation(keymaster_purpose_t purpose, keymaster_digest_t digest, + keymaster_padding_t padding, EVP_PKEY* key) + : RsaOperation(purpose, digest, padding, key) {} + + protected: + keymaster_error_t SetOaepDigestIfRequired(EVP_PKEY_CTX* pkey_ctx); + + private: + int GetOpensslPadding(keymaster_error_t* error) override; + bool require_digest() const override { return padding_ == KM_PAD_RSA_OAEP; } +}; + +/** + * RSA public key encryption operation. + */ +class RsaEncryptOperation : public RsaCryptOperation { + public: + RsaEncryptOperation(keymaster_digest_t digest, keymaster_padding_t padding, EVP_PKEY* key) + : RsaCryptOperation(KM_PURPOSE_ENCRYPT, digest, padding, key) {} + keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override; +}; + +/** + * RSA private key decryption operation. + */ +class RsaDecryptOperation : public RsaCryptOperation { + public: + RsaDecryptOperation(keymaster_digest_t digest, keymaster_padding_t padding, EVP_PKEY* key) + : RsaCryptOperation(KM_PURPOSE_DECRYPT, digest, padding, key) {} + keymaster_error_t Finish(const AuthorizationSet& additional_params, const Buffer& input, + const Buffer& signature, AuthorizationSet* output_params, + Buffer* output) override; +}; + +/** + * Abstract base for all RSA operation factories. This class exists mainly to centralize some code + * common to all RSA operation factories. + */ +class RsaOperationFactory : public OperationFactory { + public: + KeyType registry_key() const override { return KeyType(KM_ALGORITHM_RSA, purpose()); } + virtual keymaster_purpose_t purpose() const = 0; + + Operation* CreateOperation(const Key& key, const AuthorizationSet& begin_params, + keymaster_error_t* error) override { + return CreateRsaOperation(key, begin_params, error); + } + const keymaster_digest_t* SupportedDigests(size_t* digest_count) const override; + + protected: + static EVP_PKEY* GetRsaKey(const Key& key, keymaster_error_t* error); + virtual RsaOperation* CreateRsaOperation(const Key& key, const AuthorizationSet& begin_params, + keymaster_error_t* error); + + private: + virtual RsaOperation* InstantiateOperation(keymaster_digest_t digest, + keymaster_padding_t padding, EVP_PKEY* key) = 0; +}; + +/** + * Abstract base for RSA operations that digest their input (signing and verification). + */ +class RsaDigestingOperationFactory : public RsaOperationFactory { + public: + const keymaster_padding_t* SupportedPaddingModes(size_t* padding_mode_count) const override; +}; + +/** + * Abstract base for en/de-crypting RSA operation factories. This class does most of the work of + * creating such operations, delegating only the actual operation instantiation. + */ +class RsaCryptingOperationFactory : public RsaOperationFactory { + public: + RsaOperation* CreateRsaOperation(const Key& key, const AuthorizationSet& begin_params, + keymaster_error_t* error) override; + const keymaster_padding_t* SupportedPaddingModes(size_t* padding_mode_count) const override; +}; + +/** + * Concrete factory for RSA signing operations. + */ +class RsaSigningOperationFactory : public RsaDigestingOperationFactory { + public: + keymaster_purpose_t purpose() const override { return KM_PURPOSE_SIGN; } + RsaOperation* InstantiateOperation(keymaster_digest_t digest, keymaster_padding_t padding, + EVP_PKEY* key) override { + return new (std::nothrow) RsaSignOperation(digest, padding, key); + } +}; + +/** + * Concrete factory for RSA signing operations. + */ +class RsaVerificationOperationFactory : public RsaDigestingOperationFactory { + keymaster_purpose_t purpose() const override { return KM_PURPOSE_VERIFY; } + RsaOperation* InstantiateOperation(keymaster_digest_t digest, keymaster_padding_t padding, + EVP_PKEY* key) override { + return new (std::nothrow) RsaVerifyOperation(digest, padding, key); + } +}; + +/** + * Concrete factory for RSA signing operations. + */ +class RsaEncryptionOperationFactory : public RsaCryptingOperationFactory { + keymaster_purpose_t purpose() const override { return KM_PURPOSE_ENCRYPT; } + RsaOperation* InstantiateOperation(keymaster_digest_t digest, keymaster_padding_t padding, + EVP_PKEY* key) override { + return new (std::nothrow) RsaEncryptOperation(digest, padding, key); + } +}; + +/** + * Concrete factory for RSA signing operations. + */ +class RsaDecryptionOperationFactory : public RsaCryptingOperationFactory { + keymaster_purpose_t purpose() const override { return KM_PURPOSE_DECRYPT; } + RsaOperation* InstantiateOperation(keymaster_digest_t digest, keymaster_padding_t padding, + EVP_PKEY* key) override { + return new (std::nothrow) RsaDecryptOperation(digest, padding, key); + } +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_RSA_OPERATION_H_
diff --git a/keymaster/rsa_privkey_pk8.der b/keymaster/rsa_privkey_pk8.der new file mode 100644 index 0000000..0336f80 --- /dev/null +++ b/keymaster/rsa_privkey_pk8.der Binary files differ
diff --git a/keymaster/serializable.cpp b/keymaster/serializable.cpp new file mode 100644 index 0000000..5db64f8 --- /dev/null +++ b/keymaster/serializable.cpp
@@ -0,0 +1,165 @@ +/* + * Copyright 2014 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 <keymaster/serializable.h> + +#include <assert.h> + +#include <new> + +#include <keymaster/android_keymaster_utils.h> + +namespace keymaster { + +uint8_t* append_to_buf(uint8_t* buf, const uint8_t* end, const void* data, size_t data_len) { + if (buf + data_len < buf) // Pointer wrap check + return buf; + + if (buf + data_len <= end) { + memcpy(buf, data, data_len); + return buf + data_len; + } + return buf; +} + +bool copy_from_buf(const uint8_t** buf_ptr, const uint8_t* end, void* dest, size_t size) { + if (*buf_ptr + size < *buf_ptr) // Pointer wrap check + return false; + + if (end < *buf_ptr + size) + return false; + memcpy(dest, *buf_ptr, size); + *buf_ptr += size; + return true; +} + +bool copy_size_and_data_from_buf(const uint8_t** buf_ptr, const uint8_t* end, size_t* size, + UniquePtr<uint8_t[]>* dest) { + if (!copy_uint32_from_buf(buf_ptr, end, size)) + return false; + + if (*buf_ptr + *size < *buf_ptr) // Pointer wrap check + return false; + + if (*buf_ptr + *size > end) + return false; + + if (*size == 0) { + dest->reset(); + return true; + } + dest->reset(new (std::nothrow) uint8_t[*size]); + if (!dest->get()) + return false; + return copy_from_buf(buf_ptr, end, dest->get(), *size); +} + +bool Buffer::reserve(size_t size) { + if (available_write() < size) { + size_t new_size = buffer_size_ + size - available_write(); + uint8_t* new_buffer = new (std::nothrow) uint8_t[new_size]; + if (!new_buffer) + return false; + memcpy(new_buffer, buffer_.get() + read_position_, available_read()); + memset_s(buffer_.get(), 0, buffer_size_); + buffer_.reset(new_buffer); + buffer_size_ = new_size; + write_position_ -= read_position_; + read_position_ = 0; + } + return true; +} + +bool Buffer::Reinitialize(size_t size) { + Clear(); + buffer_.reset(new (std::nothrow) uint8_t[size]); + if (!buffer_.get()) + return false; + buffer_size_ = size; + read_position_ = 0; + write_position_ = 0; + return true; +} + +bool Buffer::Reinitialize(const void* data, size_t data_len) { + Clear(); + if (static_cast<const uint8_t*>(data) + data_len < data) // Pointer wrap check + return false; + buffer_.reset(new (std::nothrow) uint8_t[data_len]); + if (!buffer_.get()) + return false; + buffer_size_ = data_len; + memcpy(buffer_.get(), data, data_len); + read_position_ = 0; + write_position_ = buffer_size_; + return true; +} + +size_t Buffer::available_write() const { + assert(buffer_size_ >= write_position_); + return buffer_size_ - write_position_; +} + +size_t Buffer::available_read() const { + assert(buffer_size_ >= write_position_); + assert(write_position_ >= read_position_); + return write_position_ - read_position_; +} + +bool Buffer::write(const uint8_t* src, size_t write_length) { + if (available_write() < write_length) + return false; + memcpy(buffer_.get() + write_position_, src, write_length); + write_position_ += write_length; + return true; +} + +bool Buffer::read(uint8_t* dest, size_t read_length) { + if (available_read() < read_length) + return false; + memcpy(dest, buffer_.get() + read_position_, read_length); + read_position_ += read_length; + return true; +} + +size_t Buffer::SerializedSize() const { + return sizeof(uint32_t) + available_read(); +} + +uint8_t* Buffer::Serialize(uint8_t* buf, const uint8_t* end) const { + return append_size_and_data_to_buf(buf, end, peek_read(), available_read()); +} + +bool Buffer::Deserialize(const uint8_t** buf_ptr, const uint8_t* end) { + Clear(); + if (!copy_size_and_data_from_buf(buf_ptr, end, &buffer_size_, &buffer_)) { + buffer_.reset(); + buffer_size_ = 0; + return false; + } + write_position_ = buffer_size_; + return true; +} + +void Buffer::Clear() { + memset_s(buffer_.get(), 0, buffer_size_); + buffer_.reset(); + read_position_ = 0; + write_position_ = 0; + buffer_size_ = 0; +} + +} // namespace keymaster
diff --git a/keymaster/soft_keymaster_context.cpp b/keymaster/soft_keymaster_context.cpp new file mode 100644 index 0000000..a143245 --- /dev/null +++ b/keymaster/soft_keymaster_context.cpp
@@ -0,0 +1,883 @@ +/* + * 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 <keymaster/soft_keymaster_context.h> + +#include <memory> +#include <time.h> + +#include <openssl/aes.h> +#include <openssl/rand.h> +#include <openssl/sha.h> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/logger.h> + +#include "aes_key.h" +#include "auth_encrypted_key_blob.h" +#include "ec_keymaster0_key.h" +#include "ec_keymaster1_key.h" +#include "hmac_key.h" +#include "integrity_assured_key_blob.h" +#include "keymaster0_engine.h" +#include "ocb_utils.h" +#include "openssl_err.h" +#include "rsa_keymaster0_key.h" +#include "rsa_keymaster1_key.h" + +using std::unique_ptr; + +namespace keymaster { + +namespace { +static uint8_t master_key_bytes[AES_BLOCK_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +const KeymasterKeyBlob MASTER_KEY(master_key_bytes, array_length(master_key_bytes)); + +static uint8_t kRsaAttestKey[] = { + 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xc0, 0x83, 0x23, 0xdc, 0x56, + 0x88, 0x1b, 0xb8, 0x30, 0x20, 0x69, 0xf5, 0xb0, 0x85, 0x61, 0xc6, 0xee, 0xbe, 0x7f, 0x05, 0xe2, + 0xf5, 0xa8, 0x42, 0x04, 0x8a, 0xbe, 0x8b, 0x47, 0xbe, 0x76, 0xfe, 0xae, 0xf2, 0x5c, 0xf2, 0x9b, + 0x2a, 0xfa, 0x32, 0x00, 0x14, 0x16, 0x01, 0x42, 0x99, 0x89, 0xa1, 0x5f, 0xcf, 0xc6, 0x81, 0x5e, + 0xb3, 0x63, 0x58, 0x3c, 0x2f, 0xd2, 0xf2, 0x0b, 0xe4, 0x98, 0x32, 0x83, 0xdd, 0x81, 0x4b, 0x16, + 0xd7, 0xe1, 0x85, 0x41, 0x7a, 0xe5, 0x4a, 0xbc, 0x29, 0x6a, 0x3a, 0x6d, 0xb5, 0xc0, 0x04, 0x08, + 0x3b, 0x68, 0xc5, 0x56, 0xc1, 0xf0, 0x23, 0x39, 0x91, 0x64, 0x19, 0x86, 0x4d, 0x50, 0xb7, 0x4d, + 0x40, 0xae, 0xca, 0x48, 0x4c, 0x77, 0x35, 0x6c, 0x89, 0x5a, 0x0c, 0x27, 0x5a, 0xbf, 0xac, 0x49, + 0x9d, 0x5d, 0x7d, 0x23, 0x62, 0xf2, 0x9c, 0x5e, 0x02, 0xe8, 0x71, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x81, 0x81, 0x00, 0xbe, 0x86, 0x0b, 0x0b, 0x99, 0xa8, 0x02, 0xa6, 0xfb, 0x1a, 0x59, 0x43, + 0x8a, 0x7b, 0xb7, 0x15, 0x06, 0x5b, 0x09, 0xa3, 0x6d, 0xc6, 0xe9, 0xca, 0xcc, 0x6b, 0xf3, 0xc0, + 0x2c, 0x34, 0xd7, 0xd7, 0x9e, 0x94, 0xc6, 0x60, 0x64, 0x28, 0xd8, 0x8c, 0x7b, 0x7f, 0x65, 0x77, + 0xc1, 0xcd, 0xea, 0x64, 0x07, 0x4a, 0xbe, 0x8e, 0x72, 0x86, 0xdf, 0x1f, 0x08, 0x11, 0xdc, 0x97, + 0x28, 0x26, 0x08, 0x68, 0xde, 0x95, 0xd3, 0x2e, 0xfc, 0x96, 0xb6, 0xd0, 0x84, 0xff, 0x27, 0x1a, + 0x5f, 0x60, 0xde, 0xfc, 0xc7, 0x03, 0xe7, 0xa3, 0x8e, 0x6e, 0x29, 0xba, 0x9a, 0x3c, 0x5f, 0xc2, + 0xc2, 0x80, 0x76, 0xb6, 0xa8, 0x96, 0xaf, 0x1d, 0x34, 0xd7, 0x88, 0x28, 0xce, 0x9b, 0xdd, 0xb1, + 0xf3, 0x4f, 0x9c, 0x94, 0x04, 0x43, 0x07, 0x81, 0x29, 0x8e, 0x20, 0x13, 0x16, 0x72, 0x5b, 0xbd, + 0xbc, 0x99, 0x3a, 0x41, 0x02, 0x41, 0x00, 0xe1, 0xc6, 0xd9, 0x27, 0x64, 0x6c, 0x09, 0x16, 0xec, + 0x36, 0x82, 0x6d, 0x59, 0x49, 0x83, 0x74, 0x0c, 0x21, 0xf1, 0xb0, 0x74, 0xc4, 0xa1, 0xa5, 0x98, + 0x67, 0xc6, 0x69, 0x79, 0x5c, 0x85, 0xd3, 0xdc, 0x46, 0x4c, 0x5b, 0x92, 0x9e, 0x94, 0xbf, 0xb3, + 0x4e, 0x0d, 0xcc, 0x50, 0x14, 0xb1, 0x0f, 0x13, 0x34, 0x1a, 0xb7, 0xfd, 0xd5, 0xf6, 0x04, 0x14, + 0xd2, 0xa3, 0x26, 0xca, 0xd4, 0x1c, 0xc5, 0x02, 0x41, 0x00, 0xda, 0x48, 0x59, 0x97, 0x78, 0x5c, + 0xd5, 0x63, 0x0f, 0xb0, 0xfd, 0x8c, 0x52, 0x54, 0xf9, 0x8e, 0x53, 0x8e, 0x18, 0x98, 0x3a, 0xae, + 0x9e, 0x6b, 0x7e, 0x6a, 0x5a, 0x7b, 0x5d, 0x34, 0x37, 0x55, 0xb9, 0x21, 0x8e, 0xbd, 0x40, 0x32, + 0x0d, 0x28, 0x38, 0x7d, 0x78, 0x9f, 0x76, 0xfa, 0x21, 0x8b, 0xcc, 0x2d, 0x8b, 0x68, 0xa5, 0xf6, + 0x41, 0x8f, 0xbb, 0xec, 0xa5, 0x17, 0x9a, 0xb3, 0xaf, 0xbd, 0x02, 0x40, 0x50, 0xfe, 0xfc, 0x32, + 0x64, 0x95, 0x59, 0x61, 0x6e, 0xd6, 0x53, 0x4e, 0x15, 0x45, 0x09, 0x32, 0x9d, 0x93, 0xa3, 0xd8, + 0x10, 0xdb, 0xe5, 0xbd, 0xb9, 0x82, 0x29, 0x2c, 0xf7, 0x8b, 0xd8, 0xba, 0xdb, 0x80, 0x20, 0xae, + 0x8d, 0x57, 0xf4, 0xb7, 0x1d, 0x05, 0x38, 0x6f, 0xfe, 0x9e, 0x9d, 0xb2, 0x71, 0xca, 0x34, 0x77, + 0xa3, 0x49, 0x99, 0xdb, 0x76, 0xf8, 0xe5, 0xec, 0xe9, 0xc0, 0xd4, 0x9d, 0x02, 0x40, 0x15, 0xb7, + 0x4c, 0xf2, 0x7c, 0xce, 0xff, 0x8b, 0xb3, 0x6b, 0xf0, 0x4d, 0x9d, 0x83, 0x46, 0xb0, 0x9a, 0x2f, + 0x70, 0xd2, 0xf4, 0x43, 0x9b, 0x0f, 0x26, 0xac, 0x7e, 0x03, 0xf7, 0xe9, 0xd1, 0xf7, 0x7d, 0x4b, + 0x91, 0x5f, 0xd2, 0x9b, 0x28, 0x23, 0xf0, 0x3a, 0xcb, 0x5d, 0x52, 0x00, 0xe0, 0x85, 0x7f, 0xf2, + 0xa8, 0x03, 0xe9, 0x3e, 0xee, 0x96, 0xd6, 0x23, 0x5c, 0xe9, 0x54, 0x42, 0xbc, 0x21, 0x02, 0x41, + 0x00, 0x90, 0xa7, 0x45, 0xda, 0x89, 0x70, 0xb2, 0xcd, 0x64, 0x96, 0x60, 0x32, 0x42, 0x28, 0xc5, + 0xf8, 0x28, 0x56, 0xff, 0xd6, 0x65, 0xba, 0x9a, 0x85, 0xc8, 0xd6, 0x0f, 0x1b, 0x8b, 0xee, 0x71, + 0x7e, 0xcd, 0x2c, 0x72, 0xea, 0xe0, 0x1d, 0xad, 0x86, 0xba, 0x76, 0x54, 0xd4, 0xcf, 0x45, 0xad, + 0xb5, 0xf1, 0xf2, 0xb3, 0x1d, 0x9f, 0x81, 0x22, 0xcf, 0xa5, 0xf1, 0xa5, 0x57, 0x0f, 0x9b, 0x2d, + 0x25, +}; + +static uint8_t kRsaAttestCert[] = { + 0x30, 0x82, 0x02, 0xb6, 0x30, 0x82, 0x02, 0x1f, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x10, + 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x30, 0x63, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, + 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0d, 0x4d, + 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x15, 0x30, 0x13, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x07, 0x41, 0x6e, + 0x64, 0x72, 0x6f, 0x69, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x31, 0x30, 0x34, 0x31, + 0x32, 0x34, 0x30, 0x35, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x35, 0x31, 0x32, 0x33, 0x30, 0x31, 0x32, + 0x34, 0x30, 0x35, 0x33, 0x5a, 0x30, 0x76, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x43, + 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x0c, 0x0c, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x07, 0x41, 0x6e, 0x64, 0x72, 0x6f, + 0x69, 0x64, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x20, 0x41, 0x6e, 0x64, + 0x72, 0x6f, 0x69, 0x64, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x41, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x81, 0x9f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xc0, 0x83, 0x23, 0xdc, 0x56, 0x88, + 0x1b, 0xb8, 0x30, 0x20, 0x69, 0xf5, 0xb0, 0x85, 0x61, 0xc6, 0xee, 0xbe, 0x7f, 0x05, 0xe2, 0xf5, + 0xa8, 0x42, 0x04, 0x8a, 0xbe, 0x8b, 0x47, 0xbe, 0x76, 0xfe, 0xae, 0xf2, 0x5c, 0xf2, 0x9b, 0x2a, + 0xfa, 0x32, 0x00, 0x14, 0x16, 0x01, 0x42, 0x99, 0x89, 0xa1, 0x5f, 0xcf, 0xc6, 0x81, 0x5e, 0xb3, + 0x63, 0x58, 0x3c, 0x2f, 0xd2, 0xf2, 0x0b, 0xe4, 0x98, 0x32, 0x83, 0xdd, 0x81, 0x4b, 0x16, 0xd7, + 0xe1, 0x85, 0x41, 0x7a, 0xe5, 0x4a, 0xbc, 0x29, 0x6a, 0x3a, 0x6d, 0xb5, 0xc0, 0x04, 0x08, 0x3b, + 0x68, 0xc5, 0x56, 0xc1, 0xf0, 0x23, 0x39, 0x91, 0x64, 0x19, 0x86, 0x4d, 0x50, 0xb7, 0x4d, 0x40, + 0xae, 0xca, 0x48, 0x4c, 0x77, 0x35, 0x6c, 0x89, 0x5a, 0x0c, 0x27, 0x5a, 0xbf, 0xac, 0x49, 0x9d, + 0x5d, 0x7d, 0x23, 0x62, 0xf2, 0x9c, 0x5e, 0x02, 0xe8, 0x71, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x66, 0x30, 0x64, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xd4, 0x0c, + 0x10, 0x1b, 0xf8, 0xcd, 0x63, 0xb9, 0xf7, 0x39, 0x52, 0xb5, 0x0e, 0x13, 0x5c, 0xa6, 0xd7, 0x99, + 0x93, 0x86, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x29, + 0xfa, 0xf1, 0xac, 0xcc, 0x4d, 0xd2, 0x4c, 0x96, 0x40, 0x27, 0x75, 0xb6, 0xb0, 0xe9, 0x32, 0xe5, + 0x07, 0xfe, 0x2e, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, + 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x9e, 0x2d, 0x48, 0x5f, 0x8c, 0x67, + 0x33, 0xdc, 0x1a, 0x85, 0xad, 0x99, 0xd7, 0x50, 0x23, 0xea, 0x14, 0xec, 0x43, 0xb0, 0xe1, 0x9d, + 0xea, 0xc2, 0x23, 0x46, 0x1e, 0x72, 0xb5, 0x19, 0xdc, 0x60, 0x22, 0xe4, 0xa5, 0x68, 0x31, 0x6c, + 0x0b, 0x55, 0xc4, 0xe6, 0x9c, 0xa2, 0x2d, 0x9f, 0x3a, 0x4f, 0x93, 0x6b, 0x31, 0x8b, 0x16, 0x78, + 0x16, 0x0d, 0x88, 0xcb, 0xd9, 0x8b, 0xcc, 0x80, 0x9d, 0x84, 0xf0, 0xc2, 0x27, 0xe3, 0x6b, 0x38, + 0xf1, 0xfd, 0xd1, 0xe7, 0x17, 0x72, 0x31, 0x59, 0x35, 0x7d, 0x96, 0xf3, 0xc5, 0x7f, 0xab, 0x9d, + 0x8f, 0x96, 0x61, 0x26, 0x4f, 0xb2, 0xbe, 0x81, 0xbb, 0x0d, 0x49, 0x04, 0x22, 0x8a, 0xce, 0x9f, + 0xf7, 0xf5, 0x42, 0x2e, 0x25, 0x44, 0xfa, 0x21, 0x07, 0x12, 0x5a, 0x83, 0xb5, 0x55, 0xad, 0x18, + 0x82, 0xf8, 0x40, 0x14, 0x9b, 0x9c, 0x20, 0x63, 0x04, 0x7f, +}; + +static uint8_t kRsaAttestRootCert[] = { + 0x30, 0x82, 0x02, 0xa7, 0x30, 0x82, 0x02, 0x10, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0xff, 0x94, 0xd9, 0xdd, 0x9f, 0x07, 0xc8, 0x0c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x63, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0d, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, + 0x69, 0x65, 0x77, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x47, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x0c, 0x07, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x36, 0x30, 0x31, 0x30, 0x34, 0x31, 0x32, 0x33, 0x31, 0x30, 0x38, 0x5a, 0x17, 0x0d, 0x33, + 0x35, 0x31, 0x32, 0x33, 0x30, 0x31, 0x32, 0x33, 0x31, 0x30, 0x38, 0x5a, 0x30, 0x63, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0d, 0x4d, 0x6f, 0x75, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x0c, 0x0c, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x07, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xa2, 0x6b, + 0xad, 0xeb, 0x6e, 0x2e, 0x44, 0x61, 0xef, 0xd5, 0x0e, 0x82, 0xe6, 0xb7, 0x94, 0xd1, 0x75, 0x23, + 0x1f, 0x77, 0x9b, 0x63, 0x91, 0x63, 0xff, 0xf7, 0xaa, 0xff, 0x0b, 0x72, 0x47, 0x4e, 0xc0, 0x2c, + 0x43, 0xec, 0x33, 0x7c, 0xd7, 0xac, 0xed, 0x40, 0x3e, 0x8c, 0x28, 0xa0, 0x66, 0xd5, 0xf7, 0x87, + 0x0b, 0x33, 0x97, 0xde, 0x0e, 0xb8, 0x4e, 0x13, 0x40, 0xab, 0xaf, 0xa5, 0x27, 0xbf, 0x95, 0x69, + 0xa0, 0x31, 0xdb, 0x06, 0x52, 0x65, 0xf8, 0x44, 0x59, 0x57, 0x61, 0xf0, 0xbb, 0xf2, 0x17, 0x4b, + 0xb7, 0x41, 0x80, 0x64, 0xc0, 0x28, 0x0e, 0x8f, 0x52, 0x77, 0x8e, 0xdb, 0xd2, 0x47, 0xb6, 0x45, + 0xe9, 0x19, 0xc8, 0xe9, 0x8b, 0xc3, 0xdb, 0xc2, 0x91, 0x3f, 0xd7, 0xd7, 0x50, 0xc4, 0x1d, 0x35, + 0x66, 0xf9, 0x57, 0xe4, 0x97, 0x96, 0x0b, 0x09, 0xac, 0xce, 0x92, 0x35, 0x85, 0x9b, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0x29, 0xfa, 0xf1, 0xac, 0xcc, 0x4d, 0xd2, 0x4c, 0x96, 0x40, 0x27, 0x75, 0xb6, 0xb0, + 0xe9, 0x32, 0xe5, 0x07, 0xfe, 0x2e, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x29, 0xfa, 0xf1, 0xac, 0xcc, 0x4d, 0xd2, 0x4c, 0x96, 0x40, 0x27, 0x75, 0xb6, + 0xb0, 0xe9, 0x32, 0xe5, 0x07, 0xfe, 0x2e, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, + 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, + 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x4f, 0x72, 0xf3, 0x36, 0x59, + 0x8d, 0x0e, 0xc1, 0xb9, 0x74, 0x5b, 0x31, 0x59, 0xf6, 0xf0, 0x8d, 0x25, 0x49, 0x30, 0x9e, 0xa3, + 0x1c, 0x1c, 0x29, 0xd2, 0x45, 0x2d, 0x20, 0xb9, 0x4d, 0x5f, 0x64, 0xb4, 0xe8, 0x80, 0xc7, 0x78, + 0x7a, 0x9c, 0x39, 0xde, 0xa8, 0xb3, 0xf5, 0xbf, 0x2f, 0x70, 0x5f, 0x47, 0x10, 0x5c, 0xc5, 0xe6, + 0xeb, 0x4d, 0x06, 0x99, 0x61, 0xd2, 0xae, 0x9a, 0x07, 0xff, 0xf7, 0x7c, 0xb8, 0xab, 0xeb, 0x9c, + 0x0f, 0x24, 0x07, 0x5e, 0xb1, 0x7f, 0xba, 0x79, 0x71, 0xfd, 0x4d, 0x5b, 0x9e, 0xdf, 0x14, 0xa9, + 0xfe, 0xdf, 0xed, 0x7c, 0xc0, 0x88, 0x5d, 0xf8, 0xdd, 0x9b, 0x64, 0x32, 0x56, 0xd5, 0x35, 0x9a, + 0xe2, 0x13, 0xf9, 0x8f, 0xce, 0xc1, 0x7c, 0xdc, 0xef, 0xa4, 0xaa, 0xb2, 0x55, 0xc3, 0x83, 0xa9, + 0x2e, 0xfb, 0x5c, 0xf6, 0x62, 0xf5, 0x27, 0x52, 0x17, 0xbe, 0x63, +}; + +static uint8_t kEcAttestKey[] = { + 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x21, 0xe0, 0x86, 0x43, 0x2a, 0x15, 0x19, 0x84, 0x59, + 0xcf, 0x36, 0x3a, 0x50, 0xfc, 0x14, 0xc9, 0xda, 0xad, 0xf9, 0x35, 0xf5, 0x27, 0xc2, 0xdf, 0xd7, + 0x1e, 0x4d, 0x6d, 0xbc, 0x42, 0xe5, 0x44, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xeb, 0x9e, 0x79, 0xf8, 0x42, 0x63, 0x59, + 0xac, 0xcb, 0x2a, 0x91, 0x4c, 0x89, 0x86, 0xcc, 0x70, 0xad, 0x90, 0x66, 0x93, 0x82, 0xa9, 0x73, + 0x26, 0x13, 0xfe, 0xac, 0xcb, 0xf8, 0x21, 0x27, 0x4c, 0x21, 0x74, 0x97, 0x4a, 0x2a, 0xfe, 0xa5, + 0xb9, 0x4d, 0x7f, 0x66, 0xd4, 0xe0, 0x65, 0x10, 0x66, 0x35, 0xbc, 0x53, 0xb7, 0xa0, 0xa3, 0xa6, + 0x71, 0x58, 0x3e, 0xdb, 0x3e, 0x11, 0xae, 0x10, 0x14, +}; + +static uint8_t kEcAttestCert[] = { + 0x30, 0x82, 0x02, 0x78, 0x30, 0x82, 0x02, 0x1e, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x10, + 0x01, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x81, 0x98, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, + 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0d, 0x4d, 0x6f, 0x75, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x07, 0x41, 0x6e, 0x64, 0x72, + 0x6f, 0x69, 0x64, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2a, 0x41, 0x6e, + 0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x53, + 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x31, + 0x31, 0x31, 0x30, 0x30, 0x34, 0x36, 0x30, 0x39, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x30, 0x31, 0x30, + 0x38, 0x30, 0x30, 0x34, 0x36, 0x30, 0x39, 0x5a, 0x30, 0x81, 0x88, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x0c, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x15, 0x30, + 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x07, 0x41, + 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x32, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, + 0x61, 0x74, 0x65, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, + 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xeb, 0x9e, + 0x79, 0xf8, 0x42, 0x63, 0x59, 0xac, 0xcb, 0x2a, 0x91, 0x4c, 0x89, 0x86, 0xcc, 0x70, 0xad, 0x90, + 0x66, 0x93, 0x82, 0xa9, 0x73, 0x26, 0x13, 0xfe, 0xac, 0xcb, 0xf8, 0x21, 0x27, 0x4c, 0x21, 0x74, + 0x97, 0x4a, 0x2a, 0xfe, 0xa5, 0xb9, 0x4d, 0x7f, 0x66, 0xd4, 0xe0, 0x65, 0x10, 0x66, 0x35, 0xbc, + 0x53, 0xb7, 0xa0, 0xa3, 0xa6, 0x71, 0x58, 0x3e, 0xdb, 0x3e, 0x11, 0xae, 0x10, 0x14, 0xa3, 0x66, + 0x30, 0x64, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3f, 0xfc, 0xac, + 0xd6, 0x1a, 0xb1, 0x3a, 0x9e, 0x81, 0x20, 0xb8, 0xd5, 0x25, 0x1c, 0xc5, 0x65, 0xbb, 0x1e, 0x91, + 0xa9, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc8, 0xad, + 0xe9, 0x77, 0x4c, 0x45, 0xc3, 0xa3, 0xcf, 0x0d, 0x16, 0x10, 0xe4, 0x79, 0x43, 0x3a, 0x21, 0x5a, + 0x30, 0xcf, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, + 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, + 0x04, 0x04, 0x03, 0x02, 0x02, 0x84, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, + 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x4b, 0x8a, 0x9b, 0x7b, 0xee, 0x82, 0xbc, + 0xc0, 0x33, 0x87, 0xae, 0x2f, 0xc0, 0x89, 0x98, 0xb4, 0xdd, 0xc3, 0x8d, 0xab, 0x27, 0x2a, 0x45, + 0x9f, 0x69, 0x0c, 0xc7, 0xc3, 0x92, 0xd4, 0x0f, 0x8e, 0x02, 0x21, 0x00, 0xee, 0xda, 0x01, 0x5d, + 0xb6, 0xf4, 0x32, 0xe9, 0xd4, 0x84, 0x3b, 0x62, 0x4c, 0x94, 0x04, 0xef, 0x3a, 0x7c, 0xcc, 0xbd, + 0x5e, 0xfb, 0x22, 0xbb, 0xe7, 0xfe, 0xb9, 0x77, 0x3f, 0x59, 0x3f, 0xfb, +}; + +static uint8_t kEcAttestRootCert[] = { + 0x30, 0x82, 0x02, 0x8b, 0x30, 0x82, 0x02, 0x32, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0xa2, 0x05, 0x9e, 0xd1, 0x0e, 0x43, 0x5b, 0x57, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, + 0x3d, 0x04, 0x03, 0x02, 0x30, 0x81, 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x43, + 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x0c, 0x0d, 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, + 0x77, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x0c, 0x07, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x2a, 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4b, 0x65, 0x79, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x41, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x31, 0x31, 0x31, 0x30, 0x30, 0x34, 0x33, 0x35, 0x30, 0x5a, + 0x17, 0x0d, 0x33, 0x36, 0x30, 0x31, 0x30, 0x36, 0x30, 0x30, 0x34, 0x33, 0x35, 0x30, 0x5a, 0x30, + 0x81, 0x98, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, + 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0d, 0x4d, + 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x15, 0x30, 0x13, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x07, 0x41, 0x6e, + 0x64, 0x72, 0x6f, 0x69, 0x64, 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2a, + 0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4b, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, + 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, + 0x07, 0x03, 0x42, 0x00, 0x04, 0xee, 0x5d, 0x5e, 0xc7, 0xe1, 0xc0, 0xdb, 0x6d, 0x03, 0xa6, 0x7e, + 0xe6, 0xb6, 0x1b, 0xec, 0x4d, 0x6a, 0x5d, 0x6a, 0x68, 0x2e, 0x0f, 0xff, 0x7f, 0x49, 0x0e, 0x7d, + 0x77, 0x1f, 0x44, 0x22, 0x6d, 0xbd, 0xb1, 0xaf, 0xfa, 0x16, 0xcb, 0xc7, 0xad, 0xc5, 0x77, 0xd2, + 0x56, 0x9c, 0xaa, 0xb7, 0xb0, 0x2d, 0x54, 0x01, 0x5d, 0x3e, 0x43, 0x2b, 0x2a, 0x8e, 0xd7, 0x4e, + 0xec, 0x48, 0x75, 0x41, 0xa4, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xc8, 0xad, 0xe9, 0x77, 0x4c, 0x45, 0xc3, 0xa3, 0xcf, 0x0d, 0x16, 0x10, + 0xe4, 0x79, 0x43, 0x3a, 0x21, 0x5a, 0x30, 0xcf, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0xc8, 0xad, 0xe9, 0x77, 0x4c, 0x45, 0xc3, 0xa3, 0xcf, 0x0d, 0x16, + 0x10, 0xe4, 0x79, 0x43, 0x3a, 0x21, 0x5a, 0x30, 0xcf, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x84, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x35, 0x21, 0xa3, + 0xef, 0x8b, 0x34, 0x46, 0x1e, 0x9c, 0xd5, 0x60, 0xf3, 0x1d, 0x58, 0x89, 0x20, 0x6a, 0xdc, 0xa3, + 0x65, 0x41, 0xf6, 0x0d, 0x9e, 0xce, 0x8a, 0x19, 0x8c, 0x66, 0x48, 0x60, 0x7b, 0x02, 0x20, 0x4d, + 0x0b, 0xf3, 0x51, 0xd9, 0x30, 0x7c, 0x7d, 0x5b, 0xda, 0x35, 0x34, 0x1d, 0xa8, 0x47, 0x1b, 0x63, + 0xa5, 0x85, 0x65, 0x3c, 0xad, 0x4f, 0x24, 0xa7, 0xe7, 0x4d, 0xaf, 0x41, 0x7d, 0xf1, 0xbf, +}; + +size_t kCertificateChainLength = 2; + +} // anonymous namespace + +SoftKeymasterContext::SoftKeymasterContext(const std::string& root_of_trust) + : rsa_factory_(new RsaKeyFactory(this)), ec_factory_(new EcKeyFactory(this)), + aes_factory_(new AesKeyFactory(this)), hmac_factory_(new HmacKeyFactory(this)), + km1_dev_(nullptr), root_of_trust_(root_of_trust) {} + +SoftKeymasterContext::~SoftKeymasterContext() {} + +keymaster_error_t SoftKeymasterContext::SetHardwareDevice(keymaster0_device_t* keymaster0_device) { + if (!keymaster0_device) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if ((keymaster0_device->flags & KEYMASTER_SOFTWARE_ONLY) != 0) { + LOG_E("SoftKeymasterContext only wraps hardware keymaster0 devices", 0); + return KM_ERROR_INVALID_ARGUMENT; + } + + km0_engine_.reset(new Keymaster0Engine(keymaster0_device)); + rsa_factory_.reset(new RsaKeymaster0KeyFactory(this, km0_engine_.get())); + ec_factory_.reset(new EcdsaKeymaster0KeyFactory(this, km0_engine_.get())); + // Keep AES and HMAC factories. + + return KM_ERROR_OK; +} + +keymaster_error_t SoftKeymasterContext::SetHardwareDevice(keymaster1_device_t* keymaster1_device) { + if (!keymaster1_device) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + km1_dev_ = keymaster1_device; + + km1_engine_.reset(new Keymaster1Engine(keymaster1_device)); + rsa_factory_.reset(new RsaKeymaster1KeyFactory(this, km1_engine_.get())); + ec_factory_.reset(new EcdsaKeymaster1KeyFactory(this, km1_engine_.get())); + + // All AES and HMAC operations should be passed directly to the keymaster1 device. Explicitly + // do not handle them, to provoke errors in case the higher layers fail to send them to the + // device. + aes_factory_.reset(nullptr); + hmac_factory_.reset(nullptr); + + return KM_ERROR_OK; +} + +KeyFactory* SoftKeymasterContext::GetKeyFactory(keymaster_algorithm_t algorithm) const { + switch (algorithm) { + case KM_ALGORITHM_RSA: + return rsa_factory_.get(); + case KM_ALGORITHM_EC: + return ec_factory_.get(); + case KM_ALGORITHM_AES: + return aes_factory_.get(); + case KM_ALGORITHM_HMAC: + return hmac_factory_.get(); + default: + return nullptr; + } +} + +static keymaster_algorithm_t supported_algorithms[] = {KM_ALGORITHM_RSA, KM_ALGORITHM_EC, + KM_ALGORITHM_AES, KM_ALGORITHM_HMAC}; + +keymaster_algorithm_t* +SoftKeymasterContext::GetSupportedAlgorithms(size_t* algorithms_count) const { + *algorithms_count = array_length(supported_algorithms); + return supported_algorithms; +} + +OperationFactory* SoftKeymasterContext::GetOperationFactory(keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose) const { + KeyFactory* key_factory = GetKeyFactory(algorithm); + if (!key_factory) + return nullptr; + return key_factory->GetOperationFactory(purpose); +} + +static keymaster_error_t TranslateAuthorizationSetError(AuthorizationSet::Error err) { + switch (err) { + case AuthorizationSet::OK: + return KM_ERROR_OK; + case AuthorizationSet::ALLOCATION_FAILURE: + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + case AuthorizationSet::MALFORMED_DATA: + return KM_ERROR_UNKNOWN_ERROR; + } + return KM_ERROR_OK; +} + +static keymaster_error_t SetAuthorizations(const AuthorizationSet& key_description, + keymaster_key_origin_t origin, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) { + sw_enforced->Clear(); + + for (auto& entry : key_description) { + switch (entry.tag) { + // These cannot be specified by the client. + case KM_TAG_ROOT_OF_TRUST: + case KM_TAG_ORIGIN: + LOG_E("Root of trust and origin tags may not be specified", 0); + return KM_ERROR_INVALID_TAG; + + // These don't work. + case KM_TAG_ROLLBACK_RESISTANT: + LOG_E("KM_TAG_ROLLBACK_RESISTANT not supported", 0); + return KM_ERROR_UNSUPPORTED_TAG; + + // These are hidden. + case KM_TAG_APPLICATION_ID: + case KM_TAG_APPLICATION_DATA: + break; + + // Everything else we just copy into sw_enforced, unless the KeyFactory has placed it in + // hw_enforced, in which case we defer to its decision. + default: + if (hw_enforced->GetTagCount(entry.tag) == 0) + sw_enforced->push_back(entry); + break; + } + } + + sw_enforced->push_back(TAG_CREATION_DATETIME, java_time(time(NULL))); + sw_enforced->push_back(TAG_ORIGIN, origin); + return TranslateAuthorizationSetError(sw_enforced->is_valid()); +} + +keymaster_error_t SoftKeymasterContext::CreateKeyBlob(const AuthorizationSet& key_description, + const keymaster_key_origin_t origin, + const KeymasterKeyBlob& key_material, + KeymasterKeyBlob* blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + keymaster_error_t error = SetAuthorizations(key_description, origin, hw_enforced, sw_enforced); + if (error != KM_ERROR_OK) + return error; + + AuthorizationSet hidden; + error = BuildHiddenAuthorizations(key_description, &hidden); + if (error != KM_ERROR_OK) + return error; + + return SerializeIntegrityAssuredBlob(key_material, hidden, *hw_enforced, *sw_enforced, blob); +} + +static keymaster_error_t ParseOcbAuthEncryptedBlob(const KeymasterKeyBlob& blob, + const AuthorizationSet& hidden, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) { + Buffer nonce, tag; + KeymasterKeyBlob encrypted_key_material; + keymaster_error_t error = DeserializeAuthEncryptedBlob(blob, &encrypted_key_material, + hw_enforced, sw_enforced, &nonce, &tag); + if (error != KM_ERROR_OK) + return error; + + if (nonce.available_read() != OCB_NONCE_LENGTH || tag.available_read() != OCB_TAG_LENGTH) + return KM_ERROR_INVALID_KEY_BLOB; + + return OcbDecryptKey(*hw_enforced, *sw_enforced, hidden, MASTER_KEY, encrypted_key_material, + nonce, tag, key_material); +} + +// Note: This parsing code in below is from system/security/softkeymaster/keymaster_openssl.cpp's +// unwrap_key function, modified for the preferred function signature and formatting. It does some +// odd things, but they have been left unchanged to avoid breaking compatibility. +static const uint8_t SOFT_KEY_MAGIC[] = {'P', 'K', '#', '8'}; +keymaster_error_t SoftKeymasterContext::ParseOldSoftkeymasterBlob( + const KeymasterKeyBlob& blob, KeymasterKeyBlob* key_material, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + long publicLen = 0; + long privateLen = 0; + const uint8_t* p = blob.key_material; + const uint8_t* end = blob.key_material + blob.key_material_size; + + int type = 0; + ptrdiff_t min_size = + sizeof(SOFT_KEY_MAGIC) + sizeof(type) + sizeof(publicLen) + 1 + sizeof(privateLen) + 1; + if (end - p < min_size) { + LOG_W("key blob appears to be truncated (if an old SW key)", 0); + return KM_ERROR_INVALID_KEY_BLOB; + } + + if (memcmp(p, SOFT_KEY_MAGIC, sizeof(SOFT_KEY_MAGIC)) != 0) + return KM_ERROR_INVALID_KEY_BLOB; + p += sizeof(SOFT_KEY_MAGIC); + + for (size_t i = 0; i < sizeof(type); i++) + type = (type << 8) | *p++; + + for (size_t i = 0; i < sizeof(type); i++) + publicLen = (publicLen << 8) | *p++; + + if (p + publicLen > end) { + LOG_W("public key length encoding error: size=%ld, end=%td", publicLen, end - p); + return KM_ERROR_INVALID_KEY_BLOB; + } + p += publicLen; + + if (end - p < 2) { + LOG_W("key blob appears to be truncated (if an old SW key)", 0); + return KM_ERROR_INVALID_KEY_BLOB; + } + + for (size_t i = 0; i < sizeof(type); i++) + privateLen = (privateLen << 8) | *p++; + + if (p + privateLen > end) { + LOG_W("private key length encoding error: size=%ld, end=%td", privateLen, end - p); + return KM_ERROR_INVALID_KEY_BLOB; + } + + // Just to be sure, make sure that the ASN.1 structure parses correctly. We don't actually use + // the EVP_PKEY here. + const uint8_t* key_start = p; + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PrivateKey(type, nullptr, &p, privateLen)); + if (pkey.get() == nullptr) { + LOG_W("Failed to parse PKCS#8 key material (if old SW key)", 0); + return KM_ERROR_INVALID_KEY_BLOB; + } + + // All auths go into sw_enforced, including those that would be HW-enforced if we were faking + // auths for a HW-backed key. + hw_enforced->Clear(); + keymaster_error_t error = FakeKeyAuthorizations(pkey.get(), sw_enforced, sw_enforced); + if (error != KM_ERROR_OK) + return error; + + if (!key_material->Reset(privateLen)) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + memcpy(key_material->writable_data(), key_start, privateLen); + + return KM_ERROR_OK; +} + +keymaster_error_t SoftKeymasterContext::ParseKeyBlob(const KeymasterKeyBlob& blob, + const AuthorizationSet& additional_params, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + // This is a little bit complicated. + // + // The SoftKeymasterContext has to handle a lot of different kinds of key blobs. + // + // 1. New keymaster1 software key blobs. These are integrity-assured but not encrypted. The + // raw key material and auth sets should be extracted and returned. This is the kind + // produced by this context when the KeyFactory doesn't use keymaster0 to back the keys. + // + // 2. Old keymaster1 software key blobs. These are OCB-encrypted with an all-zero master key. + // They should be decrypted and the key material and auth sets extracted and returned. + // + // 3. Old keymaster0 software key blobs. These are raw key material with a small header tacked + // on the front. They don't have auth sets, so reasonable defaults are generated and + // returned along with the raw key material. + // + // 4. New keymaster0 hardware key blobs. These are integrity-assured but not encrypted (though + // they're protected by the keymaster0 hardware implementation). The keymaster0 key blob + // and auth sets should be extracted and returned. + // + // 5. Keymaster1 hardware key blobs. These are raw hardware key blobs. They contain auth + // sets, which we retrieve from the hardware module. + // + // 6. Old keymaster0 hardware key blobs. These are raw hardware key blobs. They don't have + // auth sets so reasonable defaults are generated and returned along with the key blob. + // + // Determining what kind of blob has arrived is somewhat tricky. What helps is that + // integrity-assured and OCB-encrypted blobs are self-consistent and effectively impossible to + // parse as anything else. Old keymaster0 software key blobs have a header. It's reasonably + // unlikely that hardware keys would have the same header. So anything that is neither + // integrity-assured nor OCB-encrypted and lacks the old software key header is assumed to be + // keymaster0 hardware. + + AuthorizationSet hidden; + keymaster_error_t error = BuildHiddenAuthorizations(additional_params, &hidden); + if (error != KM_ERROR_OK) + return error; + + // Assume it's an integrity-assured blob (new software-only blob, or new keymaster0-backed + // blob). + error = DeserializeIntegrityAssuredBlob(blob, hidden, key_material, hw_enforced, sw_enforced); + if (error != KM_ERROR_INVALID_KEY_BLOB) + return error; + + // Wasn't an integrity-assured blob. Maybe it's an OCB-encrypted blob. + error = ParseOcbAuthEncryptedBlob(blob, hidden, key_material, hw_enforced, sw_enforced); + if (error == KM_ERROR_OK) + LOG_D("Parsed an old keymaster1 software key", 0); + if (error != KM_ERROR_INVALID_KEY_BLOB) + return error; + + // Wasn't an OCB-encrypted blob. Maybe it's an old softkeymaster blob. + error = ParseOldSoftkeymasterBlob(blob, key_material, hw_enforced, sw_enforced); + if (error == KM_ERROR_OK) + LOG_D("Parsed an old sofkeymaster key", 0); + if (error != KM_ERROR_INVALID_KEY_BLOB) + return error; + + if (km1_dev_) + return ParseKeymaster1HwBlob(blob, additional_params, key_material, hw_enforced, + sw_enforced); + else if (km0_engine_) + return ParseKeymaster0HwBlob(blob, key_material, hw_enforced, sw_enforced); + + LOG_E("Failed to parse key; not a valid software blob, no hardware module configured", 0); + return KM_ERROR_INVALID_KEY_BLOB; +} + +keymaster_error_t SoftKeymasterContext::DeleteKey(const KeymasterKeyBlob& blob) const { + if (km1_engine_) { + keymaster_error_t error = km1_engine_->DeleteKey(blob); + if (error == KM_ERROR_INVALID_KEY_BLOB) { + // Note that we succeed on invalid blob, because it probably just indicates that the + // blob is a software blob, not a hardware blob. + error = KM_ERROR_OK; + } + return error; + } + + if (km0_engine_) { + // This could be a keymaster0 hardware key, and it could be either raw or encapsulated in an + // integrity-assured blob. If it's integrity-assured, we can't validate it strongly, + // because we don't have the necessary additional_params data. However, the probability + // that anything other than an integrity-assured blob would have all of the structure + // required to decode as a valid blob is low -- unless it's maliciously-constructed, but the + // deserializer should be proof against bad data, as should the keymaster0 hardware. + // + // Thus, we first try to parse it as integrity-assured. If that works, we pass the result + // to the underlying hardware. If not, we pass blob unmodified to the underlying hardware. + KeymasterKeyBlob key_material; + AuthorizationSet hw_enforced, sw_enforced; + keymaster_error_t error = DeserializeIntegrityAssuredBlob_NoHmacCheck( + blob, &key_material, &hw_enforced, &sw_enforced); + if (error == KM_ERROR_OK && km0_engine_->DeleteKey(key_material)) + return KM_ERROR_OK; + + km0_engine_->DeleteKey(blob); + + // We succeed unconditionally at this point, even if delete failed. Failure indicates that + // either the blob is a software blob (which we can't distinguish with certainty without + // additional_params) or because it is a hardware blob and the hardware failed. In the + // first case, there is no error. In the second case, the client can't do anything to fix + // it anyway, so it's not too harmful to simply swallow the error. This is not ideal, but + // it's the least-bad alternative. + return KM_ERROR_OK; + } + + // Nothing to do for software-only contexts. + return KM_ERROR_OK; +} + +keymaster_error_t SoftKeymasterContext::DeleteAllKeys() const { + if (km1_engine_) + return km1_engine_->DeleteAllKeys(); + + if (km0_engine_ && !km0_engine_->DeleteAllKeys()) + return KM_ERROR_UNKNOWN_ERROR; + + return KM_ERROR_OK; +} + +keymaster_error_t SoftKeymasterContext::AddRngEntropy(const uint8_t* buf, size_t length) const { + RAND_add(buf, length, 0 /* Don't assume any entropy is added to the pool. */); + return KM_ERROR_OK; +} + +keymaster_error_t SoftKeymasterContext::GenerateRandom(uint8_t* buf, size_t length) const { + if (RAND_bytes(buf, length) != 1) + return KM_ERROR_UNKNOWN_ERROR; + return KM_ERROR_OK; +} + +EVP_PKEY* SoftKeymasterContext::AttestationKey(keymaster_algorithm_t algorithm, + keymaster_error_t* error) const { + + const uint8_t* key; + size_t key_length; + int evp_key_type; + + switch (algorithm) { + case KM_ALGORITHM_RSA: + key = kRsaAttestKey; + key_length = array_length(kRsaAttestKey); + evp_key_type = EVP_PKEY_RSA; + break; + + case KM_ALGORITHM_EC: + key = kEcAttestKey; + key_length = array_length(kEcAttestKey); + evp_key_type = EVP_PKEY_EC; + break; + + default: + *error = KM_ERROR_UNSUPPORTED_ALGORITHM; + return nullptr; + } + + EVP_PKEY* pkey = d2i_PrivateKey(evp_key_type, nullptr /* pkey */, &key, key_length); + if (!pkey) + *error = TranslateLastOpenSslError(); + + return pkey; +} + +keymaster_cert_chain_t* SoftKeymasterContext::AttestationChain(keymaster_algorithm_t algorithm, + keymaster_error_t* error) const { + // If we have to bail it will be because of an allocation failure. + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + + UniquePtr<keymaster_cert_chain_t, CertificateChainDelete> chain(new keymaster_cert_chain_t); + if (!chain.get()) + return nullptr; + memset(chain.get(), 0, sizeof(keymaster_cert_chain_t)); + + chain->entries = new keymaster_blob_t[kCertificateChainLength]; + if (!chain->entries) + return nullptr; + + memset(chain->entries, 0, sizeof(chain->entries[0]) * kCertificateChainLength); + chain->entry_count = kCertificateChainLength; + + size_t entry = 0; + + switch (algorithm) { + case KM_ALGORITHM_RSA: + chain->entries[entry].data = dup_array(kRsaAttestCert); + if (!chain->entries[entry].data) + return nullptr; + chain->entries[entry].data_length = array_length(kRsaAttestCert); + entry++; + chain->entries[entry].data = dup_array(kRsaAttestRootCert); + if (!chain->entries[entry].data) + return nullptr; + chain->entries[entry].data_length = array_length(kRsaAttestRootCert); + entry++; + break; + + case KM_ALGORITHM_EC: + chain->entries[entry].data = dup_array(kEcAttestCert); + if (!chain->entries[entry].data) + return nullptr; + chain->entries[entry].data_length = array_length(kEcAttestCert); + entry++; + chain->entries[entry].data = dup_array(kEcAttestRootCert); + if (!chain->entries[entry].data) + return nullptr; + chain->entries[entry].data_length = array_length(kEcAttestRootCert); + entry++; + break; + + default: + *error = KM_ERROR_UNSUPPORTED_ALGORITHM; + return nullptr; + }; + + assert(entry == kCertificateChainLength); + + *error = KM_ERROR_OK; + return chain.release(); +} + +keymaster_error_t SoftKeymasterContext::ParseKeymaster1HwBlob( + const KeymasterKeyBlob& blob, const AuthorizationSet& additional_params, + KeymasterKeyBlob* key_material, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + assert(km1_dev_); + + keymaster_blob_t client_id = {nullptr, 0}; + keymaster_blob_t app_data = {nullptr, 0}; + keymaster_blob_t* client_id_ptr = nullptr; + keymaster_blob_t* app_data_ptr = nullptr; + if (additional_params.GetTagValue(TAG_APPLICATION_ID, &client_id)) + client_id_ptr = &client_id; + if (additional_params.GetTagValue(TAG_APPLICATION_DATA, &app_data)) + app_data_ptr = &app_data; + + // Get key characteristics, which incidentally verifies that the HW recognizes the key. + keymaster_key_characteristics_t* characteristics; + keymaster_error_t error = km1_dev_->get_key_characteristics(km1_dev_, &blob, client_id_ptr, + app_data_ptr, &characteristics); + if (error != KM_ERROR_OK) + return error; + unique_ptr<keymaster_key_characteristics_t, Characteristics_Delete> characteristics_deleter( + characteristics); + + LOG_D("Module \"%s\" accepted key", km1_dev_->common.module->name); + + hw_enforced->Reinitialize(characteristics->hw_enforced); + sw_enforced->Reinitialize(characteristics->sw_enforced); + *key_material = blob; + return KM_ERROR_OK; +} + +keymaster_error_t SoftKeymasterContext::ParseKeymaster0HwBlob(const KeymasterKeyBlob& blob, + KeymasterKeyBlob* key_material, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + assert(km0_engine_); + + unique_ptr<EVP_PKEY, EVP_PKEY_Delete> tmp_key(km0_engine_->GetKeymaster0PublicKey(blob)); + + if (!tmp_key) + return KM_ERROR_INVALID_KEY_BLOB; + + LOG_D("Module \"%s\" accepted key", km0_engine_->device()->common.module->name); + keymaster_error_t error = FakeKeyAuthorizations(tmp_key.get(), hw_enforced, sw_enforced); + if (error == KM_ERROR_OK) + *key_material = blob; + + return error; +} + +keymaster_error_t SoftKeymasterContext::FakeKeyAuthorizations(EVP_PKEY* pubkey, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + hw_enforced->Clear(); + sw_enforced->Clear(); + + switch (EVP_PKEY_type(pubkey->type)) { + case EVP_PKEY_RSA: { + hw_enforced->push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_NONE); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_MD5); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA1); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_224); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_384); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_512); + hw_enforced->push_back(TAG_PADDING, KM_PAD_NONE); + hw_enforced->push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN); + hw_enforced->push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + hw_enforced->push_back(TAG_PADDING, KM_PAD_RSA_PSS); + hw_enforced->push_back(TAG_PADDING, KM_PAD_RSA_OAEP); + + sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN); + sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY); + sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_ENCRYPT); + sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_DECRYPT); + + unique_ptr<RSA, RSA_Delete> rsa(EVP_PKEY_get1_RSA(pubkey)); + if (!rsa) + return TranslateLastOpenSslError(); + hw_enforced->push_back(TAG_KEY_SIZE, RSA_size(rsa.get()) * 8); + uint64_t public_exponent = BN_get_word(rsa->e); + if (public_exponent == 0xffffffffL) + return KM_ERROR_INVALID_KEY_BLOB; + hw_enforced->push_back(TAG_RSA_PUBLIC_EXPONENT, public_exponent); + break; + } + + case EVP_PKEY_EC: { + hw_enforced->push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_NONE); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_MD5); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA1); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_224); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_384); + hw_enforced->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_512); + + sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN); + sw_enforced->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY); + + UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(EVP_PKEY_get1_EC_KEY(pubkey)); + if (!ec_key.get()) + return TranslateLastOpenSslError(); + size_t key_size_bits; + keymaster_error_t error = + ec_get_group_size(EC_KEY_get0_group(ec_key.get()), &key_size_bits); + if (error != KM_ERROR_OK) + return error; + hw_enforced->push_back(TAG_KEY_SIZE, key_size_bits); + break; + } + + default: + return KM_ERROR_UNSUPPORTED_ALGORITHM; + } + + sw_enforced->push_back(TAG_ALL_USERS); + sw_enforced->push_back(TAG_NO_AUTH_REQUIRED); + + return KM_ERROR_OK; +} + +keymaster_error_t SoftKeymasterContext::BuildHiddenAuthorizations(const AuthorizationSet& input_set, + AuthorizationSet* hidden) const { + keymaster_blob_t entry; + if (input_set.GetTagValue(TAG_APPLICATION_ID, &entry)) + hidden->push_back(TAG_APPLICATION_ID, entry.data, entry.data_length); + if (input_set.GetTagValue(TAG_APPLICATION_DATA, &entry)) + hidden->push_back(TAG_APPLICATION_DATA, entry.data, entry.data_length); + + hidden->push_back(TAG_ROOT_OF_TRUST, reinterpret_cast<const uint8_t*>(root_of_trust_.data()), + root_of_trust_.size()); + + return TranslateAuthorizationSetError(hidden->is_valid()); +} + +} // namespace keymaster
diff --git a/keymaster/soft_keymaster_device.cpp b/keymaster/soft_keymaster_device.cpp new file mode 100644 index 0000000..cbeaec7 --- /dev/null +++ b/keymaster/soft_keymaster_device.cpp
@@ -0,0 +1,1341 @@ +/* + * 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 <keymaster/soft_keymaster_device.h> + +#include <assert.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <algorithm> + +#include <type_traits> + +#include <openssl/x509.h> + +#include <hardware/keymaster1.h> +#define LOG_TAG "SoftKeymasterDevice" +#include <cutils/log.h> + +#include <keymaster/android_keymaster.h> +#include <keymaster/android_keymaster_messages.h> +#include <keymaster/authorization_set.h> +#include <keymaster/soft_keymaster_context.h> +#include <keymaster/soft_keymaster_logger.h> + +#include "openssl_utils.h" + +struct keystore_module soft_keymaster1_device_module = { + .common = + { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = KEYMASTER_MODULE_API_VERSION_1_0, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = KEYSTORE_HARDWARE_MODULE_ID, + .name = "OpenSSL-based SoftKeymaster HAL", + .author = "The Android Open Source Project", + .methods = nullptr, + .dso = 0, + .reserved = {}, + }, +}; + +struct keystore_module soft_keymaster2_device_module = { + .common = + { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = KEYSTORE_HARDWARE_MODULE_ID, + .name = "OpenSSL-based SoftKeymaster HAL", + .author = "The Android Open Source Project", + .methods = nullptr, + .dso = 0, + .reserved = {}, + }, +}; + +namespace keymaster { + +const size_t kOperationTableSize = 16; + +template <typename T> std::vector<T> make_vector(const T* array, size_t len) { + return std::vector<T>(array, array + len); +} + +static keymaster_error_t add_digests(keymaster1_device_t* dev, keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + SoftKeymasterDevice::DigestMap* map) { + auto key = std::make_pair(algorithm, purpose); + + keymaster_digest_t* digests; + size_t digests_length; + keymaster_error_t error = + dev->get_supported_digests(dev, algorithm, purpose, &digests, &digests_length); + if (error != KM_ERROR_OK) { + LOG_E("Error %d getting supported digests from keymaster1 device", error); + return error; + } + std::unique_ptr<keymaster_digest_t, Malloc_Delete> digests_deleter(digests); + + (*map)[key] = make_vector(digests, digests_length); + return KM_ERROR_OK; +} + +static keymaster_error_t map_digests(keymaster1_device_t* dev, + SoftKeymasterDevice::DigestMap* map) { + map->clear(); + + keymaster_algorithm_t sig_algorithms[] = {KM_ALGORITHM_RSA, KM_ALGORITHM_EC}; + keymaster_purpose_t sig_purposes[] = {KM_PURPOSE_SIGN, KM_PURPOSE_VERIFY}; + for (auto algorithm : sig_algorithms) + for (auto purpose : sig_purposes) { + keymaster_error_t error = add_digests(dev, algorithm, purpose, map); + if (error != KM_ERROR_OK) + return error; + } + + keymaster_algorithm_t crypt_algorithms[] = {KM_ALGORITHM_RSA}; + keymaster_purpose_t crypt_purposes[] = {KM_PURPOSE_ENCRYPT, KM_PURPOSE_DECRYPT}; + for (auto algorithm : crypt_algorithms) + for (auto purpose : crypt_purposes) { + keymaster_error_t error = add_digests(dev, algorithm, purpose, map); + if (error != KM_ERROR_OK) + return error; + } + + return KM_ERROR_OK; +} + +SoftKeymasterDevice::SoftKeymasterDevice() + : wrapped_km0_device_(nullptr), wrapped_km1_device_(nullptr), + context_(new SoftKeymasterContext), + impl_(new AndroidKeymaster(context_, kOperationTableSize)) { + LOG_I("Creating device", 0); + LOG_D("Device address: %p", this); + + initialize_device_struct(KEYMASTER_SOFTWARE_ONLY | KEYMASTER_BLOBS_ARE_STANDALONE | + KEYMASTER_SUPPORTS_EC); +} + +SoftKeymasterDevice::SoftKeymasterDevice(SoftKeymasterContext* context) + : wrapped_km0_device_(nullptr), wrapped_km1_device_(nullptr), context_(context), + impl_(new AndroidKeymaster(context_, kOperationTableSize)) { + LOG_I("Creating test device", 0); + LOG_D("Device address: %p", this); + + initialize_device_struct(KEYMASTER_SOFTWARE_ONLY | KEYMASTER_BLOBS_ARE_STANDALONE | + KEYMASTER_SUPPORTS_EC); +} + +keymaster_error_t SoftKeymasterDevice::SetHardwareDevice(keymaster0_device_t* keymaster0_device) { + assert(keymaster0_device); + LOG_D("Reinitializing SoftKeymasterDevice to use HW keymaster0", 0); + + if (!context_) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + keymaster_error_t error = context_->SetHardwareDevice(keymaster0_device); + if (error != KM_ERROR_OK) + return error; + + initialize_device_struct(keymaster0_device->flags); + + module_name_ = km1_device_.common.module->name; + module_name_.append("(Wrapping "); + module_name_.append(keymaster0_device->common.module->name); + module_name_.append(")"); + + updated_module_ = *km1_device_.common.module; + updated_module_.name = module_name_.c_str(); + + km1_device_.common.module = &updated_module_; + + wrapped_km0_device_ = keymaster0_device; + wrapped_km1_device_ = nullptr; + return KM_ERROR_OK; +} + +keymaster_error_t SoftKeymasterDevice::SetHardwareDevice(keymaster1_device_t* keymaster1_device) { + assert(keymaster1_device); + LOG_D("Reinitializing SoftKeymasterDevice to use HW keymaster1", 0); + + if (!context_) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + keymaster_error_t error = map_digests(keymaster1_device, &km1_device_digests_); + if (error != KM_ERROR_OK) + return error; + + error = context_->SetHardwareDevice(keymaster1_device); + if (error != KM_ERROR_OK) + return error; + + initialize_device_struct(keymaster1_device->flags); + + module_name_ = km1_device_.common.module->name; + module_name_.append(" (Wrapping "); + module_name_.append(keymaster1_device->common.module->name); + module_name_.append(")"); + + updated_module_ = *km1_device_.common.module; + updated_module_.name = module_name_.c_str(); + + km1_device_.common.module = &updated_module_; + + wrapped_km0_device_ = nullptr; + wrapped_km1_device_ = keymaster1_device; + return KM_ERROR_OK; +} + +bool SoftKeymasterDevice::Keymaster1DeviceIsGood() { + std::vector<keymaster_digest_t> expected_rsa_digests = { + KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, + KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; + std::vector<keymaster_digest_t> expected_ec_digests = { + KM_DIGEST_NONE, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, + KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512}; + + for (auto& entry : km1_device_digests_) { + if (entry.first.first == KM_ALGORITHM_RSA) + if (!std::is_permutation(entry.second.begin(), entry.second.end(), + expected_rsa_digests.begin())) + return false; + if (entry.first.first == KM_ALGORITHM_EC) + if (!std::is_permutation(entry.second.begin(), entry.second.end(), + expected_ec_digests.begin())) + return false; + } + return true; +} + +void SoftKeymasterDevice::initialize_device_struct(uint32_t flags) { + memset(&km1_device_, 0, sizeof(km1_device_)); + + km1_device_.common.tag = HARDWARE_DEVICE_TAG; + km1_device_.common.version = 1; + km1_device_.common.module = reinterpret_cast<hw_module_t*>(&soft_keymaster1_device_module); + km1_device_.common.close = &close_device; + + km1_device_.flags = flags; + + km1_device_.context = this; + + // keymaster0 APIs + km1_device_.generate_keypair = nullptr; + km1_device_.import_keypair = nullptr; + km1_device_.get_keypair_public = nullptr; + km1_device_.delete_keypair = nullptr; + km1_device_.delete_all = nullptr; + km1_device_.sign_data = nullptr; + km1_device_.verify_data = nullptr; + + // keymaster1 APIs + km1_device_.get_supported_algorithms = get_supported_algorithms; + km1_device_.get_supported_block_modes = get_supported_block_modes; + km1_device_.get_supported_padding_modes = get_supported_padding_modes; + km1_device_.get_supported_digests = get_supported_digests; + km1_device_.get_supported_import_formats = get_supported_import_formats; + km1_device_.get_supported_export_formats = get_supported_export_formats; + km1_device_.add_rng_entropy = add_rng_entropy; + km1_device_.generate_key = generate_key; + km1_device_.get_key_characteristics = get_key_characteristics; + km1_device_.import_key = import_key; + km1_device_.export_key = export_key; + km1_device_.delete_key = delete_key; + km1_device_.delete_all_keys = delete_all_keys; + km1_device_.begin = begin; + km1_device_.update = update; + km1_device_.finish = finish; + km1_device_.abort = abort; + + // keymaster2 APIs + memset(&km2_device_, 0, sizeof(km2_device_)); + + km2_device_.context = this; + + km2_device_.common.tag = HARDWARE_DEVICE_TAG; + km2_device_.common.version = 1; + km2_device_.common.module = reinterpret_cast<hw_module_t*>(&soft_keymaster2_device_module); + km2_device_.common.close = &close_device; + + km2_device_.add_rng_entropy = add_rng_entropy; + km2_device_.generate_key = generate_key; + km2_device_.get_key_characteristics = get_key_characteristics; + km2_device_.import_key = import_key; + km2_device_.export_key = export_key; + km2_device_.agree_key = nullptr; // TODO(swillden) Implement ECDH + km2_device_.attest_key = attest_key; + km2_device_.upgrade_key = nullptr; // TODO(swillden) Implement upgrade + km2_device_.delete_key = delete_key; + km2_device_.delete_all_keys = delete_all_keys; + km2_device_.begin = begin; + km2_device_.update = update; + km2_device_.finish = finish; + km2_device_.abort = abort; +} + +hw_device_t* SoftKeymasterDevice::hw_device() { + return &km1_device_.common; +} + +keymaster1_device_t* SoftKeymasterDevice::keymaster_device() { + return &km1_device_; +} + +keymaster2_device_t* SoftKeymasterDevice::keymaster2_device() { + return &km2_device_; +} + +namespace { + +keymaster_key_characteristics_t* BuildCharacteristics(const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced) { + keymaster_key_characteristics_t* characteristics = + reinterpret_cast<keymaster_key_characteristics_t*>( + malloc(sizeof(keymaster_key_characteristics_t))); + if (characteristics) { + hw_enforced.CopyToParamSet(&characteristics->hw_enforced); + sw_enforced.CopyToParamSet(&characteristics->sw_enforced); + } + return characteristics; +} + +template <typename RequestType> +void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data, + RequestType* request) { + request->additional_params.Clear(); + if (client_id) + request->additional_params.push_back(TAG_APPLICATION_ID, *client_id); + if (app_data) + request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data); +} + +template <typename T> SoftKeymasterDevice* convert_device(const T* dev) { + static_assert((std::is_same<T, keymaster0_device_t>::value || + std::is_same<T, keymaster1_device_t>::value || + std::is_same<T, keymaster2_device_t>::value), + "convert_device should only be applied to keymaster devices"); + return reinterpret_cast<SoftKeymasterDevice*>(dev->context); +} + +bool FindAlgorithm(const keymaster_key_param_set_t& params, keymaster_algorithm_t* algorithm) { + for (size_t i = 0; i < params.length; ++i) + if (params.params[i].tag == KM_TAG_ALGORITHM) { + *algorithm = static_cast<keymaster_algorithm_t>(params.params[i].enumerated); + return true; + } + return false; +} + +keymaster_error_t GetAlgorithm(const keymaster1_device_t* dev, const keymaster_key_blob_t& key, + const AuthorizationSet& in_params, + keymaster_algorithm_t* algorithm) { + keymaster_blob_t client_id = {nullptr, 0}; + keymaster_blob_t app_data = {nullptr, 0}; + keymaster_blob_t* client_id_ptr = nullptr; + keymaster_blob_t* app_data_ptr = nullptr; + if (in_params.GetTagValue(TAG_APPLICATION_ID, &client_id)) + client_id_ptr = &client_id; + if (in_params.GetTagValue(TAG_APPLICATION_DATA, &app_data)) + app_data_ptr = &app_data; + + keymaster_key_characteristics_t* characteristics; + keymaster_error_t error = + dev->get_key_characteristics(dev, &key, client_id_ptr, app_data_ptr, &characteristics); + if (error != KM_ERROR_OK) + return error; + std::unique_ptr<keymaster_key_characteristics_t, Characteristics_Delete> + characteristics_deleter(characteristics); + + if (FindAlgorithm(characteristics->hw_enforced, algorithm)) + return KM_ERROR_OK; + + if (FindAlgorithm(characteristics->sw_enforced, algorithm)) + return KM_ERROR_OK; + + return KM_ERROR_INVALID_KEY_BLOB; +} + +} // unnamed namespaced + +/* static */ +int SoftKeymasterDevice::close_device(hw_device_t* dev) { + switch (dev->module->module_api_version) { + case KEYMASTER_MODULE_API_VERSION_2_0: { + delete convert_device(reinterpret_cast<keymaster2_device_t*>(dev)); + break; + } + + case KEYMASTER_MODULE_API_VERSION_1_0: { + delete convert_device(reinterpret_cast<keymaster1_device_t*>(dev)); + break; + } + + default: + return -1; + } + + return 0; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::get_supported_algorithms(const keymaster1_device_t* dev, + keymaster_algorithm_t** algorithms, + size_t* algorithms_length) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!algorithms || !algorithms_length) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) + return km1_dev->get_supported_algorithms(km1_dev, algorithms, algorithms_length); + + SupportedAlgorithmsRequest request; + SupportedAlgorithmsResponse response; + convert_device(dev)->impl_->SupportedAlgorithms(request, &response); + if (response.error != KM_ERROR_OK) { + LOG_E("get_supported_algorithms failed with %d", response.error); + + return response.error; + } + + *algorithms_length = response.results_length; + *algorithms = + reinterpret_cast<keymaster_algorithm_t*>(malloc(*algorithms_length * sizeof(**algorithms))); + if (!*algorithms) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + std::copy(response.results, response.results + response.results_length, *algorithms); + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::get_supported_block_modes(const keymaster1_device_t* dev, + keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + keymaster_block_mode_t** modes, + size_t* modes_length) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!modes || !modes_length) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) + return km1_dev->get_supported_block_modes(km1_dev, algorithm, purpose, modes, modes_length); + + SupportedBlockModesRequest request; + request.algorithm = algorithm; + request.purpose = purpose; + SupportedBlockModesResponse response; + convert_device(dev)->impl_->SupportedBlockModes(request, &response); + + if (response.error != KM_ERROR_OK) { + LOG_E("get_supported_block_modes failed with %d", response.error); + + return response.error; + } + + *modes_length = response.results_length; + *modes = reinterpret_cast<keymaster_block_mode_t*>(malloc(*modes_length * sizeof(**modes))); + if (!*modes) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + std::copy(response.results, response.results + response.results_length, *modes); + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::get_supported_padding_modes(const keymaster1_device_t* dev, + keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + keymaster_padding_t** modes, + size_t* modes_length) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!modes || !modes_length) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) + return km1_dev->get_supported_padding_modes(km1_dev, algorithm, purpose, modes, + modes_length); + + SupportedPaddingModesRequest request; + request.algorithm = algorithm; + request.purpose = purpose; + SupportedPaddingModesResponse response; + convert_device(dev)->impl_->SupportedPaddingModes(request, &response); + + if (response.error != KM_ERROR_OK) { + LOG_E("get_supported_padding_modes failed with %d", response.error); + return response.error; + } + + *modes_length = response.results_length; + *modes = reinterpret_cast<keymaster_padding_t*>(malloc(*modes_length * sizeof(**modes))); + if (!*modes) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + std::copy(response.results, response.results + response.results_length, *modes); + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::get_supported_digests(const keymaster1_device_t* dev, + keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + keymaster_digest_t** digests, + size_t* digests_length) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!digests || !digests_length) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) + return km1_dev->get_supported_digests(km1_dev, algorithm, purpose, digests, digests_length); + + SupportedDigestsRequest request; + request.algorithm = algorithm; + request.purpose = purpose; + SupportedDigestsResponse response; + convert_device(dev)->impl_->SupportedDigests(request, &response); + + if (response.error != KM_ERROR_OK) { + LOG_E("get_supported_digests failed with %d", response.error); + return response.error; + } + + *digests_length = response.results_length; + *digests = reinterpret_cast<keymaster_digest_t*>(malloc(*digests_length * sizeof(**digests))); + if (!*digests) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + std::copy(response.results, response.results + response.results_length, *digests); + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::get_supported_import_formats( + const keymaster1_device_t* dev, keymaster_algorithm_t algorithm, + keymaster_key_format_t** formats, size_t* formats_length) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!formats || !formats_length) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) + return km1_dev->get_supported_import_formats(km1_dev, algorithm, formats, formats_length); + + SupportedImportFormatsRequest request; + request.algorithm = algorithm; + SupportedImportFormatsResponse response; + convert_device(dev)->impl_->SupportedImportFormats(request, &response); + + if (response.error != KM_ERROR_OK) { + LOG_E("get_supported_import_formats failed with %d", response.error); + return response.error; + } + + *formats_length = response.results_length; + *formats = + reinterpret_cast<keymaster_key_format_t*>(malloc(*formats_length * sizeof(**formats))); + if (!*formats) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + std::copy(response.results, response.results + response.results_length, *formats); + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::get_supported_export_formats( + const keymaster1_device_t* dev, keymaster_algorithm_t algorithm, + keymaster_key_format_t** formats, size_t* formats_length) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!formats || !formats_length) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) + return km1_dev->get_supported_export_formats(km1_dev, algorithm, formats, formats_length); + + SupportedExportFormatsRequest request; + request.algorithm = algorithm; + SupportedExportFormatsResponse response; + convert_device(dev)->impl_->SupportedExportFormats(request, &response); + + if (response.error != KM_ERROR_OK) { + LOG_E("get_supported_export_formats failed with %d", response.error); + return response.error; + } + + *formats_length = response.results_length; + *formats = + reinterpret_cast<keymaster_key_format_t*>(malloc(*formats_length * sizeof(**formats))); + if (!*formats) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + std::copy(response.results, response.results + *formats_length, *formats); + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::add_rng_entropy(const keymaster1_device_t* dev, + const uint8_t* data, size_t data_length) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) + return km1_dev->add_rng_entropy(km1_dev, data, data_length); + + AddEntropyRequest request; + request.random_data.Reinitialize(data, data_length); + AddEntropyResponse response; + convert_device(dev)->impl_->AddRngEntropy(request, &response); + if (response.error != KM_ERROR_OK) + LOG_E("add_rng_entropy failed with %d", response.error); + return response.error; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev, + const uint8_t* data, size_t data_length) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + return add_rng_entropy(&sk_dev->km1_device_, data, data_length); +} + +template <typename Collection, typename Value> bool contains(const Collection& c, const Value& v) { + return std::find(c.begin(), c.end(), v) != c.end(); +} + +bool SoftKeymasterDevice::FindUnsupportedDigest(keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + const AuthorizationSet& params, + keymaster_digest_t* unsupported) const { + assert(wrapped_km1_device_); + + auto supported_digests = km1_device_digests_.find(std::make_pair(algorithm, purpose)); + if (supported_digests == km1_device_digests_.end()) + // Invalid algorith/purpose pair (e.g. EC encrypt). Let the error be handled by HW module. + return false; + + for (auto& entry : params) + if (entry.tag == TAG_DIGEST) + if (!contains(supported_digests->second, entry.enumerated)) { + LOG_I("Digest %d requested but not supported by module %s", entry.enumerated, + wrapped_km1_device_->common.module->name); + *unsupported = static_cast<keymaster_digest_t>(entry.enumerated); + return true; + } + return false; +} + +bool SoftKeymasterDevice::RequiresSoftwareDigesting(keymaster_algorithm_t algorithm, + keymaster_purpose_t purpose, + const AuthorizationSet& params) const { + assert(wrapped_km1_device_); + if (!wrapped_km1_device_) + return true; + + switch (algorithm) { + case KM_ALGORITHM_AES: + case KM_ALGORITHM_HMAC: + LOG_D("Not performing software digesting for algorithm %d", algorithm); + return false; + case KM_ALGORITHM_RSA: + case KM_ALGORITHM_EC: + break; + } + + keymaster_digest_t unsupported; + if (!FindUnsupportedDigest(algorithm, purpose, params, &unsupported)) { + LOG_D("Requested digest(s) supported for algorithm %d and purpose %d", algorithm, purpose); + return false; + } + + return true; +} + +bool SoftKeymasterDevice::KeyRequiresSoftwareDigesting( + const AuthorizationSet& key_description) const { + assert(wrapped_km1_device_); + if (!wrapped_km1_device_) + return true; + + keymaster_algorithm_t algorithm; + if (!key_description.GetTagValue(TAG_ALGORITHM, &algorithm)) { + // The hardware module will return an error during keygen. + return false; + } + + for (auto& entry : key_description) + if (entry.tag == TAG_PURPOSE) { + keymaster_purpose_t purpose = static_cast<keymaster_purpose_t>(entry.enumerated); + if (RequiresSoftwareDigesting(algorithm, purpose, key_description)) + return true; + } + + return false; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::generate_key( + const keymaster1_device_t* dev, const keymaster_key_param_set_t* params, + keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t** characteristics) { + if (!dev || !params) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!key_blob) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + + GenerateKeyRequest request; + request.key_description.Reinitialize(*params); + + keymaster1_device_t* km1_dev = sk_dev->wrapped_km1_device_; + if (km1_dev && !sk_dev->KeyRequiresSoftwareDigesting(request.key_description)) + return km1_dev->generate_key(km1_dev, params, key_blob, characteristics); + + GenerateKeyResponse response; + sk_dev->impl_->GenerateKey(request, &response); + if (response.error != KM_ERROR_OK) + return response.error; + + key_blob->key_material_size = response.key_blob.key_material_size; + uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(key_blob->key_material_size)); + if (!tmp) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + memcpy(tmp, response.key_blob.key_material, response.key_blob.key_material_size); + key_blob->key_material = tmp; + + if (characteristics) { + *characteristics = BuildCharacteristics(response.enforced, response.unenforced); + if (!*characteristics) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + + return KM_ERROR_OK; +} + +keymaster_error_t +SoftKeymasterDevice::generate_key(const keymaster2_device_t* dev, // + const keymaster_key_param_set_t* params, + keymaster_key_blob_t* key_blob, + keymaster_key_characteristics_t* characteristics) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!key_blob) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + + GenerateKeyRequest request; + request.key_description.Reinitialize(*params); + + keymaster1_device_t* km1_dev = sk_dev->wrapped_km1_device_; + if (km1_dev && !sk_dev->KeyRequiresSoftwareDigesting(request.key_description)) { + keymaster_key_characteristics_t* chars_ptr; + keymaster_error_t error = km1_dev->generate_key(km1_dev, params, key_blob, + characteristics ? &chars_ptr : nullptr); + if (error != KM_ERROR_OK) + return error; + + if (characteristics) { + *characteristics = *chars_ptr; + free(chars_ptr); + } + return KM_ERROR_OK; + } + + GenerateKeyResponse response; + sk_dev->impl_->GenerateKey(request, &response); + if (response.error != KM_ERROR_OK) + return response.error; + + key_blob->key_material_size = response.key_blob.key_material_size; + uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(key_blob->key_material_size)); + if (!tmp) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + memcpy(tmp, response.key_blob.key_material, response.key_blob.key_material_size); + key_blob->key_material = tmp; + + if (characteristics) { + response.enforced.CopyToParamSet(&characteristics->hw_enforced); + response.unenforced.CopyToParamSet(&characteristics->sw_enforced); + } + + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::get_key_characteristics( + const keymaster1_device_t* dev, const keymaster_key_blob_t* key_blob, + const keymaster_blob_t* client_id, const keymaster_blob_t* app_data, + keymaster_key_characteristics_t** characteristics) { + if (!dev || !key_blob || !key_blob->key_material) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!characteristics) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) + return km1_dev->get_key_characteristics(km1_dev, key_blob, client_id, app_data, + characteristics); + + GetKeyCharacteristicsRequest request; + request.SetKeyMaterial(*key_blob); + AddClientAndAppData(client_id, app_data, &request); + + GetKeyCharacteristicsResponse response; + convert_device(dev)->impl_->GetKeyCharacteristics(request, &response); + if (response.error != KM_ERROR_OK) + return response.error; + + *characteristics = BuildCharacteristics(response.enforced, response.unenforced); + if (!*characteristics) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::get_key_characteristics( + const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob, + const keymaster_blob_t* client_id, const keymaster_blob_t* app_data, + keymaster_key_characteristics_t* characteristics) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!characteristics) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + + keymaster_error_t error; + keymaster_key_characteristics_t* key_characteristics; + error = get_key_characteristics(&sk_dev->km1_device_, key_blob, client_id, app_data, + &key_characteristics); + if (error != KM_ERROR_OK) + return error; + *characteristics = *key_characteristics; + free(key_characteristics); + + return error; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::import_key( + const keymaster1_device_t* dev, const keymaster_key_param_set_t* params, + keymaster_key_format_t key_format, const keymaster_blob_t* key_data, + keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t** characteristics) { + if (!params || !key_data) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!key_blob) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + + ImportKeyRequest request; + request.key_description.Reinitialize(*params); + + keymaster1_device_t* km1_dev = sk_dev->wrapped_km1_device_; + if (km1_dev && !sk_dev->KeyRequiresSoftwareDigesting(request.key_description)) + return km1_dev->import_key(km1_dev, params, key_format, key_data, key_blob, + characteristics); + + *characteristics = nullptr; + + request.key_format = key_format; + request.SetKeyMaterial(key_data->data, key_data->data_length); + + ImportKeyResponse response; + convert_device(dev)->impl_->ImportKey(request, &response); + if (response.error != KM_ERROR_OK) + return response.error; + + key_blob->key_material_size = response.key_blob.key_material_size; + key_blob->key_material = reinterpret_cast<uint8_t*>(malloc(key_blob->key_material_size)); + if (!key_blob->key_material) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + memcpy(const_cast<uint8_t*>(key_blob->key_material), response.key_blob.key_material, + response.key_blob.key_material_size); + + if (characteristics) { + *characteristics = BuildCharacteristics(response.enforced, response.unenforced); + if (!*characteristics) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::import_key( + const keymaster2_device_t* dev, const keymaster_key_param_set_t* params, + keymaster_key_format_t key_format, const keymaster_blob_t* key_data, + keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + + keymaster_error_t error; + if (characteristics) { + keymaster_key_characteristics_t* characteristics_ptr; + error = import_key(&sk_dev->km1_device_, params, key_format, key_data, key_blob, + &characteristics_ptr); + if (error == KM_ERROR_OK) { + *characteristics = *characteristics_ptr; + free(characteristics_ptr); + } + } else { + error = import_key(&sk_dev->km1_device_, params, key_format, key_data, key_blob, nullptr); + } + + return error; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::export_key(const keymaster1_device_t* dev, + keymaster_key_format_t export_format, + const keymaster_key_blob_t* key_to_export, + const keymaster_blob_t* client_id, + const keymaster_blob_t* app_data, + keymaster_blob_t* export_data) { + if (!key_to_export || !key_to_export->key_material) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!export_data) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) + return km1_dev->export_key(km1_dev, export_format, key_to_export, client_id, app_data, + export_data); + + export_data->data = nullptr; + export_data->data_length = 0; + + ExportKeyRequest request; + request.key_format = export_format; + request.SetKeyMaterial(*key_to_export); + AddClientAndAppData(client_id, app_data, &request); + + ExportKeyResponse response; + convert_device(dev)->impl_->ExportKey(request, &response); + if (response.error != KM_ERROR_OK) + return response.error; + + export_data->data_length = response.key_data_length; + uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(export_data->data_length)); + if (!tmp) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + memcpy(tmp, response.key_data, export_data->data_length); + export_data->data = tmp; + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::export_key(const keymaster2_device_t* dev, + keymaster_key_format_t export_format, + const keymaster_key_blob_t* key_to_export, + const keymaster_blob_t* client_id, + const keymaster_blob_t* app_data, + keymaster_blob_t* export_data) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + return export_key(&sk_dev->km1_device_, export_format, key_to_export, client_id, app_data, + export_data); +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::attest_key(const keymaster2_device_t* dev, + const keymaster_key_blob_t* key_to_attest, + const keymaster_key_param_set_t* attest_params, + keymaster_cert_chain_t* cert_chain) { + if (!dev || !key_to_attest || !attest_params || !cert_chain) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + cert_chain->entry_count = 0; + cert_chain->entries = nullptr; + + AttestKeyRequest request; + request.SetKeyMaterial(*key_to_attest); + request.attest_params.Reinitialize(*attest_params); + + AttestKeyResponse response; + convert_device(dev)->impl_->AttestKey(request, &response); + if (response.error != KM_ERROR_OK) + return response.error; + + // Allocate and clear storage for cert_chain. + keymaster_cert_chain_t& rsp_chain = response.certificate_chain; + cert_chain->entries = reinterpret_cast<keymaster_blob_t*>( + malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries))); + if (!cert_chain->entries) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + cert_chain->entry_count = rsp_chain.entry_count; + for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) + entry = {}; + + // Copy cert_chain contents + size_t i = 0; + for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) { + cert_chain->entries[i].data = reinterpret_cast<uint8_t*>(malloc(entry.data_length)); + if (!cert_chain->entries[i].data) { + keymaster_free_cert_chain(cert_chain); + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + cert_chain->entries[i].data_length = entry.data_length; + memcpy(const_cast<uint8_t*>(cert_chain->entries[i].data), entry.data, entry.data_length); + ++i; + } + + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::delete_key(const keymaster1_device_t* dev, + const keymaster_key_blob_t* key) { + if (!dev || !key || !key->key_material) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + KeymasterKeyBlob blob(*key); + return convert_device(dev)->context_->DeleteKey(blob); +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::delete_key(const keymaster2_device_t* dev, + const keymaster_key_blob_t* key) { + if (!dev || !key || !key->key_material) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + KeymasterKeyBlob blob(*key); + return convert_device(dev)->context_->DeleteKey(blob); +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::delete_all_keys(const keymaster1_device_t* dev) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + return convert_device(dev)->context_->DeleteAllKeys(); +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::delete_all_keys(const keymaster2_device_t* dev) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + return convert_device(dev)->context_->DeleteAllKeys(); +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::begin(const keymaster1_device_t* dev, + keymaster_purpose_t purpose, + const keymaster_key_blob_t* key, + const keymaster_key_param_set_t* in_params, + keymaster_key_param_set_t* out_params, + keymaster_operation_handle_t* operation_handle) { + if (!key || !key->key_material) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!operation_handle) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev) { + AuthorizationSet in_params_set(*in_params); + + keymaster_algorithm_t algorithm = KM_ALGORITHM_AES; + keymaster_error_t error = GetAlgorithm(km1_dev, *key, in_params_set, &algorithm); + if (error != KM_ERROR_OK) + return error; + + if (!convert_device(dev)->RequiresSoftwareDigesting(algorithm, purpose, in_params_set)) { + LOG_D("Operation supported by %s, passing through to keymaster1 module", + km1_dev->common.module->name); + return km1_dev->begin(km1_dev, purpose, key, in_params, out_params, operation_handle); + } + LOG_I("Doing software digesting for keymaster1 module %s", km1_dev->common.module->name); + } + + if (out_params) { + out_params->params = nullptr; + out_params->length = 0; + } + + BeginOperationRequest request; + request.purpose = purpose; + request.SetKeyMaterial(*key); + request.additional_params.Reinitialize(*in_params); + + BeginOperationResponse response; + convert_device(dev)->impl_->BeginOperation(request, &response); + if (response.error != KM_ERROR_OK) + return response.error; + + if (response.output_params.size() > 0) { + if (out_params) + response.output_params.CopyToParamSet(out_params); + else + return KM_ERROR_OUTPUT_PARAMETER_NULL; + } + + *operation_handle = response.op_handle; + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::begin(const keymaster2_device_t* dev, + keymaster_purpose_t purpose, + const keymaster_key_blob_t* key, + const keymaster_key_param_set_t* in_params, + keymaster_key_param_set_t* out_params, + keymaster_operation_handle_t* operation_handle) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + return begin(&sk_dev->km1_device_, purpose, key, in_params, out_params, operation_handle); +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::update(const keymaster1_device_t* dev, + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* input, size_t* input_consumed, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) { + if (!input) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (!input_consumed) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev && !convert_device(dev)->impl_->has_operation(operation_handle)) { + // This operation is being handled by km1_dev (or doesn't exist). Pass it through to + // km1_dev. Otherwise, we'll use the software AndroidKeymaster, which may delegate to + // km1_dev after doing necessary digesting. + return km1_dev->update(km1_dev, operation_handle, in_params, input, input_consumed, + out_params, output); + } + + if (out_params) { + out_params->params = nullptr; + out_params->length = 0; + } + if (output) { + output->data = nullptr; + output->data_length = 0; + } + + UpdateOperationRequest request; + request.op_handle = operation_handle; + if (input) + request.input.Reinitialize(input->data, input->data_length); + if (in_params) + request.additional_params.Reinitialize(*in_params); + + UpdateOperationResponse response; + convert_device(dev)->impl_->UpdateOperation(request, &response); + if (response.error != KM_ERROR_OK) + return response.error; + + if (response.output_params.size() > 0) { + if (out_params) + response.output_params.CopyToParamSet(out_params); + else + return KM_ERROR_OUTPUT_PARAMETER_NULL; + } + + *input_consumed = response.input_consumed; + if (output) { + output->data_length = response.output.available_read(); + uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(output->data_length)); + if (!tmp) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + memcpy(tmp, response.output.peek_read(), output->data_length); + output->data = tmp; + } else if (response.output.available_read() > 0) { + return KM_ERROR_OUTPUT_PARAMETER_NULL; + } + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::update(const keymaster2_device_t* dev, + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* in_params, + const keymaster_blob_t* input, size_t* input_consumed, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + return update(&sk_dev->km1_device_, operation_handle, in_params, input, input_consumed, + out_params, output); +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::finish(const keymaster1_device_t* dev, + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* params, + const keymaster_blob_t* signature, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) { + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev && !convert_device(dev)->impl_->has_operation(operation_handle)) { + // This operation is being handled by km1_dev (or doesn't exist). Pass it through to + // km1_dev. Otherwise, we'll use the software AndroidKeymaster, which may delegate to + // km1_dev after doing necessary digesting. + return km1_dev->finish(km1_dev, operation_handle, params, signature, out_params, output); + } + + if (out_params) { + out_params->params = nullptr; + out_params->length = 0; + } + + if (output) { + output->data = nullptr; + output->data_length = 0; + } + + FinishOperationRequest request; + request.op_handle = operation_handle; + if (signature && signature->data_length > 0) + request.signature.Reinitialize(signature->data, signature->data_length); + request.additional_params.Reinitialize(*params); + + FinishOperationResponse response; + convert_device(dev)->impl_->FinishOperation(request, &response); + if (response.error != KM_ERROR_OK) + return response.error; + + if (response.output_params.size() > 0) { + if (out_params) + response.output_params.CopyToParamSet(out_params); + else + return KM_ERROR_OUTPUT_PARAMETER_NULL; + } + if (output) { + output->data_length = response.output.available_read(); + uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(output->data_length)); + if (!tmp) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + memcpy(tmp, response.output.peek_read(), output->data_length); + output->data = tmp; + } else if (response.output.available_read() > 0) { + return KM_ERROR_OUTPUT_PARAMETER_NULL; + } + + return KM_ERROR_OK; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::finish(const keymaster2_device_t* dev, + keymaster_operation_handle_t operation_handle, + const keymaster_key_param_set_t* params, + const keymaster_blob_t* input, + const keymaster_blob_t* signature, + keymaster_key_param_set_t* out_params, + keymaster_blob_t* output) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + if (input && input->data) + return KM_ERROR_UNIMPLEMENTED; // TODO(swillden): Implement this + + SoftKeymasterDevice* sk_dev = convert_device(dev); + return finish(&sk_dev->km1_device_, operation_handle, params, signature, out_params, output); +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::abort(const keymaster1_device_t* dev, + keymaster_operation_handle_t operation_handle) { + const keymaster1_device_t* km1_dev = convert_device(dev)->wrapped_km1_device_; + if (km1_dev && !convert_device(dev)->impl_->has_operation(operation_handle)) { + // This operation is being handled by km1_dev (or doesn't exist). Pass it through to + // km1_dev. Otherwise, we'll use the software AndroidKeymaster, which may delegate to + // km1_dev. + return km1_dev->abort(km1_dev, operation_handle); + } + + AbortOperationRequest request; + request.op_handle = operation_handle; + AbortOperationResponse response; + convert_device(dev)->impl_->AbortOperation(request, &response); + return response.error; +} + +/* static */ +keymaster_error_t SoftKeymasterDevice::abort(const keymaster2_device_t* dev, + keymaster_operation_handle_t operation_handle) { + if (!dev) + return KM_ERROR_UNEXPECTED_NULL_POINTER; + + SoftKeymasterDevice* sk_dev = convert_device(dev); + return abort(&sk_dev->km1_device_, operation_handle); +} + +/* static */ +void SoftKeymasterDevice::StoreDefaultNewKeyParams(keymaster_algorithm_t algorithm, + AuthorizationSet* auth_set) { + auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_SIGN); + auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_VERIFY); + auth_set->push_back(TAG_ALL_USERS); + auth_set->push_back(TAG_NO_AUTH_REQUIRED); + + // All digests. + auth_set->push_back(TAG_DIGEST, KM_DIGEST_NONE); + auth_set->push_back(TAG_DIGEST, KM_DIGEST_MD5); + auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA1); + auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_224); + auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256); + auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_384); + auth_set->push_back(TAG_DIGEST, KM_DIGEST_SHA_2_512); + + if (algorithm == KM_ALGORITHM_RSA) { + auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_ENCRYPT); + auth_set->push_back(TAG_PURPOSE, KM_PURPOSE_DECRYPT); + auth_set->push_back(TAG_PADDING, KM_PAD_NONE); + auth_set->push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN); + auth_set->push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + auth_set->push_back(TAG_PADDING, KM_PAD_RSA_PSS); + auth_set->push_back(TAG_PADDING, KM_PAD_RSA_OAEP); + } +} + +} // namespace keymaster
diff --git a/keymaster/soft_keymaster_logger.cpp b/keymaster/soft_keymaster_logger.cpp new file mode 100644 index 0000000..3c55a64 --- /dev/null +++ b/keymaster/soft_keymaster_logger.cpp
@@ -0,0 +1,51 @@ +/* + * 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 <keymaster/soft_keymaster_logger.h> + +#include <stdarg.h> +#include <syslog.h> + +#define LOG_TAG "SoftKeymaster" +#include <cutils/log.h> + +namespace keymaster { + +int SoftKeymasterLogger::log_msg(LogLevel level, const char* fmt, va_list args) const { + + int android_log_level = ANDROID_LOG_ERROR; + switch (level) { + case DEBUG_LVL: + android_log_level = ANDROID_LOG_DEBUG; + break; + case INFO_LVL: + android_log_level = ANDROID_LOG_INFO; + break; + case WARNING_LVL: + android_log_level = ANDROID_LOG_WARN; + break; + case ERROR_LVL: + android_log_level = ANDROID_LOG_ERROR; + break; + case SEVERE_LVL: + android_log_level = ANDROID_LOG_ERROR; + break; + } + + return LOG_PRI_VA(android_log_level, LOG_TAG, fmt, args); +} + +} // namespace keymaster
diff --git a/keymaster/sw_rsa_attest_root.key.pem b/keymaster/sw_rsa_attest_root.key.pem new file mode 100644 index 0000000..387a852 --- /dev/null +++ b/keymaster/sw_rsa_attest_root.key.pem
@@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAsQ+wz +fNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdLt0GA +ZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwIDAQAB +AoGAU8dxXchmqzVNbbvff7zgUa63YErk51Yem/EXzhkMaIXRkMO0edaCtZtnkRvg +9OQ2qEiLWaCTlUoyU7H/HUn2lwTQsOXyZI7dHijVDRMIv1mmrHCrGW/JC8FXfPLS +r3L3KoHXQVYL2mslbR8Rpogxq4WwnwK6XqSTH9mynFwQwEkCQQDMX3EZk3ricWVH +ruXD0BpXOMMpZuLu4rg5+1L51WEJvItIMeSjLuNa+g3AI8AYTYYi/aSLk6XEv82L +iXFGmJ2XAkEAy3M8k8Z0QzHae4olduqoHVWEarBtDE+fqFQBWgdm8fZhdHWrvlAc +qwJIXMUVc+dWm/FAQarCjbqWqhCRdaYgnQJBAJ7z7GdUCVNtlrQ2F4ZAqPwFreTZ +nM7njxmpm1Os3hhQiJPSGl3A7huoOGGkbJd6VEWKuRvF7jwkYZ2RfITH1mkCQAvh +X9E1Toa5+4spRwTJsSV9X+0m/kcwwx7+QNH0CrPockptsKi9Xt8xk+4u6BDLmogi +r2DmStQh6DhoHUZkfBUCQQCOgBkqH/15drpdR+BQH3VaP4/ALFfxR9E3G+lS+M5a +IqJEk9kh8vjuGzTaAZyU5keUmpWNc1gI7OvDMaH4+8vQ +-----END RSA PRIVATE KEY-----
diff --git a/keymaster/symmetric_key.cpp b/keymaster/symmetric_key.cpp new file mode 100644 index 0000000..1d51e3a --- /dev/null +++ b/keymaster/symmetric_key.cpp
@@ -0,0 +1,141 @@ +/* + * Copyright 2014 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 "symmetric_key.h" + +#include <assert.h> + +#include <openssl/err.h> +#include <openssl/rand.h> + +#include <keymaster/android_keymaster_utils.h> +#include <keymaster/logger.h> +#include <keymaster/keymaster_context.h> + +#include "aes_key.h" +#include "hmac_key.h" +#include "openssl_err.h" + +namespace keymaster { + +keymaster_error_t SymmetricKeyFactory::GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + if (!key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + uint32_t key_size_bits; + if (!key_description.GetTagValue(TAG_KEY_SIZE, &key_size_bits) || + !key_size_supported(key_size_bits)) + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + + keymaster_error_t error = validate_algorithm_specific_new_key_params(key_description); + if (error != KM_ERROR_OK) + return error; + + size_t key_data_size = key_size_bits / 8; + KeymasterKeyBlob key_material(key_data_size); + if (!key_material.key_material) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + + error = context_->GenerateRandom(key_material.writable_data(), key_data_size); + if (error != KM_ERROR_OK) { + LOG_E("Error generating %d bit symmetric key", key_size_bits); + return error; + } + + return context_->CreateKeyBlob(key_description, KM_ORIGIN_GENERATED, key_material, key_blob, + hw_enforced, sw_enforced); +} + +keymaster_error_t SymmetricKeyFactory::ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, + AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const { + if (!output_key_blob || !hw_enforced || !sw_enforced) + return KM_ERROR_OUTPUT_PARAMETER_NULL; + + AuthorizationSet authorizations(key_description); + + uint32_t key_size_bits; + if (!authorizations.GetTagValue(TAG_KEY_SIZE, &key_size_bits)) { + // Default key size if not specified. + key_size_bits = input_key_material.key_material_size * 8; + authorizations.push_back(TAG_KEY_SIZE, key_size_bits); + } + + keymaster_error_t error = validate_algorithm_specific_new_key_params(key_description); + if (error != KM_ERROR_OK) + return error; + + if (!key_size_supported(key_size_bits)) + return KM_ERROR_UNSUPPORTED_KEY_SIZE; + + if (input_key_material_format != KM_KEY_FORMAT_RAW) + return KM_ERROR_UNSUPPORTED_KEY_FORMAT; + + if (key_size_bits != input_key_material.key_material_size * 8) { + LOG_E("Expected %d-bit key data but got %d bits", key_size_bits, + input_key_material.key_material_size * 8); + return KM_ERROR_INVALID_KEY_BLOB; + } + + return context_->CreateKeyBlob(authorizations, KM_ORIGIN_IMPORTED, input_key_material, + output_key_blob, hw_enforced, sw_enforced); +} + +static const keymaster_key_format_t supported_import_formats[] = {KM_KEY_FORMAT_RAW}; +const keymaster_key_format_t* +SymmetricKeyFactory::SupportedImportFormats(size_t* format_count) const { + *format_count = array_length(supported_import_formats); + return supported_import_formats; +} + +SymmetricKey::SymmetricKey(const KeymasterKeyBlob& key_material, + const AuthorizationSet& hw_enforced, const AuthorizationSet& sw_enforced, + keymaster_error_t* error) + : Key(hw_enforced, sw_enforced, error) { + if (*error != KM_ERROR_OK) + return; + + uint8_t* tmp = dup_buffer(key_material.key_material, key_material.key_material_size); + if (tmp) { + key_data_.reset(tmp); + key_data_size_ = key_material.key_material_size; + *error = KM_ERROR_OK; + } else { + *error = KM_ERROR_MEMORY_ALLOCATION_FAILED; + } +} + +SymmetricKey::~SymmetricKey() { + memset_s(key_data_.get(), 0, key_data_size_); +} + +keymaster_error_t SymmetricKey::key_material(UniquePtr<uint8_t[]>* key_material, + size_t* size) const { + *size = key_data_size_; + key_material->reset(new (std::nothrow) uint8_t[*size]); + if (!key_material->get()) + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + memcpy(key_material->get(), key_data_.get(), *size); + return KM_ERROR_OK; +} + +} // namespace keymaster
diff --git a/keymaster/symmetric_key.h b/keymaster/symmetric_key.h new file mode 100644 index 0000000..979df62 --- /dev/null +++ b/keymaster/symmetric_key.h
@@ -0,0 +1,81 @@ +/* + * Copyright 2014 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 SYSTEM_KEYMASTER_SYMMETRIC_KEY_H_ +#define SYSTEM_KEYMASTER_SYMMETRIC_KEY_H_ + +#include <keymaster/key_factory.h> + +#include "key.h" + +namespace keymaster { + +class SymmetricKey; + +class SymmetricKeyFactory : public KeyFactory { + public: + explicit SymmetricKeyFactory(const KeymasterContext* context) : KeyFactory(context) {} + + keymaster_error_t GenerateKey(const AuthorizationSet& key_description, + KeymasterKeyBlob* key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + keymaster_error_t ImportKey(const AuthorizationSet& key_description, + keymaster_key_format_t input_key_material_format, + const KeymasterKeyBlob& input_key_material, + KeymasterKeyBlob* output_key_blob, AuthorizationSet* hw_enforced, + AuthorizationSet* sw_enforced) const override; + + virtual const keymaster_key_format_t* SupportedImportFormats(size_t* format_count) const; + virtual const keymaster_key_format_t* SupportedExportFormats(size_t* format_count) const { + return NoFormats(format_count); + }; + + private: + virtual bool key_size_supported(size_t key_size_bits) const = 0; + virtual keymaster_error_t + validate_algorithm_specific_new_key_params(const AuthorizationSet& key_description) const = 0; + + const keymaster_key_format_t* NoFormats(size_t* format_count) const { + *format_count = 0; + return NULL; + } +}; + +class SymmetricKey : public Key { + public: + ~SymmetricKey(); + + virtual keymaster_error_t key_material(UniquePtr<uint8_t[]>* key_material, size_t* size) const; + virtual keymaster_error_t formatted_key_material(keymaster_key_format_t, UniquePtr<uint8_t[]>*, + size_t*) const { + return KM_ERROR_UNSUPPORTED_KEY_FORMAT; + } + + const uint8_t* key_data() const { return key_data_.get(); } + size_t key_data_size() const { return key_data_size_; } + + protected: + SymmetricKey(const KeymasterKeyBlob& key_material, const AuthorizationSet& hw_enforced, + const AuthorizationSet& sw_enforced, keymaster_error_t* error); + + private: + size_t key_data_size_; + UniquePtr<uint8_t[]> key_data_; +}; + +} // namespace keymaster + +#endif // SYSTEM_KEYMASTER_AES_KEY_H_
diff --git a/keymaster/valgrind.supp b/keymaster/valgrind.supp new file mode 100644 index 0000000..2838278 --- /dev/null +++ b/keymaster/valgrind.supp
@@ -0,0 +1,40 @@ +{ + BoringSSLErrorLeak + Memcheck:Leak + fun:malloc + ... + fun:err_get_state + ... +} +{ + EcKeyErrorLeak + Memcheck:Leak + fun:malloc + fun:err_add_error_vdata + fun:ERR_add_error_data + fun:ASN1_item_ex_d2i + fun:ASN1_item_d2i + fun:d2i_EC_PRIVATEKEY + fun:d2i_ECPrivateKey + fun:old_ec_priv_decode + fun:d2i_PrivateKey + ... +} +{ + BoringSSLGetNewIndexLeak1 + Memcheck:Leak + match-leak-kinds: reachable + fun:malloc + ... + fun:CRYPTO_get_ex_new_index + ... +} +{ + BoringSSLGetNewIndexLeak2 + Memcheck:Leak + match-leak-kinds: reachable + fun:realloc + ... + fun:CRYPTO_get_ex_new_index + ... +} \ No newline at end of file