* [PATCH v4 00/20] Split fsverity-utils into a shared library
@ 2020-04-24 20:54 Jes Sorensen
2020-04-24 20:54 ` [PATCH 01/20] Build basic shared library framework Jes Sorensen
` (20 more replies)
0 siblings, 21 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Hi
This is an update to the libfsverity patches I posted about a month
ago, which I believe address all the issues in the feedback I received.
I have a version of rpm that requires this library which is able to
sign files and a plugin which will install fsverity signatures when
the rpm is installed. The code for rpm can be found on github - note
that I do rebase the repo as I fix bugs:
https://github.com/jessorensen/rpm/tree/rpm-fsverity
A git tree with these patches can also be found here:
https://git.kernel.org/pub/scm/linux/kernel/git/jes/fsverity-utils.git
This update changes a number of issues:
- Change the API for libfsverity_compute_digest() to take a callback
read function, which is needed to deal with the internal cpio
processing of rpm.
- Provides the option to build fsverity linked statically against
libfsverity
- Makefile support to install libfsverity.so, libfsverity.h and sets
the soname
- Make struct fsverity_descriptor and struct fsverity_hash_alg
internal to the library
- Improved documentation of the API in libfsverity.h
I have a .spec file for it that packages this into an rpm for Fedora,
as well as a packaged version of rpm with fsverity support in it,
which I am happy to share.
Let me know what you think!
Thanks,
Jes
Jes Sorensen (20):
Build basic shared library framework
Change compute_file_measurement() to take a file descriptor as
argument
Move fsverity_descriptor definition to libfsverity.h
Move hash algorithm code to shared library
Create libfsverity_compute_digest() and adapt cmd_sign to use it
Introduce libfsverity_sign_digest()
Validate input arguments to libfsverity_compute_digest()
Validate input parameters for libfsverity_sign_digest()
Document API of libfsverity
Change libfsverity_compute_digest() to take a read function
Make full_{read,write}() return proper error codes instead of bool
libfsverity: Remove dependencies on util.c
Update Makefile to install libfsverity and fsverity.h
Change libfsverity_find_hash_alg_by_name() to return the alg number
Make libfsverity_find_hash_alg_by_name() private to the shared library
libfsverity_sign_digest() use ARRAY_SIZE()
fsverity_cmd_sign() use sizeof() input argument instead of struct
fsverity_cmd_sign() don't exit on error without closing file
descriptor
Improve documentation of libfsverity.h API
Fixup Makefile
Makefile | 49 +++-
cmd_enable.c | 19 +-
cmd_measure.c | 19 +-
cmd_sign.c | 565 +++++------------------------------------
fsverity.c | 17 +-
hash_algs.c | 95 ++++---
hash_algs.h | 36 +--
helpers.h | 43 ++++
libfsverity.h | 138 ++++++++++
libfsverity_private.h | 52 ++++
libverity.c | 572 ++++++++++++++++++++++++++++++++++++++++++
util.c | 15 +-
util.h | 62 +----
13 files changed, 1029 insertions(+), 653 deletions(-)
create mode 100644 helpers.h
create mode 100644 libfsverity.h
create mode 100644 libfsverity_private.h
create mode 100644 libverity.c
--
2.25.3
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 01/20] Build basic shared library framework
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 02/20] Change compute_file_measurement() to take a file descriptor as argument Jes Sorensen
` (19 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
This introduces a dummy shared library to start moving things into.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
Makefile | 18 +++++++++++++++---
libverity.c | 10 ++++++++++
2 files changed, 25 insertions(+), 3 deletions(-)
create mode 100644 libverity.c
diff --git a/Makefile b/Makefile
index b9c09b9..bb85896 100644
--- a/Makefile
+++ b/Makefile
@@ -1,20 +1,32 @@
EXE := fsverity
+LIB := libfsverity.so
CFLAGS := -O2 -Wall
CPPFLAGS := -D_FILE_OFFSET_BITS=64
LDLIBS := -lcrypto
DESTDIR := /usr/local
+LIBDIR := /usr/lib64
SRC := $(wildcard *.c)
-OBJ := $(SRC:.c=.o)
+OBJ := fsverity.o hash_algs.o cmd_enable.o cmd_measure.o cmd_sign.o util.o
+SSRC := libverity.c
+SOBJ := libverity.so
HDRS := $(wildcard *.h)
all:$(EXE)
-$(EXE):$(OBJ)
+$(EXE):$(OBJ) $(LIB)
+ $(CC) -o $@ $(OBJ) $(LDLIBS) -L . -l fsverity
$(OBJ): %.o: %.c $(HDRS)
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+$(SOBJ): %.so: %.c $(HDRS)
+ $(CC) -c -fPIC $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+libfsverity.so: $(SOBJ)
+ $(CC) $(LDLIBS) -shared -o libfsverity.so $(SOBJ)
clean:
- rm -f $(EXE) $(OBJ)
+ rm -f $(EXE) $(OBJ) $(SOBJ) $(LIB)
install:all
install -Dm755 -t $(DESTDIR)/bin $(EXE)
diff --git a/libverity.c b/libverity.c
new file mode 100644
index 0000000..6821aa2
--- /dev/null
+++ b/libverity.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * The 'fsverity library'
+ *
+ * Copyright (C) 2018 Google LLC
+ * Copyright (C) 2020 Facebook
+ *
+ * Written by Eric Biggers and Jes Sorensen.
+ */
+
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 02/20] Change compute_file_measurement() to take a file descriptor as argument
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
2020-04-24 20:54 ` [PATCH 01/20] Build basic shared library framework Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 03/20] Move fsverity_descriptor definition to libfsverity.h Jes Sorensen
` (18 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
This preps the code for splitting the signing into the shared library
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_sign.c | 48 +++++++++++++++++++++++++++++++++++++-----------
fsverity.c | 1 +
libfsverity.h | 41 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 79 insertions(+), 11 deletions(-)
create mode 100644 libfsverity.h
diff --git a/cmd_sign.c b/cmd_sign.c
index dcb37ce..dcc44f8 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -16,6 +16,8 @@
#include <openssl/pkcs7.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "commands.h"
#include "fsverity_uapi.h"
@@ -382,11 +384,30 @@ static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur,
return next->filled + hash->alg->digest_size > block_size;
}
+static int full_read_fd(int fd, void *buf, size_t count)
+{
+ while (count) {
+ int n = read(fd, buf, min(count, INT_MAX));
+
+ if (n < 0) {
+ error_msg_errno("reading from file");
+ return n;
+ }
+ if (n == 0) {
+ error_msg("unexpected end-of-file");
+ return -ENODATA;
+ }
+ buf += n;
+ count -= n;
+ }
+ return 0;
+}
+
/*
* Compute the file's Merkle tree root hash using the given hash algorithm,
* block size, and salt.
*/
-static bool compute_root_hash(struct filedes *file, u64 file_size,
+static bool compute_root_hash(int fd, u64 file_size,
struct hash_ctx *hash, u32 block_size,
const u8 *salt, u32 salt_size, u8 *root_hash)
{
@@ -424,7 +445,7 @@ static bool compute_root_hash(struct filedes *file, u64 file_size,
for (offset = 0; offset < file_size; offset += block_size) {
buffers[-1].filled = min(block_size, file_size - offset);
- if (!full_read(file, buffers[-1].data, buffers[-1].filled))
+ if (full_read_fd(fd, buffers[-1].data, buffers[-1].filled))
goto out;
level = -1;
@@ -457,22 +478,22 @@ out:
* The fs-verity measurement is the hash of the fsverity_descriptor, which
* contains the Merkle tree properties including the root hash.
*/
-static bool compute_file_measurement(const char *filename,
+static bool compute_file_measurement(int fd,
const struct fsverity_hash_alg *hash_alg,
u32 block_size, const u8 *salt,
u32 salt_size, u8 *measurement)
{
- struct filedes file = { .fd = -1 };
struct hash_ctx *hash = hash_create(hash_alg);
u64 file_size;
struct fsverity_descriptor desc;
+ struct stat stbuf;
bool ok = false;
- if (!open_file(&file, filename, O_RDONLY, 0))
- goto out;
-
- if (!get_file_size(&file, &file_size))
+ if (fstat(fd, &stbuf) != 0) {
+ error_msg_errno("can't stat input file");
goto out;
+ }
+ file_size = stbuf.st_size;
memset(&desc, 0, sizeof(desc));
desc.version = 1;
@@ -495,14 +516,13 @@ static bool compute_file_measurement(const char *filename,
/* Root hash of empty file is all 0's */
if (file_size != 0 &&
- !compute_root_hash(&file, file_size, hash, block_size, salt,
+ !compute_root_hash(fd, file_size, hash, block_size, salt,
salt_size, desc.root_hash))
goto out;
hash_full(hash, &desc, sizeof(desc), measurement);
ok = true;
out:
- filedes_close(&file);
hash_free(hash);
return ok;
}
@@ -529,6 +549,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
int argc, char *argv[])
{
const struct fsverity_hash_alg *hash_alg = NULL;
+ struct filedes file = { .fd = -1 };
u32 block_size = 0;
u8 *salt = NULL;
u32 salt_size = 0;
@@ -603,10 +624,15 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
digest->digest_algorithm = cpu_to_le16(hash_alg - fsverity_hash_algs);
digest->digest_size = cpu_to_le16(hash_alg->digest_size);
- if (!compute_file_measurement(argv[0], hash_alg, block_size,
+ if (!open_file(&file, argv[0], O_RDONLY, 0))
+ goto out_err;
+
+ if (!compute_file_measurement(file.fd, hash_alg, block_size,
salt, salt_size, digest->digest))
goto out_err;
+ filedes_close(&file);
+
if (!sign_data(digest, sizeof(*digest) + hash_alg->digest_size,
keyfile, certfile, hash_alg, &sig, &sig_size))
goto out_err;
diff --git a/fsverity.c b/fsverity.c
index 9a44df1..c8fa1b5 100644
--- a/fsverity.c
+++ b/fsverity.c
@@ -14,6 +14,7 @@
#include "commands.h"
#include "hash_algs.h"
+#include "libfsverity.h"
static const struct fsverity_command {
const char *name;
diff --git a/libfsverity.h b/libfsverity.h
new file mode 100644
index 0000000..ceebae1
--- /dev/null
+++ b/libfsverity.h
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * libfsverity API
+ *
+ * Copyright (C) 2018 Google LLC
+ * Copyright (C) 2020 Facebook
+ *
+ * Written by Eric Biggers and modified by Jes Sorensen.
+ */
+
+#ifndef _LIBFSVERITY_H
+#define _LIBFSVERITY_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define FS_VERITY_HASH_ALG_SHA256 1
+#define FS_VERITY_HASH_ALG_SHA512 2
+
+struct libfsverity_merkle_tree_params {
+ uint16_t version;
+ uint16_t hash_algorithm;
+ uint32_t block_size;
+ uint32_t salt_size;
+ const uint8_t *salt;
+ uint64_t reserved[11];
+};
+
+struct libfsverity_digest {
+ uint16_t digest_algorithm;
+ uint16_t digest_size;
+ uint8_t digest[];
+};
+
+struct libfsverity_signature_params {
+ const char *keyfile;
+ const char *certfile;
+ uint64_t reserved[11];
+};
+
+#endif
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 03/20] Move fsverity_descriptor definition to libfsverity.h
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
2020-04-24 20:54 ` [PATCH 01/20] Build basic shared library framework Jes Sorensen
2020-04-24 20:54 ` [PATCH 02/20] Change compute_file_measurement() to take a file descriptor as argument Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 04/20] Move hash algorithm code to shared library Jes Sorensen
` (17 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_sign.c | 19 +------------------
libfsverity.h | 26 +++++++++++++++++++++++++-
2 files changed, 26 insertions(+), 19 deletions(-)
diff --git a/cmd_sign.c b/cmd_sign.c
index dcc44f8..1792084 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -20,26 +20,9 @@
#include <unistd.h>
#include "commands.h"
-#include "fsverity_uapi.h"
+#include "libfsverity.h"
#include "hash_algs.h"
-/*
- * Merkle tree properties. The file measurement is the hash of this structure
- * excluding the signature and with the sig_size field set to 0.
- */
-struct fsverity_descriptor {
- __u8 version; /* must be 1 */
- __u8 hash_algorithm; /* Merkle tree hash algorithm */
- __u8 log_blocksize; /* log2 of size of data and tree blocks */
- __u8 salt_size; /* size of salt in bytes; 0 if none */
- __le32 sig_size; /* size of signature in bytes; 0 if none */
- __le64 data_size; /* size of file the Merkle tree is built over */
- __u8 root_hash[64]; /* Merkle tree root hash */
- __u8 salt[32]; /* salt prepended to each hashed block */
- __u8 __reserved[144]; /* must be 0's */
- __u8 signature[]; /* optional PKCS#7 signature */
-};
-
/*
* Format in which verity file measurements are signed. This is the same as
* 'struct fsverity_digest', except here some magic bytes are prepended to
diff --git a/libfsverity.h b/libfsverity.h
index ceebae1..396a6ee 100644
--- a/libfsverity.h
+++ b/libfsverity.h
@@ -13,13 +13,14 @@
#include <stddef.h>
#include <stdint.h>
+#include <linux/types.h>
#define FS_VERITY_HASH_ALG_SHA256 1
#define FS_VERITY_HASH_ALG_SHA512 2
struct libfsverity_merkle_tree_params {
uint16_t version;
- uint16_t hash_algorithm;
+ uint16_t hash_algorithm; /* Matches the digest_algorithm type */
uint32_t block_size;
uint32_t salt_size;
const uint8_t *salt;
@@ -27,6 +28,7 @@ struct libfsverity_merkle_tree_params {
};
struct libfsverity_digest {
+ char magic[8]; /* must be "FSVerity" */
uint16_t digest_algorithm;
uint16_t digest_size;
uint8_t digest[];
@@ -38,4 +40,26 @@ struct libfsverity_signature_params {
uint64_t reserved[11];
};
+/*
+ * Merkle tree properties. The file measurement is the hash of this structure
+ * excluding the signature and with the sig_size field set to 0.
+ */
+struct fsverity_descriptor {
+ uint8_t version; /* must be 1 */
+ uint8_t hash_algorithm; /* Merkle tree hash algorithm */
+ uint8_t log_blocksize; /* log2 of size of data and tree blocks */
+ uint8_t salt_size; /* size of salt in bytes; 0 if none */
+ __le32 sig_size; /* size of signature in bytes; 0 if none */
+ __le64 data_size; /* size of file the Merkle tree is built over */
+ uint8_t root_hash[64]; /* Merkle tree root hash */
+ uint8_t salt[32]; /* salt prepended to each hashed block */
+ uint8_t __reserved[144];/* must be 0's */
+ uint8_t signature[]; /* optional PKCS#7 signature */
+};
+
+int
+libfsverity_compute_digest(int fd,
+ const struct libfsverity_merkle_tree_params *params,
+ struct libfsverity_digest **digest_ret);
+
#endif
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 04/20] Move hash algorithm code to shared library
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (2 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 03/20] Move fsverity_descriptor definition to libfsverity.h Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 05/20] Create libfsverity_compute_digest() and adapt cmd_sign to use it Jes Sorensen
` (16 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Reimplement show_all_hash_algs() to not rely on direct access to the list,
and add the algorithm number to the struct, so the user can find it easily.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
Makefile | 6 +++---
cmd_enable.c | 11 ++++++++---
cmd_measure.c | 4 ++--
cmd_sign.c | 18 ++++++++++++------
fsverity.c | 15 ++++++++++++++-
hash_algs.c | 26 +++++++-------------------
hash_algs.h | 27 ---------------------------
libfsverity.h | 22 ++++++++++++++++++++++
util.h | 2 ++
9 files changed, 70 insertions(+), 61 deletions(-)
diff --git a/Makefile b/Makefile
index bb85896..966afa0 100644
--- a/Makefile
+++ b/Makefile
@@ -6,9 +6,9 @@ LDLIBS := -lcrypto
DESTDIR := /usr/local
LIBDIR := /usr/lib64
SRC := $(wildcard *.c)
-OBJ := fsverity.o hash_algs.o cmd_enable.o cmd_measure.o cmd_sign.o util.o
-SSRC := libverity.c
-SOBJ := libverity.so
+OBJ := fsverity.o cmd_enable.o cmd_measure.o cmd_sign.o util.o
+SSRC := libverity.c hash_algs.c
+SOBJ := libverity.so hash_algs.so
HDRS := $(wildcard *.h)
all:$(EXE)
diff --git a/cmd_enable.c b/cmd_enable.c
index 1646299..1bed3ef 100644
--- a/cmd_enable.c
+++ b/cmd_enable.c
@@ -16,7 +16,7 @@
#include "commands.h"
#include "fsverity_uapi.h"
-#include "hash_algs.h"
+#include "libfsverity.h"
static bool parse_hash_alg_option(const char *arg, u32 *alg_ptr)
{
@@ -36,11 +36,16 @@ static bool parse_hash_alg_option(const char *arg, u32 *alg_ptr)
}
/* Specified by name? */
- alg = find_hash_alg_by_name(arg);
+ alg = libfsverity_find_hash_alg_by_name(arg);
if (alg != NULL) {
- *alg_ptr = alg - fsverity_hash_algs;
+ *alg_ptr = alg->hash_num;
return true;
}
+ error_msg("unknown hash algorithm: '%s'", arg);
+ fputs("Available hash algorithms: ", stderr);
+ show_all_hash_algs(stderr);
+ putc('\n', stderr);
+
return false;
}
diff --git a/cmd_measure.c b/cmd_measure.c
index 574e3ca..4c0777f 100644
--- a/cmd_measure.c
+++ b/cmd_measure.c
@@ -13,7 +13,7 @@
#include "commands.h"
#include "fsverity_uapi.h"
-#include "hash_algs.h"
+#include "libfsverity.h"
/* Display the measurement of the given verity file(s). */
int fsverity_cmd_measure(const struct fsverity_command *cmd,
@@ -48,7 +48,7 @@ int fsverity_cmd_measure(const struct fsverity_command *cmd,
ASSERT(d->digest_size <= FS_VERITY_MAX_DIGEST_SIZE);
bin2hex(d->digest, d->digest_size, digest_hex);
- hash_alg = find_hash_alg_by_num(d->digest_algorithm);
+ hash_alg = libfsverity_find_hash_alg_by_num(d->digest_algorithm);
if (hash_alg) {
hash_alg_name = hash_alg->name;
} else {
diff --git a/cmd_sign.c b/cmd_sign.c
index 1792084..5ad4eda 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -466,7 +466,7 @@ static bool compute_file_measurement(int fd,
u32 block_size, const u8 *salt,
u32 salt_size, u8 *measurement)
{
- struct hash_ctx *hash = hash_create(hash_alg);
+ struct hash_ctx *hash = hash_alg->create_ctx(hash_alg);
u64 file_size;
struct fsverity_descriptor desc;
struct stat stbuf;
@@ -480,7 +480,7 @@ static bool compute_file_measurement(int fd,
memset(&desc, 0, sizeof(desc));
desc.version = 1;
- desc.hash_algorithm = hash_alg - fsverity_hash_algs;
+ desc.hash_algorithm = hash_alg->hash_num;
ASSERT(is_power_of_2(block_size));
desc.log_blocksize = ilog2(block_size);
@@ -552,9 +552,15 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
error_msg("--hash-alg can only be specified once");
goto out_usage;
}
- hash_alg = find_hash_alg_by_name(optarg);
- if (hash_alg == NULL)
+ hash_alg = libfsverity_find_hash_alg_by_name(optarg);
+ if (hash_alg == NULL) {
+ error_msg("unknown hash algorithm: '%s'",
+ optarg);
+ fputs("Available hash algorithms: ", stderr);
+ show_all_hash_algs(stderr);
+ putc('\n', stderr);
goto out_usage;
+ }
break;
case OPT_BLOCK_SIZE:
if (!parse_block_size_option(optarg, &block_size))
@@ -590,7 +596,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
goto out_usage;
if (hash_alg == NULL)
- hash_alg = &fsverity_hash_algs[FS_VERITY_HASH_ALG_DEFAULT];
+ hash_alg = libfsverity_find_hash_alg_by_num(FS_VERITY_HASH_ALG_DEFAULT);
if (block_size == 0)
block_size = get_default_block_size();
@@ -604,7 +610,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
digest = xzalloc(sizeof(*digest) + hash_alg->digest_size);
memcpy(digest->magic, "FSVerity", 8);
- digest->digest_algorithm = cpu_to_le16(hash_alg - fsverity_hash_algs);
+ digest->digest_algorithm = cpu_to_le16(hash_alg->hash_num);
digest->digest_size = cpu_to_le16(hash_alg->digest_size);
if (!open_file(&file, argv[0], O_RDONLY, 0))
diff --git a/fsverity.c b/fsverity.c
index c8fa1b5..f9df72e 100644
--- a/fsverity.c
+++ b/fsverity.c
@@ -13,7 +13,6 @@
#include <unistd.h>
#include "commands.h"
-#include "hash_algs.h"
#include "libfsverity.h"
static const struct fsverity_command {
@@ -48,6 +47,20 @@ static const struct fsverity_command {
}
};
+void show_all_hash_algs(FILE *fp)
+{
+ int i = 1;
+ const char *sep = "";
+ const struct fsverity_hash_alg *alg;
+
+ while ((alg = libfsverity_find_hash_alg_by_num(i++))) {
+ if (alg && alg->name) {
+ fprintf(fp, "%s%s", sep, alg->name);
+ sep = ", ";
+ }
+ }
+}
+
static void usage_all(FILE *fp)
{
int i;
diff --git a/hash_algs.c b/hash_algs.c
index 7251bf2..d9f70b4 100644
--- a/hash_algs.c
+++ b/hash_algs.c
@@ -12,6 +12,7 @@
#include <string.h>
#include "fsverity_uapi.h"
+#include "libfsverity.h"
#include "hash_algs.h"
/* ========== libcrypto (OpenSSL) wrappers ========== */
@@ -106,17 +107,20 @@ const struct fsverity_hash_alg fsverity_hash_algs[] = {
.name = "sha256",
.digest_size = 32,
.block_size = 64,
+ .hash_num = FS_VERITY_HASH_ALG_SHA256,
.create_ctx = create_sha256_ctx,
},
[FS_VERITY_HASH_ALG_SHA512] = {
.name = "sha512",
.digest_size = 64,
.block_size = 128,
+ .hash_num = FS_VERITY_HASH_ALG_SHA512,
.create_ctx = create_sha512_ctx,
},
};
-const struct fsverity_hash_alg *find_hash_alg_by_name(const char *name)
+const struct fsverity_hash_alg *
+libfsverity_find_hash_alg_by_name(const char *name)
{
int i;
@@ -125,14 +129,11 @@ const struct fsverity_hash_alg *find_hash_alg_by_name(const char *name)
!strcmp(name, fsverity_hash_algs[i].name))
return &fsverity_hash_algs[i];
}
- error_msg("unknown hash algorithm: '%s'", name);
- fputs("Available hash algorithms: ", stderr);
- show_all_hash_algs(stderr);
- putc('\n', stderr);
return NULL;
}
-const struct fsverity_hash_alg *find_hash_alg_by_num(unsigned int num)
+const struct fsverity_hash_alg *
+libfsverity_find_hash_alg_by_num(unsigned int num)
{
if (num < ARRAY_SIZE(fsverity_hash_algs) &&
fsverity_hash_algs[num].name)
@@ -141,19 +142,6 @@ const struct fsverity_hash_alg *find_hash_alg_by_num(unsigned int num)
return NULL;
}
-void show_all_hash_algs(FILE *fp)
-{
- int i;
- const char *sep = "";
-
- for (i = 0; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
- if (fsverity_hash_algs[i].name) {
- fprintf(fp, "%s%s", sep, fsverity_hash_algs[i].name);
- sep = ", ";
- }
- }
-}
-
/* ->init(), ->update(), and ->final() all in one step */
void hash_full(struct hash_ctx *ctx, const void *data, size_t size, u8 *digest)
{
diff --git a/hash_algs.h b/hash_algs.h
index 3e90f49..2c7269a 100644
--- a/hash_algs.h
+++ b/hash_algs.h
@@ -6,15 +6,6 @@
#include "util.h"
-struct fsverity_hash_alg {
- const char *name;
- unsigned int digest_size;
- unsigned int block_size;
- struct hash_ctx *(*create_ctx)(const struct fsverity_hash_alg *alg);
-};
-
-extern const struct fsverity_hash_alg fsverity_hash_algs[];
-
struct hash_ctx {
const struct fsverity_hash_alg *alg;
void (*init)(struct hash_ctx *ctx);
@@ -23,24 +14,6 @@ struct hash_ctx {
void (*free)(struct hash_ctx *ctx);
};
-const struct fsverity_hash_alg *find_hash_alg_by_name(const char *name);
-const struct fsverity_hash_alg *find_hash_alg_by_num(unsigned int num);
-void show_all_hash_algs(FILE *fp);
-
-/* The hash algorithm that fsverity-utils assumes when none is specified */
-#define FS_VERITY_HASH_ALG_DEFAULT FS_VERITY_HASH_ALG_SHA256
-
-/*
- * Largest digest size among all hash algorithms supported by fs-verity.
- * This can be increased if needed.
- */
-#define FS_VERITY_MAX_DIGEST_SIZE 64
-
-static inline struct hash_ctx *hash_create(const struct fsverity_hash_alg *alg)
-{
- return alg->create_ctx(alg);
-}
-
static inline void hash_init(struct hash_ctx *ctx)
{
ctx->init(ctx);
diff --git a/libfsverity.h b/libfsverity.h
index 396a6ee..318dcd7 100644
--- a/libfsverity.h
+++ b/libfsverity.h
@@ -18,6 +18,9 @@
#define FS_VERITY_HASH_ALG_SHA256 1
#define FS_VERITY_HASH_ALG_SHA512 2
+/* The hash algorithm that fsverity-utils assumes when none is specified */
+#define FS_VERITY_HASH_ALG_DEFAULT FS_VERITY_HASH_ALG_SHA256
+
struct libfsverity_merkle_tree_params {
uint16_t version;
uint16_t hash_algorithm; /* Matches the digest_algorithm type */
@@ -27,6 +30,12 @@ struct libfsverity_merkle_tree_params {
uint64_t reserved[11];
};
+/*
+ * Largest digest size among all hash algorithms supported by fs-verity.
+ * This can be increased if needed.
+ */
+#define FS_VERITY_MAX_DIGEST_SIZE 64
+
struct libfsverity_digest {
char magic[8]; /* must be "FSVerity" */
uint16_t digest_algorithm;
@@ -57,9 +66,22 @@ struct fsverity_descriptor {
uint8_t signature[]; /* optional PKCS#7 signature */
};
+struct fsverity_hash_alg {
+ const char *name;
+ unsigned int digest_size;
+ unsigned int block_size;
+ uint16_t hash_num;
+ struct hash_ctx *(*create_ctx)(const struct fsverity_hash_alg *alg);
+};
+
int
libfsverity_compute_digest(int fd,
const struct libfsverity_merkle_tree_params *params,
struct libfsverity_digest **digest_ret);
+const struct fsverity_hash_alg *
+libfsverity_find_hash_alg_by_name(const char *name);
+const struct fsverity_hash_alg *
+libfsverity_find_hash_alg_by_num(unsigned int num);
+
#endif
diff --git a/util.h b/util.h
index dfa10f2..dd9b803 100644
--- a/util.h
+++ b/util.h
@@ -122,4 +122,6 @@ bool filedes_close(struct filedes *file);
bool hex2bin(const char *hex, u8 *bin, size_t bin_len);
void bin2hex(const u8 *bin, size_t bin_len, char *hex);
+void show_all_hash_algs();
+
#endif /* UTIL_H */
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 05/20] Create libfsverity_compute_digest() and adapt cmd_sign to use it
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (3 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 04/20] Move hash algorithm code to shared library Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 06/20] Introduce libfsverity_sign_digest() Jes Sorensen
` (15 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
This reorganizes the digest signing code and moves it to the shared library.
In addition libfsverity_private.h is created for library internal data
structures, in particular struct fsverity_decriptor.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_sign.c | 194 ++-------------------------------------
libfsverity.h | 17 ----
libfsverity_private.h | 33 +++++++
libverity.c | 207 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 250 insertions(+), 201 deletions(-)
create mode 100644 libfsverity_private.h
diff --git a/cmd_sign.c b/cmd_sign.c
index 5ad4eda..6a5d185 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -16,12 +16,9 @@
#include <openssl/pkcs7.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include "commands.h"
#include "libfsverity.h"
-#include "hash_algs.h"
/*
* Format in which verity file measurements are signed. This is the same as
@@ -337,179 +334,6 @@ static bool write_signature(const char *filename, const u8 *sig, u32 sig_size)
return ok;
}
-#define FS_VERITY_MAX_LEVELS 64
-
-struct block_buffer {
- u32 filled;
- u8 *data;
-};
-
-/*
- * Hash a block, writing the result to the next level's pending block buffer.
- * Returns true if the next level's block became full, else false.
- */
-static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur,
- u32 block_size, const u8 *salt, u32 salt_size)
-{
- struct block_buffer *next = cur + 1;
-
- /* Zero-pad the block if it's shorter than block_size. */
- memset(&cur->data[cur->filled], 0, block_size - cur->filled);
-
- hash_init(hash);
- hash_update(hash, salt, salt_size);
- hash_update(hash, cur->data, block_size);
- hash_final(hash, &next->data[next->filled]);
-
- next->filled += hash->alg->digest_size;
- cur->filled = 0;
-
- return next->filled + hash->alg->digest_size > block_size;
-}
-
-static int full_read_fd(int fd, void *buf, size_t count)
-{
- while (count) {
- int n = read(fd, buf, min(count, INT_MAX));
-
- if (n < 0) {
- error_msg_errno("reading from file");
- return n;
- }
- if (n == 0) {
- error_msg("unexpected end-of-file");
- return -ENODATA;
- }
- buf += n;
- count -= n;
- }
- return 0;
-}
-
-/*
- * Compute the file's Merkle tree root hash using the given hash algorithm,
- * block size, and salt.
- */
-static bool compute_root_hash(int fd, u64 file_size,
- struct hash_ctx *hash, u32 block_size,
- const u8 *salt, u32 salt_size, u8 *root_hash)
-{
- const u32 hashes_per_block = block_size / hash->alg->digest_size;
- const u32 padded_salt_size = roundup(salt_size, hash->alg->block_size);
- u8 *padded_salt = xzalloc(padded_salt_size);
- u64 blocks;
- int num_levels = 0;
- int level;
- struct block_buffer _buffers[1 + FS_VERITY_MAX_LEVELS + 1] = {};
- struct block_buffer *buffers = &_buffers[1];
- u64 offset;
- bool ok = false;
-
- if (salt_size != 0)
- memcpy(padded_salt, salt, salt_size);
-
- /* Compute number of levels */
- for (blocks = DIV_ROUND_UP(file_size, block_size); blocks > 1;
- blocks = DIV_ROUND_UP(blocks, hashes_per_block)) {
- ASSERT(num_levels < FS_VERITY_MAX_LEVELS);
- num_levels++;
- }
-
- /*
- * Allocate the block buffers. Buffer "-1" is for data blocks.
- * Buffers 0 <= level < num_levels are for the actual tree levels.
- * Buffer 'num_levels' is for the root hash.
- */
- for (level = -1; level < num_levels; level++)
- buffers[level].data = xmalloc(block_size);
- buffers[num_levels].data = root_hash;
-
- /* Hash each data block, also hashing the tree blocks as they fill up */
- for (offset = 0; offset < file_size; offset += block_size) {
- buffers[-1].filled = min(block_size, file_size - offset);
-
- if (full_read_fd(fd, buffers[-1].data, buffers[-1].filled))
- goto out;
-
- level = -1;
- while (hash_one_block(hash, &buffers[level], block_size,
- padded_salt, padded_salt_size)) {
- level++;
- ASSERT(level < num_levels);
- }
- }
- /* Finish all nonempty pending tree blocks */
- for (level = 0; level < num_levels; level++) {
- if (buffers[level].filled != 0)
- hash_one_block(hash, &buffers[level], block_size,
- padded_salt, padded_salt_size);
- }
-
- /* Root hash was filled by the last call to hash_one_block() */
- ASSERT(buffers[num_levels].filled == hash->alg->digest_size);
- ok = true;
-out:
- for (level = -1; level < num_levels; level++)
- free(buffers[level].data);
- free(padded_salt);
- return ok;
-}
-
-/*
- * Compute the fs-verity measurement of the given file.
- *
- * The fs-verity measurement is the hash of the fsverity_descriptor, which
- * contains the Merkle tree properties including the root hash.
- */
-static bool compute_file_measurement(int fd,
- const struct fsverity_hash_alg *hash_alg,
- u32 block_size, const u8 *salt,
- u32 salt_size, u8 *measurement)
-{
- struct hash_ctx *hash = hash_alg->create_ctx(hash_alg);
- u64 file_size;
- struct fsverity_descriptor desc;
- struct stat stbuf;
- bool ok = false;
-
- if (fstat(fd, &stbuf) != 0) {
- error_msg_errno("can't stat input file");
- goto out;
- }
- file_size = stbuf.st_size;
-
- memset(&desc, 0, sizeof(desc));
- desc.version = 1;
- desc.hash_algorithm = hash_alg->hash_num;
-
- ASSERT(is_power_of_2(block_size));
- desc.log_blocksize = ilog2(block_size);
-
- if (salt_size != 0) {
- if (salt_size > sizeof(desc.salt)) {
- error_msg("Salt too long (got %u bytes; max is %zu bytes)",
- salt_size, sizeof(desc.salt));
- goto out;
- }
- memcpy(desc.salt, salt, salt_size);
- desc.salt_size = salt_size;
- }
-
- desc.data_size = cpu_to_le64(file_size);
-
- /* Root hash of empty file is all 0's */
- if (file_size != 0 &&
- !compute_root_hash(fd, file_size, hash, block_size, salt,
- salt_size, desc.root_hash))
- goto out;
-
- hash_full(hash, &desc, sizeof(desc), measurement);
- ok = true;
-out:
- hash_free(hash);
- return ok;
-}
-
enum {
OPT_HASH_ALG,
OPT_BLOCK_SIZE,
@@ -538,7 +362,8 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
u32 salt_size = 0;
const char *keyfile = NULL;
const char *certfile = NULL;
- struct fsverity_signed_digest *digest = NULL;
+ struct libfsverity_digest *digest = NULL;
+ struct libfsverity_merkle_tree_params params;
char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
u8 *sig = NULL;
u32 sig_size;
@@ -608,16 +433,17 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
if (certfile == NULL)
certfile = keyfile;
- digest = xzalloc(sizeof(*digest) + hash_alg->digest_size);
- memcpy(digest->magic, "FSVerity", 8);
- digest->digest_algorithm = cpu_to_le16(hash_alg->hash_num);
- digest->digest_size = cpu_to_le16(hash_alg->digest_size);
-
if (!open_file(&file, argv[0], O_RDONLY, 0))
goto out_err;
- if (!compute_file_measurement(file.fd, hash_alg, block_size,
- salt, salt_size, digest->digest))
+ memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params));
+ params.version = 1;
+ params.hash_algorithm = hash_alg->hash_num;
+ params.block_size = block_size;
+ params.salt_size = salt_size;
+ params.salt = salt;
+
+ if (libfsverity_compute_digest(file.fd, ¶ms, &digest))
goto out_err;
filedes_close(&file);
diff --git a/libfsverity.h b/libfsverity.h
index 318dcd7..cb5f5b6 100644
--- a/libfsverity.h
+++ b/libfsverity.h
@@ -49,23 +49,6 @@ struct libfsverity_signature_params {
uint64_t reserved[11];
};
-/*
- * Merkle tree properties. The file measurement is the hash of this structure
- * excluding the signature and with the sig_size field set to 0.
- */
-struct fsverity_descriptor {
- uint8_t version; /* must be 1 */
- uint8_t hash_algorithm; /* Merkle tree hash algorithm */
- uint8_t log_blocksize; /* log2 of size of data and tree blocks */
- uint8_t salt_size; /* size of salt in bytes; 0 if none */
- __le32 sig_size; /* size of signature in bytes; 0 if none */
- __le64 data_size; /* size of file the Merkle tree is built over */
- uint8_t root_hash[64]; /* Merkle tree root hash */
- uint8_t salt[32]; /* salt prepended to each hashed block */
- uint8_t __reserved[144];/* must be 0's */
- uint8_t signature[]; /* optional PKCS#7 signature */
-};
-
struct fsverity_hash_alg {
const char *name;
unsigned int digest_size;
diff --git a/libfsverity_private.h b/libfsverity_private.h
new file mode 100644
index 0000000..5f3e1b4
--- /dev/null
+++ b/libfsverity_private.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * libfsverity private interfaces
+ *
+ * Copyright (C) 2018 Google LLC
+ * Copyright (C) 2020 Facebook
+ *
+ * Written by Eric Biggers and modified by Jes Sorensen.
+ */
+#ifndef _LIBFSVERITY_PRIVATE_H
+#define _LIBFSVERITY_PRIVATE_H
+
+#include <stdint.h>
+#include <linux/types.h>
+
+/*
+ * Merkle tree properties. The file measurement is the hash of this structure
+ * excluding the signature and with the sig_size field set to 0.
+ */
+struct fsverity_descriptor {
+ uint8_t version; /* must be 1 */
+ uint8_t hash_algorithm; /* Merkle tree hash algorithm */
+ uint8_t log_blocksize; /* log2 of size of data and tree blocks */
+ uint8_t salt_size; /* size of salt in bytes; 0 if none */
+ __le32 sig_size; /* size of signature in bytes; 0 if none */
+ __le64 data_size; /* size of file the Merkle tree is built over */
+ uint8_t root_hash[64]; /* Merkle tree root hash */
+ uint8_t salt[32]; /* salt prepended to each hashed block */
+ uint8_t __reserved[144];/* must be 0's */
+ uint8_t signature[]; /* optional PKCS#7 signature */
+};
+
+#endif
diff --git a/libverity.c b/libverity.c
index 6821aa2..19272f7 100644
--- a/libverity.c
+++ b/libverity.c
@@ -8,3 +8,210 @@
* Written by Eric Biggers and Jes Sorensen.
*/
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "libfsverity.h"
+#include "libfsverity_private.h"
+#include "hash_algs.h"
+
+#define FS_VERITY_MAX_LEVELS 64
+
+struct block_buffer {
+ u32 filled;
+ u8 *data;
+};
+
+/*
+ * Hash a block, writing the result to the next level's pending block buffer.
+ * Returns true if the next level's block became full, else false.
+ */
+static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur,
+ u32 block_size, const u8 *salt, u32 salt_size)
+{
+ struct block_buffer *next = cur + 1;
+
+ /* Zero-pad the block if it's shorter than block_size. */
+ memset(&cur->data[cur->filled], 0, block_size - cur->filled);
+
+ hash_init(hash);
+ hash_update(hash, salt, salt_size);
+ hash_update(hash, cur->data, block_size);
+ hash_final(hash, &next->data[next->filled]);
+
+ next->filled += hash->alg->digest_size;
+ cur->filled = 0;
+
+ return next->filled + hash->alg->digest_size > block_size;
+}
+
+static int full_read_fd(int fd, void *buf, size_t count)
+{
+ while (count) {
+ int n = read(fd, buf, min(count, INT_MAX));
+
+ if (n < 0) {
+ error_msg_errno("reading from file");
+ return n;
+ }
+ if (n == 0) {
+ error_msg("unexpected end-of-file");
+ return -ENODATA;
+ }
+ buf += n;
+ count -= n;
+ }
+ return 0;
+}
+
+/*
+ * Compute the file's Merkle tree root hash using the given hash algorithm,
+ * block size, and salt.
+ */
+static bool compute_root_hash(int fd, u64 file_size,
+ struct hash_ctx *hash, u32 block_size,
+ const u8 *salt, u32 salt_size, u8 *root_hash)
+{
+ const u32 hashes_per_block = block_size / hash->alg->digest_size;
+ const u32 padded_salt_size = roundup(salt_size, hash->alg->block_size);
+ u8 *padded_salt = xzalloc(padded_salt_size);
+ u64 blocks;
+ int num_levels = 0;
+ int level;
+ struct block_buffer _buffers[1 + FS_VERITY_MAX_LEVELS + 1] = {};
+ struct block_buffer *buffers = &_buffers[1];
+ u64 offset;
+ bool ok = false;
+
+ if (salt_size != 0)
+ memcpy(padded_salt, salt, salt_size);
+
+ /* Compute number of levels */
+ for (blocks = DIV_ROUND_UP(file_size, block_size); blocks > 1;
+ blocks = DIV_ROUND_UP(blocks, hashes_per_block)) {
+ ASSERT(num_levels < FS_VERITY_MAX_LEVELS);
+ num_levels++;
+ }
+
+ /*
+ * Allocate the block buffers. Buffer "-1" is for data blocks.
+ * Buffers 0 <= level < num_levels are for the actual tree levels.
+ * Buffer 'num_levels' is for the root hash.
+ */
+ for (level = -1; level < num_levels; level++)
+ buffers[level].data = xmalloc(block_size);
+ buffers[num_levels].data = root_hash;
+
+ /* Hash each data block, also hashing the tree blocks as they fill up */
+ for (offset = 0; offset < file_size; offset += block_size) {
+ buffers[-1].filled = min(block_size, file_size - offset);
+
+ if (full_read_fd(fd, buffers[-1].data, buffers[-1].filled))
+ goto out;
+
+ level = -1;
+ while (hash_one_block(hash, &buffers[level], block_size,
+ padded_salt, padded_salt_size)) {
+ level++;
+ ASSERT(level < num_levels);
+ }
+ }
+ /* Finish all nonempty pending tree blocks */
+ for (level = 0; level < num_levels; level++) {
+ if (buffers[level].filled != 0)
+ hash_one_block(hash, &buffers[level], block_size,
+ padded_salt, padded_salt_size);
+ }
+
+ /* Root hash was filled by the last call to hash_one_block() */
+ ASSERT(buffers[num_levels].filled == hash->alg->digest_size);
+ ok = true;
+out:
+ for (level = -1; level < num_levels; level++)
+ free(buffers[level].data);
+ free(padded_salt);
+ return ok;
+}
+
+/*
+ * Compute the fs-verity measurement of the given file.
+ *
+ * The fs-verity measurement is the hash of the fsverity_descriptor, which
+ * contains the Merkle tree properties including the root hash.
+ */
+int
+libfsverity_compute_digest(int fd,
+ const struct libfsverity_merkle_tree_params *params,
+ struct libfsverity_digest **digest_ret)
+{
+ const struct fsverity_hash_alg *hash_alg;
+ struct libfsverity_digest *digest;
+ struct hash_ctx *hash;
+ struct fsverity_descriptor desc;
+ struct stat stbuf;
+ u64 file_size;
+ int retval = -EINVAL;
+
+ hash_alg = libfsverity_find_hash_alg_by_num(params->hash_algorithm);
+ hash = hash_alg->create_ctx(hash_alg);
+
+ digest = malloc(sizeof(struct libfsverity_digest) +
+ hash_alg->digest_size);
+ if (!digest_ret)
+ return -ENOMEM;
+ memcpy(digest->magic, "FSVerity", 8);
+ digest->digest_algorithm = cpu_to_le16(hash_alg->hash_num);
+ digest->digest_size = cpu_to_le16(hash_alg->digest_size);
+ memset(digest->digest, 0, hash_alg->digest_size);
+
+ if (fstat(fd, &stbuf) != 0) {
+ error_msg_errno("can't stat input file");
+ retval = -EBADF;
+ goto error_out;
+ }
+ file_size = stbuf.st_size;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.version = 1;
+ desc.hash_algorithm = params->hash_algorithm;
+
+ ASSERT(is_power_of_2(params->block_size));
+ desc.log_blocksize = ilog2(params->block_size);
+
+ if (params->salt_size != 0) {
+ if (params->salt_size > sizeof(desc.salt)) {
+ error_msg("Salt too long (got %u bytes; max is %zu bytes)",
+ params->salt_size, sizeof(desc.salt));
+ retval = EINVAL;
+ goto error_out;
+ }
+ memcpy(desc.salt, params->salt, params->salt_size);
+ desc.salt_size = params->salt_size;
+ }
+
+ desc.data_size = cpu_to_le64(file_size);
+
+ /* Root hash of empty file is all 0's */
+ if (file_size != 0 &&
+ !compute_root_hash(fd, file_size, hash, params->block_size,
+ params->salt, params->salt_size,
+ desc.root_hash)) {
+ retval = -EAGAIN;
+ goto error_out;
+ }
+
+ hash_full(hash, &desc, sizeof(desc), digest->digest);
+ hash_free(hash);
+ *digest_ret = digest;
+
+ return 0;
+
+ error_out:
+ free(digest);
+ return retval;
+}
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 06/20] Introduce libfsverity_sign_digest()
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (4 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 05/20] Create libfsverity_compute_digest() and adapt cmd_sign to use it Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 07/20] Validate input arguments to libfsverity_compute_digest() Jes Sorensen
` (14 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
This moves the signing code into libfsverity and switches cmd_sign to use it.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_sign.c | 317 ++------------------------------------------------
libfsverity.h | 12 ++
libverity.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 329 insertions(+), 309 deletions(-)
diff --git a/cmd_sign.c b/cmd_sign.c
index 6a5d185..e48e0aa 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -10,318 +10,12 @@
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
-#include <openssl/bio.h>
-#include <openssl/err.h>
-#include <openssl/pem.h>
-#include <openssl/pkcs7.h>
#include <stdlib.h>
#include <string.h>
#include "commands.h"
#include "libfsverity.h"
-/*
- * Format in which verity file measurements are signed. This is the same as
- * 'struct fsverity_digest', except here some magic bytes are prepended to
- * provide some context about what is being signed in case the same key is used
- * for non-fsverity purposes, and here the fields have fixed endianness.
- */
-struct fsverity_signed_digest {
- char magic[8]; /* must be "FSVerity" */
- __le16 digest_algorithm;
- __le16 digest_size;
- __u8 digest[];
-};
-
-static void __printf(1, 2) __cold
-error_msg_openssl(const char *format, ...)
-{
- va_list va;
-
- va_start(va, format);
- do_error_msg(format, va, 0);
- va_end(va);
-
- if (ERR_peek_error() == 0)
- return;
-
- fprintf(stderr, "OpenSSL library errors:\n");
- ERR_print_errors_fp(stderr);
-}
-
-/* Read a PEM PKCS#8 formatted private key */
-static EVP_PKEY *read_private_key(const char *keyfile)
-{
- BIO *bio;
- EVP_PKEY *pkey;
-
- bio = BIO_new_file(keyfile, "r");
- if (!bio) {
- error_msg_openssl("can't open '%s' for reading", keyfile);
- return NULL;
- }
-
- pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
- if (!pkey) {
- error_msg_openssl("Failed to parse private key file '%s'.\n"
- " Note: it must be in PEM PKCS#8 format.",
- keyfile);
- }
- BIO_free(bio);
- return pkey;
-}
-
-/* Read a PEM X.509 formatted certificate */
-static X509 *read_certificate(const char *certfile)
-{
- BIO *bio;
- X509 *cert;
-
- bio = BIO_new_file(certfile, "r");
- if (!bio) {
- error_msg_openssl("can't open '%s' for reading", certfile);
- return NULL;
- }
- cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
- if (!cert) {
- error_msg_openssl("Failed to parse X.509 certificate file '%s'.\n"
- " Note: it must be in PEM format.",
- certfile);
- }
- BIO_free(bio);
- return cert;
-}
-
-#ifdef OPENSSL_IS_BORINGSSL
-
-static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
- EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
- u8 **sig_ret, u32 *sig_size_ret)
-{
- CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo,
- null, content_info, issuer_and_serial, signer_infos,
- signer_info, sign_algo, signature;
- EVP_MD_CTX md_ctx;
- u8 *name_der = NULL, *sig = NULL, *pkcs7_data = NULL;
- size_t pkcs7_data_len, sig_len;
- int name_der_len, sig_nid;
- bool ok = false;
-
- EVP_MD_CTX_init(&md_ctx);
- BIGNUM *serial = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), NULL);
-
- if (!CBB_init(&out, 1024)) {
- error_msg("out of memory");
- goto out;
- }
-
- name_der_len = i2d_X509_NAME(X509_get_subject_name(cert), &name_der);
- if (name_der_len < 0) {
- error_msg_openssl("i2d_X509_NAME failed");
- goto out;
- }
-
- if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey)) {
- error_msg_openssl("EVP_DigestSignInit failed");
- goto out;
- }
-
- sig_len = EVP_PKEY_size(pkey);
- sig = xmalloc(sig_len);
- if (!EVP_DigestSign(&md_ctx, sig, &sig_len, data_to_sign, data_size)) {
- error_msg_openssl("EVP_DigestSign failed");
- goto out;
- }
-
- sig_nid = EVP_PKEY_id(pkey);
- /* To mirror OpenSSL behaviour, always use |NID_rsaEncryption| with RSA
- * rather than the combined hash+pkey NID. */
- if (sig_nid != NID_rsaEncryption) {
- OBJ_find_sigid_by_algs(&sig_nid, EVP_MD_type(md),
- EVP_PKEY_id(pkey));
- }
-
- // See https://tools.ietf.org/html/rfc2315#section-7
- if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&outer_seq, NID_pkcs7_signed) ||
- !CBB_add_asn1(&outer_seq, &wrapped_seq, CBS_ASN1_CONTEXT_SPECIFIC |
- CBS_ASN1_CONSTRUCTED | 0) ||
- // See https://tools.ietf.org/html/rfc2315#section-9.1
- !CBB_add_asn1(&wrapped_seq, &seq, CBS_ASN1_SEQUENCE) ||
- !CBB_add_asn1_uint64(&seq, 1 /* version */) ||
- !CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
- !CBB_add_asn1(&digest_algos_set, &digest_algo, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&digest_algo, EVP_MD_type(md)) ||
- !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
- !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&content_info, NID_pkcs7_data) ||
- !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
- !CBB_add_asn1(&signer_infos, &signer_info, CBS_ASN1_SEQUENCE) ||
- !CBB_add_asn1_uint64(&signer_info, 1 /* version */) ||
- !CBB_add_asn1(&signer_info, &issuer_and_serial,
- CBS_ASN1_SEQUENCE) ||
- !CBB_add_bytes(&issuer_and_serial, name_der, name_der_len) ||
- !BN_marshal_asn1(&issuer_and_serial, serial) ||
- !CBB_add_asn1(&signer_info, &digest_algo, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&digest_algo, EVP_MD_type(md)) ||
- !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
- !CBB_add_asn1(&signer_info, &sign_algo, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&sign_algo, sig_nid) ||
- !CBB_add_asn1(&sign_algo, &null, CBS_ASN1_NULL) ||
- !CBB_add_asn1(&signer_info, &signature, CBS_ASN1_OCTETSTRING) ||
- !CBB_add_bytes(&signature, sig, sig_len) ||
- !CBB_finish(&out, &pkcs7_data, &pkcs7_data_len)) {
- error_msg_openssl("failed to construct PKCS#7 data");
- goto out;
- }
-
- *sig_ret = xmemdup(pkcs7_data, pkcs7_data_len);
- *sig_size_ret = pkcs7_data_len;
- ok = true;
-out:
- BN_free(serial);
- EVP_MD_CTX_cleanup(&md_ctx);
- CBB_cleanup(&out);
- free(sig);
- OPENSSL_free(name_der);
- OPENSSL_free(pkcs7_data);
- return ok;
-}
-
-#else /* OPENSSL_IS_BORINGSSL */
-
-static BIO *new_mem_buf(const void *buf, size_t size)
-{
- BIO *bio;
-
- ASSERT(size <= INT_MAX);
- /*
- * Prior to OpenSSL 1.1.0, BIO_new_mem_buf() took a non-const pointer,
- * despite still marking the resulting bio as read-only. So cast away
- * the const to avoid a compiler warning with older OpenSSL versions.
- */
- bio = BIO_new_mem_buf((void *)buf, size);
- if (!bio)
- error_msg_openssl("out of memory");
- return bio;
-}
-
-static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
- EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
- u8 **sig_ret, u32 *sig_size_ret)
-{
- /*
- * PKCS#7 signing flags:
- *
- * - PKCS7_BINARY signing binary data, so skip MIME translation
- *
- * - PKCS7_DETACHED omit the signed data (include signature only)
- *
- * - PKCS7_NOATTR omit extra authenticated attributes, such as
- * SMIMECapabilities
- *
- * - PKCS7_NOCERTS omit the signer's certificate
- *
- * - PKCS7_PARTIAL PKCS7_sign() creates a handle only, then
- * PKCS7_sign_add_signer() can add a signer later.
- * This is necessary to change the message digest
- * algorithm from the default of SHA-1. Requires
- * OpenSSL 1.0.0 or later.
- */
- int pkcs7_flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOATTR |
- PKCS7_NOCERTS | PKCS7_PARTIAL;
- u8 *sig;
- u32 sig_size;
- BIO *bio = NULL;
- PKCS7 *p7 = NULL;
- bool ok = false;
-
- bio = new_mem_buf(data_to_sign, data_size);
- if (!bio)
- goto out;
-
- p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags);
- if (!p7) {
- error_msg_openssl("failed to initialize PKCS#7 signature object");
- goto out;
- }
-
- if (!PKCS7_sign_add_signer(p7, cert, pkey, md, pkcs7_flags)) {
- error_msg_openssl("failed to add signer to PKCS#7 signature object");
- goto out;
- }
-
- if (PKCS7_final(p7, bio, pkcs7_flags) != 1) {
- error_msg_openssl("failed to finalize PKCS#7 signature");
- goto out;
- }
-
- BIO_free(bio);
- bio = BIO_new(BIO_s_mem());
- if (!bio) {
- error_msg_openssl("out of memory");
- goto out;
- }
-
- if (i2d_PKCS7_bio(bio, p7) != 1) {
- error_msg_openssl("failed to DER-encode PKCS#7 signature object");
- goto out;
- }
-
- sig_size = BIO_get_mem_data(bio, &sig);
- *sig_ret = xmemdup(sig, sig_size);
- *sig_size_ret = sig_size;
- ok = true;
-out:
- PKCS7_free(p7);
- BIO_free(bio);
- return ok;
-}
-
-#endif /* !OPENSSL_IS_BORINGSSL */
-
-/*
- * Sign the specified @data_to_sign of length @data_size bytes using the private
- * key in @keyfile, the certificate in @certfile, and the hash algorithm
- * @hash_alg. Returns the DER-formatted PKCS#7 signature in @sig_ret and
- * @sig_size_ret.
- */
-static bool sign_data(const void *data_to_sign, size_t data_size,
- const char *keyfile, const char *certfile,
- const struct fsverity_hash_alg *hash_alg,
- u8 **sig_ret, u32 *sig_size_ret)
-{
- EVP_PKEY *pkey = NULL;
- X509 *cert = NULL;
- const EVP_MD *md;
- bool ok = false;
-
- pkey = read_private_key(keyfile);
- if (!pkey)
- goto out;
-
- cert = read_certificate(certfile);
- if (!cert)
- goto out;
-
- OpenSSL_add_all_digests();
- md = EVP_get_digestbyname(hash_alg->name);
- if (!md) {
- fprintf(stderr,
- "Warning: '%s' algorithm not found in OpenSSL library.\n"
- " Falling back to SHA-256 signature.\n",
- hash_alg->name);
- md = EVP_sha256();
- }
-
- ok = sign_pkcs7(data_to_sign, data_size, pkey, cert, md,
- sig_ret, sig_size_ret);
-out:
- EVP_PKEY_free(pkey);
- X509_free(cert);
- return ok;
-}
-
static bool write_signature(const char *filename, const u8 *sig, u32 sig_size)
{
struct filedes file;
@@ -364,9 +58,10 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
const char *certfile = NULL;
struct libfsverity_digest *digest = NULL;
struct libfsverity_merkle_tree_params params;
+ struct libfsverity_signature_params sig_params;
char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
u8 *sig = NULL;
- u32 sig_size;
+ size_t sig_size;
int status;
int c;
@@ -448,9 +143,13 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
filedes_close(&file);
- if (!sign_data(digest, sizeof(*digest) + hash_alg->digest_size,
- keyfile, certfile, hash_alg, &sig, &sig_size))
+ memset(&sig_params, 0, sizeof(struct libfsverity_signature_params));
+ sig_params.keyfile = keyfile;
+ sig_params.certfile = certfile;
+ if (libfsverity_sign_digest(digest, &sig_params, &sig, &sig_size)) {
+ error_msg("Failed to sign digest");
goto out_err;
+ }
if (!write_signature(argv[1], sig, sig_size))
goto out_err;
diff --git a/libfsverity.h b/libfsverity.h
index cb5f5b6..a2abdb3 100644
--- a/libfsverity.h
+++ b/libfsverity.h
@@ -36,6 +36,13 @@ struct libfsverity_merkle_tree_params {
*/
#define FS_VERITY_MAX_DIGEST_SIZE 64
+/*
+ * Format in which verity file measurements are signed. This is the same as
+ * 'struct fsverity_digest', except here some magic bytes are prepended to
+ * provide some context about what is being signed in case the same key is used
+ * for non-fsverity purposes, and here the fields have fixed endianness.
+ */
+
struct libfsverity_digest {
char magic[8]; /* must be "FSVerity" */
uint16_t digest_algorithm;
@@ -62,6 +69,11 @@ libfsverity_compute_digest(int fd,
const struct libfsverity_merkle_tree_params *params,
struct libfsverity_digest **digest_ret);
+int
+libfsverity_sign_digest(const struct libfsverity_digest *digest,
+ const struct libfsverity_signature_params *sig_params,
+ uint8_t **sig_ret, size_t *sig_size_ret);
+
const struct fsverity_hash_alg *
libfsverity_find_hash_alg_by_name(const char *name);
const struct fsverity_hash_alg *
diff --git a/libverity.c b/libverity.c
index 19272f7..183259e 100644
--- a/libverity.c
+++ b/libverity.c
@@ -215,3 +215,312 @@ libfsverity_compute_digest(int fd,
free(digest);
return retval;
}
+
+static void __printf(1, 2) __cold
+error_msg_openssl(const char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ do_error_msg(format, va, 0);
+ va_end(va);
+
+ if (ERR_peek_error() == 0)
+ return;
+
+ fprintf(stderr, "OpenSSL library errors:\n");
+ ERR_print_errors_fp(stderr);
+}
+
+/* Read a PEM PKCS#8 formatted private key */
+static EVP_PKEY *read_private_key(const char *keyfile)
+{
+ BIO *bio;
+ EVP_PKEY *pkey;
+
+ bio = BIO_new_file(keyfile, "r");
+ if (!bio) {
+ error_msg_openssl("can't open '%s' for reading", keyfile);
+ return NULL;
+ }
+
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ if (!pkey) {
+ error_msg_openssl("Failed to parse private key file '%s'.\n"
+ " Note: it must be in PEM PKCS#8 format.",
+ keyfile);
+ }
+ BIO_free(bio);
+ return pkey;
+}
+
+/* Read a PEM X.509 formatted certificate */
+static X509 *read_certificate(const char *certfile)
+{
+ BIO *bio;
+ X509 *cert;
+
+ bio = BIO_new_file(certfile, "r");
+ if (!bio) {
+ error_msg_openssl("can't open '%s' for reading", certfile);
+ return NULL;
+ }
+ cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (!cert) {
+ error_msg_openssl("Failed to parse X.509 certificate file '%s'.\n"
+ " Note: it must be in PEM format.",
+ certfile);
+ }
+ BIO_free(bio);
+ return cert;
+}
+
+#ifdef OPENSSL_IS_BORINGSSL
+
+static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
+ EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
+ u8 **sig_ret, size_t *sig_size_ret)
+{
+ CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo,
+ null, content_info, issuer_and_serial, signer_infos,
+ signer_info, sign_algo, signature;
+ EVP_MD_CTX md_ctx;
+ u8 *name_der = NULL, *sig = NULL, *pkcs7_data = NULL;
+ size_t pkcs7_data_len, sig_len;
+ int name_der_len, sig_nid;
+ bool ok = false;
+
+ EVP_MD_CTX_init(&md_ctx);
+ BIGNUM *serial = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), NULL);
+
+ if (!CBB_init(&out, 1024)) {
+ error_msg("out of memory");
+ goto out;
+ }
+
+ name_der_len = i2d_X509_NAME(X509_get_subject_name(cert), &name_der);
+ if (name_der_len < 0) {
+ error_msg_openssl("i2d_X509_NAME failed");
+ goto out;
+ }
+
+ if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey)) {
+ error_msg_openssl("EVP_DigestSignInit failed");
+ goto out;
+ }
+
+ sig_len = EVP_PKEY_size(pkey);
+ sig = xmalloc(sig_len);
+ if (!EVP_DigestSign(&md_ctx, sig, &sig_len, data_to_sign, data_size)) {
+ error_msg_openssl("EVP_DigestSign failed");
+ goto out;
+ }
+
+ sig_nid = EVP_PKEY_id(pkey);
+ /* To mirror OpenSSL behaviour, always use |NID_rsaEncryption| with RSA
+ * rather than the combined hash+pkey NID. */
+ if (sig_nid != NID_rsaEncryption) {
+ OBJ_find_sigid_by_algs(&sig_nid, EVP_MD_type(md),
+ EVP_PKEY_id(pkey));
+ }
+
+ // See https://tools.ietf.org/html/rfc2315#section-7
+ if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&outer_seq, NID_pkcs7_signed) ||
+ !CBB_add_asn1(&outer_seq, &wrapped_seq, CBS_ASN1_CONTEXT_SPECIFIC |
+ CBS_ASN1_CONSTRUCTED | 0) ||
+ // See https://tools.ietf.org/html/rfc2315#section-9.1
+ !CBB_add_asn1(&wrapped_seq, &seq, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1_uint64(&seq, 1 /* version */) ||
+ !CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&digest_algos_set, &digest_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&digest_algo, EVP_MD_type(md)) ||
+ !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&content_info, NID_pkcs7_data) ||
+ !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&signer_infos, &signer_info, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1_uint64(&signer_info, 1 /* version */) ||
+ !CBB_add_asn1(&signer_info, &issuer_and_serial,
+ CBS_ASN1_SEQUENCE) ||
+ !CBB_add_bytes(&issuer_and_serial, name_der, name_der_len) ||
+ !BN_marshal_asn1(&issuer_and_serial, serial) ||
+ !CBB_add_asn1(&signer_info, &digest_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&digest_algo, EVP_MD_type(md)) ||
+ !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&signer_info, &sign_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&sign_algo, sig_nid) ||
+ !CBB_add_asn1(&sign_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&signer_info, &signature, CBS_ASN1_OCTETSTRING) ||
+ !CBB_add_bytes(&signature, sig, sig_len) ||
+ !CBB_finish(&out, &pkcs7_data, &pkcs7_data_len)) {
+ error_msg_openssl("failed to construct PKCS#7 data");
+ goto out;
+ }
+
+ *sig_ret = xmemdup(pkcs7_data, pkcs7_data_len);
+ *sig_size_ret = pkcs7_data_len;
+ ok = true;
+out:
+ BN_free(serial);
+ EVP_MD_CTX_cleanup(&md_ctx);
+ CBB_cleanup(&out);
+ free(sig);
+ OPENSSL_free(name_der);
+ OPENSSL_free(pkcs7_data);
+ return ok;
+}
+
+#else /* OPENSSL_IS_BORINGSSL */
+
+static BIO *new_mem_buf(const void *buf, size_t size)
+{
+ BIO *bio;
+
+ ASSERT(size <= INT_MAX);
+ /*
+ * Prior to OpenSSL 1.1.0, BIO_new_mem_buf() took a non-const pointer,
+ * despite still marking the resulting bio as read-only. So cast away
+ * the const to avoid a compiler warning with older OpenSSL versions.
+ */
+ bio = BIO_new_mem_buf((void *)buf, size);
+ if (!bio)
+ error_msg_openssl("out of memory");
+ return bio;
+}
+
+static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
+ EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
+ u8 **sig_ret, size_t *sig_size_ret)
+{
+ /*
+ * PKCS#7 signing flags:
+ *
+ * - PKCS7_BINARY signing binary data, so skip MIME translation
+ *
+ * - PKCS7_DETACHED omit the signed data (include signature only)
+ *
+ * - PKCS7_NOATTR omit extra authenticated attributes, such as
+ * SMIMECapabilities
+ *
+ * - PKCS7_NOCERTS omit the signer's certificate
+ *
+ * - PKCS7_PARTIAL PKCS7_sign() creates a handle only, then
+ * PKCS7_sign_add_signer() can add a signer later.
+ * This is necessary to change the message digest
+ * algorithm from the default of SHA-1. Requires
+ * OpenSSL 1.0.0 or later.
+ */
+ int pkcs7_flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOATTR |
+ PKCS7_NOCERTS | PKCS7_PARTIAL;
+ u8 *sig;
+ u32 sig_size;
+ BIO *bio = NULL;
+ PKCS7 *p7 = NULL;
+ bool ok = false;
+
+ bio = new_mem_buf(data_to_sign, data_size);
+ if (!bio)
+ goto out;
+
+ p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags);
+ if (!p7) {
+ error_msg_openssl("failed to initialize PKCS#7 signature object");
+ goto out;
+ }
+
+ if (!PKCS7_sign_add_signer(p7, cert, pkey, md, pkcs7_flags)) {
+ error_msg_openssl("failed to add signer to PKCS#7 signature object");
+ goto out;
+ }
+
+ if (PKCS7_final(p7, bio, pkcs7_flags) != 1) {
+ error_msg_openssl("failed to finalize PKCS#7 signature");
+ goto out;
+ }
+
+ BIO_free(bio);
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) {
+ error_msg_openssl("out of memory");
+ goto out;
+ }
+
+ if (i2d_PKCS7_bio(bio, p7) != 1) {
+ error_msg_openssl("failed to DER-encode PKCS#7 signature object");
+ goto out;
+ }
+
+ sig_size = BIO_get_mem_data(bio, &sig);
+ *sig_ret = xmemdup(sig, sig_size);
+ *sig_size_ret = sig_size;
+ ok = true;
+out:
+ PKCS7_free(p7);
+ BIO_free(bio);
+ return ok;
+}
+
+#endif /* !OPENSSL_IS_BORINGSSL */
+
+/*
+ * Sign the digest using the private key in @keyfile, the certificate in
+ * @certfile, and the hash algorithm specified in the digest.
+ * Return 0 on success, the DER-formatted PKCS#7 signature in @sig_ret and
+ * it's size in @sig_size_ret.
+ */
+int
+libfsverity_sign_digest(const struct libfsverity_digest *digest,
+ const struct libfsverity_signature_params *sig_params,
+ uint8_t **sig_ret, size_t *sig_size_ret)
+{
+ const struct fsverity_hash_alg *hash_alg;
+ EVP_PKEY *pkey = NULL;
+ X509 *cert = NULL;
+ const EVP_MD *md;
+ size_t data_size;
+ uint16_t alg_nr;
+ int retval = -EAGAIN;
+
+ data_size = sizeof(struct libfsverity_digest) +
+ le16_to_cpu(digest->digest_size);
+ alg_nr = le16_to_cpu(digest->digest_algorithm);
+ hash_alg = libfsverity_find_hash_alg_by_num(alg_nr);
+
+ if (!hash_alg) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ pkey = read_private_key(sig_params->keyfile);
+ if (!pkey) {
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ cert = read_certificate(sig_params->certfile);
+ if (!cert) {
+ retval = -EAGAIN;
+ goto out;
+ }
+
+ OpenSSL_add_all_digests();
+
+ md = EVP_get_digestbyname(hash_alg->name);
+ if (!md) {
+ fprintf(stderr,
+ "Warning: '%s' algorithm not found in OpenSSL library.\n"
+ " Falling back to SHA-256 signature.\n",
+ hash_alg->name);
+ md = EVP_sha256();
+ }
+
+ if (sign_pkcs7(digest, data_size, pkey, cert, md,
+ sig_ret, sig_size_ret))
+ retval = 0;
+
+ out:
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ return retval;
+}
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 07/20] Validate input arguments to libfsverity_compute_digest()
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (5 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 06/20] Introduce libfsverity_sign_digest() Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 08/20] Validate input parameters for libfsverity_sign_digest() Jes Sorensen
` (13 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
If any argument is invalid, return -EINVAL. Similarly
if any of the reserved fields in the params struct
are set, return -EINVAL;
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
libverity.c | 31 +++++++++++++++++++++++--------
1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/libverity.c b/libverity.c
index 183259e..1cef544 100644
--- a/libverity.c
+++ b/libverity.c
@@ -155,9 +155,31 @@ libfsverity_compute_digest(int fd,
struct fsverity_descriptor desc;
struct stat stbuf;
u64 file_size;
- int retval = -EINVAL;
+ int i, retval = -EINVAL;
+
+ if (!digest_ret)
+ return -EINVAL;
+ if (params->version != 1)
+ return -EINVAL;
+ if (!is_power_of_2(params->block_size))
+ return -EINVAL;
+ if (params->salt_size > sizeof(desc.salt)) {
+ error_msg("Salt too long (got %u bytes; max is %zu bytes)",
+ params->salt_size, sizeof(desc.salt));
+ return -EINVAL;
+ }
+ if (params->salt_size && !params->salt)
+ return -EINVAL;
+ for (i = 0;
+ i < sizeof(params->reserved) / sizeof(params->reserved[0]); i++) {
+ if (params->reserved[i])
+ return -EINVAL;
+ }
hash_alg = libfsverity_find_hash_alg_by_num(params->hash_algorithm);
+ if (!hash_alg)
+ return -EINVAL;
+
hash = hash_alg->create_ctx(hash_alg);
digest = malloc(sizeof(struct libfsverity_digest) +
@@ -180,16 +202,9 @@ libfsverity_compute_digest(int fd,
desc.version = 1;
desc.hash_algorithm = params->hash_algorithm;
- ASSERT(is_power_of_2(params->block_size));
desc.log_blocksize = ilog2(params->block_size);
if (params->salt_size != 0) {
- if (params->salt_size > sizeof(desc.salt)) {
- error_msg("Salt too long (got %u bytes; max is %zu bytes)",
- params->salt_size, sizeof(desc.salt));
- retval = EINVAL;
- goto error_out;
- }
memcpy(desc.salt, params->salt, params->salt_size);
desc.salt_size = params->salt_size;
}
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 08/20] Validate input parameters for libfsverity_sign_digest()
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (6 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 07/20] Validate input arguments to libfsverity_compute_digest() Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 09/20] Document API of libfsverity Jes Sorensen
` (12 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Return -EINVAL on any invalid input argument, as well
as if any of the reserved fields are set in
struct libfsverity_signature_digest
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
libverity.c | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
diff --git a/libverity.c b/libverity.c
index 1cef544..e16306d 100644
--- a/libverity.c
+++ b/libverity.c
@@ -494,18 +494,36 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest,
X509 *cert = NULL;
const EVP_MD *md;
size_t data_size;
- uint16_t alg_nr;
- int retval = -EAGAIN;
+ uint16_t alg_nr, digest_size;
+ int i, retval = -EAGAIN;
+ const char magic[8] = "FSVerity";
+
+ if (!digest || !sig_params || !sig_ret || !sig_size_ret)
+ return -EINVAL;
+
+ if (strncmp(digest->magic, magic, sizeof(magic)))
+ return -EINVAL;
+
+ if (!sig_params->keyfile || !sig_params->certfile)
+ return -EINVAL;
+
+ for (i = 0; i < sizeof(sig_params->reserved) /
+ sizeof(sig_params->reserved[0]); i++) {
+ if (sig_params->reserved[i])
+ return -EINVAL;
+ }
+
+ digest_size = le16_to_cpu(digest->digest_size);
+ data_size = sizeof(struct libfsverity_digest) + digest_size;
- data_size = sizeof(struct libfsverity_digest) +
- le16_to_cpu(digest->digest_size);
alg_nr = le16_to_cpu(digest->digest_algorithm);
hash_alg = libfsverity_find_hash_alg_by_num(alg_nr);
- if (!hash_alg) {
- retval = -EINVAL;
- goto out;
- }
+ if (!hash_alg)
+ return -EINVAL;
+
+ if (digest_size != hash_alg->digest_size)
+ return -EINVAL;
pkey = read_private_key(sig_params->keyfile);
if (!pkey) {
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 09/20] Document API of libfsverity
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (7 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 08/20] Validate input parameters for libfsverity_sign_digest() Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 10/20] Change libfsverity_compute_digest() to take a read function Jes Sorensen
` (11 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
libfsverity.h | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/libfsverity.h b/libfsverity.h
index a2abdb3..f6c4b13 100644
--- a/libfsverity.h
+++ b/libfsverity.h
@@ -64,18 +64,63 @@ struct fsverity_hash_alg {
struct hash_ctx *(*create_ctx)(const struct fsverity_hash_alg *alg);
};
+/*
+ * libfsverity_compute_digest - Compute digest of a file
+ * @fd: open file descriptor of file to compute digest for
+ * @params: struct libfsverity_merkle_tree_params specifying hash algorithm,
+ * block size, version, and optional salt parameters.
+ * reserved parameters must be zero.
+ * @digest_ret: Pointer to pointer for computed digest
+ *
+ * Returns:
+ * * 0 for success, -EINVAL for invalid input arguments, -ENOMEM if failed
+ * to allocate memory, -EBADF if fd is invalid, and -EAGAIN if root hash
+ * fails to compute.
+ * * digest_ret returns a pointer to the digest on success.
+ */
int
libfsverity_compute_digest(int fd,
const struct libfsverity_merkle_tree_params *params,
struct libfsverity_digest **digest_ret);
+/*
+ * libfsverity_sign_digest - Sign previously computed digest of a file
+ * @digest: pointer to previously computed digest
+ * @sig_params: struct libfsverity_signature_params providing filenames of
+ * the keyfile and certificate file. Reserved parameters must be zero.
+ * @sig_ret: Pointer to pointer for signed digest
+ * @sig_size_ret: Pointer to size of signed return digest
+ *
+ * Returns:
+ * * 0 for success, -EINVAL for invalid input arguments, -EAGAIN if key or
+ * certificate files fail to read, or if signing the digest fails.
+ * * sig_ret returns a pointer to the signed digest on success.
+ * * sig_size_ret returns the size of the signed digest on success.
+ */
int
libfsverity_sign_digest(const struct libfsverity_digest *digest,
const struct libfsverity_signature_params *sig_params,
uint8_t **sig_ret, size_t *sig_size_ret);
+/*
+ * libfsverity_find_hash_alg_by_name - Find hash algorithm by name
+ * @name: Pointer to name of hash algorithm
+ *
+ * Returns:
+ * struct fsverity_hash_alg success
+ * NULL on error
+ */
const struct fsverity_hash_alg *
libfsverity_find_hash_alg_by_name(const char *name);
+
+/*
+ * libfsverity_find_hash_alg_by_num - Find hash algorithm by number
+ * @name: Number of hash algorithm
+ *
+ * Returns:
+ * struct fsverity_hash_alg success
+ * NULL on error
+ */
const struct fsverity_hash_alg *
libfsverity_find_hash_alg_by_num(unsigned int num);
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 10/20] Change libfsverity_compute_digest() to take a read function
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (8 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 09/20] Document API of libfsverity Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 11/20] Make full_{read,write}() return proper error codes instead of bool Jes Sorensen
` (10 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
This changes the library to take a read_fn as callback and a pointer to
an opaque file descriptor. This allows us to provide a custome read function
for things like rpm which reads from an cpio archive instead of a file on
disk.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_sign.c | 20 +++++++++++++++++++-
libfsverity.h | 3 ++-
libverity.c | 39 +++++++--------------------------------
3 files changed, 28 insertions(+), 34 deletions(-)
diff --git a/cmd_sign.c b/cmd_sign.c
index e48e0aa..15d0937 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -12,6 +12,7 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include "commands.h"
#include "libfsverity.h"
@@ -45,6 +46,16 @@ static const struct option longopts[] = {
{NULL, 0, NULL, 0}
};
+static int read_callback(void *opague, void *buf, size_t count)
+{
+ int retval = -EBADF;
+
+ if (full_read(opague, buf, count))
+ retval = 0;
+
+ return retval;
+}
+
/* Sign a file for fs-verity by computing its measurement, then signing it. */
int fsverity_cmd_sign(const struct fsverity_command *cmd,
int argc, char *argv[])
@@ -59,6 +70,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
struct libfsverity_digest *digest = NULL;
struct libfsverity_merkle_tree_params params;
struct libfsverity_signature_params sig_params;
+ u64 file_size;
char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
u8 *sig = NULL;
size_t sig_size;
@@ -131,6 +143,11 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
if (!open_file(&file, argv[0], O_RDONLY, 0))
goto out_err;
+ if (!get_file_size(&file, &file_size)) {
+ error_msg_errno("unable to get file size");
+ goto out_err;
+ }
+
memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params));
params.version = 1;
params.hash_algorithm = hash_alg->hash_num;
@@ -138,7 +155,8 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
params.salt_size = salt_size;
params.salt = salt;
- if (libfsverity_compute_digest(file.fd, ¶ms, &digest))
+ if (libfsverity_compute_digest(&file, file_size, read_callback,
+ ¶ms, &digest))
goto out_err;
filedes_close(&file);
diff --git a/libfsverity.h b/libfsverity.h
index f6c4b13..ea36b8e 100644
--- a/libfsverity.h
+++ b/libfsverity.h
@@ -79,7 +79,8 @@ struct fsverity_hash_alg {
* * digest_ret returns a pointer to the digest on success.
*/
int
-libfsverity_compute_digest(int fd,
+libfsverity_compute_digest(void *fd, size_t file_size,
+ int (*read_fn)(void *, void *buf, size_t count),
const struct libfsverity_merkle_tree_params *params,
struct libfsverity_digest **digest_ret);
diff --git a/libverity.c b/libverity.c
index e16306d..f82f2d6 100644
--- a/libverity.c
+++ b/libverity.c
@@ -50,30 +50,13 @@ static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur,
return next->filled + hash->alg->digest_size > block_size;
}
-static int full_read_fd(int fd, void *buf, size_t count)
-{
- while (count) {
- int n = read(fd, buf, min(count, INT_MAX));
-
- if (n < 0) {
- error_msg_errno("reading from file");
- return n;
- }
- if (n == 0) {
- error_msg("unexpected end-of-file");
- return -ENODATA;
- }
- buf += n;
- count -= n;
- }
- return 0;
-}
-
/*
* Compute the file's Merkle tree root hash using the given hash algorithm,
* block size, and salt.
*/
-static bool compute_root_hash(int fd, u64 file_size,
+static bool compute_root_hash(void *fd,
+ int (*read_fn)(void *, void *buf, size_t count),
+ u64 file_size,
struct hash_ctx *hash, u32 block_size,
const u8 *salt, u32 salt_size, u8 *root_hash)
{
@@ -111,7 +94,7 @@ static bool compute_root_hash(int fd, u64 file_size,
for (offset = 0; offset < file_size; offset += block_size) {
buffers[-1].filled = min(block_size, file_size - offset);
- if (full_read_fd(fd, buffers[-1].data, buffers[-1].filled))
+ if (read_fn(fd, buffers[-1].data, buffers[-1].filled))
goto out;
level = -1;
@@ -145,7 +128,8 @@ out:
* contains the Merkle tree properties including the root hash.
*/
int
-libfsverity_compute_digest(int fd,
+libfsverity_compute_digest(void *fd, size_t file_size,
+ int (*read_fn)(void *, void *buf, size_t count),
const struct libfsverity_merkle_tree_params *params,
struct libfsverity_digest **digest_ret)
{
@@ -153,8 +137,6 @@ libfsverity_compute_digest(int fd,
struct libfsverity_digest *digest;
struct hash_ctx *hash;
struct fsverity_descriptor desc;
- struct stat stbuf;
- u64 file_size;
int i, retval = -EINVAL;
if (!digest_ret)
@@ -191,13 +173,6 @@ libfsverity_compute_digest(int fd,
digest->digest_size = cpu_to_le16(hash_alg->digest_size);
memset(digest->digest, 0, hash_alg->digest_size);
- if (fstat(fd, &stbuf) != 0) {
- error_msg_errno("can't stat input file");
- retval = -EBADF;
- goto error_out;
- }
- file_size = stbuf.st_size;
-
memset(&desc, 0, sizeof(desc));
desc.version = 1;
desc.hash_algorithm = params->hash_algorithm;
@@ -213,7 +188,7 @@ libfsverity_compute_digest(int fd,
/* Root hash of empty file is all 0's */
if (file_size != 0 &&
- !compute_root_hash(fd, file_size, hash, params->block_size,
+ !compute_root_hash(fd, read_fn, file_size, hash, params->block_size,
params->salt, params->salt_size,
desc.root_hash)) {
retval = -EAGAIN;
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 11/20] Make full_{read,write}() return proper error codes instead of bool
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (9 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 10/20] Change libfsverity_compute_digest() to take a read function Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 12/20] libfsverity: Remove dependencies on util.c Jes Sorensen
` (9 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
This allows for better error reporting, and simplifies the read callback
handler.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_enable.c | 4 +++-
cmd_sign.c | 13 +++++--------
util.c | 14 +++++++-------
util.h | 4 ++--
4 files changed, 17 insertions(+), 18 deletions(-)
diff --git a/cmd_enable.c b/cmd_enable.c
index 1bed3ef..9612778 100644
--- a/cmd_enable.c
+++ b/cmd_enable.c
@@ -56,6 +56,7 @@ static bool read_signature(const char *filename, u8 **sig_ret,
u64 file_size;
u8 *sig = NULL;
bool ok = false;
+ int status;
if (!open_file(&file, filename, O_RDONLY, 0))
goto out;
@@ -70,7 +71,8 @@ static bool read_signature(const char *filename, u8 **sig_ret,
goto out;
}
sig = xmalloc(file_size);
- if (!full_read(&file, sig, file_size))
+ status = full_read(&file, sig, file_size);
+ if (status < 0)
goto out;
*sig_ret = sig;
*sig_size_ret = file_size;
diff --git a/cmd_sign.c b/cmd_sign.c
index 15d0937..959e6d9 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <unistd.h>
#include "commands.h"
#include "libfsverity.h"
@@ -20,12 +21,13 @@
static bool write_signature(const char *filename, const u8 *sig, u32 sig_size)
{
struct filedes file;
+ int status;
bool ok;
if (!open_file(&file, filename, O_WRONLY|O_CREAT|O_TRUNC, 0644))
return false;
- ok = full_write(&file, sig, sig_size);
- ok &= filedes_close(&file);
+ status = full_write(&file, sig, sig_size);
+ ok = (!status && filedes_close(&file));
return ok;
}
@@ -48,12 +50,7 @@ static const struct option longopts[] = {
static int read_callback(void *opague, void *buf, size_t count)
{
- int retval = -EBADF;
-
- if (full_read(opague, buf, count))
- retval = 0;
-
- return retval;
+ return full_read(opague, buf, count);
}
/* Sign a file for fs-verity by computing its measurement, then signing it. */
diff --git a/util.c b/util.c
index 2218f2e..586d2b0 100644
--- a/util.c
+++ b/util.c
@@ -117,38 +117,38 @@ bool get_file_size(struct filedes *file, u64 *size_ret)
return true;
}
-bool full_read(struct filedes *file, void *buf, size_t count)
+int full_read(struct filedes *file, void *buf, size_t count)
{
while (count) {
int n = read(file->fd, buf, min(count, INT_MAX));
if (n < 0) {
error_msg_errno("reading from '%s'", file->name);
- return false;
+ return n;
}
if (n == 0) {
error_msg("unexpected end-of-file on '%s'", file->name);
- return false;
+ return -EBADFD;
}
buf += n;
count -= n;
}
- return true;
+ return 0;
}
-bool full_write(struct filedes *file, const void *buf, size_t count)
+int full_write(struct filedes *file, const void *buf, size_t count)
{
while (count) {
int n = write(file->fd, buf, min(count, INT_MAX));
if (n < 0) {
error_msg_errno("writing to '%s'", file->name);
- return false;
+ return n;
}
buf += n;
count -= n;
}
- return true;
+ return 0;
}
bool filedes_close(struct filedes *file)
diff --git a/util.h b/util.h
index dd9b803..c4dc066 100644
--- a/util.h
+++ b/util.h
@@ -113,8 +113,8 @@ struct filedes {
bool open_file(struct filedes *file, const char *filename, int flags, int mode);
bool get_file_size(struct filedes *file, u64 *size_ret);
-bool full_read(struct filedes *file, void *buf, size_t count);
-bool full_write(struct filedes *file, const void *buf, size_t count);
+int full_read(struct filedes *file, void *buf, size_t count);
+int full_write(struct filedes *file, const void *buf, size_t count);
bool filedes_close(struct filedes *file);
/* ========== String utilities ========== */
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 12/20] libfsverity: Remove dependencies on util.c
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (10 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 11/20] Make full_{read,write}() return proper error codes instead of bool Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 13/20] Update Makefile to install libfsverity and fsverity.h Jes Sorensen
` (8 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
There were a ton of cross dependencies to util.c. This gets rid of all
the use of string function wrappers, u<x> data types, and various debug
functions. Useful independent macros and inline functions are moved to
helpers.h which may be included by both libfsverity and the fsverity
application.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
fsverity.c | 1 +
hash_algs.c | 47 ++++++++++-----
hash_algs.h | 9 ++-
helpers.h | 43 ++++++++++++++
libverity.c | 165 ++++++++++++++++++++++++++++++++--------------------
util.c | 1 +
util.h | 56 ------------------
7 files changed, 184 insertions(+), 138 deletions(-)
create mode 100644 helpers.h
diff --git a/fsverity.c b/fsverity.c
index f9df72e..a176ead 100644
--- a/fsverity.c
+++ b/fsverity.c
@@ -13,6 +13,7 @@
#include <unistd.h>
#include "commands.h"
+#include "helpers.h"
#include "libfsverity.h"
static const struct fsverity_command {
diff --git a/hash_algs.c b/hash_algs.c
index d9f70b4..3066d87 100644
--- a/hash_algs.c
+++ b/hash_algs.c
@@ -10,7 +10,9 @@
#include <openssl/evp.h>
#include <stdlib.h>
#include <string.h>
+#include <assert.h>
+#include "helpers.h"
#include "fsverity_uapi.h"
#include "libfsverity.h"
#include "hash_algs.h"
@@ -27,9 +29,12 @@ static void openssl_digest_init(struct hash_ctx *_ctx)
{
struct openssl_hash_ctx *ctx = (void *)_ctx;
- if (EVP_DigestInit_ex(ctx->md_ctx, ctx->md, NULL) != 1)
- fatal_error("EVP_DigestInit_ex() failed for algorithm '%s'",
- ctx->base.alg->name);
+ if (EVP_DigestInit_ex(ctx->md_ctx, ctx->md, NULL) != 1) {
+ fprintf(stderr,
+ "%s: EVP_DigestInit_ex() failed for algorithm '%s'",
+ __func__, ctx->base.alg->name);
+ abort();
+ }
}
static void openssl_digest_update(struct hash_ctx *_ctx,
@@ -37,18 +42,24 @@ static void openssl_digest_update(struct hash_ctx *_ctx,
{
struct openssl_hash_ctx *ctx = (void *)_ctx;
- if (EVP_DigestUpdate(ctx->md_ctx, data, size) != 1)
- fatal_error("EVP_DigestUpdate() failed for algorithm '%s'",
- ctx->base.alg->name);
+ if (EVP_DigestUpdate(ctx->md_ctx, data, size) != 1) {
+ fprintf(stderr,
+ "%s: EVP_DigestUpdate() failed for algorithm '%s'",
+ __func__, ctx->base.alg->name);
+ abort();
+ }
}
-static void openssl_digest_final(struct hash_ctx *_ctx, u8 *digest)
+static void openssl_digest_final(struct hash_ctx *_ctx, uint8_t *digest)
{
struct openssl_hash_ctx *ctx = (void *)_ctx;
- if (EVP_DigestFinal_ex(ctx->md_ctx, digest, NULL) != 1)
- fatal_error("EVP_DigestFinal_ex() failed for algorithm '%s'",
- ctx->base.alg->name);
+ if (EVP_DigestFinal_ex(ctx->md_ctx, digest, NULL) != 1) {
+ fprintf(stderr,
+ "%s: EVP_DigestFinal_ex() failed for algorithm '%s'",
+ __func__, ctx->base.alg->name);
+ abort();
+ }
}
static void openssl_digest_ctx_free(struct hash_ctx *_ctx)
@@ -69,7 +80,10 @@ openssl_digest_ctx_create(const struct fsverity_hash_alg *alg, const EVP_MD *md)
{
struct openssl_hash_ctx *ctx;
- ctx = xzalloc(sizeof(*ctx));
+ ctx = malloc(sizeof(*ctx));
+ if (!ctx)
+ goto out_of_memory;
+ memset(ctx, 0, sizeof(*ctx));
ctx->base.alg = alg;
ctx->base.init = openssl_digest_init;
ctx->base.update = openssl_digest_update;
@@ -82,12 +96,16 @@ openssl_digest_ctx_create(const struct fsverity_hash_alg *alg, const EVP_MD *md)
*/
ctx->md_ctx = EVP_MD_CTX_create();
if (!ctx->md_ctx)
- fatal_error("out of memory");
+ goto out_of_memory;
ctx->md = md;
- ASSERT(EVP_MD_size(md) == alg->digest_size);
+ assert(EVP_MD_size(md) == alg->digest_size);
return &ctx->base;
+
+ out_of_memory:
+ fprintf(stderr, "%s: out of memory", __func__);
+ abort();
}
static struct hash_ctx *create_sha256_ctx(const struct fsverity_hash_alg *alg)
@@ -143,7 +161,8 @@ libfsverity_find_hash_alg_by_num(unsigned int num)
}
/* ->init(), ->update(), and ->final() all in one step */
-void hash_full(struct hash_ctx *ctx, const void *data, size_t size, u8 *digest)
+void hash_full(struct hash_ctx *ctx, const void *data, size_t size,
+ uint8_t *digest)
{
hash_init(ctx);
hash_update(ctx, data, size);
diff --git a/hash_algs.h b/hash_algs.h
index 2c7269a..546a601 100644
--- a/hash_algs.h
+++ b/hash_algs.h
@@ -4,13 +4,11 @@
#include <stdio.h>
-#include "util.h"
-
struct hash_ctx {
const struct fsverity_hash_alg *alg;
void (*init)(struct hash_ctx *ctx);
void (*update)(struct hash_ctx *ctx, const void *data, size_t size);
- void (*final)(struct hash_ctx *ctx, u8 *out);
+ void (*final)(struct hash_ctx *ctx, uint8_t *out);
void (*free)(struct hash_ctx *ctx);
};
@@ -25,7 +23,7 @@ static inline void hash_update(struct hash_ctx *ctx,
ctx->update(ctx, data, size);
}
-static inline void hash_final(struct hash_ctx *ctx, u8 *digest)
+static inline void hash_final(struct hash_ctx *ctx, uint8_t *digest)
{
ctx->final(ctx, digest);
}
@@ -36,6 +34,7 @@ static inline void hash_free(struct hash_ctx *ctx)
ctx->free(ctx);
}
-void hash_full(struct hash_ctx *ctx, const void *data, size_t size, u8 *digest);
+void hash_full(struct hash_ctx *ctx, const void *data, size_t size,
+ uint8_t *digest);
#endif /* HASH_ALGS_H */
diff --git a/helpers.h b/helpers.h
new file mode 100644
index 0000000..35ce626
--- /dev/null
+++ b/helpers.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Helper macros and inline functions for fsverity and libfsverity
+ *
+ * Copyright (C) 2018 Google LLC
+ * Copyright (C) 2020 Facebook
+ */
+#ifndef HELPERS_H
+#define HELPERS_H
+
+#include <stdbool.h>
+
+#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
+
+#define min(a, b) ({ \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ _a < _b ? _a : _b; \
+})
+#define max(a, b) ({ \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ _a > _b ? _a : _b; \
+})
+
+static inline bool is_power_of_2(unsigned long n)
+{
+ return n != 0 && ((n & (n - 1)) == 0);
+}
+
+static inline int ilog2(unsigned long n)
+{
+ return (8 * sizeof(n) - 1) - __builtin_clzl(n);
+}
+
+#define roundup(x, y) ({ \
+ __typeof__(y) _y = (y); \
+ (((x) + _y - 1) / _y) * _y; \
+})
+
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+#endif
diff --git a/libverity.c b/libverity.c
index f82f2d6..975d86e 100644
--- a/libverity.c
+++ b/libverity.c
@@ -14,17 +14,47 @@
#include <openssl/pkcs7.h>
#include <string.h>
#include <sys/stat.h>
-#include <unistd.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <assert.h>
#include "libfsverity.h"
#include "libfsverity_private.h"
#include "hash_algs.h"
+#include "helpers.h"
#define FS_VERITY_MAX_LEVELS 64
+#ifndef __force
+# ifdef __CHECKER__
+# define __force __attribute__((force))
+# else
+# define __force
+# endif
+#endif
+
+/* ========== Endianness conversion ========== */
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define cpu_to_le16(v) ((__force __le16)(uint16_t)(v))
+# define le16_to_cpu(v) ((__force uint16_t)(__le16)(v))
+# define cpu_to_le32(v) ((__force __le32)(uint32_t)(v))
+# define le32_to_cpu(v) ((__force uint32_t)(__le32)(v))
+# define cpu_to_le64(v) ((__force __le64)(uint64_t)(v))
+# define le64_to_cpu(v) ((__force uint64_t)(__le64)(v))
+#else
+# define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
+# define le16_to_cpu(v) (__builtin_bswap16((__force uint16_t)(v)))
+# define cpu_to_le32(v) ((__force __le32)__builtin_bswap32(v))
+# define le32_to_cpu(v) (__builtin_bswap32((__force uint32_t)(v)))
+# define cpu_to_le64(v) ((__force __le64)__builtin_bswap64(v))
+# define le64_to_cpu(v) (__builtin_bswap64((__force uint64_t)(v)))
+#endif
+
struct block_buffer {
- u32 filled;
- u8 *data;
+ uint32_t filled;
+ uint8_t *data;
};
/*
@@ -32,7 +62,8 @@ struct block_buffer {
* Returns true if the next level's block became full, else false.
*/
static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur,
- u32 block_size, const u8 *salt, u32 salt_size)
+ uint32_t block_size, const uint8_t *salt,
+ uint32_t salt_size)
{
struct block_buffer *next = cur + 1;
@@ -56,28 +87,33 @@ static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur,
*/
static bool compute_root_hash(void *fd,
int (*read_fn)(void *, void *buf, size_t count),
- u64 file_size,
- struct hash_ctx *hash, u32 block_size,
- const u8 *salt, u32 salt_size, u8 *root_hash)
+ uint64_t file_size, struct hash_ctx *hash,
+ uint32_t block_size, const uint8_t *salt,
+ uint32_t salt_size, uint8_t *root_hash)
{
- const u32 hashes_per_block = block_size / hash->alg->digest_size;
- const u32 padded_salt_size = roundup(salt_size, hash->alg->block_size);
- u8 *padded_salt = xzalloc(padded_salt_size);
- u64 blocks;
+ const uint32_t hashes_per_block = block_size / hash->alg->digest_size;
+ const uint32_t padded_salt_size = roundup(salt_size, hash->alg->block_size);
+ uint8_t *padded_salt = NULL;
+ uint64_t blocks;
int num_levels = 0;
int level;
struct block_buffer _buffers[1 + FS_VERITY_MAX_LEVELS + 1] = {};
struct block_buffer *buffers = &_buffers[1];
- u64 offset;
+ uint64_t offset;
bool ok = false;
- if (salt_size != 0)
+ if (salt_size != 0) {
+ padded_salt = malloc(padded_salt_size);
+ assert(padded_salt);
+ memset(padded_salt, 0, padded_salt_size);
+
memcpy(padded_salt, salt, salt_size);
+ }
/* Compute number of levels */
for (blocks = DIV_ROUND_UP(file_size, block_size); blocks > 1;
blocks = DIV_ROUND_UP(blocks, hashes_per_block)) {
- ASSERT(num_levels < FS_VERITY_MAX_LEVELS);
+ assert(num_levels < FS_VERITY_MAX_LEVELS);
num_levels++;
}
@@ -86,8 +122,10 @@ static bool compute_root_hash(void *fd,
* Buffers 0 <= level < num_levels are for the actual tree levels.
* Buffer 'num_levels' is for the root hash.
*/
- for (level = -1; level < num_levels; level++)
- buffers[level].data = xmalloc(block_size);
+ for (level = -1; level < num_levels; level++) {
+ buffers[level].data = malloc(block_size);
+ assert (buffers[level].data);
+ }
buffers[num_levels].data = root_hash;
/* Hash each data block, also hashing the tree blocks as they fill up */
@@ -101,7 +139,7 @@ static bool compute_root_hash(void *fd,
while (hash_one_block(hash, &buffers[level], block_size,
padded_salt, padded_salt_size)) {
level++;
- ASSERT(level < num_levels);
+ assert(level < num_levels);
}
}
/* Finish all nonempty pending tree blocks */
@@ -112,7 +150,7 @@ static bool compute_root_hash(void *fd,
}
/* Root hash was filled by the last call to hash_one_block() */
- ASSERT(buffers[num_levels].filled == hash->alg->digest_size);
+ assert(buffers[num_levels].filled == hash->alg->digest_size);
ok = true;
out:
for (level = -1; level < num_levels; level++)
@@ -146,8 +184,9 @@ libfsverity_compute_digest(void *fd, size_t file_size,
if (!is_power_of_2(params->block_size))
return -EINVAL;
if (params->salt_size > sizeof(desc.salt)) {
- error_msg("Salt too long (got %u bytes; max is %zu bytes)",
- params->salt_size, sizeof(desc.salt));
+ fprintf(stderr,
+ "%s: Salt too long (got %u bytes; max is %zu bytes)",
+ __func__, params->salt_size, sizeof(desc.salt));
return -EINVAL;
}
if (params->salt_size && !params->salt)
@@ -206,22 +245,6 @@ libfsverity_compute_digest(void *fd, size_t file_size,
return retval;
}
-static void __printf(1, 2) __cold
-error_msg_openssl(const char *format, ...)
-{
- va_list va;
-
- va_start(va, format);
- do_error_msg(format, va, 0);
- va_end(va);
-
- if (ERR_peek_error() == 0)
- return;
-
- fprintf(stderr, "OpenSSL library errors:\n");
- ERR_print_errors_fp(stderr);
-}
-
/* Read a PEM PKCS#8 formatted private key */
static EVP_PKEY *read_private_key(const char *keyfile)
{
@@ -230,15 +253,16 @@ static EVP_PKEY *read_private_key(const char *keyfile)
bio = BIO_new_file(keyfile, "r");
if (!bio) {
- error_msg_openssl("can't open '%s' for reading", keyfile);
+ fprintf(stderr, "%s: can't open '%s' for reading",
+ __func__, keyfile);
return NULL;
}
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
if (!pkey) {
- error_msg_openssl("Failed to parse private key file '%s'.\n"
- " Note: it must be in PEM PKCS#8 format.",
- keyfile);
+ fprintf(stderr, "%s: Failed to parse private key file '%s'.\n"
+ " Note: it must be in PEM PKCS#8 format.",
+ __func__, keyfile);
}
BIO_free(bio);
return pkey;
@@ -252,14 +276,16 @@ static X509 *read_certificate(const char *certfile)
bio = BIO_new_file(certfile, "r");
if (!bio) {
- error_msg_openssl("can't open '%s' for reading", certfile);
+ fprintf(stderr, "%s: can't open '%s' for reading",
+ __func__, certfile);
return NULL;
}
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (!cert) {
- error_msg_openssl("Failed to parse X.509 certificate file '%s'.\n"
- " Note: it must be in PEM format.",
- certfile);
+ fprintf(stderr,
+ "%s: Failed to parse X.509 certificate file '%s'.\n"
+ " Note: it must be in PEM format.",
+ __func__, certfile);
}
BIO_free(bio);
return cert;
@@ -269,13 +295,13 @@ static X509 *read_certificate(const char *certfile)
static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
- u8 **sig_ret, size_t *sig_size_ret)
+ uint8_t **sig_ret, size_t *sig_size_ret)
{
CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo,
null, content_info, issuer_and_serial, signer_infos,
signer_info, sign_algo, signature;
EVP_MD_CTX md_ctx;
- u8 *name_der = NULL, *sig = NULL, *pkcs7_data = NULL;
+ uint8_t *name_der = NULL, *sig = NULL, *pkcs7_data = NULL;
size_t pkcs7_data_len, sig_len;
int name_der_len, sig_nid;
bool ok = false;
@@ -290,19 +316,20 @@ static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
name_der_len = i2d_X509_NAME(X509_get_subject_name(cert), &name_der);
if (name_der_len < 0) {
- error_msg_openssl("i2d_X509_NAME failed");
+ fprintf(stderr, "%s: i2d_X509_NAME failed", __func__);
goto out;
}
if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey)) {
- error_msg_openssl("EVP_DigestSignInit failed");
+ fprintf(stderr, "%s: EVP_DigestSignInit failed", __func__);
goto out;
}
sig_len = EVP_PKEY_size(pkey);
- sig = xmalloc(sig_len);
+ sig = malloc(sig_len);
+ assert(sig);
if (!EVP_DigestSign(&md_ctx, sig, &sig_len, data_to_sign, data_size)) {
- error_msg_openssl("EVP_DigestSign failed");
+ fprintf(stderr, "%s: EVP_DigestSign failed", __func__);
goto out;
}
@@ -344,11 +371,14 @@ static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
!CBB_add_asn1(&signer_info, &signature, CBS_ASN1_OCTETSTRING) ||
!CBB_add_bytes(&signature, sig, sig_len) ||
!CBB_finish(&out, &pkcs7_data, &pkcs7_data_len)) {
- error_msg_openssl("failed to construct PKCS#7 data");
+ fprintf(stderr, "%s: failed to construct PKCS#7 data",
+ __func__);
goto out;
}
- *sig_ret = xmemdup(pkcs7_data, pkcs7_data_len);
+ *sig_ret = malloc(pkcs7_data_len);
+ assert(*sig_ret);
+ memcpy(*sig_ret, pkcs7_data, pkcs7_data_len);
*sig_size_ret = pkcs7_data_len;
ok = true;
out:
@@ -367,7 +397,7 @@ static BIO *new_mem_buf(const void *buf, size_t size)
{
BIO *bio;
- ASSERT(size <= INT_MAX);
+ assert(size <= INT_MAX);
/*
* Prior to OpenSSL 1.1.0, BIO_new_mem_buf() took a non-const pointer,
* despite still marking the resulting bio as read-only. So cast away
@@ -375,13 +405,13 @@ static BIO *new_mem_buf(const void *buf, size_t size)
*/
bio = BIO_new_mem_buf((void *)buf, size);
if (!bio)
- error_msg_openssl("out of memory");
+ fprintf(stderr, "%s: out of memory", __func__);
return bio;
}
static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
- u8 **sig_ret, size_t *sig_size_ret)
+ uint8_t **sig_ret, size_t *sig_size_ret)
{
/*
* PKCS#7 signing flags:
@@ -403,8 +433,8 @@ static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
*/
int pkcs7_flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOATTR |
PKCS7_NOCERTS | PKCS7_PARTIAL;
- u8 *sig;
- u32 sig_size;
+ uint8_t *sig;
+ uint32_t sig_size;
BIO *bio = NULL;
PKCS7 *p7 = NULL;
bool ok = false;
@@ -415,34 +445,43 @@ static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags);
if (!p7) {
- error_msg_openssl("failed to initialize PKCS#7 signature object");
+ fprintf(stderr,
+ "%s: failed to initialize PKCS#7 signature object",
+ __func__);
goto out;
}
if (!PKCS7_sign_add_signer(p7, cert, pkey, md, pkcs7_flags)) {
- error_msg_openssl("failed to add signer to PKCS#7 signature object");
+ fprintf(stderr,
+ "%s: failed to add signer to PKCS#7 signature object",
+ __func__);
goto out;
}
if (PKCS7_final(p7, bio, pkcs7_flags) != 1) {
- error_msg_openssl("failed to finalize PKCS#7 signature");
+ fprintf(stderr, "%s: failed to finalize PKCS#7 signature",
+ __func__);
goto out;
}
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
if (!bio) {
- error_msg_openssl("out of memory");
+ fprintf(stderr, "%s: out of memory", __func__);
goto out;
}
if (i2d_PKCS7_bio(bio, p7) != 1) {
- error_msg_openssl("failed to DER-encode PKCS#7 signature object");
+ fprintf(stderr,
+ "%s: failed to DER-encode PKCS#7 signature object",
+ __func__);
goto out;
}
sig_size = BIO_get_mem_data(bio, &sig);
- *sig_ret = xmemdup(sig, sig_size);
+ *sig_ret = malloc(sig_size);
+ assert(*sig_ret);
+ memcpy(*sig_ret, sig, sig_size);
*sig_size_ret = sig_size;
ok = true;
out:
diff --git a/util.c b/util.c
index 586d2b0..0c4bf79 100644
--- a/util.c
+++ b/util.c
@@ -18,6 +18,7 @@
#include <unistd.h>
#include "util.h"
+#include "helpers.h"
/* ========== Memory allocation ========== */
diff --git a/util.h b/util.h
index c4dc066..bd7ab9c 100644
--- a/util.h
+++ b/util.h
@@ -17,14 +17,6 @@ typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
-#ifndef __force
-# ifdef __CHECKER__
-# define __force __attribute__((force))
-# else
-# define __force
-# endif
-#endif
-
#ifndef __printf
# define __printf(fmt_idx, vargs_idx) \
__attribute__((format(printf, fmt_idx, vargs_idx)))
@@ -38,54 +30,6 @@ typedef uint64_t u64;
# define __cold __attribute__((cold))
#endif
-#define min(a, b) ({ \
- __typeof__(a) _a = (a); \
- __typeof__(b) _b = (b); \
- _a < _b ? _a : _b; \
-})
-#define max(a, b) ({ \
- __typeof__(a) _a = (a); \
- __typeof__(b) _b = (b); \
- _a > _b ? _a : _b; \
-})
-
-#define roundup(x, y) ({ \
- __typeof__(y) _y = (y); \
- (((x) + _y - 1) / _y) * _y; \
-})
-
-#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
-
-#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
-
-static inline bool is_power_of_2(unsigned long n)
-{
- return n != 0 && ((n & (n - 1)) == 0);
-}
-
-static inline int ilog2(unsigned long n)
-{
- return (8 * sizeof(n) - 1) - __builtin_clzl(n);
-}
-
-/* ========== Endianness conversion ========== */
-
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-# define cpu_to_le16(v) ((__force __le16)(u16)(v))
-# define le16_to_cpu(v) ((__force u16)(__le16)(v))
-# define cpu_to_le32(v) ((__force __le32)(u32)(v))
-# define le32_to_cpu(v) ((__force u32)(__le32)(v))
-# define cpu_to_le64(v) ((__force __le64)(u64)(v))
-# define le64_to_cpu(v) ((__force u64)(__le64)(v))
-#else
-# define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
-# define le16_to_cpu(v) (__builtin_bswap16((__force u16)(v)))
-# define cpu_to_le32(v) ((__force __le32)__builtin_bswap32(v))
-# define le32_to_cpu(v) (__builtin_bswap32((__force u32)(v)))
-# define cpu_to_le64(v) ((__force __le64)__builtin_bswap64(v))
-# define le64_to_cpu(v) (__builtin_bswap64((__force u64)(v)))
-#endif
-
/* ========== Memory allocation ========== */
void *xmalloc(size_t size);
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 13/20] Update Makefile to install libfsverity and fsverity.h
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (11 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 12/20] libfsverity: Remove dependencies on util.c Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 14/20] Change libfsverity_find_hash_alg_by_name() to return the alg number Jes Sorensen
` (7 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
In addition this adds a 'static' build target, which links in libfsverity
statically rather than relying on the shared library.
This also honors PREFIX for installation location.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
Makefile | 34 ++++++++++++++++++++++++++--------
1 file changed, 26 insertions(+), 8 deletions(-)
diff --git a/Makefile b/Makefile
index 966afa0..5bbcd87 100644
--- a/Makefile
+++ b/Makefile
@@ -1,34 +1,52 @@
EXE := fsverity
+STATIC := fsverity-static
LIB := libfsverity.so
+INC := libfsverity.h
CFLAGS := -O2 -Wall
CPPFLAGS := -D_FILE_OFFSET_BITS=64
LDLIBS := -lcrypto
-DESTDIR := /usr/local
-LIBDIR := /usr/lib64
+PREFIX = /usr
+BINDIR := $(PREFIX)/bin
+LIBDIR := $(PREFIX)/lib64
+INCDIR := $(PREFIX)/include
SRC := $(wildcard *.c)
OBJ := fsverity.o cmd_enable.o cmd_measure.o cmd_sign.o util.o
SSRC := libverity.c hash_algs.c
-SOBJ := libverity.so hash_algs.so
+SHOBJ := libverity.so hash_algs.so
+STOBJ := libverity.o hash_algs.o
HDRS := $(wildcard *.h)
all:$(EXE)
+static:$(STATIC)
+
$(EXE):$(OBJ) $(LIB)
$(CC) -o $@ $(OBJ) $(LDLIBS) -L . -l fsverity
+$(STATIC):$(OBJ) $(STOBJ)
+ $(CC) -o $@ $(OBJ) $(STOBJ) $(LDLIBS)
+
$(OBJ): %.o: %.c $(HDRS)
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
-$(SOBJ): %.so: %.c $(HDRS)
+$(STOBJ): %.o: %.c $(HDRS)
+ $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+$(SHOBJ): %.so: %.c $(HDRS)
$(CC) -c -fPIC $(CFLAGS) $(CPPFLAGS) $< -o $@
-libfsverity.so: $(SOBJ)
- $(CC) $(LDLIBS) -shared -o libfsverity.so $(SOBJ)
+libfsverity.so: $(SHOBJ)
+ $(CC) $(LDLIBS) -shared -o $@ $(SHOBJ)
clean:
- rm -f $(EXE) $(OBJ) $(SOBJ) $(LIB)
+ rm -f $(EXE) $(OBJ) $(SHOBJ) $(LIB) $(STOBJ) $(STATIC)
install:all
- install -Dm755 -t $(DESTDIR)/bin $(EXE)
+ install -Dm755 -t $(BINDIR) $(EXE)
+ install -Dm755 -t $(LIBDIR) $(LIB)
+ install -Dm644 -t $(INCDIR) $(INC)
+
+install-static:static
+ install -Dm755 -t $(BINDIR) $(STATIC)
.PHONY: all clean install
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 14/20] Change libfsverity_find_hash_alg_by_name() to return the alg number
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (12 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 13/20] Update Makefile to install libfsverity and fsverity.h Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:54 ` [PATCH 15/20] Make libfsverity_find_hash_alg_by_name() private to the shared library Jes Sorensen
` (6 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
This elimnates the need for struct fsverity_hash_alg in the use case
of libfsverity_find_hash_alg_by_name(). In addition this introduces a
libfsverity_digest_size() which returns the size of the digest for the
given algorithm, and libfsverity_hash_name() which returns a string
with the name of the algorithm. Note the returned string must be
freed by the caller.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_enable.c | 6 +++---
cmd_sign.c | 25 +++++++++++++++----------
hash_algs.c | 35 ++++++++++++++++++++++++++++++-----
libfsverity.h | 28 +++++++++++++++++++++++-----
4 files changed, 71 insertions(+), 23 deletions(-)
diff --git a/cmd_enable.c b/cmd_enable.c
index 9612778..ac977e7 100644
--- a/cmd_enable.c
+++ b/cmd_enable.c
@@ -22,7 +22,7 @@ static bool parse_hash_alg_option(const char *arg, u32 *alg_ptr)
{
char *end;
unsigned long n = strtoul(arg, &end, 10);
- const struct fsverity_hash_alg *alg;
+ uint16_t alg;
if (*alg_ptr != 0) {
error_msg("--hash-alg can only be specified once");
@@ -37,8 +37,8 @@ static bool parse_hash_alg_option(const char *arg, u32 *alg_ptr)
/* Specified by name? */
alg = libfsverity_find_hash_alg_by_name(arg);
- if (alg != NULL) {
- *alg_ptr = alg->hash_num;
+ if (alg) {
+ *alg_ptr = alg;
return true;
}
error_msg("unknown hash algorithm: '%s'", arg);
diff --git a/cmd_sign.c b/cmd_sign.c
index 959e6d9..80e62d5 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -57,7 +57,6 @@ static int read_callback(void *opague, void *buf, size_t count)
int fsverity_cmd_sign(const struct fsverity_command *cmd,
int argc, char *argv[])
{
- const struct fsverity_hash_alg *hash_alg = NULL;
struct filedes file = { .fd = -1 };
u32 block_size = 0;
u8 *salt = NULL;
@@ -69,7 +68,10 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
struct libfsverity_signature_params sig_params;
u64 file_size;
char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
+ char *hash_name = NULL;
u8 *sig = NULL;
+ u16 alg_nr = 0;
+ int digest_size;
size_t sig_size;
int status;
int c;
@@ -77,12 +79,12 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
switch (c) {
case OPT_HASH_ALG:
- if (hash_alg != NULL) {
+ if (alg_nr) {
error_msg("--hash-alg can only be specified once");
goto out_usage;
}
- hash_alg = libfsverity_find_hash_alg_by_name(optarg);
- if (hash_alg == NULL) {
+ alg_nr = libfsverity_find_hash_alg_by_name(optarg);
+ if (!alg_nr) {
error_msg("unknown hash algorithm: '%s'",
optarg);
fputs("Available hash algorithms: ", stderr);
@@ -124,8 +126,8 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
if (argc != 2)
goto out_usage;
- if (hash_alg == NULL)
- hash_alg = libfsverity_find_hash_alg_by_num(FS_VERITY_HASH_ALG_DEFAULT);
+ if (!alg_nr)
+ alg_nr = FS_VERITY_HASH_ALG_DEFAULT;
if (block_size == 0)
block_size = get_default_block_size();
@@ -147,7 +149,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params));
params.version = 1;
- params.hash_algorithm = hash_alg->hash_num;
+ params.hash_algorithm = alg_nr;
params.block_size = block_size;
params.salt_size = salt_size;
params.salt = salt;
@@ -158,6 +160,8 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
filedes_close(&file);
+ digest_size = libfsverity_digest_size(alg_nr);
+
memset(&sig_params, 0, sizeof(struct libfsverity_signature_params));
sig_params.keyfile = keyfile;
sig_params.certfile = certfile;
@@ -169,9 +173,10 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
if (!write_signature(argv[1], sig, sig_size))
goto out_err;
- bin2hex(digest->digest, hash_alg->digest_size, digest_hex);
- printf("Signed file '%s' (%s:%s)\n", argv[0], hash_alg->name,
- digest_hex);
+ hash_name = libfsverity_hash_name(alg_nr);
+ bin2hex(digest->digest, digest_size, digest_hex);
+ printf("Signed file '%s' (%s:%s)\n", argv[0], hash_name, digest_hex);
+ free(hash_name);
status = 0;
out:
free(salt);
diff --git a/hash_algs.c b/hash_algs.c
index 3066d87..120d1be 100644
--- a/hash_algs.c
+++ b/hash_algs.c
@@ -137,17 +137,17 @@ const struct fsverity_hash_alg fsverity_hash_algs[] = {
},
};
-const struct fsverity_hash_alg *
-libfsverity_find_hash_alg_by_name(const char *name)
+uint16_t libfsverity_find_hash_alg_by_name(const char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
if (fsverity_hash_algs[i].name &&
- !strcmp(name, fsverity_hash_algs[i].name))
- return &fsverity_hash_algs[i];
+ !strcmp(name, fsverity_hash_algs[i].name)) {
+ return fsverity_hash_algs[i].hash_num;
+ }
}
- return NULL;
+ return 0;
}
const struct fsverity_hash_alg *
@@ -160,6 +160,31 @@ libfsverity_find_hash_alg_by_num(unsigned int num)
return NULL;
}
+int libfsverity_digest_size(uint16_t alg_nr)
+{
+ if (alg_nr < ARRAY_SIZE(fsverity_hash_algs) &&
+ fsverity_hash_algs[alg_nr].name)
+ return fsverity_hash_algs[alg_nr].digest_size;
+
+ return -1;
+}
+
+char *libfsverity_hash_name(uint16_t alg_nr)
+{
+ int namelen;
+ char *hash_name = NULL;
+
+ if (alg_nr < ARRAY_SIZE(fsverity_hash_algs) &&
+ fsverity_hash_algs[alg_nr].name) {
+ namelen = strlen(fsverity_hash_algs[alg_nr].name);
+ hash_name = malloc(namelen + 1);
+ if (hash_name)
+ strcpy(hash_name, fsverity_hash_algs[alg_nr].name);
+ }
+
+ return hash_name;
+}
+
/* ->init(), ->update(), and ->final() all in one step */
void hash_full(struct hash_ctx *ctx, const void *data, size_t size,
uint8_t *digest)
diff --git a/libfsverity.h b/libfsverity.h
index ea36b8e..a505cbe 100644
--- a/libfsverity.h
+++ b/libfsverity.h
@@ -58,7 +58,7 @@ struct libfsverity_signature_params {
struct fsverity_hash_alg {
const char *name;
- unsigned int digest_size;
+ int digest_size;
unsigned int block_size;
uint16_t hash_num;
struct hash_ctx *(*create_ctx)(const struct fsverity_hash_alg *alg);
@@ -108,11 +108,9 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest,
* @name: Pointer to name of hash algorithm
*
* Returns:
- * struct fsverity_hash_alg success
- * NULL on error
+ * uint16_t containing hash algorithm number, zero on error.
*/
-const struct fsverity_hash_alg *
-libfsverity_find_hash_alg_by_name(const char *name);
+uint16_t libfsverity_find_hash_alg_by_name(const char *name);
/*
* libfsverity_find_hash_alg_by_num - Find hash algorithm by number
@@ -125,4 +123,24 @@ libfsverity_find_hash_alg_by_name(const char *name);
const struct fsverity_hash_alg *
libfsverity_find_hash_alg_by_num(unsigned int num);
+/*
+ * libfsverity_digest_size - Return size of digest for a given algorithm
+ * @alg_nr: Valid hash algorithm number
+ *
+ * Returns:
+ * int containing size of digest, -1 on error.
+ */
+int libfsverity_digest_size(uint16_t alg_nr);
+
+/*
+ * libfsverity_find_hash_name - Find name of hash algorithm by number
+ * @name: Number of hash algorithm
+ *
+ * Returns:
+ * New allocated string containing name of algorithm.
+ * String must be freed by caller.
+ * NULL on error
+ */
+char *libfsverity_hash_name(uint16_t num);
+
#endif
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 15/20] Make libfsverity_find_hash_alg_by_name() private to the shared library
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (13 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 14/20] Change libfsverity_find_hash_alg_by_name() to return the alg number Jes Sorensen
@ 2020-04-24 20:54 ` Jes Sorensen
2020-04-24 20:55 ` [PATCH 16/20] libfsverity_sign_digest() use ARRAY_SIZE() Jes Sorensen
` (5 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:54 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
This moves struct fsverity_hash_alg out of the public API. Instead
implement show_all_hash_algs() by calling libfsverity_hash_name()
until it returns null.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_enable.c | 2 +-
cmd_measure.c | 17 ++++++++---------
cmd_sign.c | 2 +-
fsverity.c | 16 +++++++---------
hash_algs.c | 1 +
libfsverity.h | 19 -------------------
libfsverity_private.h | 19 +++++++++++++++++++
7 files changed, 37 insertions(+), 39 deletions(-)
diff --git a/cmd_enable.c b/cmd_enable.c
index ac977e7..632ac84 100644
--- a/cmd_enable.c
+++ b/cmd_enable.c
@@ -42,7 +42,7 @@ static bool parse_hash_alg_option(const char *arg, u32 *alg_ptr)
return true;
}
error_msg("unknown hash algorithm: '%s'", arg);
- fputs("Available hash algorithms: ", stderr);
+ fputs("Available hash algorithms:", stderr);
show_all_hash_algs(stderr);
putc('\n', stderr);
diff --git a/cmd_measure.c b/cmd_measure.c
index 4c0777f..df39da0 100644
--- a/cmd_measure.c
+++ b/cmd_measure.c
@@ -22,9 +22,8 @@ int fsverity_cmd_measure(const struct fsverity_command *cmd,
struct fsverity_digest *d = NULL;
struct filedes file;
char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
- const struct fsverity_hash_alg *hash_alg;
char _hash_alg_name[32];
- const char *hash_alg_name;
+ char *hash_alg_name;
int status;
int i;
@@ -48,14 +47,14 @@ int fsverity_cmd_measure(const struct fsverity_command *cmd,
ASSERT(d->digest_size <= FS_VERITY_MAX_DIGEST_SIZE);
bin2hex(d->digest, d->digest_size, digest_hex);
- hash_alg = libfsverity_find_hash_alg_by_num(d->digest_algorithm);
- if (hash_alg) {
- hash_alg_name = hash_alg->name;
- } else {
+ hash_alg_name = libfsverity_hash_name(d->digest_algorithm);
+ if (!hash_alg_name)
sprintf(_hash_alg_name, "ALG_%u", d->digest_algorithm);
- hash_alg_name = _hash_alg_name;
- }
- printf("%s:%s %s\n", hash_alg_name, digest_hex, argv[i]);
+
+ printf("%s:%s %s\n",
+ hash_alg_name ? hash_alg_name :_hash_alg_name,
+ digest_hex, argv[i]);
+ free(hash_alg_name);
}
status = 0;
out:
diff --git a/cmd_sign.c b/cmd_sign.c
index 80e62d5..57a9250 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -87,7 +87,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
if (!alg_nr) {
error_msg("unknown hash algorithm: '%s'",
optarg);
- fputs("Available hash algorithms: ", stderr);
+ fputs("Available hash algorithms:", stderr);
show_all_hash_algs(stderr);
putc('\n', stderr);
goto out_usage;
diff --git a/fsverity.c b/fsverity.c
index a176ead..2e2b553 100644
--- a/fsverity.c
+++ b/fsverity.c
@@ -51,14 +51,12 @@ static const struct fsverity_command {
void show_all_hash_algs(FILE *fp)
{
int i = 1;
- const char *sep = "";
- const struct fsverity_hash_alg *alg;
-
- while ((alg = libfsverity_find_hash_alg_by_num(i++))) {
- if (alg && alg->name) {
- fprintf(fp, "%s%s", sep, alg->name);
- sep = ", ";
- }
+ const char *sep = " ";
+ char *alg;
+
+ while ((alg = libfsverity_hash_name(i++))) {
+ fprintf(fp, "%s%s", sep, alg);
+ free(alg);
}
}
@@ -75,7 +73,7 @@ static void usage_all(FILE *fp)
" fsverity --help\n"
" fsverity --version\n"
"\n"
-"Available hash algorithms: ", fp);
+"Available hash algorithms:", fp);
show_all_hash_algs(fp);
putc('\n', fp);
}
diff --git a/hash_algs.c b/hash_algs.c
index 120d1be..03b9de9 100644
--- a/hash_algs.c
+++ b/hash_algs.c
@@ -15,6 +15,7 @@
#include "helpers.h"
#include "fsverity_uapi.h"
#include "libfsverity.h"
+#include "libfsverity_private.h"
#include "hash_algs.h"
/* ========== libcrypto (OpenSSL) wrappers ========== */
diff --git a/libfsverity.h b/libfsverity.h
index a505cbe..4f0f885 100644
--- a/libfsverity.h
+++ b/libfsverity.h
@@ -56,14 +56,6 @@ struct libfsverity_signature_params {
uint64_t reserved[11];
};
-struct fsverity_hash_alg {
- const char *name;
- int digest_size;
- unsigned int block_size;
- uint16_t hash_num;
- struct hash_ctx *(*create_ctx)(const struct fsverity_hash_alg *alg);
-};
-
/*
* libfsverity_compute_digest - Compute digest of a file
* @fd: open file descriptor of file to compute digest for
@@ -112,17 +104,6 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest,
*/
uint16_t libfsverity_find_hash_alg_by_name(const char *name);
-/*
- * libfsverity_find_hash_alg_by_num - Find hash algorithm by number
- * @name: Number of hash algorithm
- *
- * Returns:
- * struct fsverity_hash_alg success
- * NULL on error
- */
-const struct fsverity_hash_alg *
-libfsverity_find_hash_alg_by_num(unsigned int num);
-
/*
* libfsverity_digest_size - Return size of digest for a given algorithm
* @alg_nr: Valid hash algorithm number
diff --git a/libfsverity_private.h b/libfsverity_private.h
index 5f3e1b4..f8eebe2 100644
--- a/libfsverity_private.h
+++ b/libfsverity_private.h
@@ -30,4 +30,23 @@ struct fsverity_descriptor {
uint8_t signature[]; /* optional PKCS#7 signature */
};
+struct fsverity_hash_alg {
+ const char *name;
+ int digest_size;
+ unsigned int block_size;
+ uint16_t hash_num;
+ struct hash_ctx *(*create_ctx)(const struct fsverity_hash_alg *alg);
+};
+
+/*
+ * libfsverity_find_hash_alg_by_num - Find hash algorithm by number
+ * @name: Number of hash algorithm
+ *
+ * Returns:
+ * struct fsverity_hash_alg success
+ * NULL on error
+ */
+const struct fsverity_hash_alg *
+libfsverity_find_hash_alg_by_num(unsigned int num);
+
#endif
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 16/20] libfsverity_sign_digest() use ARRAY_SIZE()
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (14 preceding siblings ...)
2020-04-24 20:54 ` [PATCH 15/20] Make libfsverity_find_hash_alg_by_name() private to the shared library Jes Sorensen
@ 2020-04-24 20:55 ` Jes Sorensen
2020-04-24 20:55 ` [PATCH 17/20] fsverity_cmd_sign() use sizeof() input argument instead of struct Jes Sorensen
` (4 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:55 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
libverity.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libverity.c b/libverity.c
index 975d86e..7908dcd 100644
--- a/libverity.c
+++ b/libverity.c
@@ -521,8 +521,7 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest,
if (!sig_params->keyfile || !sig_params->certfile)
return -EINVAL;
- for (i = 0; i < sizeof(sig_params->reserved) /
- sizeof(sig_params->reserved[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(sig_params->reserved); i++) {
if (sig_params->reserved[i])
return -EINVAL;
}
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 17/20] fsverity_cmd_sign() use sizeof() input argument instead of struct
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (15 preceding siblings ...)
2020-04-24 20:55 ` [PATCH 16/20] libfsverity_sign_digest() use ARRAY_SIZE() Jes Sorensen
@ 2020-04-24 20:55 ` Jes Sorensen
2020-04-24 20:55 ` [PATCH 18/20] fsverity_cmd_sign() don't exit on error without closing file descriptor Jes Sorensen
` (3 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:55 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_sign.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cmd_sign.c b/cmd_sign.c
index 57a9250..d699d85 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -147,7 +147,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
goto out_err;
}
- memset(¶ms, 0, sizeof(struct libfsverity_merkle_tree_params));
+ memset(¶ms, 0, sizeof(params));
params.version = 1;
params.hash_algorithm = alg_nr;
params.block_size = block_size;
@@ -162,7 +162,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
digest_size = libfsverity_digest_size(alg_nr);
- memset(&sig_params, 0, sizeof(struct libfsverity_signature_params));
+ memset(&sig_params, 0, sizeof(sig_params));
sig_params.keyfile = keyfile;
sig_params.certfile = certfile;
if (libfsverity_sign_digest(digest, &sig_params, &sig, &sig_size)) {
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 18/20] fsverity_cmd_sign() don't exit on error without closing file descriptor
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (16 preceding siblings ...)
2020-04-24 20:55 ` [PATCH 17/20] fsverity_cmd_sign() use sizeof() input argument instead of struct Jes Sorensen
@ 2020-04-24 20:55 ` Jes Sorensen
2020-04-24 20:55 ` [PATCH 19/20] Improve documentation of libfsverity.h API Jes Sorensen
` (2 subsequent siblings)
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:55 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
cmd_sign.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/cmd_sign.c b/cmd_sign.c
index d699d85..7d8ec58 100644
--- a/cmd_sign.c
+++ b/cmd_sign.c
@@ -73,7 +73,7 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
u16 alg_nr = 0;
int digest_size;
size_t sig_size;
- int status;
+ int status, ret;
int c;
while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
@@ -154,12 +154,13 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd,
params.salt_size = salt_size;
params.salt = salt;
- if (libfsverity_compute_digest(&file, file_size, read_callback,
- ¶ms, &digest))
- goto out_err;
-
+ ret = libfsverity_compute_digest(&file, file_size, read_callback,
+ ¶ms, &digest);
filedes_close(&file);
+ if (ret)
+ goto out_err;
+
digest_size = libfsverity_digest_size(alg_nr);
memset(&sig_params, 0, sizeof(sig_params));
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 19/20] Improve documentation of libfsverity.h API
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (17 preceding siblings ...)
2020-04-24 20:55 ` [PATCH 18/20] fsverity_cmd_sign() don't exit on error without closing file descriptor Jes Sorensen
@ 2020-04-24 20:55 ` Jes Sorensen
2020-04-24 20:55 ` [PATCH 20/20] Fixup Makefile Jes Sorensen
2020-05-07 14:03 ` [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:55 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
libfsverity.h | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/libfsverity.h b/libfsverity.h
index 4f0f885..c4f6b8d 100644
--- a/libfsverity.h
+++ b/libfsverity.h
@@ -58,17 +58,21 @@ struct libfsverity_signature_params {
/*
* libfsverity_compute_digest - Compute digest of a file
+ * An fsverity digest is the root of the Merkle tree of the file.
+ * Not to be confused with a traditional file digests computed over
+ * the entire file.
* @fd: open file descriptor of file to compute digest for
* @params: struct libfsverity_merkle_tree_params specifying hash algorithm,
* block size, version, and optional salt parameters.
* reserved parameters must be zero.
- * @digest_ret: Pointer to pointer for computed digest
+ * @digest_ret: Pointer to pointer for computed digest.
*
* Returns:
* * 0 for success, -EINVAL for invalid input arguments, -ENOMEM if failed
* to allocate memory, -EBADF if fd is invalid, and -EAGAIN if root hash
* fails to compute.
- * * digest_ret returns a pointer to the digest on success.
+ * * digest_ret returns a pointer to the digest on success. The digest object
+ * is allocated by libfsverity and must be freed by the caller.
*/
int
libfsverity_compute_digest(void *fd, size_t file_size,
@@ -78,6 +82,12 @@ libfsverity_compute_digest(void *fd, size_t file_size,
/*
* libfsverity_sign_digest - Sign previously computed digest of a file
+ * This is signature is used by the file system to validate the
+ * signed file measurement against a public key loaded into the
+ * .fs-verity kernel keyring, when CONFIG_FS_VERITY_BUILTIN_SIGNATURES
+ * is enabled. The signature is formatted as PKCS#7 stored in DER
+ * format. See Documentation/filesystems/fsverity.rst for further
+ * details.
* @digest: pointer to previously computed digest
* @sig_params: struct libfsverity_signature_params providing filenames of
* the keyfile and certificate file. Reserved parameters must be zero.
@@ -87,7 +97,8 @@ libfsverity_compute_digest(void *fd, size_t file_size,
* Returns:
* * 0 for success, -EINVAL for invalid input arguments, -EAGAIN if key or
* certificate files fail to read, or if signing the digest fails.
- * * sig_ret returns a pointer to the signed digest on success.
+ * * sig_ret returns a pointer to the signed digest on success. This object
+ * is allocated by libfsverity_sign_digest and must be freed by the caller.
* * sig_size_ret returns the size of the signed digest on success.
*/
int
@@ -100,7 +111,7 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest,
* @name: Pointer to name of hash algorithm
*
* Returns:
- * uint16_t containing hash algorithm number, zero on error.
+ * uint16_t containing hash algorithm number, zero if not found.
*/
uint16_t libfsverity_find_hash_alg_by_name(const char *name);
@@ -109,7 +120,7 @@ uint16_t libfsverity_find_hash_alg_by_name(const char *name);
* @alg_nr: Valid hash algorithm number
*
* Returns:
- * int containing size of digest, -1 on error.
+ * int containing size of digest, -1 if algorithm is not known.
*/
int libfsverity_digest_size(uint16_t alg_nr);
@@ -118,9 +129,9 @@ int libfsverity_digest_size(uint16_t alg_nr);
* @name: Number of hash algorithm
*
* Returns:
- * New allocated string containing name of algorithm.
- * String must be freed by caller.
- * NULL on error
+ * New allocated string containing name of algorithm.
+ * The string must be freed by caller.
+ * NULL if algorithm is not known.
*/
char *libfsverity_hash_name(uint16_t num);
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 20/20] Fixup Makefile
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (18 preceding siblings ...)
2020-04-24 20:55 ` [PATCH 19/20] Improve documentation of libfsverity.h API Jes Sorensen
@ 2020-04-24 20:55 ` Jes Sorensen
2020-05-07 14:03 ` [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
20 siblings, 0 replies; 23+ messages in thread
From: Jes Sorensen @ 2020-04-24 20:55 UTC (permalink / raw)
To: linux-fscrypt; +Cc: ebiggers, kernel-team, jsorensen
From: Jes Sorensen <jsorensen@fb.com>
Set soname for libfsverity, install shared library and header file,
and make clean handle shared library too.
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
---
Makefile | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/Makefile b/Makefile
index 5bbcd87..0b82c82 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,5 @@
EXE := fsverity
STATIC := fsverity-static
-LIB := libfsverity.so
INC := libfsverity.h
CFLAGS := -O2 -Wall
CPPFLAGS := -D_FILE_OFFSET_BITS=64
@@ -15,12 +14,17 @@ SSRC := libverity.c hash_algs.c
SHOBJ := libverity.so hash_algs.so
STOBJ := libverity.o hash_algs.o
HDRS := $(wildcard *.h)
+LIB_MAJOR := 1
+LIB_MINOR := 0.0
+LIB_SO := libfsverity.so
+LIB_SONAME := $(LIB_SO).$(LIB_MAJOR)
+LIB_FULL := $(LIB_SONAME).$(LIB_MINOR)
all:$(EXE)
static:$(STATIC)
-$(EXE):$(OBJ) $(LIB)
+$(EXE):$(OBJ) $(LIB_FULL)
$(CC) -o $@ $(OBJ) $(LDLIBS) -L . -l fsverity
$(STATIC):$(OBJ) $(STOBJ)
@@ -35,16 +39,21 @@ $(STOBJ): %.o: %.c $(HDRS)
$(SHOBJ): %.so: %.c $(HDRS)
$(CC) -c -fPIC $(CFLAGS) $(CPPFLAGS) $< -o $@
-libfsverity.so: $(SHOBJ)
- $(CC) $(LDLIBS) -shared -o $@ $(SHOBJ)
+$(LIB_FULL): $(SHOBJ)
+ $(CC) $(LDLIBS) -shared -Wl,-soname,$(LIB_SONAME) -o $@ $(SHOBJ)
+ rm -f $(LIB_SONAME) $(LIB_SO)
+ ln -s $(LIB_FULL) $(LIB_SONAME)
+ ln -s $(LIB_SONAME) $(LIB_SO)
clean:
- rm -f $(EXE) $(OBJ) $(SHOBJ) $(LIB) $(STOBJ) $(STATIC)
+ rm -f $(EXE) $(OBJ) $(SHOBJ) $(LIB_SONAME) $(LIB_SO) $(LIB_FULL) $(STOBJ) $(STATIC)
install:all
- install -Dm755 -t $(BINDIR) $(EXE)
- install -Dm755 -t $(LIBDIR) $(LIB)
- install -Dm644 -t $(INCDIR) $(INC)
+ install -Dm755 -t $(DESTDIR)$(BINDIR) $(EXE)
+ install -Dm755 -t $(DESTDIR)$(LIBDIR) $(LIB_FULL)
+ ln -s $(LIB_FULL) $(DESTDIR)$(LIBDIR)/$(LIB_SONAME)
+ ln -s $(LIB_SONAME) $(DESTDIR)$(LIBDIR)/$(LIB_SO)
+ install -Dm644 -t $(DESTDIR)$(INCDIR) $(INC)
install-static:static
install -Dm755 -t $(BINDIR) $(STATIC)
--
2.25.3
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v4 00/20] Split fsverity-utils into a shared library
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
` (19 preceding siblings ...)
2020-04-24 20:55 ` [PATCH 20/20] Fixup Makefile Jes Sorensen
@ 2020-05-07 14:03 ` Jes Sorensen
2020-05-07 17:35 ` Eric Biggers
20 siblings, 1 reply; 23+ messages in thread
From: Jes Sorensen @ 2020-05-07 14:03 UTC (permalink / raw)
To: ebiggers; +Cc: Jes Sorensen, linux-fscrypt, kernel-team
On 4/24/20 4:54 PM, Jes Sorensen wrote:
> From: Jes Sorensen <jsorensen@fb.com>
>
> Hi
>
> This is an update to the libfsverity patches I posted about a month
> ago, which I believe address all the issues in the feedback I received.
Hi Eric,
Wanted to check in and hear if you had a chance to look at this?
Thanks,
Jes
> I have a version of rpm that requires this library which is able to
> sign files and a plugin which will install fsverity signatures when
> the rpm is installed. The code for rpm can be found on github - note
> that I do rebase the repo as I fix bugs:
> https://github.com/jessorensen/rpm/tree/rpm-fsverity
>
> A git tree with these patches can also be found here:
> https://git.kernel.org/pub/scm/linux/kernel/git/jes/fsverity-utils.git
>
> This update changes a number of issues:
> - Change the API for libfsverity_compute_digest() to take a callback
> read function, which is needed to deal with the internal cpio
> processing of rpm.
> - Provides the option to build fsverity linked statically against
> libfsverity
> - Makefile support to install libfsverity.so, libfsverity.h and sets
> the soname
> - Make struct fsverity_descriptor and struct fsverity_hash_alg
> internal to the library
> - Improved documentation of the API in libfsverity.h
>
> I have a .spec file for it that packages this into an rpm for Fedora,
> as well as a packaged version of rpm with fsverity support in it,
> which I am happy to share.
>
> Let me know what you think!
>
> Thanks,
> Jes
>
>
> Jes Sorensen (20):
> Build basic shared library framework
> Change compute_file_measurement() to take a file descriptor as
> argument
> Move fsverity_descriptor definition to libfsverity.h
> Move hash algorithm code to shared library
> Create libfsverity_compute_digest() and adapt cmd_sign to use it
> Introduce libfsverity_sign_digest()
> Validate input arguments to libfsverity_compute_digest()
> Validate input parameters for libfsverity_sign_digest()
> Document API of libfsverity
> Change libfsverity_compute_digest() to take a read function
> Make full_{read,write}() return proper error codes instead of bool
> libfsverity: Remove dependencies on util.c
> Update Makefile to install libfsverity and fsverity.h
> Change libfsverity_find_hash_alg_by_name() to return the alg number
> Make libfsverity_find_hash_alg_by_name() private to the shared library
> libfsverity_sign_digest() use ARRAY_SIZE()
> fsverity_cmd_sign() use sizeof() input argument instead of struct
> fsverity_cmd_sign() don't exit on error without closing file
> descriptor
> Improve documentation of libfsverity.h API
> Fixup Makefile
>
> Makefile | 49 +++-
> cmd_enable.c | 19 +-
> cmd_measure.c | 19 +-
> cmd_sign.c | 565 +++++------------------------------------
> fsverity.c | 17 +-
> hash_algs.c | 95 ++++---
> hash_algs.h | 36 +--
> helpers.h | 43 ++++
> libfsverity.h | 138 ++++++++++
> libfsverity_private.h | 52 ++++
> libverity.c | 572 ++++++++++++++++++++++++++++++++++++++++++
> util.c | 15 +-
> util.h | 62 +----
> 13 files changed, 1029 insertions(+), 653 deletions(-)
> create mode 100644 helpers.h
> create mode 100644 libfsverity.h
> create mode 100644 libfsverity_private.h
> create mode 100644 libverity.c
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v4 00/20] Split fsverity-utils into a shared library
2020-05-07 14:03 ` [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
@ 2020-05-07 17:35 ` Eric Biggers
0 siblings, 0 replies; 23+ messages in thread
From: Eric Biggers @ 2020-05-07 17:35 UTC (permalink / raw)
To: Jes Sorensen; +Cc: Jes Sorensen, linux-fscrypt, kernel-team
On Thu, May 07, 2020 at 10:03:47AM -0400, Jes Sorensen wrote:
> On 4/24/20 4:54 PM, Jes Sorensen wrote:
> > From: Jes Sorensen <jsorensen@fb.com>
> >
> > Hi
> >
> > This is an update to the libfsverity patches I posted about a month
> > ago, which I believe address all the issues in the feedback I received.
>
> Hi Eric,
>
> Wanted to check in and hear if you had a chance to look at this?
>
> Thanks,
> Jes
>
No, it's on my list of things to review though.
- Eric
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2020-05-07 17:35 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-04-24 20:54 [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
2020-04-24 20:54 ` [PATCH 01/20] Build basic shared library framework Jes Sorensen
2020-04-24 20:54 ` [PATCH 02/20] Change compute_file_measurement() to take a file descriptor as argument Jes Sorensen
2020-04-24 20:54 ` [PATCH 03/20] Move fsverity_descriptor definition to libfsverity.h Jes Sorensen
2020-04-24 20:54 ` [PATCH 04/20] Move hash algorithm code to shared library Jes Sorensen
2020-04-24 20:54 ` [PATCH 05/20] Create libfsverity_compute_digest() and adapt cmd_sign to use it Jes Sorensen
2020-04-24 20:54 ` [PATCH 06/20] Introduce libfsverity_sign_digest() Jes Sorensen
2020-04-24 20:54 ` [PATCH 07/20] Validate input arguments to libfsverity_compute_digest() Jes Sorensen
2020-04-24 20:54 ` [PATCH 08/20] Validate input parameters for libfsverity_sign_digest() Jes Sorensen
2020-04-24 20:54 ` [PATCH 09/20] Document API of libfsverity Jes Sorensen
2020-04-24 20:54 ` [PATCH 10/20] Change libfsverity_compute_digest() to take a read function Jes Sorensen
2020-04-24 20:54 ` [PATCH 11/20] Make full_{read,write}() return proper error codes instead of bool Jes Sorensen
2020-04-24 20:54 ` [PATCH 12/20] libfsverity: Remove dependencies on util.c Jes Sorensen
2020-04-24 20:54 ` [PATCH 13/20] Update Makefile to install libfsverity and fsverity.h Jes Sorensen
2020-04-24 20:54 ` [PATCH 14/20] Change libfsverity_find_hash_alg_by_name() to return the alg number Jes Sorensen
2020-04-24 20:54 ` [PATCH 15/20] Make libfsverity_find_hash_alg_by_name() private to the shared library Jes Sorensen
2020-04-24 20:55 ` [PATCH 16/20] libfsverity_sign_digest() use ARRAY_SIZE() Jes Sorensen
2020-04-24 20:55 ` [PATCH 17/20] fsverity_cmd_sign() use sizeof() input argument instead of struct Jes Sorensen
2020-04-24 20:55 ` [PATCH 18/20] fsverity_cmd_sign() don't exit on error without closing file descriptor Jes Sorensen
2020-04-24 20:55 ` [PATCH 19/20] Improve documentation of libfsverity.h API Jes Sorensen
2020-04-24 20:55 ` [PATCH 20/20] Fixup Makefile Jes Sorensen
2020-05-07 14:03 ` [PATCH v4 00/20] Split fsverity-utils into a shared library Jes Sorensen
2020-05-07 17:35 ` Eric Biggers
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).