Summary

  • draft-ietf-cose-hpke-27 permits HPKE ciphertext to travel separately from a COSE_Encrypt0 object, whose ciphertext field is then nil. The draft explicitly warns that a later COSE signature or MAC over that object does not cover the detached ciphertext.
  • An AEAD can still protect the detached bytes, but an AEAD tag is not the same receipt as an outer signature, a sender-identity decision, application authorization or a committed external effect.
  • Revision 27 was in IESG AD Evaluation::AD Followup on 1 October 2026. Three independent implementations reportedly validated examples; that does not establish correct detached-object custody in any deployed product.

Security reviews often begin with a picture: a signed object contains an encrypted object, which contains a protected message. The boxes nest, so the assurances appear to nest with them. Detached ciphertext breaks the picture. The COSE control object can remain in one database, the bulk bytes in another object store, and the cryptographic signature can cover the first without covering the second.

That is not an exotic interpretation imposed from outside the standard. Revision 27 of the COSE-HPKE draft says it directly. If COSE_Encrypt or COSE_Encrypt0 uses detached ciphertext, subsequently applied COSE_Sign, COSE_Sign1, COSE_Mac or COSE_Mac0 integrity protection does not cover that ciphertext. Implementers must ensure that the detached ciphertext receives integrity protection of its own.

The sentence is short because the protocol point is precise. Its operational consequences are large because most systems do not present five different green lights. They present one: verified=true.

The empty slot is a transport instruction, not a missing message

RFC 9052 defines the basic COSE structures. Content may be transported separately; when it is detached, the location in the COSE array is nil. For a signed object, the application supplies the detached payload when constructing the signature input. For an encrypted object, the ciphertext can likewise live outside the control structure.

In COSE-HPKE integrated mode, HPKE produces an encapsulated key and ciphertext. The encapsulated key is placed in the ek header parameter. The ciphertext is either placed in the COSE_Encrypt0 ciphertext field or transported separately while that field is nil. The second form is valuable when the content is large, already lives in a blob system or needs a lifecycle different from its metadata.

Key-encryption mode adds another layer. Layer 0 encrypts content under a content-encryption key, or CEK. Layer 1 uses HPKE to encrypt that CEK for each recipient. The content is encrypted once; the relatively small CEK can be wrapped for several recipients. The draft's plain-text form and XML source expose the same architecture.

Detachment therefore is not absence. It is a reference problem. Which bytes did the sender mean? Which object version did the verifier fetch? Did storage normalization, range assembly, compression or content-address translation alter them? If the outer object is signed but the fetch path is mutable, the verifier can possess a valid statement about metadata and the wrong ciphertext at the same time.

Four cryptographic receipts that dashboards collapse into one

The first receipt is the outer signature or MAC. Its evidence is the exact Sig_structure or MAC_structure, the protected-header bytes, any external authenticated data, the payload supplied to the primitive, the key reference and the verification result. It can authenticate only what entered that computation.

The second receipt is ciphertext integrity. In the draft's normal constructions, an authenticated-encryption algorithm protects layer 0. A valid AEAD tag can detect modification of the ciphertext and associated data under the CEK. This is real integrity, and the draft gives it as the obvious way detached ciphertext can meet the requirement. Yet the claim is narrower than “the signer signed these bytes.” It says the ciphertext is valid under a symmetric key and the associated-data context used for decryption.

The third receipt is recipient processing. HPKE establishes or transports keying material for the recipient. The COSE-HPKE Recipient_structure binds the next lower layer's algorithm and protected recipient headers into HPKE's info input. This closes a dangerous ambiguity: the recipient key agreement should not float free of the bulk-encryption algorithm that consumes the resulting key.

The fourth receipt is application authority. A recipient may successfully decrypt authentic ciphertext and still be forbidden to apply the contained configuration, transfer funds, rotate a credential or actuate a device. Cryptographic validity does not answer whether the signer was allowed to request this action now, against this resource, within this limit.

An honest status model therefore cannot be a single boolean:

Receipt Establishes Does not establish
Outer signature/MAC Validity of the exact covered object under a key Integrity of bytes not supplied to the computation
Layer 0 AEAD Ciphertext and associated-data integrity under the CEK Public sender identity or policy authority
HPKE open Recipient-side key processing and decryption success That kid names an authorized principal
Identity policy Binding from keys to an accepted actor Permission for this operation
Authorization Permission under current policy That the effect committed
Effect receipt External state actually changed Cryptographic provenance unless linked back to it

A key identifier is a lookup handle, not a badge

The draft recommends kid to identify the static recipient public key used by the sender. In key-encryption mode, placing kid in protected recipient headers also brings it into the HPKE key schedule through Recipient_structure. That is valuable binding. It still does not turn an arbitrary byte string into institutional identity.

A verifier needs evidence for how kid resolved: tenant, key version, algorithm restrictions, activation and retirement times, issuer, purpose and revocation state. Two services can use the same short identifier in different namespaces. A stale cache can resolve yesterday's key after rotation. A recovery key may decrypt data without carrying production authority. The lookup record is part of the security boundary.

The encapsulated key ek is placed in the unprotected header bucket. “Unprotected” here should not be read as “unchecked.” Tampering normally causes HPKE opening or later authentication to fail. The correct record is the exact received value and the actual cryptographic result, not a policy invented from the visual location of the field.

The same discipline applies to recipient_extra_info. The field lets an embedding protocol bind context known by sender and recipient but not transmitted in the COSE message. It can be powerful: a deployment might bind a tenant, channel, device class or transaction context. It can also fail invisibly if the two sides derive different values or if one side falls back to an empty string. Possession, derivation and versioning of that context need evidence.

Sender authentication remains a separate design choice

RFC 9180 established HPKE's modes and security model. The active successor HPKE draft is work in progress and would obsolete RFC 9180 if approved. The COSE document builds against that moving standards dependency; operators should pin the implemented revision and ciphersuite rather than writing “HPKE” as if it were one timeless behavior.

COSE-HPKE also says Base mode does not authenticate the sender as part of the HPKE KEM. A COSE signature or MAC can add authentication. This is exactly where architectural shorthand becomes dangerous. “Encrypted for Bob and signed by Alice” is true only if the recipient key actually belongs to Bob, the signing key actually belongs to Alice, the signature covers the intended data, and local policy accepts Alice for the requested purpose.

RFC 9338 makes a neighboring distinction for countersignatures: attesting to encrypted data is not the same as attesting to the unencrypted data. If the assurance sought concerns plaintext meaning, the protocol must construct that assurance deliberately. Neither successful decryption nor a countersignature on ciphertext can be upgraded into a semantic approval after the fact.

Deterministic encoding prevents one class of disagreement

Recipient_structure must follow the core deterministic encoding requirements in RFC 8949. This matters because sender and recipient independently build an input that is not transmitted. If equivalent data can serialize differently, sound cryptography can fail because the two parties did not hash the same bytes.

Deterministic encoding solves that serialization problem. It does not solve key ownership, storage mutability, authorization or freshness. This is a recurring governance error: a technical control that removes one ambiguity gets reported as if it removed all ambiguity. The control should receive credit for exactly the layer it governs.

RFC 9053, the IANA COSE registry and the IANA HPKE registry provide algorithm definitions and code points. Registries coordinate names. They do not certify that a product rejected an unknown suite, bound a detached blob, rotated a compromised key or enforced a business limit.

Review progress is not deployment evidence

The Datatracker record identifies revision 27 as a COSE Working Group document intended for Proposed Standard, submitted to the IESG and in AD Evaluation::AD Followup on 1 October 2026. The history records revision 27 on 12 September. It has no RFC number and can still change.

The document shepherd's write-up reports broad discussion. It also says the authors shared at IETF 125 that three independent implementations had been used to validate the examples. That is useful running-code evidence. It is not a claim that three production systems correctly bind every detached blob, preserve audit receipts or implement the same authorization policy.

Publication status, example interoperability and deployment assurance belong in different fields. A procurement form that asks only “supports COSE-HPKE: yes/no” discards the details that determine whether the support is safe.

Build a coverage manifest, not a trust adjective

For every message, retain the exact COSE bytes; message type and tag; protected and unprotected header bytes; algorithm identifiers; kid namespace and resolved key version; embedded-versus-detached state; immutable blob locator; detached ciphertext length and digest; exact signature or MAC input digest; exact AEAD associated-data digest; tag result; HPKE mode and ciphersuite; encapsulated-key digest; Recipient_structure digest; recipient_extra_info provenance; recipient-opening result; plaintext digest inside the trusted boundary; sender-identity result; authorization decision; commit identifier; and observed effect.

Do not log private keys or plaintext merely to make the ledger complete. Digests, versioned key references and decision receipts are enough to establish continuity while respecting confidentiality. Absence should stay visible: outer_signature_covers_detached_ciphertext=false is not an error if AEAD integrity is the designed control, but it must not be silently rewritten as signed=true.

Minimum Initial Specification supports this thin common rule: publish the byte-coverage manifest and leave application-specific authorization local. Running-Code Primacy says to inspect the actual buffers passed into cryptographic APIs, not the nesting shown in a diagram. The Policy Mirror reveals that object-store lookup, key resolution and verifier defaults allocate authority. Reality Layers keeps syntax, bytes, cryptographic proof, identity, permission and effect from becoming one comforting symbol.

Sources