Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/securesystemslib/signer/_spx_signer.py: 51%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

67 statements  

1"""Signer implementation for project SPHINCS+ post-quantum signature support.""" 

2 

3from __future__ import annotations 

4 

5import logging 

6import os 

7from typing import Any 

8 

9from securesystemslib.exceptions import ( 

10 UnsupportedLibraryError, 

11 UnverifiedSignatureError, 

12 VerificationError, 

13) 

14from securesystemslib.signer._key import Key 

15from securesystemslib.signer._signature import Signature 

16from securesystemslib.signer._signer import SecretsHandler, Signer 

17from securesystemslib.signer._utils import compute_default_keyid 

18 

19SPX_IMPORT_ERROR = None 

20try: 

21 from pyspx import shake_128s 

22except ImportError: 

23 SPX_IMPORT_ERROR = "spinhcs+ key support requires the pyspx library" 

24 

25_SHAKE_SEED_LEN = 48 

26 

27logger = logging.getLogger(__name__) 

28 

29 

30def generate_spx_key_pair() -> tuple[bytes, bytes]: 

31 """Generate SPHINCS+ key pair and return public and private bytes.""" 

32 if SPX_IMPORT_ERROR: 

33 raise UnsupportedLibraryError(SPX_IMPORT_ERROR) 

34 

35 seed = os.urandom(_SHAKE_SEED_LEN) 

36 public, private = shake_128s.generate_keypair(seed) 

37 

38 return public, private 

39 

40 

41class SpxKey(Key): 

42 """SPHINCS+ verifier. 

43 

44 NOTE: The SPHINCS+ key and signature serialization formats are not yet 

45 considered stable in securesystemslib. They may change in future releases 

46 and may not be supported by other implementations. 

47 """ 

48 

49 DEFAULT_KEY_TYPE = "sphincs" 

50 DEFAULT_SCHEME = "sphincs-shake-128s" 

51 

52 @classmethod 

53 def from_dict(cls, keyid: str, key_dict: dict[str, Any]) -> SpxKey: 

54 keytype, scheme, keyval = cls._from_dict(key_dict) 

55 return cls(keyid, keytype, scheme, keyval, key_dict) 

56 

57 @classmethod 

58 def from_bytes(cls, public: bytes) -> SpxKey: 

59 """Create SpxKey instance from public key bytes.""" 

60 keytype = cls.DEFAULT_KEY_TYPE 

61 scheme = cls.DEFAULT_SCHEME 

62 keyval = {"public": public.hex()} 

63 

64 keyid = compute_default_keyid(keytype, scheme, keyval) 

65 return cls(keyid, keytype, scheme, keyval) 

66 

67 def to_dict(self) -> dict[str, Any]: 

68 return self._to_dict() 

69 

70 def verify_signature(self, signature: Signature, data: bytes) -> None: 

71 valid = None 

72 try: 

73 if SPX_IMPORT_ERROR: 

74 raise UnsupportedLibraryError(SPX_IMPORT_ERROR) 

75 

76 key = bytes.fromhex(self.keyval["public"]) 

77 sig = bytes.fromhex(signature.signature) 

78 

79 valid = shake_128s.verify(data, sig, key) 

80 

81 except Exception as e: 

82 logger.info("Key %s failed to verify sig: %s", self.keyid, str(e)) 

83 raise VerificationError( 

84 f"Unknown failure to verify signature by {self.keyid}" 

85 ) from e 

86 

87 if not valid: 

88 raise UnverifiedSignatureError( 

89 f"Failed to verify signature by {self.keyid}" 

90 ) 

91 

92 

93class SpxSigner(Signer): 

94 """SPHINCS+ signer. 

95 

96 NOTE: The SPHINCS+ key and signature serialization formats are not yet 

97 considered stable in securesystemslib. They may change in future releases 

98 and may not be supported by other implementations. 

99 

100 Usage:: 

101 

102 public_bytes, private_bytes = generate_spx_key_pair() 

103 public_key = SpxKey.from_bytes(public_bytes) 

104 signer = SpxSigner(private_bytes, public_key) 

105 signature = signer.sign(b"payload") 

106 

107 # Use public_key.to_dict() / Key.from_dict() to transport public key data 

108 public_key = signer.public_key 

109 public_key.verify_signature(signature, b"payload") 

110 

111 """ 

112 

113 def __init__(self, private: bytes, public: SpxKey): 

114 self.private_key = private 

115 self._public_key = public 

116 

117 @property 

118 def public_key(self) -> Key: 

119 return self._public_key 

120 

121 @classmethod 

122 def from_priv_key_uri( 

123 cls, 

124 priv_key_uri: str, 

125 public_key: Key, 

126 secrets_handler: SecretsHandler | None = None, 

127 ) -> SpxSigner: 

128 raise NotImplementedError 

129 

130 def sign(self, payload: bytes) -> Signature: 

131 """Signs payload with SPHINCS+ private key on the instance. 

132 

133 Arguments: 

134 payload: bytes to be signed. 

135 

136 Raises: 

137 UnsupportedLibraryError: PySPX is not available. 

138 

139 Returns: 

140 Signature. 

141 

142 """ 

143 if SPX_IMPORT_ERROR: 

144 raise UnsupportedLibraryError(SPX_IMPORT_ERROR) 

145 

146 raw = shake_128s.sign(payload, self.private_key) 

147 return Signature(self.public_key.keyid, raw.hex())