mercury

package
v1.26.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 10, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package mercury provides functionality for the TDTP framework.

Index

Constants

View Source
const (
	ErrCodeMercuryUnavailable     = "MERCURY_UNAVAILABLE"      // xZMercury не отвечает (timeout / connection refused)
	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).

View Source
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

View Source
var (
	ErrMercuryUnavailable     = errors.New(ErrCodeMercuryUnavailable)
	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.

View Source
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 DecodeKey

func DecodeKey(keyB64 string) ([]byte, error)

DecodeKey декодирует ключ из base64 в []byte.

func ErrorCode

func ErrorCode(err error) string

ErrorCode преобразует sentinel error в строковый код для error-пакета.

func VerifyHMAC

func VerifyHMAC(packageUUID, receivedHMAC, serverSecret, mode string) bool

VerifyHMAC проверяет HMAC-SHA256(packageUUID+":"+mode, serverSecret). mode должен совпадать с режимом сервера ("dev" или "prod") — он включён в подпись, поэтому dev-binding не пройдёт верификацию на prod-консьюмере даже при утечке секрета. serverSecret — значение MERCURY_SERVER_SECRET, идентичное SERVER_SECRET xZMercury. Возвращает false если подпись или mode не совпадают.

func VerifyHMACDev

func VerifyHMACDev(_, _, _ string) bool

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

func NewClient(baseURL string, timeoutMs int) *Client

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

func (c *Client) RetrieveKey(ctx context.Context, packageUUID, caller string) (string, error)

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

func (d *DevClient) BindKey(_ context.Context, packageUUID, _ string) (*KeyBinding, error)

BindKey генерирует случайный AES-256 ключ локально. HMAC возвращается как фиктивный — верификация в dev-режиме всегда проходит.

func (*DevClient) HMACVerificationExempt

func (d *DevClient) HMACVerificationExempt() bool

HMACVerificationExempt сообщает, что привязки этого клиента верифицировать нечем: ключ сгенерирован здесь же, и подписи от сервера не существует.

Именно этим dev-режим и включается теперь — выбором клиента, а не строкой "dev-mode" в MERCURY_SERVER_SECRET. Прежняя связка требовала двух действий вместо одного и отказывала тихо, если сделано было только одно: на боевом сервере секрет выставлен всегда, так что --enc-dev в одиночку не работал ровно там, где нужен, — при аварийном откате.

Файл собирается только без -tags production, поэтому в продакшен-сборке освобождения не существует вовсе.

func (*DevClient) RegisterHash

func (d *DevClient) RegisterHash(_ context.Context, _ string, _ int, xxh3, _, _, _ string) error

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, вызывается получателем).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL