22import zlib
33import os
44import socket
5- import tempfile
65from io import RawIOBase
76from pathlib import Path
87from types import TracebackType
98from typing import Self , IO , Any
109
11- import gnupg
12-
1310from functools import lru_cache
1411from gettext import gettext as _
1512from urllib .parse import urlparse
@@ -384,9 +381,45 @@ def get_request_without_query_params(context):
384381 return request
385382
386383
384+ class VerifyResult :
385+ """
386+ Verification result mimicking the interface of gnupg.Verify for compatibility.
387+
388+ Attributes:
389+ valid (bool): Always True; invalid signatures raise InvalidSignatureError instead.
390+ fingerprint (str): Fingerprint of the signing subkey (uppercase hex).
391+ pubkey_fingerprint (str): Fingerprint of the signing certificate (uppercase hex).
392+ key_id (str): Short (16-char) key ID derived from the fingerprint.
393+ username (str): Empty string; primary UID not available without extending pysequoia.
394+ data (bytes or None): The verified plaintext content for inline signatures, None for
395+ detached signatures.
396+ """
397+
398+ def __init__ (self , decrypted ):
399+ self .valid = True
400+ self .data = bytes (decrypted .bytes ) if decrypted .bytes is not None else None
401+ if decrypted .valid_sigs :
402+ vs = decrypted .valid_sigs [0 ]
403+ self .fingerprint = vs .signing_key .upper ()
404+ self .pubkey_fingerprint = vs .certificate .upper ()
405+ self .key_id = vs .signing_key [- 16 :].upper ()
406+ self .username = ""
407+ else :
408+ self .fingerprint = None
409+ self .pubkey_fingerprint = None
410+ self .key_id = None
411+ self .username = ""
412+
413+ def __repr__ (self ):
414+ return (
415+ f"<VerifyResult valid={ self .valid } fingerprint={ self .fingerprint } "
416+ f" key_id={ self .key_id } >"
417+ )
418+
419+
387420def gpg_verify (public_keys , signature , detached_data = None ):
388421 """
389- Check whether the provided gnupg signature is valid for one of the provided public keys.
422+ Check whether the provided signature is valid for one of the provided public keys.
390423
391424 Args:
392425 public_keys (str): Ascii armored public key data
@@ -395,25 +428,37 @@ def gpg_verify(public_keys, signature, detached_data=None):
395428 signature
396429
397430 Returns:
398- gnupg.Verify: The result of the verification
431+ VerifyResult: The verification result with `valid`, `fingerprint`, `pubkey_fingerprint`,
432+ `key_id`, `username`, and `data` attributes, mimicking the gnupg.Verify interface.
399433
400434 Raises:
401435 pulpcore.exceptions.validation.InvalidSignatureError: In case the signature is invalid.
402436 """
403- with tempfile .TemporaryDirectory (dir = settings .WORKING_DIRECTORY ) as temp_directory_name :
404- gpg = gnupg .GPG (gnupghome = temp_directory_name )
405- gpg .import_keys (public_keys )
406-
407- with ExitStack () as stack :
408- if isinstance (signature , str ):
409- signature = stack .enter_context (open (signature , "rb" ))
410- elif isinstance (signature , models .Artifact ):
411- signature = stack .enter_context (signature .file )
412-
413- verified = gpg .verify_file (signature , detached_data )
414- if not verified .valid :
415- raise InvalidSignatureError (_ ("The signature is not valid." ), verified = verified )
416- return verified
437+ from pysequoia import Cert , Sig , verify
438+
439+ certs = Cert .split_bytes (public_keys .encode ("utf8" ))
440+
441+ def store (key_ids ):
442+ return certs
443+
444+ with ExitStack () as stack :
445+ if isinstance (signature , str ):
446+ sig_data = stack .enter_context (open (signature , "rb" )).read ()
447+ elif isinstance (signature , models .Artifact ):
448+ sig_data = stack .enter_context (signature .file ).read ()
449+ else :
450+ sig_data = signature .read ()
451+
452+ try :
453+ sig = Sig .from_bytes (sig_data )
454+ if detached_data is not None :
455+ result = verify (file = detached_data , store = store , signature = sig )
456+ else :
457+ result = verify (bytes = sig_data , store = store )
458+ except Exception :
459+ raise InvalidSignatureError (_ ("The signature is not valid." ))
460+
461+ return VerifyResult (result )
417462
418463
419464def compute_file_hash (filename , hasher = None , cumulative_hash = None , blocksize = 8192 ):
0 commit comments