Class: Vagrant::Util::Keypair::Ecdsa

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

Overview

Base class for Ecdsa type keys to subclass

Direct Known Subclasses

Ecdsa256, Ecdsa384, Ecdsa521

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



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/vagrant/util/keypair.rb', line 198

def self.create(password=nil)
  if password
    raise NotImplementedError,
          "Ecdsa key pair generation does not support passwords"
  end

  # Generate the key
  base_key = OpenSSL::PKey::EC.generate(self.const_get(:OPENSSL_CURVE))
  # Define the comment used for the key
  comment = "vagrant"

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

  pk_value = base_key.private_key.to_s(2)
  # Pad the start of the key if required
  if pk_value.length % 8 == 0
    pk_value = "\0#{pk_value}"
  end

  # 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(pk_value), # private key
    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)


189
190
191
192
# File 'lib/vagrant/util/keypair.rb', line 189

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)


180
181
182
# File 'lib/vagrant/util/keypair.rb', line 180

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