Exemple #1
0
    def _read_private_key_new_format(self, typepfx, lines, password):
        """
        Read the new OpenSSH SSH2 private key format available
        since OpenSSH version 6.5
        Reference:
        https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
        https://coolaj86.com/articles/the-openssh-private-key-format/
        """
        try:
            data = decodebytes(b(''.join(lines)))
        except base64.binascii.Error as e:
            raise SSHException('base64 decoding error: ' + str(e))

        message = Message(data)
        OPENSSH_AUTH_MAGIC = b"openssh-key-v1\x00"
        if message.get_bytes(len(OPENSSH_AUTH_MAGIC)) != OPENSSH_AUTH_MAGIC:
            raise SSHException("unexpected OpenSSH key header encountered")

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

        if num_keys > 1:
            raise SSHException(
                "unsupported: private keyfile has multiple keys")

        public_data = message.get_binary()
        privkey_blob = message.get_binary()

        keytype = Message(public_data).get_text()
        if not keytype.startswith(typepfx):
            raise SSHException("Invalid key type name (public part)")

        if kdfname == "none":
            if kdfoptions or cipher != "none":
                raise SSHException("Invalid key options for kdf 'none'")
            private_data = privkey_blob

        elif kdfname == "bcrypt":
            if not password:
                raise PasswordRequiredException(
                    "Private key file is encrypted")
            if cipher == 'aes256-cbc':
                mode = modes.CBC
            elif cipher == 'aes256-ctr':
                mode = modes.CTR
            else:
                raise SSHException(
                    "unknown cipher '%s' used in private key file" % cipher)

            kdf = Message(kdfoptions)
            salt = kdf.get_binary()
            rounds = kdf.get_int()

            # run bcrypt kdf to derive key and iv/nonce (desired_key_bytes = 32 + 16 bytes)
            key_iv = bcrypt.kdf(b(password),
                                salt,
                                48,
                                rounds,
                                ignore_few_rounds=True)
            key = key_iv[:32]
            iv = key_iv[32:]
            # decrypt private key blob
            decryptor = Cipher(algorithms.AES(key), mode(iv),
                               default_backend()).decryptor()
            private_data = decryptor.update(
                privkey_blob) + decryptor.finalize()

        else:
            raise SSHException(
                "unknown cipher or kdf used in private key file")

        # Unpack private key and verify checkints
        priv_msg = Message(private_data)
        checkint1 = priv_msg.get_int()
        checkint2 = priv_msg.get_int()
        if checkint1 != checkint2:
            raise SSHException(
                'OpenSSH private key file checkints do not match')

        keytype = priv_msg.get_text()
        if not keytype.startswith(typepfx):
            raise SSHException("Invalid key type name (private part)")

        keydata = priv_msg.get_remainder()
        return unpad(keydata)