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()
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
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
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))
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())
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
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())
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
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()
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)
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)
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
def __init__(self, content=bytes()): self.content = b(content) self.idx = 0
def asbytes(self): return b(str(self))
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]