Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/securesystemslib/_gpg/eddsa.py: 31%

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

59 statements  

1""" 

2<Module Name> 

3 eddsa.py 

4 

5<Author> 

6 Lukas Puehringer <lukas.puehringer@nyu.edu> 

7 

8<Started> 

9 Oct 22, 2019 

10 

11<Copyright> 

12 See LICENSE for licensing information. 

13 

14<Purpose> 

15 EdDSA/ed25519 algorithm-specific handling routines for pubkey and signature 

16 parsing and verification. 

17 

18""" 

19 

20import binascii 

21 

22from securesystemslib import exceptions 

23from securesystemslib._gpg import util as gpg_util 

24from securesystemslib._gpg.exceptions import PacketParsingError 

25 

26CRYPTO = True 

27NO_CRYPTO_MSG = "EdDSA key support for GPG requires the cryptography library" 

28try: 

29 from cryptography.exceptions import InvalidSignature 

30 from cryptography.hazmat.primitives.asymmetric import ( 

31 ed25519 as pyca_ed25519, 

32 ) 

33except ImportError: 

34 CRYPTO = False 

35 

36# ECC Curve OID (see RFC4880-bis8 9.2.) 

37ED25519_PUBLIC_KEY_OID = bytearray.fromhex("2B 06 01 04 01 DA 47 0F 01") 

38 

39# EdDSA Point Format (see RFC4880-bis8 13.3.) 

40ED25519_PUBLIC_KEY_LENGTH = 33 

41ED25519_PUBLIC_KEY_PREFIX = 0x40 

42# EdDSA signature byte length (see RFC 8032 5.1.6. (6)) 

43ED25519_SIG_LENGTH = 64 

44 

45 

46def get_pubkey_params(data): 

47 """ 

48 <Purpose> 

49 Parse algorithm-specific part for EdDSA public keys 

50 

51 See RFC4880-bis8 sections 5.6.5. Algorithm-Specific Part for EdDSA Keys, 

52 9.2. ECC Curve OID and 13.3. EdDSA Point Format for more details. 

53 

54 <Arguments> 

55 data: 

56 The EdDSA public key data AFTER the one-octet number denoting the 

57 public-key algorithm of this key. 

58 

59 <Exceptions> 

60 securesystemslib._gpg.exceptions.PacketParsingError or IndexError: 

61 if the public key data is malformed. 

62 

63 <Side Effects> 

64 None. 

65 

66 <Returns> 

67 A dictionary with an element "q" that holds the ascii hex representation 

68 of the MPI of an EC point representing an EdDSA public key. 

69 

70 """ 

71 ptr = 0 

72 

73 curve_oid_len = data[ptr] 

74 ptr += 1 

75 

76 curve_oid = data[ptr : ptr + curve_oid_len] 

77 ptr += curve_oid_len 

78 

79 # See 9.2. ECC Curve OID 

80 if curve_oid != ED25519_PUBLIC_KEY_OID: 

81 raise PacketParsingError( 

82 f"bad ed25519 curve OID '{curve_oid}', expected {ED25519_PUBLIC_KEY_OID}'" 

83 ) 

84 

85 # See 13.3. EdDSA Point Format 

86 public_key_len = gpg_util.get_mpi_length(data[ptr : ptr + 2]) 

87 ptr += 2 

88 

89 if public_key_len != ED25519_PUBLIC_KEY_LENGTH: 

90 raise PacketParsingError( 

91 f"bad ed25519 MPI length '{public_key_len}', " 

92 f"expected {ED25519_PUBLIC_KEY_LENGTH}'" 

93 ) 

94 

95 public_key_prefix = data[ptr] 

96 ptr += 1 

97 

98 if public_key_prefix != ED25519_PUBLIC_KEY_PREFIX: 

99 raise PacketParsingError( 

100 f"bad ed25519 MPI prefix '{public_key_prefix}', " 

101 f"expected '{ED25519_PUBLIC_KEY_PREFIX}'" 

102 ) 

103 

104 public_key = data[ptr : ptr + public_key_len - 1] 

105 

106 return {"q": binascii.hexlify(public_key).decode("ascii")} 

107 

108 

109def get_signature_params(data): 

110 """ 

111 <Purpose> 

112 Parse algorithm-specific fields for EdDSA signatures. 

113 

114 See RFC4880-bis8 section 5.2.3. Version 4 and 5 Signature Packet Formats 

115 for more details. 

116 

117 <Arguments> 

118 data: 

119 The EdDSA signature data AFTER the two-octet field holding the 

120 left 16 bits of the signed hash value. 

121 

122 <Exceptions> 

123 IndexError if the signature data is malformed. 

124 

125 <Side Effects> 

126 None. 

127 

128 <Returns> 

129 The concatenation of the parsed MPI R and S values of the EdDSA signature, 

130 i.e. ENC(R) || ENC(S) (see RFC8032 3.4 Verify). 

131 

132 """ 

133 ptr = 0 

134 r_length = gpg_util.get_mpi_length(data[ptr : ptr + 2]) 

135 

136 ptr += 2 

137 r = data[ptr : ptr + r_length] 

138 ptr += r_length 

139 

140 s_length = gpg_util.get_mpi_length(data[ptr : ptr + 2]) 

141 ptr += 2 

142 s = data[ptr : ptr + s_length] 

143 

144 # Left-zero-pad 'r' and 's' values that are shorter than required by RFC 8032 

145 # (5.1.6.), to make up for omitted leading zeros in RFC 4880 (3.2.) MPIs. 

146 # This is especially important for 's', which is little-endian. 

147 r = r.rjust(ED25519_SIG_LENGTH // 2, b"\x00") 

148 s = s.rjust(ED25519_SIG_LENGTH // 2, b"\x00") 

149 

150 return r + s 

151 

152 

153def create_pubkey(pubkey_info): 

154 """ 

155 <Purpose> 

156 Create and return an Ed25519PublicKey object from the passed pubkey_info 

157 using pyca/cryptography. 

158 

159 <Arguments> 

160 pubkey_info: 

161 The ED25519 public key dict. 

162 

163 <Exceptions> 

164 

165 securesystemslib.exceptions.UnsupportedLibraryError if 

166 the cryptography module is unavailable 

167 

168 <Returns> 

169 A cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PublicKey based 

170 on the passed pubkey_info. 

171 

172 """ 

173 if not CRYPTO: # pragma: no cover 

174 raise exceptions.UnsupportedLibraryError(NO_CRYPTO_MSG) 

175 

176 public_bytes = binascii.unhexlify(pubkey_info["keyval"]["public"]["q"]) 

177 public_key = pyca_ed25519.Ed25519PublicKey.from_public_bytes(public_bytes) 

178 

179 return public_key 

180 

181 

182def verify_signature(signature_object, pubkey_info, content, hash_algorithm_id): 

183 """ 

184 <Purpose> 

185 Verify the passed signature against the passed content with the passed 

186 ED25519 public key using pyca/cryptography. 

187 

188 <Arguments> 

189 signature_object: 

190 A signature dict. 

191 

192 pubkey_info: 

193 A DSA public key dict. 

194 

195 hash_algorithm_id: 

196 one of SHA1, SHA256, SHA512 (see securesystemslib._gpg.constants) 

197 used to verify the signature 

198 NOTE: Overrides any hash algorithm specification in "pubkey_info"'s 

199 "hashes" or "method" fields. 

200 

201 content: 

202 The signed bytes against which the signature is verified 

203 

204 <Exceptions> 

205 securesystemslib.exceptions.UnsupportedLibraryError if: 

206 the cryptography module is unavailable 

207 

208 ValueError: 

209 if the passed hash_algorithm_id is not supported (see 

210 securesystemslib._gpg.util.get_hashing_class) 

211 

212 <Returns> 

213 True if signature verification passes and False otherwise. 

214 

215 """ 

216 if not CRYPTO: # pragma: no cover 

217 raise exceptions.UnsupportedLibraryError(NO_CRYPTO_MSG) 

218 

219 hasher = gpg_util.get_hashing_class(hash_algorithm_id) 

220 

221 pubkey_object = create_pubkey(pubkey_info) 

222 

223 # See RFC4880-bis8 14.8. EdDSA and 5.2.4 "Computing Signatures" 

224 digest = gpg_util.hash_object( 

225 binascii.unhexlify(signature_object["other_headers"]), hasher(), content 

226 ) 

227 

228 try: 

229 pubkey_object.verify(binascii.unhexlify(signature_object["signature"]), digest) 

230 return True 

231 

232 except InvalidSignature: 

233 return False