{"id":423,"date":"2024-02-21T19:55:49","date_gmt":"2024-02-21T19:55:49","guid":{"rendered":"https:\/\/blog.mozilla.org\/webrtc\/?p=423"},"modified":"2024-05-16T18:18:14","modified_gmt":"2024-05-16T18:18:14","slug":"end-to-end-encrypt-webrtc-in-all-browsers","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/","title":{"rendered":"End-to-end-encrypt WebRTC in all browsers!"},"content":{"rendered":"<p>Hi all, we haven&#8217;t blogged since the WebRTC API was standardized back in <a href=\"https:\/\/www.w3.org\/standards\/history\/webrtc\/\">January of 2021<\/a>! But now in 2024 folks are asking us what&#8217;s up with all the new APIs? \u2014 We&#8217;re coming off an exciting second period of innovation and experimentation in WebRTC, so we&#8217;re brushing off the old blogging muscles to cover what&#8217;s new, in what&#8217;s promising to be a new series of posts.<\/p>\n<p>First, don&#8217;t worry. WebRTC 1.0 has been tremendously successful and has not fundamentally changed. Instead, its success has led to developers wanting to use it for a variety of things. This demand has driven continued collaboration in the W3C\u00a0 to standardize new functionality on top of the 1.0 baseline. Each new API exposes exciting new functionality, and each one deserves its own blog post, so let&#8217;s start right away with one of them:<\/p>\n<p>Today&#8217;s topic is <strong>End-to-End-Encryption (E2EE)<\/strong> in WebRTC.<\/p>\n<p>First, we should note that WebRTC is <a href=\"https:\/\/www.w3.org\/TR\/webrtc\/#dom-rtcdtlstransport\">already peer-to-peer encrypted by DTLS<\/a>, so there&#8217;s no need to use additional APIs except to protect data against any middle-boxes your service might employ as WebRTC end-points. That said&#8230;<\/p>\n<p><strong>Did you know all major browsers have an API to encrypt WebRTC calls end-to-end?<\/strong> They do! The caveat is the API shape differs slightly between browsers right now. But don&#8217;t despair! We&#8217;re here to cover that gap. If you&#8217;ve been with us for a while you know we&#8217;ve been here before, and this is how the sausage is made.<br \/>\n<!--more--><\/p>\n<h2>A worker-first API<\/h2>\n<p>Chromium experimented early with shipping APIs for this. Unfortunately the early APIs exposed the media pipeline on main-thread, subjecting it to risks of <a href=\"https:\/\/docs.google.com\/document\/d\/18OFTfCuZvcEbq_Xmt9VxMqEflV02Z8_2YODDXD_XdFg\/edit#heading=h.m504bcvia657\">jank<\/a> given the nature of the JavaScript event model. The Working Group learned from these experiments, and settled instead on a <em>&#8220;worker-first&#8221;<\/em> API, which means an API that is simpler to use in a worker than from main-thread.<\/p>\n<p>This standards-track API is <a href=\"https:\/\/www.w3.org\/TR\/webrtc-encoded-transform\/#rtcrtpscripttransform\"><code>RTCRtpScriptTransform<\/code><\/a>, and lets you transform encoded frames before sending, and back again on reception. The most obvious use-case is E2EE, but it can also be used for other things like adding metadata.<\/p>\n<p>See <a href=\"https:\/\/www.w3.org\/TR\/webrtc-encoded-transform\/#rtcrtpscripttransform\"><code>RTCRtpScriptTransform<\/code><\/a> in action below, XOR-ing data on send and receive (runs in all browsers):<\/p>\n<ol>\n<li>Click <code>Start!<\/code> and share your camera, and you&#8217;ll see two videos: what&#8217;s sent and what&#8217;s received by a peer<\/li>\n<li>Uncheck <code>\u2705 descramble<\/code> to skip XOR on receive for some spectacle (the garbage illustrates that the receiver was &#8220;decrypting&#8221; with XOR before!)<\/li>\n<\/ol>\n<p><iframe loading=\"lazy\" src=\"https:\/\/jsfiddle.net\/jib1\/wx3Lczrs\/138\/embedded\/result,js,html\/\" allow=\"camera\" width=\"100%\" height=\"450px\" frameborder=\"0\"><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><\/iframe><\/p>\n<p>Replace XOR in a real application with crypto of course, using keys secret to your application. <strong>Disclaimer:<\/strong> This is <em><strong>not zero-knowledge <a href=\"https:\/\/en.wikipedia.org\/wiki\/End-to-end_encryption#Etymology_of_the_term\">E2EE<\/a><\/strong><\/em> since you hold the keys \u2014 Mozilla wanted the stronger <a href=\"https:\/\/www.w3.org\/TR\/webrtc-encoded-transform\/#sframe\">SFrameTransform<\/a> API, but that&#8217;s in limbo \u2014 so until something stronger is standardized, this is what&#8217;s available, and protects end-users mostly against middle-boxes, not against the content service provider (you).<\/p>\n<h2>How it works<\/h2>\n<p>Click on the &#8220;JavaScript&#8221; tab above to see the example code. Sender-side we add a transform like this:<br \/>\n<code>sender.transform = new RTCRtpScriptTransform(worker, {side: \"send\"});<\/code><\/p>\n<p>&#8230;and receiver-side it looks the same, since it&#8217;s XOR-ing (the <code>side<\/code> parameter is for the \u2705 checkbox):<br \/>\n<code>receiver.transform = new RTCRtpScriptTransform(worker, {side: \"receive\"});<\/code><\/p>\n<p>And that&#8217;s (almost) it! Scroll down to see the worker in action. On the worker-side, things get exposed here:<br \/>\n<code>onrtctransform = async ({transformer: {readable, writable, options}}) => {<\/code><\/p>\n<p>&#8230;and the worker connects the redable to the writable through a custom (XOR) TransformStream:<br \/>\n<code>await readable.pipeThrough(new TransformStream({transform})).pipeTo(writable);<\/code><\/p>\n<p>And that&#8217;s it!<\/p>\n<p>The rest is standard worker stuff. A <code>\"descramble\"<\/code> message is passed to the worker to flip the switch, and a clever worker-in-a-string trick is used to keep everything in one fiddle (hence the use of <code>\/* *\/<\/code> code comments over <code>\/\/<\/code>). The same worker can be reused for any number of transforms, which scales well.<\/p>\n<p>This remains an imperfect API however, requiring applications to know which bits to leave alone to not upset the codec-specific packetizer, with some codecs tougher to deal with than others \u2014 looking at you <code>H.264<\/code> \u2014 which bits are they? Trial and error is required. <\/p>\n<p>These changes are also not negotiated with the peer, something that is still being ironed out in the Working Group. But as of right now, this works in all browsers for any app that wants it, so that&#8217;s something!<\/p>\n<h2>The shim<\/h2>\n<p>The standards-track API is implemented natively in Safari and Firefox. It might not be obvious in the example \u2014 JSFiddle hides plug-ins well \u2014 but it relies on a <a href=\"https:\/\/jan-ivar.github.io\/samples\/src\/content\/insertable-streams\/video-analyzer\/js\/adapter-shim.js\">small shim<\/a> in Chrome. Also, two minor workarounds highlighted by the comment <code>\/* needed by chrome shim: *\/<\/code> were too hard to shim (like <code>onrtctransform<\/code> in a worker). Some of these are being <a href=\"https:\/\/issues.chromium.org\/issues\/40943169#comment7\">fixed<\/a> in Chromium already, so they may not be needed for long. Implementations should converge soon, which means this shim will hopefully be short-lived.<\/p>\n<h2>What about main-thread use cases?<\/h2>\n<p>There&#8217;s a WebRTC Samples example called <a href=\"https:\/\/webrtc.github.io\/samples\/src\/content\/insertable-streams\/video-analyzer\/\">Video Analyzer<\/a> that dumps some metrics on the page. As of this writing, it is still written the old way, blocking video frame delivery on main-thread even though frames aren&#8217;t being modified. Jank may not be an issue in such a simple demo, but it still encourages developers to follow a pattern likely to scale poorly, and it also won&#8217;t work in Firefox or Safari. The pull-request <a href=\"https:\/\/github.com\/webrtc\/samples\/pull\/1646\">webrtc\/samples#1646<\/a> updates it to use the standards-track API with the shim mentioned earlier which may end up in <a href=\"https:\/\/github.com\/webrtcHacks\/adapter\/pull\/1145\"><code>adapter.js<\/code><\/a>. Try it here in all browsers:<\/p>\n<ol>\n<li>Click <code>Start!<\/code>, share your camera, and then press <code>Call<\/code><\/li>\n<p>.\n<\/ol>\n<p><iframe loading=\"lazy\" src=\"https:\/\/jan-ivar.github.io\/samples\/src\/content\/insertable-streams\/video-analyzer\/\" allow=\"camera\" width=\"100%\" height=\"850px\" frameborder=\"0\"><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><span style=\"display: inline-block; width: 0px; overflow: hidden; line-height: 0;\" data-mce-type=\"bookmark\" class=\"mce_SELRES_start\">\ufeff<\/span><\/iframe><\/p>\n<p>The updated example shows video-frame delivery didn&#8217;t need to block on main-thread in the first place. Instead, <a href=\"https:\/\/github.com\/webrtc\/samples\/blob\/fee52c645434db6670eede2c1df7d5b90e75fecd\/src\/content\/insertable-streams\/video-analyzer\/js\/worker.js#L46\">simple worker messages<\/a> post to main-thread to <a href=\"https:\/\/github.com\/webrtc\/samples\/blob\/fee52c645434db6670eede2c1df7d5b90e75fecd\/src\/content\/insertable-streams\/video-analyzer\/js\/main.js#L236\">update the visible counters<\/a>, which seems cleaner and more efficient.<\/p>\n<p>Any application that needs the encoded frames on main-thread can still get them by using <a href=\"https:\/\/github.com\/whatwg\/streams\/blob\/main\/transferable-streams-explainer.md\">transferable streams<\/a>. The burden of transfer is merely reversed.<\/p>\n<p>Hopefully this blog post helps demystify the use of workers with this API! The need for a shim should hopefully diminish soon. In the meantime, I hope you&#8217;re able to put this API to good use, and feel free to ask any questions. There&#8217;s no comments section here, but you can reach me on <a href=\"https:\/\/twitter.com\/jibrewery\/status\/1760426148461158663\">twitter<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"Hi all, we haven&#8217;t blogged since the WebRTC API was standardized back in January of 2021! But now in 2024 folks are asking us what&#8217;s up with all the new APIs? \u2014 We&#8217;re coming off an exciting second period of innovation and experimentation in WebRTC, so we&#8217;re brushing off the old blogging muscles to cover [&hellip;]","protected":false},"author":1399,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"coauthors":[301098],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>End-to-end-encrypt WebRTC in all browsers! - Advancing WebRTC<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"End-to-end-encrypt WebRTC in all browsers! - Advancing WebRTC\" \/>\n<meta property=\"og:description\" content=\"Hi all, we haven&#8217;t blogged since the WebRTC API was standardized back in January of 2021! But now in 2024 folks are asking us what&#8217;s up with all the new APIs? \u2014 We&#8217;re coming off an exciting second period of innovation and experimentation in WebRTC, so we&#8217;re brushing off the old blogging muscles to cover [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/\" \/>\n<meta property=\"og:site_name\" content=\"Advancing WebRTC\" \/>\n<meta property=\"article:published_time\" content=\"2024-02-21T19:55:49+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-05-16T18:18:14+00:00\" \/>\n<meta name=\"author\" content=\"Jan-Ivar Bruaroey\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jan-Ivar Bruaroey\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/\",\"url\":\"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/\",\"name\":\"End-to-end-encrypt WebRTC in all browsers! - Advancing WebRTC\",\"isPartOf\":{\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#website\"},\"datePublished\":\"2024-02-21T19:55:49+00:00\",\"dateModified\":\"2024-05-16T18:18:14+00:00\",\"author\":{\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f2eb9712b8d85b70aebe1faf24e731fd\"},\"breadcrumb\":{\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blog.mozilla.org\/webrtc\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"End-to-end-encrypt WebRTC in all browsers!\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#website\",\"url\":\"https:\/\/blog.mozilla.org\/webrtc\/\",\"name\":\"Advancing WebRTC\",\"description\":\"Committed to moving Firefox and WebRTC forward\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blog.mozilla.org\/webrtc\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f2eb9712b8d85b70aebe1faf24e731fd\",\"name\":\"Jan-Ivar Bruaroey\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/image\/5f3d49a61b032619d0d33c4cc7c7433f\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/16d7e05dc9f8a855a02e0796b00aad3f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/16d7e05dc9f8a855a02e0796b00aad3f?s=96&d=mm&r=g\",\"caption\":\"Jan-Ivar Bruaroey\"},\"url\":\"https:\/\/blog.mozilla.org\/webrtc\/author\/jbruaroeymozilla-com\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"End-to-end-encrypt WebRTC in all browsers! - Advancing WebRTC","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/","og_locale":"en_US","og_type":"article","og_title":"End-to-end-encrypt WebRTC in all browsers! - Advancing WebRTC","og_description":"Hi all, we haven&#8217;t blogged since the WebRTC API was standardized back in January of 2021! But now in 2024 folks are asking us what&#8217;s up with all the new APIs? \u2014 We&#8217;re coming off an exciting second period of innovation and experimentation in WebRTC, so we&#8217;re brushing off the old blogging muscles to cover [&hellip;]","og_url":"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/","og_site_name":"Advancing WebRTC","article_published_time":"2024-02-21T19:55:49+00:00","article_modified_time":"2024-05-16T18:18:14+00:00","author":"Jan-Ivar Bruaroey","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jan-Ivar Bruaroey","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/","url":"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/","name":"End-to-end-encrypt WebRTC in all browsers! - Advancing WebRTC","isPartOf":{"@id":"https:\/\/blog.mozilla.org\/webrtc\/#website"},"datePublished":"2024-02-21T19:55:49+00:00","dateModified":"2024-05-16T18:18:14+00:00","author":{"@id":"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f2eb9712b8d85b70aebe1faf24e731fd"},"breadcrumb":{"@id":"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.mozilla.org\/webrtc\/end-to-end-encrypt-webrtc-in-all-browsers\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.mozilla.org\/webrtc\/"},{"@type":"ListItem","position":2,"name":"End-to-end-encrypt WebRTC in all browsers!"}]},{"@type":"WebSite","@id":"https:\/\/blog.mozilla.org\/webrtc\/#website","url":"https:\/\/blog.mozilla.org\/webrtc\/","name":"Advancing WebRTC","description":"Committed to moving Firefox and WebRTC forward","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.mozilla.org\/webrtc\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/f2eb9712b8d85b70aebe1faf24e731fd","name":"Jan-Ivar Bruaroey","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.mozilla.org\/webrtc\/#\/schema\/person\/image\/5f3d49a61b032619d0d33c4cc7c7433f","url":"https:\/\/secure.gravatar.com\/avatar\/16d7e05dc9f8a855a02e0796b00aad3f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/16d7e05dc9f8a855a02e0796b00aad3f?s=96&d=mm&r=g","caption":"Jan-Ivar Bruaroey"},"url":"https:\/\/blog.mozilla.org\/webrtc\/author\/jbruaroeymozilla-com\/"}]}},"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/posts\/423"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/users\/1399"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/comments?post=423"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/posts\/423\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/media?parent=423"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/categories?post=423"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/tags?post=423"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webrtc\/wp-json\/wp\/v2\/coauthors?post=423"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}