Class: Vagrant::Util::Keypair::Ed25519

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant/util/keypair.rb

Constant Summary collapse

KEY_TYPE =

Key type identifier

"ssh-ed25519".freeze

Class Method Summary collapse

Class Method Details

.create(password = nil) ⇒ Array<String, String, String>

Note:

Password support was not included as it's not actively used anywhere. If it ends up being

Creates an ed25519 SSH key pair something that's needed, it can be revisited

Returns:

  • (Array<String, String, String>)

    Public key, openssh private key, openssh public key with comment



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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) ⇒ String

Encodes given string with padding to block size

Parameters:

  • s (String)

    String to encode

  • blocksize (Integer)

    Defined block size

Returns:

  • (String)


64
65
66
67
# 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) ⇒ String

Encodes given string

Parameters:

  • s (String)

    String to encode

Returns:

  • (String)


55
56
57
# File 'lib/vagrant/util/keypair.rb', line 55

def self.string(s)
  [s.length].pack("N") + s
end