Skip to content

feat: DSSE signing extension#1371

Closed
xibz wants to merge 170 commits into
cloudevents:mainfrom
xibz:verify
Closed

feat: DSSE signing extension#1371
xibz wants to merge 170 commits into
cloudevents:mainfrom
xibz:verify

Conversation

@xibz
Copy link
Copy Markdown

@xibz xibz commented Oct 6, 2025

Proposed Changes

feat: Add DSSE-based cryptographic signing for CloudEvents verification

Implements verifiable CloudEvents using DSSE (Dead Simple Signing Envelope) to ensure event authenticity and integrity across untrusted transport layers.

Key features:

  • Sign CloudEvents with DSSE v1.0.2 protocol using SHA256 digests
  • Transport verification material in 'dssematerial' extension attribute
  • Support for binary, structured, and batch CloudEvent modes
  • Backward compatible - unsigned events still work, consumers can ignore signatures (but highly inadvisable)

Technical approach:

  • Creates SHA256 digest chain of all context attributes and event data
  • Wraps digest in DSSE envelope with Base64 encoding
  • Verifies by recomputing digests and comparing against signed payload
  • Returns only verified data to consumers (strips unverified extensions)

This enables cryptographic proof that events:

  1. Were produced by the claimed source (authenticity)
  2. Were not modified in transit (integrity)

Does NOT address: event ordering, completeness, replay attacks, or confidentiality

Fixes #1302

Release Note


afrittoli and others added 30 commits October 20, 2021 18:50
Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Add a simple workflow based on GitHub super-linter to validate our markdown.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Add a CloudEvents binding document.
The CloudEvents Binding for CDEvents defines how
CDEvents are mapped to CloudEvents headers and body.

Add a primer document.
The primer document gives an overview of the CDEvents
spec, it's architecture, goals and design decisions.

Both documents are only stubs for now.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Import the draft spec as-is from github.com/cdfoundation/sig-events,
except for minor lint and spelling fixes.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Add a tool to generate and manage the TOC of various docs.
We might want to add a CI job that checks that the TOC is
up to date once we start adding specs.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Explain relations to CloudEvents

Signed-off-by: Mattias Linnér <mattias.linner@ericsson.com>
Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Add a roadmap doc for cdevents that include the mission and vision
for the project too.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Include the work on reference implementations, or "adapters" that
will be required to produce / consume cdevents in various platforms.

Relocate the use cases to the primer.md document.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
According to the CloudEvents spec, https://github.com/cloudevents/spec/blob/v1.0.1/spec.md
the event type SHOULD be prefixed with a reverse-DNS name.
The prefix domain dictates the organisation which defines
the semantics of the event type.

Since CDEvents owns the `cdevents.dev` domain, and has a website
hosted on that domain, we should use that domain in the event
types as well.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Restructure a the spec root page, add more sections and introduce
the initial terminology, including subjects and predicates.

This provides the basis for restructuring the vocabulary and
defining the schemas that will be used to build the SDKs.

Partially addresses: cloudevents#12
Related to: cloudevents#11

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Add a link to CONTRIBUTING.md and improve the README.md to include links to
the various documents.

Remove the code of conduct that is now hosted in the .github repo.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Add syntax details for attributes in CDEvents

Expand the definition of mandatory attributes by adding syntax
details and examples.

Introduce the "version" as a mandatory attribute.
Add the subject as an optional attribute as discussed in the WG.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Co-authored-by: Emil Bäckmark <emil.backmark@ericsson.com>
Co-authored-by: Mattias Linnér <mattias.linner@ericsson.com>
Add an introduction to CDEvents explaining what it is and what it provides.

Co-authored-by: Emil Bäckmark <emil.backmark@ericsson.com>
Introduce objects and subjects

Add the type system including objects.
Define subjects in the core protocol stage and rework the spec
accordingly. Add the general cloudevents binding specification.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Format source code events same as core

Reformat the source code version control stage page to match what
done for the core stage. No spec changes.

Part of cloudevents#13

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
* Add service published event to the spec

This is WRT the issue cdfoundation/sig-events#111

add distinction for when to use Deployed vs Published events
* Format the CI and CD stage spec

Format the CI and CD stage spec. Complete the SCM spec with the
available part of the data model.
Fix some linting issues.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Co-authored-by: Emil Bäckmark <emil.backmark@ericsson.com>
* Remove binary mode from CDEvents

As discussed in the WG, we're not going to have a CDEvents binary
mode, so remove it from the specification.

Organise links in the cloudevents binding doc.
Update the TOC.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
* Add support for third party data in the spec

Add a new field. Organise the spec document so that it's clear
which are the three root elements of a CDEvent and how the spec
document itself is structured.

Fixes: cloudevents#60

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Co-authored-by: Emil Bäckmark <emil.backmark@ericsson.com>
* Add jsonschema for a few of the events

Add initial jsonschemas for all of the events.

These json schemas are automatically generated through the
go types defined in the new cdevents/go-sdk, using the
library github.com/invopop/jsonschema, see
cdevents/sdk-go#6 for details.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Add the white paper as PDF to the repo, including a paragraph about it
in the primer.md

Document that decision decisions are included in primer.md, and add a
pull request template to remind PR authors about it

Fixes cloudevents#17

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Add details about the specification versioning, the
event versioning and the schemas. Update the spec version
to 0.1.0-draft as starting in-progress version.

Signed-off-by: Andrea Frittoli <andrea.frittoli@gmail.com>
Co-authored-by: Emil Bäckmark <emil.backmark@ericsson.com>
dependabot Bot and others added 12 commits March 17, 2026 18:15
Bumps [rojopolis/spellcheck-github-actions](https://github.com/rojopolis/spellcheck-github-actions) from 0.59.0 to 0.60.0.
- [Release notes](https://github.com/rojopolis/spellcheck-github-actions/releases)
- [Changelog](https://github.com/rojopolis/spellcheck-github-actions/blob/master/CHANGELOG.md)
- [Commits](rojopolis/spellcheck-github-actions@0.59.0...0.60.0)

---
updated-dependencies:
- dependency-name: rojopolis/spellcheck-github-actions
  dependency-version: 0.60.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
* feat(schema): Add approval events

Introduces three new event types to model the lifecycle of an approval
request in a CI/CD pipeline or workflow.

Many CI/CD systems require a formal decision point before execution can
continue, like a deployment to production, a release cut, or a regulated
change proceeding through a pipeline. The decision may come from a human
approver, an automated policy engine, an external compliance system, or
any other entity capable of authorizing progression. There is currently
no canonical CDEvents representation for this pattern, leaving teams to
model approvals through customData or out of band mechanisms with no
shared traversal or governance model.

**approval.created**: emitted when an approval request is initiated.
Captures the requestor and references to the approval details and the
resource being gated.

**approval.updated**: emitted when a non-terminal decision is recorded
or when the resource target of the approval changes. Supports flows
where multiple decisions may be recorded before a terminal state is
reached, whether from humans, automated systems, or a combination.

**approval.closed**: emitted when the approval reaches a terminal
state.  Captures the final decision, the responder, and the terminal
status: Approved, Rejected, Cancelled, or Expired.

All three events share a common core:
- `approvalDetailsUrl`: reference URL to the approval request page
- `resourceTargetUrl`: reference URL to the resource being gated

approval.created adds:
- `requestor`: the entity that initiated the approval request

approval.updated and approval.closed add:
- `responder`: the entity that recorded a decision (URN format)
- `decision`: the decision selected by the responder

approval.closed additionally adds:
- `status`: example terminal state: approved, rejected, cancelled,
  expired

- Deployment gates requiring sign-off before production rollout
- Automated policy checks that must authorize pipeline progression
- Regulated change management pipelines requiring auditable approval
  records
- Multistage release workflows where approvals gate progression between
  environments
- Compliance workflows requiring documented authorization before
  sensitive operations
- External system integrations where a thirdparty tool must approve
  before execution continues

* fixup! feat(schema): Add approval events

Signed-off-by: xibz <bjp@apple.com>

* fixup! feat(schema): Add approval events

Signed-off-by: xibz <bjp@apple.com>

---------

Signed-off-by: xibz <bjp@apple.com>
Signed-off-by: xibz <bjp@apple.com>
Signed-off-by: xibz <bjp@apple.com>
This commit changes releases to be tag based given the branch is
constantly being stomped over during the resync

Signed-off-by: xibz <bjp@apple.com>
* Bump ajv from 8.18.0 to 8.20.0 in /tools

Bumps [ajv](https://github.com/ajv-validator/ajv) from 8.18.0 to 8.20.0.
- [Release notes](https://github.com/ajv-validator/ajv/releases)
- [Commits](ajv-validator/ajv@v8.18.0...v8.20.0)

---
updated-dependencies:
- dependency-name: ajv
  dependency-version: 8.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* feat(rio): add rio.yml

Signed-off-by: xibz <bjp@apple.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: xibz <bjp@apple.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit changes releases to be tag based given the branch is
constantly being stomped over during the resync

Signed-off-by: xibz <bjp@apple.com>
Comment thread .github/workflows/release-notes.yaml Outdated
end-sha:
description: 'Ending SHA? (latest HEAD)'

permissions:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rebase? These are not part of your PR

attributes alongside the event data.
verify those signatures. The verification material is transported in an
extension attribute alongside the event data.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra line

Comment thread cloudevents/extensions/verifiability.md Outdated
8. recompute `core_digest` from the event according to steps 3-4 of the signing [Protocol](#protocol) and compare it to the `core` byte sequence from step 6.2
1. if the values are not equal, the event has been modified in transit and MUST be discarded
11. the event is returned as verified successfully.
9. if the `ext` field is present (step 6.3) and the consumer wishes to verify extension attributes, recompute `ext_digest` from the event using the `signedextattrs` list from step 6.4 according to step 5 of the signing [Protocol](#protocol) and compare it to the `ext` byte sequence from step 6.3
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap entire doc at 80 chars

Comment thread cloudevents/extensions/verifiability.md Outdated
data. Users handle either a complete unverified event or a verified event with
only verified values—never a mixture of both.
Upon verification of a CloudEvent, implementations MUST return a new event
containing only verified data. If only core verification was performed (step 8),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, if there are CE extensions but they're not verified, they're silently dropped?
Is this what we want instead of rejecting the event? I'm wondering if the presence of extra stuff is sign that someone is trying to mess with things and we need to play it safe.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned about this too. I'd far rather be able to keep everything, but make it easy to determine which extensions were signed and which weren't.

@duglin
Copy link
Copy Markdown
Collaborator

duglin commented May 7, 2026

Some errors from the tooling:

cloudevents/extensions/verifiability.md: cloudevents/extensions/verifiability.md does not contain '#protocol-1' segment
cloudevents/extensions/verifiability.md: line 141: 'required' MUST be capitalized ('REQUIRED')
cloudevents/extensions/verifiability.md: line 141: 'optional' MUST be capitalized ('OPTIONAL')
cloudevents/extensions/verifiability.md: line 281: 'Optional' MUST be capitalized ('OPTIONAL')
cloudevents/extensions/verifiability.md: line 416: 'should' MUST be capitalized ('SHOULD')
cloudevents/extensions/verifiability.md: line 440: 'should' MUST be capitalized ('SHOULD')
cloudevents/extensions/verifiability.md: line 473: 'must' MUST be capitalized ('MUST')
cloudevents/extensions/verifiability.md: line 482: 'must' MUST be capitalized ('MUST')
cloudevents/extensions/verifiability.md: title ('# extension: Verifiable CloudEvents with DSSE') does not match the title of cloudevents/languages/he/extensions/verifiability.md ('# Verifiable CloudEvents with DSSE')
cloudevents/extensions/verifiability.md: title ('# extension: Verifiable CloudEvents with DSSE') does not match the title of cloudevents/languages/zh-CN/extensions/verifiability.md ('# Verifiable CloudEvents with DSSE')

You may want to squash and rebase

@duglin
Copy link
Copy Markdown
Collaborator

duglin commented May 7, 2026

We agreed to try to vote on this next week so:

  • @xibz please try to address all open comments and CI issues
  • everyone else PLEASE review and comment ASAP so @xibz can respond with enough time for folks to review his responses before next week's call

Comment thread cloudevents/extensions/verifiability.md
Comment thread cloudevents/extensions/verifiability.md Outdated
to modify and re-sign events, it explicitly does not aim to provide a
cryptographic audit trail of event modifications.

As a general principle, this extension aims to avoid cryptographic agility in
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what "cryptographic agility" is - is that a term most folks in the relevant audience would understand? (It's entirely possible.)

Comment thread cloudevents/extensions/verifiability.md
Comment thread cloudevents/extensions/verifiability.md Outdated

```
{
"core": "<Base64(core_digest)>",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we capitalize these for consistency with VERIFICATION_MATERIAL, KEY_ID and SIGNATURE? Aside from anything else, that makes it easier to tell that they're not expected to be attribute names.

Comment thread cloudevents/extensions/verifiability.md Outdated
This extension defines the following attribute:

### dssematerial
- Type: `String`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me why this is a string and specified as base64. If the intention is to transport arbitrary bytes, I think it should be of type Binary, which would happen to be base64-encoded in the JSON CloudEvents format, but not necessarily in other formats.

I think it would be better to make it Binary, and explicitly state that logically it's a string, but binary is used to avoid the restrictions on the String type. Alternatively, would it be reasonable to put the JSON on a single line and have it as a string without any additional encoding? I suspect the only character disallowed in https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#type-system that causes a problem at the moment is for the line break.

Comment thread cloudevents/extensions/verifiability.md Outdated
5. if the list from step 2 is not empty:
1. create an empty byte sequence for the ext digest
2. for each extension attribute in the list from step 2 (in the given order, from lowest to highest index)
1. compute the SHA256 digest of the extension attribute's value and append it to the byte sequence. Non-string attribute values MUST be serialized to their [CloudEvents string representation](https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#type-system) before hashing. *(if the attribute is not present on the event, use the digest of the empty byte sequence)*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need more clarity here. Effectively I'd suggest a whole separate section about how each type is hashed, and then use that for both the core and extension attributes.

(I'd also suggest that when hashing a binary value, there's no point in converting it to base64 first - likewise we could use simple encodings for Integer and Boolean values.)

Comment thread cloudevents/extensions/verifiability.md Outdated
2. if verification fails, the event MUST be discarded
8. recompute `core_digest` from the event according to steps 3-4 of the signing [Protocol](#protocol) and compare it to the `core` byte sequence from step 6.2
1. if the values are not equal, the event has been modified in transit and MUST be discarded
9. if the `ext` field is present (step 6.3) and the consumer wishes to verify extension attributes, recompute `ext_digest` from the event using the `signedextattrs` list from step 6.4 according to step 5 of the signing [Protocol](#protocol) and compare it to the `ext` byte sequence from step 6.3
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where I think we need to be clear that the verifier must know the types of the extension attributes in order to verify them. The overall result of verification could be:

  • Verified both core and extensions
  • Verified core, there were no signed extensions
  • Verified code, can't verify extensions due to at least one extension attribute's type not being known
  • Verification failed (one of the signatures doesn't match)

Comment thread cloudevents/extensions/verifiability.md Outdated
data. Users handle either a complete unverified event or a verified event with
only verified values—never a mixture of both.
Upon verification of a CloudEvent, implementations MUST return a new event
containing only verified data. If only core verification was performed (step 8),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm concerned about this too. I'd far rather be able to keep everything, but make it easy to determine which extensions were signed and which weren't.

* *In [CloudEvent's official Protocol Buffers format](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/formats/cloudevents.proto#L57), the `time` Context Attribute is encoded as a `google.protobuf.Timestamp` and hence does not include time zone information (which RFC 3339 would allow). For interoperability with CloudEvent setups using the Protocol Buffers format, time zone information is ignored in the signing and verification process.*
* *The signature covers the exact byte representation of the event data. Intermediaries that deserialize and reserialize event data (e.g., reformatting JSON whitespace or reordering keys) will invalidate the signature. Implementations that route signed events MUST preserve the original byte sequence of the event data.*

## Verification Walkthrough
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've stopped reviewing at this point as I suspect changes above will affect the text below.

Comment thread cloudevents/extensions/verifiability.md Outdated
This is how to sign a CloudEvent using DSSE:

1. choose a signing key
2. choose the list of "signed extension attributes" (the list MUST adhere to the constraints defined in the [`dssematerial`](#dssematerial) attribute definition: no repetitions, no Context Attribute names, and no `dssematerial`)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note either here or elsewhere that extension attributes with a known value but not a known type (which could happen in some SDK implementations if this is an event which has been received and is being propagated) cannot be signed.

@xibz
Copy link
Copy Markdown
Author

xibz commented May 14, 2026

@duglin @jskeet - I forgot an important commit T-T. i was so confused why you kept mentioning types because I was like "I thought that was in the proposal". Turns out I forgot to push that commit. Sorry about that :(

Comment thread cloudevents/extensions/verifiability.md Outdated
- Attribute names MUST NOT contain repetitions
- Attribute names MUST NOT include any [core context attribute](https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#context-attributes) name (`id`, `source`, `specversion`, `type`, `datacontenttype`, `dataschema`, `subject`, `time`)
- Attribute names MUST NOT include `dssematerial` (the verification material attribute)
- Extension attribute values MUST be of a supported CloudEvents type (`Boolean`, `Integer`, `String`, `Binary`, `URI`, `URI-reference`, or `Timestamp`)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what this means - there's no concept of an attribute which exists but isn't of a supported type.

If this is intended to mean that the signer knows the type of the attribute, that's slightly different. I think I'd probably talk about that separate from this bullet list though.

Comment thread cloudevents/extensions/verifiability.md Outdated
normalized to UTC ("Zulu") time with second precision.
- `CEString(v)` is the CloudEvents canonical string encoding of an extension
attribute value, as defined in step 5.2.1 of the [Signing Protocol](#signing-protocol)
(`Boolean`, `Integer`, `String`, `Binary`, `URI`, `URI-reference`, `Timestamp`).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what this list is intended for, but I think we can just remove the line (and move the period to the line above)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mostly cause security typically wants things to be VERY clear. No way to interpret anything.

Comment thread cloudevents/extensions/verifiability.md Outdated
- `CEString(v)` is the CloudEvents canonical string encoding of an extension
attribute value, as defined in step 5.2.1 of the [Signing Protocol](#signing-protocol)
(`Boolean`, `Integer`, `String`, `Binary`, `URI`, `URI-reference`, `Timestamp`).
- `event.data` is hashed as its raw byte representation — no UTF-8 wrapping
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... this could be tricky. I've been focused on the representations of the attributes, but data is a whole other tricky story. What is the "raw byte representation" of an in-memory CloudEvent which has a JSON object as its data? If this is intended to be "the bytes that appear in the serialized form" then that gets us back to implementation difficulties.

(Historically, attributes have been relatively straightforward - we've had many, many discussions about the nature of data, particularly for event formats which can represents certain data types in a different way, e.g. where the data for a JSON-formatted event is itself a JSON object...)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only applies to event.data, not to context attributes. Context attributes use semantic canonicalization (Option 3 from our email thread), which you described as having “really significant benefits: Signature doesn’t change based on format, Much simpler implementation than either 1 or 2”, and that’s what the spec implements for all typed attributes (UTF8(s) for strings, RFC3339(UTC(t)) for timestamps, etc.).

For event.data, raw bytes are unavoidable: there is no CE type system for event data, so we cannot apply semantic canonicalization. The note at lines 327–328 calls this out explicitly, any intermediary that reserializes event data (whitespace changes, key reordering) must re-sign. This is the same trade-off you acknowledged in the email when discussing Option 2: “That’s ‘only’ inefficient rather than particularly difficult, but it does mean that support for this extension needs to be built into every event formatter.” We avoided that for attributes via Option 3; for data there is no equivalent.

Comment thread cloudevents/extensions/verifiability.md Outdated
Comment thread cloudevents/extensions/verifiability.md Outdated
- `Binary`: Base64 encoding per RFC 4648
- `URI`: the absolute URI string per RFC 3986 Section 4.3
- `URI-reference`: the URI-reference string per RFC 3986 Section 4.1
- `Timestamp`: RFC 3339 Zulu format with second precision (normalized to UTC as described in step 8)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I'd separate out "here's how you hash a value" (on a per type basis) from the uses of it. That way you don't need any repetition, and key implementation details (e.g. how to hash a timestamp value) aren't buried in the list.

Comment thread cloudevents/extensions/verifiability.md Outdated
1. compute the SHA256 digest of the extension attribute's value and append it to the byte sequence. Extension attribute values MUST be serialized using their CloudEvents canonical string encoding before hashing:
- `Boolean`: "true" or "false" (case-sensitive)
- `Integer`: decimal representation without leading zeros, fraction, or exponent (per RFC 7159 Section 6)
- `String`: the string value as-is
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A string value can't be hashed, as hashing is an operation on binary data. I suspect we want to say you hash the UTF-8-encoded form of the string.

Comment thread cloudevents/extensions/verifiability.md Outdated

*Notes:*

* *In [CloudEvent's type system](https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#type-system) a `Timestamp`'s string encoding is [RFC 3339](https://tools.ietf.org/html/rfc3339). This means that verification of the `time` Context Attribute can only be done with second precision, even though an SDK might allow passing in a timestamp with nanosecond precision.*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not RFC3339 which limits us to second precision - it's interoperability. We don't know what degree of precision any given consumer will use, so it's simplest to truncate to the second.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call out. I remember there's a timestamp RFC that is specifically second precisions, but I may be misremembering or blurring RFCs.

But good callout

Comment thread cloudevents/extensions/verifiability.md Outdated

* *In [CloudEvent's type system](https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#type-system) a `Timestamp`'s string encoding is [RFC 3339](https://tools.ietf.org/html/rfc3339). This means that verification of the `time` Context Attribute can only be done with second precision, even though an SDK might allow passing in a timestamp with nanosecond precision.*
* *In [CloudEvent's official Protocol Buffers format](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/formats/cloudevents.proto#L57), the `time` Context Attribute is encoded as a `google.protobuf.Timestamp` and hence does not include time zone information (which RFC 3339 would allow). For interoperability with CloudEvent setups using the Protocol Buffers format, time zone information is ignored in the signing and verification process.*
* *The signature covers the exact byte representation of the event data. Intermediaries that deserialize and reserialize event data (e.g., reformatting JSON whitespace or reordering keys) will invalidate the signature. Deployments requiring end-to-end verifiability SHOULD avoid such intermediaries or accept re-signing at trust boundaries.*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comes back to what "the exact byte representation of the event data" is, and how tricky that could be for implementations...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same answer as above

This applies only to event.data, not to context attributes. For context attributes the spec uses semantic canonicalization (Option 3 from our March 11 discussion), which you preferred precisely because it avoids the wire-bytes implementation pain you described: “often the bytes that were on the wire aren’t known by the time you’re dealing with the parsed representation... the JSON event formatter reads the whole body as a document. At that point, the original bytes on the wire have gone.”

For event.data we cannot apply the same trick, there is no CE type for data. Signing requires stable bytes, and the spec documents the consequence clearly (lines 327–328): intermediaries that reserialize data must resign. SDK authors should sign as close to the wire as possible. Happy to add a note in the spec making this explicit for SDK implementors.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right - sorry for missing this before. I think in many cases this may make implementation close to infeasible, unfortunately.

For example in https://github.com/cloudevents/sdk-csharp/blob/main/src/CloudNative.CloudEvents.SystemTextJson/JsonEventFormatter.cs#L138 (and similar event formatters) the first thing that's done is to read the event as a JSON document. At that point, we simply don't have the original bytes. So to read the wire data, we'd probably have to maintain our own very low-level JSON parser in order to get the raw bytes.

I hate to be a downer, but I think for at least a lot of languages, this will simply break any layered architecture of the SDK. It may be feasible in some other languages of course.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For event.data I want to acknowledge you raised this in our March email too, and at the time you said "That feels somewhat reasonable as a spec, but a real pain to implement in an SDK." We proceeded on that basis, and the spec documents the consequence clearly. So I dont think it was missed, per se.

On the C# SDK example: the formatter reading the whole JSON document before the raw bytes are available is a real constraint, but the solution is to capture the bytes during parsing rather than after. System.Text.Json exposes raw UTF-8 bytes for a value node via GetRawText() or an equivalent reader-level API, no custom low-level parser needed. The SDK would need to thread this through the signing path, which is additional work, but it's the same class of work any checksum/integrity system requires.

The alternative, defining a canonical serialization for event.data, would require specifying rules for every possible content type (JSON, XML, binary, protobuf, etc.), which is far out of scope. Raw bytes is the only content-type-agnostic option.

Producers typically sign before serialization where raw bytes are always available. For SDKs that receive-and-propagate, capturing bytes at the formatter layer is the right integration point. We can capture that in the spec if needed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but the solution is to capture the bytes during parsing rather than after. System.Text.Json exposes raw UTF-8 bytes for a value node via GetRawText() or an equivalent reader-level API, no custom low-level parser needed.

I may have chosen a bad example - it's possible that we can do this for System.Text.Json without very many changes. I think I'd need to check it really carefully for every format - for example, in protobuf it may be feasible due to parsing effectively preserving everything we need.

At the very least, I think it'll mean work on a "per language, per format" basis, and in a way which makes the implementation significantly more complex. That may well be unavoidable, and I acknowledge that there's probably no better approach - but with our current state of SDKs being "it's hard to find maintainers at all" that may well mean we end up with an extension that is very spottily implemented :(

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As someone who used to maintain SDKs, I understand the pain :(.

I will have to check with legal, but we may be able to help with some implementations, e.g., golang, java, python.

C# is a little out of skillset though ><. At this point it seems like figuring out the best path to get this implemented.

I will have a commit that updates the other points, e.g., the duplicated canonicalization stuff, type lists, etc, soon

Comment thread cloudevents/extensions/verifiability.md Outdated
called `dssematerial` (see [Attributes](#attributes) section below).

The `dssematerial` binary value MUST be the UTF-8 encoding of a proper DSSE
envelope:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DSSE JSON envelope (like below)

Comment thread cloudevents/extensions/verifiability.md Outdated
5. compute the SHA256 digest of the event's [`datacontenttype`](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#datacontenttype) Context Attribute in UTF8 and append it to the byte sequence *(if the attribute is not set, use the digest of the empty byte sequence)*
6. compute the SHA256 digest of the event's [`dataschema`](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#dataschema) Context Attribute in UTF8 and append it to the byte sequence *(if the attribute is not set, use the digest of the empty byte sequence)*
7. compute the SHA256 digest of the event's [`subject`](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#subject) Context Attribute in UTF8 and append it to the byte sequence *(if the attribute is not set, use the digest of the empty byte sequence)*
8. compute the SHA256 digest of the event's [`time`](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#time) Context Attribute normalized to RFC 3339 Zulu format with second precision (no subsecond component) and append it to the byte sequence (if the attribute is not set, use the digest of the empty byte sequence)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Zulu" is mentioned once in RFC3339, but it will be helpful to show a formatted datetime example of what you mean here and also link to the RFC. That's a good potential source of hours of debugging fun if one misses this detail.

Comment thread cloudevents/extensions/verifiability.md Outdated
*Notes:*

* *In [CloudEvent's type system](https://github.com/cloudevents/spec/blob/main/cloudevents/spec.md#type-system) a `Timestamp`'s string encoding is [RFC 3339](https://tools.ietf.org/html/rfc3339). This means that verification of the `time` Context Attribute can only be done with second precision, even though an SDK might allow passing in a timestamp with nanosecond precision.*
* *In [CloudEvent's official Protocol Buffers format](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/formats/cloudevents.proto#L57), the `time` Context Attribute is encoded as a `google.protobuf.Timestamp` and hence does not include time zone information (which RFC 3339 would allow). For interoperability with CloudEvent setups using the Protocol Buffers format, time zone information is ignored in the signing and verification process.*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should assume that all timezone-less datetime values are UTC.

Copy link
Copy Markdown
Contributor

@clemensv clemensv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am in favor of merging this once the remaining raised issues are addressed, which all seem editorial. The mechanism is clearly explained.

Introduces the verifiability extension, enabling producers to
cryptographically sign CloudEvents and consumers to verify authenticity
and integrity using DSSE (Dead Simple Signing Envelope) v1.0.2.

Key design points:
- Canonical value serialization per CloudEvents type for format-neutral
  signatures (no wire-bytes requirement for context attributes)
- Optional signing of extension attributes via signedextattrs
- Consumer behavior configuration matrix (strict/passthrough/core-only)
- Proxy guidance: SHOULD re-sign when modifying signed fields
- SDK phased rollout: JSON format first, additional formats by vote
- Timestamp normalization to RFC 3339 Zulu with second precision

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

Adding zero trust to cloudevents