diff --git a/Makefile.am b/Makefile.am index 22995db..4451d1a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -48,6 +48,7 @@ src/crypto/hmac.c \ src/crypto/sha1.c \ src/crypto/sha2.c \ + src/keystore.c \ src/barebox-state.c barebox_state_CFLAGS = $(LIBDT_CFLAGS) barebox_state_LDADD = src/libdt-utils.la diff --git a/src/barebox-state.c b/src/barebox-state.c index 840c700..61ea160 100644 --- a/src/barebox-state.c +++ b/src/barebox-state.c @@ -36,6 +36,7 @@ #include #include #include +#include static int verbose; @@ -59,7 +60,7 @@ struct state { struct device_d dev; - const struct device_node *root; + struct device_node *root; struct list_head variables; const char *name; struct list_head list; @@ -74,6 +75,7 @@ const char *name; const char *of_path; const char *path; + struct digest *digest; }; enum state_variable_type { @@ -764,6 +766,16 @@ if (ret) goto out; + if (state->backend->digest) { + p = of_new_property(new_node, "algo", + digest_name(state->backend->digest), + strlen(digest_name(state->backend->digest)) + 1); + if (!p) { + ret = -ENOMEM; + goto out; + } + } + /* address-cells + size-cells */ ret = of_property_write_u32(root, "#address-cells", 1); if (ret) @@ -1059,7 +1071,7 @@ struct state_backend_raw { struct state_backend backend; unsigned long size_data; /* The raw data size (without header) */ - unsigned long size_full; /* The size header + raw data */ + unsigned long size_full; /* The size header + raw data + hmac */ unsigned long stride; /* The stride size in bytes of the copies */ off_t offset; /* offset in the storage file */ size_t size; /* size of the storage area */ @@ -1082,8 +1094,9 @@ struct state_variable *sv; struct backend_raw_header header = {}; unsigned long max_len; + int d_len = 0; int ret; - void *buf; + void *buf, *data, *hmac; max_len = backend_raw->stride; @@ -1111,6 +1124,11 @@ return -EINVAL; } + if (backend_raw->backend.digest) { + d_len = digest_length(backend_raw->backend.digest); + max_len -= d_len; + } + if (header.data_len > max_len) { dev_err(&state->dev, "invalid data_len %u in header, max is %lu\n", @@ -1118,13 +1136,19 @@ return -EINVAL; } - buf = xzalloc(header.data_len); + buf = xzalloc(sizeof(header) + header.data_len + d_len); + data = buf + sizeof(header); + hmac = data + header.data_len; - ret = read_full(fd, buf, header.data_len); + ret = lseek(fd, offset, SEEK_SET); if (ret < 0) goto out_free; - crc = crc32(0, buf, header.data_len); + ret = read_full(fd, buf, sizeof(header) + header.data_len + d_len); + if (ret < 0) + goto out_free; + + crc = crc32(0, data, header.data_len); if (crc != header.data_crc) { dev_err(&state->dev, "invalid crc, calculated 0x%08x, found 0x%08x\n", @@ -1133,10 +1157,27 @@ goto out_free; } + if (backend_raw->backend.digest) { + struct digest *d = backend_raw->backend.digest; + + ret = digest_init(d); + if (ret) + goto out_free; + + /* hmac over header and data */ + ret = digest_update(d, buf, sizeof(header) + header.data_len); + if (ret) + goto out_free; + + ret = digest_verify(d, hmac); + if (ret < 0) + goto out_free; + } + list_for_each_entry(sv, &state->variables, list) { if (sv->start + sv->size > header.data_len) break; - memcpy(sv->raw, buf + sv->start, sv->size); + memcpy(sv->raw, data + sv->start, sv->size); } free(buf); @@ -1207,7 +1248,7 @@ struct state_backend_raw *backend_raw = container_of(backend, struct state_backend_raw, backend); int ret = 0, fd, i; - void *buf, *data; + void *buf, *data, *hmac; struct backend_raw_header *header; struct state_variable *sv; @@ -1215,6 +1256,7 @@ header = buf; data = buf + sizeof(*header); + hmac = data + backend_raw->size_data; list_for_each_entry(sv, &state->variables, list) memcpy(data + sv->start, sv->raw, sv->size); @@ -1225,6 +1267,23 @@ header->header_crc = crc32(0, header, sizeof(*header) - sizeof(uint32_t)); + if (backend_raw->backend.digest) { + struct digest *d = backend_raw->backend.digest; + + ret = digest_init(d); + if (ret) + goto out_free; + + /* hmac over header and data */ + ret = digest_update(d, buf, sizeof(*header) + backend_raw->size_data); + if (ret) + goto out_free; + + ret = digest_final(d, hmac); + if (ret < 0) + goto out_free; + } + fd = open(backend->path, O_WRONLY); if (fd < 0) goto out_free; @@ -1309,6 +1368,41 @@ return ret; } +static int state_backend_raw_file_init_digest(struct state *state, struct state_backend_raw *backend_raw) +{ + struct digest *digest; + const char *algo; + const unsigned char *key; + int key_len, ret; + + ret = of_property_read_string(state->root, "algo", &algo); + if (ret == -EINVAL) /* -EINVAL == does not exist */ + return 0; + else if (ret) + return ret; + + ret = keystore_get_secret(state->name, &key, &key_len); + if (ret) + return ret; + + digest = digest_alloc(algo); + if (!digest) { + dev_err(&state->dev, "unsupported algo %s\n", algo); + return -EINVAL; + } + + ret = digest_set_key(digest, key, key_len); + if (ret) { + digest_free(digest); + return ret; + } + + backend_raw->backend.digest = digest; + backend_raw->size_full += digest_length(digest); + + return 0; +} + /* * state_backend_raw_file - create a raw file backend store for a state instance * @@ -1366,6 +1460,10 @@ state->backend = backend; + ret = state_backend_raw_file_init_digest(state, backend_raw); + if (ret) + return ret; + ret = mtd_get_meminfo(backend->path, &meminfo); if (!ret && !(meminfo.flags & MTD_NO_ERASE)) { backend_raw->need_erase = true; @@ -1387,6 +1485,9 @@ return 0; err: + if (backend_raw->backend.digest) + digest_free(backend_raw->backend.digest); + free(backend_raw); return ret; } diff --git a/src/keystore.c b/src/keystore.c new file mode 100644 index 0000000..da40d41 --- /dev/null +++ b/src/keystore.c @@ -0,0 +1,19 @@ +/* + * keystore - simple keystore + * + * Copyright (c) 2015 Pengutronix, Marc Kleine-Budde + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +int __attribute__((weak)) keystore_get_secret(const char *name, const unsigned char **key, int *key_len) +{ + return 0; +} diff --git a/src/keystore.h b/src/keystore.h new file mode 100644 index 0000000..193a413 --- /dev/null +++ b/src/keystore.h @@ -0,0 +1,24 @@ +/* + * (C) Copyright 2015 Pengutronix, Marc Kleine-Budde + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __KEYSTORE_H +#define __KEYSTORE_H + +int keystore_get_secret(const char *name, const unsigned char **key, int *key_len); + +#endif