From patchwork Thu Mar 12 21:47:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11435517 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 28CAF921 for ; Thu, 12 Mar 2020 21:48:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 07743206E2 for ; Thu, 12 Mar 2020 21:48:11 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="k2btOuwt" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726528AbgCLVsK (ORCPT ); Thu, 12 Mar 2020 17:48:10 -0400 Received: from mail-qk1-f193.google.com ([209.85.222.193]:47038 "EHLO mail-qk1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726481AbgCLVsK (ORCPT ); Thu, 12 Mar 2020 17:48:10 -0400 Received: by mail-qk1-f193.google.com with SMTP id f28so8977989qkk.13 for ; Thu, 12 Mar 2020 14:48:08 -0700 (PDT) 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=k2btOuwtnx5M7qpUBN2Mo6QrfJVrRfK5OMBHzSeqA6M/9C5540t0eT4A2Ns+n6SMld C98JppvDF6Uh8/VBhh+DcPjwupPQWwzk21w4tJZJLdGT+fE1CZIc0CsrgsEjR17M42wQ uskSFtnQxfvNjFRr4znxQysgebGHxN86H1r4vegvyfwxkiSNOxjhUjPDqiUiGWAQKuGD 9fqXiI7K7i6zr6+4xcQWNvpymbmnSEupdLRtmJ8qpNQA2UqoBhLvAt2rpo8+Ic/ftFzy Agq1v3ShBndhdYqHq9bqnW9xRivwSVpz17bFfBTQ29uz6fnBCnEe97M+bhdHkmQSL3Z/ QBDA== 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=JppHhgApte7Sguxq2FA6ELWQJwYXfqKfvfjxogx5pGuLc6WJnol1HQKTUm3I5i/uwt 2aMslH784y2BQmc0Zhe6sY8TEM5Ijg96YoadcqkgAGRWgv6c0i9lkyMTcr+b9cureh0F CLPuUerjP3J5eLwaD0rJTW1+CowB82Fc7TW/5XzDyatqYrBxFhvgASVX/2Szw7xThyDR wdND/QdrpSfTtDlWXw1zoVaqBrQf/p/WS3uwZ4hZiXYQAPl4W+qaXm1PKN9JoSyaFLin /vEUl2uJsm04OS9/C5Z+AdG/xk2SCcaLkX2oVllpPYifnhTIVz7BinEsNgG4WjfOHtwQ gMLA== X-Gm-Message-State: ANhLgQ3VxzGISzWiMeAk1qQJL7AiuRwY1gxgudmvSI4xaKz68vr17JbD Nn+HkAFGDf4nqXG0Uo70Jd26jhfz X-Google-Smtp-Source: ADFU+vstnp5DPx9uHs4nkRTjMdh2ULfQaKksHVGOq9L26Txvdu53dXMSfkQGsikuqEDfEk1qBK9FQA== X-Received: by 2002:ae9:eb12:: with SMTP id b18mr10218445qkg.168.1584049687721; Thu, 12 Mar 2020 14:48:07 -0700 (PDT) Received: from localhost ([2620:10d:c091:480::fa82]) by smtp.gmail.com with ESMTPSA id 4sm23598524qky.106.2020.03.12.14.48.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2020 14:48:07 -0700 (PDT) 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/9] Build basic shared library framework Date: Thu, 12 Mar 2020 17:47:50 -0400 Message-Id: <20200312214758.343212-2-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200312214758.343212-1-Jes.Sorensen@gmail.com> References: <20200312214758.343212-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 Thu Mar 12 21:47:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11435519 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 EEE29921 for ; Thu, 12 Mar 2020 21:48:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C4C1D206E2 for ; Thu, 12 Mar 2020 21:48:13 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FdRX3T7W" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726605AbgCLVsN (ORCPT ); Thu, 12 Mar 2020 17:48:13 -0400 Received: from mail-qk1-f195.google.com ([209.85.222.195]:39633 "EHLO mail-qk1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726481AbgCLVsN (ORCPT ); Thu, 12 Mar 2020 17:48:13 -0400 Received: by mail-qk1-f195.google.com with SMTP id e16so8997614qkl.6 for ; Thu, 12 Mar 2020 14:48:12 -0700 (PDT) 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=FdRX3T7WEDOAohTclktdLNxwzM/s12LLVWFlx9sHEO33WieLJNW5aLXPGsBjTV7vkw atYB47+/c9ADrIQxk3DbTTV99ZNVGePSaFHhHvOvsSBceaNJkwCUZKbp5iRreZ4ZATg7 dakB2NGXQvzwKhCNADLzCzcIG4wn6ZM6fm4dcPGsYuOVXUQ0ctMKGr0KltH1rsQ3S3xI Mz7BzHu5Cflv5jZNHQb6woSLVENtt0u2zVJPFgtxGnO/mBJa/sZ4LXXpu1GnxCxiud0x t7JoKjWc9bRRLsvK84dhHH7d9AZPBGKOMWwqtsQtby0aYjagC8jU1kWDfvEV7BV0xIcT IvZg== 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=nHYS6nNlF+/2rCEomKEoYtMGRxYH9ZBKLlqOqiEm0sjh8zJ/h6OGDO2glZBl6kn3Jy vCB/d6LECTvwbnTgaG7JICXtJrFpYVptS9al0BFqktk37p5EF8xHBprjft781Wp+6gtH d1UCmvjjF4f6tDoGtdJuwxh6LikC564uS4ETpEtgG4Ry/aXe+BGoNouM/+8GRlNgnloZ oakNGqkyGorMj69cya3KOzGDNUqSp5j4yCvF9j7UKyjti86etFJbrmvIYj9XC6dtj3T8 3weSwJg4bSK2TSTDlbK0zlo+LHNd86zG+o5pRsdISL5vkTgt3aYDJ45tJo3jCNzU+UjU d6VQ== X-Gm-Message-State: ANhLgQ2jBCTXv6AumV6ussFpnUYhI8xXd7WV7uYmyd/V1XJ/aLGheyR7 eN1p90mWx8XmmTNWneyewYn4rola X-Google-Smtp-Source: ADFU+vsceMzPrrq02hbB6+yzo8+ty3BKj99h0WQI78wC4UhQXYdoef1YiNp6staE45Wn7vUC53G4mQ== X-Received: by 2002:a05:620a:12a3:: with SMTP id x3mr10303648qki.367.1584049691536; Thu, 12 Mar 2020 14:48:11 -0700 (PDT) Received: from localhost ([2620:10d:c091:480::fa82]) by smtp.gmail.com with ESMTPSA id x1sm27773530qkf.38.2020.03.12.14.48.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2020 14:48:10 -0700 (PDT) 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/9] Change compute_file_measurement() to take a file descriptor as argument Date: Thu, 12 Mar 2020 17:47:51 -0400 Message-Id: <20200312214758.343212-3-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200312214758.343212-1-Jes.Sorensen@gmail.com> References: <20200312214758.343212-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 Thu Mar 12 21:47:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11435521 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 F1084921 for ; Thu, 12 Mar 2020 21:48:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CF4DC206E2 for ; Thu, 12 Mar 2020 21:48:16 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="tIrOTssJ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726620AbgCLVsQ (ORCPT ); Thu, 12 Mar 2020 17:48:16 -0400 Received: from mail-qt1-f193.google.com ([209.85.160.193]:42683 "EHLO mail-qt1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726481AbgCLVsQ (ORCPT ); Thu, 12 Mar 2020 17:48:16 -0400 Received: by mail-qt1-f193.google.com with SMTP id g16so5748713qtp.9 for ; Thu, 12 Mar 2020 14:48:15 -0700 (PDT) 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=tIrOTssJ2v62INPxEtKGH2HBFxIMg6sEx72XIfZLPhQtV+dLuPma2SoQKYSbxbCv/w lRNwqgK+cfqTQ5gZ91+/POntyoLfgCR9YZ+YDwmMMJAngnaUn4aK0EBlnHgWiZzy8D29 QMzg+cEziMcz4dvlBwnvBRVDYpDtC0jV1YdGkQ2ucx9LtBSLVe+N16oj1s8mXh/RXT9i Y4ErEgFIX87IhuHz99lrzVhyhbRILMSAeLrY1fOfuj3iknKD3AvJXVibQkZl0ZiCDDMA URTXXQZROTRcuijeu5tWlQOC2G2UacwvR5qVFfPdCctWFEQ11jPSr9P/KDlKmTzAoLOa FIrA== 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=pYkvgNWSSEwS/kMTbbAvx6q9SlAUe4fYlyzIrl8PXVX3oMXvCiL2VhPyCAxmPRBkD6 dLk/jt11p/JcyvP+PTioi/fYjRA54hvv04pyFAUDZWlQVcSf4HD4v8VulY4x5KCsuw6v g6/9xIVgf3Lfc5j2yAc1xOramnr94qcylCk+jFbxxybuFYFJfeLlfT5xasTnxdC+7a75 xFFp+TtLZeRSQhGjEdZluzbGDlejG1w6WAUuRmDZxHrhzxWYckcz/uiWyTjJsOpI+bfX pH8N8HJ4GJgWUfYaIe7XHdkEK/XMXG/NOGETugEJ1w+3GV7nU5uU4TxVfU+ynG0JPat/ 0ECA== X-Gm-Message-State: ANhLgQ3DvyUs9oi793RcCowaLJX2DKigoRWyCLbwIrssCnaef2ARi1Ya oAA1IsI2r8CrjDlKGa4syxGoYhxV X-Google-Smtp-Source: ADFU+vtrRPDPAtfSua1hu2qjQ56JyokNjKKcecK4V8jB+jq8e1+j4k7t6HH1E8SZjZZ14Bsscn+l5w== X-Received: by 2002:ac8:6b54:: with SMTP id x20mr9453088qts.41.1584049694841; Thu, 12 Mar 2020 14:48:14 -0700 (PDT) Received: from localhost ([2620:10d:c091:480::fa82]) by smtp.gmail.com with ESMTPSA id 199sm9853918qkm.7.2020.03.12.14.48.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2020 14:48:14 -0700 (PDT) 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/9] Move fsverity_descriptor definition to libfsverity.h Date: Thu, 12 Mar 2020 17:47:52 -0400 Message-Id: <20200312214758.343212-4-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200312214758.343212-1-Jes.Sorensen@gmail.com> References: <20200312214758.343212-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 Thu Mar 12 21:47:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11435523 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 79D0813B1 for ; Thu, 12 Mar 2020 21:48:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3CB5B206E2 for ; Thu, 12 Mar 2020 21:48:21 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="O2XoH49W" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726548AbgCLVsV (ORCPT ); Thu, 12 Mar 2020 17:48:21 -0400 Received: from mail-qk1-f195.google.com ([209.85.222.195]:42028 "EHLO mail-qk1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726481AbgCLVsU (ORCPT ); Thu, 12 Mar 2020 17:48:20 -0400 Received: by mail-qk1-f195.google.com with SMTP id e11so8988733qkg.9 for ; Thu, 12 Mar 2020 14:48:20 -0700 (PDT) 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=/1zpd8nL3AVJxNtJCcbQWyKUJc4t+uMhNqfQ81NWrRA=; b=O2XoH49WETweRIavuk9m9Q2GblcWASLsYUbaFzj2FyYGa6M98QPtSDxYVcsHLiNZrF jQtrKQiFvFZKVRCfKNABrhEVlRzVcyX2TrnWDY4SGsoR+SiS0lBF1jRqYNhsg8fPk9Tr sYM9AEWxRzN9KIhcUn4TG6wIzagz+42avRVQ0fRzn8wmlbxBaFeN0y+BV7dTASwYjFvR tVQo+Asf67vrslGxQ9g3jTT57l22jf2sTisgTU/EJu6iELT+tvkRu3VtrlT2mLLiyCO0 iSueeeHal4DGrTeDQKVw9CnjebttUmeGtRAv3z98ijFwazVpfGOpdWLqf0/fyVOVAwTx ampA== 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=/1zpd8nL3AVJxNtJCcbQWyKUJc4t+uMhNqfQ81NWrRA=; b=uOH6cBhDmXXgTuvbyUipTMAruEFX4BPBodm6JJjbAA7XItucuvoD6U24yhRdjA0WCf gtny8Qd47I+iJjyLuv3tB8IHRfTfN2RjI+TRy3S/gX1K7WOre+OQGashaXnDvbH2HmRI 99Z63Y4kHTwCI3i9RW0zUcQ7tF5YozPHWdeGvAgFQqeclOgdSZNTZp55gMVLx8uiradY ukgsHWDMBmWYEEGj5ONGs4pI+nxhjakI+Te3AXmDHZypprM+sz4sHKISwywk9nAyPIWY cNkRFMcwh9akA2xWuFOyOGPed02RSG0HFQ3ghGlK+4RCY+FNbVTygnfr4ZWOpXAlQeKd JZvg== X-Gm-Message-State: ANhLgQ2i/aggEJgNIunIvCNRKhL6jyNwyOJ7DpB5HxF1nVWpFia8SVnr 55/BT5FWURryxScbSnDecN499F3O X-Google-Smtp-Source: ADFU+vun/hhqTuEsnCWCPFCin+R/nkLllQUskbGmMv5tHsRcBL2QaMhdlpMOTgbpdV0iMXpseQKwOw== X-Received: by 2002:a37:6807:: with SMTP id d7mr9938554qkc.112.1584049698904; Thu, 12 Mar 2020 14:48:18 -0700 (PDT) Received: from localhost ([2620:10d:c091:480::fa82]) by smtp.gmail.com with ESMTPSA id b145sm7637728qkg.52.2020.03.12.14.48.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2020 14:48:18 -0700 (PDT) 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/9] Move hash algorithm code to shared library Date: Thu, 12 Mar 2020 17:47:53 -0400 Message-Id: <20200312214758.343212-5-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200312214758.343212-1-Jes.Sorensen@gmail.com> References: <20200312214758.343212-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 | 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 #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 #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 Thu Mar 12 21:47:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11435525 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 4967613B1 for ; Thu, 12 Mar 2020 21:48:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 15F55206E2 for ; Thu, 12 Mar 2020 21:48:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="so06BDgp" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726550AbgCLVsY (ORCPT ); Thu, 12 Mar 2020 17:48:24 -0400 Received: from mail-qk1-f196.google.com ([209.85.222.196]:34227 "EHLO mail-qk1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726481AbgCLVsY (ORCPT ); Thu, 12 Mar 2020 17:48:24 -0400 Received: by mail-qk1-f196.google.com with SMTP id f3so9031507qkh.1 for ; Thu, 12 Mar 2020 14:48:23 -0700 (PDT) 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=ok6ER6RFfhHaEaOxgnPvuc6QdJ04aInl1ZOVdWBdHxY=; b=so06BDgplMMSBWBKmIAQsaVyZnJiYqdUUp28UQoCuACcpHbffur7TEUGhlBgUJHqPf cTzLZMadCsOC0xJt6GVVB/ONikarpD0pHxkhho/yy5W/8ksYGWV5/052QHV3crvhc0/I hLlHI3Xzf23tV3/40U7Bp5MgejBWH3Pe5gn2WP3ep1njv1ogkJxSDrjaclCP8JIbRdvq PvQGsIhIUe6u9oLCKuhM0OcLpxLI4bFKgP3mGmT+l6UtpADE18pkrjPens/f7hx16Fbe Ng18dsGau4YLZUSwPgFiezr0M3diRYk1MXFzIPvGUEujNIaNea+HYBH+IPKHsM37/bzi NR1A== 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=ok6ER6RFfhHaEaOxgnPvuc6QdJ04aInl1ZOVdWBdHxY=; b=jAzxAlpKhQJS8UzQdZeMYSkUTx8dDL1NJcrOJaNbf2bDlZhcw6sACfkO0sUVL9r+KU SRVVQtDLlr84pz9Z9ErjmIixC6bjJz4e6hflZMybz9fW8zWAtEJftKO1KG1aDWOA3yWX Iapm1dU/Ifym74q1HtlEu3G6eUM4v/Wq7brIlUQEhQ8MkCVonNDUBSgSXlkKUSWjsWMv Ko/p81AwOnGDCmJr9248DsCSuQr0rFKZVXEzEt6e26Pta7q0mgtHoh+U7gQ+asN1wN4R hW/bMy1zcFBp0oFgjC3vclqa8UXvARbT9HgwSLXEHMzoMKEYX9HxRvs8maDra1U9y2aX WYFg== X-Gm-Message-State: ANhLgQ2QYdmFLOr9EEoOnP5MZSDaxKB9SFNC7lTj/aDCt44Rgng7TbkY F6Uavonxr/0ttPFwnzU/l+FSgV7E X-Google-Smtp-Source: ADFU+vtKDvPyw9Ol2h6B7jc8OeL3Y4A++/9OV/xee0tFlSoU4EyjBQ0Enz3gxV9CkzfCd47JxfJUog== X-Received: by 2002:a37:6e06:: with SMTP id j6mr9964690qkc.171.1584049702444; Thu, 12 Mar 2020 14:48:22 -0700 (PDT) Received: from localhost ([2620:10d:c091:480::fa82]) by smtp.gmail.com with ESMTPSA id n6sm7961420qkh.70.2020.03.12.14.48.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2020 14:48:21 -0700 (PDT) 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/9] Create libfsverity_compute_digest() and adapt cmd_sign to use it Date: Thu, 12 Mar 2020 17:47:54 -0400 Message-Id: <20200312214758.343212-6-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200312214758.343212-1-Jes.Sorensen@gmail.com> References: <20200312214758.343212-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. In addition libfsverity_private.h is created for library internal data structures, in particular struct fsverity_decriptor. Signed-off-by: Jes Sorensen --- 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 #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/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 +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include + +#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; +} From patchwork Thu Mar 12 21:47:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11435527 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 7B1F0921 for ; Thu, 12 Mar 2020 21:48:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3D80120674 for ; Thu, 12 Mar 2020 21:48:30 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Bgqa16yD" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726491AbgCLVsa (ORCPT ); Thu, 12 Mar 2020 17:48:30 -0400 Received: from mail-qt1-f196.google.com ([209.85.160.196]:43018 "EHLO mail-qt1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726481AbgCLVs3 (ORCPT ); Thu, 12 Mar 2020 17:48:29 -0400 Received: by mail-qt1-f196.google.com with SMTP id l13so5748718qtv.10 for ; Thu, 12 Mar 2020 14:48:27 -0700 (PDT) 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=124bUjJbZbXn/P1mXZFZfkNVP043w3RC/02D+Wz9ovs=; b=Bgqa16yD187KPiqgC3XHnVE2iKEF9cFc7nwcyUcFGnli/F2tBhJ9XqrRj/RifcCvdk GveiHc1snTAhh/VEIa715Zg4KNWEMp3ZF57KcpTphAIikNv5Rr7ahwywFU6eh0M7cX+S 60TBZqX0mzknBGQZCgGN1CvAts75ljpcwqWWlScMYYEvUckuXoDAR1w11EAOtKNpsu1k 3gYihNhvjS16EvW6KhOl9hXcwNgk82KOlgfbWjShHkJhmVqYBtdkIwI3JPCcurepGjQY jS951F4oCI951Kz3fE3nQGbeQt7H8Fpy4I0z4m8ErOSKCqadK1CPizWzz/pE5sCfOx1K qJrA== 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=124bUjJbZbXn/P1mXZFZfkNVP043w3RC/02D+Wz9ovs=; b=Y5x+qS/cDJ7oW+mwN3xgSIt3BdRKeym0yAd4VJfzF5rOUVYjZ7SA2zo/CmfU4BJkLo tDpXI+vp5Amdhx7Ey4IXJPkHd2qOnW/Mo2JVcOnO1rowmnCA26rWKke1Sc9e1RenkZuL //f4d5BZ6UWYIysM5JRDnxMCTxUWD8MbY3nyLRnccZjJW+LWhOWMPQPcDNKcTyDmHBTS +a/VlYTvTGaT8WtflFlXIGZr0Ze7Yia0/z/M8oZyX3MIgaiuJPkVUl4bdHLi7JdtmZR7 /dlcBYdN7I8DDeYgE18d2HoSeUGrlupDhQuQCWEpSwY4esOuXWbgk5Ov7NmplngvyEf8 2v5g== X-Gm-Message-State: ANhLgQ0s9yPF/cxLh+c6Z1e4Ib21u/NtQpJvZ3GdCzOOGs0XF3s/Wq5z QXB0nGDfbf+2jca4qC/ub5OxJ7G2 X-Google-Smtp-Source: ADFU+vsRp//+B+JPsVbAAVoNcqLHFUNCRpKIoxCspfr9jjS3heVs3VIXtcbxsKJvlsJfZNvc8tsDGg== X-Received: by 2002:ac8:340d:: with SMTP id u13mr8276061qtb.235.1584049706775; Thu, 12 Mar 2020 14:48:26 -0700 (PDT) Received: from localhost ([2620:10d:c091:480::fa82]) by smtp.gmail.com with ESMTPSA id r15sm10542866qtr.40.2020.03.12.14.48.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2020 14:48:26 -0700 (PDT) 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/9] Introduce libfsverity_sign_digest() Date: Thu, 12 Mar 2020 17:47:55 -0400 Message-Id: <20200312214758.343212-7-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200312214758.343212-1-Jes.Sorensen@gmail.com> References: <20200312214758.343212-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 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; +} From patchwork Thu Mar 12 21:47:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11435529 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 01FE6921 for ; Thu, 12 Mar 2020 21:48:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D5C8F206E2 for ; Thu, 12 Mar 2020 21:48:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="YHKyny6j" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726528AbgCLVsb (ORCPT ); Thu, 12 Mar 2020 17:48:31 -0400 Received: from mail-qk1-f195.google.com ([209.85.222.195]:40245 "EHLO mail-qk1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726481AbgCLVsb (ORCPT ); Thu, 12 Mar 2020 17:48:31 -0400 Received: by mail-qk1-f195.google.com with SMTP id m2so9000373qka.7 for ; Thu, 12 Mar 2020 14:48:31 -0700 (PDT) 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=TbwQ8Nkh9ryZ62Z61Q+wwi0GmxpOzNF5ROiOILlzUo8=; b=YHKyny6j6lJGx4otKkM4evYgGnamgtqbHEzummHb2s2k7mCLloq5ZqvpXZ59BHe8J3 gEOFvAYRFiRuXI4o12X345owXdTjXfoeK+igM8maTnfCJXEEDIhKQU3ercj1cdY1h0yK /0uGEPoL08PxIIEt1NQ6cuCnJC4qnWeAejnm5dwiNA4aYOrMrzUwbrFcVuseL7OXVlRx z/UecToukr/T5jIO1Yz8I5TYl/1zg2bRXziPrFY6DmZzHdo5rADlkJvb3tbXPjEQc28j fX4bJkbkj8EgfZgh1I1prgcRE36kPY7i0Sy8k+jqQ59i8rbv6Kxr9jG+Xga56+qkgQwK miow== 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=TbwQ8Nkh9ryZ62Z61Q+wwi0GmxpOzNF5ROiOILlzUo8=; b=FxCTaEXngUk5bKoY7hlZtoJ1lDz4tTu3sN1CZwcvQUISjKo/8RqOwePgqduCSS4BKc wlf6DNgVyypb6bJS9RZZ1v6nIm7fjRJOx27Vp3STA+BQvDVhsuiTjeBxHNcOqlG9zHyj 1DAtneqxf6Q8mIgcDU/+FgFqvPLC7hoZHJANVdxQyNTNIsYruSrJRjimyMKE0NPV5ba6 I/iJbnhEBdBDhturaimbTglOA+gz/SmBYryQ4Jtx3toEIm7iDyc2HSelA6mBPcGgm2GD 8x/g6uALeFqbM+mNtHwSxJ/mjezW3S/Kf5TeNTE8VhouPjXbfODsRI+qq/ylGlUiUk8u Ytlg== X-Gm-Message-State: ANhLgQ1GDFAWxuLlVYYVUIY+NuEhDDGyE6WREdEA9GuBhE1BJ/uUaEK7 JrurkiYFnGur6IXtTES8J2pjFZ9L X-Google-Smtp-Source: ADFU+vt+m/A5RadJSYzoUS4oaiyiJ3oh2e1u6EC8FqCyDLS0QnFFlLsA3mNCPtG+vKB6jv0rtZ4/1g== X-Received: by 2002:a37:bb03:: with SMTP id l3mr10005731qkf.458.1584049710222; Thu, 12 Mar 2020 14:48:30 -0700 (PDT) Received: from localhost ([2620:10d:c091:480::fa82]) by smtp.gmail.com with ESMTPSA id n50sm3162990qtc.5.2020.03.12.14.48.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2020 14:48:29 -0700 (PDT) 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 7/9] Validate input arguments to libfsverity_compute_digest() Date: Thu, 12 Mar 2020 17:47:56 -0400 Message-Id: <20200312214758.343212-8-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200312214758.343212-1-Jes.Sorensen@gmail.com> References: <20200312214758.343212-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 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 --- 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; } From patchwork Thu Mar 12 21:47:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11435531 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 6A0BC13B1 for ; Thu, 12 Mar 2020 21:48:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4587D206E2 for ; Thu, 12 Mar 2020 21:48:37 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="uOo3ZX39" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726491AbgCLVsh (ORCPT ); Thu, 12 Mar 2020 17:48:37 -0400 Received: from mail-qt1-f194.google.com ([209.85.160.194]:33121 "EHLO mail-qt1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726481AbgCLVsg (ORCPT ); Thu, 12 Mar 2020 17:48:36 -0400 Received: by mail-qt1-f194.google.com with SMTP id d22so5796554qtn.0 for ; Thu, 12 Mar 2020 14:48:34 -0700 (PDT) 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=Mboe5DkI6RRf3Q66k+PqyztyiTAhipJjuCV9W1VjrMw=; b=uOo3ZX39HGcaRDhv+vyer2HyZrTfHp1qwV0XZELLjJQ8BXZXv/piDLYwhFPrtP+O0F GLYOWBDaXMIOHidYnBBsxfQQUFcWtVxl7xubFW4cFGqrrqhh0TvvzS3ZkdV3Xjg8aUIh ApXY1ujfWVYBEDo5/n66z+9jSzOK5F820NkV6Eft4VCNdkr1Nx7tF7nyYId4lTQKDtzK 0H+QTyMjHpqQXe32apslr7JphPyCnAHlvQPMl6/dlr2syOID6eqjFVTtgro3L7CR3uM3 bnD/u79d58NPX7DUSrG5VUqcQkevTiOkpkeeF7LnTxU8KtWNYM29mY8lOL+d2VZ+wwnU KUHA== 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=Mboe5DkI6RRf3Q66k+PqyztyiTAhipJjuCV9W1VjrMw=; b=JMCFBOm7QLVoI+TwarShu3iYeesEqnS25j99RMMxqNvDrdcscU+DYiUHFOwsgmcGUF 0ZXlcO5WlBEAwzBx38BIcZva2ogg+L6oVpnuiSxFiCAs+KSE/Eq36k1c2jntqbFxUqQ0 OabHgH+XbC9Y9tXSWaP6DHqOSZas8jExBXof54Kbn0g/Q3jy6PbK3Lf3Y/FSrW7CTgOx pLnXKB7aGbUv6bvkIiKSqZ21bbJJfovqkSNVhKrf5Vp7pBYP2wnkvVkQFCZD1EmXWUfN wkyx1SFLhYfmZa5JLsHpXM9RX961XI0yYH/yVRPQdZ5F9mXdInHzGfERmBINiDJrYMWH UddQ== X-Gm-Message-State: ANhLgQ3Bn7vnRYogvrpuGyxhOBAqkYxlweTCsIEadlSOYJj3L5IsHl8O 0jFEJ2IAeK5YezrVJUd0sYU+0/bY X-Google-Smtp-Source: ADFU+vvupx1KkgC3R/uwHWhGJOHcIYGK++rUXemUvTu5dfuYdyQcOV0GPPgtiHPlntw3bBEtm5l+ZA== X-Received: by 2002:ac8:7210:: with SMTP id a16mr2084252qtp.167.1584049713658; Thu, 12 Mar 2020 14:48:33 -0700 (PDT) Received: from localhost ([2620:10d:c091:480::fa82]) by smtp.gmail.com with ESMTPSA id a141sm28620587qkb.50.2020.03.12.14.48.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2020 14:48:33 -0700 (PDT) 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 8/9] Validate input parameters for libfsverity_sign_digest() Date: Thu, 12 Mar 2020 17:47:57 -0400 Message-Id: <20200312214758.343212-9-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200312214758.343212-1-Jes.Sorensen@gmail.com> References: <20200312214758.343212-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 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 --- 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) { From patchwork Thu Mar 12 21:47:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jes Sorensen X-Patchwork-Id: 11435533 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 A256813B1 for ; Thu, 12 Mar 2020 21:48:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8206E206E2 for ; Thu, 12 Mar 2020 21:48:39 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Zb+IXMq/" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726528AbgCLVsj (ORCPT ); Thu, 12 Mar 2020 17:48:39 -0400 Received: from mail-qt1-f196.google.com ([209.85.160.196]:44493 "EHLO mail-qt1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726481AbgCLVsj (ORCPT ); Thu, 12 Mar 2020 17:48:39 -0400 Received: by mail-qt1-f196.google.com with SMTP id h16so5749786qtr.11 for ; Thu, 12 Mar 2020 14:48:38 -0700 (PDT) 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=gp+fuub9JdchdC/m/CyOPCVRL22yDc1KGEhZ0n1tIfo=; b=Zb+IXMq/i+F8mdddtBqUO3vlFidpKW3Vtqu7zD8FJq6W4w4VuyC9OXmcnb9XaoLx8Y BfNDewKdfCpEYJBYPEDBH/a1WWudKsli8RSFpTQCbs1lMUm7doAnRrC0yXhMUnzIJWkK SSt5RjkRzKyb5KwDESvFaKf4BZIGespz4x+55r0mVGqf0f9CRTorzL9JONfeW3NNh0NJ elt+F6cQCBLPIXqGXxvKEBgvCXZAOzTpCXVMHMipC26KjJhxYGkxpSZ7P6VGQ1VYa6Ov RKghXvMp3SQEDr6z/mTyi/qyqSqZCEVmT46Jc3VL0uyYQlSNMRvhyYW19k8N9FBoE8/N k3Qw== 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=gp+fuub9JdchdC/m/CyOPCVRL22yDc1KGEhZ0n1tIfo=; b=a0AyqxCmLymh1nGJTd6w53SyWemKsW26gcUbO3NPiywOpjDy6zyEmVUwM9Ddr2a5n2 8CCfLnsbCjIZOl4L8y77rWP228/ICfJZDhXXo/V8PUpp1txjdrPuOqwLF4oLZ0L12P/d RxwC0HNhJP6Ize6Cz2shjyw6W/xyXMAq+PYXOBr6S/gxkxYNLlGd5cw8qve61k88C0v3 /pr4u+v47JTN7DjJ36D9o164L7xFaRd+DJ/H2q3ViapwPJmHZXIbb+6spQZkApSAOzZX A4IAWRhbkIk/J++tetJCfO1JKdhN1Db5OX67QM/59EzWm9vbNoBZOs1VGGeAf6TSWLQ7 79lQ== X-Gm-Message-State: ANhLgQ0uDem2M8T19cBiHedSXKxOmjplMslB2omBGR1qcnzr6TnTOZQq gdIu7fXNp3ZKtMwZgXMjvCh8JTd4 X-Google-Smtp-Source: ADFU+vtfheySTMlcSUiHhLe5l8Y3iApkRbXavY7BzToTSlFfXFA/5L5QSiR5M1POZ/IzQccTcEzmxg== X-Received: by 2002:ac8:36a1:: with SMTP id a30mr9246179qtc.103.1584049717515; Thu, 12 Mar 2020 14:48:37 -0700 (PDT) Received: from localhost ([2620:10d:c091:480::fa82]) by smtp.gmail.com with ESMTPSA id s49sm10638326qtc.29.2020.03.12.14.48.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 12 Mar 2020 14:48:36 -0700 (PDT) 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 9/9] Document API of libfsverity Date: Thu, 12 Mar 2020 17:47:58 -0400 Message-Id: <20200312214758.343212-10-Jes.Sorensen@gmail.com> X-Mailer: git-send-email 2.24.1 In-Reply-To: <20200312214758.343212-1-Jes.Sorensen@gmail.com> References: <20200312214758.343212-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 --- 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);