ssh-tresor-ruby

API docs

ssh-tresor provides SSH-agent-mediated encryption at rest: secrets are stored encrypted on disk and can be decrypted only while a matching SSH agent signing capability is available.

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.

The live capability is agent signing, not possession of the public key. Public key fingerprints are stored in the tresor metadata so the right slot can be found, but the file cannot be unlocked unless the agent can sign the stored challenge for that slot.

For a detailed cryptographic analysis of the construction, see the ssh-tresor-ruby white paper. Generated API documentation is published at capripot.github.io/ssh-tresor-ruby.

It is freely inspired by the ssh-tresor project but doesn't depend on it.

Usage

gem install ssh-tresor
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

bundle add ssh-tresor

Your application or 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