diff options
Diffstat (limited to 'security/keymint/support/authorization_set.cpp')
-rw-r--r-- | security/keymint/support/authorization_set.cpp | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/security/keymint/support/authorization_set.cpp b/security/keymint/support/authorization_set.cpp new file mode 100644 index 0000000000..aa9638f256 --- /dev/null +++ b/security/keymint/support/authorization_set.cpp @@ -0,0 +1,526 @@ +/* + * Copyright 2020 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 <keymint_support/authorization_set.h> + +#include <assert.h> +#include <sstream> + +#include <android-base/logging.h> + +#include <android/hardware/security/keymint/Algorithm.h> +#include <android/hardware/security/keymint/BlockMode.h> +#include <android/hardware/security/keymint/Digest.h> +#include <android/hardware/security/keymint/KeyParameter.h> +#include <android/hardware/security/keymint/KeyPurpose.h> +#include <android/hardware/security/keymint/TagType.h> + +namespace android::hardware::security::keymint { + +void AuthorizationSet::Sort() { + std::sort(data_.begin(), data_.end()); +} + +void AuthorizationSet::Deduplicate() { + if (data_.empty()) return; + + Sort(); + std::vector<KeyParameter> result; + + auto curr = data_.begin(); + auto prev = curr++; + for (; curr != data_.end(); ++prev, ++curr) { + if (prev->tag == Tag::INVALID) continue; + + if (*prev != *curr) { + result.push_back(std::move(*prev)); + } + } + result.push_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 && (*i == data_[pos])) { + data_.erase(data_.begin() + pos); + break; + } + } while (pos != -1); + ++i; + } +} + +void AuthorizationSet::Filter(std::function<bool(const KeyParameter&)> doKeep) { + std::vector<KeyParameter> result; + for (auto& param : data_) { + if (doKeep(param)) { + result.push_back(std::move(param)); + } + } + std::swap(data_, result); +} + +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; +} + +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; +} + +NullOr<const KeyParameter&> 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; + size_t skipped; +}; + +OutStreams& serializeParamValue(OutStreams& out, const vector<uint8_t>& blob) { + uint32_t buffer; + + // write blob_length + auto blob_length = blob.size(); + if (blob_length > std::numeric_limits<uint32_t>::max()) { + out.elements.setstate(std::ios_base::badbit); + return out; + } + buffer = blob_length; + out.elements.write(reinterpret_cast<const char*>(&buffer), sizeof(uint32_t)); + + // write indirect_offset + auto offset = out.indirect.tellp(); + if (offset < 0 || offset > std::numeric_limits<uint32_t>::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<const char*>(&buffer), sizeof(uint32_t)); + + // write blob to indirect stream + if (blob_length) out.indirect.write(reinterpret_cast<const char*>(&blob[0]), blob_length); + + return out; +} + +template <typename T> +OutStreams& serializeParamValue(OutStreams& out, const T& value) { + out.elements.write(reinterpret_cast<const char*>(&value), sizeof(T)); + return out; +} + +OutStreams& serialize(TAG_INVALID_t&&, OutStreams& out, const KeyParameter&) { + // skip invalid entries. + ++out.skipped; + return out; +} +template <typename T> +OutStreams& serialize(T ttag, OutStreams& out, const KeyParameter& param) { + out.elements.write(reinterpret_cast<const char*>(¶m.tag), sizeof(int32_t)); + return serializeParamValue(out, accessTagValue(ttag, param)); +} + +template <typename... T> +struct choose_serializer; +template <typename... Tags> +struct choose_serializer<MetaList<Tags...>> { + static OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + return choose_serializer<Tags...>::serialize(out, param); + } +}; + +template <> +struct choose_serializer<> { + static OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + LOG(WARNING) << "Trying to serialize unknown tag " << unsigned(param.tag) + << ". Did you forget to add it to all_tags_t?"; + ++out.skipped; + return out; + } +}; + +template <TagType tag_type, Tag tag, typename... Tail> +struct choose_serializer<android::hardware::security::keymint::TypedTag<tag_type, tag>, Tail...> { + static OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + if (param.tag == tag) { + return android::hardware::security::keymint::serialize(TypedTag<tag_type, tag>(), out, + param); + } else { + return choose_serializer<Tail...>::serialize(out, param); + } + } +}; + +OutStreams& serialize(OutStreams& out, const KeyParameter& param) { + return choose_serializer<all_tags_t>::serialize(out, param); +} + +std::ostream& serialize(std::ostream& out, const std::vector<KeyParameter>& params) { + std::stringstream indirect; + std::stringstream elements; + OutStreams streams = {indirect, elements, 0}; + 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<uint32_t>::max()) { + out.setstate(std::ios_base::badbit); + return out; + } + uint32_t indirect_size = pos; + pos = elements.tellp(); + if (pos < 0 || pos > std::numeric_limits<uint32_t>::max()) { + out.setstate(std::ios_base::badbit); + return out; + } + uint32_t elements_size = pos; + uint32_t element_count = params.size() - streams.skipped; + + out.write(reinterpret_cast<const char*>(&indirect_size), sizeof(uint32_t)); + + pos = out.tellp(); + if (indirect_size) out << indirect.rdbuf(); + assert(out.tellp() - pos == indirect_size); + + out.write(reinterpret_cast<const char*>(&element_count), sizeof(uint32_t)); + out.write(reinterpret_cast<const char*>(&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; + size_t invalids; +}; + +InStreams& deserializeParamValue(InStreams& in, vector<uint8_t>* blob) { + uint32_t blob_length = 0; + uint32_t offset = 0; + in.elements.read(reinterpret_cast<char*>(&blob_length), sizeof(uint32_t)); + blob->resize(blob_length); + in.elements.read(reinterpret_cast<char*>(&offset), sizeof(uint32_t)); + in.indirect.seekg(offset); + in.indirect.read(reinterpret_cast<char*>(&(*blob)[0]), blob->size()); + return in; +} + +template <typename T> +InStreams& deserializeParamValue(InStreams& in, T* value) { + in.elements.read(reinterpret_cast<char*>(value), sizeof(T)); + return in; +} + +InStreams& deserialize(TAG_INVALID_t&&, InStreams& in, KeyParameter*) { + // there should be no invalid KeyParameters but if handle them as zero sized. + ++in.invalids; + return in; +} + +template <typename T> +InStreams& deserialize(T&& ttag, InStreams& in, KeyParameter* param) { + return deserializeParamValue(in, &accessTagValue(ttag, *param)); +} + +template <typename... T> +struct choose_deserializer; +template <typename... Tags> +struct choose_deserializer<MetaList<Tags...>> { + static InStreams& deserialize(InStreams& in, KeyParameter* param) { + return choose_deserializer<Tags...>::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 <TagType tag_type, Tag tag, typename... Tail> +struct choose_deserializer<TypedTag<tag_type, tag>, Tail...> { + static InStreams& deserialize(InStreams& in, KeyParameter* param) { + if (param->tag == tag) { + return android::hardware::security::keymint::deserialize(TypedTag<tag_type, tag>(), in, + param); + } else { + return choose_deserializer<Tail...>::deserialize(in, param); + } + } +}; + +InStreams& deserialize(InStreams& in, KeyParameter* param) { + in.elements.read(reinterpret_cast<char*>(¶m->tag), sizeof(Tag)); + return choose_deserializer<all_tags_t>::deserialize(in, param); +} + +std::istream& deserialize(std::istream& in, std::vector<KeyParameter>* params) { + uint32_t indirect_size = 0; + in.read(reinterpret_cast<char*>(&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<char*>(&element_count), sizeof(uint32_t)); + uint32_t elements_size = 0; + in.read(reinterpret_cast<char*>(&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, 0}; + + params->resize(element_count); + + for (uint32_t i = 0; i < element_count; ++i) { + deserialize(streams, &(*params)[i]); + } + + /* + * There are legacy blobs which have invalid tags in them due to a bug during serialization. + * This makes sure that invalid tags are filtered from the result before it is returned. + */ + if (streams.invalids > 0) { + std::vector<KeyParameter> filtered(element_count - streams.invalids); + auto ifiltered = filtered.begin(); + for (auto& p : *params) { + if (p.tag != Tag::INVALID) { + *ifiltered++ = std::move(p); + } + } + *params = std::move(filtered); + } + return in; +} + +void AuthorizationSet::Serialize(std::ostream* out) const { + serialize(*out, data_); +} + +void AuthorizationSet::Deserialize(std::istream* in) { + deserialize(*in, &data_); +} + +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; +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, Algorithm::EC); + Authorization(TAG_KEY_SIZE, key_size); + return *this; +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(EcCurve curve) { + Authorization(TAG_ALGORITHM, Algorithm::EC); + Authorization(TAG_EC_CURVE, curve); + return *this; +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, Algorithm::AES); + return Authorization(TAG_KEY_SIZE, key_size); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, Algorithm::TRIPLE_DES); + return Authorization(TAG_KEY_SIZE, key_size); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) { + Authorization(TAG_ALGORITHM, Algorithm::HMAC); + Authorization(TAG_KEY_SIZE, key_size); + return SigningKey(); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::RsaSigningKey(uint32_t key_size, + uint64_t public_exponent) { + RsaKey(key_size, public_exponent); + return SigningKey(); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::RsaEncryptionKey(uint32_t key_size, + uint64_t public_exponent) { + RsaKey(key_size, public_exponent); + return EncryptionKey(); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) { + EcdsaKey(key_size); + return SigningKey(); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(EcCurve curve) { + EcdsaKey(curve); + return SigningKey(); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::AesEncryptionKey(uint32_t key_size) { + AesKey(key_size); + return EncryptionKey(); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesEncryptionKey(uint32_t key_size) { + TripleDesKey(key_size); + return EncryptionKey(); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::SigningKey() { + Authorization(TAG_PURPOSE, KeyPurpose::SIGN); + return Authorization(TAG_PURPOSE, KeyPurpose::VERIFY); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() { + Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT); + return Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() { + Authorization(TAG_DIGEST, Digest::NONE); + return Authorization(TAG_PADDING, PaddingMode::NONE); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() { + return Authorization(TAG_BLOCK_MODE, BlockMode::ECB); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMinMacLen(uint32_t minMacLength) { + return BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MIN_MAC_LENGTH, minMacLength); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMacLen(uint32_t macLength) { + return BlockMode(BlockMode::GCM) + .Padding(PaddingMode::NONE) + .Authorization(TAG_MAC_LENGTH, macLength); +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::BlockMode( + std::initializer_list<android::hardware::security::keymint::BlockMode> blockModes) { + for (auto mode : blockModes) { + push_back(TAG_BLOCK_MODE, mode); + } + return *this; +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::Digest(std::vector<keymint::Digest> digests) { + for (auto digest : digests) { + push_back(TAG_DIGEST, digest); + } + return *this; +} + +AuthorizationSetBuilder& AuthorizationSetBuilder::Padding( + std::initializer_list<PaddingMode> paddingModes) { + for (auto paddingMode : paddingModes) { + push_back(TAG_PADDING, paddingMode); + } + return *this; +} + +} // namespace android::hardware::security::keymint |