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

117 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +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 AlreadyFinalized 

12from cryptography.hazmat.bindings._rust import ( 

13 check_ansix923_padding, 

14 check_pkcs7_padding, 

15) 

16 

17 

18class PaddingContext(metaclass=abc.ABCMeta): 

19 @abc.abstractmethod 

20 def update(self, data: bytes) -> bytes: 

21 """ 

22 Pads the provided bytes and returns any available data as bytes. 

23 """ 

24 

25 @abc.abstractmethod 

26 def finalize(self) -> bytes: 

27 """ 

28 Finalize the padding, returns bytes. 

29 """ 

30 

31 

32def _byte_padding_check(block_size: int) -> None: 

33 if not (0 <= block_size <= 2040): 

34 raise ValueError("block_size must be in range(0, 2041).") 

35 

36 if block_size % 8 != 0: 

37 raise ValueError("block_size must be a multiple of 8.") 

38 

39 

40def _byte_padding_update( 

41 buffer_: typing.Optional[bytes], data: bytes, block_size: int 

42) -> typing.Tuple[bytes, bytes]: 

43 if buffer_ is None: 

44 raise AlreadyFinalized("Context was already finalized.") 

45 

46 utils._check_byteslike("data", data) 

47 

48 buffer_ += bytes(data) 

49 

50 finished_blocks = len(buffer_) // (block_size // 8) 

51 

52 result = buffer_[: finished_blocks * (block_size // 8)] 

53 buffer_ = buffer_[finished_blocks * (block_size // 8) :] 

54 

55 return buffer_, result 

56 

57 

58def _byte_padding_pad( 

59 buffer_: typing.Optional[bytes], 

60 block_size: int, 

61 paddingfn: typing.Callable[[int], bytes], 

62) -> bytes: 

63 if buffer_ is None: 

64 raise AlreadyFinalized("Context was already finalized.") 

65 

66 pad_size = block_size // 8 - len(buffer_) 

67 return buffer_ + paddingfn(pad_size) 

68 

69 

70def _byte_unpadding_update( 

71 buffer_: typing.Optional[bytes], data: bytes, block_size: int 

72) -> typing.Tuple[bytes, bytes]: 

73 if buffer_ is None: 

74 raise AlreadyFinalized("Context was already finalized.") 

75 

76 utils._check_byteslike("data", data) 

77 

78 buffer_ += bytes(data) 

79 

80 finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0) 

81 

82 result = buffer_[: finished_blocks * (block_size // 8)] 

83 buffer_ = buffer_[finished_blocks * (block_size // 8) :] 

84 

85 return buffer_, result 

86 

87 

88def _byte_unpadding_check( 

89 buffer_: typing.Optional[bytes], 

90 block_size: int, 

91 checkfn: typing.Callable[[bytes], int], 

92) -> bytes: 

93 if buffer_ is None: 

94 raise AlreadyFinalized("Context was already finalized.") 

95 

96 if len(buffer_) != block_size // 8: 

97 raise ValueError("Invalid padding bytes.") 

98 

99 valid = checkfn(buffer_) 

100 

101 if not valid: 

102 raise ValueError("Invalid padding bytes.") 

103 

104 pad_size = buffer_[-1] 

105 return buffer_[:-pad_size] 

106 

107 

108class PKCS7: 

109 def __init__(self, block_size: int): 

110 _byte_padding_check(block_size) 

111 self.block_size = block_size 

112 

113 def padder(self) -> PaddingContext: 

114 return _PKCS7PaddingContext(self.block_size) 

115 

116 def unpadder(self) -> PaddingContext: 

117 return _PKCS7UnpaddingContext(self.block_size) 

118 

119 

120class _PKCS7PaddingContext(PaddingContext): 

121 _buffer: typing.Optional[bytes] 

122 

123 def __init__(self, block_size: int): 

124 self.block_size = block_size 

125 # TODO: more copies than necessary, we should use zero-buffer (#193) 

126 self._buffer = b"" 

127 

128 def update(self, data: bytes) -> bytes: 

129 self._buffer, result = _byte_padding_update( 

130 self._buffer, data, self.block_size 

131 ) 

132 return result 

133 

134 def _padding(self, size: int) -> bytes: 

135 return bytes([size]) * size 

136 

137 def finalize(self) -> bytes: 

138 result = _byte_padding_pad( 

139 self._buffer, self.block_size, self._padding 

140 ) 

141 self._buffer = None 

142 return result 

143 

144 

145class _PKCS7UnpaddingContext(PaddingContext): 

146 _buffer: typing.Optional[bytes] 

147 

148 def __init__(self, block_size: int): 

149 self.block_size = block_size 

150 # TODO: more copies than necessary, we should use zero-buffer (#193) 

151 self._buffer = b"" 

152 

153 def update(self, data: bytes) -> bytes: 

154 self._buffer, result = _byte_unpadding_update( 

155 self._buffer, data, self.block_size 

156 ) 

157 return result 

158 

159 def finalize(self) -> bytes: 

160 result = _byte_unpadding_check( 

161 self._buffer, self.block_size, check_pkcs7_padding 

162 ) 

163 self._buffer = None 

164 return result 

165 

166 

167class ANSIX923: 

168 def __init__(self, block_size: int): 

169 _byte_padding_check(block_size) 

170 self.block_size = block_size 

171 

172 def padder(self) -> PaddingContext: 

173 return _ANSIX923PaddingContext(self.block_size) 

174 

175 def unpadder(self) -> PaddingContext: 

176 return _ANSIX923UnpaddingContext(self.block_size) 

177 

178 

179class _ANSIX923PaddingContext(PaddingContext): 

180 _buffer: typing.Optional[bytes] 

181 

182 def __init__(self, block_size: int): 

183 self.block_size = block_size 

184 # TODO: more copies than necessary, we should use zero-buffer (#193) 

185 self._buffer = b"" 

186 

187 def update(self, data: bytes) -> bytes: 

188 self._buffer, result = _byte_padding_update( 

189 self._buffer, data, self.block_size 

190 ) 

191 return result 

192 

193 def _padding(self, size: int) -> bytes: 

194 return bytes([0]) * (size - 1) + bytes([size]) 

195 

196 def finalize(self) -> bytes: 

197 result = _byte_padding_pad( 

198 self._buffer, self.block_size, self._padding 

199 ) 

200 self._buffer = None 

201 return result 

202 

203 

204class _ANSIX923UnpaddingContext(PaddingContext): 

205 _buffer: typing.Optional[bytes] 

206 

207 def __init__(self, block_size: int): 

208 self.block_size = block_size 

209 # TODO: more copies than necessary, we should use zero-buffer (#193) 

210 self._buffer = b"" 

211 

212 def update(self, data: bytes) -> bytes: 

213 self._buffer, result = _byte_unpadding_update( 

214 self._buffer, data, self.block_size 

215 ) 

216 return result 

217 

218 def finalize(self) -> bytes: 

219 result = _byte_unpadding_check( 

220 self._buffer, 

221 self.block_size, 

222 check_ansix923_padding, 

223 ) 

224 self._buffer = None 

225 return result