Tags: record, crypto
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.
Secret-key generation and scalar derivation, verification-key derivation, Mark construction, and Mark checking.
&.<b64a>.H3, where the payload is
32 secret bytes from which the internal scalar is derived.V.<b64a>.H3, where the payload
is a 32-byte public x-coordinate.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.
G.n.p.msg32, the 32 decoded digest bytes
carried by the marked Plex record’s P.<b64a>.H3
glyph. Under 010, those bytes are the BLAKE3-256 digest of the Plex
record’s canonical payload after its record glyph line.bytes(x), the 32-byte big-endian
encoding of a scalar or field element.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.
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:
lace-🖧/adhoc-key.d0 satisfies
0 < d0 < n.P = d0*G.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.
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
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:
t = derive_key("lace-🖧/aux", auxRand32).mask = t xor bytes(d).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.R = k0*G; use k = n-k0 when
R.y is odd, else k = k0.e = derive_key("lace-🖧/challenge", bytes(R.x) || Px || msg32) mod n.s = (k + e*d) mod n.bytes(R.x) || bytes(s).Inputs are verification-key x-coordinate Px, Mark
r||s, and message digest msg32.
Implementations MUST check as follows:
Px >= p, r >= p, or
s >= n.P from Px using the even-y
root; reject Px values that do not identify a secp256k1
point.e = derive_key("lace-🖧/challenge", bytes(r) || Px || msg32) mod n.R' = s*G - e*P.R'.y.R'.x == r.Implementations MUST use constant-time scalar and point operations.
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 |
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.
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.