diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-19 12:01:13 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-19 12:01:13 +0000 |
commit | 6ac7fe4e9369f341a4cfe22cb09f27ec99ae882b (patch) | |
tree | 4cdaa88e6505f5a9cc97967952415b32a3383eff | |
parent | cc5e51366174ddd4580c78c9845d86e44586c034 (diff) | |
parent | 596ec1420a085648278e390b61ed1cadb5d08fc3 (diff) | |
download | fsverity-utils-android12-mainline-media-release.tar.gz |
Snap for 7474514 from 596ec1420a085648278e390b61ed1cadb5d08fc3 to mainline-media-releaseandroid-mainline-12.0.0_r89android-mainline-12.0.0_r74android-mainline-12.0.0_r62android-mainline-12.0.0_r46android-mainline-12.0.0_r29android-mainline-12.0.0_r12android-mainline-12.0.0_r119android-mainline-12.0.0_r104android12-mainline-media-release
Change-Id: I86d29eda57d08f2099ab23f4e4ff231b75c43157
-rw-r--r-- | .clang-format | 17 | ||||
-rw-r--r-- | .gitignore | 12 | ||||
-rw-r--r-- | Android.bp | 68 | ||||
-rw-r--r-- | COPYING | 339 | ||||
-rw-r--r-- | LICENSE | 360 | ||||
-rw-r--r-- | METADATA | 11 | ||||
-rw-r--r-- | MODULE_LICENSE_MIT (renamed from MODULE_LICENSE_GPL) | 0 | ||||
-rw-r--r-- | Makefile | 261 | ||||
-rw-r--r-- | NEWS.md | 43 | ||||
-rw-r--r-- | NOTICE | 339 | ||||
-rw-r--r-- | README.md | 114 | ||||
-rw-r--r-- | cmd_sign.c | 635 | ||||
-rw-r--r-- | commands.h | 24 | ||||
-rw-r--r-- | common/common_defs.h (renamed from util.h) | 58 | ||||
-rw-r--r-- | common/fsverity_uapi.h | 91 | ||||
-rw-r--r-- | common/win32_defs.h | 57 | ||||
-rw-r--r-- | fsverity_uapi.h | 40 | ||||
-rw-r--r-- | hash_algs.h | 68 | ||||
-rw-r--r-- | include/libfsverity.h | 213 | ||||
-rw-r--r-- | lib/compute_digest.c | 238 | ||||
-rw-r--r-- | lib/enable.c | 53 | ||||
-rw-r--r-- | lib/hash_algs.c (renamed from hash_algs.c) | 135 | ||||
-rw-r--r-- | lib/lib_private.h | 95 | ||||
-rw-r--r-- | lib/libfsverity.pc.in | 10 | ||||
-rw-r--r-- | lib/sign_digest.c | 390 | ||||
-rw-r--r-- | lib/utils.c | 132 | ||||
-rw-r--r-- | programs/cmd_digest.c | 129 | ||||
-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.c | 133 | ||||
-rw-r--r-- | programs/fsverity.c (renamed from fsverity.c) | 117 | ||||
-rw-r--r-- | programs/fsverity.h | 58 | ||||
-rw-r--r-- | programs/test_compute_digest.c | 310 | ||||
-rw-r--r-- | programs/test_hash_algs.c | 45 | ||||
-rw-r--r-- | programs/test_sign_digest.c | 57 | ||||
-rw-r--r-- | programs/utils.c (renamed from util.c) | 52 | ||||
-rw-r--r-- | programs/utils.h | 51 | ||||
-rwxr-xr-x | scripts/do-release.sh | 46 | ||||
-rwxr-xr-x | scripts/run-sparse.sh | 14 | ||||
-rwxr-xr-x | scripts/run-tests.sh | 187 | ||||
-rw-r--r-- | testdata/cert.pem | 31 | ||||
-rw-r--r-- | testdata/file.sig | bin | 0 -> 708 bytes | |||
-rw-r--r-- | testdata/key.pem | 52 |
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 @@ -1,5 +1,13 @@ -fsverity +*.a *.o -tags +*.patch +*.so +*.so.* +/.build-config +/fsverity +/fsverity.sig +/run-tests.log +/test_* cscope.* ncscope.* +tags @@ -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. @@ -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. @@ -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 @@ -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 @@ -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. @@ -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. @@ -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, ¶ms->hash_algorithm); + case OPT_BLOCK_SIZE: + return parse_block_size_option(arg, ¶ms->block_size); + case OPT_SALT: + return parse_salt_option(arg, (u8 **)¶ms->salt, + ¶ms->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, ¶ms, &d) == -EINVAL); + params.version = 1000; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL); + + /* bad hash_algorithm */ + params = good_params; + params.hash_algorithm = 1000; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL); + + /* bad block_size */ + params = good_params; + params.block_size = 1; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL); + params.block_size = 4097; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL); + + /* bad salt_size */ + params = good_params; + params.salt_size = 1000; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL); + params.salt = (u8 *)""; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL); + + /* bad reserved fields */ + params = good_params; + params.reserved1[0] = 1; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL); + params = good_params; + params.reserved1[ARRAY_SIZE(params.reserved1) - 1] = 1; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL); + params = good_params; + params.reserved2[0] = 1; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &d) == -EINVAL); + params = good_params; + params.reserved2[ARRAY_SIZE(params.reserved2) - 1] = 1; + ASSERT(libfsverity_compute_digest(&f, read_fn, ¶ms, &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(¶ms, 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, ¶ms, &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, ¶ms, &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 Binary files differnew file mode 100644 index 0000000..1ba61f8 --- /dev/null +++ b/testdata/file.sig 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----- |