diff --git a/src/commit.c b/src/commit.c index 7d06c7571..0ce7c38ef 100644 --- a/src/commit.c +++ b/src/commit.c @@ -65,6 +65,38 @@ Commit_message__get__(Commit *commit) return to_unicode(message, encoding, "strict"); } +PyDoc_STRVAR(Commit_gpg_signature__doc__, "A tuple with the GPG signature and the signed payload."); + +PyObject * +Commit_gpg_signature__get__(Commit *commit) +{ + git_buf gpg_signature = { NULL }, signed_data = { NULL }; + PyObject *py_gpg_signature, *py_signed_data; + + git_oid *oid = git_commit_id(commit->commit); + int err = git_commit_extract_signature( + &gpg_signature, &signed_data, commit->repo->repo, oid, NULL + ); + + if (err != GIT_OK){ + git_buf_free(&gpg_signature); + git_buf_free(&signed_data); + + if (err == GIT_ENOTFOUND){ + return Py_BuildValue("OO", Py_None, Py_None); + } + + return Error_set(err); + } + + py_gpg_signature = PyBytes_FromString(gpg_signature.ptr); + py_signed_data = PyBytes_FromString(signed_data.ptr); + git_buf_free(&gpg_signature); + git_buf_free(&signed_data); + + return Py_BuildValue("NN", py_gpg_signature, py_signed_data); +} + PyDoc_STRVAR(Commit_raw_message__doc__, "Message (bytes)."); @@ -233,6 +265,7 @@ PyGetSetDef Commit_getseters[] = { GETTER(Commit, commit_time_offset), GETTER(Commit, committer), GETTER(Commit, author), + GETTER(Commit, gpg_signature), GETTER(Commit, tree), GETTER(Commit, tree_id), GETTER(Commit, parents), diff --git a/test/data/gpgsigned.tar b/test/data/gpgsigned.tar new file mode 100644 index 000000000..5502a5dd6 Binary files /dev/null and b/test/data/gpgsigned.tar differ diff --git a/test/test_commit.py b/test/test_commit.py index e81e43e20..656370a67 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -160,5 +160,43 @@ def test_modify_commit(self): self.assertRaises(error_type, setattr, commit, 'parents', None) +class GpgSignatureTestCase(utils.GpgSignedRepoTestCase): + signed_hash = 'a00b212d5455ad8c4c1779f778c7d2a81bb5da23' + unsigned_hash = 'a84938d1d885e80dae24b86b06621cec47ff6edd' + + def test_get_gpg_signature_when_signed(self): + expected_signature = ( + '-----BEGIN PGP SIGNATURE-----\n\n' + 'iQFGBAABCgAwFiEEQZu9JtePgJbDk7VC0+mlK74z13oFAlpzXykSHG1hcmtAbWFy\n' + 'a2FkYW1zLm1lAAoJENPppSu+M9d6FRoIAJXeQRRT1V47nnHITiel6426loYkeij7\n' + '66doGNIyll95H92SwH4LAjPyEEByIG1VsA6NztzUoNgnEvAXI0iAz3LyI7N16M4b\n' + 'dPDkC72pp8tu280H5Qt5b2V5hmlKKSgtOS5iNhdU/FbWVS8MlHsqzQTZfoTdi6ch\n' + 'KWUsjzudVd3F/H/AU+1Jsxt8Iz/oK4T/puUQLnJZKjKlljGP994FA3JIpnZpZmbG\n' + 'FybYJEDXnng7uhx3Fz/Mo3KBJoQfAExTtaToY0n0hSjOe6GN9rEsRSMK3mWdysf2\n' + 'wOdtYMMcT16hG5tAwnD/myZ4rIIpyZJ/9mjymdUsj6UKf7D+vJuqfsI=\n=IyYy\n' + '-----END PGP SIGNATURE-----' + ).encode('ascii') + + expected_payload = ( + 'tree c36c20831e43e5984c672a714661870b67ab1d95\nauthor Mark Adams ' + ' 1517510299 -0600\ncommitter Mark Adams 1517510441 -0600\n\nMaking a GPG signed commi' + 't\n' + ).encode('ascii') + + commit = self.repo.get(self.signed_hash) + signature, payload = commit.gpg_signature + + assert signature == expected_signature + assert payload == expected_payload + + def test_get_gpg_signature_when_unsigned(self): + commit = self.repo.get(self.unsigned_hash) + signature, payload = commit.gpg_signature + + assert signature is None + assert payload is None + + if __name__ == '__main__': unittest.main() diff --git a/test/utils.py b/test/utils.py index e7a6af162..c8e9aba67 100644 --- a/test/utils.py +++ b/test/utils.py @@ -191,3 +191,8 @@ class SubmoduleRepoTestCase(AutoRepoTestCase): class BinaryFileRepoTestCase(AutoRepoTestCase): repo_spec = 'tar', 'binaryfilerepo' + + +class GpgSignedRepoTestCase(AutoRepoTestCase): + + repo_spec = 'tar', 'gpgsigned'