• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# -*- coding: utf-8 -*-
2#
3#  Copyright 2011 Sybren A. Stüvel <sybren@stuvel.eu>
4#
5#  Licensed under the Apache License, Version 2.0 (the "License");
6#  you may not use this file except in compliance with the License.
7#  You may obtain a copy of the License at
8#
9#      https://www.apache.org/licenses/LICENSE-2.0
10#
11#  Unless required by applicable law or agreed to in writing, software
12#  distributed under the License is distributed on an "AS IS" BASIS,
13#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#  See the License for the specific language governing permissions and
15#  limitations under the License.
16
17"""Data transformation functions.
18
19From bytes to a number, number to bytes, etc.
20"""
21
22from __future__ import absolute_import
23
24import binascii
25from struct import pack
26
27from rsa._compat import byte, is_integer
28from rsa import common, machine_size
29
30
31def bytes2int(raw_bytes):
32    r"""Converts a list of bytes or an 8-bit string to an integer.
33
34    When using unicode strings, encode it to some encoding like UTF8 first.
35
36    >>> (((128 * 256) + 64) * 256) + 15
37    8405007
38    >>> bytes2int(b'\x80@\x0f')
39    8405007
40
41    """
42
43    return int(binascii.hexlify(raw_bytes), 16)
44
45
46def _int2bytes(number, block_size=None):
47    r"""Converts a number to a string of bytes.
48
49    Usage::
50
51        >>> _int2bytes(123456789)
52        b'\x07[\xcd\x15'
53        >>> bytes2int(_int2bytes(123456789))
54        123456789
55
56        >>> _int2bytes(123456789, 6)
57        b'\x00\x00\x07[\xcd\x15'
58        >>> bytes2int(_int2bytes(123456789, 128))
59        123456789
60
61        >>> _int2bytes(123456789, 3)
62        Traceback (most recent call last):
63        ...
64        OverflowError: Needed 4 bytes for number, but block size is 3
65
66    @param number: the number to convert
67    @param block_size: the number of bytes to output. If the number encoded to
68        bytes is less than this, the block will be zero-padded. When not given,
69        the returned block is not padded.
70
71    @throws OverflowError when block_size is given and the number takes up more
72        bytes than fit into the block.
73    """
74
75    # Type checking
76    if not is_integer(number):
77        raise TypeError("You must pass an integer for 'number', not %s" %
78                        number.__class__)
79
80    if number < 0:
81        raise ValueError('Negative numbers cannot be used: %i' % number)
82
83    # Do some bounds checking
84    if number == 0:
85        needed_bytes = 1
86        raw_bytes = [b'\x00']
87    else:
88        needed_bytes = common.byte_size(number)
89        raw_bytes = []
90
91    # You cannot compare None > 0 in Python 3x. It will fail with a TypeError.
92    if block_size and block_size > 0:
93        if needed_bytes > block_size:
94            raise OverflowError('Needed %i bytes for number, but block size '
95                                'is %i' % (needed_bytes, block_size))
96
97    # Convert the number to bytes.
98    while number > 0:
99        raw_bytes.insert(0, byte(number & 0xFF))
100        number >>= 8
101
102    # Pad with zeroes to fill the block
103    if block_size and block_size > 0:
104        padding = (block_size - needed_bytes) * b'\x00'
105    else:
106        padding = b''
107
108    return padding + b''.join(raw_bytes)
109
110
111def bytes_leading(raw_bytes, needle=b'\x00'):
112    """
113    Finds the number of prefixed byte occurrences in the haystack.
114
115    Useful when you want to deal with padding.
116
117    :param raw_bytes:
118        Raw bytes.
119    :param needle:
120        The byte to count. Default \x00.
121    :returns:
122        The number of leading needle bytes.
123    """
124
125    leading = 0
126    # Indexing keeps compatibility between Python 2.x and Python 3.x
127    _byte = needle[0]
128    for x in raw_bytes:
129        if x == _byte:
130            leading += 1
131        else:
132            break
133    return leading
134
135
136def int2bytes(number, fill_size=None, chunk_size=None, overflow=False):
137    """
138    Convert an unsigned integer to bytes (base-256 representation)::
139
140    Does not preserve leading zeros if you don't specify a chunk size or
141    fill size.
142
143    .. NOTE:
144        You must not specify both fill_size and chunk_size. Only one
145        of them is allowed.
146
147    :param number:
148        Integer value
149    :param fill_size:
150        If the optional fill size is given the length of the resulting
151        byte string is expected to be the fill size and will be padded
152        with prefix zero bytes to satisfy that length.
153    :param chunk_size:
154        If optional chunk size is given and greater than zero, pad the front of
155        the byte string with binary zeros so that the length is a multiple of
156        ``chunk_size``.
157    :param overflow:
158        ``False`` (default). If this is ``True``, no ``OverflowError``
159        will be raised when the fill_size is shorter than the length
160        of the generated byte sequence. Instead the byte sequence will
161        be returned as is.
162    :returns:
163        Raw bytes (base-256 representation).
164    :raises:
165        ``OverflowError`` when fill_size is given and the number takes up more
166        bytes than fit into the block. This requires the ``overflow``
167        argument to this function to be set to ``False`` otherwise, no
168        error will be raised.
169    """
170
171    if number < 0:
172        raise ValueError("Number must be an unsigned integer: %d" % number)
173
174    if fill_size and chunk_size:
175        raise ValueError("You can either fill or pad chunks, but not both")
176
177    # Ensure these are integers.
178    number & 1
179
180    raw_bytes = b''
181
182    # Pack the integer one machine word at a time into bytes.
183    num = number
184    word_bits, _, max_uint, pack_type = machine_size.get_word_alignment(num)
185    pack_format = ">%s" % pack_type
186    while num > 0:
187        raw_bytes = pack(pack_format, num & max_uint) + raw_bytes
188        num >>= word_bits
189    # Obtain the index of the first non-zero byte.
190    zero_leading = bytes_leading(raw_bytes)
191    if number == 0:
192        raw_bytes = b'\x00'
193    # De-padding.
194    raw_bytes = raw_bytes[zero_leading:]
195
196    length = len(raw_bytes)
197    if fill_size and fill_size > 0:
198        if not overflow and length > fill_size:
199            raise OverflowError(
200                    "Need %d bytes for number, but fill size is %d" %
201                    (length, fill_size)
202            )
203        raw_bytes = raw_bytes.rjust(fill_size, b'\x00')
204    elif chunk_size and chunk_size > 0:
205        remainder = length % chunk_size
206        if remainder:
207            padding_size = chunk_size - remainder
208            raw_bytes = raw_bytes.rjust(length + padding_size, b'\x00')
209    return raw_bytes
210
211
212if __name__ == '__main__':
213    import doctest
214
215    doctest.testmod()
216