pax_global_header00006660000000000000000000000064126651071000014510gustar00rootroot0000000000000052 comment=74fb3c0814152de193bac65a53b86a08a25ecb79 git-evtag-2016.1/000077500000000000000000000000001266510710000134705ustar00rootroot00000000000000git-evtag-2016.1/COPYING000066400000000000000000000614471266510710000145370ustar00rootroot00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! git-evtag-2016.1/Makefile-decls.am000066400000000000000000000026701266510710000166210ustar00rootroot00000000000000# Copyright (C) 2011,2014 Colin Walters # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # Common variables ACLOCAL_AMFLAGS = AM_CPPFLAGS = AM_CFLAGS = DISTCHECK_CONFIGURE_FLAGS = SUBDIRS = NULL = BUILT_SOURCES = MANPAGES = CLEANFILES = EXTRA_DIST = bin_PROGRAMS = sbin_PROGRAMS = bin_SCRIPTS = lib_LTLIBRARIES = libexec_PROGRAMS = noinst_LTLIBRARIES = noinst_PROGRAMS = privlibdir = $(pkglibdir) privlib_LTLIBRARIES = pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = INTROSPECTION_GIRS = girdir = $(datadir)/gir-1.0 gir_DATA = typelibdir = $(libdir)/girepository-1.0 typelib_DATA = gsettings_SCHEMAS = TESTS = # git.mk GITIGNOREFILES = # This is a special facility to chain together hooks easily INSTALL_DATA_HOOKS = install-data-hook: $(INSTALL_DATA_HOOKS) git-evtag-2016.1/Makefile.am000066400000000000000000000023641266510710000155310ustar00rootroot00000000000000# Copyright (C) 2014 Colin Walters # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. include Makefile-decls.am privdatadir=$(pkglibdir) ACLOCAL_AMFLAGS += -I m4 ${ACLOCAL_FLAGS} AM_CPPFLAGS += -DDATADIR='"$(datadir)"' \ -DLIBEXECDIR='"$(libexecdir)"' \ -DLOCALEDIR=\"$(datadir)/locale\" AM_CFLAGS += $(WARN_CFLAGS) include $(INTROSPECTION_MAKEFILE) girdir = $(pkgdatadir)/gir-1.0 gir_DATA = $(INTROSPECTION_GIRS) typelibdir = $(pkglibdir)/girepository-1.0 typelib_DATA = $(gir_DATA:.gir=.typelib) include src/Makefile-git-evtag.am include tests/Makefile-tests.am git-evtag-2016.1/README.md000066400000000000000000000167101266510710000147540ustar00rootroot00000000000000# git-evtag `git-evtag` can be used as a replacement for `git-tag -s`. It will generate a strong checksum (called `Git-EVTag-v0-SHA512`) over the commit, tree, and blobs it references (and recursively over submodules). Git mailing list thread: - permalink: http://permalink.gmane.org/gmane.comp.version-control.git/264533 - comments: http://comments.gmane.org/gmane.comp.version-control.git/264533 ### Using git-evtag Create a new `v2015.10` tag, covering the `HEAD` revision with GPG signature and `Git-EVTag-v0-SHA512`: ``` $ git-evtag sign v2015.10 ( type your tag message, note a Git-EVTag-v0-SHA512 line in the message ) $ git show v2015.10 ( Note signature covered by GPG signature ) ``` Verify a tag: ``` $ git-evtag verify v2015.10 gpg: Signature made Sun 28 Jun 2015 10:49:11 AM EDT gpg: using RSA key 0xDC45FD5921C13F0B gpg: Good signature from "Colin Walters " [ultimate] gpg: aka "Colin Walters " [ultimate] Primary key fingerprint: 1CEC 7A9D F7DA 85AB EF84 3DC0 A866 D7CC AE08 7291 Subkey fingerprint: AB92 8A9C F8DD 0629 09C3 7BBD DC45 FD59 21C1 3F0B Successfully verified: Git-EVTag-v0-SHA512: b05f10f9adb0eff352d90938588834508d33fdfcedbcfc332999ee397efa321d1f49a539f1b82f024111a281c1f441002e7f536b06eb04d41857b01636f6f268 ``` ### Replacing tarballs - i.e. be the primary artifact This is similar to what project distributors often accomplish by using `git archive`, or `make dist`, or similar tools to generate a tarball, and then checksumming that, and (ideally) providing a GPG signature covering it. ### Tarball reproducibility The problem with `git archive` and `make dist` is that tarballs (and other tools like zip files) are not easily reproducible *exactly* from a git repository commit. The authors of git reserve the right to change the file format output by `git archive` in the future. Also, there are a variety of reasons why compressors like `gzip` and `xz` aren't necessarily reproducible, such as compression levels, included timestamps, optimizations in the algorithm, etc. See [Pristine tar](http://git.kitenet.net/?p=zzattic/pristine-tar.git;a=summary) for some examples of the difficulties involved (e.g. trying to retroactively guess the compression level arguments from the xz dictionary size). If the checksum is not reproducible, it becomes much more difficult to easily and reliably verify that a generated tarball contains the same source code as a particular git commit. What `git-evtag` implements is an algorithm for providing a strong checksum over the complete source objects for the target commit (+ trees + blobs + submodules). Then it's integrated with GPG for end-to-end verification. (Although, one could also wrap the checksum in X.509 or some other public/private signature solution). Then no out of band distribution mechanism is necessary, and better, the checksums strengthen the ability to verify integrity of the git repository. (And if you want to avoid downloading the entire history, that's what `git clone --depth=1` is for.) ### Git and SHA1 Git uses a modified Merkle tree with SHA1, which means that if an attacker managed to create a SHA1 collision for a source file object (git blob), it would affect *all* revisions and checkouts - invalidating the security of *all* GPG signed tags whose commits point to that object. Now, the author of this tool believes that *today*, GPG signed git tags are fairly secure, especially if one is careful to ensure transport integrity (e.g. pinned TLS certificates from the origin). That said, while it is true that at the time of this writing, no public SHA1 collision is known, there are attacks against reduced round variants of SHA1. We expect git repositories to be used for many, many years to come. It makes a lot of sense to take additional steps now to add security. ### The Git-EVTag algorithm (v0) There is currently only one version of the `Git-EVTag` algorithm, called `v0` - and it only supports [SHA-512](https://en.wikipedia.org/wiki/SHA-2). It is declared stable. All further text refers to this version of the algorithm. In the unlikely event that it is necessary to introduce a new version, this tool will support all known versions. `Git-EVTag-v0-SHA512` covers the complete contents of all objects for a commit; again similar to checksumming `git archive`, except reproducible. Each object is added to the checksum in its raw canonicalized form, including the header. For a given commit (in Rust-style pseudocode): ```rust fn git_evtag(repo: GitRepo, commitid: String) -> SHA512 { let checksum = new SHA512(); walk_commit(repo, checksum, commitid) return checksum } fn walk_commit(repo: GitRepo, checksum : SHA512, commitid : String) { checksum_object(repo, checksum, commitid) let treeid = repo.load_commit(commitid).treeid(); walk(repo, checksum, treeid) } fn checksum_object(repo: GitRepo, checksum: SHA512, objid: String) -> () { // This is the canonical header of the object; // https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#Object-Storage let header : &str = repo.load_object_header(objid); // The NUL byte after the header, explicitly included in the checksum let nul = [0u8]; // The remaining raw content of the object as a byte array let body : &[u8] = repo.load_object_body(objid); checksum.update(header.as_bytes()) checksum.update(&nul); checksum.update(body) } fn walk(repo: GitRepo, checksum: SHA512, treeid: String) -> () { // First, add the tree object itself checksum_object(repo, checksum, treeid); let tree = repo.load_tree(treeid); for child in tree.children() { match childtype { Blob(blobid) => checksum_object(repo, checksum, blobid), Tree(child_treeid) => walk(repo, checksum, child_treeid), Commit(commitid, path) => { let child_repo = repo.get_submodule(path) walk_commit(child_repo, checksum, commitid) } } } } ``` This strong checksum, can be verified reproducibly offline after cloning a git repository for a particular tag. When covered by a GPG signature, it provides a strong end-to-end integrity guarantee. It's quite inexpensive and practical to compute `Git-EVTag-v0-SHA512` once per tag/release creation. At the time of this writing, on the Linux kernel (a large project by most standards), it takes about 5 seconds to compute on this author's laptop. On most smaller projects, it's completely negligible. ### Aside: other aspects of tarballs This project is just addressing one small part of the larger git/tarball question. Anything else is out of scope, but a brief discussion of other aspects is included below. Historically, many projects include additional content in tarballs. For example, the GNU Autotools pregenerate a `configure` script from `configure.ac` and the like. Other projects don't include translations in git, but merge them out of band when generating tarballs. There are many other things like this, and they all harm reproducibility and continuous integration/delivery. For example, while many of my projects use Autotools, I simply have downstream authors run `autogen.sh`. It works just fine - the autotools are no longer changing often, and many downstreams want to do it anyways. For the translation issue, note that bad translations can actually crash one's application. If they're part of the git repository, they can be more easily tested as a unit continuously. git-evtag-2016.1/autogen.sh000077500000000000000000000005631266510710000154750ustar00rootroot00000000000000#!/bin/sh set -e test -n "$srcdir" || srcdir=`dirname "$0"` test -n "$srcdir" || srcdir=. olddir=`pwd` cd $srcdir AUTORECONF=`which autoreconf` if test -z $AUTORECONF; then echo "*** No autoreconf found, please intall it ***" exit 1 fi mkdir -p m4 autoreconf --force --install --verbose cd $olddir test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" git-evtag-2016.1/configure.ac000066400000000000000000000026351266510710000157640ustar00rootroot00000000000000AC_PREREQ([2.63]) AC_INIT([git-evtag], [2015.2], [walters@verbum.org]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([1.11 -Wno-portability foreign no-define tar-ustar no-dist-gzip dist-xz]) AM_MAINTAINER_MODE([enable]) AM_SILENT_RULES([yes]) AC_USE_SYSTEM_EXTENSIONS AC_SYS_LARGEFILE AC_PROG_CC AM_PROG_CC_C_O changequote(,)dnl if test "x$GCC" = "xyes"; then WARN_CFLAGS="-Wall -Wstrict-prototypes -Werror=missing-prototypes \ -Werror=implicit-function-declaration \ -Werror=pointer-arith -Werror=init-self -Werror=format=2 \ -Werror=format-security \ -Werror=missing-include-dirs -Werror=aggregate-return \ -Werror=declaration-after-statement" fi changequote([,])dnl AC_SUBST(WARN_CFLAGS) # Initialize libtool LT_PREREQ([2.2.4]) LT_INIT([disable-static]) PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES(BUILDDEP_LIBGIT_GLIB, [libgit2 gio-2.0]) save_LIBS=$LIBS LIBS=$BUILDDEP_LIBGIT_GLIB_LIBS AC_CHECK_FUNCS(git_libgit2_init) LIBS=$save_LIBS AC_ARG_ENABLE(installed_tests, AS_HELP_STRING([--enable-installed-tests], [Install test programs (default: no)]),, [enable_installed_tests=no]) AM_CONDITIONAL(BUILDOPT_INSTALL_TESTS, test x$enable_installed_tests = xyes) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT echo " $PACKAGE $VERSION installed tests: $enable_installed_tests " git-evtag-2016.1/packaging/000077500000000000000000000000001266510710000154145ustar00rootroot00000000000000git-evtag-2016.1/packaging/Makefile.dist-packaging000066400000000000000000000022111266510710000217340ustar00rootroot00000000000000# -*- mode: Makefile -*- GITREV = $$(git describe --always --tags) GITREV_FOR_PKG = $(shell echo "$(GITREV)" | sed -e 's,-,\.,g' -e 's,^v,,') srcdir=$(shell git rev-parse --show-toplevel) PACKAGE=git-evtag PKG_VER = $(PACKAGE)-$(GITREV_FOR_PKG) dist-snapshot: set -x; \ echo "PACKAGE=$(PACKAGE)"; \ TARFILE_TMP=$(PKG_VER).tar.tmp; \ echo "Archiving $(PACKAGE) at $(GITREV)"; \ (cd $(srcdir); git archive --format=tar --prefix=$(PKG_VER)/ $(GITREV)) > $${TARFILE_TMP}; \ (cd $$(git rev-parse --show-toplevel); git submodule status) | while read line; do \ rev=$$(echo $$line | cut -f 1 -d ' '); path=$$(echo $$line | cut -f 2 -d ' '); \ echo "Archiving $${path} at $${rev}"; \ (cd $(srcdir)/$$path; git archive --format=tar --prefix=$(PKG_VER)/$$path/ $${rev}) > submodule.tar; \ tar -A -f $${TARFILE_TMP} submodule.tar; \ rm submodule.tar; \ done; \ mv $(PKG_VER).tar{.tmp,}; \ rm -f $(PKG_VER).tar.xz; \ xz $(PKG_VER).tar srpm: dist-snapshot sed -e "s,^Version:.*,Version: $(GITREV_FOR_PKG)," $(PACKAGE).spec.in > $(PACKAGE).spec; \ ./rpmbuild-cwd -bs $(PACKAGE).spec rpm: srpm ./rpmbuild-cwd --rebuild $(PKG_VER)*.src.rpm git-evtag-2016.1/packaging/git-evtag.spec.in000066400000000000000000000012361266510710000205660ustar00rootroot00000000000000Summary: Strong GPG verification of git tags Name: git-evtag Version: 2015.1 Release: 1%{?dist} #VCS: https://github.com/cgwalters/git-evtag Source0: %{name}-%{version}.tar.xz License: LGPLv2+ URL: https://github.com/cgwalters/git-evtag # For autosetup BuildRequires: git # We always run autogen.sh BuildRequires: autoconf automake libtool BuildRequires: pkgconfig(libgit2) BuildRequires: pkgconfig(gio-2.0) %description %{summary} %prep %autosetup -Sgit %build env NOCONFIGURE=1 ./autogen.sh %configure --disable-silent-rules make %{?_smp_mflags} %install make install DESTDIR=%{buildroot} INSTALL="install -p -c" %files %doc COPYING README.md %{_bindir}/%{name} git-evtag-2016.1/packaging/rpmbuild-cwd000077500000000000000000000010051266510710000177270ustar00rootroot00000000000000#!/bin/sh # rpmbuild-cwd: # Run "rpmbuild", defining all RPM variables to use the current directory. # This matches Fedora's system. # # Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php) # Copyright (C) 2010 Red Hat, Inc. # Written by Colin Walters pwd=$(pwd) exec rpmbuild --define "_sourcedir ${pwd}" --define "_specdir ${pwd}" --define "_builddir ${pwd}" --define "_srcrpmdir ${pwd}" --define "_rpmdir ${pwd}" --define "_buildrootdir ${pwd}/.build" "$@" git-evtag-2016.1/src/000077500000000000000000000000001266510710000142575ustar00rootroot00000000000000git-evtag-2016.1/src/Makefile-git-evtag.am000066400000000000000000000017701266510710000202050ustar00rootroot00000000000000# Makefile for C source code # # Copyright (C) 2015 Colin Walters # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. bin_PROGRAMS += git-evtag git_evtag_SOURCES = src/git-evtag.c \ $(NULL) git_evtag_CFLAGS = $(AM_CFLAGS) $(BUILDDEP_LIBGIT_GLIB_CFLAGS) -I$(srcdir)/src git_evtag_LDADD = $(BUILDDEP_LIBGIT_GLIB_LIBS) git-evtag-2016.1/src/git-evtag-compute-py000077500000000000000000000064201266510710000201760ustar00rootroot00000000000000#!/usr/bin/env python # # An implementation of Git-EVTag in a mixture of Python and # git-as-subprocess. Slower than C, but easier to understand # and test. # # For correct submodule handling, requires the working directory have # submodule checkouts. from __future__ import print_function import os import sys import argparse import subprocess import hashlib try: from subprocess import DEVNULL # pylint: disable=no-name-in-module except ImportError: import os DEVNULL = open(os.devnull, 'wb') parser = argparse.ArgumentParser(description="Compute Git-EVTag checksum") parser.add_argument('rev', help='Revision to checksum') opts = parser.parse_args() csum = hashlib.sha512() stats = {'commit': 0, 'blob': 0, 'tree': 0, 'commitbytes': 0, 'blobbytes': 0, 'treebytes': 0} def checksum_bytes(otype, buf): blen = len(buf) csum.update(buf) stats[otype + 'bytes'] += blen return blen def checksum_object(repo, objid): p = subprocess.Popen(['git', 'cat-file', '--batch'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True, cwd=repo) p.stdin.write(objid + '\n') p.stdin.close() (objid,objtype,lenstr) = p.stdout.readline().split(None, 2) olen = int(lenstr) lenstr = lenstr.strip() buf = "{0} {1}\000".format(objtype, lenstr) checksum_bytes(objtype, buf) stats[objtype] += 1 if objtype == 'commit': buf = p.stdout.readline() olen -= checksum_bytes(objtype, buf) (treestr, treeobjid) = buf.split(None, 1) treeobjid = treeobjid.strip() assert treestr == 'tree' else: treeobjid = None while olen > 0: b = p.stdout.read(min(8192, olen)) bytes_read = checksum_bytes(objtype, b) olen -= bytes_read if olen > 0: raise ValueError("Failed to read {0} bytes from object".format(olen)) p.wait() if p.returncode != 0: raise subprocess.CalledProcessError(p.returncode, 'git cat-file') return treeobjid def checksum_tree(repo, objid): checksum_object(repo, objid) p = subprocess.Popen(['git', 'ls-tree', objid], stdin=DEVNULL, stdout=subprocess.PIPE, close_fds=True, cwd=repo) for line in p.stdout: (mode, otype, subid, fname) = line.split(None, 3) fname = fname.strip() if otype == 'blob': checksum_object(repo, subid) elif otype == 'tree': checksum_tree(repo, subid) elif otype == 'commit': checksum_repo(os.path.join(repo, fname), subid) else: assert False p.wait() if p.returncode != 0: raise subprocess.CalledProcessError(p.returncode, 'git ls-tree') def checksum_repo(repo, objid): treeid = checksum_object(repo, objid) checksum_tree(repo, treeid) checksum_repo('.', opts.rev) print("# git-evtag comment: submodules={0} commits={1} ({2}) trees={3} ({4}) blobs={5} ({6})".format(stats['commit']-1, stats['commit'], stats['commitbytes'], stats['tree'], stats['treebytes'], stats['blob'], stats['blobbytes'])) print("Git-EVTag-v0-SHA512: {0}".format(csum.hexdigest())) git-evtag-2016.1/src/git-evtag.c000066400000000000000000000670451266510710000163260ustar00rootroot00000000000000/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- * * Copyright (C) 2015 Colin Walters * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #define EVTAG_SHA512 "Git-EVTag-v0-SHA512:" #define LEGACY_EVTAG_ARCHIVE_TAR "ExtendedVerify-SHA256-archive-tar:" #define LEGACY_EVTAG_ARCHIVE_TAR_GITVERSION "ExtendedVerify-git-version:" struct EvTag; typedef struct { const char *name; gboolean (*fn) (struct EvTag *self, int argc, char **argv, GCancellable *cancellable, GError **error); } Subcommand; #define SUBCOMMANDPROTO(name) static gboolean git_evtag_builtin_ ## name (struct EvTag *self, int argc, char **argv, GCancellable *cancellable, GError **error) SUBCOMMANDPROTO(sign); SUBCOMMANDPROTO(verify); static Subcommand commands[] = { { "sign", git_evtag_builtin_sign }, { "verify", git_evtag_builtin_verify }, { NULL, NULL } }; static gboolean opt_verbose; static gboolean opt_version; static gboolean opt_print_only; static gboolean opt_no_signature; static gboolean opt_with_legacy_archive_tag; static char *opt_keyid; static GOptionEntry global_entries[] = { { "version", 0, 0, G_OPTION_ARG_NONE, &opt_version, "Print version information and exit", NULL }, { NULL } }; static GOptionEntry sign_options[] = { { "print-only", 0, 0, G_OPTION_ARG_NONE, &opt_print_only, "Don't create a tag, just compute and print evtag data", NULL }, { "no-signature", 0, 0, G_OPTION_ARG_NONE, &opt_no_signature, "Do create or verify GPG signature", NULL }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print statistics on what we're hashing", NULL }, { "local-user", 'u', 0, G_OPTION_ARG_STRING, &opt_keyid, "Use the given GPG KEYID", "KEYID" }, { "with-legacy-archive-tag", 'u', 0, G_OPTION_ARG_NONE, &opt_with_legacy_archive_tag, "Also append a legacy variant of the checksum using `git archive`", NULL }, { NULL } }; static GOptionEntry verify_options[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print statistics on what we're hashing", NULL }, { "no-signature", 0, 0, G_OPTION_ARG_NONE, &opt_no_signature, "Do create or verify GPG signature", NULL }, { NULL } }; static gboolean option_context_parse (GOptionContext *context, const GOptionEntry *main_entries, int *argc, char ***argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; if (main_entries != NULL) g_option_context_add_main_entries (context, main_entries, NULL); g_option_context_add_main_entries (context, global_entries, NULL); if (!g_option_context_parse (context, argc, argv, error)) goto out; if (opt_version) { g_print ("%s\n +default\n", PACKAGE_STRING); exit (EXIT_SUCCESS); } ret = TRUE; out: return ret; } static char * debug_oid2str (const git_oid *oid) __attribute ((unused)); static char * debug_oid2str (const git_oid *oid) { static char buf[GIT_OID_HEXSZ]; return git_oid_tostr (buf, sizeof (buf), oid); } static gboolean spawn_sync_require_success (char **argv, GSpawnFlags flags, GError **error) { gboolean ret = FALSE; int estatus; if (!g_spawn_sync (NULL, argv, NULL, flags, NULL, NULL, NULL, NULL, &estatus, error)) goto out; if (!g_spawn_check_exit_status (estatus, error)) goto out; ret = TRUE; out: return ret; } static gboolean handle_libgit_ret (int r, GError **error) { const git_error *giterror; if (!r) return TRUE; giterror = giterr_last(); g_assert (giterror != NULL); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, giterror->message ? giterror->message: "???"); return FALSE; } struct EvTag { git_repository *top_repo; GChecksum *checksum; guint n_submodules; guint n_commits; guint64 commit_bytes; guint n_trees; guint64 tree_bytes; guint n_blobs; guint64 blob_bytes; }; static void checksum_odb_object (struct EvTag *self, git_odb_object *object) { git_otype otype = git_odb_object_type (object); const char *otypestr = git_object_type2string (otype); size_t size = git_odb_object_size (object); char *header; size_t headerlen; header = g_strdup_printf ("%s %" G_GSIZE_FORMAT, otypestr, size); /* Also include the trailing NUL byte */ headerlen = strlen (header) + 1; g_checksum_update (self->checksum, (guint8*)header, headerlen); g_free (header); switch (otype) { case GIT_OBJ_BLOB: self->n_blobs++; self->blob_bytes += size + headerlen; break; case GIT_OBJ_COMMIT: self->n_commits++; self->commit_bytes += size + headerlen; break; case GIT_OBJ_TREE: self->n_trees++; self->tree_bytes += size + headerlen; break; default: g_assert_not_reached (); } g_checksum_update (self->checksum, git_odb_object_data (object), size); } struct TreeWalkData { gboolean caught_error; struct EvTag *evtag; git_repository *repo; git_odb *odb; GCancellable *cancellable; GError **error; }; static gboolean checksum_object_id (struct TreeWalkData *twdata, const git_oid *oid, GError **error) { gboolean ret = FALSE; int r; git_odb_object *odbobj = NULL; r = git_odb_read (&odbobj, twdata->odb, oid); if (!handle_libgit_ret (r, error)) goto out; checksum_odb_object (twdata->evtag, odbobj); ret = TRUE; out: if (odbobj) git_odb_object_free (odbobj); return ret; } static int checksum_submodule (struct TreeWalkData *twdata, git_submodule *sm); static int checksum_tree_callback (const char *root, const git_tree_entry *entry, void *data) { int iter_r = 1; int tmp_r; struct TreeWalkData *twdata = data; git_otype otype = git_tree_entry_type (entry); switch (otype) { case GIT_OBJ_TREE: case GIT_OBJ_BLOB: if (!checksum_object_id (twdata, git_tree_entry_id (entry), twdata->error)) { twdata->caught_error = TRUE; return -1; } break; case GIT_OBJ_COMMIT: { git_submodule *submod = NULL; tmp_r = git_submodule_lookup (&submod, twdata->repo, git_tree_entry_name (entry)); if (!handle_libgit_ret (tmp_r, twdata->error)) goto out; tmp_r = checksum_submodule (twdata, submod); if (tmp_r != 0) goto out; git_submodule_free (submod); } break; default: g_assert_not_reached (); } iter_r = 0; out: if (iter_r > 0) twdata->caught_error = TRUE; return iter_r; } static gboolean checksum_commit_contents (struct TreeWalkData *twdata, const git_oid *commit_oid, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int r; git_commit *commit = NULL; git_tree *tree = NULL; r = git_commit_lookup (&commit, twdata->repo, commit_oid); if (!handle_libgit_ret (r, error)) goto out; if (!checksum_object_id (twdata, commit_oid, error)) goto out; r = git_commit_tree (&tree, commit); if (!handle_libgit_ret (r, error)) goto out; if (!checksum_object_id (twdata, git_object_id((git_object*)tree), error)) goto out; r = git_tree_walk (tree, GIT_TREEWALK_PRE, checksum_tree_callback, twdata); if (twdata->caught_error) goto out; if (!handle_libgit_ret (r, error)) goto out; ret = TRUE; out: if (commit) git_commit_free (commit); if (tree) git_tree_free (tree); return ret; } static int checksum_submodule (struct TreeWalkData *parent_twdata, git_submodule *sub) { int r = 1; const git_oid *sub_head; struct TreeWalkData child_twdata = { FALSE, parent_twdata->evtag, NULL, NULL, parent_twdata->cancellable, parent_twdata->error }; parent_twdata->evtag->n_submodules++; r = git_submodule_open (&child_twdata.repo, sub); if (!handle_libgit_ret (r, child_twdata.error)) goto out; r = git_repository_odb (&child_twdata.odb, child_twdata.repo); if (!handle_libgit_ret (r, child_twdata.error)) goto out; sub_head = git_submodule_wd_id (sub); if (!checksum_commit_contents (&child_twdata, sub_head, child_twdata.cancellable, child_twdata.error)) goto out; r = 0; out: if (r > 0) child_twdata.caught_error = TRUE; if (child_twdata.repo) git_repository_free (child_twdata.repo); if (child_twdata.odb) git_odb_free (child_twdata.odb); return r; } static gboolean verify_line (const char *expected_checksum, const char *line, const char *rev, GError **error) { gboolean ret = FALSE; const char *provided_checksum; g_assert (g_str_has_prefix (line, EVTAG_SHA512)); provided_checksum = line + strlen (EVTAG_SHA512); provided_checksum += strspn (provided_checksum, " \t"); if (strcmp (provided_checksum, expected_checksum) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid %s, actual checksum of %s is %s", line, rev, expected_checksum); goto out; } ret = TRUE; out: return ret; } static char * get_stats (struct EvTag *self) { return g_strdup_printf ("# git-evtag comment: submodules=%u " "commits=%u (%" G_GUINT64_FORMAT ") " "trees=%u (%" G_GUINT64_FORMAT ") " "blobs=%u (%" G_GUINT64_FORMAT ")", self->n_submodules, self->n_commits, self->commit_bytes, self->n_trees, self->tree_bytes, self->n_blobs, self->blob_bytes); } static gboolean check_file_has_evtag (const char *path, gboolean *out_have_evtag, GError **error) { gboolean ret = FALSE; char *message = NULL; const char *p; const char *nl; gboolean found = FALSE; if (!g_file_get_contents (path, &message, NULL, error)) goto out; p = message; while (TRUE) { nl = strchr (p, '\n'); if (g_str_has_prefix (p, EVTAG_SHA512)) { found = TRUE; break; } if (!nl) break; p = nl + 1; } ret = TRUE; *out_have_evtag = found; out: g_free (message); return ret; } static int status_cb (const char *path, unsigned int status_flags, void *payload) { int r = 1; struct TreeWalkData *twdata = payload; if (status_flags != 0) { g_assert (!twdata->caught_error); twdata->caught_error = TRUE; g_set_error (twdata->error, G_IO_ERROR, G_IO_ERROR_FAILED, "Attempting to tag or verify dirty tree (%s); use --force-unclean to override", path); goto out; } r = 0; out: return r; } static gboolean compute_and_append_legacy_archive_checksum (const char *commit, GString *buf, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *archive_argv[] = {"git", "archive", "--format=tar", commit, NULL}; GSubprocess *gitarchive_proc = NULL; GInputStream *gitarchive_output = NULL; guint64 legacy_checksum_start; guint64 legacy_checksum_end; GChecksum *legacy_archive_sha256 = g_checksum_new (G_CHECKSUM_SHA256); gssize bytes_read; char readbuf[4096]; char *gitversion = NULL; int estatus; char *nl; legacy_checksum_start = g_get_monotonic_time (); gitarchive_proc = g_subprocess_newv (archive_argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE, error); if (!gitarchive_proc) goto out; gitarchive_output = g_subprocess_get_stdout_pipe (gitarchive_proc); while ((bytes_read = g_input_stream_read (gitarchive_output, readbuf, sizeof (readbuf), cancellable, error)) > 0) g_checksum_update (legacy_archive_sha256, (guint8*)readbuf, bytes_read); if (bytes_read < 0) goto out; legacy_checksum_end = g_get_monotonic_time (); g_string_append_printf (buf, "# git-evtag comment: Computed legacy checksum in %0.1fs\n", (double)(legacy_checksum_end - legacy_checksum_start) / (double) G_USEC_PER_SEC); g_string_append (buf, LEGACY_EVTAG_ARCHIVE_TAR); g_string_append_c (buf, ' '); g_string_append (buf, g_checksum_get_string (legacy_archive_sha256)); g_string_append_c (buf, '\n'); if (!g_spawn_command_line_sync ("git --version", &gitversion, NULL, &estatus, error)) goto out; if (!g_spawn_check_exit_status (estatus, error)) goto out; nl = strchr (gitversion, '\n'); if (!nl) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "git --version returned invalid content without a newline"); goto out; } *nl = '\0'; g_strchomp (gitversion); g_string_append (buf, LEGACY_EVTAG_ARCHIVE_TAR_GITVERSION); g_string_append_c (buf, ' '); g_string_append (buf, gitversion); g_string_append_c (buf, '\n'); ret = TRUE; out: return ret; } static gboolean validate_at_head (struct EvTag *self, git_oid *specified_oid, GError **error) { gboolean ret = FALSE; git_oid head_oid; int r; r = git_reference_name_to_id (&head_oid, self->top_repo, "HEAD"); if (!handle_libgit_ret (r, error)) goto out; /* We have this restriction due to submodules; we require them to be * checked out and initialized. */ if (git_oid_cmp (&head_oid, specified_oid) != 0) { char head_oid_hexstr[GIT_OID_HEXSZ+1]; char specified_oid_hexstr[GIT_OID_HEXSZ+1]; g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Target %s is not HEAD (%s); currently git-evtag can only tag or verify HEAD in a pristine checkout", git_oid_tostr (specified_oid_hexstr, sizeof (specified_oid_hexstr), specified_oid), git_oid_tostr (head_oid_hexstr, sizeof (head_oid_hexstr), &head_oid)); goto out; } ret = TRUE; out: return ret; } static gboolean checksum_commit_recurse (struct EvTag *self, git_oid *specified_oid, guint64 *out_elapsed_time, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int r; guint64 checksum_start_time; guint64 checksum_end_time; checksum_start_time = g_get_monotonic_time (); { struct TreeWalkData twdata = { FALSE, self, self->top_repo, NULL, cancellable, error }; r = git_repository_odb (&twdata.odb, self->top_repo); if (!handle_libgit_ret (r, error)) goto out; if (!checksum_commit_contents (&twdata, specified_oid, cancellable, error)) goto out; } checksum_end_time = g_get_monotonic_time (); ret = TRUE; if (out_elapsed_time) *out_elapsed_time = checksum_end_time - checksum_start_time; out: return ret; } static gboolean git_evtag_builtin_sign (struct EvTag *self, int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int r; const char *tagname; git_object *obj = NULL; git_oid specified_oid; GOptionContext *optcontext; guint64 elapsed_ns; char commit_oid_hexstr[GIT_OID_HEXSZ+1]; optcontext = g_option_context_new ("TAGNAME - Create a new GPG signed tag"); if (!option_context_parse (optcontext, sign_options, &argc, &argv, cancellable, error)) goto out; if (argc < 2) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A TAGNAME argument is required"); goto out; } else if (argc > 2) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Too many arguments"); goto out; } tagname = argv[1]; r = git_revparse_single (&obj, self->top_repo, "HEAD"); if (!handle_libgit_ret (r, error)) goto out; specified_oid = *git_object_id (obj); git_oid_fmt (commit_oid_hexstr, &specified_oid); commit_oid_hexstr[sizeof(commit_oid_hexstr)-1] = '\0'; if (!validate_at_head (self, &specified_oid, error)) goto out; if (!checksum_commit_recurse (self, &specified_oid, &elapsed_ns, cancellable, error)) goto out; if (opt_print_only) { char *stats = get_stats (self); g_print ("%s\n", stats); g_free (stats); g_print ("%s %s\n", EVTAG_SHA512, g_checksum_get_string (self->checksum)); } else { const char *editor; int tmpfd; char *temppath; char *editor_child_argv[] = { NULL, NULL, NULL }; GPtrArray *gittag_child_argv = g_ptr_array_new (); GString *buf = g_string_new ("\n\n"); gboolean have_evtag; tmpfd = g_file_open_tmp ("git-evtag-XXXXXX.md", &temppath, error); if (tmpfd < 0) goto out; (void) close (tmpfd); g_string_append_printf (buf, "# git-evtag comment: Computed checksum in %0.1fs\n", (double)(elapsed_ns) / (double) G_USEC_PER_SEC); { char *stats = get_stats (self); g_string_append (buf, stats); g_string_append_c (buf, '\n'); g_free (stats); } g_string_append (buf, EVTAG_SHA512); g_string_append_c (buf, ' '); g_string_append (buf, g_checksum_get_string (self->checksum)); g_string_append_c (buf, '\n'); if (opt_with_legacy_archive_tag) { if (!compute_and_append_legacy_archive_checksum (commit_oid_hexstr, buf, cancellable, error)) goto out; } if (!g_file_set_contents (temppath, buf->str, -1, error)) goto out; g_string_free (buf, TRUE); editor = getenv ("EDITOR"); if (!editor) editor = "vi"; editor_child_argv[0] = (char*)editor; editor_child_argv[1] = (char*)temppath; if (!spawn_sync_require_success (editor_child_argv, G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, error)) goto out; if (!check_file_has_evtag (temppath, &have_evtag, error)) goto out; if (!have_evtag) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Aborting tag due to deleted Git-EVTag line"); goto out; } g_ptr_array_add (gittag_child_argv, "git"); g_ptr_array_add (gittag_child_argv, "tag"); if (!opt_no_signature) g_ptr_array_add (gittag_child_argv, "-s"); if (opt_keyid) { g_ptr_array_add (gittag_child_argv, "--local-user"); g_ptr_array_add (gittag_child_argv, opt_keyid); } g_ptr_array_add (gittag_child_argv, "-F"); g_ptr_array_add (gittag_child_argv, temppath); g_ptr_array_add (gittag_child_argv, (char*)tagname); g_ptr_array_add (gittag_child_argv, (char*)commit_oid_hexstr); g_ptr_array_add (gittag_child_argv, NULL); if (!spawn_sync_require_success ((char**)gittag_child_argv->pdata, G_SPAWN_SEARCH_PATH, error)) { g_printerr ("Saved tag message in: %s\n", temppath); goto out; } (void) unlink (temppath); g_ptr_array_free (gittag_child_argv, TRUE); } ret = TRUE; out: return ret; } static gboolean git_evtag_builtin_verify (struct EvTag *self, int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; int r; gboolean verified = FALSE; GOptionContext *optcontext; git_oid tag_oid; git_object *obj = NULL; char *long_tagname = NULL; git_tag *tag; const char *tagname; const char *message; git_oid specified_oid; const char *nl; guint64 elapsed_ns; const char *expected_checksum; char commit_oid_hexstr[GIT_OID_HEXSZ+1]; char tag_oid_hexstr[GIT_OID_HEXSZ+1]; char *git_verify_tag_argv[] = {"git", "verify-tag", NULL, NULL }; optcontext = g_option_context_new ("TAGNAME - Verify a signed tag"); if (!option_context_parse (optcontext, verify_options, &argc, &argv, cancellable, error)) goto out; if (argc < 2) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A TAGNAME argument is required"); goto out; } else if (argc > 2) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Too many arguments"); goto out; } tagname = argv[1]; long_tagname = g_strconcat ("refs/tags/", tagname, NULL); r = git_reference_name_to_id (&tag_oid, self->top_repo, long_tagname); if (!handle_libgit_ret (r, error)) goto out; r = git_tag_lookup (&tag, self->top_repo, &tag_oid); if (!handle_libgit_ret (r, error)) goto out; r = git_tag_target (&obj, tag); if (!handle_libgit_ret (r, error)) goto out; specified_oid = *git_object_id (obj); git_oid_fmt (commit_oid_hexstr, &specified_oid); commit_oid_hexstr[sizeof(commit_oid_hexstr)-1] = '\0'; if (!validate_at_head (self, &specified_oid, error)) goto out; message = git_tag_message (tag); if (!git_oid_tostr (tag_oid_hexstr, sizeof (tag_oid_hexstr), git_tag_id (tag))) g_assert_not_reached (); if (!opt_no_signature) { git_verify_tag_argv[2] = tag_oid_hexstr; if (!spawn_sync_require_success (git_verify_tag_argv, G_SPAWN_SEARCH_PATH, error)) goto out; } if (!checksum_commit_recurse (self, &specified_oid, &elapsed_ns, cancellable, error)) goto out; expected_checksum = g_checksum_get_string (self->checksum); while (TRUE) { nl = strchr (message, '\n'); if (g_str_has_prefix (message, EVTAG_SHA512)) { char *line; if (nl) line = g_strndup (message, nl - message); else line = g_strdup (message); g_strchomp (line); if (!verify_line (expected_checksum, line, commit_oid_hexstr, error)) goto out; { char *stats = get_stats (self); g_print ("%s\n", stats); g_free (stats); } g_print ("Successfully verified: %s\n", line); verified = TRUE; break; } if (!nl) break; message = nl + 1; } if (!verified) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to find %s in tag message", EVTAG_SHA512); goto out; } ret = TRUE; out: return ret; } static GOptionContext * option_context_new_with_commands (Subcommand *commands) { GOptionContext *context; GString *summary; context = g_option_context_new ("COMMAND"); summary = g_string_new ("Builtin Commands:"); while (commands->name != NULL) { g_string_append_printf (summary, "\n %s", commands->name); commands++; } g_option_context_set_summary (context, summary->str); g_string_free (summary, TRUE); return context; } static gboolean submain (struct EvTag *self, int argc, char **argv, GError **error) { gboolean ret = FALSE; git_status_options statusopts = GIT_STATUS_OPTIONS_INIT; GCancellable *cancellable = NULL; const char *command_name = NULL; Subcommand *command; char *prgname = NULL; int in, out; int r; /* * Parse the global options. We rearrange the options as * necessary, in order to pass relevant options through * to the commands, but also have them take effect globally. */ for (in = 1, out = 1; in < argc; in++, out++) { /* The non-option is the command, take it out of the arguments */ if (argv[in][0] != '-') { if (command_name == NULL) { command_name = argv[in]; out--; continue; } } else if (g_str_equal (argv[in], "--")) { break; } argv[out] = argv[in]; } argc = out; command = commands; while (command->name) { if (g_strcmp0 (command_name, command->name) == 0) break; command++; } if (!command->fn) { GOptionContext *context; char *help; context = option_context_new_with_commands (commands); /* This will not return for some options (e.g. --version). */ if (option_context_parse (context, NULL, &argc, &argv, cancellable, error)) { if (command_name == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No command specified"); } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown command '%s'", command_name); } } help = g_option_context_get_help (context, FALSE, NULL); g_printerr ("%s", help); g_option_context_free (context); goto out; } prgname = g_strdup_printf ("%s %s", g_get_prgname (), command_name); g_set_prgname (prgname); r = git_repository_open_ext (&self->top_repo, ".", 0, NULL); if (!handle_libgit_ret (r, error)) goto out; r = git_status_init_options (&statusopts, GIT_STATUS_OPTIONS_VERSION); if (!handle_libgit_ret (r, error)) goto out; { struct TreeWalkData twdata = { FALSE, self, self->top_repo, NULL, cancellable, error }; r = git_status_foreach_ext (self->top_repo, &statusopts, status_cb, &twdata); if (twdata.caught_error) goto out; if (!handle_libgit_ret (r, error)) goto out; } self->checksum = g_checksum_new (G_CHECKSUM_SHA512); if (!command->fn (self, argc, argv, cancellable, error)) goto out; ret = TRUE; out: return ret; } int main (int argc, char **argv) { GError *local_error = NULL; GError **error = &local_error; struct EvTag self = { NULL, }; /* avoid gvfs (http://bugzilla.gnome.org/show_bug.cgi?id=526454) */ g_setenv ("GIO_USE_VFS", "local", TRUE); #ifdef HAVE_GIT_LIBGIT2_INIT git_libgit2_init (); #else git_threads_init (); #endif if (!submain (&self, argc, argv, error)) goto out; out: if (self.top_repo) git_repository_free (self.top_repo); if (self.checksum) g_checksum_free (self.checksum); if (local_error) { int is_tty = isatty (1); const char *prefix = ""; const char *suffix = ""; if (is_tty) { prefix = "\x1b[31m\x1b[1m"; /* red, bold */ suffix = "\x1b[22m\x1b[0m"; /* bold off, color reset */ } g_printerr ("%serror: %s%s\n", prefix, suffix, local_error->message); g_error_free (local_error); return 1; } return 0; } git-evtag-2016.1/tests/000077500000000000000000000000001266510710000146325ustar00rootroot00000000000000git-evtag-2016.1/tests/Makefile-tests.am000066400000000000000000000035271266510710000200350ustar00rootroot00000000000000# Makefile for tests code # # Copyright (C) 2013,2015 Colin Walters # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. if BUILDOPT_INSTALL_TESTS insttestdir=$(pkglibexecdir)/installed-tests testfiles = test-basic \ $(NULL) insttest_SCRIPTS = $(addprefix tests/,$(testfiles:=.sh)) testmetadir = $(datadir)/installed-tests/$(PACKAGE) testmeta_DATA = $(testfiles:=.test) insttest_DATA = \ tests/libtest.sh \ $(NULL) insttest_SCRIPTS += src/git-evtag-compute-py gpginsttestdir = $(pkglibexecdir)/installed-tests/gpghome gpginsttest_DATA = tests/gpghome/secring.gpg \ tests/gpghome/trustdb.gpg \ tests/gpghome/key1.asc \ tests/gpghome/key2.asc \ tests/gpghome/key3.asc gpginsttest_trusteddir = $(pkglibexecdir)/installed-tests/gpghome/trusted gpginsttest_trusted_DATA = tests/gpghome/trusted/pubring.gpg install-gpg-data-hook: ln -sf trusted/pubring.gpg $(DESTDIR)$(gpginsttestdir)/pubring.gpg INSTALL_DATA_HOOKS += install-gpg-data-hook %.test: tests/%.sh Makefile $(AM_V_GEN) (echo '[Test]' > $@.tmp; \ echo 'Exec=$(pkglibexecdir)/installed-tests/$(notdir $<)' >> $@.tmp; \ echo 'Type=session' >> $@.tmp; \ echo 'Output=TAP' >> $@.tmp; \ mv $@.tmp $@) endif git-evtag-2016.1/tests/gpghome/000077500000000000000000000000001266510710000162605ustar00rootroot00000000000000git-evtag-2016.1/tests/gpghome/key1.asc000066400000000000000000000032421266510710000176220ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 mQENBFIuhBYBCADTbnocQsJgMfOELkFt3wRrAZShijoBPYZT9BrIuIKZxAbaxZJr Tbw8eIGgHZ51NCfdoikul0i82dt4hwtsACNVL5EGRmvTIKHPacb0yJMr1YBjcSwD Slo+niLPb/oVtLTbDWFt/msYKREF/lGJT9dJyXkQ5UOwWdipDaHIlwb0IKUvL7cu NpNthRFRm1M5d5M9OtqTCrCja6zckQ6OfvoStsbneHzfVWeH7vLcKBxxkfDhusVt y1iVaDk1EYT8ZxsrAWw4S7nRK/bjr86IYpFPjG2aKMd9qxyIo7hcX4r8od24jzfM v/ysOapnkTJuv8J6v7MakM1HkCz+TKF6gXxVABEBAAG0HU9zdHJlZSBUZXN0ZXIg PHRlc3RAdGVzdC5jb20+iQE5BBMBAgAjBQJSLoQWAhsDBwsJCAcDAgEGFQgCCQoL BBYCAwECHgECF4AACgkQf8oj2Ecs2vr/9wgAnme6WsWQy8CYeGH4q/5I6XFL6q1m S0+qdeGnYRmR0jJAGJ84vqDhnKxjeQzp+8Nq81DHGEJBszCkMW2o22neFi2Mo95h Dq3GWNZVldCDshjPs563AY6j7zACUN7Cy5XB3MK/vj5R/SrHBtJmSgPTx9WfmUgn n5Udg+fzSsS8z8DUtJFtexgrSnEmwH+nOmIfrsjIYL5EPg+CTTalhygROrERjINr pCYiShaFCKbuyt/XvyQ71y0JbB2yS7tDv0mL4SZjSuBQ1PkNE8ZQsymqBOJHA1Y3 ppgPs1OenmtYgxaR8HQQv7uxHWZz0dmwQN93Qx8zMZwW40Odmdh1zLNQf7kBDQRS LoQWAQgA9i9QWg28qmFrPIzn90ZlNlUtFzoZy/8/lIk34awge1uO5aHydYBzkuWU jCDyBtQLWZQlwOKq8oHBbjENR2sfsmNkrYKcceQ02hSXqEJkc6jcDMCpB9eWy34K sPZmdl76Eo/vIIgRqJ9JPeGoMPaIBg2ouEz6Ft6jcX3EriYIKebCEA9wPk29z40x 7D8mBZn06WrZ3JyePfbCdNJlQANEnrk7KDMNwPhhE1wcfPkiVtqBR0/FwIoUP0jn PishIWOuFObYnXQQ2R8sxrw/V0hGqVTh+k+iNAjzEp4yPsAvB+LdMH9nCY5rU3Vo 1paEqVM1EHoBPu4NupRN0AjIJPr5UQARAQABiQEfBBgBAgAJBQJSLoQWAhsMAAoJ EH/KI9hHLNr68i4IAMdc+EgAaPZFWZcXFGBfhnOKQFC/u/W6Cu1JjqIYkGO0HxSh SfBkxArqlp37w4YVH4bUku6ja421bfGFNMtMfXjw2mU3HRdaDenP6OGv2jYmYmFt 6zi0JZZhvi8ZCcAQTStZ2Ms3hwstCMiBXPmYA7KW9Gzo4JQSKCW5haICGVSWl7kh n0OkhOTVI9uzNr7+LhYn2ib/ynSaMKeI4hZ8v1HDuY0V1E63vFPGLFBTPaoRPpnm 9yBnXMWhrbV97L6eEoe7faurSyPcF11LEFC5x8oENnbH+wtAXOayQo3lld+JRa9C JEZl8STdRU9o2NFwF8XM8BEOWntMS8aNpPoILC8= =ZNNc -----END PGP PUBLIC KEY BLOCK----- git-evtag-2016.1/tests/gpghome/key2.asc000066400000000000000000000032461266510710000176270ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 mQENBFUIM4ABCADYBuvzGgzv5nMy2wICv79l+N4bN9/o9hTdFEOzyAeCEaF5Wugc L9nfTgUS9NRHsSpGt9DeZVEzRm5XzccgHOPs7MlYH0Irhc4Hb9ycOO2vBZ7ZiBK/ jbY+R5GN4Ut9XIRexbXWddOjJpRUTCWQeXw5iqV9Puqm1ge2Vcal+NZOi2AFRKKe p/QI9EXIIx7ca6OWtH2SS/qE2p9obDYsMNrW+Dk623dbNKQiWaWyfRD+hB91UNbt vK7agokTeU0hKr9C8dHrhepgl9B/Hz8SFibZQQiTxSiVH3fUu10eQsyuDC/01KHp z0MR28Lc4VlCs6dsJBmGMBayHHVzbyXgw6uZABEBAAG0IU9zdHJlZSBUZXN0ZXIg SUkgPHRlc3QyQHRlc3QuY29tPokBOAQTAQIAIgUCVQgzgAIbAwYLCQgHAwIGFQgC CQoLBBYCAwECHgECF4AACgkQ2CKM/sqVDUFpJAgAirtYbbkvnlKtBxDsCu+A6qyl 7r+cW8IH5U1P4MqxqQwkAe1ZalfjuTSHVKYqt/K6gt0+4NvCee3A2JxXTvLq1hdR DNMUFAjkbZv3Y6VS8Qtj3edsviNEB7s8uyWgR/EBB312YSZCwzk5uSLzM5E7AmvM 0/ZPIAAxjz8TpQKc8vJx1/4nqgt0Wjv5B74vuOQJT26zoFygCQM76YeN+ULzk/hN hW7aNp/S2STasvEv7NgwqAe6eWy45xTrvxhEhQV760/toLbI0DuuBGr6Ue/G+Id4 P/R604HAbMg3GdCztyoD3WTuvcsY6oXD7GlSEX4DZ0LA2TqQDZB+Pqe2yF7Gi7kB DQRVCDOAAQgAsxH2E9JeQPbcdXGyxLCa4FyMeziCrxn7tOEsRkeqZmb76mAOn67Q ZuZ6SXcAQDjKOBu7QNEcFQ+bAW/urohzD/sjr09vKqibLh6v8t81DE79GHI1UZ7F SuYDLgcGUvOCJej8iftJcudWuzCW9SvoykNcgPcIOYEXbbJVRr2xvK4z2a34DyWL jEXU2r6g1KNwtGyT6hZ7Ax99MKAzCFX3to0V51EXcrnUojwz/8i4Lal9t3d4P3lu FZw3ITWh4e9zNxp8aSxsAN/vQ1EHccMPrNmO+d+yjhH7inTxf+vLErP3Cs5rPjxl FgoLvBCSCT0jQ0xP+8Aa2TDyXDcMHg85QwARAQABiQEfBBgBAgAJBQJVCDOAAhsM AAoJENgijP7KlQ1Bc68H/RBn5PpUe8CA1CJ8eN4LIfRee3DjacwvjGsdgiMzcLhb Hp/ke42kentYjT+gF1ABPbeUERDlhnZ8BguKGZV+jOGDWRI2KFrQXL444aNznjn8 aTOQY/d4LibwSaQ3qzf4Zp4CyZq2X2Vg3+B3HoUM6pkIL/r2ao5TnFqKubCE3sEo St+LV4eHktoAS1GXmxYKo4Q67yMVekTZt7C/VQ2a20qfAXBn2U8UA3tUvNqKtyyJ XrxeTJ+T4MMv60zdC/B/UPNjjHLNyB6culIzyiYFglGw3ctx2erJN2d+aQLrw78E vIuMy+JcH6y3JlzVGdByWbC7628OcWWa+NL/CXnYMyQ= =j9RC -----END PGP PUBLIC KEY BLOCK----- git-evtag-2016.1/tests/gpghome/key3.asc000066400000000000000000000032461266510710000176300ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 mQENBFUIM6EBCACr57QUYEEuxvkX20yM1LVt2jyYZRKKQsqXx/xCF+Pg1MNz6mYx Qz6R6+yZZmlADsfRdnEpRvl4Dq2g3cP0DqkjnIKwI7ffEsyXlves8OMlpwT2Vh4x 8Lx92eIEeqmb+PT8m88+x+EPVaR2R5KBHFkGXGyVgw+Ry8Oa9ZtJEKSkL/EQvzWv 5q+OR1Pm8rnIPe64XPh3yAx5SBJ2m7hykH/XMVrdGqaZvpuGBx77pmmqfMMjNWMC U09hURyuyGWUsj9lFWYgpBvAzASmJNpAf7FZTjzCwLJwqpxCYm6a3sp76yyjuY7q vgJOolRHp9F/XETsSLdy6966oBxclGNaD6gnABEBAAG0Ik9zdHJlZSBUZXN0ZXIg SUlJIDx0ZXN0M0B0ZXN0LmNvbT6JATgEEwECACIFAlUIM6ECGwMGCwkIBwMCBhUI AgkKCwQWAgMBAh4BAheAAAoJEA0V+uffRE1n0n4H/0Z8bC1cdr5cMFZ6YBaJlw4c b61krhan2qCrwQupwaXi6LHt0zMwgljOcN+X2sAlZj9Jv0CabU5S1vM1fh9DZ6OY 2OQ/Pq2lXGk22JjrbPPq5o//xTzo92Uxptuxq6O4frVzuGCo1yPlrHJh+TxbXIc8 XOz9C9KTfcb9OwidSSW5LlUBzQ2e3oQLSUQPsdB3TZP5zlqPIYerWn+LdETKcOTr JyoaobFqX2BN223d3vkA1/GcuB17eBnzbnS0OWLJH+E3bsCqjtCJMEc1uTq97tyF XStIk9i0gVbA+GiK/ZFMt+a5kagR5dOUwpNZ0BE+Kzf0CtkSaSWkAh1vQV/j2E65 AQ0EVQgzoQEIAMeXa6sp5kdmJn/fVw0Pk5oluBXif7BiFt+T7K03RxCOKRpne6dI SS98ruwZ1B5hn0lZO0UiL5RKpBQUrI9Y7251tz+oWohU2ZkUwwP3OcBlTXtErhe0 LctcJ1nUA5NICVP5brhJR94durULiM+Rrhr12Ccs+a9bV268btNLN51z7ICMwNI6 xuNxLt9orVJwP82a2eelQOgkPyFpiq7UxZ1erJg4aBVfWHP+rlxyQlzawVebbQMO gwYW+gAawTxd2x7PV9CC3KsaM+HI6wBvDOtcWlbzo+TxzcVocd5oern4Mr5Y86Gt lajuO9DVsuxxIfBrvHdRut613ShhOVlfy2MAEQEAAYkBHwQYAQIACQUCVQgzoQIb DAAKCRANFfrn30RNZyPVB/9jNFOjcNCAZSrz9vylaO0xHsPhIn4osmkiU6BvodwO n+qR4eEUw7BzoWC5QqGxUPYuDneQK7N7U31SFYjmY5Y1CDMsFtcYzjPgN5qWhtaN iNTtE9pb5f97PyLSUwcdW1y/cfDfqoAY6rpRXieo7hJv1xBtlEzJIbSSTS1SUEd1 4qwPCqNWMSM6qBcaFB5Yuw0Z/E6B1JfNTUw5J5jDxbGdOzkLx2mXCldte1axq9Lp 1V17jMTvn7Ml1QdoEAqzvt4VNQci/Su/qd3XjQ46b4dFFP03+jJv7mO6tHka2luZ RX0zfsk5q8wqbtV2k0XZFRD+22ddKMf1j4wID157lNQs =Dq+d -----END PGP PUBLIC KEY BLOCK----- git-evtag-2016.1/tests/gpghome/secring.gpg000066400000000000000000000164701266510710000204210ustar00rootroot00000000000000R.nzB`1.Amk:=SȸŒkM"o amk)QOIyCYة ȗ //.6mQS9w=:ړ kܑ~x|Ug(qmXh95g+l8K+ΈbOm(}\_ݸ7̿9g2nzG,Lz|UC$.ݛ7l&/$C\sG.YRz"J滄z[~ =+ʲ3ĆQ/-leSt쪽ٛnTm( .▤ﵡ2J=tG@0?#ӌ%|7+NUBu\qb޼Ne勐9W]0Ө`\%#9YADc&<4A""AہkMhor02'"U{f)b`Rt񾴯=-Q,ǣ,܋ tF9Z*7]Cip9#R.   #G,gZŐxaHqKfKOua2@8ᜬcy jPBA01mi-aXUЃϳ0P˕¿>Q*fJ՟H'JļԴm{+Jq&:b`D>M6(:k&"J׿$;- lKCI&cJP P)GV7SkXtfsٰ@wC31Cu̳PR./PZ akMύ1?&jܜ=te@D;(3 a\|"VځGO?H>+!!c؝t,Ƽ?WHFTO42>/0g kSuh֖S5z> M$Qρ% e=_ޯ ')b"8[RPkK21KTG8okռ긗TRaJ_5F{]#X@=-ˋOHbdP7pwXmk=XW!&)ә/ n*=+ø(QNyBgKJ^.1mVƥ>TwԘƲ\~ 24Ӣzu'_RbJ!9@eX#breMy8%Um:%!Hqvφ)CO\ʲ}[0s'\f 1*~Qk5m.eǞH,B g\š}쾞}K#]KP6v @\B߉EB$Fe$EOhpZ{LKƍ,/U3 s2e7CyZ/NG*FeQ3FnW XB+oܜ8و>GK}\^ŵuӣ&TL%y|9}>UƥN`DE#k}Kڟhl6,09:w[4"Y}uPڂyM!*B`?&A(wԻ]B̮ /ԡCYBl$0uso%ëJÞ~l$~Ȣ~`(/!@x,VS*á|--7`o9q(Q5폠I`1ݤлʌӔ[].m[n վ8_85Y:J3hq8;'D7>d/u -` |Ӷ?`R7M~hƊHNG %,6fcEkQQEџ\i<O߷"5{ VZ**c/hɋ6Ӎk '[ޥ5!{e ,($Z $%?W6]{Qm@t^՛KUD.qsX΂C-j )GWU(7~49ߺbB`, CA Q;yuޱAbUdUtx@ɺGYi[SRB7uLB08EIi~a/Iw,;M(4@O6$)Rdg׳~k /fW3Za2_ Gu˖;0p6J"GtTe6f:c/QEX[n1Lɻ.;}KՓƟ(N@(|}jElSWȾm;!Ostree Tester II 8"U3   "ʕ Ai$Xm/R ꬥ[MOʱ $YjW4T*>y؜WNQ mcR cl#D<%G}va&B99"3;kO 1?q' tZ;/ On\ ;重BMn6$ڲ/0ylD{O;jQx?zӁl7г*diR~gB: ~>^ƋU3^@uqİ\{8,FGff`fzIw@88@os#Oo*.5 Nr5QJ.R%IrV0+C\9mUF3٭%Eھԣpl{}03UQrԢ<3ȸ-}wx?yn7!5s7|i,lCQqَ߲t k>n{Oݲ< 1džE@sk-n[2Hs8VW4!{Ձޛz669Kvd^Իcͧgz *|seo4HtN؞,B)HHh[NQG)NyRb>CfPғ0A.YցnD"\Md<)2, :lG!bȒK|> ӿ]bx4?AwJX7΄֎ &BUx-^ʨWr@;ﺏ$$R?KBڞZgsWhUyτ;>RY!LԱO*ɥKܘ t TNdR7 ৺~т^tˠ=yP7%҈ؚմ`,8OcV'XBNAs͛kYdLMќQLKI U3 "ʕ AsgT{"|x !^{pi/k#3p[{z{X?P=v| ~Y6(Z\8s9i3cx.&I77fɚ_e`w /jSZ(JߋWKQ :#zDٷU JpgO{Tڊ,^^L/L PcrR3&Qq7g~iÿ\&\rYoqe y3$U3`A.LԵmfi@vq)Fx##̗%V1}z>UvGY\lÚI/5毎GS=\w yHvr1Zi|#5cSOaQe?ef $@YNcTP,aP=eӌd`ˤuD >6d^S6e.0Yۄ\`*WK AybDBֹ9JkQFjhTEm2TS1ei5q4'[DkzEx%! fSTN`! fp@' UP2ּ4N!ޠ\$-(!Pa/Z1agaf eH(m?3-s(~cfw <;d"Ostree Tester III 8"U3    DMg~F|l-\v\0Vz`odڠ 30Xpߗ%f?I@mNR5~Cg?>\i6ؘl+7 i%oA_NU3Ǘk)Gf&W %bߓ7G)g{HI/|aIY;E"/JXnu?ZTٙ9eM{D-\'YH SnIG ϑ',[WnnK7s쀌:q.hRp?͚@$?!iŝ^8h_Xs\rB\Wm<]WЂܫ3o \ZVhqhz2X󡭕;ղq!kwQ޵(a9Y_c?}PU+mck3tD>lh(wCf+qؔJ o_XD:{飿 /@,Х&'b$3WaqW7~X΢D&(3'@iIrT\`1!߾mq2Y>Թ)4ľbЖ1Zz'Mmx&8%ΜI1f`7Ehԙgf'2b5=rQ4EA*ijGsQwnM/3ohwyט8QsDdT֑>:UM~UgRBGt޵y;`G1P`IIӋ c1υV9l Ghqn>< RsMHE5Z6)Nto712Sj8[&{zBØL`J 4LXwB#m?2 ̨/A`I؂ɲ4H#W+TfKJ<rPc dhh]s!2JaDV:W|=~H`1l( ͤ8{ۘ9߆b2!t%Ӫߝ7Tx Os(L(`2GiiB4 U3  DMg#c4SpЀe*h1"~(i"Soðs`BP.w+{S}Rc53,37֍[{?"S[\qߪQ^'omL!M-RPGu V1#:X NԗML9'ű;9 i Wm{V]{%h 5"+׍:oE72ocy[E}3~9*nvEg](^{,git-evtag-2016.1/tests/gpghome/trustdb.gpg000066400000000000000000000026401266510710000204500ustar00rootroot00000000000000gpgU3  " ^euPbv4#G, uv@*K<| {; Dyh"s"ʕ A! [W"o amk)QOIyCYة ȗ //.6mQS9w=:ړ kܑ~x|Ug(qmXh95g+l8K+ΈbOm(}\_ݸ7̿9g2nzG,Lz|UOstree Tester 9#R.   #G,gZŐxaHqKfKOua2@8ᜬcy jPBA01mi-aXUЃϳ0P˕¿>Q*fJ՟H'JļԴm{+Jq&:b`D>M6(:k&"J׿$;- lKCI&cJP P)GV7SkXtfsٰ@wC31Cu̳P R./PZ akMύ1?&jܜ=te@D;(3 a\|"VځGO?H>+!!c؝t,Ƽ?WHFTO42>/0g kSuh֖S5z> M$Q R. #G,.\HhEY`_s@P IcId ꖝÆԒkm4L}xe7Z 6&bam8%a/ M+Y7 -ȁ\l(%T!C#۳6.'&t0|QùNS,PS=> g\š}쾞}K#]KP6v @\B߉EB$Fe$EOhpZ{LKƍ,/ U3 s2e7CyZ/NG*FeQ3FnW XB+oܜ8و>GK}\^ŵuӣ&TL%y|9}>UƥN`DE#k}Kڟhl6,09:w[4"Y}uPڂyM!*B`?&A(wԻ]B̮ /ԡCYBl$0uso%ë!Ostree Tester II 8"U3   "ʕ Ai$Xm/R ꬥ[MOʱ $YjW4T*>y؜WNQ mcR cl#D<%G}va&B99"3;kO 1?q' tZ;/ On\ ;重BMn6$ڲ/0ylD{O;jQx?zӁl7г*diR~gB: ~>^Ƌ U3^@uqİ\{8,FGff`fzIw@88@os#Oo*.5 Nr5QJ.R%IrV0+C\9mUF3٭%Eھԣpl{}03UQrԢ<3ȸ-}wx?yn7!5s7|i,lCQqَ߲t k>fi@vq)Fx##̗%V1}z>UvGY\lÚI/5毎GS=\w yHvr1Zi|#5cSOaQe?ef $@YN8"U3    DMg~F|l-\v\0Vz`odڠ 30Xpߗ%f?I@mNR5~Cg?>\i6ؘl+7 i%oA_N U3Ǘk)Gf&W %bߓ7G)g{HI/|aIY;E"/JXnu?ZTٙ9eM{D-\'YH SnIG ϑ',[WnnK7s쀌:q.hRp?͚@$?!iŝ^8h_Xs\rB\Wm<]WЂܫ3o \ZVhqhz2X󡭕;ղq!kwQ޵(a9Y_c U3  DMg#c4SpЀe*h1"~(i"Soðs`BP.w+{S}Rc53,37֍[{?"S[\qߪQ^'omL!M-RPGu V1#:X NԗML9'ű;9 i Wm{V]{%h 5"+׍:oE72ocy[E}3~9*nvEg](^{,git-evtag-2016.1/tests/libtest.sh000066400000000000000000000101151266510710000166320ustar00rootroot00000000000000# Source library for shell script tests # # Copyright (C) 2011,2015 Colin Walters # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. SRCDIR=$(dirname $0) test_tmpdir=$(pwd) export G_DEBUG=fatal-warnings export TEST_GPG_KEYID_1="472CDAFA" export TEST_GPG_KEYID_2="CA950D41" export TEST_GPG_KEYID_3="DF444D67" # GPG when creating signatures demands a writable # homedir in order to create lockfiles. Work around # this by copying locally. cp -a ${SRCDIR}/gpghome ${test_tmpdir} chmod 0700 ${test_tmpdir}/gpghome export GNUPGHOME=${test_tmpdir}/gpghome if test -n "${OT_TESTS_DEBUG}"; then set -x fi assert_not_reached () { echo $@ 1>&2; exit 1 } assert_streq () { test "$1" = "$2" || (echo 1>&2 "$1 != $2"; exit 1) } assert_not_streq () { (! test "$1" = "$2") || (echo 1>&2 "$1 == $2"; exit 1) } assert_has_file () { test -f "$1" || (echo 1>&2 "Couldn't find '$1'"; exit 1) } assert_has_dir () { test -d "$1" || (echo 1>&2 "Couldn't find '$1'"; exit 1) } assert_not_has_file () { if test -f "$1"; then echo 1>&2 "File '$1' exists"; exit 1 fi } assert_not_file_has_content () { if grep -q -e "$2" "$1"; then echo 1>&2 "File '$1' incorrectly matches regexp '$2'"; exit 1 fi } assert_not_has_dir () { if test -d "$1"; then echo 1>&2 "Directory '$1' exists"; exit 1 fi } assert_file_has_content () { if ! grep -q -e "$2" "$1"; then echo 1>&2 "File '$1' doesn't match regexp '$2'"; exit 1 fi } assert_file_empty() { if test -s "$1"; then echo 1>&2 "File '$1' is not empty"; exit 1 fi } gitcommit_reset_time() { TSCOUNTER=1436222301 } gitcommit_inctime() { TSCOUNTER=$(($TSCOUNTER + 1)) TSV="$TSCOUNTER +0000" env GIT_AUTHOR_DATE="$TSV" GIT_COMMITTER_DATE="$TSV" git commit "$@" } setup_test_repository () { oldpwd=`pwd` cd ${test_tmpdir} mkdir coolproject cd coolproject git init gitcommit_reset_time echo 'So cool!' > README.md git add . gitcommit_inctime -a -m 'Initial commit' mkdir src echo 'printf("hello world")' > src/cool.c git add . gitcommit_inctime -a -m 'Add C source' cd ${test_tmpdir} mkdir -p repos/coolproject cd repos/coolproject && git init --bare cd ${test_tmpdir}/coolproject git remote add origin file://${test_tmpdir}/repos/coolproject git push --set-upstream origin master cd ${test_tmpdir} mkdir subproject cd subproject git init echo 'this is libsub.c' > libsub.c echo 'An example submodule' > README.md git add . gitcommit_inctime -a -m 'init' mkdir src mv libsub.c src echo 'an update to libsub.c, now in src/' > src/libsub.c gitcommit_inctime -a -m 'an update' cd ${test_tmpdir} mkdir -p repos/subproject cd repos/subproject && git init --bare cd ${test_tmpdir}/subproject git remote add origin file://${test_tmpdir}/repos/subproject git push --set-upstream origin master cd ${test_tmpdir}/coolproject git submodule add ../subproject subproject git add subproject echo '#include subproject/src/libsub.c' >> src/cool.c gitcommit_inctime -a -m 'Add libsub' git push cd ${test_tmpdir} rm coolproject -rf rm subproject -rf cd $oldpwd } create_editor_script() { cat >${test_tmpdir}/editor.sh < \$1 EOF chmod a+x editor.sh } with_editor_script() { env EDITOR=${test_tmpdir}/editor.sh "$@" } git-evtag-2016.1/tests/test-basic.sh000077500000000000000000000101621266510710000172270ustar00rootroot00000000000000#!/bin/bash # # Copyright (C) 2011,2015 Colin Walters # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. set -e set -x set -o pipefail echo "1..1" . $(dirname $0)/libtest.sh setup_test_repository cd ${test_tmpdir} create_editor_script 'Release 2015.1' echo "ok setup" git clone repos/coolproject cd coolproject git submodule update --init with_editor_script git evtag sign -u 472CDAFA v2015.1 git show refs/tags/v2015.1 > tag.txt TAG='Git-EVTag-v0-SHA512: 58e9834248c054f844f00148a030876f77eb85daa3caa15a20f3061f181403bae7b7e497fca199d25833b984c60f3202b16ebe0ed3a36e6b82f33618d75c569d' assert_file_has_content tag.txt "${TAG}" with_editor_script git evtag verify v2015.1 | tee verify.out assert_file_has_content verify.out "Successfully verified: ${TAG}" # Also test subdirectory (cd src && with_editor_script git evtag verify v2015.1 | tee ../verify2.out) assert_file_has_content verify2.out "Successfully verified: ${TAG}" ${SRCDIR}/git-evtag-compute-py HEAD > tag-py.txt assert_file_has_content tag-py.txt "${TAG}" rm -f tag.txt rm -f verify.out echo "ok tag + verify" cd ${test_tmpdir} rm coolproject -rf git clone repos/coolproject cd coolproject git submodule update --init echo 'super cool' > src/cool.c if with_editor_script git evtag sign -u 472CDAFA v2015.1 2>err.txt; then assert_not_reached "expected failure due to dirty tree" fi assert_file_has_content err.txt 'Attempting to tag or verify dirty tree' git checkout src/cool.c # But untracked files are ok touch unknownfile with_editor_script git evtag sign -u 472CDAFA v2015.1 git evtag verify v2015.1 echo 'ok no tag on dirty tree' cd ${test_tmpdir} rm coolproject -rf git clone repos/coolproject cd coolproject git submodule update --init with_editor_script git evtag sign -u 472CDAFA v2015.1 git checkout -q HEAD^ if git evtag verify v2015.1 2>err.txt; then assert_not_reached 'Expected failure due to non HEAD' fi assert_file_has_content err.txt "Target.*is not HEAD" echo 'ok no tag verify on non-HEAD' cd ${test_tmpdir} rm coolproject -rf git clone repos/coolproject cd coolproject git submodule update --init with_editor_script git evtag sign --with-legacy-archive-tag -u 472CDAFA v2015.1 git show refs/tags/v2015.1 > tag.txt assert_file_has_content tag.txt 'ExtendedVerify-SHA256-archive-tar: 83991ee23a027d97ad1e06432ad87c6685e02eac38706e7fbfe6e5e781939dab' gitversion=$(git --version) assert_file_has_content tag.txt "ExtendedVerify-git-version: ${gitversion}" with_editor_script git evtag verify v2015.1 | tee verify.out assert_file_has_content verify.out 'Successfully verified: Git-EVTag-v0-SHA512: 58e9834248c054f844f00148a030876f77eb85daa3caa15a20f3061f181403bae7b7e497fca199d25833b984c60f3202b16ebe0ed3a36e6b82f33618d75c569d' rm -f tag.txt rm -f verify.out echo "ok tag + verify legacy" cd ${test_tmpdir} rm coolproject -rf git clone repos/coolproject cd coolproject git submodule update --init with_editor_script git evtag sign --no-signature v2015.1 git show refs/tags/v2015.1 > tag.txt assert_file_has_content tag.txt 'Git-EVTag-v0-SHA512: 58e9834248c054f844f00148a030876f77eb85daa3caa15a20f3061f181403bae7b7e497fca199d25833b984c60f3202b16ebe0ed3a36e6b82f33618d75c569d' assert_not_file_has_content tag.txt '-----BEGIN PGP SIGNATURE-----' if git evtag verify v2015.1 2>err.txt; then assert_not_reached 'Expected failure due to no GPG signature' fi assert_file_has_content err.txt "no signature found" git evtag verify --no-signature v2015.1 rm -f tag.txt rm -f verify.out echo "ok checksum-only tag + verify"