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

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

Constant Summary collapse

AUTH_MAGIC =

Magic string header

"openssh-key-v1".freeze
KEY_TYPE =

Key type identifier

"ssh-ed25519".freeze
PRIVATE_KEY_START =

Header of private key file content

"-----BEGIN OPENSSH PRIVATE KEY-----\n".freeze
PRIVATE_KEY_END =

Footer of private key file content

"-----END OPENSSH PRIVATE KEY-----\n".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



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/vagrant/util/keypair.rb', line 45

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)


36
37
38
39
# File 'lib/vagrant/util/keypair.rb', line 36

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)


27
28
29
# File 'lib/vagrant/util/keypair.rb', line 27

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