Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 

5 

6import abc 

7import typing 

8 

9from cryptography import utils 

10from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm 

11 

12 

13class Mode(metaclass=abc.ABCMeta): 

14 @abc.abstractproperty 

15 def name(self) -> str: 

16 """ 

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

18 """ 

19 

20 @abc.abstractmethod 

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

22 """ 

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

24 combination are met. 

25 """ 

26 

27 

28class ModeWithInitializationVector(metaclass=abc.ABCMeta): 

29 @abc.abstractproperty 

30 def initialization_vector(self) -> bytes: 

31 """ 

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

33 """ 

34 

35 

36class ModeWithTweak(metaclass=abc.ABCMeta): 

37 @abc.abstractproperty 

38 def tweak(self) -> bytes: 

39 """ 

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

41 """ 

42 

43 

44class ModeWithNonce(metaclass=abc.ABCMeta): 

45 @abc.abstractproperty 

46 def nonce(self) -> bytes: 

47 """ 

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

49 """ 

50 

51 

52class ModeWithAuthenticationTag(metaclass=abc.ABCMeta): 

53 @abc.abstractproperty 

54 def tag(self) -> bytes: 

55 """ 

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

57 """ 

58 

59 

60def _check_aes_key_length(self, algorithm): 

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

62 raise ValueError( 

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

64 ) 

65 

66 

67def _check_iv_length(self, algorithm): 

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

69 raise ValueError( 

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

71 len(self.initialization_vector), self.name 

72 ) 

73 ) 

74 

75 

76def _check_nonce_length(nonce: bytes, name: str, algorithm): 

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

78 raise ValueError( 

79 "Invalid nonce size ({}) for {}.".format(len(nonce), name) 

80 ) 

81 

82 

83def _check_iv_and_key_length(self, algorithm): 

84 _check_aes_key_length(self, algorithm) 

85 _check_iv_length(self, algorithm) 

86 

87 

88class CBC(Mode, ModeWithInitializationVector): 

89 name = "CBC" 

90 

91 def __init__(self, initialization_vector: bytes): 

92 utils._check_byteslike("initialization_vector", initialization_vector) 

93 self._initialization_vector = initialization_vector 

94 

95 initialization_vector = utils.read_only_property("_initialization_vector") 

96 validate_for_algorithm = _check_iv_and_key_length 

97 

98 

99class XTS(Mode, ModeWithTweak): 

100 name = "XTS" 

101 

102 def __init__(self, tweak: bytes): 

103 utils._check_byteslike("tweak", tweak) 

104 

105 if len(tweak) != 16: 

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

107 

108 self._tweak = tweak 

109 

110 tweak = utils.read_only_property("_tweak") 

111 

112 def validate_for_algorithm(self, algorithm: CipherAlgorithm): 

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

114 raise ValueError( 

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

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

117 ) 

118 

119 

120class ECB(Mode): 

121 name = "ECB" 

122 

123 validate_for_algorithm = _check_aes_key_length 

124 

125 

126class OFB(Mode, ModeWithInitializationVector): 

127 name = "OFB" 

128 

129 def __init__(self, initialization_vector: bytes): 

130 utils._check_byteslike("initialization_vector", initialization_vector) 

131 self._initialization_vector = initialization_vector 

132 

133 initialization_vector = utils.read_only_property("_initialization_vector") 

134 validate_for_algorithm = _check_iv_and_key_length 

135 

136 

137class CFB(Mode, ModeWithInitializationVector): 

138 name = "CFB" 

139 

140 def __init__(self, initialization_vector: bytes): 

141 utils._check_byteslike("initialization_vector", initialization_vector) 

142 self._initialization_vector = initialization_vector 

143 

144 initialization_vector = utils.read_only_property("_initialization_vector") 

145 validate_for_algorithm = _check_iv_and_key_length 

146 

147 

148class CFB8(Mode, ModeWithInitializationVector): 

149 name = "CFB8" 

150 

151 def __init__(self, initialization_vector: bytes): 

152 utils._check_byteslike("initialization_vector", initialization_vector) 

153 self._initialization_vector = initialization_vector 

154 

155 initialization_vector = utils.read_only_property("_initialization_vector") 

156 validate_for_algorithm = _check_iv_and_key_length 

157 

158 

159class CTR(Mode, ModeWithNonce): 

160 name = "CTR" 

161 

162 def __init__(self, nonce: bytes): 

163 utils._check_byteslike("nonce", nonce) 

164 self._nonce = nonce 

165 

166 nonce = utils.read_only_property("_nonce") 

167 

168 def validate_for_algorithm(self, algorithm: CipherAlgorithm): 

169 _check_aes_key_length(self, algorithm) 

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

171 

172 

173class GCM(Mode, ModeWithInitializationVector, ModeWithAuthenticationTag): 

174 name = "GCM" 

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

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

177 

178 def __init__( 

179 self, 

180 initialization_vector: bytes, 

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

182 min_tag_length: int = 16, 

183 ): 

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

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

186 utils._check_byteslike("initialization_vector", initialization_vector) 

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

188 raise ValueError( 

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

190 "and 1024 bits)." 

191 ) 

192 self._initialization_vector = initialization_vector 

193 if tag is not None: 

194 utils._check_bytes("tag", tag) 

195 if min_tag_length < 4: 

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

197 if len(tag) < min_tag_length: 

198 raise ValueError( 

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

200 min_tag_length 

201 ) 

202 ) 

203 self._tag = tag 

204 self._min_tag_length = min_tag_length 

205 

206 tag = utils.read_only_property("_tag") 

207 initialization_vector = utils.read_only_property("_initialization_vector") 

208 

209 def validate_for_algorithm(self, algorithm: CipherAlgorithm): 

210 _check_aes_key_length(self, algorithm)