f452774030
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
420 lines
13 KiB
C++
420 lines
13 KiB
C++
/*
|
|
* 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 <assert.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <istream>
|
|
#include <limits>
|
|
#include <ostream>
|
|
|
|
#include <new>
|
|
|
|
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<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.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<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;
|
|
};
|
|
|
|
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.
|
|
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&) { 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 keystore::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};
|
|
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();
|
|
|
|
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;
|
|
};
|
|
|
|
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.
|
|
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 keystore::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};
|
|
|
|
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
|