Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/tls/crypto/pkcs1.py: 18%

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

130 statements  

1# SPDX-License-Identifier: GPL-2.0-only 

2# This file is part of Scapy 

3# See https://scapy.net/ for more information 

4# Copyright (C) 2008 Arnaud Ebalard <arno@natisbad.org> 

5# 2015, 2016, 2017 Maxence Tury <maxence.tury@ssi.gouv.fr> 

6 

7""" 

8PKCS #1 methods as defined in RFC 3447. 

9 

10We cannot rely solely on the cryptography library, because the openssl package 

11used by the cryptography library may not implement the md5-sha1 hash, as with 

12Ubuntu or OSX. This is why we reluctantly keep some legacy crypto here. 

13""" 

14 

15from scapy.compat import bytes_encode, hex_bytes, bytes_hex 

16 

17from scapy.config import conf, crypto_validator 

18from scapy.error import warning 

19if conf.crypto_valid: 

20 from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm 

21 from cryptography.hazmat.backends import default_backend 

22 from cryptography.hazmat.primitives import hashes 

23 from cryptography.hazmat.primitives.asymmetric import padding 

24 from cryptography.hazmat.primitives.hashes import HashAlgorithm 

25 

26 

27##################################################################### 

28# Some helpers 

29##################################################################### 

30 

31def pkcs_os2ip(s): 

32 """ 

33 OS2IP conversion function from RFC 3447. 

34 

35 :param s: octet string to be converted 

36 :return: n, the corresponding nonnegative integer 

37 """ 

38 return int(bytes_hex(s), 16) 

39 

40 

41def pkcs_i2osp(n, sLen): 

42 """ 

43 I2OSP conversion function from RFC 3447. 

44 The length parameter allows the function to perform the padding needed. 

45 Note that the user is responsible for providing a sufficient xLen. 

46 

47 :param n: nonnegative integer to be converted 

48 :param sLen: intended length of the resulting octet string 

49 :return: corresponding octet string 

50 """ 

51 # if n >= 256**sLen: 

52 # raise Exception("Integer too large for provided sLen %d" % sLen) 

53 fmt = "%%0%dx" % (2 * sLen) 

54 return hex_bytes(fmt % n) 

55 

56 

57def pkcs_ilen(n): 

58 """ 

59 This is a log base 256 which determines the minimum octet string 

60 length for unequivocal representation of integer n by pkcs_i2osp. 

61 """ 

62 i = 0 

63 while n > 0: 

64 n >>= 8 

65 i += 1 

66 return i 

67 

68 

69@crypto_validator 

70def _legacy_pkcs1_v1_5_encode_md5_sha1(M, emLen): 

71 """ 

72 Legacy method for PKCS1 v1.5 encoding with MD5-SHA1 hash. 

73 """ 

74 M = bytes_encode(M) 

75 md5_hash = hashes.Hash(_get_hash("md5"), backend=default_backend()) 

76 md5_hash.update(M) 

77 sha1_hash = hashes.Hash(_get_hash("sha1"), backend=default_backend()) 

78 sha1_hash.update(M) 

79 H = md5_hash.finalize() + sha1_hash.finalize() 

80 if emLen < 36 + 11: 

81 warning("pkcs_emsa_pkcs1_v1_5_encode: " 

82 "intended encoded message length too short") 

83 return None 

84 PS = b'\xff' * (emLen - 36 - 3) 

85 return b'\x00' + b'\x01' + PS + b'\x00' + H 

86 

87 

88##################################################################### 

89# Hash and padding helpers 

90##################################################################### 

91 

92_get_hash = None 

93if conf.crypto_valid: 

94 

95 # first, we add the "md5-sha1" hash from openssl to python-cryptography 

96 class MD5_SHA1(HashAlgorithm): 

97 name = "md5-sha1" 

98 digest_size = 36 

99 block_size = 64 

100 

101 _hashes = { 

102 "md5": hashes.MD5, 

103 "sha1": hashes.SHA1, 

104 "sha224": hashes.SHA224, 

105 "sha256": hashes.SHA256, 

106 "sha384": hashes.SHA384, 

107 "sha512": hashes.SHA512, 

108 "md5-sha1": MD5_SHA1 

109 } 

110 

111 def _get_hash(hashStr): 

112 try: 

113 return _hashes[hashStr]() 

114 except KeyError: 

115 raise KeyError("Unknown hash function %s" % hashStr) 

116 

117 def _get_padding(padStr, mgf=padding.MGF1, h=hashes.SHA256, label=None): 

118 if padStr == "pkcs": 

119 return padding.PKCS1v15() 

120 elif padStr == "pss": 

121 # Can't find where this is written, but we have to use the digest 

122 # size instead of the automatic padding.PSS.MAX_LENGTH. 

123 return padding.PSS(mgf=mgf(h), salt_length=h.digest_size) 

124 elif padStr == "oaep": 

125 return padding.OAEP(mgf=mgf(h), algorithm=h, label=label) 

126 else: 

127 warning("Key.encrypt(): Unknown padding type (%s)", padStr) 

128 return None 

129 

130 

131##################################################################### 

132# Asymmetric Cryptography wrappers 

133##################################################################### 

134 

135# Make sure that default values are consistent across the whole TLS module, 

136# lest they be explicitly set to None between cert.py and pkcs1.py. 

137 

138class _EncryptAndVerifyRSA(object): 

139 

140 @crypto_validator 

141 def encrypt(self, m, t="pkcs", h="sha256", mgf=None, L=None): 

142 mgf = mgf or padding.MGF1 

143 h = _get_hash(h) 

144 pad = _get_padding(t, mgf, h, L) 

145 return self.pubkey.encrypt(m, pad) 

146 

147 @crypto_validator 

148 def verify(self, M, S, t="pkcs", h="sha256", mgf=None, L=None): 

149 M = bytes_encode(M) 

150 mgf = mgf or padding.MGF1 

151 h = _get_hash(h) 

152 pad = _get_padding(t, mgf, h, L) 

153 try: 

154 try: 

155 self.pubkey.verify(S, M, pad, h) 

156 except UnsupportedAlgorithm: 

157 if t != "pkcs" and h != "md5-sha1": 

158 raise UnsupportedAlgorithm("RSA verification with %s" % h) 

159 self._legacy_verify_md5_sha1(M, S) 

160 return True 

161 except InvalidSignature: 

162 return False 

163 

164 def _legacy_verify_md5_sha1(self, M, S): 

165 k = self._modulusLen // 8 

166 if len(S) != k: 

167 warning("invalid signature (len(S) != k)") 

168 return False 

169 s = pkcs_os2ip(S) 

170 n = self._modulus 

171 if s > n - 1: 

172 warning("Key._rsaep() expects a long between 0 and n-1") 

173 return None 

174 m = pow(s, self._pubExp, n) 

175 EM = pkcs_i2osp(m, k) 

176 EMPrime = _legacy_pkcs1_v1_5_encode_md5_sha1(M, k) 

177 if EMPrime is None: 

178 warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.") 

179 return False 

180 return EM == EMPrime 

181 

182 

183class _DecryptAndSignRSA(object): 

184 

185 @crypto_validator 

186 def decrypt(self, C, t="pkcs", h="sha256", mgf=None, L=None): 

187 mgf = mgf or padding.MGF1 

188 h = _get_hash(h) 

189 pad = _get_padding(t, mgf, h, L) 

190 return self.key.decrypt(C, pad) 

191 

192 @crypto_validator 

193 def sign(self, M, t="pkcs", h="sha256", mgf=None, L=None): 

194 M = bytes_encode(M) 

195 mgf = mgf or padding.MGF1 

196 h = _get_hash(h) 

197 pad = _get_padding(t, mgf, h, L) 

198 try: 

199 return self.key.sign(M, pad, h) 

200 except UnsupportedAlgorithm: 

201 if t != "pkcs" and h != "md5-sha1": 

202 raise UnsupportedAlgorithm("RSA signature with %s" % h) 

203 return self._legacy_sign_md5_sha1(M) 

204 

205 def _legacy_sign_md5_sha1(self, M): 

206 M = bytes_encode(M) 

207 k = self._modulusLen // 8 

208 EM = _legacy_pkcs1_v1_5_encode_md5_sha1(M, k) 

209 if EM is None: 

210 warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode") 

211 return None 

212 m = pkcs_os2ip(EM) 

213 n = self._modulus 

214 if m > n - 1: 

215 warning("Key._rsaep() expects a long between 0 and n-1") 

216 return None 

217 privExp = self.key.private_numbers().d 

218 s = pow(m, privExp, n) 

219 return pkcs_i2osp(s, k)