Internet-Draft CCF Profile for COSE Receipts September 2026
Birkholz, et al. Expires 27 March 2027 [Page]
Workgroup:
SCITT
Internet-Draft:
draft-ietf-scitt-receipts-ccf-profile-05
Published:
Intended Status:
Standards Track
Expires:
Authors:
H. Birkholz
Fraunhofer SIT
A. Delignat-Lavaud
Microsoft Research
C. Fournet
Microsoft Research
A. Chamayou
Microsoft Research

CCF Profile for COSE Receipts

Abstract

This document defines a new verifiable data structure (VDS) type for COSE Receipts and the associated inclusion and consistency proofs, specifically designed for append-only logs produced by the Confidential Consortium Framework (CCF) to provide stronger tamper-evidence guarantees.

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 27 March 2027.

Table of Contents

1. Introduction

The COSE Receipts document [RFC9942] defines a common framework for expressing different types of proofs about verifiable data structures (VDS), providing a standardized way to convey trust-relevant evidence. For instance, inclusion proofs guarantee to a verifier that a given serializable element is recorded at a given state of the VDS, while consistency proofs are used to establish that an inclusion proof is still consistent with the new state of the VDS at a later time.

In this document, we define a new type of VDS and the associated inclusion and consistency proofs for an application of the Confidential Consortium Framework (CCF) ledger that implements the SCITT Architecture defined in [RFC9943]. This VDS carries indexed transaction information in a binary Merkle Tree, where new transactions are appended to the right, so that the binary decomposition of the index of a transaction can be interpreted as the position in the tree if 0 represents the left branch and 1 the right branch. Compared to [RFC9162], the leaves of CCF trees carry additional internal information for the following purposes:

  1. To bind the full details of the transaction executed, which is a superset of what is exposed in the proof and captures internal details useful for detailed system audit, but not for application purposes.

  2. To allow the distributed system executing the application logic in Trusted Execution Environments (TEEs) to persist signatures to storage early. Receipt production is only enabled once transactions are fully committed by the consensus protocol.

1.1. Requirements Notation

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

2. Description of the Confidential Consortium Framework Ledger Verifiable Data Structure

This document defines CCF_LEDGER_SHA256 for append-only CCF transaction ledgers containing a mix of public and confidential information. These ledgers are integrity-protected by a Merkle Tree and signatures produced via Trusted Execution Environments. The registration request for this verifiable data structure is specified in Section 8.1.1.

The placeholder TBD_1 denotes the algorithm identifier, with requested assignment 2. The CDDL and examples use this requested value pending IANA allocation; it is not an assigned value.

2.1. Merkle Tree Shape

A CCF ledger is a binary Merkle Tree constructed from a hash function H, which is defined from the log type. For instance, the hash function for CCF_LEDGER_SHA256 is SHA256, whose HASH_SIZE is 32 bytes.

The Merkle Tree encodes an ordered list of n transactions T_n = {T[0], T[1], ..., T[n-1]}. Each transaction T[i] is serialized to a byte string d[i] as defined in Section 2.2, giving the list D_n = {d[0], d[1], ..., d[n-1]}. We define the Merkle Tree Hash (MTH) function, which takes as input such a list of serialized transactions, and outputs a single HASH_SIZE byte string called the Merkle root hash, by induction on the list.

This function is defined as follows:

The hash of an empty list is the hash of an empty string:

The hash of a list with one entry (also known as a leaf hash) is:

MTH({d[0]}) = HASH(d[0]).
Figure 2: Merkle Tree Hash of a Single Entry

For n > 1, let k be the largest power of two smaller than n (i.e., k < n <= 2k). The Merkle Tree Hash of an n-element list D_n is then defined recursively as:

MTH(D_n) = HASH(MTH(D[0:k]) || MTH(D[k:n])),
Figure 3: Recursive Merkle Tree Hash

where:

  • || denotes concatenation

  • : denotes concatenation of lists

  • D[k1:k2] = D'_(k2-k1) denotes the list {d'[0] = d[k1], d'[1] = d[k1+1], ..., d'[k2-k1-1] = d[k2-1]} of length (k2 - k1).

2.2. Transaction Components

Each leaf in a CCF ledger carries the following components:

ccf-leaf = [
  ; Byte string of size HASH_SIZE(32)
  internal-transaction-hash: bstr .size 32

  ; Text string of at most 1024 bytes
  internal-evidence: tstr .size (1..1024)

  ; Byte string of size HASH_SIZE(32)
  data-hash: bstr .size 32
]
Figure 4: CCF Leaf CDDL

The ccf-leaf array is the representation of these components in an inclusion proof (Section 3). It is not what is hashed into the tree. The serialized transaction d[i] that is input to MTH in Section 2.1 is the byte string of length 3 * HASH_SIZE obtained by concatenating the internal transaction hash, the hash of the internal evidence, and the data hash:

d[i] = internal-transaction-hash
       || HASH(internal-evidence)
       || data-hash
Figure 5: Transaction Serialization

where HASH(internal-evidence) is the digest of the UTF-8 encoding of the internal-evidence text string. The leaf hash MTH({d[i]}) = HASH(d[i]) is therefore HASH(internal-transaction-hash || HASH(internal-evidence) || data-hash), which is the value computed by the first step of compute_root in Figure 7. Note that the proof carries internal-evidence itself rather than its digest: revealing the evidence is what demonstrates that the transaction was committed (see below).

The internal-transaction-hash and internal-evidence values are internal to the CCF implementation. They can be safely ignored by receipt Verifiers, but they commit the transparency service (TS) to the whole tree contents and may be used for additional, CCF-specific auditing.

internal-transaction-hash is a hash over the complete entry in the [CCF-Ledger-Format], and internal-evidence is a revealable [CCF-Commit-Evidence] value that allows early persistence of ledger entries before distributed consensus can be established. This mechanism is useful to implement high-throughput transparency applications in Trusted Execution Environments (TEEs) that only provide a limited amount of memory, while maintaining high availability afforded by distributed consensus. Using a secure one-way function f to publish an f(x) commitment to an x value that can be revealed at a later time is a common feature of distributed protocols ([COIN-FLIPPING]). The hash function used for internal-transaction-hash is the same hash function H as in the Merkle Tree construction for the selected verifiable data structure algorithm. For CCF_LEDGER_SHA256, this function is SHA256.

data-hash summarizes the application data included in the ledger at this transaction, which is a Signed Statement as defined by [RFC9943]. The hash function used for data-hash is also the same hash function H as in the Merkle Tree construction for the selected verifiable data structure algorithm. For CCF_LEDGER_SHA256, this function is SHA256.

3. CCF Inclusion Proofs

CCF inclusion proofs consist of a list of digests tagged with a single left-or-right bit.

ccf-proof-element = [
  ; Position of the element
  left: bool

  ; Hash of the proof element: byte string of size HASH_SIZE(32)
  hash: bstr .size 32
]

ccf-inclusion-proof = bstr .cbor {
  &(leaf: 1) => ccf-leaf
  &(path: 2) => [+ ccf-proof-element]
}
Figure 6: CCF Inclusion Proof CDDL

Unlike some other tree algorithms, neither the index of the element nor the size of the tree is explicit in the inclusion proof. Read from the leaf up, the left-or-right bits are the binary decomposition of the index for the first k elements, where k is the largest power of two not greater than the tree size; for later elements the proof may have fewer elements than the depth of the index, and the index cannot be read from the proof alone.

3.1. CCF Inclusion Proof Signature

The proof signature for a CCF inclusion proof is a COSE signature (encoded with the COSE_Sign1 CBOR type) which includes the following additional requirements for protected and unprotected headers. These follow the conventions of Section 5.2.1 of [RFC9942]; the corresponding CDDL is given in Section 5. Please note that there may be additional header parameters defined by the application.

The protected header parameters for the CCF inclusion proof signature MUST include the following:

  • vds (label 395): int. This header MUST be set to the verifiable data structure algorithm identifier for CCF_LEDGER_SHA256 (TBD_1).

The unprotected header for a CCF inclusion proof signature MUST include the following:

  • vdp (label 396): map. This header conveys the verifiable data structure proofs, keyed by proof type. It MUST contain the inclusion-proof (-1) key, whose value is an array of one or more ccf-inclusion-proof values as defined above. The proof type is identified solely by this key; no other header parameter is used to convey it.

The payload of the signature is the CCF ledger Merkle root digest, and MUST be detached in order to force verifiers to recompute the root from the inclusion proofs in the unprotected header. This provides a safeguard against implementation errors that use the payload of the signature but do not recompute the root from the inclusion proof. When the array contains more than one inclusion proof, every proof MUST compute to the same root.

3.2. Inclusion Proof Verification Algorithm

CCF uses the following algorithm to verify an inclusion receipt:

compute_root(proof):
  h := HASH(
       proof.leaf.internal-transaction-hash
           || HASH(proof.leaf.internal-evidence)
           || proof.leaf.data-hash
       )

  for [left, hash] in proof.path:
      h := HASH(hash || h) if left
           HASH(h || hash) else
  return h

verify_inclusion_receipt(inclusion_receipt):
  assert(VDP_LABEL in inclusion_receipt.unprotected_header)
  let vdp = inclusion_receipt.unprotected_header[VDP_LABEL]
  assert(INCLUSION_PROOF_LABEL in vdp)
  let proofs = vdp[INCLUSION_PROOF_LABEL]
  assert(len(proofs) > 0)
  assert(inclusion_receipt.payload == nil)

  for proof in proofs:
      # Use the Merkle Root as the detached payload
      let payload = compute_root(proof)
      assert(verify_cose(inclusion_receipt, payload))
  return true
Figure 7: CCF Inclusion Receipt Verification

VDP_LABEL is the vdp header parameter label (396) and INCLUSION_PROOF_LABEL is the inclusion-proof proof type label (-1), both defined by [RFC9942]. Each element of proofs is a ccf-inclusion-proof, i.e., a byte string wrapping a CBOR-encoded map, which is decoded before compute_root is applied.

A description can also be found at [CCF-Receipt-Verification].

4. CCF Consistency Proofs

A CCF consistency proof establishes that the tree of m transactions with root R_m is a prefix of the tree of n transactions with root R_n, where 0 < m < n: every transaction of the older tree is at the same position in the newer tree.

ccf-consistency-proof = bstr .cbor {
  ; Root of the largest complete subtree containing
  ; the last transaction of the older tree
  &(anchor: 1) => bstr .size 32

  ; Siblings from the anchor to the newer root
  &(path: 2) => [+ ccf-proof-element]
}
Figure 8: CCF Consistency Proof CDDL

The anchor MUST be the root of the subtree covering transactions T[m - 2^t], ..., T[m - 1], where 2^t is the largest power of two dividing m; when m is a power of two, the anchor is R_m. The path lists the sibling of each node on the path from the anchor to R_n, tagged as in inclusion proofs. Left siblings belong to both trees and right siblings only to the newer one, so folding the anchor with the left siblings alone yields R_m, and with all siblings R_n. Neither tree size is needed for verification. Apart from the tags, path holds the same digests in the same order as the consistency proof of Section 2.1.4.1 of [RFC9162] for the same sizes, with the anchor as the first element of that proof when m is not a power of two.

4.1. CCF Consistency Proof Signature

The proof signature for a CCF consistency proof is a COSE_Sign1 with the same protected header requirements as an inclusion proof signature (Section 3.1). Its unprotected header MUST include:

  • vdp (label 396): map. It MUST contain the consistency-proof (-2) key, whose value is an array of one or more ccf-consistency-proof values, each relating one older root to the newer root.

The payload is the newer root R_n, and MUST be detached. When the array contains more than one consistency proof, every proof MUST compute to the same newer root. The older root R_m is not carried by the receipt: the verifier already holds it, typically as the root recomputed from an inclusion receipt (Figure 7), and checks that the proof recomputes it.

4.2. Consistency Proof Verification Algorithm

A consistency receipt is verified against a trusted older_root as follows:

compute_roots(proof):
  older := proof.anchor
  newer := proof.anchor

  for [left, hash] in proof.path:
      if left:
          older := HASH(hash || older)
          newer := HASH(hash || newer)
      else:
          newer := HASH(newer || hash)
  return older, newer

verify_consistency_receipt(consistency_receipt, older_root):
  assert(VDP_LABEL in consistency_receipt.unprotected_header)
  let vdp = consistency_receipt.unprotected_header[VDP_LABEL]
  assert(CONSISTENCY_PROOF_LABEL in vdp)
  let proofs = vdp[CONSISTENCY_PROOF_LABEL]
  assert(len(proofs) > 0)
  assert(consistency_receipt.payload == nil)

  let payloads = []
  for proof in proofs:
      let older, newer = compute_roots(proof)
      if older == older_root:
          payloads.append(newer)
  # At least one proof must start from older_root
  assert(len(payloads) > 0)

  for payload in payloads:
      # Use the newer Merkle Root as the detached payload
      assert(verify_cose(consistency_receipt, payload))
  return true
Figure 9: CCF Consistency Receipt Verification

CONSISTENCY_PROOF_LABEL is the consistency-proof proof type label (-2) defined by [RFC9942]. Each element of proofs is a ccf-consistency-proof, i.e., a byte string wrapping a CBOR-encoded map, which is decoded before compute_roots is applied. The comparison with older_root binds the receipt to a state the verifier already trusts: a receipt in which no proof recomputes older_root proves nothing about that state. It also confirms that the anchor is a node of that state, but not that it is the anchor required in Section 4, which cannot be checked without knowing m.

5. Usage in COSE Receipts

A COSE Receipt with CCF inclusion or consistency proofs is described by the following CDDL definition, which follows Section 5.2.1 of [RFC9942] and Section 5.3.1 of [RFC9942]:

protected-header-map = {
  &(alg: 1) => int
  &(vds: 395) => TBD_1
  * cose-label => cose-value
}

TBD_1 = 2 ; Requested assignment for CCF_LEDGER_SHA256
Figure 10: Protected Header Map CDDL

The unprotected header is described by the following CDDL definition:

inclusion-proof = ccf-inclusion-proof
inclusion-proofs = [ + inclusion-proof ]

consistency-proof = ccf-consistency-proof
consistency-proofs = [ + consistency-proof ]

verifiable-proofs = {
  &(inclusion-proof: -1) => inclusion-proofs
  ? &(consistency-proof: -2) => consistency-proofs
  //
  &(consistency-proof: -2) => consistency-proofs
}

unprotected-header-map = {
  &(vdp: 396) => verifiable-proofs
  * cose-label => cose-value
}
Figure 11: Unprotected Header Map CDDL

At least one of inclusion-proof and consistency-proof MUST be present. All proofs in a receipt recompute the same root (the newer root, for consistency proofs), which is the detached payload. The proof type is conveyed by the key of the vdp map, as specified by [RFC9942]; it is not carried in a separate header parameter.

6. Privacy Considerations

See the privacy considerations section of:

7. Security Considerations

The security considerations of [RFC9942] apply.

7.1. Trusted Execution Environments

CCF networks of nodes rely on executing in TEEs to secure their function, in particular:

  1. The evaluation of registration policies

  2. The creation and usage of receipt signing keys

A compromise in the TEE platform used to execute the network may allow an attacker to produce invalid and divergent ledger branches. Clients can mitigate this risk in two ways: by regularly auditing the consistency of the CCF ledger, for instance by verifying consistency receipts (Figure 9) from roots they hold to the current root; and by regularly fetching attestation information about the TEE instances, available in the ledger and from the network itself, and confirming that the nodes composing the network are running up-to-date, trusted platform components.

7.2. Operators

An operator has the ability to start successor networks with a distinct identity. The operator of a CCF network can recover the service by starting a successor network, for example a new CCF network with its own service identity, that endorses the ledger state of the previous instance. This provides service continuity after a catastrophic failure of a majority of the nodes. However, a malicious operator could exploit this mechanism and truncate the ledger’s history by initializing the successor network from an earlier ledger prefix, thereby omitting some later entries. Clients can mitigate this risk by auditing the successor ledger and verifying that their latest known receipts from the prior service are included in the successor’s ledger.

7.3. Consistency Receipts

A consistency receipt relates two roots and establishes nothing about ledger contents on its own. Verifiers MUST compare the recomputed older root with a root they have already verified, not with one supplied alongside the receipt. Consistency receipts also cannot provide guarantees about the correct application of Registration Policies, which requires additional material as outlined in Section 5.1.1.2 of [RFC9943].

8. IANA Considerations

8.1. Additions to Existing Registries

The registries in this section are defined by [RFC9942].

8.1.1. COSE Verifiable Data Structure Algorithms

This document requests IANA to add the following new value to the "COSE Verifiable Data Structure Algorithms" registry:

  • Name: CCF_LEDGER_SHA256

  • Value: TBD_1 (requested assignment 2)

  • Description: Append-only CCF transaction ledgers protected by SHA-256 Merkle Trees and signatures produced via Trusted Execution Environments.

  • Reference: RFCthis

  • Change Controller: IETF

8.1.2. COSE Verifiable Data Structure Proofs

This document requests IANA to add the following new entries to the "COSE Verifiable Data Structure Proofs" registry:

  • Verifiable Data Structure: TBD_1 (requested assignment 2)

  • Name: inclusion proofs

  • Label: -1

  • CBOR Type: array (of bstr)

  • Description: Proof of inclusion

  • Reference: RFCthis, Section 3 and Section 5

  • Change Controller: IETF

and:

  • Verifiable Data Structure: TBD_1 (requested assignment 2)

  • Name: consistency proofs

  • Label: -2

  • CBOR Type: array (of bstr)

  • Description: Proof of consistency

  • Reference: RFCthis, Section 4 and Section 5

  • Change Controller: IETF

9. References

9.1. Normative References

[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>.
[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>.
[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>.
[RFC9943]
Birkholz, H., Delignat-Lavaud, A., Fournet, C., Deshpande, Y., and S. Lasker, "An Architecture for Trustworthy and Transparent Digital Supply Chains", RFC 9943, DOI 10.17487/RFC9943, , <https://www.rfc-editor.org/rfc/rfc9943>.

9.2. Informative References

[CCF]
"Confidential Consortium Framework", n.d., <https://github.com/microsoft/ccf>.
[CCF-Commit-Evidence]
"CCF Commit Evidence", n.d., <https://microsoft.github.io/CCF/main/use_apps/verify_tx.html#commit-evidence>.
[CCF-Ledger-Format]
"CCF Ledger Format", n.d., <https://microsoft.github.io/CCF/main/architecture/ledger.html>.
[CCF-Receipt-Verification]
"CCF Receipt Verification", n.d., <https://microsoft.github.io/CCF/main/use_apps/verify_tx.html#receipt-verification>.
[COIN-FLIPPING]
"Coin Flipping By Telephone - A Protocol For Solving Impossible Problems", DOI 10.1145/1323293.1294280, n.d., <https://dl.acm.org/doi/epdf/10.1145/1008908.1008911>.

Authors' Addresses

Henk Birkholz
Fraunhofer SIT
Rheinstrasse 75
64295 Darmstadt
Germany
Antoine Delignat-Lavaud
Microsoft Research
21 Station Road
Cambridge
CB1 2FB
United Kingdom
Cedric Fournet
Microsoft Research
21 Station Road
Cambridge
CB1 2FB
United Kingdom
Amaury Chamayou
Microsoft Research
21 Station Road
Cambridge
CB1 2FB
United Kingdom