From f4527740309a9f89d9a32de9da40cafaa628cd88 Mon Sep 17 00:00:00 2001 From: Shawn Willden Date: Thu, 9 Nov 2017 15:59:39 -0700 Subject: [PATCH] Break vold dependency on keystore utilities. This is temporary. Keystore is in the process of being upgraded to use the new Keymaster 4.0 HAL, and I want to leave vold alone, using Keymaster 3.0 for the moment. This CL just copies relevant bits of keystore support utilities into vold, so it can stop depending on the copies from keystore. After the keystore update is complete, vold will be changed either to use Keymaster 4.0 or -- more likely -- to use keystore rather than talking to Keymaster directly. At that point the files added by this CL will be deleted. Test: Device boots and successfully decrypts /data Change-Id: I73f6d4cc4c5e20d89d7ac37d29d025bf279f9e12 --- Android.bp | 2 +- KeyStorage.cpp | 5 +- Keymaster.cpp | 7 +- Keymaster.h | 3 +- authorization_set.cpp | 420 ++++++++++++++++++++++++++++++++++++++++ authorization_set.h | 327 +++++++++++++++++++++++++++++++ keymaster_tags.h | 372 +++++++++++++++++++++++++++++++++++ keystore_hidl_support.h | 108 +++++++++++ 8 files changed, 1237 insertions(+), 7 deletions(-) create mode 100644 authorization_set.cpp create mode 100644 authorization_set.h create mode 100644 keymaster_tags.h create mode 100644 keystore_hidl_support.h diff --git a/Android.bp b/Android.bp index 5941cd9..cc015bf 100644 --- a/Android.bp +++ b/Android.bp @@ -50,7 +50,6 @@ cc_defaults { "libhardware_legacy", "libhidlbase", "libhwbinder", - "libkeystore_binder", "libkeyutils", "liblog", "liblogwrap", @@ -111,6 +110,7 @@ cc_library_static { "VoldNativeService.cpp", "VoldUtil.cpp", "VolumeManager.cpp", + "authorization_set.cpp", "cryptfs.cpp", "fs/Ext4.cpp", "fs/F2fs.cpp", diff --git a/KeyStorage.cpp b/KeyStorage.cpp index 8878a3c..7e1d4a7 100644 --- a/KeyStorage.cpp +++ b/KeyStorage.cpp @@ -40,14 +40,15 @@ #include -#include -#include extern "C" { #include "crypto_scrypt.h" } +#include "authorization_set.h" +#include "keystore_hidl_support.h" + namespace android { namespace vold { using namespace keystore; diff --git a/Keymaster.cpp b/Keymaster.cpp index 1bbeb61..e2e21e8 100644 --- a/Keymaster.cpp +++ b/Keymaster.cpp @@ -17,9 +17,10 @@ #include "Keymaster.h" #include -#include -#include -#include + +#include "authorization_set.h" +#include "keymaster_tags.h" +#include "keystore_hidl_support.h" using namespace ::keystore; using android::hardware::hidl_string; diff --git a/Keymaster.h b/Keymaster.h index f24a0c0..34fe59c 100644 --- a/Keymaster.h +++ b/Keymaster.h @@ -25,7 +25,8 @@ #include #include -#include + +#include "authorization_set.h" namespace android { namespace vold { diff --git a/authorization_set.cpp b/authorization_set.cpp new file mode 100644 index 0000000..e7a3401 --- /dev/null +++ b/authorization_set.cpp @@ -0,0 +1,420 @@ +/* + * 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 "authorization_set.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace keystore { + +inline bool keyParamLess(const KeyParameter& a, const KeyParameter& b) { + if (a.tag != b.tag) return a.tag < b.tag; + int retval; + switch (typeFromTag(a.tag)) { + case TagType::INVALID: + case TagType::BOOL: + return false; + case TagType::ENUM: + case TagType::ENUM_REP: + case TagType::UINT: + case TagType::UINT_REP: + return a.f.integer < b.f.integer; + case TagType::ULONG: + case TagType::ULONG_REP: + return a.f.longInteger < b.f.longInteger; + case TagType::DATE: + return a.f.dateTime < b.f.dateTime; + case TagType::BIGNUM: + case TagType::BYTES: + // Handle the empty cases. + if (a.blob.size() == 0) return b.blob.size() != 0; + if (b.blob.size() == 0) return false; + + retval = memcmp(&a.blob[0], &b.blob[0], std::min(a.blob.size(), b.blob.size())); + // if one is the prefix of the other the longer wins + if (retval == 0) return a.blob.size() < b.blob.size(); + // Otherwise a is less if a is less. + else + return retval < 0; + } + return false; +} + +inline bool keyParamEqual(const KeyParameter& a, const KeyParameter& b) { + if (a.tag != b.tag) return false; + + switch (typeFromTag(a.tag)) { + case TagType::INVALID: + case TagType::BOOL: + return true; + case TagType::ENUM: + case TagType::ENUM_REP: + case TagType::UINT: + case TagType::UINT_REP: + return a.f.integer == b.f.integer; + case TagType::ULONG: + case TagType::ULONG_REP: + return a.f.longInteger == b.f.longInteger; + case TagType::DATE: + return a.f.dateTime == b.f.dateTime; + case TagType::BIGNUM: + case TagType::BYTES: + if (a.blob.size() != b.blob.size()) return false; + return a.blob.size() == 0 || memcmp(&a.blob[0], &b.blob[0], a.blob.size()) == 0; + } + return false; +} + +void AuthorizationSet::Sort() { + std::sort(data_.begin(), data_.end(), keyParamLess); +} + +void AuthorizationSet::Deduplicate() { + if (data_.empty()) return; + + Sort(); + std::vector result; + + auto curr = data_.begin(); + auto prev = curr++; + for (; curr != data_.end(); ++prev, ++curr) { + if (prev->tag == Tag::INVALID) continue; + + if (!keyParamEqual(*prev, *curr)) { + result.emplace_back(std::move(*prev)); + } + } + result.emplace_back(std::move(*prev)); + + std::swap(data_, result); +} + +void AuthorizationSet::Union(const AuthorizationSet& other) { + data_.insert(data_.end(), other.data_.begin(), other.data_.end()); + Deduplicate(); +} + +void AuthorizationSet::Subtract(const AuthorizationSet& other) { + Deduplicate(); + + auto i = other.begin(); + while (i != other.end()) { + int pos = -1; + do { + pos = find(i->tag, pos); + if (pos != -1 && keyParamEqual(*i, data_[pos])) { + data_.erase(data_.begin() + pos); + break; + } + } while (pos != -1); + ++i; + } +} + +int AuthorizationSet::find(Tag tag, int begin) const { + auto iter = data_.begin() + (1 + begin); + + while (iter != data_.end() && iter->tag != tag) ++iter; + + if (iter != data_.end()) return iter - data_.begin(); + return -1; +} + +bool AuthorizationSet::erase(int index) { + auto pos = data_.begin() + index; + if (pos != data_.end()) { + data_.erase(pos); + return true; + } + return false; +} + +KeyParameter& AuthorizationSet::operator[](int at) { + return data_[at]; +} + +const KeyParameter& AuthorizationSet::operator[](int at) const { + return data_[at]; +} + +void AuthorizationSet::Clear() { + data_.clear(); +} + +size_t AuthorizationSet::GetTagCount(Tag tag) const { + size_t count = 0; + for (int pos = -1; (pos = find(tag, pos)) != -1;) ++count; + return count; +} + +NullOr AuthorizationSet::GetEntry(Tag tag) const { + int pos = find(tag); + if (pos == -1) return {}; + return data_[pos]; +} + +/** + * Persistent format is: + * | 32 bit indirect_size | + * -------------------------------- + * | indirect_size bytes of data | this is where the blob data is stored + * -------------------------------- + * | 32 bit element_count | number of entries + * | 32 bit elements_size | total bytes used by entries (entries have variable length) + * -------------------------------- + * | elementes_size bytes of data | where the elements are stored + */ + +/** + * Persistent format of blobs and bignums: + * | 32 bit tag | + * | 32 bit blob_length | + * | 32 bit indirect_offset | + */ + +struct OutStreams { + std::ostream& indirect; + std::ostream& elements; +}; + +OutStreams& serializeParamValue(OutStreams& out, const hidl_vec& blob) { + uint32_t buffer; + + // write blob_length + auto blob_length = blob.size(); + if (blob_length > std::numeric_limits::max()) { + out.elements.setstate(std::ios_base::badbit); + return out; + } + buffer = blob_length; + out.elements.write(reinterpret_cast(&buffer), sizeof(uint32_t)); + + // write indirect_offset + auto offset = out.indirect.tellp(); + if (offset < 0 || offset > std::numeric_limits::max() || + uint32_t(offset) + uint32_t(blob_length) < uint32_t(offset)) { // overflow check + out.elements.setstate(std::ios_base::badbit); + return out; + } + buffer = offset; + out.elements.write(reinterpret_cast(&buffer), sizeof(uint32_t)); + + // write blob to indirect stream + if (blob_length) out.indirect.write(reinterpret_cast(&blob[0]), blob_length); + + return out; +} + +template +OutStreams& serializeParamValue(OutStreams& out, const T& value) { + out.elements.write(reinterpret_cast(&value), sizeof(T)); + return out; +} + +OutStreams& serialize(TAG_INVALID_t&&, OutStreams& out, const KeyParameter&) { + // skip invalid entries. + return out; +} +template +OutStreams& serialize(T ttag, OutStreams& out, const KeyParameter& param) { + out.elements.write(reinterpret_cast(¶m.tag), sizeof(int32_t)); + return serializeParamValue(out, accessTagValue(ttag, param)); +} + +template +struct choose_serializer; +template +struct choose_serializer> { + static OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + return choose_serializer::serialize(out, param); + } +}; +template <> +struct choose_serializer<> { + static OutStreams& serialize(OutStreams& out, const KeyParameter&) { return out; } +}; +template +struct choose_serializer, Tail...> { + static OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + if (param.tag == tag) { + return keystore::serialize(TypedTag(), out, param); + } else { + return choose_serializer::serialize(out, param); + } + } +}; + +OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + return choose_serializer::serialize(out, param); +} + +std::ostream& serialize(std::ostream& out, const std::vector& params) { + std::stringstream indirect; + std::stringstream elements; + OutStreams streams = {indirect, elements}; + for (const auto& param : params) { + serialize(streams, param); + } + if (indirect.bad() || elements.bad()) { + out.setstate(std::ios_base::badbit); + return out; + } + auto pos = indirect.tellp(); + if (pos < 0 || pos > std::numeric_limits::max()) { + out.setstate(std::ios_base::badbit); + return out; + } + uint32_t indirect_size = pos; + pos = elements.tellp(); + if (pos < 0 || pos > std::numeric_limits::max()) { + out.setstate(std::ios_base::badbit); + return out; + } + uint32_t elements_size = pos; + uint32_t element_count = params.size(); + + out.write(reinterpret_cast(&indirect_size), sizeof(uint32_t)); + + pos = out.tellp(); + if (indirect_size) out << indirect.rdbuf(); + assert(out.tellp() - pos == indirect_size); + + out.write(reinterpret_cast(&element_count), sizeof(uint32_t)); + out.write(reinterpret_cast(&elements_size), sizeof(uint32_t)); + + pos = out.tellp(); + if (elements_size) out << elements.rdbuf(); + assert(out.tellp() - pos == elements_size); + + return out; +} + +struct InStreams { + std::istream& indirect; + std::istream& elements; +}; + +InStreams& deserializeParamValue(InStreams& in, hidl_vec* blob) { + uint32_t blob_length = 0; + uint32_t offset = 0; + in.elements.read(reinterpret_cast(&blob_length), sizeof(uint32_t)); + blob->resize(blob_length); + in.elements.read(reinterpret_cast(&offset), sizeof(uint32_t)); + in.indirect.seekg(offset); + in.indirect.read(reinterpret_cast(&(*blob)[0]), blob->size()); + return in; +} + +template +InStreams& deserializeParamValue(InStreams& in, T* value) { + in.elements.read(reinterpret_cast(value), sizeof(T)); + return in; +} + +InStreams& deserialize(TAG_INVALID_t&&, InStreams& in, KeyParameter*) { + // there should be no invalid KeyParamaters but if handle them as zero sized. + return in; +} + +template +InStreams& deserialize(T&& ttag, InStreams& in, KeyParameter* param) { + return deserializeParamValue(in, &accessTagValue(ttag, *param)); +} + +template +struct choose_deserializer; +template +struct choose_deserializer> { + static InStreams& deserialize(InStreams& in, KeyParameter* param) { + return choose_deserializer::deserialize(in, param); + } +}; +template <> +struct choose_deserializer<> { + static InStreams& deserialize(InStreams& in, KeyParameter*) { + // encountered an unknown tag -> fail parsing + in.elements.setstate(std::ios_base::badbit); + return in; + } +}; +template +struct choose_deserializer, Tail...> { + static InStreams& deserialize(InStreams& in, KeyParameter* param) { + if (param->tag == tag) { + return keystore::deserialize(TypedTag(), in, param); + } else { + return choose_deserializer::deserialize(in, param); + } + } +}; + +InStreams& deserialize(InStreams& in, KeyParameter* param) { + in.elements.read(reinterpret_cast(¶m->tag), sizeof(Tag)); + return choose_deserializer::deserialize(in, param); +} + +std::istream& deserialize(std::istream& in, std::vector* params) { + uint32_t indirect_size = 0; + in.read(reinterpret_cast(&indirect_size), sizeof(uint32_t)); + std::string indirect_buffer(indirect_size, '\0'); + if (indirect_buffer.size() != indirect_size) { + in.setstate(std::ios_base::badbit); + return in; + } + in.read(&indirect_buffer[0], indirect_buffer.size()); + + uint32_t element_count = 0; + in.read(reinterpret_cast(&element_count), sizeof(uint32_t)); + uint32_t elements_size = 0; + in.read(reinterpret_cast(&elements_size), sizeof(uint32_t)); + + std::string elements_buffer(elements_size, '\0'); + if (elements_buffer.size() != elements_size) { + in.setstate(std::ios_base::badbit); + return in; + } + in.read(&elements_buffer[0], elements_buffer.size()); + + if (in.bad()) return in; + + // TODO write one-shot stream buffer to avoid copying here + std::stringstream indirect(indirect_buffer); + std::stringstream elements(elements_buffer); + InStreams streams = {indirect, elements}; + + params->resize(element_count); + + for (uint32_t i = 0; i < element_count; ++i) { + deserialize(streams, &(*params)[i]); + } + return in; +} +void AuthorizationSet::Serialize(std::ostream* out) const { + serialize(*out, data_); +} +void AuthorizationSet::Deserialize(std::istream* in) { + deserialize(*in, &data_); +} + +} // namespace keystore diff --git a/authorization_set.h b/authorization_set.h new file mode 100644 index 0000000..8f68bb0 --- /dev/null +++ b/authorization_set.h @@ -0,0 +1,327 @@ +/* + * 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_VOLD_AUTHORIZATION_SET_H_ +#define SYSTEM_VOLD_AUTHORIZATION_SET_H_ + +#include + +#include "keymaster_tags.h" + +namespace keystore { + +class AuthorizationSetBuilder; + +/** + * An ordered collection of KeyParameters. It provides memory ownership and some convenient + * functionality for sorting, deduplicating, joining, and subtracting sets of KeyParameters. + * For serialization, wrap the backing store of this structure in a hidl_vec. + */ +class AuthorizationSet { + public: + /** + * Construct an empty, dynamically-allocated, growable AuthorizationSet. + */ + AuthorizationSet(){}; + + // Copy constructor. + AuthorizationSet(const AuthorizationSet& other) : data_(other.data_) {} + + // Move constructor. + AuthorizationSet(AuthorizationSet&& other) : data_(std::move(other.data_)) {} + + // Constructor from hidl_vec + AuthorizationSet(const hidl_vec& other) { *this = other; } + + // Copy assignment. + AuthorizationSet& operator=(const AuthorizationSet& other) { + data_ = other.data_; + return *this; + } + + // Move assignment. + AuthorizationSet& operator=(AuthorizationSet&& other) { + data_ = std::move(other.data_); + return *this; + } + + AuthorizationSet& operator=(const hidl_vec& other) { + if (other.size() > 0) { + data_.resize(other.size()); + for (size_t i = 0; i < data_.size(); ++i) { + /* This makes a deep copy even of embedded blobs. + * See assignment operator/copy constructor of hidl_vec.*/ + data_[i] = other[i]; + } + } + return *this; + } + + /** + * Clear existing authorization set data + */ + void Clear(); + + ~AuthorizationSet() = default; + + /** + * Returns the size of the set. + */ + size_t size() const { return data_.size(); } + + /** + * Returns true if the set is empty. + */ + bool empty() const { return size() == 0; } + + /** + * Returns the data in the set, directly. Be careful with this. + */ + const KeyParameter* data() const { return data_.data(); } + + /** + * Sorts the set + */ + void Sort(); + + /** + * Sorts the set and removes duplicates (inadvertently duplicating tags is easy to do with the + * AuthorizationSetBuilder). + */ + void Deduplicate(); + + /** + * Adds all elements from \p set that are not already present in this AuthorizationSet. As a + * side-effect, if \p set is not null this AuthorizationSet will end up sorted. + */ + void Union(const AuthorizationSet& set); + + /** + * Removes all elements in \p set from this AuthorizationSet. + */ + void Subtract(const AuthorizationSet& set); + + /** + * 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(Tag 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(int index); + + /** + * Returns iterator (pointer) to beginning of elems array, to enable STL-style iteration + */ + std::vector::const_iterator begin() const { return data_.begin(); } + + /** + * Returns iterator (pointer) one past end of elems array, to enable STL-style iteration + */ + std::vector::const_iterator end() const { return data_.end(); } + + /** + * Returns the nth element of the set. + * Like for std::vector::operator[] there is no range check performed. Use of out of range + * indices is undefined. + */ + KeyParameter& operator[](int n); + + /** + * Returns the nth element of the set. + * Like for std::vector::operator[] there is no range check performed. Use of out of range + * indices is undefined. + */ + const KeyParameter& operator[](int n) const; + + /** + * Returns true if the set contains at least one instance of \p tag + */ + bool Contains(Tag tag) const { return find(tag) != -1; } + + template + bool Contains(TypedTag ttag, const ValueT& value) const { + for (const auto& param : data_) { + auto entry = authorizationValue(ttag, param); + if (entry.isOk() && entry.value() == value) return true; + } + return false; + } + /** + * Returns the number of \p tag entries. + */ + size_t GetTagCount(Tag tag) const; + + template + inline NullOr::type&> GetTagValue(T tag) const { + auto entry = GetEntry(tag); + if (entry.isOk()) return authorizationValue(tag, entry.value()); + return {}; + } + + void push_back(const KeyParameter& param) { data_.push_back(param); } + void push_back(KeyParameter&& param) { data_.push_back(std::move(param)); } + + /** + * Append the tag and enumerated value to the set. + * "val" may be exactly one parameter unless a boolean parameter is added. + * In this case "val" is omitted. This condition is checked at compile time by Authorization() + */ + template + void push_back(TypedTagT tag, Value&&... val) { + push_back(Authorization(tag, std::forward(val)...)); + } + + template + void append(Iterator begin, Iterator end) { + while (begin != end) { + push_back(*begin); + ++begin; + } + } + + hidl_vec hidl_data() const { + hidl_vec result; + result.setToExternal(const_cast(data()), size()); + return result; + } + + void Serialize(std::ostream* out) const; + void Deserialize(std::istream* in); + + private: + NullOr GetEntry(Tag tag) const; + + std::vector data_; +}; + +class AuthorizationSetBuilder : public AuthorizationSet { + public: + template + AuthorizationSetBuilder& Authorization(TagType ttag, ValueType&&... value) { + push_back(ttag, std::forward(value)...); + return *this; + } + + template + AuthorizationSetBuilder& Authorization(TypedTag ttag, const uint8_t* data, + size_t data_length) { + hidl_vec new_blob; + new_blob.setToExternal(const_cast(data), data_length); + push_back(ttag, std::move(new_blob)); + return *this; + } + + template + AuthorizationSetBuilder& Authorization(TypedTag ttag, const char* data, + size_t data_length) { + return Authorization(ttag, reinterpret_cast(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(Digest digest) { return Authorization(TAG_DIGEST, digest); } + + AuthorizationSetBuilder& Padding(PaddingMode padding) { + return Authorization(TAG_PADDING, padding); + } +}; + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::RsaKey(uint32_t key_size, + uint64_t public_exponent) { + Authorization(TAG_ALGORITHM, 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, Algorithm::EC); + Authorization(TAG_KEY_SIZE, key_size); + return *this; +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, Algorithm::AES); + return Authorization(TAG_KEY_SIZE, key_size); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, 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, KeyPurpose::SIGN); + return Authorization(TAG_PURPOSE, KeyPurpose::VERIFY); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() { + Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT); + return Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() { + Authorization(TAG_DIGEST, Digest::NONE); + return Authorization(TAG_PADDING, PaddingMode::NONE); +} + +inline AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() { + return Authorization(TAG_BLOCK_MODE, BlockMode::ECB); +} + +} // namespace keystore + +#endif // SYSTEM_VOLD_AUTHORIZATION_SET_H_ diff --git a/keymaster_tags.h b/keymaster_tags.h new file mode 100644 index 0000000..c89354d --- /dev/null +++ b/keymaster_tags.h @@ -0,0 +1,372 @@ +/* + * 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_VOLD_KEYMASTER_TAGS_H_ +#define SYSTEM_VOLD_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. 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 KeyParameter with tag == Tag::PURPOSE + * and then to assign Algorithm::RSA to algorithm element of its union. But because the user + * must choose the union field, there could be a mismatch which the compiler has now way to + * diagnose. + * + * The machinery in this header solves these problems by describing which union field corresponds + * to which Tag. Central to this mechanism is the template TypedTag. It has zero size and binds a + * numeric Tag to a type that the compiler understands. By means of the macro DECLARE_TYPED_TAG, + * we declare types for each of the tags defined in hardware/interfaces/keymaster/2.0/types.hal. + * + * The macro DECLARE_TYPED_TAG(name) generates a typename TAG_name_t and a zero sized instance + * TAG_name. Once these typed tags have been declared we define metafunctions mapping the each tag + * to its value c++ type and the correct union element of KeyParameter. This is done by means of + * the macros MAKE_TAG_*VALUE_ACCESSOR, which generates TypedTag2ValueType, a metafunction mapping + * a typed tag to the corresponding c++ type, and access function, accessTagValue returning a + * reference to the correct element of KeyParameter. + * E.g.: + * given "KeyParameter param;" then "accessTagValue(TAG_PURPOSE, param)" + * yields a reference to param.f.purpose + * If used in an assignment the compiler can now check the compatibility of the assigned value. + * + * For convenience we also provide the constructor like function Authorization(). + * Authorization takes a typed tag and a value and checks at compile time whether the value given + * is suitable for the given tag. At runtime it creates a new KeyParameter initialized with the + * given tag and value and returns it by value. + * + * The second convenience function, authorizationValue, allows access to the KeyParameter value in + * a safe way. It takes a typed tag and a KeyParameter and returns a reference to the value wrapped + * by NullOr. NullOr has out-of-band information about whether it is save to access the wrapped + * reference. + * E.g.: + * auto param = Authorization(TAG_ALGORITM, Algorithm::RSA); + * auto value1 = authorizationValue(TAG_PURPOSE, param); + * auto value2 = authorizationValue(TAG_ALGORITM, param); + * value1.isOk() yields false, but value2.isOk() yields true, thus value2.value() is save to access. + */ + +#include +#include +#include + +namespace keystore { + +using ::android::hardware::keymaster::V3_0::Algorithm; +using ::android::hardware::keymaster::V3_0::BlockMode; +using ::android::hardware::keymaster::V3_0::Digest; +using ::android::hardware::keymaster::V3_0::EcCurve; +using ::android::hardware::keymaster::V3_0::ErrorCode; +using ::android::hardware::keymaster::V3_0::HardwareAuthToken; +using ::android::hardware::keymaster::V3_0::HardwareAuthenticatorType; +using ::android::hardware::keymaster::V3_0::IKeymasterDevice; +using ::android::hardware::keymaster::V3_0::KeyBlobUsageRequirements; +using ::android::hardware::keymaster::V3_0::KeyCharacteristics; +using ::android::hardware::keymaster::V3_0::KeyDerivationFunction; +using ::android::hardware::keymaster::V3_0::KeyFormat; +using ::android::hardware::keymaster::V3_0::KeyOrigin; +using ::android::hardware::keymaster::V3_0::KeyParameter; +using ::android::hardware::keymaster::V3_0::KeyPurpose; +using ::android::hardware::keymaster::V3_0::PaddingMode; +using ::android::hardware::keymaster::V3_0::Tag; +using ::android::hardware::keymaster::V3_0::TagType; + +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; + +// 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 int32_t KM_TAG_DIGEST_OLD = static_cast(TagType::ENUM) | 5; +static const int32_t KM_TAG_PADDING_OLD = static_cast(TagType::ENUM) | 7; + +constexpr TagType typeFromTag(Tag tag) { + return static_cast(static_cast(tag) & static_cast(0xf0000000)); +} + +/** + * TypedTag is a templatized version of Tag, which provides compile-time checking of + * keymaster tag types. Instances are convertible to Tag, so they can be used wherever + * Tag is expected, and because they encode the tag type it's possible to create + * function overloads that only operate on tags with a particular type. + */ +template +struct TypedTag { + 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), with no run-time cost. + static_assert(typeFromTag(tag) == tag_type, "mismatch between tag and tag_type"); + } + operator Tag() const { return tag; } +}; + +template +struct Tag2TypedTag { + typedef TypedTag type; +}; + +template +struct Tag2String; + +#define _TAGS_STRINGIFY(x) #x +#define TAGS_STRINGIFY(x) _TAGS_STRINGIFY(x) + +#define DECLARE_TYPED_TAG(name) \ + typedef typename Tag2TypedTag::type TAG_##name##_t; \ + extern TAG_##name##_t TAG_##name; \ + template <> \ + struct Tag2String { \ + static const char* value() { return "Tag::" TAGS_STRINGIFY(name); } \ + } + +DECLARE_TYPED_TAG(INVALID); +DECLARE_TYPED_TAG(KEY_SIZE); +DECLARE_TYPED_TAG(MAC_LENGTH); +DECLARE_TYPED_TAG(CALLER_NONCE); +DECLARE_TYPED_TAG(MIN_MAC_LENGTH); +DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT); +DECLARE_TYPED_TAG(ECIES_SINGLE_HASH_MODE); +DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID); +DECLARE_TYPED_TAG(ACTIVE_DATETIME); +DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME); +DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME); +DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS); +DECLARE_TYPED_TAG(MAX_USES_PER_BOOT); +DECLARE_TYPED_TAG(ALL_USERS); +DECLARE_TYPED_TAG(USER_ID); +DECLARE_TYPED_TAG(USER_SECURE_ID); +DECLARE_TYPED_TAG(NO_AUTH_REQUIRED); +DECLARE_TYPED_TAG(AUTH_TIMEOUT); +DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY); +DECLARE_TYPED_TAG(ALL_APPLICATIONS); +DECLARE_TYPED_TAG(APPLICATION_ID); +DECLARE_TYPED_TAG(APPLICATION_DATA); +DECLARE_TYPED_TAG(CREATION_DATETIME); +DECLARE_TYPED_TAG(ROLLBACK_RESISTANT); +DECLARE_TYPED_TAG(ROOT_OF_TRUST); +DECLARE_TYPED_TAG(ASSOCIATED_DATA); +DECLARE_TYPED_TAG(NONCE); +DECLARE_TYPED_TAG(AUTH_TOKEN); +DECLARE_TYPED_TAG(BOOTLOADER_ONLY); +DECLARE_TYPED_TAG(OS_VERSION); +DECLARE_TYPED_TAG(OS_PATCHLEVEL); +DECLARE_TYPED_TAG(UNIQUE_ID); +DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE); +DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID); +DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION); + +DECLARE_TYPED_TAG(PURPOSE); +DECLARE_TYPED_TAG(ALGORITHM); +DECLARE_TYPED_TAG(BLOCK_MODE); +DECLARE_TYPED_TAG(DIGEST); +DECLARE_TYPED_TAG(PADDING); +DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS); +DECLARE_TYPED_TAG(ORIGIN); +DECLARE_TYPED_TAG(USER_AUTH_TYPE); +DECLARE_TYPED_TAG(KDF); +DECLARE_TYPED_TAG(EC_CURVE); + +template +struct MetaList {}; + +using all_tags_t = MetaList< + TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t, TAG_MIN_MAC_LENGTH_t, + TAG_RSA_PUBLIC_EXPONENT_t, TAG_ECIES_SINGLE_HASH_MODE_t, TAG_INCLUDE_UNIQUE_ID_t, + TAG_ACTIVE_DATETIME_t, TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t, + TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_ALL_USERS_t, TAG_USER_ID_t, + TAG_USER_SECURE_ID_t, TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t, TAG_ALLOW_WHILE_ON_BODY_t, + TAG_ALL_APPLICATIONS_t, TAG_APPLICATION_ID_t, TAG_APPLICATION_DATA_t, TAG_CREATION_DATETIME_t, + TAG_ROLLBACK_RESISTANT_t, TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t, + TAG_AUTH_TOKEN_t, TAG_BOOTLOADER_ONLY_t, TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t, + TAG_ATTESTATION_CHALLENGE_t, TAG_ATTESTATION_APPLICATION_ID_t, TAG_RESET_SINCE_ID_ROTATION_t, + TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t, TAG_DIGEST_t, TAG_PADDING_t, + TAG_BLOB_USAGE_REQUIREMENTS_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_KDF_t, TAG_EC_CURVE_t>; + +/* implementation in keystore_utils.cpp */ +extern const char* stringifyTag(Tag tag); + +template +struct TypedTag2ValueType; + +#define MAKE_TAG_VALUE_ACCESSOR(tag_type, field_name) \ + template \ + struct TypedTag2ValueType> { \ + typedef decltype(static_cast(nullptr)->field_name) type; \ + }; \ + template \ + inline auto accessTagValue(TypedTag, const KeyParameter& param) \ + ->const decltype(param.field_name)& { \ + return param.field_name; \ + } \ + template \ + inline auto accessTagValue(TypedTag, KeyParameter& param) \ + ->decltype(param.field_name)& { \ + return param.field_name; \ + } + +MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG, f.longInteger) +MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG_REP, f.longInteger) +MAKE_TAG_VALUE_ACCESSOR(TagType::DATE, f.dateTime) +MAKE_TAG_VALUE_ACCESSOR(TagType::UINT, f.integer) +MAKE_TAG_VALUE_ACCESSOR(TagType::UINT_REP, f.integer) +MAKE_TAG_VALUE_ACCESSOR(TagType::BOOL, f.boolValue) +MAKE_TAG_VALUE_ACCESSOR(TagType::BYTES, blob) +MAKE_TAG_VALUE_ACCESSOR(TagType::BIGNUM, blob) + +#define MAKE_TAG_ENUM_VALUE_ACCESSOR(typed_tag, field_name) \ + template <> \ + struct TypedTag2ValueType { \ + typedef decltype(static_cast(nullptr)->field_name) type; \ + }; \ + inline auto accessTagValue(decltype(typed_tag), const KeyParameter& param) \ + ->const decltype(param.field_name)& { \ + return param.field_name; \ + } \ + inline auto accessTagValue(decltype(typed_tag), KeyParameter& param) \ + ->decltype(param.field_name)& { \ + return param.field_name; \ + } + +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ALGORITHM, f.algorithm) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOB_USAGE_REQUIREMENTS, f.keyBlobUsageRequirements) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOCK_MODE, f.blockMode) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_DIGEST, f.digest) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_EC_CURVE, f.ecCurve) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_KDF, f.keyDerivationFunction) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ORIGIN, f.origin) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PADDING, f.paddingMode) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PURPOSE, f.purpose) +MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_USER_AUTH_TYPE, f.hardwareAuthenticatorType) + +template +inline KeyParameter makeKeyParameter(TypedTag ttag, ValueT&& value) { + KeyParameter param; + param.tag = tag; + param.f.longInteger = 0; + accessTagValue(ttag, param) = std::forward(value); + return param; +} + +// the boolean case +template +inline KeyParameter makeKeyParameter(TypedTag) { + KeyParameter param; + param.tag = tag; + param.f.boolValue = true; + return param; +} + +template +struct FirstOrNoneHelper; +template +struct FirstOrNoneHelper { + typedef First type; +}; +template <> +struct FirstOrNoneHelper<> { + struct type {}; +}; + +template +using FirstOrNone = typename FirstOrNoneHelper::type; + +template +inline KeyParameter Authorization(TypedTag ttag, Args&&... args) { + static_assert(tag_type != TagType::BOOL || (sizeof...(args) == 0), + "TagType::BOOL Authorizations do not take parameters. Presence is truth."); + static_assert(tag_type == TagType::BOOL || (sizeof...(args) == 1), + "Authorization other then TagType::BOOL take exactly one parameter."); + static_assert( + tag_type == TagType::BOOL || + std::is_convertible>>, + typename TypedTag2ValueType>::type>::value, + "Invalid argument type for given tag."); + + return makeKeyParameter(ttag, std::forward(args)...); +} + +/** + * This class wraps a (mostly return) value and stores whether or not the wrapped value is valid out + * of band. Note that if the wrapped value is a reference it is unsafe to access the value if + * !isOk(). If the wrapped type is a pointer or value and !isOk(), it is still safe to access the + * wrapped value. In this case the pointer will be NULL though, and the value will be default + * constructed. + */ +template +class NullOr { + template + struct reference_initializer { + static T&& init() { return *static_cast*>(nullptr); } + }; + template + struct pointer_initializer { + static T init() { return nullptr; } + }; + template + struct value_initializer { + static T init() { return T(); } + }; + template + using initializer_t = std::conditional_t< + std::is_lvalue_reference::value, reference_initializer, + std::conditional_t::value, pointer_initializer, value_initializer>>; + + public: + NullOr() : value_(initializer_t::init()), null_(true) {} + NullOr(ValueT&& value) : value_(std::forward(value)), null_(false) {} + + bool isOk() const { return !null_; } + + const ValueT& value() const & { return value_; } + ValueT& value() & { return value_; } + ValueT&& value() && { return std::move(value_); } + + private: + ValueT value_; + bool null_; +}; + +template +std::remove_reference_t NullOrOr(T&& v) { + if (v.isOk()) return v; + return {}; +} + +template +std::remove_reference_t NullOrOr(Head&& head, Tail&&... tail) { + if (head.isOk()) return head; + return NullOrOr(std::forward(tail)...); +} + +template +std::remove_reference_t defaultOr(NullOr&& optional, Default&& def) { + static_assert( + std::is_convertible, std::remove_reference_t>::value, + "Type of default value must match the type wrapped by NullOr"); + if (optional.isOk()) return optional.value(); + return def; +} + +template +inline NullOr>::type&> authorizationValue( + TypedTag ttag, const KeyParameter& param) { + if (tag != param.tag) return {}; + return accessTagValue(ttag, param); +} + +} // namespace keystore + +#endif // SYSTEM_SECURITY_KEYSTORE_KEYMASTER_TAGS_H_ diff --git a/keystore_hidl_support.h b/keystore_hidl_support.h new file mode 100644 index 0000000..d21e02a --- /dev/null +++ b/keystore_hidl_support.h @@ -0,0 +1,108 @@ +/* + ** + ** 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_VOLD_KEYSTORE_HIDL_SUPPORT_H_ +#define SYSTEM_VOLD_KEYSTORE_HIDL_SUPPORT_H_ + +#include +#include +#include + +#include + +#include "keymaster_tags.h" + +namespace keystore { + +inline static std::ostream& formatArgs(std::ostream& out) { + return out; +} + +template +inline static std::ostream& formatArgs(std::ostream& out, First&& first, Args&&... args) { + out << first; + return formatArgs(out, args...); +} + +template +inline static std::string argsToString(Args&&... args) { + std::stringstream s; + formatArgs(s, args...); + return s.str(); +} + +template +inline static ErrorCode ksHandleHidlError(const Return& error, Msgs&&... msgs) { + if (!error.isOk()) { + ALOGE("HIDL call failed with %s @ %s", error.description().c_str(), + argsToString(msgs...).c_str()); + return ErrorCode::UNKNOWN_ERROR; + } + return ErrorCode(error); +} +template +inline static ErrorCode ksHandleHidlError(const Return& error, Msgs&&... msgs) { + if (!error.isOk()) { + ALOGE("HIDL call failed with %s @ %s", error.description().c_str(), + argsToString(msgs...).c_str()); + return ErrorCode::UNKNOWN_ERROR; + } + return ErrorCode::OK; +} + +#define KS_HANDLE_HIDL_ERROR(rc) \ + ::keystore::ksHandleHidlError(rc, __FILE__, ":", __LINE__, ":", __PRETTY_FUNCTION__) + +inline static hidl_vec blob2hidlVec(const uint8_t* data, const size_t length, + bool inPlace = true) { + hidl_vec result; + if (inPlace) + result.setToExternal(const_cast(data), length); + else { + result.resize(length); + memcpy(&result[0], data, length); + } + return result; +} + +inline static hidl_vec blob2hidlVec(const std::string& value) { + hidl_vec result; + result.setToExternal( + reinterpret_cast(const_cast(value.data())), + static_cast(value.size())); + return result; +} + +inline static hidl_vec blob2hidlVec(const std::vector& blob) { + hidl_vec result; + result.setToExternal(const_cast(blob.data()), static_cast(blob.size())); + return result; +} + +template +inline static OutIter copy_bytes_to_iterator(const T& value, OutIter dest) { + const uint8_t* value_ptr = reinterpret_cast(&value); + return std::copy(value_ptr, value_ptr + sizeof(value), dest); +} + +inline std::string hidlVec2String(const hidl_vec& value) { + return std::string(reinterpret_cast(&value[0]), value.size()); +} + +} // namespace keystore + +#endif // SYSTEM_VOLD_KEYSTORE_HIDL_SUPPORT_H_