Module: Xshellz::Keys
- Defined in:
- lib/xshellz/keys.rb
Overview
In-memory ed25519 SSH keypair generation.
The keypair is generated per Sandbox.create and never touches disk; the
control plane only ever sees the public half. Both halves are encoded by
hand (RFC 4253 public line, openssh-key-v1 private container) so the
private key can be handed to net-ssh via key_data without any file IO.
Defined Under Namespace
Classes: KeyPair
Constant Summary collapse
- DEFAULT_KEY_COMMENT =
"xshellz-sdk"- AUTH_MAGIC =
"openssh-key-v1\0"- KEY_TYPE =
"ssh-ed25519"- BLOCK_SIZE =
8
Class Method Summary collapse
-
.base64(data) ⇒ Object
Strict (unwrapped) base64 without pulling in the base64 gem.
-
.encode_openssh_private_key(seed, public_bytes, comment) ⇒ Object
Serialize the private key into the openssh-key-v1 container (unencrypted: cipher "none", kdf "none") - the format ssh-keygen writes and the one net-ssh's ED25519 support parses from
key_data. -
.generate(comment: DEFAULT_KEY_COMMENT) ⇒ KeyPair
Generate a fresh in-memory ed25519 keypair.
-
.public_key_blob(public_bytes) ⇒ Object
The RFC 4253 public key blob: string "ssh-ed25519" + string key-bytes.
-
.ssh_string(value) ⇒ Object
A length-prefixed SSH wire string.
Class Method Details
.base64(data) ⇒ Object
Strict (unwrapped) base64 without pulling in the base64 gem.
89 90 91 |
# File 'lib/xshellz/keys.rb', line 89 def base64(data) [data].pack("m0") end |
.encode_openssh_private_key(seed, public_bytes, comment) ⇒ Object
Serialize the private key into the openssh-key-v1 container (unencrypted:
cipher "none", kdf "none") - the format ssh-keygen writes and the one
net-ssh's ED25519 support parses from key_data.
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 |
# File 'lib/xshellz/keys.rb', line 60 def encode_openssh_private_key(seed, public_bytes, comment) check = SecureRandom.bytes(4) section = "".b section << check << check section << ssh_string(KEY_TYPE) section << ssh_string(public_bytes) section << ssh_string(seed + public_bytes) section << ssh_string(comment) pad = 1 until (section.bytesize % BLOCK_SIZE).zero? section << [pad].pack("C") pad += 1 end blob = "".b blob << AUTH_MAGIC.b blob << ssh_string("none") # ciphername blob << ssh_string("none") # kdfname blob << ssh_string("") # kdfoptions blob << [1].pack("N") # number of keys blob << ssh_string(public_key_blob(public_bytes)) blob << ssh_string(section) body = base64(blob).scan(/.{1,70}/).join("\n") "-----BEGIN OPENSSH PRIVATE KEY-----\n#{body}\n-----END OPENSSH PRIVATE KEY-----\n" end |
.generate(comment: DEFAULT_KEY_COMMENT) ⇒ KeyPair
Generate a fresh in-memory ed25519 keypair.
33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/xshellz/keys.rb', line 33 def generate(comment: DEFAULT_KEY_COMMENT) signing_key = Ed25519::SigningKey.generate seed = signing_key.to_bytes public_bytes = signing_key.verify_key.to_bytes line = "#{KEY_TYPE} #{base64(public_key_blob(public_bytes))}" line = "#{line} #{comment}" unless comment.to_s.empty? KeyPair.new( private_key_openssh: encode_openssh_private_key(seed, public_bytes, comment.to_s), public_key_line: line ) end |
.public_key_blob(public_bytes) ⇒ Object
The RFC 4253 public key blob: string "ssh-ed25519" + string key-bytes.
48 49 50 |
# File 'lib/xshellz/keys.rb', line 48 def public_key_blob(public_bytes) ssh_string(KEY_TYPE) + ssh_string(public_bytes) end |
.ssh_string(value) ⇒ Object
A length-prefixed SSH wire string.
53 54 55 |
# File 'lib/xshellz/keys.rb', line 53 def ssh_string(value) [value.bytesize].pack("N") + value.dup.force_encoding(Encoding::BINARY) end |