Перейти к содержимому
← Документация API

Specification

Формат пакета INTD v1

Формат открыт намеренно: конфиденциальность и целостность дают шифрование и подпись запроса, а не неизвестность разметки. Фиксированная структура и CRC32 позволяют серверу дёшево отбрасывать мусор до записи в хранилище.

Stable Big-endian CRC-32/IEEE X25519 · HKDF · AES-256-GCM

Контейнер

СмещениеРазмерПолеЗначение
0 4 MAGIC 0x49 0x4E 0x54 0x44 («INTD»)
4 1 VERSION 0x01
5 1 FLAGS 0x00, зарезервировано
6 8 TIMESTAMP uint64 BE, unix мс — база для дельт
14 4 EVENTS_COUNT uint32 BE
18 4 BLOB_LENGTH uint32 BE, длина EVENTS_BLOB
22 N EVENTS_BLOB события подряд
22+N 4 CRC32 uint32 BE, CRC-32/IEEE от EVENTS_BLOB

Итоговый размер ровно 26 + N байт; лишние байты — ошибка.

Событие

РазмерПолеЗначение
1 TYPE 0x01 pageview, 0x02 search
4 TS_DELTA uint32 BE, мс после TIMESTAMP
2 + var DOMAIN uint16 длина ≤ 255, UTF-8
2 + var URL uint16 длина ≤ 2048, UTF-8
2 + var QUERY uint16 длина ≤ 500, 0 если нет
1 + var LANG uint8 длина ≤ 20, BCP 47
1 DEVICE_TYPE 0x00 unknown, 0x01 desktop, 0x02 mobile, 0x03 tablet, 0x04 bot

Строки обрезаются по границе символа UTF-8. Сервер отклоняет пакет, если длина больше лимита, строка не является корректным UTF-8, число событий не совпадает с заголовком или CRC32 не сходится.

Что делает сервер

  • Домен пересчитывается из URL (eTLD+1), значение клиента не используется.
  • Из URL и запроса вырезаются токены, пароли, e-mail и номера карт.
  • События старше 7 дней и из будущего (больше 10 минут) отбрасываются.
  • Страна определяется по IP соединения, идентификатор установки хешируется отдельно для каждого расширения.

Шифрование и подпись

envelope
sessionKey   = random(32)
iv           = random(12)
ciphertext   = AES-256-GCM(sessionKey, iv, bucket)
ephemeral    = X25519 — новая пара на каждый пакет
shared       = X25519(ephemeral.private, extension.publicKey)
derivedKey   = HKDF-SHA256(IKM = shared, salt = iv, info = "intentd-bucket-v1", 32)
encryptedKey = AES-256-GCM(derivedKey, iv, sessionKey)   // 48 байт
signature
X-Signature = hex(HMAC-SHA256(secret,
  "intentd-v2|bucket|" + X-Timestamp + "|" + X-API-Key + "|" + hex(SHA-256(body))))

Схема не даёт прямой секретности при последующей компрометации приватного ключа расширения и не защищает от владельца копии бандла: от переупаковки защищают сессии на каждую установку, проверка ID расширения и проверки на сервере.

Тестовые векторы

bucket (hex)
// Одно событие search: база 1758000000000, дельта 7,
// домен a.io, URL https://a.io/?q=x, запрос x, язык en, desktop
494e5444 01 00 0000019950f72c00 00000001 00000025
02 00000007 0004 612e696f 0011 68747470733a2f2f612e696f2f3f713d78 0001 78 02 656e 01
cee33ee2
hkdf
HKDF-SHA256(IKM = 0x11 × 32, salt = 0x22 × 12, info = "intentd-bucket-v1", L = 32)
=> 4a359b8818fc073f6f81a3cb4acd9065302e285fbef0d35d2ff71fba107e6056