conformance

package module
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: MIT Imports: 7 Imported by: 0

README

qurl-conformance

The single public source of truth for the qURL cross-language conformance vectors: the language-agnostic wire-truth that every qURL verifier re-runs against its own implementation. Separate artifact ids keep the qURL v2 verify path, Noise-handshake packets, agent registration, and registered-agent knock application bodies decoupled by layer.

The verify-path vectors are behavioral. Each class names the verifier operation it targets and the input shape it consumes; a consumer feeds that input through its real parser/validator and asserts the declared accept/reject outcome (and, where the class is about the distinction, the reject_class). A verifier that drifts from the contract fails its own run — there are no stored booleans to trust.

Layout

Path What it is
vectors/qv2_conformance_vectors.json the conformance classes: claims/secret parse, strict base64url, fragment shape, relay allowlist, server-id, and the composed signature class
vectors/issuer_signature_vectors.json the issuer-signature golden vectors (P-256 raw r||s low-S) the signature class composes by reference
vectors/relay_knock_golden.json the relay/NHP-handshake golden packets (X25519 / AES-256-GCM / BLAKE2s): a deterministic knock packet plus a frozen, server-sealed ack reply (see Scope)
vectors/agent_registration_golden.json the NHP agent-registration golden packets (X25519 / AES-256-GCM / BLAKE2s): deterministic OTP/REG requests plus frozen, server-sealed RAK replies (see Scope)
vectors/agent_knock_application_vectors.json registered-agent KNK body plus already-decrypted ACK/COK disposition vectors; no Noise packet duplication
vectors/README_agent_knock_application_vectors.md application-vector schema, outcome/reject vocabulary, and consumer algorithm
vectors/README_qv2_conformance_vectors.md the schema, reject_class vocabulary, class-to-entry-point map, and the derived tamper case
schema.go, embed.go a stdlib-only Go module that embeds the artifacts and exposes strict, typed loaders

Using it from Go

import conformance "github.com/layervai/qurl-conformance"

cf, err := conformance.ConformanceVectors()        // strict-parsed conformance artifact
vf, err := conformance.SignatureVectors()          // strict-parsed issuer-signature vectors
rk, err := conformance.RelayKnockGolden()          // strict-parsed relay-knock golden packets
ar, err := conformance.AgentRegistrationGolden()   // strict-parsed agent-registration golden packets
ka, err := conformance.AgentKnockApplication()      // strict-parsed agent KNK/ACK application vectors
raw := conformance.QV2Vectors()                    // raw bytes, if you drive your own parser

The loaders fail (never return an empty document) on a malformed or unexpected artifact, so the contract can never silently drop out of a test suite.

Using it from another language

Copy qv2_conformance_vectors.json and issuer_signature_vectors.json verbatim (same bytes, no reformatting), load them with a strict JSON reader that rejects duplicate keys and unknown fields, route each class's input to your real entry point, and assert the declared outcome. Treat a missing fixture as a hard failure, not a skip. See vectors/README_qv2_conformance_vectors.md for the full schema and vocabulary.

Scope

This module hosts four artifact families, each under its own artifact id:

  • qURL v2 verify path (qurl-v2-conformance-vectors, composing the issuer-signature golden bytes) — the claims/secret/base64/fragment/relay/ server-id classes described above.
  • Relay/NHP handshake (qurl-relay-knock-golden-vectors, relay_knock_golden.json) — the Noise-handshake golden packets, kept in a separate artifact because the qURL verify path does not import the handshake layer. The knock packet is deterministic: a conformant initiator must reproduce its packet_hex byte-for-byte from the listed inputs. The ack reply is sealed at origin with a random server ephemeral key, so it is not reproducible by a client — consumers can only decrypt it and assert the recovered fields. It is re-hosted here verbatim as a frozen golden value. These packets originate from the NHP cross-language handshake fixtures and are pinned here.
  • NHP agent registration (qurl-agent-registration-golden-vectors, agent_registration_golden.json) — the OTP/REG/RAK Noise-handshake golden packets for agent enrollment, again a separate artifact from the verify path. The otp, reg_emailed, and reg_preissued requests are deterministic: a conformant initiator must reproduce each packet_hex byte-for-byte. The REG body is {usrId, devId, aspId, otp, usrData} with usrData = {hostname, version, takeover} (fields omitted when empty/false), matching the live agent implementations byte-for-byte. The two REG packets differ in the body otp value (an emailed code vs a pre-issued key secret) and in usrData.takeover (omitted vs true); the framing is identical. The rak_success / rak_error replies are sealed at origin with a random server ephemeral, so they are frozen decrypt-only, mirroring the relay-knock ack. The RAK cases echo reg_emailed's counter, so a consumer can validate the RAK-must-echo-its-REG counter contract against a positive fixture. All keys/ids/secrets are synthetic.
  • Registered-agent knock application contract (qurl-agent-knock-application-vectors, agent_knock_application_vectors.json) — the exact compact five-field KNK body and synthetic, already-decrypted reply dispositions for ACK success, authenticated deny, cookie challenge, wrong resource, malformed/missing maps, and reply counter/type mismatch. It contains no Noise packets or key material; consumers compose it with their real body serializer, reply parser, and transport correlation gates. See vectors/README_agent_knock_application_vectors.md.

This module is intentionally dependency-free (stdlib only). The generator that produces the verify-path vectors lives at tools/gen and is run via make gen-vectors once per issuer-key rotation; it is never run in CI (the accept signature uses a random nonce, so it is not reproducible). The committed JSON is the artifact. Vectors are edited under vectors/.

Releases

Versioning is automated with Release Please in manifest mode: the Go module, the npm package, and the Python package are released together under one linked version (see release-please-config.json). Merging the release PR tags the repo, which is what releases the Go module.

npm and PyPI registry publishing on release is a token-gated follow-up (it needs NPM_TOKEN / PyPI trusted publishing wired up); for now Release Please only automates the version-bump PRs and the Go tag.

License

MIT — see LICENSE.

Documentation

Overview

Package conformance is the single public source of truth for the qURL cross-language conformance vectors. It embeds the JSON artifacts under vectors/ and exposes strict, typed loaders so any consumer — in any language that can call this Go module, or that copies the JSON directly — can re-run the same wire-truth against its own implementation.

Four families live here, each under its own artifact id so they stay decoupled by layer:

  • The qURL v2 verify-path vectors (qv2_conformance_vectors.json composing issuer_signature_vectors.json): the claims/secret/base64/fragment/relay/ server-id classes and the issuer-signature golden bytes.
  • The relay/NHP-handshake golden packets (relay_knock_golden.json): the deterministic relay-knock packet plus a frozen, server-sealed ack reply for the Noise handshake layer, which the qURL verify path does not import.
  • The NHP agent-registration golden packets (agent_registration_golden.json): deterministic OTP/REG requests plus frozen RAK replies.
  • The registered-agent knock application contract (agent_knock_application_vectors.json): exact KNK JSON plus already- decrypted ACK/COK disposition vectors, with no duplicate packet bytes.

The verify-path artifact is BEHAVIORAL: a consumer feeds each class's input through its real parser/validator and asserts the declared accept/reject outcome (and, where the class is about the distinction, the reject_class), rather than trusting a stored boolean. A verifier that drifts from the contract fails its own run.

This module is stdlib-only and has no build-time dependencies; the generator that produces the vectors is intentionally not part of it.

Index

Constants

View Source
const (
	ExpectAccept = "accept"
	ExpectReject = "reject"
)

Expectation constants for a vector's Expect field, shared by both artifacts.

View Source
const (
	// RejectClassParse is the coarse class for a JSON-schema violation (duplicate
	// key, unknown field, null, wrong type, missing required, out-of-range/ordering).
	RejectClassParse = "parse"
	// RejectClassEncoding is a base64url encoding-layer rejection.
	RejectClassEncoding = "encoding"
	// RejectClassKeyLength is a decoded-key wrong-length rejection.
	RejectClassKeyLength = "key_length"
	// RejectClassFragment is a fragment wire-shape rejection.
	RejectClassFragment = "fragment"
	// RejectClassRelayURL is a relay_url HTTPS/allowlist rejection.
	RejectClassRelayURL = "relay_url"
	// RejectClassTamper is the signature-class payload-tamper rejection: a valid
	// signature verified against a flipped claims input (derived, not stored).
	RejectClassTamper = "tamper"
	// RejectClassHighS is a signature that is not low-S normalized.
	RejectClassHighS = "high_s"
	// RejectClassWrongLength is a signature that is not exactly 64 bytes (raw r||s).
	RejectClassWrongLength = "wrong_length"
)

reject_class vocabulary. These constants are the fixed cross-language vocabulary the README pins, so every consumer can switch on a closed, known set. They are pinned precisely only where the class is about the distinction (signature high_s vs wrong_length; encoding; key_length); JSON-schema faults use the coarse "parse" because a conformant verifier may surface any of several internal sentinels for them.

View Source
const (
	// TamperDeriveFromAccept is the only supported derive_from: start from the
	// composed file's accept vector.
	TamperDeriveFromAccept = "accept_vector"
	// TamperTransformFlipFirstB64 flips the FIRST base64url character of the accept
	// vector's claims_b64 between 'A' and 'B' ('A'->'B', any other char->'A'). The
	// first symbol encodes the top 6 bits of decoded byte 0, so this changes the
	// DECODED claims (not just don't-care tail bits) AND keeps the string canonical
	// base64url. That makes the derived tamper identical for every consumer
	// regardless of whether it hashes the base64 string, decodes-then-hashes, or
	// strict-decodes before verifying.
	TamperTransformFlipFirstB64 = "flip_first_base64url_char_A_B"
)

Signature-class tamper derivation identifiers. These pin the artifact's language-agnostic derivation so a consumer applies exactly what the JSON specifies rather than a hardcoded rule.

View Source
const (
	AgentKnockOutcomeSuccess = "success"
	AgentKnockOutcomeDeny    = "deny"
	AgentKnockOutcomeRetry   = "retry"
	AgentKnockOutcomeReject  = "reject"
)

Agent-knock application outcomes. A consumer drives each reply through its real reply interpreter and derives one of these outcomes; it must not trust the stored label without exercising the application parser and correlation gates.

View Source
const (
	AgentKnockRejectServerDeny    = "server_deny"
	AgentKnockRejectServerBusy    = "server_busy"
	AgentKnockRejectWrongResource = "wrong_resource"
	AgentKnockRejectMissingToken  = "missing_token"
	AgentKnockRejectMissingHost   = "missing_host"
	AgentKnockRejectBodyParse     = "body_parse"
	AgentKnockRejectCounter       = "counter"
	AgentKnockRejectReplyType     = "reply_type"
)

Agent-knock application reject classes form a closed, consumer-neutral vocabulary. ServerDeny and ServerBusy are authenticated platform outcomes; the other classes are fail-closed client validation failures.

View Source
const AgentKnockApplicationArtifactID = "qurl-agent-knock-application-vectors"

AgentKnockApplicationArtifactID is the fixed identity of the registered-agent knock application-body artifact. It is deliberately separate from RelayKnockArtifactID: this document starts after Noise decryption and carries no packet bytes, private keys, nonces, or ciphertext.

View Source
const AgentRegistrationArtifactID = "qurl-agent-registration-golden-vectors"

AgentRegistrationArtifactID is the fixed identity string the agent-registration artifact's top-level "artifact" field must carry. The loader enforces it so a consumer that relies on "the loader rejects malformed files" cannot silently load a DIFFERENT document into these structs. A consumer in another language should assert the same id.

View Source
const ConformanceArtifactID = "qurl-v2-conformance-vectors"

ConformanceArtifactID is the fixed identity string the top-level "artifact" field must carry. The loader enforces it so a consumer that relies on "the loader rejects malformed files" cannot silently load a DIFFERENT document into these structs. A consumer in another language should assert the same id.

View Source
const RelayKnockArtifactID = "qurl-relay-knock-golden-vectors"

RelayKnockArtifactID is the fixed identity string the relay-knock artifact's top-level "artifact" field must carry. The loader enforces it so a consumer that relies on "the loader rejects malformed files" cannot silently load a DIFFERENT document into these structs. A consumer in another language should assert the same id.

Variables

This section is empty.

Functions

func AgentKnockApplicationVectors added in v0.1.3

func AgentKnockApplicationVectors() []byte

AgentKnockApplicationVectors returns the raw bytes of the registered-agent knock application-body vectors. Unlike RelayKnockVectors, this artifact starts after Noise decryption and contains no packet bytes.

func AgentRegistrationVectors added in v0.1.3

func AgentRegistrationVectors() []byte

AgentRegistrationVectors returns the raw bytes of the embedded NHP agent-registration golden packets (agent_registration_golden.json): the OTP/REG requests and the RAK replies. The bytes are the canonical wire-truth; a consumer that prefers to drive its own strict parser can feed these directly.

func IssuerSignatureVectors

func IssuerSignatureVectors() []byte

IssuerSignatureVectors returns the raw bytes of the embedded issuer-signature golden vectors (issuer_signature_vectors.json), which the signature class composes by reference.

func Open

func Open(name string) ([]byte, error)

Open returns the raw bytes of an embedded vectors file by its base name (for example "qv2_conformance_vectors.json" or "issuer_signature_vectors.json"), or by its full "vectors/..." path. It returns an error for any other name.

func QV2Vectors

func QV2Vectors() []byte

QV2Vectors returns the raw bytes of the embedded qURL v2 conformance vectors (qv2_conformance_vectors.json). The bytes are the canonical wire-truth; a consumer that prefers to drive its own strict parser can feed these directly.

func RelayKnockVectors added in v0.1.1

func RelayKnockVectors() []byte

RelayKnockVectors returns the raw bytes of the embedded relay/NHP-handshake golden packets (relay_knock_golden.json). The bytes are the canonical wire-truth; a consumer that prefers to drive its own strict parser can feed these directly.

Types

type AgentKnockApplicationFile added in v0.1.3

type AgentKnockApplicationFile struct {
	Artifact      string                       `json:"artifact"`
	SchemaVersion int                          `json:"schema_version"`
	Description   string                       `json:"description"`
	SourceOfTruth string                       `json:"source_of_truth"`
	Notes         []string                     `json:"notes"`
	Request       AgentKnockApplicationRequest `json:"request"`
	ReplyCases    []AgentKnockReplyCase        `json:"reply_cases"`
}

AgentKnockApplicationFile is the versioned application-layer contract for a registered-agent NHP knock. Request carries one deterministic body golden; ReplyCases cover success, authenticated deny, overload, and fail-closed application/correlation negatives without duplicating Noise packet vectors.

func AgentKnockApplication added in v0.1.3

func AgentKnockApplication() (*AgentKnockApplicationFile, error)

AgentKnockApplication strictly parses the embedded registered-agent knock application-body artifact into a typed document.

func ParseAgentKnockApplicationFile added in v0.1.3

func ParseAgentKnockApplicationFile(data []byte) (*AgentKnockApplicationFile, error)

ParseAgentKnockApplicationFile strictly parses and validates the application-body artifact. It rejects duplicate/unknown/trailing outer fields, stale schema versions, missing mandatory cases, invalid enums/counters, duplicate case names, and a request golden that does not exactly match its semantic fields. Reply body semantics remain the consumer's job: those bodies include intentional wrong-map shapes that must reach the production parser.

type AgentKnockApplicationRequest added in v0.1.3

type AgentKnockApplicationRequest struct {
	WireType int                                `json:"wire_type"`
	Fields   AgentKnockApplicationRequestFields `json:"fields"`
	BodyJSON string                             `json:"body_json"`
}

AgentKnockApplicationRequest pins the outer NHP type, semantic synthetic inputs, and exact compact JSON body. Keeping the semantic inputs beside the golden prevents a consumer from merely copying BodyJSON: it must construct the body through its production serializer and compare the resulting bytes.

type AgentKnockApplicationRequestFields added in v0.1.3

type AgentKnockApplicationRequestFields struct {
	HeaderType    int    `json:"header_type"`
	UserID        string `json:"user_id"`
	DeviceID      string `json:"device_id"`
	AuthServiceID string `json:"auth_service_id"`
	ResourceID    string `json:"resource_id"`
}

AgentKnockApplicationRequestFields names the five load-bearing registered- agent fields without importing any producer implementation type.

type AgentKnockReplyCase added in v0.1.3

type AgentKnockReplyCase struct {
	Name           string `json:"name"`
	ReplyType      int    `json:"reply_type"`
	RequestCounter string `json:"request_counter"`
	ReplyCounter   string `json:"reply_counter"`
	BodyJSON       string `json:"body_json"`
	Outcome        string `json:"outcome"`
	RejectClass    string `json:"reject_class,omitempty"`
}

AgentKnockReplyCase is one already-decrypted reply disposition. Counter values are decimal strings so JavaScript consumers never lose uint64 precision. BodyJSON stays raw so malformed application shapes survive the artifact and reach the consumer's real parser.

type AgentRegistrationCase added in v0.1.3

type AgentRegistrationCase struct {
	// ServerStaticPrivHex / ServerStaticPubHex are the server static X25519 key.
	// Deterministic cases carry both; frozen (RAK) cases carry only the public half.
	ServerStaticPrivHex string `json:"server_static_priv_hex,omitempty"`
	ServerStaticPubHex  string `json:"server_static_pub_hex"`
	// DeviceStaticPrivHex / DeviceStaticPubHex are the initiator (agent/device)
	// static X25519 key, used by the deterministic OTP/REG cases.
	DeviceStaticPrivHex string `json:"device_static_priv_hex,omitempty"`
	DeviceStaticPubHex  string `json:"device_static_pub_hex,omitempty"`
	// AgentStaticPrivHex is the agent (responder-side decryptor) static private
	// X25519 key, used by the frozen RAK cases to open the reply. It is the same
	// key as the deterministic cases' device_static_priv_hex.
	AgentStaticPrivHex string `json:"agent_static_priv_hex,omitempty"`
	// EphemeralPrivHex is the fixed initiator ephemeral private key the
	// deterministic cases seal under (so the packet is reproducible).
	EphemeralPrivHex string `json:"ephemeral_priv_hex,omitempty"`
	// TimestampNanos is the handshake timestamp, decimal string (exceeds 2^53).
	TimestampNanos string `json:"timestamp_nanos"`
	// Counter is the deterministic-case counter as a decimal string.
	Counter string `json:"counter,omitempty"`
	// CounterHex is the frozen RAK counter as a hex string (no 0x prefix, no
	// padding). It echoes reg_emailed's counter for the matched pair.
	CounterHex string `json:"counter_hex,omitempty"`
	// PreambleHex is the 32-bit HeaderCommon preamble as a hex string
	// (deterministic cases).
	PreambleHex string `json:"preamble_hex,omitempty"`
	// BodyHex is the plaintext registration body the case carries, hex-encoded
	// (AgentOTPMsg / AgentRegisterMsg / ServerRegisterAckMsg JSON).
	BodyHex string `json:"body_hex"`
	// PacketHex is the full wire packet, hex-encoded: for a deterministic case,
	// the value a conformant builder must reproduce; for a frozen RAK case, the
	// value a conformant decryptor must open.
	PacketHex string `json:"packet_hex"`
}

AgentRegistrationCase is one golden packet: an OTP/REG initiator request or a RAK reply. Every value is the exact hex (or, for the numeric fields, the stringified value) the case uses; only the fields relevant to a given case are populated. All fields are strings — including timestamp_nanos, which exceeds 2^53 and so is carried as a decimal string rather than a JSON number.

Deterministic cases (otp, reg_emailed, reg_preissued) carry the same fields as relay_knock's knock: the server/device static keypairs, the fixed initiator ephemeral, the timestamp/counter/preamble, the plaintext body, and the full packet a conformant builder must reproduce. Frozen cases (rak_success, rak_error) carry the ack-style fields: the server static PUBLIC key, the agent (responder-side decryptor) static PRIVATE key, the counter as hex, the body, and the frozen packet a conformant decryptor must open.

type AgentRegistrationFile added in v0.1.3

type AgentRegistrationFile struct {
	Artifact      string                `json:"artifact"`
	SchemaVersion int                   `json:"schema_version"`
	Description   string                `json:"description"`
	SourceOfTruth string                `json:"source_of_truth"`
	Notes         []string              `json:"notes"`
	OTP           AgentRegistrationCase `json:"otp"`
	RegEmailed    AgentRegistrationCase `json:"reg_emailed"`
	RegPreissued  AgentRegistrationCase `json:"reg_preissued"`
	RakSuccess    AgentRegistrationCase `json:"rak_success"`
	RakError      AgentRegistrationCase `json:"rak_error"`
}

AgentRegistrationFile is the top-level NHP agent-registration golden artifact: the OTP request, the emailed-code and pre-issued-key REG requests (all three DETERMINISTIC — a conformant initiator must reproduce packet_hex byte-for-byte), and the server register-ack (RAK) success/error replies (FROZEN, sealed at origin with a random server ephemeral, so NOT reproducible by a client — only decryptable). Every case decodes into the same AgentRegistrationCase, which carries the UNION of the fields the deterministic and frozen cases use.

The reg_emailed → rak pair is counter-matched (rak_success/rak_error echo reg_emailed's counter), so a consumer can validate the RAK-must-echo-its-REG counter contract (conformance#19) against a positive fixture.

func AgentRegistrationGolden added in v0.1.3

func AgentRegistrationGolden() (*AgentRegistrationFile, error)

AgentRegistrationGolden strictly parses the embedded NHP agent-registration golden artifact into a typed document, returning an error if it is malformed or is not the expected artifact.

func ParseAgentRegistrationFile added in v0.1.3

func ParseAgentRegistrationFile(data []byte) (*AgentRegistrationFile, error)

ParseAgentRegistrationFile strictly parses the agent-registration golden artifact from raw bytes. It returns an error (never an empty/zero document) when the bytes are malformed or are not the agent-registration artifact, so a consumer test FAILS rather than silently skipping or misreading the contract. DisallowUnknownFields keeps a typo'd or stale schema field from being ignored.

type ConformanceClass

type ConformanceClass struct {
	EntryPoint string              `json:"entry_point"`
	Input      string              `json:"input"`
	Comment    string              `json:"comment"`
	Vectors    []ConformanceVector `json:"vectors"`
}

ConformanceClass is one named class: an entry-point label, the input field name, an optional human comment, and the ordered vectors.

type ConformanceFile

type ConformanceFile struct {
	Artifact       string                      `json:"artifact"`
	SchemaVersion  int                         `json:"schema_version"`
	Description    string                      `json:"description"`
	SourceOfTruth  string                      `json:"source_of_truth"`
	Notes          []string                    `json:"notes"`
	SignatureClass ConformanceSignatureClass   `json:"signature_class"`
	Classes        map[string]ConformanceClass `json:"classes"`
}

ConformanceFile is the top-level conformance artifact document.

func ConformanceVectors

func ConformanceVectors() (*ConformanceFile, error)

ConformanceVectors strictly parses the embedded qURL v2 conformance artifact into a typed document, returning an error if it is malformed or is not the expected artifact.

func ParseConformanceFile

func ParseConformanceFile(data []byte) (*ConformanceFile, error)

ParseConformanceFile strictly parses the conformance artifact from raw bytes. It returns an error (never an empty/zero document) when the bytes are malformed or are not the qURL v2 conformance artifact, so a consumer test FAILS rather than silently skipping or misreading the contract. DisallowUnknownFields keeps a typo'd or stale schema field from being ignored.

type ConformanceSignatureClass

type ConformanceSignatureClass struct {
	EntryPoint string `json:"entry_point"`
	Composes   string `json:"composes"`
	Comment    string `json:"comment"`
	// TamperDerivation specifies the derived payload-tamper reject. It is optional
	// in the schema's struct but consumers assert it is present and well-formed.
	TamperDerivation *ConformanceTamperDerivation `json:"tamper_derivation,omitempty"`
}

ConformanceSignatureClass records that the signature class is composed from a separate file rather than carrying its own bytes, plus the language-agnostic payload-tamper derivation every consumer synthesizes from the composed file's accept vector (so the tamper negative is portable without a second copy of signature bytes).

type ConformanceTamperDerivation

type ConformanceTamperDerivation struct {
	RejectClass     string `json:"reject_class"`
	Comment         string `json:"comment"`
	DeriveFrom      string `json:"derive_from"`
	ClaimsTransform string `json:"claims_transform"`
}

ConformanceTamperDerivation specifies how a consumer derives the payload-tamper reject from the composed signature file's accept vector. It is a derivation, not stored bytes, so every consumer synthesizes the SAME negative.

  • RejectClass: the reject_class label for the derived case ("tamper").
  • DeriveFrom: which composed vector to start from ("accept_vector").
  • ClaimsTransform: the transform applied to that vector's claims_b64 to make the signature no longer valid over it ("flip_first_base64url_char_A_B": flip the FIRST base64url character between 'A' and 'B'). The signature bytes are reused UNCHANGED, so the case fails only at the curve check.

type ConformanceVector

type ConformanceVector struct {
	Name        string `json:"name"`
	Expect      string `json:"expect"`
	RejectClass string `json:"reject_class"`
	Reason      string `json:"reason"`

	// claims_parse / secret_parse: raw JSON text fed directly to the parser.
	ClaimsJSON string `json:"claims_json"`
	SecretJSON string `json:"secret_json"`

	// strict_base64: the base64url string verbatim.
	ValueB64 string `json:"value_b64"`

	// fragment: a full fragment body.
	Fragment string `json:"fragment"`

	// relay_allowlist: the allowlist entries and the URL to validate.
	Entries []string `json:"entries"`
	URL     string   `json:"url"`

	// server_id: the cell public key (base64url) and its expected routing id.
	CellPublicKeyB64 string `json:"cell_public_key_b64"`
	ServerID         string `json:"server_id"`
}

ConformanceVector is one case. Only the fields relevant to a vector's class are populated; the loader does not interpret them — the consumer routes each class to the matching entry point and reads the fields that class uses.

type ECPublicJWK

type ECPublicJWK struct {
	Kty string `json:"kty"`
	Crv string `json:"crv"`
	X   string `json:"x"`
	Y   string `json:"y"`
}

ECPublicJWK is a minimal P-256 public-key JWK. x and y are fixed-width 32-byte base64url (leading zeros preserved) so a strict importer accepts them.

type IssuerKeyMaterial

type IssuerKeyMaterial struct {
	KID string `json:"kid"`
	// SPKIDERB64 is the DER SPKI public key, base64url.
	SPKIDERB64 string `json:"spki_der_b64"`
	// JWK is the same public key as a P-256 JWK (crv/x/y), for WebCrypto "jwk".
	JWK ECPublicJWK `json:"jwk"`
}

IssuerKeyMaterial is the issuer public key in both import forms.

type RelayKnockCase added in v0.1.1

type RelayKnockCase struct {
	// ServerStaticPrivHex / ServerStaticPubHex are the server static X25519 key.
	// The knock case carries both; the ack case carries only the public half.
	ServerStaticPrivHex string `json:"server_static_priv_hex,omitempty"`
	ServerStaticPubHex  string `json:"server_static_pub_hex"`
	// DeviceStaticPrivHex / DeviceStaticPubHex are the initiator (device) static
	// X25519 key, used by the knock case.
	DeviceStaticPrivHex string `json:"device_static_priv_hex,omitempty"`
	DeviceStaticPubHex  string `json:"device_static_pub_hex,omitempty"`
	// AgentStaticPrivHex is the agent (responder-side decryptor) static private
	// X25519 key, used by the ack case to open the reply. It is the same key as the
	// knock case's device_static_priv_hex.
	AgentStaticPrivHex string `json:"agent_static_priv_hex,omitempty"`
	// EphemeralPrivHex is the fixed initiator ephemeral private key the knock case
	// seals under (so the knock packet is deterministic).
	EphemeralPrivHex string `json:"ephemeral_priv_hex,omitempty"`
	// TimestampNanos is the handshake timestamp, decimal string (exceeds 2^53).
	TimestampNanos string `json:"timestamp_nanos"`
	// Counter is the knock counter as a decimal string.
	Counter string `json:"counter,omitempty"`
	// CounterHex is the ack counter as a hex string (no 0x prefix, no padding).
	CounterHex string `json:"counter_hex,omitempty"`
	// PreambleHex is the 32-bit knock preamble as a hex string.
	PreambleHex string `json:"preamble_hex,omitempty"`
	// BodyHex is the plaintext body the case carries, hex-encoded.
	BodyHex string `json:"body_hex"`
	// PacketHex is the full wire packet, hex-encoded: for knock, the value a
	// conformant BuildKnock must reproduce; for ack, the frozen value a conformant
	// DecryptReply must open.
	PacketHex string `json:"packet_hex"`
}

RelayKnockCase is one golden packet (knock or ack). Every value is the exact hex (or, for the numeric fields, the stringified value) the case uses; only the fields relevant to a given case are populated. All fields are strings — including timestamp_nanos, which exceeds 2^53 and so is carried as a decimal string rather than a JSON number.

type RelayKnockFile added in v0.1.1

type RelayKnockFile struct {
	Artifact      string         `json:"artifact"`
	SchemaVersion int            `json:"schema_version"`
	Description   string         `json:"description"`
	SourceOfTruth string         `json:"source_of_truth"`
	Notes         []string       `json:"notes"`
	Knock         RelayKnockCase `json:"knock"`
	Ack           RelayKnockCase `json:"ack"`
}

RelayKnockFile is the top-level relay/NHP-handshake golden artifact: a deterministic knock packet a conformant initiator must reproduce byte-for-byte, plus a frozen ack reply (sealed at origin with a random server ephemeral, so it is NOT reproducible by a client — only decryptable). Both cases decode into the same RelayKnockCase, which carries the UNION of the fields either case uses.

func ParseRelayKnockFile added in v0.1.1

func ParseRelayKnockFile(data []byte) (*RelayKnockFile, error)

ParseRelayKnockFile strictly parses the relay-knock golden artifact from raw bytes. It returns an error (never an empty/zero document) when the bytes are malformed or are not the relay-knock artifact, so a consumer test FAILS rather than silently skipping or misreading the contract. DisallowUnknownFields keeps a typo'd or stale schema field from being ignored.

func RelayKnockGolden added in v0.1.1

func RelayKnockGolden() (*RelayKnockFile, error)

RelayKnockGolden strictly parses the embedded relay/NHP-handshake golden artifact into a typed document, returning an error if it is malformed or is not the expected artifact.

type SignatureVector

type SignatureVector struct {
	Name string `json:"name"`
	// Expect is "accept" or "reject".
	Expect string `json:"expect"`
	// RejectClass is the machine-readable rejection class. It is present on reject
	// vectors and absent on accept vectors.
	RejectClass string `json:"reject_class,omitempty"`
	// Reason documents why in human-readable prose.
	Reason string `json:"reason"`
	// ClaimsB64 is the exact base64url claims string (primary verify input).
	ClaimsB64 string `json:"claims_b64"`
	// SigB64Raw is the signature as base64url. For accept/high-S it is 64-byte
	// raw r||s; for the wrong-length case it is the DER form (the realistic
	// "passed signer output straight through" mistake).
	SigB64Raw string `json:"sig_b64"`
	// SigEncoding documents the signature's byte form ("raw_r_s" or "der").
	SigEncoding string `json:"sig_encoding"`
	// SigningInputB64 is a cross-check value a verifier reconstructs itself as
	// prefix + 0x00 + claims_b64; it is not the data fed to the verifier.
	SigningInputB64 string `json:"signing_input_b64"`
}

SignatureVector is one accept-or-reject case.

type VectorFile

type VectorFile struct {
	// Description documents the contract for a human reader of the JSON.
	Description string `json:"description"`
	// Algorithm pins the signing profile (informational; verifiers do not
	// negotiate).
	Algorithm string `json:"algorithm"`
	// DomainSeparationPrefix is the ASCII prefix; the 0x00 separator follows it.
	DomainSeparationPrefix string `json:"domain_separation_prefix"`
	// Issuer is the shared issuer key all vectors are signed/verified under.
	Issuer IssuerKeyMaterial `json:"issuer"`
	// Vectors is the ordered list of accept/reject cases.
	Vectors []SignatureVector `json:"vectors"`
}

VectorFile is the top-level committed issuer-signature fixture document. The signature class of the conformance artifact composes this file by reference.

func ParseVectorFile

func ParseVectorFile(data []byte) (*VectorFile, error)

ParseVectorFile strictly parses an issuer-signature vector file from raw bytes. It returns an error (never an empty/zero document) if the bytes are malformed, so a consumer test FAILS rather than silently skipping the contract.

func SignatureVectors

func SignatureVectors() (*VectorFile, error)

SignatureVectors strictly parses the embedded issuer-signature vector file into a typed document, returning an error if it is malformed.

Jump to

Keyboard shortcuts

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