| /* |
| * Copyright 2017 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 <keymasterV4_0/authorization_set.h> |
| |
| #include <assert.h> |
| |
| #include <android-base/logging.h> |
| |
| namespace android { |
| namespace hardware { |
| namespace keymaster { |
| namespace V4_0 { |
| |
| 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; |
| } |
| |
| 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<KeyParameter> 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.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 && keyParamEqual(*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 hidl_vec<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<TypedTag<tag_type, tag>, Tail...> { |
| static OutStreams& serialize(OutStreams& out, const KeyParameter& param) { |
| if (param.tag == tag) { |
| return V4_0::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, hidl_vec<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 KeyParamaters 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 V4_0::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<V4_0::BlockMode> blockModes) { |
| for (auto mode : blockModes) { |
| push_back(TAG_BLOCK_MODE, mode); |
| } |
| return *this; |
| } |
| |
| AuthorizationSetBuilder& AuthorizationSetBuilder::Digest(std::vector<V4_0::Digest> digests) { |
| for (auto digest : digests) { |
| push_back(TAG_DIGEST, digest); |
| } |
| return *this; |
| } |
| |
| AuthorizationSetBuilder& AuthorizationSetBuilder::Padding( |
| std::initializer_list<V4_0::PaddingMode> paddingModes) { |
| for (auto paddingMode : paddingModes) { |
| push_back(TAG_PADDING, paddingMode); |
| } |
| return *this; |
| } |
| |
| } // namespace V4_0 |
| } // namespace keymaster |
| } // namespace hardware |
| } // namespace android |