Lace / docs

Lace-009 · Marks

Tags: record, crypto

Purpose

Seal records need portable byte-control evidence that survives transport boundaries: a secret key makes a Mark over one Plex-record digest, and any implementation can check that Mark with the public verification key from record bytes alone. This spec defines secret-key, verification-key, and Mark rules used by Seal records in the current .H3 suite. It marks a 32-byte BLAKE3 digest with secp256k1 Schnorr arithmetic and encodes protocol values with B64A.

This document is for record and crypto implementors. After reading it, an implementor should be able to generate canonical secret-key text, derive its verification key, make Marks, and check Marks consistently across languages.

Defines

Secret-key generation and scalar derivation, verification-key derivation, Mark construction, and Mark checking.

Text forms identify keys and Marks

A secret key is private local material. A verification key and Mark are public. Implementations MUST require the exact prefixes, suffixes, payload lengths, and canonical B64A encoding shown above.

Parameters define the current Mark system

All 32-byte scalar and field-element inputs are interpreted as unsigned big-endian integers. All scalar and field-element outputs are fixed-width 32-byte big-endian bytes.

BLAKE3 derive-key context strings:

Context Use
lace-🖧/adhoc-key secret-key scalar derivation
lace-🖧/aux Mark auxiliary-randomness tag
lace-🖧/nonce nonce tag
lace-🖧/challenge challenge tag

derive_key(context, input) means the first 32 bytes of BLAKE3 derive-key output for that context and input. Context strings are encoded as the exact UTF-8 bytes of the displayed text.

Secret-key generation produces canonical secret material

To generate a secret key, implementations MUST choose 32 bytes with a CSPRNG and encode those exact bytes as canonical &.<b64a>.H3 text. The payload, not a directly generated secp256k1 scalar, is the supported serialized private-key form.

To derive the normalized scalar d from a secret-key payload, implementations MUST:

  1. Reject payloads whose decoded length is not exactly 32 bytes.
  2. Use BLAKE3 XOF derive mode with context lace-🖧/adhoc-key.
  3. Read consecutive, non-overlapping 32-byte XOF output chunks as candidates until one interpreted as d0 satisfies 0 < d0 < n.
  4. Compute P = d0*G.
  5. If P.y is odd, set d = n - d0; otherwise set d = d0.

The scalar is an implementation detail and has no separate public text form.

Verification-key derivation uses the even-y point

Given the normalized scalar d, the verification-key bytes are bytes((d*G).x). The even-y normalization makes one verification-key text correspond to one public point.

The complete derivation is:

&.<b64a>.H3 secret-key text
        -> 32 secret bytes
        -> normalized scalar
        -> 32-byte verification key
        -> V.<b64a>.H3 verification-key text

Making a Mark binds one digest to one verification key

Inputs are normalized scalar d, its verification-key x-coordinate Px, message digest msg32, and a 32-byte auxRand32 value. Implementations SHOULD use fresh CSPRNG-generated auxiliary bytes for ordinary Mark construction. Fixed values, including all zero, MAY be used for reproducible Marks and test vectors; auxiliary randomness is supplemental to the nonce derivation’s secret and message inputs.

Implementations MUST make a Mark as follows:

  1. t = derive_key("lace-🖧/aux", auxRand32).
  2. mask = t xor bytes(d).
  3. k0 = derive_key("lace-🖧/nonce", mask || Px || msg32) mod n; if it is zero, construction fails and the caller may retry with a different auxRand32.
  4. Compute R = k0*G; use k = n-k0 when R.y is odd, else k = k0.
  5. e = derive_key("lace-🖧/challenge", bytes(R.x) || Px || msg32) mod n.
  6. s = (k + e*d) mod n.
  7. Mark bytes are bytes(R.x) || bytes(s).

Checking accepts only the matching Mark

Inputs are verification-key x-coordinate Px, Mark r||s, and message digest msg32.

Implementations MUST check as follows:

  1. Reject Px >= p, r >= p, or s >= n.
  2. Recover point P from Px using the even-y root; reject Px values that do not identify a secp256k1 point.
  3. Compute e = derive_key("lace-🖧/challenge", bytes(r) || Px || msg32) mod n.
  4. Compute R' = s*G - e*P.
  5. Reject infinity or odd R'.y.
  6. Accept only when R'.x == r.

Implementations MUST use constant-time scalar and point operations.

A known-answer vector checks the complete construction

This vector covers secret-key payload derivation, even-y verification-key derivation, BLAKE3 message hashing, fixed auxiliary input, Mark construction, and canonical text encoding. The UTF-8 source text is included only to make the msg32 digest reproducible; Mark construction receives the digest, not the source text.

Value Canonical text or hexadecimal bytes
secret payload bytes 0707070707070707070707070707070707070707070707070707070707070707
secret key &.1lS71lS71lS71lS71lS71lS71lS71lS71lS71lS71lS.H3
message source bytes UTF-8 hello world
msg32 bytes d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24
msg32 B64A qpb1wvSA38WBZOlPXT1qrxkrURbQNu_KuQguQtCQcYG
auxRand32 bytes 0101010101010101010101010101010101010101010101010101010101010101
auxRand32 B64A 0G410G410G410G410G410G410G410G410G410G410G4
verification-key bytes 235ad8ab794e4ef501e3e84507775d2575285884c2736f9ad46bc89db67ee990
verification key V.8qgOfsaEJkK1tzX51sTT9NKdM8J2SrzQq6k8cRPzvP0.H3
Mark bytes ae3c9a3a46d745dd20e485ff6e259059db90cff31db06d79e0d1e3ab1df7ad841120e93b7190e73aa91a19a41843430266fc24fc4c1f6495f8345af75fcdbcb1
Mark gZnQE_RNHTpWu8N~RYMGMTjGo~CTh6qutD7ZfmssgOGH8E_wSP3cEf_Q6QGOGpC2Pkl_~4lVP9NtD5gsNxrxhG

Seal records translate keys into record vocabulary

Lace-010 stores canonical verification-key text in the Seal field named By. From 010 upward, By means the canonical V.<b64a>.H3 encoding of an 009 verification key. From 010 upward, APIs call the corresponding private &.<b64a>.H3 value the By-secret value.

Rejection examples

Reject examples include malformed or noncanonical secret-key or verification-key text, a secret-key payload of the wrong length, Px >= p, Px with no curve point, r >= p, s >= n, odd recovered R', and any Mark whose recomputed R'.x differs from r.