aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTslil Clingman <hiato1@gmail.com>2015-06-25 02:51:02 +0200
committerTslil Clingman <hiato1@gmail.com>2015-06-25 02:51:02 +0200
commit26d1f221761ba264906f215ed53f4f57ab0f9d06 (patch)
tree7abd32ed3449e43d0b17ccee51e6b843bf63d759
Init
-rw-r--r--.gitignore4
-rw-r--r--LICENSE674
-rw-r--r--Makefile20
-rw-r--r--README.md86
-rw-r--r--base64.c153
-rw-r--r--base64.h30
-rw-r--r--crypto.c160
-rw-r--r--crypto.h92
-rw-r--r--io.c1035
-rw-r--r--io.h79
-rw-r--r--msr.c245
-rw-r--r--utils.c65
-rw-r--r--utils.h39
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
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/LICENSE
@@ -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);
diff --git a/io.c b/io.c
new file mode 100644
index 0000000..fc248e6
--- /dev/null
+++ b/io.c
@@ -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;
+}
diff --git a/io.h b/io.h
new file mode 100644
index 0000000..430b2f9
--- /dev/null
+++ b/io.h
@@ -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);
diff --git a/msr.c b/msr.c
new file mode 100644
index 0000000..67b1eb8
--- /dev/null
+++ b/msr.c
@@ -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;
+}
+
diff --git a/utils.c b/utils.c
new file mode 100644
index 0000000..1b0fcb6
--- /dev/null
+++ b/utils.c
@@ -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.";
+ }
+}
diff --git a/utils.h b/utils.h
new file mode 100644
index 0000000..a0e2a30
--- /dev/null
+++ b/utils.h
@@ -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);