def __transform_cbc(self, data): len_data = len(data) if len_data % 8: raise VCryptoException('Input not aligned to blocksize') result = [] start = 0 while start < len_data: end = start + 8 block = data[start:end] if self.__encrypt: if _pyver == 2: indata = b''.join([ _s2b(_b_chr(_b_ord(a) ^ _b_ord(b))) for a, b in zip(block, self.__iv) ]) else: indata = bytes([a ^ b for a, b in zip(block, self.__iv)]) cipher = self.__cipher.encipher(indata) self.__iv = cipher result.append(cipher) else: deciphered = self.__cipher.decipher(block) if _pyver == 2: plaintext = b''.join([ _s2b(_b_chr(_b_ord(a) ^ _b_ord(b))) for a, b in zip(deciphered, self.__iv) ]) else: plaintext = bytes( [a ^ b for a, b in zip(deciphered, self.__iv)]) self.__iv = block result.append(plaintext) start += 8 return b''.join(result)
def __transform_cbc(self, data): len_data = len(data) if len_data % self.__in_size: raise VCryptoException('Data not block aligned') result = [] start = 0 while start < len_data: end = start + self.__in_size block = data[start:end] if self.__encrypt: if _pyver == 2: indata = b''.join([ _s2b(_b_chr(_b_ord(a) ^ _b_ord(b))) for a, b in zip(block, self.__iv) ]) else: indata = bytes([a ^ b for a, b in zip(block, self.__iv)]) # Appending random data, similar to RSAES-PKCS1-V1_5-ENCRYPT _rand_data = self.__rand(8) indata = b''.join((b'\x02', indata, b'\x00', _rand_data)) cipher = self.__block_transform(indata, self.__in_size + 10, self.__out_size) # Can only take plaintext size bytes as the carry-on mask self.__iv = cipher[:(self.__plainsize)] result.append(cipher) else: deciphered = self.__block_transform(block, self.__in_size, self.__out_size + 10) if _pyver == 2: if deciphered[0] != b'\x02' or deciphered[-9] != b'\x00': raise VCryptoException('Invalid RSA ciphertext') else: if deciphered[0] != 0x02 or deciphered[-9] != 0x00: raise VCryptoException('Invalid RSA ciphertext') deciphered = deciphered[1:-8] if _pyver == 2: plaintext = b''.join([ _s2b(_b_chr(_b_ord(a) ^ _b_ord(b))) for a, b in zip(deciphered, self.__iv) ]) else: plaintext = bytes( [a ^ b for a, b in zip(deciphered, self.__iv)]) # Can only take plaintext size bytes as the carry-on mask self.__iv = block[:(self.__plainsize)] result.append(plaintext) start += self.__in_size return b''.join(result)
def data(self, num_bytes): l = [0] * num_bytes for gen in self.__generators: data = [_b_ord(c) for c in gen(num_bytes)] for i in xrange(len(l)): l[i] ^= data[i] return b''.join([_s2b(_b_chr(n)) for n in l])
def primedata_to_number(self, prime_data): """Returns a candidate prime number associated with byte data. :param prime_data: data which may represents a prime :type prime_data: bytes :returns: number associated with prime_data :rtype: int, long Sets the two most significant bits and the least significant bit of prime_data, before converting to an int or long. .. warning:: This method does not perform any type of primality check, and it is the responsibility of the caller to check whether the returned number represents a prime. """ bval = [_b_ord(c) for c in prime_data] bval[0] |= 0xc0 bval[-1] |= 0x01 if _pyver == 2: data = b''.join([_s2b(_b_chr(b)) for b in bval]) else: data = bytes(bval) return bytes_to_posint(data)
def number(self, min_num, max_num): """Returns an integer constructed from generated byte data. :param min_num: lowest integer to produce :type min_num: int, long :param max_num: highest integer to produce :type max_num: int, long :returns: number with min_num <= num <= max_num :rtype: int, long The method will internally call :meth:`data` to generate data to create the output number. .. warning:: The method may need to generate multiple iterations of byte data until a number can be constructed. If generated bytes do not appear 'random' (such as e.g. a constant byte value generator), this method may never return. """ if min_num == max_num: return min_num diff = max_num - min_num diff_hex = _s2b(hex(diff)).lstrip(b'0x').rstrip(b'L') if len(diff_hex) % 2: diff_hex = b'0' + diff_hex num_bytes = len(diff_hex) // 2 # Create a mask for the left zero bytes left_byte = int(_b2s(b'0x' + diff_hex[:2]), 16) mask = 0xff for i in xrange(7, -1, -1): bit = 0x01 << i if left_byte & bit: break else: mask = mask ^ bit while True: data = self(num_bytes) data_chars = [_s2b('0x')] first = True for d in data: b = _b_ord(d) if first: b &= mask first = False _chars = hex((b >> 4) & 0xf)[-1] + hex(b & 0xf)[-1] data_chars.append(_s2b(_chars)) data_hex = _s2b('').join(data_chars) num = long(data_hex, 16) if num <= diff: break return min_num + num
def __transform_ofb(self, data): len_data = len(data) if len_data % 8: raise VCryptoException('Input not aligned to blocksize') result = [] start = 0 while start < len_data: end = start + 8 block = data[start:end] # Same for encryption/decryption mask = self.__cipher.encipher(self.__iv) self.__iv = mask if _pyver == 2: cipher = b''.join([ _s2b(_b_chr(_b_ord(a) ^ _b_ord(b))) for a, b in zip(block, mask) ]) else: cipher = bytes([a ^ b for a, b in zip(block, mask)]) result.append(cipher) start += 8 return b''.join(result)
def hmac(cls, secret, message): """Generates and returns a :term:`HMAC` :param secret: secret key :type secret: bytes :param message: message :type message: bytes :returns: message authentication code :rtype: bytes Implements :term:`HMAC` algorithm defined by :rfc:`2104`\ . """ blocksize = cls.digest_size() if len(secret) > blocksize: secret = cls(secret).digest() elif len(secret) < blocksize: secret += (blocksize - len(secret)) * b'\x00' i_pad = blocksize * b'\x36' # TODO - TEMPORARY FIX FOR PYTHON 3 CONVERSION PROBLEM if _pyver == 3: inner = b''.join( _b_chr(_b_ord(a) ^ _b_ord(b)) for a, b in zip(secret, i_pad)) else: inner = b''.join( _s2b(_b_chr(_b_ord(a) ^ _b_ord(b))) for a, b in zip(secret, i_pad)) i_hash = cls(inner) i_hash.update(message) i_digest = i_hash.digest() o_pad = blocksize * b'\x5c' # TODO - TEMPORARY FIX FOR PYTHON 3 CONVERSION PROBLEM if _pyver == 3: outer = b''.join( _b_chr(_b_ord(a) ^ _b_ord(b)) for a, b in zip(secret, o_pad)) else: outer = b''.join( _s2b(_b_chr(_b_ord(a) ^ _b_ord(b))) for a, b in zip(secret, o_pad)) o_hash = cls(outer) o_hash.update(i_digest) return o_hash.digest()
def _increment(self, block): """Performs an increment of the previously generated block. :param block: input block :returns: output block Default is interpret the input block as an integer and to increment it by '1'. Derived classes can override to implement a different incrementation strategy. """ nums = [_b_ord(c) for c in block] for i in xrange((len(nums) - 1), -1, -1): if nums[i] == 255: nums[i] = 0 else: nums[i] += 1 break else: nums = [0] * len(nums) return b''.join([_s2b(_b_chr(n)) for n in nums])
def _decipher_block(self, block): if not isinstance(block, bytes) or len(block) != 8: raise VCryptoException('Data block must be bytes of len 8') b_l = ((_b_ord(block[0]) << 24) + (_b_ord(block[1]) << 16) + (_b_ord(block[2]) << 8) + _b_ord(block[3])) b_r = ((_b_ord(block[4]) << 24) + (_b_ord(block[5]) << 16) + (_b_ord(block[6]) << 8) + _b_ord(block[7])) P, S = self.__P, self.__S for i in xrange(17, 1, -1): b_l ^= P[i] b_r ^= self.__feistel(b_l) b_l, b_r = b_r, b_l b_l, b_r = b_r, b_l b_r ^= P[1] b_l ^= P[0] bval = [(b_l >> 24), (b_l >> 16), (b_l >> 8), b_l, (b_r >> 24), (b_r >> 16), (b_r >> 8), b_r] if _pyver == 2: return b''.join([_s2b(_b_chr(b & 0xff)) for b in bval]) else: return bytes([b & 0xff for b in bval])
def read(self, data): """Reads encrypted message data. :param data: input data to decrypt and decode :type data: bytes, :class:`versile.common.util.VByteBuffer` :returns: number of bytes read :rtype: int Reads only as much data as is required to complete processing a complete single message. If data is of type :class:`versile.common.util.VByteBuffer` then the data that was read will be popped off the buffer. .. note:: When decryption of one message has completed, :meth:`reset` must be called before a new message can be read. """ if isinstance(data, bytes): read_buf = self._read_buf read_buf.remove() read_buf.append(data) elif isinstance(data, VByteBuffer): read_buf = data else: raise TypeError('Input must be bytes or VByteBuffer') num_read = 0 _pbsize = self._plaintext_blocksize _cbsize = self._cipher_blocksize while read_buf and self._result is None and not self._invalid: # First decode single block to get blocksize if not self._have_len: max_read = _cbsize - len(self._in_buf) enc_data = read_buf.pop(max_read) self._in_buf.append(enc_data) num_read += len(enc_data) if len(self._in_buf) == _cbsize: enc_data = self._in_buf.pop() block = self._decrypter(enc_data) if _pbsize is None: _pbsize = len(block) self._plaintext_blocksize = _pbsize self._msg_buf.append(block) len_bytes = self._msg_buf.peek(2) if _pyver == 2: self._plaintext_len = 1 + ( (_b_ord(len_bytes[0]) << 8) + _b_ord(len_bytes[1])) else: self._plaintext_len = 1 + ( (len_bytes[0] << 8) + len_bytes[1]) self._have_len = True # If we have first block, decrypt more blocks as available/needed if self._have_len: msg_len = 2 + self._plaintext_len + self._hash_len pad_len = msg_len % _pbsize if pad_len: pad_len = _pbsize - pad_len msg_len += pad_len msg_left = msg_len - len(self._msg_buf) blocks_left = msg_left // _pbsize input_left = (blocks_left * _cbsize - len(self._in_buf)) in_data = read_buf.pop(input_left) num_read += len(in_data) self._in_buf.append(in_data) num_decode = len(self._in_buf) num_decode -= num_decode % _cbsize if num_decode > 0: enc_data = self._in_buf.pop(num_decode) self._msg_buf.append(self._decrypter(enc_data)) elif len(self._msg_buf) != msg_len: break if self._have_len and len(self._msg_buf) == msg_len: len_bytes = self._msg_buf.pop(2) plaintext = self._msg_buf.pop(self._plaintext_len) padding = self._msg_buf.pop(pad_len) msg_hash = self._msg_buf.pop(self._hash_len) _mac_msg = b''.join((posint_to_bytes(self._msg_num), len_bytes, plaintext, padding)) if msg_hash == self._hash_cls.hmac(self._mac_secret, _mac_msg): self._result = plaintext self._msg_num += 1 else: self._invalid = True return num_read
def __init__(self, key): if not isinstance(key, bytes): raise VCryptoException('Key must be a bytes object') elif len(key) > 56: raise VCryptoException('Max key length is 448 bits (56 bytes)') P, S = copy.deepcopy(_P_INIT), copy.deepcopy(_S_INIT) self.__P, self.__S = P, S keylen = len(key) j = 0 for i in xrange(len(P)): data = 0 for k in xrange(4): data = ((data << 8) & 0xffffffff) | _b_ord(key[j]) j += 1 if j >= keylen: j = 0 P[i] ^= data data = 8 * b'\x00' for i in xrange(0, len(P), 2): data = self.encipher(data) P[i] = ((_b_ord(data[0]) << 24) + (_b_ord(data[1]) << 16) + (_b_ord(data[2]) << 8) + _b_ord(data[3])) P[i + 1] = ((_b_ord(data[4]) << 24) + (_b_ord(data[5]) << 16) + (_b_ord(data[6]) << 8) + _b_ord(data[7])) for i in xrange(4): for j in xrange(0, 256, 2): data = self.encipher(data) S[i][j] = ((_b_ord(data[0]) << 24) + (_b_ord(data[1]) << 16) + (_b_ord(data[2]) << 8) + _b_ord(data[3])) S[i][j + 1] = ((_b_ord(data[4]) << 24) + (_b_ord(data[5]) << 16) + (_b_ord(data[6]) << 8) + _b_ord(data[7]))