This page describes Tink's wire format for keys and primitive output. The documentation is aimed at cryptographers who want to add additional languages to Tink and maintainers of other high-level crypto libraries who want a wire compatible mode. It is not intended for general audiences.
Keyset serialization
Tink uses Google protobuf to serialize its keysets.
- A binary serialized keyset is a serialized Keyset proto defined in tink.proto. The KeyData value property of a key is a serialized proto of the corresponding key type.
- A JSON serialized keyset is a Keyset proto serialized in JSON format. Note that the KeyData value is still a binary serialized proto.
- An encrypted keyset is a serialized EncryptedKeyst proto defined in tink.proto. It contains an encrypted binary serialized keyset and optionally some unencrypted KeysetInfo metadata.
Tink Output Prefix
Most Tink primitives support a 5 byte output prefix consisting of:
- 1 byte version:
0x01
- 4 bytes key hint: This is the key ID of the key used.
Some legacy keys may also support the version byte 0x00
.
Note that this prefix is not authenticated and cannot be relied on for security purposes. Tink uses it as a hint to speed up decryption or verification.
AEAD
In general, Tink formats AEAD ciphertexts as:
prefix || IV || ciphertext || tag
unless otherwise specified in the corresponding RFC. prefix
is either empty
or a 5 byte Tink output prefix.
AES-CTR-HMAC
For AES-CTR-HMAC, Tink computes the MAC with associated data (AD) as follows:
AD || IV || ciphertext || bitlen(AD)
where bitlen(AD)
is AD's length in bits represented as 64-bit big-endian
unsigned integer. This HMAC scheme follows the draft for AES-CBC-HMAC from
Mcgrew.
Deterministic AEAD
Tink implements RFC 5297 for AES-SIV, putting the synthetic initialization vector (SIV) at the beginning of the ciphertext. The primitive may add a 5 byte Tink output prefix.
While RFC 5297 supports a list of associated datas, Tink only supports exactly one associated data, which corresponds to a list with one element in RFC 5297. An empty associated data is a list with one empty element, and not an empty list.
Streaming AEAD
See AES-CTR HMAC and AES-GCM-HKDF.
Envelope encryption
Envelope encryption encrypts the data with a data encryption key DEK
using
Tink's AEAD primitives. The encryption works as follows:
- A new
DEK
is generated, using a given key template (or key parameters). - The
DEK
is serialized into a byte string. The serialization format the protocol buffer serialization of the key type proto. For example, this is a serializedAesGcmKey
protocol buffer message defined in aes_gcm.proto for DEK of key type AES GCM. See protocol buffer serialization for how to serialize a protocol buffer. - The serialized
DEK
is encrypted by an external provider (for example, GCP), into anencrypted DEK
. - The
DEK
is used to encrypt the plaintext with the associated data intociphertext
. Sociphertext
has the exact same format as the AEAD primitive corresponding to theDEK
.
The output format of envelope encryption is as follows:
encrypted DEK length || encrypted DEK || ciphertext
The encrypted DEK length
is 4 bytes, storing the length of the encrypted DEK
as a 32-bit big-endian integer.
MAC
Tink follows the corresponding RFCs. Primitives may add a 5 byte Tink output prefix to the tag.
PRF set
Tink follows the corresponding RFCs. Note that for PRF Set the key type differs from the MAC key type of the same algorithm by not including the output length. PRF Set keys never add a Tink output prefix. This ensures the output is actually a PRF.
Hybrid encryption
The general wire format for Tink hybrid encryption is the following:
prefix || encapsulated_key || encrypted_data
prefix
is either empty or a 5 byte Tink output prefix. Each key type contains
the information on how many bytes to parse, and how to parse those bytes from
encapsulated_key
.
HPKE (Hybrid Public Key Encryption)
Tink follows the HPKE standard defined in RFC 9180. An HPKE ciphersuite includes the following three primitives.
- Key encapsulation mechanism (KEM)
- Key derivation function (KDF)
- Authenticated encryption with associated data (AEAD)
The HPKE standard does not define a general wire format in RFC 9180, Section
10. Tink's HPKE implementation uses the following
encapsulated_key
and encrypted_data
values.
encapsulated_key
- Sender's serialized public key
- Defined as
enc
in RFC 9180, Section 4.1 - Format determined by the specific HPKE KEM used
encrypted_data
- Ciphertext and tag (i.e.,
ciphertext || tag
without the IV) - Defined as
ct
in RFC 9180, Section 4 - Format determined by the specific HPKE AEAD used
- Ciphertext and tag (i.e.,
X25519 Diffie-Hellman-based KEM
For X25519 DHKEMs, the value enc
is the sender's 32-byte Diffie-Hellman public
key.
ECIES-AEAD-HKDF
For Tink's ECIES-AEAD-HKDF implementation, encapsulated_key
is the output
of the Key Encapsulation Mechanism (KEM) and encrypted_data
is the output
of the Data Encapsulation Mechanism (DEM).
KEM
Depending on the key type, Tink uses compressed and uncompressed elliptic curve
points, following RFC 8422/ANSI.X9-62.2005
encoding standards. For
uncompressed points, the byte 0x04
is followed by the x
and the y
coordinate as fixed size integers. For compressed coordinates, the byte 0x02
or 0x03
and the x
coordinate as a fixed size integer is used. For X25519
,
the RFC 7748 definition is used (x
coordinate as fixed size integer).
DEM
For encrypted_data
, Tink uses the same format as the AEAD. This includes
specifying an IV.
Key derivation
First the x coordinate x_ss
of the shared point is computed. The key for the
AEAD is then set to:
HKDF(ikm = encapsulated_key || x_ss, salt = salt_of_key, info = context_info, length = dem_key_size)
where encapsulated_key
is the full KEM output as bytes.
Digital signatures
Tink follows the corresponding RFCs. Primitives may add a 5 byte Tink output prefix to the tag that is generated.
ECDSA
Depending on the EcdsaSignatureEncoding field in the in the key,
the format of a ECDSA signature is either IEEE P1363
or ASN.1 DER
.
The IEEE P1363
signature's format is r || s
, where r
and s
are
zero-padded and have the same size in bytes as the order of the curve. For
example, for NIST P-256
curve, r
and s
are zero-padded to 32 bytes.
The DER signature is encoded using ASN.1
:
ECDSA-Sig-Value :: = SEQUENCE { r INTEGER, s INTEGER }
In particular, the encoding is:
0x30 || totalLength || 0x02 || r's length || r || 0x02 || s's length || s
Tink follows the best practices for signature verification, by only accepting DER encoded ECDSA signatures (alternative BER encoded signatures are invalid).
This helps prevent signature malleability attacks, which often affect cryptocurrency systems.