source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/contrib/pyaes/blockfeeder.py

main
Last change on this file was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 7.9 KB
Line 
1# The MIT License (MIT)
2#
3# Copyright (c) 2014 Richard Moore
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy
6# of this software and associated documentation files (the "Software"), to deal
7# in the Software without restriction, including without limitation the rights
8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9# copies of the Software, and to permit persons to whom the Software is
10# furnished to do so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in
13# all copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21# THE SOFTWARE.
22
23
24from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation
25from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable
26
27
28# First we inject three functions to each of the modes of operations
29#
30#    _can_consume(size)
31#       - Given a size, determine how many bytes could be consumed in
32#         a single call to either the decrypt or encrypt method
33#
34#    _final_encrypt(data, padding = PADDING_DEFAULT)
35#       - call and return encrypt on this (last) chunk of data,
36#         padding as necessary; this will always be at least 16
37#         bytes unless the total incoming input was less than 16
38#         bytes
39#
40#    _final_decrypt(data, padding = PADDING_DEFAULT)
41#       - same as _final_encrypt except for decrypt, for
42#         stripping off padding
43#
44
45PADDING_NONE       = 'none'
46PADDING_DEFAULT    = 'default'
47
48# @TODO: Ciphertext stealing and explicit PKCS#7
49# PADDING_CIPHERTEXT_STEALING
50# PADDING_PKCS7
51
52# ECB and CBC are block-only ciphers
53
54def _block_can_consume(self, size):
55    if size >= 16: return 16
56    return 0
57
58# After padding, we may have more than one block
59def _block_final_encrypt(self, data, padding = PADDING_DEFAULT):
60    if padding == PADDING_DEFAULT:
61        data = append_PKCS7_padding(data)
62
63    elif padding == PADDING_NONE:
64        if len(data) != 16:
65            raise Exception('invalid data length for final block')
66    else:
67        raise Exception('invalid padding option')
68
69    if len(data) == 32:
70        return self.encrypt(data[:16]) + self.encrypt(data[16:])
71
72    return self.encrypt(data)
73
74
75def _block_final_decrypt(self, data, padding = PADDING_DEFAULT):
76    if padding == PADDING_DEFAULT:
77        return strip_PKCS7_padding(self.decrypt(data))
78
79    if padding == PADDING_NONE:
80        if len(data) != 16:
81            raise Exception('invalid data length for final block')
82        return self.decrypt(data)
83
84    raise Exception('invalid padding option')
85
86AESBlockModeOfOperation._can_consume = _block_can_consume
87AESBlockModeOfOperation._final_encrypt = _block_final_encrypt
88AESBlockModeOfOperation._final_decrypt = _block_final_decrypt
89
90
91
92# CFB is a segment cipher
93
94def _segment_can_consume(self, size):
95    return self.segment_bytes * int(size // self.segment_bytes)
96
97# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
98def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT):
99    if padding != PADDING_DEFAULT:
100        raise Exception('invalid padding option')
101
102    faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
103    padded = data + to_bufferable(faux_padding)
104    return self.encrypt(padded)[:len(data)]
105
106# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
107def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT):
108    if padding != PADDING_DEFAULT:
109        raise Exception('invalid padding option')
110
111    faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
112    padded = data + to_bufferable(faux_padding)
113    return self.decrypt(padded)[:len(data)]
114
115AESSegmentModeOfOperation._can_consume = _segment_can_consume
116AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt
117AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt
118
119
120
121# OFB and CTR are stream ciphers
122
123def _stream_can_consume(self, size):
124    return size
125
126def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT):
127    if padding not in [PADDING_NONE, PADDING_DEFAULT]:
128        raise Exception('invalid padding option')
129
130    return self.encrypt(data)
131
132def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT):
133    if padding not in [PADDING_NONE, PADDING_DEFAULT]:
134        raise Exception('invalid padding option')
135
136    return self.decrypt(data)
137
138AESStreamModeOfOperation._can_consume = _stream_can_consume
139AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt
140AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt
141
142
143
144class BlockFeeder(object):
145    '''The super-class for objects to handle chunking a stream of bytes
146       into the appropriate block size for the underlying mode of operation
147       and applying (or stripping) padding, as necessary.'''
148
149    def __init__(self, mode, feed, final, padding = PADDING_DEFAULT):
150        self._mode = mode
151        self._feed = feed
152        self._final = final
153        self._buffer = to_bufferable("")
154        self._padding = padding
155
156    def feed(self, data = None):
157        '''Provide bytes to encrypt (or decrypt), returning any bytes
158           possible from this or any previous calls to feed.
159
160           Call with None or an empty string to flush the mode of
161           operation and return any final bytes; no further calls to
162           feed may be made.'''
163
164        if self._buffer is None:
165            raise ValueError('already finished feeder')
166
167        # Finalize; process the spare bytes we were keeping
168        if not data:
169            result = self._final(self._buffer, self._padding)
170            self._buffer = None
171            return result
172
173        self._buffer += to_bufferable(data)
174
175        # We keep 16 bytes around so we can determine padding
176        result = to_bufferable('')
177        while len(self._buffer) > 16:
178            can_consume = self._mode._can_consume(len(self._buffer) - 16)
179            if can_consume == 0: break
180            result += self._feed(self._buffer[:can_consume])
181            self._buffer = self._buffer[can_consume:]
182
183        return result
184
185
186class Encrypter(BlockFeeder):
187    'Accepts bytes of plaintext and returns encrypted ciphertext.'
188
189    def __init__(self, mode, padding = PADDING_DEFAULT):
190        BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding)
191
192
193class Decrypter(BlockFeeder):
194    'Accepts bytes of ciphertext and returns decrypted plaintext.'
195
196    def __init__(self, mode, padding = PADDING_DEFAULT):
197        BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding)
198
199
200# 8kb blocks
201BLOCK_SIZE = (1 << 13)
202
203def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE):
204    'Uses feeder to read and convert from in_stream and write to out_stream.'
205
206    while True:
207        chunk = in_stream.read(block_size)
208        if not chunk:
209            break
210        converted = feeder.feed(chunk)
211        out_stream.write(converted)
212    converted = feeder.feed()
213    out_stream.write(converted)
214
215
216def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
217    'Encrypts a stream of bytes from in_stream to out_stream using mode.'
218
219    encrypter = Encrypter(mode, padding = padding)
220    _feed_stream(encrypter, in_stream, out_stream, block_size)
221
222
223def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
224    'Decrypts a stream of bytes from in_stream to out_stream using mode.'
225
226    decrypter = Decrypter(mode, padding = padding)
227    _feed_stream(decrypter, in_stream, out_stream, block_size)
Note: See TracBrowser for help on using the repository browser.