Documentation
¶
Overview ¶
Package mercury provides functionality for the TDTP framework.
Index ¶
- Constants
- Variables
- func DecodeKey(keyB64 string) ([]byte, error)
- func ErrorCode(err error) string
- func VerifyHMAC(packageUUID, receivedHMAC, serverSecret, mode string) bool
- func VerifyHMACDev(_, _, _ string) bool
- type BindKeyRequest
- type Client
- func (c *Client) BindKey(ctx context.Context, packageUUID, pipelineName string) (*KeyBinding, error)
- func (c *Client) RegisterHash(ctx context.Context, uuid string, part int, ...) error
- func (c *Client) RetrieveKey(ctx context.Context, packageUUID, caller string) (string, error)
- func (c *Client) VerifyHash(ctx context.Context, uuid string, part int, xxh3, packetVersion string) (*HashRecord, error)
- type DevClient
- type HashRecord
- type KeyBinding
- type KeyBurnedError
- type PipelineStatus
- type RegisterHashRequest
- type RetrieveKeyRequest
Constants ¶
const ( ErrCodeMercuryError = "MERCURY_ERROR" // xZMercury вернул HTTP 5xx ErrCodeHMACVerificationFailed = "HMAC_VERIFICATION_FAILED" // HMAC ключа не прошёл верификацию ErrCodeKeyBindRejected = "KEY_BIND_REJECTED" // xZMercury отклонил binding (квота, ACL, невалидный pipeline) ErrCodeKeyAlreadyConsumed = "KEY_ALREADY_CONSUMED" // устаревший код; используй ErrCodeKeyBurnedByOther / ErrCodeKeyExpired ErrCodeKeyBurnedByOther = "KEY_BURNED_BY_OTHER" // ключ сожжён другим потребителем (410) — кража или dev-failover ErrCodeKeyExpired = "KEY_EXPIRED" // ключ истёк по TTL или UUID не существовал (404) )
Коды ошибок шифрования — используются в error-пакете (поле error_code).
const ( ErrCodeHashNotRegistered = "HASH_NOT_REGISTERED" // slot unknown → BLOCK ErrCodeHashTampered = "HASH_TAMPERED" // slot found, hash mismatch → BLOCK ErrCodeHashRegisterFailed = "HASH_REGISTER_FAILED" // POST /api/hashes failed )
Hash-registry error codes and sentinels.
Variables ¶
var ( ErrMercuryError = errors.New(ErrCodeMercuryError) ErrHMACVerificationFailed = errors.New(ErrCodeHMACVerificationFailed) ErrKeyBindRejected = errors.New(ErrCodeKeyBindRejected) ErrKeyAlreadyConsumed = errors.New(ErrCodeKeyAlreadyConsumed) // backward compat ErrKeyBurnedByOther = errors.New(ErrCodeKeyBurnedByOther) ErrKeyExpired = errors.New(ErrCodeKeyExpired) )
Sentinel errors — используются для определения типа отказа в EncryptionProcessor.
var ( // ErrHashNotRegistered is returned by VerifyHash when Mercury has no record // for this UUID+part — packet was never registered by an authenticated producer. ErrHashNotRegistered = errors.New(ErrCodeHashNotRegistered) // ErrHashTampered is returned by VerifyHash when the slot exists but the // presented xxh3 does not match what the producer registered — packet was // modified after registration. ErrHashTampered = errors.New(ErrCodeHashTampered) // ErrHashRegisterFailed is returned when POST /api/hashes fails (including // 409 Conflict when the slot is already taken by a different registration). ErrHashRegisterFailed = errors.New(ErrCodeHashRegisterFailed) )
Functions ¶
func VerifyHMAC ¶
VerifyHMAC проверяет HMAC-SHA256(packageUUID+":"+mode, serverSecret). mode должен совпадать с режимом сервера ("dev" или "prod") — он включён в подпись, поэтому dev-binding не пройдёт верификацию на prod-консьюмере даже при утечке секрета. serverSecret — значение MERCURY_SERVER_SECRET, идентичное SERVER_SECRET xZMercury. Возвращает false если подпись или mode не совпадают.
func VerifyHMACDev ¶
VerifyHMACDev всегда возвращает true в dev-режиме.
Types ¶
type BindKeyRequest ¶
type BindKeyRequest struct {
PackageUUID string `json:"package_uuid"`
PipelineName string `json:"pipeline_name"`
}
BindKeyRequest — тело запроса POST /api/keys/bind.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client — HTTP-клиент для xZMercury API. Используется tdtpcli для UUID-binding перед шифрованием.
func NewClient ¶
NewClient создаёт клиент с заданным таймаутом. baseURL пример: "http://mercury:3000"
func (*Client) BindKey ¶
func (c *Client) BindKey(ctx context.Context, packageUUID, pipelineName string) (*KeyBinding, error)
BindKey привязывает новый AES-256 ключ к UUID пакета. POST /api/keys/bind → {key_b64, hmac} При недоступности сервиса возвращает ErrMercuryUnavailable.
func (*Client) RegisterHash ¶
func (c *Client) RegisterHash( ctx context.Context, uuid string, part int, xxh3, tableName, sender, packetVersion string, ) error
RegisterHash registers the packet's xxh3_128 fingerprint in xzMercury. Must be called by the PRODUCER after ComputeIntegrity, before queueing.
Parameters:
- uuid : pkt.Header.MessageID
- part : pkt.Header.PartNumber (0 for single-part packets)
- xxh3 : pkt.XXH3 (32-char hex from ComputeIntegrity)
- tableName : pkt.Header.TableName
- sender : service account / pipeline name (X-Caller header)
- packetVersion: pkt.Version — must be "1.4", else no-op
Returns nil on success or if already registered (idempotent retry). Returns ErrHashRegisterFailed with status 409 if the slot is taken by a DIFFERENT registration (attacker tried to pre-empt the slot).
func (*Client) RetrieveKey ¶
RetrieveKey забирает ключ по UUID пакета (burn-on-read). POST /api/keys/retrieve → {key_b64} Вызывается получателем зашифрованного пакета — ключ удаляется после первого чтения.
caller — идентификатор потребителя (sAMAccountName, имя сервиса и т.п.), записывается в audit trail Mercury. Пустая строка допустима, но рекомендуется передавать.
Возвращает ErrKeyAlreadyConsumed при HTTP 404 — это security event: ключ либо истёк по TTL, либо был уже сожжён (возможно, чужим).
func (*Client) VerifyHash ¶
func (c *Client) VerifyHash( ctx context.Context, uuid string, part int, xxh3, packetVersion string, ) (*HashRecord, error)
VerifyHash checks the packet hash against what xzMercury has registered for this UUID+part. Must be called by the CONSUMER as a pre-flight check.
Parameters:
- uuid : pkt.Header.MessageID
- part : pkt.Header.PartNumber
- xxh3 : pkt.XXH3 (what the packet claims its fingerprint is)
- packetVersion: pkt.Version — pre-1.4 returns (nil, nil) = pass-through
Returns:
- (record, nil) — registered and hash matches → proceed
- (nil, ErrHashNotRegistered) — slot unknown → BLOCK + LOG
- (nil, ErrHashTampered) — slot found, hash mismatch → BLOCK + LOG
- (nil, ErrMercuryUnavailable) — Mercury unreachable → BLOCK (fail-closed)
type DevClient ¶
type DevClient struct{}
DevClient генерирует ключ локально без обращения к xZMercury. Доступен только в не-продакшен сборках (go build без -tags production). Использование: tdtpcli --enc-dev --pipeline config.yaml
ВНИМАНИЕ: ключ не сохраняется нигде — расшифровать результат можно только в рамках одной сессии. Для отладки шифрования, не для продакшена.
func NewDevClient ¶
func NewDevClient() *DevClient
NewDevClient создаёт dev-клиент (доступен только без -tags production).
func (*DevClient) BindKey ¶
BindKey генерирует случайный AES-256 ключ локально. HMAC возвращается как фиктивный — верификация в dev-режиме всегда проходит.
func (*DevClient) HMACVerificationExempt ¶
HMACVerificationExempt сообщает, что привязки этого клиента верифицировать нечем: ключ сгенерирован здесь же, и подписи от сервера не существует.
Именно этим dev-режим и включается теперь — выбором клиента, а не строкой "dev-mode" в MERCURY_SERVER_SECRET. Прежняя связка требовала двух действий вместо одного и отказывала тихо, если сделано было только одно: на боевом сервере секрет выставлен всегда, так что --enc-dev в одиночку не работал ровно там, где нужен, — при аварийном откате.
Файл собирается только без -tags production, поэтому в продакшен-сборке освобождения не существует вовсе.
func (*DevClient) RegisterHash ¶
RegisterHash удовлетворяет pipeline.HashRegistrar, ничего никуда не отправляя.
Без этого метода --enc-dev не работал вовсе, хотя в справке флага написано "no xZMercury required". Причина в резолвинге: exporter.resolveHashRegistrar берёт кастомный биндер только если тот приводится к HashRegistrar, а DevClient умел один BindKey — приведение не проходило, и обязательная для v1.5 регистрация уходила в mercury.NewClient("") с пустым URL. Получалось `Post "/api/hashes/": unsupported protocol scheme ""`: шифрование деградировало, выходной файл не создавался.
Пустая реализация здесь — не заглушка ради зелёного теста, а точное отражение режима. Реестр хешей существует внутри xZMercury; в dev-режиме его нет, регистрировать не у кого. Проверять регистрацию тоже некому: ключ живёт только в этом процессе и никуда не сохраняется, так что потребителя, который вызвал бы VerifyAndPrepare, в принципе не будет. Сами хеши при этом остаются на месте — ComputeIntegrity штампует пакет до этого вызова, так что формат v1.5 не нарушается.
Проверка xxh3 сохранена такой же, как у настоящего клиента: забытый ComputeIntegrity — это ошибка вызывающего кода, и она должна быть видна в dev-режиме ровно так же, как в бою.
Файл собирается только без -tags production, поэтому обойти этим настоящую регистрацию в продакшен-сборке невозможно.
type HashRecord ¶
type HashRecord struct {
UUID string `json:"uuid"`
Part int `json:"part"`
StoredXXH3 string `json:"stored_xxh3"`
TableName string `json:"table"`
Sender string `json:"sender"`
PacketVersion string `json:"packet_version"`
RegisteredAt time.Time `json:"registered_at"`
ExpiresInSeconds int64 `json:"expires_in_seconds"`
}
HashRecord is the metadata returned by VerifyHash when a hash is registered. Mirrors xzmercury/internal/hashstore.HashRecord.
type KeyBinding ¶
type KeyBinding struct {
KeyB64 string `json:"key_b64"`
HMAC string `json:"hmac"`
Mode string `json:"mode"` // "dev" | "prod" — attested by HMAC
}
KeyBinding — ответ xZMercury на POST /api/keys/bind. KeyB64 — AES-256 ключ в base64 (32 байта). HMAC — HMAC-SHA256(uuid+":"+mode, SERVER_SECRET) — mode включён в подпись. Mode — "dev" или "prod"; значение аттестовано HMAC, не self-reported.
type KeyBurnedError ¶
type KeyBurnedError struct {
UUID string
Mode string // "dev" | "prod" from the burn marker
BurnedAt time.Time // UTC timestamp of the burn
}
KeyBurnedError is returned by RetrieveKey when Mercury responds 410 Gone. It carries the mode of the server that burned the key ("dev"/"prod") and the timestamp, so the receiver can distinguish dev-failover burns (expected during Redis cluster failure) from prod-theft events (requires investigation).
func (*KeyBurnedError) Error ¶
func (e *KeyBurnedError) Error() string
func (*KeyBurnedError) Is ¶
func (e *KeyBurnedError) Is(target error) bool
Is satisfies errors.Is so callers can match with ErrKeyBurnedByOther sentinel.
type PipelineStatus ¶
type PipelineStatus string
PipelineStatus — статус pipeline, публикуемый в resultlog.
const ( StatusSuccess PipelineStatus = "success" StatusFailed PipelineStatus = "failed" StatusCompletedWithErrors PipelineStatus = "completed_with_errors" )
Pipeline status constants.
type RegisterHashRequest ¶
type RegisterHashRequest struct {
UUID string `json:"uuid"`
Part int `json:"part"`
XXH3 string `json:"xxh3"`
TableName string `json:"table"`
Sender string `json:"sender"`
PacketVersion string `json:"packet_version"`
}
RegisterHashRequest is the body sent to POST /api/hashes.
type RetrieveKeyRequest ¶
type RetrieveKeyRequest struct {
PackageUUID string `json:"package_uuid"`
RequestID string `json:"request_id,omitempty"` // optional; links retrieve to the bind request for audit
Caller string `json:"caller,omitempty"` // consumer identity — recorded in Mercury audit trail
}
RetrieveKeyRequest — тело запроса POST /api/keys/retrieve (burn-on-read, вызывается получателем).