class Net::SSH::Authentication::ED25519::OpenSSHPrivateKeyLoader

def self.bcrypt_pbkdf_key(password, salt, length, rounds)

def self.bcrypt_pbkdf_key(password, salt, length, rounds)
  begin
    require_bcrypt_pbkdf
  rescue LoadError
    raise DecryptError.new("bcrypt_pbkdf is required to decrypt bcrypt-encrypted OpenSSH private keys")
  end
  raise DecryptError.new("Passphrase required for encrypted private key", encrypted_key: true) if password.nil? || password.empty?
  key = BCryptPbkdf::key(password, salt, length, rounds)
  raise DecryptError.new("BCryptPbkdf failed", encrypted_key: true) unless key
  key
end

def self.decrypt_private_key(ciphername, encrypted_private, auth_tag, options)

def self.decrypt_private_key(ciphername, encrypted_private, auth_tag, options)
  if auth_tag
    return CipherFactory.decrypt_private_key(
      ciphername,
      encrypted_private,
      auth_tag,
      options[:key],
      options[:iv]
    )
  end
  cipher = CipherFactory.get(ciphername, key: options[:key], iv: options[:iv], decrypt: true)
  decoded = cipher.update(encrypted_private)
  decoded << cipher.final
rescue Net::SSH::Exception, OpenSSL::Cipher::CipherError
  raise DecryptError.new("Decrypt failed on private key", encrypted_key: options[:encrypted_key])
end

def self.read(datafull, password)

def self.read(datafull, password)
  datafull = datafull.strip.encode(universal_newline: true)
  raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
  raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
  datab64 = datafull[MBEGIN.size...-MEND.size]
  data = datab64.unpack1("m")
  raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
  buffer = Net::SSH::Buffer.new(data[(MAGIC.size + 1)..-1])
  ciphername = buffer.read_string
  raise ArgumentError.new("#{ciphername} in private key is not supported") unless
    CipherFactory.supported?(ciphername)
  kdfname = buffer.read_string
  raise ArgumentError.new("Expected #{kdfname} to be or none or bcrypt") unless %w[none bcrypt].include?(kdfname)
  kdfopts = Net::SSH::Buffer.new(buffer.read_string)
  num_keys = buffer.read_long
  raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
  _pubkey = buffer.read_string
  len = buffer.read_long
  encrypted_private = buffer.read(len)
  keylen, blocksize, ivlen = CipherFactory.get_lengths(ciphername, iv_len: true)
  raise ArgumentError.new("Private key len:#{len} is not a multiple of #{blocksize}") if
    (len < blocksize) || ((blocksize > 0) && (len % blocksize) != 0)
  raise ArgumentError.new("Private key len:#{len} exceeds available data") unless encrypted_private.bytesize == len
  authlen = CipherFactory.auth_length(ciphername)
  auth_tag = authlen > 0 ? buffer.read(authlen) : nil
  if kdfname == 'bcrypt'
    salt = kdfopts.read_string
    rounds = kdfopts.read_long
    raise ArgumentError.new("Private key bcrypt rounds #{rounds} exceeds #{MAX_KDF_ROUNDS}") if rounds > MAX_KDF_ROUNDS
    key = bcrypt_pbkdf_key(password, salt, keylen + ivlen, rounds)
  else
    key = "\x00" * (keylen + ivlen)
  end
  decoded = decrypt_private_key(
    ciphername,
    encrypted_private,
    auth_tag,
    key: key[0...keylen],
    iv: key[keylen...(keylen + ivlen)],
    encrypted_key: kdfname == 'bcrypt'
  )
  decoded = Net::SSH::Buffer.new(decoded)
  check1 = decoded.read_long
  check2 = decoded.read_long
  raise DecryptError.new("Decrypt failed on private key", encrypted_key: kdfname == 'bcrypt') if (check1 != check2)
  type_name = decoded.read_string
  case type_name
  when "ssh-ed25519"
    PrivKey.new(decoded)
  else
    decoded.read_private_keyblob(type_name)
  end
end

def self.require_bcrypt_pbkdf

def self.require_bcrypt_pbkdf
  require 'bcrypt_pbkdf'
end