pax_global_header00006660000000000000000000000064141062670110014510gustar00rootroot0000000000000052 comment=8c37b00d83bc1d1aa5b1cffd368b0300e9a9cace osslsigncode-2.2/000077500000000000000000000000001410626701100140475ustar00rootroot00000000000000osslsigncode-2.2/.gitignore000066400000000000000000000005631410626701100160430ustar00rootroot00000000000000.deps Makefile Makefile.in aclocal.m4 autom4te.cache/ compile config.h config.h.in config.h.in~ config.log config.status configure depcomp install-sh missing osslsigncode osslsigncode.o msi.o stamp-h1 INSTALL COPYING .#*# .*.bak .*.orig .*.rej .*~ #*# *.bak *.d *.def *.dll *.exe *.la *.lib *.lo *.orig *.pdb *.rej *.u *.rc *.pc *~ *.gz *.bz2 **/*.log !myapp.exe *.pem osslsigncode-2.2/COPYING.txt000066400000000000000000001045131410626701100157240ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 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, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU 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. But first, please read . osslsigncode-2.2/INSTALL.W32.md000066400000000000000000000066101410626701100160540ustar00rootroot00000000000000# osslsigncode Windows install notes ### Building osslsigncode source with MSYS2 MinGW 64-bit and MSYS2 packages: 1) Download and install MSYS2 from https://msys2.github.io/ and follow installation instructions. Once up and running install even mingw-w64-x86_64-gcc, mingw-w64-x86_64-curl, mingw-w64-x86_64-libgsf. ``` pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-curl mingw-w64-x86_64-libgsf ``` mingw-w64-x86_64-openssl and mingw-w64-x86_64-zlib packages are installed with dependencies. 2) Run "MSYS2 MinGW 64-bit" and build 64-bit Windows executables. ``` cd osslsigncode-folder x86_64-w64-mingw32-gcc osslsigncode.c msi.c -o osslsigncode.exe \ -lcrypto -lssl -lcurl -lgsf-1 -lgobject-2.0 -lglib-2.0 -lxml2 \ -I 'C:/msys64/mingw64/include/libgsf-1' \ -I 'C:/msys64/mingw64/include/glib-2.0' \ -I 'C:/msys64/mingw64/lib/glib-2.0/include' \ -D 'PACKAGE_STRING="osslsigncode 2.1.0"' \ -D 'PACKAGE_BUGREPORT="Michal.Trojnara@stunnel.org"' \ -D ENABLE_CURL \ -D WITH_GSF ``` 3) Run "Command prompt" and include "c:\msys64\mingw64\bin" folder as part of the path. ``` path=%path%;c:\msys64\mingw64\bin cd osslsigncode-folder osslsigncode.exe -v osslsigncode 2.1.0, using: OpenSSL 1.1.1g 21 Apr 2020 libcurl/7.70.0 OpenSSL/1.1.1g (Schannel) zlib/1.2.11 brotli/1.0.7 libidn2/2.3.0 libpsl/0.21.0 (+libidn2/2.3.0) libssh2/1.9.0 nghttp2/1.40.0 libgsf 1.14.46 ``` ### Building OpenSSL, Curl and osslsigncode sources with MSYS2 MinGW 64-bit: 1) Download and install MSYS2 from https://msys2.github.io/ and follow installation instructions. Once up and running install even: perl make autoconf automake libtool pkg-config. ``` pacman -S perl make autoconf automake libtool pkg-config ``` Make sure there are no curl, brotli, libpsl, libidn2 and nghttp2 packages installed: ``` pacman -R mingw-w64-x86_64-curl \ mingw-w64-x86_64-brotli \ mingw-w64-x86_64-libpsl \ mingw-w64-x86_64-libidn2 \ mingw-w64-x86_64-nghttp2 ``` Run "MSYS2 MinGW 64-bit" in the administrator mode. 2) Build and install OpenSSL. ``` cd openssl-(version) ./config --prefix='C:/OpenSSL' --openssldir='C:/OpenSSL' make && make install ``` 3) Build and install curl. ``` cd curl-(version) ./buildconf ./configure --prefix='C:/curl' --with-ssl='C:/OpenSSL' \ --disable-ftp --disable-tftp --disable-file --disable-dict \ --disable-telnet --disable-imap --disable-smb --disable-smtp \ --disable-gopher --disable-pop --disable-pop3 --disable-rtsp \ --disable-ldap --disable-ldaps --disable-unix-sockets --disable-pthreads make && make install ``` 3) Build 64-bit Windows executables. ``` cd osslsigncode-folder x86_64-w64-mingw32-gcc osslsigncode.c -o osslsigncode.exe \ -L 'C:/OpenSSL/lib/' -lcrypto -lssl \ -I 'C:/OpenSSL/include/' \ -L 'C:/curl/lib' -lcurl \ -I 'C:/curl/include' \ -D 'PACKAGE_STRING="osslsigncode 2.1.0"' \ -D 'PACKAGE_BUGREPORT="Michal.Trojnara@stunnel.org"' \ -D ENABLE_CURL ``` 4) Run "Command prompt" and copy required libraries. ``` cd osslsigncode-folder copy C:\OpenSSL\bin\libssl-1_1-x64.dll copy C:\OpenSSL\bin\libcrypto-1_1-x64.dll copy C:\curl\bin\libcurl-4.dll copy C:\msys64\mingw64\bin\zlib1.dll osslsigncode.exe -v osslsigncode 2.1.0, using: OpenSSL 1.1.1g 21 Apr 2020 libcurl/7.70.0 OpenSSL/1.1.1g zlib/1.2.11 no libgsf available ``` osslsigncode-2.2/LICENSE.txt000066400000000000000000000027471410626701100157040ustar00rootroot00000000000000OpenSSL based Authenticode signing for PE/MSI/Java CAB files. Copyright (C) 2005-2014 Per Allansson Copyright (C) 2018-2019 Michał Trojnara 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 3 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, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. osslsigncode-2.2/Makefile.am000066400000000000000000000010701410626701100161010ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign 1.10 MAINTAINERCLEANFILES = \ config.log config.status \ $(srcdir)/Makefile.in \ $(srcdir)/config.h.in $(srcdir)/config.h.in~ $(srcdir)/configure \ $(srcdir)/install-sh $(srcdir)/ltmain.sh $(srcdir)/missing \ $(srcdir)/depcomp $(srcdir)/aclocal.m4 $(srcdir)/ylwrap \ $(srcdir)/config.guess $(srcdir)/config.sub EXTRA_DIST = .gitignore AM_CFLAGS = $(OPENSSL_CFLAGS) $(OPTIONAL_LIBCURL_CFLAGS) bin_PROGRAMS = osslsigncode osslsigncode_SOURCES = osslsigncode.c msi.c msi.h osslsigncode_LDADD = $(OPENSSL_LIBS) $(OPTIONAL_LIBCURL_LIBS) osslsigncode-2.2/NEWS.md000066400000000000000000000073601410626701100151530ustar00rootroot00000000000000### 2.2 (2021.08.15) - CAT files support (thanks to James McKenzie) - MSI support rewritten without libgsf dependency, which allows for handling of all the needed MSI metadata, such as dates - "-untrusted" option renamed to "-TSA-CAfile" - "-CRLuntrusted" option renamed to "-TSA-CRLfile" - numerous bug fixes and improvements ### 2.1 (2020-10-11) - certificate chain verification support - timestamp verification support - CRL verification support ("-CRLfile" option) - improved CAB signature support - nested signatures support - user-specified signing time ("-st" option) by vszakats - added more tests - fixed numerous bugs - dropped OpenSSL 1.1.0 support ### 2.0 (2018-12-04) - orphaned project adopted by Michał Trojnara - ported to OpenSSL 1.1.x - ported to SoftHSM2 - add support for pkcs11-based hardware tokens (Patch from Leif Johansson) - improved error reporting of timestamping errors (Patch from Carlo Teubner) ### 1.7.1 (2014-07-11) - MSI: added -add-msi-dse option (Patch from Mikkel Krautz) - MSI: fix build when GSF_CAN_READ_MSI_METADATA defined (Patch from Mikkel Krautz) ### 1.7 (2014-07-10) - add support for nested signatures (Patch from Mikkel Krautz) - fix compilation problem with OpenSSL < 1.0.0 - added OpenSSL linkage exception to license ### 1.6 (2014-01-21) - add support for reading password from file - add support for asking for password (on systems that provide support for it) - add support for compiling and running on Windows (Patch from Heiko Hund) - fix compilation without curl (Fix from Heiko Hund) - added support for giving multiple timestamp servers as arguments (first one that succeeds will be used) - signatures on hierarchical MSI files were broken (Fix from Mikkel Krautz) - MSI: Add support for MsiDigitalSignatureEx signature (Patch from Mikkel Krautz) - add support for adding additional/cross certificates through -ac option (Thanks to Lars Munch for idea + testing) - MSI: Add support for signature extract/remove/verify (Patches from Mikkel Krautz) - PE/MSI: Implement -require-leaf-hash for verify. (Patch from Mikkel Krautz) ### 1.5.2 (2013-03-13) - added support for signing with SHA-384 and SHA-512 - added support for page hashing (-ph option) ### 1.5.1 (2013-03-12) - forgot to bump version number... ### 1.5 (2013-03-12) - added support for signing MSI files (patch from Marc-André Lureau) - calculate correct PE checksum instead of setting it to 0 (patch from Roland Schwingel) - added support for RFC3161 timestamping (-ts option) - added support for extracting/removing/verifying signature on PE files - fixed problem with not being able to decode timestamps with no newlines - added stricter checks for PE file validity - added support for reading keys from PVK files (requires OpenSSL 1.0.0 or later) - added support for reading certificates from PEM files - renamed program option: -spc to -certs (old option name still valid) ### 1.4 (2011-08-12) - improved build system (patch from Alon Bar-Lev) - support reading cert+key from PKCS12 file (patch from Alon Bar-Lev) - support reading key from PEM file - added support for sha1/sha256 - default hash is now sha1 - added flag for commercial signing (default is individual) ### 1.3.1 (2009-08-07) - support signing of 64-bit executables (fix from Paul Kendall) ### 1.3 (2008-01-31) - fixed padding problem (fix from Ryan Rubley) - allow signing of already signed files (fix from Ryan Rubley) - added Ryan Rubley's PVK-to-DER guide into the README ### 1.2 (2005-01-21) - autoconf:ed (Thanks to Roy Keene) - added documentation - don't override PKCS7_get_signed_attribute, it wasn't actually needed, it was me being confused. - compiles without curl, which means no timestamping - version number output ### 1.1 (2005-01-19) - Initial release osslsigncode-2.2/README.md000066400000000000000000000147461410626701100153420ustar00rootroot00000000000000osslsigncode ============ ## WHAT IS IT? osslsigncode is a small tool that implements part of the functionality of the Microsoft tool signtool.exe - more exactly the Authenticode signing and timestamping. But osslsigncode is based on OpenSSL and cURL, and thus should be able to compile on most platforms where these exist. ## WHY? Why not use signtool.exe? Because I don't want to go to a Windows machine every time I need to sign a binary - I can compile and build the binaries using Wine on my Linux machine, but I can't sign them since the signtool.exe makes good use of the CryptoAPI in Windows, and these APIs aren't (yet?) fully implemented in Wine, so the signtool.exe tool would fail. And, so, osslsigncode was born. ## WHAT CAN IT DO? It can sign and timestamp PE (EXE/SYS/DLL/etc), CAB, CAT and MSI files. It supports the equivalent of signtool.exe's "-j javasign.dll -jp low", i.e. add a valid signature for a CAB file containing Java files. It supports getting the timestamp through a proxy as well. It also supports signature verification, removal and extraction. ## BUILDING This section covers building osslsigncode for [Unix-like](https://en.wikipedia.org/wiki/Unix-like) operating systems. See [INSTALL.W32.md](https://github.com/mtrojnar/osslsigncode/blob/master/INSTALL.W32.md) for Windows notes. ### Generate the ./configure script This step is only needed if osslsigncode was cloned from a git repository. We highly recommend downloading a [release tarball](https://github.com/mtrojnar/osslsigncode/releases) instead. * Install prerequisites on a Debian-based distributions, such as Ubuntu: ``` sudo apt update && sudo apt install automake pkg-config ``` * Install prerequisites on macOS with Homebrew: ``` brew install automake pkg-config ``` * Generate the ./configure script: ``` ./bootstrap ``` ### Configure, build and install osslsigncode * Install prerequisites on a Debian-based distributions, such as Ubuntu: ``` sudo apt update && sudo apt install build-essential pkg-config libssl-dev libcurl4-openssl-dev ``` * Install prerequisites on macOS with Homebrew: ``` brew install pkg-config openssl@1.1 export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig" ``` * Configure, build and install osslsigncode: ``` ./configure && make && sudo make install ``` ## USAGE Before you can sign a file you need a Software Publishing Certificate (spc) and a corresponding private key. This article provides a good starting point as to how to do the signing with the Microsoft signtool.exe: http://www.matthew-jones.com/articles/codesigning.html To sign with osslsigncode you need the certificate file mentioned in the article above, in SPC or PEM format, and you will also need the private key which must be a key file in DER or PEM format, or if osslsigncode was compiled against OpenSSL 1.0.0 or later, in PVK format. To sign a PE or MSI file you can now do: ``` osslsigncode sign -certs -key \ -n "Your Application" -i http://www.yourwebsite.com/ \ -in yourapp.exe -out yourapp-signed.exe ``` or if you are using a PEM or PVK key file with a password together with a PEM certificate: ``` osslsigncode sign -certs \ -key -pass \ -n "Your Application" -i http://www.yourwebsite.com/ \ -in yourapp.exe -out yourapp-signed.exe ``` or if you want to add a timestamp as well: ``` osslsigncode sign -certs -key \ -n "Your Application" -i http://www.yourwebsite.com/ \ -t http://timestamp.digicert.com \ -in yourapp.exe -out yourapp-signed.exe ``` You can use a certificate and key stored in a PKCS#12 container: ``` osslsigncode sign -pkcs12 -pass \ -n "Your Application" -i http://www.yourwebsite.com/ \ -in yourapp.exe -out yourapp-signed.exe ``` To sign a CAB file containing java class files: ``` osslsigncode sign -certs -key \ -n "Your Application" -i http://www.yourwebsite.com/ \ -jp low \ -in yourapp.cab -out yourapp-signed.cab ``` Only the 'low' parameter is currently supported. If you want to use PKCS11 token, you should indicate PKCS11 engine and module. An example of using osslsigncode with SoftHSM: ``` osslsigncode sign \ -pkcs11engine /usr/lib64/engines-1.1/pkcs11.so \ -pkcs11module /usr/lib64/pkcs11/libsofthsm2.so \ -pkcs11cert 'pkcs11:token=softhsm-token;object=cert' \ -key 'pkcs11:token=softhsm-token;object=key' \ -in yourapp.exe -out yourapp-signed.exe ``` You can check that the signed file is correct by right-clicking on it in Windows and choose Properties --> Digital Signatures, and then choose the signature from the list, and click on Details. You should then be presented with a dialog that says amongst other things that "This digital signature is OK". ## CONVERTING FROM PVK TO DER (This guide was written by Ryan Rubley) If you've managed to finally find osslsigncode from some searches, you're most likely going to have a heck of a time getting your SPC and PVK files into the formats osslsigncode wants. On the computer where you originally purchased your certificate, you probably had to use IE to get it. Run IE and select Tools/Internet Options from the menu, then under the Content tab, click the Certificates button. Under the Personal tab, select your certificate and click the Export button. On the second page of the wizard, select the PKCS #7 Certificate (.P7B) format. This file you export as a *.p7b is what you use instead of your *.spc file. It's the same basic thing, in a different format. For your PVK file, you will need to download a little utility called PVK.EXE. This can currently be downloaded at https://www.globalsign.com/support/code-signing/PVK.zip Run: ``` pvk -in foo.pvk -nocrypt -out foo.pem ``` This will convert your PVK file to a PEM file. From there, you can copy the PEM file to a Linux box, and run: ``` openssl rsa -outform der -in foo.pem -out foo.der ``` This will convert your PEM file to a DER file. You need the *.p7b and *.der files to use osslsigncode, instead of your *.spc and *.pvk files. ## BUGS, QUESTIONS etc. Check whether your your question or suspected bug was already discussed on https://github.com/mtrojnar/osslsigncode/issues. Otherwise, open a new issue. BUT, if you have questions related to generating spc files, converting between different formats and so on, *please* spend a few minutes searching on google for your particular problem since many people probably already have had your problem and solved it as well. osslsigncode-2.2/README.unauthblob.md000066400000000000000000000054171410626701100174770ustar00rootroot00000000000000# This is NOT the official repo for osslsigncode This project was copied from osslsigncode 1.7.1 to apply some patches for compiling with cygwin and being able to add unauthenticated blobs. The official source for the project is at: http://sourceforge.net/projects/osslsigncode/ ## Features added Adds the argument "-addUnauthenticatedBlob" to add a 1024 byte unauthenticated blob of data to the signature in the same area as the timestamp. This can be used while signing, while timestamping (new `add` command added to allow just time-stamping, after a file has been code signed, or by itself. Examples: ``` # Example 1. Sign and add blob to unsigned file osslsigncode sign -addUnauthenticatedBlob -pkcs12 yourcert.pfx -pass your_password -n "Your Company" -i https://YourSite.com/ -in srepp.msi -out srepp_added.msi ``` ``` # Example 2. Timestamp and add blob to signed file osslsigncode.exe add -addUnauthenticatedBlob -t http://timestamp.digicert.com -in your_signed_file.exe -out out.exe ``` ``` # Example 3. Add blob to signed and time-stamped file osslsigncode.exe add -addUnauthenticatedBlob -in your_signed_file.exe -out out.exe ``` ``` # Example 4. Sign, timestamp, and add blob # Technically you can do this, but this would mean your signing certificate # is on a computer that is connected the Internet, # which means you are doing something wrong, # so I'm not going to show how to do that. ``` This technique (but not this project) is used by Dropbox, GoToMeeting, and Summit Route. You can read more about this technique here: - https://tech.dropbox.com/2014/08/tech-behind-dropboxs-new-user-experience-for-mobile/ - http://blogs.msdn.com/b/ieinternals/archive/2014/09/04/personalizing-installers-using-unauthenticated-data-inside-authenticode-signed-binaries.aspx ## WARNING The capability this adds can allow you to do dumb things. Be very careful with what you put in the unauthenticated blob, as an attacker could modify this. Do NOT under any circumstances put a URL here that you will use to download an additional file. If you do do that, you would need to check the newly downloaded file is code signed AND that it has been signed with your cert AND that it is the version you expect. You should consider using asymmetrical encryption for the data you put in the blob, such that the executable contains the public key to decrypt the data. Basically, be VERY careful. ## Compiling under cygwin - Ensure you install the development libraries for openssl, libgfs, and curl. - Install pkg-config - Run ``` export SHELLOPTS set -o igncr ./configure make ``` ## Download - Compiled binary for cygwin: https://summitroute.com/downloads/osslsigncode.exe - Compiled binary plus all the required DLL's (self-extracting exe): https://summitroute.com/downloads/osslsigncode-cygwin_files.exe osslsigncode-2.2/TODO.md000066400000000000000000000002721410626701100151370ustar00rootroot00000000000000- signature extraction/removal/verificaton on MSI/CAB files - clean up / untangle code - separate timestamping - remove mmap usage to increase portability - fix other stuff marked 'XXX' osslsigncode-2.2/bootstrap000077500000000000000000000000611410626701100160070ustar00rootroot00000000000000#!/bin/sh autoreconf --verbose --install --force osslsigncode-2.2/configure.ac000066400000000000000000000046631410626701100163460ustar00rootroot00000000000000AC_PREREQ(2.60) AC_INIT([osslsigncode], [2.2.0], [Michal.Trojnara@stunnel.org]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([osslsigncode.c]) dnl Checks for programs. AC_PROG_CC AC_USE_SYSTEM_EXTENSIONS AC_ARG_ENABLE( [strict], [AS_HELP_STRING([--enable-strict],[enable strict compile mode @<:@disabled@:>@])], , [enable_strict="no"] ) AC_ARG_ENABLE( [pedantic], [AS_HELP_STRING([--enable-pedantic],[enable pedantic compile mode @<:@disabled@:>@])], , [enable_pedantic="no"] ) AC_ARG_WITH( [curl], [AS_HELP_STRING([--with-curl],[enable curl @<:@enabled@:>@])], , [with_curl="yes"] ) if test "${enable_pedantic}" = "yes"; then enable_strict="yes"; CFLAGS="${CFLAGS} -pedantic" fi if test "${enable_strict}" = "yes"; then CFLAGS="${CFLAGS} -Wall -Wextra" fi PKG_PROG_PKG_CONFIG AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MKDIR_P AC_PROG_SED AC_PROG_MAKE_SET AC_C_CONST AC_HEADER_STDC AC_HEADER_TIME AC_CHECK_HEADERS( [sys/mman.h], [AC_CHECK_FUNC( [mmap], [AC_DEFINE(HAVE_MMAP, [1], [Define to 1 if you have mmap])], [AC_MSG_ERROR([Need mmap to build.])] )], [have_mmap=no] ) AC_CHECK_HEADERS( [windows.h], [], [have_MapViewOfFile=no] ) AS_IF([test "x$have_mmap$have_MapViewOfFile" = "xnono"], [AC_MSG_ERROR([Need file mapping function to buid.])]) AC_CHECK_LIB( [dl], [dlopen], [DL_LIBS="-ldl"] ) AC_CHECK_HEADERS([termios.h]) AC_CHECK_FUNCS(getpass) PKG_CHECK_MODULES( [OPENSSL], [libcrypto >= 1.1.1], , [PKG_CHECK_MODULES( [OPENSSL], [openssl >= 1.1.1], , [AC_CHECK_LIB( [crypto], [EVP_MD_CTX_new], [OPENSSL_LIBS="-lcrypto ${SOCKETS_LIBS} ${DL_LIBS}"], [AC_MSG_ERROR([OpenSSL 1.1.1 or later is required. https://www.openssl.org/])], [${DL_LIBS}] )] )] ) PKG_CHECK_MODULES( [LIBCURL], [libcurl >= 7.12.0], , [AC_CHECK_LIB( [curl], [curl_easy_strerror], [LIBCURL_LIBS="-lcurl"], , [${DL_LIBS}] )] ) if test "${with_curl}" = "yes"; then test -z "${LIBCURL_LIBS}" && AC_MSG_ERROR([Curl 7.12.0 or later is required for timestamping support. http://curl.haxx.se/]) OPTIONAL_LIBCURL_CFLAGS="${LIBCURL_CFLAGS}" OPTIONAL_LIBCURL_LIBS="${LIBCURL_LIBS}" AC_DEFINE([ENABLE_CURL], [1], [libcurl is enabled]) fi AC_SUBST([OPTIONAL_LIBCURL_CFLAGS]) AC_SUBST([OPTIONAL_LIBCURL_LIBS]) AC_DEFINE_UNQUOTED([CA_BUNDLE_PATH], ["$(curl-config --ca 2>/dev/null)"], [CA bundle install path]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT # vim: set ts=4 noexpandtab: osslsigncode-2.2/misc/000077500000000000000000000000001410626701100150025ustar00rootroot00000000000000osslsigncode-2.2/misc/pagehash.py000066400000000000000000000061541410626701100171420ustar00rootroot00000000000000#!/usr/bin/python import struct import sys import hashlib from pyasn1.type import univ from pyasn1.codec.ber import encoder, decoder f = open(sys.argv[1], 'rb') filehdr = f.read(1024) if filehdr[0:2] != 'MZ': print "Not a DOS file." sys.exit(0) pepos = struct.unpack('config.py < * Author: Małgorzata Olszówka * * Reference specifications: * http://en.wikipedia.org/wiki/Compound_File_Binary_Format * https://msdn.microsoft.com/en-us/library/dd942138.aspx * https://github.com/microsoft/compoundfilereader */ #include /* memcmp */ #include "msi.h" #define MIN(a,b) ((a) < (b) ? a : b) /* Get absolute address from sector and offset */ static const u_char *sector_offset_to_address(MSI_FILE *msi, size_t sector, size_t offset) { if (sector >= MAXREGSECT || offset >= msi->m_sectorSize || msi->m_bufferLen <= msi->m_sectorSize * sector + msi->m_sectorSize + offset) { return NULL; /* FAILED */ } return msi->m_buffer + msi->m_sectorSize + msi->m_sectorSize * sector + offset; } static size_t get_fat_sector_location(MSI_FILE *msi, size_t fatSectorNumber) { size_t entriesPerSector, difatSectorLocation; const u_char *address; if (fatSectorNumber < DIFAT_IN_HEADER) { return msi->m_hdr->headerDIFAT[fatSectorNumber]; } else { fatSectorNumber -= DIFAT_IN_HEADER; entriesPerSector = msi->m_sectorSize / 4 - 1; difatSectorLocation = msi->m_hdr->firstDIFATSectorLocation; while (fatSectorNumber >= entriesPerSector) { fatSectorNumber -= entriesPerSector; address = sector_offset_to_address(msi, difatSectorLocation, msi->m_sectorSize - 4); difatSectorLocation = GET_UINT32_LE(address); } return GET_UINT32_LE(sector_offset_to_address(msi, difatSectorLocation, fatSectorNumber * 4)); } } /* Lookup FAT */ static size_t get_next_sector(MSI_FILE *msi, size_t sector) { size_t entriesPerSector = msi->m_sectorSize / 4; size_t fatSectorNumber = sector / entriesPerSector; size_t fatSectorLocation = get_fat_sector_location(msi, fatSectorNumber); return GET_UINT32_LE(sector_offset_to_address(msi, fatSectorLocation, sector % entriesPerSector * 4)); } /* Locate the final sector/offset when original offset expands multiple sectors */ static void locate_final_sector(MSI_FILE *msi, size_t sector, size_t offset, size_t *finalSector, size_t *finalOffset) { while (offset >= msi->m_sectorSize) { offset -= msi->m_sectorSize; sector = get_next_sector(msi, sector); } *finalSector = sector; *finalOffset = offset; } /* Get absolute address from mini sector and offset */ static const u_char *mini_sector_offset_to_address(MSI_FILE *msi, size_t sector, size_t offset) { if (sector >= MAXREGSECT || offset >= msi->m_minisectorSize || msi->m_bufferLen <= msi->m_minisectorSize * sector + offset) { return NULL; /* FAILED */ } locate_final_sector(msi, msi->m_miniStreamStartSector, sector * msi->m_minisectorSize + offset, §or, &offset); return sector_offset_to_address(msi, sector, offset); } /* * Copy as many as possible in each step * copylen typically iterate as: msi->m_sectorSize - offset --> msi->m_sectorSize --> msi->m_sectorSize --> ... --> remaining */ static int read_stream(MSI_FILE *msi, size_t sector, size_t offset, char *buffer, size_t len) { locate_final_sector(msi, sector, offset, §or, &offset); while (len > 0) { const u_char *address = sector_offset_to_address(msi, sector, offset); size_t copylen = MIN(len, msi->m_sectorSize - offset); if (msi->m_buffer + msi->m_bufferLen < address + copylen) { return 0; /* FAILED */ } memcpy(buffer, address, copylen); buffer += copylen; len -= copylen; sector = get_next_sector(msi, sector); offset = 0; } return 1; } /* Lookup miniFAT */ static size_t get_next_mini_sector(MSI_FILE *msi, size_t miniSector) { size_t sector, offset; locate_final_sector(msi, msi->m_hdr->firstMiniFATSectorLocation, miniSector * 4, §or, &offset); return GET_UINT32_LE(sector_offset_to_address(msi, sector, offset)); } static void locate_final_mini_sector(MSI_FILE *msi, size_t sector, size_t offset, size_t *finalSector, size_t *finalOffset) { while (offset >= msi->m_minisectorSize) { offset -= msi->m_minisectorSize; sector = get_next_mini_sector(msi, sector); } *finalSector = sector; *finalOffset = offset; } /* Same logic as "read_stream" except that use mini stream functions instead */ static int read_mini_stream(MSI_FILE *msi, size_t sector, size_t offset, char *buffer, size_t len) { locate_final_mini_sector(msi, sector, offset, §or, &offset); while (len > 0) { const u_char *address = mini_sector_offset_to_address(msi, sector, offset); size_t copylen = MIN(len, msi->m_minisectorSize - offset); if (!address || msi->m_buffer + msi->m_bufferLen < address + copylen) { return 0; /* FAILED */ } memcpy(buffer, address, copylen); buffer += copylen; len -= copylen; sector = get_next_mini_sector(msi, sector); offset = 0; } return 1; } /* * Get file (stream) data start with "offset". * The buffer must have enough space to store "len" bytes. Typically "len" is derived by the steam length. */ int msi_file_read(MSI_FILE *msi, MSI_ENTRY *entry, size_t offset, char *buffer, size_t len) { if (len < msi->m_hdr->miniStreamCutoffSize) { if (!read_mini_stream(msi, entry->startSectorLocation, offset, buffer, len)) return 0; /* FAILED */ } else { if (!read_stream(msi, entry->startSectorLocation, offset, buffer, len)) return 0; /* FAILED */ } return 1; } /* Parse MSI_FILE_HDR struct */ static MSI_FILE_HDR *parse_header(char *data) { MSI_FILE_HDR *header = (MSI_FILE_HDR *)OPENSSL_malloc(HEADER_SIZE); if (!data) { /* initialise 512 bytes */ memset(header, 0, sizeof(MSI_FILE_HDR)); } else { memcpy(header->signature, data + HEADER_SIGNATURE, sizeof header->signature); header->minorVersion = GET_UINT16_LE(data + HEADER_MINOR_VER); header->majorVersion = GET_UINT16_LE(data + HEADER_MAJOR_VER); header->byteOrder = GET_UINT16_LE(data + HEADER_BYTE_ORDER); header->sectorShift = GET_UINT16_LE(data + HEADER_SECTOR_SHIFT); header->miniSectorShift = GET_UINT16_LE(data + HEADER_MINI_SECTOR_SHIFT); header->numDirectorySector = GET_UINT32_LE(data + HEADER_DIR_SECTORS_NUM); header->numFATSector = GET_UINT32_LE(data + HEADER_FAT_SECTORS_NUM); header->firstDirectorySectorLocation = GET_UINT32_LE(data + HEADER_DIR_SECTOR_LOC); header->transactionSignatureNumber = GET_UINT32_LE(data + HEADER_TRANSACTION); header->miniStreamCutoffSize = GET_UINT32_LE(data + HEADER_MINI_STREAM_CUTOFF); header->firstMiniFATSectorLocation = GET_UINT32_LE(data + HEADER_MINI_FAT_SECTOR_LOC); header->numMiniFATSector = GET_UINT32_LE(data + HEADER_MINI_FAT_SECTORS_NUM); header->firstDIFATSectorLocation = GET_UINT32_LE(data + HEADER_DIFAT_SECTOR_LOC); header->numDIFATSector = GET_UINT32_LE(data + HEADER_DIFAT_SECTORS_NUM); memcpy(header->headerDIFAT, data + HEADER_DIFAT, sizeof header->headerDIFAT); } return header; } /* Parse MSI_ENTRY struct */ static MSI_ENTRY *parse_entry(const u_char *data) { MSI_ENTRY *entry = (MSI_ENTRY *)OPENSSL_malloc(sizeof(MSI_ENTRY)); entry->nameLen = GET_UINT16_LE(data + DIRENT_NAME_LEN); memcpy(entry->name, data + DIRENT_NAME, entry->nameLen); entry->type = GET_UINT8_LE(data + DIRENT_TYPE); entry->colorFlag = GET_UINT8_LE(data + DIRENT_COLOUR); entry->leftSiblingID = GET_UINT32_LE(data + DIRENT_LEFT_SIBLING_ID); entry->rightSiblingID = GET_UINT32_LE(data + DIRENT_RIGHT_SIBLING_ID); entry->childID = GET_UINT32_LE(data + DIRENT_CHILD_ID); memcpy(entry->clsid, data + DIRENT_CLSID, 16); memcpy(entry->stateBits, data + DIRENT_STATE_BITS, 4); memcpy(entry->creationTime, data + DIRENT_CREATE_TIME, 8); memcpy(entry->modifiedTime, data + DIRENT_MODIFY_TIME, 8); entry->startSectorLocation = GET_UINT32_LE(data + DIRENT_START_SECTOR_LOC); memcpy(entry->size, data + DIRENT_FILE_SIZE, 8); return entry; } /* * Get entry (directory or file) by its ID. * Pass "0" to get the root directory entry. -- This is the start point to navigate the compound file. * Use the returned object to access child entries. */ static MSI_ENTRY *get_entry(MSI_FILE *msi, size_t entryID) { size_t sector = 0; size_t offset = 0; const u_char *address; /* The special value NOSTREAM (0xFFFFFFFF) is used as a terminator */ if (entryID == NOSTREAM) { return NULL; /* FAILED */ } if (msi->m_bufferLen / sizeof(MSI_ENTRY) <= entryID) { printf("Invalid argument entryID\n"); return NULL; /* FAILED */ } locate_final_sector(msi, msi->m_hdr->firstDirectorySectorLocation, entryID * sizeof(MSI_ENTRY), §or, &offset); address = sector_offset_to_address(msi, sector, offset); return parse_entry(address); } MSI_ENTRY *msi_root_entry_get(MSI_FILE *msi) { return get_entry(msi, 0); } /* Parse MSI_FILE struct */ MSI_FILE *msi_file_new(char *buffer, size_t len) { MSI_FILE *msi; MSI_ENTRY *root; if (buffer == NULL || len == 0) { printf("Invalid argument\n"); return NULL; /* FAILED */ } msi = (MSI_FILE *)OPENSSL_malloc(sizeof(MSI_FILE)); msi->m_buffer = (const u_char *)(buffer); msi->m_bufferLen = len; msi->m_hdr = parse_header(buffer); msi->m_sectorSize = 1 << msi->m_hdr->sectorShift;; msi->m_minisectorSize = 1 << msi->m_hdr->miniSectorShift; msi->m_miniStreamStartSector = 0; if (msi->m_bufferLen < sizeof *(msi->m_hdr) || memcmp(msi->m_hdr->signature, msi_magic, sizeof msi_magic)) { printf("Wrong file format\n"); return NULL; /* FAILED */ } msi->m_sectorSize = msi->m_hdr->majorVersion == 3 ? 512 : 4096; /* The file must contains at least 3 sectors */ if (msi->m_bufferLen < msi->m_sectorSize * 3) { printf("The file must contains at least 3 sectors\n"); return NULL; /* FAILED */ } root = msi_root_entry_get(msi); if (root == NULL) { printf("File corrupted\n"); return NULL; /* FAILED */ } msi->m_miniStreamStartSector = root->startSectorLocation; OPENSSL_free(root); return msi; } MSI_FILE_HDR *msi_header_get(MSI_FILE *msi) { return msi->m_hdr; } /* Recursively parse MSI_DIRENT struct */ MSI_DIRENT *msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent) { MSI_DIRENT *dirent; if (!entry) { return NULL; } dirent = (MSI_DIRENT *)OPENSSL_malloc(sizeof(MSI_DIRENT)); memcpy(dirent->name, entry->name, entry->nameLen); dirent->nameLen = entry->nameLen; dirent->type = entry->type; dirent->entry = entry; dirent->children = sk_MSI_DIRENT_new_null(); if (parent != NULL) { sk_MSI_DIRENT_push(parent->children, dirent); } /* NOTE : These links are a tree, not a linked list */ msi_dirent_new(msi, get_entry(msi, entry->leftSiblingID), parent); msi_dirent_new(msi, get_entry(msi, entry->rightSiblingID), parent); if (entry->type != DIR_STREAM) { msi_dirent_new(msi, get_entry(msi, entry->childID), dirent); } return dirent; } /* Return DigitalSignature and MsiDigitalSignatureEx */ MSI_ENTRY *msi_signatures_get(MSI_DIRENT *dirent, MSI_ENTRY **dse) { int i; MSI_ENTRY *ds = NULL; for (i = 0; i < sk_MSI_DIRENT_num(dirent->children); i++) { MSI_DIRENT *child = sk_MSI_DIRENT_value(dirent->children, i); if (!memcmp(child->name, digital_signature, MIN(child->nameLen, sizeof digital_signature))) { ds = child->entry; } else if (dse && !memcmp(child->name, digital_signature_ex, MIN(child->nameLen, sizeof digital_signature_ex))) { *dse = child->entry; } else { continue; } } return ds; } void msi_file_free(MSI_FILE *msi) { if (!msi) return; OPENSSL_free(msi->m_hdr); OPENSSL_free(msi); } /* Recursively free MSI_DIRENT struct */ void msi_dirent_free(MSI_DIRENT *dirent) { if (!dirent) return; sk_MSI_DIRENT_pop_free(dirent->children, msi_dirent_free); OPENSSL_free(dirent->entry); OPENSSL_free(dirent); } /* Sorted list of MSI streams in this order is needed for hashing */ static int dirent_cmp_hash(const MSI_DIRENT *const *a, const MSI_DIRENT *const *b) { const MSI_DIRENT *dirent_a = *a; const MSI_DIRENT *dirent_b = *b; int diff = memcmp(dirent_a->name, dirent_b->name, MIN(dirent_a->nameLen, dirent_b->nameLen)); /* apparently the longer wins */ if (diff == 0) { return dirent_a->nameLen > dirent_b->nameLen ? -1 : 1; } return diff; } /* Sorting relationship for directory entries, the left sibling MUST always be less than the right sibling */ static int dirent_cmp_tree(const MSI_DIRENT *const *a, const MSI_DIRENT *const *b) { const MSI_DIRENT *dirent_a = *a; const MSI_DIRENT *dirent_b = *b; uint16_t codepoint_a, codepoint_b; int i; if (dirent_a->nameLen != dirent_b->nameLen) { return dirent_a->nameLen < dirent_b->nameLen ? -1 : 1; } for (i=0; inameLen-2; i=i+2) { codepoint_a = GET_UINT16_LE(dirent_a->name + i); codepoint_b = GET_UINT16_LE(dirent_b->name + i); if (codepoint_a != codepoint_b) { return codepoint_a < codepoint_b ? -1 : 1; } } return 0; } /* * Calculate the pre-hash used for 'MsiDigitalSignatureEx' * signatures in MSI files. The pre-hash hashes only metadata (file names, * file sizes, creation times and modification times), whereas the basic * 'DigitalSignature' MSI signature only hashes file content. * * The hash is written to the hash BIO. */ /* Hash a MSI stream's extended metadata */ static void prehash_metadata(MSI_ENTRY *entry, BIO *hash) { if (entry->type != DIR_ROOT) { BIO_write(hash, entry->name, entry->nameLen - 2); } if (entry->type != DIR_STREAM) { BIO_write(hash, entry->clsid, sizeof entry->clsid); } else { BIO_write(hash, entry->size, (sizeof entry->size)/2); } BIO_write(hash, entry->stateBits, sizeof entry->stateBits); if (entry->type != DIR_ROOT) { BIO_write(hash, entry->creationTime, sizeof entry->creationTime); BIO_write(hash, entry->modifiedTime, sizeof entry->modifiedTime); } } /* Recursively hash a MSI directory's extended metadata */ int msi_prehash_dir(MSI_DIRENT *dirent, BIO *hash, int is_root) { int i, ret = 0; STACK_OF(MSI_DIRENT) *children = sk_MSI_DIRENT_dup(dirent->children); if (dirent == NULL) { goto out; } prehash_metadata(dirent->entry, hash); sk_MSI_DIRENT_set_cmp_func(children, &dirent_cmp_hash); sk_MSI_DIRENT_sort(children); for (i = 0; i < sk_MSI_DIRENT_num(children); i++) { MSI_DIRENT *child = sk_MSI_DIRENT_value(children, i); if (is_root && (!memcmp(child->name, digital_signature, MIN(child->nameLen, sizeof digital_signature)) || !memcmp(child->name, digital_signature_ex, MIN(child->nameLen, sizeof digital_signature_ex)))) { continue; } if (child->type == DIR_STREAM) { prehash_metadata(child->entry, hash); } if (child->type == DIR_STORAGE) { if (!msi_prehash_dir(child, hash, 0)) { goto out; } } } ret = 1; /* OK */ out: sk_MSI_DIRENT_free(children); return ret; } /* Recursively hash a MSI directory (storage) */ int msi_hash_dir(MSI_FILE *msi, MSI_DIRENT *dirent, BIO *hash, int is_root) { int i, ret = 0; STACK_OF(MSI_DIRENT) *children = sk_MSI_DIRENT_dup(dirent->children); sk_MSI_DIRENT_set_cmp_func(children, &dirent_cmp_hash); sk_MSI_DIRENT_sort(children); for (i = 0; i < sk_MSI_DIRENT_num(children); i++) { MSI_DIRENT *child = sk_MSI_DIRENT_value(children, i); if (is_root && (!memcmp(child->name, digital_signature, MIN(child->nameLen, sizeof digital_signature)) || !memcmp(child->name, digital_signature_ex, MIN(child->nameLen, sizeof digital_signature_ex)))) { continue; } if (child->type == DIR_STREAM) { char *indata; uint32_t inlen = GET_UINT32_LE(child->entry->size); if (inlen == 0) { continue; } indata = (char *)OPENSSL_malloc(inlen); if (!msi_file_read(msi, child->entry, 0, indata, inlen)) { printf("Read stream data error\n\n"); goto out; } BIO_write(hash, indata, inlen); OPENSSL_free(indata); } if (child->type == DIR_STORAGE) { if (!msi_hash_dir(msi, child, hash, 0)) { goto out; } } } BIO_write(hash, dirent->entry->clsid, sizeof dirent->entry->clsid); ret = 1; /* OK */ out: sk_MSI_DIRENT_free(children); return ret; } /* Compute a simple sha1/sha256 message digest of the MSI file */ void msi_calc_digest(char *indata, const EVP_MD *md, u_char *mdbuf, size_t fileend) { BIO *bio = NULL; EVP_MD_CTX *mdctx; size_t n; bio = BIO_new_mem_buf(indata, fileend); mdctx = EVP_MD_CTX_new(); EVP_DigestInit(mdctx, md); memset(mdbuf, 0, EVP_MAX_MD_SIZE); (void)BIO_seek(bio, 0); n = 0; while (n < fileend) { int l; static u_char bfb[16*1024*1024]; size_t want = fileend - n; if (want > sizeof bfb) want = sizeof bfb; l = BIO_read(bio, bfb, want); if (l <= 0) break; EVP_DigestUpdate(mdctx, bfb, l); n += l; } EVP_DigestFinal(mdctx, mdbuf, NULL); EVP_MD_CTX_free(mdctx); BIO_free(bio); } static void ministream_append(MSI_OUT *out, char *buf, int len) { int needSectors = (len + out->sectorSize - 1) / out->sectorSize; if (out->miniStreamLen + len >= out->ministreamsMemallocCount * out->sectorSize) { out->ministreamsMemallocCount += needSectors; out->ministream = OPENSSL_realloc(out->ministream, out->ministreamsMemallocCount * out->sectorSize); } memcpy(out->ministream + out->miniStreamLen, buf, len); out->miniStreamLen += len; } static void minifat_append(MSI_OUT *out, char *buf, int len) { if (out->minifatLen == out->minifatMemallocCount * out->sectorSize) { out->minifatMemallocCount += 1; out->minifat = OPENSSL_realloc(out->minifat, out->minifatMemallocCount * out->sectorSize); } memcpy(out->minifat + out->minifatLen, buf, len); out->minifatLen += len; } static void fat_append(MSI_OUT *out, char *buf, int len) { if (out->fatLen == out->fatMemallocCount * out->sectorSize) { out->fatMemallocCount += 1; out->fat = OPENSSL_realloc(out->fat, out->fatMemallocCount * out->sectorSize); } memcpy(out->fat + out->fatLen, buf, len); out->fatLen += len; } int msi_dirent_delete(MSI_DIRENT *dirent, const u_char *name, uint16_t nameLen) { int i; for (i = 0; i < sk_MSI_DIRENT_num(dirent->children); i++) { MSI_DIRENT *child = sk_MSI_DIRENT_value(dirent->children, i); if (memcmp(child->name, name, MIN(child->nameLen, nameLen))) { continue; } if (child->type != DIR_STREAM) { printf("Can't delete or replace storages\n"); return 0; /* FAILED */ } sk_MSI_DIRENT_delete(dirent->children, i); msi_dirent_free(child); } return 1; /* OK */ } static MSI_DIRENT *dirent_add(const u_char *name, uint16_t nameLen) { MSI_DIRENT *dirent = (MSI_DIRENT *)OPENSSL_malloc(sizeof(MSI_DIRENT)); MSI_ENTRY *entry = (MSI_ENTRY *)OPENSSL_malloc(sizeof(MSI_ENTRY)); memcpy(dirent->name, name, nameLen); dirent->nameLen = nameLen; dirent->type = DIR_STREAM; dirent->children = sk_MSI_DIRENT_new_null(); memcpy(entry->name, name, nameLen); entry->nameLen = nameLen; entry->type = DIR_STREAM; entry->colorFlag = BLACK_COLOR; /* make everything black */ entry->leftSiblingID = NOSTREAM; entry->rightSiblingID = NOSTREAM; entry->childID = NOSTREAM; memset(entry->clsid, 0, 16); memset(entry->stateBits, 0, 4); memset(entry->creationTime, 0, 8); memset(entry->modifiedTime, 0, 8); entry->startSectorLocation = NOSTREAM; memset(entry->size, 0, 8); dirent->entry = entry; return dirent; } static int dirent_insert(MSI_DIRENT *dirent, const u_char *name, uint16_t nameLen) { MSI_DIRENT *new_dirent; if (!msi_dirent_delete(dirent, name, nameLen)) { return 0; /* FAILED */ } /* create new dirent */ new_dirent = dirent_add(name, nameLen); sk_MSI_DIRENT_push(dirent->children, new_dirent); return 1; /* OK */ } static int signature_insert(MSI_DIRENT *dirent, int len_msiex) { if (len_msiex > 0) { if (!dirent_insert(dirent, digital_signature_ex, sizeof digital_signature_ex)) { return 0; /* FAILED */ } } else { if (!msi_dirent_delete(dirent, digital_signature_ex, sizeof digital_signature_ex)) { return 0; /* FAILED */ } } if (!dirent_insert(dirent, digital_signature, sizeof digital_signature)) { return 0; /* FAILED */ } return 1; /* OK */ } static int stream_read(MSI_FILE *msi, MSI_ENTRY *entry, u_char *p_msi, int len_msi, u_char *p_msiex, int len_msiex, char **indata, int inlen, int is_root) { if (is_root && !memcmp(entry->name, digital_signature, sizeof digital_signature)) { *indata = (char *)p_msi; inlen = len_msi; } else if (is_root && !memcmp(entry->name, digital_signature_ex, sizeof digital_signature_ex)) { *indata = (char *)p_msiex; inlen = len_msiex; } else { if (!msi_file_read(msi, entry, 0, *indata, inlen)) { printf("Read stream data error\n"); return 0; /* FAILED */ } } return inlen; } /* Recursively handle data from MSI_DIRENT struct */ static int stream_handle(MSI_FILE *msi, MSI_DIRENT *dirent, u_char *p_msi, int len_msi, u_char *p_msiex, int len_msiex, BIO *outdata, MSI_OUT *out, int is_root) { int i; if (dirent->type == DIR_ROOT) { if (len_msi > 0 && !signature_insert(dirent, len_msiex)) { printf("Insert new signature failed\n"); return 0; /* FAILED */ } out->ministreamsMemallocCount = (GET_UINT32_LE(dirent->entry->size) + out->sectorSize - 1)/out->sectorSize; out->ministream = OPENSSL_malloc(out->ministreamsMemallocCount * out->sectorSize); } for (i = 0; i < sk_MSI_DIRENT_num(dirent->children); i++) { MSI_DIRENT *child = sk_MSI_DIRENT_value(dirent->children, i); if (child->type == DIR_STORAGE) { if (!stream_handle(msi, child, NULL, 0, NULL, 0, outdata, out, 0)) { return 0; /* FAILED */ } } else { /* DIR_STREAM */ uint32_t inlen = GET_UINT32_LE(child->entry->size); char *indata = (char *)OPENSSL_malloc(inlen); char buf[MAX_SECTOR_SIZE]; inlen = stream_read(msi, child->entry, p_msi, len_msi, p_msiex, len_msiex, &indata, inlen, is_root); if (inlen == 0) { continue; } /* set the size of the user-defined data if this is a stream object */ PUT_UINT32_LE(inlen, buf); memcpy(child->entry->size, buf, sizeof child->entry->size); if (inlen < MINI_STREAM_CUTOFF_SIZE) { /* set the index into the mini FAT to track the chain of sectors through the mini stream */ child->entry->startSectorLocation = out->miniSectorNum; ministream_append(out, indata, inlen); /* fill to the end with known data, such as all zeroes */ if (inlen % msi->m_minisectorSize > 0) { int remain = msi->m_minisectorSize - inlen % msi->m_minisectorSize; memset(buf, 0, remain); ministream_append(out, buf, remain); } while (inlen > msi->m_minisectorSize) { out->miniSectorNum += 1; PUT_UINT32_LE(out->miniSectorNum, buf); minifat_append(out, buf, 4); inlen -= msi->m_minisectorSize; } PUT_UINT32_LE(ENDOFCHAIN, buf); minifat_append(out, buf, 4); out->miniSectorNum += 1; } else { /* set the first sector location if this is a stream object */ child->entry->startSectorLocation = out->sectorNum; /* stream save */ BIO_write(outdata, indata, inlen); /* fill to the end with known data, such as all zeroes */ if (inlen % out->sectorSize > 0) { int remain = out->sectorSize - inlen % out->sectorSize; memset(buf, 0, remain); BIO_write(outdata, buf, remain); } /* set a sector chain in the FAT */ while (inlen > out->sectorSize) { out->sectorNum += 1; PUT_UINT32_LE(out->sectorNum, buf); fat_append(out, buf, 4); inlen -= out->sectorSize; } PUT_UINT32_LE(ENDOFCHAIN, buf); fat_append(out, buf, 4); out->sectorNum += 1; } OPENSSL_free(indata); } } return 1; /* OK */ } static void ministream_save(MSI_DIRENT *dirent, BIO *outdata, MSI_OUT *out) { char buf[MAX_SECTOR_SIZE]; int remain, i; int ministreamSectorsCount = (out->miniStreamLen + out->sectorSize - 1) / out->sectorSize; /* set the first sector of the mini stream in the entry root object */ dirent->entry->startSectorLocation = out->sectorNum; /* ministream save */ BIO_write(outdata, out->ministream, out->miniStreamLen); OPENSSL_free(out->ministream); /* fill to the end with known data, such as all zeroes */ if (out->miniStreamLen % out->sectorSize > 0) { remain = out->sectorSize - out->miniStreamLen % out->sectorSize; memset(buf, 0, remain); BIO_write(outdata, buf, remain); } /* set a sector chain in the FAT */ for (i=1; isectorNum + i, buf); fat_append(out, buf, 4); } /* mark the end of the mini stream data */ PUT_UINT32_LE(ENDOFCHAIN, buf); fat_append(out, buf, 4); out->sectorNum += ministreamSectorsCount; } static void minifat_save(BIO *outdata, MSI_OUT *out) { char buf[MAX_SECTOR_SIZE]; int i,remain; /* set Mini FAT Starting Sector Location in the header */ if (out->minifatLen == 0) { PUT_UINT32_LE(ENDOFCHAIN, buf); memcpy(out->header + HEADER_MINI_FAT_SECTOR_LOC, buf, 4); return; } PUT_UINT32_LE(out->sectorNum, buf); memcpy(out->header + HEADER_MINI_FAT_SECTOR_LOC, buf, 4); /* minifat save */ BIO_write(outdata, out->minifat, out->minifatLen); /* marks the end of the stream */ PUT_UINT32_LE(ENDOFCHAIN, buf); BIO_write(outdata, buf, 4); out->minifatLen += 4; /* empty unallocated free sectors in the last Mini FAT sector */ if (out->minifatLen % out->sectorSize > 0) { remain = out->sectorSize - out->minifatLen % out->sectorSize; memset(buf, FREESECT, remain); BIO_write(outdata, buf, remain); } /* set a sector chain in the FAT */ out->minifatSectorsCount = (out->minifatLen + out->sectorSize - 1) / out->sectorSize; for (i=1; iminifatSectorsCount; i++) { PUT_UINT32_LE(out->sectorNum + i, buf); fat_append(out, buf, 4); } /* mark the end of the mini FAT chain */ PUT_UINT32_LE(ENDOFCHAIN, buf); fat_append(out, buf, 4); out->sectorNum += out->minifatSectorsCount; } static char *msi_dirent_get(MSI_ENTRY *entry) { char buf[8]; char *data = OPENSSL_malloc(DIRENT_SIZE); /* initialise 128 bytes */ memset(data, 0, DIRENT_SIZE); memcpy(data + DIRENT_NAME, entry->name, entry->nameLen); memset(data + DIRENT_NAME + entry->nameLen, 0, DIRENT_MAX_NAME_SIZE - entry->nameLen); PUT_UINT16_LE(entry->nameLen, buf); memcpy(data + DIRENT_NAME_LEN, buf, 2); PUT_UINT8_LE(entry->type, buf); memcpy(data + DIRENT_TYPE, buf, 1); PUT_UINT8_LE(entry->colorFlag, buf); memcpy(data + DIRENT_COLOUR, buf, 1); PUT_UINT32_LE(entry->leftSiblingID, buf); memcpy(data + DIRENT_LEFT_SIBLING_ID, buf, 4); PUT_UINT32_LE(entry->rightSiblingID, buf); memcpy(data + DIRENT_RIGHT_SIBLING_ID, buf, 4); PUT_UINT32_LE(entry->childID, buf); memcpy(data + DIRENT_CHILD_ID, buf, 4); memcpy(data + DIRENT_CLSID, entry->clsid, 16); memcpy(data + DIRENT_STATE_BITS, entry->stateBits, 4); memcpy(data + DIRENT_CREATE_TIME, entry->creationTime, 8); memcpy(data + DIRENT_MODIFY_TIME, entry->modifiedTime, 8); PUT_UINT32_LE(entry->startSectorLocation, buf); memcpy(data + DIRENT_START_SECTOR_LOC, buf, 4); memcpy(data + DIRENT_FILE_SIZE, entry->size, 4); memset(data + DIRENT_FILE_SIZE + 4, 0, 4); return data; } static char *msi_unused_dirent_get() { char *data = OPENSSL_malloc(DIRENT_SIZE); /* initialise 127 bytes */ memset(data, 0, DIRENT_SIZE); memset(data + DIRENT_LEFT_SIBLING_ID, NOSTREAM, 4); memset(data + DIRENT_RIGHT_SIBLING_ID, NOSTREAM, 4); memset(data + DIRENT_CHILD_ID, NOSTREAM, 4); return data; } static int dirents_save(MSI_DIRENT *dirent, BIO *outdata, MSI_OUT *out, int *streamId, int count, int last) { int i, childenNum; char *entry; STACK_OF(MSI_DIRENT) *children = sk_MSI_DIRENT_dup(dirent->children); sk_MSI_DIRENT_set_cmp_func(children, &dirent_cmp_tree); sk_MSI_DIRENT_sort(children); childenNum = sk_MSI_DIRENT_num(children); /* make everything black */ dirent->entry->colorFlag = BLACK_COLOR; dirent->entry->leftSiblingID = NOSTREAM; if (dirent->type == DIR_STORAGE) { if (last) { dirent->entry->rightSiblingID = NOSTREAM; } else { /* make linked list rather than tree, only use next - right sibling */ count += childenNum; dirent->entry->rightSiblingID = *streamId + count + 1; } } else { /* DIR_ROOT */ dirent->entry->rightSiblingID = NOSTREAM; } dirent->entry->childID = *streamId + 1; entry = msi_dirent_get(dirent->entry); BIO_write(outdata, entry, DIRENT_SIZE); OPENSSL_free(entry); out->dirtreeLen += DIRENT_SIZE; for (i = 0; i < childenNum; i++) { MSI_DIRENT *child = sk_MSI_DIRENT_value(children, i); int last = i == childenNum - 1 ? 1 : 0; *streamId += 1; if (child->type == DIR_STORAGE) { count += dirents_save(child, outdata, out, streamId, count, last); } else { /* DIR_STREAM */ count = 0; child->entry->colorFlag = BLACK_COLOR; child->entry->leftSiblingID = NOSTREAM; if (last) { child->entry->rightSiblingID = NOSTREAM; } else { child->entry->rightSiblingID = *streamId + 1; } entry = msi_dirent_get(child->entry); BIO_write(outdata, entry, DIRENT_SIZE); OPENSSL_free(entry); out->dirtreeLen += DIRENT_SIZE; } } sk_MSI_DIRENT_free(children); return count; } static void dirtree_save(MSI_DIRENT *dirent, BIO *outdata, MSI_OUT *out) { char buf[MAX_SECTOR_SIZE]; char *unused_entry; int i, remain; int streamId = 0; /* set Directory Starting Sector Location in the header */ PUT_UINT32_LE(out->sectorNum, buf); memcpy(out->header + HEADER_DIR_SECTOR_LOC, buf, 4); /* set the size of the mini stream in the root object */ if (dirent->type == DIR_ROOT) { PUT_UINT32_LE(out->miniStreamLen, buf); memcpy(dirent->entry->size, buf, sizeof dirent->entry->size); } /* sort and save all directory entries */ dirents_save(dirent, outdata, out, &streamId, 0, 0); /* set free (unused) directory entries */ unused_entry = msi_unused_dirent_get(); if (out->dirtreeLen % out->sectorSize > 0) { remain = out->sectorSize - out->dirtreeLen % out->sectorSize; while (remain > 0) { BIO_write(outdata, unused_entry, DIRENT_SIZE); remain -= DIRENT_SIZE; } } OPENSSL_free(unused_entry); /* set a sector chain in the FAT */ out->dirtreeSectorsCount = (out->dirtreeLen + out->sectorSize - 1) / out->sectorSize; for (i=1; idirtreeSectorsCount; i++) { PUT_UINT32_LE(out->sectorNum + i, buf); fat_append(out, buf, 4); } /* mark the end of the directory chain */ PUT_UINT32_LE(ENDOFCHAIN, buf); fat_append(out, buf, 4); out->sectorNum += out->dirtreeSectorsCount; } static int fat_save(BIO *outdata, MSI_OUT *out) { char buf[MAX_SECTOR_SIZE]; int i, remain; remain = (out->fatLen + out->sectorSize - 1) / out->sectorSize; out->fatSectorsCount = (out->fatLen + remain * 4 + out->sectorSize - 1) / out->sectorSize; /* mark FAT sectors in the FAT chain */ PUT_UINT32_LE(FATSECT, buf); for (i=0; ifatSectorsCount; i++) { fat_append(out, buf, 4); } /* set 109 FAT sectors in HEADER_DIFAT table */ for (i=0; ifatSectorsCount, DIFAT_IN_HEADER); i++) { PUT_UINT32_LE(out->sectorNum + i, buf); memcpy(out->header + HEADER_DIFAT + i * 4, buf, 4); } out->sectorNum += out->fatSectorsCount; if (out->fatSectorsCount > DIFAT_IN_HEADER) { /* TODO set FAT sectors in DIFAT sector */ printf("DIFAT sectors are not supported\n"); return 0; /* FAILED */ } /* empty unallocated free sectors in the last FAT sector */ if (out->fatLen % out->sectorSize > 0) { remain = out->sectorSize - out->fatLen % out->sectorSize; memset(buf, FREESECT, remain); fat_append(out, buf, remain); } BIO_write(outdata, out->fat, out->fatLen); return 1; /* OK */ } static void header_save(BIO *outdata, MSI_OUT *out) { char buf[MAX_SECTOR_SIZE]; int remain; /* set Number of FAT sectors in the header */ PUT_UINT32_LE(out->fatSectorsCount, buf); memcpy(out->header + HEADER_FAT_SECTORS_NUM, buf, 4); /* set Number of Mini FAT sectors in the header */ PUT_UINT32_LE(out->minifatSectorsCount, buf); memcpy(out->header + HEADER_MINI_FAT_SECTORS_NUM, buf, 4); /* set Number of Directory Sectors in the header if Major Version is 4 */ if (out->sectorSize == 4096) { PUT_UINT32_LE(out->dirtreeSectorsCount, buf); memcpy(out->header + HEADER_DIR_SECTORS_NUM, buf, 4); } (void)BIO_seek(outdata, 0); BIO_write(outdata, out->header, HEADER_SIZE); remain = out->sectorSize - HEADER_SIZE; memset(buf, 0, remain); BIO_write(outdata, buf, remain); } static char *header_new(MSI_FILE_HDR *hdr, MSI_OUT *out) { int i; char buf[4]; char *data = OPENSSL_malloc(HEADER_SIZE); static u_char dead_food[] = { 0xde, 0xad, 0xf0, 0x0d }; /* initialise 512 bytes */ memset(data, 0, HEADER_SIZE); memcpy(data + HEADER_SIGNATURE, msi_magic, sizeof msi_magic); memset(data + HEADER_CLSID, 0, 16); PUT_UINT16_LE(hdr->minorVersion, buf); memcpy(data + HEADER_MINOR_VER, buf, 2); if (out->sectorSize == 4096) { PUT_UINT16_LE(0x0004, buf); } else { PUT_UINT16_LE(0x0003, buf); } memcpy(data + HEADER_MAJOR_VER, buf, 2); PUT_UINT16_LE(hdr->byteOrder, buf); memcpy(data + HEADER_BYTE_ORDER, buf, 2); PUT_UINT16_LE(hdr->sectorShift, buf); if (out->sectorSize == 4096) { PUT_UINT16_LE(0x000C, buf); } else { PUT_UINT16_LE(0x0009, buf); } memcpy(data + HEADER_SECTOR_SHIFT, buf, 2); PUT_UINT16_LE(hdr->miniSectorShift, buf); memcpy(data + HEADER_MINI_SECTOR_SHIFT, buf, 2); memset(data + RESERVED, 0, 6); memset(data + HEADER_DIR_SECTORS_NUM, 0, 4); /* not used for version 3 */ memcpy(data + HEADER_FAT_SECTORS_NUM, dead_food, 4); memcpy(data + HEADER_DIR_SECTOR_LOC, dead_food, 4); memset(data + HEADER_TRANSACTION, 0, 4); /* reserved */ PUT_UINT32_LE(MINI_STREAM_CUTOFF_SIZE, buf); memcpy(data + HEADER_MINI_STREAM_CUTOFF, buf, 4); memcpy(data + HEADER_MINI_FAT_SECTOR_LOC, dead_food, 4); memcpy(data + HEADER_MINI_FAT_SECTORS_NUM, dead_food, 4); PUT_UINT32_LE(ENDOFCHAIN, buf); memcpy(data + HEADER_DIFAT_SECTOR_LOC, buf, 4); memset(data + HEADER_DIFAT_SECTORS_NUM, 0, 4); /* no DIFAT */ memcpy(data + HEADER_DIFAT, dead_food, 4); /* sector number for FAT */ for (i = 1; i < DIFAT_IN_HEADER; i++) { memset(data + HEADER_DIFAT + 4*i, FREESECT, 4); /* free FAT sectors */ } return data; } static int msiout_set(MSI_FILE *msi, int len_msi, int len_msiex, MSI_OUT *out) { MSI_FILE_HDR *hdr = msi_header_get(msi); int msi_size, msiex_size; out->sectorSize = msi->m_sectorSize; if (len_msi <= MINI_STREAM_CUTOFF_SIZE) { msi_size = ((len_msi + msi->m_minisectorSize - 1) / msi->m_minisectorSize) * msi->m_minisectorSize; } else { msi_size = ((len_msi + msi->m_sectorSize - 1) / msi->m_sectorSize) * msi->m_sectorSize; } msiex_size = ((len_msiex + msi->m_minisectorSize - 1) / msi->m_minisectorSize) * msi->m_minisectorSize; /* * no DIFAT sectors will be needed in a file that is smaller than * 6,813 MB (version 3 files), respectively 436,004 MB (version 4 files) */ if (msi->m_bufferLen + msi_size + msiex_size > 7143936) { out->sectorSize = 4096; } if (msi->m_bufferLen + msi_size + msiex_size > 457183232) { printf("DIFAT sectors are not supported\n"); return 0;/* FAILED */ } out->header = header_new(hdr, out); out->minifatMemallocCount = hdr->numMiniFATSector; out->fatMemallocCount = hdr->numFATSector; out->ministream = NULL; out->minifat = OPENSSL_malloc(out->minifatMemallocCount * out->sectorSize); out->fat = OPENSSL_malloc(out->fatMemallocCount * out->sectorSize); out->miniSectorNum = 0; out->sectorNum = 0; return 1; /* OK */ } int msi_file_write(MSI_FILE *msi, MSI_DIRENT *dirent, u_char *p_msi, int len_msi, u_char *p_msiex, int len_msiex, BIO *outdata) { MSI_OUT out; int ret = 0; memset(&out, 0, sizeof(MSI_OUT)); if (!msiout_set(msi, len_msi, len_msiex, &out)) { goto out; /* FAILED */ } (void)BIO_seek(outdata, out.sectorSize); if (!stream_handle(msi, dirent, p_msi, len_msi, p_msiex, len_msiex, outdata, &out, 1)) { goto out; /* FAILED */ } ministream_save(dirent, outdata, &out); minifat_save(outdata, &out); dirtree_save(dirent, outdata, &out); if (!fat_save(outdata, &out)) { goto out; /* FAILED */ } header_save(outdata, &out); ret = 1; /* OK */ out: OPENSSL_free(out.header); OPENSSL_free(out.fat); OPENSSL_free(out.minifat); return ret; } osslsigncode-2.2/msi.h000066400000000000000000000157631410626701100150240ustar00rootroot00000000000000/* * MSI file support library * * Copyright (C) 2021 Michał Trojnara * Author: Małgorzata Olszówka * * Reference specifications: * http://en.wikipedia.org/wiki/Compound_File_Binary_Format * https://msdn.microsoft.com/en-us/library/dd942138.aspx * https://github.com/microsoft/compoundfilereader */ #include #include #include #include #include #define MAXREGSECT 0xfffffffa /* maximum regular sector number */ #define DIFSECT 0xfffffffc /* specifies a DIFAT sector in the FAT */ #define FATSECT 0xfffffffd /* specifies a FAT sector in the FAT */ #define ENDOFCHAIN 0xfffffffe /* end of a linked chain of sectors */ #define NOSTREAM 0xffffffff /* terminator or empty pointer */ #define FREESECT 0xffffffff /* empty unallocated free sectors */ #define DIR_UNKNOWN 0 #define DIR_STORAGE 1 #define DIR_STREAM 2 #define DIR_ROOT 5 #define RED_COLOR 0 #define BLACK_COLOR 1 #define DIFAT_IN_HEADER 109 #define MINI_STREAM_CUTOFF_SIZE 0x00001000 /* 4096 bytes */ #define HEADER_SIZE 0x200 /* 512 bytes, independent of sector size */ #define MAX_SECTOR_SIZE 0x1000 /* 4096 bytes */ #define HEADER_SIGNATURE 0x00 /* 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 */ #define HEADER_CLSID 0x08 /* reserved and unused */ #define HEADER_MINOR_VER 0x18 /* SHOULD be set to 0x003E */ #define HEADER_MAJOR_VER 0x1a /* MUST be set to either 0x0003 (version 3) or 0x0004 (version 4) */ #define HEADER_BYTE_ORDER 0x1c /* 0xfe 0xff == Intel Little Endian */ #define HEADER_SECTOR_SHIFT 0x1e /* MUST be set to 0x0009, or 0x000c */ #define HEADER_MINI_SECTOR_SHIFT 0x20 /* MUST be set to 0x0006 */ #define RESERVED 0x22 /* reserved and unused */ #define HEADER_DIR_SECTORS_NUM 0x28 #define HEADER_FAT_SECTORS_NUM 0x2c #define HEADER_DIR_SECTOR_LOC 0x30 #define HEADER_TRANSACTION 0x34 #define HEADER_MINI_STREAM_CUTOFF 0x38 /* 4096 bytes */ #define HEADER_MINI_FAT_SECTOR_LOC 0x3c #define HEADER_MINI_FAT_SECTORS_NUM 0x40 #define HEADER_DIFAT_SECTOR_LOC 0x44 #define HEADER_DIFAT_SECTORS_NUM 0x48 #define HEADER_DIFAT 0x4c #define DIRENT_SIZE 0x80 /* 128 bytes */ #define DIRENT_MAX_NAME_SIZE 0x40 /* 64 bytes */ #define DIRENT_NAME 0x00 #define DIRENT_NAME_LEN 0x40 /* length in bytes incl 0 terminator */ #define DIRENT_TYPE 0x42 #define DIRENT_COLOUR 0x43 #define DIRENT_LEFT_SIBLING_ID 0x44 #define DIRENT_RIGHT_SIBLING_ID 0x48 #define DIRENT_CHILD_ID 0x4c #define DIRENT_CLSID 0x50 #define DIRENT_STATE_BITS 0x60 #define DIRENT_CREATE_TIME 0x64 #define DIRENT_MODIFY_TIME 0x6c #define DIRENT_START_SECTOR_LOC 0x74 #define DIRENT_FILE_SIZE 0x78 #define GET_UINT8_LE(p) ((u_char*)(p))[0] #define GET_UINT16_LE(p) (((u_char*)(p))[0] | (((u_char*)(p))[1]<<8)) #define GET_UINT32_LE(p) (((u_char*)(p))[0] | (((u_char*)(p))[1]<<8) | \ (((u_char*)(p))[2]<<16) | (((u_char*)(p))[3]<<24)) #define PUT_UINT8_LE(i,p) \ ((u_char*)(p))[0] = (i) & 0xff; #define PUT_UINT16_LE(i,p) \ ((u_char*)(p))[0] = (i) & 0xff; \ ((u_char*)(p))[1] = ((i)>>8) & 0xff #define PUT_UINT32_LE(i,p) \ ((u_char*)(p))[0] = (i) & 0xff; \ ((u_char*)(p))[1] = ((i)>>8) & 0xff; \ ((u_char*)(p))[2] = ((i)>>16) & 0xff; \ ((u_char*)(p))[3] = ((i)>>24) & 0xff typedef unsigned char u_char; typedef struct { u_char signature[8]; /* 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 */ u_char unused_clsid[16]; /* reserved and unused */ uint16_t minorVersion; uint16_t majorVersion; uint16_t byteOrder; uint16_t sectorShift; /* power of 2 */ uint16_t miniSectorShift; /* power of 2 */ u_char reserved[6]; /* reserved and unused */ uint32_t numDirectorySector; uint32_t numFATSector; uint32_t firstDirectorySectorLocation; uint32_t transactionSignatureNumber; /* reserved */ uint32_t miniStreamCutoffSize; uint32_t firstMiniFATSectorLocation; uint32_t numMiniFATSector; uint32_t firstDIFATSectorLocation; uint32_t numDIFATSector; uint32_t headerDIFAT[DIFAT_IN_HEADER]; } MSI_FILE_HDR; typedef struct { u_char name[DIRENT_MAX_NAME_SIZE]; uint16_t nameLen; uint8_t type; uint8_t colorFlag; uint32_t leftSiblingID; uint32_t rightSiblingID; uint32_t childID; u_char clsid[16]; u_char stateBits[4]; u_char creationTime[8]; u_char modifiedTime[8]; uint32_t startSectorLocation; u_char size[8]; } MSI_ENTRY; typedef struct { u_char name[DIRENT_MAX_NAME_SIZE]; uint16_t nameLen; uint8_t type; MSI_ENTRY *entry; STACK_OF(MSI_DIRENT) *children; } MSI_DIRENT; DEFINE_STACK_OF(MSI_DIRENT) typedef struct { const u_char *m_buffer; size_t m_bufferLen; MSI_FILE_HDR *m_hdr; size_t m_sectorSize; size_t m_minisectorSize; size_t m_miniStreamStartSector; } MSI_FILE; typedef struct { char *header; char *ministream; char *minifat; char *fat; uint32_t dirtreeLen; uint32_t miniStreamLen; uint32_t minifatLen; uint32_t fatLen; int ministreamsMemallocCount; int minifatMemallocCount; int fatMemallocCount; int dirtreeSectorsCount; int minifatSectorsCount; int fatSectorsCount; int miniSectorNum; int sectorNum; size_t sectorSize; } MSI_OUT; static u_char msi_magic[] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; static const u_char digital_signature[] = { 0x05, 0x00, 0x44, 0x00, 0x69, 0x00, 0x67, 0x00, 0x69, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x53, 0x00, 0x69, 0x00, 0x67, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x00, 0x00 }; static const u_char digital_signature_ex[] = { 0x05, 0x00, 0x4D, 0x00, 0x73, 0x00, 0x69, 0x00, 0x44, 0x00, 0x69, 0x00, 0x67, 0x00, 0x69, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x53, 0x00, 0x69, 0x00, 0x67, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x45, 0x00, 0x78, 0x00, 0x00, 0x00 }; int msi_file_read(MSI_FILE *msi, MSI_ENTRY *entry, size_t offset, char *buffer, size_t len); MSI_FILE *msi_file_new(char *buffer, size_t len); void msi_file_free(MSI_FILE *msi); MSI_ENTRY *msi_root_entry_get(MSI_FILE *msi); MSI_DIRENT *msi_dirent_new(MSI_FILE *msi, MSI_ENTRY *entry, MSI_DIRENT *parent); MSI_ENTRY *msi_signatures_get(MSI_DIRENT *dirent, MSI_ENTRY **dse); void msi_dirent_free(MSI_DIRENT *dirent); MSI_FILE_HDR *msi_header_get(MSI_FILE *msi); int msi_prehash_dir(MSI_DIRENT *dirent, BIO *hash, int is_root); int msi_hash_dir(MSI_FILE *msi, MSI_DIRENT *dirent, BIO *hash, int is_root); void msi_calc_digest(char *indata, const EVP_MD *md, u_char *mdbuf, size_t fileend); int msi_dirent_delete(MSI_DIRENT *dirent, const u_char *name, uint16_t nameLen); int msi_file_write(MSI_FILE *msi, MSI_DIRENT *dirent, u_char *p, int len, u_char *p_msiex, int len_msiex, BIO *outdata); osslsigncode-2.2/osslsigncode.c000066400000000000000000005455501410626701100167250ustar00rootroot00000000000000/* OpenSSL based Authenticode signing for PE/MSI/Java CAB files. Copyright (C) 2005-2015 Per Allansson Copyright (C) 2018-2021 Michał Trojnara 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 3 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, see . In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library under certain conditions as described in each individual source file, and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than OpenSSL. If you modify file(s) with this exception, you may extend this exception to your version of the file(s), but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. */ /* Implemented with good help from: * Peter Gutmann's analysis of Authenticode: https://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt * MS CAB SDK documentation https://docs.microsoft.com/en-us/previous-versions/ms974336(v=msdn.10) * MS PE/COFF documentation https://docs.microsoft.com/en-us/windows/win32/debug/pe-format * MS Windows Authenticode PE Signature Format http://msdn.microsoft.com/en-US/windows/hardware/gg463183 (Although the part of how the actual checksumming is done is not how it is done inside Windows. The end result is however the same on all "normal" PE files.) * tail -c, tcpdump, mimencode & openssl asn1parse :) */ #define OPENSSL_API_COMPAT 0x10100000L #define OPENSSL_NO_DEPRECATED #ifdef __MINGW32__ #define HAVE_WINDOWS_H #endif /* __MINGW32__ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #ifdef HAVE_WINDOWS_H #define NOCRYPT #define WIN32_LEAN_AND_MEAN #include #endif /* HAVE_WINDOWS_H */ #include #include #include #ifndef _WIN32 #include #endif /* _WIN32 */ #include #include #include #include #include #include #ifndef _WIN32 #ifdef HAVE_SYS_MMAN_H #include #endif /* HAVE_SYS_MMAN_H */ #ifdef HAVE_TERMIOS_H #include #endif /* HAVE_TERMIOS_H */ #endif /* _WIN32 */ #include #include #include #include #include /* X509_PURPOSE */ #include #include #include #include #include #include #include #ifndef OPENSSL_NO_ENGINE #include #endif /* OPENSSL_NO_ENGINE */ #include "msi.h" #ifdef ENABLE_CURL #ifdef __CYGWIN__ #ifndef SOCKET #define SOCKET UINT_PTR #endif /* SOCKET */ #endif /* __CYGWIN__ */ #include #define MAX_TS_SERVERS 256 #endif /* ENABLE_CURL */ #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #if defined (HAVE_TERMIOS_H) || defined (HAVE_GETPASS) #define PROVIDE_ASKPASS 1 #endif #ifdef _WIN32 #define FILE_CREATE_MODE "w+b" #else #define FILE_CREATE_MODE "w+bx" #endif /* Microsoft OID Authenticode */ #define SPC_INDIRECT_DATA_OBJID "1.3.6.1.4.1.311.2.1.4" #define SPC_STATEMENT_TYPE_OBJID "1.3.6.1.4.1.311.2.1.11" #define SPC_SP_OPUS_INFO_OBJID "1.3.6.1.4.1.311.2.1.12" #define SPC_PE_IMAGE_DATA_OBJID "1.3.6.1.4.1.311.2.1.15" #define SPC_CAB_DATA_OBJID "1.3.6.1.4.1.311.2.1.25" #define SPC_SIPINFO_OBJID "1.3.6.1.4.1.311.2.1.30" #define SPC_PE_IMAGE_PAGE_HASHES_V1 "1.3.6.1.4.1.311.2.3.1" /* SHA1 */ #define SPC_PE_IMAGE_PAGE_HASHES_V2 "1.3.6.1.4.1.311.2.3.2" /* SHA256 */ #define SPC_NESTED_SIGNATURE_OBJID "1.3.6.1.4.1.311.2.4.1" /* Microsoft OID Time Stamping */ #define SPC_TIME_STAMP_REQUEST_OBJID "1.3.6.1.4.1.311.3.2.1" #define SPC_RFC3161_OBJID "1.3.6.1.4.1.311.3.3.1" /* Microsoft OID Crypto 2.0 */ #define MS_CTL_OBJID "1.3.6.1.4.1.311.10.1" /* Microsoft OID Microsoft_Java */ #define MS_JAVA_SOMETHING "1.3.6.1.4.1.311.15.1" #define SPC_UNAUTHENTICATED_DATA_BLOB_OBJID "1.3.6.1.4.1.42921.1.2.1" /* Public Key Cryptography Standards PKCS#9 */ #define PKCS9_MESSAGE_DIGEST "1.2.840.113549.1.9.4" #define PKCS9_SIGNING_TIME "1.2.840.113549.1.9.5" #define PKCS9_COUNTER_SIGNATURE "1.2.840.113549.1.9.6" #define WIN_CERT_REVISION_2 0x0200 #define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 /* * FLAG_PREV_CABINET is set if the cabinet file is not the first in a set * of cabinet files. When this bit is set, the szCabinetPrev and szDiskPrev * fields are present in this CFHEADER. */ #define FLAG_PREV_CABINET 0x0001 /* * FLAG_NEXT_CABINET is set if the cabinet file is not the last in a set of * cabinet files. When this bit is set, the szCabinetNext and szDiskNext * fields are present in this CFHEADER. */ #define FLAG_NEXT_CABINET 0x0002 /* * FLAG_RESERVE_PRESENT is set if the cabinet file contains any reserved * fields. When this bit is set, the cbCFHeader, cbCFFolder, and cbCFData * fields are present in this CFHEADER. */ #define FLAG_RESERVE_PRESENT 0x0004 #define INVALID_TIME ((time_t)-1) typedef struct SIGNATURE_st { PKCS7 *p7; int md_nid; ASN1_STRING *digest; time_t signtime; char *url; char *desc; char *purpose; char *level; CMS_ContentInfo *timestamp; time_t time; ASN1_STRING *blob; } SIGNATURE; DEFINE_STACK_OF(SIGNATURE) DECLARE_ASN1_FUNCTIONS(SIGNATURE) typedef struct { char *infile; char *outfile; char *sigfile; char *certfile; char *xcertfile; char *keyfile; char *pvkfile; char *pkcs12file; int output_pkcs7; #ifndef OPENSSL_NO_ENGINE char *p11engine; char *p11module; char *p11cert; #endif /* OPENSSL_NO_ENGINE */ int askpass; char *readpass; char *pass; int comm; int pagehash; char *desc; const EVP_MD *md; char *url; time_t signing_time; #ifdef ENABLE_CURL char *turl[MAX_TS_SERVERS]; int nturl; char *tsurl[MAX_TS_SERVERS]; int ntsurl; char *proxy; int noverifypeer; #endif /* ENABLE_CURL */ int addBlob; int nest; int timestamp_expiration; int verbose; int add_msi_dse; char *catalog; char *cafile; char *crlfile; char *tsa_cafile; char *tsa_crlfile; char *leafhash; int jp; } GLOBAL_OPTIONS; typedef struct { uint32_t header_size; int pe32plus; uint16_t magic; uint32_t pe_checksum; uint32_t nrvas; uint32_t sigpos; uint32_t siglen; size_t fileend; uint16_t flags; } FILE_HEADER; typedef struct { EVP_PKEY *pkey; X509 *cert; STACK_OF(X509) *certs; STACK_OF(X509) *xcerts; STACK_OF(X509_CRL) *crls; } CRYPTO_PARAMS; typedef struct { MSI_FILE *msi; MSI_DIRENT *dirent; unsigned char *p_msiex; int len_msiex; } MSI_PARAMS; /* * ASN.1 definitions (more or less from official MS Authenticode docs) */ typedef struct { int type; union { ASN1_BMPSTRING *unicode; ASN1_IA5STRING *ascii; } value; } SpcString; DECLARE_ASN1_FUNCTIONS(SpcString) ASN1_CHOICE(SpcString) = { ASN1_IMP_OPT(SpcString, value.unicode, ASN1_BMPSTRING, 0), ASN1_IMP_OPT(SpcString, value.ascii, ASN1_IA5STRING, 1) } ASN1_CHOICE_END(SpcString) IMPLEMENT_ASN1_FUNCTIONS(SpcString) typedef struct { ASN1_OCTET_STRING *classId; ASN1_OCTET_STRING *serializedData; } SpcSerializedObject; DECLARE_ASN1_FUNCTIONS(SpcSerializedObject) ASN1_SEQUENCE(SpcSerializedObject) = { ASN1_SIMPLE(SpcSerializedObject, classId, ASN1_OCTET_STRING), ASN1_SIMPLE(SpcSerializedObject, serializedData, ASN1_OCTET_STRING) } ASN1_SEQUENCE_END(SpcSerializedObject) IMPLEMENT_ASN1_FUNCTIONS(SpcSerializedObject) typedef struct { int type; union { ASN1_IA5STRING *url; SpcSerializedObject *moniker; SpcString *file; } value; } SpcLink; DECLARE_ASN1_FUNCTIONS(SpcLink) ASN1_CHOICE(SpcLink) = { ASN1_IMP_OPT(SpcLink, value.url, ASN1_IA5STRING, 0), ASN1_IMP_OPT(SpcLink, value.moniker, SpcSerializedObject, 1), ASN1_EXP_OPT(SpcLink, value.file, SpcString, 2) } ASN1_CHOICE_END(SpcLink) IMPLEMENT_ASN1_FUNCTIONS(SpcLink) typedef struct { SpcString *programName; SpcLink *moreInfo; } SpcSpOpusInfo; DECLARE_ASN1_FUNCTIONS(SpcSpOpusInfo) ASN1_SEQUENCE(SpcSpOpusInfo) = { ASN1_EXP_OPT(SpcSpOpusInfo, programName, SpcString, 0), ASN1_EXP_OPT(SpcSpOpusInfo, moreInfo, SpcLink, 1) } ASN1_SEQUENCE_END(SpcSpOpusInfo) IMPLEMENT_ASN1_FUNCTIONS(SpcSpOpusInfo) typedef struct { ASN1_OBJECT *type; ASN1_TYPE *value; } SpcAttributeTypeAndOptionalValue; DECLARE_ASN1_FUNCTIONS(SpcAttributeTypeAndOptionalValue) ASN1_SEQUENCE(SpcAttributeTypeAndOptionalValue) = { ASN1_SIMPLE(SpcAttributeTypeAndOptionalValue, type, ASN1_OBJECT), ASN1_OPT(SpcAttributeTypeAndOptionalValue, value, ASN1_ANY) } ASN1_SEQUENCE_END(SpcAttributeTypeAndOptionalValue) IMPLEMENT_ASN1_FUNCTIONS(SpcAttributeTypeAndOptionalValue) typedef struct { ASN1_OBJECT *algorithm; ASN1_TYPE *parameters; } AlgorithmIdentifier; DECLARE_ASN1_FUNCTIONS(AlgorithmIdentifier) ASN1_SEQUENCE(AlgorithmIdentifier) = { ASN1_SIMPLE(AlgorithmIdentifier, algorithm, ASN1_OBJECT), ASN1_OPT(AlgorithmIdentifier, parameters, ASN1_ANY) } ASN1_SEQUENCE_END(AlgorithmIdentifier) IMPLEMENT_ASN1_FUNCTIONS(AlgorithmIdentifier) typedef struct { AlgorithmIdentifier *digestAlgorithm; ASN1_OCTET_STRING *digest; } DigestInfo; DECLARE_ASN1_FUNCTIONS(DigestInfo) ASN1_SEQUENCE(DigestInfo) = { ASN1_SIMPLE(DigestInfo, digestAlgorithm, AlgorithmIdentifier), ASN1_SIMPLE(DigestInfo, digest, ASN1_OCTET_STRING) } ASN1_SEQUENCE_END(DigestInfo) IMPLEMENT_ASN1_FUNCTIONS(DigestInfo) typedef struct { SpcAttributeTypeAndOptionalValue *data; DigestInfo *messageDigest; } SpcIndirectDataContent; DECLARE_ASN1_FUNCTIONS(SpcIndirectDataContent) ASN1_SEQUENCE(SpcIndirectDataContent) = { ASN1_SIMPLE(SpcIndirectDataContent, data, SpcAttributeTypeAndOptionalValue), ASN1_SIMPLE(SpcIndirectDataContent, messageDigest, DigestInfo) } ASN1_SEQUENCE_END(SpcIndirectDataContent) IMPLEMENT_ASN1_FUNCTIONS(SpcIndirectDataContent) typedef struct CatalogAuthAttr_st { ASN1_OBJECT *type; ASN1_TYPE *contents; } CatalogAuthAttr; DEFINE_STACK_OF(CatalogAuthAttr) DECLARE_ASN1_FUNCTIONS(CatalogAuthAttr) ASN1_SEQUENCE(CatalogAuthAttr) = { ASN1_SIMPLE(CatalogAuthAttr, type, ASN1_OBJECT), ASN1_OPT(CatalogAuthAttr, contents, ASN1_ANY) } ASN1_SEQUENCE_END(CatalogAuthAttr) IMPLEMENT_ASN1_FUNCTIONS(CatalogAuthAttr) typedef struct { ASN1_OCTET_STRING *digest; STACK_OF(CatalogAuthAttr) *attributes; } CatalogInfo; DEFINE_STACK_OF(CatalogInfo) DECLARE_ASN1_FUNCTIONS(CatalogInfo) ASN1_SEQUENCE(CatalogInfo) = { ASN1_SIMPLE(CatalogInfo, digest, ASN1_OCTET_STRING), ASN1_SET_OF(CatalogInfo, attributes, CatalogAuthAttr) } ASN1_SEQUENCE_END(CatalogInfo) IMPLEMENT_ASN1_FUNCTIONS(CatalogInfo) typedef struct { /* 1.3.6.1.4.1.311.12.1.1 szOID_CATALOG_LIST */ SpcAttributeTypeAndOptionalValue *type; ASN1_OCTET_STRING *identifier; ASN1_UTCTIME *time; /* 1.3.6.1.4.1.311.12.1.2 CatalogVersion = 1 * 1.3.6.1.4.1.311.12.1.3 CatalogVersion = 2 */ SpcAttributeTypeAndOptionalValue *version; STACK_OF(CatalogInfo) *header_attributes; /* 1.3.6.1.4.1.311.12.2.1 CAT_NAMEVALUE_OBJID */ ASN1_TYPE *filename; } MsCtlContent; DECLARE_ASN1_FUNCTIONS(MsCtlContent) ASN1_SEQUENCE(MsCtlContent) = { ASN1_SIMPLE(MsCtlContent, type, SpcAttributeTypeAndOptionalValue), ASN1_SIMPLE(MsCtlContent, identifier, ASN1_OCTET_STRING), ASN1_SIMPLE(MsCtlContent, time, ASN1_UTCTIME), ASN1_SIMPLE(MsCtlContent, version, SpcAttributeTypeAndOptionalValue), ASN1_SEQUENCE_OF(MsCtlContent, header_attributes, CatalogInfo), ASN1_OPT(MsCtlContent, filename, ASN1_ANY) } ASN1_SEQUENCE_END(MsCtlContent) IMPLEMENT_ASN1_FUNCTIONS(MsCtlContent) typedef struct { ASN1_BIT_STRING* flags; SpcLink *file; } SpcPeImageData; DECLARE_ASN1_FUNCTIONS(SpcPeImageData) ASN1_SEQUENCE(SpcPeImageData) = { ASN1_SIMPLE(SpcPeImageData, flags, ASN1_BIT_STRING), ASN1_EXP_OPT(SpcPeImageData, file, SpcLink, 0) } ASN1_SEQUENCE_END(SpcPeImageData) IMPLEMENT_ASN1_FUNCTIONS(SpcPeImageData) typedef struct { ASN1_INTEGER *a; ASN1_OCTET_STRING *string; ASN1_INTEGER *b; ASN1_INTEGER *c; ASN1_INTEGER *d; ASN1_INTEGER *e; ASN1_INTEGER *f; } SpcSipInfo; DECLARE_ASN1_FUNCTIONS(SpcSipInfo) ASN1_SEQUENCE(SpcSipInfo) = { ASN1_SIMPLE(SpcSipInfo, a, ASN1_INTEGER), ASN1_SIMPLE(SpcSipInfo, string, ASN1_OCTET_STRING), ASN1_SIMPLE(SpcSipInfo, b, ASN1_INTEGER), ASN1_SIMPLE(SpcSipInfo, c, ASN1_INTEGER), ASN1_SIMPLE(SpcSipInfo, d, ASN1_INTEGER), ASN1_SIMPLE(SpcSipInfo, e, ASN1_INTEGER), ASN1_SIMPLE(SpcSipInfo, f, ASN1_INTEGER), } ASN1_SEQUENCE_END(SpcSipInfo) IMPLEMENT_ASN1_FUNCTIONS(SpcSipInfo) typedef struct { AlgorithmIdentifier *digestAlgorithm; ASN1_OCTET_STRING *digest; } MessageImprint; DECLARE_ASN1_FUNCTIONS(MessageImprint) ASN1_SEQUENCE(MessageImprint) = { ASN1_SIMPLE(MessageImprint, digestAlgorithm, AlgorithmIdentifier), ASN1_SIMPLE(MessageImprint, digest, ASN1_OCTET_STRING) } ASN1_SEQUENCE_END(MessageImprint) IMPLEMENT_ASN1_FUNCTIONS(MessageImprint) #ifdef ENABLE_CURL typedef struct { ASN1_OBJECT *type; ASN1_OCTET_STRING *signature; } TimeStampRequestBlob; DECLARE_ASN1_FUNCTIONS(TimeStampRequestBlob) ASN1_SEQUENCE(TimeStampRequestBlob) = { ASN1_SIMPLE(TimeStampRequestBlob, type, ASN1_OBJECT), ASN1_EXP_OPT(TimeStampRequestBlob, signature, ASN1_OCTET_STRING, 0) } ASN1_SEQUENCE_END(TimeStampRequestBlob) IMPLEMENT_ASN1_FUNCTIONS(TimeStampRequestBlob) typedef struct { ASN1_OBJECT *type; TimeStampRequestBlob *blob; } TimeStampRequest; DECLARE_ASN1_FUNCTIONS(TimeStampRequest) ASN1_SEQUENCE(TimeStampRequest) = { ASN1_SIMPLE(TimeStampRequest, type, ASN1_OBJECT), ASN1_SIMPLE(TimeStampRequest, blob, TimeStampRequestBlob) } ASN1_SEQUENCE_END(TimeStampRequest) IMPLEMENT_ASN1_FUNCTIONS(TimeStampRequest) /* RFC3161 Time stamping */ typedef struct { ASN1_INTEGER *status; STACK_OF(ASN1_UTF8STRING) *statusString; ASN1_BIT_STRING *failInfo; } PKIStatusInfo; DECLARE_ASN1_FUNCTIONS(PKIStatusInfo) ASN1_SEQUENCE(PKIStatusInfo) = { ASN1_SIMPLE(PKIStatusInfo, status, ASN1_INTEGER), ASN1_SEQUENCE_OF_OPT(PKIStatusInfo, statusString, ASN1_UTF8STRING), ASN1_OPT(PKIStatusInfo, failInfo, ASN1_BIT_STRING) } ASN1_SEQUENCE_END(PKIStatusInfo) IMPLEMENT_ASN1_FUNCTIONS(PKIStatusInfo) typedef struct { PKIStatusInfo *status; PKCS7 *token; } TimeStampResp; DECLARE_ASN1_FUNCTIONS(TimeStampResp) ASN1_SEQUENCE(TimeStampResp) = { ASN1_SIMPLE(TimeStampResp, status, PKIStatusInfo), ASN1_OPT(TimeStampResp, token, PKCS7) } ASN1_SEQUENCE_END(TimeStampResp) IMPLEMENT_ASN1_FUNCTIONS(TimeStampResp) typedef struct { ASN1_INTEGER *version; MessageImprint *messageImprint; ASN1_OBJECT *reqPolicy; ASN1_INTEGER *nonce; ASN1_BOOLEAN *certReq; STACK_OF(X509_EXTENSION) *extensions; } TimeStampReq; DECLARE_ASN1_FUNCTIONS(TimeStampReq) ASN1_SEQUENCE(TimeStampReq) = { ASN1_SIMPLE(TimeStampReq, version, ASN1_INTEGER), ASN1_SIMPLE(TimeStampReq, messageImprint, MessageImprint), ASN1_OPT (TimeStampReq, reqPolicy, ASN1_OBJECT), ASN1_OPT (TimeStampReq, nonce, ASN1_INTEGER), ASN1_SIMPLE(TimeStampReq, certReq, ASN1_BOOLEAN), ASN1_IMP_SEQUENCE_OF_OPT(TimeStampReq, extensions, X509_EXTENSION, 0) } ASN1_SEQUENCE_END(TimeStampReq) IMPLEMENT_ASN1_FUNCTIONS(TimeStampReq) #endif /* ENABLE_CURL */ typedef struct { ASN1_INTEGER *seconds; ASN1_INTEGER *millis; ASN1_INTEGER *micros; } TimeStampAccuracy; DECLARE_ASN1_FUNCTIONS(TimeStampAccuracy) ASN1_SEQUENCE(TimeStampAccuracy) = { ASN1_OPT(TimeStampAccuracy, seconds, ASN1_INTEGER), ASN1_IMP_OPT(TimeStampAccuracy, millis, ASN1_INTEGER, 0), ASN1_IMP_OPT(TimeStampAccuracy, micros, ASN1_INTEGER, 1) } ASN1_SEQUENCE_END(TimeStampAccuracy) IMPLEMENT_ASN1_FUNCTIONS(TimeStampAccuracy) typedef struct { ASN1_INTEGER *version; ASN1_OBJECT *policy_id; MessageImprint *messageImprint; ASN1_INTEGER *serial; ASN1_GENERALIZEDTIME *time; TimeStampAccuracy *accuracy; ASN1_BOOLEAN ordering; ASN1_INTEGER *nonce; GENERAL_NAME *tsa; STACK_OF(X509_EXTENSION) *extensions; } TimeStampToken; DECLARE_ASN1_FUNCTIONS(TimeStampToken) ASN1_SEQUENCE(TimeStampToken) = { ASN1_SIMPLE(TimeStampToken, version, ASN1_INTEGER), ASN1_SIMPLE(TimeStampToken, policy_id, ASN1_OBJECT), ASN1_SIMPLE(TimeStampToken, messageImprint, MessageImprint), ASN1_SIMPLE(TimeStampToken, serial, ASN1_INTEGER), ASN1_SIMPLE(TimeStampToken, time, ASN1_GENERALIZEDTIME), ASN1_OPT(TimeStampToken, accuracy, TimeStampAccuracy), ASN1_OPT(TimeStampToken, ordering, ASN1_FBOOLEAN), ASN1_OPT(TimeStampToken, nonce, ASN1_INTEGER), ASN1_EXP_OPT(TimeStampToken, tsa, GENERAL_NAME, 0), ASN1_IMP_SEQUENCE_OF_OPT(TimeStampToken, extensions, X509_EXTENSION, 1) } ASN1_SEQUENCE_END(TimeStampToken) IMPLEMENT_ASN1_FUNCTIONS(TimeStampToken) /* * $ echo -n 3006030200013000 | xxd -r -p | openssl asn1parse -i -inform der * 0:d=0 hl=2 l= 6 cons: SEQUENCE * 2:d=1 hl=2 l= 2 prim: BIT STRING * 6:d=1 hl=2 l= 0 cons: SEQUENCE */ static const u_char java_attrs_low[] = { 0x30, 0x06, 0x03, 0x02, 0x00, 0x01, 0x30, 0x00 }; /* * $ echo -n 300c060a2b060104018237020115 | xxd -r -p | openssl asn1parse -i -inform der * 0:d=0 hl=2 l= 12 cons: SEQUENCE * 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Individual Code Signing */ static u_char purpose_ind[] = { 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x15 }; /* * $ echo -n 300c060a2b060104018237020116 | xxd -r -p | openssl asn1parse -i -inform der * 0:d=0 hl=2 l= 12 cons: SEQUENCE * 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Commercial Code Signing */ static u_char purpose_comm[] = { 0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x16 }; static SpcSpOpusInfo *createOpus(const char *desc, const char *url) { SpcSpOpusInfo *info = SpcSpOpusInfo_new(); if (desc) { info->programName = SpcString_new(); info->programName->type = 1; info->programName->value.ascii = ASN1_IA5STRING_new(); ASN1_STRING_set((ASN1_STRING *)info->programName->value.ascii, (const unsigned char*)desc, strlen(desc)); } if (url) { info->moreInfo = SpcLink_new(); info->moreInfo->type = 0; info->moreInfo->value.url = ASN1_IA5STRING_new(); ASN1_STRING_set((ASN1_STRING *)info->moreInfo->value.url, (const unsigned char*)url, strlen(url)); } return info; } /* * Return the header length (tag and length octets) of the ASN.1 type */ static size_t asn1_simple_hdr_len(const unsigned char *p, size_t len) { if (len <= 2 || p[0] > 0x31) return 0; return (p[1]&0x80) ? (2 + (p[1]&0x7f)) : 2; } /* * Add a custom, non-trusted time to the PKCS7 structure to prevent OpenSSL * adding the _current_ time. This allows to create a deterministic signature * when no trusted timestamp server was specified, making osslsigncode * behaviour closer to signtool.exe (which doesn't include any non-trusted * time in this case.) */ static int pkcs7_add_signing_time(PKCS7_SIGNER_INFO *si, time_t signing_time) { if (signing_time == INVALID_TIME) /* -st option was not specified */ return 1; /* success */ return PKCS7_add_signed_attribute(si, NID_pkcs9_signingTime, V_ASN1_UTCTIME, ASN1_TIME_adj(NULL, signing_time, 0, 0)); } static void tohex(const unsigned char *v, char *b, int len) { int i; for(i=0; id.sign->contents->type, indir_objid) && (p7->d.sign->contents->d.other->type == V_ASN1_SEQUENCE || p7->d.sign->contents->d.other->type == V_ASN1_OCTET_STRING); ASN1_OBJECT_free(indir_objid); return retval; } #ifdef ENABLE_CURL static int blob_has_nl = 0; /* * Callback for writing received data */ static size_t curl_write(void *ptr, size_t sz, size_t nmemb, void *stream) { if (sz*nmemb > 0 && !blob_has_nl) { if (memchr(ptr, '\n', sz*nmemb)) blob_has_nl = 1; } return BIO_write((BIO*)stream, ptr, sz*nmemb); } static void print_timestamp_error(const char *url, long http_code) { if (http_code != -1) { printf("Failed to convert timestamp reply from %s; " "HTTP status %ld\n", url, http_code); } else { printf("Failed to convert timestamp reply from %s; " "no HTTP status available", url); } ERR_print_errors_fp(stdout); } /* A timestamp request looks like this: POST HTTP/1.1 Content-Type: application/octet-stream Content-Length: ... Accept: application/octet-stream User-Agent: Transport Host: ... Cache-Control: no-cache .. and the blob has the following ASN1 structure: 0:d=0 hl=4 l= 291 cons: SEQUENCE 4:d=1 hl=2 l= 10 prim: OBJECT :1.3.6.1.4.1.311.3.2.1 16:d=1 hl=4 l= 275 cons: SEQUENCE 20:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-data 31:d=2 hl=4 l= 260 cons: cont [ 0 ] 35:d=3 hl=4 l= 256 prim: OCTET STRING .. and it returns a base64 encoded PKCS#7 structure. */ /* * Encode RFC 3161 timestamp request and write it into BIO */ static BIO *encode_rfc3161_request(PKCS7 *sig, const EVP_MD *md) { PKCS7_SIGNER_INFO *si; unsigned char mdbuf[EVP_MAX_MD_SIZE]; EVP_MD_CTX *mdctx; TimeStampReq *req; BIO *bout; u_char *p; int len; si = sk_PKCS7_SIGNER_INFO_value(sig->d.sign->signer_info, 0); mdctx = EVP_MD_CTX_new(); EVP_DigestInit(mdctx, md); EVP_DigestUpdate(mdctx, si->enc_digest->data, si->enc_digest->length); EVP_DigestFinal(mdctx, mdbuf, NULL); EVP_MD_CTX_free(mdctx); req = TimeStampReq_new(); ASN1_INTEGER_set(req->version, 1); req->messageImprint->digestAlgorithm->algorithm = OBJ_nid2obj(EVP_MD_nid(md)); req->messageImprint->digestAlgorithm->parameters = ASN1_TYPE_new(); req->messageImprint->digestAlgorithm->parameters->type = V_ASN1_NULL; ASN1_OCTET_STRING_set(req->messageImprint->digest, mdbuf, EVP_MD_size(md)); req->certReq = (void*)0x1; len = i2d_TimeStampReq(req, NULL); p = OPENSSL_malloc(len); len = i2d_TimeStampReq(req, &p); p -= len; TimeStampReq_free(req); bout = BIO_new(BIO_s_mem()); BIO_write(bout, p, len); OPENSSL_free(p); (void)BIO_flush(bout); return bout; } /* * Encode authenticode timestamp request and write it into BIO */ static BIO *encode_authenticode_request(PKCS7 *sig) { PKCS7_SIGNER_INFO *si; TimeStampRequest *req; BIO *bout, *b64; u_char *p; int len; req = TimeStampRequest_new(); req->type = OBJ_txt2obj(SPC_TIME_STAMP_REQUEST_OBJID, 1); req->blob->type = OBJ_nid2obj(NID_pkcs7_data); si = sk_PKCS7_SIGNER_INFO_value(sig->d.sign->signer_info, 0); req->blob->signature = si->enc_digest; len = i2d_TimeStampRequest(req, NULL); p = OPENSSL_malloc(len); len = i2d_TimeStampRequest(req, &p); p -= len; req->blob->signature = NULL; TimeStampRequest_free(req); bout = BIO_new(BIO_s_mem()); b64 = BIO_new(BIO_f_base64()); bout = BIO_push(b64, bout); BIO_write(bout, p, len); OPENSSL_free(p); (void)BIO_flush(bout); return bout; } /* * Decode a curl response from BIO. * If successful the RFC 3161 timestamp will be written into * the PKCS7 SignerInfo structure as an unauthorized attribute - cont[1]. */ static int decode_rfc3161_response(PKCS7 *sig, BIO *bin, int verbose) { PKCS7_SIGNER_INFO *si; STACK_OF(X509_ATTRIBUTE) *attrs; TimeStampResp *reply; u_char *p; int len; reply = ASN1_item_d2i_bio(ASN1_ITEM_rptr(TimeStampResp), bin, NULL); BIO_free_all(bin); if (!reply) return 1; /* FAILED */ if (ASN1_INTEGER_get(reply->status->status) != 0) { if (verbose) printf("Timestamping failed: %ld\n", ASN1_INTEGER_get(reply->status->status)); TimeStampResp_free(reply); return 1; /* FAILED */ } if (((len = i2d_PKCS7(reply->token, NULL)) <= 0) || (p = OPENSSL_malloc(len)) == NULL) { if (verbose) { printf("Failed to convert pkcs7: %d\n", len); ERR_print_errors_fp(stdout); } TimeStampResp_free(reply); return 1; /* FAILED */ } len = i2d_PKCS7(reply->token, &p); p -= len; TimeStampResp_free(reply); attrs = sk_X509_ATTRIBUTE_new_null(); attrs = X509at_add1_attr_by_txt(&attrs, SPC_RFC3161_OBJID, V_ASN1_SET, p, len); OPENSSL_free(p); si = sk_PKCS7_SIGNER_INFO_value(sig->d.sign->signer_info, 0); PKCS7_set_attributes(si, attrs); sk_X509_ATTRIBUTE_pop_free(attrs, X509_ATTRIBUTE_free); return 0; /* OK */ } /* * Decode a curl response from BIO. * If successful the authenticode timestamp will be written into * the PKCS7 SignerInfo structure as an unauthorized attribute - cont[1]. */ static int decode_authenticode_response(PKCS7 *sig, BIO *bin, int verbose) { PKCS7 *p7; PKCS7_SIGNER_INFO *info, *si; STACK_OF(X509_ATTRIBUTE) *attrs; BIO* b64, *b64_bin; u_char *p; int len, i; b64 = BIO_new(BIO_f_base64()); if (!blob_has_nl) BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); b64_bin = BIO_push(b64, bin); p7 = d2i_PKCS7_bio(b64_bin, NULL); BIO_free_all(b64_bin); if (p7 == NULL) return 1; /* FAILED */ for(i = sk_X509_num(p7->d.sign->cert)-1; i>=0; i--) PKCS7_add_certificate(sig, sk_X509_value(p7->d.sign->cert, i)); info = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0); if (((len = i2d_PKCS7_SIGNER_INFO(info, NULL)) <= 0) || (p = OPENSSL_malloc(len)) == NULL) { if (verbose) { printf("Failed to convert signer info: %d\n", len); ERR_print_errors_fp(stdout); } PKCS7_free(p7); return 1; /* FAILED */ } len = i2d_PKCS7_SIGNER_INFO(info, &p); p -= len; PKCS7_free(p7); attrs = sk_X509_ATTRIBUTE_new_null(); attrs = X509at_add1_attr_by_txt(&attrs, PKCS9_COUNTER_SIGNATURE, V_ASN1_SET, p, len); OPENSSL_free(p); si = sk_PKCS7_SIGNER_INFO_value(sig->d.sign->signer_info, 0); /* * PKCS7_set_attributes() frees up all elements of si->unauth_attr * and sets there a copy of attrs so overrides the previous timestamp */ PKCS7_set_attributes(si, attrs); sk_X509_ATTRIBUTE_pop_free(attrs, X509_ATTRIBUTE_free); return 0; /* OK */ } /* * Add timestamp to the PKCS7 SignerInfo structure: * sig->d.sign->signer_info->unauth_attr */ static int add_timestamp(PKCS7 *sig, char *url, char *proxy, int rfc3161, const EVP_MD *md, int verbose, int noverifypeer) { CURL *curl; struct curl_slist *slist = NULL; CURLcode res; BIO *bout, *bin; u_char *p = NULL; int len = 0; if (!url) return 1; /* FAILED */ /* Start a libcurl easy session and set options for a curl easy handle */ curl = curl_easy_init(); if (proxy) { curl_easy_setopt(curl, CURLOPT_PROXY, proxy); if (!strncmp("http:", proxy, 5)) curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); if (!strncmp("socks:", proxy, 6)) curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); } curl_easy_setopt(curl, CURLOPT_URL, url); /* * ask libcurl to show us the verbose output * curl_easy_setopt(curl, CURLOPT_VERBOSE, 42); */ if (noverifypeer) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); if (rfc3161) { slist = curl_slist_append(slist, "Content-Type: application/timestamp-query"); slist = curl_slist_append(slist, "Accept: application/timestamp-reply"); } else { slist = curl_slist_append(slist, "Content-Type: application/octet-stream"); slist = curl_slist_append(slist, "Accept: application/octet-stream"); } slist = curl_slist_append(slist, "User-Agent: Transport"); slist = curl_slist_append(slist, "Cache-Control: no-cache"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); /* Encode timestamp request */ if (rfc3161) { bout = encode_rfc3161_request(sig, md); } else { bout = encode_authenticode_request(sig); } len = BIO_get_mem_data(bout, &p); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (char*)p); bin = BIO_new(BIO_s_mem()); BIO_set_mem_eof_return(bin, 0); curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_WRITEDATA, bin); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write); /* Perform the request */ res = curl_easy_perform(curl); curl_slist_free_all(slist); BIO_free_all(bout); if (res != CURLE_OK) { BIO_free_all(bin); if (verbose) printf("CURL failure: %s %s\n", curl_easy_strerror(res), url); } else { /* CURLE_OK (0) */ long http_code = -1; (void)BIO_flush(bin); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); /* Decode a curl response from BIO and write it into the PKCS7 structure */ if (rfc3161) res = decode_rfc3161_response(sig, bin, verbose); else res = decode_authenticode_response(sig, bin, verbose); if (res && verbose) print_timestamp_error(url, http_code); } /* End a libcurl easy handle */ curl_easy_cleanup(curl); return (int)res; } static int add_timestamp_authenticode(PKCS7 *sig, GLOBAL_OPTIONS *options) { int i; for (i=0; inturl; i++) { int res = add_timestamp(sig, options->turl[i], options->proxy, 0, NULL, options->verbose || options->nturl == 1, options->noverifypeer); if (!res) return 0; /* OK */ } return 1; /* FAILED */ } static int add_timestamp_rfc3161(PKCS7 *sig, GLOBAL_OPTIONS *options) { int i; for (i=0; intsurl; i++) { int res = add_timestamp(sig, options->tsurl[i], options->proxy, 1, options->md, options->verbose || options->ntsurl == 1, options->noverifypeer); if (!res) return 0; /* OK */ } return 1; /* FAILED */ } #endif /* ENABLE_CURL */ static bool on_list(const char *txt, const char *list[]) { while (*list) if (!strcmp(txt, *list++)) return true; return false; } static void usage(const char *argv0, const char *cmd) { const char *cmds_all[] = {"all", NULL}; const char *cmds_sign[] = {"all", "sign", NULL}; const char *cmds_add[] = {"all", "add", NULL}; const char *cmds_attach[] = {"all", "attach-signature", NULL}; const char *cmds_extract[] = {"all", "extract-signature", NULL}; const char *cmds_remove[] = {"all", "remove-signature", NULL}; const char *cmds_verify[] = {"all", "verify", NULL}; printf("\nUsage: %s", argv0); if (on_list(cmd, cmds_all)) { printf("\n\n%1s[ --version | -v ]\n", ""); printf("%1s[ --help ]\n\n", ""); } if (on_list(cmd, cmds_sign)) { printf("%1s[ sign ] ( -certs | -spc -key | -pkcs12 |\n", ""); printf("%12s [ -pkcs11engine ] -pkcs11module -certs -key )\n", ""); printf("%12s[ -pass ", ""); #ifdef PROVIDE_ASKPASS printf("%1s [ -askpass ]", ""); #endif /* PROVIDE_ASKPASS */ printf("%1s[ -readpass ]\n", ""); printf("%12s[ -ac ]\n", ""); printf("%12s[ -h {md5,sha1,sha2(56),sha384,sha512} ]\n", ""); printf("%12s[ -n ] [ -i ] [ -jp ] [ -comm ]\n", ""); printf("%12s[ -ph ]\n", ""); #ifdef ENABLE_CURL printf("%12s[ -t [ -t ... ] [ -p ] [ -noverifypeer ]\n", ""); printf("%12s[ -ts [ -ts ... ] [ -p ] [ -noverifypeer ] ]\n", ""); #endif /* ENABLE_CURL */ printf("%12s[ -st ]\n", ""); printf("%12s[ -addUnauthenticatedBlob ]\n", ""); printf("%12s[ -nest ]\n", ""); printf("%12s[ -verbose ]\n", ""); printf("%12s[ -add-msi-dse ]\n", ""); printf("%12s[ -in ] [-out ] \n\n", ""); } if (on_list(cmd, cmds_add)) { printf("%1sadd [-addUnauthenticatedBlob]\n", ""); #ifdef ENABLE_CURL printf("%12s[ -t [ -t ... ] [ -p ] [ -noverifypeer ]\n", ""); printf("%12s[ -ts [ -ts ... ] [ -p ] [ -noverifypeer ] ]\n", ""); #endif /* ENABLE_CURL */ printf("%12s[ -verbose ]\n", ""); printf("%12s[ -add-msi-dse ]\n", ""); printf("%12s[ -in ] [ -out ] \n\n", ""); } if (on_list(cmd, cmds_attach)) { printf("%1sattach-signature [ -sigin ] \n", ""); printf("%12s[ -CAfile ]\n", ""); printf("%12s[ -CRLfile ]\n", ""); printf("%12s[ -TSA-CAfile ]\n", ""); printf("%12s[ -TSA-CRLfile ]\n", ""); printf("%12s[ -nest ]\n", ""); printf("%12s[ -add-msi-dse ]\n", ""); printf("%12s[ -in ] [ -out ] \n\n", ""); } if (on_list(cmd, cmds_extract)) { printf("%1sextract-signature [ -pem ]\n", ""); printf("%12s[ -in ] [ -out ] \n\n", ""); } if (on_list(cmd, cmds_remove)) printf("%1sremove-signature [ -in ] [ -out ] \n\n", ""); if (on_list(cmd, cmds_verify)) { printf("%1sverify [ -in ] \n", ""); printf("%12s[ -c | -catalog ]\n", ""); printf("%12s[ -CAfile ]\n", ""); printf("%12s[ -CRLfile ]\n", ""); printf("%12s[ -TSA-CAfile ]\n", ""); printf("%12s[ -TSA-CRLfile ]\n", ""); printf("%12s[ -require-leaf-hash {md5,sha1,sha2(56),sha384,sha512}:XXXXXXXXXXXX... ]\n", ""); printf("%12s[ -timestamp-expiration ]\n", ""); printf("%12s[ -verbose ]\n\n", ""); } } static void help_for(const char *argv0, const char *cmd) { const char *cmds_all[] = {"all", NULL}; const char *cmds_add[] = {"add", NULL}; const char *cmds_attach[] = {"attach-signature", NULL}; const char *cmds_extract[] = {"extract-signature", NULL}; const char *cmds_remove[] = {"remove-signature", NULL}; const char *cmds_sign[] = {"sign", NULL}; const char *cmds_verify[] = {"verify", NULL}; const char *cmds_ac[] = {"sign", NULL}; const char *cmds_add_msi_dse[] = {"sign", NULL}; const char *cmds_addUnauthenticatedBlob[] = {"sign", "add", NULL}; #ifdef PROVIDE_ASKPASS const char *cmds_askpass[] = {"sign", NULL}; #endif /* PROVIDE_ASKPASS */ const char *cmds_CAfile[] = {"attach-signature", "verify", NULL}; const char *cmds_catalog[] = {"verify", NULL}; const char *cmds_certs[] = {"sign", NULL}; const char *cmds_comm[] = {"sign", NULL}; const char *cmds_CRLfile[] = {"attach-signature", "verify", NULL}; const char *cmds_CRLfileTSA[] = {"attach-signature", "verify", NULL}; const char *cmds_h[] = {"sign", NULL}; const char *cmds_i[] = {"sign", NULL}; const char *cmds_in[] = {"add", "attach-signature", "extract-signature", "remove-signature", "sign", "verify", NULL}; const char *cmds_jp[] = {"sign", NULL}; const char *cmds_key[] = {"sign", NULL}; const char *cmds_n[] = {"sign", NULL}; const char *cmds_nest[] = {"attach-signature", "sign", NULL}; #ifdef ENABLE_CURL const char *cmds_noverifypeer[] = {"add", "sign", NULL}; #endif /* ENABLE_CURL */ const char *cmds_out[] = {"add", "attach-signature", "extract-signature", "remove-signature", "sign", NULL}; #ifdef ENABLE_CURL const char *cmds_p[] = {"add", "sign", NULL}; #endif /* ENABLE_CURL */ const char *cmds_pass[] = {"sign", NULL}; const char *cmds_pem[] = {"extract-signature", NULL}; const char *cmds_ph[] = {"sign", NULL}; const char *cmds_pkcs11cert[] = {"sign", NULL}; const char *cmds_pkcs11engine[] = {"sign", NULL}; const char *cmds_pkcs11module[] = {"sign", NULL}; const char *cmds_pkcs12[] = {"sign", NULL}; const char *cmds_readpass[] = {"sign", NULL}; const char *cmds_require_leaf_hash[] = {"verify", NULL}; const char *cmds_sigin[] = {"attach-signature", NULL}; const char *cmds_st[] = {"sign", NULL}; const char *cmds_timestamp_expiration[] = {"verify", NULL}; #ifdef ENABLE_CURL const char *cmds_t[] = {"add", "sign", NULL}; const char *cmds_ts[] = {"add", "sign", NULL}; #endif /* ENABLE_CURL */ const char *cmds_CAfileTSA[] = {"attach-signature", "verify", NULL}; const char *cmds_verbose[] = {"add", "sign", "verify", NULL}; if (on_list(cmd, cmds_all)) { printf("osslsigncode is a small tool that implements part of the functionality of the Microsoft\n"); printf("tool signtool.exe - more exactly the Authenticode signing and timestamping.\n"); printf("It can sign and timestamp PE (EXE/SYS/DLL/etc), CAB and MSI files,\n"); printf("supports getting the timestamp through a proxy as well.\n"); printf("osslsigncode also supports signature verification, removal and extraction.\n\n"); printf("%-22s = print osslsigncode version and usage\n", "--version | -v"); printf("%-22s = print osslsigncode help menu\n\n", "--help"); printf("Commands:\n"); printf("%-22s = add an unauthenticated blob or a timestamp to a previously-signed file\n", "add"); printf("%-22s = sign file using a given signature\n", "attach-signature"); printf("%-22s = extract signature from a previously-signed file\n", "extract-signature"); printf("%-22s = remove sections of the embedded signature on a file\n", "remove-signature"); printf("%-22s = digitally sign a file\n", "sign"); printf("%-22s = verifies the digital signature of a file\n\n", "verify"); printf("For help on a specific command, enter %s --help\n", argv0); } if (on_list(cmd, cmds_add)) { printf("\nUse the \"add\" command to add an unauthenticated blob or a timestamp to a previously-signed file.\n\n"); printf("Options:\n"); } if (on_list(cmd, cmds_attach)) { printf("\nUse the \"attach-signature\" command to attach the signature stored in the \"sigin\" file.\n"); printf("In order to verify this signature you should specify how to find needed CA or TSA\n"); printf("certificates, if appropriate.\n\n"); printf("Options:\n"); } if (on_list(cmd, cmds_extract)) { printf("\nUse the \"extract-signature\" command to extract the embedded signature from a previously-signed file.\n"); printf("DER is the default format of the output file, but can be changed to PEM.\n\n"); printf("Options:\n"); } if (on_list(cmd, cmds_remove)) { printf("\nUse the \"remove-signature\" command to remove sections of the embedded signature on a file.\n\n"); printf("Options:\n"); } if (on_list(cmd, cmds_sign)) { printf("\nUse the \"sign\" command to sign files using embedded signatures.\n"); printf("Signing protects a file from tampering, and allows users to verify the signer\n"); printf("based on a signing certificate. The options below allow you to specify signing\n"); printf("parameters and to select the signing certificate you wish to use.\n\n"); printf("Options:\n"); } if (on_list(cmd, cmds_verify)) { printf("\nUse the \"verify\" command to verify embedded signatures.\n"); printf("Verification determines if the signing certificate was issued by a trusted party,\n"); printf("whether that certificate has been revoked, and whether the certificate is valid\n"); printf("under a specific policy. Options allow you to specify requirements that must be met\n"); printf("and to specify how to find needed CA or TSA certificates, if appropriate.\n\n"); printf("Options:\n"); } if (on_list(cmd, cmds_ac)) printf("%-24s= additional certificates to be added to the signature block\n", "-ac"); if (on_list(cmd, cmds_add_msi_dse)) printf("%-24s= sign a MSI file with the add-msi-dse option\n", "-add-msi-dse"); if (on_list(cmd, cmds_addUnauthenticatedBlob)) printf("%-24s= add an unauthenticated blob to the PE/MSI file\n", "-addUnauthenticatedBlob"); #ifdef PROVIDE_ASKPASS if (on_list(cmd, cmds_askpass)) printf("%-24s= ask for the private key password\n", "-askpass"); #endif /* PROVIDE_ASKPASS */ if (on_list(cmd, cmds_catalog)) printf("%-24s= specifies the catalog file by name\n", "-c, -catalog"); if (on_list(cmd, cmds_CAfile)) printf("%-24s= the file containing one or more trusted certificates in PEM format\n", "-CAfile"); if (on_list(cmd, cmds_certs)) printf("%-24s= the signing certificate to use\n", "-certs, -spc"); if (on_list(cmd, cmds_comm)) printf("%-24s= set commercial purpose (default: individual purpose)\n", "-comm"); if (on_list(cmd, cmds_CRLfile)) printf("%-24s= the file containing one or more CRLs in PEM format\n", "-CRLfile"); if (on_list(cmd, cmds_h)) { printf("%-24s= {md5|sha1|sha2(56)|sha384|sha512}\n", "-h"); printf("%26sset of cryptographic hash functions\n", ""); } if (on_list(cmd, cmds_i)) printf("%-24s= specifies a URL for expanded description of the signed content\n", "-i"); if (on_list(cmd, cmds_in)) printf("%-24s= input file\n", "-in"); if (on_list(cmd, cmds_jp)) { printf("%-24s= low | medium | high\n", "-jp"); printf("%26slevels of permissions in Microsoft Internet Explorer 4.x for CAB files\n", ""); printf("%26sonly \"low\" level is now supported\n", ""); } if (on_list(cmd, cmds_key)) printf("%-24s= the private key to use or PKCS#11 URI identifies a key in the token\n", "-key"); if (on_list(cmd, cmds_n)) printf("%-24s= specifies a description of the signed content\n", "-n"); if (on_list(cmd, cmds_nest)) printf("%-24s= add the new nested signature instead of replacing the first one\n", "-nest"); #ifdef ENABLE_CURL if (on_list(cmd, cmds_noverifypeer)) printf("%-24s= do not verify the Time-Stamp Authority's SSL certificate\n", "-noverifypeer"); #endif /* ENABLE_CURL */ if (on_list(cmd, cmds_out)) printf("%-24s= output file\n", "-out"); #ifdef ENABLE_CURL if (on_list(cmd, cmds_p)) printf("%-24s= proxy to connect to the desired Time-Stamp Authority server\n", "-p"); #endif /* ENABLE_CURL */ if (on_list(cmd, cmds_pass)) printf("%-24s= the private key password\n", "-pass"); if (on_list(cmd, cmds_pem)) printf("%-24s= output data format PEM to use (default: DER)\n", "-pem"); if (on_list(cmd, cmds_ph)) printf("%-24s= generate page hashes for executable files\n", "-ph"); if (on_list(cmd, cmds_pkcs11cert)) printf("%-24s= PKCS#11 URI identifies a certificate in the token\n", "-pkcs11cert"); if (on_list(cmd, cmds_pkcs11engine)) printf("%-24s= PKCS11 engine\n", "-pkcs11engine"); if (on_list(cmd, cmds_pkcs11module)) printf("%-24s= PKCS11 module\n", "-pkcs11module"); if (on_list(cmd, cmds_pkcs12)) printf("%-24s= PKCS#12 container with the certificate and the private key\n", "-pkcs12"); if (on_list(cmd, cmds_readpass)) printf("%-24s= the private key password source\n", "-readpass"); if (on_list(cmd, cmds_require_leaf_hash)) { printf("%-24s= {md5|sha1|sha2(56)|sha384|sha512}:XXXXXXXXXXXX...\n", "-require-leaf-hash"); printf("%26sspecifies an optional hash algorithm to use when computing\n", ""); printf("%26sthe leaf certificate (in DER form) hash and compares\n", ""); printf("%26sthe provided hash against the computed hash\n", ""); } if (on_list(cmd, cmds_sigin)) printf("%-24s= a file containing the signature to be attached\n", "-sigin"); if (on_list(cmd, cmds_st)) printf("%-24s= the unix-time to set the signing time\n", "-st"); if (on_list(cmd, cmds_timestamp_expiration)) printf("%-24s= verify a finite lifetime of the TSA private key\n", "-timestamp-expiration"); #ifdef ENABLE_CURL if (on_list(cmd, cmds_t)) { printf("%-24s= specifies that the digital signature will be timestamped\n", "-t"); printf("%26sby the Time-Stamp Authority (TSA) indicated by the URL\n", ""); printf("%26sthis option cannot be used with the -ts option\n", ""); } if (on_list(cmd, cmds_ts)) { printf("%-24s= specifies the URL of the RFC 3161 Time-Stamp Authority server\n", "-ts"); printf("%26sthis option cannot be used with the -t option\n", ""); } #endif /* ENABLE_CURL */ if (on_list(cmd, cmds_CAfileTSA)) { printf("%-24s= the file containing one or more Time-Stamp Authority certificates in PEM format\n", "-TSA-CAfile"); } if (on_list(cmd, cmds_CRLfileTSA)) printf("%-24s= the file containing one or more Time-Stamp Authority CRLs in PEM format\n", "-TSA-CRLfile"); if (on_list(cmd, cmds_verbose)) { printf("%-24s= include additional output in the log\n", "-verbose"); } usage(argv0, cmd); } #define DO_EXIT_0(x) { printf(x); goto err_cleanup; } #define DO_EXIT_1(x, y) { printf(x, y); goto err_cleanup; } #define DO_EXIT_2(x, y, z) { printf(x, y, z); goto err_cleanup; } typedef enum { FILE_TYPE_CAB, FILE_TYPE_PE, FILE_TYPE_MSI, FILE_TYPE_CAT } file_type_t; typedef enum { CMD_SIGN, CMD_EXTRACT, CMD_REMOVE, CMD_VERIFY, CMD_ADD, CMD_ATTACH, CMD_HELP } cmd_type_t; static SpcLink *get_obsolete_link(void) { static const unsigned char obsolete[] = { 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x62, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e }; SpcLink *link = SpcLink_new(); link->type = 2; link->value.file = SpcString_new(); link->value.file->type = 0; link->value.file->value.unicode = ASN1_BMPSTRING_new(); ASN1_STRING_set(link->value.file->value.unicode, obsolete, sizeof obsolete); return link; } static const unsigned char classid_page_hash[] = { 0xA6, 0xB5, 0x86, 0xD5, 0xB4, 0xA1, 0x24, 0x66, 0xAE, 0x05, 0xA2, 0x17, 0xDA, 0x8E, 0x60, 0xD6 }; static unsigned char *pe_calc_page_hash(char *indata, uint32_t header_size, int pe32plus, uint32_t sigpos, int phtype, size_t *rphlen) { uint16_t nsections, opthdr_size; uint32_t pagesize, hdrsize; uint32_t rs, ro, l, lastpos = 0; int pphlen, phlen, i, pi = 1; unsigned char *res, *zeroes; char *sections; const EVP_MD *md; EVP_MD_CTX *mdctx; nsections = GET_UINT16_LE(indata + header_size + 6); pagesize = GET_UINT32_LE(indata + header_size + 56); hdrsize = GET_UINT32_LE(indata + header_size + 84); md = EVP_get_digestbynid(phtype); pphlen = 4 + EVP_MD_size(md); phlen = pphlen * (3 + nsections + sigpos / pagesize); res = OPENSSL_malloc(phlen); zeroes = OPENSSL_zalloc(pagesize); mdctx = EVP_MD_CTX_new(); EVP_DigestInit(mdctx, md); EVP_DigestUpdate(mdctx, indata, header_size + 88); EVP_DigestUpdate(mdctx, indata + header_size + 92, 60 + pe32plus*16); EVP_DigestUpdate(mdctx, indata + header_size + 160 + pe32plus*16, hdrsize - (header_size + 160 + pe32plus*16)); EVP_DigestUpdate(mdctx, zeroes, pagesize - hdrsize); memset(res, 0, 4); EVP_DigestFinal(mdctx, res + 4, NULL); opthdr_size = GET_UINT16_LE(indata + header_size + 20); sections = indata + header_size + 24 + opthdr_size; for (i=0; iheader_size, header->pe32plus, header->fileend, phtype, &phlen); if (!ph) { printf("Failed to calculate page hash\n"); return NULL; /* FAILED */ } tohex(ph, hexbuf, (phlen < 32) ? phlen : 32); printf("Calculated page hash : %s ...\n", hexbuf); tostr = ASN1_TYPE_new(); tostr->type = V_ASN1_OCTET_STRING; tostr->value.octet_string = ASN1_OCTET_STRING_new(); ASN1_OCTET_STRING_set(tostr->value.octet_string, ph, phlen); OPENSSL_free(ph); oset = sk_ASN1_TYPE_new_null(); sk_ASN1_TYPE_push(oset, tostr); l = i2d_ASN1_SET_ANY(oset, NULL); tmp = p = OPENSSL_malloc(l); i2d_ASN1_SET_ANY(oset, &tmp); ASN1_TYPE_free(tostr); sk_ASN1_TYPE_free(oset); aval = SpcAttributeTypeAndOptionalValue_new(); aval->type = OBJ_txt2obj((phtype == NID_sha1) ? \ SPC_PE_IMAGE_PAGE_HASHES_V1 : SPC_PE_IMAGE_PAGE_HASHES_V2, 1); aval->value = ASN1_TYPE_new(); aval->value->type = V_ASN1_SET; aval->value->value.set = ASN1_STRING_new(); ASN1_STRING_set(aval->value->value.set, p, l); OPENSSL_free(p); l = i2d_SpcAttributeTypeAndOptionalValue(aval, NULL); tmp = p = OPENSSL_malloc(l); i2d_SpcAttributeTypeAndOptionalValue(aval, &tmp); SpcAttributeTypeAndOptionalValue_free(aval); taval = ASN1_TYPE_new(); taval->type = V_ASN1_SEQUENCE; taval->value.sequence = ASN1_STRING_new(); ASN1_STRING_set(taval->value.sequence, p, l); OPENSSL_free(p); aset = sk_ASN1_TYPE_new_null(); sk_ASN1_TYPE_push(aset, taval); l = i2d_ASN1_SET_ANY(aset, NULL); tmp = p = OPENSSL_malloc(l); l = i2d_ASN1_SET_ANY(aset, &tmp); ASN1_TYPE_free(taval); sk_ASN1_TYPE_free(aset); so = SpcSerializedObject_new(); ASN1_OCTET_STRING_set(so->classId, classid_page_hash, sizeof classid_page_hash); ASN1_OCTET_STRING_set(so->serializedData, p, l); OPENSSL_free(p); link = SpcLink_new(); link->type = 1; link->value.moniker = so; return link; } static int get_indirect_data_blob(u_char **blob, int *len, GLOBAL_OPTIONS *options, FILE_HEADER *header, file_type_t type, char *indata) { u_char *p; int hashlen, l, phtype; void *hash; ASN1_OBJECT *dtype; SpcIndirectDataContent *idc; static const unsigned char msistr[] = { 0xf1, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }; idc = SpcIndirectDataContent_new(); idc->data->value = ASN1_TYPE_new(); idc->data->value->type = V_ASN1_SEQUENCE; idc->data->value->value.sequence = ASN1_STRING_new(); if (type == FILE_TYPE_CAB) { SpcLink *link = get_obsolete_link(); l = i2d_SpcLink(link, NULL); p = OPENSSL_malloc(l); i2d_SpcLink(link, &p); p -= l; dtype = OBJ_txt2obj(SPC_CAB_DATA_OBJID, 1); SpcLink_free(link); } else if (type == FILE_TYPE_PE) { SpcPeImageData *pid = SpcPeImageData_new(); ASN1_BIT_STRING_set(pid->flags, (unsigned char*)"0", 0); if (options->pagehash) { SpcLink *link; phtype = NID_sha1; if (EVP_MD_size(options->md) > EVP_MD_size(EVP_sha1())) phtype = NID_sha256; link = get_page_hash_link(phtype, indata, header); if (!link) return 0; /* FAILED */ pid->file = link; } else { pid->file = get_obsolete_link(); } l = i2d_SpcPeImageData(pid, NULL); p = OPENSSL_malloc(l); i2d_SpcPeImageData(pid, &p); p -= l; dtype = OBJ_txt2obj(SPC_PE_IMAGE_DATA_OBJID, 1); SpcPeImageData_free(pid); } else if (type == FILE_TYPE_MSI) { SpcSipInfo *si = SpcSipInfo_new(); ASN1_INTEGER_set(si->a, 1); ASN1_INTEGER_set(si->b, 0); ASN1_INTEGER_set(si->c, 0); ASN1_INTEGER_set(si->d, 0); ASN1_INTEGER_set(si->e, 0); ASN1_INTEGER_set(si->f, 0); ASN1_OCTET_STRING_set(si->string, msistr, sizeof msistr); l = i2d_SpcSipInfo(si, NULL); p = OPENSSL_malloc(l); i2d_SpcSipInfo(si, &p); p -= l; dtype = OBJ_txt2obj(SPC_SIPINFO_OBJID, 1); SpcSipInfo_free(si); } else { printf("Unexpected file type: %d\n", type); return 0; /* FAILED */ } idc->data->type = dtype; idc->data->value->value.sequence->data = p; idc->data->value->value.sequence->length = l; idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(EVP_MD_nid(options->md)); idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new(); idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL; hashlen = EVP_MD_size(options->md); hash = OPENSSL_malloc(hashlen); memset(hash, 0, hashlen); ASN1_OCTET_STRING_set(idc->messageDigest->digest, hash, hashlen); OPENSSL_free(hash); *len = i2d_SpcIndirectDataContent(idc, NULL); *blob = OPENSSL_malloc(*len); p = *blob; i2d_SpcIndirectDataContent(idc, &p); SpcIndirectDataContent_free(idc); *len -= EVP_MD_size(options->md); return 1; /* OK */ } static int set_signing_blob(PKCS7 *sig, BIO *hash, unsigned char *buf, int len) { unsigned char mdbuf[EVP_MAX_MD_SIZE]; int mdlen; size_t seqhdrlen; BIO *sigbio; PKCS7 *td7; mdlen = BIO_gets(hash, (char*)mdbuf, EVP_MAX_MD_SIZE); memcpy(buf+len, mdbuf, mdlen); seqhdrlen = asn1_simple_hdr_len(buf, len); if ((sigbio = PKCS7_dataInit(sig, NULL)) == NULL) { printf("PKCS7_dataInit failed\n"); return 0; /* FAILED */ } BIO_write(sigbio, buf+seqhdrlen, len-seqhdrlen+mdlen); (void)BIO_flush(sigbio); if (!PKCS7_dataFinal(sig, sigbio)) { printf("PKCS7_dataFinal failed\n"); return 0; /* FAILED */ } BIO_free_all(sigbio); /* replace the data part with the MS Authenticode spcIndirectDataContext blob */ td7 = PKCS7_new(); td7->type = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1); td7->d.other = ASN1_TYPE_new(); td7->d.other->type = V_ASN1_SEQUENCE; td7->d.other->value.sequence = ASN1_STRING_new(); ASN1_STRING_set(td7->d.other->value.sequence, buf, len+mdlen); if (!PKCS7_set_content(sig, td7)) { PKCS7_free(td7); printf("PKCS7_set_content failed\n"); return 0; /* FAILED */ } return 1; /* OK */ } int set_content_blob(PKCS7 *sig, PKCS7 *cursig) { PKCS7 *contents; unsigned char *content; unsigned seqhdrlen; size_t content_length; BIO *sigbio; contents = cursig->d.sign->contents; seqhdrlen = asn1_simple_hdr_len(contents->d.other->value.sequence->data, contents->d.other->value.sequence->length); content = contents->d.other->value.sequence->data + seqhdrlen; content_length = contents->d.other->value.sequence->length - seqhdrlen; if ((sigbio = PKCS7_dataInit(sig, NULL)) == NULL) { printf("PKCS7_dataInit failed\n"); return 0; /* FAILED */ } BIO_write(sigbio, content, content_length); (void)BIO_flush(sigbio); if (!PKCS7_dataFinal(sig, sigbio)) { printf("PKCS7_dataFinal failed\n"); return 0; /* FAILED */ } BIO_free_all(sigbio); if (!PKCS7_set_content(sig, PKCS7_dup(contents))) { printf("PKCS7_set_content failed\n"); return 0; /* FAILED */ } return 1; /* OK */ } static int set_indirect_data_blob(PKCS7 *sig, BIO *hash, file_type_t type, char *indata, GLOBAL_OPTIONS *options, FILE_HEADER *header) { static unsigned char buf[64*1024]; u_char *p = NULL; int len = 0; if (!get_indirect_data_blob(&p, &len, options, header, type, indata)) return 0; /* FAILED */ memcpy(buf, p, len); OPENSSL_free(p); if (!set_signing_blob(sig, hash, buf, len)) return 0; /* FAILED */ return 1; /* OK */ } static unsigned int pe_calc_checksum(BIO *bio, FILE_HEADER *header) { unsigned int checkSum = 0; unsigned short val; size_t size = 0; unsigned short *buf; int nread; /* recalculate the checksum */ buf = OPENSSL_malloc(sizeof(unsigned short)*32768); (void)BIO_seek(bio, 0); while ((nread = BIO_read(bio, buf, sizeof(unsigned short)*32768)) > 0) { int i; for (i = 0; i < nread / 2; i++) { val = buf[i]; if (size == header->header_size + 88 || size == header->header_size + 90) val = 0; checkSum += val; checkSum = 0xffff & (checkSum + (checkSum >> 0x10)); size += 2; } } OPENSSL_free(buf); checkSum = 0xffff & (checkSum + (checkSum >> 0x10)); checkSum += size; return checkSum; } static void pe_recalc_checksum(BIO *bio, FILE_HEADER *header) { unsigned int checkSum = pe_calc_checksum(bio, header); char buf[4]; /* write back checksum */ (void)BIO_seek(bio, header->header_size + 88); PUT_UINT32_LE(checkSum, buf); BIO_write(bio, buf, 4); } static int verify_leaf_hash(X509 *leaf, const char *leafhash) { int ret = 1; unsigned char *mdbuf = NULL, *certbuf, *tmp; unsigned char cmdbuf[EVP_MAX_MD_SIZE]; char hexbuf[EVP_MAX_MD_SIZE*2+1]; const EVP_MD *md; long mdlen = 0; EVP_MD_CTX *ctx; size_t certlen; /* decode the provided hash */ char *mdid = OPENSSL_strdup(leafhash); char *hash = strchr(mdid, ':'); if (hash == NULL) { printf("\nUnable to parse -require-leaf-hash parameter: %s\n", leafhash); goto out; } *hash++ = '\0'; md = EVP_get_digestbyname(mdid); if (md == NULL) { printf("\nUnable to lookup digest by name '%s'\n", mdid); goto out; } mdbuf = OPENSSL_hexstr2buf(hash, &mdlen); if (mdlen != EVP_MD_size(md)) { printf("\nHash length mismatch: '%s' digest must be %d bytes long (got %ld bytes)\n", mdid, EVP_MD_size(md), mdlen); goto out; } /* compute the leaf certificate hash */ ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, md, NULL); certlen = i2d_X509(leaf, NULL); certbuf = OPENSSL_malloc(certlen); tmp = certbuf; i2d_X509(leaf, &tmp); EVP_DigestUpdate(ctx, certbuf, certlen); OPENSSL_free(certbuf); EVP_DigestFinal_ex(ctx, cmdbuf, NULL); EVP_MD_CTX_destroy(ctx); /* compare the provided hash against the computed hash */ if (memcmp(mdbuf, cmdbuf, EVP_MD_size(md))) { tohex(cmdbuf, hexbuf, EVP_MD_size(md)); printf("\nHash value mismatch: %s computed\n", hexbuf); goto out; } ret = 0; /* OK */ out: OPENSSL_free(mdid); OPENSSL_free(mdbuf); return ret; } static int asn1_print_time(const ASN1_TIME *time) { BIO *bp; if ((time == NULL) || (!ASN1_TIME_check(time))) { printf("N/A\n"); return 0; /* FAILED */ } bp = BIO_new_fp(stdout, BIO_NOCLOSE); ASN1_TIME_print(bp, time); BIO_free(bp); printf("\n"); return 1; /* OK */ } static int print_time_t(const time_t time) { ASN1_TIME *s; int ret; if (time == INVALID_TIME) { printf("N/A\n"); return 0; /* FAILED */ } if ((s = ASN1_TIME_set(NULL, time)) == NULL) { printf("N/A\n"); return 0; /* FAILED */ } ret = asn1_print_time(s); ASN1_TIME_free(s); return ret; } static time_t asn1_get_time_t(ASN1_TIME *s) { struct tm tm; if (ASN1_TIME_to_tm(s, &tm)) { return mktime(&tm); } else { return INVALID_TIME; } } static int print_cert(X509 *cert, int i) { char *subject, *issuer, *serial; BIGNUM *serialbn; subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); serialbn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), NULL); serial = BN_bn2hex(serialbn); if (i > 0) printf("\t------------------\n"); printf("\tSigner #%d:\n\t\tSubject: %s\n\t\tIssuer : %s\n\t\tSerial : %s\n\t\tCertificate expiration date:\n", i, subject, issuer, serial); printf("\t\t\tnotBefore : "); asn1_print_time(X509_get0_notBefore(cert)); printf("\t\t\tnotAfter : "); asn1_print_time(X509_get0_notAfter(cert)); OPENSSL_free(subject); OPENSSL_free(issuer); BN_free(serialbn); OPENSSL_free(serial); return 1; /* OK */ } static X509 *find_signer(PKCS7 *p7, char *leafhash, int *leafok) { STACK_OF(X509) *signers; X509 *cert = NULL; int ret = 0; /* * retrieve the signer's certificate from p7, * search only internal certificates if it was requested */ signers = PKCS7_get0_signers(p7, NULL, 0); if (!signers || sk_X509_num(signers) != 1) { printf("PKCS7_get0_signers error\n"); goto out; } printf("Signer's certificate:\n"); cert = sk_X509_value(signers, 0); if ((cert == NULL) || (!print_cert(cert, 0))) goto out; if (leafhash != NULL && *leafok == 0) *leafok = verify_leaf_hash(cert, leafhash) == 0; ret = 1; /* OK */ out: if (!ret) ERR_print_errors_fp(stdout); sk_X509_free(signers); return cert; } static int print_certs(PKCS7 *p7) { X509 *cert; int i, count; count = sk_X509_num(p7->d.sign->cert); printf("\nNumber of certificates: %d\n", count); for (i=0; id.sign->cert, i); if ((cert == NULL) || (!print_cert(cert, i))) return 0; /* FAILED */ } return 1; /* OK */ } static time_t si_get_time(PKCS7_SIGNER_INFO *si) { STACK_OF(X509_ATTRIBUTE) *auth_attr; X509_ATTRIBUTE *attr; ASN1_OBJECT *object; ASN1_UTCTIME *time = NULL; time_t posix_time; char object_txt[128]; int i; auth_attr = PKCS7_get_signed_attributes(si); /* cont[0] */ if (auth_attr) for (i=0; idata; token = d2i_TimeStampToken(NULL, &p, (*pos)->length); if (token) { asn1_time = token->time; posix_time = asn1_get_time_t(asn1_time); TimeStampToken_free(token); } } return posix_time; } static int verify_callback(int ok, X509_STORE_CTX *ctx) { int error = X509_STORE_CTX_get_error(ctx); int depth = X509_STORE_CTX_get_error_depth(ctx); if (!ok && error == X509_V_ERR_CERT_HAS_EXPIRED) { if (depth == 0) { printf("\nWarning: Ignoring expired signer certificate for CRL validation\n"); return 1; } else { X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx); printf("\nErrror: Expired CA certificate:\n"); print_cert(current_cert, 0); printf("\n"); } } return ok; } static int load_crlfile_lookup(X509_STORE *store, char *certs, char *crl) { X509_LOOKUP *lookup; X509_VERIFY_PARAM *param; lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); if (!lookup) return 0; /* FAILED */ if (!X509_load_cert_file(lookup, certs, X509_FILETYPE_PEM)) { printf("\nError: no certificate found\n"); return 0; /* FAILED */ } if (crl && !X509_load_crl_file(lookup, crl, X509_FILETYPE_PEM)) { printf("\nError: no CRL found in %s\n", crl); return 0; /* FAILED */ } param = X509_STORE_get0_param(store); if (param == NULL) return 0; /* FAILED */ /* enable CRL checking for the certificate chain leaf certificate */ if (!X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK)) return 0; /* FAILED */ if (!X509_STORE_set1_param(store, param)) return 0; /* FAILED */ X509_STORE_set_verify_cb(store, verify_callback); return 1; /* OK */ } static int load_file_lookup(X509_STORE *store, char *certs) { X509_LOOKUP *lookup; X509_VERIFY_PARAM *param; lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); if (!lookup) return 0; /* FAILED */ if (!X509_load_cert_file(lookup, certs, X509_FILETYPE_PEM)) { printf("\nError: no certificate found\n"); return 0; /* FAILED */ } param = X509_STORE_get0_param(store); if (param == NULL) return 0; /* FAILED */ if (!X509_VERIFY_PARAM_set_purpose(param, X509_PURPOSE_ANY)) return 0; /* FAILED */ if (!X509_STORE_set1_param(store, param)) return 0; /* FAILED */ return 1; /* OK */ } static int set_store_time(X509_STORE *store, time_t time) { X509_VERIFY_PARAM *param; param = X509_STORE_get0_param(store); if (param == NULL) return 0; /* FAILED */ X509_VERIFY_PARAM_set_time(param, time); if (!X509_STORE_set1_param(store, param)) return 0; /* FAILED */ return 1; /* OK */ } static int cms_print_timestamp(CMS_ContentInfo *cms, time_t time) { STACK_OF(CMS_SignerInfo) *sinfos; CMS_SignerInfo *si; int md_nid; ASN1_INTEGER *serialno; char *issuer_name, *serial; BIGNUM *serialbn; X509_ALGOR *pdig; X509_NAME *issuer; sinfos = CMS_get0_SignerInfos(cms); if (sinfos == NULL) return 0; /* FAILED */ si = sk_CMS_SignerInfo_value(sinfos, 0); if (si == NULL) return 0; /* FAILED */ printf("\nThe signature is timestamped: "); print_time_t(time); CMS_SignerInfo_get0_algs(si, NULL, NULL, &pdig, NULL); if (pdig == NULL || pdig->algorithm == NULL) return 0; /* FAILED */ md_nid = OBJ_obj2nid(pdig->algorithm); printf("Hash Algorithm: %s\n", (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid)); if (!CMS_SignerInfo_get0_signer_id(si, NULL, &issuer, &serialno)) return 0; /* FAILED */ issuer_name = X509_NAME_oneline(issuer, NULL, 0); serialbn = ASN1_INTEGER_to_BN(serialno, NULL); serial = BN_bn2hex(serialbn); printf("Timestamp Verified by:\n\t\tIssuer : %s\n\t\tSerial : %s\n", issuer_name, serial); OPENSSL_free(issuer_name); BN_free(serialbn); OPENSSL_free(serial); return 1; /* OK */ } /* * Create new CMS_ContentInfo struct for Authenticode Timestamp. * This struct does not contain any TimeStampToken as specified in RFC 3161. */ static CMS_ContentInfo *cms_get_timestamp(PKCS7_SIGNED *p7_signed, PKCS7_SIGNER_INFO *countersignature) { CMS_ContentInfo *cms = NULL; PKCS7_SIGNER_INFO *si; PKCS7 *p7 = NULL, *content = NULL; unsigned char *p = NULL; const unsigned char *q; int i, len = 0; p7 = PKCS7_new(); si = sk_PKCS7_SIGNER_INFO_value(p7_signed->signer_info, 0); if (si == NULL) goto out; /* Create new signed PKCS7 timestamp structure. */ if (!PKCS7_set_type(p7, NID_pkcs7_signed)) goto out; if (!PKCS7_add_signer(p7, countersignature)) goto out; for (i = 0; i < sk_X509_num(p7_signed->cert); i++) { if (!PKCS7_add_certificate(p7, sk_X509_value(p7_signed->cert, i))) goto out; } /* Create new encapsulated NID_id_smime_ct_TSTInfo content. */ content = PKCS7_new(); content->d.other = ASN1_TYPE_new(); content->type = OBJ_nid2obj(NID_id_smime_ct_TSTInfo); ASN1_TYPE_set1(content->d.other, V_ASN1_OCTET_STRING, si->enc_digest); /* Add encapsulated content to signed PKCS7 timestamp structure: p7->d.sign->contents = content */ if (!PKCS7_set_content(p7, content)) { PKCS7_free(content); goto out; } /* Convert PKCS7 into CMS_ContentInfo */ if (((len = i2d_PKCS7(p7, NULL)) <= 0) || (p = OPENSSL_malloc(len)) == NULL) { printf("Failed to convert pkcs7: %d\n", len); goto out; } len = i2d_PKCS7(p7, &p); p -= len; q = p; cms = d2i_CMS_ContentInfo(NULL, &q, len); OPENSSL_free(p); out: if (!cms) ERR_print_errors_fp(stdout); PKCS7_free(p7); return cms; } /* * RFC3852: the message-digest authenticated attribute type MUST be * present when there are any authenticated attributes present */ static int print_attributes(SIGNATURE *signature, int verbose) { char hexbuf[EVP_MAX_MD_SIZE*2+1]; unsigned char *mdbuf; int len; if (!signature->digest) return 0; /* FAILED */ printf("\nAuthenticated attributes:\n"); printf("\tMessage digest algorithm: %s\n", (signature->md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2sn(signature->md_nid)); mdbuf = (unsigned char *)ASN1_STRING_get0_data(signature->digest); len = ASN1_STRING_length(signature->digest); tohex(mdbuf, hexbuf, len); printf("\tMessage digest: %s\n", hexbuf); printf("\tSigning time: "); print_time_t(signature->signtime); if (signature->purpose) { if (!memcmp(signature->purpose, purpose_comm, sizeof purpose_comm)) printf("\tMicrosoft Commercial Code Signing purpose\n"); else if (!memcmp(signature->purpose, purpose_ind, sizeof purpose_ind)) printf("\tMicrosoft Individual Code Signing purpose\n"); else printf("\tUnrecognized Code Signing purpose\n"); } if (signature->url) { printf("\tURL description: %s\n", signature->url); } if (signature->desc) { printf("\tText description: %s\n", signature->desc); } if (signature->level) { if (!memcmp(signature->level, java_attrs_low, sizeof java_attrs_low)) printf("\tLow level of permissions in Microsoft Internet Explorer 4.x for CAB files\n"); else printf("\tUnrecognized level of permissions in Microsoft Internet Explorer 4.x for CAB files\n"); } /* Unauthenticated attributes */ if (signature->timestamp) { if (!cms_print_timestamp(signature->timestamp, signature->time)) return 0; /* FAILED */ } if (signature->blob) { if (verbose) { char *data_blob; data_blob = OPENSSL_buf2hexstr(signature->blob->data, signature->blob->length); printf("\nUnauthenticated Data Blob:\n%s\n", data_blob); OPENSSL_free(data_blob); } printf("\nUnauthenticated Data Blob length: %d bytes\n",signature->blob->length); } return 1; /* OK */ } static void get_signed_attributes(SIGNATURE *signature, STACK_OF(X509_ATTRIBUTE) *auth_attr) { X509_ATTRIBUTE *attr; ASN1_OBJECT *object; ASN1_STRING *value; char object_txt[128]; const unsigned char *data; int i; for (i=0; idigest = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_OCTET_STRING, NULL); } else if (!strcmp(object_txt, PKCS9_SIGNING_TIME)) { /* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */ ASN1_UTCTIME *time; time = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL); signature->signtime = asn1_get_time_t(time); } else if (!strcmp(object_txt, SPC_SP_OPUS_INFO_OBJID)) { /* Microsoft OID: 1.3.6.1.4.1.311.2.1.12 */ SpcSpOpusInfo *opus; value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); if (value == NULL) continue; data = ASN1_STRING_get0_data(value); opus = d2i_SpcSpOpusInfo(NULL, &data, value->length); if (opus->moreInfo && opus->moreInfo->type == 0) signature->url = OPENSSL_strdup((char *)opus->moreInfo->value.url->data); if (opus->programName) { if (opus->programName->type == 0) { unsigned char *data; int len = ASN1_STRING_to_UTF8(&data, opus->programName->value.unicode); if (len >= 0) { signature->desc = OPENSSL_strndup((char *)data, len); OPENSSL_free(data); } } else { signature->desc = OPENSSL_strdup((char *)opus->programName->value.ascii->data); } } SpcSpOpusInfo_free(opus); } else if (!strcmp(object_txt, SPC_STATEMENT_TYPE_OBJID)) { /* Microsoft OID: 1.3.6.1.4.1.311.2.1.11 */ value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); if (value == NULL) continue; signature->purpose = (char *)ASN1_STRING_get0_data(value); } else if (!strcmp(object_txt, MS_JAVA_SOMETHING)) { /* Microsoft OID: 1.3.6.1.4.1.311.15.1 */ value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); if (value == NULL) continue; signature->level = (char *)ASN1_STRING_get0_data(value); } } } void signature_free(SIGNATURE *signature) { if (signature->timestamp) { CMS_ContentInfo_free(signature->timestamp); ERR_clear_error(); } PKCS7_free(signature->p7); /* If memory has not been allocated nothing is done */ OPENSSL_free(signature->url); OPENSSL_free(signature->desc); OPENSSL_free(signature); } static int append_signature_list(STACK_OF(SIGNATURE) **signatures, PKCS7 *p7, int allownest); static void get_unsigned_attributes(STACK_OF(SIGNATURE) **signatures, SIGNATURE *signature, STACK_OF(X509_ATTRIBUTE) *unauth_attr, PKCS7 *p7, int allownest) { X509_ATTRIBUTE *attr; ASN1_OBJECT *object; ASN1_STRING *value; char object_txt[128]; const unsigned char *data; int i, j; for (i=0; ilength); if (countersi == NULL) continue; time = si_get_time(countersi); if (time != INVALID_TIME) { timestamp = cms_get_timestamp(p7->d.sign, countersi); if (timestamp) { signature->time = time; signature->timestamp = timestamp; } else { printf("Error: Authenticode Timestamp could not be decoded correctly\n\n"); PKCS7_SIGNER_INFO_free(countersi); } } else { printf("Error: PKCS9_TIMESTAMP_SIGNING_TIME attribute not found\n\n"); PKCS7_SIGNER_INFO_free(countersi); } } else if (!strcmp(object_txt, SPC_RFC3161_OBJID)) { /* RFC3161 Timestamp - Policy OID: 1.3.6.1.4.1.311.3.3.1 */ CMS_ContentInfo *timestamp = NULL; time_t time; value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); if (value == NULL) continue; data = ASN1_STRING_get0_data(value); timestamp = d2i_CMS_ContentInfo(NULL, &data, value->length); if (timestamp) { time = cms_get_time(timestamp); if (time != INVALID_TIME) { signature->time = time; signature->timestamp = timestamp; } else { printf("Error: Corrupt RFC3161 Timestamp embedded content\n\n"); ERR_print_errors_fp(stdout); } } else { printf("Error: RFC3161 Timestamp could not be decoded correctly\n\n"); ERR_print_errors_fp(stdout); } } else if (allownest && !strcmp(object_txt, SPC_NESTED_SIGNATURE_OBJID)) { /* Nested Signature - Policy OID: 1.3.6.1.4.1.311.2.4.1 */ PKCS7 *nested; for (j=0; jlength); if (nested) (void)append_signature_list(signatures, nested, 0); } } else if (!strcmp(object_txt, SPC_UNAUTHENTICATED_DATA_BLOB_OBJID)) { /* Unauthenticated Data Blob - Policy OID: 1.3.6.1.4.1.42921.1.2.1 */ signature->blob = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTF8STRING, NULL); } else printf("Unsupported Policy OID: %s\n\n", object_txt); } } static int append_signature_list(STACK_OF(SIGNATURE) **signatures, PKCS7 *p7, int allownest) { SIGNATURE *signature = NULL; PKCS7_SIGNER_INFO *si; STACK_OF(X509_ATTRIBUTE) *auth_attr, *unauth_attr; si = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0); if (si == NULL) return 0; /* FAILED */ signature = OPENSSL_malloc(sizeof(SIGNATURE)); signature->p7 = p7; signature->md_nid = OBJ_obj2nid(si->digest_alg->algorithm); signature->digest = NULL; signature->signtime = INVALID_TIME; signature->url = NULL; signature->desc = NULL; signature->purpose = NULL; signature->level = NULL; signature->timestamp = NULL; signature->time = INVALID_TIME; signature->blob = NULL; auth_attr = PKCS7_get_signed_attributes(si); /* cont[0] */ if (auth_attr) get_signed_attributes(signature, auth_attr); unauth_attr = PKCS7_get_attributes(si); /* cont[1] */ if (unauth_attr) get_unsigned_attributes(signatures, signature, unauth_attr, p7, allownest); if (!sk_SIGNATURE_unshift(*signatures, signature)) { signature_free(signature); return 0; /* FAILED */ } return 1; /* OK */ } /* * compare the hash provided from the TSTInfo object against the hash computed * from the signature created by the signing certificate's private key */ static int TST_verify(CMS_ContentInfo *timestamp, PKCS7_SIGNER_INFO *si) { ASN1_OCTET_STRING *hash, **pos; TimeStampToken *token = NULL; const unsigned char *p = NULL; unsigned char mdbuf[EVP_MAX_MD_SIZE]; char hexbuf[EVP_MAX_MD_SIZE*2+1]; const EVP_MD *md; EVP_MD_CTX *mdctx; int md_nid; pos = CMS_get0_content(timestamp); if (pos != NULL && *pos != NULL) { p = (*pos)->data; token = d2i_TimeStampToken(NULL, &p, (*pos)->length); if (token) { /* compute a hash from the encrypted message digest value of the file */ md_nid = OBJ_obj2nid(token->messageImprint->digestAlgorithm->algorithm); md = EVP_get_digestbynid(md_nid); mdctx = EVP_MD_CTX_new(); EVP_DigestInit(mdctx, md); EVP_DigestUpdate(mdctx, si->enc_digest->data, si->enc_digest->length); EVP_DigestFinal(mdctx, mdbuf, NULL); EVP_MD_CTX_free(mdctx); /* compare the provided hash against the computed hash */ hash = token->messageImprint->digest; /* hash->length == EVP_MD_size(md) */ if (memcmp(mdbuf, hash->data, hash->length)) { tohex(mdbuf, hexbuf, EVP_MD_size(md)); printf("Hash value mismatch:\n\tMessage digest algorithm: %s\n", (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid)); printf("\tComputed message digest : %s\n", hexbuf); tohex(hash->data, hexbuf, hash->length); printf("\tReceived message digest : %s\n" , hexbuf); printf("File's message digest verification: failed\n"); TimeStampToken_free(token); return 0; /* FAILED */ } /* else Computed and received message digests matched */ TimeStampToken_free(token); } else /* our CMS_ContentInfo struct created for Authenticode Timestamp * does not contain any TimeStampToken as specified in RFC 3161 */ ERR_clear_error(); } return 1; /* OK */ } static int append_nested_signature(STACK_OF(X509_ATTRIBUTE) **unauth_attr, u_char *p, int len) { X509_ATTRIBUTE *attr = NULL; int nid = OBJ_txt2nid(SPC_NESTED_SIGNATURE_OBJID); if (*unauth_attr == NULL) { if ((*unauth_attr = sk_X509_ATTRIBUTE_new_null()) == NULL) return 0; /* FAILED */ } else { /* try to find SPC_NESTED_SIGNATURE_OBJID attribute */ int i; for (i = 0; i < sk_X509_ATTRIBUTE_num(*unauth_attr); i++) { attr = sk_X509_ATTRIBUTE_value(*unauth_attr, i); if (OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)) == nid) { /* append p to the V_ASN1_SEQUENCE */ if (!X509_ATTRIBUTE_set1_data(attr, V_ASN1_SEQUENCE, p, len)) return 0; /* FAILED */ return 1; /* OK */ } } } /* create new unauthorized SPC_NESTED_SIGNATURE_OBJID attribute */ if (!(attr = X509_ATTRIBUTE_create_by_NID(NULL, nid, V_ASN1_SEQUENCE, p, len))) return 0; /* FAILED */ if (!sk_X509_ATTRIBUTE_push(*unauth_attr, attr)) { X509_ATTRIBUTE_free(attr); return 0; /* FAILED */ } return 1; /* OK */ } /* * pkcs7_set_nested_signature adds the p7nest signature to p7 * as a nested signature (SPC_NESTED_SIGNATURE). */ static int pkcs7_set_nested_signature(PKCS7 *p7, PKCS7 *p7nest, time_t signing_time) { u_char *p = NULL; int len = 0; PKCS7_SIGNER_INFO *si; if (((len = i2d_PKCS7(p7nest, NULL)) <= 0) || (p = OPENSSL_malloc(len)) == NULL) return 0; i2d_PKCS7(p7nest, &p); p -= len; si = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0); pkcs7_add_signing_time(si, signing_time); if (!append_nested_signature(&(si->unauth_attr), p, len)) { OPENSSL_free(p); return 0; } OPENSSL_free(p); return 1; } static char *get_clrdp_url(X509 *cert) { STACK_OF(DIST_POINT) *crldp; DIST_POINT *dp; GENERAL_NAMES *gens; GENERAL_NAME *gen; int i, j, gtype; ASN1_STRING *uri; char *url = NULL; crldp = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL); if (!crldp) return NULL; for (i = 0; i < sk_DIST_POINT_num(crldp); i++) { dp = sk_DIST_POINT_value(crldp, i); if (!dp->distpoint || dp->distpoint->type != 0) continue; gens = dp->distpoint->name.fullname; for (j = 0; j < sk_GENERAL_NAME_num(gens); j++) { gen = sk_GENERAL_NAME_value(gens, j); uri = GENERAL_NAME_get0_value(gen, >ype); if (gtype == GEN_URI && ASN1_STRING_length(uri) > 6) { url = OPENSSL_strdup((const char *)ASN1_STRING_get0_data(uri)); if (strncmp(url, "http://", 7) == 0) goto out; } } } out: sk_DIST_POINT_pop_free(crldp, DIST_POINT_free); return url; } static int verify_crl(char *ca_file, char *crl_file, STACK_OF(X509_CRL) *crls, X509 *signer, STACK_OF(X509) *chain) { X509_STORE *store = NULL; X509_STORE_CTX *ctx = NULL; int verok = 0; ctx = X509_STORE_CTX_new(); if (!ctx) goto out; store = X509_STORE_new(); if (!store) goto out; if (!load_crlfile_lookup(store, ca_file, crl_file)) goto out; /* initialise an X509_STORE_CTX structure for subsequent use by X509_verify_cert()*/ if (!X509_STORE_CTX_init(ctx, store, signer, chain)) goto out; /* set an additional CRLs */ if (crls) X509_STORE_CTX_set0_crls(ctx, crls); if (X509_verify_cert(ctx) <= 0) { int error = X509_STORE_CTX_get_error(ctx); printf("\nX509_verify_cert: certificate verify error: %s\n", X509_verify_cert_error_string(error)); goto out; } verok = 1; /* OK */ out: if (!verok) ERR_print_errors_fp(stdout); /* NULL is a valid parameter value for X509_STORE_free() and X509_STORE_CTX_free() */ X509_STORE_free(store); X509_STORE_CTX_free(ctx); return verok; } static int verify_timestamp(SIGNATURE *signature, GLOBAL_OPTIONS *options) { X509_STORE *store; STACK_OF(CMS_SignerInfo) *sinfos; CMS_SignerInfo *cmssi; X509 *signer; STACK_OF(X509_CRL) *crls; char *url; PKCS7_SIGNER_INFO *si; int verok = 0; store = X509_STORE_new(); if (!store) goto out; if (load_file_lookup(store, options->tsa_cafile)) { /* * The TSA signing key MUST be of a sufficient length to allow for a sufficiently * long lifetime. Even if this is done, the key will have a finite lifetime. * Thus, any token signed by the TSA SHOULD be time-stamped again or notarized * at a later date to renew the trust that exists in the TSA's signature. * https://tools.ietf.org/html/rfc3161 */ if (!options->timestamp_expiration) /* verify timestamp against the time of its creation */ if (!set_store_time(store, signature->time)) { printf("Failed to set store time\n"); X509_STORE_free(store); goto out; } } else { printf("Use the \"-TSA-CAfile\" option to add the Time-Stamp Authority certificates bundle to verify timestamp server.\n"); X509_STORE_free(store); goto out; } /* verify a CMS SignedData structure */ if (!CMS_verify(signature->timestamp, NULL, store, 0, NULL, 0)) { printf("\nCMS_verify error\n"); X509_STORE_free(store); goto out; } X509_STORE_free(store); sinfos = CMS_get0_SignerInfos(signature->timestamp); cmssi = sk_CMS_SignerInfo_value(sinfos, 0); CMS_SignerInfo_get0_algs(cmssi, NULL, &signer, NULL, NULL); url = get_clrdp_url(signer); if (url) { printf("TSA's CRL distribution point: %s\n", url); OPENSSL_free(url); } printf("\n"); /* verify a Certificate Revocation List */ crls = signature->p7->d.sign->crl; if (options->tsa_crlfile || crls) { STACK_OF(X509) *chain = CMS_get1_certs(signature->timestamp); int crlok = verify_crl(options->tsa_cafile, options->tsa_crlfile, crls, signer, chain); sk_X509_pop_free(chain, X509_free); printf("Timestamp Server Signature CRL verification: %s\n", crlok ? "ok" : "failed"); if (!crlok) goto out; } else printf("\n"); /* check extended key usage flag XKU_TIMESTAMP */ if (!(X509_get_extended_key_usage(signer) & XKU_TIMESTAMP)) { printf("Unsupported Signer's certificate purpose XKU_TIMESTAMP\n"); goto out; } /* verify the hash provided from the trusted timestamp */ si = sk_PKCS7_SIGNER_INFO_value(signature->p7->d.sign->signer_info, 0); if (!TST_verify(signature->timestamp, si)) goto out; verok = 1; /* OK */ out: if (!verok) ERR_print_errors_fp(stdout); return verok; } static int verify_authenticode(SIGNATURE *signature, GLOBAL_OPTIONS *options, X509 *signer) { X509_STORE *store; STACK_OF(X509_CRL) *crls; BIO *bio = NULL; int verok = 0; store = X509_STORE_new(); if (!store) goto out; if (!load_file_lookup(store, options->cafile)) { printf("Failed to add store lookup file\n"); X509_STORE_free(store); goto out; } if (signature->time != INVALID_TIME && !set_store_time(store, signature->time)) { printf("Failed to set store time\n"); X509_STORE_free(store); goto out; } /* verify a PKCS#7 signedData structure */ if (signature->p7->d.sign->contents->d.other->type == V_ASN1_SEQUENCE) { /* only verify the contents of the sequence */ size_t seqhdrlen; seqhdrlen = asn1_simple_hdr_len(signature->p7->d.sign->contents->d.other->value.sequence->data, signature->p7->d.sign->contents->d.other->value.sequence->length); bio = BIO_new_mem_buf(signature->p7->d.sign->contents->d.other->value.sequence->data + seqhdrlen, signature->p7->d.sign->contents->d.other->value.sequence->length - seqhdrlen); } else { /* verify the entire value */ bio = BIO_new_mem_buf(signature->p7->d.sign->contents->d.other->value.sequence->data, signature->p7->d.sign->contents->d.other->value.sequence->length); } if (!PKCS7_verify(signature->p7, NULL, store, bio, NULL, 0)) { printf("\nPKCS7_verify error\n"); X509_STORE_free(store); BIO_free(bio); goto out; } X509_STORE_free(store); BIO_free(bio); /* verify a Certificate Revocation List */ crls = signature->p7->d.sign->crl; if (options->crlfile || crls) { STACK_OF(X509) *chain = signature->p7->d.sign->cert; int crlok = verify_crl(options->cafile, options->crlfile, crls, signer, chain); printf("Signature CRL verification: %s\n", crlok ? "ok" : "failed"); if (!crlok) goto out; } /* check extended key usage flag XKU_CODE_SIGN */ if (!(X509_get_extended_key_usage(signer) & XKU_CODE_SIGN)) { printf("Unsupported Signer's certificate purpose XKU_CODE_SIGN\n"); goto out; } verok = 1; /* OK */ out: if (!verok) ERR_print_errors_fp(stdout); return verok; } static int verify_signature(SIGNATURE *signature, GLOBAL_OPTIONS *options) { int leafok = 0, verok; X509 *signer; char *url; signer = find_signer(signature->p7, options->leafhash, &leafok); if (!signer) { printf("Find signer error\n"); return 1; /* FAILED */ } if (!print_certs(signature->p7)) printf("Print certs error\n"); if (!print_attributes(signature, options->verbose)) printf("Print attributes error\n"); if (options->leafhash != NULL) { printf("\nLeaf hash match: %s\n", leafok ? "ok" : "failed"); if (!leafok) { printf("Signature verification: failed\n\n"); return 1; /* FAILED */ } } if (options->catalog) printf("\nFile is signed in catalog: %s\n", options->catalog); printf("\nCAfile: %s\n", options->cafile); if (options->crlfile) printf("CRLfile: %s\n", options->crlfile); if (options->tsa_cafile) printf("TSA's certificates file: %s\n", options->tsa_cafile); if (options->tsa_crlfile) printf("TSA's CRL file: %s\n", options->tsa_crlfile); url = get_clrdp_url(signer); if (url) { printf("CRL distribution point: %s\n", url); OPENSSL_free(url); } if (signature->timestamp) { int timeok = verify_timestamp(signature, options); printf("Timestamp Server Signature verification: %s\n", timeok ? "ok" : "failed"); if (!timeok) { signature->time = INVALID_TIME; } } else printf("\nTimestamp is not available\n\n"); verok = verify_authenticode(signature, options, signer); printf("Signature verification: %s\n\n", verok ? "ok" : "failed"); if (!verok) return 1; /* FAILED */ return 0; /* OK */ } /* * MSI file support * https://msdn.microsoft.com/en-us/library/dd942138.aspx */ static int msi_verify_header(char *indata, char *infile, size_t filesize, MSI_PARAMS *msiparams) { int ret = 1; MSI_ENTRY *root; MSI_FILE_HDR *hdr; msiparams->msi = msi_file_new(indata, filesize); if (!msiparams->msi) { printf("Corrupt MSI file: %s\n", infile); return 0; /* FAILED */ } root = msi_root_entry_get(msiparams->msi); msiparams->dirent = msi_dirent_new(msiparams->msi, root, NULL); hdr = msi_header_get(msiparams->msi); /* Minor Version field SHOULD be set to 0x003E. * Major Version field MUST be set to either 0x0003 (version 3) or 0x0004 (version 4). */ if (hdr->majorVersion != 0x0003 && hdr->majorVersion != 0x0004) { printf("Unknown Major Version: 0x%04X\n", hdr->majorVersion); ret = 0; /* FAILED */ } /* Byte Order field MUST be set to 0xFFFE, specifies little-endian byte order. */ if (hdr->byteOrder != 0xFFFE) { printf("Unknown Byte Order: 0x%04X\n", hdr->byteOrder); ret = 0; /* FAILED */ } /* Sector Shift field MUST be set to 0x0009, or 0x000c, depending on the Major Version field. * This field specifies the sector size of the compound file as a power of 2. */ if ((hdr->majorVersion == 0x0003 && hdr->sectorShift != 0x0009) || (hdr->majorVersion == 0x0004 && hdr->sectorShift != 0x000C)) { printf("Unknown Sector Shift: 0x%04X\n", hdr->sectorShift); ret = 0; /* FAILED */ } /* Mini Sector Shift field MUST be set to 0x0006. * This field specifies the sector size of the Mini Stream as a power of 2. * The sector size of the Mini Stream MUST be 64 bytes. */ if (hdr->miniSectorShift != 0x0006) { printf("Unknown Mini Sector Shift: 0x%04X\n", hdr->miniSectorShift); ret = 0; /* FAILED */ } /* Number of Directory Sectors field contains the count of the number * of directory sectors in the compound file. * If Major Version is 3, the Number of Directory Sectors MUST be zero. */ if (hdr->majorVersion == 0x0003 && hdr->numDirectorySector != 0x00000000) { printf("Unsupported Number of Directory Sectors: 0x%08X\n", hdr->numDirectorySector); ret = 0; /* FAILED */ } /* Mini Stream Cutoff Size field MUST be set to 0x00001000. * This field specifies the maximum size of a user-defined data stream that is allocated * from the mini FAT and mini stream, and that cutoff is 4,096 bytes. * Any user-defined data stream that is greater than or equal to this cutoff size * must be allocated as normal sectors from the FAT. */ if (hdr->miniStreamCutoffSize != 0x00001000) { printf("Unsupported Mini Stream Cutoff Size: 0x%08X\n", hdr->miniStreamCutoffSize); ret = 0; /* FAILED */ } return ret; } static int msi_verify_pkcs7(SIGNATURE *signature, MSI_FILE *msi, MSI_DIRENT *dirent, char *exdata, uint32_t exlen, GLOBAL_OPTIONS *options) { int ret = 1, mdok, mdtype = -1; unsigned char mdbuf[EVP_MAX_MD_SIZE]; unsigned char cmdbuf[EVP_MAX_MD_SIZE]; unsigned char cexmdbuf[EVP_MAX_MD_SIZE]; char hexbuf[EVP_MAX_MD_SIZE*2+1]; const EVP_MD *md; BIO *hash; if (is_content_type(signature->p7, SPC_INDIRECT_DATA_OBJID)) { ASN1_STRING *content_val = signature->p7->d.sign->contents->d.other->value.sequence; const unsigned char *p = content_val->data; SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); if (idc) { if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) { mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm); memcpy(mdbuf, idc->messageDigest->digest->data, idc->messageDigest->digest->length); } SpcIndirectDataContent_free(idc); } } if (mdtype == -1) { printf("Failed to extract current message digest\n\n"); goto out; } printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype)); md = EVP_get_digestbynid(mdtype); hash = BIO_new(BIO_f_md()); BIO_set_md(hash, md); BIO_push(hash, BIO_new(BIO_s_null())); if (exdata) { BIO *prehash = BIO_new(BIO_f_md()); BIO_set_md(prehash, md); BIO_push(prehash, BIO_new(BIO_s_null())); tohex((unsigned char *)exdata, hexbuf, exlen); printf("Current MsiDigitalSignatureEx : %s\n", hexbuf); if (!msi_prehash_dir(dirent, prehash, 1)) { printf("Failed to calculate pre-hash used for MsiDigitalSignatureEx\n\n"); BIO_free_all(hash); BIO_free_all(prehash); goto out; } BIO_gets(prehash, (char*)cexmdbuf, EVP_MAX_MD_SIZE); BIO_free_all(prehash); BIO_write(hash, (char*)cexmdbuf, EVP_MD_size(md)); tohex(cexmdbuf, hexbuf, EVP_MD_size(md)); printf("Calculated MsiDigitalSignatureEx : %s\n", hexbuf); } if (!msi_hash_dir(msi, dirent, hash, 1)) { printf("Failed to calculate DigitalSignature\n\n"); BIO_free_all(hash); goto out; } tohex(mdbuf, hexbuf, EVP_MD_size(md)); printf("Current DigitalSignature : %s\n", hexbuf); BIO_gets(hash, (char*)cmdbuf, EVP_MAX_MD_SIZE); BIO_free_all(hash); tohex(cmdbuf, hexbuf, EVP_MD_size(md)); mdok = !memcmp(mdbuf, cmdbuf, EVP_MD_size(md)); printf("Calculated DigitalSignature : %s%s\n\n", hexbuf, mdok ? "" : " MISMATCH!!!"); if (!mdok) { printf("Signature verification: failed\n\n"); goto out; } ret = verify_signature(signature, options); out: if (!ret) ERR_print_errors_fp(stdout); return ret; } static int msi_verify_file(MSI_PARAMS *msiparams, GLOBAL_OPTIONS *options) { int i, ret = 1; char *indata = NULL; char *exdata = NULL; const unsigned char *blob; uint32_t inlen, exlen = 0; PKCS7 *p7; STACK_OF(SIGNATURE) *signatures = sk_SIGNATURE_new_null(); MSI_ENTRY *dse = NULL; MSI_ENTRY *ds = msi_signatures_get(msiparams->dirent, &dse); if (!ds) { printf("MSI file has no signature\n\n"); goto out; } inlen = GET_UINT32_LE(ds->size); indata = OPENSSL_malloc(inlen); if (!msi_file_read(msiparams->msi, ds, 0, indata, inlen)) { printf("DigitalSignature stream data error\n\n"); goto out; } if (!dse) { printf("Warning: MsiDigitalSignatureEx stream doesn't exist\n"); } else { exlen = GET_UINT32_LE(dse->size); exdata = OPENSSL_malloc(exlen); if (!msi_file_read(msiparams->msi, dse, 0, exdata, exlen)) { printf("MsiDigitalSignatureEx stream data error\n\n"); goto out; } } blob = (unsigned char *)indata; p7 = d2i_PKCS7(NULL, &blob, inlen); if (!p7) { printf("Failed to extract PKCS7 data\n\n"); goto out; } if (!append_signature_list(&signatures, p7, 1)) { printf("Failed to create signature list\n\n"); PKCS7_free(p7); goto out; } for (i = 0; i < sk_SIGNATURE_num(signatures); i++) { SIGNATURE *signature = sk_SIGNATURE_value(signatures, i); printf("Signature Index: %d %s\n", i, i==0 ? " (Primary Signature)" : ""); ret &= msi_verify_pkcs7(signature, msiparams->msi, msiparams->dirent, exdata, exlen, options); } printf("Number of verified signatures: %d\n", i); out: sk_SIGNATURE_pop_free(signatures, signature_free); OPENSSL_free(indata); OPENSSL_free(exdata); return ret; } static PKCS7 *msi_extract_existing_pkcs7(MSI_PARAMS *msiparams, MSI_ENTRY *ds, char **data, uint32_t len) { PKCS7 *p7 = NULL; const unsigned char *blob; if (!msi_file_read(msiparams->msi, ds, 0, *data, len)) { printf("DigitalSignature stream data error\n"); return NULL; } blob = (unsigned char *)*data; p7 = d2i_PKCS7(NULL, &blob, len); if (!p7) { printf("Failed to extract PKCS7 data\n"); return NULL; } return p7; } static int msi_extract_file(MSI_PARAMS *msiparams, BIO *outdata, int output_pkcs7) { int ret; PKCS7 *sig; uint32_t len; char *data; MSI_ENTRY *ds = msi_signatures_get(msiparams->dirent, NULL); if (!ds) { printf("MSI file has no signature\n\n"); return 1; /* FAILED */ } len = GET_UINT32_LE(ds->size); data = OPENSSL_malloc(len); (void)BIO_reset(outdata); sig = msi_extract_existing_pkcs7(msiparams, ds, &data, len); if (!sig) { printf("Unable to extract existing signature\n"); return 1; /* FAILED */ } if (output_pkcs7) { ret = !PEM_write_bio_PKCS7(outdata, sig); } else { ret = !BIO_write(outdata, data, len); } PKCS7_free(sig); OPENSSL_free(data); return ret; } static int msi_remove_file(MSI_PARAMS *msiparams, BIO *outdata) { if (!msi_dirent_delete(msiparams->dirent, digital_signature_ex, sizeof digital_signature_ex)) { return 1; /* FAILED */ } if (!msi_dirent_delete(msiparams->dirent, digital_signature, sizeof digital_signature)) { return 1; /* FAILED */ } if (!msi_file_write(msiparams->msi, msiparams->dirent, NULL, 0, NULL, 0, outdata)) { printf("Saving the msi file failed\n"); return 1; /* FAILED */ } return 0; /* OK */ } /* * MsiDigitalSignatureEx is an enhanced signature type that * can be used when signing MSI files. In addition to * file content, it also hashes some file metadata, specifically * file names, file sizes, creation times and modification times. * * The file content hashing part stays the same, so the * msi_handle_dir() function can be used across both variants. * * When an MsiDigitalSigntaureEx section is present in an MSI file, * the meaning of the DigitalSignature section changes: Instead * of being merely a file content hash (as what is output by the * msi_handle_dir() function), it is now hashes both content * and metadata. * * Here is how it works: * * First, a "pre-hash" is calculated. This is the "metadata" hash. * It iterates over the files in the MSI in the same order as the * file content hashing method would - but it only processes the * metadata. * * Once the pre-hash is calculated, a new hash is created for * calculating the hash of the file content. The output of the * pre-hash is added as the first element of the file content hash. * * After the pre-hash is written, what follows is the "regular" * stream of data that would normally be written when performing * file content hashing. * * The output of this hash, which combines both metadata and file * content, is what will be output in signed form to the * DigitalSignature section when in 'MsiDigitalSignatureEx' mode. * * As mentioned previously, this new mode of operation is signalled * by the presence of a 'MsiDigitalSignatureEx' section in the MSI * file. This section must come after the 'DigitalSignature' * section, and its content must be the output of the pre-hash * ("metadata") hash. */ static int msi_calc_MsiDigitalSignatureEx(MSI_PARAMS *msiparams, const EVP_MD *md, BIO *hash) { BIO *prehash = BIO_new(BIO_f_md()); BIO_set_md(prehash, md); BIO_push(prehash, BIO_new(BIO_s_null())); if (!msi_prehash_dir(msiparams->dirent, prehash, 1)) { printf("Unable to calculate MSI pre-hash ('metadata') hash\n"); return 0; /* FAILED */ } msiparams->p_msiex = OPENSSL_malloc(EVP_MAX_MD_SIZE); msiparams->len_msiex = BIO_gets(prehash, (char*)msiparams->p_msiex, EVP_MAX_MD_SIZE); BIO_write(hash, msiparams->p_msiex, msiparams->len_msiex); BIO_free_all(prehash); return 1; /* OK */ } /* * PE file support */ /* Compute a message digest value of the signed or unsigned PE file */ static void pe_calc_digest(char *indata, const EVP_MD *md, unsigned char *mdbuf, FILE_HEADER *header) { BIO *bio = NULL; static unsigned char bfb[16*1024*1024]; EVP_MD_CTX *mdctx; size_t n; size_t offset; if (header->sigpos) offset = header->sigpos; else offset = header->fileend; bio = BIO_new_mem_buf(indata, offset); mdctx = EVP_MD_CTX_new(); EVP_DigestInit(mdctx, md); memset(mdbuf, 0, EVP_MAX_MD_SIZE); (void)BIO_seek(bio, 0); BIO_read(bio, bfb, header->header_size + 88); EVP_DigestUpdate(mdctx, bfb, header->header_size + 88); BIO_read(bio, bfb, 4); BIO_read(bio, bfb, 60 + header->pe32plus * 16); EVP_DigestUpdate(mdctx, bfb, 60 + header->pe32plus * 16); BIO_read(bio, bfb, 8); n = header->header_size + 88 + 4 + 60 + header->pe32plus * 16 + 8; while (n < offset) { int l; size_t want = offset - n; if (want > sizeof bfb) want = sizeof bfb; l = BIO_read(bio, bfb, want); if (l <= 0) break; EVP_DigestUpdate(mdctx, bfb, l); n += l; } if (!header->sigpos) { /* pad (with 0's) unsigned PE file to 8 byte boundary */ int len = 8 - header->fileend % 8; if (len > 0 && len != 8) { memset(bfb, 0, len); EVP_DigestUpdate(mdctx, bfb, len); } } EVP_DigestFinal(mdctx, mdbuf, NULL); EVP_MD_CTX_free(mdctx); BIO_free(bio); } static void pe_extract_page_hash(SpcAttributeTypeAndOptionalValue *obj, unsigned char **ph, size_t *phlen, int *phtype) { const unsigned char *blob; SpcPeImageData *id; SpcSerializedObject *so; size_t l, l2; char buf[128]; *phlen = 0; blob = obj->value->value.sequence->data; id = d2i_SpcPeImageData(NULL, &blob, obj->value->value.sequence->length); if (id == NULL) return; if (id->file->type != 1) { SpcPeImageData_free(id); return; } so = id->file->value.moniker; if (so->classId->length != sizeof classid_page_hash || memcmp(so->classId->data, classid_page_hash, sizeof classid_page_hash)) { SpcPeImageData_free(id); return; } /* skip ASN.1 SET hdr */ l = asn1_simple_hdr_len(so->serializedData->data, so->serializedData->length); blob = so->serializedData->data + l; obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, so->serializedData->length - l); SpcPeImageData_free(id); if (!obj) return; *phtype = 0; buf[0] = 0x00; OBJ_obj2txt(buf, sizeof buf, obj->type, 1); if (!strcmp(buf, SPC_PE_IMAGE_PAGE_HASHES_V1)) { *phtype = NID_sha1; } else if (!strcmp(buf, SPC_PE_IMAGE_PAGE_HASHES_V2)) { *phtype = NID_sha256; } else { SpcAttributeTypeAndOptionalValue_free(obj); return; } /* Skip ASN.1 SET hdr */ l2 = asn1_simple_hdr_len(obj->value->value.sequence->data, obj->value->value.sequence->length); /* Skip ASN.1 OCTET STRING hdr */ l = asn1_simple_hdr_len(obj->value->value.sequence->data + l2, obj->value->value.sequence->length - l2); l += l2; *phlen = obj->value->value.sequence->length - l; *ph = OPENSSL_malloc(*phlen); memcpy(*ph, obj->value->value.sequence->data + l, *phlen); SpcAttributeTypeAndOptionalValue_free(obj); } static int pe_verify_pkcs7(SIGNATURE *signature, char *indata, FILE_HEADER *header, GLOBAL_OPTIONS *options) { int ret = 1, mdok, mdtype = -1, phtype = -1; unsigned char mdbuf[EVP_MAX_MD_SIZE]; unsigned char cmdbuf[EVP_MAX_MD_SIZE]; char hexbuf[EVP_MAX_MD_SIZE*2+1]; unsigned char *ph = NULL; size_t phlen = 0; const EVP_MD *md; if (is_content_type(signature->p7, SPC_INDIRECT_DATA_OBJID)) { ASN1_STRING *content_val = signature->p7->d.sign->contents->d.other->value.sequence; const unsigned char *p = content_val->data; SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); if (idc) { pe_extract_page_hash(idc->data, &ph, &phlen, &phtype); if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) { mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm); memcpy(mdbuf, idc->messageDigest->digest->data, idc->messageDigest->digest->length); } SpcIndirectDataContent_free(idc); } } if (mdtype == -1) { printf("Failed to extract current message digest\n\n"); goto out; } printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype)); md = EVP_get_digestbynid(mdtype); tohex(mdbuf, hexbuf, EVP_MD_size(md)); printf("Current message digest : %s\n", hexbuf); pe_calc_digest(indata, md, cmdbuf, header); tohex(cmdbuf, hexbuf, EVP_MD_size(md)); mdok = !memcmp(mdbuf, cmdbuf, EVP_MD_size(md)); printf("Calculated message digest : %s%s\n\n", hexbuf, mdok ? "" : " MISMATCH!!!"); if (!mdok) { printf("Signature verification: failed\n\n"); goto out; } if (phlen > 0) { size_t cphlen = 0; unsigned char *cph; printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype)); tohex(ph, hexbuf, (phlen < 32) ? phlen : 32); printf("Page hash : %s ...\n", hexbuf); cph = pe_calc_page_hash(indata, header->header_size, header->pe32plus, header->sigpos, phtype, &cphlen); tohex(cph, hexbuf, (cphlen < 32) ? cphlen : 32); mdok = (phlen = cphlen) && !memcmp(ph, cph, phlen); OPENSSL_free(cph); printf("Calculated page hash : %s ...%s\n\n", hexbuf, mdok ? "" : " MISMATCH!!!"); if (!mdok) { printf("Signature verification: failed\n\n"); goto out; } } ret = verify_signature(signature, options); out: if (!ret) ERR_print_errors_fp(stdout); OPENSSL_free(ph); return ret; } /* * pe_extract_existing_pkcs7 retrieves a decoded PKCS7 struct * corresponding to the existing signature of the PE file. */ static PKCS7 *pe_extract_existing_pkcs7(char *indata, FILE_HEADER *header) { uint32_t pos = 0; PKCS7 *p7 = NULL; while (pos < header->siglen) { uint32_t l = GET_UINT32_LE(indata + header->sigpos + pos); uint16_t certrev = GET_UINT16_LE(indata + header->sigpos + pos + 4); uint16_t certtype = GET_UINT16_LE(indata + header->sigpos + pos + 6); if (certrev == WIN_CERT_REVISION_2 && certtype == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { const unsigned char *blob = (unsigned char*)indata + header->sigpos + pos + 8; p7 = d2i_PKCS7(NULL, &blob, l - 8); } if (l%8) l += (8 - l%8); pos += l; } return p7; } static int pe_verify_file(char *indata, FILE_HEADER *header, GLOBAL_OPTIONS *options) { int i, peok = 1, ret = 1; BIO *bio; unsigned int real_pe_checksum; PKCS7 *p7; STACK_OF(SIGNATURE) *signatures = sk_SIGNATURE_new_null(); if (header->siglen == 0) header->siglen = header->fileend; /* check PE checksum */ printf("Current PE checksum : %08X\n", header->pe_checksum); bio = BIO_new_mem_buf(indata, header->sigpos + header->siglen); real_pe_checksum = pe_calc_checksum(bio, header); BIO_free(bio); if (header->pe_checksum && header->pe_checksum != real_pe_checksum) peok = 0; printf("Calculated PE checksum: %08X%s\n\n", real_pe_checksum, peok ? "" : " MISMATCH!!!"); if (header->sigpos == 0) { printf("No signature found\n\n"); goto out; } p7 = pe_extract_existing_pkcs7(indata, header); if (!p7) { printf("Failed to extract PKCS7 data\n\n"); goto out; } if (!append_signature_list(&signatures, p7, 1)) { printf("Failed to create signature list\n\n"); PKCS7_free(p7); goto out; } for (i = 0; i < sk_SIGNATURE_num(signatures); i++) { SIGNATURE *signature = sk_SIGNATURE_value(signatures, i); printf("Signature Index: %d %s\n", i, i==0 ? " (Primary Signature)" : ""); ret &= pe_verify_pkcs7(signature, indata, header, options); } printf("Number of verified signatures: %d\n", i); out: sk_SIGNATURE_pop_free(signatures, signature_free); return ret; } static int pe_extract_file(char *indata, FILE_HEADER *header, BIO *outdata, int output_pkcs7) { int ret = 0; PKCS7 *sig; (void)BIO_reset(outdata); if (output_pkcs7) { sig = pe_extract_existing_pkcs7(indata, header); if (!sig) { printf("Unable to extract existing signature\n"); return 1; /* FAILED */ } ret = !PEM_write_bio_PKCS7(outdata, sig); PKCS7_free(sig); } else ret = !BIO_write(outdata, indata + header->sigpos, header->siglen); return ret; } static int pe_verify_header(char *indata, char *infile, size_t filesize, FILE_HEADER *header) { int ret = 1; if (filesize < 64) { printf("Corrupt DOS file - too short: %s\n", infile); ret = 0; /* FAILED */ } /* SizeOfHeaders field specifies the combined size of an MS-DOS stub, PE header, * and section headers rounded up to a multiple of FileAlignment. */ header->header_size = GET_UINT32_LE(indata + 60); if (filesize < header->header_size + 160) { printf("Corrupt DOS file - too short: %s\n", infile); ret = 0; /* FAILED */ } if (memcmp(indata + header->header_size, "PE\0\0", 4)) { printf("Unrecognized DOS file type: %s\n", infile); ret = 0; /* FAILED */ } /* Magic field identifies the state of the image file. The most common number is * 0x10B, which identifies it as a normal executable file, * 0x20B identifies it as a PE32+ executable, * 0x107 identifies it as a ROM image (not supported) */ header->magic = GET_UINT16_LE(indata + header->header_size + 24); if (header->magic == 0x20b) { header->pe32plus = 1; } else if (header->magic == 0x10b) { header->pe32plus = 0; } else { printf("Corrupt PE file - found unknown magic %04X: %s\n", header->magic, infile); ret = 0; /* FAILED */ } /* The image file checksum */ header->pe_checksum = GET_UINT32_LE(indata + header->header_size + 88); /* NumberOfRvaAndSizes field specifies the number of data-directory entries * in the remainder of the optional header. Each describes a location and size. */ header->nrvas = GET_UINT32_LE(indata + header->header_size + 116 + header->pe32plus * 16); if (header->nrvas < 5) { printf("Can not handle PE files without certificate table resource: %s\n", infile); ret = 0; /* FAILED */ } /* Certificate Table field specifies the attribute certificate table address (4 bytes) and size (4 bytes) */ header->sigpos = GET_UINT32_LE(indata + header->header_size + 152 + header->pe32plus * 16); header->siglen = GET_UINT32_LE(indata + header->header_size + 152 + header->pe32plus * 16 + 4); /* Since fix for MS Bulletin MS12-024 we can really assume that signature should be last part of file */ if (header->sigpos > 0 && header->sigpos < filesize && header->sigpos + header->siglen != filesize) { printf("Corrupt PE file - current signature not at end of file: %s\n", infile); ret = 0; /* FAILED */ } return ret; } static void pe_modify_header(char *indata, FILE_HEADER *header, BIO *hash, BIO *outdata) { int len = 0, i; static char buf[64*1024]; i = header->header_size + 88; BIO_write(hash, indata, i); memset(buf, 0, 4); BIO_write(outdata, buf, 4); /* zero out checksum */ i += 4; BIO_write(hash, indata + i, 60 + header->pe32plus * 16); i += 60 + header->pe32plus * 16; memset(buf, 0, 8); BIO_write(outdata, buf, 8); /* zero out sigtable offset + pos */ i += 8; BIO_write(hash, indata + i, header->fileend - i); /* pad (with 0's) pe file to 8 byte boundary */ len = 8 - header->fileend % 8; if (len > 0 && len != 8) { memset(buf, 0, len); BIO_write(hash, buf, len); header->fileend += len; } } /* * CAB file support * https://www.file-recovery.com/cab-signature-format.htm */ static int cab_verify_header(char *indata, char *infile, size_t filesize, FILE_HEADER *header) { int ret = 1; uint32_t reserved; if (filesize < 44) { printf("Corrupt cab file - too short: %s\n", infile); ret = 0; /* FAILED */ } reserved = GET_UINT32_LE(indata + 4); if (reserved) { printf("Reserved1: 0x%08X\n", reserved); ret = 0; /* FAILED */ } /* flags specify bit-mapped values that indicate the presence of optional data */ header->flags = GET_UINT16_LE(indata + 30); #if 1 if (header->flags & FLAG_PREV_CABINET) { /* FLAG_NEXT_CABINET works */ printf("Multivolume cabinet file is unsupported: flags 0x%04X\n", header->flags); ret = 0; /* FAILED */ } #endif if (header->flags & FLAG_RESERVE_PRESENT) { /* * Additional headers is located at offset 36 (cbCFHeader, cbCFFolder, cbCFData); * size of header (4 bytes, little-endian order) must be 20 (checkpoint). */ header->header_size = GET_UINT32_LE(indata + 36); if (header->header_size != 20) { printf("Additional header size: 0x%08X\n", header->header_size); ret = 0; /* FAILED */ } reserved = GET_UINT32_LE(indata + 40); if (reserved != 0x00100000) { printf("abReserved: 0x%08X\n", reserved); ret = 0; /* FAILED */ } /* * File size is defined at offset 8, however if additional header exists, this size is not valid. * sigpos - additional data offset is located at offset 44 (from file beginning) * and consist of 4 bytes (little-endian order) * siglen - additional data size is located at offset 48 (from file beginning) * and consist of 4 bytes (little-endian order) * If there are additional headers, size of the CAB archive file is calcualted * as additional data offset plus additional data size. */ header->sigpos = GET_UINT32_LE(indata + 44); header->siglen = GET_UINT32_LE(indata + 48); if (header->sigpos < filesize && header->sigpos + header->siglen != filesize) { printf("Additional data offset:\t%u bytes\nAdditional data size:\t%u bytes\n", header->sigpos, header->siglen); printf("File size:\t\t%lu bytes\n", filesize); ret = 0; /* FAILED */ } } return ret; } /* Compute a message digest value of the signed or unsigned CAB file */ static void cab_calc_digest(char *indata, const EVP_MD *md, unsigned char *mdbuf, FILE_HEADER *header) { BIO *bio; static unsigned char bfb[16*1024*1024]; EVP_MD_CTX *mdctx; uint32_t offset, coffFiles; if (header->sigpos) offset = header->sigpos; else offset = header->fileend; bio = BIO_new_mem_buf(indata, offset); mdctx = EVP_MD_CTX_new(); EVP_DigestInit(mdctx, md); memset(mdbuf, 0, EVP_MAX_MD_SIZE); (void)BIO_seek(bio, 0); /* u1 signature[4] 4643534D MSCF: 0-3 */ BIO_read(bio, bfb, 4); EVP_DigestUpdate(mdctx, bfb, 4); /* u4 reserved1 00000000: 4-7 */ BIO_read(bio, bfb, 4); if (header->sigpos) { uint16_t nfolders, flags; /* * u4 cbCabinet - size of this cabinet file in bytes: 8-11 * u4 reserved2 00000000: 12-15 */ BIO_read(bio, bfb, 8); EVP_DigestUpdate(mdctx, bfb, 8); /* u4 coffFiles - offset of the first CFFILE entry: 16-19 */ BIO_read(bio, bfb, 4); coffFiles = GET_UINT32_LE(bfb); EVP_DigestUpdate(mdctx, bfb, 4); /* * u4 reserved3 00000000: 20-23 * u1 versionMinor 03: 24 * u1 versionMajor 01: 25 */ BIO_read(bio, bfb, 6); EVP_DigestUpdate(mdctx, bfb, 6); /* u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27 */ BIO_read(bio, bfb, 2); nfolders = GET_UINT16_LE(bfb); EVP_DigestUpdate(mdctx, bfb, 2); /* u2 cFiles - number of CFFILE entries in this cabinet: 28-29 */ BIO_read(bio, bfb, 2); EVP_DigestUpdate(mdctx, bfb, 2); /* u2 flags: 30-31 */ BIO_read(bio, bfb, 2); flags = GET_UINT16_LE(bfb); EVP_DigestUpdate(mdctx, bfb, 2); /* u2 setID must be the same for all cabinets in a set: 32-33 */ BIO_read(bio, bfb, 2); EVP_DigestUpdate(mdctx, bfb, 2); /* * u2 iCabinet - number of this cabinet file in a set: 34-35 * u2 cbCFHeader: 36-37 * u1 cbCFFolder: 38 * u1 cbCFData: 39 * u22 abReserve: 40-55 * - Additional data offset: 44-47 * - Additional data size: 48-51 */ BIO_read(bio, bfb, 22); /* u22 abReserve: 56-59 */ BIO_read(bio, bfb, 4); EVP_DigestUpdate(mdctx, bfb, 4); /* TODO */ if (flags & FLAG_PREV_CABINET) { /* szCabinetPrev */ do { BIO_read(bio, bfb, 1); EVP_DigestUpdate(mdctx, bfb, 1); } while (bfb[0]); /* szDiskPrev */ do { BIO_read(bio, bfb, 1); EVP_DigestUpdate(mdctx, bfb, 1); } while (bfb[0]); } if (flags & FLAG_NEXT_CABINET) { /* szCabinetNext */ do { BIO_read(bio, bfb, 1); EVP_DigestUpdate(mdctx, bfb, 1); } while (bfb[0]); /* szDiskNext */ do { BIO_read(bio, bfb, 1); EVP_DigestUpdate(mdctx, bfb, 1); } while (bfb[0]); } /* * (u8 * cFolders) CFFOLDER - structure contains information about * one of the folders or partial folders stored in this cabinet file */ while (nfolders) { BIO_read(bio, bfb, 8); EVP_DigestUpdate(mdctx, bfb, 8); nfolders--; } } else { /* read what's left of the unsigned PE file */ coffFiles = 8; } /* (variable) ab - the compressed data bytes */ while (coffFiles < offset) { int l; uint32_t want = offset - coffFiles; if (want > sizeof bfb) want = sizeof bfb; l = BIO_read(bio, bfb, want); if (l <= 0) break; EVP_DigestUpdate(mdctx, bfb, l); coffFiles += l; } EVP_DigestFinal(mdctx, mdbuf, NULL); EVP_MD_CTX_free(mdctx); BIO_free(bio); } static int cab_verify_pkcs7(SIGNATURE *signature, char *indata, FILE_HEADER *header, GLOBAL_OPTIONS *options) { int ret = 1, mdok, mdtype = -1; unsigned char mdbuf[EVP_MAX_MD_SIZE]; unsigned char cmdbuf[EVP_MAX_MD_SIZE]; char hexbuf[EVP_MAX_MD_SIZE*2+1]; const EVP_MD *md; if (is_content_type(signature->p7, SPC_INDIRECT_DATA_OBJID)) { ASN1_STRING *content_val = signature->p7->d.sign->contents->d.other->value.sequence; const unsigned char *p = content_val->data; SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); if (idc) { if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) { mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm); memcpy(mdbuf, idc->messageDigest->digest->data, idc->messageDigest->digest->length); } SpcIndirectDataContent_free(idc); } } if (mdtype == -1) { printf("Failed to extract current message digest\n\n"); goto out; } printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype)); md = EVP_get_digestbynid(mdtype); tohex(mdbuf, hexbuf, EVP_MD_size(md)); printf("Current message digest : %s\n", hexbuf); cab_calc_digest(indata, md, cmdbuf, header); tohex(cmdbuf, hexbuf, EVP_MD_size(md)); mdok = !memcmp(mdbuf, cmdbuf, EVP_MD_size(md)); printf("Calculated message digest : %s%s\n\n", hexbuf, mdok ? "" : " MISMATCH!!!"); if (!mdok) { printf("Signature verification: failed\n\n"); goto out; } ret = verify_signature(signature, options); out: if (!ret) ERR_print_errors_fp(stdout); return ret; } static PKCS7 *extract_existing_pkcs7(char *indata, FILE_HEADER *header) { PKCS7 *p7 = NULL; const unsigned char *blob; blob = (unsigned char*)indata + header->sigpos; p7 = d2i_PKCS7(NULL, &blob, header->siglen); return p7; } static int cab_verify_file(char *indata, FILE_HEADER *header, GLOBAL_OPTIONS *options) { int i, ret = 1; PKCS7 *p7; STACK_OF(SIGNATURE) *signatures = sk_SIGNATURE_new_null(); if (header->header_size != 20) { printf("No signature found\n\n"); goto out; } p7 = extract_existing_pkcs7(indata, header); if (!p7) { printf("Failed to extract PKCS7 data\n\n"); goto out; } if (!append_signature_list(&signatures, p7, 1)) { printf("Failed to create signature list\n\n"); PKCS7_free(p7); goto out; } for (i = 0; i < sk_SIGNATURE_num(signatures); i++) { SIGNATURE *signature = sk_SIGNATURE_value(signatures, i); printf("Signature Index: %d %s\n", i, i==0 ? " (Primary Signature)" : ""); ret &= cab_verify_pkcs7(signature, indata, header, options); } printf("Number of verified signatures: %d\n", i); out: sk_SIGNATURE_pop_free(signatures, signature_free); return ret; } static int cab_extract_file(char *indata, FILE_HEADER *header, BIO *outdata, int output_pkcs7) { int ret = 0; PKCS7 *sig; (void)BIO_reset(outdata); if (output_pkcs7) { sig = extract_existing_pkcs7(indata, header); if (!sig) { printf("Unable to extract existing signature\n"); return 1; /* FAILED */ } ret = !PEM_write_bio_PKCS7(outdata, sig); PKCS7_free(sig); } else ret = !BIO_write(outdata, indata + header->sigpos, header->siglen); return ret; } static void cab_optional_names(uint16_t flags, char *indata, BIO *outdata, int *len) { int i; i = *len; /* TODO */ if (flags & FLAG_PREV_CABINET) { /* szCabinetPrev */ while (GET_UINT8_LE(indata+i)) { BIO_write(outdata, indata+i, 1); i++; } BIO_write(outdata, indata+i, 1); i++; /* szDiskPrev */ while (GET_UINT8_LE(indata+i)) { BIO_write(outdata, indata+i, 1); i++; } BIO_write(outdata, indata+i, 1); i++; } if (flags & FLAG_NEXT_CABINET) { /* szCabinetNext */ while (GET_UINT8_LE(indata+i)) { BIO_write(outdata, indata+i, 1); i++; } BIO_write(outdata, indata+i, 1); i++; /* szDiskNext */ while (GET_UINT8_LE(indata+i)) { BIO_write(outdata, indata+i, 1); i++; } BIO_write(outdata, indata+i, 1); i++; } *len = i; } static int cab_remove_file(char *indata, FILE_HEADER *header, size_t filesize, BIO *outdata) { int i; uint32_t tmp; uint16_t nfolders, flags; static char buf[64*1024]; /* * u1 signature[4] 4643534D MSCF: 0-3 * u4 reserved1 00000000: 4-7 */ BIO_write(outdata, indata, 8); /* u4 cbCabinet - size of this cabinet file in bytes: 8-11 */ tmp = GET_UINT32_LE(indata+8) - 24; PUT_UINT32_LE(tmp, buf); BIO_write(outdata, buf, 4); /* u4 reserved2 00000000: 12-15 */ BIO_write(outdata, indata+12, 4); /* u4 coffFiles - offset of the first CFFILE entry: 16-19 */ tmp = GET_UINT32_LE(indata+16) - 24; PUT_UINT32_LE(tmp, buf); BIO_write(outdata, buf, 4); /* * u4 reserved3 00000000: 20-23 * u1 versionMinor 03: 24 * u1 versionMajor 01: 25 * u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27 * u2 cFiles - number of CFFILE entries in this cabinet: 28-29 */ BIO_write(outdata, indata+20, 10); /* u2 flags: 30-31 */ flags = GET_UINT16_LE(indata+30); PUT_UINT32_LE(flags & (FLAG_PREV_CABINET | FLAG_NEXT_CABINET), buf); BIO_write(outdata, buf, 2); /* * u2 setID must be the same for all cabinets in a set: 32-33 * u2 iCabinet - number of this cabinet file in a set: 34-35 */ BIO_write(outdata, indata+32, 4); i = 60; cab_optional_names(flags, indata, outdata, &i); /* * (u8 * cFolders) CFFOLDER - structure contains information about * one of the folders or partial folders stored in this cabinet file */ nfolders = GET_UINT16_LE(indata + 26); while (nfolders) { tmp = GET_UINT32_LE(indata+i); tmp -= 24; PUT_UINT32_LE(tmp, buf); BIO_write(outdata, buf, 4); BIO_write(outdata, indata+i+4, 4); i+=8; nfolders--; } /* Write what's left - the compressed data bytes */ BIO_write(outdata, indata + i, filesize - header->siglen - i); return 0; /* OK */ } static void cab_modify_header(char *indata, FILE_HEADER *header, BIO *hash, BIO *outdata) { int i; uint16_t nfolders, flags; static char buf[64*1024]; /* u1 signature[4] 4643534D MSCF: 0-3 */ BIO_write(hash, indata, 4); /* u4 reserved1 00000000: 4-7 */ BIO_write(outdata, indata+4, 4); /* * u4 cbCabinet - size of this cabinet file in bytes: 8-11 * u4 reserved2 00000000: 12-15 * u4 coffFiles - offset of the first CFFILE entry: 16-19 * u4 reserved3 00000000: 20-23 * u1 versionMinor 03: 24 * u1 versionMajor 01: 25 * u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27 * u2 cFiles - number of CFFILE entries in this cabinet: 28-29 */ BIO_write(hash, indata+8, 22); /* u2 flags: 30-31 */ flags = GET_UINT16_LE(indata+30); PUT_UINT32_LE(flags, buf); BIO_write(hash, buf, 2); /* u2 setID must be the same for all cabinets in a set: 32-33 */ BIO_write(hash, indata+32, 2); /* * u2 iCabinet - number of this cabinet file in a set: 34-35 * u2 cbCFHeader: 36-37 * u1 cbCFFolder: 38 * u1 cbCFData: 39 * u16 abReserve: 40-55 * - Additional data offset: 44-47 * - Additional data size: 48-51 */ BIO_write(outdata, indata+34, 22); /* u4 abReserve: 56-59 */ BIO_write(hash, indata+56, 4); i = 60; cab_optional_names(flags, indata, hash, &i); /* * (u8 * cFolders) CFFOLDER - structure contains information about * one of the folders or partial folders stored in this cabinet file */ nfolders = GET_UINT16_LE(indata + 26); while (nfolders) { BIO_write(hash, indata + i, 8); i += 8; nfolders--; } /* Write what's left - the compressed data bytes */ BIO_write(hash, indata + i, header->sigpos - i); } static void cab_add_header(char *indata, FILE_HEADER *header, BIO *hash, BIO *outdata) { int i; uint32_t tmp; uint16_t nfolders, flags; static char buf[64*1024]; u_char cabsigned[] = { 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xde, 0xad, 0xbe, 0xef, /* size of cab file */ 0xde, 0xad, 0xbe, 0xef, /* size of asn1 blob */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* u1 signature[4] 4643534D MSCF: 0-3 */ BIO_write(hash, indata, 4); /* u4 reserved1 00000000: 4-7 */ BIO_write(outdata, indata+4, 4); /* u4 cbCabinet - size of this cabinet file in bytes: 8-11 */ tmp = GET_UINT32_LE(indata+8) + 24; PUT_UINT32_LE(tmp, buf); BIO_write(hash, buf, 4); /* u4 reserved2 00000000: 12-15 */ BIO_write(hash, indata+12, 4); /* u4 coffFiles - offset of the first CFFILE entry: 16-19 */ tmp = GET_UINT32_LE(indata+16) + 24; PUT_UINT32_LE(tmp, buf+4); BIO_write(hash, buf+4, 4); /* * u4 reserved3 00000000: 20-23 * u1 versionMinor 03: 24 * u1 versionMajor 01: 25 * u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27 * u2 cFiles - number of CFFILE entries in this cabinet: 28-29 */ memcpy(buf+4, indata+20, 10); flags = GET_UINT16_LE(indata+30); buf[4+10] = flags | FLAG_RESERVE_PRESENT; /* u2 setID must be the same for all cabinets in a set: 32-33 */ memcpy(buf+16, indata+32, 2); BIO_write(hash, buf+4, 14); /* u2 iCabinet - number of this cabinet file in a set: 34-35 */ BIO_write(outdata, indata+34, 2); memcpy(cabsigned+8, buf, 4); BIO_write(outdata, cabsigned, 20); BIO_write(hash, cabsigned+20, 4); i = 36; cab_optional_names(flags, indata, hash, &i); /* * (u8 * cFolders) CFFOLDER - structure contains information about * one of the folders or partial folders stored in this cabinet file */ nfolders = GET_UINT16_LE(indata + 26); while (nfolders) { tmp = GET_UINT32_LE(indata + i); tmp += 24; PUT_UINT32_LE(tmp, buf); BIO_write(hash, buf, 4); BIO_write(hash, indata + i + 4, 4); i += 8; nfolders--; } /* Write what's left - the compressed data bytes */ BIO_write(hash, indata + i, header->fileend - i); } /* * CAT file support * Catalog files are a bit odd, in that they are only a PKCS7 blob. */ static PKCS7 *cat_extract_existing_pkcs7(char *indata, FILE_HEADER *header) { PKCS7 *p7 = NULL; const unsigned char *blob; blob = (unsigned char*)indata; p7 = d2i_PKCS7(NULL, &blob, header->fileend); return p7; } static int cat_verify_header(char *indata, size_t filesize, FILE_HEADER *header) { PKCS7 *p7; PKCS7_SIGNER_INFO *si; p7 = cat_extract_existing_pkcs7(indata, header); if (!p7) { return 0; /* FAILED */ } if (!PKCS7_type_is_signed(p7)) { PKCS7_free(p7); return 0; /* FAILED */ } si = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0); if (si == NULL) { /* catalog file is unsigned */ header->sigpos = filesize; } header->fileend = filesize; PKCS7_free(p7); return 1; /* OK */ } /* * If the attribute type is SPC_INDIRECT_DATA_OBJID, get a digest algorithm and a message digest * from the content and compare the message digest against the computed message digest of the file */ static int cat_verify_member(CatalogAuthAttr *attribute, char *indata, FILE_HEADER *header, file_type_t filetype) { int ret = 1; unsigned char *ph = NULL; ASN1_OBJECT *indir_objid = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1); if (attribute && !OBJ_cmp(attribute->type, indir_objid)) { int mdok, mdtype = -1, phtype = -1; unsigned char mdbuf[EVP_MAX_MD_SIZE]; unsigned char cmdbuf[EVP_MAX_MD_SIZE]; char hexbuf[EVP_MAX_MD_SIZE*2+1]; size_t phlen = 0; const EVP_MD *md; ASN1_TYPE *content; SpcIndirectDataContent *idc; ASN1_STRING *content_val = attribute->contents->value.sequence; const unsigned char *p = content_val->data; STACK_OF(ASN1_TYPE) *contents = d2i_ASN1_SET_ANY(NULL, &p, content_val->length); if (contents == NULL) goto out; content = sk_ASN1_TYPE_value(contents, 0); sk_ASN1_TYPE_free(contents); content_val = content->value.sequence; p = content_val->data; idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); if (idc) { if (header->sigpos) { /* try to get a page hash if the file is signed */ pe_extract_page_hash(idc->data, &ph, &phlen, &phtype); } if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) { /* get a digest algorithm a message digest of the file from the content */ mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm); memcpy(mdbuf, idc->messageDigest->digest->data, idc->messageDigest->digest->length); } SpcIndirectDataContent_free(idc); } ASN1_TYPE_free(content); if (mdtype == -1) { printf("Failed to extract current message digest\n\n"); goto out; } md = EVP_get_digestbynid(mdtype); /* compute a message digest of the input file */ switch (filetype) { case FILE_TYPE_CAB: cab_calc_digest(indata, md, cmdbuf, header); break; case FILE_TYPE_PE: pe_calc_digest(indata, md, cmdbuf, header); break; case FILE_TYPE_MSI: msi_calc_digest(indata, md, cmdbuf, header->fileend); break; default: break; } mdok = !memcmp(mdbuf, cmdbuf, EVP_MD_size(md)); if (mdok) { printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype)); tohex(mdbuf, hexbuf, EVP_MD_size(md)); printf("Current message digest : %s\n", hexbuf); tohex(cmdbuf, hexbuf, EVP_MD_size(md)); printf("Calculated message digest : %s\n\n", hexbuf); } else { goto out; } if (phlen > 0) { size_t cphlen = 0; unsigned char *cph; cph = pe_calc_page_hash(indata, header->header_size, header->pe32plus, header->sigpos, phtype, &cphlen); tohex(cph, hexbuf, (cphlen < 32) ? cphlen : 32); mdok = (phlen = cphlen) && !memcmp(ph, cph, phlen); OPENSSL_free(cph); if (mdok) { printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype)); tohex(ph, hexbuf, (phlen < 32) ? phlen : 32); printf("Page hash : %s\n", hexbuf); printf("Calculated page hash : %s\n\n", hexbuf); } else { goto out; } } ret = 0; /* OK */ } out: ASN1_OBJECT_free(indir_objid); OPENSSL_free(ph); return ret; } /* * If the message digest of the input file is found in the catalog file, * or the input file itself is a catalog file, verify the signature. */ static int cat_verify_pkcs7(SIGNATURE *signature, char *indata, FILE_HEADER *header, file_type_t filetype, GLOBAL_OPTIONS *options) { int ret = 1, ok = 0; /* A CTL (MS_CTL_OBJID) is a list of hashes of certificates or a list of hashes files */ if (options->catalog && is_content_type(signature->p7, MS_CTL_OBJID)) { ASN1_STRING *content_val = signature->p7->d.sign->contents->d.other->value.sequence; const unsigned char *p = content_val->data; MsCtlContent *ctlc = d2i_MsCtlContent(NULL, &p, content_val->length); if (ctlc) { int i, j; /* find the message digest of the file for all files added to the catalog file */ for (i = 0; i < sk_CatalogInfo_num(ctlc->header_attributes); i++) { STACK_OF(CatalogAuthAttr) *attributes; CatalogInfo *header_attr = sk_CatalogInfo_value(ctlc->header_attributes, i); if (header_attr == NULL) continue; attributes = header_attr->attributes; for (j = 0; j < sk_CatalogAuthAttr_num(attributes); j++) { CatalogAuthAttr *attribute = sk_CatalogAuthAttr_value(attributes, j); if (!cat_verify_member(attribute, indata, header, filetype)) { /* computed message digest of the file is found in the catalog file */ ok = 1; break; } } if (ok) { break; } } MsCtlContent_free(ctlc); } } else { /* the input file is a catalog file */ ok = 1; } if (ok) { /* a message digest value of the catalog file is checked by PKCS7_verify() */ ret = verify_signature(signature, options); } else { printf("File not found in the specified catalog.\n\n"); } if (!ret) ERR_print_errors_fp(stdout); return ret; } static int cat_verify_file(char *catdata, FILE_HEADER *catheader, char *indata, FILE_HEADER *header, file_type_t filetype, GLOBAL_OPTIONS *options) { int i, ret = 1; PKCS7 *p7; STACK_OF(SIGNATURE) *signatures = sk_SIGNATURE_new_null(); if (header->sigpos == header->fileend || (options->catalog && (catheader->sigpos == catheader->fileend))) { printf("No signature found\n\n"); goto out; } if (options->catalog) p7 = cat_extract_existing_pkcs7(catdata, catheader); else p7 = cat_extract_existing_pkcs7(indata, header); if (!append_signature_list(&signatures, p7, 1)) { printf("Failed to create signature list\n\n"); PKCS7_free(p7); goto out; } for (i = 0; i < sk_SIGNATURE_num(signatures); i++) { SIGNATURE *signature = sk_SIGNATURE_value(signatures, i); if (!options->catalog) printf("Signature Index: %d %s\n", i, i==0 ? " (Primary Signature)" : ""); ret &= cat_verify_pkcs7(signature, indata, header, filetype, options); } printf("Number of verified signatures: %d\n", i); out: sk_SIGNATURE_pop_free(signatures, signature_free); return ret; } static void add_jp_attribute(PKCS7_SIGNER_INFO *si, int jp) { ASN1_STRING *astr; int len; const u_char *attrs = NULL; switch (jp) { case 0: attrs = java_attrs_low; len = sizeof java_attrs_low; break; case 1: /* XXX */ case 2: /* XXX */ default: break; } if (attrs) { astr = ASN1_STRING_new(); ASN1_STRING_set(astr, attrs, len); PKCS7_add_signed_attribute(si, OBJ_txt2nid(MS_JAVA_SOMETHING), V_ASN1_SEQUENCE, astr); } } static void add_purpose_attribute(PKCS7_SIGNER_INFO *si, int comm) { ASN1_STRING *astr; astr = ASN1_STRING_new(); if (comm) { ASN1_STRING_set(astr, purpose_comm, sizeof purpose_comm); } else { ASN1_STRING_set(astr, purpose_ind, sizeof purpose_ind); } PKCS7_add_signed_attribute(si, OBJ_txt2nid(SPC_STATEMENT_TYPE_OBJID), V_ASN1_SEQUENCE, astr); } static int add_opus_attribute(PKCS7_SIGNER_INFO *si, char *desc, char *url) { SpcSpOpusInfo *opus; ASN1_STRING *astr; int len; u_char *p = NULL; opus = createOpus(desc, url); if ((len = i2d_SpcSpOpusInfo(opus, NULL)) <= 0 || (p = OPENSSL_malloc(len)) == NULL) { SpcSpOpusInfo_free(opus); return 0; /* FAILED */ } i2d_SpcSpOpusInfo(opus, &p); p -= len; astr = ASN1_STRING_new(); ASN1_STRING_set(astr, p, len); OPENSSL_free(p); PKCS7_add_signed_attribute(si, OBJ_txt2nid(SPC_SP_OPUS_INFO_OBJID), V_ASN1_SEQUENCE, astr); SpcSpOpusInfo_free(opus); return 1; /* OK */ } static PKCS7 *create_new_signature(file_type_t type, GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams) { int i, signer = -1; PKCS7 *sig; PKCS7_SIGNER_INFO *si = NULL; sig = PKCS7_new(); PKCS7_set_type(sig, NID_pkcs7_signed); if (cparams->cert != NULL) { /* * the private key and corresponding certificate are parsed from the PKCS12 * structure or loaded from the security token, so we may omit to check * the consistency of a private key with the public key in an X509 certificate */ si = PKCS7_add_signature(sig, cparams->cert, cparams->pkey, options->md); if (si == NULL) return NULL; /* FAILED */ } else { /* find the signer's certificate located somewhere in the whole certificate chain */ for (i=0; icerts); i++) { X509 *signcert = sk_X509_value(cparams->certs, i); if (X509_check_private_key(signcert, cparams->pkey)) { si = PKCS7_add_signature(sig, signcert, cparams->pkey, options->md); signer = i; break; } } if (si == NULL) { printf("Failed to checking the consistency of a private key: %s\n", options->keyfile); printf(" with a public key in any X509 certificate: %s\n\n", options->certfile); return NULL; /* FAILED */ } } pkcs7_add_signing_time(si, options->signing_time); if (type == FILE_TYPE_CAT) { PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_txt2obj(MS_CTL_OBJID, 1)); } else { PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1)); } if (type == FILE_TYPE_CAB && options->jp >= 0) add_jp_attribute(si, options->jp); add_purpose_attribute(si, options->comm); if ((options->desc || options->url) && !add_opus_attribute(si, options->desc, options->url)) { printf("Couldn't allocate memory for opus info\n"); return NULL; /* FAILED */ } PKCS7_content_new(sig, NID_pkcs7_data); /* add the signer's certificate */ if (cparams->cert != NULL) PKCS7_add_certificate(sig, cparams->cert); if (signer != -1) PKCS7_add_certificate(sig, sk_X509_value(cparams->certs, signer)); /* add the certificate chain */ for (i=0; icerts); i++) { if (i == signer) continue; PKCS7_add_certificate(sig, sk_X509_value(cparams->certs, i)); } /* add all cross certificates */ if (cparams->xcerts) { for (i=0; ixcerts); i++) PKCS7_add_certificate(sig, sk_X509_value(cparams->xcerts, i)); } /* add crls */ if (cparams->crls) { for (i=0; icrls); i++) PKCS7_add_crl(sig, sk_X509_CRL_value(cparams->crls, i)); } return sig; /* OK */ } static int add_unauthenticated_blob(PKCS7 *sig) { PKCS7_SIGNER_INFO *si; ASN1_STRING *astr; u_char *p = NULL; int nid, len = 1024+4; /* Length data for ASN1 attribute plus prefix */ char prefix[] = "\x0c\x82\x04\x00---BEGIN_BLOB---"; char postfix[] = "---END_BLOB---"; si = sk_PKCS7_SIGNER_INFO_value(sig->d.sign->signer_info, 0); if ((p = OPENSSL_malloc(len)) == NULL) return 1; /* FAILED */ memset(p, 0, len); memcpy(p, prefix, sizeof prefix); memcpy(p + len - sizeof postfix, postfix, sizeof postfix); astr = ASN1_STRING_new(); ASN1_STRING_set(astr, p, len); nid = OBJ_create(SPC_UNAUTHENTICATED_DATA_BLOB_OBJID, "unauthenticatedData", "unauthenticatedData"); PKCS7_add_attribute(si, nid, V_ASN1_SEQUENCE, astr); OPENSSL_free(p); return 0; /* OK */ } /* * Append signature to the outfile */ static int append_signature(PKCS7 *sig, PKCS7 *cursig, file_type_t type, GLOBAL_OPTIONS *options, MSI_PARAMS *msiparams, size_t *padlen, int *len, BIO *outdata) { u_char *p = NULL; static char buf[64*1024]; PKCS7 *outsig = NULL; if (type != FILE_TYPE_CAT && options->nest) { if (cursig == NULL) { printf("Internal error: No 'cursig' was extracted\n"); return 1; /* FAILED */ } if (pkcs7_set_nested_signature(cursig, sig, options->signing_time) == 0) { printf("Unable to append the nested signature to the current signature\n"); return 1; /* FAILED */ } outsig = cursig; } else { outsig = sig; } /* Append signature to outfile */ if (((*len = i2d_PKCS7(outsig, NULL)) <= 0) || (p = OPENSSL_malloc(*len)) == NULL) { printf("i2d_PKCS memory allocation failed: %d\n", *len); return 1; /* FAILED */ } i2d_PKCS7(outsig, &p); p -= *len; *padlen = (8 - *len%8) % 8; if (type == FILE_TYPE_PE) { PUT_UINT32_LE(*len + 8 + *padlen, buf); PUT_UINT16_LE(WIN_CERT_REVISION_2, buf + 4); PUT_UINT16_LE(WIN_CERT_TYPE_PKCS_SIGNED_DATA, buf + 6); BIO_write(outdata, buf, 8); } if (type == FILE_TYPE_PE || type == FILE_TYPE_CAB) { BIO_write(outdata, p, *len); /* pad (with 0's) asn1 blob to 8 byte boundary */ if (*padlen > 0) { memset(p, 0, *padlen); BIO_write(outdata, p, *padlen); } } else if (type == FILE_TYPE_MSI) { int len_msi = *len; unsigned char *p_msi = OPENSSL_malloc(len_msi); memcpy(p_msi, p, len_msi); if (!msi_file_write(msiparams->msi, msiparams->dirent, p_msi, len_msi, msiparams->p_msiex, msiparams->len_msiex, outdata)) { printf("Saving the msi file failed\n"); return 1; /* FAILED */ } } else if (type == FILE_TYPE_CAT) { i2d_PKCS7_bio(outdata, outsig); } OPENSSL_free(p); return 0; /* OK */ } static void update_data_size(file_type_t type, cmd_type_t cmd, FILE_HEADER *header, size_t padlen, int len, BIO *outdata) { static char buf[64*1024]; if (type == FILE_TYPE_PE) { if (cmd == CMD_SIGN || cmd == CMD_ADD || cmd == CMD_ATTACH) { /* Update signature position and size */ (void)BIO_seek(outdata, header->header_size + 152 + header->pe32plus * 16); PUT_UINT32_LE(header->fileend, buf); /* Previous file end = signature table start */ BIO_write(outdata, buf, 4); PUT_UINT32_LE(len+8+padlen, buf); BIO_write(outdata, buf, 4); } if (cmd == CMD_SIGN || cmd == CMD_REMOVE || cmd == CMD_ADD || cmd == CMD_ATTACH) pe_recalc_checksum(outdata, header); } else if (type == FILE_TYPE_CAB && (cmd == CMD_SIGN || cmd == CMD_ADD || cmd == CMD_ATTACH)) { /* * Update additional data size. * Additional data size is located at offset 0x30 (from file beginning) * and consist of 4 bytes (little-endian order). */ (void)BIO_seek(outdata, 0x30); PUT_UINT32_LE(len+padlen, buf); BIO_write(outdata, buf, 4); } } static STACK_OF(X509) *PEM_read_certs_with_pass(BIO *bin, char *certpass) { STACK_OF(X509) *certs = sk_X509_new_null(); X509 *x509; (void)BIO_seek(bin, 0); while((x509 = PEM_read_bio_X509(bin, NULL, NULL, certpass))) sk_X509_push(certs, x509); if (!sk_X509_num(certs)) { sk_X509_free(certs); return NULL; } return certs; } static STACK_OF(X509) *PEM_read_certs(BIO *bin, char *certpass) { STACK_OF(X509) *certs = PEM_read_certs_with_pass(bin, certpass); if (!certs) certs = PEM_read_certs_with_pass(bin, NULL); return certs; } static off_t get_file_size(const char *infile) { int ret; #ifdef _WIN32 struct _stat st; ret = _stat(infile, &st); #else struct stat st; ret = stat(infile, &st); #endif if (ret) { printf("Failed to open file: %s\n", infile); return 0; } if (st.st_size < 4) { printf("Unrecognized file type - file is too short: %s\n", infile); return 0; } return st.st_size; } static char *map_file(const char *infile, const off_t size) { char *indata = NULL; #ifdef WIN32 HANDLE fh, fm; fh = CreateFile(infile, GENERIC_READ, FILE_SHARE_READ , NULL, OPEN_EXISTING, 0, NULL); if (fh == INVALID_HANDLE_VALUE) return NULL; fm = CreateFileMapping(fh, NULL, PAGE_READONLY, 0, 0, NULL); if (fm == NULL) return NULL; indata = MapViewOfFile(fm, FILE_MAP_READ, 0, 0, 0); #else int fd = open(infile, O_RDONLY); if (fd < 0) return NULL; indata = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0); if (indata == MAP_FAILED) return NULL; #endif return indata; } static int input_validation(file_type_t type, GLOBAL_OPTIONS *options, FILE_HEADER *header, MSI_PARAMS *msiparams, char *indata, size_t filesize) { if (type == FILE_TYPE_CAB) { if (options->pagehash == 1) printf("Warning: -ph option is only valid for PE files\n"); if (options->add_msi_dse == 1) printf("Warning: -add-msi-dse option is only valid for MSI files\n"); if (!cab_verify_header(indata, options->infile, filesize, header)) { printf("Corrupt CAB file\n"); return 0; /* FAILED */ } } else if (type == FILE_TYPE_CAT) { if (options->nest) /* I've not tried using pkcs7_set_nested_signature as signtool won't do this */ printf("Warning: CAT files do not support nesting\n"); if (options->jp >= 0) printf("Warning: -jp option is only valid for CAB files\n"); if (options->pagehash == 1) printf("Warning: -ph option is only valid for PE files\n"); if (options->add_msi_dse == 1) printf("Warning: -add-msi-dse option is only valid for MSI files\n"); if (!cat_verify_header(indata, filesize, header)) { printf("Corrupt CAT file: %s\n", options->infile); return 0; /* FAILED */ } } else if (type == FILE_TYPE_PE) { if (options->jp >= 0) printf("Warning: -jp option is only valid for CAB files\n"); if (options->add_msi_dse == 1) printf("Warning: -add-msi-dse option is only valid for MSI files\n"); if (!pe_verify_header(indata, options->infile, filesize, header)) { printf("Corrupt PE file\n"); return 0; /* FAILED */ } } else if (type == FILE_TYPE_MSI) { if (options->pagehash == 1) printf("Warning: -ph option is only valid for PE files\n"); if (options->jp >= 0) printf("Warning: -jp option is only valid for CAB files\n"); if (!msi_verify_header(indata, options->infile, filesize, msiparams)) { printf("Corrupt MSI file\n"); return 0; /* FAILED */ } } return 1; /* OK */ } static int check_attached_data(file_type_t type, FILE_HEADER *header, GLOBAL_OPTIONS *options, MSI_PARAMS *msiparams) { size_t filesize; char *outdata; if (type == FILE_TYPE_PE) { filesize = get_file_size(options->outfile); if (!filesize) { printf("Error verifying result\n"); return 1; /* FAILED */ } outdata = map_file(options->outfile, filesize); if (!outdata) { printf("Error verifying result\n"); return 1; /* FAILED */ } if (!pe_verify_header(outdata, options->outfile, filesize, header)) { printf("Corrupt PE file\n"); return 1; /* FAILED */ } if (pe_verify_file(outdata, header, options)) { printf("Signature mismatch\n"); return 1; /* FAILED */ } } else if (type == FILE_TYPE_CAB) { filesize = get_file_size(options->outfile); if (!filesize) { printf("Error verifying result\n"); return 1; /* FAILED */ } outdata = map_file(options->outfile, filesize); if (!outdata) { printf("Error verifying result\n"); return 1; /* FAILED */ } if (!cab_verify_header(outdata, options->outfile, filesize, header)) { printf("Corrupt CAB file\n"); return 1; /* FAILED */ } if (cab_verify_file(outdata, header, options)) { printf("Signature mismatch\n"); return 1; /* FAILED */ } } else if (type == FILE_TYPE_MSI) { filesize = get_file_size(options->outfile); if (!filesize) { printf("Error verifying result\n"); return 1; /* FAILED */ } outdata = map_file(options->outfile, filesize); if (!outdata) { printf("Error verifying result\n"); return 1; /* FAILED */ } if (!msi_verify_header(outdata, options->outfile, filesize, msiparams)) { printf("Corrupt CAB file\n"); return 1; /* FAILED */ } if (msi_verify_file(msiparams, options)) { printf("Signature mismatch\n"); return 1; /* FAILED */ } } else { printf("Unknown input type for file: %s\n", options->infile); return 1; /* FAILED */ } return 0; /* OK */ } static int get_file_type(char *indata, char *infile, file_type_t *type) { static u_char pkcs7_signed_data[] = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, }; if (!memcmp(indata, "MSCF", 4)) { *type = FILE_TYPE_CAB; } else if (!memcmp(indata, "MZ", 2)) { *type = FILE_TYPE_PE; } else if (!memcmp(indata, msi_magic, sizeof msi_magic)) { *type = FILE_TYPE_MSI; } else if (!memcmp(indata + ((GET_UINT8_LE(indata+1) == 0x82) ? 4 : 5), pkcs7_signed_data, sizeof pkcs7_signed_data)) { /* the maximum size of a supported cat file is (2^24 -1) bytes */ *type = FILE_TYPE_CAT; } else { printf("Unrecognized file type: %s\n", infile); return 0; /* FAILED */ } return 1; /* OK */ } #ifdef PROVIDE_ASKPASS static char *getpassword(const char *prompt) { #ifdef HAVE_TERMIOS_H struct termios ofl, nfl; char *p, passbuf[1024], *pass; fputs(prompt, stdout); tcgetattr(fileno(stdin), &ofl); nfl = ofl; nfl.c_lflag &= ~ECHO; nfl.c_lflag |= ECHONL; if (tcsetattr(fileno(stdin), TCSANOW, &nfl) != 0) { printf("Failed to set terminal attributes\n"); return NULL; } p = fgets(passbuf, sizeof passbuf, stdin); if (tcsetattr(fileno(stdin), TCSANOW, &ofl) != 0) printf("Failed to restore terminal attributes\n"); if (!p) { printf("Failed to read password\n"); return NULL; } passbuf[strlen(passbuf)-1] = 0x00; pass = OPENSSL_strdup(passbuf); memset(passbuf, 0, sizeof passbuf); return pass; #else return getpass(prompt); #endif } #endif static int read_password(GLOBAL_OPTIONS *options) { char passbuf[4096]; int passfd, passlen; static const u_char utf8_bom[] = {0xef, 0xbb, 0xbf}; if (options->readpass) { passfd = open(options->readpass, O_RDONLY); if (passfd < 0) { printf("Failed to open password file: %s\n", options->readpass); return 0; /* FAILED */ } passlen = read(passfd, passbuf, sizeof passbuf - 1); close(passfd); if (passlen <= 0) { printf("Failed to read password from file: %s\n", options->readpass); return 0; /* FAILED */ } while (passlen > 0 && (passbuf[passlen-1] == 0x0a || passbuf[passlen-1] == 0x0d)) { passlen--; } passbuf[passlen] = 0x00; if (!memcmp(passbuf, utf8_bom, sizeof utf8_bom)) { options->pass = OPENSSL_strdup(passbuf + sizeof utf8_bom); } else { options->pass = OPENSSL_strdup(passbuf); } memset(passbuf, 0, sizeof(passbuf)); #ifdef PROVIDE_ASKPASS } else if (options->askpass) { options->pass = getpassword("Password: "); #endif } return 1; /* OK */ } /* * Parse a PKCS#12 container with certificates and a private key. * If successful the private key will be written to cparams->pkey, * the corresponding certificate to cparams->cert * and any additional certificates to cparams->certs. */ static int read_pkcs12file(GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams) { BIO *btmp; PKCS12 *p12; int ret = 0; btmp = BIO_new_file(options->pkcs12file, "rb"); if (!btmp) { printf("Failed to read PKCS#12 file: %s\n", options->pkcs12file); return 0; /* FAILED */ } p12 = d2i_PKCS12_bio(btmp, NULL); if (!p12) { printf("Failed to extract PKCS#12 data: %s\n", options->pkcs12file); goto out; /* FAILED */ } if (!PKCS12_parse(p12, options->pass ? options->pass : "", &cparams->pkey, &cparams->cert, &cparams->certs)) { printf("Failed to parse PKCS#12 file: %s (Wrong password?)\n", options->pkcs12file); PKCS12_free(p12); goto out; /* FAILED */ } PKCS12_free(p12); ret = 1; /* OK */ out: BIO_free(btmp); return ret; } /* Obtain a copy of the whole X509_CRL chain */ STACK_OF(X509_CRL) *X509_CRL_chain_up_ref(STACK_OF(X509_CRL) *chain) { STACK_OF(X509_CRL) *ret; int i; ret = sk_X509_CRL_dup(chain); if (ret == NULL) return NULL; for (i = 0; i < sk_X509_CRL_num(ret); i++) { X509_CRL *x = sk_X509_CRL_value(ret, i); if (!X509_CRL_up_ref(x)) goto err; } return ret; err: while (i-- > 0) X509_CRL_free(sk_X509_CRL_value(ret, i)); sk_X509_CRL_free(ret); return NULL; } /* * Load certificates from a file. * If successful all certificates will be written to cparams->certs * and optional CRLs will be written to cparams->crls. */ static int read_certfile(GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams) { BIO *btmp; int ret = 0; btmp = BIO_new_file(options->certfile, "rb"); if (!btmp) { printf("Failed to read certificate file: %s\n", options->certfile); return 0; /* FAILED */ } /* .pem certificate file */ cparams->certs = PEM_read_certs(btmp, ""); /* .der certificate file */ if (!cparams->certs) { X509 *x = NULL; (void)BIO_seek(btmp, 0); if (d2i_X509_bio(btmp, &x)) { cparams->certs = sk_X509_new_null(); if (!sk_X509_push(cparams->certs, x)) { X509_free(x); goto out; /* FAILED */ } printf("Warning: The certificate file contains a single x509 certificate\n"); } } /* .spc or .p7b certificate file (PKCS#7 structure) */ if (!cparams->certs) { PKCS7 *p7; (void)BIO_seek(btmp, 0); p7 = d2i_PKCS7_bio(btmp, NULL); if (!p7) goto out; /* FAILED */ cparams->certs = X509_chain_up_ref(p7->d.sign->cert); /* additional CRLs may be supplied as part of a PKCS#7 signed data structure */ if (p7->d.sign->crl) cparams->crls = X509_CRL_chain_up_ref(p7->d.sign->crl); PKCS7_free(p7); } ret = 1; /* OK */ out: if (ret == 0) printf("No certificate found\n"); BIO_free(btmp); return ret; } /* Load additional (cross) certificates from a .pem file */ static int read_xcertfile(GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams) { BIO *btmp; int ret = 0; btmp = BIO_new_file(options->xcertfile, "rb"); if (!btmp) { printf("Failed to read cross certificates file: %s\n", options->xcertfile); return 0; /* FAILED */ } cparams->xcerts = PEM_read_certs(btmp, ""); if (!cparams->xcerts) { printf("Failed to read cross certificates file: %s\n", options->xcertfile); goto out; /* FAILED */ } ret = 1; /* OK */ out: BIO_free(btmp); return ret; } /* Load the private key from a file */ static int read_keyfile(GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams) { BIO *btmp; int ret = 0; btmp = BIO_new_file(options->keyfile, "rb"); if (!btmp) { printf("Failed to read private key file: %s\n", options->keyfile); return 0; /* FAILED */ } if (((cparams->pkey = d2i_PrivateKey_bio(btmp, NULL)) == NULL && (BIO_seek(btmp, 0) == 0) && (cparams->pkey = PEM_read_bio_PrivateKey(btmp, NULL, NULL, options->pass ? options->pass : "")) == NULL && (BIO_seek(btmp, 0) == 0) && (cparams->pkey = PEM_read_bio_PrivateKey(btmp, NULL, NULL, NULL)) == NULL)) { printf("Failed to decode private key file: %s (Wrong password?)\n", options->keyfile); goto out; /* FAILED */ } ret = 1; /* OK */ out: BIO_free(btmp); return ret; } /* * Decode Microsoft Private Key (PVK) file. * PVK is a proprietary Microsoft format that stores a cryptographic private key. * PVK files are often password-protected. * A PVK file may have an associated .spc (PKCS7) certificate file. */ static char *find_pvk_key(GLOBAL_OPTIONS *options) { unsigned char magic[4]; /* Microsoft Private Key format Header Hexdump */ unsigned char pvkhdr[4] = { 0x1e, 0xf1, 0xb5, 0xb0 }; char *pvkfile = NULL; BIO *btmp; if (!options->keyfile #ifndef OPENSSL_NO_ENGINE || options->p11module #endif /* OPENSSL_NO_ENGINE */ ) return NULL; /* FAILED */ btmp = BIO_new_file(options->keyfile, "rb"); if (!btmp) return NULL; /* FAILED */ magic[0] = 0x00; BIO_read(btmp, magic, 4); if (!memcmp(magic, pvkhdr, 4)) { pvkfile = options->keyfile; options->keyfile = NULL; } BIO_free(btmp); return pvkfile; } static int read_pvk_key(GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams) { BIO *btmp; int ret = 0; btmp = BIO_new_file(options->pvkfile, "rb"); if (!btmp) { printf("Failed to read private key file: %s\n", options->pvkfile); return 0; /* FAILED */ } if (((cparams->pkey = b2i_PVK_bio(btmp, NULL, options->pass ? options->pass : "")) == NULL && (BIO_seek(btmp, 0) == 0) && (cparams->pkey = b2i_PVK_bio(btmp, NULL, NULL)) == NULL)) { printf("Failed to decode private key file: %s\n", options->pvkfile); goto out; /* FAILED */ } ret = 1; /* OK */ out: BIO_free(btmp); return ret; } #ifndef OPENSSL_NO_ENGINE /* Load an engine in a shareable library */ ENGINE *dynamic_engine(GLOBAL_OPTIONS *options) { ENGINE *engine = ENGINE_by_id("dynamic"); if (!engine) { printf("Failed to load 'dynamic' engine\n"); return NULL; /* FAILED */ } if (!ENGINE_ctrl_cmd_string(engine, "SO_PATH", options->p11engine, 0) || !ENGINE_ctrl_cmd_string(engine, "ID", "pkcs11", 0) || !ENGINE_ctrl_cmd_string(engine, "LIST_ADD", "1", 0) || !ENGINE_ctrl_cmd_string(engine, "LOAD", NULL, 0)) { printf("Failed to set 'dynamic' engine\n"); ENGINE_free(engine); return NULL; /* FAILED */ } return engine; /* OK */ } /* Load a pkcs11 engine */ ENGINE *pkcs11_engine() { ENGINE *engine = ENGINE_by_id("pkcs11"); if (!engine) { printf("Failed to find and load 'pkcs11' engine\n"); return NULL; /* FAILED */ } return engine; /* OK */ } /* Load the private key and the signer certificate from a security token */ static int read_token(GLOBAL_OPTIONS *options, ENGINE *engine, CRYPTO_PARAMS *cparams) { if (options->p11module && !ENGINE_ctrl_cmd_string(engine, "MODULE_PATH", options->p11module, 0)) { printf("Failed to set pkcs11 engine MODULE_PATH to '%s'\n", options->p11module); ENGINE_free(engine); return 0; /* FAILED */ } if (options->pass != NULL && !ENGINE_ctrl_cmd_string(engine, "PIN", options->pass, 0)) { printf("Failed to set pkcs11 PIN\n"); ENGINE_free(engine); return 0; /* FAILED */ } if (!ENGINE_init(engine)) { printf("Failed to initialize pkcs11 engine\n"); ENGINE_free(engine); return 0; /* FAILED */ } /* * ENGINE_init() returned a functional reference, so free the structural * reference from ENGINE_by_id(). */ ENGINE_free(engine); if (options->p11cert) { struct { const char *id; X509 *cert; } parms; parms.id = options->p11cert; parms.cert = NULL; ENGINE_ctrl_cmd(engine, "LOAD_CERT_CTRL", 0, &parms, NULL, 1); if (!parms.cert) { printf("Failed to load certificate %s\n", options->p11cert); ENGINE_finish(engine); return 0; /* FAILED */ } else cparams->cert = parms.cert; } cparams->pkey = ENGINE_load_private_key(engine, options->keyfile, NULL, NULL); /* Free the functional reference from ENGINE_init */ ENGINE_finish(engine); if (!cparams->pkey) { printf("Failed to load private key %s\n", options->keyfile); return 0; /* FAILED */ } return 1; /* OK */ } #endif /* OPENSSL_NO_ENGINE */ static int read_crypto_params(GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams) { int ret = 0; /* Microsoft Private Key format support */ options->pvkfile = find_pvk_key(options); if (options->pvkfile) { if (!read_certfile(options, cparams) || !read_pvk_key(options, cparams)) goto out; /* FAILED */ /* PKCS#12 container with certificates and the private key ("-pkcs12" option) */ } else if (options->pkcs12file) { if (!read_pkcs12file(options, cparams)) goto out; /* FAILED */ #ifndef OPENSSL_NO_ENGINE /* PKCS11 engine and module support */ } else if ((options->p11engine) || (options->p11module)) { ENGINE *engine; if (options->p11engine) engine = dynamic_engine(options); else engine = pkcs11_engine(); if (!engine) goto out; /* FAILED */ printf("Engine \"%s\" set.\n", ENGINE_get_id(engine)); /* Load the private key and the signer certificate from the security token*/ if (!read_token(options, engine, cparams)) goto out; /* FAILED */ /* Load the signer certificate and the whole certificate chain from a file */ if (options->certfile && !read_certfile(options, cparams)) goto out; /* FAILED */ /* PEM / DER / SPC file format support */ } else if (!read_certfile(options, cparams) || !read_keyfile(options, cparams)) goto out; /* FAILED */ #endif /* OPENSSL_NO_ENGINE */ /* Load additional (cross) certificates ("-ac" option) */ if (options->xcertfile && !read_xcertfile(options, cparams)) goto out; /* FAILED */ ret = 1; out: /* reset password */ if (options->pass) { memset(options->pass, 0, strlen(options->pass)); OPENSSL_free(options->pass); } return ret; /* OK */ } static void free_msi_params(MSI_PARAMS *msiparams) { msi_file_free(msiparams->msi); msi_dirent_free(msiparams->dirent); } static void free_crypto_params(CRYPTO_PARAMS *cparams) { /* If key is NULL nothing is done */ EVP_PKEY_free(cparams->pkey); cparams->pkey = NULL; /* If X509 structure is NULL nothing is done */ X509_free(cparams->cert); cparams->cert = NULL; /* Free up all elements of sk structure and sk itself */ sk_X509_pop_free(cparams->certs, X509_free); cparams->certs = NULL; sk_X509_pop_free(cparams->xcerts, X509_free); cparams->xcerts = NULL; sk_X509_CRL_pop_free(cparams->crls, X509_CRL_free); cparams->crls = NULL; } static void free_options(GLOBAL_OPTIONS *options) { /* If memory has not been allocated nothing is done */ OPENSSL_free(options->cafile); OPENSSL_free(options->tsa_cafile); OPENSSL_free(options->crlfile); OPENSSL_free(options->tsa_crlfile); } static char *get_cafile(void) { const char *sslpart1, *sslpart2; char *cafile, *openssl_dir, *str_begin, *str_end; #ifdef CA_BUNDLE_PATH if (strcmp(CA_BUNDLE_PATH, "")) return OPENSSL_strdup(CA_BUNDLE_PATH); #endif sslpart1 = OpenSSL_version(OPENSSL_DIR); sslpart2 = "/certs/ca-bundle.crt"; str_begin = strchr(sslpart1, '"'); str_end = strrchr(sslpart1, '"'); if (str_begin && str_end && str_begin < str_end) { openssl_dir = OPENSSL_strndup(str_begin + 1, str_end - str_begin - 1); } else { openssl_dir = OPENSSL_strdup("/etc"); } cafile = OPENSSL_malloc(strlen(sslpart1) + strlen(sslpart2) + 1); strcpy(cafile, openssl_dir); strcat(cafile, sslpart2); OPENSSL_free(openssl_dir); return cafile; } static PKCS7 *get_sigfile(char *sigfile, file_type_t type) { PKCS7 *sig = NULL; size_t sigfilesize; char *insigdata; FILE_HEADER header; BIO *sigbio; const char pemhdr[] = "-----BEGIN PKCS7-----"; sigfilesize = get_file_size(sigfile); if (!sigfilesize) { return NULL; /* FAILED */ } insigdata = map_file(sigfile, sigfilesize); if (!insigdata) { printf("Failed to open file: %s\n", sigfile); return NULL; /* FAILED */ } if (sigfilesize >= sizeof pemhdr && !memcmp(insigdata, pemhdr, sizeof pemhdr - 1)) { sigbio = BIO_new_mem_buf(insigdata, sigfilesize); sig = PEM_read_bio_PKCS7(sigbio, NULL, NULL, NULL); BIO_free_all(sigbio); } else { /* reset header */ memset(&header, 0, sizeof(FILE_HEADER)); header.siglen = sigfilesize; header.sigpos = 0; if (type == FILE_TYPE_PE) sig = pe_extract_existing_pkcs7(insigdata, &header); else sig = extract_existing_pkcs7(insigdata, &header); } return sig; /* OK */ } /* * Obtain an existing signature or create a new one */ static PKCS7 *get_pkcs7(cmd_type_t cmd, BIO *hash, file_type_t type, char *indata, GLOBAL_OPTIONS *options, FILE_HEADER *header, CRYPTO_PARAMS *cparams, PKCS7 *cursig) { PKCS7 *sig = NULL; if (cmd == CMD_ATTACH) { sig = get_sigfile(options->sigfile, type); if (!sig) { printf("Unable to extract valid signature\n"); return NULL; /* FAILED */ } } else if (cmd == CMD_SIGN) { sig = create_new_signature(type, options, cparams); if (!sig) { printf("Creating a new signature failed\n"); return NULL; /* FAILED */ } if (type == FILE_TYPE_CAT) { if (!set_content_blob(sig, cursig)) { PKCS7_free(sig); printf("Signing failed\n"); return NULL; /* FAILED */ } } else { if (!set_indirect_data_blob(sig, hash, type, indata, options, header)) { PKCS7_free(sig); printf("Signing failed\n"); return NULL; /* FAILED */ } } } return sig; } /* * Perform a sanity check for the MsiDigitalSignatureEx section. * If the file we're attempting to sign has an MsiDigitalSignatureEx * section, we can't add a nested signature of a different MD type * without breaking the initial signature. */ static int msi_check_MsiDigitalSignatureEx(GLOBAL_OPTIONS *options, MSI_ENTRY *dse) { if (dse && GET_UINT32_LE(dse->size) != EVP_MD_size(options->md)) { printf("Unable to add nested signature with a different MD type (-h parameter) " "than what exists in the MSI file already.\nThis is due to the presence of " "MsiDigitalSignatureEx (-add-msi-dse parameter).\n\n"); return 0; /* FAILED */ } if (!dse && options->add_msi_dse) { printf("Unable to add signature with -add-msi-dse parameter " "without breaking the initial signature.\n\n"); return 0; /* FAILED */ } if (dse && !options->add_msi_dse) { printf("Unable to add signature without -add-msi-dse parameter " "without breaking the initial signature.\nThis is due to the presence of " "MsiDigitalSignatureEx (-add-msi-dse parameter).\n" "Should use -add-msi-dse options in this case.\n\n"); return 0; /* FAILED */ } return 1; /* OK */ } /* * Prepare the output file for signing */ static PKCS7 *msi_presign_file(file_type_t type, cmd_type_t cmd, FILE_HEADER *header, GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams, char *indata, BIO *hash, PKCS7 **cursig, MSI_PARAMS *msiparams) { PKCS7 *sig = NULL; uint32_t len; char *data; if (options->add_msi_dse && !msi_calc_MsiDigitalSignatureEx(msiparams, options->md, hash)) { printf("Unable to calc MsiDigitalSignatureEx\n"); return NULL; /* FAILED */ } if (!msi_hash_dir(msiparams->msi, msiparams->dirent, hash, 1)) { printf("Unable to msi_handle_dir()\n"); return NULL; /* FAILED */ } /* Obtain a current signature from previously-signed file */ if ((cmd == CMD_SIGN && options->nest) || (cmd == CMD_ATTACH && options->nest) || cmd == CMD_ADD) { MSI_ENTRY *dse = NULL; MSI_ENTRY *ds = msi_signatures_get(msiparams->dirent, &dse); if (!ds) { printf("MSI file has no signature\n\n"); return NULL; /* FAILED */ } if (!msi_check_MsiDigitalSignatureEx(options, dse)) { return NULL; /* FAILED */ } len = GET_UINT32_LE(ds->size); data = OPENSSL_malloc(len); *cursig = msi_extract_existing_pkcs7(msiparams, ds, &data, len); OPENSSL_free(data); if (!*cursig) { printf("Unable to extract existing signature\n"); return NULL; /* FAILED */ } if (cmd == CMD_ADD) sig = *cursig; } /* Obtain an existing signature or create a new one */ if ((cmd == CMD_ATTACH) || (cmd == CMD_SIGN)) sig = get_pkcs7(cmd, hash, type, indata, options, header, cparams, NULL); return sig; /* OK */ } static PKCS7 *pe_presign_file(file_type_t type, cmd_type_t cmd, FILE_HEADER *header, GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams, char *indata, BIO *hash, BIO *outdata, PKCS7 **cursig) { PKCS7 *sig = NULL; /* Obtain a current signature from previously-signed file */ if ((cmd == CMD_SIGN && options->nest) || (cmd == CMD_ATTACH && options->nest) || cmd == CMD_ADD) { *cursig = pe_extract_existing_pkcs7(indata, header); if (!*cursig) { printf("Unable to extract existing signature\n"); return NULL; /* FAILED */ } if (cmd == CMD_ADD) sig = *cursig; } if (header->sigpos > 0) { /* Strip current signature */ header->fileend = header->sigpos; } pe_modify_header(indata, header, hash, outdata); /* Obtain an existing signature or create a new one */ if ((cmd == CMD_ATTACH) || (cmd == CMD_SIGN)) sig = get_pkcs7(cmd, hash, type, indata, options, header, cparams, NULL); return sig; /* OK */ } static PKCS7 *cab_presign_file(file_type_t type, cmd_type_t cmd, FILE_HEADER *header, GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams, char *indata, BIO *hash, BIO *outdata, PKCS7 **cursig) { PKCS7 *sig = NULL; /* Obtain a current signature from previously-signed file */ if ((cmd == CMD_SIGN && options->nest) || (cmd == CMD_ATTACH && options->nest) || cmd == CMD_ADD) { *cursig = extract_existing_pkcs7(indata, header); if (!*cursig) { printf("Unable to extract existing signature\n"); return NULL; /* FAILED */ } if (cmd == CMD_ADD) sig = *cursig; } if (header->header_size == 20) /* Strip current signature and modify header */ cab_modify_header(indata, header, hash, outdata); else cab_add_header(indata, header, hash, outdata); /* Obtain an existing signature or create a new one */ if ((cmd == CMD_ATTACH) || (cmd == CMD_SIGN)) sig = get_pkcs7(cmd, hash, type, indata, options, header, cparams, NULL); return sig; /* OK */ } static PKCS7 *cat_presign_file(file_type_t type, cmd_type_t cmd, FILE_HEADER *header, GLOBAL_OPTIONS *options, CRYPTO_PARAMS *cparams, char *indata, PKCS7 **cursig) { PKCS7 *sig; *cursig = cat_extract_existing_pkcs7(indata, header); if (!*cursig) { printf("Failed to extract PKCS7 signed data\n"); return NULL; /* FAILED */ } if (cmd == CMD_ADD) sig = *cursig; else sig = get_pkcs7(cmd, NULL, type, indata, options, header, cparams, *cursig); return sig; /* OK */ } static void print_version() { printf(PACKAGE_STRING ", using:\n\t%s (Library: %s)\n\t%s\n", OPENSSL_VERSION_TEXT, OpenSSL_version(OPENSSL_VERSION), #ifdef ENABLE_CURL curl_version() #else "no libcurl available" #endif /* ENABLE_CURL */ ); printf("\nPlease send bug-reports to " PACKAGE_BUGREPORT "\n\n"); } static cmd_type_t get_command(char **argv) { if (!strcmp(argv[1], "--help")) { print_version(); help_for(argv[0], "all"); return CMD_HELP; } else if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) { print_version(); return CMD_HELP; } else if (!strcmp(argv[1], "sign")) return CMD_SIGN; else if (!strcmp(argv[1], "extract-signature")) return CMD_EXTRACT; else if (!strcmp(argv[1], "attach-signature")) return CMD_ATTACH; else if (!strcmp(argv[1], "remove-signature")) return CMD_REMOVE; else if (!strcmp(argv[1], "verify")) return CMD_VERIFY; else if (!strcmp(argv[1], "add")) return CMD_ADD; return CMD_SIGN; } static int main_configure(int argc, char **argv, cmd_type_t *cmd, GLOBAL_OPTIONS *options) { int i; char *failarg = NULL; const char *argv0; argv0 = argv[0]; if (argc > 1) { *cmd = get_command(argv); argv++; argc--; } /* reset options */ memset(options, 0, sizeof(GLOBAL_OPTIONS)); options->md = EVP_sha1(); options->signing_time = INVALID_TIME; options->jp = -1; if (*cmd == CMD_HELP) { return 0; /* FAILED */ } if (*cmd == CMD_VERIFY || *cmd == CMD_ATTACH) { options->cafile = get_cafile(); options->tsa_cafile = get_cafile(); } for (argc--,argv++; argc >= 1; argc--,argv++) { if (!strcmp(*argv, "-in")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->infile = *(++argv); } else if (!strcmp(*argv, "-out")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->outfile = *(++argv); } else if (!strcmp(*argv, "-sigin")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->sigfile = *(++argv); } else if ((*cmd == CMD_SIGN) && (!strcmp(*argv, "-spc") || !strcmp(*argv, "-certs"))) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->certfile = *(++argv); } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-ac")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->xcertfile = *(++argv); } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-key")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->keyfile = *(++argv); } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-pkcs12")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->pkcs12file = *(++argv); } else if ((*cmd == CMD_EXTRACT) && !strcmp(*argv, "-pem")) { options->output_pkcs7 = 1; #ifndef OPENSSL_NO_ENGINE } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-pkcs11cert")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->p11cert = *(++argv); } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-pkcs11engine")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->p11engine = *(++argv); } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-pkcs11module")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->p11module = *(++argv); #endif /* OPENSSL_NO_ENGINE */ } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-pass")) { if (options->askpass || options->readpass) { usage(argv0, "all"); return 0; /* FAILED */ } if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->pass = OPENSSL_strdup(*(++argv)); memset(*argv, 0, strlen(*argv)); #ifdef PROVIDE_ASKPASS } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-askpass")) { if (options->pass || options->readpass) { usage(argv0, "all"); return 0; /* FAILED */ } options->askpass = 1; #endif } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-readpass")) { if (options->askpass || options->pass) { usage(argv0, "all"); return 0; /* FAILED */ } if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->readpass = *(++argv); } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-comm")) { options->comm = 1; } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-ph")) { options->pagehash = 1; } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-n")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->desc = *(++argv); } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-h")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } ++argv; if (!strcmp(*argv, "md5")) { options->md = EVP_md5(); } else if (!strcmp(*argv, "sha1")) { options->md = EVP_sha1(); } else if (!strcmp(*argv, "sha2") || !strcmp(*argv, "sha256")) { options->md = EVP_sha256(); } else if (!strcmp(*argv, "sha384")) { options->md = EVP_sha384(); } else if (!strcmp(*argv, "sha512")) { options->md = EVP_sha512(); } else { usage(argv0, "all"); return 0; /* FAILED */ } } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-i")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->url = *(++argv); } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "-st")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->signing_time = (time_t)strtoul(*(++argv), NULL, 10); #ifdef ENABLE_CURL } else if ((*cmd == CMD_SIGN || *cmd == CMD_ADD) && !strcmp(*argv, "-t")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->turl[options->nturl++] = *(++argv); } else if ((*cmd == CMD_SIGN || *cmd == CMD_ADD) && !strcmp(*argv, "-ts")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->tsurl[options->ntsurl++] = *(++argv); } else if ((*cmd == CMD_SIGN || *cmd == CMD_ADD) && !strcmp(*argv, "-p")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->proxy = *(++argv); } else if ((*cmd == CMD_SIGN || *cmd == CMD_ADD) && !strcmp(*argv, "-noverifypeer")) { options->noverifypeer = 1; #endif } else if ((*cmd == CMD_SIGN || *cmd == CMD_ADD) && !strcmp(*argv, "-addUnauthenticatedBlob")) { options->addBlob = 1; } else if ((*cmd == CMD_SIGN || *cmd == CMD_ATTACH) && !strcmp(*argv, "-nest")) { options->nest = 1; } else if ((*cmd == CMD_VERIFY) && !strcmp(*argv, "-timestamp-expiration")) { options->timestamp_expiration = 1; } else if ((*cmd == CMD_SIGN || *cmd == CMD_ADD || *cmd == CMD_VERIFY) && !strcmp(*argv, "-verbose")) { options->verbose = 1; } else if ((*cmd == CMD_SIGN || *cmd == CMD_ADD || *cmd == CMD_ATTACH) && !strcmp(*argv, "-add-msi-dse")) { options->add_msi_dse = 1; } else if ((*cmd == CMD_VERIFY) && (!strcmp(*argv, "-c") || !strcmp(*argv, "-catalog"))) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->catalog = *(++argv); } else if ((*cmd == CMD_VERIFY || *cmd == CMD_ATTACH) && !strcmp(*argv, "-CAfile")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } OPENSSL_free(options->cafile); options->cafile = OPENSSL_strdup(*++argv); } else if ((*cmd == CMD_VERIFY || *cmd == CMD_ATTACH) && !strcmp(*argv, "-CRLfile")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->crlfile = OPENSSL_strdup(*++argv); } else if ((*cmd == CMD_VERIFY || *cmd == CMD_ATTACH) && (!strcmp(*argv, "-untrusted") || !strcmp(*argv, "-TSA-CAfile"))) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } OPENSSL_free(options->tsa_cafile); options->tsa_cafile = OPENSSL_strdup(*++argv); } else if ((*cmd == CMD_VERIFY || *cmd == CMD_ATTACH) && (!strcmp(*argv, "-CRLuntrusted") || !strcmp(*argv, "-TSA-CRLfile"))) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->tsa_crlfile = OPENSSL_strdup(*++argv); } else if ((*cmd == CMD_VERIFY) && !strcmp(*argv, "-require-leaf-hash")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } options->leafhash = (*++argv); } else if ((*cmd == CMD_ADD) && !strcmp(*argv, "--help")) { help_for(argv0, "add"); *cmd = CMD_HELP; return 0; /* FAILED */ } else if ((*cmd == CMD_ATTACH) && !strcmp(*argv, "--help")) { help_for(argv0, "attach-signature"); *cmd = CMD_HELP; return 0; /* FAILED */ } else if ((*cmd == CMD_EXTRACT) && !strcmp(*argv, "--help")) { help_for(argv0, "extract-signature"); *cmd = CMD_HELP; return 0; /* FAILED */ } else if ((*cmd == CMD_REMOVE) && !strcmp(*argv, "--help")) { help_for(argv0, "remove-signature"); *cmd = CMD_HELP; return 0; /* FAILED */ } else if ((*cmd == CMD_SIGN) && !strcmp(*argv, "--help")) { help_for(argv0, "sign"); *cmd = CMD_HELP; return 0; /* FAILED */ } else if ((*cmd == CMD_VERIFY) && !strcmp(*argv, "--help")) { help_for(argv0, "verify"); *cmd = CMD_HELP; return 0; /* FAILED */ } else if (!strcmp(*argv, "-jp")) { char *ap; if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ } ap = *(++argv); for (i=0; ap[i]; i++) ap[i] = tolower((int)ap[i]); if (!strcmp(ap, "low")) { options->jp = 0; } else if (!strcmp(ap, "medium")) { options->jp = 1; } else if (!strcmp(ap, "high")) { options->jp = 2; } if (options->jp != 0) { /* XXX */ usage(argv0, "all"); return 0; /* FAILED */ } } else { failarg = *argv; break; } } if (!options->infile && argc > 0) { options->infile = *(argv++); argc--; } if (*cmd != CMD_VERIFY && (!options->outfile && argc > 0)) { if (!strcmp(*argv, "-out")) { argv++; argc--; } if (argc > 0) { options->outfile = *(argv++); argc--; } } if (argc > 0 || #ifdef ENABLE_CURL (options->nturl && options->ntsurl) || #endif !options->infile || (*cmd != CMD_VERIFY && !options->outfile) || (*cmd == CMD_SIGN && !((options->certfile && options->keyfile) || #ifndef OPENSSL_NO_ENGINE options->p11engine || options->p11module || #endif /* OPENSSL_NO_ENGINE */ options->pkcs12file))) { if (failarg) printf("Unknown option: %s\n", failarg); usage(argv0, "all"); return 0; /* FAILED */ } if ((*cmd == CMD_VERIFY || *cmd == CMD_ATTACH) && access(options->cafile, R_OK)) { printf("Use the \"-CAfile\" option to add one or more trusted CA certificates to verify the signature.\n"); return 0; /* FAILED */ } return 1; /* OK */ } int main(int argc, char **argv) { GLOBAL_OPTIONS options; FILE_HEADER header, catheader; MSI_PARAMS msiparams; CRYPTO_PARAMS cparams; BIO *hash = NULL, *outdata = NULL; PKCS7 *cursig = NULL, *sig = NULL; char *indata = NULL, *catdata = NULL; int ret = -1, len = 0; size_t padlen = 0, filesize = 0; file_type_t type, filetype = FILE_TYPE_CAT; cmd_type_t cmd = CMD_SIGN; /* Set up OpenSSL */ if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS | OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CONFIG, NULL)) DO_EXIT_0("Failed to init crypto\n"); /* create some MS Authenticode OIDS we need later on */ if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, NULL, NULL) || !OBJ_create(MS_JAVA_SOMETHING, NULL, NULL) || !OBJ_create(SPC_SP_OPUS_INFO_OBJID, NULL, NULL) || !OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL)) DO_EXIT_0("Failed to create objects\n"); /* reset crypto */ memset(&cparams, 0, sizeof(CRYPTO_PARAMS)); /* reset MSI parameters */ memset(&msiparams, 0, sizeof(MSI_PARAMS)); msiparams.msi = NULL; msiparams.dirent = NULL; /* commands and options initialization */ if (!main_configure(argc, argv, &cmd, &options)) goto err_cleanup; if (!read_password(&options)) goto err_cleanup; /* read key and certificates */ if (cmd == CMD_SIGN && !read_crypto_params(&options, &cparams)) goto err_cleanup; /* check if indata is cab or pe */ filesize = get_file_size(options.infile); if (filesize == 0) goto err_cleanup; /* reset file header */ memset(&header, 0, sizeof(FILE_HEADER)); header.fileend = filesize; indata = map_file(options.infile, filesize); if (indata == NULL) DO_EXIT_1("Failed to open file: %s\n", options.infile); if (!get_file_type(indata, options.infile, &type)) goto err_cleanup; if (!input_validation(type, &options, &header, &msiparams, indata, filesize)) goto err_cleanup; /* search catalog file to determine whether the file is signed in a catalog */ if (options.catalog) { size_t catsize = get_file_size(options.catalog); if (catsize == 0) goto err_cleanup; catdata = map_file(options.catalog, catsize); if (catdata == NULL) DO_EXIT_1("Failed to open file: %s\n", options.catalog); filetype = type; if (!get_file_type(catdata, options.catalog, &type)) goto err_cleanup; /* reset file header */ memset(&catheader, 0, sizeof(FILE_HEADER)); catheader.fileend = catsize; if (!input_validation(type, &options, &catheader, NULL, catdata, catsize)) goto err_cleanup; } hash = BIO_new(BIO_f_md()); BIO_set_md(hash, options.md); if (cmd != CMD_VERIFY) { /* Create outdata file */ #ifdef WIN32 if (!access(options.outfile, R_OK)) /* outdata file exists */ DO_EXIT_1("Failed to create file: %s\n", options.outfile); #endif outdata = BIO_new_file(options.outfile, FILE_CREATE_MODE); if (outdata == NULL) DO_EXIT_1("Failed to create file: %s\n", options.outfile); if (type == FILE_TYPE_MSI) BIO_push(hash, BIO_new(BIO_s_null())); else BIO_push(hash, outdata); } if (type == FILE_TYPE_MSI) { if (cmd == CMD_EXTRACT) { ret = msi_extract_file(&msiparams, outdata, options.output_pkcs7); goto skip_signing; } else if (cmd == CMD_VERIFY) { ret = msi_verify_file(&msiparams, &options); goto skip_signing; } else { sig = msi_presign_file(type, cmd, &header, &options, &cparams, indata, hash, &cursig, &msiparams); if (cmd == CMD_REMOVE) { ret = msi_remove_file(&msiparams, outdata); goto skip_signing; } else if (!sig) goto err_cleanup; } } else if (type == FILE_TYPE_CAB) { if (!(header.flags & FLAG_RESERVE_PRESENT) && (cmd == CMD_REMOVE || cmd == CMD_EXTRACT)) { DO_EXIT_1("CAB file does not have any signature: %s\n", options.infile); } else if (cmd == CMD_EXTRACT) { ret = cab_extract_file(indata, &header, outdata, options.output_pkcs7); goto skip_signing; } else if (cmd == CMD_REMOVE) { ret = cab_remove_file(indata, &header, filesize, outdata); goto skip_signing; } else if (cmd == CMD_VERIFY) { ret = cab_verify_file(indata, &header, &options); goto skip_signing; } else { sig = cab_presign_file(type, cmd, &header, &options, &cparams, indata, hash, outdata, &cursig); if (!sig) goto err_cleanup; } } else if (type == FILE_TYPE_PE) { if ((cmd == CMD_REMOVE || cmd == CMD_EXTRACT) && header.sigpos == 0) { DO_EXIT_1("PE file does not have any signature: %s\n", options.infile); } else if (cmd == CMD_EXTRACT) { ret = pe_extract_file(indata, &header, outdata, options.output_pkcs7); goto skip_signing; } else if (cmd == CMD_VERIFY) { ret = pe_verify_file(indata, &header, &options); goto skip_signing; } else { sig = pe_presign_file(type, cmd, &header, &options, &cparams, indata, hash, outdata, &cursig); if (cmd == CMD_REMOVE) { ret = 0; /* OK */ goto skip_signing; } else if (!sig) goto err_cleanup; } } else if (type == FILE_TYPE_CAT) { if (cmd == CMD_REMOVE || cmd == CMD_EXTRACT || (cmd==CMD_ATTACH)) { DO_EXIT_0("Unsupported command\n"); } else if (cmd == CMD_VERIFY) { ret = cat_verify_file(catdata, &catheader, indata, &header, filetype, &options); goto skip_signing; } else { sig = cat_presign_file(type, cmd, &header, &options, &cparams, indata, &cursig); if (!sig) goto err_cleanup; } } #ifdef ENABLE_CURL /* add counter-signature/timestamp */ if (options.nturl && add_timestamp_authenticode(sig, &options)) DO_EXIT_0("Authenticode timestamping failed\n"); if (options.ntsurl && add_timestamp_rfc3161(sig, &options)) DO_EXIT_0("RFC 3161 timestamping failed\n"); #endif /* ENABLE_CURL */ if (options.addBlob && add_unauthenticated_blob(sig)) DO_EXIT_0("Adding unauthenticated blob failed\n"); #if 0 if (!PEM_write_PKCS7(stdout, sig)) DO_EXIT_0("PKCS7 output failed\n"); #endif ret = append_signature(sig, cursig, type, &options, &msiparams, &padlen, &len, outdata); if (ret) DO_EXIT_0("Append signature to outfile failed\n"); skip_signing: update_data_size(type, cmd, &header, padlen, len, outdata); if (type == FILE_TYPE_MSI) { BIO_free_all(outdata); outdata = NULL; } else { BIO_free_all(hash); hash = outdata = NULL; } if (!ret && cmd == CMD_ATTACH) { /* reset MSI parameters */ free_msi_params(&msiparams); memset(&msiparams, 0, sizeof(MSI_PARAMS)); ret = check_attached_data(type, &header, &options, &msiparams); if (!ret) printf("Signature successfully attached\n"); /* else * the new signature has been successfully appended to the outfile * but only its verification failed (incorrect verification parameters?) * so the output file is not deleted */ } err_cleanup: if (cmd != CMD_ADD) PKCS7_free(cursig); PKCS7_free(sig); if (hash) BIO_free_all(hash); if (outdata) { if (type == FILE_TYPE_MSI) { BIO_free_all(outdata); outdata = NULL; } unlink(options.outfile); } #ifdef WIN32 UnmapViewOfFile(indata); #else munmap(indata, filesize); #endif free_msi_params(&msiparams); free_crypto_params(&cparams); free_options(&options); if (ret) ERR_print_errors_fp(stdout); if (cmd == CMD_HELP) ret = 0; /* OK */ else printf(ret ? "Failed\n" : "Succeeded\n"); return ret; } /* Local Variables: c-basic-offset: 4 tab-width: 4 indent-tabs-mode: t End: vim: set ts=4 noexpandtab: */ osslsigncode-2.2/tests/000077500000000000000000000000001410626701100152115ustar00rootroot00000000000000osslsigncode-2.2/tests/certs/000077500000000000000000000000001410626701100163315ustar00rootroot00000000000000osslsigncode-2.2/tests/certs/.gitignore000066400000000000000000000000441410626701100203170ustar00rootroot00000000000000*.der *.pem *.pvk *.p12 *.spc *.txt osslsigncode-2.2/tests/certs/ca-bundle.crt000066400000000000000000000053131410626701100206770ustar00rootroot00000000000000# Certum Trusted Network CA -----BEGIN CERTIFICATE----- MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI 03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= -----END CERTIFICATE----- # DigiCert Assured ID Root CA -----BEGIN CERTIFICATE----- MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== -----END CERTIFICATE----- osslsigncode-2.2/tests/certs/makecerts.sh000077500000000000000000000176031410626701100206550ustar00rootroot00000000000000#!/bin/sh result=0 test_result() { if test "$1" -eq 0 then printf "Succeeded\n" >> "makecerts.log" else printf "Failed\n" >> "makecerts.log" fi } make_certs() { password=passme result_path=$(pwd) cd $(dirname "$0") script_path=$(pwd) cd "${result_path}" mkdir "tmp/" # OpenSSL settings CONF="${script_path}/openssl_intermediate.cnf" TEMP_LD_LIBRARY_PATH=$LD_LIBRARY_PATH if test -n "$1" then OPENSSL="$1/bin/openssl" LD_LIBRARY_PATH="$1/lib" else OPENSSL=openssl fi mkdir "demoCA/" 2>> "makecerts.log" 1>&2 touch "demoCA/index.txt" touch "demoCA/index.txt.attr" echo 1000 > "demoCA/serial" date > "makecerts.log" $OPENSSL version 2>> "makecerts.log" 1>&2 echo -n "$password" > "password.txt" printf "\nGenerate root CA certificate\n" >> "makecerts.log" $OPENSSL genrsa -out demoCA/CA.key \ 2>> "makecerts.log" 1>&2 TZ=GMT faketime -f '@2017-01-01 00:00:00' /bin/bash -c ' script_path=$(pwd) OPENSSL=openssl CONF="${script_path}/openssl_root.cnf" $OPENSSL req -config $CONF -new -x509 -days 3600 -key demoCA/CA.key -out tmp/CACert.pem \ -subj "/C=PL/O=osslsigncode/OU=Certification Authority/CN=Root CA" \ 2>> "makecerts.log" 1>&2' test_result $? printf "\nGenerate intermediate CA certificate\n" >> "makecerts.log" $OPENSSL genrsa -out demoCA/intermediate.key \ 2>> "makecerts.log" 1>&2 TZ=GMT faketime -f '@2017-01-01 00:00:00' /bin/bash -c ' script_path=$(pwd) OPENSSL=openssl CONF="${script_path}/openssl_intermediate.cnf" $OPENSSL req -config $CONF -new -key demoCA/intermediate.key -out demoCA/intermediate.csr \ -subj "/C=PL/O=osslsigncode/OU=Certification Authority/CN=Intermediate CA" \ 2>> "makecerts.log" 1>&2' test_result $? TZ=GMT faketime -f '@2017-01-01 00:00:00' /bin/bash -c ' script_path=$(pwd) OPENSSL=openssl CONF="${script_path}/openssl_root.cnf" $OPENSSL ca -config $CONF -batch -in demoCA/intermediate.csr -out demoCA/intermediate.cer \ 2>> "makecerts.log" 1>&2' test_result $? $OPENSSL x509 -in demoCA/intermediate.cer -out tmp/intermediate.pem \ 2>> "makecerts.log" 1>&2 printf "\nGenerate private RSA encrypted key\n" >> "makecerts.log" $OPENSSL genrsa -des3 -out demoCA/private.key -passout pass:$password \ 2>> "makecerts.log" 1>&2 test_result $? cat demoCA/private.key >> tmp/keyp.pem 2>> "makecerts.log" printf "\nGenerate private RSA decrypted key\n" >> "makecerts.log" $OPENSSL rsa -in demoCA/private.key -passin pass:$password -out tmp/key.pem \ 2>> "makecerts.log" 1>&2 test_result $? printf "\nGenerate a certificate to revoke\n" >> "makecerts.log" $OPENSSL req -config $CONF -new -key demoCA/private.key -passin pass:$password -out demoCA/revoked.csr \ -subj "/C=PL/O=osslsigncode/OU=CSP/CN=Revoked/emailAddress=osslsigncode@example.com" \ 2>> "makecerts.log" 1>&2 $OPENSSL ca -config $CONF -batch -in demoCA/revoked.csr -out demoCA/revoked.cer \ 2>> "makecerts.log" 1>&2 $OPENSSL x509 -in demoCA/revoked.cer -out tmp/revoked.pem \ 2>> "makecerts.log" 1>&2 printf "\nRevoke above certificate\n" >> "makecerts.log" $OPENSSL ca -config $CONF -revoke demoCA/1001.pem \ 2>> "makecerts.log" 1>&2 printf "\nAttach intermediate certificate to revoked certificate\n" >> "makecerts.log" cat tmp/intermediate.pem >> tmp/revoked.pem printf "\nGenerate CRL file\n" >> "makecerts.log" TZ=GMT faketime -f '@2019-01-01 00:00:00' /bin/bash -c ' script_path=$(pwd) OPENSSL=openssl CONF="${script_path}/openssl_intermediate.cnf" $OPENSSL ca -config $CONF -gencrl -crldays 8766 -out tmp/CACertCRL.pem \ 2>> "makecerts.log" 1>&2' printf "\nConvert revoked certificate to SPC format\n" >> "makecerts.log" $OPENSSL crl2pkcs7 -in tmp/CACertCRL.pem -certfile tmp/revoked.pem -outform DER -out tmp/revoked.spc \ 2>> "makecerts.log" 1>&2 test_result $? printf "\nGenerate CSP Cross-Certificate\n" >> "makecerts.log" $OPENSSL genrsa -out demoCA/cross.key \ 2>> "makecerts.log" 1>&2 TZ=GMT faketime -f '@2018-01-01 00:00:00' /bin/bash -c ' script_path=$(pwd) OPENSSL=openssl CONF="${script_path}/openssl_intermediate.cnf" $OPENSSL req -config $CONF -new -x509 -days 900 -key demoCA/cross.key -out tmp/crosscert.pem \ -subj "/C=PL/O=osslsigncode/OU=CSP/CN=crosscert/emailAddress=osslsigncode@example.com" \ 2>> "makecerts.log" 1>&2' test_result $? printf "\nGenerate code signing certificate\n" >> "makecerts.log" $OPENSSL req -config $CONF -new -key demoCA/private.key -passin pass:$password -out demoCA/cert.csr \ -subj "/C=PL/ST=Mazovia Province/L=Warsaw/O=osslsigncode/OU=CSP/CN=Certificate/emailAddress=osslsigncode@example.com" \ 2>> "makecerts.log" 1>&2 test_result $? $OPENSSL ca -config $CONF -batch -in demoCA/cert.csr -out demoCA/cert.cer \ 2>> "makecerts.log" 1>&2 test_result $? $OPENSSL x509 -in demoCA/cert.cer -out tmp/cert.pem \ 2>> "makecerts.log" 1>&2 test_result $? printf "\nConvert the key to DER format\n" >> "makecerts.log" $OPENSSL rsa -in tmp/key.pem -outform DER -out tmp/key.der -passout pass:$password \ 2>> "makecerts.log" 1>&2 test_result $? printf "\nConvert the key to PVK format\n" >> "makecerts.log" $OPENSSL rsa -in tmp/key.pem -outform PVK -out tmp/key.pvk -pvk-none \ 2>> "makecerts.log" 1>&2 test_result $? printf "\nConvert the certificate to DER format\n" >> "makecerts.log" $OPENSSL x509 -in tmp/cert.pem -outform DER -out tmp/cert.der \ 2>> "makecerts.log" 1>&2 test_result $? printf "\nAttach intermediate certificate to code signing certificate\n" >> "makecerts.log" cat tmp/intermediate.pem >> tmp/cert.pem printf "\nConvert the certificate to SPC format\n" >> "makecerts.log" $OPENSSL crl2pkcs7 -nocrl -certfile tmp/cert.pem -outform DER -out tmp/cert.spc \ 2>> "makecerts.log" 1>&2 test_result $? printf "\nConvert the certificate and the key into a PKCS#12 container\n" >> "makecerts.log" $OPENSSL pkcs12 -export -in tmp/cert.pem -inkey tmp/key.pem -out tmp/cert.p12 -passout pass:$password \ 2>> "makecerts.log" 1>&2 test_result $? printf "\nGenerate expired certificate\n" >> "makecerts.log" $OPENSSL req -config $CONF -new -key demoCA/private.key -passin pass:$password -out demoCA/expired.csr \ -subj "/C=PL/ST=Mazovia Province/L=Warsaw/O=osslsigncode/OU=CSP/CN=Expired/emailAddress=osslsigncode@example.com" \ 2>> "makecerts.log" 1>&2 test_result $? $OPENSSL ca -config $CONF -enddate "190101000000Z" -batch -in demoCA/expired.csr -out demoCA/expired.cer \ 2>> "makecerts.log" 1>&2 test_result $? $OPENSSL x509 -in demoCA/expired.cer -out tmp/expired.pem \ 2>> "makecerts.log" 1>&2 test_result $? printf "\nAttach intermediate certificate to expired certificate\n" >> "makecerts.log" cat tmp/intermediate.pem >> tmp/expired.pem # copy new files if test -s tmp/intermediate.pem -a -s tmp/CACert.pem -a -s tmp/CACertCRL.pem \ -a -s tmp/key.pem -a -s tmp/keyp.pem -a -s tmp/key.der -a -s tmp/key.pvk \ -a -s tmp/cert.pem -a -s tmp/cert.p12 -a -s tmp/cert.der -a -s tmp/cert.spc \ -a -s tmp/crosscert.pem -a -s tmp/expired.pem -a -s tmp/revoked.pem -a -s tmp/revoked.spc then cp tmp/* ./ printf "%s\n" "keys & certificates successfully generated" printf "%s\n" "makecerts.sh finished" rm -f "makecerts.log" else printf "%s\n" "makecerts.sh failed" printf "%s\n" "error logs ${result_path}/makecerts.log" result=1 fi # remove the working directory rm -rf "demoCA/" rm -rf "tmp/" # restore settings LD_LIBRARY_PATH=$TEMP_LD_LIBRARY_PATH exit $result } # Tests requirement if test -n "$(command -v faketime)" then make_certs $1 result=$? else printf "%s\n" "faketime not found in \$PATH" printf "%s\n" "tests skipped, please install faketime package" result=1 fi exit $result osslsigncode-2.2/tests/certs/openssl_intermediate.cnf000066400000000000000000000047311410626701100232430ustar00rootroot00000000000000# OpenSSL intermediate CA configuration file [ ca ] default_ca = CA_default [ CA_default ] # Directory and file locations dir = . certs = $dir/demoCA crl_dir = $dir/demoCA new_certs_dir = $dir/demoCA database = $dir/demoCA/index.txt serial = $dir/demoCA/serial private_key = $dir/demoCA/intermediate.key certificate = $dir/tmp/intermediate.pem crl_extensions = crl_ext default_md = sha256 preserve = no policy = policy_loose default_startdate = 180101000000Z default_enddate = 241231000000Z x509_extensions = v3_req [ req ] # Options for the `req` tool encrypt_key = no default_bits = 2048 default_md = sha256 string_mask = utf8only distinguished_name = req_distinguished_name x509_extensions = usr_extensions [ crl_ext ] # Extension for CRLs authorityKeyIdentifier = keyid:always [ usr_extensions ] # Extension to add when the -x509 option is used basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer extendedKeyUsage = codeSigning [ v3_req ] basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer extendedKeyUsage = codeSigning [ policy_loose ] # Allow the intermediate CA to sign a more diverse range of certificates. # See the POLICY FORMAT section of the `ca` man page. countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ req_distinguished_name ] countryName = Country Name (2 letter code) stateOrProvinceName = State or Province Name localityName = Locality Name 0.organizationName = Organization Name organizationalUnitName = Organizational Unit Name commonName = Common Name emailAddress = Email Address osslsigncode-2.2/tests/certs/openssl_root.cnf000066400000000000000000000045371410626701100215600ustar00rootroot00000000000000# OpenSSL root CA configuration file [ ca ] default_ca = CA_default [ CA_default ] # Directory and file locations. dir = . certs = $dir/demoCA crl_dir = $dir/demoCA new_certs_dir = $dir/demoCA database = $dir/demoCA/index.txt serial = $dir/demoCA/serial private_key = $dir/demoCA/CA.key certificate = $dir/tmp/CACert.pem crl_extensions = crl_ext default_md = sha256 preserve = no policy = policy_match default_startdate = 180101000000Z default_enddate = 260101000000Z x509_extensions = v3_intermediate_ca [ req ] # Options for the `req` tool encrypt_key = no default_bits = 2048 default_md = sha256 string_mask = utf8only x509_extensions = ca_extensions distinguished_name = req_distinguished_name [ ca_extensions ] # Extension to add when the -x509 option is used basicConstraints = critical, CA:true subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer keyUsage = critical, digitalSignature, cRLSign, keyCertSign [ v3_intermediate_ca ] # Extensions for a typical intermediate CA (`man x509v3_config`) basicConstraints = critical, CA:true, pathlen:0 subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer keyUsage = critical, digitalSignature, cRLSign, keyCertSign [ policy_match ] countryName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ req_distinguished_name ] countryName = Country Name (2 letter code) stateOrProvinceName = State or Province Name localityName = Locality Name 0.organizationName = Organization Name organizationalUnitName = Organizational Unit Name commonName = Common Name emailAddress = Email Address osslsigncode-2.2/tests/certs/openssltest.cnf000066400000000000000000000042711410626701100214100ustar00rootroot00000000000000# OpenSSL root CA configuration file [ ca ] default_ca = CA_default [ CA_default ] # Directory and file locations. dir = . certs = $dir/demoCA crl_dir = $dir/demoCA new_certs_dir = $dir/demoCA database = $dir/demoCA/index.txt serial = $dir/demoCA/serial crl_extensions = crl_ext default_md = sha256 preserve = no policy = policy_match x509_extensions = usr_cert private_key = $dir/demoCA/CA.key certificate = $dir/tmp/CACert.pem default_startdate = 180101000000Z default_enddate = 210101000000Z [ req ] encrypt_key = no default_bits = 2048 default_md = sha256 string_mask = utf8only x509_extensions = ca_extensions distinguished_name = req_distinguished_name [ crl_ext ] authorityKeyIdentifier = keyid:always [ usr_cert ] basicConstraints = CA:FALSE subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer extendedKeyUsage = codeSigning [ ca_extensions ] basicConstraints = critical, CA:true subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer keyUsage = critical, digitalSignature, cRLSign, keyCertSign [ policy_match ] countryName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ req_distinguished_name ] countryName = Country Name (2 letter code) stateOrProvinceName = State or Province Name localityName = Locality Name 0.organizationName = Organization Name organizationalUnitName = Organizational Unit Name commonName = Common Name emailAddress = Email Address osslsigncode-2.2/tests/recipes/000077500000000000000000000000001410626701100166435ustar00rootroot00000000000000osslsigncode-2.2/tests/recipes/01_sign_pem000066400000000000000000000031701410626701100206700ustar00rootroot00000000000000#!/bin/sh # Sign a file with a certificate and a private key in the PEM format. # -st 1556668800 is the Unix time of May 1 00:00:00 2019 GMT . $(dirname $0)/../test_library script_path=$(pwd) test_nr=1 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with a certificate and a private key in the PEM format" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/02_sign_pass000066400000000000000000000031031410626701100210520ustar00rootroot00000000000000#!/bin/sh # Sign a file with an encrypted private key in the PEM format. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=2 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with an encrypted private key in the PEM format" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/keyp.pem" \ -pass passme \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/03_sign_der000066400000000000000000000031441410626701100206640ustar00rootroot00000000000000#!/bin/sh # Sign a file with an encrypted private key in the DER format. # Requires OpenSSL 1.0.0 or later . $(dirname $0)/../test_library script_path=$(pwd) test_nr=3 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with an encrypted private key in the DER format" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.der" \ -pass passme \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/04_sign_spc_pvk000066400000000000000000000031601410626701100215560ustar00rootroot00000000000000#!/bin/sh # Sign a file with a certificate in the SPC format # and a private key in the Microsoft Private Key (PVK) format. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=4 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with a SPC certificate and a PVK private key" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -spc "${script_path}/../certs/cert.spc" -key "${script_path}/../certs/key.pvk" \ -pass passme \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/05_sign_pkcs12000066400000000000000000000030621410626701100212160ustar00rootroot00000000000000#!/bin/sh # Sign a file with a certificate and a key stored in a PKCS#12 container. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=5 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with a certificate and a key stored in a PKCS#12 container" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -pkcs12 "${script_path}/../certs/cert.p12" \ -pass passme \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/06_test_sha256sum000066400000000000000000000016661410626701100217000ustar00rootroot00000000000000#!/bin/sh # Checking SHA256 message digests for 01x-05x tests . $(dirname $0)/../test_library script_path=$(pwd) result=0 test_nr=6 for file in ${script_path}/../logs/sha256sum/*.* do name="${file##*/}" case $name in "cat.log") filetype=CAT; format_nr=1 ;; "msi.log") filetype=MSI; format_nr=2 ;; "ex_.log") filetype=CAB; format_nr=3 ;; "exe.log") filetype=PE; format_nr=4 ;; "ps1.log") filetype=TXT; format_nr=5 ;; esac number="$test_nr$format_nr" test_name="Checking SHA256 message digests for a $filetype file test" printf "\n%03d. %s\n" "$number" "$test_name" if test $(cat "sha256sum/$name" | cut -d' ' -f1 | uniq | wc -l) -ne 1 then result=1 cat "sha256sum/$name" >> "results.log" printf "Non-unique SHA256 message digests found\n" >> "results.log" fi rm -f "sha256sum/$name" test_result "$result" "$number" "$test_name" done exit 0 osslsigncode-2.2/tests/recipes/07_sign_timestamp000066400000000000000000000036331410626701100221240ustar00rootroot00000000000000#!/bin/sh # Sign a file with Authenticode timestamping . $(dirname $0)/../test_library script_path=$(pwd) test_nr=7 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with Authenticode timestamping" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -t http://time.certum.pl/ \ -t http://timestamp.digicert.com/ \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done else format_nr=0 number="$test_nr$format_nr" test_name="Sign a file with Authenticode timestamping" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/08_sign_rfc3161000066400000000000000000000042541410626701100212070ustar00rootroot00000000000000#!/bin/sh # Sign a file with RFC 3161 timestamping # An RFC3161 timestamp server provides an essential function in protecting # data records for the long-term. It provides proof that the data existed # at a particular moment in time and that it has not changed, even by # a single binary bit, since it was notarized and time-stamped. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=8 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with RFC 3161 timestamping" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -ts http://time.certum.pl/ \ -ts http://timestamp.digicert.com/ \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done else format_nr=0 number="$test_nr$format_nr" test_name="Sign a file with RFC 3161 timestamping" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/09_sign_page_hashes000066400000000000000000000016271410626701100223730ustar00rootroot00000000000000#!/bin/sh # Generate page hashes for a file . $(dirname $0)/../test_library script_path=$(pwd) test_nr=9 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "exe") filetype=PE; format_nr=4 ;; *) continue ;; # Warning: -ph option is only valid for PE files esac number="$test_nr$format_nr" test_name="Generate page hashes for a $filetype file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 -ph \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" done exit 0 osslsigncode-2.2/tests/recipes/10_sign_blob000066400000000000000000000030531410626701100210250ustar00rootroot00000000000000#!/bin/sh # Sign a file with addUnauthenticatedBlob. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=10 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with addUnauthenticatedBlob" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -addUnauthenticatedBlob \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/11_sign_nest000066400000000000000000000025631410626701100210660ustar00rootroot00000000000000#!/bin/sh # Sign a file twice with the "nest" flag in the second time # in order to add the new signature instead of replacing the first one. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=11 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Warning: CAT files do not support nesting "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") continue;; # Warning: TXT files do not support nesting esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with the nest flag" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "signed_$number.$ext" ../../osslsigncode sign -h sha512 \ -nest \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "signed_$number.$ext" -out "test_$number.$ext" result=$? verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" done exit 0 osslsigncode-2.2/tests/recipes/12_sign_readpass_pem000066400000000000000000000032411410626701100225530ustar00rootroot00000000000000#!/bin/sh # Sign a file with a PEM key and a password read from password.txt file. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=12 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with a PEM key and a password read from password.txt file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -addUnauthenticatedBlob \ -readpass "${script_path}/../certs/password.txt" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/keyp.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/13_sign_readpass_pkcs12000066400000000000000000000032041410626701100230750ustar00rootroot00000000000000#!/bin/sh # Sign a file with the certificate and key stored in a PKCS#12 container # and a password read from password.txt file. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=13 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with a PKCS#12 container and the file with a password" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -readpass "${script_path}/../certs/password.txt" \ -pkcs12 "${script_path}/../certs/cert.p12" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "osslsigncode" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/14_sign_descryption000066400000000000000000000030331410626701100224540ustar00rootroot00000000000000#!/bin/sh # Sign a file with a descryption. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=14 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with a descryption" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -n "DESCRYPTION_TEXT" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "DESCRYPTION_TEXT" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/15_sign_url000066400000000000000000000033041410626701100207150ustar00rootroot00000000000000#!/bin/sh # Sign a file with specified URL for expanded description of the signed content # https://docs.microsoft.com/en-us/windows-hardware/drivers/install/authenticode-signing-of-csps . $(dirname $0)/../test_library script_path=$(pwd) test_nr=15 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with specified URL" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -i "https://www.osslsigncode.com/" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "https://www.osslsigncode.com/" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/16_sign_comm000066400000000000000000000036401410626701100210520ustar00rootroot00000000000000#!/bin/sh # Sign a file with Microsoft Commercial Code Signing purpose set for SPC_STATEMENT_TYPE_OBJID # object ID numbers (OIDs) "1.3.6.1.4.1.311.2.1.11" # changes default Microsoft Individual Code Signing: # "0x30, 0x0c, x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x15" # sets Microsoft Commercial Code Signing: # "0x30, 0x0c, x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x16" . $(dirname $0)/../test_library script_path=$(pwd) test_nr=16 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with the common purpose set" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -comm \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "Microsoft Commercial Code Signing" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/17_sign_crosscertfile000066400000000000000000000034461410626701100227730ustar00rootroot00000000000000#!/bin/sh # Add an additional certificate to the signature block of the file. # https://docs.microsoft.com/en-us/windows-hardware/drivers/install/authenticode-signing-of-csps # https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-cross-certification . $(dirname $0)/../test_library script_path=$(pwd) test_nr=17 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Add an additional certificate to the signature block of a $filetype$desc file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -ac "${script_path}/../certs/crosscert.pem" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "crosscert" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/21_sign_hash_md5000066400000000000000000000030411410626701100215760ustar00rootroot00000000000000#!/bin/sh # Sign a file with MD5 set of cryptographic hash functions. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=21 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with MD5 set of cryptographic hash functions" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h md5 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "MD5" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/22_sign_hash_sha1000066400000000000000000000030451410626701100217520ustar00rootroot00000000000000#!/bin/sh # Sign a file with SHA1 set of cryptographic hash functions. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=22 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with SHA1 set of cryptographic hash functions" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha1 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "SHA1" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/23_sign_hash_sha2000066400000000000000000000030501410626701100217500ustar00rootroot00000000000000#!/bin/sh # Signing a file with SHA2 set of cryptographic hash functions. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=23 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with SHA2 set of cryptographic hash functions" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha2 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "SHA2" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/24_sign_hash_sha384000066400000000000000000000030551410626701100221330ustar00rootroot00000000000000#!/bin/sh # Sign a file with SHA384 set of cryptographic hash functions. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=24 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with SHA384 set of cryptographic hash functions" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha384 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "SHA384" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/25_sign_hash_sha512000066400000000000000000000030551410626701100221250ustar00rootroot00000000000000#!/bin/sh # Sign a file with SHA512 set of cryptographic hash functions. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=25 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with SHA512 set of cryptographic hash functions" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha512 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "SHA512" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/26_extract_signature_pem000066400000000000000000000031651410626701100234760ustar00rootroot00000000000000#!/bin/sh # Extract the signature in the PEM format. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=26 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Unsupported command "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Extract the PEM signature from the $filetype$desc file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha512 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" ../../osslsigncode extract-signature \ -pem \ -in "test_$number.$ext" -out "sign_$format_nr.pem" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "SHA512" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/27_extract_signature_der000066400000000000000000000031471410626701100234700ustar00rootroot00000000000000#!/bin/sh # Extract the signature in the DER format. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=27 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Unsupported command "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Extract the DER signature from the $filetype$desc file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha512 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" ../../osslsigncode extract-signature\ -in "test_$number.$ext" -out "sign_$format_nr.der" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "SHA512" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/31_attach_signature_der000066400000000000000000000032711410626701100232530ustar00rootroot00000000000000#!/bin/sh # Attach the DER signature to the file. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=31 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Unsupported command "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Attach the DER signature to the $filetype$desc file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode attach-signature \ -sigin "sign_$format_nr.der" \ -CAfile "${script_path}/../certs/CACert.pem" \ -CRLfile "${script_path}/../certs/CACertCRL.pem" \ -TSA-CAfile "${script_path}/../certs/ca-bundle.crt" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$result" -ne 0; then cp "sign_$format_nr.der" "sign_$number.der" fi if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "SHA512" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/32_attach_signature_pem000066400000000000000000000032711410626701100232630ustar00rootroot00000000000000#!/bin/sh # Attach the PEM signature to the file. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=32 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Unsupported command "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Attach the PEM signature to the $filetype$desc file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode attach-signature \ -sigin "sign_$format_nr.pem" \ -CAfile "${script_path}/../certs/CACert.pem" \ -CRLfile "${script_path}/../certs/CACertCRL.pem" \ -TSA-CAfile "${script_path}/../certs/ca-bundle.crt" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$result" -ne 0; then cp "sign_$format_nr.der" "sign_$number.der" fi if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "SHA512" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/33_attach_signed000066400000000000000000000034741410626701100217000ustar00rootroot00000000000000#!/bin/sh # Attach the signature to the signed file. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=33 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Unsupported command "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Attach the PEM signature to the signed $filetype$desc file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "signed_$number.$ext" ../../osslsigncode attach-signature \ -sigin "sign_$format_nr.pem" \ -CAfile "${script_path}/../certs/CACert.pem" \ -CRLfile "${script_path}/../certs/CACertCRL.pem" \ -TSA-CAfile "${script_path}/../certs/ca-bundle.crt" \ -in "signed_$number.$ext" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "sha256sum" "SHA512" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/34_attach_nest000066400000000000000000000027621410626701100214000ustar00rootroot00000000000000#!/bin/sh # Attach the signature to the signed file with the "nest" flag in order to # attach the new signature instead of replacing the first one. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=34 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Warning: CAT files do not support nesting "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") continue;; # Warning: TXT files do not support nesting esac number="$test_nr$format_nr" test_name="Attach the PEM signature to the signed $filetype$desc file with the nest flag" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "signed_$number.$ext" ../../osslsigncode attach-signature \ -sigin "sign_$format_nr.pem" \ -nest \ -CAfile "${script_path}/../certs/CACert.pem" \ -CRLfile "${script_path}/../certs/CACertCRL.pem" \ -TSA-CAfile "${script_path}/../certs/ca-bundle.crt" \ -in "signed_$number.$ext" -out "test_$number.$ext" result=$? verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "SHA512" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" done exit 0 osslsigncode-2.2/tests/recipes/35_remove_signature000066400000000000000000000031511410626701100224530ustar00rootroot00000000000000#!/bin/sh # Remove the signature from the file. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=35 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Unsupported command "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Remove the signature from the $filetype$desc file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "signed_$number.$ext" ../../osslsigncode remove-signature \ -in "signed_$number.$ext" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "fail" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "UNUSED_PATTERN" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/36_varia_sha256sum000066400000000000000000000017071410626701100220220ustar00rootroot00000000000000#!/bin/sh # Checking SHA256 message digests for "extract" and "attach" tests. . $(dirname $0)/../test_library script_path=$(pwd) result=0 test_nr=36 for file in ${script_path}/../logs/sha256sum/*.* do name="${file##*/}" case $name in "cat.log") filetype=CAT; format_nr=1 ;; "msi.log") filetype=MSI; format_nr=2 ;; "ex_.log") filetype=CAB; format_nr=3 ;; "exe.log") filetype=PE; format_nr=4 ;; "ps1.log") filetype=TXT; format_nr=5 ;; esac number="$test_nr$format_nr" test_name="Checking SHA256 message digests for a $filetype file test" printf "\n%03d. %s\n" "$number" "$test_name" if test $(cat "sha256sum/$name" | cut -d' ' -f1 | uniq | wc -l) -ne 1 then result=1 cat "sha256sum/$name" >> "results.log" printf "Non-unique SHA256 message digests found\n" >> "results.log" fi rm -f "sha256sum/$name" test_result "$result" "$number" "$test_name" done exit 0 osslsigncode-2.2/tests/recipes/37_add_signature_timestamp000066400000000000000000000040541410626701100237760ustar00rootroot00000000000000#!/bin/sh # Add an authenticode timestamp to the signed file. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=37 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Add an authenticode timestamp to the $filetype$desc signed file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "signed_$number.$ext" ../../osslsigncode add \ -t http://time.certum.pl/ \ -t http://timestamp.digicert.com/ \ -verbose \ -in "signed_$number.$ext" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "Timestamp Server Signature" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done else format_nr=0 number="$test_nr$format_nr" test_name="Add an authenticode timestamp to the signed file" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/38_add_signature_rfc3161000066400000000000000000000040411410626701100230550ustar00rootroot00000000000000#!/bin/sh # Add a RFC 3161 timestamp to the signed file. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=38 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Add a RFC 3161 timestamp to the $filetype$desc signed file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "signed_$number.$ext" ../../osslsigncode add \ -ts http://time.certum.pl/ \ -ts http://timestamp.digicert.com/ \ -verbose \ -in "signed_$number.$ext" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "Timestamp Server Signature" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done else format_nr=0 number="$test_nr$format_nr" test_name="Add a RFC 3161 timestamp to the signed file" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/39_add_signature_blob000066400000000000000000000032261410626701100227130ustar00rootroot00000000000000#!/bin/sh # Add an unauthenticated blob to the signed file. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=39 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Add an unauthenticated blob to the $filetype$desc signed file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "signed_$number.$ext" ../../osslsigncode add \ -addUnauthenticatedBlob \ -in "signed_$number.$ext" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "Unauthenticated Data Blob" "MODIFY" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/40_verify_leaf_hash000066400000000000000000000030151410626701100223660ustar00rootroot00000000000000#!/bin/sh # Compare the leaf certificate hash against specified SHA256 message digest for the file . $(dirname $0)/../test_library script_path=$(pwd) test_nr=40 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Compare the leaf hash against SHA256 message digest for the $filetype$desc file" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.der" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_leaf_hash "$result" "$number" "$ext" "@2019-05-01 00:00:00" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/41_sign_add_msi_dse000066400000000000000000000031331410626701100223450ustar00rootroot00000000000000#!/bin/sh # Sign a MSI file with the add-msi-dse option. # MsiDigitalSignatureEx (msi-dse) is an enhanced signature type that can be used # when signing MSI files. In addition to file content, it also hashes some file metadata, # specifically file names, file sizes, creation times and modification times. # https://www.unboundtech.com/docs/UKC/UKC_Code_Signing_IG/HTML/Content/Products/UKC-EKM/UKC_Code_Signing_IG/Sign_Windows_PE_and_msi_Files.htm . $(dirname $0)/../test_library script_path=$(pwd) test_nr=41 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Warning: -add-msi-dse option is only valid for MSI files "msi") filetype=MSI; format_nr=2 ;; "ex_") continue;; # Warning: -add-msi-dse option is only valid for MSI files "exe") continue;; # Warning: -add-msi-dse option is only valid for MSI files "ps1") continue;; # Warning: -add-msi-dse option is only valid for MSI files esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with the add-msi-dse option" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -add-msi-dse \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "MsiDigitalSignatureEx" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" done exit 0 osslsigncode-2.2/tests/recipes/42_sign_jp_low000066400000000000000000000024161410626701100214100ustar00rootroot00000000000000#!/bin/sh # Sign a CAB file with "low" level of permissions in Microsoft Internet Explorer 4.x for CAB files # https://support.microsoft.com/en-us/help/193877 . $(dirname $0)/../test_library script_path=$(pwd) test_nr=42 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Warning: -jp option is only valid for CAB files "msi") continue;; # Warning: -jp option is only valid for CAB files "ex_") filetype=CAB; format_nr=3 ;; "exe") continue;; # Warning: -jp option is only valid for CAB files "ps1") continue;; # Warning: -jp option is only valid for CAB files esac number="$test_nr$format_nr" test_name="Sign a $filetype$desc file with the jp low option" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -jp low \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "Low level of permissions" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" done exit 0 osslsigncode-2.2/tests/recipes/45_verify_fake_pe000066400000000000000000000021211410626701100220500ustar00rootroot00000000000000#!/bin/sh # Verify changed file after signing. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=45 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Test is not supported for non-PE files "msi") continue;; # Test is not supported for non-PE files "ex_") continue;; # Test is not supported for non-PE files "exe") filetype=PE; format_nr=4 ;; "ps1") continue;; # Test is not supported for non-PE files esac number="$test_nr$format_nr" test_name="Verify changed $filetype$desc file after signing" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? verify_signature "$result" "$number" "$ext" "fail" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "Hello world!" "MODIFY" test_result "$?" "$number" "$test_name" done exit 0 osslsigncode-2.2/tests/recipes/46_verify_timestamp000066400000000000000000000030441410626701100224670ustar00rootroot00000000000000#!/bin/sh # Verify changed file after signing with Authenticode timestamping. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=46 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Test is not supported for non-PE files "msi") continue;; # Test is not supported for non-PE files "ex_") continue;; # Test is not supported for non-PE files "exe") filetype=PE; format_nr=4 ;; "ps1") continue;; # Test is not supported for non-PE files esac number="$test_nr$format_nr" test_name="Verify changed $filetype$desc file after signing with Authenticode timestamping" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -t http://time.certum.pl/ \ -t http://timestamp.digicert.com/ \ -verbose \ -in "notsigned/$name" -out "test_$number.$ext" result=$? verify_signature "$result" "$number" "$ext" "fail" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "Hello world!" "MODIFY" test_result "$?" "$number" "$test_name" done else format_nr=0 number="$test_nr$format_nr" test_name="Verify changed file after signing with Authenticode timestamping" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/47_verify_rfc3161000077500000000000000000000030341410626701100215540ustar00rootroot00000000000000#!/bin/sh # Verify changed file after signing with RFC 3161 timestamping. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=47 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Test is not supported for non-PE files "msi") continue;; # Test is not supported for non-PE files "ex_") continue;; # Test is not supported for non-PE files "exe") filetype=PE; format_nr=4 ;; "ps1") continue;; # Test is not supported for non-PE files esac number="$test_nr$format_nr" test_name="Verify changed $filetype$desc file after signing with RFC 3161 timestamping" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -ts http://time.certum.pl/ \ -ts http://timestamp.digicert.com/ \ -verbose \ -in "notsigned/$name" -out "test_$number.$ext" result=$? verify_signature "$result" "$number" "$ext" "fail" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "Hello world!" "MODIFY" test_result "$?" "$number" "$test_name" done else format_nr=0 number="$test_nr$format_nr" test_name="Verify changed file after signing with RFC 3161 timestamping" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/51_verify_time000066400000000000000000000030421410626701100214140ustar00rootroot00000000000000#!/bin/sh # Verify a file signed after the cert has been expired. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=51 for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Verify $filetype$desc file signed after the cert has been expired" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "fail" "@2025-01-01 12:00:00" \ "UNUSED_PATTERN" "UNUSED_PATTERN" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done exit 0 osslsigncode-2.2/tests/recipes/52_verify_timestamp000066400000000000000000000040271410626701100224660ustar00rootroot00000000000000#!/bin/sh # Verify a file signed with Authenticode timestamping after the cert has been expired. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=52 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Verify a $filetype$desc file signed with Authenticode after the cert has been expired" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -t http://time.certum.pl/ \ -t http://timestamp.digicert.com/ \ -verbose \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2025-01-01 12:00:00" \ "UNUSED_PATTERN" "UNUSED_PATTERN" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done else format_nr=0 number="$test_nr$format_nr" test_name="Verify a file signed with Authenticode after the cert has been expired" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/53_verify_rfc3161000066400000000000000000000040101410626701100215410ustar00rootroot00000000000000#!/bin/sh # Verify a file signed with RFC3161 timestamping after the cert has been expired. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=53 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Verify a $filetype$desc file signed with RFC3161 after the cert has been expired" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -ts http://time.certum.pl/ \ -ts http://timestamp.digicert.com/ \ -verbose \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "success" "@2025-01-01 12:00:00" \ "UNUSED_PATTERN" "UNUSED_PATTERN" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done else format_nr=0 number="$test_nr$format_nr" test_name="Verify a file signed with RFC3161 after the cert has been expired" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/54_verify_expired000066400000000000000000000036661410626701100221350ustar00rootroot00000000000000#!/bin/sh # Verify a file signed with the expired cert. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=54 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Verify a $filetype$desc file signed with the expired cert" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/expired.pem" -key "${script_path}/../certs/key.pem" \ -ts http://time.certum.pl/ \ -ts http://timestamp.digicert.com/ \ -verbose \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "fail" "@2025-01-01 12:00:00" \ "UNUSED_PATTERN" "UNUSED_PATTERN" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done else format_nr=0 number="$test_nr$format_nr" test_name="Verify a file signed with the expired cert" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/55_verify_revoked000066400000000000000000000036661410626701100221350ustar00rootroot00000000000000#!/bin/sh # Verify a file signed with the revoked cert. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=55 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") filetype=CAT; format_nr=1 ;; "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") filetype=TXT if xxd -p -l 2 "notsigned/$name" | grep -q "fffe"; then format_nr=5 desc=" UTF-16LE(BOM)" elif xxd -p -l 3 "notsigned/$name" | grep -q "efbbbf"; then format_nr=6 desc=" UTF-8(BOM)" else format_nr=7 desc=" UTF-8" fi ;; esac number="$test_nr$format_nr" test_name="Verify a $filetype$desc file signed with the revoked cert" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/revoked.pem" -key "${script_path}/../certs/key.pem" \ -ts http://time.certum.pl/ \ -ts http://timestamp.digicert.com/ \ -verbose \ -in "notsigned/$name" -out "test_$number.$ext" result=$? if test "$filetype" = "TXT" && ! cmp -l -n 3 "notsigned/$name" "test_$number.$ext"; then printf "%s\n" "Compare file prefix failed" test_result "1" "$number" "$test_name" else verify_signature "$result" "$number" "$ext" "fail" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "UNUSED_PATTERN" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" fi done else format_nr=0 number="$test_nr$format_nr" test_name="Verify a file signed with the revoked cert" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/recipes/56_verify_multiple000066400000000000000000000040571410626701100223250ustar00rootroot00000000000000#!/bin/sh # Verify a file signed with the multiple signature. . $(dirname $0)/../test_library script_path=$(pwd) test_nr=56 if ! grep -q "no libcurl available" "results.log"; then for file in ${script_path}/../logs/notsigned/*.* do name="${file##*/}" ext="${file##*.}" desc="" case $ext in "cat") continue;; # Warning: CAT files do not support nesting "msi") filetype=MSI; format_nr=2 ;; "ex_") filetype=CAB; format_nr=3 ;; "exe") filetype=PE; format_nr=4 ;; "ps1") continue;; # Warning: TXT files do not support nesting esac number="$test_nr$format_nr" test_name="Verify a $filetype$desc file signed with the multiple signature" printf "\n%03d. %s\n" "$number" "$test_name" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -certs "${script_path}/../certs/expired.pem" -key "${script_path}/../certs/key.pem" \ -verbose \ -in "notsigned/$name" -out "signed1_$number.$ext" ../../osslsigncode sign -h sha384 \ -st "1556668800" \ -nest \ -certs "${script_path}/../certs/revoked.pem" -key "${script_path}/../certs/key.pem" \ -t http://time.certum.pl/ \ -t http://timestamp.digicert.com/ \ -verbose \ -in "signed1_$number.$ext" -out "signed2_$number.$ext" ../../osslsigncode sign -h sha256 \ -st "1556668800" \ -nest \ -certs "${script_path}/../certs/cert.pem" -key "${script_path}/../certs/key.pem" \ -ts http://time.certum.pl/ \ -ts http://timestamp.digicert.com/ \ -verbose \ -in "signed2_$number.$ext" -out "test_$number.$ext" result=$? verify_signature "$result" "$number" "$ext" "success" "@2019-09-01 12:00:00" \ "UNUSED_PATTERN" "SHA384" "UNUSED_PATTERN" test_result "$?" "$number" "$test_name" done else format_nr=0 number="$test_nr$format_nr" test_name="Verify a file signed with the multiple signature" printf "\n%03d. %s\nTest skipped\n" "$number" "$test_name" fi exit 0 osslsigncode-2.2/tests/sources/000077500000000000000000000000001410626701100166745ustar00rootroot00000000000000osslsigncode-2.2/tests/sources/a000066400000000000000000000000041410626701100170310ustar00rootroot00000000000000aaa osslsigncode-2.2/tests/sources/b000066400000000000000000000000041410626701100170320ustar00rootroot00000000000000bbb osslsigncode-2.2/tests/sources/c000066400000000000000000000000041410626701100170330ustar00rootroot00000000000000ccc osslsigncode-2.2/tests/sources/good.cat000077500000000000000000000006121410626701100203170ustar00rootroot000000000000000 *H w0s10h +7 Y0U0  +7 ɛ$B&%= 201029102811Z0 +7 00UnsignedPE10M +71?0=0 +70 0!0 + ^gqIG[]eb~10b +7 1T0RL{C689AAB8-8E78-11D0-8C47-00C04FC295EE}@0>0< +7 .0, Movie1FirstMovie1osslsigncode-2.2/tests/sources/myapp.c000066400000000000000000000001041410626701100201610ustar00rootroot00000000000000#include void main(void) { printf("Hello world!"); } osslsigncode-2.2/tests/sources/sample.wxs000066400000000000000000000025721410626701100207260ustar00rootroot00000000000000 osslsigncode-2.2/tests/test_library000077500000000000000000000107131410626701100176440ustar00rootroot00000000000000# this file is a library sourced from recipes/* result_path=$(pwd) cd $(dirname "$0")/../ script_path=$(pwd) cd "${result_path}" test_result() { #1 last exit status #2 test number #3 test name local result=0 if test "$1" -eq 0 then printf "%s\n" "Test succeeded" else printf "%s\n" "Test failed" printf "%03d. %-90s\t%s\n" "$2" "$3" "failed" 1>&3 result=1 fi return "$result" } modify_blob() { # $1 test number # $2 filename extension # $3 text searched in a binary file local result=0 initial_blob=$(echo -n "$3" | xxd -p) modified_blob=$(echo -n "FAKE" | xxd -p) zero_blob="00000000" xxd -p -c 1000 "test_$1.$2" | \ sed "s/$initial_blob$zero_blob/$initial_blob$modified_blob/" | \ xxd -p -r > "changed_$1.$2" ../../osslsigncode verify \ -CAfile "${script_path}/../certs/CACert.pem" \ -CRLfile "${script_path}/../certs/CACertCRL.pem" \ -TSA-CAfile "${script_path}/../certs/ca-bundle.crt" \ -in "changed_$1.$2" 2>> "verify.log" 1>&2 result=$? if test "$result" -ne 0 \ -o $(grep -e "Calculated DigitalSignature" -e "Calculated message digest" "verify.log" | uniq | wc -l) -gt 1 then printf "Failed: verify error or non-unique message digests found\n" 2>> "verify.log" 1>&2 result=1 else rm -f "changed_$1.$2" fi return "$result" } search_pattern() { # $1 test number # $2 filename extension # $3 pattern searched in a binary file or verify.log local result=0 if ! grep -q "$3" "verify.log" then hex_pattern=$(echo -n "$3" | xxd -p) if ! xxd -p -c 1000 "test_$1.$2" | grep "$hex_pattern" 2>> /dev/null 1>&2 then result=1 printf "Failed: $3 not found\n" fi fi return "$result" } verify_signature() { # $1 sign exit code # $2 test number # $3 filename extension # $4 expected result # $5 fake time # $6 sha256sum requirement # $7 pattern searched in the verify.log file # $8 modify requirement local result=0 printf "" > "verify.log" if test "$1" -eq 0 then cp "test_$2.$3" "test_tmp.tmp" TZ=GMT faketime -f "$5" /bin/bash -c ' printf "Verify time: " >> "verify.log" && date >> "verify.log" && printf "\n" >> "verify.log" script_path=$(pwd) ../../osslsigncode verify \ -CAfile "${script_path}/../certs/CACert.pem" \ -CRLfile "${script_path}/../certs/CACertCRL.pem" \ -TSA-CAfile "${script_path}/../certs/ca-bundle.crt" \ -in "test_tmp.tmp" 2>> "verify.log" 1>&2' result=$? rm -f "test_tmp.tmp" if test "$result" -eq 0 -a "$7" != "UNUSED_PATTERN" then search_pattern "$2" "$3" "$7" result=$? fi if test "$result" -eq 0 -a "$8" = "MODIFY" then modify_blob "$2" "$3" "$7" result=$? fi if test "$6" = "sha256sum" then sha256sum "test_$2.$3" 2>> "sha256sum/$3.log" 1>&2 fi if test "$4" = "success" -a "$result" -eq 0 then rm -f "test_$2.$3" "signed_$2.$3" "signed1_$2.$3" "signed2_$2.$3" elif test "$4" = "fail" -a "$result" -eq 1 then rm -f "test_$2.$3" "signed_$2.$3" "signed1_$2.$3" "signed2_$2.$3" rm -f "changed_$2.$3" cat "verify.log" >> "results.log" result=0 else cat "verify.log" >> "results.log" result=1 fi else result=1 fi return "$result" } verify_leaf_hash() { # $1 sign exit code # $2 test number # $3 filename extension # $4 fake time local result=0 printf "" > "verify.log" if test "$1" -eq 0 then cp "test_$2.$3" "test_tmp.tmp" TZ=GMT faketime -f "$4" /bin/bash -c ' printf "Verify time: " >> "verify.log" && date >> "verify.log" && printf "\n" >> "verify.log" script_path=$(pwd) ../../osslsigncode verify \ -CAfile "${script_path}/../certs/CACert.pem" \ -CRLfile "${script_path}/../certs/CACertCRL.pem" \ -TSA-CAfile "${script_path}/../certs/ca-bundle.crt" \ -require-leaf-hash SHA256:$(sha256sum "${script_path}/../certs/cert.der" | cut -d" " -f1) \ -in "test_tmp.tmp" 2>> "verify.log" 1>&2' result=$? rm -f "test_tmp.tmp" if test "$result" -eq 0 then rm -f "test_$2.$3" else cat "verify.log" >> "results.log" fi else result=1 fi return "$result" } osslsigncode-2.2/tests/testall.sh000077500000000000000000000072721410626701100172300ustar00rootroot00000000000000#!/bin/sh # mingw64-gcc, gcab, msitools, libgsf, libgsf-devel # vim-common, libfaketime packages are required result=0 count=0 skip=0 fail=0 result_path=$(pwd) cd $(dirname "$0") script_path=$(pwd) result_path="${result_path}/logs" certs_path="${script_path}/certs" make_tests() { for plik in ${script_path}/recipes/* do /bin/sh $plik 3>&1 2>> "results.log" 1>&2 done count=$(grep -c "Test succeeded" "results.log") if test $count -ne 0 then skip=$(grep -c "Test skipped" "results.log") fail=$(grep -c "Test failed" "results.log") printf "%s\n" "testall.sh finished" printf "%s\n" "summary: success $count, skip $skip, fail $fail" else # no test was done result=1 fi } rm -rf "${result_path}" mkdir "${result_path}" cd "${result_path}" mkdir "notsigned" "sha256sum" date > "results.log" ../../osslsigncode -v >> "results.log" 2>/dev/null cd ${certs_path} if test -s CACert.pem -a -s crosscert.pem -a -s expired.pem -a -s cert.pem \ -a -s CACertCRL.pem -a -s revoked.pem -a -s key.pem -a -s keyp.pem \ -a -s key.der -a -s cert.der -a -s cert.spc -a -s cert.p12 then printf "%s\n" "keys & certificates path: ${certs_path}" else ./makecerts.sh $1 result=$? fi cd "${result_path}" if test "$result" -ne 0 then exit $result fi # PE files support if test -n "$(command -v x86_64-w64-mingw32-gcc)" then x86_64-w64-mingw32-gcc "../sources/myapp.c" -o "notsigned/test.exe" 2>> "results.log" 1>&2 else printf "%s\n" "x86_64-w64-mingw32-gcc not found in \$PATH" printf "%s\n" "tests for PE files skipped, please install mingw64-gcc package" fi # CAB files support if test -n "$(command -v gcab)" then gcab -c "notsigned/test.ex_" "../sources/a" "../sources/b" "../sources/c" 2>> "results.log" 1>&2 else printf "%s\n" "gcab not found in \$PATH" printf "%s\n" "tests for CAB files skipped, please install gcab package" fi # MSI files support if grep -q "no libgsf available" "results.log" then printf "%s\n" "signing MSI files requires libgsf/libgsf-devel packages and reconfiguration osslsigncode" else if test -n "$(command -v wixl)" then touch FoobarAppl10.exe cp "../sources/sample.wxs" "notsigned/sample.wxs" 2>> "results.log" 1>&2 wixl -v "notsigned/sample.wxs" 2>> "results.log" 1>&2 rm -f "notsigned/sample.wxs" rm -f "FoobarAppl10.exe" else printf "%s\n" "wixl not found in \$PATH" printf "%s\n" "tests for MSI files skipped, please install wixl or msitools package depending on your OS" fi fi # CAT files support if test -s "../sources/good.cat" then cp "../sources/good.cat" "notsigned/good.cat" fi # TXT files support if test -s "../sources/utf8.ps1" then cp "../sources/utf8.ps1" "notsigned/utf8.ps1" fi if test -s "../sources/utf8bom.ps1" then cp "../sources/utf8bom.ps1" "notsigned/utf8bom.ps1" fi if test -s "../sources/utf16le.ps1" then cp "../sources/utf16le.ps1" "notsigned/utf16le.ps1" fi # Timestamping support if grep -q "no libcurl available" "results.log" then printf "%s\n" "configure --with-curl is required for timestamping support" fi # Tests requirements if test -n "$(command -v faketime)" then if test -n "$(command -v xxd)" then make_tests result=$? rm -r -f "notsigned/" "sha256sum/" rm -f sign_[1-9].pem sign_[1-9].der rm -f "verify.log" else printf "%s\n" "xxd not found in \$PATH" printf "%s\n" "tests skipped, please install vim-common package" fi else printf "%s\n" "faketime not found in \$PATH" printf "%s\n" "tests skipped, please install faketime package" fi exit $result osslsigncode-2.2/tests/testsign.sh000077500000000000000000000050651410626701100174160ustar00rootroot00000000000000#!/bin/sh if [ -z "$(command -v keytool)" ]; then printf "%s\n" "keytool was not found in the \$PATH" printf "%s\n" "Please install the default-jre-headless package" exit 1 fi rm -f putty*.exe PUTTY_URL="http://the.earth.li/~sgtatham/putty/0.64/x86/putty.exe" [ -f putty.exe ] || wget -q -O putty.exe $PUTTY_URL [ -f putty.exe ] || curl -o putty.exe $PUTTY_URL if [ ! -f putty.exe ]; then echo "FAIL: Couldn't download putty.exe" exit 1 fi rm -f cert.pem cert.spc key.der key.p12 key.pem key.pvk keyp.pem keytool -genkey \ -alias selfsigned \ -keysize 2048 \ -keyalg RSA \ -keypass passme \ -storepass passme \ -keystore key.ks << EOF John Doe ACME In ACME Springfield LaLaLand SE yes EOF echo "Converting key/cert to PKCS12 container" keytool -importkeystore \ -srckeystore key.ks \ -srcstoretype JKS \ -srckeypass passme \ -srcstorepass passme \ -srcalias selfsigned \ -destkeystore key.p12 \ -deststoretype PKCS12 \ -destkeypass passme \ -deststorepass passme rm -f key.ks echo "Converting key to PEM format" openssl pkcs12 -in key.p12 -passin pass:passme -nocerts -nodes -out key.pem echo "Converting key to PEM format (with password)" openssl rsa -in key.pem -out keyp.pem -passout pass:passme echo "Converting key to DER format" openssl rsa -in key.pem -outform DER -out key.der -passout pass:passme echo "Converting key to PVK format" openssl rsa -in key.pem -outform PVK -pvk-strong -out key.pvk -passout pass:passme echo "Converting cert to PEM format" openssl pkcs12 -in key.p12 -passin pass:passme -nokeys -out cert.pem echo "Converting cert to SPC format" openssl crl2pkcs7 -nocrl -certfile cert.pem -outform DER -out cert.spc make -C .. ../osslsigncode sign -spc cert.spc -key key.pem putty.exe putty1.exe ../osslsigncode sign -certs cert.spc -key keyp.pem -pass passme putty.exe putty2.exe ../osslsigncode sign -certs cert.pem -key keyp.pem -pass passme putty.exe putty3.exe ../osslsigncode sign -certs cert.spc -key key.der putty.exe putty4.exe ../osslsigncode sign -pkcs12 key.p12 -pass passme putty.exe putty5.exe ../osslsigncode sign -certs cert.spc -key key.pvk -pass passme putty.exe putty6.exe rm -f cert.pem cert.spc key.der key.p12 key.pem key.pvk keyp.pem echo check=`sha1sum putty[1-9]*.exe | cut -d' ' -f1 | uniq | wc -l` cmp putty1.exe putty2.exe && \ cmp putty2.exe putty3.exe && \ cmp putty3.exe putty4.exe && \ cmp putty4.exe putty5.exe && \ cmp putty5.exe putty6.exe if [ $? -ne 0 ]; then echo "Failure is not an option." exit 1 else echo "Yes, it works." fi