@@ -85,6 +85,19 @@ class EmptyFileException(FileFormatException):
85
85
"""An unexpectedly empty file was encountered."""
86
86
87
87
88
+ class InvalidSignature (Exception ):
89
+ """A signature was rejected by a signature criterion."""
90
+
91
+
92
+ class SignatureCriterion :
93
+ def check (self , crypto_msg : bytes , signature : bytes , verify_time : int ) -> None :
94
+ """Check/verify signature for a cryptographic message.
95
+
96
+ Raises:
97
+ InvalidSignature
98
+ """
99
+
100
+
88
101
def S_ISGITLINK (m ):
89
102
"""Check if a mode indicates a submodule.
90
103
@@ -927,6 +940,10 @@ def raw_without_sig(self) -> bytes:
927
940
ret = ret [: - len (self ._signature )]
928
941
return ret
929
942
943
+ def check_signature (self , criterion : SignatureCriterion ) -> None :
944
+ if self .signature :
945
+ criterion .check (self .raw_without_sig (), self .signature , self .tag_time )
946
+
930
947
def verify (self , keyids : Optional [Iterable [str ]] = None ) -> None :
931
948
"""Verify GPG signature for this tag (if it is signed).
932
949
@@ -941,24 +958,10 @@ def verify(self, keyids: Optional[Iterable[str]] = None) -> None:
941
958
gpg.errors.MissingSignatures: if tag was not signed by a key
942
959
specified in keyids
943
960
"""
944
- if self ._signature is None :
945
- return
946
-
947
- import gpg
948
-
949
- with gpg .Context () as ctx :
950
- data , result = ctx .verify (
951
- self .raw_without_sig (),
952
- signature = self ._signature ,
953
- )
954
- if keyids :
955
- keys = [ctx .get_key (key ) for key in keyids ]
956
- for key in keys :
957
- for subkey in keys :
958
- for sig in result .signatures :
959
- if subkey .can_sign and subkey .fpr == sig .fpr :
960
- return
961
- raise gpg .errors .MissingSignatures (result , keys , results = (data , result ))
961
+ try :
962
+ self .check_signature (GpgSignatureCriterion (keyids ))
963
+ except InvalidSignature as ex :
964
+ raise ex .__cause__ from None # type: ignore[misc]
962
965
963
966
964
967
class TreeEntry (namedtuple ("TreeEntry" , ["path" , "mode" , "sha" ])):
@@ -1531,6 +1534,10 @@ def raw_without_sig(self) -> bytes:
1531
1534
tmp .gpgsig = None
1532
1535
return tmp .as_raw_string ()
1533
1536
1537
+ def check_signature (self , criterion : SignatureCriterion ) -> None :
1538
+ if self .gpgsig :
1539
+ criterion .check (self .raw_without_sig (), self .gpgsig , self .commit_time )
1540
+
1534
1541
def verify (self , keyids : Optional [Iterable [str ]] = None ) -> None :
1535
1542
"""Verify GPG signature for this commit (if it is signed).
1536
1543
@@ -1545,24 +1552,10 @@ def verify(self, keyids: Optional[Iterable[str]] = None) -> None:
1545
1552
gpg.errors.MissingSignatures: if commit was not signed by a key
1546
1553
specified in keyids
1547
1554
"""
1548
- if self ._gpgsig is None :
1549
- return
1550
-
1551
- import gpg
1552
-
1553
- with gpg .Context () as ctx :
1554
- data , result = ctx .verify (
1555
- self .raw_without_sig (),
1556
- signature = self ._gpgsig ,
1557
- )
1558
- if keyids :
1559
- keys = [ctx .get_key (key ) for key in keyids ]
1560
- for key in keys :
1561
- for subkey in keys :
1562
- for sig in result .signatures :
1563
- if subkey .can_sign and subkey .fpr == sig .fpr :
1564
- return
1565
- raise gpg .errors .MissingSignatures (result , keys , results = (data , result ))
1555
+ try :
1556
+ self .check_signature (GpgSignatureCriterion (keyids ))
1557
+ except InvalidSignature as ex :
1558
+ raise ex .__cause__ from None # type: ignore[misc]
1566
1559
1567
1560
def _serialize (self ):
1568
1561
headers = []
@@ -1681,6 +1674,30 @@ def _get_extra(self):
1681
1674
_TYPE_MAP [cls .type_num ] = cls
1682
1675
1683
1676
1677
+ class GpgSignatureCriterion (SignatureCriterion ):
1678
+ """Verifies GPG signature."""
1679
+
1680
+ def __init__ (self , keyids : Optional [Iterable [str ]] = None ):
1681
+ self .keyids = keyids
1682
+
1683
+ def check (self , crypto_msg : bytes , signature : bytes , verify_time : int ) -> None :
1684
+ import gpg
1685
+
1686
+ with gpg .Context () as ctx :
1687
+ try :
1688
+ data , result = ctx .verify (crypto_msg , signature = signature )
1689
+ except gpg .errors .BadSignatures as ex :
1690
+ raise InvalidSignature from ex
1691
+ if self .keyids is not None :
1692
+ keys = [ctx .get_key (keyid ) for keyid in self .keyids ]
1693
+ for key in keys :
1694
+ for sig in result .signatures :
1695
+ if key .can_sign and key .fpr == sig .fpr :
1696
+ return
1697
+ ex2 = gpg .errors .MissingSignatures (result , keys , results = (data , result ))
1698
+ raise InvalidSignature from ex2
1699
+
1700
+
1684
1701
# Hold on to the pure-python implementations for testing
1685
1702
_parse_tree_py = parse_tree
1686
1703
_sorted_tree_items_py = sorted_tree_items
0 commit comments