Internet-Draft Digest-Only Evidence Logs October 2026
Jangra Expires 11 April 2027 [Page]
Workgroup:
SCITT
Internet-Draft:
draft-jangra-scitt-digest-evidence-00
Published:
Intended Status:
Informational
Expires:
Author:
A. Jangra
RiskRouter

Digest-Only Evidence Logs: Completeness, Disclosure and Sampling over RFC 9162 Transparency Logs

Abstract

This document describes an evidence log that records only salted digests of records held by their makers, as leaves of an RFC 9162 Merkle tree, and three constructions over it that answer questions a single inclusion proof cannot: whether a set of records shown is complete (completeness chains), what one field of a sealed record said without revealing the others (selective disclosure), and whether records were kept, checked on a sample nobody could choose (spot checks). It profiles SCITT registration so that the log keeps a digest of each Signed Statement and never the statement. It is published as running code with test vectors.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 11 April 2027.

▲

Table of Contents

1. Introduction

Regulated firms must keep records (advice given, credit decisions, AI system logs, security logs) and, years later, show that a record is the one that existed at the time. A transparency log can make that showable without anyone trusting the firm or the log operator. Where records hold personal data, the log should never receive them: only a digest of a salted record leaves its maker, so the log holds nothing that must later be erased.

An inclusion proof answers "was this record sealed unchanged?". It does not answer "is this all of it?", "what did this one field say?", or "were the records kept?". Section 5, Section 6 and Section 7 answer those, over an unchanged RFC 9162 tree. Section 8 profiles SCITT registration for digest-only logs.

The key words "MUST", "MUST NOT", "SHOULD" and "MAY" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals.

2. Conventions

Hashes are SHA-256 [RFC6234]. A digest is written as 64 lowercase hexadecimal characters. Strings are UTF-8. The fields of a string that is hashed or signed are joined with "|", with no spaces and no escaping; no field may contain "|". Integers are written in ASCII decimal without leading zeros. Times inside such strings are UTC, written YYYY-MM-DDTHH:MM:SS.ffffffZ with exactly six fraction digits; a reader that receives another representation of the same instant MUST normalise it to this form before hashing or verifying. An account identifier is a UUID [RFC9562] in its lowercase canonical form. A kind matches ^[a-z0-9][a-z0-9.-]{0,39}$.

Unless stated otherwise, signatures are ECDSA over P-256 with SHA-256 [FIPS186-5], encoded as the 64-byte concatenation r || s and then base64 [RFC4648].

3. Records and digests

A record is a JSON object whose keys match ^[a-z][a-z0-9_]{0,63}$ at every level and whose values are strings, integers within plus or minus (2^53 - 1), true, false, null, arrays and objects, nested at most 32 deep. Its canonical form is the JSON Canonicalization Scheme [RFC8785] of the record. Its digest is

record_digest = SHA-256(salt || UTF-8(canonical))

where salt is 16 to 64 random bytes the maker keeps with the record. A record outside the profile MUST be refused, never coerced.

4. Leaves and heads

A leaf records leaf_index (assigned by the log, from 0, without gaps), created_at (assigned by the log, never by the client), the recording account, a kind, and record_digest:

leaf = "riskrouter-evidence-leaf|v2|" leaf_index "|" created_at "|" account "|" kind "|" record_digest
leaf hash = SHA-256(0x00 || UTF-8(leaf))

Leaves form the Merkle tree of [RFC9162], Section 2.1, whose hashing is unchanged from [RFC6962].

A maker MAY sign its own claim, riskrouter-evidence-claim|v3|kind|record_digest|key_id|claimed_at, with a P-256 key it registered with the log; the signature is then part of a v3 leaf string, inside the tree, so it cannot be replaced afterwards. The log verifies the signature before recording and refuses, recording nothing, a claim that does not verify. Heads are signed by the log over riskrouter-evidence-head|v2|tree_size|root_hash|timestamp. The same tree is also published as C2SP checkpoints [C2SP-CHECKPOINT], signed with a separate Ed25519 key, with tiles [C2SP-TILES], so independent witnesses can cosign them.

5. Completeness chains

A maker MAY place records in numbered chains. The record carries, inside itself:

and the maker sends chain_tag and chain_seq beside the digest, outside the leaf. The log MUST refuse a chain_seq that is not exactly the last position plus one, and a chain_tag already used by another account, recording nothing.

On request for a tag, the log reads its current signed head first and then the chain's entries below that head's size, and signs:

link line     = chain_seq "|" leaf_index "|" created_at "|" record_digest LF
links_digest  = SHA-256(UTF-8(concatenation of the link lines from `from` to `to`))
chain payload = "riskrouter-evidence-chain|v1|" chain_tag "|" length "|" tree_size "|"
                root_hash "|" timestamp "|" from "|" to "|" links_digest

A verifier holding the statement and the records a maker shows checks, for every position from 1 to length, that a record was shown which produces the digest at that position, names that tag and position, and names the digest of the position before. A position the statement lists and nobody showed is a record left out. The maker alone cannot hide a chained record; hiding one requires the log to sign a statement its own tree contradicts.

6. Selective disclosure

To seal a record from which single fields can later be disclosed, the maker seals a commitment record:

field salt    = HMAC-SHA256(secret, UTF-8("riskrouter-disclosable|1|" || name))
commitment    = SHA-256(field salt || UTF-8(canonical({name: value})))
sealed record = {"format": "riskrouter-disclosable|1", "kind": kind,
                 "fields": {name: commitment, ...}}

and records the digest of the sealed record as any record. A disclosure carries the sealed record, its salt, the proof, and, for each disclosed field, its value and field salt. The secret MUST NOT be disclosed: with it, a hidden field of low entropy could be found by trial. Field names are visible; values are not.

7. Spot checks

To check that records were kept without receiving them all, an auditor seals a plan naming a chain, a sample size and a future Bitcoin block height. When the block is mined, its hash is the seed, and the positions drawn are:

for counter = 0, 1, 2, ...
  h = SHA-256(UTF-8("riskrouter-spot-check|v1|" seed "|" population "|" counter))
  x = the first 8 bytes of h, big-endian, unsigned
  skip if x >= 2^64 - (2^64 mod population)
  position = 1 + (x mod population), kept unless already drawn
until min(sample, population) positions are drawn

where population is the chain's signed length. The maker shows the records at those positions, which are checked as in Section 5.

8. SCITT profile

Registration under policy riskrouter-scitt-profile|1 [I-D.ietf-scitt-architecture] accepts a COSE_Sign1 [RFC9052] signed ES256 with a key the caller registered, with CWT claims iss and sub in the protected header [RFC9597], and a hash envelope [I-D.ietf-cose-hash-envelope] whose payload is 32 bytes, so the artifact never reaches the log. The log keeps one leaf of kind scitt.statement whose digest is SHA-256 of the statement re-encoded with an empty unprotected header, and returns an RFC 9942 receipt [RFC9942] over the tree root. This departs from the architecture's expectation that a Transparency Service stores statements, deliberately: the log holds no content.

9. Time

Each new signed head is sent to RFC 3161 [RFC3161] time-stamping authorities and anchored in Bitcoin through OpenTimestamps [OPENTIMESTAMPS]. Neither is a qualified time stamp in the sense of EU law unless its authority is.

10. Security Considerations

A digest of a guessable record can be confirmed by anyone who guesses it; makers MUST salt records. A seal shows a record existed unchanged no later than its leaf's time; it does not show the record is true. A chain shows nothing about records that were never chained; whether every record of a kind must be chained is a policy question for the maker's supervisor. A log operator that colludes with a maker could accept a second chain position out of turn; its signed chain statements and heads, saved by holders and cosigned by witnesses, are what make such a fork evidence against the operator. On a log the maker runs itself, a chain is the maker's own word. Spot-check seeds rely on the auditor announcing a block height before it is mined, provable by the plan's leaf time.

11. Privacy Considerations

The log holds digests, short kinds, account identifiers, chain tags and times. Records, salts, chain secrets and disclosure secrets stay with makers. A chain statement reveals how many records a chain holds and when they were sealed, to anyone holding its tag.

12. IANA Considerations

This document has no IANA actions.

13. References

13.1. Normative References

[FIPS186-5]
National Institute of Standards and Technology, "Digital Signature Standard (DSS)", FIPS 186-5, .
[RFC2119]
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
[RFC3161]
Adams, C., Cain, P., Pinkas, D., and R. Zuccherato, "Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)", RFC 3161, DOI 10.17487/RFC3161, , <https://www.rfc-editor.org/rfc/rfc3161>.
[RFC4648]
Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, , <https://www.rfc-editor.org/rfc/rfc4648>.
[RFC6234]
Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)", RFC 6234, DOI 10.17487/RFC6234, , <https://www.rfc-editor.org/rfc/rfc6234>.
[RFC8174]
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/rfc/rfc8174>.
[RFC8785]
Rundgren, A., Jordan, B., and S. Erdtman, "JSON Canonicalization Scheme (JCS)", RFC 8785, DOI 10.17487/RFC8785, , <https://www.rfc-editor.org/rfc/rfc8785>.
[RFC9052]
Schaad, J., "CBOR Object Signing and Encryption (COSE): Structures and Process", STD 96, RFC 9052, DOI 10.17487/RFC9052, , <https://www.rfc-editor.org/rfc/rfc9052>.
[RFC9162]
Laurie, B., Messeri, E., and R. Stradling, "Certificate Transparency Version 2.0", RFC 9162, DOI 10.17487/RFC9162, , <https://www.rfc-editor.org/rfc/rfc9162>.
[RFC9942]
Steele, O., Birkholz, H., Delignat-Lavaud, A., and C. Fournet, "CBOR Object Signing and Encryption (COSE) Receipts", RFC 9942, DOI 10.17487/RFC9942, , <https://www.rfc-editor.org/rfc/rfc9942>.

13.2. Informative References

[C2SP-CHECKPOINT]
"Transparency Log Checkpoints", n.d., <https://c2sp.org/tlog-checkpoint>.
[C2SP-TILES]
"Tiled Transparency Logs", n.d., <https://c2sp.org/tlog-tiles>.
[I-D.ietf-cose-hash-envelope]
Steele, O., Lasker, S., and H. Birkholz, "COSE Hash Envelope", Work in Progress, Internet-Draft, draft-ietf-cose-hash-envelope-10, , <https://datatracker.ietf.org/doc/html/draft-ietf-cose-hash-envelope-10>.
[I-D.ietf-scitt-architecture]
Birkholz, H., Delignat-Lavaud, A., Fournet, C., Deshpande, Y., and S. Lasker, "An Architecture for Trustworthy and Transparent Digital Supply Chains", Work in Progress, Internet-Draft, draft-ietf-scitt-architecture-22, , <https://datatracker.ietf.org/doc/html/draft-ietf-scitt-architecture-22>.
[OPENTIMESTAMPS]
"OpenTimestamps", n.d., <https://opentimestamps.org>.
[RFC6962]
Laurie, B., Langley, A., and E. Kasper, "Certificate Transparency", RFC 6962, DOI 10.17487/RFC6962, , <https://www.rfc-editor.org/rfc/rfc6962>.
[RFC7942]
Sheffer, Y. and A. Farrel, "Improving Awareness of Running Code: The Implementation Status Section", BCP 205, RFC 7942, DOI 10.17487/RFC7942, , <https://www.rfc-editor.org/rfc/rfc7942>.
[RFC9562]
Davis, K., Peabody, B., and P. Leach, "Universally Unique IDentifiers (UUIDs)", RFC 9562, DOI 10.17487/RFC9562, , <https://www.rfc-editor.org/rfc/rfc9562>.
[RFC9597]
Looker, T. and M.B. Jones, "CBOR Web Token (CWT) Claims in COSE Headers", RFC 9597, DOI 10.17487/RFC9597, , <https://www.rfc-editor.org/rfc/rfc9597>.

Appendix A. Implementation Status

(RFC Editor: please remove this section before publication.) This section follows [RFC7942].

An implementation is published at https://riskrouter.eu/spec with test vectors (spec-vectors.json), two independent verifiers (JavaScript and Python, standard libraries only) and a conformance suite. Its result files are the implementer's own statements; nobody certifies an implementation.

Author's Address

Anil Jangra
RiskRouter