diff options
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | LICENSE | 674 | ||||
| -rw-r--r-- | Makefile | 20 | ||||
| -rw-r--r-- | README.md | 86 | ||||
| -rw-r--r-- | base64.c | 153 | ||||
| -rw-r--r-- | base64.h | 30 | ||||
| -rw-r--r-- | crypto.c | 160 | ||||
| -rw-r--r-- | crypto.h | 92 | ||||
| -rw-r--r-- | io.c | 1035 | ||||
| -rw-r--r-- | io.h | 79 | ||||
| -rw-r--r-- | msr.c | 245 | ||||
| -rw-r--r-- | utils.c | 65 | ||||
| -rw-r--r-- | utils.h | 39 |
13 files changed, 2682 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5d7c7ef --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +msr +*.pub +*.key @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 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 <http://www.gnu.org/licenses/>. + +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: + + <program> Copyright (C) <year> <name of author> + 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 +<http://www.gnu.org/licenses/>. + + 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 +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..419a264 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +CC=gcc +CFLAGS=-W --pedantic -Wall -std=c99 -D_GNU_SOURCE -O2 +LIBS=-lsodium +COMPILE=$(CC) $(CFLAGS) + +SRC = $(wildcard *.c) +OBJ = $(patsubst %.c, %.o, $(SRC)) +EXE = msr + +$(EXE): $(OBJ) + $(COMPILE) $(LIBS) -o $(EXE) $(OBJ) + +%.o: %.c + $(COMPILE) -c -o $@ $< + +clean: + $(RM) $(EXE) $(OBJ) nul + +check-syntax: + $(COMPILE) -o nul -S ${CHK_SOURCES} diff --git a/README.md b/README.md new file mode 100644 index 0000000..e30ec17 --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# msr +*Small public key verification tool* + +Inspired by OpenBSD's [signify](http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/signify/signify.c) and [minisign](https://jedisct1.github.io/minisign/) I decided to spend some time learning [libsodium](https://github.com/jedisct1/libsodium) and re-implement minisign for myself. + +Currently, this programme is a superset of minisign (and so partially signify) but in the future signatures and or keys may become incompatible -- it seemed in bad taste to do that immediately. + +## Usage + -G, --generate[=FILE] Generate a new key pair, storing in + `FILE.{pub,key}' (FILE defaults to msr) + -S, --sign-detached=FILE Sign FILE by generating a separate signature + -T, --sign-text=FILE Sign FILE by appending a signature + -V, --verify-detached=FILE Verify the detached signature on FILE + -X, --verify-text=FILE Verify the inline text signature in FILE + -f, --signature-file=FILE Use FILE as the signature file for detached + signing and verification purposes + -p, --pubkey-file=FILE Use the public key in FILE + --pubkey-string=STR Use the public key encoded in STR + -s, --seckey-file=FILE Use the secret key in FILE + --seckey-string=STR Use the secret key encoded in STR + --password-file=FILE Load secret key passphrase from FILE + --comment-pubkey=STR Use STR for the default untrusted comment in the + generated public key file + --comment-seckey=STR Use STR for the default untrusted comment in the + generated secret key file + -t, --comment-trusted=STR Use STR for the trusted comment when making a + detached signature + -u, --comment-untrusted=STR Use STR for the default untrusted comment when + making a detached signature + -q, --quiet Produce no output + -?, --help Give this help list + --usage Give a short usage message + --version Print program version + +## File specifications + +At the behest of libsodium, we use [ed25519](http://ed25519.cr.yp.to/) for all things signing, `scryptsalsa208sha256` for the KDF, and [BLAKE2](https://blake2.net/) for computing hashes otherwise. Thus, for what follows we have + +`sig_alg = Ed` +`chk_alg = B2` +`kdf_alg = Sc` + +Finally, to minimise key collisions and provide a convenient necessary match criterion, each key is assigned a `key_id` which is eight random bytes. + +### Secret Key + untrusted comment: <1024 bytes, arbitrarily changeable> + base64( <sig_alg> || <kdf_alg> || <chk_alg> || <kdf_salt> || <kdf_opsl> || <kdf_meml> || <encrypted key> ) + +where +* `kdf_salt = 32 random bytes` +* `kdf_opsl` and `kdf_meml` are the operations and memory limits for the KDF (defined in libsodium as `crypto_pwhash_scryptsalsa208sha256_{OPS,MEM}LIMIT_SENSITIVE`) +* `checksum = BLAKE2( <sig_alg> || <key id> || <secret key)` +* `encrypted key = <kdf output> ^ (<key id> || <secret key> || <checksum>)` + +### Public Key + untrusted comment: <1024 bytes, arbitrarily changeable> + base64( <signature algorithm> || <key id> || <public key> ) + +### Signature +Detached signatures have the format + + untrusted comment: <1024 bytes, arbitrarily changeable> + base64( <signature algorithm> || <key id> || <signature> ) + trusted comment: <1024 bytes, fixed at signing> + base64( <global signature> ) + +where +* `signature = ed25519( <file data> )` +* `global signature = ed25519( <signature> || <trusted comment> )` + +whereas inline signatures are simply of the form + + <file contents> + --- BEGIN SIGNATURE --- + base64( <signature algorithm> || <key id> || <signature> ) + +### Dependencies +`libsodium`, `argp` and a compiler/stdlib that will understand `-D_GNU_SOURCE` (for non-modifying `basename`) + +I doubt it's *that* portable, but it should probably work on most unix-y systems. + +### License +GPL3+ + +### Motivation +The idea that you can squeeze public key verification into just a few bytes (sub ~100 for everything concerned) and have it still be "128 bit strong" is really amazing. Moreover, I felt that this would be a good learning exercise -- I haven't really done much library interfacing in C and this project entailed two (libsodium and argp), and I haven't ever concluded a mid-sized C project before. I have no doubt the code is crufty and poorly designed, but I had fun and it was an interesting paradigm shift from my usual language of choice. diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..b5fecff --- /dev/null +++ b/base64.c @@ -0,0 +1,153 @@ +/* + This file is part of msr. + + msr 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. + + msr 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 Foobar. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "base64.h" + +static const char b64padchar = (uint8_t)'='; +static const char b64chars[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* +;; Precompute inverse lookup with skippable stuff and padding +(insert + (apply #'concat + (let ((str "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") + (skip " \t\n\r") (skipnum 65) + (pad ?=) (padnum 64) (invalidnum 66)) + (cl-loop for k from 0 to 255 + for j = (let* ((s (char-to-string k)) + (v (s-index-of s str)) + (w (s-index-of s skip)) + (x (char-equal k pad))) + (if v (format "%2dU " v) + (if w "SKIP" + (if x "PADD" "INVD")))) + collect (concat ", " j))))) +*/ +#define INVD (uint8_t)66 +#define SKIP (uint8_t)65 +#define PADD (uint8_t)64 +static const uint8_t b64inverse[256] = { + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, SKIP, SKIP, INVD, INVD, SKIP, INVD, INVD, + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, + SKIP, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, 62 , INVD, INVD, INVD, 63 , + 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 , INVD, INVD, INVD, PADD, INVD, INVD, + INVD, 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , + 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , INVD, INVD, INVD, INVD, INVD, + INVD, 26 , 27 , 28 , 29 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , + 41 , 42 , 43 , 44 , 45 , 46 , 47 , 48 , 49 , 50 , 51 , INVD, INVD, INVD, INVD, INVD, + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, + INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD, INVD +}; + +Error bin_to_base64(uint8_t const * const bin, size_t binlen, + uint8_t *out, size_t maxoutsize, size_t *outlen) { + if ((*outlen = 4*(binlen/3)+4) > maxoutsize) return E_SMALL_BUFF; + size_t k, l; + uint32_t n = 0; + // Look at floor(binlen/3) groups of three bytes + for (k=0, l=0; k<binlen/3; k++) { + n = ((uint32_t)bin[3*k])<<16; + n |= ((uint32_t)bin[3*k+1])<<8; + n |= ((uint32_t)bin[3*k+2]); + + out[l++] = b64chars[(n>>18)&63]; + out[l++] = b64chars[(n>>12)&63]; + out[l++] = b64chars[(n>>6)&63]; + out[l++] = b64chars[n&63]; + } + // What is the actual index in bin? + k *= 3; + // Now deal with remainders + if (k<binlen) { + n = ((uint32_t)bin[k])<<16; + out[l+3] = b64padchar; + if (k+1<binlen) { + n |= ((uint32_t)bin[k+1])<<8; + out[l+2] = b64chars[(n>>6)&63]; + } else out[l+2] = b64padchar; + out[l] = b64chars[(n>>18)&63]; + out[l+1] = b64chars[(n>>12)&63]; + l+=4; + }; + out[l]=0; + return SUCCESS; +} + +// Note: will read first valid b64 encoded string and stop caring +Error base64_to_bin(uint8_t const * const str, size_t strlen, + uint8_t *buf, size_t bufmaxsize, size_t *buflen) { + if (str == NULL) return E_NULL; + *buflen = 0; + size_t k = 0; + uint8_t c = 0; + uint32_t t = 0; + while(k<strlen) { + uint8_t l = b64inverse[str[k++]]; + switch (l) { + // Skip = Skip, a universal truth + case SKIP: break; + // We don't take these transgressions lightly + case INVD: return E_B64_BAD_STR; + // Annoying validity checks here + case PADD: { + if (c<2) return E_B64_BAD_STR; + // If we have only consumed 2/4 chars, then there must be 2 x pad || .* || eof + if (c==2 && ( str[k]!=b64padchar)) + return E_B64_BAD_STR; + k = strlen; + // If we have 3/4 chars, pad || .* || eof + break; + } + // A valid b64 char (not incl. padding) + default: { + // Store these six bits + t = (t<<6) | l; + // If we've done this four times, then output three bytes + if (++c > 3) { + if ((*buflen)>=bufmaxsize) return E_SMALL_BUFF; + buf[(*buflen)++] = (uint8_t)((t>>16)&255); + if ((*buflen)>=bufmaxsize) return E_SMALL_BUFF; + buf[(*buflen)++] = (uint8_t)((t>>8)&255); + if ((*buflen)>=bufmaxsize) return E_SMALL_BUFF; + buf[(*buflen)++] = (uint8_t)(t&255); + c = t = 0; + } + } + } + } + // This should not happen + if (c==1) return E_B64_BAD_STR; + // Finish up, c=2 => 1 byte, c=3 => 2 bytes + else if (c==2) { + if ((*buflen)>=bufmaxsize) return E_SMALL_BUFF; + buf[(*buflen)++] = (uint8_t)((t>>4)&255); + } + else if (c==3) { + if ((*buflen)>=bufmaxsize) return E_SMALL_BUFF; + buf[(*buflen)++] = (uint8_t)((t>>10)&255); + if ((*buflen)>=bufmaxsize) return E_SMALL_BUFF; + buf[(*buflen)++] = (uint8_t)((t>>2)&255); + } + // done + return SUCCESS; +} diff --git a/base64.h b/base64.h new file mode 100644 index 0000000..4c36a97 --- /dev/null +++ b/base64.h @@ -0,0 +1,30 @@ +/* + This file is part of msr. + + msr 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. + + msr 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 Foobar. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include "utils.h" + +Error bin_to_base64(uint8_t const * const bin, size_t binlen, + uint8_t *out, size_t maxoutlen, size_t *outlen); + +Error base64_to_bin(uint8_t const * const str, size_t strlen, + uint8_t *buf, size_t bufmaxlen, size_t *buflen); diff --git a/crypto.c b/crypto.c new file mode 100644 index 0000000..799cba3 --- /dev/null +++ b/crypto.c @@ -0,0 +1,160 @@ +/* + This file is part of msr. + + msr 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. + + msr 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 Foobar. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "crypto.h" + +void compute_checksum_seckey(SecretKey sk, uint8_t *checksum) { + // checksum = BLAKE2( ALG || KEY_ID || SECRET_KEY ) + crypto_generichash_state st; + crypto_generichash_init(&st, NULL, 0, CHK_WIDTH); + crypto_generichash_update(&st, sk->sig_alg, SIG_ALG_WIDTH); + crypto_generichash_update(&st, sk->key_id, KEY_ID_WIDTH); + crypto_generichash_update(&st, sk->secret_key, SEC_KEY_WIDTH); + crypto_generichash_final(&st, checksum, CHK_WIDTH); +} + +Error generate_key_pair(SecretKey *sk, PublicKey *pk) { + *sk = catch_smalloc(sizeof(struct secret_key_s), + "Unable to securely allocate secrety key memory."); + *pk = catch_smalloc(sizeof(struct public_key_s), + "Unable to securely allocate public key memory."); + // Initialise structures randomly, for hysterical raisons + randombytes_buf((*sk),sizeof(struct secret_key_s)); + randombytes_buf((*pk),sizeof(struct public_key_s)); + // -- Public key init --- + memcpy((*pk)->sig_alg, SIG_ALG, SIG_ALG_WIDTH); + randombytes_buf((*pk)->key_id, KEY_ID_WIDTH); + // --- Secret key init --- + memcpy((*sk)->sig_alg, SIG_ALG, SIG_ALG_WIDTH); + memcpy((*sk)->kdf_alg, KDF_ALG, KDF_ALG_WIDTH); + memcpy((*sk)->chk_alg, CHK_ALG, CHK_ALG_WIDTH); + (*sk)->kdf_memlimit = crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE; + (*sk)->kdf_opslimit = crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE; + memcpy((*sk)->key_id, (*pk)->key_id, KEY_ID_WIDTH); + // Generate keypair + crypto_sign_keypair((*pk)->public_key, (*sk)->secret_key); + // Checksum on secret key to wrap-up generation + compute_checksum_seckey((*sk),(*sk)->checksum); + return SUCCESS; +} + +Error is_valid_sigmsg(SignedMsg sm) { + if (sm == NULL) return E_NULL; + if (sodium_memcmp(sm->sig_alg, SIG_ALG, SIG_ALG_WIDTH) != 0) + return E_ALG_CHOICE; + return SUCCESS; +} + +Error is_valid_pubkey(PublicKey pk) { + if (pk == NULL) return E_NULL; + // Same as before + if (sodium_memcmp(pk->sig_alg, SIG_ALG, SIG_ALG_WIDTH) != 0) + return E_ALG_CHOICE; + return SUCCESS; +} + +Error is_correct_options(SecretKey sk) { + if (sk == NULL) return E_NULL; + // For a secret key to be valid, it *must* use the same algorithms + if ( (sodium_memcmp(sk->sig_alg, SIG_ALG, SIG_ALG_WIDTH) != 0) + || (sodium_memcmp(sk->kdf_alg, KDF_ALG, KDF_ALG_WIDTH) != 0) + || (sodium_memcmp(sk->chk_alg, CHK_ALG, CHK_ALG_WIDTH) != 0) ) + return E_ALG_CHOICE; + // If so, sucess + return SUCCESS; +} + +Error is_correct_checksum(SecretKey sk) { + Error e; + uint8_t checksum[CHK_WIDTH]; + compute_checksum_seckey(sk,checksum); + if (sodium_memcmp(sk->checksum, checksum, CHK_WIDTH) == 0) + e = SUCCESS; + else e = E_CHECKSUM; + sodium_memzero(checksum,CHK_WIDTH); + return e; +} + +void xor_keynum_sk(SecretKey sk, const uint8_t outp[ENCRYPTED_KEY_WIDTH]) { + // Xor correctly aligned to blocks + size_t k; + for (k=0;k<KEY_ID_WIDTH;k++) sk->key_id[k] ^= outp[k]; + for (k=0;k<SEC_KEY_WIDTH;k++) sk->secret_key[k] ^= outp[k+KEY_ID_WIDTH]; + for (k=0;k<CHK_WIDTH;k++) sk->checksum[k] ^= outp[k+KEY_ID_WIDTH+SEC_KEY_WIDTH]; +} + +Error alter_seckey(char encrypt, SecretKey sk, const uint8_t * const passwd, + unsigned long long pwlen) { + Error e; + // Cannot decrypt invalid secret keys, or reencrypt encrypted and so on + if ( (e = is_correct_options(sk)) != SUCCESS ) return e; + e = is_correct_checksum(sk); + if ( (encrypt != 0) && (e != SUCCESS) ) return E_ALREADY_ENCRYPTED; + if ( (encrypt == 0) && (e == SUCCESS) ) return E_ALREADY_DECRYPTED; + // Actual processing + uint8_t *outp = catch_smalloc(ENCRYPTED_KEY_WIDTH, + "Unable to allocate secure internal storage for KDF."); + sodium_memzero(outp,ENCRYPTED_KEY_WIDTH); + // Run the KDF + if (crypto_pwhash_scryptsalsa208sha256(outp, ENCRYPTED_KEY_WIDTH, + (char*)passwd, pwlen, + sk->kdf_salt,sk->kdf_opslimit, + sk->kdf_memlimit) != 0 ) + return E_KDF_FAIL; + // XOR into blocks appropriately + xor_keynum_sk(sk,outp); + if (!encrypt) { + // If decrypting, check for correct password + if ( is_correct_checksum(sk) == SUCCESS ) return SUCCESS; + else { + // Password was bad, undo what we did + xor_keynum_sk(sk,outp); + e = E_BAD_PASS; + } + // Otherwise done + } else e = SUCCESS; + // Wipe kdf output + sodium_free(outp); + return e; +} + +Error verify_message(PublicKey pk, SignedMsg sm) { + Error e; + // Check validity of public key and signed message algo choice + if ( (e = is_valid_pubkey(pk)) != SUCCESS ) return e; + if ( (e = is_valid_sigmsg(sm)) != SUCCESS ) return e; + // Ensure that the key id is correct + if ( sodium_memcmp(pk->key_id,sm->key_id,KEY_ID_WIDTH) != 0) + return E_WRONG_PUBKEY; + // Verify if + if ( crypto_sign_verify_detached(sm->sig, + sm->msg, sm->msglen, + pk->public_key) != 0 ) + return E_VERIFY; + return SUCCESS; +} + +Error sign_message(SecretKey sk, SignedMsg sm) { + Error e; + // Ensure that SK uses correct algo choices and has ok checksum + if ( (e = is_correct_options(sk)) != SUCCESS ) return e; + if ( is_correct_checksum(sk) != SUCCESS ) return e; + // Sign it + if ( crypto_sign_detached(sm->sig, NULL, sm->msg, sm->msglen, sk->secret_key) != 0 ) + return E_SIGN; + return SUCCESS; +} diff --git a/crypto.h b/crypto.h new file mode 100644 index 0000000..67055f7 --- /dev/null +++ b/crypto.h @@ -0,0 +1,92 @@ +/* + This file is part of msr. + + msr 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. + + msr 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 Foobar. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once +#pragma pack(1) // VERY IMPORTANT + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <sodium.h> + +#include "utils.h" + +// --- Algorithm choices --- +#define SIG_ALG "Ed" +#define KDF_ALG "Sc" +#define CHK_ALG "B2" + +// --- Widths for various fields in the structs --- +#define SIG_ALG_WIDTH 2 +#define KDF_ALG_WIDTH 2 +#define CHK_ALG_WIDTH 2 +#define KEY_ID_WIDTH 8 +#define SIG_WIDTH (size_t)crypto_sign_BYTES +#define CHK_WIDTH (size_t)crypto_generichash_BYTES +#define SEC_KEY_WIDTH (size_t)crypto_sign_SECRETKEYBYTES +#define PUB_KEY_WIDTH (size_t)crypto_sign_PUBLICKEYBYTES +#define KDF_SALT_WIDTH (size_t)crypto_pwhash_scryptsalsa208sha256_SALTBYTES +#define ENCRYPTED_KEY_WIDTH (KEY_ID_WIDTH + SEC_KEY_WIDTH + CHK_WIDTH) + +#define PUB_KEY_STORE_WIDTH sizeof(struct public_key_s) +#define SEC_KEY_STORE_WIDTH sizeof(struct secret_key_s) +#define SIGNED_MSG_WIDTH (SIG_ALG_WIDTH+KEY_ID_WIDTH+SIG_WIDTH) + +// --- Structs for secret key, public key and messages --- +typedef struct secret_key_s { + uint8_t sig_alg[SIG_ALG_WIDTH]; + uint8_t kdf_alg[KDF_ALG_WIDTH]; + uint8_t chk_alg[CHK_ALG_WIDTH]; + uint8_t kdf_salt[KDF_SALT_WIDTH]; + uint64_t kdf_opslimit; + uint64_t kdf_memlimit; + // Below this line is essentially "keynum_sk" + // We encrypt for security + uint8_t key_id[KEY_ID_WIDTH]; + uint8_t secret_key[SEC_KEY_WIDTH]; + uint8_t checksum[CHK_WIDTH]; +} * SecretKey; + +typedef struct public_key_s { + uint8_t sig_alg[SIG_ALG_WIDTH]; + uint8_t key_id[KEY_ID_WIDTH]; + uint8_t public_key[PUB_KEY_WIDTH]; +} * PublicKey; + +typedef struct signed_msg_s { + uint8_t sig_alg[SIG_ALG_WIDTH]; + uint8_t key_id[KEY_ID_WIDTH]; + uint8_t sig[SIG_WIDTH]; + size_t msglen; + uint8_t *msg; +} * SignedMsg; + +// --- Functions --- +Error generate_key_pair(SecretKey *sk, PublicKey *pk); + +Error is_valid_sigmsg(SignedMsg sm); +Error is_valid_pubkey(PublicKey pk); +Error is_correct_options(SecretKey sk); +Error is_correct_checksum(SecretKey sk); + +Error alter_seckey(char encrypt, SecretKey sk, + const uint8_t * const passwd, + unsigned long long pwlen); + +Error verify_message(PublicKey pk, SignedMsg sm); + +Error sign_message(SecretKey sk, SignedMsg sm); @@ -0,0 +1,1035 @@ +/* + This file is part of msr. + + msr 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. + + msr 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 Foobar. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "io.h" + +Error read_password_stdin(uint8_t * passwd, char repeat, size_t *len) { + Error e = SUCCESS; + struct termios old,noecho; + + if (len) *len = 0; + + tcgetattr(STDIN_FILENO,&old); + noecho = old; + noecho.c_lflag &= ~ECHO; + tcsetattr(STDIN_FILENO,TCSANOW,&noecho); + + printf("Password: "); + + // Read in the passwd, if its blank then warn + if ( fgets((char*)passwd,MAX_PASS_LEN,stdin) == NULL ) { + sodium_free(passwd); + return E_NULL; + } + // If we were interactive then we don't want the terminal return + size_t k = strnlen((char*)passwd,MAX_PASS_LEN); + if (k==0) puts("(blank)"); + else if (passwd[k-1]=='\n') passwd[--k]=0; + // Are we asking the user to repeat the password? + if (repeat) { + // Reset terminal settings in the event that we die when allocating + tcsetattr(STDIN_FILENO,TCSANOW,&old); + uint8_t *passwd2 = catch_smalloc(MAX_PASS_LEN, + "Could not allocate secure memory for password storage"); + // Prompt and set no echo + printf("Repeat password: "); + tcsetattr(STDIN_FILENO,TCSANOW,&noecho); + // Read in, if this one is blank they must both be for a match + if ( fgets((char*)passwd2,MAX_PASS_LEN,stdin) == NULL) { + putchar('\n'); + sodium_free(passwd2); + sodium_free(passwd); + return E_NULL; + } + // Set back terminal + tcsetattr(STDIN_FILENO,TCSANOW,&old); + size_t k2 = strnlen((char*)passwd2,MAX_PASS_LEN); + // Trim this one too + if (k2 == 0) puts("(blank)"); + else if (passwd2[k2-1]=='\n') passwd2[--k2]=0; + // They must be of the same length (note: both taken with possible terminal return) + if (k2 != k) { + sodium_memzero(passwd,MAX_PASS_LEN); + sodium_free(passwd2); + return E_PASS_MISMATCH; + }; + // Do they match? + if (sodium_memcmp(passwd,passwd2,k) != 0) { + sodium_memzero(passwd,MAX_PASS_LEN); + sodium_free(passwd2); + return E_PASS_MISMATCH; + } + sodium_free(passwd2); + } + if (len) *len = k; + return e; +} + +Error memory_map_file(const char * const fn, + uint8_t **dataptr, + size_t *size) { + if (fn == NULL || *fn == 0 || dataptr == NULL || size == NULL ) + return E_NULL; + *size = 0; + *dataptr = 0; + // --- Map --- + int fd = open(fn, O_RDONLY); + if (fd <= 0) return E_BAD_FILE; + struct stat stt; + if ( fstat(fd, &stt) != 0 || stt.st_size == 0) { + close(fd); + return E_BAD_FILE; + } + *dataptr = mmap(0, stt.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + // Check that all is kosher + if (*dataptr == MAP_FAILED) { + close(fd); + return E_BAD_FILE; + } + *size = stt.st_size; + return SUCCESS; +} + +Error read_password_file(const char * const fn, uint8_t **passptr, size_t *len) { + if (fn == NULL) return E_NULL; + size_t flen; uint8_t *data; + *passptr = NULL; + if (len) *len = 0; + Error e = memory_map_file(fn,&data,&flen); + if (e!=SUCCESS) { + if (verbose) printf("Error reading password from file: %s\n",error_to_str(e)); + return e; + } + size_t to_read = (flen>=MAX_PASS_LEN)?MAX_PASS_LEN:flen; + *passptr = catch_smalloc(to_read,"Unable to allocate secure password storage."); + memcpy(*passptr,data,to_read); + munmap(data,flen); + if (len) *len = to_read; + return SUCCESS; +} + +Error prompt_decryption_password(uint8_t ** passptr, size_t *len) { + uint8_t *passwd = catch_smalloc(MAX_PASS_LEN, + "Unable to allocate secure memory for password."); + *len = 0; + Error e = read_password_stdin(passwd,1,len); + if (e != SUCCESS) { + sodium_free(passwd); + passwd = NULL; + if (verbose) printf("Error reading decryption password: %s\n",error_to_str(e)); + } + *passptr = passwd; + return e; +} + +Error prompt_encryption_password(uint8_t ** passptr, size_t *len) { + uint8_t *passwd = catch_smalloc(MAX_PASS_LEN, + "Unable to allocate secure memory for password."); + *len = 0; + Error e = read_password_stdin(passwd,1,len); + if ( e != SUCCESS ) { + sodium_free(passwd); + passwd = NULL; + if (verbose) printf("Error reading encryption password: %s\n",error_to_str(e)); + } + *passptr = passwd; + return e; +} + +Error read_pubkey_string(const char * const str, PublicKey *pk) { + // This would be bad + if (str == NULL) return E_NULL; + // Init stuff + *pk = NULL; + size_t size = 0; + // We use smalloc even though we don't *have to* + PublicKey p = catch_smalloc(PUB_KEY_STORE_WIDTH, + "Could not allocate secure public key memory."); + // Try to decode the alleged public key + Error e = base64_to_bin((uint8_t*)str,strnlen(str,4*(PUB_KEY_STORE_WIDTH/3+1)), + (uint8_t*)(void*)p,PUB_KEY_STORE_WIDTH,&size); + // Fatal = die + if ( e != SUCCESS ) { + sodium_free(p); + if (verbose) printf("Error decoding public key: %s\n",error_to_str(e)); + return e; + } + // Maybe it was valid base 64, but was it the correct size? + if (size!=PUB_KEY_STORE_WIDTH) { + sodium_free(p); + if (verbose) printf("Error decoding public key: %s\n",error_to_str(E_BAD_PUBKEY_DAT)); + return E_BAD_PUBKEY_DAT; + } + // Check if we have a valid public key + e = is_valid_pubkey(p); + if ( e != SUCCESS ) { + sodium_free(p); + if (verbose) printf("Error decoding public key: %s\n",error_to_str(e)); + return e; + }; + // Cool + *pk = p; + return e; +} + +Error read_seckey_string(const char * const str, SecretKey *sk) { + // Same as public one, really + if (str == NULL) return E_NULL; + // Init stuff + *sk = NULL; + size_t size = 0; + SecretKey s = catch_smalloc(SEC_KEY_STORE_WIDTH, + "Could not allocate secure secure secret key memory."); + // Attempt to decode + Error e = base64_to_bin((uint8_t*)str,strnlen(str,4*(SEC_KEY_STORE_WIDTH/3+1)), + ((uint8_t*)(void*)s),SEC_KEY_STORE_WIDTH,&size); + // Fatal = die + if ( e != SUCCESS ) { + sodium_free(s); + if (verbose) printf("Error decoding secret key: %s\n",error_to_str(e)); + return e; + } + // Maybe it was valid base 64, but was it the correct size? + if (size!=SEC_KEY_STORE_WIDTH) { + sodium_free(s); + if (verbose) printf("Error decoding secret key: %s\n",error_to_str(E_BAD_SECKEY_DAT)); + return E_BAD_SECKEY_DAT; + } + // Check if we have a 'valid' secret key + e = is_correct_options(s); + if ( e != SUCCESS ) { + sodium_free(s); + if (verbose) printf("Error decoding secret key: %s\n",error_to_str(e)); + return e; + } + // Cool + *sk = s; + return e; +} + +Error read_file_internal(const char * const fn, + size_t maxclen, size_t maxdlen, + char **comment, size_t *commentlen, + char **data, size_t *datalen) { + // Initial wipes, to be sure + *comment = NULL; + *data = NULL; + *commentlen = 0; + *datalen = 0; + // Open the file, do some checks + FILE *fl = fopen(fn,"r"); + if (fl == NULL) return E_BAD_FILE; + char *buf = catch_smalloc(maxclen, + "Unable to allocate secure internal storage to read from file."); + // Read the comment line + if (fgets(buf,maxclen,fl) == NULL) { + sodium_free(buf); + fclose(fl); + return E_BAD_FILE; + } + *comment = buf; + *commentlen = strnlen(*comment,maxclen); + // Try and read the second line + buf = catch_smalloc(maxdlen, + "Unable to allocate secure internal storage to read from file."); + if (fgets(buf,maxdlen,fl) == NULL) { + sodium_free(buf); + sodium_free(*comment); + *comment = NULL; + *commentlen = 0; + fclose(fl); + return E_BAD_FILE; + } + fclose(fl); + + *data = buf; + *datalen = strnlen(*data,maxdlen); + + // Trime terminal newlines if there are any + if (*datalen!=0 && (*data)[*datalen-1]=='\n') { + (*data)[*datalen-1]=0; (*datalen)--; + } + if (*commentlen!=0 && (*comment)[*commentlen-1]=='\n') { + (*comment)[*commentlen-1]=0; (*commentlen)--; + } + return SUCCESS; +} + +Error display_key_id(const uint8_t * const id) { + if (id==NULL) return E_NULL; + size_t k; + for (k=1;k<=KEY_ID_WIDTH;k++) { + printf("%02X",id[KEY_ID_WIDTH-k]); + if (k<KEY_ID_WIDTH) putchar(':'); + } + return SUCCESS; +} + +Error read_pubkey_file(char const * const fn, PublicKey *pk) { + char *comment, *pkstr; + size_t csize, pksize; + Error e = read_file_internal(fn,sizeof(DEFAULT_COMMENT_PREFIX)+MAX_COMMENT_LEN-1, + 4*(PUB_KEY_STORE_WIDTH/3+1), + &comment, &csize, &pkstr, &pksize); + if ( e != SUCCESS ) { + sodium_free(comment); + sodium_free(pkstr); + if (verbose) printf("Error reading public key file: %s\n",error_to_str(e)); + return e; + } + e = read_pubkey_string(pkstr,pk); + if ( e != SUCCESS ) { + sodium_free(comment); + sodium_free(pkstr); + if (verbose) printf("Error reading public key file: %s\n",error_to_str(e)); + return e; + } + if (verbose) { + printf("Public key "); + display_key_id((*pk)->key_id); + printf(" loaded successfully.\n'%s'\n\n",comment); + } + sodium_free(comment); + return SUCCESS; +} + +Error read_seckey_file(char const * const fn, SecretKey *sk) { + char *comment, *skstr; + size_t csize, pksize; + Error e = read_file_internal(fn,sizeof(DEFAULT_COMMENT_PREFIX)+MAX_COMMENT_LEN-1, + 4*(SEC_KEY_STORE_WIDTH/3+1), + &comment, &csize, &skstr, &pksize); + if ( e != SUCCESS ) { + sodium_free(comment); + sodium_free(skstr); + if (verbose) printf("Error reading secret key file: %s\n",error_to_str(e)); + return e; + } + e = read_seckey_string(skstr,sk); + if ( e != SUCCESS ) { + sodium_free(comment); + sodium_free(skstr); + if (verbose) printf("Error reading secret key file: %s\n",error_to_str(e)); + return e; + } + // Check if it's not encrypted, then we can display the id + if (verbose) { + if (is_correct_checksum(*sk) == SUCCESS) { + printf("Secret key "); + display_key_id((*sk)->key_id); + puts(" loaded successfully."); + } else puts("Encrypted secret key loaded successfully."); + printf("'%s'\n\n",comment); + } + sodium_free(comment); + return SUCCESS; +} + +Error pubkey_to_string(PublicKey pk, char **pksptr) { + if (pksptr == NULL) return E_NULL; + Error e = is_valid_pubkey(pk); + if ( e != SUCCESS ) return e; + *pksptr = catch_smalloc(4*(PUB_KEY_STORE_WIDTH/3)+4+1, + "Unable to allocate secure internal storage for public key string."); + size_t l = 0; + e = bin_to_base64((uint8_t*)(void*)pk,PUB_KEY_STORE_WIDTH, + (uint8_t*)*pksptr,4*(PUB_KEY_STORE_WIDTH/3)+4,&l); + if ( e != SUCCESS ) { + sodium_free(*pksptr); + *pksptr = NULL; + } + return e; +} + +// TODO: Warn when writing unencrypted secret key? +Error seckey_to_string(SecretKey sk, char **sksptr) { + if (sksptr == NULL) return E_NULL; + Error e = is_correct_options(sk); + if ( e != SUCCESS ) return e; + *sksptr = catch_smalloc(4*(SEC_KEY_STORE_WIDTH/3)+4+1, + "Unable to allocate secure internal storage for secret key string."); + size_t l = 0; + e = bin_to_base64((uint8_t*)(void*)sk,SEC_KEY_STORE_WIDTH, + (uint8_t*)*sksptr,4*(SEC_KEY_STORE_WIDTH/3)+4,&l); + if ( e != SUCCESS ) { + sodium_free(*sksptr); + *sksptr = NULL; + } + return e; +} + +Error unlock_seckey(const char * const passwdfn, SecretKey sk) { + uint8_t *passwd; size_t passlen; + Error e; + if (passwdfn == NULL) e = prompt_decryption_password(&passwd,&passlen); + else e = read_password_file(passwdfn, &passwd, &passlen); + if ( e != SUCCESS ) { + if (verbose) printf("Error reading passphrase: %s\n",error_to_str(e)); + return e; + } + if (verbose) puts("Attempting to decrypt key..."); + e = alter_seckey(0,sk,passwd,passlen); + if ( e != SUCCESS ) { + if (verbose) printf("Error decrypting secret key: %s\n",error_to_str(e)); + return e; + } + if (verbose) { + printf("Successfully decrypted secret key "); + display_key_id(sk->key_id); + putchar('\n'); + } + return e; +} + +Error write_to_file_internal(const char * const fn, + const char * const keystr, + const char * const comment) { + if (fn == NULL || keystr == NULL || comment == NULL) + return E_NULL; + FILE *f = fopen(fn,"w"); + if ( f == NULL ) return E_BAD_FILE; + // Error on writing no bytes? Why not. + Error e = SUCCESS; + if ( fprintf(f,"%s\n%s\n",comment,keystr) <= 0 ) + e = E_BAD_FILE; + fclose(f); + return e; +} + +Error display_key_id_s(const uint8_t * const id, char * s) { + if (id==NULL) return E_NULL; + size_t k; + for (k=1;k<=KEY_ID_WIDTH;k++) { + sprintf(s,"%02X",id[KEY_ID_WIDTH-k]); + s+=2; + if (k<KEY_ID_WIDTH) sprintf(s++,":"); + } + return SUCCESS; +} + +Error write_pubkey_to_file(const char * const pkfn, PublicKey pk, + const char * const comment) { + // Pretty standard stuff, check for errors + if (pkfn == NULL || pk == NULL) return E_NULL; + char *pkstr = NULL; + // Generate the b64 string + Error e = pubkey_to_string(pk,&pkstr); + if ( e != SUCCESS ) return e; + // If the comment is blank, make the default one + char *comm = catch_malloc(MAX_COMMENT_LEN+sizeof(DEFAULT_COMMENT_PREFIX), + "Unable to allocate secure internal memory."); + if (comment == NULL) { + /* + We want 'untrusted comment: <default_pubkey_prefix> <key id>' where key + id is hex for eight bytes with colons between. Note -1 for 0 + delim double counting. + */ + memcpy(comm,DEFAULT_COMMENT_PREFIX,sizeof(DEFAULT_COMMENT_PREFIX)); + int l = sizeof(DEFAULT_COMMENT_PREFIX)-1; + memcpy(comm+l, DEFAULT_PUBKEY_PREFIX, sizeof(DEFAULT_PUBKEY_PREFIX)); + l += sizeof(DEFAULT_PUBKEY_PREFIX)-1; + // No errors are checked here as it's impossible for pk to be + // invalid and have passed pubkey_to_string + display_key_id_s(pk->key_id,comm+l); + // Otherwise we use the given comment + } else snprintf(comm,MAX_COMMENT_LEN+sizeof(DEFAULT_COMMENT_PREFIX), + "%s%s",DEFAULT_COMMENT_PREFIX,comment); + e = write_to_file_internal(pkfn,pkstr,comm); + free(comm); + sodium_free(pkstr); + return e; +} + +Error write_seckey_to_file(const char * const skfn, SecretKey sk, + const char * const comment) { + // This is copy pasta of the public one, mutatis mutandis + if (skfn == NULL || sk == NULL) return E_NULL; + char *skstr = NULL; + Error e = seckey_to_string(sk,&skstr); + if ( e != SUCCESS ) return e; + char *comm = catch_malloc(MAX_COMMENT_LEN+sizeof(DEFAULT_COMMENT_PREFIX), + "Unable to allocate internal storage for comment."); + snprintf(comm,MAX_COMMENT_LEN+sizeof(DEFAULT_COMMENT_PREFIX), + "%s%s",DEFAULT_COMMENT_PREFIX, + (comment==NULL)?DEFAULT_SECKEY_PREFIX:comment); + e = write_to_file_internal(skfn,skstr,comm); + free(comm); + sodium_free(skstr); + return e; +} + +Error generate_to_file(const char * fn, + const char * const passwdfn, + const char * const pkcomment, + const char * const skcomment) { + if (pkcomment != NULL && + strnlen(pkcomment,MAX_COMMENT_LEN) > MAX_COMMENT_LEN) { + if (verbose) puts("Error generating new key pair: Desired public key comment is too long."); + return E_INVALID_LEN; + } + if (skcomment != NULL && + strnlen(skcomment,MAX_COMMENT_LEN) > MAX_COMMENT_LEN) { + if (verbose) puts("Error generating new key pair: Desired secret key comment is too long."); + return E_INVALID_LEN; + } + PublicKey pk = NULL; + SecretKey sk = NULL; + uint8_t *passwd = NULL; + size_t passlen = 0; + Error e; + // --- Generate key pair --- + if (verbose) printf("Generating a new key pair... "); + e = generate_key_pair(&sk,&pk); + if ( e != SUCCESS ) { + if (verbose) printf("error!\nError generating key pair: %s\n",error_to_str(e)); + return e; + } + if (verbose) puts("done.\n"); + // --- Retrieve password --- + if (passwdfn == NULL) { + if (verbose) puts("Please enter a password to encrypt the secret key."); + e = prompt_encryption_password(&passwd,&passlen); + } else e = read_password_file(passwdfn,&passwd,&passlen); + if ( e != SUCCESS ) { + sodium_free(sk); sodium_free(pk); + return e; + } + // --- Encrypt the key --- + if (verbose) printf("\nDeriving a key from the password for encryption... "); + fflush(stdout); + e = alter_seckey(1,sk,passwd,passlen); + if ( e != SUCCESS ) { + sodium_free(sk); sodium_free(pk); + if (verbose) printf("error!\nError encrypting secret key: %s\n",error_to_str(e)); + return e; + } + // --- Write files --- + char *skfn, *pkfn; + char default_fn[] = "msr"; + if (fn==NULL) fn = default_fn; + size_t fnlen = strnlen(fn,MAX_FILENAME_LEN); + skfn = catch_malloc(fnlen+5, "Unable to allocate filename storage."); + pkfn = catch_malloc(fnlen+5, "Unable to allocate filename storage."); + snprintf(skfn,fnlen+5,"%s.key",fn); + snprintf(pkfn,fnlen+5,"%s.pub",fn); + + if (verbose) printf("done.\n\nCreating key files:\n\tWriting secret key into '%s'... ",skfn); + fflush(stdout); + e = write_seckey_to_file(skfn,sk,skcomment); + if ( e != SUCCESS ) { + sodium_free(sk); sodium_free(pk); + if (verbose) printf("error!\nError writing secret key: %s\n",error_to_str(e)); + return e; + } + if (verbose) printf("done.\n\tWriting public key into '%s'... ",pkfn); + e = write_pubkey_to_file(pkfn,pk,pkcomment); + if ( e != SUCCESS ) { + sodium_free(sk); sodium_free(pk); + if (verbose) printf("error!\nError writing public key: %s\n",error_to_str(e)); + return e; + } + if (verbose) { + printf("done.\n\nThe new key pair has ID "); + display_key_id(pk->key_id); + putchar('\n'); + } + return SUCCESS; +} + +Error load_and_sign_file_contents(const char * const fn, SecretKey sk, SignedMsg *smptr) { + // Usual checks + if (fn == NULL || sk == NULL || smptr == NULL) return E_NULL; + /* + Note: we effectively do the following checks twice because of the + way sign_message was written. I'd rather we hash twice than crypto + reveal an unsafe API, though. Design input welcome. + */ + Error e; + if ((e=is_correct_options(sk))!=SUCCESS) return e; + if (is_correct_checksum(sk)!=SUCCESS) return E_ENCRYPTED; + // Map the file + size_t size; uint8_t *data; + e = memory_map_file(fn, &data, &size); + if ( e != SUCCESS ) return e; + // Initialise the signed message structure + *smptr = catch_smalloc(sizeof(struct signed_msg_s), + "Unable to allocate secure internal storage for message signing."); + memcpy((*smptr)->sig_alg,sk->sig_alg,SIG_ALG_WIDTH); + memcpy((*smptr)->key_id,sk->key_id,KEY_ID_WIDTH); + (*smptr)->msglen = size; + (*smptr)->msg = data; + // Sign the message + if ( (e = sign_message(sk,*smptr)) != SUCCESS) { + sodium_free(*smptr); *smptr = NULL; + } + munmap(data, size); + return e; +} + +Error trusted_comment_sign(const uint8_t * const sig, const char * const tc, + SecretKey sk, char **sptr) { + // Let's skip some of the normal validation here beacuaz I am laz + if (sig == NULL || tc == NULL || sk == NULL || sptr == NULL) + return E_NULL; + *sptr = NULL; + // This stuff will probably happen multiple times for a single key, + // but hey, data integrity checks! + Error e = is_correct_options(sk); + if ( e != SUCCESS ) return e; + if ( is_correct_checksum(sk) != SUCCESS ) return E_ENCRYPTED; + // Do lots of accounting, wow this is taxing + const size_t tclen = strnlen(tc,MAX_COMMENT_LEN); + const size_t len = SIG_WIDTH+tclen; + uint8_t* cat = catch_malloc(len+1, + "Could not allocate internal memory for second signature."); + // Blah blah, cat = <sig> || <trusted comment> + memcpy(cat,sig,SIG_WIDTH); + memcpy(cat+SIG_WIDTH,tc,tclen); + // Now sign it (maybe I should have designed the crypto interface better) + // (in my defense, I didn't think I'd be doing trusted comments) + uint8_t ssig[SIG_WIDTH]; + if ( crypto_sign_detached(ssig, NULL, cat, len, sk->secret_key) != 0 ) { + free(cat); + return E_SIGN; + } else free(cat); + // Now encode the signature + char *b64 = catch_malloc(4*(SIG_WIDTH/3)+4+1 , + "Could not allocate internal memory for second signature."); + size_t out; + e = bin_to_base64(ssig, SIG_WIDTH, + (uint8_t*)b64, 4*(SIG_WIDTH/3)+4, + &out); + if ( e == SUCCESS ) *sptr = b64; + else free(b64); + return e; +} + +Error signed_message_to_signature(SignedMsg sm, char **sigptr) { + *sigptr = NULL; + char *b64sig = catch_malloc(4*(SIGNED_MSG_WIDTH/3)+4+1 , + "Could not allocate internal memory for signature."); + size_t out; + Error e = bin_to_base64((uint8_t*)(void*)sm, SIGNED_MSG_WIDTH, + (uint8_t*)b64sig, 4*(SIGNED_MSG_WIDTH/3)+4, + &out); + if ( e == SUCCESS ) *sigptr = b64sig; + else free(b64sig); + return e; +} + +#define SA_ERR "Error attaching signature: %s\n" +Error sign_attached(const char * const fn, + const char * const passwdfn, SecretKey sk) { + // Standard checks + if (fn == NULL) { + if (verbose) printf(SA_ERR,"No file specified."); + return E_NULL; + } + if (strnlen(fn,MAX_FILENAME_LEN)==MAX_FILENAME_LEN) { + if (verbose) printf(SA_ERR,"Filename is too long."); + return E_BAD_FILE; + } + Error e; + if ( (e = is_correct_options(sk)) != SUCCESS ) { + if (verbose) printf(SA_ERR,error_to_str(e)); + return e; + } + // Decrypt key if necessary + e = is_correct_checksum(sk); + if ( e == E_CHECKSUM ) { + if ( (e = unlock_seckey(passwdfn,sk)) != SUCCESS ) { + if (verbose) printf(SA_ERR,error_to_str(e)); + return e; + } + } else if (e != SUCCESS) { + if (verbose) printf(SA_ERR,error_to_str(e)); + return e; + } + // Attempt to sign + SignedMsg sm; + if ( (e = load_and_sign_file_contents(fn,sk,&sm)) != SUCCESS ) { + if (verbose) printf(SA_ERR,error_to_str(e)); + return e; + } + // Write signature to file + FILE *fle = fopen(fn, "a+"); + if (fle == NULL) { + if (verbose) printf(SA_ERR,"Could not open the file to append signature."); + sodium_free(sm); + return E_BAD_FILE; + } + char *b64sig; + e = signed_message_to_signature(sm,&b64sig); + if ( e != SUCCESS) { if (verbose) printf(SA_ERR,error_to_str(e)); } + else { + fprintf(fle,"\n%s\n%s\n",DEFAULT_SIG_DELIM,b64sig); + free(b64sig); + } + + // Clean up, one way or another + sodium_free(sm); + fclose(fle); + + if (verbose) printf("\nSuccessfully appended signature to file %s\n",fn); + + return e; +} + +#define SD_ERR "Error creating detached signature: %s\n" +Error sign_detached(const char * const fn, const char * const sfn, + const char * const comment, const char * const trusted_comment, + const char * const passwdfn, SecretKey sk) { + // Standard checks + if (fn == NULL) { + if (verbose) printf(SD_ERR,"No file to sign specified."); + return E_NULL; + } + if (strnlen(fn,MAX_FILENAME_LEN)==MAX_FILENAME_LEN) { + if (verbose) printf(SD_ERR,"Filename is too long."); + return E_BAD_FILE; + } + if (comment != NULL && strnlen(comment,MAX_COMMENT_LEN)==MAX_COMMENT_LEN) { + if (verbose) printf(SD_ERR,"Comment is too long."); + return E_INVALID_ARG; + } + // Handle the signature file (if not specificied be intelligent) + char *sfn_internal; + if (sfn == NULL) { + sfn_internal = catch_malloc(strnlen(fn,MAX_FILENAME_LEN)+5, + "Unable to allocate filename storage."); + sprintf(sfn_internal,"%s.sig",fn); + } else { + size_t l = strnlen(sfn,MAX_FILENAME_LEN); + if (l == MAX_FILENAME_LEN) { + if (verbose) printf(SD_ERR,"Signature filename is too long."); + } + sfn_internal = catch_malloc(l+1, "Unable to allocate signature filename storage."); + memcpy(sfn_internal,sfn,l); + } + // Handle the comment, just do your best + char *comment_internal = catch_malloc(MAX_COMMENT_LEN+1, + "Unable to allocate internal comment storage."); + snprintf(comment_internal, MAX_COMMENT_LEN, "%s", (comment==NULL)?DEFAULT_COMMENT_TEXT:comment); + // Handle the trusted comment (if not specified be intelligent) + char *tcomment_internal = catch_malloc(MAX_COMMENT_LEN+1, + "Unable to allocate internal trusted comment storage."); + if (trusted_comment == NULL) { + /* + We want 'trusted comment: timestamp: <...>, file: <basename>' + Using -D_GNU_SOURCE we get the nice basename function and we + truncate the basename if it's too long. Such is life. + */ + snprintf(tcomment_internal, + MAX_COMMENT_LEN, + DEFAULT_TCOMMENT_FORMAT, + time(NULL), basename(fn)); + } else snprintf(tcomment_internal,MAX_COMMENT_LEN,"%s",trusted_comment); + + Error e; + if ( (e = is_correct_options(sk)) != SUCCESS ) { + if (verbose) printf(SD_ERR,error_to_str(e)); + free(sfn_internal); + free(comment_internal); + free(tcomment_internal); + return e; + } + // Decrypt key if necessary + e = is_correct_checksum(sk); + if ( e == E_CHECKSUM ) { + if ( (e = unlock_seckey(passwdfn,sk)) != SUCCESS ) { + if (verbose) printf(SD_ERR,error_to_str(e)); + free(sfn_internal); + free(comment_internal); + free(tcomment_internal); + return e; + } + } else if (e != SUCCESS) { + if (verbose) printf(SD_ERR,error_to_str(e)); + free(sfn_internal); + free(comment_internal); + free(tcomment_internal); + return e; + } + // Attempt to sign the content of the file for the first signature + SignedMsg sm; + if ( (e = load_and_sign_file_contents(fn,sk,&sm)) != SUCCESS ) { + if (verbose) printf(SD_ERR,error_to_str(e)); + free(sfn_internal); + free(comment_internal); + free(tcomment_internal); + return e; + } + // Write signature to file + FILE *fle = fopen(sfn_internal, "w+"); + if (fle == NULL) { + if (verbose) printf(SA_ERR,"Could not open the signature file for writing."); + sodium_free(sm); + free(comment_internal); + free(tcomment_internal); + free(sfn_internal); + return E_BAD_FILE; + } + char *b64sig; + e = signed_message_to_signature(sm,&b64sig); + if ( e != SUCCESS) { if (verbose) printf(SA_ERR,error_to_str(e)); } + else { + // We now have what we need for the first signature, so write it + fprintf(fle,"%s%s\n%s\n",DEFAULT_COMMENT_PREFIX,comment_internal,b64sig); + // Now we must compute the second signature + char *secondsig; + e = trusted_comment_sign(sm->sig,tcomment_internal,sk,&secondsig); + if ( e != SUCCESS ) { if (verbose) printf(SA_ERR,error_to_str(e)); } + else { + fprintf(fle,"%s%s\n%s\n",DEFAULT_TCOMMENT_PREFIX,tcomment_internal,secondsig); + free(secondsig); + if (verbose) printf("\nSuccessfully wrote signature to file %s\n",sfn_internal); + } + free(b64sig); + } + + // Clean up, one way or another + sodium_free(sm); + free(comment_internal); + free(tcomment_internal); + free(sfn_internal); + fclose(fle); + + return e; +} + +Error generic_verify_detached(uint8_t * msg, size_t msg_len, + uint8_t * b64sig, size_t b64sig_len, + uint8_t *savesig, PublicKey pk) { + // --- Forgo most verification --- + if (msg == NULL || msg_len == 0 || b64sig == NULL || b64sig_len == 0 || pk == NULL) + return E_NULL; + // --- Create signedmessage and init it --- + SignedMsg sm = catch_malloc(sizeof(struct signed_msg_s), + "Unable to allocate signed message structure for verification."); + sm->msg = msg; + sm->msglen = msg_len; + // --- Attempt decode --- + size_t rawlen; + Error e = base64_to_bin(b64sig, b64sig_len, + (uint8_t*)(void*)sm, SIGNED_MSG_WIDTH, &rawlen); + if ( e != SUCCESS ) { + free(sm); + return e; + } + // --- Verify --- + e = verify_message(pk,sm); + // Ugly hack, but the format spec is weird and this saves effort + if (savesig != NULL) memcpy(savesig,sm->sig,SIG_WIDTH); + free(sm); + return e; +} + +#define VA_ERR "Error verifying inline signature: %s\n" +Error verify_attached(const char * const fn, PublicKey pk) { + // --- Standard checks --- + if (fn == NULL) { + if (verbose) printf(VA_ERR,"No file specified."); + return E_NULL; + } + if (strnlen(fn,MAX_FILENAME_LEN)==MAX_FILENAME_LEN) { + if (verbose) printf(VA_ERR,"Filename is too long."); + return E_BAD_FILE; + } + Error e; + if ( (e = is_valid_pubkey(pk)) != SUCCESS ) { + if (verbose) printf(VA_ERR,error_to_str(e)); + return e; + } + // --- Memory map file --- + size_t flen; uint8_t *data; + e = memory_map_file(fn,&data,&flen); + if (e != SUCCESS ) { + if (verbose) printf(VA_ERR,error_to_str(e)); + return e; + } + // --- Split file into content + sig --- + size_t k = 1; + while (k<flen) { + if (data[k-1] == '\n' && + k+sizeof(DEFAULT_SIG_DELIM) < flen+1 && + (sodium_memcmp(data+k,DEFAULT_SIG_DELIM,sizeof(DEFAULT_SIG_DELIM)-1) == 0)) + break; + else k++; + } + if (k==flen) { + munmap(data,flen); + if (verbose) printf(VA_ERR,"Unable to find signature delimeter."); + return E_BAD_FILE; + } + // --- Verfify --- + e = generic_verify_detached(data, k-1, + data+k+sizeof(DEFAULT_SIG_DELIM), + flen-(k+sizeof(DEFAULT_SIG_DELIM)), + NULL,pk); + if ( e != SUCCESS ) { + if (verbose) printf(VA_ERR,error_to_str(e)); + } else { + if (verbose) { + printf("Successfully verified %s with key ",fn); + display_key_id(pk->key_id); + putchar('\n'); + } + } + munmap(data,flen); + return e; +} + +void trim_str(char *buf, size_t *buflen, size_t maxbuflen) { + if (buf == NULL || *buf == 0 || maxbuflen == 0) return; + size_t k = strnlen(buf,maxbuflen); + if (buf[k-1]=='\n') { + buf[k-1]=0; + k--; + } + if (buflen != NULL) *buflen = k; +} + +#define VD_ERR "Error verifying detached signature: %s\n" +Error verify_detached(const char * const fn, const char * const sfn, + PublicKey pk) { + // --- Standard checks --- + if (fn == NULL || *fn == 0) { + if (verbose) printf(VD_ERR,"No file specified."); + return E_NULL; + } + if (strnlen(fn,MAX_FILENAME_LEN)==MAX_FILENAME_LEN) { + if (verbose) printf(VD_ERR,"Filename is too long."); + return E_BAD_FILE; + } + Error e; + if ( (e = is_valid_pubkey(pk)) != SUCCESS ) { + if (verbose) printf(VD_ERR,error_to_str(e)); + return e; + } + char *sfn_internal; + if (sfn == NULL || *fn == 0) { + sfn_internal = catch_malloc(strnlen(fn,MAX_FILENAME_LEN)+5, + "Unable to allocate memory for signature filename."); + snprintf(sfn_internal,MAX_FILENAME_LEN+4,"%s.sig",fn); + } else { + size_t l = strnlen(sfn,MAX_FILENAME_LEN); + if (l == MAX_FILENAME_LEN) { + if (verbose) printf(VD_ERR,"Signature filename too long."); + return E_INVALID_LEN; + } + sfn_internal = catch_malloc(l, "Unable to allocate memory for signature filename."); + memcpy(sfn_internal,sfn,l); + } + // --- Memory map data --- + size_t datalen; uint8_t *data; + e = memory_map_file(fn,&data,&datalen); + if (e != SUCCESS ) { + if (verbose) printf(VD_ERR,error_to_str(e)); + free(sfn_internal); + return e; + } + // --- Parse signature file --- + FILE *fle = fopen(sfn_internal, "r"); + if (fle == NULL) { + if (verbose) printf("Error verifying detached signature: Unable to open the signature file %s\n",sfn); + munmap(data,datalen); + free(sfn_internal); + return E_BAD_FILE; + } + #define ERR_STR_MALLOC "Unable to allocate internal memory for verification." + char *tc = catch_malloc(MAX_COMMENT_LEN+2, ERR_STR_MALLOC), *tcc; + char *sig1 = catch_malloc(4*(SIGNED_MSG_WIDTH/3+1)+2, ERR_STR_MALLOC); + char *sig2 = catch_malloc(4*(SIG_WIDTH/3+1)+2, ERR_STR_MALLOC); + if (fgets(tc,MAX_COMMENT_LEN+sizeof(DEFAULT_COMMENT_PREFIX),fle) == NULL + || sodium_memcmp(tc,DEFAULT_COMMENT_PREFIX,sizeof(DEFAULT_COMMENT_PREFIX)-1) != 0) { + if (verbose) printf(VD_ERR,"Error reading signature file, untrusted comment."); + free(sig2); free(sig1); free(tc); free(sfn_internal); + munmap(data,datalen); + fclose(fle); + return E_BAD_FILE; + } + if (fgets(sig1,4*(SIGNED_MSG_WIDTH/3+1)+2,fle) == NULL) { + if (verbose) printf(VD_ERR,"Error reading signature file, data signature."); + free(sig2); free(sig1); free(tc); free(sfn_internal); + munmap(data,datalen); + fclose(fle); + return E_BAD_FILE; + } + trim_str(sig1,NULL,4*(SIGNED_MSG_WIDTH/3+1)+1); + if (fgets(tc,MAX_COMMENT_LEN+sizeof(DEFAULT_TCOMMENT_PREFIX),fle) == NULL + || sodium_memcmp(tc,DEFAULT_TCOMMENT_PREFIX,sizeof(DEFAULT_TCOMMENT_PREFIX)-1) != 0) { + if (verbose) printf(VD_ERR,"Error reading signature file, trusted comment."); + free(sig2); free(sig1); free(tc); free(sfn_internal); + munmap(data,datalen); + fclose(fle); + return E_BAD_FILE; + } + size_t tclen; + tcc = tc + sizeof(DEFAULT_TCOMMENT_PREFIX) - 1; + trim_str(tcc,&tclen,MAX_COMMENT_LEN); + if (fgets(sig2,4*(SIG_WIDTH/3+1)+1,fle) == NULL) { + if (verbose) printf(VD_ERR,"Error reading signature file, global signature."); + free(sig2); free(sig1); free(tc); free(sfn_internal); + munmap(data,datalen); + fclose(fle); + return E_BAD_FILE; + } + fclose(fle); + // --- Verify standard signature --- + uint8_t *sig_and_tcc = catch_malloc(SIG_WIDTH+tclen, + "Unable to allocate internal storage for verification."); + e = generic_verify_detached(data, datalen, + (uint8_t*)sig1, 4*(SIGNED_MSG_WIDTH/3+1), + sig_and_tcc, pk); + munmap(data,datalen); + if ( e != SUCCESS ) { + if (verbose) printf(VA_ERR,error_to_str(e)); + free(sig1); free(tc); free(sig2); free(sfn_internal); + free(sig_and_tcc); + return e; + } + // -- Verify global signature --- + memcpy(sig_and_tcc + SIG_WIDTH,tcc,tclen); + uint8_t rawsig2[SIG_WIDTH]; size_t t; + e = base64_to_bin((uint8_t*)sig2, 4*(SIG_WIDTH/3+1), + rawsig2, SIGNED_MSG_WIDTH, &t); + if ( e != SUCCESS ) { + if (verbose) printf(VA_ERR,error_to_str(e)); + free(sig1); free(tc); free(sig2); free(sfn_internal); + free(sig_and_tcc); + return e; + } + if (crypto_sign_verify_detached(rawsig2,sig_and_tcc,SIG_WIDTH+tclen,pk->public_key) != 0) + e = E_VERIFY; + if ( e != SUCCESS ) { + if (verbose) printf(VA_ERR,error_to_str(e)); + free(sig1); free(tc); free(sig2); free(sfn_internal); + free(sig_and_tcc); + return e; + } + // Output + if (verbose) { + printf("Successfully verified %s against %s with key ",fn,sfn_internal); + display_key_id(pk->key_id); + putchar('\n'); + printf("%s\n",tc); + } + // Cleanup + free(sig1); free(tc); free(sig2); free(sfn_internal); + free(sig_and_tcc); + return e; +} @@ -0,0 +1,79 @@ +/* + This file is part of msr. + + msr 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. + + msr 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 Foobar. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <time.h> + +#include <sodium.h> + +#include "utils.h" +#include "base64.h" +#include "crypto.h" + +#define DEFAULT_COMMENT_PREFIX "untrusted comment: " +#define DEFAULT_COMMENT_TEXT "signature generated by msr" + +#define DEFAULT_TCOMMENT_PREFIX "trusted comment: " +#define DEFAULT_TCOMMENT_FORMAT "timestamp=%lu, file=%s" + +#define DEFAULT_PUBKEY_PREFIX "public key " +#define DEFAULT_SECKEY_PREFIX "encrypted secret key generated by msr" + +#define DEFAULT_SIG_DELIM "--- BEGIN SIGNATURE ---" + +#define MAX_PASS_LEN 1024 +#define MAX_COMMENT_LEN 512 +#define MAX_FILENAME_LEN 1024 + +static char verbose = 1; + + +Error unlock_seckey(const char * const passwdfn, SecretKey sk); + +Error generate_to_file(const char * fn, + const char * const passwdfn, + const char * const pkcomment, + const char * const skcomment); + +Error read_pubkey_string(const char * const str, PublicKey *pk); +Error read_seckey_string(const char * const str, SecretKey *sk); + +Error read_pubkey_file(const char * const fn, + PublicKey *pk); +Error read_seckey_file(const char * const fn, + SecretKey *sk); + +Error sign_attached(const char * const fn, + const char * const passwdfn, SecretKey sk); + +Error sign_detached(const char * const fn, const char * const sfn, + const char * const comment, const char * const trusted_comment, + const char * const passwdfn, SecretKey sk); + +Error verify_attached(const char * const fn, PublicKey pk); + +Error verify_detached(const char * const fn, const char * const sfn, + PublicKey pk); @@ -0,0 +1,245 @@ +/* + This file is part of msr. + + msr 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. + + msr 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 Foobar. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <argp.h> +#include "io.h" + +enum opt_key {O_PUBKEY_STR, O_SECKEY_STR, O_PUBKEY_COMMENT, O_SECKEY_COMMENT, + O_PUBKEY_FLE, O_SECKEY_FLE, O_SIG_FLE, O_SIGN_INLINE, O_PASS_FLE}; + +enum action {A_NULL, A_SIGN_D, A_SIGN_T, A_VERIFY_D, A_VERIFY_T, A_GEN}; + +typedef struct option_state_s { + char *pkstr, *pkfn, *pkc; + char *skstr, *skfn, *skc, *skpf; + char *utc, *tc; + char *fn, *sfn; + char quiet; + enum action action; +} * OptionState; + +static struct argp_option options[] = { + // Actions + {"generate", 'G', "FILE", OPTION_ARG_OPTIONAL, "Generate a new key pair, storing in `FILE.{pub,key}' (FILE defaults to msr)", 1}, + {"sign-detached", 'S', "FILE", 0, "Sign FILE by generating a separate signature", 1}, + {"sign-text", 'T', "FILE", 0, "Sign FILE by appending a signature", 1}, + {"verify-detached", 'V', "FILE", 0, "Verify the detached signature on FILE", 1}, + {"verify-text", 'X', "FILE", 0, "Verify the inline text signature in FILE", 1}, + // Odd-one-out + {"signature-file", 'f', "FILE", 0, "Use FILE as the signature file for detached signing and verification purposes", 2}, + // Keys + {"pubkey-file", 'p', "FILE", 0, "Use the public key in FILE", 3}, + {"pubkey-string", O_PUBKEY_STR, "STR", 0, "Use the public key encoded in STR", 3}, + {"seckey-file", 's', "FILE", 0, "Use the secret key in FILE", 3}, + {"seckey-string", O_SECKEY_STR, "STR", 0, "Use the secret key encoded in STR", 3}, + {"password-file", O_PASS_FLE, "FILE", 0, "Load secret key passphrase from FILE", 4}, + // Comments + {"comment-untrusted", 'u', "STR", 0, "Use STR for the default untrusted comment when making a detached signature", 5}, + {"comment-trusted", 't', "STR", 0, "Use STR for the trusted comment when making a detached signature", 5}, + {"comment-pubkey", O_PUBKEY_COMMENT, "STR", 0, "Use STR for the default untrusted comment in the generated public key file", 5}, + {"comment-seckey", O_SECKEY_COMMENT, "STR", 0, "Use STR for the default untrusted comment in the generated secret key file", 5}, + // Miscellaneous + {"quiet", 'q', 0, 0, "Produce no output",6}, + {0,0,0,0,0,0} +}; + + +char *action_to_string(enum action a) { + switch (a) { + case A_GEN: return "generation of key pair"; + case A_SIGN_D: return "signing detached"; + case A_SIGN_T: return "signing inline text"; + case A_VERIFY_D: return "verification of detached signature"; + case A_VERIFY_T: return "verification of inline text signature"; + default: return "null action"; + } +} + +// --- Some macros to make argument checking sane --- +#define FAIL(str) (argp_error(state, (str))) +#define ACT_CHECK { if (s->action!=A_NULL) FAIL("Cannot execute conflicting actions."); } +#define BADARG(arg) (argp_error(state, "%s stipulated for %s",arg,action_to_string(s->action))) +#define ARGCHECK(a,b,c,d,e,f,g,h,i,j) { \ + if(a && s->pkc) BADARG("public key comment"); \ + if(b && s->pkfn) BADARG("public key file"); \ + if(c && s->pkstr) BADARG("public key string"); \ + if(d && s->skc) BADARG("secret key comment"); \ + if(e && s->skfn) BADARG("secret key file"); \ + if(f && s->skstr) BADARG("secret key string"); \ + if(g && s->tc) BADARG("trusted comment"); \ + if(h && s->utc) BADARG("untrusted comment"); \ + if(i && s->skpf) BADARG("secret key password file"); \ + if(j && s->sfn) BADARG("signature file"); } + +error_t parse_opt(int key, char *arg, struct argp_state *state) { + OptionState s = state->input; + switch (key) { + case ARGP_KEY_FINI: { + switch (s->action) { + case A_NULL: argp_state_help(state,state->out_stream,ARGP_HELP_SEE); + case A_GEN: { ARGCHECK(0,1,1,0,1,1,1,1,1,1); break; }; + case A_VERIFY_D: + case A_SIGN_D: { ARGCHECK(1,0,0,1,0,0,0,0,0,0); break; } + case A_VERIFY_T: + case A_SIGN_T: { ARGCHECK(1,0,0,1,0,0,1,1,0,1); break; } + } + break; + } + case ARGP_KEY_INIT: { + s->action = A_NULL; + s->pkc = s->pkfn = s->pkstr = NULL; + s->skc = s->skfn = s->skstr = NULL; + s->tc = s->utc = NULL; + s->fn = s->sfn = NULL; + s->skpf = NULL; + s->quiet = 0; + break; + } + case 'G': {ACT_CHECK; s->action = A_GEN; s->fn = arg; break; } + case 'S': {ACT_CHECK; s->action = A_SIGN_D; s->fn = arg; break; } + case 'T': {ACT_CHECK; s->action = A_SIGN_T; s->fn = arg; break; } + case 'V': {ACT_CHECK; s->action = A_VERIFY_D; s->fn = arg; break; } + case 'X': {ACT_CHECK; s->action = A_VERIFY_T; s->fn = arg; break; } + case 'p': { + if (s->pkfn) FAIL("Already set public key filename."); + if (s->pkstr) FAIL("Already set public key string."); + s->pkfn = arg; + break; + } + case O_PUBKEY_STR: { + if (s->pkfn) FAIL("Already set public key filename."); + if (s->pkstr) FAIL("Already set public key string."); + s->pkstr = arg; + break; + } + case 's': { + if (s->skfn) FAIL("Already set private key filename."); + if (s->skstr) FAIL("Already set private key string."); + s->skfn = arg; + break; + } + case O_SECKEY_STR: { + if (s->skfn) FAIL("Already set private key filename."); + if (s->skstr) FAIL("Already set private key string."); + s->skstr = arg; + break; + } + case O_PASS_FLE: { + if (s->skpf) FAIL("Already set secret key password file."); + s->skpf = arg; + } + case 't': { + if (s->tc) FAIL("Already set trusted comment string."); + s->tc = arg; + break; + } + case 'u': { + if (s->utc) FAIL("Already set untrusted comment string."); + s->utc = arg; + break; + } + case O_PUBKEY_COMMENT: { + if (s->pkc) FAIL("Already set public key comment."); + s->pkc = arg; + break; + } + case O_SECKEY_COMMENT: { + if (s->skc) FAIL("Already set secret key comment."); + s->skc = arg; + break; + } + case 'f': { + if (s->sfn) FAIL("Already set signature file name."); + s->sfn = arg; + break; + } + case 'q': {s->quiet = 1; break; } + default: return ARGP_ERR_UNKNOWN; + } + return 0; +} + +const char *argp_program_version = "msr 0.0.2"; +const char *argp_program_bug_address = "http://github.com/tslilc/msr or <tslil@posteo.de>"; +static char doc[] = "msr -- small public key verification tool.\v\ +Copyright (C) 2015 Tslil Clingman\nThis is free software and is \ +licensed under the terms of the GNU GPL version three or later. This \ +program comes with ABSOLUTELY NO WARRANTY; consult the LICENSE file \ +for details."; + +static struct argp argp = {options, parse_opt, NULL, doc, NULL, NULL, NULL}; + +int main(int argc, char ** argv) { + + struct option_state_s s; + argp_parse(&argp,argc,argv, ARGP_NO_ARGS, 0, &s); + + if (s.quiet) verbose = 0; + + if (sodium_init() == -1) { + puts("Unable to initialise libsodium"); + return 1; + } + Error e; + SecretKey sk; + PublicKey pk; + switch (s.action) { + case A_GEN: { + e = generate_to_file(s.fn,s.skpf,s.pkc,s.skc); + if (e != SUCCESS) exit(1); + break; + } + case A_SIGN_D: { + if (s.skstr) e = read_seckey_string(s.skstr,&sk); + else e = read_seckey_file(s.skstr,&sk); + if (e != SUCCESS) exit(1); + e = sign_detached(s.fn,s.sfn,s.utc,s.tc,s.skpf,sk); + if ( e != SUCCESS ) exit(1); + break; + } + case A_SIGN_T: { + if (s.skstr) e = read_seckey_string(s.skstr,&sk); + else e = read_seckey_file(s.skstr,&sk); + if (e != SUCCESS) exit(1); + e = sign_attached(s.fn,s.skpf,sk); + if ( e != SUCCESS ) exit(1); + break; + } + case A_VERIFY_D: { + if (s.pkstr) e = read_pubkey_string(s.pkstr,&pk); + else e = read_pubkey_file(s.pkfn,&pk); + if (e != SUCCESS) exit(1); + e = verify_detached(s.fn,s.sfn,pk); + if ( e != SUCCESS ) exit(1); + break; + } + case A_VERIFY_T: { + if (s.pkstr) e = read_pubkey_string(s.pkstr,&pk); + else e = read_pubkey_file(s.pkfn,&pk); + if (e != SUCCESS) exit(1); + e = verify_attached(s.fn,pk); + if ( e != SUCCESS ) exit(1); + break; + } + // Should not be here + case A_NULL: exit(1); + } + + return 0; +} + @@ -0,0 +1,65 @@ +/* + This file is part of msr. + + msr 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. + + msr 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 Foobar. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "utils.h" + +void* catch_malloc(size_t size, const char * const err) { + errno = 0; + void *m = malloc(size); + if (m == NULL || errno != 0) { + fprintf(stderr,"Error: %s\n",err); + exit(1); + } + return m; +} + + +void* catch_smalloc(size_t size, const char * const err) { + errno = 0; + void* m = sodium_malloc(size); + if (m == NULL || errno != 0) { + fprintf(stderr,"Error: %s\n",err); + exit(1); + } + return m; +} + +const char * error_to_str(const Error e) { + switch (e) { + case SUCCESS: return NULL; + case E_NULL: return "Null argument."; + case E_CHECKSUM: return "Secret key checksum failed."; + case E_ALG_CHOICE: return "Unsupported algorithm choice."; + case E_ALREADY_ENCRYPTED: return "Secret key already encrypted."; + case E_ALREADY_DECRYPTED: return "Secret key already decrypted."; + case E_KDF_FAIL: return "Internal KDF error."; + case E_BAD_PASS: return "Incorrect password for secret key."; + case E_VERIFY: return "Verification failed"; + case E_WRONG_PUBKEY: return "Public key ID mismatch."; + case E_SIGN: return "Internal signing error."; + case E_SMALL_BUFF: return "Buffer too small."; + case E_INVALID_LEN: return "Invalid buffer length."; + case E_B64_BAD_STR: return "Invalid characters found in string."; + case E_PASS_MISMATCH: return "Passwords do not match."; + case E_BAD_FILE: return "Unable to open file."; + case E_BAD_PUBKEY_DAT: return "Invalid public key data."; + case E_BAD_SECKEY_DAT: return "Invalid secret key data."; + case E_ENCRYPTED: return "Secret key is encrypted and so unusable."; + case E_INVALID_ARG: return "Invalid argument."; + default: return "Unreachable."; + } +} @@ -0,0 +1,39 @@ +/* + This file is part of msr. + + msr 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. + + msr 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 Foobar. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sodium.h> + +#define PROGRAMME_NAME "msr" +#define PROGRAMME_VER "0.0.1" + +typedef enum error_e { SUCCESS, E_NULL, E_CHECKSUM, E_ALG_CHOICE, + E_ALREADY_DECRYPTED, E_ALREADY_ENCRYPTED, + E_ENCRYPTED, E_KDF_FAIL, E_BAD_PASS, E_BAD_PUBKEY_DAT, + E_VERIFY, E_WRONG_PUBKEY, E_SIGN, + E_SMALL_BUFF, E_INVALID_LEN, E_B64_BAD_STR, + E_PASS_MISMATCH, E_BAD_FILE, E_BAD_SECKEY_DAT, + E_INVALID_ARG} Error; + +void* catch_malloc(size_t size, const char * const err); +void* catch_smalloc(size_t size, const char * const err); + +const char * error_to_str(const Error e); |
