Tink wire format

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 serialized AesGcmKey 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 an encrypted DEK.
  • The DEK is used to encrypt the plaintext with the associated data into ciphertext. So ciphertext has the exact same format as the AEAD primitive corresponding to the DEK.

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
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.