<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt"?>
<!DOCTYPE rfc [ ]>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     category="info"
     docName="draft-templeman-scitt-framing-space-00"
     ipr="trust200902"
     submissionType="IETF"
     consensus="false"
     obsoletes=""
     updates=""
     xml:lang="en"
     version="3">

  <front>
    <title abbrev="COSE_Sign1 Framing Space">Measuring the CBOR Framing Space of COSE_Sign1 Data-Hash Pre-images</title>
    <seriesInfo name="Internet-Draft" value="draft-templeman-scitt-framing-space-00" stream="IETF"/>

    <author fullname="Nicholas Templeman" initials="N." surname="Templeman">
      <organization abbrev="CSOAI">Council of AI (CSOAI Ltd)</organization>
      <address>
        <postal>
          <street>3rd Floor, 86-90 Paul Street</street>
          <city>London</city>
          <code>EC2A 4NE</code>
          <country>GB</country>
        </postal>
        <email>nicholas@csoai.org</email>
        <uri>https://councilof.ai/</uri>
      </address>
    </author>

    <date year="2026" month="September" day="5"/>

    <area>Security</area>
    <workgroup>SCITT</workgroup>

    <keyword>COSE</keyword>
    <keyword>CBOR</keyword>
    <keyword>SCITT</keyword>
    <keyword>data-hash</keyword>
    <keyword>transparency</keyword>

    <abstract>
      <t>
        A signed statement conveyed as a COSE_Sign1 object may be serialized into
        many distinct byte sequences that all decode to the same data item. Where a
        protocol identifies such a statement by a digest computed over its wire
        octets (referred to here as a data-hash), the identifier is sensitive to
        that framing while the signature over the statement is not.
      </t>
      <t>
        This document reports a measurement of the size of that class. Taking one
        165-octet COSE_Sign1 object and re-emitting it under every combination of
        six CBOR encoding freedoms yields 64 distinct octet sequences. All 64 carry
        an identical Sig_structure and therefore an identical, valid signature. All
        64 produce distinct data-hash values, with no collisions. A stock CBOR
        decoder rejected none of them, and 31 were silently repaired into the
        canonical form by the act of being read.
      </t>
      <t>
        This document specifies nothing and proposes no wording. It reports a
        measurement, publishes the reproduction recipe, and identifies the prior
        work that already addresses the problem it measures.
      </t>
    </abstract>
  </front>

  <middle>

    <section anchor="introduction">
      <name>Introduction</name>
      <t>
        Transparency protocols in the SCITT architecture <xref target="RFC9943"/>
        frequently need a stable identifier for a signed statement. A natural choice
        is a digest over the octets of the statement as it appeared on the wire,
        since that is what a client fetches and can byte-compare. This document
        refers to such a digest as a <em>data-hash</em>.
      </t>
      <t>
        Identifiers of this shape appear in the SCITT Reference APIs
        <xref target="I-D.ietf-scitt-scrapi"/> and in the CCF profile for COSE
        receipts <xref target="I-D.ietf-scitt-receipts-ccf-profile"/>. The
        measurement reported here is offered as input to that work rather than as
        a comment on either document.
      </t>
      <t>
        CBOR <xref target="RFC8949"/> permits a single data item to be encoded in
        more than one way. A COSE_Sign1 object <xref target="RFC9052"/> may be
        emitted with or without its tag, with definite or indefinite length arrays
        and maps, and with byte strings emitted whole or in chunks. Each such choice
        changes the octets without changing the decoded value.
      </t>
      <t>
        The signature does not observe those choices. Per <xref target="RFC9052" section="4.4" sectionFormat="comma"/>, the Sig_structure covers the protected header and the payload;
        it does not cover the framing of the enclosing container. The consequence is
        an asymmetry: two parties may agree that a signature is valid and still
        disagree on the data-hash of the statement they have both just verified.
      </t>
      <t>
        Two instances of this asymmetry were raised independently on the SCITT
        mailing list within two days of each other in September 2026: one
        concerning the CBOR tag, one concerning the outer array. That coincidence
        prompted the question this document answers: not "which framings exist", but
        "how large is the class they belong to".
      </t>
    </section>

    <section anchor="conventions">
      <name>Conventions and Definitions</name>
      <t>
        This document is Informational and contains no normative requirements.
      </t>
      <dl>
        <dt>data-hash:</dt>
        <dd>
          A digest computed over the octet sequence of a signed statement as
          transmitted, used as an identifier for that statement.
        </dd>
        <dt>framing:</dt>
        <dd>
          Those aspects of a CBOR encoding that may vary without changing the
          decoded data item.
        </dd>
      </dl>
    </section>

    <section anchor="method">
      <name>Method</name>
      <t>
        A single COSE_Sign1 object, referred to here as A, was taken as the
        starting point. A is 165 octets and has the SHA-256 digest
        <tt>8595e4a4c8b93e7b1b7b798dc302a2b7d2890021f7eff372d79b32f78867e4ac</tt>.
      </t>
      <t>
        A was decoded once, and the resulting data item re-emitted under every
        combination of six independent encoding freedoms:
      </t>
      <ol>
        <li>presence or absence of CBOR tag 18;</li>
        <li>definite or indefinite length outer array;</li>
        <li>definite or indefinite length unprotected header map;</li>
        <li>protected header byte string emitted whole or chunked;</li>
        <li>payload byte string emitted whole or chunked;</li>
        <li>signature byte string emitted whole or chunked.</li>
      </ol>
      <t>
        Six binary freedoms give 64 combinations. No element value was altered at
        any point; only its encoding. Each resulting octet sequence was then
        decoded with a stock CBOR decoder, and both its data-hash and its
        Sig_structure were computed.
      </t>
      <t>
        The decoder used was cbor2 6.1.3 on CPython 3.13, macOS arm64. The
        measurement, its inputs and the minting script are published at
        <eref target="https://councilof.ai/interop/scrapi-ccf/data-hash-framing-space.json"/>.
      </t>
    </section>

    <section anchor="results">
      <name>Results</name>

      <table>
        <name>Framing space of one COSE_Sign1 object</name>
        <thead>
          <tr><th align="left">Quantity</th><th align="right">Value</th></tr>
        </thead>
        <tbody>
          <tr><td align="left">Encoding freedoms varied</td><td align="right">6</td></tr>
          <tr><td align="left">Distinct octet sequences produced</td><td align="right">64</td></tr>
          <tr><td align="left">Distinct data-hash values</td><td align="right">64</td></tr>
          <tr><td align="left">Data-hash collisions</td><td align="right">0</td></tr>
          <tr><td align="left">Rejected by the stock decoder</td><td align="right">0</td></tr>
          <tr><td align="left">Silently re-serialized to A on read</td><td align="right">31</td></tr>
          <tr><td align="left">Distinct Sig_structures across all 64</td><td align="right">1</td></tr>
        </tbody>
      </table>

      <t>
        The last two rows carry the substance of the result.
      </t>
      <t>
        There is exactly one Sig_structure across all 64 encodings: 109 octets, with
        the SHA-256 digest
        <tt>60b4c76b84c456ed0604305075f51f6d050476177ef17be70a13a7b9abfb370f</tt>.
        A single signature therefore verifies over every one of the 64 encodings,
        while each of those encodings has a different data-hash.
      </t>
      <t>
        Of the 64, 31 were repaired into the canonical form by the act of being
        read: a decode-then-re-encode cycle returned exactly A's octets. An
        implementation that stores a re-serialization of what it received, rather
        than the received octets themselves, will therefore compute a data-hash it
        cannot later reproduce from what it published, and no error is raised at
        any point in that sequence.
      </t>
      <t>
        The 64 encodings ranged from 164 to 170 octets. The smallest, at 164
        octets, is A with the tag removed; it is the one variant of the seven named
        in the published artifact that does not re-serialize to A.
      </t>
      <t>
        This measurement does not vary integer-width encoding or map key ordering.
        The true class is therefore larger than 64.
      </t>
    </section>

    <section anchor="discussion">
      <name>Discussion</name>
      <t>
        The observation this document supports is narrow: a rule that enumerates
        disallowed framings cannot close a combinatorial class, because the class is
        larger than the set of framings anyone has happened to notice. Two axes were
        found by accident in one week; six freedoms give 64 encodings; and the six
        tested are not all of them.
      </t>
      <t>
        This document does not propose that data-hash be made
        framing-invariant. The wire octets are what a client fetches and
        byte-compares, so hashing them is the appropriate choice. The open question
        is only which octets are meant.
      </t>
      <t>
        A consequence worth stating explicitly, because it is an implementation
        requirement rather than a wording question: a service that publishes a
        data-hash cannot reproduce the identifier it published unless it retains
        the octets it received rather than a re-serialization of them. The figure of 31
        silent repairs out of 64 indicates that a decode-and-re-encode storage path
        will encounter this in practice rather than in theory.
      </t>
    </section>

    <section anchor="prior-work">
      <name>Relationship to Existing Work</name>
      <t>
        This document discovers nothing. It measures the size of a class whose
        members were identified by others, and the approaches that address it were
        published before this measurement was made.
      </t>
      <t>
        <xref target="I-D.mih-sokolov-scitt-payload-binding"/> registers an
        <tt>as-transmitted</tt> binding, under which no canonicalization is applied
        and the pre-image is the exact octet sequence, and withdrew two
        canonicalization profiles before it. That approach closes the whole class
        measured here, and predates this measurement.
      </t>
      <t>
        The outer-array framing axis was raised on the SCITT mailing list on
        2026-09-04 by Konrad Gruszka, together with an independent recomputation
        performed on a different platform with a different reader and no COSE
        library. The tag axis was raised on the list on 2026-09-03 by the author of
        this document. The tag axis surfaced only because Emek Can Dogru recomputed
        a previously published vector rather than reading it.
      </t>
      <t>
        This document is offered because the measurement is reproducible and the
        magnitude may be useful to those weighing the alternatives, not because the
        alternatives are in doubt.
      </t>
    </section>

    <section anchor="security">
      <name>Security Considerations</name>
      <t>
        The behaviour reported here is a naming and reproducibility hazard rather
        than a break of any cryptographic primitive. No signature is forged, no
        digest is collided, and the signature scheme behaves exactly as
        <xref target="RFC9052"/> specifies.
      </t>
      <t>
        The hazard is that a verifier and an issuer may both be correct and still
        disagree. Where a data-hash is used as a lookup key, an index, a
        deduplication key, or as the subject of a further attestation, an
        adversary able to influence the framing of a statement in transit, without
        invalidating its signature, can cause that statement to be filed under an
        identifier the issuer does not expect, or to appear absent from a service
        that in fact holds it.
      </t>
      <t>
        Retaining received octets verbatim, as
        <xref target="I-D.mih-sokolov-scitt-payload-binding"/> describes, removes
        this class of hazard. Enumerating individual disallowed framings does not,
        since the enumeration cannot be shown to be complete.
      </t>
    </section>

    <section anchor="iana">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</t>
    </section>

  </middle>

  <back>
    <references>
      <name>Normative References</name>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8949.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9052.xml"/>
    </references>

    <references>
      <name>Informative References</name>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9943.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.mih-sokolov-scitt-payload-binding.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-scitt-scrapi.xml"/>
      <xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-scitt-receipts-ccf-profile.xml"/>
    </references>

    <section anchor="reproducing" numbered="true">
      <name>Reproducing the Measurement</name>
      <t>
        The published artifact contains the starting object A, the seven named
        variants with their sizes and digests, the full finding, and the script
        that mints them:
      </t>
      <ul>
        <li><eref target="https://councilof.ai/interop/scrapi-ccf/data-hash-framing-space.json"/></li>
        <li><eref target="https://councilof.ai/interop/scrapi-ccf/data-hash-vector.json"/></li>
      </ul>
      <t>
        The starting object A is field
        <tt>A_signed_statement_as_registered.bytes_hex</tt> of the second file. Its
        SHA-256 is recorded in the first. Re-emitting it under the six freedoms
        listed in <xref target="method"/> and hashing each result reproduces the
        table in <xref target="results"/>.
      </t>
    </section>

    <section anchor="acknowledgements" numbered="false">
      <name>Acknowledgements</name>
      <t>
        Konrad Gruszka raised the outer-array framing axis and independently
        recomputed the vector on a separate platform. Emek Can Dogru recomputed a
        published vector rather than reading it, which is what surfaced the tag
        axis. Anton Sokolov's registration of an as-transmitted binding reached the
        conclusion this measurement supports before the measurement was made.
      </t>
    </section>
  </back>
</rfc>
