SSH-BOX(5) | File Formats Manual | SSH-BOX(5) |
ssh-box
—
encrypted file format
A file encrypted by ssh-box
is represented
as PEM-encapsulated binary data. The binary consists of a mostly-cleartext
header, followed by the ciphertext of the encrypted file.
The header can contain user-defined metatada about the file and the list of public keys that were used to encrypt the file. A recipient who owns a corresponding private key can decrypt a blob in the header that contains the symmetric key to decrypt the file itself.
An ssh-box
encrypted file is
self-contained. Anyone who has a private key that is able to decrypt the
file can recover all the inputs that were used to encrypt the file.
See Data representation below for details of the types used in the following subsections.
The binary header and encrypted file are base64-encoded and
delimited by prefix and suffix lines with the label
‘SSH-BOX ENCRYPTED FILE
’, as described
in RFC 7468. For example,
-----BEGIN SSH-BOX ENCRYPTED FILE----- c3NoLWJveC12MQAAAAABAAAAC3NzaC1lZDI1NTE5AAAAIHRE3hd+N+jMlLuQsnB/IozFl/5O 4SBvM4uWlCN+Fs8PAAAAAmVnAAAAaKZcNtnpfC0VwHKA2EX/s7zNyuSraWc9xGVmpYJqeKMC Py10Oi9sXUN/Q4Kk9aNvbSXVaXQz76Q94cGT89pPx/lD5QusSNxmc8F1PmaGlakDwinczXT7 JDoDtw/CJDXQ7qdnt/OVDnTRDakxZU+eGgRVMeiwAgkzphgDXFN0IXvW -----END SSH-BOX ENCRYPTED FILE-----
An ssh-box
file should be generated
according to the ‘stricttextualmsg
’
syntax, and it should be parsed according to the
‘laxtextualmsg
’ syntax, as described
in RFC 7468 section 3.
The encrypted file's ciphertext follows immediately after the
ssh-box
header. The file is encrypted using the
libsodium function
crypto_aead_xchacha20poly1305_ietf_encrypt
(),
with the file's contents as the message, and the
ssh-box
header as the additional data.
The XChaCha20-Poly1305 AEAD construction also requires a nonce and
key, which are laid out with the following fixed-size structure and
encrypted using each recipient public key, before being included in the
ssh-box
header.
byte[24] | nonce |
byte[32] | key |
The header of an ssh-box
encrypted file
consists of a file format identifier followed by any number of header
items.
The format identifier is a URL pointing to this specification, terminated by a zero byte.
Each item starts with a byte containing a count of the number of string fields in the item. The list of items, and the whole header, is terminated by an item whose count byte is zero.
byte[] | “https://dotat.at/prog/ssh-box/v1\0 ” |
item | |
item | |
... | |
byte | equal to zero |
Following its count byte, an item contains that many string fields. The first field is a name indicating the type of the item. The number of fields that are required in an item, and the contents of those fields, depends on the first type field.
byte | count | count is at least one |
name | type | |
string | ||
... | item has count strings including the type |
In general, a recipient in an ssh-box
header is represented by their ssh
public key; and a
comment, like those in an authorized_keys file; and
a blob, encrypted using the public key, containing the
secrets used for
AEAD Encryption.
byte | count | |
name | type | public key format identifier |
string | ... | public key fields |
utf8 | comment | |
string | blob | encrypted secrets |
The comment and blob fields must be ignored when comparing a recipient item to a public key. The type and all public key fields should be equal for the keys to match.
When an ssh-box
header contains multiple
recipient items matching the user's public key, a decryption utility should
try to decrypt with all of them, and not give up at the first failure.
The ‘ssh-rsa
’ public key
format is described in RFC 4253 section 6.6.
The AEAD secrets are encrypted using RSAES-OAEP
with MFG1 and SHA-256, and the label
‘ssh-box-v1-rsa-oaep
’. RSAES-OAEP is
described in RFC 8017.
byte | 5 | |
name | “ssh-rsa ” |
|
mpint | e | public exponent |
mpint | n | public modulus |
utf8 | comment | |
string | blob | encrypted secrets |
The ‘ssh-ed25519
’ public key
format is described in RFC 8709 section 4. Ed25519 keys are for signatures
and authentication; for encryption and decryption the key must be converted
to curve25519, but note that ssh-box
always stores
keys in ‘ssh-ed25519
’ form.
Each
‘ssh-ed25519
’ recipient public key is
converted using the libsodium function
crypto_sign_ed25519_pk_to_curve25519
()
and the resulting key is used to encrypt the AEAD secrets
using the libsodium function
crypto_box_seal
().
Decryption uses the
libsodium functions
crypto_sign_ed25519_sk_to_curve25519
()
and
crypto_box_seal_open
().
byte | 4 | |
name | “ssh-ed25519 ” |
|
string[32] | key | |
utf8 | comment | |
string | blob | encrypted secrets |
The ‘label
’ on an
ssh-box
is arbitrary application-defined public
metadata describing the encrypted contents of the file. For example, if the
box contains a password, the label might be a JSON object containing the
corresponding username and the URL of the login form.
The label is unencrypted cleartext, so that you can find out what a file is for even without a decryption key. The label (and the rest of the header) is authenticated, so if you do have a decryption key, you can be sure the label has not been tampered with.
byte | 2 | |
name | “label ” |
|
string | contents |
When an ssh-box
header contains multiple
‘label
’ items, the complete contents
of the label should be constructed by concatenating the
contents of every
‘label
’ item, in the same order as
they appear in the ssh-box
header, without any
framing.
A program must not fail to read an ssh-box
encrypted file because its header contains an item
with an unknown type.
A program that is generating or manipulating an
ssh-box
encrypted file must not include any header
item that it does not understand. (In particular, it
must not carelessly copy items from one ssh-box
header to another.)
Any file with a different PEM encapsulation label or a different format identifier is not covered by this spec.
The ssh-box
header is based on data types
and structures used by SSH, as described in RFC 4251 section 5, and as
follows:
There are a few kinds of field that have the same representation as a string but whose contents have a particular purpose, or a restricted syntax.
Standard names consist of ASCII characters with codes between
33 and 126 (inclusive), excluding
‘,
’ (ASCII 44) and
‘@
’ (ASCII 64).
Non-standard extensions can use names of the form
‘name
’.@
domain
It is generally considered to be a bad idea to use the same key
pair for signing and encryption. SSH key pairs are normally used for signing
(i.e for authentication), but ssh-box
repurposes
them as encryption keys.
The risk with this kind of reuse is that it opens you up to cross-protocol attacks, where one protocol is used to gain access to a signing or encryption oracle that allows you to break the other protocol.
Another tool that re-uses ssh keys for encryption is
age
. The
‘age-encryption
’ format specification
argues that key reuse is safe with age
's tweaked
curve25519 scheme. In ssh-box
, there is no
additional tweak of the curve25519 keys, because of
ssh-box
's goal to use off-the-shelf cryptographic
constructions.
You can also use RSA keys with age
, but
its spec doesn't explain why this use of RSA is safe. Both
age
and ssh-box
use
RSAES-OAEP, whereas ssh uses PKCS #1 v1.5. This difference may reduce the
risk of cross-protocol attacks.
To reduce risks, you can:
ssh-box
, separate from the ssh keys you use for
authentication.ssh-box
with ssh host keys, because ssh
host authentication allows an attacker to provoke private key operations
much more easily than user authentication.Given an unencrypted cleartext file,
EXAMPLE
ssh-box
encrypted version is conventionally called
EXAMPLE.box
EXAMPLE.label
See ssh-box(1) for more details of the files it uses.
Libsodium
documentation
specifically the sections on:
The Secure Shell (SSH) Protocol Assigned Numbers, RFC 4250, https://www.rfc-editor.org/rfc/rfc4250.
The Secure Shell (SSH) Protocol Architecture, RFC 4251, https://www.rfc-editor.org/rfc/rfc4251.
The Secure Shell (SSH) Transport Layer Protocol, RFC 4253, https://www.rfc-editor.org/rfc/rfc4253.
Textual Encodings of PKIX, PKCS, and CMS Structures, RFC 7468, https://www.rfc-editor.org/rfc/rfc7468.
PKCS #1: RSA Cryptography Specifications Version 2.2, RFC 8017, https://www.rfc-editor.org/rfc/rfc8017.
Ed25519 and Ed448 Public Key Algorithms for the Secure Shell (SSH) Protocol, RFC 8709, https://www.rfc-editor.org/rfc/rfc4251.
This specification is written in terms of several libsodium functions. The aim is to use the best available misuse-resistant high-level cryptographic functions, and avoid being too clever. It would be better to have a description of what these functions do, in enough detail that an expert would be able to write an alternative implementation. However the libsodium documentation and source code do not cite any specifications.
Tony Finch ⟨dot@dotat.at⟩
November 14, 2021 | SSH-BOX Pq 5 |