Skip to content

Commit

Permalink
Merge 7df73e3 into e8da6cf
Browse files Browse the repository at this point in the history
  • Loading branch information
tomato42 committed Sep 24, 2020
2 parents e8da6cf + 7df73e3 commit e695f54
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 135 deletions.
12 changes: 11 additions & 1 deletion tlslite/bufferedsocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(self, socket):
self.socket = socket
self._write_queue = deque()
self.buffer_writes = False
self._read_buffer = bytearray()

def send(self, data):
"""Send data to the socket"""
Expand All @@ -51,7 +52,16 @@ def flush(self):

def recv(self, bufsize):
"""Receive data from socket (socket emulation)"""
return self.socket.recv(bufsize)
if self._read_buffer: # and len(self._read_buffer) < bufsize:
ret = self._read_buffer[:bufsize]
self._read_buffer = self._read_buffer[bufsize:]
return ret
read = bufsize - len(self._read_buffer)
read_bytes = self.socket.recv(max(4096, read))
self._read_buffer += read_bytes
ret = self._read_buffer[:bufsize]
self._read_buffer = self._read_buffer[bufsize:]
return ret

def getsockname(self):
"""Return the socket's own address (socket emulation)."""
Expand Down
38 changes: 23 additions & 15 deletions tlslite/mathtls.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,19 +676,27 @@ def paramStrength(param):
return 256 # NIST SP 800-57


def P_hash(macFunc, secret, seed, length):
bytes = bytearray(length)
def P_hash(mac_name, secret, seed, length):
"""Internal method for calculation the PRF in TLS."""
ret = bytearray(length)
seed = compatHMAC(seed)
A = seed
index = 0
while 1:
A = macFunc(secret, A)
output = macFunc(secret, A + seed)
for c in output:
if index >= length:
return bytes
bytes[index] = c
index += 1
return bytes
mac = hmac.HMAC(compatHMAC(secret), digestmod=mac_name)
while index < length:
a_fun = mac.copy()
a_fun.update(A)
A = a_fun.digest()
out_fun = mac.copy()
out_fun.update(A)
out_fun.update(seed)
output = out_fun.digest()

how_many = min(length - index, len(output))
ret[index:index+how_many] = output[:how_many]
index += how_many
return ret


def PRF(secret, label, seed, length):
#Split the secret into left and right halves
Expand All @@ -697,8 +705,8 @@ def PRF(secret, label, seed, length):
S2 = secret[ int(math.floor(len(secret)/2.0)) : ]

#Run the left half through P_MD5 and the right half through P_SHA1
p_md5 = P_hash(HMAC_MD5, S1, label + seed, length)
p_sha1 = P_hash(HMAC_SHA1, S2, label + seed, length)
p_md5 = P_hash("md5", S1, label + seed, length)
p_sha1 = P_hash("sha1", S2, label + seed, length)

#XOR the output values and return the result
for x in range(length):
Expand All @@ -707,11 +715,11 @@ def PRF(secret, label, seed, length):

def PRF_1_2(secret, label, seed, length):
"""Pseudo Random Function for TLS1.2 ciphers that use SHA256"""
return P_hash(HMAC_SHA256, secret, label + seed, length)
return P_hash("sha256", secret, label + seed, length)

def PRF_1_2_SHA384(secret, label, seed, length):
"""Pseudo Random Function for TLS1.2 ciphers that use SHA384"""
return P_hash(HMAC_SHA384, secret, label + seed, length)
return P_hash("sha384", secret, label + seed, length)

def PRF_SSL(secret, seed, length):
bytes = bytearray(length)
Expand Down
8 changes: 4 additions & 4 deletions tlslite/utils/asn1parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ def getChildCount(self):
while True:
if p.getRemainingLength() == 0:
break
p.get(1) # skip Type
p.skip_bytes(1) # skip Type
length = self._getASN1Length(p)
p.getFixBytes(length) # skip value
p.skip_bytes(length) # skip value
count += 1
return count

Expand All @@ -104,9 +104,9 @@ def getChildBytes(self, which):
p = Parser(self.value)
for _ in range(which+1):
markIndex = p.index
p.get(1) #skip Type
p.skip_bytes(1) # skip Type
length = self._getASN1Length(p)
p.getFixBytes(length)
p.skip_bytes(length)
return p.bytes[markIndex : p.index]

@staticmethod
Expand Down
24 changes: 13 additions & 11 deletions tlslite/utils/codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sys
import struct
from struct import pack
from .compat import bytes_to_int


class DecodeError(SyntaxError):
Expand Down Expand Up @@ -305,14 +306,8 @@ def get(self, length):
:rtype: int
"""
if self.index + length > len(self.bytes):
raise DecodeError("Read past end of buffer")
x = 0
for _ in range(length):
x <<= 8
x |= self.bytes[self.index]
self.index += 1
return x
ret = self.getFixBytes(length)
return bytes_to_int(ret, 'big')

def getFixBytes(self, lengthBytes):
"""
Expand All @@ -323,11 +318,18 @@ def getFixBytes(self, lengthBytes):
:rtype: bytearray
"""
if self.index + lengthBytes > len(self.bytes):
end = self.index + lengthBytes
if end > len(self.bytes):
raise DecodeError("Read past end of buffer")
bytes = self.bytes[self.index : self.index+lengthBytes]
ret = self.bytes[self.index : end]
self.index += lengthBytes
return bytes
return ret

def skip_bytes(self, length):
"""Move the internal pointer ahead length bytes."""
if self.index + length > len(self.bytes):
raise DecodeError("Read past end of buffer")
self.index += length

def getVarBytes(self, lengthLength):
"""
Expand Down
77 changes: 72 additions & 5 deletions tlslite/utils/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,21 @@
if sys.version_info >= (3,0):

def compat26Str(x): return x

# Python 3 requires bytes instead of bytearrays for HMAC


# Python 3.3 requires bytes instead of bytearrays for HMAC
# So, python 2.6 requires strings, python 3 requires 'bytes',
# and python 2.7 can handle bytearrays...
def compatHMAC(x): return bytes(x)
# and python 2.7 and 3.5 can handle bytearrays...
# pylint: disable=invalid-name
# we need to keep compatHMAC and `x` for API compatibility
if sys.version_info < (3, 4):
def compatHMAC(x):
"""Convert bytes-like input to format acceptable for HMAC."""
return bytes(x)
else:
def compatHMAC(x):
"""Convert bytes-like input to format acceptable for HMAC."""
return x
# pylint: enable=invalid-name

def compatAscii2Bytes(val):
"""Convert ASCII string to bytes."""
Expand Down Expand Up @@ -80,6 +89,25 @@ def remove_whitespace(text):
"""Removes all whitespace from passed in string"""
return re.sub(r"\s+", "", text, flags=re.UNICODE)

# pylint: disable=invalid-name
# pylint is stupid here and deson't notice it's a function, not
# constant
bytes_to_int = int.from_bytes
# pylint: enable=invalid-name

def bit_length(val):
"""Return number of bits necessary to represent an integer."""
return val.bit_length()

def int_to_bytes(val, length=None, byteorder="big"):
"""Return number converted to bytes"""
if length is None:
length = byte_length(val)
# for gmpy we need to convert back to native int
if type(val) != int:
val = int(val)
return bytearray(val.to_bytes(length=length, byteorder=byteorder))

else:
# Python 2.6 requires strings instead of bytearrays in a couple places,
# so we define this function so it does the conversion if needed.
Expand All @@ -92,13 +120,23 @@ def compat26Str(x): return str(x)
def remove_whitespace(text):
"""Removes all whitespace from passed in string"""
return re.sub(r"\s+", "", text)

def bit_length(val):
"""Return number of bits necessary to represent an integer."""
if val == 0:
return 0
return len(bin(val))-2
else:
def compat26Str(x): return x

def remove_whitespace(text):
"""Removes all whitespace from passed in string"""
return re.sub(r"\s+", "", text, flags=re.UNICODE)

def bit_length(val):
"""Return number of bytes necessary to represent an integer."""
return val.bit_length()

def compatAscii2Bytes(val):
"""Convert ASCII string to bytes."""
return val
Expand Down Expand Up @@ -147,6 +185,35 @@ def time_stamp():
"""Returns system time as a float"""
return time.clock()

def bytes_to_int(val, byteorder):
"""Convert bytes to an int."""
if not val:
return 0
if byteorder == "big":
return int(b2a_hex(val), 16)
if byteorder == "little":
return int(b2a_hex(val[::-1]), 16)
raise ValueError("Only 'big' and 'little' endian supported")

def int_to_bytes(val, length=None, byteorder="big"):
"""Return number converted to bytes"""
if length is None:
length = byte_length(val)
if byteorder == "big":
return bytearray((val >> i) & 0xff
for i in reversed(range(0, length*8, 8)))
if byteorder == "little":
return bytearray((val >> i) & 0xff
for i in range(0, length*8, 8))
raise ValueError("Only 'big' or 'little' endian supported")


def byte_length(val):
"""Return number of bytes necessary to represent an integer."""
length = bit_length(val)
return (length + 7) // 8


try:
# Fedora and Red Hat Enterprise Linux versions have small curves removed
getattr(ecdsa, 'NIST192p')
Expand Down
59 changes: 20 additions & 39 deletions tlslite/utils/cryptomath.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
import math
import base64
import binascii
import sys

from .compat import compat26Str, compatHMAC, compatLong, b2a_hex
from .compat import compat26Str, compatHMAC, compatLong, \
bytes_to_int, int_to_bytes, bit_length, byte_length
from .codec import Writer

from . import tlshashlib as hashlib
Expand Down Expand Up @@ -204,18 +204,8 @@ def bytesToNumber(b, endian="big"):
By default assumes big-endian encoding of the number.
"""
# if string is empty, consider it to be representation of zero
# while it may be a bit unorthodox, it is the inverse of numberToByteArray
# with default parameters
if not b:
return 0
return bytes_to_int(b, endian)

if endian == "big":
return int(b2a_hex(b), 16)
elif endian == "little":
return int(b2a_hex(b[::-1]), 16)
else:
raise ValueError("Only 'big' and 'little' endian supported")

def numberToByteArray(n, howManyBytes=None, endian="big"):
"""
Expand All @@ -225,16 +215,14 @@ def numberToByteArray(n, howManyBytes=None, endian="big"):
not be larger. The returned bytearray will contain a big- or little-endian
encoding of the input integer (n). Big endian encoding is used by default.
"""
if howManyBytes == None:
howManyBytes = numBytes(n)
if endian == "big":
return bytearray((n >> i) & 0xff
for i in reversed(range(0, howManyBytes*8, 8)))
elif endian == "little":
return bytearray((n >> i) & 0xff
for i in range(0, howManyBytes*8, 8))
else:
raise ValueError("Only 'big' and 'little' endian supported")
if howManyBytes is not None:
length = byte_length(n)
if howManyBytes < length:
ret = int_to_bytes(n, length, endian)
if endian == "big":
return ret[length-howManyBytes:length]
return ret[:howManyBytes]
return int_to_bytes(n, howManyBytes, endian)


def mpiToNumber(mpi):
Expand Down Expand Up @@ -265,23 +253,16 @@ def numberToMPI(n):
# Misc. Utility Functions
# **************************************************************************

def numBits(n):
"""Return number of bits necessary to represent the integer in binary"""
if n==0:
return 0
if sys.version_info < (2, 7):
# bit_length() was introduced in 2.7, and it is an order of magnitude
# faster than the below code
return len(bin(n))-2
else:
return n.bit_length()

def numBytes(n):
"""Return number of bytes necessary to represent the integer in bytes"""
if n==0:
return 0
bits = numBits(n)
return (bits + 7) // 8
# pylint: disable=invalid-name
# pylint recognises them as constants, not function names, also
# we can't change their names without API change
numBits = bit_length


numBytes = byte_length
# pylint: enable=invalid-name


# **************************************************************************
# Big Number Math
Expand Down
5 changes: 2 additions & 3 deletions tlslite/utils/rsakey.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,11 +534,10 @@ def _addPKCS1Padding(self, bytes, blockType):
pad = bytearray(0)
while len(pad) < padLength:
padBytes = getRandomBytes(padLength * 2)
pad = [b for b in padBytes if b != 0]
pad = [b for b in padBytes if b]
pad = pad[:padLength]
else:
raise AssertionError()

padding = bytearray([0,blockType] + pad + [0])
paddedBytes = padding + bytes
return paddedBytes
return padding + bytes

0 comments on commit e695f54

Please sign in to comment.