Skip to content

Added support for Cryptex (RFC 9335)#3357

Open
sirzooro wants to merge 1 commit intopion:mainfrom
sirzooro:webrtc_cryptex
Open

Added support for Cryptex (RFC 9335)#3357
sirzooro wants to merge 1 commit intopion:mainfrom
sirzooro:webrtc_cryptex

Conversation

@sirzooro
Copy link
Copy Markdown
Contributor

This is final part of Cryptex (RFC 9335) implementation for pion/webrtc. Public API is based on WebRTC Extensions specification (W3C Editor's Draft from 19 December 2025).

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 12, 2026

Codecov Report

❌ Patch coverage is 94.90741% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.71%. Comparing base (aa3b95c) to head (d7eb692).

Files with missing lines Patch % Lines
peerconnection.go 95.23% 7 Missing ⚠️
rtpheaderencryptionpolicy.go 92.85% 1 Missing and 1 partial ⚠️
rtptransceiver.go 80.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3357      +/-   ##
==========================================
+ Coverage   85.46%   85.71%   +0.24%     
==========================================
  Files          81       82       +1     
  Lines        9738     9933     +195     
==========================================
+ Hits         8323     8514     +191     
  Misses        998      998              
- Partials      417      421       +4     
Flag Coverage Δ
go 85.71% <94.90%> (+0.24%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements support for RFC 9335 Cryptex (RTP Header Extension Encryption) in the pion/webrtc library, based on the W3C WebRTC Extensions specification. The implementation adds negotiation capabilities for encrypting RTP header extensions, with configurable policies that allow applications to require, negotiate, or disable this security feature.

Changes:

  • Added RTPHeaderEncryptionPolicy enum with Negotiate and Require options for controlling encryption behavior
  • Added CryptexMode enum and integrated Cryptex negotiation into SDP offer/answer exchange
  • Extended Configuration, SettingEngine, and DTLSParameters to support Cryptex settings

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
rtpheaderencryptionpolicy.go New enum defining RTP header encryption policies with JSON marshaling support
rtpheaderencryptionpolicy_test.go Unit tests for RTPHeaderEncryptionPolicy enum
cryptexmode.go New enum for internal Cryptex mode state (Disabled/Enabled/Required)
configuration.go Added RTPHeaderEncryptionPolicy field to Configuration struct
settingengine.go Added DisableRTPHeaderEncryption method and field
settingengine_js.go Added DisableRTPHeaderEncryption method for JS/WASM build
peerconnection.go Integrated Cryptex negotiation into SDP generation and remote description processing
peerconnection_js.go Minor formatting change
peerconnection_test.go Added comment noting RTPHeaderEncryptionPolicy is not tested for WASM
sdp.go Added cryptex attribute handling in SDP media sections
sdp_test.go Added test for cryptex attribute in generated SDP
rtptransceiver.go Added RtpHeaderEncryptionNegotiated method to query encryption status
dtlsparameters.go Added CryptexMode field to DTLSParameters
dtlstransport.go Integrated Cryptex mode into SRTP session configuration
cryptex_negotiation_test.go Comprehensive integration tests for Cryptex negotiation scenarios
errors.go Added error constants for Cryptex-related failures

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread peerconnection.go Outdated
Comment thread rtptransceiver.go Outdated
Comment thread rtpheaderencryptionpolicy_test.go Outdated
Comment thread rtpheaderencryptionpolicy_test.go Outdated
Comment thread cryptex_negotiation_test.go Outdated
Comment thread settingengine.go Outdated
Comment thread rtpheaderencryptionpolicy.go Outdated
Comment thread cryptex_negotiation_test.go Outdated
Comment thread cryptex_negotiation_test.go Outdated
Comment thread settingengine_js.go Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rtpheaderencryptionpolicy_test.go Outdated
Comment thread rtpheaderencryptionpolicy_test.go Outdated
Comment thread rtpheaderencryptionpolicy_test.go Outdated
Comment thread rtpheaderencryptionpolicy.go Outdated
Comment thread rtpheaderencryptionpolicy.go Outdated
Comment thread dtlstransport.go Outdated
Comment thread peerconnection.go Outdated
Comment thread peerconnection.go Outdated
Comment thread rtptransceiver.go Outdated
@JoTurk
Copy link
Copy Markdown
Member

JoTurk commented Jan 13, 2026

I'm going to review this soon, I think we should try to avoid adding new files to pion/webrtc, i think all the new files can be moved to already existing files :)

@sirzooro sirzooro force-pushed the webrtc_cryptex branch 5 times, most recently from 7f850a1 to aa23775 Compare January 18, 2026 09:30
@sirzooro
Copy link
Copy Markdown
Contributor Author

I'm going to review this soon, I think we should try to avoid adding new files to pion/webrtc, i think all the new files can be moved to already existing files :)

I have moved contents of these files elsewhere and removed them.

@sirzooro sirzooro requested a review from boks1971 January 18, 2026 09:42
@sirzooro sirzooro requested a review from at-wat January 22, 2026 06:19
@sirzooro sirzooro force-pushed the webrtc_cryptex branch 2 times, most recently from 2f8e5d6 to 9b1aba5 Compare January 23, 2026 08:36
@sirzooro sirzooro force-pushed the webrtc_cryptex branch 2 times, most recently from 211a2c8 to 2cb8551 Compare February 1, 2026 18:00
Copy link
Copy Markdown
Member

@JoTurk JoTurk left a comment

Choose a reason for hiding this comment

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

sorry this took time, it's mostly fine, it would be cooler if we could clean the negations / knob area so we don't carry multiple states and types, the mapping between srtpCryptexMode and RTPHeaderEncryptionPolicy could get cleaner, but it's not a blocker IMO.

it would be nice to add a clear test to guarantee that "negotiate" only turns on when actually negotiated.

I'll continue with the test part of the review tomorrow.

thank you. Sorry this took a while.

Comment thread peerconnection_js.go Outdated
Comment thread peerconnection_test.go Outdated
Comment thread rtpheaderencryptionpolicy.go Outdated
Comment thread dtlsparameters.go Outdated
Comment thread errors.go Outdated
@JoTurk
Copy link
Copy Markdown
Member

JoTurk commented Feb 2, 2026

@sirzooro I just noticed now, why we add and test for a=cryptex in applications media sections?

@sirzooro
Copy link
Copy Markdown
Contributor Author

sirzooro commented Feb 2, 2026

@sirzooro I just noticed now, why we add and test for a=cryptex in applications media sections?

a=cryptex has to be added to all sections which are part of BUNDLE, and application section is in one bundle with video and audio sections. Beside this there is no reason to add it there.

@JoTurk
Copy link
Copy Markdown
Member

JoTurk commented Feb 2, 2026

@sirzooro this doesn't sound right. per spec this rule only apply to rtp sections not applications.

If BUNDLE is in use as per [RFC9143] and the "a=cryptex" attribute is present for a media line, it MUST be present for all RTP-based "m=" sections belonging to the same bundle group. This ensures that the encrypted Media Identifier (MID) header extensions can be processed, allowing RTP streams to be associated with the correct "m=" section in each BUNDLE group as specified in Section 9.2 of [RFC9143]. When used with BUNDLE, this attribute is assigned to the TRANSPORT category [RFC8859].

https://www.rfc-editor.org/rfc/rfc9335.html#section-4

Maybe i missed other spec?

@sirzooro
Copy link
Copy Markdown
Contributor Author

sirzooro commented Feb 2, 2026

@sirzooro this doesn't sound right. per spec this rule only apply to rtp sections not applications.

If BUNDLE is in use as per [RFC9143] and the "a=cryptex" attribute is present for a media line, it MUST be present for all RTP-based "m=" sections belonging to the same bundle group. This ensures that the encrypted Media Identifier (MID) header extensions can be processed, allowing RTP streams to be associated with the correct "m=" section in each BUNDLE group as specified in Section 9.2 of [RFC9143]. When used with BUNDLE, this attribute is assigned to the TRANSPORT category [RFC8859].

https://www.rfc-editor.org/rfc/rfc9335.html#section-4

Maybe i missed other spec?

You are right, I interpreted this incorrectly. I will fix this.

@sirzooro
Copy link
Copy Markdown
Contributor Author

sirzooro commented Feb 2, 2026

You are right, I interpreted this incorrectly. I will fix this.

Fixed, a=cryptex is no longer added to application section.

@sirzooro sirzooro force-pushed the webrtc_cryptex branch 3 times, most recently from 8d50397 to 068594c Compare February 3, 2026 06:01
@JoTurk
Copy link
Copy Markdown
Member

JoTurk commented Feb 5, 2026

im sry i found few issues, i'll double check tomorrow morning before i comment.

@fippo
Copy link
Copy Markdown
Contributor

fippo commented Feb 10, 2026

w3c/webrtc-extensions#47 (comment) -- currently pushing for some simplification of the API. I don't see why anyone would want the extra complication of not requiring cryptex but dealing with the result at a transceiver level.

Copy link
Copy Markdown
Member

@JoTurk JoTurk left a comment

Choose a reason for hiding this comment

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

sorry for being late, more comments.

Comment thread peerconnection.go
Comment thread peerconnection.go
Comment on lines +1389 to +1408
func cryptexNegotiatedInSDP(desc *sdp.SessionDescription) (negotiatedForAnyMedia, negotiatedForAllMedia bool) {
negotiatedForAnyMedia = false
negotiatedForAllMedia = true
haveMedia := false

if _, hasCryptex := desc.Attribute(sdp.AttrKeyCryptex); hasCryptex {
return true, true
}

for _, media := range desc.MediaDescriptions {
if media.MediaName.Port.Value == 0 || media.MediaName.Media == mediaSectionApplication {
continue
}

haveMedia = true
hasCryptex := isCryptexSet(media)
negotiatedForAnyMedia = negotiatedForAnyMedia || hasCryptex
negotiatedForAllMedia = negotiatedForAllMedia && hasCryptex
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we're going with always negotiating at session-level, if yes, can we consider enforcing/normalizing to session-level only, and simplifying this helper?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I changed this to make session-level preferred. Media-level may be needed as a fallback, so I kept support for it and updated code to respond with attribute at proper level (session or media).

Comment thread peerconnection.go
Comment on lines 3022 to 3120
@@ -2955,6 +3049,11 @@ func (pc *PeerConnection) generateMatchedSDP(
isExtmapAllowMixed := isExtMapAllowMixedSet(remoteDescription.parsed)
localTransceivers := append([]*RTPTransceiver{}, transceivers...)

cryptex := false
if pc.configuration.RTPHeaderEncryptionPolicy != RTPHeaderEncryptionPolicyDisable {
cryptex, _ = cryptexNegotiatedInSDP(remoteDescription.parsed)
}

detectedPlanB := descriptionIsPlanB(remoteDescription, pc.log)
if pc.configuration.SDPSemantics != SDPSemanticsUnifiedPlan {
detectedPlanB = descriptionPossiblyPlanB(remoteDescription)
@@ -2969,7 +3068,10 @@ func (pc *PeerConnection) generateMatchedSDP(
}

if media.MediaName.Media == mediaSectionApplication {
mediaSections = append(mediaSections, mediaSection{id: midValue, data: true})
mediaSections = append(mediaSections, mediaSection{
id: midValue,
data: true,
})
alreadyHaveApplicationMediaSection = true

continue
@@ -3010,7 +3112,11 @@ func (pc *PeerConnection) generateMatchedSDP(
}
mediaTransceivers = append(mediaTransceivers, transceiver)
}
mediaSections = append(mediaSections, mediaSection{id: midValue, transceivers: mediaTransceivers})
mediaSections = append(mediaSections, mediaSection{
id: midValue,
transceivers: mediaTransceivers,
cryptex: cryptex,
})
case sdpSemantics == SDPSemanticsUnifiedPlan || sdpSemantics == SDPSemanticsUnifiedPlanWithFallback:
Copy link
Copy Markdown
Member

@JoTurk JoTurk Feb 23, 2026

Choose a reason for hiding this comment

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

Does this mean we will never offer cryptex again on later renegotiations if the first negotiation doesn't echo cryptex?
It's fine if we don't support cryptex for renegotiations, but i feel this should be explicit somewhere.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I fixed this. Unfortunately now it is not possible to update SRTP context on the fly or recreate it, so renegotiation actually cannot change cryptex mode. I also added extra check for this.

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.

Interesting point! The RFC only says

Generating subsequent SDP offers and answers MUST use the same procedures for including the "a=cryptex" attribute as the ones on the initial offer and answer.

but this is generation, not handling of subsequent answers. The problem is if you do this:

O: cryptex
A: no
(create SRTP sessions)
O: cryptex
A: cryptex
(create more SRTP sessions)

you end up with a weird mix of cryptex and non-cryptex. Technically this is valid and distinguishable and... you are asking for trouble. And the require policy should prevent this.

Copy link
Copy Markdown
Contributor Author

@sirzooro sirzooro Mar 1, 2026

Choose a reason for hiding this comment

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

When cryptex attribute is at session level, after renegotiation like in this example old SRTP sessions should start using cryptex too. I am not sure why someone would want to do this. Plus this is a potential attack vector, to trick peer to downgrade its security a bit for existing sessions by disabling header encryption (require policy prevents this). So even for negotiate policy it makes sense to prevent renegotiation of cryptex.

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.

Good point - @juberti since I see anything in the RFC is it worth pointing it out as an errata?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not sure if I understood the whole issue, but note that cryptex attribute only indicate the capability to receive and process cryptex packets. Sending side behavior is not controlled by SDP signaling but by app APIs (negotiate, require).

It could be possible for a forwarding proxy/gateway to renegotiate the SDP to modify the upstream destination server, and change the cryptex flag. It makes not sense for a terminating RTP endpoint to modify the cryptex flag between negotiation.

Also note that cryptex may not be bidirectional, if offerer (A) doesn't add the cryptex attribute in the SDP offer and answerer (B) doesn't add the attribute in the answer SDP, B could still send cryptex packets to A.

Even further, even if both A and B support cryptex in the SDP O/A, they could still send each other plain rtp packet.

It is up to local policy (i.e. APIs) to decide what to do when the other side does not support receiving cryptex or what to do with non-cryptex rtp packet is received.

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.

@murillo128 thank you, I missed this is declarative (or did not want to see?)

The W3C API controls negotiation but does that mean it is also missing a way to control and determine whether encryption is used? I would strongly prefer not to because this will be a headache.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would neither like an API for wether encryption is used on the sender side if the received supports it. I think sender should always use it in that case.

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.

@murillo128 made a great point in https://mailarchive.ietf.org/arch/msg/avt/pGnete_6IqExxTERNl44_9GqVRU/ which probably requires W3C API changes.

The most pragmatic thing here is to ship cryptex with the two policy options disabled and negotiate for the time being and add "require" with a new definition of "mandatory to negotiate and use" later which shouldn't require many changes to the PR.

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.

are these the right definitions?

  • disabled: don't offer a=cryptex, ignore incoming cryptex, and don't send cryptex even if the remote side supports it in SDP
  • negotiate: offer a=cryptex, handle incoming cryptex (or not), and send cryptex if the remote side supports it in SDP (as usual)
  • require: offer a=cryptex, fail on incoming non-cryptex, and fail if the remote side doesn't support cryptex in SDP

Comment thread peerconnection.go Outdated
Comment thread sdp.go Outdated
@sirzooro sirzooro force-pushed the webrtc_cryptex branch 2 times, most recently from 4e0a8ad to 4824643 Compare February 28, 2026 21:31
This is final part of Cryptex (RFC 9335) implementation for pion/webrtc.
Public API is based on WebRTC Extensions specification (W3C Editor's
Draft from 19 December 2025).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

8 participants