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

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

140 statements  

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 

8 

9from cryptography import utils 

10from cryptography.exceptions import UnsupportedAlgorithm, _Reasons 

11from cryptography.hazmat.primitives._cipheralgorithm import ( 

12 BlockCipherAlgorithm, 

13 CipherAlgorithm, 

14) 

15from cryptography.hazmat.primitives.ciphers import algorithms 

16 

17 

18class Mode(metaclass=abc.ABCMeta): 

19 @property 

20 @abc.abstractmethod 

21 def name(self) -> str: 

22 """ 

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

24 """ 

25 

26 @abc.abstractmethod 

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

28 """ 

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

30 combination are met. 

31 """ 

32 

33 

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

35 @property 

36 @abc.abstractmethod 

37 def initialization_vector(self) -> bytes: 

38 """ 

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

40 """ 

41 

42 

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

44 @property 

45 @abc.abstractmethod 

46 def tweak(self) -> bytes: 

47 """ 

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

49 """ 

50 

51 

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

53 @property 

54 @abc.abstractmethod 

55 def nonce(self) -> bytes: 

56 """ 

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

58 """ 

59 

60 

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

62 @property 

63 @abc.abstractmethod 

64 def tag(self) -> bytes | None: 

65 """ 

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

67 """ 

68 

69 

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

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

72 raise ValueError( 

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

74 ) 

75 

76 

77def _check_iv_length( 

78 self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm 

79) -> None: 

80 iv_len = len(self.initialization_vector) 

81 if iv_len * 8 != algorithm.block_size: 

82 raise ValueError(f"Invalid IV size ({iv_len}) for {self.name}.") 

83 

84 

85def _check_nonce_length( 

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

87) -> None: 

88 if not isinstance(algorithm, BlockCipherAlgorithm): 

89 raise UnsupportedAlgorithm( 

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

91 _Reasons.UNSUPPORTED_CIPHER, 

92 ) 

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

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

95 

96 

97def _check_iv_and_key_length( 

98 self: ModeWithInitializationVector, algorithm: CipherAlgorithm 

99) -> None: 

100 if not isinstance(algorithm, BlockCipherAlgorithm): 

101 raise UnsupportedAlgorithm( 

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

103 _Reasons.UNSUPPORTED_CIPHER, 

104 ) 

105 _check_aes_key_length(self, algorithm) 

106 _check_iv_length(self, algorithm) 

107 

108 

109class CBC(ModeWithInitializationVector): 

110 name = "CBC" 

111 

112 def __init__(self, initialization_vector: bytes): 

113 utils._check_byteslike("initialization_vector", initialization_vector) 

114 self._initialization_vector = initialization_vector 

115 

116 @property 

117 def initialization_vector(self) -> bytes: 

118 return self._initialization_vector 

119 

120 validate_for_algorithm = _check_iv_and_key_length 

121 

122 

123class XTS(ModeWithTweak): 

124 name = "XTS" 

125 

126 def __init__(self, tweak: bytes): 

127 utils._check_byteslike("tweak", tweak) 

128 

129 if len(tweak) != 16: 

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

131 

132 self._tweak = tweak 

133 

134 @property 

135 def tweak(self) -> bytes: 

136 return self._tweak 

137 

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

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

140 raise TypeError( 

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

142 "the standard AES class instead." 

143 ) 

144 

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

146 raise ValueError( 

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

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

149 ) 

150 

151 

152class ECB(Mode): 

153 name = "ECB" 

154 

155 validate_for_algorithm = _check_aes_key_length 

156 

157 

158class OFB(ModeWithInitializationVector): 

159 name = "OFB" 

160 

161 def __init__(self, initialization_vector: bytes): 

162 utils._check_byteslike("initialization_vector", initialization_vector) 

163 self._initialization_vector = initialization_vector 

164 

165 @property 

166 def initialization_vector(self) -> bytes: 

167 return self._initialization_vector 

168 

169 validate_for_algorithm = _check_iv_and_key_length 

170 

171 

172class CFB(ModeWithInitializationVector): 

173 name = "CFB" 

174 

175 def __init__(self, initialization_vector: bytes): 

176 utils._check_byteslike("initialization_vector", initialization_vector) 

177 self._initialization_vector = initialization_vector 

178 

179 @property 

180 def initialization_vector(self) -> bytes: 

181 return self._initialization_vector 

182 

183 validate_for_algorithm = _check_iv_and_key_length 

184 

185 

186class CFB8(ModeWithInitializationVector): 

187 name = "CFB8" 

188 

189 def __init__(self, initialization_vector: bytes): 

190 utils._check_byteslike("initialization_vector", initialization_vector) 

191 self._initialization_vector = initialization_vector 

192 

193 @property 

194 def initialization_vector(self) -> bytes: 

195 return self._initialization_vector 

196 

197 validate_for_algorithm = _check_iv_and_key_length 

198 

199 

200class CTR(ModeWithNonce): 

201 name = "CTR" 

202 

203 def __init__(self, nonce: bytes): 

204 utils._check_byteslike("nonce", nonce) 

205 self._nonce = nonce 

206 

207 @property 

208 def nonce(self) -> bytes: 

209 return self._nonce 

210 

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

212 _check_aes_key_length(self, algorithm) 

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

214 

215 

216class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): 

217 name = "GCM" 

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

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

220 

221 def __init__( 

222 self, 

223 initialization_vector: bytes, 

224 tag: bytes | None = None, 

225 min_tag_length: int = 16, 

226 ): 

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

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

229 utils._check_byteslike("initialization_vector", initialization_vector) 

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

231 raise ValueError( 

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

233 "and 1024 bits)." 

234 ) 

235 self._initialization_vector = initialization_vector 

236 if tag is not None: 

237 utils._check_bytes("tag", tag) 

238 if min_tag_length < 4: 

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

240 if len(tag) < min_tag_length: 

241 raise ValueError( 

242 f"Authentication tag must be {min_tag_length} bytes or " 

243 "longer." 

244 ) 

245 self._tag = tag 

246 self._min_tag_length = min_tag_length 

247 

248 @property 

249 def tag(self) -> bytes | None: 

250 return self._tag 

251 

252 @property 

253 def initialization_vector(self) -> bytes: 

254 return self._initialization_vector 

255 

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

257 _check_aes_key_length(self, algorithm) 

258 if not isinstance(algorithm, BlockCipherAlgorithm): 

259 raise UnsupportedAlgorithm( 

260 "GCM requires a block cipher algorithm", 

261 _Reasons.UNSUPPORTED_CIPHER, 

262 ) 

263 block_size_bytes = algorithm.block_size // 8 

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

265 raise ValueError( 

266 f"Authentication tag cannot be more than {block_size_bytes} " 

267 "bytes." 

268 )