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