Esempio n. 1
0
 def _read_private_key(self, tag, f, password=None):
     lines = f.readlines()
     start = 0
     beginning_of_key = '-----BEGIN ' + tag + ' PRIVATE KEY-----'
     while start < len(lines) and lines[start].strip() != beginning_of_key:
         start += 1
     if start >= len(lines):
         raise SSHException('not a valid ' + tag + ' private key file')
     # parse any headers first
     headers = {}
     start += 1
     while start < len(lines):
         l = lines[start].split(': ')
         if len(l) == 1:
             break
         headers[l[0].lower()] = l[1].strip()
         start += 1
     # find end
     end = start
     ending_of_key = '-----END ' + tag + ' PRIVATE KEY-----'
     while end < len(lines) and lines[end].strip() != ending_of_key:
         end += 1
     # if we trudged to the end of the file, just try to cope.
     try:
         data = decodebytes(b(''.join(lines[start:end])))
     except base64.binascii.Error as e:
         raise SSHException('base64 decoding error: ' + str(e))
     if 'proc-type' not in headers:
         # unencryped: done
         return data
     # encrypted keyfile: will need a password
     proc_type = headers['proc-type']
     if proc_type != '4,ENCRYPTED':
         raise SSHException(
             'Unknown private key structure "{}"'.format(proc_type))
     try:
         encryption_type, saltstr = headers['dek-info'].split(',')
     except:
         raise SSHException("Can't parse DEK-info in private key file")
     if encryption_type not in self._CIPHER_TABLE:
         raise SSHException(
             'Unknown private key cipher "{}"'.format(encryption_type))
     # if no password was passed in,
     # raise an exception pointing out that we need one
     if password is None:
         raise PasswordRequiredException('Private key file is encrypted')
     cipher = self._CIPHER_TABLE[encryption_type]['cipher']
     keysize = self._CIPHER_TABLE[encryption_type]['keysize']
     mode = self._CIPHER_TABLE[encryption_type]['mode']
     salt = unhexlify(b(saltstr))
     key = util.generate_key_bytes(md5, salt, password, keysize)
     decryptor = Cipher(cipher(key), mode(salt),
                        backend=default_backend()).decryptor()
     return decryptor.update(data) + decryptor.finalize()
Esempio n. 2
0
def _query_pageant(msg):
    """
    Communication with the Pageant process is done through a shared
    memory-mapped file.
    """
    hwnd = _get_pageant_window_object()
    if not hwnd:
        # Raise a failure to connect exception, pageant isn't running anymore!
        return None

    # create a name for the mmap
    map_name = 'PageantRequest%08x' % thread.get_ident()

    pymap = _winapi.MemoryMap(map_name, _AGENT_MAX_MSGLEN,
        _winapi.get_security_attributes_for_user(),
        )
    with pymap:
        pymap.write(msg)
        # Create an array buffer containing the mapped filename
        char_buffer = array.array("b", b(map_name) + zero_byte)  # noqa
        char_buffer_address, char_buffer_size = char_buffer.buffer_info()
        # Create a string to use for the SendMessage function call
        cds = COPYDATASTRUCT(_AGENT_COPYDATA_ID, char_buffer_size,
            char_buffer_address)

        response = ctypes.windll.user32.SendMessageA(hwnd,
            win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds))

        if response > 0:
            pymap.seek(0)
            datalen = pymap.read(4)
            retlen = struct.unpack('>I', datalen)[0]
            return datalen + pymap.read(retlen)
        return None
Esempio n. 3
0
def generate_key_bytes(hash_alg, salt, key, nbytes):
    """
    Given a password, passphrase, or other human-source key, scramble it
    through a secure hash into some keyworthy bytes.  This specific algorithm
    is used for encrypting/decrypting private key files.

    :param function hash_alg: A function which creates a new hash object, such
        as ``hashlib.sha256``.
    :param salt: data to salt the hash with.
    :type salt: byte string
    :param str key: human-entered password or passphrase.
    :param int nbytes: number of bytes to generate.
    :return: Key data `str`
    """
    keydata = bytes()
    digest = bytes()
    if len(salt) > 8:
        salt = salt[:8]
    while nbytes > 0:
        hash_obj = hash_alg()
        if len(digest) > 0:
            hash_obj.update(digest)
        hash_obj.update(b(key))
        hash_obj.update(salt)
        digest = hash_obj.digest()
        size = min(nbytes, len(digest))
        keydata += digest[:size]
        nbytes -= size
    return keydata
Esempio n. 4
0
    def chdir(self, path=None):
        """
        Change the "current directory" of this SFTP session.  Since SFTP
        doesn't really have the concept of a current working directory, this is
        emulated by Paramiko.  Once you use this method to set a working
        directory, all operations on this `.SFTPClient` object will be relative
        to that path. You can pass in ``None`` to stop using a current working
        directory.

        :param str path: new current working directory

        :raises:
            ``IOError`` -- if the requested path doesn't exist on the server

        .. versionadded:: 1.4
        """
        if path is None:
            self._cwd = None
            return
        if not stat.S_ISDIR(self.stat(path).st_mode):
            code = errno.ENOTDIR
            raise SFTPError(
                code, "{}: {}".format(os.strerror(code), path)
            )
        self._cwd = b(self.normalize(path))
Esempio n. 5
0
    def _write_private_key(self, f, key, format, password=None):
        if password is None:
            encryption = serialization.NoEncryption()
        else:
            encryption = serialization.BestAvailableEncryption(b(password))

        f.write(
            key.private_bytes(serialization.Encoding.PEM, format,
                              encryption).decode())
Esempio n. 6
0
def safe_string(s):
    out = b''
    for c in s:
        i = byte_ord(c)
        if 32 <= i <= 127:
            out += byte_chr(i)
        else:
            out += b('%{:02X}'.format(i))
    return out
Esempio n. 7
0
 def _send_handle_response(self, request_number, handle, folder=False):
     if not issubclass(type(handle), SFTPHandle):
         # must be error code
         self._send_status(request_number, handle)
         return
     handle._set_name(b('hx{:d}'.format(self.next_handle)))
     self.next_handle += 1
     if folder:
         self.folder_table[handle._get_name()] = handle
     else:
         self.file_table[handle._get_name()] = handle
     self._response(request_number, CMD_HANDLE, handle._get_name())
Esempio n. 8
0
 def _adjust_cwd(self, path):
     """
     Return an adjusted path if we're emulating a "current working
     directory" for the server.
     """
     path = b(path)
     if self._cwd is None:
         return path
     if len(path) and path[0:1] == b_slash:
         # absolute path
         return path
     if self._cwd == b_slash:
         return self._cwd + path
     return self._cwd + b_slash + path
Esempio n. 9
0
    def feed(self, data):
        """
        Feed new data into this pipe.  This method is assumed to be called
        from a separate thread, so synchronization is done.

        :param data: the data to add, as a ``str`` or ``bytes``
        """
        self._lock.acquire()
        try:
            if self._event is not None:
                self._event.set()
            self._buffer_frombytes(b(data))
            self._cv.notifyAll()
        finally:
            self._lock.release()
Esempio n. 10
0
 def from_string(cls, string):
     """
     Create a public blob from a ``-cert.pub``-style string.
     """
     fields = string.split(None, 2)
     if len(fields) < 2:
         msg = "Not enough fields for public blob: {}"
         raise ValueError(msg.format(fields))
     key_type = fields[0]
     key_blob = decodebytes(b(fields[1]))
     try:
         comment = fields[2].strip()
     except IndexError:
         comment = None
     # Verify that the blob message first (string) field matches the
     # key_type
     m = Message(key_blob)
     blob_type = m.get_text()
     if blob_type != key_type:
         msg = "Invalid PublicBlob contents: key type={!r}, but blob type={!r}"  # noqa
         raise ValueError(msg.format(key_type, blob_type))
     # All good? All good.
     return cls(type_=key_type, blob=key_blob, comment=comment)
Esempio n. 11
0
    URIRef(_XSD_PFX + 'nonPositiveInteger'): int,
    URIRef(_XSD_PFX + 'long'): long,
    URIRef(_XSD_PFX + 'nonNegativeInteger'): int,
    URIRef(_XSD_PFX + 'negativeInteger'): int,
    URIRef(_XSD_PFX + 'int'): long,
    URIRef(_XSD_PFX + 'unsignedLong'): long,
    URIRef(_XSD_PFX + 'positiveInteger'): int,
    URIRef(_XSD_PFX + 'short'): int,
    URIRef(_XSD_PFX + 'unsignedInt'): long,
    URIRef(_XSD_PFX + 'byte'): int,
    URIRef(_XSD_PFX + 'unsignedShort'): int,
    URIRef(_XSD_PFX + 'unsignedByte'): int,
    URIRef(_XSD_PFX + 'float'): float,
    URIRef(_XSD_PFX + 'double'): float,
    URIRef(_XSD_PFX + 'base64Binary'):
    lambda s: base64.b64decode(py3compat.b(s)),
    URIRef(_XSD_PFX + 'anyURI'): None,
}

_toPythonMapping = {}
_toPythonMapping.update(XSDToPython)


def bind(datatype, conversion_function):
    """
    bind a datatype to a function for converting it into a Python
    instance.
    """
    if datatype in _toPythonMapping:
        _LOGGER.warning("datatype '%s' was already bound. Rebinding." %
                        datatype)
Esempio n. 12
0
    URIRef(_XSD_PFX+'integer')            : long,
    URIRef(_XSD_PFX+'nonPositiveInteger') : int,
    URIRef(_XSD_PFX+'long')               : long,
    URIRef(_XSD_PFX+'nonNegativeInteger') : int,
    URIRef(_XSD_PFX+'negativeInteger')    : int,
    URIRef(_XSD_PFX+'int')                : long,
    URIRef(_XSD_PFX+'unsignedLong')       : long,
    URIRef(_XSD_PFX+'positiveInteger')    : int,
    URIRef(_XSD_PFX+'short')              : int,
    URIRef(_XSD_PFX+'unsignedInt')        : long,
    URIRef(_XSD_PFX+'byte')               : int,
    URIRef(_XSD_PFX+'unsignedShort')      : int,
    URIRef(_XSD_PFX+'unsignedByte')       : int,
    URIRef(_XSD_PFX+'float')              : float,
    URIRef(_XSD_PFX+'double')             : float,
    URIRef(_XSD_PFX+'base64Binary')       : lambda s: base64.b64decode(py3compat.b(s)),
    URIRef(_XSD_PFX+'anyURI')             : None,
}

_toPythonMapping = {}
_toPythonMapping.update(XSDToPython)

def bind(datatype, conversion_function):
    """
    bind a datatype to a function for converting it into a Python
    instance.
    """
    if datatype in _toPythonMapping:
        _LOGGER.warning("datatype '%s' was already bound. Rebinding." % 
                        datatype)
    _toPythonMapping[datatype] = conversion_function
Esempio n. 13
0
 def __init__(self, content=bytes()):
     self.content = b(content)
     self.idx = 0
Esempio n. 14
0
 def asbytes(self):
     return b(str(self))
Esempio n. 15
0
    def _parse_signing_key_data(self, data, password):
        from transport import Transport
        # We may eventually want this to be usable for other key types, as
        # OpenSSH moves to it, but for now this is just for Ed25519 keys.
        # This format is described here:
        # https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
        # The description isn't totally complete, and I had to refer to the
        # source for a full implementation.
        message = Message(data)
        if message.get_bytes(len(OPENSSH_AUTH_MAGIC)) != OPENSSH_AUTH_MAGIC:
            raise SSHException("Invalid key")

        ciphername = message.get_text()
        kdfname = message.get_text()
        kdfoptions = message.get_binary()
        num_keys = message.get_int()

        if kdfname == "none":
            # kdfname of "none" must have an empty kdfoptions, the ciphername
            # must be "none"
            if kdfoptions or ciphername != "none":
                raise SSHException("Invalid key")
        elif kdfname == "bcrypt":
            if not password:
                raise PasswordRequiredException(
                    "Private key file is encrypted")
            kdf = Message(kdfoptions)
            bcrypt_salt = kdf.get_binary()
            bcrypt_rounds = kdf.get_int()
        else:
            raise SSHException("Invalid key")

        if ciphername != "none" and ciphername not in Transport._cipher_info:
            raise SSHException("Invalid key")

        public_keys = []
        for _ in range(num_keys):
            pubkey = Message(message.get_binary())
            if pubkey.get_text() != "ssh-ed25519":
                raise SSHException("Invalid key")
            public_keys.append(pubkey.get_binary())

        private_ciphertext = message.get_binary()
        if ciphername == "none":
            private_data = private_ciphertext
        else:
            cipher = Transport._cipher_info[ciphername]
            key = bcrypt.kdf(
                password=b(password),
                salt=bcrypt_salt,
                desired_key_bytes=cipher["key-size"] + cipher["block-size"],
                rounds=bcrypt_rounds,
                # We can't control how many rounds are on disk, so no sense
                # warning about it.
                ignore_few_rounds=True,
            )
            decryptor = Cipher(cipher["class"](key[:cipher["key-size"]]),
                               cipher["mode"](key[cipher["key-size"]:]),
                               backend=default_backend()).decryptor()
            private_data = (decryptor.update(private_ciphertext) +
                            decryptor.finalize())

        message = Message(unpad(private_data))
        if message.get_int() != message.get_int():
            raise SSHException("Invalid key")

        signing_keys = []
        for i in range(num_keys):
            if message.get_text() != "ssh-ed25519":
                raise SSHException("Invalid key")
            # A copy of the public key, again, ignore.
            public = message.get_binary()
            key_data = message.get_binary()
            # The second half of the key data is yet another copy of the public
            # key...
            signing_key = nacl.signing.SigningKey(key_data[:32])
            # Verify that all the public keys are the same...
            assert (signing_key.verify_key.encode() == public == public_keys[i]
                    == key_data[32:])
            signing_keys.append(signing_key)
            # Comment, ignore.
            message.get_binary()

        if len(signing_keys) != 1:
            raise SSHException("Invalid key")
        return signing_keys[0]