Browse Source

add keyids param for verify()

Peter Rowlands 3 years ago
parent
commit
bdd2bd4698
2 changed files with 33 additions and 3 deletions
  1. 25 3
      dulwich/objects.py
  2. 8 0
      dulwich/tests/test_porcelain.py

+ 25 - 3
dulwich/objects.py

@@ -30,6 +30,7 @@ import stat
 from typing import (
     Optional,
     Dict,
+    Iterable,
     Union,
     Type,
 )
@@ -881,11 +882,19 @@ class Tag(ShaFile):
                     self.as_raw_string(), mode=gpg.constants.sig.mode.DETACH
                 )
 
-    def verify(self):
+    def verify(self, keyids: Optional[Iterable[str]] = None):
         """Verify GPG signature for this tag (if it is signed).
 
+        Args:
+          keyids: Optional iterable of trusted keyids for this tag.
+            If this tag is not signed by any key in keyids verification will
+            fail. If not specified, this function only verifies that the tag
+            has a valid signature.
+
         Raises:
           gpg.errors.BadSignatures: if GPG signature verification fails
+          gpg.errors.MissingSignatures: if tag was not signed by a key
+            specified in keyids
         """
         if self._signature is None:
             return
@@ -893,10 +902,23 @@ class Tag(ShaFile):
         import gpg
 
         with gpg.Context() as ctx:
-            unused_data, unused_result = ctx.verify(
+            data, result = ctx.verify(
                 self.as_raw_string()[: -len(self._signature)],
-                self._signature,
+                signature=self._signature,
             )
+            if keyids:
+                keys = [
+                    ctx.get_key(key)
+                    for key in keyids
+                ]
+                for key in keys:
+                    for subkey in keys:
+                        for sig in result.signatures:
+                            if subkey.can_sign and subkey.fpr == sig.fpr:
+                                return
+                raise gpg.errors.MissingSignatures(
+                    result, keys, results=(data, result)
+                )
 
 
 class TreeEntry(namedtuple("TreeEntry", ["path", "mode", "sha"])):

+ 8 - 0
dulwich/tests/test_porcelain.py

@@ -1134,6 +1134,14 @@ class TagCreateSignTests(PorcelainGpgTestCase):
         tag = self.repo[b'refs/tags/tryme']
         # GPG Signatures aren't deterministic, so we can't do a static assertion.
         tag.verify()
+        tag.verify(keyids=[PorcelainGpgTestCase.DEFAULT_KEY_ID])
+
+        self.import_non_default_key()
+        self.assertRaises(
+            gpg.errors.MissingSignatures,
+            tag.verify,
+            keyids=[PorcelainGpgTestCase.NON_DEFAULT_KEY_ID],
+        )
 
         tag._chunked_text = [b"bad data", tag._signature]
         self.assertRaises(