Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy_utils/types/encrypted/padding.py: 29%

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

78 statements  

1class InvalidPaddingError(Exception): 

2 pass 

3 

4 

5class Padding: 

6 """Base class for padding and unpadding.""" 

7 

8 def __init__(self, block_size): 

9 self.block_size = block_size 

10 

11 def pad(self, value): 

12 raise NotImplementedError('Subclasses must implement this!') 

13 

14 def unpad(self, value): 

15 raise NotImplementedError('Subclasses must implement this!') 

16 

17 

18class PKCS5Padding(Padding): 

19 """Provide PKCS5 padding and unpadding.""" 

20 

21 def pad(self, value): 

22 if not isinstance(value, bytes): 

23 value = value.encode() 

24 padding_length = self.block_size - len(value) % self.block_size 

25 padding_sequence = padding_length * bytes((padding_length,)) 

26 value_with_padding = value + padding_sequence 

27 

28 return value_with_padding 

29 

30 def unpad(self, value): 

31 # Perform some input validations. 

32 # In case of error, we throw a generic InvalidPaddingError() 

33 if not value or len(value) < self.block_size: 

34 # PKCS5 padded output will always be at least 1 block size 

35 raise InvalidPaddingError() 

36 if len(value) % self.block_size != 0: 

37 # PKCS5 padded output will be a multiple of the block size 

38 raise InvalidPaddingError() 

39 if isinstance(value, bytes): 

40 padding_length = value[-1] 

41 if isinstance(value, str): 

42 padding_length = ord(value[-1]) 

43 if padding_length == 0 or padding_length > self.block_size: 

44 raise InvalidPaddingError() 

45 

46 def convert_byte_or_char_to_number(x): 

47 return ord(x) if isinstance(x, str) else x 

48 

49 if any( 

50 [ 

51 padding_length != convert_byte_or_char_to_number(x) 

52 for x in value[-padding_length:] 

53 ] 

54 ): 

55 raise InvalidPaddingError() 

56 

57 value_without_padding = value[0:-padding_length] 

58 

59 return value_without_padding 

60 

61 

62class OneAndZeroesPadding(Padding): 

63 """Provide the one and zeroes padding and unpadding. 

64 

65 This mechanism pads with 0x80 followed by zero bytes. 

66 For unpadding it strips off all trailing zero bytes and the 0x80 byte. 

67 """ 

68 

69 BYTE_80 = 0x80 

70 BYTE_00 = 0x00 

71 

72 def pad(self, value): 

73 if not isinstance(value, bytes): 

74 value = value.encode() 

75 padding_length = self.block_size - len(value) % self.block_size 

76 one_part_bytes = bytes((self.BYTE_80,)) 

77 zeroes_part_bytes = (padding_length - 1) * bytes((self.BYTE_00,)) 

78 padding_sequence = one_part_bytes + zeroes_part_bytes 

79 value_with_padding = value + padding_sequence 

80 

81 return value_with_padding 

82 

83 def unpad(self, value): 

84 value_without_padding = value.rstrip(bytes((self.BYTE_00,))) 

85 value_without_padding = value_without_padding.rstrip(bytes((self.BYTE_80,))) 

86 

87 return value_without_padding 

88 

89 

90class ZeroesPadding(Padding): 

91 """Provide zeroes padding and unpadding. 

92 

93 This mechanism pads with 0x00 except the last byte equals 

94 to the padding length. For unpadding it reads the last byte 

95 and strips off that many bytes. 

96 """ 

97 

98 BYTE_00 = 0x00 

99 

100 def pad(self, value): 

101 if not isinstance(value, bytes): 

102 value = value.encode() 

103 padding_length = self.block_size - len(value) % self.block_size 

104 zeroes_part_bytes = (padding_length - 1) * bytes((self.BYTE_00,)) 

105 last_part_bytes = bytes((padding_length,)) 

106 padding_sequence = zeroes_part_bytes + last_part_bytes 

107 value_with_padding = value + padding_sequence 

108 

109 return value_with_padding 

110 

111 def unpad(self, value): 

112 if isinstance(value, bytes): 

113 padding_length = value[-1] 

114 if isinstance(value, str): 

115 padding_length = ord(value[-1]) 

116 value_without_padding = value[0:-padding_length] 

117 

118 return value_without_padding 

119 

120 

121class NaivePadding(Padding): 

122 """Naive padding and unpadding using '*'. 

123 

124 The class is provided only for backwards compatibility. 

125 """ 

126 

127 CHARACTER = b'*' 

128 

129 def pad(self, value): 

130 num_of_bytes = self.block_size - len(value) % self.block_size 

131 value_with_padding = value + num_of_bytes * self.CHARACTER 

132 

133 return value_with_padding 

134 

135 def unpad(self, value): 

136 value_without_padding = value.rstrip(self.CHARACTER) 

137 

138 return value_without_padding 

139 

140 

141PADDING_MECHANISM = { 

142 'pkcs5': PKCS5Padding, 

143 'oneandzeroes': OneAndZeroesPadding, 

144 'zeroes': ZeroesPadding, 

145 'naive': NaivePadding, 

146}