Skip to content

Errata 2 for BRC-145: verifier verdict vocabulary, adopter-name profile, and dated forgery costs - #243

Open
RexStarBSV wants to merge 1 commit into
bsv-blockchain:masterfrom
RexStarBSV:brc-145-errata-2
Open

RexStarBSV wants to merge 1 commit into
bsv-blockchain:masterfrom
RexStarBSV:brc-145-errata-2

Conversation

@RexStarBSV

Copy link
Copy Markdown
Contributor

This PR amends apps/0145.md. It follows PR #212, which merged three verification errata on 2026-08-10: rejection of a kind that is not encodable as valid UTF-8 (§4), the every-pixel check on an integer downscale (§5), and rejection of rendering-affecting ancillary PNG chunks (§5). Those three closed every divergence that touched a presented image. What remains is the signed-record verifier, plus three prose items, and this PR closes them.

Scope statement first, because it is the important one. Every change below is verifier-side reporting or prose. No anchor, no ik1 code, no canonical seal, no canonical record byte and no signature changes value under any entry in this PR. Test vectors TV1 through TV7 stand unaltered, and an existing conformant writer stays conformant without touching a line. What changes is what a verifier is required to check before it speaks, and what verdict it is permitted to report when a check could not be performed.

The reference implementation already behaves as described here. The defect is that a second implementation built strictly from the published text would not, and in two places the published text actively directs it to the wrong verdict.


1. §7.3 has no verdict for a check that never ran, and routes one into INVALID (high)

As merged. §7.3 opens a closed enumeration: "A verifier reports exactly one of:" followed by INVALID, STALE, OK. INVALID is defined by outcome: "the signature does not verify against the published key over SHA-256(canonical_record_bytes)."

Why this is wrong. A truncated public key, a DER blob that will not decode, or an OP_RETURN container whose pushes do not parse (§7.1 already requires rejecting that container) all fail to verify, so §7.3's own definition sends them to INVALID. INVALID is the maximum-severity verdict and it asserts a specific fact: a signature check ran and the signature was wrong. For what is nearly always a truncated paste, that is a false accusation of forgery. The Security Considerations section already states the correct principle, "INVALID means a signature check was performed and failed", and then supplies no status for the case where no check was performed. The document is at odds with itself, and §7.3 wins for an implementer.

Amendment. Add a MALFORMED status to §7.3 and make it fail-closed. Combined with §§2 and 3 below, §7.3 becomes:

**7.3 Status.** A verifier reports exactly one of:

- `MALFORMED`, the container, the record, the signature, or the public key could
  not be parsed, so no signature check was performed. A key or signature that does
  not decode, a script that fails the §7.1 parse, and a record that is not a JSON
  object carrying exactly the seven fields of §7.2 with those types, MUST all be
  reported as `MALFORMED` and MUST NOT be reported as `INVALID`. `MALFORMED` is
  fail-closed and is never `OK`.
- `UNSUPPORTED`, the record parsed but carries a record version this verifier does
  not implement (§7.2, `v`).
- `INVALID`, a signature check was performed against the published key over
  `SHA-256(canonical_record_bytes)` and failed.
- `STALE`, signature valid but the record is outside the freshness window
  (window definition unchanged).
- `OK`, record well-formed, version supported, signature valid, and record fresh.

A verifier whose signature backend is unavailable MUST raise an explicit error and
MUST NOT report any of these five statuses.

For a canary, absence, `MALFORMED`, `UNSUPPORTED`, `STALE`, or `INVALID` is itself
the warning.

One clarification travels with this rewrite. §7.3 evaluates the signature against "the published key", while §7.1 pushes a public key into the output. The amendment makes the intent explicit: the key the status is evaluated against is the operator's out-of-band published key, and the on-chain key push is material for offline re-derivation, never the authority for OK. A verifier that accepts the presenter's own key makes OK self-certifying.

2. Nothing requires a verifier to check the record version, and an unknown version reports OK (high)

As merged. v appears once, as a row in the §7.2 field table: "v | int | record version (1)". No sentence anywhere obliges a verifier to read it. §7.3 defines "OK, signature valid and fresh", with no version predicate, and §8's summary requires only that an on-chain record "be re-verified offline (signature + freshness) after parsing".

Why this is wrong. A well-signed, fresh record of version 2, whose fields a version-1 verifier does not understand, reports OK under the merged text. The version-checking discipline the document applies rigorously to codes (§3, "the reader MUST reject it rather than assume SHA-256") is absent for records. The second half of this erratum, the obligation on an unavailable signature backend, is already present and correct in Security Considerations and needs no change.

Amendment. Add UNSUPPORTED to §7.3 as above, and add to §7.2:

A verifier MUST read `v` before interpreting any other field, and MUST report a
record whose version it does not implement as `UNSUPPORTED` (§7.3), never as
`INVALID` and never as `OK`. Reporting `INVALID` accuses a well-formed record of a
bad signature; reporting `OK` vouches for fields the verifier did not understand.

3. The seven-field record shape is described, never required (high)

As merged. §7.2 introduces the field table with "The reference canary record has fields:". That is descriptive. §7.1 says the container is general. Nothing requires a verifier to reject a record with a missing field, an extra field, or a field of the wrong type, and §7.3 gives it no verdict to report if it tried.

Why this is wrong. A wrong-shaped record cannot be forged, since the signature covers whatever fields are present. It can be emitted by mistake, and under the merged text every verifier reports OK while downstream readers disagree about what they are reading. A record missing block_hash loses the anti-backdate binding §7.2 relies on, and still reports OK.

Amendment. Make the table normative and give it a verdict, by appending to §7.2:

A signed record MUST carry exactly these seven fields, with these types. A record
with a missing field, an extra field, or a field of the wrong type MUST be reported
as `MALFORMED` (§7.3), whether or not its signature verifies over the fields
present. The shape check runs before the signature check.

4. The adopter name in the tamper fingerprint has no character profile (medium)

As merged. §6 constrains the adopter name only by normalization and length: "where adopter_bytes = utf8(NFC(adopter)), len(adopter_bytes) ≤ 255 (reject otherwise), and version defaults to 1."

Why this is wrong. The length prefix closes the adopter/payload boundary, which Security Considerations correctly credits. It does not close a cross-context shape. u8(len(adopter_bytes)) is a single byte chosen by the adopter name's own length, so a 105-byte adopter name beginning nkan/v1 followed by a NUL yields a length byte 0x69, which is ASCII i. The fingerprint preimage then opens with the exact nine bytes of INKAN_DOMAIN and parses as a glyph seed under §2. One digest is simultaneously a valid fingerprint and a valid anchor. Every honest adopter name already conforms to the fix, so nothing in the field breaks.

Amendment. Append to §6, after the length and normalization sentence:

An adopter name MUST be printable UTF-8, MUST NOT contain a NUL byte, and MUST NOT
begin with `nkan/`. With the right length, `u8(len(adopter_bytes)) || adopter_bytes`
can reproduce the nine bytes of `INKAN_DOMAIN`, which would let a fingerprint
preimage also parse as a glyph seed (§2). This profile closes that shape. Every
honest adopter name already satisfies it.

Add the matching bullet to §8: "An adopter name MUST satisfy the §6 profile before it is used as a fingerprint domain."

5. Visual comparison has no ceiling, and no verdict rule below the full digest (medium)

As merged. §3 carries the label half correctly: "A short code is a label only; trust decisions MUST use the full 32-byte anchor." Security Considerations adds "Implementers MUST NOT market a seal as human-proof". Missing is any rule binding the GENUINE verdict itself, and the tier guidance "value-at-risk kinds SHOULD use t1" reads as though a high tier makes a visual check adequate for a value-at-risk decision. It does not, at any tier.

Amendment. Append to §5, after the verification paragraph:

Visual comparison is a human tamper-notice channel. It is never sufficient
authentication for a value-at-risk decision, at any tier. A verifier MUST NOT return
GENUINE on the basis of any comparison shorter than the full 32-byte anchor. The
tier recommendation for value-at-risk kinds raises an attacker's cost; it does not
make a visual check authoritative.

6. The forgery-cost figures carry no epoch (medium)

As merged. Security Considerations states "a perfect visual collision costs on the order of 2^16 / 2^28 / 2^40 renders (birthday) for t3 / t2 / t1", with no date and no decay note. The only occurrence of "quantum" in the document is the reserved signature-algorithm identifier in §7.2, which concerns signatures, not seal-forgery cost. A reader in 2036 has no notice that these numbers age.

Amendment. Append to the "Seal forgery economics" bullet:

These figures are stated for 2026 classical compute. Compute growth and quadratic
quantum search erode every visual-forgery figure over decades. The anchor's own
security does not rest on them.

Not included

One erratum in the underlying standard's list has no counterpart here and needs no amendment: the audit-evidence pointer correction is internal repo hygiene, and apps/0145.md makes no audit claim and carries no evidence path, so there is nothing to retarget. The pixel-level seal construction and the PNG palette and filter conventions remain deliberately out of scope per §5, and this PR does not reopen that decision.

Test vectors

No test vector changes, and none is required. TV1 through TV7 are reproduced unchanged by an implementation carrying every amendment above, because every amendment constrains what a verifier reads and reports, never what a writer produces. TV7's canonical record already carries exactly the seven fields §7.2 now requires, its 222 canonical bytes and its DER signature are untouched, and the 339-byte OP_RETURN script is byte-identical before and after.

The new obligations are exercised by inputs that no current vector contains and that produce no signed bytes: a public key truncated by one byte, a DER signature with a corrupted length octet, a record with v set to 2, a record with block_hash removed, a record with seq carried as a string, and an adopter name beginning nkan/. Each has a single correct verdict under the amended §7.3 and §6, and each is reportable without any new canonical form. Reviewers who want them pinned in the document can have a TV8 covering the five status outcomes; it would add no new signing material, only expected verdicts.

To restate the guarantee in one line: this PR changes what a verifier is required to check and what it is permitted to say, and it changes nothing that anyone has ever hashed, rendered, signed or broadcast.

…pe validation, the adopter-name profile, and dated forgery costs

Section 7.3 enumerates a closed set of three statuses, so a verifier built from
this text has nowhere to put a key or signature that did not parse and routes it
into INVALID, which asserts a signature check ran and failed. This adds MALFORMED
for inputs that did not parse and UNSUPPORTED for a record version the verifier
does not implement, and requires that v be checked.

Section 7.2 documents the record fields without requiring a verifier to validate
the shape, so a record of the wrong shape reads OK. Shape validation is now
required before OK, with MALFORMED as its verdict.

Section 6 constrains the adopter name: printable UTF-8, no NUL, and no nkan/
prefix, which closes a preimage that would parse both as a fingerprint and as a
glyph seed.

Section Security Considerations date-stamps the visual-forgery figures as 2026
classical compute and adds the erosion note.

No anchor, code, canonical seal, canonical record or signature changes value.
Every test vector stands unaltered. An existing conformant writer stays
conformant; the changes bind what a verifier checks and what it may report.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant