581 lines
18 KiB
C++
581 lines
18 KiB
C++
/*
|
|
* 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
|