aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-06-19 12:01:13 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-06-19 12:01:13 +0000
commit6ac7fe4e9369f341a4cfe22cb09f27ec99ae882b (patch)
tree4cdaa88e6505f5a9cc97967952415b32a3383eff
parentcc5e51366174ddd4580c78c9845d86e44586c034 (diff)
parent596ec1420a085648278e390b61ed1cadb5d08fc3 (diff)
downloadfsverity-utils-android12-mainline-media-release.tar.gz
Change-Id: I86d29eda57d08f2099ab23f4e4ff231b75c43157
-rw-r--r--.clang-format17
-rw-r--r--.gitignore12
-rw-r--r--Android.bp68
-rw-r--r--COPYING339
-rw-r--r--LICENSE360
-rw-r--r--METADATA11
-rw-r--r--MODULE_LICENSE_MIT (renamed from MODULE_LICENSE_GPL)0
-rw-r--r--Makefile261
-rw-r--r--NEWS.md43
-rw-r--r--NOTICE339
-rw-r--r--README.md114
-rw-r--r--cmd_sign.c635
-rw-r--r--commands.h24
-rw-r--r--common/common_defs.h (renamed from util.h)58
-rw-r--r--common/fsverity_uapi.h91
-rw-r--r--common/win32_defs.h57
-rw-r--r--fsverity_uapi.h40
-rw-r--r--hash_algs.h68
-rw-r--r--include/libfsverity.h213
-rw-r--r--lib/compute_digest.c238
-rw-r--r--lib/enable.c53
-rw-r--r--lib/hash_algs.c (renamed from hash_algs.c)135
-rw-r--r--lib/lib_private.h95
-rw-r--r--lib/libfsverity.pc.in10
-rw-r--r--lib/sign_digest.c390
-rw-r--r--lib/utils.c132
-rw-r--r--programs/cmd_digest.c129
-rw-r--r--programs/cmd_enable.c (renamed from cmd_enable.c)77
-rw-r--r--programs/cmd_measure.c (renamed from cmd_measure.c)24
-rw-r--r--programs/cmd_sign.c133
-rw-r--r--programs/fsverity.c (renamed from fsverity.c)117
-rw-r--r--programs/fsverity.h58
-rw-r--r--programs/test_compute_digest.c310
-rw-r--r--programs/test_hash_algs.c45
-rw-r--r--programs/test_sign_digest.c57
-rw-r--r--programs/utils.c (renamed from util.c)52
-rw-r--r--programs/utils.h51
-rwxr-xr-xscripts/do-release.sh46
-rwxr-xr-xscripts/run-sparse.sh14
-rwxr-xr-xscripts/run-tests.sh187
-rw-r--r--testdata/cert.pem31
-rw-r--r--testdata/file.sigbin0 -> 708 bytes
-rw-r--r--testdata/key.pem52
43 files changed, 3118 insertions, 2068 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..834d0a4
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: MIT
+# Copyright 2020 Google LLC
+#
+# Use of this source code is governed by an MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT.
+
+# Formatting settings to approximate the Linux kernel coding style.
+BasedOnStyle: LLVM
+AllowShortFunctionsOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: false
+BreakBeforeBraces: Linux
+Cpp11BracedListStyle: false
+IncludeBlocks: Preserve
+IndentCaseLabels: false
+IndentWidth: 8
+UseTab: Always
diff --git a/.gitignore b/.gitignore
index 95457ca..04f9a6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,13 @@
-fsverity
+*.a
*.o
-tags
+*.patch
+*.so
+*.so.*
+/.build-config
+/fsverity
+/fsverity.sig
+/run-tests.log
+/test_*
cscope.*
ncscope.*
+tags
diff --git a/Android.bp b/Android.bp
index 8b9f1e6..025de57 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,11 +1,69 @@
+package {
+ default_applicable_licenses: ["external_fsverity-utils_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "external_fsverity-utils_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-GPL-2.0",
+ "SPDX-license-identifier-MIT",
+ ],
+ license_text: [
+ "LICENSE",
+ ],
+}
+
+cc_defaults {
+ name: "fsverity_default_flags",
+
+ cflags: [
+ "-Wall",
+ "-Wno-pointer-arith",
+ "-D_GNU_SOURCE",
+ "-D_FILE_OFFSET_BITS=64",
+ ],
+}
+
cc_binary {
name: "fsverity",
+ defaults: [
+ "fsverity_default_flags",
+ ],
+ host_supported: true,
+ shared_libs: ["libfsverity"],
+ srcs: [
+ "programs/cmd_*.c",
+ "programs/fsverity.c",
+ "programs/utils.c",
+ ],
+}
+
+cc_library {
+ name: "libfsverity",
+ defaults: [
+ "fsverity_default_flags",
+ ],
host_supported: true,
shared_libs: ["libcrypto"],
- cflags: [
- "-Wall",
- "-Wno-pointer-arith",
- "-D_FILE_OFFSET_BITS=64"
+
+ export_include_dirs: ["include"],
+
+ srcs: [
+ "lib/*.c",
],
- srcs: ["*.c"],
}
diff --git a/COPYING b/COPYING
deleted file mode 100644
index d159169..0000000
--- a/COPYING
+++ /dev/null
@@ -1,339 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
diff --git a/LICENSE b/LICENSE
index d159169..3211373 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,339 +1,21 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
+Copyright 2019 the fsverity-utils authors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/METADATA b/METADATA
index c92888e..6fb345f 100644
--- a/METADATA
+++ b/METADATA
@@ -1,15 +1,16 @@
name: "fsverity-utils"
-description: "This is `fsverity`, a userspace utility for fs-verity. fs-verity is a Linux kernel feature that does transparent on-demand integrity/authenticity verification of the contents of read-only files, using a Merkle tree (hash tree) hidden after the end of the file. The mechanism is similar to dm-verity, but implemented at the file level rather than at the block device level. The `fsverity` utility allows you to set up fs-verity protected files."
+description: "This is fsverity-utils, a set of userspace utilities for fs-verity. fs-verity is a Linux kernel feature that does transparent on-demand integrity/authenticity verification of the contents of read-only files, using a hidden Merkle tree (hash tree) associated with the file. fsverity-utils currently contains just one program, `fsverity`. The `fsverity` program allows you to set up fs-verity protected files."
third_party {
url {
type: GIT
value: "https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/fsverity-utils.git"
}
- version: "2151209ce1dae61c4ee7480e2c39ada1d912fcb2"
+ version: "v1.3"
+ # would be NOTICE save for common/fsverity_uapi.h
license_type: RESTRICTED
last_upgrade_date {
- year: 2019
- month: 7
- day: 24
+ year: 2021
+ month: 1
+ day: 19
}
}
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_MIT
index e69de29..e69de29 100644
--- a/MODULE_LICENSE_GPL
+++ b/MODULE_LICENSE_MIT
diff --git a/Makefile b/Makefile
index b9c09b9..0354f62 100644
--- a/Makefile
+++ b/Makefile
@@ -1,22 +1,253 @@
-EXE := fsverity
-CFLAGS := -O2 -Wall
-CPPFLAGS := -D_FILE_OFFSET_BITS=64
-LDLIBS := -lcrypto
-DESTDIR := /usr/local
-SRC := $(wildcard *.c)
-OBJ := $(SRC:.c=.o)
-HDRS := $(wildcard *.h)
+# SPDX-License-Identifier: MIT
+# Copyright 2020 Google LLC
+#
+# Use of this source code is governed by an MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT.
-all:$(EXE)
-$(EXE):$(OBJ)
+# Use 'make help' to list available targets.
+#
+# Define V=1 to enable "verbose" mode, showing all executed commands.
+#
+# Define USE_SHARED_LIB=1 to link the fsverity binary to the shared library
+# libfsverity.so rather than to the static library libfsverity.a.
+#
+# Define PREFIX to override the installation prefix, like './configure --prefix'
+# in autotools-based projects (default: /usr/local)
+#
+# Define BINDIR to override where to install binaries, like './configure
+# --bindir' in autotools-based projects (default: PREFIX/bin)
+#
+# Define INCDIR to override where to install headers, like './configure
+# --includedir' in autotools-based projects (default: PREFIX/include)
+#
+# Define LIBDIR to override where to install libraries, like './configure
+# --libdir' in autotools-based projects (default: PREFIX/lib)
+#
+# Define DESTDIR to override the installation destination directory
+# (default: empty string)
+#
+# You can also specify custom CC, CFLAGS, CPPFLAGS, LDFLAGS, and/or PKGCONF.
+#
+##############################################################################
-$(OBJ): %.o: %.c $(HDRS)
+cc-option = $(shell if $(CC) $(1) -c -x c /dev/null -o /dev/null > /dev/null 2>&1; \
+ then echo $(1); fi)
-clean:
- rm -f $(EXE) $(OBJ)
+# Support building with MinGW for minimal Windows fsverity.exe, but not for
+# libfsverity. fsverity.exe will be statically linked.
+ifneq ($(findstring -mingw,$(shell $(CC) -dumpmachine 2>/dev/null)),)
+MINGW = 1
+endif
+
+CFLAGS ?= -O2
+
+override CFLAGS := -Wall -Wundef \
+ $(call cc-option,-Wdeclaration-after-statement) \
+ $(call cc-option,-Wimplicit-fallthrough) \
+ $(call cc-option,-Wmissing-field-initializers) \
+ $(call cc-option,-Wmissing-prototypes) \
+ $(call cc-option,-Wstrict-prototypes) \
+ $(call cc-option,-Wunused-parameter) \
+ $(call cc-option,-Wvla) \
+ $(CFLAGS)
+
+override CPPFLAGS := -Iinclude -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(CPPFLAGS)
+
+ifneq ($(V),1)
+QUIET_CC = @echo ' CC ' $@;
+QUIET_CCLD = @echo ' CCLD ' $@;
+QUIET_AR = @echo ' AR ' $@;
+QUIET_LN = @echo ' LN ' $@;
+QUIET_GEN = @echo ' GEN ' $@;
+endif
+USE_SHARED_LIB ?=
+PREFIX ?= /usr/local
+BINDIR ?= $(PREFIX)/bin
+INCDIR ?= $(PREFIX)/include
+LIBDIR ?= $(PREFIX)/lib
+DESTDIR ?=
+ifneq ($(MINGW),1)
+PKGCONF ?= pkg-config
+else
+PKGCONF := false
+EXEEXT := .exe
+endif
+FSVERITY := fsverity$(EXEEXT)
+
+# Rebuild if a user-specified setting that affects the build changed.
+.build-config: FORCE
+ @flags=$$( \
+ echo 'CC=$(CC)'; \
+ echo 'CFLAGS=$(CFLAGS)'; \
+ echo 'CPPFLAGS=$(CPPFLAGS)'; \
+ echo 'LDFLAGS=$(LDFLAGS)'; \
+ echo 'LDLIBS=$(LDLIBS)'; \
+ echo 'USE_SHARED_LIB=$(USE_SHARED_LIB)'; \
+ ); \
+ if [ "$$flags" != "`cat $@ 2>/dev/null`" ]; then \
+ [ -e $@ ] && echo "Rebuilding due to new settings"; \
+ echo "$$flags" > $@; \
+ fi
+
+DEFAULT_TARGETS :=
+COMMON_HEADERS := $(wildcard common/*.h)
+LDLIBS := $(shell "$(PKGCONF)" libcrypto --libs 2>/dev/null || echo -lcrypto)
+CFLAGS += $(shell "$(PKGCONF)" libcrypto --cflags 2>/dev/null || echo)
+
+# If we are dynamically linking, when running tests we need to override
+# LD_LIBRARY_PATH as no RPATH is set
+ifdef USE_SHARED_LIB
+RUN_FSVERITY = LD_LIBRARY_PATH=./ $(TEST_WRAPPER_PROG) ./$(FSVERITY)
+else
+RUN_FSVERITY = $(TEST_WRAPPER_PROG) ./$(FSVERITY)
+endif
+
+##############################################################################
+
+#### Library
+
+SOVERSION := 0
+LIB_CFLAGS := $(CFLAGS) -fvisibility=hidden
+LIB_SRC := $(wildcard lib/*.c)
+ifeq ($(MINGW),1)
+LIB_SRC := $(filter-out lib/enable.c,${LIB_SRC})
+endif
+LIB_HEADERS := $(wildcard lib/*.h) $(COMMON_HEADERS)
+STATIC_LIB_OBJ := $(LIB_SRC:.c=.o)
+SHARED_LIB_OBJ := $(LIB_SRC:.c=.shlib.o)
+
+# Compile static library object files
+$(STATIC_LIB_OBJ): %.o: %.c $(LIB_HEADERS) .build-config
+ $(QUIET_CC) $(CC) -o $@ -c $(CPPFLAGS) $(LIB_CFLAGS) $<
+
+# Compile shared library object files
+$(SHARED_LIB_OBJ): %.shlib.o: %.c $(LIB_HEADERS) .build-config
+ $(QUIET_CC) $(CC) -o $@ -c $(CPPFLAGS) $(LIB_CFLAGS) -fPIC $<
+
+# Create static library
+libfsverity.a:$(STATIC_LIB_OBJ)
+ $(QUIET_AR) $(AR) cr $@ $+
+
+DEFAULT_TARGETS += libfsverity.a
+
+# Create shared library
+libfsverity.so.$(SOVERSION):$(SHARED_LIB_OBJ)
+ $(QUIET_CCLD) $(CC) -o $@ -Wl,-soname=$@ -shared $+ \
+ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
+
+DEFAULT_TARGETS += libfsverity.so.$(SOVERSION)
+
+# Create the symlink libfsverity.so => libfsverity.so.$(SOVERSION)
+libfsverity.so:libfsverity.so.$(SOVERSION)
+ $(QUIET_LN) ln -sf $+ $@
+
+DEFAULT_TARGETS += libfsverity.so
+
+##############################################################################
+
+#### Programs
+
+ALL_PROG_SRC := $(wildcard programs/*.c)
+ALL_PROG_OBJ := $(ALL_PROG_SRC:.c=.o)
+ALL_PROG_HEADERS := $(wildcard programs/*.h) $(COMMON_HEADERS)
+PROG_COMMON_SRC := programs/utils.c
+PROG_COMMON_OBJ := $(PROG_COMMON_SRC:.c=.o)
+FSVERITY_PROG_OBJ := $(PROG_COMMON_OBJ) \
+ programs/cmd_digest.o \
+ programs/cmd_sign.o \
+ programs/fsverity.o
+ifneq ($(MINGW),1)
+FSVERITY_PROG_OBJ += \
+ programs/cmd_enable.o \
+ programs/cmd_measure.o
+endif
+TEST_PROG_SRC := $(wildcard programs/test_*.c)
+TEST_PROGRAMS := $(TEST_PROG_SRC:programs/%.c=%$(EXEEXT))
+
+# Compile program object files
+$(ALL_PROG_OBJ): %.o: %.c $(ALL_PROG_HEADERS) .build-config
+ $(QUIET_CC) $(CC) -o $@ -c $(CPPFLAGS) $(CFLAGS) $<
+
+# Link the fsverity program
+ifdef USE_SHARED_LIB
+$(FSVERITY): $(FSVERITY_PROG_OBJ) libfsverity.so
+ $(QUIET_CCLD) $(CC) -o $@ $(FSVERITY_PROG_OBJ) \
+ $(CFLAGS) $(LDFLAGS) -L. -lfsverity
+else
+$(FSVERITY): $(FSVERITY_PROG_OBJ) libfsverity.a
+ $(QUIET_CCLD) $(CC) -o $@ $+ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
+endif
+
+DEFAULT_TARGETS += $(FSVERITY)
+
+# Link the test programs
+$(TEST_PROGRAMS): %$(EXEEXT): programs/%.o $(PROG_COMMON_OBJ) libfsverity.a
+ $(QUIET_CCLD) $(CC) -o $@ $+ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
+
+##############################################################################
+
+SPECIAL_TARGETS := all test_programs check install uninstall help clean
+
+FORCE:
+
+.PHONY: $(SPECIAL_TARGETS) FORCE
+
+.DEFAULT_GOAL = all
+
+all:$(DEFAULT_TARGETS)
+
+test_programs:$(TEST_PROGRAMS)
+
+# This just runs some quick, portable tests. Use scripts/run-tests.sh if you
+# want to run the full tests.
+check:$(FSVERITY) test_programs
+ for prog in $(TEST_PROGRAMS); do \
+ $(TEST_WRAPPER_PROG) ./$$prog || exit 1; \
+ done
+ $(RUN_FSVERITY) --help > /dev/null
+ $(RUN_FSVERITY) --version > /dev/null
+ $(RUN_FSVERITY) sign $(FSVERITY) fsverity.sig \
+ --key=testdata/key.pem --cert=testdata/cert.pem > /dev/null
+ $(RUN_FSVERITY) sign $(FSVERITY) fsverity.sig --hash=sha512 \
+ --block-size=512 --salt=12345678 \
+ --key=testdata/key.pem --cert=testdata/cert.pem > /dev/null
+ $(RUN_FSVERITY) digest $(FSVERITY) --hash=sha512 \
+ --block-size=512 --salt=12345678 > /dev/null
+ rm -f fsverity.sig
+ @echo "All tests passed!"
install:all
- install -Dm755 -t $(DESTDIR)/bin $(EXE)
+ install -d $(DESTDIR)$(LIBDIR)/pkgconfig $(DESTDIR)$(INCDIR) $(DESTDIR)$(BINDIR)
+ install -m755 $(FSVERITY) $(DESTDIR)$(BINDIR)
+ install -m644 libfsverity.a $(DESTDIR)$(LIBDIR)
+ install -m755 libfsverity.so.$(SOVERSION) $(DESTDIR)$(LIBDIR)
+ ln -sf libfsverity.so.$(SOVERSION) $(DESTDIR)$(LIBDIR)/libfsverity.so
+ install -m644 include/libfsverity.h $(DESTDIR)$(INCDIR)
+ sed -e "s|@PREFIX@|$(PREFIX)|" \
+ -e "s|@LIBDIR@|$(LIBDIR)|" \
+ -e "s|@INCDIR@|$(INCDIR)|" \
+ lib/libfsverity.pc.in \
+ > $(DESTDIR)$(LIBDIR)/pkgconfig/libfsverity.pc
+ chmod 644 $(DESTDIR)$(LIBDIR)/pkgconfig/libfsverity.pc
+
+uninstall:
+ rm -f $(DESTDIR)$(BINDIR)/$(FSVERITY)
+ rm -f $(DESTDIR)$(LIBDIR)/libfsverity.a
+ rm -f $(DESTDIR)$(LIBDIR)/libfsverity.so.$(SOVERSION)
+ rm -f $(DESTDIR)$(LIBDIR)/libfsverity.so
+ rm -f $(DESTDIR)$(LIBDIR)/pkgconfig/libfsverity.pc
+ rm -f $(DESTDIR)$(INCDIR)/libfsverity.h
-.PHONY: all clean install
+help:
+ @echo "Available targets:"
+ @echo "------------------"
+ @for target in $(DEFAULT_TARGETS) $(TEST_PROGRAMS) $(SPECIAL_TARGETS); \
+ do \
+ echo $$target; \
+ done
+
+clean:
+ rm -f $(DEFAULT_TARGETS) $(TEST_PROGRAMS) \
+ lib/*.o programs/*.o .build-config fsverity.sig
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 0000000..8745745
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,43 @@
+# fsverity-utils release notes
+
+## Version 1.3
+
+* Added a `fsverity digest` subcommand.
+
+* Added `libfsverity_enable()` and `libfsverity_enable_with_sig()`.
+
+* Added basic support for Windows builds of `fsverity` using MinGW.
+
+* `fsverity` now defaults to 4096-byte blocks on all platforms.
+
+* libfsverity now will use SHA-256 with 4096-byte blocks if the
+ `hash_algorithm` and `block_size` fields are left 0.
+
+* `make install` now installs a pkg-config file for libfsverity.
+
+* The Makefile now uses pkg-config to get the libcrypto build flags.
+
+* Fixed `make check` with `USE_SHARED_LIB=1`.
+
+## Version 1.2
+
+* Changed license from GPL to MIT.
+
+* Fixed build error when /bin/sh is dash.
+
+## Version 1.1
+
+* Split the file digest computation and signing functionality of the
+ `fsverity` program into a library `libfsverity`. See `README.md`
+ and `Makefile` for more details.
+
+* Improved the Makefile.
+
+* Added some tests. They can be run using `make check`. Also added
+ `scripts/run-tests.sh` which does more extensive prerelease tests.
+
+* Lots of cleanups and other small improvements.
+
+## Version 1.0
+
+* First official release of fsverity-utils.
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index d159169..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,339 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
diff --git a/README.md b/README.md
index 8a72088..2b63488 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,31 @@
-# Introduction
+# fsverity-utils
-This is `fsverity`, a userspace utility for fs-verity. fs-verity is a
-Linux kernel feature that does transparent on-demand
+## Introduction
+
+This is fsverity-utils, a set of userspace utilities for fs-verity.
+fs-verity is a Linux kernel feature that does transparent on-demand
integrity/authenticity verification of the contents of read-only
files, using a hidden Merkle tree (hash tree) associated with the
-file. The mechanism is similar to dm-verity, but implemented at the
-file level rather than at the block device level. The `fsverity`
-utility allows you to set up fs-verity protected files.
+file. It is similar to dm-verity, but implemented at the file level
+rather than at the block device level. See the [kernel
+documentation](https://www.kernel.org/doc/html/latest/filesystems/fsverity.html)
+for more information about fs-verity.
+
+fs-verity is supported by the ext4 and f2fs filesystems in Linux v5.4
+and later when configured with `CONFIG_FS_VERITY=y` and when the
+`verity` filesystem feature flag has been enabled. Other filesystems
+might add support for fs-verity in the future.
-fs-verity will initially be supported by the ext4 and f2fs
-filesystems, but it may later be supported by other filesystems too.
+fsverity-utils currently contains just one program, `fsverity`. The
+`fsverity` program allows you to set up fs-verity protected files.
+In addition, the file digest computation and signing functionality of
+`fsverity` is optionally exposed through a C library `libfsverity`.
+See `libfsverity.h` for the API of this library.
-# Building and installing
+## Building and installing
-The `fsverity` utility uses the OpenSSL library, so you first must
-install the needed development files. For example, on Debian-based
-systems, run:
+fsverity-utils uses the OpenSSL library, so you first must install the
+needed development files. For example, on Debian-based systems, run:
```bash
sudo apt-get install libssl-dev
@@ -23,16 +33,37 @@ systems, run:
OpenSSL must be version 1.0.0 or later.
-Then, to build and install:
+Then, to build and install fsverity-utils:
```bash
make
sudo make install
```
-# Examples
+By default, the following targets are built and installed: the program
+`fsverity`, the static library `libfsverity.a`, and the shared library
+`libfsverity.so`. You can also run `make check` to build and run the
+tests, or `make help` to display all available build targets.
+
+By default, `fsverity` is statically linked to `libfsverity`. You can
+use `make USE_SHARED_LIB=1` to use dynamic linking instead.
+
+See the `Makefile` for other supported build and installation options.
+
+### Building on Windows
+
+There is minimal support for building Windows executables using MinGW.
+```bash
+ make CC=x86_64-w64-mingw32-gcc
+```
+
+`fsverity.exe` will be built, and it supports the `digest` and `sign` commands.
+
+A Windows build of OpenSSL/libcrypto needs to be available.
+
+## Examples
-## Basic use
+### Basic use
```bash
mkfs.ext4 -O verity /dev/vdc
@@ -41,19 +72,19 @@ Then, to build and install:
# Create a test file
head -c 1000000 /dev/urandom > file
- md5sum file
+ sha256sum file
# Enable verity on the file
fsverity enable file
- # Show the verity file measurement
+ # Show the verity file digest
fsverity measure file
# File should still be readable as usual. However, all data read
# is now transparently checked against a hidden Merkle tree, whose
- # root hash is incorporated into the verity file measurement.
- # Reads of any corrupted parts of the data will fail.
- md5sum file
+ # root hash is incorporated into the verity file digest. Reads of
+ # any corrupted parts of the data will fail.
+ sha256sum file
```
Note that in the above example, the file isn't signed. Therefore, to
@@ -61,13 +92,13 @@ get any authenticity protection (as opposed to just integrity
protection), the output of `fsverity measure` needs to be compared
against a trusted value.
-## Using builtin signatures
+### Using builtin signatures
With `CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y`, the filesystem supports
-automatically verifying a signed file measurement that has been
-included in the verity metadata. The signature is verified against
-the set of X.509 certificates that have been loaded into the
-".fs-verity" kernel keyring. Here's an example:
+automatically verifying a signed file digest that has been included in
+the verity metadata. The signature is verified against the set of
+X.509 certificates that have been loaded into the ".fs-verity" kernel
+keyring. Here's an example:
```bash
# Generate a new certificate and private key:
@@ -87,11 +118,15 @@ the set of X.509 certificates that have been loaded into the
sysctl fs.verity.require_signatures=1
# Now set up fs-verity on a test file:
- md5sum file
+ sha256sum file
fsverity sign file file.sig --key=key.pem --cert=cert.pem
fsverity enable file --signature=file.sig
rm -f file.sig
- md5sum file
+ sha256sum file
+
+ # The digest to be signed can also be printed separately, hex
+ # encoded, in case the integrated signing cannot be used:
+ fsverity digest file --compact --for-builtin-sig | tr -d '\n' | xxd -p -r | openssl smime -sign -in /dev/stdin ...
```
By default, it's not required that verity files have a signature.
@@ -103,25 +138,28 @@ Note: applications generally still need to check whether the file
they're accessing really is a verity file, since an attacker could
replace a verity file with a regular one.
-## With IMA
+### With IMA
IMA support for fs-verity is planned.
-# Notices
-
-This project is provided under the terms of the GNU General Public
-License, version 2; or at your option, any later version. A copy of the
-GPLv2 can be found in the file named [COPYING](COPYING).
+## Notices
-Permission to link to OpenSSL (libcrypto) is granted.
+fsverity-utils is provided under the terms of the MIT license. A copy
+of this license can be found in the file named [LICENSE](LICENSE).
Send questions and bug reports to linux-fscrypt@vger.kernel.org.
-# Submitting patches
+Signed release tarballs for fsverity-utils can be found on
+[kernel.org](https://kernel.org/pub/linux/kernel/people/ebiggers/fsverity-utils/).
+
+## Contributing
-Send patches to linux-fscrypt@vger.kernel.org. Patches should follow
-the Linux kernel's coding style. Additionally, like the Linux kernel
-itself, patches require the following "sign-off" procedure:
+Send patches to linux-fscrypt@vger.kernel.org with the additional tag
+`fsverity-utils` in the subject, i.e. `[fsverity-utils PATCH]`.
+Patches should follow the Linux kernel's coding style. A
+`.clang-format` file is provided to approximate this coding style;
+consider using `git clang-format`. Additionally, like the Linux
+kernel itself, patches require the following "sign-off" procedure:
The sign-off is a simple line at the end of the explanation for the
patch, which certifies that you wrote it or otherwise have the right
diff --git a/cmd_sign.c b/cmd_sign.c
deleted file mode 100644
index dcb37ce..0000000
--- a/cmd_sign.c
+++ /dev/null
@@ -1,635 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * The 'fsverity sign' command
- *
- * Copyright (C) 2018 Google LLC
- *
- * Written by Eric Biggers.
- */
-
-#include <fcntl.h>
-#include <getopt.h>
-#include <limits.h>
-#include <openssl/bio.h>
-#include <openssl/err.h>
-#include <openssl/pem.h>
-#include <openssl/pkcs7.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "commands.h"
-#include "fsverity_uapi.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
- * 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;
- bool ok;
-
- if (!open_file(&file, filename, O_WRONLY|O_CREAT|O_TRUNC, 0644))
- return false;
- ok = full_write(&file, sig, sig_size);
- ok &= filedes_close(&file);
- 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;
-}
-
-/*
- * 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,
- 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(file, 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(const char *filename,
- 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;
- bool ok = false;
-
- if (!open_file(&file, filename, O_RDONLY, 0))
- goto out;
-
- if (!get_file_size(&file, &file_size))
- goto out;
-
- memset(&desc, 0, sizeof(desc));
- desc.version = 1;
- desc.hash_algorithm = hash_alg - fsverity_hash_algs;
-
- 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(&file, 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;
-}
-
-enum {
- OPT_HASH_ALG,
- OPT_BLOCK_SIZE,
- OPT_SALT,
- OPT_KEY,
- OPT_CERT,
-};
-
-static const struct option longopts[] = {
- {"hash-alg", required_argument, NULL, OPT_HASH_ALG},
- {"block-size", required_argument, NULL, OPT_BLOCK_SIZE},
- {"salt", required_argument, NULL, OPT_SALT},
- {"key", required_argument, NULL, OPT_KEY},
- {"cert", required_argument, NULL, OPT_CERT},
- {NULL, 0, NULL, 0}
-};
-
-/* Sign a file for fs-verity by computing its measurement, then signing it. */
-int fsverity_cmd_sign(const struct fsverity_command *cmd,
- int argc, char *argv[])
-{
- const struct fsverity_hash_alg *hash_alg = NULL;
- u32 block_size = 0;
- u8 *salt = NULL;
- u32 salt_size = 0;
- const char *keyfile = NULL;
- const char *certfile = NULL;
- struct fsverity_signed_digest *digest = NULL;
- char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
- u8 *sig = NULL;
- u32 sig_size;
- int status;
- int c;
-
- while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
- switch (c) {
- case OPT_HASH_ALG:
- if (hash_alg != NULL) {
- error_msg("--hash-alg can only be specified once");
- goto out_usage;
- }
- hash_alg = find_hash_alg_by_name(optarg);
- if (hash_alg == NULL)
- goto out_usage;
- break;
- case OPT_BLOCK_SIZE:
- if (!parse_block_size_option(optarg, &block_size))
- goto out_usage;
- break;
- case OPT_SALT:
- if (!parse_salt_option(optarg, &salt, &salt_size))
- goto out_usage;
- break;
- case OPT_KEY:
- if (keyfile != NULL) {
- error_msg("--key can only be specified once");
- goto out_usage;
- }
- keyfile = optarg;
- break;
- case OPT_CERT:
- if (certfile != NULL) {
- error_msg("--cert can only be specified once");
- goto out_usage;
- }
- certfile = optarg;
- break;
- default:
- goto out_usage;
- }
- }
-
- argv += optind;
- argc -= optind;
-
- if (argc != 2)
- goto out_usage;
-
- if (hash_alg == NULL)
- hash_alg = &fsverity_hash_algs[FS_VERITY_HASH_ALG_DEFAULT];
-
- if (block_size == 0)
- block_size = get_default_block_size();
-
- if (keyfile == NULL) {
- error_msg("Missing --key argument");
- goto out_usage;
- }
- 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 - fsverity_hash_algs);
- digest->digest_size = cpu_to_le16(hash_alg->digest_size);
-
- if (!compute_file_measurement(argv[0], hash_alg, block_size,
- salt, salt_size, digest->digest))
- goto out_err;
-
- if (!sign_data(digest, sizeof(*digest) + hash_alg->digest_size,
- keyfile, certfile, hash_alg, &sig, &sig_size))
- goto out_err;
-
- if (!write_signature(argv[1], sig, sig_size))
- goto out_err;
-
- bin2hex(digest->digest, hash_alg->digest_size, digest_hex);
- printf("Signed file '%s' (%s:%s)\n", argv[0], hash_alg->name,
- digest_hex);
- status = 0;
-out:
- free(salt);
- free(digest);
- free(sig);
- return status;
-
-out_err:
- status = 1;
- goto out;
-
-out_usage:
- usage(cmd, stderr);
- status = 2;
- goto out;
-}
diff --git a/commands.h b/commands.h
deleted file mode 100644
index 98f9745..0000000
--- a/commands.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-#ifndef COMMANDS_H
-#define COMMANDS_H
-
-#include <stdio.h>
-
-#include "util.h"
-
-struct fsverity_command;
-
-void usage(const struct fsverity_command *cmd, FILE *fp);
-
-int fsverity_cmd_enable(const struct fsverity_command *cmd,
- int argc, char *argv[]);
-int fsverity_cmd_measure(const struct fsverity_command *cmd,
- int argc, char *argv[]);
-int fsverity_cmd_sign(const struct fsverity_command *cmd,
- int argc, char *argv[]);
-
-bool parse_block_size_option(const char *arg, u32 *size_ptr);
-u32 get_default_block_size(void);
-bool parse_salt_option(const char *arg, u8 **salt_ptr, u32 *salt_size_ptr);
-
-#endif /* COMMANDS_H */
diff --git a/util.h b/common/common_defs.h
index dfa10f2..3ae5561 100644
--- a/util.h
+++ b/common/common_defs.h
@@ -1,16 +1,21 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-License-Identifier: MIT */
/*
- * Utility functions and macros for the 'fsverity' program
+ * Common definitions for libfsverity and the 'fsverity' program
*
- * Copyright (C) 2018 Google LLC
+ * Copyright 2018 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
*/
-#ifndef UTIL_H
-#define UTIL_H
+#ifndef COMMON_COMMON_DEFS_H
+#define COMMON_COMMON_DEFS_H
-#include <inttypes.h>
-#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdint.h>
+
+#include "win32_defs.h"
typedef uint8_t u8;
typedef uint16_t u16;
@@ -43,6 +48,7 @@ typedef uint64_t u64;
__typeof__(b) _b = (b); \
_a < _b ? _a : _b; \
})
+
#define max(a, b) ({ \
__typeof__(a) _a = (a); \
__typeof__(b) _b = (b); \
@@ -86,40 +92,4 @@ static inline int ilog2(unsigned long n)
# define le64_to_cpu(v) (__builtin_bswap64((__force u64)(v)))
#endif
-/* ========== Memory allocation ========== */
-
-void *xmalloc(size_t size);
-void *xzalloc(size_t size);
-void *xmemdup(const void *mem, size_t size);
-char *xstrdup(const char *s);
-
-/* ========== Error messages and assertions ========== */
-
-__cold void do_error_msg(const char *format, va_list va, int err);
-__printf(1, 2) __cold void error_msg(const char *format, ...);
-__printf(1, 2) __cold void error_msg_errno(const char *format, ...);
-__printf(1, 2) __cold __noreturn void fatal_error(const char *format, ...);
-__cold __noreturn void assertion_failed(const char *expr,
- const char *file, int line);
-
-#define ASSERT(e) ({ if (!(e)) assertion_failed(#e, __FILE__, __LINE__); })
-
-/* ========== File utilities ========== */
-
-struct filedes {
- int fd;
- char *name; /* filename, for logging or error messages */
-};
-
-bool open_file(struct filedes *file, const char *filename, int flags, int mode);
-bool get_file_size(struct filedes *file, u64 *size_ret);
-bool full_read(struct filedes *file, void *buf, size_t count);
-bool full_write(struct filedes *file, const void *buf, size_t count);
-bool filedes_close(struct filedes *file);
-
-/* ========== String utilities ========== */
-
-bool hex2bin(const char *hex, u8 *bin, size_t bin_len);
-void bin2hex(const u8 *bin, size_t bin_len, char *hex);
-
-#endif /* UTIL_H */
+#endif /* COMMON_COMMON_DEFS_H */
diff --git a/common/fsverity_uapi.h b/common/fsverity_uapi.h
new file mode 100644
index 0000000..a739c9a
--- /dev/null
+++ b/common/fsverity_uapi.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * fs-verity user API
+ *
+ * These ioctls can be used on filesystems that support fs-verity. See the
+ * "User API" section of Documentation/filesystems/fsverity.rst.
+ *
+ * Copyright 2019 Google LLC
+ */
+#ifndef _UAPI_LINUX_FSVERITY_H
+#define _UAPI_LINUX_FSVERITY_H
+
+#ifndef _WIN32
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#endif /* !_WIN32 */
+
+#define FS_VERITY_HASH_ALG_SHA256 1
+#define FS_VERITY_HASH_ALG_SHA512 2
+
+struct fsverity_enable_arg {
+ __u32 version;
+ __u32 hash_algorithm;
+ __u32 block_size;
+ __u32 salt_size;
+ __u64 salt_ptr;
+ __u32 sig_size;
+ __u32 __reserved1;
+ __u64 sig_ptr;
+ __u64 __reserved2[11];
+};
+
+struct fsverity_digest {
+ __u16 digest_algorithm;
+ __u16 digest_size; /* input/output */
+ __u8 digest[];
+};
+
+/*
+ * Struct containing a file's Merkle tree properties. The fs-verity file digest
+ * is the hash of this struct. A userspace program needs this struct only if it
+ * needs to compute fs-verity file digests itself, e.g. in order to sign files.
+ * It isn't needed just to enable fs-verity on a file.
+ *
+ * Note: when computing the file digest, 'sig_size' and 'signature' must be left
+ * zero and empty, respectively. These fields are present only because some
+ * filesystems reuse this struct as part of their on-disk format.
+ */
+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 */
+#ifdef __KERNEL__
+ __le32 sig_size;
+#else
+ __le32 __reserved_0x04; /* must be 0 */
+#endif
+ __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 */
+#ifdef __KERNEL__
+ __u8 signature[];
+#endif
+};
+
+/*
+ * Format in which fs-verity file digests are signed in built-in signatures.
+ * 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.
+ *
+ * This struct is specific to the built-in signature verification support, which
+ * is optional. fs-verity users may also verify signatures in userspace, in
+ * which case userspace is responsible for deciding on what bytes are signed.
+ * This struct may still be used, but it doesn't have to be. For example,
+ * userspace could instead use a string like "sha256:$digest_as_hex_string".
+ */
+struct fsverity_formatted_digest {
+ char magic[8]; /* must be "FSVerity" */
+ __le16 digest_algorithm;
+ __le16 digest_size;
+ __u8 digest[];
+};
+
+#define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg)
+#define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest)
+
+#endif /* _UAPI_LINUX_FSVERITY_H */
diff --git a/common/win32_defs.h b/common/win32_defs.h
new file mode 100644
index 0000000..29ef9b2
--- /dev/null
+++ b/common/win32_defs.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * WIN32 compat definitions for libfsverity and the 'fsverity' program
+ *
+ * Copyright 2020 Microsoft
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+#ifndef COMMON_WIN32_DEFS_H
+#define COMMON_WIN32_DEFS_H
+
+/* Some minimal definitions to allow the digest/sign commands to run under Windows */
+
+/* All file reads we do need this flag on _WIN32 */
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+#ifdef _WIN32
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#ifndef ENOPKG
+# define ENOPKG 65
+#endif
+
+#ifndef __cold
+# define __cold
+#endif
+
+/* For %zu in printf() */
+#ifndef __printf
+# define __printf(fmt_idx, vargs_idx) \
+ __attribute__((format(gnu_printf, fmt_idx, vargs_idx)))
+#endif
+
+typedef __signed__ char __s8;
+typedef unsigned char __u8;
+typedef __signed__ short __s16;
+typedef unsigned short __u16;
+typedef __signed__ int __s32;
+typedef unsigned int __u32;
+typedef __signed__ long long __s64;
+typedef unsigned long long __u64;
+typedef __u16 __le16;
+typedef __u16 __be16;
+typedef __u32 __le32;
+typedef __u32 __be32;
+typedef __u64 __le64;
+typedef __u64 __be64;
+
+#endif /* _WIN32 */
+
+#endif /* COMMON_WIN32_DEFS_H */
diff --git a/fsverity_uapi.h b/fsverity_uapi.h
deleted file mode 100644
index da0daf6..0000000
--- a/fsverity_uapi.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * fs-verity user API
- *
- * These ioctls can be used on filesystems that support fs-verity. See the
- * "User API" section of Documentation/filesystems/fsverity.rst.
- *
- * Copyright 2019 Google LLC
- */
-#ifndef _UAPI_LINUX_FSVERITY_H
-#define _UAPI_LINUX_FSVERITY_H
-
-#include <linux/ioctl.h>
-#include <linux/types.h>
-
-#define FS_VERITY_HASH_ALG_SHA256 1
-#define FS_VERITY_HASH_ALG_SHA512 2
-
-struct fsverity_enable_arg {
- __u32 version;
- __u32 hash_algorithm;
- __u32 block_size;
- __u32 salt_size;
- __u64 salt_ptr;
- __u32 sig_size;
- __u32 __reserved1;
- __u64 sig_ptr;
- __u64 __reserved2[11];
-};
-
-struct fsverity_digest {
- __u16 digest_algorithm;
- __u16 digest_size; /* input/output */
- __u8 digest[];
-};
-
-#define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg)
-#define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest)
-
-#endif /* _UAPI_LINUX_FSVERITY_H */
diff --git a/hash_algs.h b/hash_algs.h
deleted file mode 100644
index 3e90f49..0000000
--- a/hash_algs.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-#ifndef HASH_ALGS_H
-#define HASH_ALGS_H
-
-#include <stdio.h>
-
-#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);
- void (*update)(struct hash_ctx *ctx, const void *data, size_t size);
- void (*final)(struct hash_ctx *ctx, u8 *out);
- 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);
-}
-
-static inline void hash_update(struct hash_ctx *ctx,
- const void *data, size_t size)
-{
- ctx->update(ctx, data, size);
-}
-
-static inline void hash_final(struct hash_ctx *ctx, u8 *digest)
-{
- ctx->final(ctx, digest);
-}
-
-static inline void hash_free(struct hash_ctx *ctx)
-{
- if (ctx)
- ctx->free(ctx);
-}
-
-void hash_full(struct hash_ctx *ctx, const void *data, size_t size, u8 *digest);
-
-#endif /* HASH_ALGS_H */
diff --git a/include/libfsverity.h b/include/libfsverity.h
new file mode 100644
index 0000000..6c42e5e
--- /dev/null
+++ b/include/libfsverity.h
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * libfsverity API
+ *
+ * Copyright 2018 Google LLC
+ * Copyright (C) 2020 Facebook
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#ifndef LIBFSVERITY_H
+#define LIBFSVERITY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define FSVERITY_UTILS_MAJOR_VERSION 1
+#define FSVERITY_UTILS_MINOR_VERSION 3
+
+#define FS_VERITY_HASH_ALG_SHA256 1
+#define FS_VERITY_HASH_ALG_SHA512 2
+
+/**
+ * struct libfsverity_merkle_tree_params - properties of a file's Merkle tree
+ *
+ * Zero this, then fill in at least @version and @file_size.
+ */
+struct libfsverity_merkle_tree_params {
+
+ /** @version: must be 1 */
+ uint32_t version;
+
+ /**
+ * @hash_algorithm: one of FS_VERITY_HASH_ALG_*, or 0 to use the default
+ * of FS_VERITY_HASH_ALG_SHA256
+ */
+ uint32_t hash_algorithm;
+
+ /** @file_size: the file size in bytes */
+ uint64_t file_size;
+
+ /**
+ * @block_size: the Merkle tree block size in bytes, or 0 to use the
+ * default of 4096 bytes
+ */
+ uint32_t block_size;
+
+ /** @salt_size: the salt size in bytes, or 0 if unsalted */
+ uint32_t salt_size;
+
+ /** @salt: pointer to the salt, or NULL if unsalted */
+ const uint8_t *salt;
+
+ /** @reserved1: must be 0 */
+ uint64_t reserved1[8];
+
+ /** @reserved2: must be 0 */
+ uintptr_t reserved2[8];
+};
+
+struct libfsverity_digest {
+ uint16_t digest_algorithm; /* one of FS_VERITY_HASH_ALG_* */
+ uint16_t digest_size; /* digest size in bytes */
+ uint8_t digest[]; /* the actual digest */
+};
+
+struct libfsverity_signature_params {
+ const char *keyfile; /* path to key file (PEM format) */
+ const char *certfile; /* path to certificate (PEM format) */
+ uint64_t reserved1[8]; /* must be 0 */
+ uintptr_t reserved2[8]; /* must be 0 */
+};
+
+/*
+ * libfsverity_read_fn_t - callback that incrementally provides a file's data
+ * @fd: the user-provided "file descriptor" (opaque to library)
+ * @buf: buffer into which to read the next chunk of the file's data
+ * @count: number of bytes to read in this chunk
+ *
+ * Must return 0 on success (all 'count' bytes read), or a negative errno value
+ * on failure.
+ */
+typedef int (*libfsverity_read_fn_t)(void *fd, void *buf, size_t count);
+
+/**
+ * libfsverity_compute_digest() - Compute digest of a file
+ * A fs-verity file digest is the hash of a file's fsverity_descriptor.
+ * Not to be confused with a traditional file digest computed over the
+ * entire file, or with the bare fsverity_descriptor::root_hash.
+ * @fd: context that will be passed to @read_fn
+ * @read_fn: a function that will read the data of the file
+ * @params: Pointer to the Merkle tree parameters
+ * @digest_ret: Pointer to pointer for computed digest.
+ *
+ * Returns:
+ * * 0 for success, -EINVAL for invalid input arguments, -ENOMEM if libfsverity
+ * failed to allocate memory, or an error returned by @read_fn.
+ * * digest_ret returns a pointer to the digest on success. The digest object
+ * is allocated by libfsverity and must be freed by the caller using free().
+ */
+int
+libfsverity_compute_digest(void *fd, libfsverity_read_fn_t read_fn,
+ const struct libfsverity_merkle_tree_params *params,
+ struct libfsverity_digest **digest_ret);
+
+/**
+ * libfsverity_sign_digest() - Sign previously computed digest of a file
+ * This signature is used by the filesystem to validate the signed file
+ * digest against a public key loaded into the .fs-verity kernel
+ * keyring, when CONFIG_FS_VERITY_BUILTIN_SIGNATURES is enabled. The
+ * signature is formatted as PKCS#7 stored in DER format. See
+ * Documentation/filesystems/fsverity.rst in the kernel source tree for
+ * further details.
+ * @digest: pointer to previously computed digest
+ * @sig_params: struct libfsverity_signature_params providing filenames of
+ * the keyfile and certificate file. Reserved fields must be zero.
+ * @sig_ret: Pointer to pointer for signed digest
+ * @sig_size_ret: Pointer to size of signed return digest
+ *
+ * Return:
+ * * 0 for success, -EINVAL for invalid input arguments or if the cryptographic
+ * operations to sign the digest failed, -EBADMSG if the key and/or
+ * certificate file is invalid, or another negative errno value.
+ * * sig_ret returns a pointer to the signed digest on success. This object
+ * is allocated by libfsverity and must be freed by the caller using free().
+ * * sig_size_ret returns the size (in bytes) 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_enable() - Enable fs-verity on a file
+ * @fd: read-only file descriptor to the file
+ * @params: pointer to the Merkle tree parameters
+ *
+ * This is a simple wrapper around the FS_IOC_ENABLE_VERITY ioctl.
+ *
+ * Return: 0 on success, -EINVAL for invalid arguments, or a negative errno
+ * value from the FS_IOC_ENABLE_VERITY ioctl. See
+ * Documentation/filesystems/fsverity.rst in the kernel source tree for
+ * the possible error codes from FS_IOC_ENABLE_VERITY.
+ */
+int
+libfsverity_enable(int fd, const struct libfsverity_merkle_tree_params *params);
+
+/**
+ * libfsverity_enable_with_sig() - Enable fs-verity on a file, with a signature
+ * @fd: read-only file descriptor to the file
+ * @params: pointer to the Merkle tree parameters
+ * @sig: pointer to the file's signature
+ * @sig_size: size of the file's signature in bytes
+ *
+ * Like libfsverity_enable(), but allows specifying a built-in signature (i.e. a
+ * singature created with libfsverity_sign_digest()) to associate with the file.
+ * This is only needed if the in-kernel signature verification support is being
+ * used; it is not needed if signatures are being verified in userspace.
+ *
+ * If @sig is NULL and @sig_size is 0, this is the same as libfsverity_enable().
+ *
+ * Return: See libfsverity_enable().
+ */
+int
+libfsverity_enable_with_sig(int fd,
+ const struct libfsverity_merkle_tree_params *params,
+ const uint8_t *sig, size_t sig_size);
+
+/**
+ * libfsverity_find_hash_alg_by_name() - Find hash algorithm by name
+ * @name: Pointer to name of hash algorithm
+ *
+ * Return: The hash algorithm number, or zero if not found.
+ */
+uint32_t libfsverity_find_hash_alg_by_name(const char *name);
+
+/**
+ * libfsverity_get_digest_size() - Get size of digest for a given algorithm
+ * @alg_num: Number of hash algorithm
+ *
+ * Return: size of digest in bytes, or -1 if algorithm is unknown.
+ */
+int libfsverity_get_digest_size(uint32_t alg_num);
+
+/**
+ * libfsverity_get_hash_name() - Get name of hash algorithm by number
+ * @alg_num: Number of hash algorithm
+ *
+ * Return: The name of the hash algorithm, or NULL if algorithm is unknown.
+ */
+const char *libfsverity_get_hash_name(uint32_t alg_num);
+
+/**
+ * libfsverity_set_error_callback() - Set callback to handle error messages
+ * @cb: the callback function.
+ *
+ * If a callback is already set, it is replaced. @cb may be NULL in order to
+ * remove the existing callback.
+ */
+void libfsverity_set_error_callback(void (*cb)(const char *msg));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBFSVERITY_H */
diff --git a/lib/compute_digest.c b/lib/compute_digest.c
new file mode 100644
index 0000000..a4f649c
--- /dev/null
+++ b/lib/compute_digest.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Implementation of libfsverity_compute_digest().
+ *
+ * Copyright 2018 Google LLC
+ * Copyright (C) 2020 Facebook
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "lib_private.h"
+
+#include <stdlib.h>
+#include <string.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);
+
+ libfsverity_hash_init(hash);
+ libfsverity_hash_update(hash, salt, salt_size);
+ libfsverity_hash_update(hash, cur->data, block_size);
+ libfsverity_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;
+}
+
+/*
+ * Compute the file's Merkle tree root hash using the given hash algorithm,
+ * block size, and salt.
+ */
+static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn,
+ 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 = NULL;
+ 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;
+ int err = 0;
+
+ /* Root hash of empty file is all 0's */
+ if (file_size == 0) {
+ memset(root_hash, 0, hash->alg->digest_size);
+ return 0;
+ }
+
+ if (salt_size != 0) {
+ padded_salt = libfsverity_zalloc(padded_salt_size);
+ if (!padded_salt)
+ return -ENOMEM;
+ 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)) {
+ if (WARN_ON(num_levels >= FS_VERITY_MAX_LEVELS)) {
+ err = -EINVAL;
+ goto out;
+ }
+ 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 = libfsverity_zalloc(block_size);
+ if (!buffers[level].data) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ 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);
+
+ err = read_fn(fd, buffers[-1].data, buffers[-1].filled);
+ if (err) {
+ libfsverity_error_msg("error reading file");
+ goto out;
+ }
+
+ level = -1;
+ while (hash_one_block(hash, &buffers[level], block_size,
+ padded_salt, padded_salt_size)) {
+ level++;
+ if (WARN_ON(level >= num_levels)) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+ }
+ /* 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() */
+ if (WARN_ON(buffers[num_levels].filled != hash->alg->digest_size)) {
+ err = -EINVAL;
+ goto out;
+ }
+ err = 0;
+out:
+ for (level = -1; level < num_levels; level++)
+ free(buffers[level].data);
+ free(padded_salt);
+ return err;
+}
+
+LIBEXPORT int
+libfsverity_compute_digest(void *fd, libfsverity_read_fn_t read_fn,
+ const struct libfsverity_merkle_tree_params *params,
+ struct libfsverity_digest **digest_ret)
+{
+ u32 alg_num;
+ u32 block_size;
+ const struct fsverity_hash_alg *hash_alg;
+ struct hash_ctx *hash = NULL;
+ struct libfsverity_digest *digest;
+ struct fsverity_descriptor desc;
+ int err;
+
+ if (!read_fn || !params || !digest_ret) {
+ libfsverity_error_msg("missing required parameters for compute_digest");
+ return -EINVAL;
+ }
+ if (params->version != 1) {
+ libfsverity_error_msg("unsupported version (%u)",
+ params->version);
+ return -EINVAL;
+ }
+
+ alg_num = params->hash_algorithm ?: FS_VERITY_HASH_ALG_DEFAULT;
+ block_size = params->block_size ?: FS_VERITY_BLOCK_SIZE_DEFAULT;
+
+ if (!is_power_of_2(block_size)) {
+ libfsverity_error_msg("unsupported block size (%u)",
+ block_size);
+ return -EINVAL;
+ }
+ if (params->salt_size > sizeof(desc.salt)) {
+ libfsverity_error_msg("unsupported salt size (%u)",
+ params->salt_size);
+ return -EINVAL;
+ }
+ if (params->salt_size && !params->salt) {
+ libfsverity_error_msg("salt_size specified, but salt is NULL");
+ return -EINVAL;
+ }
+ if (!libfsverity_mem_is_zeroed(params->reserved1,
+ sizeof(params->reserved1)) ||
+ !libfsverity_mem_is_zeroed(params->reserved2,
+ sizeof(params->reserved2))) {
+ libfsverity_error_msg("reserved bits set in merkle_tree_params");
+ return -EINVAL;
+ }
+
+ hash_alg = libfsverity_find_hash_alg_by_num(alg_num);
+ if (!hash_alg) {
+ libfsverity_error_msg("unknown hash algorithm: %u", alg_num);
+ return -EINVAL;
+ }
+
+ if (block_size < 2 * hash_alg->digest_size) {
+ libfsverity_error_msg("block size (%u) too small for hash algorithm %s",
+ block_size, hash_alg->name);
+ return -EINVAL;
+ }
+
+ hash = hash_alg->create_ctx(hash_alg);
+ if (!hash)
+ return -ENOMEM;
+
+ memset(&desc, 0, sizeof(desc));
+ desc.version = 1;
+ desc.hash_algorithm = alg_num;
+ desc.log_blocksize = ilog2(block_size);
+ desc.data_size = cpu_to_le64(params->file_size);
+ if (params->salt_size != 0) {
+ memcpy(desc.salt, params->salt, params->salt_size);
+ desc.salt_size = params->salt_size;
+ }
+
+ err = compute_root_hash(fd, read_fn, params->file_size, hash,
+ block_size, params->salt,
+ params->salt_size, desc.root_hash);
+ if (err)
+ goto out;
+
+ digest = libfsverity_zalloc(sizeof(*digest) + hash_alg->digest_size);
+ if (!digest) {
+ err = -ENOMEM;
+ goto out;
+ }
+ digest->digest_algorithm = alg_num;
+ digest->digest_size = hash_alg->digest_size;
+ libfsverity_hash_full(hash, &desc, sizeof(desc), digest->digest);
+ *digest_ret = digest;
+ err = 0;
+out:
+ libfsverity_free_hash_ctx(hash);
+ return err;
+}
diff --git a/lib/enable.c b/lib/enable.c
new file mode 100644
index 0000000..2478077
--- /dev/null
+++ b/lib/enable.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Implementation of libfsverity_enable() and libfsverity_enable_with_sig().
+ *
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "lib_private.h"
+
+#include <sys/ioctl.h>
+
+LIBEXPORT int
+libfsverity_enable(int fd, const struct libfsverity_merkle_tree_params *params)
+{
+ return libfsverity_enable_with_sig(fd, params, NULL, 0);
+}
+
+LIBEXPORT int
+libfsverity_enable_with_sig(int fd,
+ const struct libfsverity_merkle_tree_params *params,
+ const uint8_t *sig, size_t sig_size)
+{
+ struct fsverity_enable_arg arg = {};
+
+ if (!params) {
+ libfsverity_error_msg("missing required parameters for enable");
+ return -EINVAL;
+ }
+
+ if (params->version != 1) {
+ libfsverity_error_msg("unsupported version (%u)",
+ params->version);
+ return -EINVAL;
+ }
+
+ arg.version = 1;
+ arg.hash_algorithm =
+ params->hash_algorithm ?: FS_VERITY_HASH_ALG_DEFAULT;
+ arg.block_size =
+ params->block_size ?: FS_VERITY_BLOCK_SIZE_DEFAULT;
+ arg.salt_size = params->salt_size;
+ arg.salt_ptr = (uintptr_t)params->salt;
+ arg.sig_size = sig_size;
+ arg.sig_ptr = (uintptr_t)sig;
+
+ if (ioctl(fd, FS_IOC_ENABLE_VERITY, &arg) != 0)
+ return -errno;
+ return 0;
+}
diff --git a/hash_algs.c b/lib/hash_algs.c
index 7251bf2..510ca3e 100644
--- a/hash_algs.c
+++ b/lib/hash_algs.c
@@ -1,19 +1,20 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: MIT
/*
* fs-verity hash algorithms
*
- * Copyright (C) 2018 Google LLC
+ * Copyright 2018 Google LLC
*
- * Written by Eric Biggers.
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
*/
+#include "lib_private.h"
+
#include <openssl/evp.h>
#include <stdlib.h>
#include <string.h>
-#include "fsverity_uapi.h"
-#include "hash_algs.h"
-
/* ========== libcrypto (OpenSSL) wrappers ========== */
struct openssl_hash_ctx {
@@ -25,29 +26,29 @@ struct openssl_hash_ctx {
static void openssl_digest_init(struct hash_ctx *_ctx)
{
struct openssl_hash_ctx *ctx = (void *)_ctx;
+ int ret;
- if (EVP_DigestInit_ex(ctx->md_ctx, ctx->md, NULL) != 1)
- fatal_error("EVP_DigestInit_ex() failed for algorithm '%s'",
- ctx->base.alg->name);
+ ret = EVP_DigestInit_ex(ctx->md_ctx, ctx->md, NULL);
+ BUG_ON(ret != 1);
}
static void openssl_digest_update(struct hash_ctx *_ctx,
const void *data, size_t size)
{
struct openssl_hash_ctx *ctx = (void *)_ctx;
+ int ret;
- if (EVP_DigestUpdate(ctx->md_ctx, data, size) != 1)
- fatal_error("EVP_DigestUpdate() failed for algorithm '%s'",
- ctx->base.alg->name);
+ ret = EVP_DigestUpdate(ctx->md_ctx, data, size);
+ BUG_ON(ret != 1);
}
static void openssl_digest_final(struct hash_ctx *_ctx, u8 *digest)
{
struct openssl_hash_ctx *ctx = (void *)_ctx;
+ int ret;
- if (EVP_DigestFinal_ex(ctx->md_ctx, digest, NULL) != 1)
- fatal_error("EVP_DigestFinal_ex() failed for algorithm '%s'",
- ctx->base.alg->name);
+ ret = EVP_DigestFinal_ex(ctx->md_ctx, digest, NULL);
+ BUG_ON(ret != 1);
}
static void openssl_digest_ctx_free(struct hash_ctx *_ctx)
@@ -68,7 +69,10 @@ openssl_digest_ctx_create(const struct fsverity_hash_alg *alg, const EVP_MD *md)
{
struct openssl_hash_ctx *ctx;
- ctx = xzalloc(sizeof(*ctx));
+ ctx = libfsverity_zalloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
ctx->base.alg = alg;
ctx->base.init = openssl_digest_init;
ctx->base.update = openssl_digest_update;
@@ -80,13 +84,22 @@ openssl_digest_ctx_create(const struct fsverity_hash_alg *alg, const EVP_MD *md)
* with older OpenSSL versions.
*/
ctx->md_ctx = EVP_MD_CTX_create();
- if (!ctx->md_ctx)
- fatal_error("out of memory");
+ if (!ctx->md_ctx) {
+ libfsverity_error_msg("failed to allocate EVP_MD_CTX");
+ goto err1;
+ }
ctx->md = md;
- ASSERT(EVP_MD_size(md) == alg->digest_size);
+ if (WARN_ON(EVP_MD_size(md) != alg->digest_size))
+ goto err2;
return &ctx->base;
+
+err2:
+ EVP_MD_CTX_destroy(ctx->md_ctx);
+err1:
+ free(ctx);
+ return NULL;
}
static struct hash_ctx *create_sha256_ctx(const struct fsverity_hash_alg *alg)
@@ -99,9 +112,42 @@ static struct hash_ctx *create_sha512_ctx(const struct fsverity_hash_alg *alg)
return openssl_digest_ctx_create(alg, EVP_sha512());
}
+/* ========== Hash utilities ========== */
+
+void libfsverity_hash_init(struct hash_ctx *ctx)
+{
+ ctx->init(ctx);
+}
+
+void libfsverity_hash_update(struct hash_ctx *ctx, const void *data,
+ size_t size)
+{
+ ctx->update(ctx, data, size);
+}
+
+void libfsverity_hash_final(struct hash_ctx *ctx, u8 *digest)
+{
+ ctx->final(ctx, digest);
+}
+
+/* ->init(), ->update(), and ->final() all in one step */
+void libfsverity_hash_full(struct hash_ctx *ctx, const void *data, size_t size,
+ u8 *digest)
+{
+ libfsverity_hash_init(ctx);
+ libfsverity_hash_update(ctx, data, size);
+ libfsverity_hash_final(ctx, digest);
+}
+
+void libfsverity_free_hash_ctx(struct hash_ctx *ctx)
+{
+ if (ctx)
+ ctx->free(ctx);
+}
+
/* ========== Hash algorithm definitions ========== */
-const struct fsverity_hash_alg fsverity_hash_algs[] = {
+static const struct fsverity_hash_alg fsverity_hash_algs[] = {
[FS_VERITY_HASH_ALG_SHA256] = {
.name = "sha256",
.digest_size = 32,
@@ -116,48 +162,45 @@ const struct fsverity_hash_alg fsverity_hash_algs[] = {
},
};
-const struct fsverity_hash_alg *find_hash_alg_by_name(const char *name)
+LIBEXPORT u32
+libfsverity_find_hash_alg_by_name(const char *name)
{
int i;
- for (i = 0; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
+ if (!name)
+ return 0;
+
+ for (i = 1; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
if (fsverity_hash_algs[i].name &&
!strcmp(name, fsverity_hash_algs[i].name))
- return &fsverity_hash_algs[i];
+ return i;
}
- error_msg("unknown hash algorithm: '%s'", name);
- fputs("Available hash algorithms: ", stderr);
- show_all_hash_algs(stderr);
- putc('\n', stderr);
- return NULL;
+ return 0;
}
-const struct fsverity_hash_alg *find_hash_alg_by_num(unsigned int num)
+const struct fsverity_hash_alg *libfsverity_find_hash_alg_by_num(u32 alg_num)
{
- if (num < ARRAY_SIZE(fsverity_hash_algs) &&
- fsverity_hash_algs[num].name)
- return &fsverity_hash_algs[num];
+ if (alg_num < ARRAY_SIZE(fsverity_hash_algs) &&
+ fsverity_hash_algs[alg_num].name)
+ return &fsverity_hash_algs[alg_num];
return NULL;
}
-void show_all_hash_algs(FILE *fp)
+LIBEXPORT int
+libfsverity_get_digest_size(u32 alg_num)
{
- int i;
- const char *sep = "";
+ const struct fsverity_hash_alg *alg =
+ libfsverity_find_hash_alg_by_num(alg_num);
- 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 = ", ";
- }
- }
+ return alg ? alg->digest_size : -1;
}
-/* ->init(), ->update(), and ->final() all in one step */
-void hash_full(struct hash_ctx *ctx, const void *data, size_t size, u8 *digest)
+LIBEXPORT const char *
+libfsverity_get_hash_name(u32 alg_num)
{
- hash_init(ctx);
- hash_update(ctx, data, size);
- hash_final(ctx, digest);
+ const struct fsverity_hash_alg *alg =
+ libfsverity_find_hash_alg_by_num(alg_num);
+
+ return alg ? alg->name : NULL;
}
diff --git a/lib/lib_private.h b/lib/lib_private.h
new file mode 100644
index 0000000..7768eea
--- /dev/null
+++ b/lib/lib_private.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Private header for libfsverity
+ *
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+#ifndef LIB_LIB_PRIVATE_H
+#define LIB_LIB_PRIVATE_H
+
+#include "libfsverity.h"
+#include "../common/common_defs.h"
+#include "../common/fsverity_uapi.h"
+
+#include <stdarg.h>
+
+#define LIBEXPORT __attribute__((visibility("default")))
+
+/* The hash algorithm that libfsverity assumes when none is specified */
+#define FS_VERITY_HASH_ALG_DEFAULT FS_VERITY_HASH_ALG_SHA256
+
+/* The block size that libfsverity assumes when none is specified */
+#define FS_VERITY_BLOCK_SIZE_DEFAULT 4096
+
+/* hash_algs.c */
+
+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);
+};
+
+const struct fsverity_hash_alg *libfsverity_find_hash_alg_by_num(u32 alg_num);
+
+struct hash_ctx {
+ const struct fsverity_hash_alg *alg;
+ void (*init)(struct hash_ctx *ctx);
+ void (*update)(struct hash_ctx *ctx, const void *data, size_t size);
+ void (*final)(struct hash_ctx *ctx, u8 *out);
+ void (*free)(struct hash_ctx *ctx);
+};
+
+void libfsverity_hash_init(struct hash_ctx *ctx);
+void libfsverity_hash_update(struct hash_ctx *ctx, const void *data,
+ size_t size);
+void libfsverity_hash_final(struct hash_ctx *ctx, u8 *digest);
+void libfsverity_hash_full(struct hash_ctx *ctx, const void *data, size_t size,
+ u8 *digest);
+void libfsverity_free_hash_ctx(struct hash_ctx *ctx);
+
+/* utils.c */
+
+void *libfsverity_zalloc(size_t size);
+void *libfsverity_memdup(const void *mem, size_t size);
+
+__cold void
+libfsverity_do_error_msg(const char *format, va_list va, int err);
+
+__printf(1, 2) __cold void
+libfsverity_error_msg(const char *format, ...);
+
+__printf(1, 2) __cold void
+libfsverity_error_msg_errno(const char *format, ...);
+
+__cold void
+libfsverity_warn_on(const char *condition, const char *file, int line);
+
+#define WARN_ON(condition) \
+({ \
+ bool c = (condition); \
+ \
+ if (c) \
+ libfsverity_warn_on(#condition, __FILE__, __LINE__); \
+ c; \
+})
+
+__cold void
+libfsverity_bug_on(const char *condition, const char *file, int line);
+
+#define BUG_ON(condition) \
+({ \
+ bool c = (condition); \
+ \
+ if (c) \
+ libfsverity_bug_on(#condition, __FILE__, __LINE__); \
+ c; \
+})
+
+bool libfsverity_mem_is_zeroed(const void *mem, size_t size);
+
+#endif /* LIB_LIB_PRIVATE_H */
diff --git a/lib/libfsverity.pc.in b/lib/libfsverity.pc.in
new file mode 100644
index 0000000..be3ef44
--- /dev/null
+++ b/lib/libfsverity.pc.in
@@ -0,0 +1,10 @@
+prefix=@PREFIX@
+libdir=@LIBDIR@
+includedir=@INCDIR@
+
+Name: libfsverity
+Description: fs-verity library
+Version: 1.3
+Libs: -L${libdir} -lfsverity
+Requires.private: libcrypto
+Cflags: -I${includedir}
diff --git a/lib/sign_digest.c b/lib/sign_digest.c
new file mode 100644
index 0000000..9a35256
--- /dev/null
+++ b/lib/sign_digest.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Implementation of libfsverity_sign_digest().
+ *
+ * Copyright 2018 Google LLC
+ * Copyright (C) 2020 Facebook
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "lib_private.h"
+
+#include <limits.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+#include <string.h>
+
+static int print_openssl_err_cb(const char *str,
+ size_t len __attribute__((unused)),
+ void *u __attribute__((unused)))
+{
+ libfsverity_error_msg("%s", str);
+ return 1;
+}
+
+static void __printf(1, 2) __cold
+error_msg_openssl(const char *format, ...)
+{
+ int saved_errno = errno;
+ va_list va;
+
+ va_start(va, format);
+ libfsverity_do_error_msg(format, va, 0);
+ va_end(va);
+
+ if (ERR_peek_error() == 0)
+ return;
+
+ libfsverity_error_msg("OpenSSL library errors:");
+ ERR_print_errors_cb(print_openssl_err_cb, NULL);
+ errno = saved_errno;
+}
+
+/* Read a PEM PKCS#8 formatted private key */
+static int read_private_key(const char *keyfile, EVP_PKEY **pkey_ret)
+{
+ BIO *bio;
+ EVP_PKEY *pkey;
+ int err;
+
+ errno = 0;
+ bio = BIO_new_file(keyfile, "r");
+ if (!bio) {
+ error_msg_openssl("can't open '%s' for reading", keyfile);
+ return errno ? -errno : -EIO;
+ }
+
+ 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);
+ err = -EBADMSG;
+ goto out;
+ }
+ *pkey_ret = pkey;
+ err = 0;
+out:
+ BIO_free(bio);
+ return err;
+}
+
+/* Read a PEM X.509 formatted certificate */
+static int read_certificate(const char *certfile, X509 **cert_ret)
+{
+ BIO *bio;
+ X509 *cert;
+ int err;
+
+ errno = 0;
+ bio = BIO_new_file(certfile, "r");
+ if (!bio) {
+ error_msg_openssl("can't open '%s' for reading", certfile);
+ return errno ? -errno : -EIO;
+ }
+ 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);
+ err = -EBADMSG;
+ goto out;
+ }
+ *cert_ret = cert;
+ err = 0;
+out:
+ BIO_free(bio);
+ return err;
+}
+
+#ifdef OPENSSL_IS_BORINGSSL
+
+static int 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)
+{
+ BIGNUM *serial;
+ 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;
+ int err;
+
+ EVP_MD_CTX_init(&md_ctx);
+ serial = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), NULL);
+
+ if (!CBB_init(&out, 1024)) {
+ error_msg_openssl("out of memory");
+ err = -ENOMEM;
+ 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");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey)) {
+ error_msg_openssl("EVP_DigestSignInit failed");
+ err = -EINVAL;
+ goto out;
+ }
+
+ sig_len = EVP_PKEY_size(pkey);
+ sig = libfsverity_zalloc(sig_len);
+ if (!sig) {
+ err = -ENOMEM;
+ goto out;
+ }
+ if (!EVP_DigestSign(&md_ctx, sig, &sig_len, data_to_sign, data_size)) {
+ error_msg_openssl("EVP_DigestSign failed");
+ err = -EINVAL;
+ 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");
+ err = -EINVAL;
+ goto out;
+ }
+
+ *sig_ret = libfsverity_memdup(pkcs7_data, pkcs7_data_len);
+ if (!*sig_ret) {
+ err = -ENOMEM;
+ goto out;
+ }
+ *sig_size_ret = pkcs7_data_len;
+ err = 0;
+out:
+ BN_free(serial);
+ EVP_MD_CTX_cleanup(&md_ctx);
+ CBB_cleanup(&out);
+ free(sig);
+ OPENSSL_free(name_der);
+ OPENSSL_free(pkcs7_data);
+ return err;
+}
+
+#else /* OPENSSL_IS_BORINGSSL */
+
+static BIO *new_mem_buf(const void *buf, size_t size)
+{
+ BIO *bio;
+
+ if (WARN_ON(size > INT_MAX))
+ return NULL;
+
+ /*
+ * 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 int 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;
+ int err;
+
+ bio = new_mem_buf(data_to_sign, data_size);
+ if (!bio) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags);
+ if (!p7) {
+ error_msg_openssl("failed to initialize PKCS#7 signature object");
+ err = -EINVAL;
+ 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");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (PKCS7_final(p7, bio, pkcs7_flags) != 1) {
+ error_msg_openssl("failed to finalize PKCS#7 signature");
+ err = -EINVAL;
+ goto out;
+ }
+
+ BIO_free(bio);
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) {
+ error_msg_openssl("out of memory");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (i2d_PKCS7_bio(bio, p7) != 1) {
+ error_msg_openssl("failed to DER-encode PKCS#7 signature object");
+ err = -EINVAL;
+ goto out;
+ }
+
+ sig_size = BIO_get_mem_data(bio, &sig);
+ *sig_ret = libfsverity_memdup(sig, sig_size);
+ if (!*sig_ret) {
+ err = -ENOMEM;
+ goto out;
+ }
+ *sig_size_ret = sig_size;
+ err = 0;
+out:
+ PKCS7_free(p7);
+ BIO_free(bio);
+ return err;
+}
+
+#endif /* !OPENSSL_IS_BORINGSSL */
+
+LIBEXPORT int
+libfsverity_sign_digest(const struct libfsverity_digest *digest,
+ const struct libfsverity_signature_params *sig_params,
+ u8 **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;
+ struct fsverity_formatted_digest *d = NULL;
+ int err;
+
+ if (!digest || !sig_params || !sig_ret || !sig_size_ret) {
+ libfsverity_error_msg("missing required parameters for sign_digest");
+ return -EINVAL;
+ }
+
+ if (!sig_params->keyfile || !sig_params->certfile) {
+ libfsverity_error_msg("keyfile and certfile must be specified");
+ return -EINVAL;
+ }
+
+ if (!libfsverity_mem_is_zeroed(sig_params->reserved1,
+ sizeof(sig_params->reserved1)) ||
+ !libfsverity_mem_is_zeroed(sig_params->reserved2,
+ sizeof(sig_params->reserved2))) {
+ libfsverity_error_msg("reserved bits set in signature_params");
+ return -EINVAL;
+ }
+
+ hash_alg = libfsverity_find_hash_alg_by_num(digest->digest_algorithm);
+ if (!hash_alg || digest->digest_size != hash_alg->digest_size) {
+ libfsverity_error_msg("malformed fsverity digest");
+ return -EINVAL;
+ }
+
+ err = read_private_key(sig_params->keyfile, &pkey);
+ if (err)
+ goto out;
+
+ err = read_certificate(sig_params->certfile, &cert);
+ if (err)
+ goto out;
+
+ OpenSSL_add_all_digests();
+ md = EVP_get_digestbyname(hash_alg->name);
+ if (!md) {
+ libfsverity_error_msg("'%s' algorithm not found in OpenSSL library",
+ hash_alg->name);
+ err = -ENOPKG;
+ goto out;
+ }
+
+ d = libfsverity_zalloc(sizeof(*d) + digest->digest_size);
+ if (!d) {
+ err = -ENOMEM;
+ goto out;
+ }
+ memcpy(d->magic, "FSVerity", 8);
+ d->digest_algorithm = cpu_to_le16(digest->digest_algorithm);
+ d->digest_size = cpu_to_le16(digest->digest_size);
+ memcpy(d->digest, digest->digest, digest->digest_size);
+
+ err = sign_pkcs7(d, sizeof(*d) + digest->digest_size,
+ pkey, cert, md, sig_ret, sig_size_ret);
+ out:
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ free(d);
+ return err;
+}
diff --git a/lib/utils.c b/lib/utils.c
new file mode 100644
index 0000000..036dd60
--- /dev/null
+++ b/lib/utils.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Utility functions for libfsverity
+ *
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "lib_private.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void *xmalloc(size_t size)
+{
+ void *p = malloc(size);
+
+ if (!p)
+ libfsverity_error_msg("out of memory (tried to allocate %zu bytes)",
+ size);
+ return p;
+}
+
+void *libfsverity_zalloc(size_t size)
+{
+ void *p = xmalloc(size);
+
+ if (!p)
+ return NULL;
+ return memset(p, 0, size);
+}
+
+void *libfsverity_memdup(const void *mem, size_t size)
+{
+ void *p = xmalloc(size);
+
+ if (!p)
+ return NULL;
+ return memcpy(p, mem, size);
+}
+
+static void (*libfsverity_error_cb)(const char *msg);
+
+LIBEXPORT void
+libfsverity_set_error_callback(void (*cb)(const char *msg))
+{
+ libfsverity_error_cb = cb;
+}
+
+#ifdef _WIN32
+static char *strerror_r(int errnum, char *buf, size_t buflen)
+{
+ strerror_s(buf, buflen, errnum);
+
+ return buf;
+}
+#endif
+
+void libfsverity_do_error_msg(const char *format, va_list va, int err)
+{
+ int saved_errno = errno;
+ char *msg = NULL;
+
+ if (!libfsverity_error_cb)
+ return;
+
+ if (vasprintf(&msg, format, va) < 0)
+ goto out;
+
+ if (err) {
+ char *msg2 = NULL;
+ char errbuf[64];
+
+ if (asprintf(&msg2, "%s: %s", msg,
+ strerror_r(err, errbuf, sizeof(errbuf))) < 0)
+ goto out2;
+ free(msg);
+ msg = msg2;
+ }
+ (*libfsverity_error_cb)(msg);
+out2:
+ free(msg);
+out:
+ errno = saved_errno;
+}
+
+void libfsverity_error_msg(const char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ libfsverity_do_error_msg(format, va, 0);
+ va_end(va);
+}
+
+void libfsverity_error_msg_errno(const char *format, ...)
+{
+ va_list va;
+
+ va_start(va, format);
+ libfsverity_do_error_msg(format, va, errno);
+ va_end(va);
+}
+
+void libfsverity_warn_on(const char *condition, const char *file, int line)
+{
+ fprintf(stderr, "libfsverity internal error! %s at %s:%d\n",
+ condition, file, line);
+}
+
+void libfsverity_bug_on(const char *condition, const char *file, int line)
+{
+ fprintf(stderr, "libfsverity internal error! %s at %s:%d\n"
+ "Non-recoverable, aborting program.\n", condition, file, line);
+ abort();
+}
+
+bool libfsverity_mem_is_zeroed(const void *mem, size_t size)
+{
+ const u8 *p = mem;
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (p[i])
+ return false;
+ }
+ return true;
+}
diff --git a/programs/cmd_digest.c b/programs/cmd_digest.c
new file mode 100644
index 0000000..1a3c769
--- /dev/null
+++ b/programs/cmd_digest.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: MIT
+/*
+ * The 'fsverity digest' command
+ *
+ * Copyright 2020 Microsoft
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "fsverity.h"
+
+#include <fcntl.h>
+#include <getopt.h>
+
+static const struct option longopts[] = {
+ {"hash-alg", required_argument, NULL, OPT_HASH_ALG},
+ {"block-size", required_argument, NULL, OPT_BLOCK_SIZE},
+ {"salt", required_argument, NULL, OPT_SALT},
+ {"compact", no_argument, NULL, OPT_COMPACT},
+ {"for-builtin-sig", no_argument, NULL, OPT_FOR_BUILTIN_SIG},
+ {NULL, 0, NULL, 0}
+};
+
+/*
+ * Compute the fs-verity digest of the given file(s), for offline signing.
+ */
+int fsverity_cmd_digest(const struct fsverity_command *cmd,
+ int argc, char *argv[])
+{
+ struct filedes file = { .fd = -1 };
+ struct libfsverity_merkle_tree_params tree_params = { .version = 1 };
+ bool compact = false, for_builtin_sig = false;
+ int status;
+ int c;
+
+ while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
+ switch (c) {
+ case OPT_HASH_ALG:
+ case OPT_BLOCK_SIZE:
+ case OPT_SALT:
+ if (!parse_tree_param(c, optarg, &tree_params))
+ goto out_usage;
+ break;
+ case OPT_COMPACT:
+ compact = true;
+ break;
+ case OPT_FOR_BUILTIN_SIG:
+ for_builtin_sig = true;
+ break;
+ default:
+ goto out_usage;
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 1)
+ goto out_usage;
+
+ for (int i = 0; i < argc; i++) {
+ struct fsverity_formatted_digest *d = NULL;
+ struct libfsverity_digest *digest = NULL;
+ char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 +
+ sizeof(*d) * 2 + 1];
+
+ if (!open_file(&file, argv[i], O_RDONLY, 0))
+ goto out_err;
+
+ if (!get_file_size(&file, &tree_params.file_size))
+ goto out_err;
+
+ if (libfsverity_compute_digest(&file, read_callback,
+ &tree_params, &digest) != 0) {
+ error_msg("failed to compute digest");
+ goto out_err;
+ }
+
+ ASSERT(digest->digest_size <= FS_VERITY_MAX_DIGEST_SIZE);
+
+ if (for_builtin_sig) {
+ /*
+ * Format the digest for use with the built-in signature
+ * support.
+ */
+ d = xzalloc(sizeof(*d) + digest->digest_size);
+ memcpy(d->magic, "FSVerity", 8);
+ d->digest_algorithm =
+ cpu_to_le16(digest->digest_algorithm);
+ d->digest_size = cpu_to_le16(digest->digest_size);
+ memcpy(d->digest, digest->digest, digest->digest_size);
+
+ bin2hex((const u8 *)d, sizeof(*d) + digest->digest_size,
+ digest_hex);
+ } else {
+ bin2hex(digest->digest, digest->digest_size,
+ digest_hex);
+ }
+
+ if (compact)
+ printf("%s\n", digest_hex);
+ else if (for_builtin_sig)
+ printf("%s %s\n", digest_hex, argv[i]);
+ else
+ printf("%s:%s %s\n",
+ libfsverity_get_hash_name(digest->digest_algorithm),
+ digest_hex, argv[i]);
+
+ filedes_close(&file);
+ free(digest);
+ free(d);
+ }
+ status = 0;
+out:
+ destroy_tree_params(&tree_params);
+ return status;
+
+out_err:
+ filedes_close(&file);
+ status = 1;
+ goto out;
+
+out_usage:
+ usage(cmd, stderr);
+ status = 2;
+ goto out;
+}
diff --git a/cmd_enable.c b/programs/cmd_enable.c
index 1646299..14c3c17 100644
--- a/cmd_enable.c
+++ b/programs/cmd_enable.c
@@ -1,48 +1,19 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: MIT
/*
* The 'fsverity enable' command
*
- * Copyright (C) 2018 Google LLC
+ * Copyright 2018 Google LLC
*
- * Written by Eric Biggers.
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
*/
+#include "fsverity.h"
+
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-
-#include "commands.h"
-#include "fsverity_uapi.h"
-#include "hash_algs.h"
-
-static bool parse_hash_alg_option(const char *arg, u32 *alg_ptr)
-{
- char *end;
- unsigned long n = strtoul(arg, &end, 10);
- const struct fsverity_hash_alg *alg;
-
- if (*alg_ptr != 0) {
- error_msg("--hash-alg can only be specified once");
- return false;
- }
-
- /* Specified by number? */
- if (n > 0 && n < INT32_MAX && *end == '\0') {
- *alg_ptr = n;
- return true;
- }
-
- /* Specified by name? */
- alg = find_hash_alg_by_name(arg);
- if (alg != NULL) {
- *alg_ptr = alg - fsverity_hash_algs;
- return true;
- }
- return false;
-}
static bool read_signature(const char *filename, u8 **sig_ret,
u32 *sig_size_ret)
@@ -77,13 +48,6 @@ out:
return ok;
}
-enum {
- OPT_HASH_ALG,
- OPT_BLOCK_SIZE,
- OPT_SALT,
- OPT_SIGNATURE,
-};
-
static const struct option longopts[] = {
{"hash-alg", required_argument, NULL, OPT_HASH_ALG},
{"block-size", required_argument, NULL, OPT_BLOCK_SIZE},
@@ -96,9 +60,9 @@ static const struct option longopts[] = {
int fsverity_cmd_enable(const struct fsverity_command *cmd,
int argc, char *argv[])
{
- struct fsverity_enable_arg arg = { .version = 1 };
- u8 *salt = NULL;
+ struct libfsverity_merkle_tree_params tree_params = { .version = 1 };
u8 *sig = NULL;
+ u32 sig_size = 0;
struct filedes file;
int status;
int c;
@@ -106,26 +70,18 @@ int fsverity_cmd_enable(const struct fsverity_command *cmd,
while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
switch (c) {
case OPT_HASH_ALG:
- if (!parse_hash_alg_option(optarg, &arg.hash_algorithm))
- goto out_usage;
- break;
case OPT_BLOCK_SIZE:
- if (!parse_block_size_option(optarg, &arg.block_size))
- goto out_usage;
- break;
case OPT_SALT:
- if (!parse_salt_option(optarg, &salt, &arg.salt_size))
+ if (!parse_tree_param(c, optarg, &tree_params))
goto out_usage;
- arg.salt_ptr = (uintptr_t)salt;
break;
case OPT_SIGNATURE:
if (sig != NULL) {
error_msg("--signature can only be specified once");
goto out_usage;
}
- if (!read_signature(optarg, &sig, &arg.sig_size))
+ if (!read_signature(optarg, &sig, &sig_size))
goto out_err;
- arg.sig_ptr = (uintptr_t)sig;
break;
default:
goto out_usage;
@@ -138,15 +94,10 @@ int fsverity_cmd_enable(const struct fsverity_command *cmd,
if (argc != 1)
goto out_usage;
- if (arg.hash_algorithm == 0)
- arg.hash_algorithm = FS_VERITY_HASH_ALG_DEFAULT;
-
- if (arg.block_size == 0)
- arg.block_size = get_default_block_size();
-
if (!open_file(&file, argv[0], O_RDONLY, 0))
goto out_err;
- if (ioctl(file.fd, FS_IOC_ENABLE_VERITY, &arg) != 0) {
+
+ if (libfsverity_enable_with_sig(file.fd, &tree_params, sig, sig_size)) {
error_msg_errno("FS_IOC_ENABLE_VERITY failed on '%s'",
file.name);
filedes_close(&file);
@@ -157,7 +108,7 @@ int fsverity_cmd_enable(const struct fsverity_command *cmd,
status = 0;
out:
- free(salt);
+ destroy_tree_params(&tree_params);
free(sig);
return status;
diff --git a/cmd_measure.c b/programs/cmd_measure.c
index 574e3ca..d78969c 100644
--- a/cmd_measure.c
+++ b/programs/cmd_measure.c
@@ -1,28 +1,26 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: MIT
/*
* The 'fsverity measure' command
*
- * Copyright (C) 2018 Google LLC
+ * Copyright 2018 Google LLC
*
- * Written by Eric Biggers.
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
*/
+#include "fsverity.h"
+
#include <fcntl.h>
-#include <stdlib.h>
#include <sys/ioctl.h>
-#include "commands.h"
-#include "fsverity_uapi.h"
-#include "hash_algs.h"
-
-/* Display the measurement of the given verity file(s). */
+/* Display the fs-verity digest of the given verity file(s). */
int fsverity_cmd_measure(const struct fsverity_command *cmd,
int argc, char *argv[])
{
struct fsverity_digest *d = NULL;
struct filedes file;
char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
- const struct fsverity_hash_alg *hash_alg;
char _hash_alg_name[32];
const char *hash_alg_name;
int status;
@@ -48,10 +46,8 @@ 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);
- if (hash_alg) {
- hash_alg_name = hash_alg->name;
- } else {
+ hash_alg_name = libfsverity_get_hash_name(d->digest_algorithm);
+ if (!hash_alg_name) {
sprintf(_hash_alg_name, "ALG_%u", d->digest_algorithm);
hash_alg_name = _hash_alg_name;
}
diff --git a/programs/cmd_sign.c b/programs/cmd_sign.c
new file mode 100644
index 0000000..47ba6a2
--- /dev/null
+++ b/programs/cmd_sign.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: MIT
+/*
+ * The 'fsverity sign' command
+ *
+ * Copyright 2018 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "fsverity.h"
+
+#include <fcntl.h>
+#include <getopt.h>
+
+static bool write_signature(const char *filename, const u8 *sig, u32 sig_size)
+{
+ struct filedes file;
+ bool ok;
+
+ if (!open_file(&file, filename, O_WRONLY|O_CREAT|O_TRUNC, 0644))
+ return false;
+ ok = full_write(&file, sig, sig_size);
+ ok &= filedes_close(&file);
+ return ok;
+}
+
+static const struct option longopts[] = {
+ {"hash-alg", required_argument, NULL, OPT_HASH_ALG},
+ {"block-size", required_argument, NULL, OPT_BLOCK_SIZE},
+ {"salt", required_argument, NULL, OPT_SALT},
+ {"key", required_argument, NULL, OPT_KEY},
+ {"cert", required_argument, NULL, OPT_CERT},
+ {NULL, 0, NULL, 0}
+};
+
+/* Sign a file for fs-verity by computing its digest, then signing it. */
+int fsverity_cmd_sign(const struct fsverity_command *cmd,
+ int argc, char *argv[])
+{
+ struct filedes file = { .fd = -1 };
+ struct libfsverity_merkle_tree_params tree_params = { .version = 1 };
+ struct libfsverity_signature_params sig_params = {};
+ struct libfsverity_digest *digest = NULL;
+ char digest_hex[FS_VERITY_MAX_DIGEST_SIZE * 2 + 1];
+ u8 *sig = NULL;
+ size_t sig_size;
+ int status;
+ int c;
+
+ while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
+ switch (c) {
+ case OPT_HASH_ALG:
+ case OPT_BLOCK_SIZE:
+ case OPT_SALT:
+ if (!parse_tree_param(c, optarg, &tree_params))
+ goto out_usage;
+ break;
+ case OPT_KEY:
+ if (sig_params.keyfile != NULL) {
+ error_msg("--key can only be specified once");
+ goto out_usage;
+ }
+ sig_params.keyfile = optarg;
+ break;
+ case OPT_CERT:
+ if (sig_params.certfile != NULL) {
+ error_msg("--cert can only be specified once");
+ goto out_usage;
+ }
+ sig_params.certfile = optarg;
+ break;
+ default:
+ goto out_usage;
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 2)
+ goto out_usage;
+
+ if (sig_params.keyfile == NULL) {
+ error_msg("Missing --key argument");
+ goto out_usage;
+ }
+ if (sig_params.certfile == NULL)
+ sig_params.certfile = sig_params.keyfile;
+
+ if (!open_file(&file, argv[0], O_RDONLY, 0))
+ goto out_err;
+
+ if (!get_file_size(&file, &tree_params.file_size))
+ goto out_err;
+
+ if (libfsverity_compute_digest(&file, read_callback,
+ &tree_params, &digest) != 0) {
+ error_msg("failed to compute digest");
+ goto out_err;
+ }
+
+ if (libfsverity_sign_digest(digest, &sig_params,
+ &sig, &sig_size) != 0) {
+ error_msg("failed to sign digest");
+ goto out_err;
+ }
+
+ if (!write_signature(argv[1], sig, sig_size))
+ goto out_err;
+
+ ASSERT(digest->digest_size <= FS_VERITY_MAX_DIGEST_SIZE);
+ bin2hex(digest->digest, digest->digest_size, digest_hex);
+ printf("Signed file '%s' (%s:%s)\n", argv[0],
+ libfsverity_get_hash_name(digest->digest_algorithm), digest_hex);
+ status = 0;
+out:
+ filedes_close(&file);
+ destroy_tree_params(&tree_params);
+ free(digest);
+ free(sig);
+ return status;
+
+out_err:
+ status = 1;
+ goto out;
+
+out_usage:
+ usage(cmd, stderr);
+ status = 2;
+ goto out;
+}
diff --git a/fsverity.c b/programs/fsverity.c
index 6fabfa4..b911b2e 100644
--- a/fsverity.c
+++ b/programs/fsverity.c
@@ -1,19 +1,17 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: MIT
/*
* fs-verity userspace tool
*
- * Copyright (C) 2018 Google LLC
+ * Copyright 2018 Google LLC
*
- * Written by Eric Biggers.
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
*/
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include "fsverity.h"
-#include "commands.h"
-#include "hash_algs.h"
+#include <limits.h>
static const struct fsverity_command {
const char *name;
@@ -22,6 +20,16 @@ static const struct fsverity_command {
const char *usage_str;
} fsverity_commands[] = {
{
+ .name = "digest",
+ .func = fsverity_cmd_digest,
+ .short_desc =
+"Compute the fs-verity digest of the given file(s), for offline signing",
+ .usage_str =
+" fsverity digest FILE...\n"
+" [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n"
+" [--compact] [--for-builtin-sig]\n"
+#ifndef _WIN32
+ }, {
.name = "enable",
.func = fsverity_cmd_enable,
.short_desc = "Enable fs-verity on a file",
@@ -33,9 +41,10 @@ static const struct fsverity_command {
.name = "measure",
.func = fsverity_cmd_measure,
.short_desc =
-"Display the measurement of the given verity file(s)",
+"Display the fs-verity digest of the given verity file(s)",
.usage_str =
" fsverity measure FILE...\n"
+#endif /* !_WIN32 */
}, {
.name = "sign",
.func = fsverity_cmd_sign,
@@ -47,6 +56,17 @@ static const struct fsverity_command {
}
};
+static void show_all_hash_algs(FILE *fp)
+{
+ u32 alg_num = 1;
+ const char *name;
+
+ fprintf(fp, "Available hash algorithms:");
+ while ((name = libfsverity_get_hash_name(alg_num++)) != NULL)
+ fprintf(fp, " %s", name);
+ putc('\n', fp);
+}
+
static void usage_all(FILE *fp)
{
int i;
@@ -59,10 +79,8 @@ static void usage_all(FILE *fp)
" Standard options:\n"
" fsverity --help\n"
" fsverity --version\n"
-"\n"
-"Available hash algorithms: ", fp);
+"\n", fp);
show_all_hash_algs(fp);
- fputs("\nSee `man fsverity` for more details.\n", fp);
}
static void usage_cmd(const struct fsverity_command *cmd, FILE *fp)
@@ -78,20 +96,10 @@ void usage(const struct fsverity_command *cmd, FILE *fp)
usage_all(fp);
}
-#define PACKAGE_VERSION "v0.0-alpha"
-#define PACKAGE_BUGREPORT "linux-fscrypt@vger.kernel.org"
-
static void show_version(void)
{
- static const char * const str =
-"fsverity " PACKAGE_VERSION "\n"
-"Copyright (C) 2018 Google LLC\n"
-"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
-"This is free software: you are free to change and redistribute it.\n"
-"There is NO WARRANTY, to the extent permitted by law.\n"
-"\n"
-"Report bugs to " PACKAGE_BUGREPORT ".\n";
- fputs(str, stdout);
+ printf("fsverity v%d.%d\n", FSVERITY_UTILS_MAJOR_VERSION,
+ FSVERITY_UTILS_MINOR_VERSION);
}
static void handle_common_options(int argc, char *argv[],
@@ -127,7 +135,32 @@ static const struct fsverity_command *find_command(const char *name)
return NULL;
}
-bool parse_block_size_option(const char *arg, u32 *size_ptr)
+static bool parse_hash_alg_option(const char *arg, u32 *alg_ptr)
+{
+ char *end;
+ unsigned long n = strtoul(arg, &end, 10);
+
+ if (*alg_ptr != 0) {
+ error_msg("--hash-alg can only be specified once");
+ return false;
+ }
+
+ /* Specified by number? */
+ if (n > 0 && n < INT32_MAX && *end == '\0') {
+ *alg_ptr = n;
+ return true;
+ }
+
+ /* Specified by name? */
+ *alg_ptr = libfsverity_find_hash_alg_by_name(arg);
+ if (*alg_ptr)
+ return true;
+ error_msg("unknown hash algorithm: '%s'", arg);
+ show_all_hash_algs(stderr);
+ return false;
+}
+
+static bool parse_block_size_option(const char *arg, u32 *size_ptr)
{
char *end;
unsigned long n = strtoul(arg, &end, 10);
@@ -145,7 +178,8 @@ bool parse_block_size_option(const char *arg, u32 *size_ptr)
return true;
}
-bool parse_salt_option(const char *arg, u8 **salt_ptr, u32 *salt_size_ptr)
+static bool parse_salt_option(const char *arg, u8 **salt_ptr,
+ u32 *salt_size_ptr)
{
if (*salt_ptr != NULL) {
error_msg("--salt can only be specified once");
@@ -160,23 +194,34 @@ bool parse_salt_option(const char *arg, u8 **salt_ptr, u32 *salt_size_ptr)
return true;
}
-u32 get_default_block_size(void)
+bool parse_tree_param(int opt_char, const char *arg,
+ struct libfsverity_merkle_tree_params *params)
{
- long n = sysconf(_SC_PAGESIZE);
-
- if (n <= 0 || n >= INT_MAX || !is_power_of_2(n)) {
- fprintf(stderr,
- "Warning: invalid _SC_PAGESIZE (%ld). Assuming 4K blocks.\n",
- n);
- return 4096;
+ switch (opt_char) {
+ case OPT_HASH_ALG:
+ return parse_hash_alg_option(arg, &params->hash_algorithm);
+ case OPT_BLOCK_SIZE:
+ return parse_block_size_option(arg, &params->block_size);
+ case OPT_SALT:
+ return parse_salt_option(arg, (u8 **)&params->salt,
+ &params->salt_size);
+ default:
+ ASSERT(0);
}
- return n;
+}
+
+void destroy_tree_params(struct libfsverity_merkle_tree_params *params)
+{
+ free((u8 *)params->salt);
+ memset(params, 0, sizeof(*params));
}
int main(int argc, char *argv[])
{
const struct fsverity_command *cmd;
+ install_libfsverity_error_handler();
+
if (argc < 2) {
error_msg("no command specified");
usage_all(stderr);
diff --git a/programs/fsverity.h b/programs/fsverity.h
new file mode 100644
index 0000000..45c4fe1
--- /dev/null
+++ b/programs/fsverity.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Private header for the 'fsverity' program
+ *
+ * Copyright 2018 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+#ifndef PROGRAMS_FSVERITY_H
+#define PROGRAMS_FSVERITY_H
+
+#include "utils.h"
+#include "../common/fsverity_uapi.h"
+
+/*
+ * Largest digest size among all hash algorithms supported by fs-verity.
+ * This can be increased if needed.
+ */
+#define FS_VERITY_MAX_DIGEST_SIZE 64
+
+enum {
+ OPT_BLOCK_SIZE,
+ OPT_CERT,
+ OPT_COMPACT,
+ OPT_FOR_BUILTIN_SIG,
+ OPT_HASH_ALG,
+ OPT_KEY,
+ OPT_SALT,
+ OPT_SIGNATURE,
+};
+
+struct fsverity_command;
+
+/* cmd_digest.c */
+int fsverity_cmd_digest(const struct fsverity_command *cmd,
+ int argc, char *argv[]);
+
+/* cmd_enable.c */
+int fsverity_cmd_enable(const struct fsverity_command *cmd,
+ int argc, char *argv[]);
+
+/* cmd_measure.c */
+int fsverity_cmd_measure(const struct fsverity_command *cmd,
+ int argc, char *argv[]);
+
+/* cmd_sign.c */
+int fsverity_cmd_sign(const struct fsverity_command *cmd,
+ int argc, char *argv[]);
+
+/* fsverity.c */
+void usage(const struct fsverity_command *cmd, FILE *fp);
+bool parse_tree_param(int opt_char, const char *arg,
+ struct libfsverity_merkle_tree_params *params);
+void destroy_tree_params(struct libfsverity_merkle_tree_params *params);
+
+#endif /* PROGRAMS_FSVERITY_H */
diff --git a/programs/test_compute_digest.c b/programs/test_compute_digest.c
new file mode 100644
index 0000000..e7f2645
--- /dev/null
+++ b/programs/test_compute_digest.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Test libfsverity_compute_digest().
+ *
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "utils.h"
+
+#include <ctype.h>
+#include <inttypes.h>
+
+struct mem_file {
+ u8 *data;
+ size_t size;
+ size_t offset;
+};
+
+static int read_fn(void *fd, void *buf, size_t count)
+{
+ struct mem_file *f = fd;
+
+ ASSERT(count <= f->size - f->offset);
+ memcpy(buf, &f->data[f->offset], count);
+ f->offset += count;
+ return 0;
+}
+
+static int error_read_fn(void *fd __attribute__((unused)),
+ void *buf __attribute__((unused)),
+ size_t count __attribute__((unused)))
+{
+ return -EIO;
+}
+
+static const struct test_case {
+ u32 hash_algorithm;
+ u32 block_size;
+ const char *salt;
+ u64 file_size;
+ const char *digest;
+} test_cases[] = {
+ { /* large file */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 1000000,
+ .block_size = 4096,
+ .digest = "\x48\xdf\x0c\x46\x23\x29\xcd\x87"
+ "\x96\x61\xbd\x05\xb3\x9a\xa8\x1b"
+ "\x05\xcc\x16\xaf\xd2\x7a\x71\x96"
+ "\xa5\x59\xda\x83\x53\x1d\x39\xd9",
+ }, { /* small file */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 100000,
+ .block_size = 4096,
+ .digest = "\xf2\x09\x6a\x36\xc5\xcd\xca\x4f"
+ "\xa3\x3e\xe8\x85\x28\x33\x15\x0b"
+ "\xb3\x24\x99\x2e\x54\x17\xa9\xd5"
+ "\x71\xf1\xbf\xff\xf7\x3b\x9e\xfc",
+ }, { /* single-block file */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 4096,
+ .block_size = 4096,
+ .digest = "\x6a\xc3\x99\x79\x01\x6e\x3d\xdf"
+ "\x3d\x39\xff\xf6\xcb\x98\x4f\x7c"
+ "\x11\x8a\xcd\xf1\x85\x29\x19\xf5"
+ "\xc1\x00\xc4\xb1\x42\xc1\x81\x8e",
+ }, { /* tiny file */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 1,
+ .block_size = 4096,
+ .digest = "\xb8\x03\x42\x95\x03\xd9\x59\x15"
+ "\x82\x9b\x29\xfd\xbc\x8b\xba\xd1"
+ "\x42\xf3\xab\xfd\x11\xb1\xca\xdf"
+ "\x55\x26\x58\x2e\x68\x5c\x05\x51",
+ }, { /* empty file */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 0,
+ .block_size = 4096,
+ .digest = "\x3d\x24\x8c\xa5\x42\xa2\x4f\xc6"
+ "\x2d\x1c\x43\xb9\x16\xea\xe5\x01"
+ "\x68\x78\xe2\x53\x3c\x88\x23\x84"
+ "\x80\xb2\x61\x28\xa1\xf1\xaf\x95",
+ }, { /* salt */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 1000000,
+ .block_size = 4096,
+ .salt = "abcd",
+ .digest = "\x91\x79\x00\xb0\xd2\x99\x45\x4a"
+ "\xa3\x04\xd5\xde\xbc\x6f\x39\xe4"
+ "\xaf\x7b\x5a\xbe\x33\xbd\xbc\x56"
+ "\x8d\x5d\x8f\x1e\x5c\x4d\x86\x52",
+ }, { /* max length salt (32 bytes) */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 1000000,
+ .block_size = 4096,
+ .salt = "0123456789:;<=>?@ABCDEFGHIJKLMNO",
+ .digest = "\xbc\x2d\x70\x32\x4c\x04\x8c\x22"
+ "\x0a\x2c\xb1\x90\x83\x21\x40\x86"
+ "\x3e\xb2\x68\xe6\x80\x42\x79\x39"
+ "\xe5\xd4\x67\xbe\xa5\xec\x5a\xd9",
+ }, { /* 1K block size */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 1000000,
+ .block_size = 1024,
+ .digest = "\xe9\xdf\x92\x7c\x14\xfc\xb9\x61"
+ "\xd5\xf5\x1c\x66\x6d\x8a\xe4\xc1"
+ "\x4f\xe4\xff\x98\xa3\x74\xc7\x33"
+ "\xe8\x98\xd0\x0c\x9e\x74\xa8\xe3",
+ }, { /* 512-byte block size */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 1000000,
+ .block_size = 512,
+ .digest = "\x03\x93\xee\x3d\xfd\x4a\x28\x96"
+ "\x6e\x2a\xf4\xe0\x7c\xfa\x5b\x03"
+ "\x2c\x30\xda\x5b\xb8\xe8\xef\x63"
+ "\xb9\xa5\x5b\xf9\x63\x26\x23\x34",
+ }, { /* 64K block size */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 1000000,
+ .block_size = 65536,
+ .digest = "\xf3\xb6\x41\x8f\x26\xd4\xd0\xe7"
+ "\x47\x28\x19\x3b\xae\x76\xf1\x5c"
+ "\xb4\xbb\x2c\xe9\x77\x74\x48\xd7"
+ "\x6b\xd8\x13\x8b\x69\xec\x61\xc2",
+ }, { /* SHA-512 */
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA512,
+ .file_size = 1000000,
+ .block_size = 4096,
+ .salt = "abcd",
+ .digest = "\x84\x25\xc6\xd0\xc9\x4f\x84\xed"
+ "\x90\x4c\x12\x93\x68\x45\xfb\xb7"
+ "\xaf\x99\x53\x75\x37\x89\x71\x2d"
+ "\xcc\x3b\xe1\x42\xdb\x3d\x4b\x6b"
+ "\x47\xa3\x99\xad\x52\xaa\x60\x92"
+ "\x56\xce\x29\xa9\x60\xbf\x4b\xb0"
+ "\xe5\x95\xec\x38\x6c\xa5\x8c\x06"
+ "\x51\x9d\x54\x6d\xc5\xb1\x97\xbb",
+ }, { /* default hash algorithm (SHA-256) and block size (4096) */
+ .file_size = 100000,
+ .digest = "\xf2\x09\x6a\x36\xc5\xcd\xca\x4f"
+ "\xa3\x3e\xe8\x85\x28\x33\x15\x0b"
+ "\xb3\x24\x99\x2e\x54\x17\xa9\xd5"
+ "\x71\xf1\xbf\xff\xf7\x3b\x9e\xfc",
+ },
+};
+
+static void fix_digest_and_print(const struct test_case *t,
+ const struct libfsverity_digest *d)
+{
+ char alg_name[32] = {};
+ size_t i;
+
+ strncpy(alg_name, libfsverity_get_hash_name(t->hash_algorithm),
+ sizeof(alg_name) - 1);
+ for (i = 0; i < sizeof(alg_name) - 1; i++)
+ alg_name[i] = toupper((u8)alg_name[i]);
+
+ printf("\t}, {\n"
+ "\t\t.hash_algorithm = FS_VERITY_HASH_ALG_%s,\n"
+ "\t\t.file_size = %" PRIu64 ",\n"
+ "\t\t.block_size = %u,\n",
+ alg_name, t->file_size, t->block_size);
+ if (t->salt != NULL)
+ printf("\t\t.salt = \"%s\",\n", t->salt);
+ for (i = 0; i < d->digest_size; i++) {
+ if (i == 0)
+ printf("\t\t.digest = \"");
+ else if (i % 8 == 0)
+ printf("\t\t\t \"");
+ printf("\\x%02x", d->digest[i]);
+ if (i + 1 == d->digest_size)
+ printf("\",\n");
+ else if (i % 8 == 7)
+ printf("\"\n");
+ }
+}
+
+static void test_invalid_params(void)
+{
+ struct mem_file f = { .data = (u8 *)"abcd", .size = 4 };
+ struct libfsverity_merkle_tree_params good_params = {
+ .version = 1,
+ .hash_algorithm = FS_VERITY_HASH_ALG_SHA256,
+ .file_size = 4,
+ .block_size = 4096,
+ };
+ struct libfsverity_merkle_tree_params params;
+ struct libfsverity_digest *d = NULL;
+
+ libfsverity_set_error_callback(NULL);
+
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &good_params, &d) == 0);
+ f.offset = 0;
+ free(d);
+ d = NULL;
+
+ /* missing required arguments */
+ ASSERT(libfsverity_compute_digest(&f, NULL, &good_params, &d) == -EINVAL);
+ ASSERT(libfsverity_compute_digest(&f, read_fn, NULL, &d) == -EINVAL);
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &good_params, NULL) == -EINVAL);
+
+ /* bad version */
+ params = good_params;
+ params.version = 0;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+ params.version = 1000;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+
+ /* bad hash_algorithm */
+ params = good_params;
+ params.hash_algorithm = 1000;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+
+ /* bad block_size */
+ params = good_params;
+ params.block_size = 1;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+ params.block_size = 4097;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+
+ /* bad salt_size */
+ params = good_params;
+ params.salt_size = 1000;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+ params.salt = (u8 *)"";
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+
+ /* bad reserved fields */
+ params = good_params;
+ params.reserved1[0] = 1;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+ params = good_params;
+ params.reserved1[ARRAY_SIZE(params.reserved1) - 1] = 1;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+ params = good_params;
+ params.reserved2[0] = 1;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+ params = good_params;
+ params.reserved2[ARRAY_SIZE(params.reserved2) - 1] = 1;
+ ASSERT(libfsverity_compute_digest(&f, read_fn, &params, &d) == -EINVAL);
+
+ /* error reading file */
+ ASSERT(libfsverity_compute_digest(&f, error_read_fn, &good_params, &d) == -EIO);
+
+ ASSERT(d == NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ const bool update = (argc == 2 && !strcmp(argv[1], "--update"));
+ size_t i;
+ struct mem_file f = {};
+ struct libfsverity_merkle_tree_params params;
+ struct libfsverity_digest *d;
+ int err;
+
+ install_libfsverity_error_handler();
+
+ for (i = 0; i < ARRAY_SIZE(test_cases); i++)
+ f.size = max(f.size, test_cases[i].file_size);
+
+ f.data = xmalloc(f.size);
+ for (i = 0; i < f.size; i++)
+ f.data[i] = (i % 11) + (i % 439) + (i % 1103);
+
+ for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
+ u32 expected_alg = test_cases[i].hash_algorithm ?:
+ FS_VERITY_HASH_ALG_SHA256;
+
+ memset(&params, 0, sizeof(params));
+ params.version = 1;
+ params.hash_algorithm = test_cases[i].hash_algorithm;
+ params.file_size = test_cases[i].file_size;
+ params.block_size = test_cases[i].block_size;
+ if (test_cases[i].salt) {
+ params.salt = (const u8 *)test_cases[i].salt;
+ params.salt_size = strlen(test_cases[i].salt);
+ }
+
+ f.size = test_cases[i].file_size;
+ f.offset = 0;
+
+ err = libfsverity_compute_digest(&f, read_fn, &params, &d);
+ ASSERT(err == 0);
+
+ ASSERT(d->digest_algorithm == expected_alg);
+ ASSERT(d->digest_size ==
+ libfsverity_get_digest_size(expected_alg));
+ if (update)
+ fix_digest_and_print(&test_cases[i], d);
+ else
+ ASSERT(!memcmp(d->digest, test_cases[i].digest,
+ d->digest_size));
+ free(d);
+ d = NULL;
+ }
+ free(f.data);
+ if (update) {
+ printf("\t}\n");
+ return 1;
+ }
+
+ test_invalid_params();
+ printf("test_compute_digest passed\n");
+ return 0;
+}
diff --git a/programs/test_hash_algs.c b/programs/test_hash_algs.c
new file mode 100644
index 0000000..ede9f05
--- /dev/null
+++ b/programs/test_hash_algs.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Test the hash algorithm-related libfsverity APIs.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "utils.h"
+
+#define SHA256_DIGEST_SIZE 32
+#define SHA512_DIGEST_SIZE 64
+
+int main(void)
+{
+ install_libfsverity_error_handler();
+
+ ASSERT(libfsverity_get_digest_size(0) == -1);
+ ASSERT(libfsverity_get_hash_name(0) == NULL);
+ ASSERT(libfsverity_find_hash_alg_by_name("bad") == 0);
+ ASSERT(libfsverity_find_hash_alg_by_name(NULL) == 0);
+
+ ASSERT(libfsverity_get_digest_size(100) == -1);
+ ASSERT(libfsverity_get_hash_name(100) == NULL);
+
+ ASSERT(libfsverity_get_digest_size(FS_VERITY_HASH_ALG_SHA256) ==
+ SHA256_DIGEST_SIZE);
+ ASSERT(!strcmp("sha256",
+ libfsverity_get_hash_name(FS_VERITY_HASH_ALG_SHA256)));
+ ASSERT(libfsverity_find_hash_alg_by_name("sha256") ==
+ FS_VERITY_HASH_ALG_SHA256);
+
+ ASSERT(libfsverity_get_digest_size(FS_VERITY_HASH_ALG_SHA512) ==
+ SHA512_DIGEST_SIZE);
+ ASSERT(!strcmp("sha512",
+ libfsverity_get_hash_name(FS_VERITY_HASH_ALG_SHA512)));
+ ASSERT(libfsverity_find_hash_alg_by_name("sha512") ==
+ FS_VERITY_HASH_ALG_SHA512);
+
+ printf("test_hash_algs passed\n");
+ return 0;
+}
diff --git a/programs/test_sign_digest.c b/programs/test_sign_digest.c
new file mode 100644
index 0000000..412c6cb
--- /dev/null
+++ b/programs/test_sign_digest.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Test libfsverity_sign_digest().
+ *
+ * Copyright 2020 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "utils.h"
+
+#include <fcntl.h>
+
+#define SHA256_DIGEST_SIZE 32
+
+int main(void)
+{
+ struct libfsverity_digest *d = xzalloc(sizeof(*d) + SHA256_DIGEST_SIZE);
+ const struct libfsverity_signature_params params = {
+ .keyfile = "testdata/key.pem",
+ .certfile = "testdata/cert.pem",
+ };
+ u8 *sig;
+ size_t sig_size;
+ struct filedes file;
+ u8 *expected_sig;
+ u64 expected_sig_size;
+ int err;
+
+ install_libfsverity_error_handler();
+
+ d->digest_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ d->digest_size = SHA256_DIGEST_SIZE;
+ memcpy(d->digest,
+ "\x91\x79\x00\xb0\xd2\x99\x45\x4a\xa3\x04\xd5\xde\xbc\x6f\x39"
+ "\xe4\xaf\x7b\x5a\xbe\x33\xbd\xbc\x56\x8d\x5d\x8f\x1e\x5c\x4d"
+ "\x86\x52", SHA256_DIGEST_SIZE);
+
+ err = libfsverity_sign_digest(d, &params, &sig, &sig_size);
+ ASSERT(err == 0);
+
+ ASSERT(open_file(&file, "testdata/file.sig", O_RDONLY, 0));
+ ASSERT(get_file_size(&file, &expected_sig_size));
+ ASSERT(sig_size == expected_sig_size);
+ expected_sig = xmalloc(sig_size);
+ ASSERT(full_read(&file, expected_sig, sig_size));
+ ASSERT(!memcmp(sig, expected_sig, sig_size));
+
+ free(d);
+ free(sig);
+ free(expected_sig);
+ filedes_close(&file);
+ printf("test_sign_digest passed\n");
+ return 0;
+}
diff --git a/util.c b/programs/utils.c
index 2218f2e..ce19b57 100644
--- a/util.c
+++ b/programs/utils.c
@@ -1,24 +1,23 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: MIT
/*
* Utility functions for the 'fsverity' program
*
- * Copyright (C) 2018 Google LLC
+ * Copyright 2018 Google LLC
*
- * Written by Eric Biggers.
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
*/
+#include "utils.h"
+
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
-#include "util.h"
-
/* ========== Memory allocation ========== */
void *xmalloc(size_t size)
@@ -47,7 +46,7 @@ char *xstrdup(const char *s)
/* ========== Error messages and assertions ========== */
-void do_error_msg(const char *format, va_list va, int err)
+static void do_error_msg(const char *format, va_list va, int err)
{
fputs("ERROR: ", stderr);
vfprintf(stderr, format, va);
@@ -89,11 +88,21 @@ __noreturn void assertion_failed(const char *expr, const char *file, int line)
fatal_error("Assertion failed: %s at %s:%d", expr, file, line);
}
+static void print_libfsverity_error(const char *msg)
+{
+ error_msg("%s", msg);
+}
+
+void install_libfsverity_error_handler(void)
+{
+ libfsverity_set_error_callback(print_libfsverity_error);
+}
+
/* ========== File utilities ========== */
bool open_file(struct filedes *file, const char *filename, int flags, int mode)
{
- file->fd = open(filename, flags, mode);
+ file->fd = open(filename, flags | O_BINARY, mode);
if (file->fd < 0) {
error_msg_errno("can't open '%s' for %s", filename,
(flags & O_ACCMODE) == O_RDONLY ? "reading" :
@@ -166,6 +175,14 @@ bool filedes_close(struct filedes *file)
return res == 0;
}
+int read_callback(void *file, void *buf, size_t count)
+{
+ errno = 0;
+ if (!full_read(file, buf, count))
+ return errno ? -errno : -EIO;
+ return 0;
+}
+
/* ========== String utilities ========== */
static int hex2bin_char(char c)
@@ -181,16 +198,18 @@ static int hex2bin_char(char c)
bool hex2bin(const char *hex, u8 *bin, size_t bin_len)
{
+ size_t i;
+
if (strlen(hex) != 2 * bin_len)
return false;
- while (bin_len--) {
+ for (i = 0; i < bin_len; i++) {
int hi = hex2bin_char(*hex++);
int lo = hex2bin_char(*hex++);
if (hi < 0 || lo < 0)
return false;
- *bin++ = (hi << 4) | lo;
+ bin[i] = (hi << 4) | lo;
}
return true;
}
@@ -206,10 +225,11 @@ static char bin2hex_char(u8 nibble)
void bin2hex(const u8 *bin, size_t bin_len, char *hex)
{
- while (bin_len--) {
- *hex++ = bin2hex_char(*bin >> 4);
- *hex++ = bin2hex_char(*bin & 0xf);
- bin++;
+ size_t i;
+
+ for (i = 0; i < bin_len; i++) {
+ *hex++ = bin2hex_char(bin[i] >> 4);
+ *hex++ = bin2hex_char(bin[i] & 0xf);
}
*hex = '\0';
}
diff --git a/programs/utils.h b/programs/utils.h
new file mode 100644
index 0000000..ab5005f
--- /dev/null
+++ b/programs/utils.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Utility functions for programs
+ *
+ * Copyright 2018 Google LLC
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+#ifndef PROGRAMS_UTILS_H
+#define PROGRAMS_UTILS_H
+
+#include "libfsverity.h"
+#include "../common/common_defs.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void *xmalloc(size_t size);
+void *xzalloc(size_t size);
+void *xmemdup(const void *mem, size_t size);
+char *xstrdup(const char *s);
+
+__printf(1, 2) __cold void error_msg(const char *format, ...);
+__printf(1, 2) __cold void error_msg_errno(const char *format, ...);
+__printf(1, 2) __cold __noreturn void fatal_error(const char *format, ...);
+__cold __noreturn void assertion_failed(const char *expr,
+ const char *file, int line);
+
+#define ASSERT(e) ({ if (!(e)) assertion_failed(#e, __FILE__, __LINE__); })
+
+void install_libfsverity_error_handler(void);
+
+struct filedes {
+ int fd;
+ char *name; /* filename, for logging or error messages */
+};
+
+bool open_file(struct filedes *file, const char *filename, int flags, int mode);
+bool get_file_size(struct filedes *file, u64 *size_ret);
+bool full_read(struct filedes *file, void *buf, size_t count);
+bool full_write(struct filedes *file, const void *buf, size_t count);
+bool filedes_close(struct filedes *file);
+int read_callback(void *file, void *buf, size_t count);
+
+bool hex2bin(const char *hex, u8 *bin, size_t bin_len);
+void bin2hex(const u8 *bin, size_t bin_len, char *hex);
+
+#endif /* PROGRAMS_UTILS_H */
diff --git a/scripts/do-release.sh b/scripts/do-release.sh
new file mode 100755
index 0000000..352fcf1
--- /dev/null
+++ b/scripts/do-release.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+# Copyright 2020 Google LLC
+#
+# Use of this source code is governed by an MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT.
+
+set -e -u -o pipefail
+cd "$(dirname "$0")/.."
+
+if [ $# != 1 ]; then
+ echo "Usage: $0 VERS" 1>&2
+ echo " e.g. $0 1.0" 1>&2
+ exit 2
+fi
+
+VERS=$1
+PKG=fsverity-utils-$VERS
+
+git checkout -f
+git clean -fdx
+./scripts/run-tests.sh
+git clean -fdx
+
+major=$(echo "$VERS" | cut -d. -f1)
+minor=$(echo "$VERS" | cut -d. -f2)
+sed -E -i -e "/FSVERITY_UTILS_MAJOR_VERSION/s/[0-9]+/$major/" \
+ -e "/FSVERITY_UTILS_MINOR_VERSION/s/[0-9]+/$minor/" \
+ include/libfsverity.h
+sed -E -i "/Version:/s/[0-9]+\.[0-9]+/$VERS/" \
+ lib/libfsverity.pc.in
+git commit -a --signoff --message="v$VERS"
+git tag --sign "v$VERS" --message="$PKG"
+
+git archive "v$VERS" --prefix="$PKG/" > "$PKG.tar"
+tar xf "$PKG.tar"
+( cd "$PKG" && make check )
+rm -r "$PKG"
+
+gpg --detach-sign --armor "$PKG.tar"
+DESTDIR=/pub/linux/kernel/people/ebiggers/fsverity-utils/v$VERS
+kup mkdir "$DESTDIR"
+kup put "$PKG.tar" "$PKG.tar.asc" "$DESTDIR/$PKG.tar.gz"
+git push
+git push --tags
diff --git a/scripts/run-sparse.sh b/scripts/run-sparse.sh
new file mode 100755
index 0000000..f75b837
--- /dev/null
+++ b/scripts/run-sparse.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+# Copyright 2020 Google LLC
+#
+# Use of this source code is governed by an MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT.
+
+set -e -u -o pipefail
+
+find . -name '*.c' | while read -r file; do
+ sparse "$file" -gcc-base-dir "$(gcc --print-file-name=)" \
+ -Iinclude -D_FILE_OFFSET_BITS=64 -Wbitwise -D_GNU_SOURCE
+done
diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh
new file mode 100755
index 0000000..530fe34
--- /dev/null
+++ b/scripts/run-tests.sh
@@ -0,0 +1,187 @@
+#!/bin/bash
+# SPDX-License-Identifier: MIT
+# Copyright 2020 Google LLC
+#
+# Use of this source code is governed by an MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT.
+#
+#
+# Test script for fsverity-utils. Runs 'make check' in lots of configurations,
+# runs static analysis, and does a few other tests.
+#
+# Note: for more test coverage, in addition to running this script, also build
+# fsverity-utils into a kvm-xfstests test appliance and run
+# 'kvm-xfstests -c ext4,f2fs -g verity'
+
+set -e -u -o pipefail
+cd "$(dirname "$0")/.."
+
+log() {
+ echo "[$(date)] $*" 1>&2
+}
+
+fail() {
+ echo "FAIL: $*" 1>&2
+ exit 1
+}
+
+TMPDIR=$(mktemp -d -t libfsverity_test.XXXXXXXXX)
+trap 'rm -r "$TMPDIR"' EXIT
+
+# Both stdout and stderr go to log file.
+# Only stderr goes to terminal.
+echo "Starting fsverity-utils tests. See run-tests.log for full output."
+rm -f run-tests.log
+exec >> run-tests.log
+exec 2> >(tee -ia run-tests.log 1>&2)
+
+MAKE="make -j$(getconf _NPROCESSORS_ONLN)"
+
+log "Build and test with statically linking"
+$MAKE CFLAGS="-Werror"
+if ldd fsverity | grep libfsverity.so; then
+ fail "fsverity binary should be statically linked to libfsverity by default"
+fi
+./fsverity --version
+
+log "Check that all global symbols are prefixed with \"libfsverity_\""
+if nm libfsverity.a | grep ' T ' | grep -v " libfsverity_"; then
+ fail "Some global symbols are not prefixed with \"libfsverity_\""
+fi
+
+log "Build and test with dynamic linking"
+$MAKE CFLAGS="-Werror" USE_SHARED_LIB=1 check
+if ! ldd fsverity | grep libfsverity.so; then
+ fail "fsverity binary should be dynamically linked to libfsverity when USE_SHARED_LIB=1"
+fi
+
+log "Check that all exported symbols are prefixed with \"libfsverity_\""
+if nm libfsverity.so | grep ' T ' | grep -v " libfsverity_"; then
+ fail "Some exported symbols are not prefixed with \"libfsverity_\""
+fi
+
+log "Test using libfsverity from C++ program"
+cat > "$TMPDIR/test.cc" <<EOF
+#include <libfsverity.h>
+#include <iostream>
+int main()
+{
+ std::cout << libfsverity_get_digest_size(FS_VERITY_HASH_ALG_SHA256) << std::endl;
+}
+EOF
+c++ -Wall -Werror "$TMPDIR/test.cc" -Iinclude -L. -lfsverity -o "$TMPDIR/test"
+[ "$(LD_LIBRARY_PATH=. "$TMPDIR/test")" = "32" ]
+rm "${TMPDIR:?}"/*
+
+log "Check that build doesn't produce untracked files"
+$MAKE CFLAGS="-Werror" all test_programs
+if git status --short | grep -q '^??'; then
+ git status
+ fail "Build produced untracked files (check 'git status'). Missing gitignore entry?"
+fi
+
+log "Test that 'make uninstall' uninstalls all files"
+make DESTDIR="$TMPDIR" install
+if [ "$(find "$TMPDIR" -type f -o -type l | wc -l)" = 0 ]; then
+ fail "'make install' didn't install any files"
+fi
+make DESTDIR="$TMPDIR" uninstall
+if [ "$(find "$TMPDIR" -type f -o -type l | wc -l)" != 0 ]; then
+ fail "'make uninstall' didn't uninstall all files"
+fi
+rm -r "${TMPDIR:?}"/*
+
+log "Build, install, and uninstall with dash"
+make clean SHELL=/bin/dash
+make DESTDIR="$TMPDIR" SHELL=/bin/dash install
+make DESTDIR="$TMPDIR" SHELL=/bin/dash uninstall
+
+log "Check that all files have license and copyright info"
+list="$TMPDIR/filelist"
+filter_license_info() {
+ # files to exclude from license and copyright info checks
+ grep -E -v '(\.gitignore|LICENSE|NEWS|README|testdata|fsverity_uapi\.h|libfsverity\.pc\.in)'
+}
+git grep -L 'SPDX-License-Identifier: MIT' \
+ | filter_license_info > "$list" || true
+if [ -s "$list" ]; then
+ fail "The following files are missing an appropriate SPDX license identifier: $(<"$list")"
+fi
+# For now some people still prefer a free-form license statement, not just SPDX.
+git grep -L 'Use of this source code is governed by an MIT-style' \
+ | filter_license_info > "$list" || true
+if [ -s "$list" ]; then
+ fail "The following files are missing an appropriate license statement: $(<"$list")"
+fi
+git grep -L '\<Copyright\>' | filter_license_info > "$list" || true
+if [ -s "$list" ]; then
+ fail "The following files are missing a copyright statement: $(<"$list")"
+fi
+rm "$list"
+
+log "Build and test with gcc (-O2)"
+$MAKE CC=gcc CFLAGS="-O2 -Werror" check
+
+log "Build and test with gcc (-O3)"
+$MAKE CC=gcc CFLAGS="-O3 -Werror" check
+
+log "Build and test with gcc (32-bit)"
+$MAKE CC=gcc CFLAGS="-O2 -Werror -m32" check
+
+log "Build and test with clang (-O2)"
+$MAKE CC=clang CFLAGS="-O2 -Werror" check
+
+log "Build and test with clang (-O3)"
+$MAKE CC=clang CFLAGS="-O3 -Werror" check
+
+log "Build and test with clang + UBSAN"
+$MAKE CC=clang \
+ CFLAGS="-O2 -Werror -fsanitize=undefined -fno-sanitize-recover=undefined" \
+ check
+
+log "Build and test with clang + ASAN"
+$MAKE CC=clang \
+ CFLAGS="-O2 -Werror -fsanitize=address -fno-sanitize-recover=address" \
+ check
+
+log "Build and test with clang + unsigned integer overflow sanitizer"
+$MAKE CC=clang \
+ CFLAGS="-O2 -Werror -fsanitize=unsigned-integer-overflow -fno-sanitize-recover=unsigned-integer-overflow" \
+ check
+
+log "Build and test with clang + CFI"
+$MAKE CC=clang CFLAGS="-O2 -Werror -fsanitize=cfi -flto -fvisibility=hidden" \
+ check
+
+log "Build and test with valgrind"
+$MAKE TEST_WRAPPER_PROG="valgrind --quiet --error-exitcode=100 --leak-check=full --errors-for-leak-kinds=all" \
+ CFLAGS="-O2 -Werror" check
+
+log "Build and test using BoringSSL instead of OpenSSL"
+BSSL=$HOME/src/boringssl
+$MAKE CFLAGS="-O2 -Werror" LDFLAGS="-L$BSSL/build/crypto" \
+ CPPFLAGS="-I$BSSL/include" LDLIBS="-lcrypto -lpthread" check
+
+log "Build and test using -funsigned-char"
+$MAKE CFLAGS="-O2 -Werror -funsigned-char" check
+
+log "Build and test using -fsigned-char"
+$MAKE CFLAGS="-O2 -Werror -fsigned-char" check
+
+log "Cross-compile for Windows (32-bit)"
+$MAKE CC=i686-w64-mingw32-gcc CFLAGS="-O2 -Werror"
+
+log "Cross-compile for Windows (64-bit)"
+$MAKE CC=x86_64-w64-mingw32-gcc CFLAGS="-O2 -Werror"
+
+log "Run sparse"
+./scripts/run-sparse.sh
+
+log "Run clang static analyzer"
+scan-build --status-bugs make CFLAGS="-O2 -Werror" all test_programs
+
+log "Run shellcheck"
+shellcheck scripts/*.sh 1>&2
+
+log "All tests passed!"
diff --git a/testdata/cert.pem b/testdata/cert.pem
new file mode 100644
index 0000000..c63b965
--- /dev/null
+++ b/testdata/cert.pem
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIUYaRYcyZGDIv9fIxx/RoJwQu23+owDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA1MTUwMjUyMzFaFw0yMDA2
+MTQwMjUyMzFaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQC5lXk9otBz/VM/tbvBBK6sK//HE+q3ctY4+fPVv3Ob
+D1YNYuWRD59U+7K8fVUfZlXyjgxt3n4VPzkLgRSr/w5YUTa5NEOVJIltKT4ugswL
+5oY8eRVSuIr1O+vTbu+EpUk3DhTaFkalVzwspwipBeiVTDO9kh0NAueRk2HyLJte
+IoPyfzSCKxg9sED6/WtLFqrhDb5+1qeGoMNGM66ueWXKX0QjomMEODGXC04ypIY3
+sTwB+sYhZUe3YRpY0HyaVNh/6cxCxSiKr2jkC5UL+ry+46EJerNZbKmeqqyqmhXh
+P2cHv8MO91zdH1xbXUUenLcdpK/0oq+/sTAVV1/qPvnAofpN8tdZdrH65JD753jt
+s+lH/f0iGuKAb9ZpLOTM2d3wjY13OcHElj0zu2Usw9PXQpTK/DYlbcapOI4NTVyU
+NpK3yP4i5dPnkHoxpjLWz75Eq6gP9ZXohGq3YG0LxtvELWfaFmpzEUTlD59hJJOZ
+ELGxAXzsxLbelX/EmpX+GIqnFBpdMIuPO4HEfJwD5IcdeHqGl2iH9LIqsY5DcGj4
+hnqYplIYYk5mWgbhkexRbJIGNdn8WyXlraVp2MoSr3p7xJbmo0qYRRtt3kQShzDG
+0FrZX7wqxemc/g/hr+g2xqMQj0nYLehDeodqxA2n9grDUr4AdgQyxXDMUkGZdrg3
+cwIDAQABo1MwUTAdBgNVHQ4EFgQUxpaj6YjgLFyh9UVM71hf18cb/hkwHwYDVR0j
+BBgwFoAUxpaj6YjgLFyh9UVM71hf18cb/hkwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEAQnXMCgI+eSjK+l3nrpE+TRrXZhHeB0aT3gilVtBqFM95
+GxkLzOgJnW4SU+BCKTiGjwhCEXQiFj6UNDrI7vaNzmurI370uqmC9/pwKn4/L27V
+ToqLHk5d8kmvjSJyfgY/9H73srzHjNcqLG3uy+JP3/fIzaUzy7x9OUJtzdH19zic
+b61kCbqe6TJrlpL0Y50FY91QTzupsIS9IsAAeYXrJiEwpkXv/O9c51swtGZmQhVD
+TDn4B2aKOHecR+gKZQbPcuwTCbNLDRRPT4q0IM9yKjUxIM8vkAaxlrW3O7fgqUV0
+GU3/i0zZugq2XEludF3VelrqMUhSMqaREAtRUe3ufipwEsovDa43Hr9P6bINAfU3
+92Kv6adgeKZc4DmuEg6sFje/ET85teioHtwmjviJu6vnkbZg7x+IU01Y6YHboz/z
+hTAjz9g6owgdsbTG9nptvgJllY83zBtnAGOqhJLNVZ+TC3pbbKht/7sT+s+WP2+K
+81oZ3gmxIr6myMVyR5CCt+FNJ1hFxYNBJDao35iiZLxDe2s5nMJKHYezUY60ujqT
+Ljv3Ku4uAk5PgXltZnWGz152ntjopA0gbnlU4f+SgnmPoBFcvn36BcUQWQbTDqmh
+h+Y0OaXR3x/27M/qkPBov4IAfoCkWeF7i02wxwtdTLiSF7OjTDkQXtZemUzN5+w=
+-----END CERTIFICATE-----
diff --git a/testdata/file.sig b/testdata/file.sig
new file mode 100644
index 0000000..1ba61f8
--- /dev/null
+++ b/testdata/file.sig
Binary files differ
diff --git a/testdata/key.pem b/testdata/key.pem
new file mode 100644
index 0000000..e76db4c
--- /dev/null
+++ b/testdata/key.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC5lXk9otBz/VM/
+tbvBBK6sK//HE+q3ctY4+fPVv3ObD1YNYuWRD59U+7K8fVUfZlXyjgxt3n4VPzkL
+gRSr/w5YUTa5NEOVJIltKT4ugswL5oY8eRVSuIr1O+vTbu+EpUk3DhTaFkalVzws
+pwipBeiVTDO9kh0NAueRk2HyLJteIoPyfzSCKxg9sED6/WtLFqrhDb5+1qeGoMNG
+M66ueWXKX0QjomMEODGXC04ypIY3sTwB+sYhZUe3YRpY0HyaVNh/6cxCxSiKr2jk
+C5UL+ry+46EJerNZbKmeqqyqmhXhP2cHv8MO91zdH1xbXUUenLcdpK/0oq+/sTAV
+V1/qPvnAofpN8tdZdrH65JD753jts+lH/f0iGuKAb9ZpLOTM2d3wjY13OcHElj0z
+u2Usw9PXQpTK/DYlbcapOI4NTVyUNpK3yP4i5dPnkHoxpjLWz75Eq6gP9ZXohGq3
+YG0LxtvELWfaFmpzEUTlD59hJJOZELGxAXzsxLbelX/EmpX+GIqnFBpdMIuPO4HE
+fJwD5IcdeHqGl2iH9LIqsY5DcGj4hnqYplIYYk5mWgbhkexRbJIGNdn8WyXlraVp
+2MoSr3p7xJbmo0qYRRtt3kQShzDG0FrZX7wqxemc/g/hr+g2xqMQj0nYLehDeodq
+xA2n9grDUr4AdgQyxXDMUkGZdrg3cwIDAQABAoICACla0aWWfnUaYk60JJ6ieHoN
+Y/XszkUK5gnUSS28d/p5tGdPPnDQ1mSNogq2sx1IJKbkWIizJ818RS33GbAqKfws
+PNGQf+7gMW+N3TloFCgiuo8HPGUukmiLbcWz1tPsMSB/ls3yYNO/WL1qi1d+5ZE/
+Zdg8kxSvLQMXoJ/iqMyVTGnhRsYq7D/y4sgLaLlW18VG1shU9QffEyS1p5thmfk6
+uWhna0EpdIOAFXDbkL0gVYrrYvNWKmEG1mQsMVgCyCvY4ZePb7VX2TvYCOKegSjY
+eK4wFX8746Bj0A5EP9Pt2Pu1E7ZmEN+FeYMyiZCEw5lrdXpCNn+08E4RJmKAng6Z
+LXu7FeBwOffsEcYDk1IPtT4JAkpEO0ennjyY2X+bWu6faaSGqoTDRm0igL/2qeDK
+xJCCOTPDePSDKkVFaimBncQlz4SSVS7WhqqofFMBIPBtZ6oe0c+0ZeKoaH2ZX7jU
+q2DTsRbUKeRerjOlAg5WxQoLxTmLl4t7aHjm4HVafEZHqja5szyNLy13qJZyo7+x
+R8kDRkVdui7XKdC9ajCMAsVcen4NyrvNqQHSoocgqmSuD3d5S/eZnE0cSGGX3Nae
+pb0bAFthQdPq2qfZDDL/cRpe+0FAsOtXG66yEcQjZdsdNerU+pSi/sgArJkNj/U1
+Dqd0LCPOOFiLa/f9LO6hAoIBAQD2HHn2Ghl7W1j4t806i6tfDNocgUCaO+2wjaOY
+cNrg0WiJP24oaRmXew1Le0q5NhX8wHgGZAvVOh0rkcX+sUBoSURzWFK5cavxIGmL
+XqW5wHFS9ahS3mQ3I0zEbueYPyFKEg2a2aFqqq8g3XRqgZtc4kfg4GUbIoltey4z
+uQlp3tIpbRjD9w2cYF9bU9xEgdp7PU5TDmYDiRetSd0HxwD2osj40sWsEbV9UN+4
+xIh6mUwn1ZcJmt7NzuHwyXGs63EhSQoxQwS/vAt0pIKwAlyn06//GBFZGhpnpjCT
+flN9vQPaqJZA93kugsBEt3fX+oGbFTVHgEYsyMTEzH2by6c1AoIBAQDBCmg0ziga
+2WZJE0YARf/gek3MKZNG6qjahuRynpGdtgqa6cSgnRzntDCEkLhU6KZ5+SYJNp0x
+9EBkO7o8YOGWfGUtnccqdbSCt14959Vqn+kcrKnadjFiSVOAGc1728YUjd3jYGS8
+ZXkGe3xXhmXiB1mv8ekKrGY2mmx7mBiZ40gNGJNg1gboenffMeP5l7JjaO7ew5QY
+A3swJkBHNIyGCdntlmrlw1vxT7bmDQoHVLKdQ+VFJFvUVyY40nVkeH1/9WB215Lu
+hzDTzn5Q+Z19pjEouohsJDY6k2FbOyaT0c/KI1NDlNZmUpWcoqf7IIusg1eCCGCH
+nXafifGxv7EHAoIBAAehemaXCJM6kdekW0ila/rWeyzHFSmzEfuXaKshVKgD1inr
+PY8jMxfvSMo+WGLFuojLru0DzRofYygmrOzosgaJvwWUh3wYeixPxPX9SUYpIVph
+I4buPk03Wvn8NlISIwYY6TMT7F1STXvHYgSrYBXRLklaq8fbmkc6uoQACLqvnfSK
+3Wm2D0X59vrt7rZxEEUh8XvBxof1iDZnQ+Mp2G3NPk34uwhKxEXObCFedpzWg/X4
+OWai1qWq9HZyyIOECU3u5dIBMfR/8Br9vs+WQykw9xQBuwf4NzlffcIU+KG9apEt
+CPuasLcwdqWqypx3t+0HC0/cOlDJKNCxRnO+LMECggEABzLoJ+/4NugclGUPmzsB
+C9IDzLVQNLjTizK0mkGnlIYRZy2Ik6TISyvBE3CCL0htzOapsHZE7nP5YsOHcnD6
+eK4y57yWjNLO5IEKFqzqnItSGiumOetmdA/f+Ur9Cr1raaDQwYX6u7vdA4zfWjQ8
+4Gz9vz36PtenCCpCGWnWoQaEzVg5Rsc0gr7ucXhe1BQAJwzmu4/3md2nXmhOxVkE
+VItRgTa2zdK3PwyF+ZZK5XMXJh4+EpIEiqqlVkEi95g2terkqgnoBNUt0PhGZaap
+ZOIpuycZp07CZvTQEKLoEWMlqJggpsiKJk62HZ1DPm48RzausL63Otd4cQKn7MUF
+SQKCAQAHbuzSCvI1YUnFiXtfNLIXdfTV1UJ+y9Mmb3/Lz/wvfbGiAC+TrJ4sdA7R
+k4riwnrv3F3kMop1gxQk1iWSt6qPg2tvw6ZoBcnzE6qJ2/xSdIXgrpV4kCrxAVYz
+vYt9KMWp17mgk4BYbnYU/1U1hZVowlhyx+lzsZtaRRnySvxeEcCO+eR6jTKAXWfH
+rVlbGuuBcTQ/Sz+Jw7mIM3gcI+fE9FyRznSjgVnsXqIsV9bntqkEUFb8VfunR2g5
+6eMoP8XSQdTcEvtKNJ2hrr9fkRABE4L/EeaMT4XOcYyNN9nPka+uj2nRoB9Vsb25
+x55d2r87sh6ScDvl57EIav09Y6W/
+-----END PRIVATE KEY-----