Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/cryptography/hazmat/primitives/ciphers/modes.py: 59%

140 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:05 +0000

1# This file is dual licensed under the terms of the Apache License, Version 

2# 2.0, and the BSD License. See the LICENSE file in the root of this repository 

3# for complete details. 

4 

5from __future__ import annotations 

6 

7import abc 

8import typing 

9 

10from cryptography import utils 

11from cryptography.exceptions import UnsupportedAlgorithm, _Reasons 

12from cryptography.hazmat.primitives._cipheralgorithm import ( 

13 BlockCipherAlgorithm, 

14 CipherAlgorithm, 

15) 

16from cryptography.hazmat.primitives.ciphers import algorithms 

17 

18 

19class Mode(metaclass=abc.ABCMeta): 

20 @property 

21 @abc.abstractmethod 

22 def name(self) -> str: 

23 """ 

24 A string naming this mode (e.g. "ECB", "CBC"). 

25 """ 

26 

27 @abc.abstractmethod 

28 def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: 

29 """ 

30 Checks that all the necessary invariants of this (mode, algorithm) 

31 combination are met. 

32 """ 

33 

34 

35class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta): 

36 @property 

37 @abc.abstractmethod 

38 def initialization_vector(self) -> bytes: 

39 """ 

40 The value of the initialization vector for this mode as bytes. 

41 """ 

42 

43 

44class ModeWithTweak(Mode, metaclass=abc.ABCMeta): 

45 @property 

46 @abc.abstractmethod 

47 def tweak(self) -> bytes: 

48 """ 

49 The value of the tweak for this mode as bytes. 

50 """ 

51 

52 

53class ModeWithNonce(Mode, metaclass=abc.ABCMeta): 

54 @property 

55 @abc.abstractmethod 

56 def nonce(self) -> bytes: 

57 """ 

58 The value of the nonce for this mode as bytes. 

59 """ 

60 

61 

62class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta): 

63 @property 

64 @abc.abstractmethod 

65 def tag(self) -> typing.Optional[bytes]: 

66 """ 

67 The value of the tag supplied to the constructor of this mode. 

68 """ 

69 

70 

71def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None: 

72 if algorithm.key_size > 256 and algorithm.name == "AES": 

73 raise ValueError( 

74 "Only 128, 192, and 256 bit keys are allowed for this AES mode" 

75 ) 

76 

77 

78def _check_iv_length( 

79 self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm 

80) -> None: 

81 if len(self.initialization_vector) * 8 != algorithm.block_size: 

82 raise ValueError( 

83 "Invalid IV size ({}) for {}.".format( 

84 len(self.initialization_vector), self.name 

85 ) 

86 ) 

87 

88 

89def _check_nonce_length( 

90 nonce: bytes, name: str, algorithm: CipherAlgorithm 

91) -> None: 

92 if not isinstance(algorithm, BlockCipherAlgorithm): 

93 raise UnsupportedAlgorithm( 

94 f"{name} requires a block cipher algorithm", 

95 _Reasons.UNSUPPORTED_CIPHER, 

96 ) 

97 if len(nonce) * 8 != algorithm.block_size: 

98 raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.") 

99 

100 

101def _check_iv_and_key_length( 

102 self: ModeWithInitializationVector, algorithm: CipherAlgorithm 

103) -> None: 

104 if not isinstance(algorithm, BlockCipherAlgorithm): 

105 raise UnsupportedAlgorithm( 

106 f"{self} requires a block cipher algorithm", 

107 _Reasons.UNSUPPORTED_CIPHER, 

108 ) 

109 _check_aes_key_length(self, algorithm) 

110 _check_iv_length(self, algorithm) 

111 

112 

113class CBC(ModeWithInitializationVector): 

114 name = "CBC" 

115 

116 def __init__(self, initialization_vector: bytes): 

117 utils._check_byteslike("initialization_vector", initialization_vector) 

118 self._initialization_vector = initialization_vector 

119 

120 @property 

121 def initialization_vector(self) -> bytes: 

122 return self._initialization_vector 

123 

124 validate_for_algorithm = _check_iv_and_key_length 

125 

126 

127class XTS(ModeWithTweak): 

128 name = "XTS" 

129 

130 def __init__(self, tweak: bytes): 

131 utils._check_byteslike("tweak", tweak) 

132 

133 if len(tweak) != 16: 

134 raise ValueError("tweak must be 128-bits (16 bytes)") 

135 

136 self._tweak = tweak 

137 

138 @property 

139 def tweak(self) -> bytes: 

140 return self._tweak 

141 

142 def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: 

143 if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)): 

144 raise TypeError( 

145 "The AES128 and AES256 classes do not support XTS, please use " 

146 "the standard AES class instead." 

147 ) 

148 

149 if algorithm.key_size not in (256, 512): 

150 raise ValueError( 

151 "The XTS specification requires a 256-bit key for AES-128-XTS" 

152 " and 512-bit key for AES-256-XTS" 

153 ) 

154 

155 

156class ECB(Mode): 

157 name = "ECB" 

158 

159 validate_for_algorithm = _check_aes_key_length 

160 

161 

162class OFB(ModeWithInitializationVector): 

163 name = "OFB" 

164 

165 def __init__(self, initialization_vector: bytes): 

166 utils._check_byteslike("initialization_vector", initialization_vector) 

167 self._initialization_vector = initialization_vector 

168 

169 @property 

170 def initialization_vector(self) -> bytes: 

171 return self._initialization_vector 

172 

173 validate_for_algorithm = _check_iv_and_key_length 

174 

175 

176class CFB(ModeWithInitializationVector): 

177 name = "CFB" 

178 

179 def __init__(self, initialization_vector: bytes): 

180 utils._check_byteslike("initialization_vector", initialization_vector) 

181 self._initialization_vector = initialization_vector 

182 

183 @property 

184 def initialization_vector(self) -> bytes: 

185 return self._initialization_vector 

186 

187 validate_for_algorithm = _check_iv_and_key_length 

188 

189 

190class CFB8(ModeWithInitializationVector): 

191 name = "CFB8" 

192 

193 def __init__(self, initialization_vector: bytes): 

194 utils._check_byteslike("initialization_vector", initialization_vector) 

195 self._initialization_vector = initialization_vector 

196 

197 @property 

198 def initialization_vector(self) -> bytes: 

199 return self._initialization_vector 

200 

201 validate_for_algorithm = _check_iv_and_key_length 

202 

203 

204class CTR(ModeWithNonce): 

205 name = "CTR" 

206 

207 def __init__(self, nonce: bytes): 

208 utils._check_byteslike("nonce", nonce) 

209 self._nonce = nonce 

210 

211 @property 

212 def nonce(self) -> bytes: 

213 return self._nonce 

214 

215 def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: 

216 _check_aes_key_length(self, algorithm) 

217 _check_nonce_length(self.nonce, self.name, algorithm) 

218 

219 

220class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): 

221 name = "GCM" 

222 _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8 

223 _MAX_AAD_BYTES = (2**64) // 8 

224 

225 def __init__( 

226 self, 

227 initialization_vector: bytes, 

228 tag: typing.Optional[bytes] = None, 

229 min_tag_length: int = 16, 

230 ): 

231 # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive 

232 # This is a sane limit anyway so we'll enforce it here. 

233 utils._check_byteslike("initialization_vector", initialization_vector) 

234 if len(initialization_vector) < 8 or len(initialization_vector) > 128: 

235 raise ValueError( 

236 "initialization_vector must be between 8 and 128 bytes (64 " 

237 "and 1024 bits)." 

238 ) 

239 self._initialization_vector = initialization_vector 

240 if tag is not None: 

241 utils._check_bytes("tag", tag) 

242 if min_tag_length < 4: 

243 raise ValueError("min_tag_length must be >= 4") 

244 if len(tag) < min_tag_length: 

245 raise ValueError( 

246 "Authentication tag must be {} bytes or longer.".format( 

247 min_tag_length 

248 ) 

249 ) 

250 self._tag = tag 

251 self._min_tag_length = min_tag_length 

252 

253 @property 

254 def tag(self) -> typing.Optional[bytes]: 

255 return self._tag 

256 

257 @property 

258 def initialization_vector(self) -> bytes: 

259 return self._initialization_vector 

260 

261 def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: 

262 _check_aes_key_length(self, algorithm) 

263 if not isinstance(algorithm, BlockCipherAlgorithm): 

264 raise UnsupportedAlgorithm( 

265 "GCM requires a block cipher algorithm", 

266 _Reasons.UNSUPPORTED_CIPHER, 

267 ) 

268 block_size_bytes = algorithm.block_size // 8 

269 if self._tag is not None and len(self._tag) > block_size_bytes: 

270 raise ValueError( 

271 "Authentication tag cannot be more than {} bytes.".format( 

272 block_size_bytes 

273 ) 

274 )