Tink Wire Format

This is a description of Tink's wire format for keys and primitive output. The documentation is aimed at cryptographers wanting to add additional languages to Tink and maintainers of other high-level crypto libraries wanting to give a wire compatible mode. It is not intended for general audiences.

Keys

Tink uses Google protobuf to store its keys. A keyset contains a serialized proto of the corresponding type as its KeyData value property. An encrypted keyset similarly is the wire format of the proto library, encrypted with the given AEAD, stored in the corresponding proto.

Crypto Formats

By default, primitives use the Tink prefix output mode. This mode results in a five byte prefix consisting of:

  • 1 byte version (0x01 for Tink, 0x00 for Google internal legacy formats)
  • 4 bytes key hint. This is the key id to be used to try to decrypt/verify this ciphertext first.

Note that this prefix is not authenticated and cannot be relied on for security purposes. Tink will first try and decrypt/validate a ciphertext with the hinted key and if the operation fails, it will proceed to attempt to decrypt/validate with all keys that have RAW prefix type specified.

AEAD

In general, Tink will format AEAD ciphertexts as

IV || ciphertext || tag,

unless otherwise specified in the corresponding RFC.

AES-CTR-HMAC

For AES-CTR-HMAC, Tink will compute the MAC with associated data as follows:

AAD || IV || ciphertext || bitlen(AAD)

with bitlen(AAD) is aad's length in bits represented as 64-bit bigendian 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 SIV/Tag at the beginning of the ciphertext.

Streaming Encryption

The format of the ciphertext is

header || segment_0 || segment_1 || ... || segment_k

where:

  • segment_i is the i-th segment of the ciphertext.
  • the size of segment_1 .. segment_{k-1} is get_ciphertext_segment_size()
  • segment_0 is shorter, so that segment_0, the header and other information of size get_ciphertext_offset() align with get_ciphertext_segment_size().

The format of the header is

header_size || salt || nonce_prefix

where:

  • header_size is 1 byte determining the size of the header
  • salt is a salt used in the key derivation. It has the same size as the key size chosen for the block cipher.
  • nonce_prefix is the prefix of the nonce. Currently it has the same size of 7 bytes for all encryption modes.

The salt is generated randomly on stream generation. The file key is derived as

HKDF(ikm=key, salt=salt, info=associated_data, len=key_size)

and each segment_i is encrypted using the IV

nonce_prefix || i || last_segment

where i is the segment number as 32 bit integer, last_segment is equal to 0 for all but the last segment, where it equals 1. The segment is encrypted without associated data being set. Note that the header_size is completely determined by the key parameters and should be checked independently.

AES-CTR-HMAC-HKDF

AES-CTR-HMAC-HKDF uses a nonce prefix of 7 bytes and sets the last 4 bytes of the IV to zero, to get a 16 byte IV for use in CTR mode. The first ikm size bytes of the key derivation result are used as key for CTR mode, the next 32 bytes are used as HMAC key. The tag is computed as the HMAC of

IV || ciphertext.

AES-GCM-HKDF

AES-GCM-HKDF uses a nonce prefix of 7 bytes to get 12 byte IVs for the segment encryption. The key derived is the same size as the input key material.

Envelope Encryption

Envelope encryption encrypts the data with a data encryption key DEK using Tink's AEAD primitives. In addition the DEK is encrypted with an external provider (e.g. GCP) and prepended to the ciphertext. The format for envelope encryption is as follows:

DEK length || encrypted DEK || ciphertext

The DEK length is 4 bytes, storing the length of the encrypted DEK as a 32-bit big endian integer. The format of the encrypted DEK depends on the external provider which was used for encrypting the DEK. The ciphertext will have the exact same format as the AEAD primitive corresponding to the DEK.

MAC

Tink follows the corresponding RFCs.

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 outputlength. PRF Set keys have to have a RAW output prefix type, as the key ID handling is done by the user. This ensures the output is actually a PRF.

Hybrid Encryption

Hybrid Encryption uses a Key Encryption Message (KEM) and a Data Encryption Message (DEM). The general format is

KEM || DEM,

with the key type knowing how many bytes to parse for the KEM.

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, RFC 7748 defining it is used (x coordinate as fixed size integer).

DEM

For the data encryption message, Tink uses the same format as the AEAD uses. 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 = kem || x_ss, salt = salt_of_key, info = context_info, length = dem_key_size),

where kem is the full kem as bytes.

Digital Signatures

Depending on the corresponding field in the key, the format of a digital signature is either IEEE P1363 format and ASN.1 DER format for ECDSA.

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 as outlined by bitcoin.