Skip to content

Commit 6e2bca5

Browse files
committed
commit: Add support for getting GPG signature data
This change adds a new Commit.gpg_signature property which returns a tuple containing the GPG signature for a given commit and the bytes that the signature was computed over. This is accomplished by calling libgit2's git_commit_extract_signature() function.
1 parent bf74b2a commit 6e2bca5

File tree

4 files changed

+76
-0
lines changed

4 files changed

+76
-0
lines changed

src/commit.c

+33
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,38 @@ Commit_message__get__(Commit *commit)
6565
return to_unicode(message, encoding, "strict");
6666
}
6767

68+
PyDoc_STRVAR(Commit_gpg_signature__doc__, "A tuple with the GPG signature and the signed payload.");
69+
70+
PyObject *
71+
Commit_gpg_signature__get__(Commit *commit)
72+
{
73+
git_buf gpg_signature = { NULL }, signed_data = { NULL };
74+
75+
const git_oid *oid = git_commit_id(commit->commit);
76+
int err = git_commit_extract_signature(
77+
&gpg_signature, &signed_data, commit->repo->repo, oid, NULL
78+
);
79+
80+
if (err != GIT_OK){
81+
git_buf_free(&gpg_signature);
82+
git_buf_free(&signed_data);
83+
84+
if (err == GIT_ENOTFOUND){
85+
Py_INCREF(Py_None); Py_INCREF(Py_None);
86+
return Py_BuildValue("OO", Py_None, Py_None);
87+
}
88+
89+
return Error_set(err);
90+
}
91+
92+
PyObject *py_gpg_signature = PyBytes_FromString(gpg_signature.ptr);
93+
PyObject *py_signed_data = PyBytes_FromString(signed_data.ptr);
94+
git_buf_free(&gpg_signature);
95+
git_buf_free(&signed_data);
96+
97+
return Py_BuildValue("OO", py_gpg_signature, py_signed_data);
98+
}
99+
68100

69101
PyDoc_STRVAR(Commit_raw_message__doc__, "Message (bytes).");
70102

@@ -233,6 +265,7 @@ PyGetSetDef Commit_getseters[] = {
233265
GETTER(Commit, commit_time_offset),
234266
GETTER(Commit, committer),
235267
GETTER(Commit, author),
268+
GETTER(Commit, gpg_signature),
236269
GETTER(Commit, tree),
237270
GETTER(Commit, tree_id),
238271
GETTER(Commit, parents),

test/data/gpgsigned.tar

48 KB
Binary file not shown.

test/test_commit.py

+38
Original file line numberDiff line numberDiff line change
@@ -160,5 +160,43 @@ def test_modify_commit(self):
160160
self.assertRaises(error_type, setattr, commit, 'parents', None)
161161

162162

163+
class GpgSignatureTestCase(utils.GpgSignedRepoTestCase):
164+
signed_hash = 'a00b212d5455ad8c4c1779f778c7d2a81bb5da23'
165+
unsigned_hash = 'a84938d1d885e80dae24b86b06621cec47ff6edd'
166+
167+
def test_get_gpg_signature_when_signed(self):
168+
expected_signature = (
169+
'-----BEGIN PGP SIGNATURE-----\n\n'
170+
'iQFGBAABCgAwFiEEQZu9JtePgJbDk7VC0+mlK74z13oFAlpzXykSHG1hcmtAbWFy\n'
171+
'a2FkYW1zLm1lAAoJENPppSu+M9d6FRoIAJXeQRRT1V47nnHITiel6426loYkeij7\n'
172+
'66doGNIyll95H92SwH4LAjPyEEByIG1VsA6NztzUoNgnEvAXI0iAz3LyI7N16M4b\n'
173+
'dPDkC72pp8tu280H5Qt5b2V5hmlKKSgtOS5iNhdU/FbWVS8MlHsqzQTZfoTdi6ch\n'
174+
'KWUsjzudVd3F/H/AU+1Jsxt8Iz/oK4T/puUQLnJZKjKlljGP994FA3JIpnZpZmbG\n'
175+
'FybYJEDXnng7uhx3Fz/Mo3KBJoQfAExTtaToY0n0hSjOe6GN9rEsRSMK3mWdysf2\n'
176+
'wOdtYMMcT16hG5tAwnD/myZ4rIIpyZJ/9mjymdUsj6UKf7D+vJuqfsI=\n=IyYy\n'
177+
'-----END PGP SIGNATURE-----'
178+
).encode('ascii')
179+
180+
expected_payload = (
181+
'tree c36c20831e43e5984c672a714661870b67ab1d95\nauthor Mark Adams '
182+
'<[email protected]> 1517510299 -0600\ncommitter Mark Adams <ma'
183+
'[email protected]> 1517510441 -0600\n\nMaking a GPG signed commi'
184+
't\n'
185+
).encode('ascii')
186+
187+
commit = self.repo.get(self.signed_hash)
188+
signature, payload = commit.gpg_signature
189+
190+
assert signature == expected_signature
191+
assert payload == expected_payload
192+
193+
def test_get_gpg_signature_when_unsigned(self):
194+
commit = self.repo.get(self.unsigned_hash)
195+
signature, payload = commit.gpg_signature
196+
197+
assert signature is None
198+
assert payload is None
199+
200+
163201
if __name__ == '__main__':
164202
unittest.main()

test/utils.py

+5
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,8 @@ class SubmoduleRepoTestCase(AutoRepoTestCase):
186186
class BinaryFileRepoTestCase(AutoRepoTestCase):
187187

188188
repo_spec = 'tar', 'binaryfilerepo'
189+
190+
191+
class GpgSignedRepoTestCase(AutoRepoTestCase):
192+
193+
repo_spec = 'tar', 'gpgsigned'

0 commit comments

Comments
 (0)