def write_some(self, data): """See :meth:`versile.reactor.io.IVByteOutput.write_some`\ .""" if not self.was_connected: raise VIOError('Socket not connected') if self._sock_out_closed: if isinstance(self._sock_out_closed_reason, VFIOCompleted): raise VIOCompleted() else: raise VIOLost() if not self.sock: raise VIOError('No socket') try: if _pyver == 2: num_written = self.sock.send(_b2s(data)) else: num_written = self.sock.send(data) except IOError as e: if e.errno in _errno_block: return 0 elif (e.errno in (errno.EPIPE, errno.ENOTCONN) and not self._sock_verified): # ISSUE - these have been seen to be raised after # socket thinks it is connected due to select() event, # however ignoring it causes the connection to be # 'resumed'. For now we log a message and resume. self.log.debug('Ignoring post-connect write errno %s' % e.errno) return 0 else: self.log.debug('Write got errno %s' % e.errno) raise VIOError('Socket write error, errno %s' % e.errno) else: self._sock_verified = True return num_written
def __fmt(self, fmt): with self: result = [self._str_prefix()] index = [0] * len(self._dim) first = True while True: if not first: if _pyver == 2: result.append(b', ') else: result.append(', ') first = False if _pyver == 2: result.append(b'%s: %s' % (tuple(index), fmt(self[index]))) else: result.append('%s: %s' % (tuple(index), fmt(self[index]))) for i in xrange(len(index)): index[i] += 1 if index[i] < self._dim[i]: break else: index[i] = 0 else: break if _pyver == 2: result.append(b']') else: result.append(']') if _pyver == 2: return (_b2s(b''.join(result))) else: return ''.join(result)
def __init__(self, daemon=False): super(VFDWaitReactor, self).__init__() if daemon: self.daemon = True self.name = 'VSelectReactor-' + str(self.name.split('-')[1]) self.__readers, self.__writers = set(), set() self.__finished = False self.__thread = None if _vplatform == 'ironpython' or sys.platform == _b2s(b'win32'): self.__ctrl_is_pipe = False from versile.reactor.io.tcp import VTCPSocket self.__ctrl_r, self.__ctrl_w = VTCPSocket.create_native_pair() else: self.__ctrl_is_pipe = True self.__ctrl_r, self.__ctrl_w = os.pipe() self.__ctrl_queue = deque() self.__ctrl_msg_flag = False self.__ctrl_stop = False # Locks __ctrl_msg_flag, __ctrl_queue, writing to __ctrl_w self.__ctrl_lock = threading.Lock() self.__scheduled_calls = [] # heapq-ordered list self.__grouped_calls = {} self.__calls_lock = threading.Lock() # Locks scheduled/grouped calls self.__t_next_call = None # Timestamp next call (or None) self.__core_log = VLogger() self.__logger = VReactorLogger(self) self.__logger.add_watcher(self.__core_log) # Reactor-only proxy logger which adds a prefix self.__rlog = self.__core_log.create_proxy_logger(prefix='Reactor')
def export_spki_public_key(cls, key, fmt=VX509Format.PEM_BLOCK): """Exports a X.509 SPKI public key. :param key: key to export :type key: :class:`versile.crypto.VAsymmetricKey` :param fmt: format to export to :type fmt: :class:`VX509Format` constant :returns: exported key data Exports an encoding of the key's :term:`ASN.1` representation defined by X.509 SubjectPublicKeyInfo encoding format, associated with the PEM header 'BEGIN PUBLIC KEY'. .. note:: For the :term:`PKCS#1`\ encoding format (PEM header 'BEGIN RSA PUBLIC KEY'), see :meth:`export_public_key`\ . """ if not isinstance(key, VAsymmetricKey): raise TypeError('Invalid key type') if key.cipher_name != 'rsa': raise VCryptoException('Encoding not supported') if key.has_public: n, e = key.keydata[:2] # Create spki structure from versile.crypto.x509.asn1def.cert import SubjectPublicKeyInfo asn1 = SubjectPublicKeyInfo().create() # - algorithm alg = AlgorithmIdentifier().create() _alg_id = VObjectIdentifier(1, 2, 840, 113549, 1, 1, 1) alg.append(_alg_id, name='algorithm') alg.append(VASN1Null(), name='parameters') asn1.append(alg, name='algorithm') # - keydata spk = cls.export_public_key(key, fmt=VX509Format.DER) spk = VBitfield.from_octets(spk) asn1.append(spk, name='subjectPublicKey') if not asn1.validate(): raise VCryptoException('ASN.1 structure validation error') if fmt == VX509Format.ASN1: return asn1 der = asn1.encode_der() if fmt == VX509Format.DER: return der if fmt == VX509Format.PEM: if _pyver == 2: return _s2b(base64.encodestring(_b2s(der))) else: return base64.encodebytes(der) elif fmt == VX509Format.PEM_BLOCK: return encode_pem_block(b'PUBLIC KEY', der) else: raise VCryptoException('Invalid encoding')
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 __msg_push(self, code, data): """Push internal message onto msg queue and interrupts event loop.""" if self.__finished: # Not accepting messages if reactor has finished return self.__ctrl_lock.acquire() try: self.__ctrl_queue.append((code, data)) if not self.__ctrl_msg_flag: if _pyver == 2: if self.__ctrl_is_pipe: os.write(self.__ctrl_w, _b2s(b'x')) else: self.__ctrl_w.send(_b2s(b'x')) else: if self.__ctrl_is_pipe: os.write(self.__ctrl_w, b'x') else: self.__ctrl_w.send(b'x') self.__ctrl_msg_flag = True finally: self.__ctrl_lock.release()
def export_public_key(cls, key, fmt=VX509Format.PEM_BLOCK): """Exports a public key in PKCS#1 format. :param key: key to export :type key: :class:`versile.crypto.VAsymmetricKey` :param fmt: format to export to :type fmt: :class:`VX509Format` constant :returns: exported key data Exports an encoding of the key's :term:`ASN.1` representation defined by :term:`PKCS#1`\, associated with the PEM header 'BEGIN RSA PUBLIC KEY'. .. note:: For the X.509 SubjectPublicKeyInfo encoding format (PEM header 'BEGIN PUBLIC KEY'), see :meth:`export_spki_public_key`\ . """ if not isinstance(key, VAsymmetricKey): raise TypeError('Invalid key type') if key.cipher_name == 'rsa': if key.has_public: n, e = key.keydata[:2] asn1 = RSAPublicKey().create() asn1.append(n, name='modulus') asn1.append(e, name='publicExponent') if not asn1.validate(): raise VCryptoException('ASN.1 structure validation error') if fmt == VX509Format.ASN1: return asn1 der = asn1.encode_der() if fmt == VX509Format.DER: return der if fmt == VX509Format.PEM: if _pyver == 2: return _s2b(base64.encodestring(_b2s(der))) else: return base64.encodebytes(der) elif fmt == VX509Format.PEM_BLOCK: return encode_pem_block(b'RSA PUBLIC KEY', der) else: raise VCryptoException('Invalid encoding') else: raise VCryptoException('Encoding not supported')
def _encode_ascii(self, name, numbers): """Internal convenience method for exporting as ASCII. :param name: cipher name :type name: unicode :param numbers: keydata number components :type numbers: list<int> Can be used internally by implementations of derived classes. """ header = b'-----BEGIN ' + name + b'-----\n' footer = b'-----END ' + name + b'-----\n' num_data = b''.join([posint_to_netbytes(i) for i in numbers]) if _pyver == 2: num_data = _s2b(base64.encodestring(_b2s(num_data))) else: num_data = base64.encodebytes(num_data) data = b''.join((header, num_data, footer)) data = b'\r\n'.join(data.split(b'\n')) return data
def write_some(self, data): """See :meth:`versile.reactor.io.IVByteOutput.write_some`\ .""" if self._out_closed: if isinstance(self._out_closed_reason, VFIOCompleted): raise VIOCompleted() else: raise VIOLost() try: if _pyver == 2: data = _b2s(data) num_written = os.write(self._fd, data) except OSError as e: if e.errno in _errno_block: return 0 else: self.log.debug('Write got errno %s' % e.errno) raise VIOError('Pipe read error') else: if num_written > 0: return num_written else: self.log.debug('Pipe write error') raise VIOError('Pipe write error')
def _decode_ascii(cls, block): """Decodes an ASCII block. :param block: standard ASCII block representation :type block: bytes :returns: (block_name, block_data) :raises: VCryptoException Internal convenience method for derived classes implementing ascii key import. """ block = block.strip() m = re.match(b'-----BEGIN ', block) if not m: raise VCryptoException() block = block[m.end():] m = re.search(b'-----', block) if not m: raise VCryptoException() block_name = block[:m.start()] block = block[m.end():] block = block.strip() end = b'-----END ' + block_name + b'-----' if not block.endswith(end): raise VCryptoException() block = block[:(-len(end))] try: if _pyver == 2: decoded = _s2b(base64.decodestring(_b2s(block))) else: decoded = base64.decodebytes(block) except: raise VCryptoException() else: return (block_name, decoded)
def import_spki_public_key(cls, data, fmt=VX509Format.PEM_BLOCK, keytype='rsa', crypto=None): """Imports a X.509 SPKI public key. :param data: keydata to import :param fmt: format to import from :type fmt: :class:`VX509Format` constant :param keytype: key type to import (or None) :param keytype: unicode :param crypto: crypto provider (default if None) :type crypto: :class:`versile.crypto.VCrypto` :returns: imported key :rtype: :class:`versile.crypto.VAsymmetricKey` Imports an encoding of the key's :term:`ASN.1` representation defined by X.509 SubjectPublicKeyInfo encoding format, associated with the PEM header 'BEGIN PUBLIC KEY'. .. note:: For the :term:`PKCS#1`\ encoding format (PEM header 'BEGIN RSA PUBLIC KEY'), see :meth:`import_public_key`\ . """ crypto = VCrypto.lazy(crypto) if fmt == VX509Format.PEM_BLOCK: heading, data = decode_pem_block(data) if heading == b'PUBLIC KEY': if keytype is None: keytype = 'rsa' elif keytype != 'rsa': raise VCryptoException('Key type mismatch') else: raise VCryptoException('Key type not supported') fmt = VX509Format.DER elif fmt == VX509Format.PEM: if keytype != 'rsa': raise VCryptoException('Key type not supported') if _pyver == 2: data = _s2b(base64.decodestring(_b2s(data))) else: data = base64.decodebytes(data) fmt = VX509Format.DER if fmt == VX509Format.DER: from versile.crypto.x509.asn1def.cert import SubjectPublicKeyInfo spki, len_read = SubjectPublicKeyInfo().parse_der(data) if len_read != len(data): raise VCryptoException('Public Key DER data overflow') data = spki fmt = VX509Format.ASN1 if fmt == VX509Format.ASN1: if keytype != 'rsa': raise VCryptoException('Key type not supported') spki = data.native(deep=True) _alg, keydata = spki # Verify supported key type alg, _params = _alg if alg != VObjectIdentifier(1, 2, 840, 113549, 1, 1, 1): raise VCryptoException('Unsupported key algorithm') # Import key from embedded DER data pub_key = cls.import_public_key(keydata.as_octets(), VX509Format.DER) return pub_key
def import_private_key(cls, data, fmt=VX509Format.PEM_BLOCK, keytype='rsa', crypto=None): """Imports a PKCS#1 private key (i.e. a complete keypair). :param data: keydata to import :param fmt: format to import from :type fmt: :class:`VX509Format` constant :param keytype: key type to import (or None) :param keytype: unicode :param crypto: crypto provider (default if None) :type crypto: :class:`versile.crypto.VCrypto` :returns: imported key :rtype: :class:`versile.crypto.VAsymmetricKey` Imports an encoding of the key's :term:`ASN.1` representation defined by :term:`PKCS#1`\, associated with the PEM header 'BEGIN RSA PRIVATE KEY'. .. note:: Whereas :term:`VPy` refers to complete keys as 'key pairs' and private keys as only the private component of the key, private keys in :term:`PKCS#1` refer to the complete key - thus the naming convention for this method. .. note:: Importing from the PKCS#8 PrivateKeyInfo encoding format (PEM header 'BEGIN PRIVATE KEY') is not supported. """ crypto = VCrypto.lazy(crypto) if fmt == VX509Format.PEM_BLOCK: heading, data = decode_pem_block(data) if heading == b'RSA PRIVATE KEY': if keytype is None: keytype = 'rsa' elif keytype != 'rsa': raise VCryptoException('Key type mismatch') else: raise VCryptoException('Key type not supported') fmt = VX509Format.DER elif fmt == VX509Format.PEM: if keytype != 'rsa': raise VCryptoException('Key type not supported') if _pyver == 2: data = _s2b(base64.decodestring(_b2s(data))) else: data = base64.decodebytes(data) fmt = VX509Format.DER if fmt == VX509Format.DER: asn1key, len_read = RSAPrivateKey().parse_der(data) if len_read != len(data): raise VCryptoException('Public Key DER data overflow') data = asn1key fmt = VX509Format.ASN1 if fmt == VX509Format.ASN1: if keytype != 'rsa': raise VCryptoException('Key type not supported') data = data.native(deep=True) version, n, e, d, p, q, exp1, exp2, coeff = data[:9] for _param in (e, d, p, q, exp1, exp2, coeff): if not 0 < _param < n: raise VCryptoException('Invalid key parameter(s)') key_params = (n, e, d, p, q) return crypto.num_rsa.key_factory.load(key_params)
def import_public_key(cls, data, fmt=VX509Format.PEM_BLOCK, keytype='rsa', crypto=None): """Imports a PKCS#1 public key. :param data: keydata to import :param fmt: format to import from :type fmt: :class:`VX509Format` constant :param keytype: key type to import (or None) :param keytype: unicode :param crypto: crypto provider (default if None) :type crypto: :class:`versile.crypto.VCrypto` :returns: imported key :rtype: :class:`versile.crypto.VAsymmetricKey` Imports an encoding of the key's :term:`ASN.1` representation defined by :term:`PKCS#1`\, associated with the PEM header 'BEGIN RSA PUBLIC KEY'. .. note:: For the X.509 SubjectPublicKeyInfo encoding format (PEM header 'BEGIN PUBLIC KEY'), see :meth:`import_spki_public_key`\ . """ crypto = VCrypto.lazy(crypto) if fmt == VX509Format.PEM_BLOCK: heading, data = decode_pem_block(data) if heading == b'RSA PUBLIC KEY': if keytype is None: keytype = 'rsa' elif keytype != 'rsa': raise VCryptoException('Key type mismatch') else: raise VCryptoException('Key type not supported') fmt = VX509Format.DER elif fmt == VX509Format.PEM: if keytype != 'rsa': raise VCryptoException('Key type not supported') if _pyver == 2: data = _s2b(base64.decodestring(_b2s(data))) else: data = base64.decodebytes(data) fmt = VX509Format.DER if fmt == VX509Format.DER: asn1key, len_read = RSAPublicKey().parse_der(data) if len_read != len(data): raise VCryptoException('Public Key DER data overflow') data = asn1key fmt = VX509Format.ASN1 if fmt == VX509Format.ASN1: if keytype != 'rsa': raise VCryptoException('Key type not supported') n = data.modulus.native() e = data.publicExponent.native() if not 0 < e < n: raise VCryptoException('Invalid key parameters') key_params = (n, e, None, None, None) return crypto.num_rsa.key_factory.load(key_params)
def export_private_key(cls, key, fmt=VX509Format.PEM_BLOCK): """Exports a private key (complete keypair) as PKCS#1. :param key: key to export :type key: :class:`versile.crypto.VAsymmetricKey` :param fmt: format to export to :type fmt: :class:`VX509Format` constant :returns: exported key data Exports an encoding of the key's :term:`ASN.1` representation defined by :term:`PKCS#1`\, associated with the PEM header 'BEGIN RSA PRIVATE KEY'. .. note:: Whereas :term:`VPy` refers to complete keys as 'key pairs' and private keys as only the private component of the key, private keys in :term:`PKCS#1` refer to the complete key - thus the naming convention for this method. .. note:: Exporting to the PKCS#8 key format (PEM header 'BEGIN PRIVATE KEY') is not supported. """ if not isinstance(key, VAsymmetricKey): raise TypeError('Invalid key type') if key.cipher_name == 'rsa': if key.has_private: n, e, d, p, q = key.keydata params = (0, n, e, d, p, q, key._exp1, key._exp2, key._coeff) if None in params: raise VCryptoException( 'X.509 parameters missing from key.') asn1 = RSAPrivateKey().create() asn1.append(0, name='version') asn1.append(n, name='modulus') asn1.append(e, name='publicExponent') asn1.append(d, name='privateExponent') asn1.append(p, name='prime1') asn1.append(q, name='prime2') asn1.append(key._exp1, name='exponent1') asn1.append(key._exp2, name='exponent2') asn1.append(key._coeff, name='coefficient') if not asn1.validate(): raise VCryptoException('ASN.1 structure validation error') if fmt == VX509Format.ASN1: return asn1 der = asn1.encode_der() if fmt == VX509Format.DER: return der if fmt == VX509Format.PEM: if _pyver == 2: return _s2b(base64.encodestring(_b2s(der))) else: return base64.encodebytes(der) elif fmt == VX509Format.PEM_BLOCK: return encode_pem_block(b'RSA PRIVATE KEY', der) else: raise VCryptoException('Invalid encoding') else: raise VCryptoException('Encoding not supported')
from versile.reactor.io import VIOCompleted, VIOLost, VIOError, VIOException from versile.reactor.io import VFIOCompleted, VFIOLost, IVByteIO from versile.reactor.io import IVSelectable, IVSelectableIO, IVByteInput from versile.reactor.io import IVByteProducer, IVByteConsumer from versile.reactor.io import VHalfClose, VNoHalfClose from versile.reactor.io import VIOControl, VIOMissingControl from versile.reactor.io.descriptor import IVDescriptor __all__ = [ 'IVClientSocket', 'IVSocket', 'VClientSocket', 'VClientSocketAgent', 'VClientSocketFactory', 'VListeningSocket', 'VSocket', 'VSocketBase' ] __all__ = _vexport(__all__) # Workaround for Windows-specific error codes if sys.platform == _b2s(b'win32') or _vplatform == 'ironpython': _errno_block = (errno.EWOULDBLOCK, errno.WSAEWOULDBLOCK) _errno_connect = (errno.EINPROGRESS, errno.WSAEWOULDBLOCK) else: _errno_block = (errno.EWOULDBLOCK, ) _errno_connect = (errno.EINPROGRESS, ) class IVSocket(IVDescriptor, IVSelectable): """Interface for a general socket descriptor.""" @classmethod def create_native(cls): """Creates a native socket. :returns: :class:`socket.socket`
def update(self, data): if _pyver == 2: self.__hash.update(_b2s(data)) else: self.__hash.update(data)