summaryrefslogtreecommitdiff
path: root/libfec
diff options
context:
space:
mode:
authorSami Tolvanen <samitolvanen@google.com>2016-06-03 13:56:07 -0700
committerSami Tolvanen <samitolvanen@google.com>2016-06-08 09:41:23 -0700
commit6df850bee54cbbac57aea05feb2790218e145325 (patch)
tree8295ad00ba6cd77586a48816f73b477560c68347 /libfec
parent86bebf8ff08a2573ad505b93376b7fb15a3b1a56 (diff)
downloadextras-6df850bee54cbbac57aea05feb2790218e145325.tar.gz
libfec: return raw and corrected verity metadata signatures
Since it's not possible for libfec to identify whether the signature field was successfully corrected, return both and allow the caller to validate either signature. Bug: 28943429 Change-Id: Ie913c21ba1d07d6df4c6feeb7226b2ec963f4d19 (cherry picked from commit 65cbaeb020b209f3d75d594ebbe49a609dd3c7e7)
Diffstat (limited to 'libfec')
-rw-r--r--libfec/fec_open.cpp5
-rw-r--r--libfec/fec_private.h1
-rw-r--r--libfec/fec_verity.cpp103
-rw-r--r--libfec/include/fec/io.h1
4 files changed, 79 insertions, 31 deletions
diff --git a/libfec/fec_open.cpp b/libfec/fec_open.cpp
index c1d4afdb..0e41bf4f 100644
--- a/libfec/fec_open.cpp
+++ b/libfec/fec_open.cpp
@@ -460,7 +460,10 @@ int fec_verity_get_metadata(struct fec_handle *f, struct fec_verity_metadata *da
data->disabled = f->verity.disabled;
data->data_size = f->data_size;
- memcpy(data->signature, f->verity.header.signature, sizeof(data->signature));
+ memcpy(data->signature, f->verity.header.signature,
+ sizeof(data->signature));
+ memcpy(data->ecc_signature, f->verity.ecc_header.signature,
+ sizeof(data->ecc_signature));
data->table = f->verity.table;
data->table_length = f->verity.header.length;
diff --git a/libfec/fec_private.h b/libfec/fec_private.h
index c54b5b17..0d94228e 100644
--- a/libfec/fec_private.h
+++ b/libfec/fec_private.h
@@ -88,6 +88,7 @@ struct verity_info {
uint64_t metadata_start; /* offset in file */
uint8_t zero_hash[SHA256_DIGEST_LENGTH];
verity_header header;
+ verity_header ecc_header;
};
struct verity_block_info {
diff --git a/libfec/fec_verity.cpp b/libfec/fec_verity.cpp
index ca4572c6..393e9624 100644
--- a/libfec/fec_verity.cpp
+++ b/libfec/fec_verity.cpp
@@ -481,6 +481,45 @@ static int rewrite_metadata(fec_handle *f, uint64_t offset)
return raw_pwrite(f, metadata.get(), VERITY_METADATA_SIZE, offset);
}
+static int validate_header(const fec_handle *f, const verity_header *header,
+ uint64_t offset)
+{
+ check(f);
+ check(header);
+
+ if (header->magic != VERITY_MAGIC &&
+ header->magic != VERITY_MAGIC_DISABLE) {
+ return -1;
+ }
+
+ if (header->version != VERITY_VERSION) {
+ error("unsupported verity version %u", header->version);
+ return -1;
+ }
+
+ if (header->length < VERITY_MIN_TABLE_SIZE ||
+ header->length > VERITY_MAX_TABLE_SIZE) {
+ error("invalid verity table size: %u; expected ["
+ stringify(VERITY_MIN_TABLE_SIZE) ", "
+ stringify(VERITY_MAX_TABLE_SIZE) ")", header->length);
+ return -1;
+ }
+
+ /* signature is skipped, because for our purposes it won't matter from
+ where the data originates; the caller of the library is responsible
+ for signature verification */
+
+ if (offset > UINT64_MAX - header->length) {
+ error("invalid verity table length: %u", header->length);
+ return -1;
+ } else if (offset + header->length >= f->data_size) {
+ error("invalid verity table length: %u", header->length);
+ return -1;
+ }
+
+ return 0;
+}
+
/* attempts to read verity metadata from `f->fd' position `offset'; if in r/w
mode, rewrites the metadata if it had errors */
int verity_parse_header(fec_handle *f, uint64_t offset)
@@ -497,55 +536,59 @@ int verity_parse_header(fec_handle *f, uint64_t offset)
verity_info *v = &f->verity;
uint64_t errors = f->errors;
- if (fec_pread(f, &v->header, sizeof(v->header), offset) !=
- sizeof(v->header)) {
+ if (!raw_pread(f, &v->header, sizeof(v->header), offset)) {
error("failed to read verity header: %s", strerror(errno));
return -1;
}
- verity_header raw_header;
-
- if (!raw_pread(f, &raw_header, sizeof(raw_header), offset)) {
- error("failed to read verity header: %s", strerror(errno));
- return -1;
- }
/* use raw data to check for the alternative magic, because it will
be error corrected to VERITY_MAGIC otherwise */
- if (raw_header.magic == VERITY_MAGIC_DISABLE) {
+ if (v->header.magic == VERITY_MAGIC_DISABLE) {
/* this value is not used by us, but can be used by a caller to
decide whether dm-verity should be enabled */
v->disabled = true;
- } else if (v->header.magic != VERITY_MAGIC) {
- return -1;
}
- if (v->header.version != VERITY_VERSION) {
- error("unsupported verity version %u", v->header.version);
+ if (fec_pread(f, &v->ecc_header, sizeof(v->ecc_header), offset) !=
+ sizeof(v->ecc_header)) {
+ warn("failed to read verity header: %s", strerror(errno));
return -1;
}
- if (v->header.length < VERITY_MIN_TABLE_SIZE ||
- v->header.length > VERITY_MAX_TABLE_SIZE) {
- error("invalid verity table size: %u; expected ["
- stringify(VERITY_MIN_TABLE_SIZE) ", "
- stringify(VERITY_MAX_TABLE_SIZE) ")", v->header.length);
- return -1;
- }
+ if (validate_header(f, &v->header, offset)) {
+ /* raw verity header is invalid; this could be due to corruption, or
+ due to missing verity metadata */
- v->metadata_start = offset;
+ if (validate_header(f, &v->ecc_header, offset)) {
+ return -1; /* either way, we cannot recover */
+ }
- /* signature is skipped, because for our purposes it won't matter from
- where the data originates; the caller of the library is responsible
- for signature verification */
+ /* report mismatching fields */
+ if (!v->disabled && v->header.magic != v->ecc_header.magic) {
+ warn("corrected verity header magic");
+ v->header.magic = v->ecc_header.magic;
+ }
- if (offset > UINT64_MAX - v->header.length) {
- error("invalid verity table length: %u", v->header.length);
- return -1;
- } else if (offset + v->header.length >= f->data_size) {
- error("invalid verity table length: %u", v->header.length);
- return -1;
+ if (v->header.version != v->ecc_header.version) {
+ warn("corrected verity header version");
+ v->header.version = v->ecc_header.version;
+ }
+
+ if (v->header.length != v->ecc_header.length) {
+ warn("corrected verity header length");
+ v->header.length = v->ecc_header.length;
+ }
+
+ if (memcmp(v->header.signature, v->ecc_header.signature,
+ sizeof(v->header.signature))) {
+ warn("corrected verity header signature");
+ /* we have no way of knowing which signature is correct, if either
+ of them is */
+ }
}
+ v->metadata_start = offset;
+
if (parse_table(f, offset + sizeof(v->header), v->header.length) == -1) {
return -1;
}
diff --git a/libfec/include/fec/io.h b/libfec/include/fec/io.h
index 8c0759b4..d8c88daa 100644
--- a/libfec/include/fec/io.h
+++ b/libfec/include/fec/io.h
@@ -72,6 +72,7 @@ struct fec_verity_metadata {
bool disabled;
uint64_t data_size;
uint8_t signature[ANDROID_PUBKEY_MODULUS_SIZE];
+ uint8_t ecc_signature[ANDROID_PUBKEY_MODULUS_SIZE];
const char *table;
uint32_t table_length;
};