Class: SshTresor::Vault

Inherits:
Object
  • Object
show all
Defined in:
lib/ssh_tresor/vault.rb

Overview

Public high-level API for SSH-agent-mediated encryption at rest from another Ruby application or gem.

Vault is intentionally a small facade over the lower-level SSH agent, crypto, and wire-format objects. It connects to SSH_AUTH_SOCK by default, but accepts an injected agent object for tests or alternate transports. The live capability required to decrypt is agent signing for a stored challenge, not possession of the public key or fingerprint alone.

Examples:

Encrypt and decrypt using the current SSH agent

vault = SshTresor::Vault.new
encrypted = vault.encrypt("secret", armor: true)
plaintext = vault.decrypt(encrypted)

Inject a custom agent implementation

vault = SshTresor::Vault.new(agent: my_agent)

See Also:

Instance Method Summary collapse

Constructor Details

#initialize(agent: Agent.connect) ⇒ SshTresor::Vault

Creates a vault bound to an SSH agent.

The default agent is opened from ENV["SSH_AUTH_SOCK"]. The injected agent must implement the subset of Agent used by the high-level operations: first_key, find_key, list_keys, and sign.

Parameters:

  • agent (#first_key, #find_key, #list_keys, #sign) (defaults to: Agent.connect)

    SSH agent-like object.

Raises:



36
37
38
# File 'lib/ssh_tresor/vault.rb', line 36

def initialize(agent: Agent.connect)
  @agent = agent
end

Instance Method Details

#add_all_keys(encrypted, armor: nil) ⇒ Array(String, Integer)

Adds slots for all available SSH agent keys not already present.

Keys that are already present or cannot sign are skipped.

Parameters:

  • encrypted (String)

    Binary or armored tresor content.

  • armor (Boolean, nil) (defaults to: nil)

    Output armor mode. nil preserves input format.

Returns:

  • (Array(String, Integer))

    Updated tresor content and number of slots added.

Raises:



97
98
99
100
101
102
# File 'lib/ssh_tresor/vault.rb', line 97

def add_all_keys(encrypted, armor: nil)
  input_was_armored = armored?(encrypted)
  blob = TresorBlob.from_bytes(encrypted)
  updated, added = Tresor.add_all_keys_with_agent(@agent, blob)
  [serialize(updated, armor.nil? ? input_was_armored : armor), added]
end

#add_key(encrypted, fingerprint:, armor: nil) ⇒ String

Adds one SSH key slot to an existing tresor.

The current agent must be able to decrypt an existing slot before adding a new one, because the master key must be recovered and re-wrapped for the new key.

Parameters:

  • encrypted (String)

    Binary or armored tresor content.

  • fingerprint (String)

    Fingerprint or unambiguous prefix of the key to add.

  • armor (Boolean, nil) (defaults to: nil)

    Output armor mode. nil preserves input format.

Returns:

  • (String)

    Updated encrypted tresor content.

Raises:



82
83
84
85
86
87
# File 'lib/ssh_tresor/vault.rb', line 82

def add_key(encrypted, fingerprint:, armor: nil)
  input_was_armored = armored?(encrypted)
  blob = TresorBlob.from_bytes(encrypted)
  updated = Tresor.add_key_with_agent(@agent, blob, fingerprint)
  serialize(updated, armor.nil? ? input_was_armored : armor)
end

#decrypt(encrypted) ⇒ String

Decrypts an encrypted tresor using any matching signing key in the SSH agent.

The input may be binary SSHTRESR v3 data or armored text. The agent is asked to sign the stored slot challenge for matching key fingerprints.

Parameters:

  • encrypted (String)

    Binary or armored tresor content.

Returns:

  • (String)

    Decrypted plaintext bytes.

Raises:



66
67
68
# File 'lib/ssh_tresor/vault.rb', line 66

def decrypt(encrypted)
  Tresor.decrypt_with_agent(@agent, TresorBlob.from_bytes(encrypted))
end

#encrypt(plaintext, fingerprints: [], armor: false) ⇒ String

Encrypts plaintext for one or more signing keys available in the SSH agent.

When no fingerprints are given, the first key returned by the agent is used. Fingerprints may be full SHA256:... values or unambiguous prefixes.

Parameters:

  • plaintext (String)

    Plaintext bytes to encrypt.

  • fingerprints (Array<String>) (defaults to: [])

    SSH key fingerprints to encrypt for.

  • armor (Boolean) (defaults to: false)

    Whether to return base64 armor instead of binary format.

Returns:

  • (String)

    Encrypted tresor bytes or armored text.

Raises:



51
52
53
54
# File 'lib/ssh_tresor/vault.rb', line 51

def encrypt(plaintext, fingerprints: [], armor: false)
  blob = Tresor.encrypt_with_agent(@agent, plaintext, fingerprints: fingerprints)
  armor ? blob.to_armored : blob.to_bytes
end

#list_keysArray<SshTresor::AgentKey>

Lists keys currently available through the configured SSH agent.

Returns:

Raises:



126
127
128
# File 'lib/ssh_tresor/vault.rb', line 126

def list_keys
  @agent.list_keys
end

#list_slots(encrypted) ⇒ Array<String>

Lists key slot fingerprints present in encrypted tresor content.

This does not require access to an SSH agent because slot fingerprints are stored in the tresor header.

Parameters:

  • encrypted (String)

    Binary or armored tresor content.

Returns:

  • (Array<String>)

    Raw 32-byte SHA-256 fingerprint bytes for each slot.

Raises:



138
139
140
# File 'lib/ssh_tresor/vault.rb', line 138

def list_slots(encrypted)
  TresorBlob.from_bytes(encrypted).slot_fingerprints
end

#remove_key(encrypted, fingerprint:, armor: nil) ⇒ String

Removes one key slot from an existing tresor.

This operation only edits metadata and does not require the SSH agent to hold the removed key. Removing the final slot is rejected.

Parameters:

  • encrypted (String)

    Binary or armored tresor content.

  • fingerprint (String)

    Fingerprint or unambiguous prefix of the slot to remove.

  • armor (Boolean, nil) (defaults to: nil)

    Output armor mode. nil preserves input format.

Returns:

  • (String)

    Updated encrypted tresor content.

Raises:



115
116
117
118
119
120
# File 'lib/ssh_tresor/vault.rb', line 115

def remove_key(encrypted, fingerprint:, armor: nil)
  input_was_armored = armored?(encrypted)
  blob = TresorBlob.from_bytes(encrypted)
  updated = Tresor.remove_key(blob, fingerprint)
  serialize(updated, armor.nil? ? input_was_armored : armor)
end