/* * 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 #include #include 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 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 doKeep) { std::vector 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 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& 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. ++out.skipped; 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& 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 struct choose_serializer, Tail...> { static OutStreams& serialize(OutStreams& out, const KeyParameter& param) { if (param.tag == tag) { return V4_0::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, 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::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() - streams.skipped; 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; size_t invalids; }; 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. ++in.invalids; 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 V4_0::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, 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 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 blockModes) { for (auto mode : blockModes) { push_back(TAG_BLOCK_MODE, mode); } return *this; } AuthorizationSetBuilder& AuthorizationSetBuilder::Digest(std::vector digests) { for (auto digest : digests) { push_back(TAG_DIGEST, digest); } return *this; } AuthorizationSetBuilder& AuthorizationSetBuilder::Padding( std::initializer_list paddingModes) { for (auto paddingMode : paddingModes) { push_back(TAG_PADDING, paddingMode); } return *this; } } // namespace V4_0 } // namespace keymaster } // namespace hardware } // namespace android