ssh-tresor-ruby
ssh-tresor encrypts and decrypts secrets using keys available through
ssh-agent.
The private key never leaves the SSH agent. Encryption creates a random master
key, asks the agent to sign random per-key challenges, derives slot keys with
HKDF-SHA256, and stores an AES-256-GCM encrypted master key slot for each SSH
key. Decryption works when one matching SSH key is loaded locally or forwarded
with ssh -A.
It is freely inspired by the ssh-tresor project but doesn't depend
on it.
Usage
ssh-tresor list-keys
echo -n "secret" | ssh-tresor encrypt -a > secret.tresor
ssh-tresor decrypt secret.tresor
ssh-tresor list-slots secret.tresor
ssh-tresor add-key -k SHA256:abc < secret.tresor > updated.tresor
ssh-tresor remove-key -k SHA256:abc < updated.tresor > reduced.tresor
Library API
Other gems can depend on ssh-tresor-ruby and call it directly:
require "ssh_tresor"
vault = SshTresor::Vault.new
encrypted = vault.encrypt("secret", armor: true)
plaintext = vault.decrypt(encrypted)
updated = vault.add_key(encrypted, fingerprint: "SHA256:abc", armor: true)
slots = vault.list_slots(updated)
keys = vault.list_keys
The Vault instance connects to SSH_AUTH_SOCK by default. You can inject a
custom agent object for tests or alternate transports:
vault = SshTresor::Vault.new(agent: my_agent)
The lower-level SshTresor::TresorBlob parser and SshTresor::Tresor module
remain available if you need direct access to parsed slots.
Wire Format
The implementation writes and reads the SSHTRESR v3 format:
Header: SSHTRESR (8) + version (1) + slot_count (1)
Slot: fingerprint (32) + challenge (32) + nonce (12) + encrypted_key (48)
Data: nonce (12) + ciphertext including 16-byte AES-GCM auth tag