Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sigstore/_internal/key_details.py: 33%

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

55 statements  

1# Copyright 2025 The Sigstore Authors 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15""" 

16Utilities for PublicKeyDetails and the algorithm registry. 

17""" 

18 

19from __future__ import annotations 

20 

21import hashlib 

22from collections.abc import Callable 

23from dataclasses import dataclass 

24 

25from cryptography.hazmat.primitives.asymmetric import ec, ed25519, padding, rsa 

26from cryptography.x509 import Certificate 

27from sigstore_models.common.v1 import HashAlgorithm, PublicKeyDetails 

28 

29 

30@dataclass(frozen=True) 

31class AlgorithmDetails: 

32 """Details for a single entry in the algorithm registry.""" 

33 

34 key_details: PublicKeyDetails 

35 hash_algorithm: HashAlgorithm | None 

36 hash_func: Callable[[bytes], hashlib._Hash] | None 

37 

38 

39# Algorithm registry table. 

40# See https://github.com/sigstore/architecture-docs/blob/main/algorithm-registry.md 

41_ALGORITHM_REGISTRY: list[AlgorithmDetails] = [ 

42 # RSA PKCS1v15 

43 AlgorithmDetails( 

44 PublicKeyDetails.PKIX_RSA_PKCS1V15_2048_SHA256, 

45 HashAlgorithm.SHA2_256, 

46 hashlib.sha256, 

47 ), 

48 AlgorithmDetails( 

49 PublicKeyDetails.PKIX_RSA_PKCS1V15_3072_SHA256, 

50 HashAlgorithm.SHA2_256, 

51 hashlib.sha256, 

52 ), 

53 AlgorithmDetails( 

54 PublicKeyDetails.PKIX_RSA_PKCS1V15_4096_SHA256, 

55 HashAlgorithm.SHA2_256, 

56 hashlib.sha256, 

57 ), 

58 # ECDSA 

59 AlgorithmDetails( 

60 PublicKeyDetails.PKIX_ECDSA_P256_SHA_256, 

61 HashAlgorithm.SHA2_256, 

62 hashlib.sha256, 

63 ), 

64 AlgorithmDetails( 

65 PublicKeyDetails.PKIX_ECDSA_P384_SHA_384, 

66 HashAlgorithm.SHA2_384, 

67 hashlib.sha384, 

68 ), 

69 AlgorithmDetails( 

70 PublicKeyDetails.PKIX_ECDSA_P521_SHA_512, 

71 HashAlgorithm.SHA2_512, 

72 hashlib.sha512, 

73 ), 

74 # Ed25519 

75 AlgorithmDetails( 

76 PublicKeyDetails.PKIX_ED25519, 

77 None, 

78 None, 

79 ), 

80 AlgorithmDetails( 

81 PublicKeyDetails.PKIX_ED25519_PH, 

82 HashAlgorithm.SHA2_512, 

83 hashlib.sha512, 

84 ), 

85] 

86 

87_DETAILS_BY_KEY: dict[PublicKeyDetails, AlgorithmDetails] = { 

88 entry.key_details: entry for entry in _ALGORITHM_REGISTRY 

89} 

90 

91 

92def _get_algorithm_details(key_details: PublicKeyDetails) -> AlgorithmDetails: 

93 """ 

94 Look up algorithm details by ``PublicKeyDetails`` enum value. 

95 """ 

96 details = _DETAILS_BY_KEY.get(key_details) 

97 if details is None: 

98 raise ValueError(f"unknown signature algorithm: {key_details}") 

99 return details 

100 

101 

102def _get_prehash( 

103 key_details: PublicKeyDetails, 

104) -> tuple[HashAlgorithm, Callable[[bytes], hashlib._Hash]]: 

105 """ 

106 Return the externalized hash function for a signing algorithm. 

107 

108 Only algorithms with an externalized prehash can be used in hashedrekord 

109 entries. Pure ed25519 (no prehash) raises ``ValueError``. 

110 """ 

111 details = _get_algorithm_details(key_details) 

112 if details.hash_algorithm is None or details.hash_func is None: 

113 raise ValueError( 

114 f"signing algorithm {key_details} has no externalized prehash; " 

115 "cannot be used for a hashedrekord entry (rekor-v2-spec §6.1.4)" 

116 ) 

117 return details.hash_algorithm, details.hash_func 

118 

119 

120def _get_key_details(certificate: Certificate) -> PublicKeyDetails: 

121 """ 

122 Determine PublicKeyDetails from the Certificate. 

123 We disclude the unrecommended types. 

124 See 

125 - https://github.com/sigstore/architecture-docs/blob/6a8d78108ef4bb403046817fbcead211a9dca71d/algorithm-registry.md. 

126 - https://github.com/sigstore/protobuf-specs/blob/3aaae418f76fb4b34df4def4cd093c464f20fed3/protos/sigstore_common.proto 

127 """ 

128 public_key = certificate.public_key() 

129 params = certificate.signature_algorithm_parameters 

130 if isinstance(public_key, ec.EllipticCurvePublicKey): 

131 if isinstance(public_key.curve, ec.SECP256R1): 

132 key_details = PublicKeyDetails.PKIX_ECDSA_P256_SHA_256 

133 elif isinstance(public_key.curve, ec.SECP384R1): 

134 key_details = PublicKeyDetails.PKIX_ECDSA_P384_SHA_384 

135 elif isinstance(public_key.curve, ec.SECP521R1): 

136 key_details = PublicKeyDetails.PKIX_ECDSA_P521_SHA_512 

137 else: 

138 raise ValueError(f"Unsupported EC curve: {public_key.curve.name}") 

139 elif isinstance(public_key, rsa.RSAPublicKey): 

140 if public_key.key_size == 2048: 

141 if isinstance(params, padding.PKCS1v15): 

142 key_details = PublicKeyDetails.PKIX_RSA_PKCS1V15_2048_SHA256 

143 else: 

144 raise ValueError( 

145 f"Unsupported public key type, size, and padding: {type(public_key)}, {public_key.key_size}, {params}" 

146 ) 

147 elif public_key.key_size == 3072: 

148 if isinstance(params, padding.PKCS1v15): 

149 key_details = PublicKeyDetails.PKIX_RSA_PKCS1V15_3072_SHA256 

150 else: 

151 raise ValueError( 

152 f"Unsupported public key type, size, and padding: {type(public_key)}, {public_key.key_size}, {params}" 

153 ) 

154 elif public_key.key_size == 4096: 

155 if isinstance(params, padding.PKCS1v15): 

156 key_details = PublicKeyDetails.PKIX_RSA_PKCS1V15_4096_SHA256 

157 else: 

158 raise ValueError( 

159 f"Unsupported public key type, size, and padding: {type(public_key)}, {public_key.key_size}, {params}" 

160 ) 

161 else: 

162 raise ValueError(f"Unsupported RSA key size: {public_key.key_size}") 

163 elif isinstance(public_key, ed25519.Ed25519PublicKey): 

164 key_details = PublicKeyDetails.PKIX_ED25519 

165 # There is likely no need to explicitly detect PKIX_ED25519_PH, especially since the cryptography 

166 # library does not yet support Ed25519ph. 

167 else: 

168 raise ValueError(f"Unsupported public key type: {type(public_key)}") 

169 return key_details