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

116 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +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 

5 

6import abc 

7import typing 

8 

9from cryptography import utils 

10from cryptography.exceptions import AlreadyFinalized 

11from cryptography.hazmat.bindings._rust import ( 

12 check_ansix923_padding, 

13 check_pkcs7_padding, 

14) 

15 

16 

17class PaddingContext(metaclass=abc.ABCMeta): 

18 @abc.abstractmethod 

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

20 """ 

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

22 """ 

23 

24 @abc.abstractmethod 

25 def finalize(self) -> bytes: 

26 """ 

27 Finalize the padding, returns bytes. 

28 """ 

29 

30 

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

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

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

34 

35 if block_size % 8 != 0: 

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

37 

38 

39def _byte_padding_update( 

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

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

42 if buffer_ is None: 

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

44 

45 utils._check_byteslike("data", data) 

46 

47 buffer_ += bytes(data) 

48 

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

50 

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

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

53 

54 return buffer_, result 

55 

56 

57def _byte_padding_pad( 

58 buffer_: typing.Optional[bytes], 

59 block_size: int, 

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

61) -> bytes: 

62 if buffer_ is None: 

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

64 

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

66 return buffer_ + paddingfn(pad_size) 

67 

68 

69def _byte_unpadding_update( 

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

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

72 if buffer_ is None: 

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

74 

75 utils._check_byteslike("data", data) 

76 

77 buffer_ += bytes(data) 

78 

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

80 

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

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

83 

84 return buffer_, result 

85 

86 

87def _byte_unpadding_check( 

88 buffer_: typing.Optional[bytes], 

89 block_size: int, 

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

91) -> bytes: 

92 if buffer_ is None: 

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

94 

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

96 raise ValueError("Invalid padding bytes.") 

97 

98 valid = checkfn(buffer_) 

99 

100 if not valid: 

101 raise ValueError("Invalid padding bytes.") 

102 

103 pad_size = buffer_[-1] 

104 return buffer_[:-pad_size] 

105 

106 

107class PKCS7: 

108 def __init__(self, block_size: int): 

109 _byte_padding_check(block_size) 

110 self.block_size = block_size 

111 

112 def padder(self) -> PaddingContext: 

113 return _PKCS7PaddingContext(self.block_size) 

114 

115 def unpadder(self) -> PaddingContext: 

116 return _PKCS7UnpaddingContext(self.block_size) 

117 

118 

119class _PKCS7PaddingContext(PaddingContext): 

120 _buffer: typing.Optional[bytes] 

121 

122 def __init__(self, block_size: int): 

123 self.block_size = block_size 

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

125 self._buffer = b"" 

126 

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

128 self._buffer, result = _byte_padding_update( 

129 self._buffer, data, self.block_size 

130 ) 

131 return result 

132 

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

134 return bytes([size]) * size 

135 

136 def finalize(self) -> bytes: 

137 result = _byte_padding_pad( 

138 self._buffer, self.block_size, self._padding 

139 ) 

140 self._buffer = None 

141 return result 

142 

143 

144class _PKCS7UnpaddingContext(PaddingContext): 

145 _buffer: typing.Optional[bytes] 

146 

147 def __init__(self, block_size: int): 

148 self.block_size = block_size 

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

150 self._buffer = b"" 

151 

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

153 self._buffer, result = _byte_unpadding_update( 

154 self._buffer, data, self.block_size 

155 ) 

156 return result 

157 

158 def finalize(self) -> bytes: 

159 result = _byte_unpadding_check( 

160 self._buffer, self.block_size, check_pkcs7_padding 

161 ) 

162 self._buffer = None 

163 return result 

164 

165 

166class ANSIX923: 

167 def __init__(self, block_size: int): 

168 _byte_padding_check(block_size) 

169 self.block_size = block_size 

170 

171 def padder(self) -> PaddingContext: 

172 return _ANSIX923PaddingContext(self.block_size) 

173 

174 def unpadder(self) -> PaddingContext: 

175 return _ANSIX923UnpaddingContext(self.block_size) 

176 

177 

178class _ANSIX923PaddingContext(PaddingContext): 

179 _buffer: typing.Optional[bytes] 

180 

181 def __init__(self, block_size: int): 

182 self.block_size = block_size 

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

184 self._buffer = b"" 

185 

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

187 self._buffer, result = _byte_padding_update( 

188 self._buffer, data, self.block_size 

189 ) 

190 return result 

191 

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

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

194 

195 def finalize(self) -> bytes: 

196 result = _byte_padding_pad( 

197 self._buffer, self.block_size, self._padding 

198 ) 

199 self._buffer = None 

200 return result 

201 

202 

203class _ANSIX923UnpaddingContext(PaddingContext): 

204 _buffer: typing.Optional[bytes] 

205 

206 def __init__(self, block_size: int): 

207 self.block_size = block_size 

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

209 self._buffer = b"" 

210 

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

212 self._buffer, result = _byte_unpadding_update( 

213 self._buffer, data, self.block_size 

214 ) 

215 return result 

216 

217 def finalize(self) -> bytes: 

218 result = _byte_unpadding_check( 

219 self._buffer, 

220 self.block_size, 

221 check_ansix923_padding, 

222 ) 

223 self._buffer = None 

224 return result