From patchwork Fri Feb 28 21:28:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11413287 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B4C52930 for ; Fri, 28 Feb 2020 21:28:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 947F3246AF for ; Fri, 28 Feb 2020 21:28:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="txVn8XH4" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726359AbgB1V2Z (ORCPT ); Fri, 28 Feb 2020 16:28:25 -0500 Received: from mail-qk1-f196.google.com ([209.85.222.196]:41960 "EHLO mail-qk1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725805AbgB1V2Z (ORCPT ); Fri, 28 Feb 2020 16:28:25 -0500 Received: by mail-qk1-f196.google.com with SMTP id b5so4444424qkh.8 for ; Fri, 28 Feb 2020 13:28:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=++NjwGE43ioeQ7yOS5Y4eayax4+QsX0svaZ/L5IRgHo=; b=txVn8XH4LLfGe7ZCyIxDXEavTLyKBLwdlhjMCVqe2Y1pShPuuDH2jdu3xmPOqjPidi fVokFOy40K2GOLuzoNj/0xafmN7MW+7qHo+qDvqwm/H8Vex2kmtr4tXnj2u6Vw+ok1uK bbDE9F/uDg4QYwYtw9/8KHz5CgnDwcqpKkaVvFLsTKyC7eZ6UC61tunqnMiMFg0Ru4Xh FDryqBIx7a+KBLHkQdTPxanNplfO3uocwLGz1UynPC93uFPfDIkon/q3CHuAC/IBulpL GHRoGfQLwSgCnc6JBqd8ydsdtk3quQsjerLxK+WWBMOsGzL8DuR+HLknjQ7uQ5un+wZT 2G+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=++NjwGE43ioeQ7yOS5Y4eayax4+QsX0svaZ/L5IRgHo=; b=fJq+QC8loVgswVkMPkkrIOtcdGpR2bZqdGB0AfI1hjbKJw28b9u+sBxi2uDV90H6CE hGQZ1JRksHkZTodcnLdbx1J+gYS58BWfK1q0qUmiKWfJtW2S7jhHf8tVChzJQz3JIkJT Je9Zblq0yqgjk+FIODRS5pB1a7k67FfpSk2EjYr75Nw/+sqfC+7x3BWugN4Qq9T3RGdH 8mBItnBkPi7s5UIpyQC36jiOkU82f1+mOM4F6TiiRyVjHJ+VFsMAAI7u684+J9r0OC1o xW2Up8RNgjWSl2vjvVSsevMoQLaSfQidjgi9faevXlQThgnPRJjfrJfCFZWubIrRIVWj zrRA== X-Gm-Message-State: APjAAAVjRiEZ1l7p4MLz7AGfNWi/mRLfrOa3puE98OwkKxZLzV6kQozV GD8cQq7a4R9Hky61dJ5qqQrk7V6O X-Google-Smtp-Source: APXvYqwFYBU+JeUjP6uVwKqpqogAm3ZFQvfnn9ULg8g+javspHTI5MGbSDEz5w7au5PdbI3nhQqN+w== X-Received: by 2002:a05:620a:742:: with SMTP id i2mr6191110qki.226.1582925302329; Fri, 28 Feb 2020 13:28:22 -0800 (PST) Received: from localhost ([2620:10d:c091:500::1:bc9d]) by smtp.gmail.com with ESMTPSA id j11sm5840583qkl.97.2020.02.28.13.28.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 13:28:21 -0800 (PST) From: Jes Sorensen X-Google-Original-From: Jes Sorensen To: linux-fscrypt@vger.kernel.org Cc: kernel-team@fb.com, ebiggers@kernel.org, Jes Sorensen Subject: [PATCH 1/6] Build basic shared library framework Date: Fri, 28 Feb 2020 16:28:09 -0500 Message-Id: <20200228212814.105897-2-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200228212814.105897-1-Jes.Sorensen@gmail.com> References: <20200228212814.105897-1-Jes.Sorensen@gmail.com> MIME-Version: 1.0 Sender: linux-fscrypt-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Jes Sorensen This introduces a dummy shared library to start moving things into. Signed-off-by: Jes Sorensen --- 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. + */ + From patchwork Fri Feb 28 21:28:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11413289 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 17A32930 for ; Fri, 28 Feb 2020 21:28:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E23A3246AF for ; Fri, 28 Feb 2020 21:28:27 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CJOct8VU" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726783AbgB1V21 (ORCPT ); Fri, 28 Feb 2020 16:28:27 -0500 Received: from mail-qt1-f194.google.com ([209.85.160.194]:35042 "EHLO mail-qt1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725805AbgB1V21 (ORCPT ); Fri, 28 Feb 2020 16:28:27 -0500 Received: by mail-qt1-f194.google.com with SMTP id 88so3184055qtc.2 for ; Fri, 28 Feb 2020 13:28:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yTz/zrKC5DUn7VmN5TQnFzT4to91FgezOCmUOZJeHkg=; b=CJOct8VUWwcuKaYsOI0rPQ1NSBQPPJrwyrkzq/JNeTjR/XyYTx2Y3eD7HPOF168791 XLm+Q4l9hXw6zgGy8shhcfGLgf5sCgb5yaaCboZE8RLpCFbi5nd4zVp8m0YBUL/aBUnC A7lnOpoYwozoUwaRAK15UubalNlDDCxPk+N4BYUJTVHn9WKkhasy+vtX6XBtXHh3SkWK xFUbJcx6HdIJ9aBPG/yHLKgPQlrsQ+F8+i3wruSgjkxZ+h3No7hirT02MmO/RL4l4T8x 6lb2LufS+Gd4SD0jFWcK9PiSUZ49QxsyKg9/kaDh3QHpscEyu91bKR5jNna7GGoWUULz 24Lw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yTz/zrKC5DUn7VmN5TQnFzT4to91FgezOCmUOZJeHkg=; b=n969WiEGDj4ZeA2RzxwOMyAqwgH1FDouYMB3NL/AMwrQaWzTHO8y2DX4+UgrdcAvzK Xt4deEUiiNi3/TgUKEG0YSHQRh4XjoLjg18tNOBD2sw9lQinoqXF9pI2z30QQk7eBW+c N3sMDakDcUMCu/iOcOwYAJCQgr/gmXU8mcOFw4yc+Gye9B0juEB7oKKXr1zurZQmq0PJ U4wZmRFUyWngDy4wl2RrWfNXJc3UUqlrI4gClLFPQwm+457KDMmmWV08BN2PJMS/+oZ5 kgF8J8tpNWy3+kFbjqmsgQUpa7J5aEf0ilDSy6e2maRCy1RohERO3sDmdAZi5rxL+5OW X5pw== X-Gm-Message-State: APjAAAWv4gsmnod0Oyb4noNOV2UGayfzcwxUXGXfv4ZOQutPI1nlt9t0 joplQCrmEYY2seiME9ZoREG1EQFk X-Google-Smtp-Source: APXvYqxlSv6PRoiu4e+bVc53o5Ec06eMw5NXZZHBxoBC+TaUFVzTAsdOUAb1/FxuGitBvZbMH/kh5A== X-Received: by 2002:aed:3462:: with SMTP id w89mr6201349qtd.159.1582925306172; Fri, 28 Feb 2020 13:28:26 -0800 (PST) Received: from localhost ([2620:10d:c091:500::1:bc9d]) by smtp.gmail.com with ESMTPSA id b7sm5768135qtj.78.2020.02.28.13.28.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 13:28:25 -0800 (PST) From: Jes Sorensen X-Google-Original-From: Jes Sorensen To: linux-fscrypt@vger.kernel.org Cc: kernel-team@fb.com, ebiggers@kernel.org, Jes Sorensen Subject: [PATCH 2/6] Change compute_file_measurement() to take a file descriptor as argument Date: Fri, 28 Feb 2020 16:28:10 -0500 Message-Id: <20200228212814.105897-3-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200228212814.105897-1-Jes.Sorensen@gmail.com> References: <20200228212814.105897-1-Jes.Sorensen@gmail.com> MIME-Version: 1.0 Sender: linux-fscrypt-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Jes Sorensen This preps the code for splitting the signing into the shared library Signed-off-by: Jes Sorensen --- 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 #include #include +#include +#include #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 +#include + +#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 From patchwork Fri Feb 28 21:28:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11413291 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4D48814E3 for ; Fri, 28 Feb 2020 21:28:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2BFA9246AE for ; Fri, 28 Feb 2020 21:28:34 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dCRqUHF1" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726816AbgB1V2e (ORCPT ); Fri, 28 Feb 2020 16:28:34 -0500 Received: from mail-qk1-f196.google.com ([209.85.222.196]:42337 "EHLO mail-qk1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725805AbgB1V2d (ORCPT ); Fri, 28 Feb 2020 16:28:33 -0500 Received: by mail-qk1-f196.google.com with SMTP id o28so4438016qkj.9 for ; Fri, 28 Feb 2020 13:28:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=EEa6E/kcmO4TFUTsFSZc68iuqwq/gtvcWasaJFfhB/8=; b=dCRqUHF1SoWhSR3KwURCwyNMJQa7xjaCG2bkBXj9iWgAc4VkgII1ltoIbgTddazGfG d+J5QtOIKEsW0rknVQaK+Y8tgMxdBOlLqy/ouegcg3mn0otalEydmXawk6efJZQyuDAt Ni3cgumyMPZerdsXEQWMfWeK1G3RIqVINm52SUr8IJVEuKCs/EK8bTpnVqM8Xkpn4jtn RR/FTF5oFmBFtB6XbxYCKmIWThTRUmoPqS2bBXQajiLpZro7AK1135U8pRrCVabevv/d auoILPEeL+6Pvh2RYiPefxRdp6WxfgZSDgmR7/ndZS707WFKCvchQNVgG6pADLFH/2/X EwZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=EEa6E/kcmO4TFUTsFSZc68iuqwq/gtvcWasaJFfhB/8=; b=bdIMGkSJoMekctSGmG62rep/F5Mk+CLCj8RAN4zuatDUhP91RQmYlFQ+zr3WPl5j8e hFMBYV6/7VRTguXMQ6OU+yth245ythUiOZlUbETSC1ml7C6/Hes/kLIw9ZS1HwfBRZXB hjPW4p0o7nBzmQxXMZmi8iGO+YbYSicGLzP3uhNmvp6pKp0aCOjm0osytSksG84RyT/e 9zXJIbq/DmixhPJm/nN/za2KQAg0eUDM8kmCgJDh9saus0coJAgKE16kN70Jpl2eg+g4 4+znKH1APeuiPM5JTffKYctQt+BYJOZRG5zXIWjsuxUO9WR9txE5/7tuSLdbddkIIPaf bPJQ== X-Gm-Message-State: APjAAAWxgY8Hua/0eKRvqzmXA4QGKadnV2BM+DN2CGJy+5zdurd3t0rF VZxOzM5mODwzge7SQqR9+dBBBzqm X-Google-Smtp-Source: APXvYqy1FO/ZCgzgzMvpcmk1/JygIYdq6xsYW7Jxl2/xMrE1xZ0+9YmZRgATtN1jJk8sRcNN6Ynp3Q== X-Received: by 2002:a05:620a:89e:: with SMTP id b30mr6235641qka.398.1582925310913; Fri, 28 Feb 2020 13:28:30 -0800 (PST) Received: from localhost ([2620:10d:c091:500::1:bc9d]) by smtp.gmail.com with ESMTPSA id p2sm5751010qkg.102.2020.02.28.13.28.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 13:28:30 -0800 (PST) From: Jes Sorensen X-Google-Original-From: Jes Sorensen To: linux-fscrypt@vger.kernel.org Cc: kernel-team@fb.com, ebiggers@kernel.org, Jes Sorensen Subject: [PATCH 3/6] Move fsverity_descriptor definition to libfsverity.h Date: Fri, 28 Feb 2020 16:28:11 -0500 Message-Id: <20200228212814.105897-4-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200228212814.105897-1-Jes.Sorensen@gmail.com> References: <20200228212814.105897-1-Jes.Sorensen@gmail.com> MIME-Version: 1.0 Sender: linux-fscrypt-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Jes Sorensen Signed-off-by: Jes Sorensen --- 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 #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 #include +#include #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 From patchwork Fri Feb 28 21:28:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11413293 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 01F8214E3 for ; Fri, 28 Feb 2020 21:28:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B88D0246AF for ; Fri, 28 Feb 2020 21:28:37 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cq9fyX/6" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726838AbgB1V2h (ORCPT ); Fri, 28 Feb 2020 16:28:37 -0500 Received: from mail-qt1-f195.google.com ([209.85.160.195]:42735 "EHLO mail-qt1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725805AbgB1V2h (ORCPT ); Fri, 28 Feb 2020 16:28:37 -0500 Received: by mail-qt1-f195.google.com with SMTP id r5so3143334qtt.9 for ; Fri, 28 Feb 2020 13:28:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=TcNAc3CAcLhy8YnE1kUULbVeSNOTEzTZawohOh63MZo=; b=cq9fyX/68VtbqtWpFvw+xPgpc3TCpt0+gcsT0y8N2MG5sbaFyeBLSrtvFUAGrktSC6 W3Cq+W2+Hh+WUL/mbMrzvlHKFHrRco3vjP6MDpsT3kZpYrFlUkrUejy6eUsUFaAhsvns nEWy34rHEFlYIorE32SY3aeamfV2VbePYH9P1KkVOFmhirMc1iBXptdAopqhNUFEvYu5 +5+aheRf1cOAG3kbMJDeLoWNL51q0em3+3XdNAZXWFtVN3dBzNWxe+XYciaeIbBFExJF EsQbGyKKRQ4I+NweaklKDTycdagFfdPrytfLjfB84fosXlyXsWVHwGWN1rUoGZO3eGnH PWYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=TcNAc3CAcLhy8YnE1kUULbVeSNOTEzTZawohOh63MZo=; b=Hb3EXSd7CEZ9eRibX91JI6UfQFAKGgiMnONFxz4Kokt9gEqVPhaRcxdvmHsaVAtHBB wqVgOTTT4wPRBZyO0N9/iGDNSO6OFla8xgoVy9A6piWW/m6XbZeZZLDEuMCviUuk5roD QvN9E944H8pM4Y2pn3fe9QloX7krKCY+R25OfFy7+gOTmu3xisxi25bGqmnOBb4X/kS/ xhJ/i4cALZkG+YoZiDJQcagYWaHl24KfpBlCebPz0TIawEbHyN75rH7vkP/GbHPMJxm7 PxLIYvIg7VDgRumVA2VWmWxypKaPd9saVALRdcyRuNRX4orhTs9fSzy5VE4eDKXsGeCg cScw== X-Gm-Message-State: APjAAAXfC2v1G7g4BVY9ijnRqUXQ+VcJUcIscsryqWyuEI599THjZixE dGfNM6fgtuqXWSBSwz0ZnCBJVc8j X-Google-Smtp-Source: APXvYqyG/0oW0szBMv2sXbLaaVBeJTwk29RLtOjIuWpw1v9n/1iZcXm+OZXiJNIjrHSYuwlMzgpGRA== X-Received: by 2002:ac8:739a:: with SMTP id t26mr6201124qtp.53.1582925315485; Fri, 28 Feb 2020 13:28:35 -0800 (PST) Received: from localhost ([2620:10d:c091:500::1:bc9d]) by smtp.gmail.com with ESMTPSA id p19sm5838997qte.81.2020.02.28.13.28.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 13:28:34 -0800 (PST) From: Jes Sorensen X-Google-Original-From: Jes Sorensen To: linux-fscrypt@vger.kernel.org Cc: kernel-team@fb.com, ebiggers@kernel.org, Jes Sorensen Subject: [PATCH 4/6] Move hash algorithm code to shared library Date: Fri, 28 Feb 2020 16:28:12 -0500 Message-Id: <20200228212814.105897-5-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200228212814.105897-1-Jes.Sorensen@gmail.com> References: <20200228212814.105897-1-Jes.Sorensen@gmail.com> MIME-Version: 1.0 Sender: linux-fscrypt-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Jes Sorensen 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 --- Makefile | 6 +++--- cmd_enable.c | 11 ++++++++--- cmd_measure.c | 4 ++-- cmd_sign.c | 18 ++++++++++++------ fsverity.c | 14 ++++++++++++++ hash_algs.c | 26 +++++++------------------- hash_algs.h | 27 --------------------------- libfsverity.h | 22 ++++++++++++++++++++++ util.h | 2 ++ 9 files changed, 70 insertions(+), 60 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..bc71dd7 100644 --- a/fsverity.c +++ b/fsverity.c @@ -48,6 +48,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 #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 */ From patchwork Fri Feb 28 21:28:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11413295 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9B2A3930 for ; Fri, 28 Feb 2020 21:28:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 6853A246AF for ; Fri, 28 Feb 2020 21:28:43 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="aaLh2+b+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726843AbgB1V2n (ORCPT ); Fri, 28 Feb 2020 16:28:43 -0500 Received: from mail-qt1-f195.google.com ([209.85.160.195]:41053 "EHLO mail-qt1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725805AbgB1V2n (ORCPT ); Fri, 28 Feb 2020 16:28:43 -0500 Received: by mail-qt1-f195.google.com with SMTP id l21so3151183qtr.8 for ; Fri, 28 Feb 2020 13:28:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=OghQt65i9+PYAuXQHnddaF+pNnvUXk1biFeFIC0Xp5k=; b=aaLh2+b+pcaQYdHGx+2Ffjv7rVkOOk13Kxr+PYVyIptjFpuanYXskEwIbJ51eQGUJS KSzF4za0mVuzTohjVzGzYRXOI+2Z91KWcqpbHWhIGZ/bpZGXj9ncqLArUrX3YU2FMtG2 Lk4+FnTwrptR2FU7k2ytOayodzvDXOJP+8sP9+UYQe624wO13OTvNLQ0412lHjiuyfzx NTx/tuRYTADI2ehLrUmtSUwU97Tuk45c5aaZ5t1Cr3DU4RKLEXLVEq08Bi/T1dyh7GEf ns5ta0JIHh15t+2mFcy2sFI3yPLp+wo9VAK67L9koAoI/u5uFcyV2h+eTyii30BY49mv 1WbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=OghQt65i9+PYAuXQHnddaF+pNnvUXk1biFeFIC0Xp5k=; b=GxGavrhIz74w75cBOBOofshrQ42ZbolCyAKoplScSV3NoMFyKpBZHXpvRZv0ZflBCs iFVTH4lsT7HcR8ZBoqLKoEsvOu4jgqjE4B6Am4cyhaLsX0eGUsygK1vhg1rhygIKDbjS jtRcoQP6ULUzfH33SsJguD0nK07RYe0YXuqG7/CVeXbbHSRfqvXxnHDPcbnHR9sHQIei pc3fKmX1NKP6tElmK58ef2Yf059y2Co0sA3C3g1tOtmP3m6qopOLBxsogiVpHDFyWIrK HXA0n2E2sP3GxzLfAAHZD6KtmB9Pr5SfLbdL0qdTT6yFGX/H6uRCDDW0TWXOKRaaIHxu pW5Q== X-Gm-Message-State: APjAAAWmyUWpLiA9X6FJ2FWIfCTuwz/IzLSTQdYbnTy9RDApdc9tgiCY EbfzaUGL4UmoGIs20LfrN+E85l3f X-Google-Smtp-Source: APXvYqz9saL7nA5sTLcA8nqhXE69zE8Gqp2pTb8A8s7/LTfbtoDeXQNEO790htc66v9jT4w98SUM9Q== X-Received: by 2002:ac8:7751:: with SMTP id g17mr5972802qtu.77.1582925319675; Fri, 28 Feb 2020 13:28:39 -0800 (PST) Received: from localhost ([2620:10d:c091:500::1:bc9d]) by smtp.gmail.com with ESMTPSA id l4sm5767977qkl.22.2020.02.28.13.28.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 13:28:39 -0800 (PST) From: Jes Sorensen X-Google-Original-From: Jes Sorensen To: linux-fscrypt@vger.kernel.org Cc: kernel-team@fb.com, ebiggers@kernel.org, Jes Sorensen Subject: [PATCH 5/6] Create libfsverity_compute_digest() and adapt cmd_sign to use it Date: Fri, 28 Feb 2020 16:28:13 -0500 Message-Id: <20200228212814.105897-6-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200228212814.105897-1-Jes.Sorensen@gmail.com> References: <20200228212814.105897-1-Jes.Sorensen@gmail.com> MIME-Version: 1.0 Sender: linux-fscrypt-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Jes Sorensen This reorganizes the digest signing code and moves it to the shared library. Signed-off-by: Jes Sorensen --- cmd_sign.c | 194 +++--------------------------------------------- libverity.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+), 184 deletions(-) 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 #include #include -#include -#include #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/libverity.c b/libverity.c index 6821aa2..4f01992 100644 --- a/libverity.c +++ b/libverity.c @@ -8,3 +8,210 @@ * Written by Eric Biggers and Jes Sorensen. */ +#include +#include +#include +#include +#include +#include +#include + +#include "commands.h" +#include "libfsverity.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; +} From patchwork Fri Feb 28 21:28:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11413297 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 055CF14E3 for ; Fri, 28 Feb 2020 21:28:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B2039246AF for ; Fri, 28 Feb 2020 21:28:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="V/pb83fp" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725886AbgB1V2q (ORCPT ); Fri, 28 Feb 2020 16:28:46 -0500 Received: from mail-qk1-f194.google.com ([209.85.222.194]:40688 "EHLO mail-qk1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725805AbgB1V2q (ORCPT ); Fri, 28 Feb 2020 16:28:46 -0500 Received: by mail-qk1-f194.google.com with SMTP id m2so4445867qka.7 for ; Fri, 28 Feb 2020 13:28:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=HIz0xqWfvrBtKE32Cy3jEA/hhl1oyud4cGwgsAv4+rM=; b=V/pb83fpAuhnrLsQ7fOJK4r/w2lxvrFiNTUvc9Meolh2te7vSz5jzAuPXQkLR7ErhM Aql7dWwOmwnySUqQ+BZJSKtdtykJI8QLqbtWY2x8zqMJFgYxZHQQR7i/NbrmfDg6fYZR d67JQ4Hv82skzHzAEDvQ4tTr9BAhhsX5phn9WsceLrjfynTaCkyq6yT0Cy9Qiwn1SUKO /li9FVOkNjlFHekizT/53CTF7C9wpqmNrEawKNgPou6HROouamueG2VHBbBMxQIhYuWD 3ulAsEaE+SMCBBrvfBB1oE958YE385fA9KiKHjUAYURiaZ2IAdulE1NqYoXCn3RctY9G dnLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=HIz0xqWfvrBtKE32Cy3jEA/hhl1oyud4cGwgsAv4+rM=; b=ba3UGOOxqZm6sDp7hu6eLcRG0MiWF5FFHCshU7Xc8bUlGaZV829skmsCdOolUE8cUH JV7TxOdfIO8Me06WhSyOgtxIc+Ud0PzqFGiY7SeAFNGTcK+sck/2AsP0y2JXmR+K6NKI DfzzAe51D5fdJrz6EDxV6ZE3HmlopJIr4ern/oJiPwFlGh3btz8i9DbpLwa9/YFYzwx5 WvCtbmVEMTgbcJSMQmtzuG7AayEHmcGWW9CJivIoEfpLhQhS7oZRsb9Gg7NWV7ywavse YWOwst69MgWTsw6XDcae2LhgvF+F04xSc8kEleZWr/DBWuFMiEbIro8RmDfUVtJPrj3v RA1g== X-Gm-Message-State: APjAAAVQTvfGP7Pmr9FANleRIyPtV1TJnZ0OFcUggoFW44kUDh+xSb+T ptr/OIlAJcCbZiMDQzpjblE110be X-Google-Smtp-Source: APXvYqxRUjzLjn6LWEctpkkrnslwU0jytnfd2LfeVxK5RQUfr5Q02ejGWf+MSU5Z78uMy8qcn3wE2A== X-Received: by 2002:a05:620a:698:: with SMTP id f24mr6369960qkh.476.1582925323912; Fri, 28 Feb 2020 13:28:43 -0800 (PST) Received: from localhost ([2620:10d:c091:500::1:bc9d]) by smtp.gmail.com with ESMTPSA id y49sm6092641qtk.53.2020.02.28.13.28.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2020 13:28:43 -0800 (PST) From: Jes Sorensen X-Google-Original-From: Jes Sorensen To: linux-fscrypt@vger.kernel.org Cc: kernel-team@fb.com, ebiggers@kernel.org, Jes Sorensen Subject: [PATCH 6/6] Introduce libfsverity_sign_digest() Date: Fri, 28 Feb 2020 16:28:14 -0500 Message-Id: <20200228212814.105897-7-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200228212814.105897-1-Jes.Sorensen@gmail.com> References: <20200228212814.105897-1-Jes.Sorensen@gmail.com> MIME-Version: 1.0 Sender: linux-fscrypt-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Jes Sorensen This moves the signing code into libfsverity and switches cmd_sign to use it. Signed-off-by: Jes Sorensen --- 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 #include #include -#include -#include -#include -#include #include #include #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 318dcd7..ff2f6ee 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; @@ -79,6 +86,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 4f01992..c4c61a7 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; +}