class Vagrant::Util::Keypair::Ed25519

Constants

KEY_TYPE

Key type identifier

Public Class Methods

create(password=nil) click to toggle source

Creates an ed25519 SSH key pair @return [Array<String, String, String>] Public key, openssh private key, openssh public key with comment @note Password support was not included as it’s not actively used anywhere. If it ends up being something that’s needed, it can be revisited

# File lib/vagrant/util/keypair.rb, line 73
def self.create(password=nil)
  if password
    raise NotImplementedError,
          "Ed25519 key pair generation does not support passwords"
  end

  # Generate the key
  base_key = ::Ed25519::SigningKey.generate
  # Define the comment used for the key
  comment = "vagrant"

  # Grab the raw public key
  public_key = base_key.verify_key.to_bytes
  # Encode the public key for use building the openssh private key
  encoded_public_key = string(KEY_TYPE) + string(public_key)
  # Format the public key into the openssh public key format for writing
  openssh_public_key = "#{KEY_TYPE} #{Base64.encode64(encoded_public_key).gsub("\n", "")} #{comment}"

  # Agent encoded private key is used when building the openssh private key
  # (https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent#section-4.2.3)
  # (https://dnaeon.github.io/openssh-private-key-binary-format/)
  agent_private_key = [
    ([SecureRandom.random_number((2**32)-1)] * 2).pack("NN"), # checkint, random uint32 value, twice (used for encryption verification)
    encoded_public_key, # includes the key type and public key
    string(base_key.seed + public_key), # private key with public key concatenated
    string(comment), # comment for the key
  ].join

  # Build openssh private key data (https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key)
  private_key = [
    AUTH_MAGIC + "\0", # Magic string
    string("none"), # cipher name, no encryption, so none
    string("none"), # kdf name, no encryption, so none
    string(""), # kdf options/data, no encryption, so empty string
    [1].pack("N"), # Number of keys (just one)
    string(encoded_public_key), # The public key
    padded_string(agent_private_key, 8) # Private key encoded with agent rules, padded for 8 byte block size
  ].join

  # Create the openssh private key content
  openssh_private_key = [
    PRIVATE_KEY_START,
    Base64.encode64(private_key),
    PRIVATE_KEY_END,
  ].join

  return [public_key, openssh_private_key, openssh_public_key]
end
padded_string(s, blocksize) click to toggle source

Encodes given string with padding to block size

@param [String] s String to encode @param [Integer] blocksize Defined block size @return [String]

# File lib/vagrant/util/keypair.rb, line 64
def self.padded_string(s, blocksize)
  pad = blocksize - (s.length % blocksize)
  string(s + Array(1..pad).pack("c*"))
end
string(s) click to toggle source

Encodes given string

@param [String] s String to encode @return [String]

# File lib/vagrant/util/keypair.rb, line 55
def self.string(s)
  [s.length].pack("N") + s
end