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

139 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 if len(self.initialization_vector) * 8 != algorithm.block_size: 

81 raise ValueError( 

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

83 len(self.initialization_vector), self.name 

84 ) 

85 ) 

86 

87 

88def _check_nonce_length( 

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

90) -> None: 

91 if not isinstance(algorithm, BlockCipherAlgorithm): 

92 raise UnsupportedAlgorithm( 

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

94 _Reasons.UNSUPPORTED_CIPHER, 

95 ) 

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

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

98 

99 

100def _check_iv_and_key_length( 

101 self: ModeWithInitializationVector, algorithm: CipherAlgorithm 

102) -> None: 

103 if not isinstance(algorithm, BlockCipherAlgorithm): 

104 raise UnsupportedAlgorithm( 

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

106 _Reasons.UNSUPPORTED_CIPHER, 

107 ) 

108 _check_aes_key_length(self, algorithm) 

109 _check_iv_length(self, algorithm) 

110 

111 

112class CBC(ModeWithInitializationVector): 

113 name = "CBC" 

114 

115 def __init__(self, initialization_vector: bytes): 

116 utils._check_byteslike("initialization_vector", initialization_vector) 

117 self._initialization_vector = initialization_vector 

118 

119 @property 

120 def initialization_vector(self) -> bytes: 

121 return self._initialization_vector 

122 

123 validate_for_algorithm = _check_iv_and_key_length 

124 

125 

126class XTS(ModeWithTweak): 

127 name = "XTS" 

128 

129 def __init__(self, tweak: bytes): 

130 utils._check_byteslike("tweak", tweak) 

131 

132 if len(tweak) != 16: 

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

134 

135 self._tweak = tweak 

136 

137 @property 

138 def tweak(self) -> bytes: 

139 return self._tweak 

140 

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

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

143 raise TypeError( 

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

145 "the standard AES class instead." 

146 ) 

147 

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

149 raise ValueError( 

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

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

152 ) 

153 

154 

155class ECB(Mode): 

156 name = "ECB" 

157 

158 validate_for_algorithm = _check_aes_key_length 

159 

160 

161class OFB(ModeWithInitializationVector): 

162 name = "OFB" 

163 

164 def __init__(self, initialization_vector: bytes): 

165 utils._check_byteslike("initialization_vector", initialization_vector) 

166 self._initialization_vector = initialization_vector 

167 

168 @property 

169 def initialization_vector(self) -> bytes: 

170 return self._initialization_vector 

171 

172 validate_for_algorithm = _check_iv_and_key_length 

173 

174 

175class CFB(ModeWithInitializationVector): 

176 name = "CFB" 

177 

178 def __init__(self, initialization_vector: bytes): 

179 utils._check_byteslike("initialization_vector", initialization_vector) 

180 self._initialization_vector = initialization_vector 

181 

182 @property 

183 def initialization_vector(self) -> bytes: 

184 return self._initialization_vector 

185 

186 validate_for_algorithm = _check_iv_and_key_length 

187 

188 

189class CFB8(ModeWithInitializationVector): 

190 name = "CFB8" 

191 

192 def __init__(self, initialization_vector: bytes): 

193 utils._check_byteslike("initialization_vector", initialization_vector) 

194 self._initialization_vector = initialization_vector 

195 

196 @property 

197 def initialization_vector(self) -> bytes: 

198 return self._initialization_vector 

199 

200 validate_for_algorithm = _check_iv_and_key_length 

201 

202 

203class CTR(ModeWithNonce): 

204 name = "CTR" 

205 

206 def __init__(self, nonce: bytes): 

207 utils._check_byteslike("nonce", nonce) 

208 self._nonce = nonce 

209 

210 @property 

211 def nonce(self) -> bytes: 

212 return self._nonce 

213 

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

215 _check_aes_key_length(self, algorithm) 

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

217 

218 

219class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): 

220 name = "GCM" 

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

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

223 

224 def __init__( 

225 self, 

226 initialization_vector: bytes, 

227 tag: bytes | None = None, 

228 min_tag_length: int = 16, 

229 ): 

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

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

232 utils._check_byteslike("initialization_vector", initialization_vector) 

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

234 raise ValueError( 

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

236 "and 1024 bits)." 

237 ) 

238 self._initialization_vector = initialization_vector 

239 if tag is not None: 

240 utils._check_bytes("tag", tag) 

241 if min_tag_length < 4: 

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

243 if len(tag) < min_tag_length: 

244 raise ValueError( 

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

246 min_tag_length 

247 ) 

248 ) 

249 self._tag = tag 

250 self._min_tag_length = min_tag_length 

251 

252 @property 

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

254 return self._tag 

255 

256 @property 

257 def initialization_vector(self) -> bytes: 

258 return self._initialization_vector 

259 

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

261 _check_aes_key_length(self, algorithm) 

262 if not isinstance(algorithm, BlockCipherAlgorithm): 

263 raise UnsupportedAlgorithm( 

264 "GCM requires a block cipher algorithm", 

265 _Reasons.UNSUPPORTED_CIPHER, 

266 ) 

267 block_size_bytes = algorithm.block_size // 8 

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

269 raise ValueError( 

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

271 block_size_bytes 

272 ) 

273 )