CINXE.COM

Privacy Pass: upgrading to the latest protocol version

<!DOCTYPE html><script type="module" src="/_astro/index.astro_astro_type_script_index_0_lang.CoXd8L9s.js"></script> <html lang="en-us" dir="ltr"> <head><script async src="https://ot.www.cloudflare.com/public/vendor/onetrust/scripttemplates/otSDKStub.js" data-document-language="true" type="text/javascript" data-domain-script="b1e05d49-f072-4bae-9116-bdb78af15448"></script><meta name="HandheldFriendly" content="True"><meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="baidu-site-verification" content="KeThzeyMOr"><meta name="baidu-site-verification" content="code-NIlrS7gNhx"><meta charset="UTF-8"><meta name="description" content="In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the Privacy Pass protocol — including a set of open-source templates that can be used to implement Privacy Pass Origins, Issuers, and Attesters."><title>Privacy Pass: upgrading to the latest protocol version</title><meta name="title" content="Privacy Pass: upgrading to the latest protocol version"><meta name="msvalidate.01" content="CF295E1604697F9CAD18B5A232E871F6"><meta class="swiftype" name="language" data-type="string" content="en"><script src="/static/z/i.js" type="text/javascript" referrerpolicy="origin"></script><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="apple-touch-icon" sizes="180x180" href="/images/favicon-32x32.png"><link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-32x32.png"><link rel="mask-icon" href="/images/favicon-32x32.png" color="#f78100"><link rel="stylesheet" href="/themes/ashes.min.css"><link rel="sitemap" href="/sitemap.xml"><meta name="msapplication-TileColor" content="#da532c"><meta name="theme-color" content="#ffffff"><link rel="canonical" href="https://blog.cloudflare.com/privacy-pass-standard/"><link rel="alternate" hreflang="en-us" href="https://blog.cloudflare.com/privacy-pass-standard/"><link rel="alternate" hreflang="zh-cn" href="https://blog.cloudflare.com/zh-cn/privacy-pass-standard/"><link rel="alternate" hreflang="zh-tw" href="https://blog.cloudflare.com/zh-tw/privacy-pass-standard/"><!-- General Meta Tags --><meta property="article:published_time" content="2024-01-04T16:07:22.000+00:00"><meta property="article:modified_time" content="2024-10-09T23:26:44.495Z"><meta property="article:tag" content="Research"><meta property="article:tag" content="Privacy Pass"><meta property="article:tag" content="Firefox"><meta property="article:tag" content="Chrome"><meta property="article:tag" content="Privacy"><meta property="article:publisher" content="https://www.facebook.com/cloudflare"><!-- Facebook Meta Tags --><meta property="og:site_name" content="The Cloudflare Blog"><meta property="og:type" content="article"><meta property="og:title" content="Privacy Pass: upgrading to the latest protocol version"><meta property="og:description" content="In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the Privacy Pass protocol — including a set of open-source templates that can be used to implement Privacy Pass Origins, Issuers, and Attesters."><meta property="og:url" content="https://blog.cloudflare.com/privacy-pass-standard/"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="628"><!-- Twitter/X Meta Tags --><meta name="twitter:title" content="Privacy Pass: upgrading to the latest protocol version"><meta name="twitter:description" content="In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the Privacy Pass protocol — including a set of open-source templates that can be used to implement Privacy Pass Origins, Issuers, and Attesters."><meta name="twitter:url" content="https://blog.cloudflare.com/privacy-pass-standard/"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:label1" content="Written by"><meta name="twitter:data1" content="Thibault Meunier"><meta name="twitter:creator" content="@thibmeu"><meta name="twitter:label2" content="Filed under"><meta name="twitter:data2" content="Research,Privacy Pass,Firefox,Chrome,Privacy"><meta name="twitter:site" content="@cloudflare"><meta property="og:image" content="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/22RHi3sA2tR4yPZCvPKYLe/87cdf4737a0d11148e569ff370819e38/privacy-pass-standard-fxK0y1.png"><meta name="twitter:image" content="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/22RHi3sA2tR4yPZCvPKYLe/87cdf4737a0d11148e569ff370819e38/privacy-pass-standard-fxK0y1.png"><link rel="stylesheet" href="/_astro/index.Bpd2cWaZ.css"></head><style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();;(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="Z19tbUu" component-url="/_astro/GoogleAnalytics.DSjxwi8U.js" component-export="GoogleAnalytics" renderer-url="/_astro/client.DLO1yDVm.js" props="{&quot;title&quot;:[0,&quot;Privacy Pass: upgrading to the latest protocol version&quot;],&quot;canonical&quot;:[0,&quot;https://blog.cloudflare.com/privacy-pass-standard&quot;],&quot;info&quot;:[0,{&quot;id&quot;:[0,&quot;47vZ5BZfqt5cU38XabKyUA&quot;],&quot;title&quot;:[0,&quot;Privacy Pass: upgrading to the latest protocol version&quot;],&quot;slug&quot;:[0,&quot;privacy-pass-standard&quot;],&quot;excerpt&quot;:[0,&quot;In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the Privacy Pass protocol — including a set of open-source templates that can be used to implement Privacy Pass Origins, Issuers, and Attesters&quot;],&quot;featured&quot;:[0,false],&quot;html&quot;:[0,&quot;&lt;p&gt;&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card \&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2LZJxp89GI8PxGwGSPRQJL/9cfe61e756369dcad6cb78f5ad89ec1f/image9.png\&quot; alt=\&quot;Privacy Pass: Upgrading to the latest protocol version\&quot; class=\&quot;kg-image\&quot; width=\&quot;1800\&quot; height=\&quot;1013\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;enabling-anonymous-access-to-the-web-with-privacy-preserving-cryptography\&quot;&gt;Enabling anonymous access to the web with privacy-preserving cryptography&lt;/h2&gt;\n &lt;a href=\&quot;#enabling-anonymous-access-to-the-web-with-privacy-preserving-cryptography\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;The challenge of telling humans and bots apart is almost as old as the web itself. From online ticket vendors to dating apps, to ecommerce and finance — there are many legitimate reasons why you&amp;#39;d want to know if it&amp;#39;s a person or a machine knocking on the front door of your website.&lt;/p&gt;&lt;p&gt;Unfortunately, the tools for the web have traditionally been clunky and sometimes involved a bad user experience. None more so than the CAPTCHA — an irksome solution that humanity wastes a &lt;a href=\&quot;/introducing-cryptographic-attestation-of-personhood/\&quot;&gt;staggering&lt;/a&gt; amount of time on. A more subtle but intrusive approach is IP tracking, which uses IP addresses to identify and take action on suspicious traffic, but that too can come with &lt;a href=\&quot;/consequences-of-ip-blocking/\&quot;&gt;unforeseen consequences&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;And yet, the problem of distinguishing legitimate human requests from automated bots remains as vital as ever. This is why for years Cloudflare has invested in the Privacy Pass protocol — a novel approach to establishing a user’s identity by relying on cryptography, rather than crude puzzles — all while providing a streamlined, privacy-preserving, and often frictionless experience to end users.&lt;/p&gt;&lt;p&gt;Cloudflare began &lt;a href=\&quot;/cloudflare-supports-privacy-pass/\&quot;&gt;supporting Privacy Pass&lt;/a&gt; in 2017, with the release of browser extensions for Chrome and Firefox. Web admins with their sites on Cloudflare would have Privacy Pass enabled in the Cloudflare Dash; users who installed the extension in their browsers would see fewer CAPTCHAs on websites they visited that had Privacy Pass enabled.&lt;/p&gt;&lt;p&gt;Since then, Cloudflare &lt;a href=\&quot;/end-cloudflare-captcha/\&quot;&gt;stopped issuing CAPTCHAs&lt;/a&gt;, and Privacy Pass has come a long way. Apple uses a version of Privacy Pass for its &lt;a href=\&quot;https://developer.apple.com/news/?id=huqjyh7k\&quot;&gt;Private Access Tokens&lt;/a&gt; system which works in tandem with a device’s secure enclave to attest to a user’s humanity. And Cloudflare uses Privacy Pass as an important signal in our Web Application Firewall and Bot Management products — which means millions of websites natively offer Privacy Pass.&lt;/p&gt;&lt;p&gt;In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the &lt;a href=\&quot;https://www.ietf.org/archive/id/draft-ietf-privacypass-protocol-16.html\&quot;&gt;Privacy Pass protocol&lt;/a&gt; — including a &lt;a href=\&quot;https://github.com/cloudflare?q=pp-&amp;type=all&amp;language=&amp;sort=#org-repositories\&quot;&gt;set of open-source templates&lt;/a&gt; that can be used to implement Privacy Pass &lt;a href=\&quot;https://github.com/cloudflare/pp-origin\&quot;&gt;&lt;i&gt;Origins&lt;/i&gt;&lt;/a&gt;&lt;i&gt;,&lt;/i&gt; &lt;a href=\&quot;https://github.com/cloudflare/pp-issuer\&quot;&gt;&lt;i&gt;Issuers&lt;/i&gt;&lt;/a&gt;, and &lt;a href=\&quot;https://github.com/cloudflare/pp-attester\&quot;&gt;&lt;i&gt;Attesters&lt;/i&gt;&lt;/a&gt;. These are based on Cloudflare Workers, and are the easiest way to get started with a new deployment of Privacy Pass.&lt;/p&gt;&lt;p&gt;To complement the updated implementations, we are releasing a new version of our Privacy Pass browser extensions (&lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt;, &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt;), which are rolling out with the name: &lt;i&gt;Silk - Privacy Pass Client&lt;/i&gt;. Users of these extensions can expect to see fewer bot-checks around the web, and will be contributing to research about privacy preserving signals via a set of trusted attesters, which can be configured in the extension’s settings panel.&lt;/p&gt;&lt;p&gt;Finally, we will discuss how Privacy Pass can be used for an array of scenarios beyond differentiating bot from human traffic.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Notice to our users&lt;/b&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;If you use the Privacy Pass API that controls Privacy Pass configuration on Cloudflare, you can remove these calls. This API is no longer needed since Privacy Pass is now included by default in our Challenge Platform. Out of an abundance of caution for our customers, we are doing a &lt;a href=\&quot;https://developers.cloudflare.com/fundamentals/api/reference/deprecations/\&quot;&gt;four-month deprecation notice&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;If you have the Privacy Pass extension installed, it should automatically update to &lt;i&gt;Silk - Privacy Pass Client&lt;/i&gt; (&lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt;, &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt;) over the next few days. We have renamed it to keep the distinction clear between the protocol itself and a client of the protocol.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;brief-history\&quot;&gt;Brief history&lt;/h2&gt;\n &lt;a href=\&quot;#brief-history\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;In the last decade, we&amp;#39;ve seen the &lt;a href=\&quot;/next-generation-privacy-protocols/\&quot;&gt;rise of protocols&lt;/a&gt; with privacy at their core, including &lt;a href=\&quot;/building-privacy-into-internet-standards-and-how-to-make-your-app-more-private-today/\&quot;&gt;Oblivious HTTP (OHTTP)&lt;/a&gt;, &lt;a href=\&quot;/deep-dive-privacy-preserving-measurement/\&quot;&gt;Distributed aggregation protocol (DAP)&lt;/a&gt;, and &lt;a href=\&quot;/unlocking-quic-proxying-potential/\&quot;&gt;MASQUE&lt;/a&gt;. These protocols improve privacy when browsing and interacting with services online. By protecting users&amp;#39; privacy, these protocols also ask origins and website owners to revise their expectations around the data they can glean from user traffic. This might lead them to reconsider existing assumptions and mitigations around suspicious traffic, such as &lt;a href=\&quot;/consequences-of-ip-blocking/\&quot;&gt;IP filtering&lt;/a&gt;, which often has unintended consequences.&lt;/p&gt;&lt;p&gt;In 2017, Cloudflare announced &lt;a href=\&quot;/cloudflare-supports-privacy-pass/\&quot;&gt;support for Privacy Pass&lt;/a&gt;. At launch, this meant improving content accessibility for web users who would see a lot of interstitial pages (such as &lt;a href=\&quot;https://www.cloudflare.com/learning/bots/how-captchas-work/\&quot;&gt;CAPTCHAs&lt;/a&gt;) when browsing websites protected by Cloudflare. Privacy Pass tokens provide a signal about the user’s capabilities to website owners while protecting their privacy by ensuring each token redemption is unlinkable to its issuance context. Since then, the technology has turned into a &lt;a href=\&quot;https://datatracker.ietf.org/wg/privacypass/documents/\&quot;&gt;fully fledged protocol&lt;/a&gt; used by millions thanks to academic and industry effort. The existing browser extension accounts for hundreds of thousands of downloads. During the same time, Cloudflare has dramatically evolved the way it allows customers to challenge their visitors, being &lt;a href=\&quot;/end-cloudflare-captcha/\&quot;&gt;more flexible about the signals&lt;/a&gt; it receives, and &lt;a href=\&quot;/turnstile-ga/\&quot;&gt;moving away from CAPTCHA&lt;/a&gt; as a binary legitimacy signal.&lt;/p&gt;&lt;p&gt;Deployments of this research have led to a broadening of use cases, opening the door to different kinds of attestation. An attestation is a cryptographically-signed data point supporting facts. This can include a signed token indicating that the user has successfully solved a CAPTCHA, having a user’s hardware attest it’s untampered, or a piece of data that an attester can verify against another data source.&lt;/p&gt;&lt;p&gt;For example, in 2022, Apple hardware devices began to offer Privacy Pass tokens to websites who wanted to reduce how often they show CAPTCHAs, by using the hardware itself as an attestation factor. Before showing images of buses and fire hydrants to users, CAPTCHA providers can request a &lt;a href=\&quot;https://developer.apple.com/news/?id=huqjyh7k\&quot;&gt;Private Access Token&lt;/a&gt; (PAT). This native support does not require installing extensions, or any user action to benefit from a smoother and more private web browsing experience.&lt;/p&gt;&lt;p&gt;Below is a brief overview of changes to the protocol we participated in:&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3YImfph78oDPj3kgEcyvV6/37bcd89ffcfff8b636b00c8e931f3218/image8.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1808\&quot; height=\&quot;631\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;The timeline presents cryptographic changes, community inputs, and industry collaborations. These changes helped shape better standards for the web, such as VOPRF (&lt;a href=\&quot;https://www.rfc-editor.org/rfc/rfc9497\&quot;&gt;RFC 9497&lt;/a&gt;), or RSA Blind Signatures (&lt;a href=\&quot;https://www.rfc-editor.org/rfc/rfc9474\&quot;&gt;RFC 9474&lt;/a&gt;). In the next sections, we dive in the Privacy Pass protocol to understand its ins and outs.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;anonymous-credentials-in-real-life\&quot;&gt;Anonymous credentials in real life&lt;/h2&gt;\n &lt;a href=\&quot;#anonymous-credentials-in-real-life\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Before explaining the protocol in more depth, let&amp;#39;s use an analogy. You are at a music festival. You bought your ticket online with a student discount. When you arrive at the gates, an agent scans your ticket, checks your student status, and gives you a yellow wristband and two drink tickets.&lt;/p&gt;&lt;p&gt;During the festival, you go in and out by showing your wristband. When a friend asks you to grab a drink, you pay with your tickets. One for your drink and one for your friend. You give your tickets to the bartender, they check the tickets, and give you a drink. The characteristics that make this interaction private is that the drinks tickets cannot be traced back to you or your payment method, but they can be verified as having been unused and valid for purchase of a drink.&lt;/p&gt;&lt;p&gt;In the web use case, the Internet is a festival. When you arrive at the gates of a website, an agent scans your request, and gives you a session cookie as well as two Privacy Pass tokens. They could have given you just one token, or more than two, but in our example ‘two tokens’ is the given website’s policy. You can use these tokens to attest your humanity, to authenticate on certain websites, or even to confirm the legitimacy of your hardware.&lt;/p&gt;&lt;p&gt;Now, you might wonder if this is a technique we have been using for years, why do we need fancy cryptography and standardization efforts? Well, unlike at a real-world music festival where most people don’t carry around photocopiers, on the Internet it is pretty easy to copy tokens. For instance, how do we stop people using a token twice? We could put a unique number on each token, and check it is not spent twice, but that would allow the gate attendant to tell the bartender which numbers were linked to which person. So, we need cryptography.&lt;/p&gt;&lt;p&gt;When another website presents a challenge to you, you provide your Privacy Pass token and are then allowed to view a gallery of beautiful cat pictures. The difference with the festival is this challenge might be interactive, which would be similar to the bartender giving you a numbered ticket which would have to be signed by the agent before getting a drink. The website owner can verify that the token is valid but has no way of tracing or connecting the user back to the action that provided them with the Privacy Pass tokens. With Privacy Pass terminology, you are a Client, the website is an Origin, the agent is an Attester, and the bar an Issuer. The next section goes through these in more detail.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;privacy-pass-protocol\&quot;&gt;Privacy Pass protocol&lt;/h2&gt;\n &lt;a href=\&quot;#privacy-pass-protocol\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Privacy Pass specifies an extensible protocol for creating and redeeming anonymous and transferable tokens. In fact, Apple has their own implementation with Private Access Tokens (PAT), and later we will describe another implementation with the Silk browser extension. Given PAT was the first to implement the IETF defined protocol, Privacy Pass is sometimes referred to as PAT in the literature.&lt;/p&gt;&lt;p&gt;The protocol is generic, and defines four components:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Client: Web user agent with a Privacy Pass enabled browser. This could be your &lt;a href=\&quot;/eliminating-captchas-on-iphones-and-macs-using-new-standard/\&quot;&gt;Apple device with PAT&lt;/a&gt;, or your web browser with &lt;a href=\&quot;https://github.com/cloudflare/pp-browser-extension\&quot;&gt;the Silk extension installed&lt;/a&gt;. Typically, this is the actor who is requesting content and is asked to share some attribute of themselves.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Origin: Serves content requested by the Client. The Origin trusts one or more Issuers, and presents Privacy Pass challenges to the Client. For instance, Cloudflare Managed Challenge is a Privacy Pass origin serving two Privacy Pass challenges: one for Apple PAT Issuer, one for Cloudflare Research Issuer.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Issuer: Signs Privacy Pass tokens upon request from a trusted party, either an Attester or a Client depending on the deployment model. Different Issuers have their own set of trusted parties, depending on the security level they are looking for, as well as their privacy considerations. An Issuer validating device integrity should use different methods that vouch for this attribute to acknowledge the diversity of Client configurations.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Attester: Verifies an attribute of the Client and when satisfied requests a signed Privacy Pass token from the Issuer to pass back to the Client. Before vouching for the Client, an Attester may ask the Client to complete a specific task. This task could be a CAPTCHA, a location check, or age verification or some other check that will result in a single binary result. The Privacy Pass token will then share this one-bit of information in an unlinkable manner.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;They interact as illustrated below.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7tX1xRQv6Ltif1NRj2fCOa/eeb412fa39d73e2232f4b062d95cd708/Frame-699-1-.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1492\&quot; height=\&quot;780\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;Let&amp;#39;s dive into what&amp;#39;s really happening with an example. The User wants to access an Origin, say store.example.com. This website has suffered attacks or abuse in the past, and the site is using Privacy Pass to help avoid these going forward. To that end, the Origin returns &lt;a href=\&quot;https://www.rfc-editor.org/rfc/rfc9110#field.www-authenticate\&quot;&gt;an authentication request&lt;/a&gt; to the Client: &lt;code&gt;WWW-Authenticate: PrivateToken challenge=&amp;quot;A==&amp;quot;,token-key=&amp;quot;B==&amp;quot;&lt;/code&gt;. In this way, the Origin signals that it accepts tokens from the Issuer with public key “B==” to satisfy the challenge. That Issuer in turn trusts reputable Attesters to vouch for the Client not being an attacker by means of the presence of a cookie, CAPTCHA, Turnstile, or &lt;a href=\&quot;/introducing-cryptographic-attestation-of-personhood/\&quot;&gt;CAP challenge&lt;/a&gt; for example. For accessibility reasons for our example, let us say that the Client likely prefers the Turnstile method. The User’s browser prompts them to solve a Turnstile challenge. On success, it contacts the Issuer “B==” with that solution, and then replays the initial requests to store.example.com, this time sending along the token header &lt;code&gt;Authorization: PrivateToken token=&amp;quot;C==&amp;quot;&lt;/code&gt;, which the Origin accepts and returns your desired content to the Client. And that’s it.&lt;/p&gt;&lt;p&gt;We’ve described the Privacy Pass authentication protocol. While Basic authentication (&lt;a href=\&quot;https://www.rfc-editor.org/rfc/rfc7617\&quot;&gt;RFC 7671&lt;/a&gt;) asks you for a username and a password, the PrivateToken authentication scheme allows the browser to be more flexible on the type of check, while retaining privacy. The Origin store.example.com does not know your attestation method, they just know you are reputable according to the token issuer. In the same spirit, the Issuer &amp;quot;B==&amp;quot; does not see your IP, nor the website you are visiting. This separation between issuance and redemption, also referred to as unlinkability, is what &lt;a href=\&quot;https://www.ietf.org/archive/id/draft-ietf-privacypass-architecture-16.html\&quot;&gt;makes Privacy Pass private&lt;/a&gt;.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;demo-time\&quot;&gt;Demo time&lt;/h2&gt;\n &lt;a href=\&quot;#demo-time\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;To put the above in practice, let’s see how the protocol works with Silk, a browser extension providing Privacy Pass support. First, download the relevant &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt; or &lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt; extension.&lt;/p&gt;&lt;p&gt;Then, head to &lt;a href=\&quot;https://demo-pat.research.cloudflare.com/login\&quot;&gt;https://demo-pat.research.cloudflare.com/login&lt;/a&gt;. The page returns a 401 Privacy Pass Token not presented. In fact, the origin expects you to perform a PrivateToken authentication. If you don’t have the extension installed, the flow stops here. If you have the extension installed, the extension is going to orchestrate the flow required to get you a token requested by the Origin.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ZPDrhytZNVoB81Q7RILu5/7c115c9ed069aa09694373ec1adcc4d0/image10.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;With the extension installed, you are directed to a new tab &lt;a href=\&quot;https://pp-attester-turnstile.research.cloudflare.com/challenge\&quot;&gt;https://pp-attester-turnstile.research.cloudflare.com/challenge&lt;/a&gt;. This is a page provided by an Attester able to deliver you a token signed by the Issuer request by the Origin. In this case, the Attester checks you’re able to solve a Turnstile challenge.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7fmDWo3548oMK8jgZ7V0Kd/94ee9ab9bc1df6fee6e6a76dc4fb3e02/image2.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;You click, and that’s it. The Turnstile challenge solution is sent to the Attester, which upon validation, sends back a token from the requested Issuer. This page appears for a very short time, as once the extension has the token, the challenge page is no longer needed.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3KROIlp9njiXlfceDzRU7W/d1e306da3012c949e3fa5b80934f83a4/image11.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;The extension, now having a token requested by the Origin, sends your initial request for a second time, with an Authorization header containing a valid Issuer PrivateToken. Upon validation, the Origin allows you in with a 200 Privacy Pass Token valid!&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3qOSkMc5wIqS50CuNNNoZY/b36b88ba01ffa1c5f4d78727e602062f/image3.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;If you want to check behind the scenes, you can right-click on the extension logo and go to the preference/options page. It contains a list of attesters trusted by the extension, one per line. You can add your own attestation method (API described below). This allows the Client to decide on their preferred attestation methods.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/78BCHYQuOBC2aFlnPshu83/c6ee6b54d1d24b6f92f34577267a1146/image7.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;privacy-pass-protocol-extended\&quot;&gt;Privacy Pass protocol — extended&lt;/h2&gt;\n &lt;a href=\&quot;#privacy-pass-protocol-extended\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;The Privacy Pass protocol is new and not a standard yet, which implies that it’s not uniformly supported on all platforms. To improve flexibility beyond the existing standard proposal, we are introducing two mechanisms: an API for Attesters, and a replay API for web clients. The API for attesters allows developers to build new attestation methods, which only need to provide their URL to interface with the Silk browser extension. The replay API for web clients is a mechanism to enable websites to cooperate with the extension to make PrivateToken authentication work on browsers with Chrome user agents.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2TLz1CPx9OHczqLabCRmyc/c54b0b4bb637a97812c637ca0eebc78c/image12.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1999\&quot; height=\&quot;1119\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;Because more than one Attester may be supported on your machine, your Client needs to understand which Attester to use depending on the requested Issuer. As mentioned before, you as the Client do not communicate directly with the Issuer because you don’t necessarily know their relation with the attester, so you cannot retrieve its public key. To this end, the Attester API exposes all Issuers reachable by the said Attester via an endpoint: /v1/private-token-issuer-directory. This way, your client selects an appropriate Attester - one in relation with an Issuer that the Origin trusts, before triggering a validation.&lt;/p&gt;&lt;p&gt;In addition, we propose a replay API. Its goal is to allow clients to fetch a resource a second time if the first response presented a Privacy pass challenge. Some platforms do this automatically, like Silk on Firefox, but some don’t. That’s the case with the Silk Chrome extension for instance, which in its support of &lt;a href=\&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/manifest_version\&quot;&gt;manifest v3&lt;/a&gt; cannot block requests and only supports Basic authentication in the onAuthRequired extension event. The Privacy Pass Authentication scheme proposes the request to be sent once to get a challenge, and then a second time to get the actual resource. Between these requests to the Origin, the platform orchestrates the issuance of a token. To keep clients informed about the state of this process, we introduce a &lt;code&gt;private-token-client-replay: UUID header&lt;/code&gt; alongside WWW-Authenticate. Using a platform defined endpoint, this UUID informs web clients of the current state of authentication: pending, fulfilled, not-found.&lt;/p&gt;&lt;p&gt;To learn more about how you can use these today, and to deploy your own attestation method, read on.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;how-to-use-privacy-pass-today\&quot;&gt;How to use Privacy Pass today?&lt;/h2&gt;\n &lt;a href=\&quot;#how-to-use-privacy-pass-today\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;As seen in the section above, Privacy Pass is structured around four components: Origin, Client, Attester, Issuer. That’s why we created four repositories: &lt;a href=\&quot;https://github.com/cloudflare/pp-origin\&quot;&gt;cloudflare/pp-origin&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/pp-browser-extension\&quot;&gt;cloudflare/pp-browser-extension&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/pp-attester\&quot;&gt;cloudflare/pp-attester&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/pp-issuer\&quot;&gt;cloudflare/pp-issuer&lt;/a&gt;. In addition, the underlying cryptographic libraries are available &lt;a href=\&quot;https://github.com/cloudflare/privacypass-ts\&quot;&gt;cloudflare/privacypass-ts&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/blindrsa-ts\&quot;&gt;cloudflare/blindrsa-ts&lt;/a&gt;, and &lt;a href=\&quot;https://github.com/cloudflare/voprf-ts\&quot;&gt;cloudflare/voprf-ts&lt;/a&gt;. In this section, we dive into how to use each one of these depending on your use case.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Note: All examples below are designed in JavaScript and targeted at Cloudflare Workers. Privacy Pass is also implemented in &lt;a href=\&quot;https://github.com/ietf-wg-privacypass/base-drafts#existing-implementations\&quot;&gt;other languages&lt;/a&gt; and can be deployed with a configuration that suits your needs.&lt;/p&gt;&lt;/blockquote&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;as-an-origin-website-owners-service-providers\&quot;&gt;As an Origin - website owners, service providers&lt;/h3&gt;\n &lt;a href=\&quot;#as-an-origin-website-owners-service-providers\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;You are an online service that people critically rely upon (health or messaging for instance). You want to provide private payment options to users to maintain your users’ privacy. You only have one subscription tier at $10 per month. You have &lt;a href=\&quot;https://datatracker.ietf.org/doc/html/draft-davidson-pp-architecture-00#autoid-60\&quot;&gt;heard&lt;/a&gt; people are making privacy preserving apps, and want to use the latest version of Privacy Pass.&lt;/p&gt;&lt;p&gt;To access your service, users are required to prove they&amp;#39;ve paid for the service through a payment provider of their choosing (that you deem acceptable). This payment provider acknowledges the payment and requests a token for the user to access the service. As a sequence diagram, it looks as follows:&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3CDt5NsDY4c2DuYbggdleT/c2084b1b7cb141a8b528de78392833b3/image4.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1615\&quot; height=\&quot;903\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;To implement it in Workers, we rely on the &lt;a href=\&quot;https://www.npmjs.com/package/@cloudflare/privacypass-ts\&quot;&gt;&lt;code&gt;@cloudflare/privacypass-ts&lt;/code&gt;&lt;/a&gt; library, which can be installed by running:&lt;/p&gt;\n &lt;pre class=\&quot;language-bash\&quot;&gt;&lt;code class=\&quot;language-bash\&quot;&gt;npm i @cloudflare/privacypass-ts&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;This section is going to focus on the Origin work. We assume you have an Issuer up and running, which is described in a later section.&lt;/p&gt;&lt;p&gt;The Origin defines two flows:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;User redeeming token&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;User requesting a token issuance&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;import { Client } from &amp;#039;@cloudflare/privacypass-ts&amp;#039;\n\nconst issuer = &amp;#039;static issuer key&amp;#039;\n\nconst handleRedemption =&amp;gt; (req) =&amp;gt; {\n const token = TokenResponse.parse(req.headers.get(&amp;#039;authorization&amp;#039;))\n const isValid = token.verify(issuer.publicKey)\n}\n\nconst handleIssuance = () =&amp;gt; {\n return new Response(&amp;#039;Please pay to access the service&amp;#039;, {\n status: 401,\n headers: { &amp;#039;www-authenticate&amp;#039;: &amp;#039;PrivateToken challenge=, token-key=, max-age=300&amp;#039; }\n })\n}\n\nconst handleAuth = (req) =&amp;gt; {\n const authorization = req.headers.get(&amp;#039;authorization&amp;#039;)\n if (authorization.startsWith(`PrivateToken token=`)) {\n return handleRedemption(req)\n }\n return handleIssuance(req)\n}\n\nexport default {\n fetch(req: Request) {\n return handleAuth(req)\n }\n}&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;From the user’s perspective, the overhead is minimal. Their client (possibly the Silk browser extension) receives a WWW-Authenticate header with the information required for a token issuance. Then, depending on their client configuration, they are taken to the payment provider of their choice to validate their access to the service.&lt;/p&gt;&lt;p&gt;With a successful response to the PrivateToken challenge a session is established, and the traditional web service flow continues.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;as-an-attester-captcha-providers-authentication-provider\&quot;&gt;As an Attester - CAPTCHA providers, authentication provider&lt;/h3&gt;\n &lt;a href=\&quot;#as-an-attester-captcha-providers-authentication-provider\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;You are the author of a new attestation method, such as &lt;a href=\&quot;/introducing-cryptographic-attestation-of-personhood/\&quot;&gt;CAP,&lt;/a&gt; a new CAPTCHA mechanism, or a new way to validate cookie consent. You know that website owners already use Privacy Pass to trigger such challenges on the user side, and an Issuer is willing to trust your method because it guarantees a high security level. In addition, because of the Privacy Pass protocol you never see which website your attestation is being used for.&lt;/p&gt;&lt;p&gt;So you decide to expose your attestation method as a Privacy Pass Attester. An Issuer with public key B== trusts you, and that&amp;#39;s the Issuer you are going to request a token from. You can check that with the Yes/No Attester below, whose code is on &lt;a href=\&quot;https://cloudflareworkers.com/#eedc5a7a6560c44b23a24cc1414b29d7:https://tutorial.cloudflareworkers.com/v1/challenge\&quot;&gt;Cloudflare Workers playground&lt;/a&gt;&lt;/p&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;const ISSUER_URL = &amp;#039;https://pp-issuer-public.research.cloudflare.com/token-request&amp;#039;\n\nconst b64ToU8 = (b) =&amp;gt; Uint8Array.from(atob(b), c =&amp;gt; c.charCodeAt(0))\n\nconst handleGetChallenge = (req) =&amp;gt; {\n return new Response(`\n &amp;lt;html&amp;gt;\n &amp;lt;head&amp;gt;\n &amp;lt;title&amp;gt;Challenge Response&amp;lt;/title&amp;gt;\n &amp;lt;/head&amp;gt;\n &amp;lt;body&amp;gt;\n \t&amp;lt;button onclick=&amp;quot;sendResponse(&amp;#039;Yes&amp;#039;)&amp;quot;&amp;gt;Yes&amp;lt;/button&amp;gt;\n\t\t&amp;lt;button onclick=&amp;quot;sendResponse(&amp;#039;No&amp;#039;)&amp;quot;&amp;gt;No&amp;lt;/button&amp;gt;\n\t&amp;lt;/body&amp;gt;\n\t&amp;lt;script&amp;gt;\n\tfunction sendResponse(choice) {\n\t\tfetch(location.href, { method: &amp;#039;POST&amp;#039;, headers: { &amp;#039;private-token-attester-data&amp;#039;: choice } })\n\t}\n\t&amp;lt;/script&amp;gt;\n\t&amp;lt;/html&amp;gt;\n\t`, { status: 401, headers: { &amp;#039;content-type&amp;#039;: &amp;#039;text/html&amp;#039; } })\n}\n\nconst handlePostChallenge = (req) =&amp;gt; {\n const choice = req.headers.get(&amp;#039;private-token-attester-data&amp;#039;)\n if (choice !== &amp;#039;Yes&amp;#039;) {\n return new Response(&amp;#039;Unauthorised&amp;#039;, { status: 401 })\n }\n\n // hardcoded token request\n // debug here https://pepe-debug.research.cloudflare.com/?challenge=PrivateToken%20challenge=%22AAIAHnR1dG9yaWFsLmNsb3VkZmxhcmV3b3JrZXJzLmNvbSBE-oWKIYqMcyfiMXOZpcopzGBiYRvnFRP3uKknYPv1RQAicGVwZS1kZWJ1Zy5yZXNlYXJjaC5jbG91ZGZsYXJlLmNvbQ==%22,token-key=%22MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAQ8AMIIBCgKCAQEApqzusqnywE_3PZieStkf6_jwWF-nG6Es1nn5MRGoFSb3aXJFDTTIX8ljBSBZ0qujbhRDPx3ikWwziYiWtvEHSLqjeSWq-M892f9Dfkgpb3kpIfP8eBHPnhRKWo4BX_zk9IGT4H2Kd1vucIW1OmVY0Z_1tybKqYzHS299mvaQspkEcCo1UpFlMlT20JcxB2g2MRI9IZ87sgfdSu632J2OEr8XSfsppNcClU1D32iL_ETMJ8p9KlMoXI1MwTsI-8Kyblft66c7cnBKz3_z8ACdGtZ-HI4AghgW-m-yLpAiCrkCMnmIrVpldJ341yR6lq5uyPej7S8cvpvkScpXBSuyKwIDAQAB%22\n const body = b64ToU8(&amp;#039;AALoAYM+fDO53GVxBRuLbJhjFbwr0uZkl/m3NCNbiT6wal87GEuXuRw3iZUSZ3rSEqyHDhMlIqfyhAXHH8t8RP14ws3nQt1IBGE43Q9UinwglzrMY8e+k3Z9hQCEw7pBm/hVT/JNEPUKigBYSTN2IS59AUGHEB49fgZ0kA6ccu9BCdJBvIQcDyCcW5LCWCsNo57vYppIVzbV2r1R4v+zTk7IUDURTa4Mo7VYtg1krAWiFCoDxUOr+eTsc51bWqMtw2vKOyoM/20Wx2WJ0ox6JWdPvoBEsUVbENgBj11kB6/L9u2OW2APYyUR7dU9tGvExYkydXOfhRFJdKUypwKN70CiGw==&amp;#039;)\n // You can perform some check here to confirm the body is a valid token request\n\n console.log(&amp;#039;requesting token for tutorial.cloudflareworkers.com&amp;#039;)\n return fetch(ISSUER_URL, {\n method: &amp;#039;POST&amp;#039;,\n headers: { &amp;#039;content-type&amp;#039;: &amp;#039;application/private-token-request&amp;#039; },\n body: body,\n })\n}\n\nconst handleIssuerDirectory = async () =&amp;gt; {\n // These are fake issuers\n // Issuer data can be fetch at https://pp-issuer-public.research.cloudflare.com/.well-known/private-token-issuer-directory\n const TRUSTED_ISSUERS = {\n &amp;quot;issuer1&amp;quot;: { &amp;quot;token-keys&amp;quot;: [{ &amp;quot;token-type&amp;quot;: 2, &amp;quot;token-key&amp;quot;: &amp;quot;A==&amp;quot; }] },\n &amp;quot;issuer2&amp;quot;: { &amp;quot;token-keys&amp;quot;: [{ &amp;quot;token-type&amp;quot;: 2, &amp;quot;token-key&amp;quot;: &amp;quot;B==&amp;quot; }] },\n }\n return new Response(JSON.stringify(TRUSTED_ISSUERS), { headers: { &amp;quot;content-type&amp;quot;: &amp;quot;application/json&amp;quot; } })\n}\n\nconst handleRequest = (req) =&amp;gt; {\n const pathname = new URL(req.url).pathname\n console.log(pathname, req.url)\n if (pathname === &amp;#039;/v1/challenge&amp;#039;) {\n if (req.method === &amp;#039;POST&amp;#039;) {\n return handlePostChallenge(req)\n }\n return handleGetChallenge(req)\n }\n if (pathname === &amp;#039;/v1/private-token-issuer-directory&amp;#039;) {\n return handleIssuerDirectory()\n }\n return new Response(&amp;#039;Not found&amp;#039;, { status: 404 })\n}\n\naddEventListener(&amp;#039;fetch&amp;#039;, event =&amp;gt; {\n event.respondWith(handleRequest(event.request))\n})&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;The validation method above is simply checking if the user selected yes. Your method might be more complex, the wrapping stays the same.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card \&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5PnBuinoRKUpYjrBsHQbn/966c266e7de411503c5bf9a5dc9a184d/Screenshot-2024-01-04-at-10.30.04.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1356\&quot; height=\&quot;206\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Screenshot of the Yes/No Attester example&lt;/i&gt;&lt;/p&gt;&lt;p&gt;Because users might have multiple Attesters configured for a given Issuer, we recommend your Attester implements one additional endpoint exposing the keys of the issuers you are in contact with. You can try this code on &lt;a href=\&quot;https://cloudflareworkers.com/#4eeeef2fa895e519addb3ae442ee351d:https://tutorial.cloudflareworkers.com/v1/private-token-issuer-directory\&quot;&gt;Cloudflare Workers playground&lt;/a&gt;.&lt;/p&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;const handleIssuerDirectory = () =&amp;gt; {\n const TRUSTED_ISSUERS = {\n &amp;quot;issuer1&amp;quot;: { &amp;quot;token-keys&amp;quot;: [{ &amp;quot;token-type&amp;quot;: 2, &amp;quot;token-key&amp;quot;: &amp;quot;A==&amp;quot; }] },\n &amp;quot;issuer2&amp;quot;: { &amp;quot;token-keys&amp;quot;: [{ &amp;quot;token-type&amp;quot;: 2, &amp;quot;token-key&amp;quot;: &amp;quot;B==&amp;quot; }] },\n }\n return new Response(JSON.stringify(TRUSTED_ISSUERS), { headers: { &amp;quot;content-type&amp;quot;: &amp;quot;application/json&amp;quot; } })\n}\n\nexport default {\n fetch(req: Request) {\n const pathname = new URL(req.url).pathname\n if (pathname === &amp;#039;/v1/private-token-issuer-directory&amp;#039;) {\n return handleIssuerDirectory()\n }\n }\n}&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;Et voilà. You have an Attester that can be used directly with the Silk browser extension (&lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt;, &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt;). As you progress through your deployment, it can also be directly integrated into your applications.&lt;/p&gt;&lt;p&gt;If you would like to have a more advanced Attester and deployment pipeline, look at &lt;a href=\&quot;https://github.com/cloudflare/pp-attester\&quot;&gt;cloudflare/pp-attester&lt;/a&gt; template.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;as-an-issuer-foundation-consortium\&quot;&gt;As an Issuer - foundation, consortium&lt;/h3&gt;\n &lt;a href=\&quot;#as-an-issuer-foundation-consortium\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;We&amp;#39;ve mentioned the Issuer multiple times already. The role of an Issuer is to select a set of Attesters it wants to operate with, and communicate its public key to Origins. The whole cryptographic behavior of an Issuer is specified &lt;a href=\&quot;https://www.ietf.org/archive/id/draft-ietf-privacypass-protocol-16.html\&quot;&gt;by the IETF&lt;/a&gt; draft. In contrast to the Client and Attesters which have discretionary behavior, the Issuer is fully standardized. Their opportunity is to choose a signal that is strong enough for the Origin, while preserving privacy of Clients.&lt;/p&gt;&lt;p&gt;Cloudflare Research is operating a public Issuer for experimental purposes to use on &lt;a href=\&quot;https://pp-issuer-public.research.cloudflare.com\&quot;&gt;https://pp-issuer-public.research.cloudflare.com&lt;/a&gt;. It is the simplest solution to start experimenting with Privacy Pass today. Once it matures, you can consider joining a production Issuer, or deploying your own.&lt;/p&gt;&lt;p&gt;To deploy your own, you should:&lt;/p&gt;\n &lt;pre class=\&quot;language-bash\&quot;&gt;&lt;code class=\&quot;language-bash\&quot;&gt;git clone github.com/cloudflare/pp-issuer&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;Update wrangler.toml with your Cloudflare Workers account id and zone id. The open source Issuer API works as follows:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;/.well-known/private-token-issuer-directory returns the issuer configuration. Note it does not expose non-standard token-key-legacy&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;/token-request returns a token. This endpoint should be gated (by Cloudflare Access for instance) to only allow trusted attesters to call it&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;/admin/rotate to generate a new public key. This should only be accessible by your team, and be called prior to the issuer being available.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Then, &lt;code&gt;wrangler publish&lt;/code&gt;, and you&amp;#39;re good to onboard Attesters.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;development-of-silk-extension\&quot;&gt;Development of Silk extension&lt;/h2&gt;\n &lt;a href=\&quot;#development-of-silk-extension\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Just like the protocol, the browser technology on which Privacy Pass was proven viable has changed as well. For 5 years, the protocol got deployed along with a browser extension for Chrome and Firefox. In 2021, Chrome released a new version of extension configurations, usually referred to as &lt;a href=\&quot;https://developer.chrome.com/docs/extensions/mv3/intro/platform-vision/\&quot;&gt;Manifest version 3&lt;/a&gt; (MV3). Chrome also started enforcing this new configuration for all newly released extensions.&lt;/p&gt;&lt;p&gt;Privacy Pass &lt;i&gt;the extension&lt;/i&gt; is based on an agreed upon Privacy Pass &lt;a href=\&quot;https://datatracker.ietf.org/doc/draft-ietf-privacypass-auth-scheme/\&quot;&gt;&lt;i&gt;authentication protocol&lt;/i&gt;&lt;/a&gt;. Briefly looking at &lt;a href=\&quot;https://developer.chrome.com/docs/extensions/reference/webRequest/\&quot;&gt;Chrome’s API documentation&lt;/a&gt;, we should be able to use the onAuthRequired event. However, with PrivateToken authentication not yet being standard, there are no hooks provided by browsers for extensions to add logic to this event.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1iQsRopHuLfmHqjsppwImc/1a379a0cdd3de3e17de04811b1c08ac0/Screenshot-2024-01-04-at-10.32.44.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;2000\&quot; height=\&quot;932\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Image available under CC-BY-SA 4.0 provided by&lt;/i&gt; &lt;a href=\&quot;https://developer.chrome.com/docs/extensions/reference/webRequest/\&quot;&gt;&lt;i&gt;Google For Developers&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The approach we decided to use is to define a client side replay API. When a response comes with 401 WWW-Authenticate PrivateToken, the browser lets it through, but triggers the private token redemption flow. The original page is notified when a token has been retrieved, and replays the request. For this second request, the browser is able to attach an authorization token, and the request succeeds. This is an active replay performed by the client, rather than a transparent replay done by the platform. A specification is available on &lt;a href=\&quot;https://github.com/cloudflare/pp-browser-extension#chrome-support-via-client-replay-api\&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;We are looking forward to the standard progressing, and simplifying this part of the project. This should improve diversity in attestation methods. As we see in the next section, this is key to identifying new signals that can be leveraged by origins.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;a-standard-for-anonymous-credentials\&quot;&gt;A standard for anonymous credentials&lt;/h2&gt;\n &lt;a href=\&quot;#a-standard-for-anonymous-credentials\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;IP remains as a key identifier in the anti abuse system. At the same time, IP fingerprinting techniques have become a bigger concern and platforms have started to remove some of these ways of tracking users. To enable anti abuse systems to not rely on IP, while ensuring user privacy, Privacy Pass offers a reasonable alternative to deal with potentially abusive or suspicious traffic. The attestation methods vary and can be chosen as needed for a particular deployment. For example, Apple decided to back their attestation with hardware when using Privacy Pass as the authorization technology for iCloud Private Relay. Another example is Cloudflare Research which decided to deploy a Turnstile attester to signal a successful solve for Cloudflare’s challenge platform.&lt;/p&gt;&lt;p&gt;In all these deployments, Privacy Pass-like technology has allowed for specific bits of information to be shared. Instead of sharing your location, past traffic, and possibly your name and phone number simply by connecting to a website, your device is able to prove specific information to a third party in a privacy preserving manner. Which user information and attestation methods are sufficient to prevent abuse is an open question. We are looking to empower researchers with the release of this software to help in the quest for finding these answers. This could be via new experiments such as testing out new attestation methods, or fostering other privacy protocols by providing a framework for specific information sharing.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;future-recommendations\&quot;&gt;Future recommendations&lt;/h2&gt;\n &lt;a href=\&quot;#future-recommendations\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Just as we expect this latest version of Privacy Pass to lead to new applications and ideas we also expect further evolution of the standard and the clients that use it. Future development of Privacy Pass promises to cover topics like batch token issuance and rate limiting. From our work building and deploying this version of Privacy Pass we have encountered limitations that we expect to be resolved in the future as well.&lt;/p&gt;&lt;p&gt;The division of labor between Attesters and Issuers and the clear directions of trust relationships between the Origin and Issuer, and the Issuer and Attester make reasoning about the implications of a breach of trust clear. Issuers can trust more than one Attester, but since many current deployments of Privacy Pass do not identify the Attester that lead to issuance, a breach of trust in one Attester would render all tokens issued by any Issuer that trusts the Attester untrusted. This is because it would not be possible to tell which Attester was involved in the issuance process. Time will tell if this promotes a 1:1 correspondence between Attesters and Issuers.&lt;/p&gt;&lt;p&gt;The process of developing a browser extension supported by both Firefox and Chrome-based browsers can at times require quite baroque (and brittle) code paths. Privacy Pass the protocol seems a good fit for an extension of the &lt;a href=\&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onAuthRequired\&quot;&gt;webRequest.onAuthRequired&lt;/a&gt; browser event. Just as Privacy Pass appears as an alternate authentication message in the WWW-Authenticate HTTP header, browsers could fire the onAuthRequired event for Private Token authentication too and include and allow request blocking support within the onAuthRequired event. This seems a natural evolution of the use of this event which currently is limited to the now rather long-in-the-tooth Basic authentication.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;conclusion\&quot;&gt;Conclusion&lt;/h2&gt;\n &lt;a href=\&quot;#conclusion\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Privacy Pass provides a solution to one of the longstanding challenges of the web: anonymous authentication. By leveraging cryptography, the protocol allows websites to get the information they need from users, and solely this information. It&amp;#39;s already used by millions to help distinguish human requests from automated bots in a manner that is privacy protective and often seamless. We are excited by the protocol’s broad and growing adoption, and by the novel use cases that are unlocked by this latest version.&lt;/p&gt;&lt;p&gt;Cloudflare’s Privacy Pass implementations are available on GitHub, and are compliant with the standard. We have open-sourced a &lt;a href=\&quot;https://github.com/cloudflare?q=pp-&amp;type=all&amp;language=&amp;sort=#org-repositories\&quot;&gt;set of templates&lt;/a&gt; that can be used to implement Privacy Pass &lt;a href=\&quot;https://github.com/cloudflare/pp-origin\&quot;&gt;&lt;i&gt;Origins&lt;/i&gt;&lt;/a&gt;&lt;i&gt;,&lt;/i&gt; &lt;a href=\&quot;https://github.com/cloudflare/pp-issuer\&quot;&gt;&lt;i&gt;Issuers&lt;/i&gt;&lt;/a&gt;, and &lt;a href=\&quot;https://github.com/cloudflare/pp-attester\&quot;&gt;&lt;i&gt;Attesters&lt;/i&gt;&lt;/a&gt;, which leverage Cloudflare Workers to get up and running quickly.&lt;/p&gt;&lt;p&gt;For those looking to try Privacy Pass out for themselves right away, download the &lt;i&gt;Silk - Privacy Pass Client&lt;/i&gt; browser extensions (&lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt;, &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/pp-browser-extension\&quot;&gt;GitHub&lt;/a&gt;) and start browsing a web with fewer bot checks today.&lt;/p&gt;&quot;],&quot;published_at&quot;:[0,&quot;2024-01-04T16:07:22.000+00:00&quot;],&quot;updated_at&quot;:[0,&quot;2024-10-09T23:26:44.495Z&quot;],&quot;feature_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4wJwHdYhUiKvoSlAgSrFhG/cce2fbba90dbd93ef3cbc3e710e6f53b/privacy-pass-standard.png&quot;],&quot;tags&quot;:[1,[[0,{&quot;id&quot;:[0,&quot;1x7tpPmKIUCt19EDgM1Tsl&quot;],&quot;name&quot;:[0,&quot;Research&quot;],&quot;slug&quot;:[0,&quot;research&quot;]}],[0,{&quot;id&quot;:[0,&quot;3ZtL0yV0R4ScAreV1dTfIY&quot;],&quot;name&quot;:[0,&quot;Privacy Pass&quot;],&quot;slug&quot;:[0,&quot;privacy-pass&quot;]}],[0,{&quot;id&quot;:[0,&quot;kn8Lmy4luvCeAabblVvHH&quot;],&quot;name&quot;:[0,&quot;Firefox&quot;],&quot;slug&quot;:[0,&quot;firefox&quot;]}],[0,{&quot;id&quot;:[0,&quot;3skwJ34K0c3CEY1cNogR4n&quot;],&quot;name&quot;:[0,&quot;Chrome&quot;],&quot;slug&quot;:[0,&quot;chrome&quot;]}],[0,{&quot;id&quot;:[0,&quot;3BWeMuiOShelE7QM48sW9j&quot;],&quot;name&quot;:[0,&quot;Privacy&quot;],&quot;slug&quot;:[0,&quot;privacy&quot;]}]]],&quot;relatedTags&quot;:[0],&quot;authors&quot;:[1,[[0,{&quot;name&quot;:[0,&quot;Thibault Meunier&quot;],&quot;slug&quot;:[0,&quot;thibault&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1CqrdcRymVgEs1zRfSE6Xr/b8182164b0a8435b162bdd1246b7e91f/thibault.png&quot;],&quot;location&quot;:[0,null],&quot;website&quot;:[0,null],&quot;twitter&quot;:[0,&quot;@thibmeu&quot;],&quot;facebook&quot;:[0,null]}],[0,{&quot;name&quot;:[0,&quot;Cefan Daniel Rubin&quot;],&quot;slug&quot;:[0,&quot;cdrubin&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5EziMHboXmLjqS6FXaUODB/8f0daa841b1c260fae1be2a9d863457f/cdrubin.png&quot;],&quot;location&quot;:[0,null],&quot;website&quot;:[0,&quot;https://github.com/cdrubin&quot;],&quot;twitter&quot;:[0,null],&quot;facebook&quot;:[0,null]}],[0,{&quot;name&quot;:[0,&quot;Armando Faz-Hernández&quot;],&quot;slug&quot;:[0,&quot;armfazh&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1KZECWa5TCEPjjcRmbx9iT/c62263899934ff326df2b6864e42b104/armfazh.png&quot;],&quot;location&quot;:[0,null],&quot;website&quot;:[0,&quot;https://research.cloudflare.com/people/armando-faz/&quot;],&quot;twitter&quot;:[0,&quot;@armfazh&quot;],&quot;facebook&quot;:[0,null]}]]],&quot;meta_description&quot;:[0,&quot;In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the Privacy Pass protocol — including a set of open-source templates that can be used to implement Privacy Pass Origins, Issuers, and Attesters.&quot;],&quot;primary_author&quot;:[0,{}],&quot;localeList&quot;:[0,{&quot;name&quot;:[0,&quot;Privacy Pass: upgrading to the latest protocol version Config&quot;],&quot;enUS&quot;:[0,&quot;English for Locale&quot;],&quot;zhCN&quot;:[0,&quot;Translated for Locale&quot;],&quot;zhHansCN&quot;:[0,&quot;No Page for Locale&quot;],&quot;zhTW&quot;:[0,&quot;Translated for Locale&quot;],&quot;frFR&quot;:[0,&quot;No Page for Locale&quot;],&quot;deDE&quot;:[0,&quot;No Page for Locale&quot;],&quot;itIT&quot;:[0,&quot;No Page for Locale&quot;],&quot;jaJP&quot;:[0,&quot;No Page for Locale&quot;],&quot;koKR&quot;:[0,&quot;No Page for Locale&quot;],&quot;ptBR&quot;:[0,&quot;No Page for Locale&quot;],&quot;esLA&quot;:[0,&quot;No Page for Locale&quot;],&quot;esES&quot;:[0,&quot;No Page for Locale&quot;],&quot;enAU&quot;:[0,&quot;No Page for Locale&quot;],&quot;enCA&quot;:[0,&quot;No Page for Locale&quot;],&quot;enIN&quot;:[0,&quot;No Page for Locale&quot;],&quot;enGB&quot;:[0,&quot;No Page for Locale&quot;],&quot;idID&quot;:[0,&quot;No Page for Locale&quot;],&quot;ruRU&quot;:[0,&quot;No Page for Locale&quot;],&quot;svSE&quot;:[0,&quot;No Page for Locale&quot;],&quot;viVN&quot;:[0,&quot;No Page for Locale&quot;],&quot;plPL&quot;:[0,&quot;No Page for Locale&quot;],&quot;arAR&quot;:[0,&quot;No Page for Locale&quot;],&quot;nlNL&quot;:[0,&quot;No Page for Locale&quot;],&quot;thTH&quot;:[0,&quot;No Page for Locale&quot;],&quot;trTR&quot;:[0,&quot;No Page for Locale&quot;],&quot;heIL&quot;:[0,&quot;No Page for Locale&quot;],&quot;lvLV&quot;:[0,&quot;No Page for Locale&quot;],&quot;etEE&quot;:[0,&quot;No Page for Locale&quot;],&quot;ltLT&quot;:[0,&quot;No Page for Locale&quot;]}],&quot;url&quot;:[0,&quot;https://blog.cloudflare.com/privacy-pass-standard&quot;],&quot;metadata&quot;:[0,{&quot;title&quot;:[0,&quot;Privacy Pass: upgrading to the latest protocol version&quot;],&quot;description&quot;:[0,&quot;In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the Privacy Pass protocol — including a set of open-source templates that can be used to implement Privacy Pass Origins, Issuers, and Attesters.&quot;],&quot;imgPreview&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/22RHi3sA2tR4yPZCvPKYLe/87cdf4737a0d11148e569ff370819e38/privacy-pass-standard-fxK0y1.png&quot;]}]}],&quot;tagInfo&quot;:[0],&quot;authorInfo&quot;:[0],&quot;translatedPosts&quot;:[1,[]]}" ssr client="only" opts="{&quot;name&quot;:&quot;GoogleAnalytics&quot;,&quot;value&quot;:&quot;react&quot;}"></astro-island><script>(()=>{var l=(n,t)=>{let i=async()=>{await(await n())()},e=typeof t.value=="object"?t.value:void 0,s={timeout:e==null?void 0:e.timeout};"requestIdleCallback"in window?window.requestIdleCallback(i,s):setTimeout(i,s.timeout||200)};(self.Astro||(self.Astro={})).idle=l;window.dispatchEvent(new Event("astro:idle"));})();</script><astro-island uid="Z1UBhbN" prefix="r3" component-url="/_astro/Navigation.CSu6dGvY.js" component-export="Navigation" renderer-url="/_astro/client.DLO1yDVm.js" props="{&quot;title&quot;:[0,&quot;The Cloudflare Blog&quot;],&quot;logo&quot;:[0,&quot;//images.ctfassets.net/zkvhlag99gkb/69RwBidpiEHCDZ9rFVVk7T/092507edbed698420b89658e5a6d5105/CF_logo_stacked_blktype.png&quot;],&quot;pagesStore&quot;:[0,{&quot;page&quot;:[0,&quot;Post&quot;],&quot;slug&quot;:[0,&quot;privacy-pass-standard&quot;],&quot;translationsAvailable&quot;:[1,[[0,&quot;zh-cn&quot;],[0,&quot;zh-tw&quot;]]],&quot;navData&quot;:[1,[[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;J61Eszqn98amrYHq4IhTx&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T19:43:46.068Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-02-24T08:02:58.555Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,67],&quot;revision&quot;:[0,29],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;Zero Trust&quot;],&quot;name&quot;:[0,&quot;Zero Trust&quot;],&quot;slug&quot;:[0,&quot;zero-trust&quot;],&quot;featured&quot;:[0,true]}]}],[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;5kZtWqjqa7aOUoZr8NFGwI&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T19:43:26.040Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-02-18T05:02:47.858Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,106],&quot;revision&quot;:[0,33],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;Cloudflare Radar&quot;],&quot;name&quot;:[0,&quot;Radar&quot;],&quot;slug&quot;:[0,&quot;cloudflare-radar&quot;],&quot;featured&quot;:[0,true]}]}],[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;6Mp7ouACN2rT3YjL1xaXJx&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T19:42:46.231Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-02-18T05:02:46.749Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,66],&quot;revision&quot;:[0,23],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;Security&quot;],&quot;name&quot;:[0,&quot;Security&quot;],&quot;slug&quot;:[0,&quot;security&quot;],&quot;featured&quot;:[0,true]}]}],[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;6Foe3R8of95cWVnQwe5Toi&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T22:44:28.803Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-02-10T05:02:55.192Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,62],&quot;revision&quot;:[0,23],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;AI&quot;],&quot;name&quot;:[0,&quot;AI&quot;],&quot;slug&quot;:[0,&quot;ai&quot;],&quot;featured&quot;:[0,true]}]}],[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;6QktrXeEFcl4e2dZUTZVGl&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T19:43:20.198Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-02-04T17:23:05.518Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,57],&quot;revision&quot;:[0,24],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;Product News&quot;],&quot;name&quot;:[0,&quot;Product News&quot;],&quot;slug&quot;:[0,&quot;product-news&quot;],&quot;featured&quot;:[0,true]}]}],[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;4HIPcb68qM0e26fIxyfzwQ&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T19:43:21.536Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-02-04T17:19:33.689Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,59],&quot;revision&quot;:[0,26],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;Developers&quot;],&quot;name&quot;:[0,&quot;Developers&quot;],&quot;slug&quot;:[0,&quot;developers&quot;],&quot;featured&quot;:[0,true]}]}],[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;48r7QV00gLMWOIcM1CSDRy&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T19:54:22.790Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-02-04T17:17:33.067Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,59],&quot;revision&quot;:[0,26],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;Speed &amp; Reliability&quot;],&quot;name&quot;:[0,&quot;Speed &amp; Reliability&quot;],&quot;slug&quot;:[0,&quot;speed-and-reliability&quot;],&quot;featured&quot;:[0,true]}]}],[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;V86khSc459Yi1AhTlvtY7&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T19:46:53.657Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-02-04T17:12:59.473Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,57],&quot;revision&quot;:[0,21],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;Partners&quot;],&quot;name&quot;:[0,&quot;Partners&quot;],&quot;slug&quot;:[0,&quot;partners&quot;],&quot;featured&quot;:[0,true]}]}],[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;4g8tPriKOAUwdUT4jNPebe&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T19:46:40.927Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-02-04T17:11:28.566Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,55],&quot;revision&quot;:[0,24],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;Life at Cloudflare&quot;],&quot;name&quot;:[0,&quot;Life at Cloudflare&quot;],&quot;slug&quot;:[0,&quot;life-at-cloudflare&quot;],&quot;featured&quot;:[0,true]}]}],[0,{&quot;metadata&quot;:[0,{&quot;tags&quot;:[1,[]],&quot;concepts&quot;:[1,[]]}],&quot;sys&quot;:[0,{&quot;space&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Space&quot;],&quot;id&quot;:[0,&quot;zkvhlag99gkb&quot;]}]}],&quot;id&quot;:[0,&quot;16yk8DVbNNifxov5cWvAov&quot;],&quot;type&quot;:[0,&quot;Entry&quot;],&quot;createdAt&quot;:[0,&quot;2024-10-09T19:56:23.848Z&quot;],&quot;updatedAt&quot;:[0,&quot;2025-01-29T05:03:35.958Z&quot;],&quot;environment&quot;:[0,{&quot;sys&quot;:[0,{&quot;id&quot;:[0,&quot;master&quot;],&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;Environment&quot;]}]}],&quot;publishedVersion&quot;:[0,63],&quot;revision&quot;:[0,28],&quot;contentType&quot;:[0,{&quot;sys&quot;:[0,{&quot;type&quot;:[0,&quot;Link&quot;],&quot;linkType&quot;:[0,&quot;ContentType&quot;],&quot;id&quot;:[0,&quot;blogTag&quot;]}]}],&quot;locale&quot;:[0,&quot;en-US&quot;]}],&quot;fields&quot;:[0,{&quot;entryTitle&quot;:[0,&quot;Policy &amp; Legal&quot;],&quot;name&quot;:[0,&quot;Policy &amp; Legal&quot;],&quot;slug&quot;:[0,&quot;policy&quot;],&quot;featured&quot;:[0,true]}]}]]]}],&quot;locale&quot;:[0,&quot;en-us&quot;],&quot;translations&quot;:[0,{&quot;posts.by&quot;:[0,&quot;By&quot;],&quot;footer.gdpr&quot;:[0,&quot;GDPR&quot;],&quot;lang_blurb1&quot;:[0,&quot;This post is also available in {lang1}.&quot;],&quot;lang_blurb2&quot;:[0,&quot;This post is also available in {lang1} and {lang2}.&quot;],&quot;lang_blurb3&quot;:[0,&quot;This post is also available in {lang1}, {lang2} and {lang3}.&quot;],&quot;footer.press&quot;:[0,&quot;Press&quot;],&quot;header.title&quot;:[0,&quot;The Cloudflare Blog&quot;],&quot;search.clear&quot;:[0,&quot;Clear&quot;],&quot;search.filter&quot;:[0,&quot;Filter&quot;],&quot;search.source&quot;:[0,&quot;Source&quot;],&quot;footer.careers&quot;:[0,&quot;Careers&quot;],&quot;footer.company&quot;:[0,&quot;Company&quot;],&quot;footer.support&quot;:[0,&quot;Support&quot;],&quot;footer.the_net&quot;:[0,&quot;theNet&quot;],&quot;search.filters&quot;:[0,&quot;Filters&quot;],&quot;footer.our_team&quot;:[0,&quot;Our team&quot;],&quot;footer.webinars&quot;:[0,&quot;Webinars&quot;],&quot;page.more_posts&quot;:[0,&quot;More posts&quot;],&quot;posts.time_read&quot;:[0,&quot;{time} min read&quot;],&quot;search.language&quot;:[0,&quot;Language&quot;],&quot;footer.community&quot;:[0,&quot;Community&quot;],&quot;footer.resources&quot;:[0,&quot;Resources&quot;],&quot;footer.solutions&quot;:[0,&quot;Solutions&quot;],&quot;footer.trademark&quot;:[0,&quot;Trademark&quot;],&quot;header.subscribe&quot;:[0,&quot;Subscribe&quot;],&quot;footer.compliance&quot;:[0,&quot;Compliance&quot;],&quot;footer.free_plans&quot;:[0,&quot;Free plans&quot;],&quot;footer.impact_ESG&quot;:[0,&quot;Impact/ESG&quot;],&quot;posts.follow_on_X&quot;:[0,&quot;Follow on X&quot;],&quot;footer.help_center&quot;:[0,&quot;Help center&quot;],&quot;footer.network_map&quot;:[0,&quot;Network Map&quot;],&quot;header.please_wait&quot;:[0,&quot;Please Wait&quot;],&quot;page.related_posts&quot;:[0,&quot;Related posts&quot;],&quot;search.result_stat&quot;:[0,&quot;Results &lt;strong&gt;{search_range}&lt;/strong&gt; of &lt;strong&gt;{search_total}&lt;/strong&gt; for &lt;strong&gt;{search_keyword}&lt;/strong&gt;&quot;],&quot;footer.case_studies&quot;:[0,&quot;Case Studies&quot;],&quot;footer.connect_2024&quot;:[0,&quot;Connect 2024&quot;],&quot;footer.terms_of_use&quot;:[0,&quot;Terms of Use&quot;],&quot;footer.white_papers&quot;:[0,&quot;White Papers&quot;],&quot;footer.cloudflare_tv&quot;:[0,&quot;Cloudflare TV&quot;],&quot;footer.community_hub&quot;:[0,&quot;Community Hub&quot;],&quot;footer.compare_plans&quot;:[0,&quot;Compare plans&quot;],&quot;footer.contact_sales&quot;:[0,&quot;Contact Sales&quot;],&quot;header.contact_sales&quot;:[0,&quot;Contact Sales&quot;],&quot;header.email_address&quot;:[0,&quot;Email Address&quot;],&quot;page.error.not_found&quot;:[0,&quot;Page not found&quot;],&quot;footer.developer_docs&quot;:[0,&quot;Developer docs&quot;],&quot;footer.privacy_policy&quot;:[0,&quot;Privacy Policy&quot;],&quot;footer.request_a_demo&quot;:[0,&quot;Request a demo&quot;],&quot;page.continue_reading&quot;:[0,&quot;Continue reading&quot;],&quot;footer.analysts_report&quot;:[0,&quot;Analyst reports&quot;],&quot;footer.for_enterprises&quot;:[0,&quot;For enterprises&quot;],&quot;footer.getting_started&quot;:[0,&quot;Getting Started&quot;],&quot;footer.learning_center&quot;:[0,&quot;Learning Center&quot;],&quot;footer.project_galileo&quot;:[0,&quot;Project Galileo&quot;],&quot;pagination.newer_posts&quot;:[0,&quot;Newer Posts&quot;],&quot;pagination.older_posts&quot;:[0,&quot;Older Posts&quot;],&quot;posts.social_buttons.x&quot;:[0,&quot;Discuss on X&quot;],&quot;search.icon_aria_label&quot;:[0,&quot;Search&quot;],&quot;search.source_location&quot;:[0,&quot;Source/Location&quot;],&quot;footer.about_cloudflare&quot;:[0,&quot;About Cloudflare&quot;],&quot;footer.athenian_project&quot;:[0,&quot;Athenian Project&quot;],&quot;footer.become_a_partner&quot;:[0,&quot;Become a partner&quot;],&quot;footer.cloudflare_radar&quot;:[0,&quot;Cloudflare Radar&quot;],&quot;footer.network_services&quot;:[0,&quot;Network services&quot;],&quot;footer.trust_and_safety&quot;:[0,&quot;Trust &amp; Safety&quot;],&quot;header.get_started_free&quot;:[0,&quot;Get Started Free&quot;],&quot;page.search.placeholder&quot;:[0,&quot;Search Cloudflare&quot;],&quot;footer.cloudflare_status&quot;:[0,&quot;Cloudflare Status&quot;],&quot;footer.cookie_preference&quot;:[0,&quot;Cookie Preferences&quot;],&quot;header.valid_email_error&quot;:[0,&quot;Must be valid email.&quot;],&quot;search.result_stat_empty&quot;:[0,&quot;Results &lt;strong&gt;{search_range}&lt;/strong&gt; of &lt;strong&gt;{search_total}&lt;/strong&gt;&quot;],&quot;footer.connectivity_cloud&quot;:[0,&quot;Connectivity cloud&quot;],&quot;footer.developer_services&quot;:[0,&quot;Developer services&quot;],&quot;footer.investor_relations&quot;:[0,&quot;Investor relations&quot;],&quot;page.not_found.error_code&quot;:[0,&quot;Error Code: 404&quot;],&quot;search.autocomplete_title&quot;:[0,&quot;Insert a query. Press enter to send&quot;],&quot;footer.logos_and_press_kit&quot;:[0,&quot;Logos &amp; press kit&quot;],&quot;footer.application_services&quot;:[0,&quot;Application services&quot;],&quot;footer.get_a_recommendation&quot;:[0,&quot;Get a recommendation&quot;],&quot;posts.social_buttons.reddit&quot;:[0,&quot;Discuss on Reddit&quot;],&quot;footer.sse_and_sase_services&quot;:[0,&quot;SSE and SASE services&quot;],&quot;page.not_found.outdated_link&quot;:[0,&quot;You may have used an outdated link, or you may have typed the address incorrectly.&quot;],&quot;footer.report_security_issues&quot;:[0,&quot;Report Security Issues&quot;],&quot;page.error.error_message_page&quot;:[0,&quot;Sorry, we can&#39;t find the page you are looking for.&quot;],&quot;header.subscribe_notifications&quot;:[0,&quot;Subscribe to receive notifications of new posts:&quot;],&quot;footer.cloudflare_for_campaigns&quot;:[0,&quot;Cloudflare for Campaigns&quot;],&quot;header.subscription_confimation&quot;:[0,&quot;Subscription confirmed. Thank you for subscribing!&quot;],&quot;posts.social_buttons.hackernews&quot;:[0,&quot;Discuss on Hacker News&quot;],&quot;footer.diversity_equity_inclusion&quot;:[0,&quot;Diversity, equity &amp; inclusion&quot;],&quot;footer.critical_infrastructure_defense_project&quot;:[0,&quot;Critical Infrastructure Defense Project&quot;]}]}" ssr client="idle" opts="{&quot;name&quot;:&quot;NavigationComponent&quot;,&quot;value&quot;:true}" await-children><header class="flex flex-row flex-wrap justify-between items-flex-end mw8 center mv3 pl3 pr1"><div class="w-100 flex items-flex-end justify-between justify-start-l"><div class="w-100 tr flex justify-end"><div class="flex justify-between items-center"><span class="dn di-l pr1"><a href="https://dash.cloudflare.com/sign-up" class="f1 blue1 dn di-l b no-underline underline-hover" target="_blank" rel="noreferrer">Get Started Free</a></span><span class="f1 gray4 dn di-l pr1">|</span><span class="dn di-l"><a target="_blank" href="https://www.cloudflare.com/plans/enterprise/contact/" class="f1 gray4 no-underline underline-hover pr1" rel="noreferrer">Contact Sales</a></span><span class="f1 gray4 dn di-l pr1">|</span><div class="relative flex cf-dropdown"><div class="flex items-center" dir="ltr"><button type="button" class="f1 gray4 no-underline language-picker js-language-picker" style="background:transparent;border:none;padding:0"><span class="language-picker__globe-icon"></span><span class="language-picker__caret-icon ph1">▼</span></button></div></div></div></div></div><div class="w-100 w-50-l flex items-end nb5 nb1-l"><a href="/" class="header-logo mr4 dn db-l"><img class="header-logo" src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/69RwBidpiEHCDZ9rFVVk7T/092507edbed698420b89658e5a6d5105/CF_logo_stacked_blktype.png" alt="The Cloudflare Blog" width="170" height="57"/></a><h2 class="mt0 mb1 dn di-l"><a href="/" class="fw5 f5 gray3 no-underline"><span class="dn di-l">The Cloudflare Blog</span></a></h2></div><div class="w-100 w-50-l dn db-l"><div class="w-100 tr mkto-sub-message"><p class="f2">Subscribe to receive notifications of new posts:</p></div><div class="w-100 tr"><div class="marketo-form-container"><form id="mktoForm_1653"><div class="top-subscribe-form-container"><div class="top-subscribe-form-field"><input placeholder="Email Address" class="top-subscribe-form-input" name="email" type="email" title="Must be valid email."/></div><button class="top-subscribe-form-button" type="button">Subscribe</button></div></form></div></div></div></header><nav dir="ltr" class="bb b--black-10 db dn-l w-100 ph3 "><div class=" flex justify-between items-center" style="height:44px"><a href="/search/"><img class="h-6 w-6" src="/images/magnifier.svg" alt="magnifier icon"/></a><button type="button" style="background:transparent;border:none"><img src="/images/hamburger.svg" alt="hamburger menu"/></button></div><div class="js-mobile-nav-container dn"><div class="flex flex-column flex-wrap bg-gray9 o-95 absolute w-90 ph3 z-1"><div class="pv3 ph2 tl"><a href="/tag/zero-trust/" class="no-underline gray1 f4 fw7">Zero Trust</a></div><div class="pv3 ph2 tl"><a href="/tag/cloudflare-radar/" class="no-underline gray1 f4 fw7">Radar</a></div><div class="pv3 ph2 tl"><a href="/tag/security/" class="no-underline gray1 f4 fw7">Security</a></div><div class="pv3 ph2 tl"><a href="/tag/ai/" class="no-underline gray1 f4 fw7">AI</a></div><div class="pv3 ph2 tl"><a href="/tag/product-news/" class="no-underline gray1 f4 fw7">Product News</a></div><div class="pv3 ph2 tl"><a href="/tag/developers/" class="no-underline gray1 f4 fw7">Developers</a></div><div class="pv3 ph2 tl"><a href="/tag/speed-and-reliability/" class="no-underline gray1 f4 fw7">Speed &amp; Reliability</a></div><div class="pv3 ph2 tl"><a href="/tag/partners/" class="no-underline gray1 f4 fw7">Partners</a></div><div class="pv3 ph2 tl"><a href="/tag/life-at-cloudflare/" class="no-underline gray1 f4 fw7">Life at Cloudflare</a></div><div class="pv3 ph2 tl"><a href="/tag/policy/" class="no-underline gray1 f4 fw7">Policy &amp; Legal</a></div></div></div></nav><nav id="nav" class="w-100 bb-0 bb-l b--black-10 z-1"><div id="desktop-nav-items-container" class="flex flex-wrap justify-between items-center mw8 center mv3 mv0-l"><div data-tag="zero-trust" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/zero-trust/" class="no-underline gray1 f2 fw5 pv3">Zero Trust</a></div><div data-tag="cloudflare-radar" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/cloudflare-radar/" class="no-underline gray1 f2 fw5 pv3">Radar</a></div><div data-tag="security" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/security/" class="no-underline gray1 f2 fw5 pv3">Security</a></div><div data-tag="ai" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/ai/" class="no-underline gray1 f2 fw5 pv3">AI</a></div><div data-tag="product-news" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/product-news/" class="no-underline gray1 f2 fw5 pv3">Product News</a></div><div data-tag="developers" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/developers/" class="no-underline gray1 f2 fw5 pv3">Developers</a></div><div data-tag="speed-and-reliability" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/speed-and-reliability/" class="no-underline gray1 f2 fw5 pv3">Speed &amp; Reliability</a></div><div data-tag="partners" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/partners/" class="no-underline gray1 f2 fw5 pv3">Partners</a></div><div data-tag="life-at-cloudflare" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/life-at-cloudflare/" class="no-underline gray1 f2 fw5 pv3">Life at Cloudflare</a></div><div data-tag="policy" class="nav-item nav-item-desktop ml3 mr2 dn db-l pv3"><a href="/tag/policy/" class="no-underline gray1 f2 fw5 pv3">Policy &amp; Legal</a></div><div class="nav-item ml2 mr3 dn db-l pv3" data-tag="search icon"><a href="/search/"><img id="search-icon" class="h-6 w-6" src="/images/magnifier.svg" alt="magnifier icon"/></a></div></div></nav><!--astro:end--></astro-island><progress class="reading-progress" value="0" max="100" aria-label="Reading progress"></progress><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).load=e;window.dispatchEvent(new Event("astro:load"));})();</script><astro-island uid="Z1vwFx4" prefix="r1" component-url="/_astro/Post.CgzUE9si.js" component-export="Post" renderer-url="/_astro/client.DLO1yDVm.js" props="{&quot;post&quot;:[0,{&quot;id&quot;:[0,&quot;47vZ5BZfqt5cU38XabKyUA&quot;],&quot;title&quot;:[0,&quot;Privacy Pass: upgrading to the latest protocol version&quot;],&quot;slug&quot;:[0,&quot;privacy-pass-standard&quot;],&quot;excerpt&quot;:[0,&quot;In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the Privacy Pass protocol — including a set of open-source templates that can be used to implement Privacy Pass Origins, Issuers, and Attesters&quot;],&quot;featured&quot;:[0,false],&quot;html&quot;:[0,&quot;&lt;p&gt;&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card \&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2LZJxp89GI8PxGwGSPRQJL/9cfe61e756369dcad6cb78f5ad89ec1f/image9.png\&quot; alt=\&quot;Privacy Pass: Upgrading to the latest protocol version\&quot; class=\&quot;kg-image\&quot; width=\&quot;1800\&quot; height=\&quot;1013\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;enabling-anonymous-access-to-the-web-with-privacy-preserving-cryptography\&quot;&gt;Enabling anonymous access to the web with privacy-preserving cryptography&lt;/h2&gt;\n &lt;a href=\&quot;#enabling-anonymous-access-to-the-web-with-privacy-preserving-cryptography\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;The challenge of telling humans and bots apart is almost as old as the web itself. From online ticket vendors to dating apps, to ecommerce and finance — there are many legitimate reasons why you&amp;#39;d want to know if it&amp;#39;s a person or a machine knocking on the front door of your website.&lt;/p&gt;&lt;p&gt;Unfortunately, the tools for the web have traditionally been clunky and sometimes involved a bad user experience. None more so than the CAPTCHA — an irksome solution that humanity wastes a &lt;a href=\&quot;/introducing-cryptographic-attestation-of-personhood/\&quot;&gt;staggering&lt;/a&gt; amount of time on. A more subtle but intrusive approach is IP tracking, which uses IP addresses to identify and take action on suspicious traffic, but that too can come with &lt;a href=\&quot;/consequences-of-ip-blocking/\&quot;&gt;unforeseen consequences&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;And yet, the problem of distinguishing legitimate human requests from automated bots remains as vital as ever. This is why for years Cloudflare has invested in the Privacy Pass protocol — a novel approach to establishing a user’s identity by relying on cryptography, rather than crude puzzles — all while providing a streamlined, privacy-preserving, and often frictionless experience to end users.&lt;/p&gt;&lt;p&gt;Cloudflare began &lt;a href=\&quot;/cloudflare-supports-privacy-pass/\&quot;&gt;supporting Privacy Pass&lt;/a&gt; in 2017, with the release of browser extensions for Chrome and Firefox. Web admins with their sites on Cloudflare would have Privacy Pass enabled in the Cloudflare Dash; users who installed the extension in their browsers would see fewer CAPTCHAs on websites they visited that had Privacy Pass enabled.&lt;/p&gt;&lt;p&gt;Since then, Cloudflare &lt;a href=\&quot;/end-cloudflare-captcha/\&quot;&gt;stopped issuing CAPTCHAs&lt;/a&gt;, and Privacy Pass has come a long way. Apple uses a version of Privacy Pass for its &lt;a href=\&quot;https://developer.apple.com/news/?id=huqjyh7k\&quot;&gt;Private Access Tokens&lt;/a&gt; system which works in tandem with a device’s secure enclave to attest to a user’s humanity. And Cloudflare uses Privacy Pass as an important signal in our Web Application Firewall and Bot Management products — which means millions of websites natively offer Privacy Pass.&lt;/p&gt;&lt;p&gt;In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the &lt;a href=\&quot;https://www.ietf.org/archive/id/draft-ietf-privacypass-protocol-16.html\&quot;&gt;Privacy Pass protocol&lt;/a&gt; — including a &lt;a href=\&quot;https://github.com/cloudflare?q=pp-&amp;type=all&amp;language=&amp;sort=#org-repositories\&quot;&gt;set of open-source templates&lt;/a&gt; that can be used to implement Privacy Pass &lt;a href=\&quot;https://github.com/cloudflare/pp-origin\&quot;&gt;&lt;i&gt;Origins&lt;/i&gt;&lt;/a&gt;&lt;i&gt;,&lt;/i&gt; &lt;a href=\&quot;https://github.com/cloudflare/pp-issuer\&quot;&gt;&lt;i&gt;Issuers&lt;/i&gt;&lt;/a&gt;, and &lt;a href=\&quot;https://github.com/cloudflare/pp-attester\&quot;&gt;&lt;i&gt;Attesters&lt;/i&gt;&lt;/a&gt;. These are based on Cloudflare Workers, and are the easiest way to get started with a new deployment of Privacy Pass.&lt;/p&gt;&lt;p&gt;To complement the updated implementations, we are releasing a new version of our Privacy Pass browser extensions (&lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt;, &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt;), which are rolling out with the name: &lt;i&gt;Silk - Privacy Pass Client&lt;/i&gt;. Users of these extensions can expect to see fewer bot-checks around the web, and will be contributing to research about privacy preserving signals via a set of trusted attesters, which can be configured in the extension’s settings panel.&lt;/p&gt;&lt;p&gt;Finally, we will discuss how Privacy Pass can be used for an array of scenarios beyond differentiating bot from human traffic.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Notice to our users&lt;/b&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;If you use the Privacy Pass API that controls Privacy Pass configuration on Cloudflare, you can remove these calls. This API is no longer needed since Privacy Pass is now included by default in our Challenge Platform. Out of an abundance of caution for our customers, we are doing a &lt;a href=\&quot;https://developers.cloudflare.com/fundamentals/api/reference/deprecations/\&quot;&gt;four-month deprecation notice&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;If you have the Privacy Pass extension installed, it should automatically update to &lt;i&gt;Silk - Privacy Pass Client&lt;/i&gt; (&lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt;, &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt;) over the next few days. We have renamed it to keep the distinction clear between the protocol itself and a client of the protocol.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;brief-history\&quot;&gt;Brief history&lt;/h2&gt;\n &lt;a href=\&quot;#brief-history\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;In the last decade, we&amp;#39;ve seen the &lt;a href=\&quot;/next-generation-privacy-protocols/\&quot;&gt;rise of protocols&lt;/a&gt; with privacy at their core, including &lt;a href=\&quot;/building-privacy-into-internet-standards-and-how-to-make-your-app-more-private-today/\&quot;&gt;Oblivious HTTP (OHTTP)&lt;/a&gt;, &lt;a href=\&quot;/deep-dive-privacy-preserving-measurement/\&quot;&gt;Distributed aggregation protocol (DAP)&lt;/a&gt;, and &lt;a href=\&quot;/unlocking-quic-proxying-potential/\&quot;&gt;MASQUE&lt;/a&gt;. These protocols improve privacy when browsing and interacting with services online. By protecting users&amp;#39; privacy, these protocols also ask origins and website owners to revise their expectations around the data they can glean from user traffic. This might lead them to reconsider existing assumptions and mitigations around suspicious traffic, such as &lt;a href=\&quot;/consequences-of-ip-blocking/\&quot;&gt;IP filtering&lt;/a&gt;, which often has unintended consequences.&lt;/p&gt;&lt;p&gt;In 2017, Cloudflare announced &lt;a href=\&quot;/cloudflare-supports-privacy-pass/\&quot;&gt;support for Privacy Pass&lt;/a&gt;. At launch, this meant improving content accessibility for web users who would see a lot of interstitial pages (such as &lt;a href=\&quot;https://www.cloudflare.com/learning/bots/how-captchas-work/\&quot;&gt;CAPTCHAs&lt;/a&gt;) when browsing websites protected by Cloudflare. Privacy Pass tokens provide a signal about the user’s capabilities to website owners while protecting their privacy by ensuring each token redemption is unlinkable to its issuance context. Since then, the technology has turned into a &lt;a href=\&quot;https://datatracker.ietf.org/wg/privacypass/documents/\&quot;&gt;fully fledged protocol&lt;/a&gt; used by millions thanks to academic and industry effort. The existing browser extension accounts for hundreds of thousands of downloads. During the same time, Cloudflare has dramatically evolved the way it allows customers to challenge their visitors, being &lt;a href=\&quot;/end-cloudflare-captcha/\&quot;&gt;more flexible about the signals&lt;/a&gt; it receives, and &lt;a href=\&quot;/turnstile-ga/\&quot;&gt;moving away from CAPTCHA&lt;/a&gt; as a binary legitimacy signal.&lt;/p&gt;&lt;p&gt;Deployments of this research have led to a broadening of use cases, opening the door to different kinds of attestation. An attestation is a cryptographically-signed data point supporting facts. This can include a signed token indicating that the user has successfully solved a CAPTCHA, having a user’s hardware attest it’s untampered, or a piece of data that an attester can verify against another data source.&lt;/p&gt;&lt;p&gt;For example, in 2022, Apple hardware devices began to offer Privacy Pass tokens to websites who wanted to reduce how often they show CAPTCHAs, by using the hardware itself as an attestation factor. Before showing images of buses and fire hydrants to users, CAPTCHA providers can request a &lt;a href=\&quot;https://developer.apple.com/news/?id=huqjyh7k\&quot;&gt;Private Access Token&lt;/a&gt; (PAT). This native support does not require installing extensions, or any user action to benefit from a smoother and more private web browsing experience.&lt;/p&gt;&lt;p&gt;Below is a brief overview of changes to the protocol we participated in:&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3YImfph78oDPj3kgEcyvV6/37bcd89ffcfff8b636b00c8e931f3218/image8.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1808\&quot; height=\&quot;631\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;The timeline presents cryptographic changes, community inputs, and industry collaborations. These changes helped shape better standards for the web, such as VOPRF (&lt;a href=\&quot;https://www.rfc-editor.org/rfc/rfc9497\&quot;&gt;RFC 9497&lt;/a&gt;), or RSA Blind Signatures (&lt;a href=\&quot;https://www.rfc-editor.org/rfc/rfc9474\&quot;&gt;RFC 9474&lt;/a&gt;). In the next sections, we dive in the Privacy Pass protocol to understand its ins and outs.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;anonymous-credentials-in-real-life\&quot;&gt;Anonymous credentials in real life&lt;/h2&gt;\n &lt;a href=\&quot;#anonymous-credentials-in-real-life\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Before explaining the protocol in more depth, let&amp;#39;s use an analogy. You are at a music festival. You bought your ticket online with a student discount. When you arrive at the gates, an agent scans your ticket, checks your student status, and gives you a yellow wristband and two drink tickets.&lt;/p&gt;&lt;p&gt;During the festival, you go in and out by showing your wristband. When a friend asks you to grab a drink, you pay with your tickets. One for your drink and one for your friend. You give your tickets to the bartender, they check the tickets, and give you a drink. The characteristics that make this interaction private is that the drinks tickets cannot be traced back to you or your payment method, but they can be verified as having been unused and valid for purchase of a drink.&lt;/p&gt;&lt;p&gt;In the web use case, the Internet is a festival. When you arrive at the gates of a website, an agent scans your request, and gives you a session cookie as well as two Privacy Pass tokens. They could have given you just one token, or more than two, but in our example ‘two tokens’ is the given website’s policy. You can use these tokens to attest your humanity, to authenticate on certain websites, or even to confirm the legitimacy of your hardware.&lt;/p&gt;&lt;p&gt;Now, you might wonder if this is a technique we have been using for years, why do we need fancy cryptography and standardization efforts? Well, unlike at a real-world music festival where most people don’t carry around photocopiers, on the Internet it is pretty easy to copy tokens. For instance, how do we stop people using a token twice? We could put a unique number on each token, and check it is not spent twice, but that would allow the gate attendant to tell the bartender which numbers were linked to which person. So, we need cryptography.&lt;/p&gt;&lt;p&gt;When another website presents a challenge to you, you provide your Privacy Pass token and are then allowed to view a gallery of beautiful cat pictures. The difference with the festival is this challenge might be interactive, which would be similar to the bartender giving you a numbered ticket which would have to be signed by the agent before getting a drink. The website owner can verify that the token is valid but has no way of tracing or connecting the user back to the action that provided them with the Privacy Pass tokens. With Privacy Pass terminology, you are a Client, the website is an Origin, the agent is an Attester, and the bar an Issuer. The next section goes through these in more detail.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;privacy-pass-protocol\&quot;&gt;Privacy Pass protocol&lt;/h2&gt;\n &lt;a href=\&quot;#privacy-pass-protocol\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Privacy Pass specifies an extensible protocol for creating and redeeming anonymous and transferable tokens. In fact, Apple has their own implementation with Private Access Tokens (PAT), and later we will describe another implementation with the Silk browser extension. Given PAT was the first to implement the IETF defined protocol, Privacy Pass is sometimes referred to as PAT in the literature.&lt;/p&gt;&lt;p&gt;The protocol is generic, and defines four components:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Client: Web user agent with a Privacy Pass enabled browser. This could be your &lt;a href=\&quot;/eliminating-captchas-on-iphones-and-macs-using-new-standard/\&quot;&gt;Apple device with PAT&lt;/a&gt;, or your web browser with &lt;a href=\&quot;https://github.com/cloudflare/pp-browser-extension\&quot;&gt;the Silk extension installed&lt;/a&gt;. Typically, this is the actor who is requesting content and is asked to share some attribute of themselves.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Origin: Serves content requested by the Client. The Origin trusts one or more Issuers, and presents Privacy Pass challenges to the Client. For instance, Cloudflare Managed Challenge is a Privacy Pass origin serving two Privacy Pass challenges: one for Apple PAT Issuer, one for Cloudflare Research Issuer.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Issuer: Signs Privacy Pass tokens upon request from a trusted party, either an Attester or a Client depending on the deployment model. Different Issuers have their own set of trusted parties, depending on the security level they are looking for, as well as their privacy considerations. An Issuer validating device integrity should use different methods that vouch for this attribute to acknowledge the diversity of Client configurations.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Attester: Verifies an attribute of the Client and when satisfied requests a signed Privacy Pass token from the Issuer to pass back to the Client. Before vouching for the Client, an Attester may ask the Client to complete a specific task. This task could be a CAPTCHA, a location check, or age verification or some other check that will result in a single binary result. The Privacy Pass token will then share this one-bit of information in an unlinkable manner.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;They interact as illustrated below.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7tX1xRQv6Ltif1NRj2fCOa/eeb412fa39d73e2232f4b062d95cd708/Frame-699-1-.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1492\&quot; height=\&quot;780\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;Let&amp;#39;s dive into what&amp;#39;s really happening with an example. The User wants to access an Origin, say store.example.com. This website has suffered attacks or abuse in the past, and the site is using Privacy Pass to help avoid these going forward. To that end, the Origin returns &lt;a href=\&quot;https://www.rfc-editor.org/rfc/rfc9110#field.www-authenticate\&quot;&gt;an authentication request&lt;/a&gt; to the Client: &lt;code&gt;WWW-Authenticate: PrivateToken challenge=&amp;quot;A==&amp;quot;,token-key=&amp;quot;B==&amp;quot;&lt;/code&gt;. In this way, the Origin signals that it accepts tokens from the Issuer with public key “B==” to satisfy the challenge. That Issuer in turn trusts reputable Attesters to vouch for the Client not being an attacker by means of the presence of a cookie, CAPTCHA, Turnstile, or &lt;a href=\&quot;/introducing-cryptographic-attestation-of-personhood/\&quot;&gt;CAP challenge&lt;/a&gt; for example. For accessibility reasons for our example, let us say that the Client likely prefers the Turnstile method. The User’s browser prompts them to solve a Turnstile challenge. On success, it contacts the Issuer “B==” with that solution, and then replays the initial requests to store.example.com, this time sending along the token header &lt;code&gt;Authorization: PrivateToken token=&amp;quot;C==&amp;quot;&lt;/code&gt;, which the Origin accepts and returns your desired content to the Client. And that’s it.&lt;/p&gt;&lt;p&gt;We’ve described the Privacy Pass authentication protocol. While Basic authentication (&lt;a href=\&quot;https://www.rfc-editor.org/rfc/rfc7617\&quot;&gt;RFC 7671&lt;/a&gt;) asks you for a username and a password, the PrivateToken authentication scheme allows the browser to be more flexible on the type of check, while retaining privacy. The Origin store.example.com does not know your attestation method, they just know you are reputable according to the token issuer. In the same spirit, the Issuer &amp;quot;B==&amp;quot; does not see your IP, nor the website you are visiting. This separation between issuance and redemption, also referred to as unlinkability, is what &lt;a href=\&quot;https://www.ietf.org/archive/id/draft-ietf-privacypass-architecture-16.html\&quot;&gt;makes Privacy Pass private&lt;/a&gt;.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;demo-time\&quot;&gt;Demo time&lt;/h2&gt;\n &lt;a href=\&quot;#demo-time\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;To put the above in practice, let’s see how the protocol works with Silk, a browser extension providing Privacy Pass support. First, download the relevant &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt; or &lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt; extension.&lt;/p&gt;&lt;p&gt;Then, head to &lt;a href=\&quot;https://demo-pat.research.cloudflare.com/login\&quot;&gt;https://demo-pat.research.cloudflare.com/login&lt;/a&gt;. The page returns a 401 Privacy Pass Token not presented. In fact, the origin expects you to perform a PrivateToken authentication. If you don’t have the extension installed, the flow stops here. If you have the extension installed, the extension is going to orchestrate the flow required to get you a token requested by the Origin.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ZPDrhytZNVoB81Q7RILu5/7c115c9ed069aa09694373ec1adcc4d0/image10.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;With the extension installed, you are directed to a new tab &lt;a href=\&quot;https://pp-attester-turnstile.research.cloudflare.com/challenge\&quot;&gt;https://pp-attester-turnstile.research.cloudflare.com/challenge&lt;/a&gt;. This is a page provided by an Attester able to deliver you a token signed by the Issuer request by the Origin. In this case, the Attester checks you’re able to solve a Turnstile challenge.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7fmDWo3548oMK8jgZ7V0Kd/94ee9ab9bc1df6fee6e6a76dc4fb3e02/image2.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;You click, and that’s it. The Turnstile challenge solution is sent to the Attester, which upon validation, sends back a token from the requested Issuer. This page appears for a very short time, as once the extension has the token, the challenge page is no longer needed.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3KROIlp9njiXlfceDzRU7W/d1e306da3012c949e3fa5b80934f83a4/image11.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;The extension, now having a token requested by the Origin, sends your initial request for a second time, with an Authorization header containing a valid Issuer PrivateToken. Upon validation, the Origin allows you in with a 200 Privacy Pass Token valid!&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3qOSkMc5wIqS50CuNNNoZY/b36b88ba01ffa1c5f4d78727e602062f/image3.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;If you want to check behind the scenes, you can right-click on the extension logo and go to the preference/options page. It contains a list of attesters trusted by the extension, one per line. You can add your own attestation method (API described below). This allows the Client to decide on their preferred attestation methods.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/78BCHYQuOBC2aFlnPshu83/c6ee6b54d1d24b6f92f34577267a1146/image7.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1596\&quot; height=\&quot;1105\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;privacy-pass-protocol-extended\&quot;&gt;Privacy Pass protocol — extended&lt;/h2&gt;\n &lt;a href=\&quot;#privacy-pass-protocol-extended\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;The Privacy Pass protocol is new and not a standard yet, which implies that it’s not uniformly supported on all platforms. To improve flexibility beyond the existing standard proposal, we are introducing two mechanisms: an API for Attesters, and a replay API for web clients. The API for attesters allows developers to build new attestation methods, which only need to provide their URL to interface with the Silk browser extension. The replay API for web clients is a mechanism to enable websites to cooperate with the extension to make PrivateToken authentication work on browsers with Chrome user agents.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2TLz1CPx9OHczqLabCRmyc/c54b0b4bb637a97812c637ca0eebc78c/image12.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1999\&quot; height=\&quot;1119\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;Because more than one Attester may be supported on your machine, your Client needs to understand which Attester to use depending on the requested Issuer. As mentioned before, you as the Client do not communicate directly with the Issuer because you don’t necessarily know their relation with the attester, so you cannot retrieve its public key. To this end, the Attester API exposes all Issuers reachable by the said Attester via an endpoint: /v1/private-token-issuer-directory. This way, your client selects an appropriate Attester - one in relation with an Issuer that the Origin trusts, before triggering a validation.&lt;/p&gt;&lt;p&gt;In addition, we propose a replay API. Its goal is to allow clients to fetch a resource a second time if the first response presented a Privacy pass challenge. Some platforms do this automatically, like Silk on Firefox, but some don’t. That’s the case with the Silk Chrome extension for instance, which in its support of &lt;a href=\&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/manifest_version\&quot;&gt;manifest v3&lt;/a&gt; cannot block requests and only supports Basic authentication in the onAuthRequired extension event. The Privacy Pass Authentication scheme proposes the request to be sent once to get a challenge, and then a second time to get the actual resource. Between these requests to the Origin, the platform orchestrates the issuance of a token. To keep clients informed about the state of this process, we introduce a &lt;code&gt;private-token-client-replay: UUID header&lt;/code&gt; alongside WWW-Authenticate. Using a platform defined endpoint, this UUID informs web clients of the current state of authentication: pending, fulfilled, not-found.&lt;/p&gt;&lt;p&gt;To learn more about how you can use these today, and to deploy your own attestation method, read on.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;how-to-use-privacy-pass-today\&quot;&gt;How to use Privacy Pass today?&lt;/h2&gt;\n &lt;a href=\&quot;#how-to-use-privacy-pass-today\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;As seen in the section above, Privacy Pass is structured around four components: Origin, Client, Attester, Issuer. That’s why we created four repositories: &lt;a href=\&quot;https://github.com/cloudflare/pp-origin\&quot;&gt;cloudflare/pp-origin&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/pp-browser-extension\&quot;&gt;cloudflare/pp-browser-extension&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/pp-attester\&quot;&gt;cloudflare/pp-attester&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/pp-issuer\&quot;&gt;cloudflare/pp-issuer&lt;/a&gt;. In addition, the underlying cryptographic libraries are available &lt;a href=\&quot;https://github.com/cloudflare/privacypass-ts\&quot;&gt;cloudflare/privacypass-ts&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/blindrsa-ts\&quot;&gt;cloudflare/blindrsa-ts&lt;/a&gt;, and &lt;a href=\&quot;https://github.com/cloudflare/voprf-ts\&quot;&gt;cloudflare/voprf-ts&lt;/a&gt;. In this section, we dive into how to use each one of these depending on your use case.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Note: All examples below are designed in JavaScript and targeted at Cloudflare Workers. Privacy Pass is also implemented in &lt;a href=\&quot;https://github.com/ietf-wg-privacypass/base-drafts#existing-implementations\&quot;&gt;other languages&lt;/a&gt; and can be deployed with a configuration that suits your needs.&lt;/p&gt;&lt;/blockquote&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;as-an-origin-website-owners-service-providers\&quot;&gt;As an Origin - website owners, service providers&lt;/h3&gt;\n &lt;a href=\&quot;#as-an-origin-website-owners-service-providers\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;You are an online service that people critically rely upon (health or messaging for instance). You want to provide private payment options to users to maintain your users’ privacy. You only have one subscription tier at $10 per month. You have &lt;a href=\&quot;https://datatracker.ietf.org/doc/html/draft-davidson-pp-architecture-00#autoid-60\&quot;&gt;heard&lt;/a&gt; people are making privacy preserving apps, and want to use the latest version of Privacy Pass.&lt;/p&gt;&lt;p&gt;To access your service, users are required to prove they&amp;#39;ve paid for the service through a payment provider of their choosing (that you deem acceptable). This payment provider acknowledges the payment and requests a token for the user to access the service. As a sequence diagram, it looks as follows:&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3CDt5NsDY4c2DuYbggdleT/c2084b1b7cb141a8b528de78392833b3/image4.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1615\&quot; height=\&quot;903\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;To implement it in Workers, we rely on the &lt;a href=\&quot;https://www.npmjs.com/package/@cloudflare/privacypass-ts\&quot;&gt;&lt;code&gt;@cloudflare/privacypass-ts&lt;/code&gt;&lt;/a&gt; library, which can be installed by running:&lt;/p&gt;\n &lt;pre class=\&quot;language-bash\&quot;&gt;&lt;code class=\&quot;language-bash\&quot;&gt;npm i @cloudflare/privacypass-ts&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;This section is going to focus on the Origin work. We assume you have an Issuer up and running, which is described in a later section.&lt;/p&gt;&lt;p&gt;The Origin defines two flows:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;User redeeming token&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;User requesting a token issuance&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;import { Client } from &amp;#039;@cloudflare/privacypass-ts&amp;#039;\n\nconst issuer = &amp;#039;static issuer key&amp;#039;\n\nconst handleRedemption =&amp;gt; (req) =&amp;gt; {\n const token = TokenResponse.parse(req.headers.get(&amp;#039;authorization&amp;#039;))\n const isValid = token.verify(issuer.publicKey)\n}\n\nconst handleIssuance = () =&amp;gt; {\n return new Response(&amp;#039;Please pay to access the service&amp;#039;, {\n status: 401,\n headers: { &amp;#039;www-authenticate&amp;#039;: &amp;#039;PrivateToken challenge=, token-key=, max-age=300&amp;#039; }\n })\n}\n\nconst handleAuth = (req) =&amp;gt; {\n const authorization = req.headers.get(&amp;#039;authorization&amp;#039;)\n if (authorization.startsWith(`PrivateToken token=`)) {\n return handleRedemption(req)\n }\n return handleIssuance(req)\n}\n\nexport default {\n fetch(req: Request) {\n return handleAuth(req)\n }\n}&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;From the user’s perspective, the overhead is minimal. Their client (possibly the Silk browser extension) receives a WWW-Authenticate header with the information required for a token issuance. Then, depending on their client configuration, they are taken to the payment provider of their choice to validate their access to the service.&lt;/p&gt;&lt;p&gt;With a successful response to the PrivateToken challenge a session is established, and the traditional web service flow continues.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;as-an-attester-captcha-providers-authentication-provider\&quot;&gt;As an Attester - CAPTCHA providers, authentication provider&lt;/h3&gt;\n &lt;a href=\&quot;#as-an-attester-captcha-providers-authentication-provider\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;You are the author of a new attestation method, such as &lt;a href=\&quot;/introducing-cryptographic-attestation-of-personhood/\&quot;&gt;CAP,&lt;/a&gt; a new CAPTCHA mechanism, or a new way to validate cookie consent. You know that website owners already use Privacy Pass to trigger such challenges on the user side, and an Issuer is willing to trust your method because it guarantees a high security level. In addition, because of the Privacy Pass protocol you never see which website your attestation is being used for.&lt;/p&gt;&lt;p&gt;So you decide to expose your attestation method as a Privacy Pass Attester. An Issuer with public key B== trusts you, and that&amp;#39;s the Issuer you are going to request a token from. You can check that with the Yes/No Attester below, whose code is on &lt;a href=\&quot;https://cloudflareworkers.com/#eedc5a7a6560c44b23a24cc1414b29d7:https://tutorial.cloudflareworkers.com/v1/challenge\&quot;&gt;Cloudflare Workers playground&lt;/a&gt;&lt;/p&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;const ISSUER_URL = &amp;#039;https://pp-issuer-public.research.cloudflare.com/token-request&amp;#039;\n\nconst b64ToU8 = (b) =&amp;gt; Uint8Array.from(atob(b), c =&amp;gt; c.charCodeAt(0))\n\nconst handleGetChallenge = (req) =&amp;gt; {\n return new Response(`\n &amp;lt;html&amp;gt;\n &amp;lt;head&amp;gt;\n &amp;lt;title&amp;gt;Challenge Response&amp;lt;/title&amp;gt;\n &amp;lt;/head&amp;gt;\n &amp;lt;body&amp;gt;\n \t&amp;lt;button onclick=&amp;quot;sendResponse(&amp;#039;Yes&amp;#039;)&amp;quot;&amp;gt;Yes&amp;lt;/button&amp;gt;\n\t\t&amp;lt;button onclick=&amp;quot;sendResponse(&amp;#039;No&amp;#039;)&amp;quot;&amp;gt;No&amp;lt;/button&amp;gt;\n\t&amp;lt;/body&amp;gt;\n\t&amp;lt;script&amp;gt;\n\tfunction sendResponse(choice) {\n\t\tfetch(location.href, { method: &amp;#039;POST&amp;#039;, headers: { &amp;#039;private-token-attester-data&amp;#039;: choice } })\n\t}\n\t&amp;lt;/script&amp;gt;\n\t&amp;lt;/html&amp;gt;\n\t`, { status: 401, headers: { &amp;#039;content-type&amp;#039;: &amp;#039;text/html&amp;#039; } })\n}\n\nconst handlePostChallenge = (req) =&amp;gt; {\n const choice = req.headers.get(&amp;#039;private-token-attester-data&amp;#039;)\n if (choice !== &amp;#039;Yes&amp;#039;) {\n return new Response(&amp;#039;Unauthorised&amp;#039;, { status: 401 })\n }\n\n // hardcoded token request\n // debug here https://pepe-debug.research.cloudflare.com/?challenge=PrivateToken%20challenge=%22AAIAHnR1dG9yaWFsLmNsb3VkZmxhcmV3b3JrZXJzLmNvbSBE-oWKIYqMcyfiMXOZpcopzGBiYRvnFRP3uKknYPv1RQAicGVwZS1kZWJ1Zy5yZXNlYXJjaC5jbG91ZGZsYXJlLmNvbQ==%22,token-key=%22MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAQ8AMIIBCgKCAQEApqzusqnywE_3PZieStkf6_jwWF-nG6Es1nn5MRGoFSb3aXJFDTTIX8ljBSBZ0qujbhRDPx3ikWwziYiWtvEHSLqjeSWq-M892f9Dfkgpb3kpIfP8eBHPnhRKWo4BX_zk9IGT4H2Kd1vucIW1OmVY0Z_1tybKqYzHS299mvaQspkEcCo1UpFlMlT20JcxB2g2MRI9IZ87sgfdSu632J2OEr8XSfsppNcClU1D32iL_ETMJ8p9KlMoXI1MwTsI-8Kyblft66c7cnBKz3_z8ACdGtZ-HI4AghgW-m-yLpAiCrkCMnmIrVpldJ341yR6lq5uyPej7S8cvpvkScpXBSuyKwIDAQAB%22\n const body = b64ToU8(&amp;#039;AALoAYM+fDO53GVxBRuLbJhjFbwr0uZkl/m3NCNbiT6wal87GEuXuRw3iZUSZ3rSEqyHDhMlIqfyhAXHH8t8RP14ws3nQt1IBGE43Q9UinwglzrMY8e+k3Z9hQCEw7pBm/hVT/JNEPUKigBYSTN2IS59AUGHEB49fgZ0kA6ccu9BCdJBvIQcDyCcW5LCWCsNo57vYppIVzbV2r1R4v+zTk7IUDURTa4Mo7VYtg1krAWiFCoDxUOr+eTsc51bWqMtw2vKOyoM/20Wx2WJ0ox6JWdPvoBEsUVbENgBj11kB6/L9u2OW2APYyUR7dU9tGvExYkydXOfhRFJdKUypwKN70CiGw==&amp;#039;)\n // You can perform some check here to confirm the body is a valid token request\n\n console.log(&amp;#039;requesting token for tutorial.cloudflareworkers.com&amp;#039;)\n return fetch(ISSUER_URL, {\n method: &amp;#039;POST&amp;#039;,\n headers: { &amp;#039;content-type&amp;#039;: &amp;#039;application/private-token-request&amp;#039; },\n body: body,\n })\n}\n\nconst handleIssuerDirectory = async () =&amp;gt; {\n // These are fake issuers\n // Issuer data can be fetch at https://pp-issuer-public.research.cloudflare.com/.well-known/private-token-issuer-directory\n const TRUSTED_ISSUERS = {\n &amp;quot;issuer1&amp;quot;: { &amp;quot;token-keys&amp;quot;: [{ &amp;quot;token-type&amp;quot;: 2, &amp;quot;token-key&amp;quot;: &amp;quot;A==&amp;quot; }] },\n &amp;quot;issuer2&amp;quot;: { &amp;quot;token-keys&amp;quot;: [{ &amp;quot;token-type&amp;quot;: 2, &amp;quot;token-key&amp;quot;: &amp;quot;B==&amp;quot; }] },\n }\n return new Response(JSON.stringify(TRUSTED_ISSUERS), { headers: { &amp;quot;content-type&amp;quot;: &amp;quot;application/json&amp;quot; } })\n}\n\nconst handleRequest = (req) =&amp;gt; {\n const pathname = new URL(req.url).pathname\n console.log(pathname, req.url)\n if (pathname === &amp;#039;/v1/challenge&amp;#039;) {\n if (req.method === &amp;#039;POST&amp;#039;) {\n return handlePostChallenge(req)\n }\n return handleGetChallenge(req)\n }\n if (pathname === &amp;#039;/v1/private-token-issuer-directory&amp;#039;) {\n return handleIssuerDirectory()\n }\n return new Response(&amp;#039;Not found&amp;#039;, { status: 404 })\n}\n\naddEventListener(&amp;#039;fetch&amp;#039;, event =&amp;gt; {\n event.respondWith(handleRequest(event.request))\n})&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;The validation method above is simply checking if the user selected yes. Your method might be more complex, the wrapping stays the same.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card \&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5PnBuinoRKUpYjrBsHQbn/966c266e7de411503c5bf9a5dc9a184d/Screenshot-2024-01-04-at-10.30.04.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1356\&quot; height=\&quot;206\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Screenshot of the Yes/No Attester example&lt;/i&gt;&lt;/p&gt;&lt;p&gt;Because users might have multiple Attesters configured for a given Issuer, we recommend your Attester implements one additional endpoint exposing the keys of the issuers you are in contact with. You can try this code on &lt;a href=\&quot;https://cloudflareworkers.com/#4eeeef2fa895e519addb3ae442ee351d:https://tutorial.cloudflareworkers.com/v1/private-token-issuer-directory\&quot;&gt;Cloudflare Workers playground&lt;/a&gt;.&lt;/p&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;const handleIssuerDirectory = () =&amp;gt; {\n const TRUSTED_ISSUERS = {\n &amp;quot;issuer1&amp;quot;: { &amp;quot;token-keys&amp;quot;: [{ &amp;quot;token-type&amp;quot;: 2, &amp;quot;token-key&amp;quot;: &amp;quot;A==&amp;quot; }] },\n &amp;quot;issuer2&amp;quot;: { &amp;quot;token-keys&amp;quot;: [{ &amp;quot;token-type&amp;quot;: 2, &amp;quot;token-key&amp;quot;: &amp;quot;B==&amp;quot; }] },\n }\n return new Response(JSON.stringify(TRUSTED_ISSUERS), { headers: { &amp;quot;content-type&amp;quot;: &amp;quot;application/json&amp;quot; } })\n}\n\nexport default {\n fetch(req: Request) {\n const pathname = new URL(req.url).pathname\n if (pathname === &amp;#039;/v1/private-token-issuer-directory&amp;#039;) {\n return handleIssuerDirectory()\n }\n }\n}&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;Et voilà. You have an Attester that can be used directly with the Silk browser extension (&lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt;, &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt;). As you progress through your deployment, it can also be directly integrated into your applications.&lt;/p&gt;&lt;p&gt;If you would like to have a more advanced Attester and deployment pipeline, look at &lt;a href=\&quot;https://github.com/cloudflare/pp-attester\&quot;&gt;cloudflare/pp-attester&lt;/a&gt; template.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;as-an-issuer-foundation-consortium\&quot;&gt;As an Issuer - foundation, consortium&lt;/h3&gt;\n &lt;a href=\&quot;#as-an-issuer-foundation-consortium\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;We&amp;#39;ve mentioned the Issuer multiple times already. The role of an Issuer is to select a set of Attesters it wants to operate with, and communicate its public key to Origins. The whole cryptographic behavior of an Issuer is specified &lt;a href=\&quot;https://www.ietf.org/archive/id/draft-ietf-privacypass-protocol-16.html\&quot;&gt;by the IETF&lt;/a&gt; draft. In contrast to the Client and Attesters which have discretionary behavior, the Issuer is fully standardized. Their opportunity is to choose a signal that is strong enough for the Origin, while preserving privacy of Clients.&lt;/p&gt;&lt;p&gt;Cloudflare Research is operating a public Issuer for experimental purposes to use on &lt;a href=\&quot;https://pp-issuer-public.research.cloudflare.com\&quot;&gt;https://pp-issuer-public.research.cloudflare.com&lt;/a&gt;. It is the simplest solution to start experimenting with Privacy Pass today. Once it matures, you can consider joining a production Issuer, or deploying your own.&lt;/p&gt;&lt;p&gt;To deploy your own, you should:&lt;/p&gt;\n &lt;pre class=\&quot;language-bash\&quot;&gt;&lt;code class=\&quot;language-bash\&quot;&gt;git clone github.com/cloudflare/pp-issuer&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;Update wrangler.toml with your Cloudflare Workers account id and zone id. The open source Issuer API works as follows:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;/.well-known/private-token-issuer-directory returns the issuer configuration. Note it does not expose non-standard token-key-legacy&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;/token-request returns a token. This endpoint should be gated (by Cloudflare Access for instance) to only allow trusted attesters to call it&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;/admin/rotate to generate a new public key. This should only be accessible by your team, and be called prior to the issuer being available.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Then, &lt;code&gt;wrangler publish&lt;/code&gt;, and you&amp;#39;re good to onboard Attesters.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;development-of-silk-extension\&quot;&gt;Development of Silk extension&lt;/h2&gt;\n &lt;a href=\&quot;#development-of-silk-extension\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Just like the protocol, the browser technology on which Privacy Pass was proven viable has changed as well. For 5 years, the protocol got deployed along with a browser extension for Chrome and Firefox. In 2021, Chrome released a new version of extension configurations, usually referred to as &lt;a href=\&quot;https://developer.chrome.com/docs/extensions/mv3/intro/platform-vision/\&quot;&gt;Manifest version 3&lt;/a&gt; (MV3). Chrome also started enforcing this new configuration for all newly released extensions.&lt;/p&gt;&lt;p&gt;Privacy Pass &lt;i&gt;the extension&lt;/i&gt; is based on an agreed upon Privacy Pass &lt;a href=\&quot;https://datatracker.ietf.org/doc/draft-ietf-privacypass-auth-scheme/\&quot;&gt;&lt;i&gt;authentication protocol&lt;/i&gt;&lt;/a&gt;. Briefly looking at &lt;a href=\&quot;https://developer.chrome.com/docs/extensions/reference/webRequest/\&quot;&gt;Chrome’s API documentation&lt;/a&gt;, we should be able to use the onAuthRequired event. However, with PrivateToken authentication not yet being standard, there are no hooks provided by browsers for extensions to add logic to this event.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card kg-width-wide\&quot;&gt;\n \n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1iQsRopHuLfmHqjsppwImc/1a379a0cdd3de3e17de04811b1c08ac0/Screenshot-2024-01-04-at-10.32.44.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;2000\&quot; height=\&quot;932\&quot; loading=\&quot;lazy\&quot;/&gt;\n \n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Image available under CC-BY-SA 4.0 provided by&lt;/i&gt; &lt;a href=\&quot;https://developer.chrome.com/docs/extensions/reference/webRequest/\&quot;&gt;&lt;i&gt;Google For Developers&lt;/i&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The approach we decided to use is to define a client side replay API. When a response comes with 401 WWW-Authenticate PrivateToken, the browser lets it through, but triggers the private token redemption flow. The original page is notified when a token has been retrieved, and replays the request. For this second request, the browser is able to attach an authorization token, and the request succeeds. This is an active replay performed by the client, rather than a transparent replay done by the platform. A specification is available on &lt;a href=\&quot;https://github.com/cloudflare/pp-browser-extension#chrome-support-via-client-replay-api\&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;We are looking forward to the standard progressing, and simplifying this part of the project. This should improve diversity in attestation methods. As we see in the next section, this is key to identifying new signals that can be leveraged by origins.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;a-standard-for-anonymous-credentials\&quot;&gt;A standard for anonymous credentials&lt;/h2&gt;\n &lt;a href=\&quot;#a-standard-for-anonymous-credentials\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;IP remains as a key identifier in the anti abuse system. At the same time, IP fingerprinting techniques have become a bigger concern and platforms have started to remove some of these ways of tracking users. To enable anti abuse systems to not rely on IP, while ensuring user privacy, Privacy Pass offers a reasonable alternative to deal with potentially abusive or suspicious traffic. The attestation methods vary and can be chosen as needed for a particular deployment. For example, Apple decided to back their attestation with hardware when using Privacy Pass as the authorization technology for iCloud Private Relay. Another example is Cloudflare Research which decided to deploy a Turnstile attester to signal a successful solve for Cloudflare’s challenge platform.&lt;/p&gt;&lt;p&gt;In all these deployments, Privacy Pass-like technology has allowed for specific bits of information to be shared. Instead of sharing your location, past traffic, and possibly your name and phone number simply by connecting to a website, your device is able to prove specific information to a third party in a privacy preserving manner. Which user information and attestation methods are sufficient to prevent abuse is an open question. We are looking to empower researchers with the release of this software to help in the quest for finding these answers. This could be via new experiments such as testing out new attestation methods, or fostering other privacy protocols by providing a framework for specific information sharing.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;future-recommendations\&quot;&gt;Future recommendations&lt;/h2&gt;\n &lt;a href=\&quot;#future-recommendations\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Just as we expect this latest version of Privacy Pass to lead to new applications and ideas we also expect further evolution of the standard and the clients that use it. Future development of Privacy Pass promises to cover topics like batch token issuance and rate limiting. From our work building and deploying this version of Privacy Pass we have encountered limitations that we expect to be resolved in the future as well.&lt;/p&gt;&lt;p&gt;The division of labor between Attesters and Issuers and the clear directions of trust relationships between the Origin and Issuer, and the Issuer and Attester make reasoning about the implications of a breach of trust clear. Issuers can trust more than one Attester, but since many current deployments of Privacy Pass do not identify the Attester that lead to issuance, a breach of trust in one Attester would render all tokens issued by any Issuer that trusts the Attester untrusted. This is because it would not be possible to tell which Attester was involved in the issuance process. Time will tell if this promotes a 1:1 correspondence between Attesters and Issuers.&lt;/p&gt;&lt;p&gt;The process of developing a browser extension supported by both Firefox and Chrome-based browsers can at times require quite baroque (and brittle) code paths. Privacy Pass the protocol seems a good fit for an extension of the &lt;a href=\&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onAuthRequired\&quot;&gt;webRequest.onAuthRequired&lt;/a&gt; browser event. Just as Privacy Pass appears as an alternate authentication message in the WWW-Authenticate HTTP header, browsers could fire the onAuthRequired event for Private Token authentication too and include and allow request blocking support within the onAuthRequired event. This seems a natural evolution of the use of this event which currently is limited to the now rather long-in-the-tooth Basic authentication.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;conclusion\&quot;&gt;Conclusion&lt;/h2&gt;\n &lt;a href=\&quot;#conclusion\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Privacy Pass provides a solution to one of the longstanding challenges of the web: anonymous authentication. By leveraging cryptography, the protocol allows websites to get the information they need from users, and solely this information. It&amp;#39;s already used by millions to help distinguish human requests from automated bots in a manner that is privacy protective and often seamless. We are excited by the protocol’s broad and growing adoption, and by the novel use cases that are unlocked by this latest version.&lt;/p&gt;&lt;p&gt;Cloudflare’s Privacy Pass implementations are available on GitHub, and are compliant with the standard. We have open-sourced a &lt;a href=\&quot;https://github.com/cloudflare?q=pp-&amp;type=all&amp;language=&amp;sort=#org-repositories\&quot;&gt;set of templates&lt;/a&gt; that can be used to implement Privacy Pass &lt;a href=\&quot;https://github.com/cloudflare/pp-origin\&quot;&gt;&lt;i&gt;Origins&lt;/i&gt;&lt;/a&gt;&lt;i&gt;,&lt;/i&gt; &lt;a href=\&quot;https://github.com/cloudflare/pp-issuer\&quot;&gt;&lt;i&gt;Issuers&lt;/i&gt;&lt;/a&gt;, and &lt;a href=\&quot;https://github.com/cloudflare/pp-attester\&quot;&gt;&lt;i&gt;Attesters&lt;/i&gt;&lt;/a&gt;, which leverage Cloudflare Workers to get up and running quickly.&lt;/p&gt;&lt;p&gt;For those looking to try Privacy Pass out for themselves right away, download the &lt;i&gt;Silk - Privacy Pass Client&lt;/i&gt; browser extensions (&lt;a href=\&quot;https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/\&quot;&gt;Firefox&lt;/a&gt;, &lt;a href=\&quot;https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi\&quot;&gt;Chrome&lt;/a&gt;, &lt;a href=\&quot;https://github.com/cloudflare/pp-browser-extension\&quot;&gt;GitHub&lt;/a&gt;) and start browsing a web with fewer bot checks today.&lt;/p&gt;&quot;],&quot;published_at&quot;:[0,&quot;2024-01-04T16:07:22.000+00:00&quot;],&quot;updated_at&quot;:[0,&quot;2024-10-09T23:26:44.495Z&quot;],&quot;feature_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4wJwHdYhUiKvoSlAgSrFhG/cce2fbba90dbd93ef3cbc3e710e6f53b/privacy-pass-standard.png&quot;],&quot;tags&quot;:[1,[[0,{&quot;id&quot;:[0,&quot;1x7tpPmKIUCt19EDgM1Tsl&quot;],&quot;name&quot;:[0,&quot;Research&quot;],&quot;slug&quot;:[0,&quot;research&quot;]}],[0,{&quot;id&quot;:[0,&quot;3ZtL0yV0R4ScAreV1dTfIY&quot;],&quot;name&quot;:[0,&quot;Privacy Pass&quot;],&quot;slug&quot;:[0,&quot;privacy-pass&quot;]}],[0,{&quot;id&quot;:[0,&quot;kn8Lmy4luvCeAabblVvHH&quot;],&quot;name&quot;:[0,&quot;Firefox&quot;],&quot;slug&quot;:[0,&quot;firefox&quot;]}],[0,{&quot;id&quot;:[0,&quot;3skwJ34K0c3CEY1cNogR4n&quot;],&quot;name&quot;:[0,&quot;Chrome&quot;],&quot;slug&quot;:[0,&quot;chrome&quot;]}],[0,{&quot;id&quot;:[0,&quot;3BWeMuiOShelE7QM48sW9j&quot;],&quot;name&quot;:[0,&quot;Privacy&quot;],&quot;slug&quot;:[0,&quot;privacy&quot;]}]]],&quot;relatedTags&quot;:[0],&quot;authors&quot;:[1,[[0,{&quot;name&quot;:[0,&quot;Thibault Meunier&quot;],&quot;slug&quot;:[0,&quot;thibault&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1CqrdcRymVgEs1zRfSE6Xr/b8182164b0a8435b162bdd1246b7e91f/thibault.png&quot;],&quot;location&quot;:[0,null],&quot;website&quot;:[0,null],&quot;twitter&quot;:[0,&quot;@thibmeu&quot;],&quot;facebook&quot;:[0,null]}],[0,{&quot;name&quot;:[0,&quot;Cefan Daniel Rubin&quot;],&quot;slug&quot;:[0,&quot;cdrubin&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5EziMHboXmLjqS6FXaUODB/8f0daa841b1c260fae1be2a9d863457f/cdrubin.png&quot;],&quot;location&quot;:[0,null],&quot;website&quot;:[0,&quot;https://github.com/cdrubin&quot;],&quot;twitter&quot;:[0,null],&quot;facebook&quot;:[0,null]}],[0,{&quot;name&quot;:[0,&quot;Armando Faz-Hernández&quot;],&quot;slug&quot;:[0,&quot;armfazh&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1KZECWa5TCEPjjcRmbx9iT/c62263899934ff326df2b6864e42b104/armfazh.png&quot;],&quot;location&quot;:[0,null],&quot;website&quot;:[0,&quot;https://research.cloudflare.com/people/armando-faz/&quot;],&quot;twitter&quot;:[0,&quot;@armfazh&quot;],&quot;facebook&quot;:[0,null]}]]],&quot;meta_description&quot;:[0,&quot;In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the Privacy Pass protocol — including a set of open-source templates that can be used to implement Privacy Pass Origins, Issuers, and Attesters.&quot;],&quot;primary_author&quot;:[0,{}],&quot;localeList&quot;:[0,{&quot;name&quot;:[0,&quot;Privacy Pass: upgrading to the latest protocol version Config&quot;],&quot;enUS&quot;:[0,&quot;English for Locale&quot;],&quot;zhCN&quot;:[0,&quot;Translated for Locale&quot;],&quot;zhHansCN&quot;:[0,&quot;No Page for Locale&quot;],&quot;zhTW&quot;:[0,&quot;Translated for Locale&quot;],&quot;frFR&quot;:[0,&quot;No Page for Locale&quot;],&quot;deDE&quot;:[0,&quot;No Page for Locale&quot;],&quot;itIT&quot;:[0,&quot;No Page for Locale&quot;],&quot;jaJP&quot;:[0,&quot;No Page for Locale&quot;],&quot;koKR&quot;:[0,&quot;No Page for Locale&quot;],&quot;ptBR&quot;:[0,&quot;No Page for Locale&quot;],&quot;esLA&quot;:[0,&quot;No Page for Locale&quot;],&quot;esES&quot;:[0,&quot;No Page for Locale&quot;],&quot;enAU&quot;:[0,&quot;No Page for Locale&quot;],&quot;enCA&quot;:[0,&quot;No Page for Locale&quot;],&quot;enIN&quot;:[0,&quot;No Page for Locale&quot;],&quot;enGB&quot;:[0,&quot;No Page for Locale&quot;],&quot;idID&quot;:[0,&quot;No Page for Locale&quot;],&quot;ruRU&quot;:[0,&quot;No Page for Locale&quot;],&quot;svSE&quot;:[0,&quot;No Page for Locale&quot;],&quot;viVN&quot;:[0,&quot;No Page for Locale&quot;],&quot;plPL&quot;:[0,&quot;No Page for Locale&quot;],&quot;arAR&quot;:[0,&quot;No Page for Locale&quot;],&quot;nlNL&quot;:[0,&quot;No Page for Locale&quot;],&quot;thTH&quot;:[0,&quot;No Page for Locale&quot;],&quot;trTR&quot;:[0,&quot;No Page for Locale&quot;],&quot;heIL&quot;:[0,&quot;No Page for Locale&quot;],&quot;lvLV&quot;:[0,&quot;No Page for Locale&quot;],&quot;etEE&quot;:[0,&quot;No Page for Locale&quot;],&quot;ltLT&quot;:[0,&quot;No Page for Locale&quot;]}],&quot;url&quot;:[0,&quot;https://blog.cloudflare.com/privacy-pass-standard&quot;],&quot;metadata&quot;:[0,{&quot;title&quot;:[0,&quot;Privacy Pass: upgrading to the latest protocol version&quot;],&quot;description&quot;:[0,&quot;In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the Privacy Pass protocol — including a set of open-source templates that can be used to implement Privacy Pass Origins, Issuers, and Attesters.&quot;],&quot;imgPreview&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/22RHi3sA2tR4yPZCvPKYLe/87cdf4737a0d11148e569ff370819e38/privacy-pass-standard-fxK0y1.png&quot;]}]}],&quot;initialReadingTime&quot;:[0,&quot;16&quot;],&quot;relatedPosts&quot;:[1,[[0,{&quot;id&quot;:[0,&quot;63yGQGTniOUOFneFLwTb7a&quot;],&quot;title&quot;:[0,&quot;Cloudflare meets new Global Cross-Border Privacy (CBPR) standards&quot;],&quot;slug&quot;:[0,&quot;cloudflare-cbpr-a-global-privacy-first&quot;],&quot;excerpt&quot;:[0,&quot;Cloudflare is the first organization globally to announce having been successfully audited against the ‘Global Cross-Border Privacy Rules’ system and ‘Global Privacy Recognition for Processors’.&quot;],&quot;featured&quot;:[0,false],&quot;html&quot;:[0,&quot;&lt;p&gt;Cloudflare proudly leads the way with our approach to &lt;a href=\&quot;https://www.cloudflare.com/learning/privacy/what-is-data-privacy/\&quot;&gt;data privacy&lt;/a&gt; and the protection of &lt;a href=\&quot;https://www.cloudflare.com/learning/privacy/what-is-personal-information/\&quot;&gt;personal information&lt;/a&gt;, and we’ve been an ardent supporter of the need for the free flow of data across jurisdictional borders. So today, on Data Privacy Day (also known internationally as Data Protection Day), we’re happy to announce that we’re adding our fourth and fifth privacy validations, and this time, they are global firsts! Cloudflare is the first organisation to announce that we have been successfully audited against the brand new &lt;a href=\&quot;https://www.globalcbpr.org/privacy-certifications/\&quot;&gt;&lt;u&gt;Global Cross-Border Privacy Rules (Global CBPRs) for data controllers and the Global Privacy Recognition for Processors (Global PRP)&lt;/u&gt;&lt;/a&gt;. These validations demonstrate our support and adherence to global standards that provide for privacy-respecting data flows across jurisdictions. Organizations that have been successfully audited will be formally certified when the certifications officially launch, which we expect to happen later in 2025. &lt;/p&gt;&lt;p&gt;Our participation in the Global CBPRs and Global PRP joins our roster of privacy validations: we were one of the first cybersecurity organizations to certify to the international privacy standard &lt;a href=\&quot;https://blog.cloudflare.com/iso-27701-privacy-certification/\&quot;&gt;&lt;u&gt;ISO 27701:2019&lt;/u&gt;&lt;/a&gt; when it was published, and in 2022 we also certified to the cloud privacy certification, &lt;a href=\&quot;https://www.cloudflare.com/trust-hub/compliance-resources/iso-certifications/\&quot;&gt;&lt;u&gt;ISO 27018:2019&lt;/u&gt;&lt;/a&gt;. In 2023, we added our third privacy validation, undergoing a review by an independent monitoring body in the European Union (EU) and declared to be adherent to the first official GDPR code of conduct — &lt;i&gt;the &lt;/i&gt;&lt;a href=\&quot;https://blog.cloudflare.com/cloudflare-official-gdpr-code-of-conduct/\&quot;&gt;&lt;i&gt;&lt;u&gt;EU Cloud Code of Conduct&lt;/u&gt;&lt;/i&gt;&lt;/a&gt;.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;why-this-matters-to-cloudflare-customers\&quot;&gt;Why this matters to Cloudflare customers&lt;/h3&gt;\n &lt;a href=\&quot;#why-this-matters-to-cloudflare-customers\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Taking these privacy certifications together, Cloudflare demonstrates that we are meeting key official privacy validations in 39 jurisdictions around the world, from Australia and Austria to Sweden and the United States. An additional four jurisdictions (United Kingdom, Bermuda, Mauritius, and the Dubai International Finance Centre) are also in the process of joining and recognising the Global CBPR certifications. That&amp;#39;s important for Cloudflare customers as it provides reassurance that the privacy practices we have built are recognised by governments around the world.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2RFlkr3Wht9Gu34lv2xxN9/8f3c8e5dc23963614d275dab085cd8ce/unnamed.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1600\&quot; height=\&quot;843\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;what-is-the-global-cbpr-system\&quot;&gt;What is the Global CBPR System?&lt;/h3&gt;\n &lt;a href=\&quot;#what-is-the-global-cbpr-system\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;In the last three years, governments across the world have been busy preparing two brand-new international privacy standards. A major milestone was achieved on April 30, 2024 when &lt;a href=\&quot;https://www.globalcbpr.org/global-cbpr-forum-announces-the-establishment-of-the-global-cbpr-and-global-prp-systems-and-welcomes-new-global-cape-participants/\&quot;&gt;&lt;u&gt;the Global CBPR System was established&lt;/u&gt;&lt;/a&gt;. The CBPRs are a voluntary, enforceable, international, accountability-based system that facilitates privacy-respecting data flows among members’ economies. They provide a baseline level of privacy protection for consumers through a set of rules on how to handle people’s personal information. This facilitates the free flow of data by upholding consumer privacy across participating members, despite each jurisdiction having their own individual data protection laws.&lt;/p&gt;&lt;p&gt;The CBPR System was developed by the &lt;a href=\&quot;https://www.globalcbpr.org/about/membership/\&quot;&gt;&lt;u&gt;Global CBPR Forum&lt;/u&gt;&lt;/a&gt;, an intergovernmental forum between the governments of Australia, Canada, Japan, Republic of Korea, Mexico, Philippines, Singapore, Chinese Taipei, and the United States. The United Kingdom is also an associate member of the CBPR Forum, as are Bermuda, Mauritius, and the Dubai IFC, signifying their intent to join as full members in the future.&lt;/p&gt;&lt;p&gt;Over the last year, we have been busy preparing for the launch of the Global CBPR System. On May 1, 2024 — the very first day after the establishment of the system — Cloudflare applied to join. And we have now achieved the major milestone of successfully completing audits against the requirements, meaning we expect to be the first organization in the world to be newly certified to the Global CBPR system, as well as the related Global Privacy Recognition for Processors, when companies can officially be certified, which is expected later in 2025.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5F7HXXU071UJtx68KHGn41/0228087d6420c26802d77c13fafe935c/image1.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1200\&quot; height=\&quot;675\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;what-the-global-cbpr-system-covers\&quot;&gt;What the Global CBPR System covers&lt;/h3&gt;\n &lt;a href=\&quot;#what-the-global-cbpr-system-covers\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;The Global CBPR System contains a detailed list of fifty requirements that organizations must meet in order to be certified under the scheme. The requirements derive from the nine &lt;b&gt;Global CBPR Privacy Principles&lt;/b&gt;, which are consistent with the core principles of the &lt;a href=\&quot;https://www.oecd.org/\&quot;&gt;&lt;u&gt;Organisation for Economic Co-operation and Development (OECD)&lt;/u&gt;&lt;/a&gt; &lt;a href=\&quot;https://www.oecd.org/en/publications/2002/02/oecd-guidelines-on-the-protection-of-privacy-and-transborder-flows-of-personal-data_g1gh255f.html\&quot;&gt;&lt;u&gt;Guidelines on the Protection of Privacy and Trans-Border Flows of Personal Data&lt;/u&gt;&lt;/a&gt;. The fifty requirements cover how organizations should collect, manage, and safeguard personal information in their custody. Organizations must meet every one of the fifty requirements in order to be Global CBPR certified. The nine principles underlying the requirements are:&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;Preventing Harm&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Notice&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Collection Limitation&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;Uses of Personal Information&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Choice&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Integrity of Personal Information&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;Security Safeguards&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Access and Correction&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;Accountability&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;&lt;sup&gt;&lt;i&gt;The nine Global CBPR Privacy Principles&lt;/i&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;The Global CBPR certification covers the handling of personal information controlled by the organization, such as the personal details of customers, employees, and job applicants. For Cloudflare, this also includes network information — our observations about how our global cloud platform handles server, network, or traffic data generated by Cloudflare in the course of providing our services.&lt;/p&gt;&lt;p&gt;The related Global Privacy Recognition for Processors (PRP) certification covers the handling of personal information processed by the organization on behalf of a different organization, usually their customer. The eighteen requirements of the PRP relate to the two privacy principles most relevant when processing this information on behalf of another organization: &lt;i&gt;Security Safeguards and Accountability&lt;/i&gt;. For Cloudflare, this covers the processing of data pursuant to the &lt;a href=\&quot;https://www.cloudflare.com/cloudflare-customer-dpa/\&quot;&gt;&lt;u&gt;Data Processing Addendum&lt;/u&gt;&lt;/a&gt; we sign with all of our customers, chiefly, the Customer Content flowing across our network and the Customer Logs generated by those data flows. Organizations must meet every one of the eighteen requirements in order to be Global PRP certified.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;a-deeper-dive-into-some-of-the-requirements-of-the-global-cbprs\&quot;&gt;A deeper dive into some of the requirements of the Global CBPRs&lt;/h3&gt;\n &lt;a href=\&quot;#a-deeper-dive-into-some-of-the-requirements-of-the-global-cbprs\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;As noted, the key requirements of the Global CBPRs and the Global PRP cover the well-known data protection principles of notice, choice, collection limitation (data minimization), the right of data subject access and correction, providing adequate security, preventing harm, integrity of personal information, accountability, and uses of personal information. There are dozens of requirements that cover these principles, so we’ll just touch on a few of them here.&lt;/p&gt;&lt;p&gt;Let’s first look at the principle of notice. One of the more obvious requirements from the CBPRs is question 1:&lt;/p&gt;&lt;p&gt;&lt;i&gt;Do you provide clear and easily accessible statements about your practices and policies that govern the personal information described above (a privacy statement)?&lt;/i&gt;&lt;/p&gt;&lt;p&gt;Being transparent about the collection and use of personal information is a key principle of privacy and data protection, and transparency is one of Cloudflare’s core commitments. Documenting our practices and policies in regard to how we use personal information allows individuals to decide if they want to provide their information, and that’s why it’s best practice for the privacy notice to be available and visible at the time the information is being collected. Indeed, this concept of providing notice is clear from &lt;a href=\&quot;https://eur-lex.europa.eu/eli/reg/2016/679/oj/eng#art_13\&quot;&gt;&lt;u&gt;Article 13 of the EU’s GDPR&lt;/u&gt;&lt;/a&gt;. Cloudflare meets this CBPR requirement by providing a clear and accessible privacy notice visible from the footer of each page on our website. We also provide a link to the notice when we collect personal data such as through a form on a webpage.&lt;/p&gt;&lt;p&gt;In terms of how we use personal information, question 8 asks:&lt;/p&gt;&lt;p&gt;&lt;i&gt;Do you limit the use of the personal information you collect (whether directly or through the use of third parties acting on your behalf) as identified in your privacy statement?&lt;/i&gt;&lt;/p&gt;&lt;p&gt;It has long been a commitment of Cloudflare’s that we only use the personal information we collect for the purposes of providing the services we offer. Our business is built on providing customers with the tools to protect their network applications and to make them faster, more secure, more reliable, and more private. In our &lt;a href=\&quot;https://www.cloudflare.com/privacypolicy/\&quot;&gt;&lt;u&gt;Privacy Policy&lt;/u&gt;&lt;/a&gt;, we commit that we will “only share or otherwise disclose your personal information as necessary to provide our Services or as otherwise described in this Policy, except in cases where we first provide you with notice and the opportunity to consent.” And we maintain internal documentation (in keeping with the CBPR’s accountability principle) to document the data we are processing and the purposes for which we process it.&lt;/p&gt;&lt;p&gt;Another key set of requirements in both the Global CBPRs and the Global PRP have to do with security safeguards. CBPR requirement question 27 asks:&lt;/p&gt;&lt;p&gt;&lt;i&gt;Describe the physical, technical and administrative safeguards you have implemented to protect personal information against risks such as loss or unauthorized access, destruction, use, modification or disclosure of information or other misuses?&lt;/i&gt;&lt;/p&gt;&lt;p&gt;The similar requirement in the Global PRP is question 2: &lt;/p&gt;&lt;p&gt;&lt;i&gt;Describe the physical, technical and administrative safeguards that implement your organization’s information security policy.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;Cloudflare has implemented an information security program in accordance with the ISO/IEC 27000 family of standards. Details of Cloudflare’s security program are documented in Annex 2 (“Technical and Organizational Security Measures”) of Cloudflare&amp;#39;s &lt;a href=\&quot;https://www.cloudflare.com/cloudflare-customer-dpa/\&quot;&gt;&lt;u&gt;Customer Data Processing Addendum&lt;/u&gt;&lt;/a&gt;, including the physical, technical and administrative safeguards implemented to protect personal information.&lt;/p&gt;&lt;p&gt;Related to the Accountability principle, question 46 asks:&lt;/p&gt;&lt;p&gt;&lt;i&gt;Do you have mechanisms in place with personal information processors, agents, contractors, or other service providers pertaining to personal information they process on your behalf, to ensure that your obligations to the individual will be met? &lt;/i&gt;&lt;/p&gt;&lt;p&gt;When we have vendors who handle any of our, or our customers’, personal information, we require them to sign a Data Processing Addendum with us. This ensures the commitments we make to our customers in our customer agreements in turn flow through to our vendors, including the security requirements — holding them, and us, accountable.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;more-information\&quot;&gt;More information&lt;/h3&gt;\n &lt;a href=\&quot;#more-information\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;We are excited about the launch of the Global CBPR certifications, expected later in 2025, and we are proud that on this Data Privacy Day, we can yet again demonstrate our commitment to universally held principles for protecting the privacy of personal data.&lt;/p&gt;&lt;p&gt;You can find more about the Global CBPR System, the Global PRP, download a full copy of the requirements, and keep up to date with related news at &lt;a href=\&quot;https://www.globalcbpr.org/\&quot;&gt;&lt;u&gt;globalcbpr.org&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;For the latest information about our certifications, please visit our &lt;a href=\&quot;https://www.cloudflare.com/trust-hub/compliance-resources/\&quot;&gt;&lt;u&gt;Trust Hub&lt;/u&gt;&lt;/a&gt;. Customers can also find out how to download a copy of Cloudflare’s certifications and reports from the &lt;a href=\&quot;https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/access-compliance-docs/\&quot;&gt;&lt;u&gt;Cloudflare dashboard&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/36LV7CkbF5b5IuXN4ZVXZC/77775c3e2791418d87c36d46e755fbbc/image2.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1999\&quot; height=\&quot;571\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&quot;],&quot;published_at&quot;:[0,&quot;2025-01-28T00:00+00:00&quot;],&quot;updated_at&quot;:[0,&quot;2025-02-03T16:07:08.820Z&quot;],&quot;feature_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4SJhJ9Vtg6wCK1SPhlLs75/95ce4ff10ba8cf1e1197ec5abfe52e8b/image3.png&quot;],&quot;tags&quot;:[1,[[0,{&quot;id&quot;:[0,&quot;4NRwgGJnSZwfl5aeAPjvOe&quot;],&quot;name&quot;:[0,&quot;Certification&quot;],&quot;slug&quot;:[0,&quot;certification&quot;]}],[0,{&quot;id&quot;:[0,&quot;3BWeMuiOShelE7QM48sW9j&quot;],&quot;name&quot;:[0,&quot;Privacy&quot;],&quot;slug&quot;:[0,&quot;privacy&quot;]}],[0,{&quot;id&quot;:[0,&quot;6twWoAUd2y0j3cAMfKjwcW&quot;],&quot;name&quot;:[0,&quot;Compliance&quot;],&quot;slug&quot;:[0,&quot;compliance&quot;]}],[0,{&quot;id&quot;:[0,&quot;6Mp7ouACN2rT3YjL1xaXJx&quot;],&quot;name&quot;:[0,&quot;Security&quot;],&quot;slug&quot;:[0,&quot;security&quot;]}],[0,{&quot;id&quot;:[0,&quot;16yk8DVbNNifxov5cWvAov&quot;],&quot;name&quot;:[0,&quot;Policy &amp; Legal&quot;],&quot;slug&quot;:[0,&quot;policy&quot;]}]]],&quot;relatedTags&quot;:[0],&quot;authors&quot;:[1,[[0,{&quot;name&quot;:[0,&quot;Rory Malone&quot;],&quot;slug&quot;:[0,&quot;rory&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2bKOaTS3yX5KSIZXsCwLS4/81eed0f0e7ce7b3a6b5c2683c30a8205/rory.png&quot;],&quot;location&quot;:[0,&quot;London, UK&quot;],&quot;website&quot;:[0,&quot;https://www.cloudflare.com/trust-hub/compliance-resources&quot;],&quot;twitter&quot;:[0,&quot;@roryinlondon&quot;],&quot;facebook&quot;:[0,null]}],[0,{&quot;name&quot;:[0,&quot;Emily Hancock&quot;],&quot;slug&quot;:[0,&quot;emily-hancock&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6WoITZeDW2tg5JO91UOF0Y/0510a8ee70b2daeb2cb325aa6f8e0ceb/emily-hancock.jpg&quot;],&quot;location&quot;:[0,null],&quot;website&quot;:[0,null],&quot;twitter&quot;:[0,null],&quot;facebook&quot;:[0,null]}]]],&quot;meta_description&quot;:[0,&quot;Cloudflare is the first organisation globally to announce we have been successfully audited against the brand-new international ‘Global Cross-Border Privacy Rules’ system and the brand new ‘Global Privacy Recognition for Processors’. Cloudflare will be certified from day one when the certifications launch later in 2025.&quot;],&quot;primary_author&quot;:[0,{}],&quot;localeList&quot;:[0,{&quot;name&quot;:[0,&quot;LOC: New Global CBPR and Global PRP privacy certifications&quot;],&quot;enUS&quot;:[0,&quot;English for Locale&quot;],&quot;zhCN&quot;:[0,&quot;Translated for Locale&quot;],&quot;zhHansCN&quot;:[0,&quot;No Page for Locale&quot;],&quot;zhTW&quot;:[0,&quot;No Page for Locale&quot;],&quot;frFR&quot;:[0,&quot;Translated for Locale&quot;],&quot;deDE&quot;:[0,&quot;Translated for Locale&quot;],&quot;itIT&quot;:[0,&quot;No Page for Locale&quot;],&quot;jaJP&quot;:[0,&quot;Translated for Locale&quot;],&quot;koKR&quot;:[0,&quot;Translated for Locale&quot;],&quot;ptBR&quot;:[0,&quot;No Page for Locale&quot;],&quot;esLA&quot;:[0,&quot;No Page for Locale&quot;],&quot;esES&quot;:[0,&quot;Translated for Locale&quot;],&quot;enAU&quot;:[0,&quot;No Page for Locale&quot;],&quot;enCA&quot;:[0,&quot;No Page for Locale&quot;],&quot;enIN&quot;:[0,&quot;No Page for Locale&quot;],&quot;enGB&quot;:[0,&quot;No Page for Locale&quot;],&quot;idID&quot;:[0,&quot;No Page for Locale&quot;],&quot;ruRU&quot;:[0,&quot;No Page for Locale&quot;],&quot;svSE&quot;:[0,&quot;No Page for Locale&quot;],&quot;viVN&quot;:[0,&quot;No Page for Locale&quot;],&quot;plPL&quot;:[0,&quot;No Page for Locale&quot;],&quot;arAR&quot;:[0,&quot;No Page for Locale&quot;],&quot;nlNL&quot;:[0,&quot;No Page for Locale&quot;],&quot;thTH&quot;:[0,&quot;No Page for Locale&quot;],&quot;trTR&quot;:[0,&quot;No Page for Locale&quot;],&quot;heIL&quot;:[0,&quot;No Page for Locale&quot;],&quot;lvLV&quot;:[0,&quot;No Page for Locale&quot;],&quot;etEE&quot;:[0,&quot;No Page for Locale&quot;],&quot;ltLT&quot;:[0,&quot;No Page for Locale&quot;]}],&quot;url&quot;:[0,&quot;https://blog.cloudflare.com/cloudflare-cbpr-a-global-privacy-first&quot;],&quot;metadata&quot;:[0,{&quot;title&quot;:[0,&quot;Cloudflare meets new Global Cross Border Privacy standards&quot;],&quot;description&quot;:[0,&quot;Cloudflare is the first organisation globally to announce we have been successfully audited against the brand-new international ‘Global Cross-Border Privacy Rules’ system and the brand new ‘Global Privacy Rules for Processors’. Cloudflare will be certified from day one when the certifications launch later in 2025.&quot;],&quot;imgPreview&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/8yWeBIDbBGS88QDJHOQGd/c71c0b1ff73bfda977bb0597bcf5c5aa/OG_Share_2024__6_.png&quot;]}]}],[0,{&quot;id&quot;:[0,&quot;4Xek2BRcXVKNsI4vCa3Zuj&quot;],&quot;title&quot;:[0,&quot;Sometimes I cache: implementing lock-free probabilistic caching&quot;],&quot;slug&quot;:[0,&quot;sometimes-i-cache&quot;],&quot;excerpt&quot;:[0,&quot;If you want to know what cache revalidation is, how it works, and why it can involve rolling a die, read on. This blog post presents a lock-free probabilistic approach to cache revalidation, along \n&quot;],&quot;featured&quot;:[0,false],&quot;html&quot;:[0,&quot;&lt;p&gt;HTTP caching is conceptually simple: if the response to a request is in the cache, serve it, and if not, pull it from your origin, put it in the cache, and return it. When the response is old, you repeat the process. This is called cache revalidation. If you are worried about too many requests going to your origin at once, you protect it with a &lt;a href=\&quot;https://developers.cloudflare.com/cache/concepts/revalidation/\&quot;&gt;&lt;u&gt;cache lock&lt;/u&gt;&lt;/a&gt;: a small program, possibly distinct from your cache, that indicates if a request is already going to your origin. This is called request collapsing.&lt;/p&gt;&lt;p&gt;In this blog post, we dive into how cache revalidation works, and present a new approach based on probability. For every request going to the origin, we simulate a die roll. If it’s 6, the request can go to the origin. Otherwise, it stays stale to protect our origin from being overloaded. To see how this is built and optimised, read on.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;background\&quot;&gt;Background&lt;/h2&gt;\n &lt;a href=\&quot;#background\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Let&amp;#39;s take the example of an online image library. When a client requests an image, the service first checks its cache to see if the resource is present. If it is, it returns it. If it is not, the image server processes the request, places the response into the cache for a day, and returns it. When the cache expires, the process is repeated.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6zdgMhKkU3cVKRk9T2CO2I/5302e6ef68cabd04b8bd65a7e416f033/BLOG-2639_2.png\&quot; alt=\&quot;BLOG-2639 2\&quot; class=\&quot;kg-image\&quot; width=\&quot;1048\&quot; height=\&quot;188\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Figure 1: Uncached request goes to the origin&lt;/i&gt;&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4J5KIW2CocyvlLXLuVBTWH/1f6174983e1fc1de72b48a9801da160e/BLOG-2639_3.png\&quot; alt=\&quot;BLOG-2639 3\&quot; class=\&quot;kg-image\&quot; width=\&quot;1048\&quot; height=\&quot;188\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Figure 2: Cached request stops at the cache&lt;/i&gt;&lt;/p&gt;&lt;p&gt;And this is where things get complex. The image of a cat might be quite popular. Let&amp;#39;s say it&amp;#39;s requested 10 times per second. Let’s also assume the image server cannot handle more than 1 request per second. After a day, the cache expires. 10 requests hit the service. Given there are no up-to-date items in cache, these 10 requests are going to go directly to the image server. This problem is known as &lt;a href=\&quot;https://en.wikipedia.org/wiki/Cache_stampede\&quot;&gt;&lt;u&gt;cache stampede&lt;/u&gt;&lt;/a&gt;. When the image server sees these 10 requests all happening at the same time, it gets overloaded.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3xDMJ6iloIjNHWpAbkfXWv/bee11bc4e1bb83a5b330529d8d98fbaa/BLOG-2639_4.png\&quot; alt=\&quot;BLOG-2639 4\&quot; class=\&quot;kg-image\&quot; width=\&quot;1200\&quot; height=\&quot;208\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Figure 3: Image server overloaded upon cache expiration. This can happen to one or multiple users, across locations.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;This all stops if the cache gets populated, as it can handle a lot more requests than the origin.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7LVi2SItiAUYWdEm3ll3ZH/368c6ff8e5d9017d2d3a8cc96246b73c/BLOG-2639_5.png\&quot; alt=\&quot;BLOG-2639 5\&quot; class=\&quot;kg-image\&quot; width=\&quot;1180\&quot; height=\&quot;192\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Figure 4: Cache is populated and can handle the load. The image server is healthy again.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;In the following sections, we build this image service, see how it can prevent cache stampede with a cache lock, then dive into probabilistic cache revalidation, and its optimisation.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;setup\&quot;&gt;Setup&lt;/h2&gt;\n &lt;a href=\&quot;#setup\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Let&amp;#39;s write this image service. We need an image, a server, and a cache. For the image we&amp;#39;re going to use a picture of &lt;a href=\&quot;https://files.research.cloudflare.com/images/cat.jpg\&quot;&gt;&lt;u&gt;my cat&lt;/u&gt;&lt;/a&gt;, Cloudflare Workers for the server, and the Cloudflare Cache API for caching.&lt;/p&gt;&lt;p&gt;Note to the reader: On purpose, we aren’t using &lt;a href=\&quot;https://developers.cloudflare.com/kv/\&quot;&gt;&lt;u&gt;Cloudflare KV&lt;/u&gt;&lt;/a&gt; or &lt;a href=\&quot;https://developers.cloudflare.com/cache/\&quot;&gt;&lt;u&gt;Cloudflare CDN Cache&lt;/u&gt;&lt;/a&gt;, because they already solve our cache validation problem by using a cache lock.&lt;/p&gt;\n &lt;pre class=\&quot;language-JavaScript\&quot;&gt;&lt;code class=\&quot;language-JavaScript\&quot;&gt;let cache = caches.default\nconst CACHE_KEY = new Request(&amp;#039;https://cache.local/&amp;#039;)\nconst CACHE_AGE_IN_S = 86_400 // 1 day\n\nfunction cacheExpirationDate() {\n return new Date(Date.now() + 1000*CACHE_AGE_IN_S)\n}\n\nfunction fetchAndCache(ctx) {\n let response = await fetch(&amp;#039;https://files.research.cloudflare.com/images/cat.jpg&amp;#039;)\n response = new Response(\n\tawait response.arrayBuffer(),\n\t{\n \t headers: {\n \t &amp;#039;Content-Type&amp;#039;: response.headers.get(&amp;#039;Content-Type&amp;#039;),\n \t &amp;#039;Expires&amp;#039;: cacheExpirationDate().toUTCString(),\n \t },\n\t},\n )\n ctx.waitUntil(cache.put(CACHE_KEY, response.clone()))\n return response\n}\n\nexport default {\n async fetch(request, env, ctx) {\n\tlet cachedResponse = await cache.match(CACHE_KEY)\n\tif (cachedResponse) {\n \t return cachedResponse\n\t}\n\treturn fetchAndCache(ctx)\n }\n}&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;&lt;i&gt;Codeblock 1: Image server with a non-collapsing cache&lt;/i&gt;&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;expectation-about-cache-revalidation\&quot;&gt;Expectation about cache revalidation&lt;/h2&gt;\n &lt;a href=\&quot;#expectation-about-cache-revalidation\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;The image service is receiving 10 requests per second, and it caches images for a day. It&amp;#39;s reasonable to assume we would like to start revalidating the cache 5 minutes before it expires. The code evolves as follows:&lt;/p&gt;\n &lt;pre class=\&quot;language-JavaScript\&quot;&gt;&lt;code class=\&quot;language-JavaScript\&quot;&gt;let cache = caches.default\nconst CACHE_KEY = new Request(&amp;#039;https://cache.local/&amp;#039;)\nconst CACHE_AGE_IN_S = 86_400 // 1 day\nconst CACHE_REVALIDATION_INTERVAL_IN_S = 300\n\nfunction cacheExpirationDate() {\n // Date constructor in workers takes Unix time in milliseconds\n // Date.now() returns time in milliseconds as well\n return new Date(Date.now() + 1000*CACHE_AGE_IN_S)\n}\n\nasync function fetchAndCache(ctx) {\n let response = await fetch(&amp;#039;https://files.research.cloudflare.com/images/cat.jpg&amp;#039;)\n response = new Response(\n\tawait response.arrayBuffer(),\n\t{\n \t headers: {\n \t &amp;#039;Content-Type&amp;#039;: response.headers.get(&amp;#039;Content-Type&amp;#039;),\n \t &amp;#039;Expires&amp;#039;: cacheExpirationDate().toUTCString(),\n \t },\n\t},\n )\n ctx.waitUntil(cache.put(CACHE_KEY, response.clone()))\n return response\n}\n\n// Revalidation function added here\n// This is were we are going to focus our effort: should the request be revalidated ?\nfunction shouldRevalidate(expirationDate) {\n let remainingCacheTimeInS = (expirationDate.getTime() - Date.now()) / 1000\n\n return remainingCacheTimeInS &amp;lt;= CACHE_REVALIDATION_INTERVAL_IN_S\n}\n\nexport default {\n async fetch(request, env, ctx) {\n\tlet cachedResponse = await cache.match(CACHE_KEY)\n\tif (cachedResponse) {\n // revalidation happens only if the request was cached. Otherwise, the resource is fetched anyway\n \t if (shouldRevalidate()) {\n \t ctx.waitUntil(fetchAndCache(ctx))\n \t }\n \t return cachedResponse\n\t}\n\treturn fetchAndCache(ctx)\n }\n}&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;&lt;i&gt;Codeblock 2: Image server with early-revalidation and a non-collapsing cache&lt;/i&gt;&lt;/p&gt;&lt;p&gt;That code works, and we can now revalidate 5 minutes in advance of cache expiration. However, instead of fetching the image from the origin server at expiration time, all requests are going to be made 5 minutes in advance, and that does not solve our cache stampede problem. This happens no matter if requests are coming to a single location or not, given the code above does not collapse requests.&lt;/p&gt;&lt;p&gt;To solve our cache stampede problem, we need the revalidation process to not send too many requests at the same time. Ideally, we would like only one request to be sent between &lt;code&gt;expiration - 5min&lt;/code&gt; and &lt;code&gt;expiration&lt;/code&gt;.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;the-usual-solution-a-cache-lock\&quot;&gt;The usual solution: a cache lock&lt;/h2&gt;\n &lt;a href=\&quot;#the-usual-solution-a-cache-lock\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;To make sure there is only one request at a time going to the origin server, the solution that&amp;#39;s usually deployed is a cache lock. The idea is that for a specific item, a cat picture in our case, requests to the origin try to obtain a lock. The request obtaining the lock can go to the origin, the others will serve stale content.&lt;/p&gt;&lt;p&gt;The lock has two methods: &lt;code&gt;try_lock&lt;/code&gt; and &lt;code&gt;unlock&lt;/code&gt;.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;try_lock&lt;/code&gt; if the lock is free, take it and return &lt;code&gt;true&lt;/code&gt;. If not, return &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;unlock&lt;/code&gt; releases the lock.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Such a lock can be implemented as a &lt;a href=\&quot;https://developers.cloudflare.com/workers/runtime-apis/rpc/\&quot;&gt;&lt;u&gt;Cloudflare RPC service&lt;/u&gt;&lt;/a&gt;:&lt;/p&gt;\n &lt;pre class=\&quot;language-Javascript\&quot;&gt;&lt;code class=\&quot;language-Javascript\&quot;&gt;import { WorkerEntrypoint } from &amp;#039;cloudflare:workers&amp;#039;\n\nclass Lock extends WorkerEntryPoint {\n async try_lock(key) {\n\tlet value = await this.ctx.storage.get(key)\n\tif (!value) {\n \t await this.ctx.storage.put(key, true)\n \t return true\n\t}\n\treturn false\n }\n\n unlock() {\n\treturn this.ctx.storage.delete(key)\n }\n}\n&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;&lt;i&gt;Codeblock 3: Lock service implemented with a Durable Object&lt;/i&gt;&lt;/p&gt;&lt;p&gt;That service can then be used as a cache lock.&lt;/p&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;// CACHE_LOCK is an instantiation of the above binding\n// Assuming the above is deployed as a worker with name `lock`\n// It can be bound in wrangler.toml as follows\n// services = [ { binding = &amp;quot;CACHE_LOCK&amp;quot;, service = &amp;quot;lock&amp;quot; } ]\n\nconst LOCK_KEY = &amp;quot;cat_image_service&amp;quot;\n\nasync function fetchAndCache(env, ctx) {\n let response = await fetch(&amp;#039;...&amp;#039;)\n ctx.waitUntil(env.CACHE_LOCK.unlock(LOCK_KEY))\n ...\n}\n\nfunction shouldRevalidate(env, expirationDate) {\n let remainingCacheTimeInS = (expirationDate.getTime() - Date.now()) / 1000\n\n // check if the expiry window is now, and then if the revalidation lock is available. if it is, take it\n return remainingCacheTimeInS &amp;lt;= CACHE_REVALIDATION_INTERVAL_IN_S &amp;amp;&amp;amp; env.CACHE_LOCK.try_lock(LOCK_KEY)\n}\n&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;&lt;i&gt;Codeblock 4: Image server with early-revalidation and a cache using a cache-lock&lt;/i&gt;&lt;/p&gt;&lt;p&gt;Now you might say &amp;quot;Et voilà. No need for probabilities and mathematics. Peak engineering has triumphed.&amp;quot; And you might be right, in most cases. That&amp;#39;s why cache locks are so &lt;a href=\&quot;https://developers.cloudflare.com/cache/concepts/revalidation/\&quot;&gt;&lt;u&gt;predominant&lt;/u&gt;&lt;/a&gt;: they are conceptually simple, deterministic for the same key, and scale well with predictable resource usage.&lt;/p&gt;&lt;p&gt;On the other hand, cache locks add latency and fallibility. To take ownership of a lock, cache revalidation has to contact the lock service. This service is shared across different processes, possibly different machines in different locations. Requests therefore take time. In addition, this service might be unavailable. Probabilistic cache revalidation does not suffer from these, given it does not reach out to an external service but rolls a die with the local randomness generator. It does so at the cost of not guaranteeing the number of requests going to the origin server: maybe zero for an extended period, maybe more than one. On average, this is going to be fine. But there can be border cases, similar to how one can roll a die 10 times and get 10 sixes. It’s unlikely, but not unrealistic, and certain services need that certainty. In the following sections, we dissect this approach.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;first-dive-into-probabilities-given-a-stable-request-rate\&quot;&gt;First dive into probabilities given a stable request rate&lt;/h2&gt;\n &lt;a href=\&quot;#first-dive-into-probabilities-given-a-stable-request-rate\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;A first approach is to reduce the number of requests going to the origin server. Instead of always sending a request to revalidate, we are going to send 1 out of 10. This means that instead of sending 10 requests per second when the cache is invalidated, we send 1 per second.&lt;/p&gt;&lt;p&gt;Because we don&amp;#39;t have a lock, we do that with probabilities. We set the probability of sending a request to the origin to be $p=\\frac{1}{10}$. With a rate $r$ of 10 requests per second, after 1 second, the expectancy of a request being sent to the origin is $1-(1-p)^{10}=65\\%$. We draw the evolution of the function $E(r, t)=1-(1-p)^{r \\times t}$ representing the expectancy of a request being sent to the server over time.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/78UvqHlQ6Se6cifbla3ozw/111decceed55ba1387b78a75e28f04cc/BLOG-2639_6.jpg\&quot; alt=\&quot;BLOG-2639 6\&quot; class=\&quot;kg-image\&quot; width=\&quot;1000\&quot; height=\&quot;600\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Figure 5: Revalidation time $E(t)$ with $r=10$ and $p=\\frac{1}{10}$. At time $t$, $E(t)$ is the probability that an early revalidation occurred.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;The graph moves very quickly towards $1$. This means we might still have space to reduce the number of requests going to our origin server. We can set a lower probability, such as $p_2=\\frac{1}{500}$ (1 request every 5 seconds on average). The graph looks as follows:&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/TtLqU9nyKqtYxIz5oJefX/fba679de0599854ba344f19bd0b97939/BLOG-2639_7.jpg\&quot; alt=\&quot;BLOG-2639 7\&quot; class=\&quot;kg-image\&quot; width=\&quot;1000\&quot; height=\&quot;600\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Figure 6: Revalidation time $E(t)$ with $r=10$ and $p=\\frac{1}{500}$.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;This looks great. Let&amp;#39;s implement it.&lt;/p&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;const CACHE_REVALIDATION_INTERVAL_IN_S = 300\nconst CACHE_REVALIDATION_PROBABILITY = 1/500\n\nfunction shouldRevalidate(expirationDate) {\n let remainingCacheTimeInS = (expirationDate.getTime() - Date.now()) / 1000\n\n if (remainingCacheTimeInS &amp;gt; CACHE_REVALIDATION_INTERVAL_IN_S) {\n\treturn false\n }\n if (remainingCacheTimeInS &amp;lt;= 0) {\n\treturn true\n }\n return Math.random() &amp;lt; CACHE_REVALIDATION_PROBABILITY\n}\n&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;&lt;i&gt;Codeblock 5: Image server with early-revalidation and a probabilistic cache using uniform distribution&lt;/i&gt;&lt;/p&gt;&lt;p&gt;That&amp;#39;s it. If the cache is not close to expiration, we don&amp;#39;t revalidate. If the cache is expired, we revalidate. Otherwise, we revalidate based on a probability.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;adaptive-cache-revalidation\&quot;&gt;Adaptive cache revalidation&lt;/h2&gt;\n &lt;a href=\&quot;#adaptive-cache-revalidation\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Until now, we assumed the picture of the cat received a stable request rate. However, for a real service, this does not necessarily hold. For instance, if instead of 10 requests per second, imagine the service receives only 1. The expectancy function does not look as good. After 5 minutes (300s), $E(r=1, t=300)=45\\%$. On the other hand, if the image service is receiving 10,000 requests per second, $E(r=10000, t = 300) \\approx 100\\%$, but our server receives on average $10000 \\times \\frac{1}{500} = 20$ requests per second. It would be ideal to design a probability function that would adapt to the request rate.&lt;/p&gt;&lt;p&gt;That function would return a low probability when expiration time is far in the future, and increase over time such that the cache is revalidated before it expires. It would cap the request rate going to the origin server.&lt;/p&gt;&lt;p&gt;Let’s design the variation of probability $p$ over 5 minutes. When far from the expiration, the probability to revalidate should be low. This should help match the high request rate. For example, with a request rate of 10k requests per second, we would like the revalidation probability $p$ to be $\\frac{1}{100000}$. This ensures the request rates seen by our server are going to be low on average, at about 1 request every 10 seconds. As time passes, we increase this probability to allow for revalidation even at a lower request rate.&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;&lt;b&gt;Time to expiration $t$ (in s)&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;b&gt;Revalidation probability $p$&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;b&gt;Target request rate $r$ (in rps)&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;300&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;1/100000&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;10000&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;240&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;1/10000&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;1000&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;180&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;1/1000&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;100&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;120&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;1/100&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;10&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;60&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;1/10&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;p&gt;0&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;1&lt;/p&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;-&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;&lt;i&gt;Table 1: Variation of revalidation probability over time&lt;/i&gt;&lt;/p&gt;&lt;p&gt;For each of these intervals, there is a high likelihood that a request rate &lt;code&gt;$r$&lt;/code&gt; will trigger a cache revalidation, and low likelihood that a lower request rate will trigger it. If it does, it&amp;#39;s ok.&lt;/p&gt;&lt;p&gt;We can update our revalidation function as follows:&lt;/p&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;const CACHE_REVALIDATION_INTERVAL_IN_S = 300\nconst CACHE_REVALIDATION_PROBABILITY_PER_MIN = [1/100_000, 1/10_000, 1/1000, 1/100, 1/10, 1]\n\nfunction shouldRevalidate(expirationDate) {\n let remainingCacheTimeInS = (expirationDate.getTime() - Date.now()) / 1000\n\n if (remainingCacheTimeInS &amp;gt; CACHE_REVALIDATION_INTERVAL_IN_S) {\n\treturn false\n }\n if (remainingCacheTimeInS &amp;lt;= 0) {\n\treturn true\n }\n let currentMinute = Math.floor(remainingCacheTimeInS/60)\n return Math.random() &amp;lt; CACHE_REVALIDATION_PROBABILITY_PER_MIN[currentMinute]\n}\n&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;&lt;i&gt;Codeblock 6: Image server with early-revalidation and a probabilistic cache using piecewise uniform distribution&lt;/i&gt;&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;optimal-cache-stampede-solution\&quot;&gt;Optimal cache stampede solution&lt;/h2&gt;\n &lt;a href=\&quot;#optimal-cache-stampede-solution\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;There seems to be a lot of decisions going on here. To solve this, we can reference an academic paper written by A Vattani, T Chierichetti, and K Lowenstein in 2015 called &lt;a href=\&quot;https://cseweb.ucsd.edu/~avattani/papers/cache_stampede.pdf\&quot;&gt;&lt;u&gt;Optimal Probabilistic Cache Stampede Prevention&lt;/u&gt;&lt;/a&gt;. If you read it, you&amp;#39;ll recognise that what we have been discussing until now is close to what the paper presents. For instance, both the cache revalidation algorithm structure and the early revalidation function look similar.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1MLwwNsci3XxhK0uAvWcFu/62a17cc9056d6b12832160acc9e40fb9/BLOG-2639_8.png\&quot; alt=\&quot;BLOG-2639 8\&quot; class=\&quot;kg-image\&quot; width=\&quot;850\&quot; height=\&quot;445\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Figure 7: Probabilistic early expiration of a cache item as defined by Figure 2 of Optimal Probabilistic Cache Stampede Prevention paper. In our case, $\\mathcal{D}=300$&lt;/i&gt;&lt;/p&gt;&lt;p&gt;One takeaway from the paper is that instead of discretization, with a probability from 0 to 60s, then from 60s to 120s, …, the probability function can be continuous. Instead of a fixed $p$, there is a function $p(t)$ of time $t$.&lt;/p&gt;&lt;p&gt;$p(t)=e^{-\\lambda (expiry-t)}, \\text{ with } expiry=300, \\text{ and } t \\in [0, 300]$&lt;/p&gt;&lt;p&gt;We call $\\lambda$ the steepness parameter, and set it to $\\frac{1}{300}$, $300$ being our early expiration gap.&lt;/p&gt;&lt;p&gt;The expectancy over time is $E(r, t)=1-e^{-rλt}$. This leads to the expectancy below for various request rates. You can note that when $r=1$, there is not a $100%$ chance that the request will be revalidated before expiry.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3lHElc96ZDNbcMfXVQ9pUm/e70e98b4be158dcde11b95366a736742/BLOG-2639_9.jpg\&quot; alt=\&quot;BLOG-2639 9\&quot; class=\&quot;kg-image\&quot; width=\&quot;1000\&quot; height=\&quot;600\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;i&gt;Figure 8: Revalidation time $E(t)$ for multiple $r$ with an exponential distribution.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;This leads to the final code snippet:&lt;/p&gt;\n &lt;pre class=\&quot;language-javascript\&quot;&gt;&lt;code class=\&quot;language-javascript\&quot;&gt;const CACHE_REVALIDATION_INTERVAL_IN_S = 300\nconst REVALIDATION_STEEPNESS = 1/300\n\nfunction shouldRevalidate(expirationDate) {\n let remainingCacheTimeInS = (expirationDate.getTime() - Date.now()) / 1000\n\n if (remainingCacheTimeInS &amp;gt; CACHE_REVALIDATION_INTERVAL_IN_S) {\n\treturn false\n }\n if (remainingCacheTimeInS &amp;lt;= 0) {\n\treturn true\n }\n// p(t) is evaluated here\n return Math.random() &amp;lt; Math.exp(-REVALIDATION_STEEPNESS*remainingCacheTimeInS)\n}\n&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;&lt;i&gt;Codeblock 7: Image server with early-revalidation and a probabilistic cache using exponential distribution&lt;/i&gt;&lt;/p&gt;&lt;p&gt;And that&amp;#39;s it. Given &lt;code&gt;Date.now(&lt;/code&gt;) has a granularity, and is not continuous, it would also be possible to discretise these functions, even though the gains are minimal. This is what we have done in a &lt;a href=\&quot;https://github.com/cloudflare/privacypass-issuer/blob/main/src/cache.ts#L60-L103\&quot;&gt;&lt;u&gt;production worker implementation&lt;/u&gt;&lt;/a&gt;, where the number of requests is important. It is a service that benefits from caching for performance consideration, and that cannot use built-in &lt;a href=\&quot;https://developers.cloudflare.com/workers/runtime-apis/cache/\&quot;&gt;&lt;u&gt;stale-while-revalidate&lt;/u&gt;&lt;/a&gt; from within Cloudflare workers. Probabilistic cache stampede prevention is well-suited here, as no new component has to be built, and it performs well at different request rates.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;conclusion\&quot;&gt;Conclusion&lt;/h2&gt;\n &lt;a href=\&quot;#conclusion\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;We have seen how to solve cache stampede without a lock, its implementation, and why it is optimal. In the real world, you likely will not encounter this issue: either because it’s good enough to optimize your origin service to serve more requests, or because you can leverage a CDN cache. In fact, most HTTP caches provide an API that follows &lt;a href=\&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control\&quot;&gt;&lt;u&gt;Cache Control&lt;/u&gt;&lt;/a&gt;, and likely have all the tools you need. This primitive is also built into certain products, such as &lt;a href=\&quot;https://developers.cloudflare.com/kv/platform/limits/\&quot;&gt;&lt;u&gt;Cloudflare KV&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you have not done so, you can go and experiment with all the code snippets presented in this blog on the Cloudflare Workers Playground at &lt;a href=\&quot;https://cloudflareworkers.com\&quot;&gt;&lt;u&gt;cloudflareworkers.com&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&quot;],&quot;published_at&quot;:[0,&quot;2024-12-26T14:00+00:00&quot;],&quot;updated_at&quot;:[0,&quot;2025-01-10T21:56:06.997Z&quot;],&quot;feature_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5JRBcwN5l6vgSUG2l3sdz/25eb0496b0b2baa30ea890dd7c1dc078/image__1_.png&quot;],&quot;tags&quot;:[1,[[0,{&quot;id&quot;:[0,&quot;1x7tpPmKIUCt19EDgM1Tsl&quot;],&quot;name&quot;:[0,&quot;Research&quot;],&quot;slug&quot;:[0,&quot;research&quot;]}],[0,{&quot;id&quot;:[0,&quot;5RrjSR5vIOJAfRdT8966hf&quot;],&quot;name&quot;:[0,&quot;Cache&quot;],&quot;slug&quot;:[0,&quot;cache&quot;]}],[0,{&quot;id&quot;:[0,&quot;6hbkItfupogJP3aRDAq6v8&quot;],&quot;name&quot;:[0,&quot;Cloudflare Workers&quot;],&quot;slug&quot;:[0,&quot;workers&quot;]}],[0,{&quot;id&quot;:[0,&quot;3JAY3z7p7An94s6ScuSQPf&quot;],&quot;name&quot;:[0,&quot;Developer Platform&quot;],&quot;slug&quot;:[0,&quot;developer-platform&quot;]}]]],&quot;relatedTags&quot;:[0],&quot;authors&quot;:[1,[[0,{&quot;name&quot;:[0,&quot;Thibault Meunier&quot;],&quot;slug&quot;:[0,&quot;thibault&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1CqrdcRymVgEs1zRfSE6Xr/b8182164b0a8435b162bdd1246b7e91f/thibault.png&quot;],&quot;location&quot;:[0,null],&quot;website&quot;:[0,null],&quot;twitter&quot;:[0,&quot;@thibmeu&quot;],&quot;facebook&quot;:[0,null]}]]],&quot;meta_description&quot;:[0,&quot;If you want to know what cache revalidation is, how it works, and why it can involve rolling a die, read on. This blog post presents a lock-free probabilistic approach to cache revalidation, along with its implementation.&quot;],&quot;primary_author&quot;:[0,{}],&quot;localeList&quot;:[0,{&quot;name&quot;:[0,&quot;blog-english-only&quot;],&quot;enUS&quot;:[0,&quot;English for Locale&quot;],&quot;zhCN&quot;:[0,&quot;No Page for Locale&quot;],&quot;zhHansCN&quot;:[0,&quot;No Page for Locale&quot;],&quot;zhTW&quot;:[0,&quot;No Page for Locale&quot;],&quot;frFR&quot;:[0,&quot;No Page for Locale&quot;],&quot;deDE&quot;:[0,&quot;No Page for Locale&quot;],&quot;itIT&quot;:[0,&quot;No Page for Locale&quot;],&quot;jaJP&quot;:[0,&quot;No Page for Locale&quot;],&quot;koKR&quot;:[0,&quot;No Page for Locale&quot;],&quot;ptBR&quot;:[0,&quot;No Page for Locale&quot;],&quot;esLA&quot;:[0,&quot;No Page for Locale&quot;],&quot;esES&quot;:[0,&quot;No Page for Locale&quot;],&quot;enAU&quot;:[0,&quot;No Page for Locale&quot;],&quot;enCA&quot;:[0,&quot;No Page for Locale&quot;],&quot;enIN&quot;:[0,&quot;No Page for Locale&quot;],&quot;enGB&quot;:[0,&quot;No Page for Locale&quot;],&quot;idID&quot;:[0,&quot;No Page for Locale&quot;],&quot;ruRU&quot;:[0,&quot;No Page for Locale&quot;],&quot;svSE&quot;:[0,&quot;No Page for Locale&quot;],&quot;viVN&quot;:[0,&quot;No Page for Locale&quot;],&quot;plPL&quot;:[0,&quot;No Page for Locale&quot;],&quot;arAR&quot;:[0,&quot;No Page for Locale&quot;],&quot;nlNL&quot;:[0,&quot;No Page for Locale&quot;],&quot;thTH&quot;:[0,&quot;No Page for Locale&quot;],&quot;trTR&quot;:[0,&quot;No Page for Locale&quot;],&quot;heIL&quot;:[0,&quot;No Page for Locale&quot;],&quot;lvLV&quot;:[0,&quot;No Page for Locale&quot;],&quot;etEE&quot;:[0,&quot;No Page for Locale&quot;],&quot;ltLT&quot;:[0,&quot;No Page for Locale&quot;]}],&quot;url&quot;:[0,&quot;https://blog.cloudflare.com/sometimes-i-cache&quot;],&quot;metadata&quot;:[0,{&quot;title&quot;:[0,&quot;Sometimes I cache: implementing lock-free probabilistic caching&quot;],&quot;description&quot;:[0,&quot;If you want to know what cache revalidation is, how it works, and why it can involve rolling a die, read on. This blog post presents a lock-free probabilistic approach to cache revalidation, along with its implementation.&quot;],&quot;imgPreview&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3IAq3KKuEXijAkIY8E3rgU/b0aa6e8f62c10cd392fa858fcb849169/BLOG-2639_OGa.png&quot;]}]}],[0,{&quot;id&quot;:[0,&quot;5LVsblxj2Git54IRxadpyg&quot;],&quot;title&quot;:[0,&quot;How we prevent conflicts in authoritative DNS configuration using formal verification&quot;],&quot;slug&quot;:[0,&quot;topaz-policy-engine-design&quot;],&quot;excerpt&quot;:[0,&quot;We describe how Cloudflare uses a custom Lisp-like programming language and formal verifier (written in Racket and Rosette) to prevent logical contradictions in our authoritative DNS nameserver’s behavior.&quot;],&quot;featured&quot;:[0,false],&quot;html&quot;:[0,&quot;&lt;p&gt;Over the last year, Cloudflare has begun formally verifying the correctness of our internal DNS addressing behavior — the logic that determines which IP address a DNS query receives when it hits our authoritative nameserver. This means that for every possible DNS query for a &lt;a href=\&quot;https://developers.cloudflare.com/dns/manage-dns-records/reference/proxied-dns-records/\&quot;&gt;&lt;u&gt;proxied&lt;/u&gt;&lt;/a&gt; domain we could receive, we try to mathematically prove properties about our DNS addressing behavior, even when different systems (owned by different teams) at Cloudflare have contradictory views on which IP addresses should be returned.&lt;/p&gt;&lt;p&gt;To achieve this, we formally verify the programs — written in a custom &lt;a href=\&quot;https://en.wikipedia.org/wiki/Lisp_(programming_language)\&quot;&gt;&lt;u&gt;Lisp&lt;/u&gt;&lt;/a&gt;-like programming language — that our nameserver executes when it receives a DNS query. These programs determine which IP addresses to return. Whenever an engineer changes one of these programs, we run all the programs through our custom model checker (written in &lt;a href=\&quot;https://racket-lang.org/\&quot;&gt;&lt;u&gt;Racket&lt;/u&gt;&lt;/a&gt; + &lt;a href=\&quot;https://emina.github.io/rosette/\&quot;&gt;&lt;u&gt;Rosette&lt;/u&gt;&lt;/a&gt;) to check for certain bugs (e.g., one program overshadowing another) before the programs are deployed.&lt;/p&gt;&lt;p&gt;Our formal verifier runs in production today, and is part of a larger addressing system called Topaz. In fact, it’s likely you’ve made a DNS query today that triggered a formally verified Topaz program.&lt;/p&gt;&lt;p&gt;This post is a technical description of how Topaz’s formal verification works. Besides being a valuable tool for Cloudflare engineers, Topaz is a real-world example of &lt;a href=\&quot;https://en.wikipedia.org/wiki/Formal_verification\&quot;&gt;&lt;u&gt;formal verification&lt;/u&gt;&lt;/a&gt; applied to networked systems. We hope it inspires other network operators to incorporate formal methods, where appropriate, to help make the Internet more reliable for all.&lt;/p&gt;&lt;p&gt;Topaz’s full technical details have been peer-reviewed and published in &lt;a href=\&quot;https://conferences.sigcomm.org/sigcomm/2024/\&quot;&gt;&lt;u&gt;ACM SIGCOMM 2024&lt;/u&gt;&lt;/a&gt;, with both a &lt;a href=\&quot;https://research.cloudflare.com/publications/Larisch2024/\&quot;&gt;&lt;u&gt;paper&lt;/u&gt;&lt;/a&gt; and short &lt;a href=\&quot;https://www.youtube.com/watch?v=hW7RjXVx7_Q\&quot;&gt;&lt;u&gt;video&lt;/u&gt;&lt;/a&gt; available online. &lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;addressing-how-ip-addresses-are-chosen\&quot;&gt;Addressing: how IP addresses are chosen&lt;/h2&gt;\n &lt;a href=\&quot;#addressing-how-ip-addresses-are-chosen\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;When a DNS query for a customer’s proxied domain hits Cloudflare’s nameserver, the nameserver returns an IP address — but how does it decide which address to return?&lt;/p&gt;&lt;p&gt;Let’s make this more concrete. When a customer, say &lt;code&gt;example.com&lt;/code&gt;, signs up for Cloudflare and &lt;a href=\&quot;https://developers.cloudflare.com/dns/manage-dns-records/reference/proxied-dns-records/\&quot;&gt;&lt;u&gt;proxies&lt;/u&gt;&lt;/a&gt; their traffic through Cloudflare, it makes Cloudflare’s nameserver &lt;i&gt;authoritative&lt;/i&gt; for their domain, which means our nameserver has the &lt;i&gt;authority &lt;/i&gt;to respond to DNS queries for &lt;code&gt;example.com&lt;/code&gt;. Later, when a client makes a DNS query for &lt;code&gt;example.com&lt;/code&gt;, the client’s recursive DNS resolver (for example, &lt;a href=\&quot;https://www.cloudflare.com/learning/dns/what-is-1.1.1.1/\&quot;&gt;&lt;u&gt;1.1.1.1&lt;/u&gt;&lt;/a&gt;) queries our nameserver for the authoritative response. Our nameserver returns &lt;b&gt;&lt;i&gt;some&lt;/i&gt;&lt;/b&gt;&lt;i&gt; &lt;/i&gt;Cloudflare IP address (of our choosing) to the resolver, which forwards that address to the client. The client then uses the IP address to connect to Cloudflare’s network, which is a global &lt;a href=\&quot;https://www.cloudflare.com/en-gb/learning/cdn/glossary/anycast-network/\&quot;&gt;&lt;u&gt;anycast&lt;/u&gt;&lt;/a&gt; network — every data center advertises all of our addresses.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/72EmlVrMTMBMhxrhZ50YI9/54e08160ea98c55bc8e2703d7c85927b/image3.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1929\&quot; height=\&quot;732\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;sup&gt;Clients query Cloudflare’s nameserver (via their resolver) for customer domains. The nameserver returns Cloudflare IP addresses, advertised by our entire global network, which the client uses to connect to the customer domain. Cloudflare may then connect to the origin server to fulfill the user’s HTTPS request.&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;When the customer has &lt;a href=\&quot;https://developers.cloudflare.com/byoip/\&quot;&gt;&lt;u&gt;configured a static IP address&lt;/u&gt;&lt;/a&gt; for their domain, our nameserver’s choice of IP address is simple: it simply returns that static address in response to queries made for that domain.&lt;/p&gt;&lt;p&gt;But for all other customer domains, our nameserver could respond with virtually any IP address that we own and operate. We may return the &lt;i&gt;same&lt;/i&gt; address in response to queries for &lt;i&gt;different&lt;/i&gt; domains, or &lt;i&gt;different&lt;/i&gt; addresses in response to different queries for the &lt;i&gt;same&lt;/i&gt; domain. We do this for resilience, but also because decoupling names and IP addresses &lt;a href=\&quot;https://blog.cloudflare.com/addressing-agility\&quot;&gt;&lt;u&gt;improves flexibility&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;With all that in mind, let’s return to our initial question: given a query for a proxied domain without a static IP, which IP address should be returned? The answer: &lt;b&gt;Cloudflare chooses IP addresses to meet various business objectives. &lt;/b&gt;For instance, we may choose IPs to:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Change the IP address of a domain that is under attack.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Direct fractions of traffic to specific IP addresses to test new features or services.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=\&quot;https://blog.cloudflare.com/cloudflare-incident-on-september-17-2024/\&quot;&gt;&lt;u&gt;Remap or “renumber”&lt;/u&gt;&lt;/a&gt; domain names to new IP address space.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;topaz-executes-dns-objectives\&quot;&gt;Topaz executes DNS objectives&lt;/h2&gt;\n &lt;a href=\&quot;#topaz-executes-dns-objectives\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;To change authoritative nameserver behavior — how we choose IPs —  a Cloudflare engineer encodes their desired DNS business objective as a declarative Topaz program. Our nameserver stores the list of all such programs such that when it receives a DNS query for a proxied domain, it executes the list of programs in sequence until one returns an IP address. It then returns that IP to the resolver.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3gyUw7j0GlTXj0Vm637aCW/9aa353d512151d5878998e199a538973/image1.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1600\&quot; height=\&quot;454\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;&lt;sup&gt;Topaz receives DNS queries (metadata included) for proxied domains from Cloudflare’s nameserver. It executes a list of policies in sequence until a match is found. It returns the resulting IP address to the nameserver, which forwards it to the resolver.&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;What do these programs look like?&lt;/p&gt;&lt;p&gt;Each Topaz program has three primary components:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;Match function: &lt;/b&gt;A program’s match function specifies under which circumstances the program should execute. It takes as input DNS query metadata (e.g., datacenter information, account information) and outputs a boolean. If, given a DNS query, the match function returns &lt;i&gt;true&lt;/i&gt;, the program’s response function is executed.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;Response function&lt;/b&gt;: A program’s response function specifies &lt;i&gt;which&lt;/i&gt; IP addresses should be chosen. It also takes as input all the DNS query metadata, but outputs a 3-tuple (IPv4 addresses, IPv6 addresses, and TTL). When a program’s match function returns true, its corresponding response function is executed. The resulting IP addresses and TTL are returned to the resolver that made the query. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;Configuration&lt;/b&gt;: A program’s configuration is a set of variables that parameterize that program’s match and response function. The match and response functions reference variables in the corresponding configuration, thereby separating the macro-level behavior of a program (match/response functions) from its nitty-gritty details (specific IP addresses, names, etc.). This separation makes it easier to understand how a Topaz program behaves at a glance, without getting bogged down by specific function parameters.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Let’s walk through an example Topaz program. The goal of this program is to give all queried domains whose metadata field “tag1” is equal to “orange” a particular IP address. The program looks like this:&lt;/p&gt;\n &lt;pre class=\&quot;language-Lisp\&quot;&gt;&lt;code class=\&quot;language-Lisp\&quot;&gt;- name: orange\n config: |\n (config\n ([desired_tag1 &amp;quot;orange&amp;quot;]\n [ipv4 (ipv4_address “192.0.2.3”)]\n [ipv6 (ipv6_address “2001:DB8:1:3”)]\n [t (ttl 300]))\n match: |\n (= query_domain_tag1 desired_tag1) \n response: |\n (response (list ipv4) (list ipv6) t)&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;Before we walk through the program, note that the program’s configuration, match, and response function are YAML strings, but more specifically they are topaz-lang expressions. Topaz-lang is the &lt;a href=\&quot;https://en.wikipedia.org/wiki/Domain-specific_language\&quot;&gt;&lt;u&gt;domain-specific language (DSL)&lt;/u&gt;&lt;/a&gt; we created specifically for expressing Topaz programs. It is based on &lt;a href=\&quot;https://www.scheme.org/\&quot;&gt;&lt;u&gt;Scheme&lt;/u&gt;&lt;/a&gt;, but is much simpler. It is dynamically typed, it is not &lt;a href=\&quot;https://en.wikipedia.org/wiki/Turing_completeness\&quot;&gt;&lt;u&gt;Turing complete&lt;/u&gt;&lt;/a&gt;, and every expression evaluates to exactly one value (though functions can throw errors). Operators cannot define functions within topaz-lang, they can only add new DSL functions by writing functions in the host language (Go). The DSL provides basic types (numbers, lists, maps) but also Topaz-specific types, like IPv4/IPv6 addresses and TTLs.&lt;/p&gt;&lt;p&gt;Let’s now examine this program in detail. &lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;The &lt;code&gt;config&lt;/code&gt; is a set of four &lt;i&gt;bindings&lt;/i&gt; from name to value. The first binds the string &lt;code&gt;”orange”&lt;/code&gt; to the name &lt;code&gt;desired_tag1&lt;/code&gt;. The second binds the IPv4 address &lt;code&gt;192.0.2.3&lt;/code&gt; to the name &lt;code&gt;ipv4&lt;/code&gt;. The third binds the IPv6 address &lt;code&gt;2001:DB8:1:3&lt;/code&gt; to the name &lt;code&gt;ipv6&lt;/code&gt;. And the fourth binds the TTL (for which we added a topaz-lang type) &lt;code&gt;300&lt;/code&gt; (seconds) to the name &lt;code&gt;t&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The &lt;code&gt;match&lt;/code&gt; function is an expression that &lt;i&gt;must&lt;/i&gt; evaluate to a boolean. It can reference configuration values (e.g., &lt;code&gt;desired_tag1&lt;/code&gt;), and can also reference DNS query fields. All DNS query fields use the prefix &lt;code&gt;query_&lt;/code&gt; and are brought into scope at evaluation time. This program’s match function checks whether &lt;code&gt;desired_tag1&lt;/code&gt; is equal to the tag attached to the queried domain, &lt;code&gt;query_domain_tag1&lt;/code&gt;. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The &lt;code&gt;response&lt;/code&gt; function is an expression that evaluates to the special &lt;code&gt;response&lt;/code&gt; type, which is really just a 3-tuple consisting of: a list of IPv4 addresses, a list of IPv6 addresses, and a TTL. This program’s response function simply returns the configured IPv4 address, IPv6 address, and TTL (seconds).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Critically, &lt;i&gt;all&lt;/i&gt; Topaz programs are encoded as YAML and live in the same version-controlled file. Imagine this program file contained only the &lt;code&gt;orange&lt;/code&gt; program above, but now, a new team wants to add a new program, which checks whether the queried domain’s “tag1” field is equal to “orange” AND that the domain’s “tag2” field is equal to true:&lt;/p&gt;\n &lt;pre class=\&quot;language-Lisp\&quot;&gt;&lt;code class=\&quot;language-Lisp\&quot;&gt;- name: orange_and_true\n config: |\n (config\n ([desired_tag1 &amp;quot;orange&amp;quot;]\n [ipv4 (ipv4_address “192.0.2.2”)]\n [ipv6 (ipv6_address “2001:DB8:1:2”)]\n [t (ttl 300)]))\n match: |\n (and (= query_domain_tag1 desired_tag1)\n query_domain_tag2)\n response: |\n (response (list ipv4) (list ipv6) t)&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;This new team must place their new &lt;code&gt;orange_and_true&lt;/code&gt; program either below or above the &lt;code&gt;orange&lt;/code&gt; program in the file containing the list of Topaz programs. For instance, they could place &lt;code&gt;orange_and_true&lt;/code&gt; after &lt;code&gt;orange&lt;/code&gt;, like so:&lt;/p&gt;\n &lt;pre class=\&quot;language-Lisp\&quot;&gt;&lt;code class=\&quot;language-Lisp\&quot;&gt;- name: orange\n config: …\n match: …\n response: …\n- name: orange_and_true\n config: …\n match: …\n response: …&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;Now let’s add a third, more interesting Topaz program. Say a Cloudflare team wants to test a modified version of our CDN’s HTTP server on a small percentage of domains, and only in a subset of Cloudflare’s data centers. Furthermore, they want to distribute these queries across a specific IP prefix such that queries for the same domain get the same IP. They write the following:&lt;/p&gt;\n &lt;pre class=\&quot;language-Lisp\&quot;&gt;&lt;code class=\&quot;language-Lisp\&quot;&gt;- name: purple\n config: |\n (config\n ([purple_datacenters (fetch_datacenters “purple”)]\n [percentage 10]\n [ipv4_prefix (ipv4_prefix “203.0.113.0/24”)]\n [ipv6_prefix (ipv6_prefix “2001:DB8:3::/48”)]))\n match: |\n (let ([rand (rand_gen (hash query_domain))])\n (and (member? purple_datacenters query_datacenter)\n (&amp;lt; (random_number (range 0 99) rand) percentage)))\n response: |\n (let ([hashed_domain (hash query_domain)]\n [ipv4_address (select_from ipv4_prefix hashed_domain)]\n [ipv6_address (select_from ipv6_prefix hashed_domain)])\n (response (list ipv4_address) (list ipv6_address) (ttl 1)))&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;This Topaz program is significantly more complicated, so let’s walk through it.&lt;/p&gt;&lt;p&gt;Starting with configuration: &lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;The first configuration value, &lt;code&gt;purple_datacenters&lt;/code&gt;, is bound to the expression &lt;code&gt;(fetch_datacenters “purple”)&lt;/code&gt;, which is a function that retrieves all Cloudflare data centers tagged “purple” via an internal HTTP API. The result of this function call is a list of data centers. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The second configuration value, &lt;code&gt;percentage&lt;/code&gt;, is a number representing the fraction of traffic we would like our program to act upon.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The third and fourth names are bound to IP prefixes, v4 and v6 respectively (note the &lt;code&gt;built-in ipv4_prefix&lt;/code&gt; and &lt;code&gt;ipv6_prefix&lt;/code&gt; types).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The match function is also more complicated. First, note the &lt;code&gt;let&lt;/code&gt; form — this lets operators define local variables. We define one local variable, a random number generator called &lt;code&gt;rand&lt;/code&gt; seeded with the hash of the queried domain name. The match expression itself is a conjunction that checks two things. &lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;First, it checks whether the query landed in a data center tagged “purple”. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Second, it checks whether a random number between 0 and 99 (produced by a generator seeded by the domain name) is less than the configured percentage. By seeding the random number generator with the domain, the program ensures that 10% of &lt;i&gt;domains&lt;/i&gt; trigger a match. If we had seeded the RNG with, say, the query ID, then queries for the same domain would behave differently.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Together, the conjuncts guarantee that the match expression evaluates to true for 10% of domains queried in “purple” data centers.&lt;/p&gt;&lt;p&gt;Now let’s look at the response function. We define three local variables. The first is a hash of the domain. The second is an IPv4 address selected from the configured IPv4 prefix. &lt;code&gt;select_from&lt;/code&gt; always chooses the same IP address given the same prefix and hash — this ensures that queries for a given domain always receive the same IP address (which makes it easier to correlate queries for a single domain), but that queries for different domains can receive different IP addresses within the configured prefix. The third local variable is an IPv6 address selected similarly. The response function returns these IP addresses and a TTL of value 1 (second).&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;topaz-programs-are-executed-on-the-hot-path\&quot;&gt;Topaz programs are executed on the hot path&lt;/h2&gt;\n &lt;a href=\&quot;#topaz-programs-are-executed-on-the-hot-path\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Topaz’s control plane validates the list of programs and distributes them to our global nameserver instances. As we’ve seen, the list of programs reside in a single, version-controlled YAML file. When an operator changes this file (i.e., adds a program, removes a program, or modifies an existing program), Topaz’s control plane does the following things in order:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;First, it validates the programs, making sure there are no syntax errors. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Second, it “finalizes” each program’s configuration by evaluating every configuration binding and storing the result. (For instance, to finalize the &lt;code&gt;purple&lt;/code&gt; program, it evaluates &lt;code&gt;fetch_datacenters&lt;/code&gt;, storing the resulting list. This way our authoritative nameservers never need to retrieve external data.) &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Third, it &lt;i&gt;verifies&lt;/i&gt; the finalized programs, which we will explain below. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Finally, it distributes the finalized programs across our network.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Topaz’s control plane distributes the programs to all servers globally by writing the list of programs to &lt;a href=\&quot;https://blog.cloudflare.com/introducing-quicksilver-configuration-distribution-at-internet-scale/\&quot;&gt;&lt;u&gt;QuickSilver&lt;/u&gt;&lt;/a&gt;, our edge key-value store. The Topaz service on each server detects changes in Quicksilver and updates its program list.&lt;/p&gt;&lt;p&gt;When our nameserver service receives a DNS query, it augments the query with additional metadata (e.g., tags) and then forwards the query to the Topaz service (both services run on every Cloudflare server) via Inter-Process Communication (IPC). Topaz, upon receiving a DNS query from the nameserver, walks through its program list, executing each program’s match function (using the topaz-lang interpreter) with the DNS query in scope (with values prefixed with &lt;code&gt;query_&lt;/code&gt;). It walks the list until a match function returns &lt;code&gt;true&lt;/code&gt;. It then executes that program’s response function, and returns the resulting IP addresses and TTL to our nameserver. The nameserver packages these addresses and TTL in valid DNS format, and then returns them to the resolver. &lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;topaz-programs-are-formally-verified\&quot;&gt;Topaz programs are formally verified&lt;/h2&gt;\n &lt;a href=\&quot;#topaz-programs-are-formally-verified\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Before programs are distributed to our global network, they are formally verified. Each program is passed through our formal verification tool which throws an error if a program has a bug, or if two programs (e.g., the &lt;code&gt;orange_and_true&lt;/code&gt; and &lt;code&gt;orange&lt;/code&gt; programs) conflict with one another.&lt;/p&gt;&lt;p&gt;The Topaz formal verifier (&lt;a href=\&quot;https://en.wikipedia.org/wiki/Model_checking\&quot;&gt;&lt;u&gt;model-checker&lt;/u&gt;&lt;/a&gt;) checks three properties.&lt;/p&gt;&lt;p&gt;First, it checks that each program is &lt;i&gt;satisfiable &lt;/i&gt;— that there exists &lt;i&gt;some&lt;/i&gt; DNS query that causes each program’s match function to return &lt;code&gt;true&lt;/code&gt;. This property is useful for detecting internally-inconsistent programs that will simply never match. For instance, if a program’s match expression was &lt;code&gt;(and true false)&lt;/code&gt;, there exists no query that will cause this to evaluate to true, so the verifier throws an error.&lt;/p&gt;&lt;p&gt;Second, it checks that each program is &lt;i&gt;reachable &lt;/i&gt;— that there exists some DNS query that causes each program’s match function to return &lt;code&gt;true&lt;/code&gt; &lt;i&gt;given all preceding programs.&lt;/i&gt; This property is useful for detecting “dead” programs that are completely overshadowed by higher-priority programs. For instance, recall the ordering of the &lt;code&gt;orange&lt;/code&gt; and &lt;code&gt;orange_and_true&lt;/code&gt; programs:&lt;/p&gt;\n &lt;pre class=\&quot;language-Lisp\&quot;&gt;&lt;code class=\&quot;language-Lisp\&quot;&gt;- name: orange\n config: …\n match: (= query_domain_tag1 &amp;quot;orange&amp;quot;) \n response: …\n- name: orange_and_true\n config: …\n match: (and (= query_domain_tag1 &amp;quot;orange&amp;quot;) query_domain_tag2)\n response: …&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;The verifier would throw an error because the &lt;code&gt;orange_and_true&lt;/code&gt; program is unreachable. For all DNS queries for which &lt;code&gt;query_domain_tag1&lt;/code&gt; is ”orange”, regardless of &lt;code&gt;metadata2&lt;/code&gt;, the &lt;code&gt;orange&lt;/code&gt; program will &lt;i&gt;always&lt;/i&gt; match, which means the &lt;code&gt;orange_and_true&lt;/code&gt; program will &lt;i&gt;never&lt;/i&gt; match. To resolve this error, we’d need to swap these two programs like we did above.&lt;/p&gt;&lt;p&gt;Finally, and most importantly, the verifier checks for program &lt;i&gt;conflicts&lt;/i&gt;: queries that cause any two programs to both match. If such a query exists, it throws an error (and prints the relevant query), and the operators are forced to resolve the conflict by changing their programs. However, it only checks whether specific programs conflict — those that are explicitly marked &lt;i&gt;exclusive. &lt;/i&gt;Operators mark their program as exclusive if they want to be sure that no other exclusive program could match on the same queries.&lt;/p&gt;&lt;p&gt;To see what conflict detection looks like, consider the corrected ordering of the &lt;code&gt;orange_and_true&lt;/code&gt; and &lt;code&gt;orange&lt;/code&gt; programs, but note that the two programs have now been marked exclusive:&lt;/p&gt;\n &lt;pre class=\&quot;language-Lisp\&quot;&gt;&lt;code class=\&quot;language-Lisp\&quot;&gt;- name: orange_and_true\n exclusive: true\n config: ...\n match: (and (= query_domain_tag1 &amp;quot;orange&amp;quot;) query_domain_tag2)\n response: ...\n- name: orange\n exclusive: true\n config: ...\n match: (= query_domain_tag1 &amp;quot;orange&amp;quot;) \n response: ...&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;After marking these two programs exclusive, the verifier will throw an error. Not only will it say that these two programs can contradict one another, but it will provide a sample query as proof:&lt;/p&gt;\n &lt;pre class=\&quot;language-Markdown\&quot;&gt;&lt;code class=\&quot;language-Markdown\&quot;&gt;Checking: no exclusive programs match the same queries: check FAILED!\nIntersecting programs found:\nprograms &amp;quot;orange_and_true&amp;quot; and &amp;quot;orange&amp;quot; both match any query...\n to any domain...\n with tag1: &amp;quot;orange&amp;quot;\n with tag2: true\n&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;The teams behind the &lt;code&gt;orange&lt;/code&gt; and &lt;code&gt;orange_and_true&lt;/code&gt; programs respectively &lt;i&gt;must&lt;/i&gt; resolve this conflict before these programs are deployed, and can use the above query to help them do so. To resolve the conflict, the teams have a few options. The simplest option is to remove the exclusive setting from one program, and acknowledge that it is simply not possible for these programs to be &lt;code&gt;exclusive&lt;/code&gt;. In that case, the order of the two programs matters (one must have higher priority). This is fine! Topaz allows developers to write certain programs that &lt;i&gt;absolutely cannot &lt;/i&gt;overlap with other programs (using &lt;code&gt;exclusive&lt;/code&gt;), but sometimes that is just not possible. And when it’s not, at least program priority is &lt;i&gt;explicit.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;i&gt;Note: in practice, we place all exclusive programs at the top of the program file. This makes it easier to reason about interactions between exclusive and non-exclusive programs.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;In short, verification is powerful not only because it catches bugs (e.g., satisfiability and reachability), but it also highlights the consequences of program changes. It helps operators understand the impact of their changes by providing immediate feedback. If two programs conflict, operators are forced to resolve it before deployment, rather than after an incident.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Bonus: verification-powered diffs. &lt;/b&gt;One of the newest features we’ve added to the verifier is one we call &lt;i&gt;semantic diffs&lt;/i&gt;. It’s in early stages, but the key insight is that operators often just want to &lt;i&gt;understand&lt;/i&gt; the impact of changes, even if these changes are deemed safe. To help operators, the verifier compares the old and new versions of the program file. Specifically, it looks for any query that matched program &lt;i&gt;X&lt;/i&gt; in the old version, but matches a different program &lt;i&gt;Y&lt;/i&gt; in the new version (or vice versa). For instance, if we changed &lt;code&gt;orange_and_true&lt;/code&gt; thus:&lt;/p&gt;\n &lt;pre class=\&quot;language-Lisp\&quot;&gt;&lt;code class=\&quot;language-Lisp\&quot;&gt;- name: orange_and_true\n config: …\n match: (and (= query_domain_tag1 &amp;quot;orange&amp;quot;) (not query_domain_tag2))\n response: …&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;Our verifier would emit:&lt;/p&gt;\n &lt;pre class=\&quot;language-Markdown\&quot;&gt;&lt;code class=\&quot;language-Markdown\&quot;&gt;Generating a report to help you understand your changes...\nNOTE: the queries below (if any) are just examples. Other such queries may exist.\n\n* program &amp;quot;orange_and_true&amp;quot; now MATCHES any query...\n to any domain...\n with tag1: &amp;quot;orange&amp;quot;\n with tag2: false&lt;/pre&gt;&lt;/code&gt;\n &lt;p&gt;While not exhaustive, this information helps operators understand whether their changes are doing what they intend or not, &lt;i&gt;before&lt;/i&gt; deployment. We look forward to expanding our verifier’s diff capabilities going forward.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;how-topazs-verifier-works-and-its-tradeoffs\&quot;&gt;How Topaz’s verifier works, and its tradeoffs&lt;/h2&gt;\n &lt;a href=\&quot;#how-topazs-verifier-works-and-its-tradeoffs\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;How does the verifier work? At a high-level, the verifier checks that, for all possible DNS queries, the three properties outlined above are satisfied. A Satisfiability Modulo Theories (SMT) solver — which we explain below — makes this seemingly impossible operation feasible. (It doesn&amp;#39;t literally loop over all DNS queries, but it is equivalent to doing so — it provides exhaustive proof.)&lt;/p&gt;&lt;p&gt;We implemented our formal verifier in &lt;a href=\&quot;https://emina.github.io/rosette/\&quot;&gt;&lt;u&gt;Rosette&lt;/u&gt;&lt;/a&gt;, a solver-enhanced domain-specific language written in the &lt;a href=\&quot;https://racket-lang.org/\&quot;&gt;&lt;u&gt;Racket&lt;/u&gt;&lt;/a&gt; programming language. Rosette makes writing a verifier more of an engineering exercise, rather than a formal logic test: if you can express the interpreter for your language in Racket/Rosette, you get verification “for free”, in some sense. We wrote a topaz-lang interpreter in Racket, then crafted our three properties using the Rosette DSL.&lt;/p&gt;&lt;p&gt;How does Rosette work? Rosette translates our desired properties into formulae in &lt;a href=\&quot;https://en.wikipedia.org/wiki/First-order_logic\&quot;&gt;&lt;u&gt;first-order logic&lt;/u&gt;&lt;/a&gt;. At a high level, these formulae are like equations from algebra class in school, with “unknowns” or variables. For instance, when checking whether the orange program is reachable (with the &lt;code&gt;orange_and_true&lt;/code&gt; program ordered before it), Rosette produces the formula &lt;code&gt;((NOT orange_and_true.match) AND orange.match)&lt;/code&gt;. The “unknowns” here are the DNS query parameters that these match functions operate over, e.g., &lt;code&gt;query_domain_tag1&lt;/code&gt;. To solve this formula, Rosette interfaces with an &lt;a href=\&quot;https://en.wikipedia.org/wiki/Satisfiability_modulo_theories\&quot;&gt;&lt;u&gt;SMT solver&lt;/u&gt;&lt;/a&gt; (like &lt;a href=\&quot;https://github.com/Z3Prover/z3\&quot;&gt;&lt;u&gt;Z3&lt;/u&gt;&lt;/a&gt;), which is specifically designed to solve these types of formulae by efficiently finding values to assign to the DNS query parameters that make the formulae true. Once the SMT solver finds satisfying values, Rosette translates them into a Racket data structure: in our case, a sample DNS query. In this example, once it finds a satisfying DNS query, it would report that the &lt;code&gt;orange&lt;/code&gt; program is indeed reachable.&lt;/p&gt;&lt;p&gt;However, verification is not free. The primary cost is maintenance. The model checker’s interpreter (Racket) must be kept in lockstep with the main interpreter (Go). If they fall out-of-sync, the verifier loses the ability to accurately detect bugs. Furthermore, functions added to topaz-lang must be compatible with formal verification.&lt;/p&gt;&lt;p&gt;Also, not all functions are easily verifiable, which means we must restrict the kinds of functions that program authors can write. Rosette can only verify functions that operate over integers and bit-vectors. This means we only permit functions whose operations can be converted into operations over integers and bit-vectors. While this seems restrictive, it actually gets us pretty far. The main challenge is strings: Topaz does not support programs that, for example, manipulate or work with substrings of the queried domain name. However, it does support simple operations on closed-set strings. For instance, it supports checking if two domain names are equal, because we can convert all strings to a small set of values representable using integers (which are easily verifiable).&lt;/p&gt;&lt;p&gt;Fortunately, thanks to our design of Topaz programs, the verifier need not be compatible with all Topaz program code. The verifier only ever examines Topaz &lt;i&gt;match&lt;/i&gt; functions, so only the functions specified in match functions need to be verification-compatible. We encountered other challenges when working to make our model accurate, like modeling randomness — if you are interested in the details, we encourage you to read the &lt;a href=\&quot;https://research.cloudflare.com/publications/Larisch2024/\&quot;&gt;&lt;u&gt;paper&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Another potential cost is verification speed. We find that the verifier can ensure our existing seven programs satisfy all three properties within about six seconds, which is acceptable because verification happens only at build time. We verify programs centrally, before programs are deployed, and only when programs change. &lt;/p&gt;&lt;p&gt;We also ran microbenchmarks to determine how fast the verifier can check more programs — we found that, for instance, it would take the verifier about 300 seconds to verify 50 programs. While 300 seconds is still acceptable, we are looking into verifier optimizations that will reduce the time further.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;bringing-formal-verification-from-research-to-production\&quot;&gt;Bringing formal verification from research to production&lt;/h2&gt;\n &lt;a href=\&quot;#bringing-formal-verification-from-research-to-production\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Topaz’s verifier began as a &lt;a href=\&quot;https://research.cloudflare.com/\&quot;&gt;&lt;u&gt;research&lt;/u&gt;&lt;/a&gt; project, and has since been deployed to production. It formally verifies all changes made to the authoritative DNS behavior specified in Topaz.&lt;/p&gt;&lt;p&gt;For more in-depth information on Topaz, see both our research &lt;a href=\&quot;https://research.cloudflare.com/publications/Larisch2024/\&quot;&gt;&lt;u&gt;paper&lt;/u&gt;&lt;/a&gt; published at SIGCOMM 2024 and the &lt;a href=\&quot;https://www.youtube.com/watch?v=hW7RjXVx7_Q\&quot;&gt;&lt;u&gt;recording&lt;/u&gt;&lt;/a&gt; of the talk.&lt;/p&gt;&lt;p&gt;We thank our former intern, Tim Alberdingk-Thijm, for his invaluable work on Topaz’s verifier.&lt;/p&gt;&quot;],&quot;published_at&quot;:[0,&quot;2024-11-08T14:00+00:00&quot;],&quot;updated_at&quot;:[0,&quot;2024-11-08T17:51:35.682Z&quot;],&quot;feature_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7jvYSp04T46jqP9h9zOSyE/8bb1594ce04a8aaed1cb35331a59c203/image2.png&quot;],&quot;tags&quot;:[1,[[0,{&quot;id&quot;:[0,&quot;5fZHv2k9HnJ7phOPmYexHw&quot;],&quot;name&quot;:[0,&quot;DNS&quot;],&quot;slug&quot;:[0,&quot;dns&quot;]}],[0,{&quot;id&quot;:[0,&quot;1x7tpPmKIUCt19EDgM1Tsl&quot;],&quot;name&quot;:[0,&quot;Research&quot;],&quot;slug&quot;:[0,&quot;research&quot;]}],[0,{&quot;id&quot;:[0,&quot;4G97lXs2uurehtdhYVj4r6&quot;],&quot;name&quot;:[0,&quot;Addressing&quot;],&quot;slug&quot;:[0,&quot;addressing&quot;]}],[0,{&quot;id&quot;:[0,&quot;35dMVPLpVqHtZrNY9drkyz&quot;],&quot;name&quot;:[0,&quot;Formal Methods&quot;],&quot;slug&quot;:[0,&quot;formal-methods&quot;]}]]],&quot;relatedTags&quot;:[0],&quot;authors&quot;:[1,[[0,{&quot;name&quot;:[0,&quot;James Larisch&quot;],&quot;slug&quot;:[0,&quot;james-larisch&quot;],&quot;bio&quot;:[0],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1CYzjboSN2jX2JkZpzkAN0/837e9a57e77efd26c440e2b216323f0f/unnamed.jpg&quot;],&quot;location&quot;:[0],&quot;website&quot;:[0],&quot;twitter&quot;:[0,&quot;@jameslarisch&quot;],&quot;facebook&quot;:[0]}],[0,{&quot;name&quot;:[0,&quot;Suleman Ahmad&quot;],&quot;slug&quot;:[0,&quot;suleman&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1WnbvI0pMk4oVLXHdvpIqH/51084688ace222ec1c1aafbb3bf0fc8b/suleman.png&quot;],&quot;location&quot;:[0,&quot;Austin, TX&quot;],&quot;website&quot;:[0,null],&quot;twitter&quot;:[0,&quot;@sulemanahmadd&quot;],&quot;facebook&quot;:[0,null]}],[0,{&quot;name&quot;:[0,&quot;Marwan Fayed&quot;],&quot;slug&quot;:[0,&quot;marwan&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/IXphrXdPbBBtMHdRMzIrB/340102a31143f4743a74c87ec0e0c11e/marwan.jpg&quot;],&quot;location&quot;:[0,null],&quot;website&quot;:[0,null],&quot;twitter&quot;:[0,&quot;@marwanfayed&quot;],&quot;facebook&quot;:[0,null]}]]],&quot;meta_description&quot;:[0,&quot;We describe how Cloudflare uses a custom Lisp-like programming language and formal verifier (written in Racket and Rosette) to prevent logical contradictions in our authoritative DNS nameserver’s behavior.&quot;],&quot;primary_author&quot;:[0,{}],&quot;localeList&quot;:[0,{&quot;name&quot;:[0,&quot;blog-english-only&quot;],&quot;enUS&quot;:[0,&quot;English for Locale&quot;],&quot;zhCN&quot;:[0,&quot;No Page for Locale&quot;],&quot;zhHansCN&quot;:[0,&quot;No Page for Locale&quot;],&quot;zhTW&quot;:[0,&quot;No Page for Locale&quot;],&quot;frFR&quot;:[0,&quot;No Page for Locale&quot;],&quot;deDE&quot;:[0,&quot;No Page for Locale&quot;],&quot;itIT&quot;:[0,&quot;No Page for Locale&quot;],&quot;jaJP&quot;:[0,&quot;No Page for Locale&quot;],&quot;koKR&quot;:[0,&quot;No Page for Locale&quot;],&quot;ptBR&quot;:[0,&quot;No Page for Locale&quot;],&quot;esLA&quot;:[0,&quot;No Page for Locale&quot;],&quot;esES&quot;:[0,&quot;No Page for Locale&quot;],&quot;enAU&quot;:[0,&quot;No Page for Locale&quot;],&quot;enCA&quot;:[0,&quot;No Page for Locale&quot;],&quot;enIN&quot;:[0,&quot;No Page for Locale&quot;],&quot;enGB&quot;:[0,&quot;No Page for Locale&quot;],&quot;idID&quot;:[0,&quot;No Page for Locale&quot;],&quot;ruRU&quot;:[0,&quot;No Page for Locale&quot;],&quot;svSE&quot;:[0,&quot;No Page for Locale&quot;],&quot;viVN&quot;:[0,&quot;No Page for Locale&quot;],&quot;plPL&quot;:[0,&quot;No Page for Locale&quot;],&quot;arAR&quot;:[0,&quot;No Page for Locale&quot;],&quot;nlNL&quot;:[0,&quot;No Page for Locale&quot;],&quot;thTH&quot;:[0,&quot;No Page for Locale&quot;],&quot;trTR&quot;:[0,&quot;No Page for Locale&quot;],&quot;heIL&quot;:[0,&quot;No Page for Locale&quot;],&quot;lvLV&quot;:[0,&quot;No Page for Locale&quot;],&quot;etEE&quot;:[0,&quot;No Page for Locale&quot;],&quot;ltLT&quot;:[0,&quot;No Page for Locale&quot;]}],&quot;url&quot;:[0,&quot;https://blog.cloudflare.com/topaz-policy-engine-design&quot;],&quot;metadata&quot;:[0,{&quot;title&quot;:[0,&quot;How we prevent conflicts in authoritative DNS configuration using formal verification&quot;],&quot;description&quot;:[0,&quot;We describe how Cloudflare uses a custom Lisp-like programming language and formal verifier (written in Racket and Rosette) to prevent logical contradictions in our authoritative DNS nameserver’s behavior.&quot;],&quot;imgPreview&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7kguebziUGENWVEKYDB09P/cbd2d1758e28879bbd52b5d46fffe905/How_we_prevent_conflicts_in_authoritative_DNS_configuration_using_formal_verification-OG.png&quot;]}]}],[0,{&quot;id&quot;:[0,&quot;3mOPXbiTgeQHBChx4vUuMs&quot;],&quot;title&quot;:[0,&quot;A look at the latest post-quantum signature standardization candidates&quot;],&quot;slug&quot;:[0,&quot;another-look-at-pq-signatures&quot;],&quot;excerpt&quot;:[0,&quot;NIST has standardized four post-quantum signature schemes so far, and they’re not done yet: there are fourteen new candidates in the running for standardization.&quot;],&quot;featured&quot;:[0,false],&quot;html&quot;:[0,&quot;&lt;p&gt;On October 24, 2024, the National Institute of Standards and Technology (NIST) &lt;a href=\&quot;https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/khAfIZPktRE/m/bBZWmET-AAAJ\&quot;&gt;&lt;u&gt;announced&lt;/u&gt;&lt;/a&gt; that they’re advancing fourteen post-quantum signature schemes to the second round of the “&lt;a href=\&quot;https://csrc.nist.gov/projects/pqc-dig-sig\&quot;&gt;&lt;u&gt;signatures on ramp&lt;/u&gt;&lt;/a&gt;” competition. “Post-quantum” means that these algorithms are designed to resist &lt;a href=\&quot;https://blog.cloudflare.com/the-quantum-menace/\&quot;&gt;&lt;u&gt;the attack of quantum computers&lt;/u&gt;&lt;/a&gt;. NIST already standardized four post-quantum signature schemes (&lt;a href=\&quot;https://blog.cloudflare.com/nists-first-post-quantum-standards/\&quot;&gt;&lt;u&gt;ML-DSA, SLH-DSA&lt;/u&gt;&lt;/a&gt;, &lt;a href=\&quot;https://csrc.nist.gov/News/2020/stateful-hash-based-signature-schemes-sp-800-208\&quot;&gt;&lt;u&gt;XMSS, and LMS&lt;/u&gt;&lt;/a&gt;) and they are drafting a standard for a fifth (&lt;a href=\&quot;https://falcon-sign.info/\&quot;&gt;&lt;u&gt;Falcon&lt;/u&gt;&lt;/a&gt;). Why do we need even more, you might ask? We’ll get to that.&lt;/p&gt;&lt;p&gt;A regular reader of the blog will know that this is not the first time we’ve taken measure of post-quantum signatures. In &lt;a href=\&quot;https://blog.cloudflare.com/sizing-up-post-quantum-signatures/\&quot;&gt;&lt;u&gt;2021&lt;/u&gt;&lt;/a&gt; we took a first hard look, and reported on the performance impact we expect from large-scale measurements.  Since then, dozens of new post-quantum algorithms have been proposed. Many of them have been submitted to this new NIST competition. We discussed some of the more promising ones in our &lt;a href=\&quot;https://blog.cloudflare.com/pq-2024/\&quot;&gt;&lt;u&gt;early 2024 blog post&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;In this blog post, we will go over the fourteen schemes advanced to the second round of the on ramp and discuss their feasibility for use in TLS — the protocol that secures browsing the Internet. The defining feature of practically all of them, is that they require much more bytes on the wire. Back in 2021 we shared &lt;a href=\&quot;https://blog.cloudflare.com/sizing-up-post-quantum-signatures/\&quot;&gt;&lt;u&gt;experimental results&lt;/u&gt;&lt;/a&gt; on the impact of these extra bytes. Today, we will share some surprising statistics on how TLS is used in practice. One is that today already almost half the data sent over more than half the QUIC connections are just for the certificates.&lt;/p&gt;&lt;p&gt;For a broader context and introduction to the post-quantum migration, check out our &lt;a href=\&quot;https://blog.cloudflare.com/pq-2024\&quot;&gt;&lt;u&gt;early 2024 blog post&lt;/u&gt;&lt;/a&gt;. One take-away to mention here: there will be two migrations for TLS. First, we urgently need to migrate key agreement to post-quantum cryptography to protect against &lt;a href=\&quot;https://en.wikipedia.org/wiki/Harvest_now,_decrypt_later\&quot;&gt;&lt;u&gt;attackers that store encrypted communication today&lt;/u&gt;&lt;/a&gt; in order to decrypt it in the future when a quantum computer is available. The industry is making good progress here: &lt;a href=\&quot;https://radar.cloudflare.com/adoption-and-usage#post-quantum-encryption-adoption\&quot;&gt;&lt;u&gt;18% of human requests&lt;/u&gt;&lt;/a&gt; to websites using Cloudflare are &lt;a href=\&quot;https://blog.cloudflare.com/post-quantum-for-all/\&quot;&gt;&lt;u&gt;secured&lt;/u&gt;&lt;/a&gt; using post-quantum key agreement. The second migration, to post-quantum signatures (certificates), is not as urgent: we will need to have this sorted by the time the quantum computer arrives. However, it will be a bigger challenge.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;the-signatures-in-tls\&quot;&gt;The signatures in TLS&lt;/h2&gt;\n &lt;a href=\&quot;#the-signatures-in-tls\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Before we have a look at the long list of post-quantum signature algorithms and their performance characteristics, let’s go through the signatures involved when browsing the Internet and their particular constraints.&lt;/p&gt;\n &lt;figure class=\&quot;kg-card kg-image-card\&quot;&gt;\n &lt;Image src=\&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/415VcZzABkhZjT60GRkQZM/f30ae24bd14e86534efd3e74d15eb5b5/image3.png\&quot; alt=\&quot;\&quot; class=\&quot;kg-image\&quot; width=\&quot;1164\&quot; height=\&quot;1268\&quot; loading=\&quot;lazy\&quot;/&gt;\n &lt;/figure&gt;&lt;p&gt;When you visit a website, the browser establishes a TLS connection with the server for that website. The connection starts with a cryptographic handshake. During this handshake, to authenticate the connection, the server signs the transcript so far, and presents the browser with a TLS &lt;i&gt;leaf&lt;/i&gt; certificate to prove that it’s allowed to serve the website. This &lt;i&gt;leaf &lt;/i&gt;certificate is signed by a certification authority (CA). Typically, it’s not signed by the CA’s &lt;i&gt;root&lt;/i&gt; certificate, but by an &lt;i&gt;intermediate&lt;/i&gt; CA certificate, which in turn is signed by the root CA, or another intermediate. That’s not all: a leaf certificate has to include at least two &lt;i&gt;signed certificate timestamps&lt;/i&gt; (SCTs). These SCTs are signatures created by &lt;a href=\&quot;https://blog.cloudflare.com/introducing-certificate-transparency-and-nimbus/\&quot;&gt;&lt;u&gt;certificate transparency (CT) logs&lt;/u&gt;&lt;/a&gt; to attest they’ve been publicly logged. &lt;a href=\&quot;https://certificate.transparency.dev/howctworks/\&quot;&gt;&lt;u&gt;Certificate Transparency&lt;/u&gt;&lt;/a&gt; is what enables you to look up a certificate on websites such &lt;a href=\&quot;http://crt.sh\&quot;&gt;&lt;u&gt;crt.sh&lt;/u&gt;&lt;/a&gt; and &lt;a href=\&quot;https://www.merklemap.com/\&quot;&gt;&lt;u&gt;merklemap&lt;/u&gt;&lt;/a&gt;. In the future three or more SCTs might be required. Finally, servers may also send an &lt;a href=\&quot;https://blog.cloudflare.com/high-reliability-ocsp-stapling/\&quot;&gt;&lt;u&gt;OCSP staple&lt;/u&gt;&lt;/a&gt; to demonstrate a certificate hasn’t been revoked.&lt;/p&gt;&lt;p&gt;Thus, we’re looking at a minimum of five signatures (not counting the OCSP staple) and two public keys transmitted across the network to establish a new TLS connection.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;tailoring\&quot;&gt;Tailoring&lt;/h3&gt;\n &lt;a href=\&quot;#tailoring\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Only the handshake transcript signature is created &lt;i&gt;online&lt;/i&gt;; the other signatures are “offline”. That is, they are created ahead of time. For these offline signatures, fast verification is much more important than fast signing. On the other hand, for the handshake signature, we want to minimize the sum of signing and verification time.&lt;/p&gt;&lt;p&gt;Only the public keys of the leaf and intermediate certificates are transmitted on the wire during the handshake, and for those we want to minimize the combined size of the signature and the public key. For the other signatures, the public key is not transmitted during the handshake, and thus a scheme with larger public keys would be tolerable, and preferable if it trades larger public keys for smaller signatures.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;the-algorithms\&quot;&gt;The algorithms&lt;/h2&gt;\n &lt;a href=\&quot;#the-algorithms\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Now that we’re up to speed, let’s have a look at the candidates that progressed (marked by 🤔 below), compared to the classical algorithms vulnerable to quantum attack (marked by ❌), and the post-quantum algorithms that are already standardized (✅) or soon will be (📝). Each submission proposes several variants. We list the most relevant variants to TLS from each submission. To explore all variants, check out &lt;a href=\&quot;https://research.cloudflare.com/outreach/academic-programs/interns/thom-wiggers/\&quot;&gt;&lt;u&gt;Thom Wigger&lt;/u&gt;&lt;/a&gt;’s &lt;a href=\&quot;https://pqshield.github.io/nist-sigs-zoo/\&quot;&gt;&lt;u&gt;signatures zoo&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&lt;style type=\&quot;text/css\&quot;&gt;\n.tg {border-collapse:collapse;border-spacing:0;margin:0px auto;}\n.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;\n overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;\n font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}\n.tg .tg-d0am{background-color:#C0FF00;border-color:inherit;text-align:left;vertical-align:top}\n.tg .tg-mrls{background-color:#FFE400;border-color:inherit;text-align:left;vertical-align:top}\n.tg .tg-jxgv{background-color:#FFF;border-color:inherit;text-align:left;vertical-align:top}\n.tg .tg-0pky{border-color:inherit;text-align:left;vertical-align:top}\n.tg .tg-ezr1{background-color:#0F0;border-color:inherit;text-align:left;vertical-align:top}\n.tg .tg-5ece{border-color:inherit;font-size:small;text-align:left;vertical-align:top}\n.tg .tg-hqhz{background-color:#FF0;border-color:inherit;text-align:left;vertical-align:top}\n.tg .tg-boa2{background-color:#F90;border-color:inherit;color:#FFF;text-align:left;vertical-align:top}\n.tg .tg-4zxv{background-color:#F00;border-color:inherit;color:#FFF;text-align:left;vertical-align:top}\n@media screen and (max-width: 767px) {.tg {width: auto !important;}.tg col {width: auto !important;}.tg-wrap {overflow-x: auto;-webkit-overflow-scrolling: touch;margin: auto 0px;}}&lt;/style&gt;\n&lt;div class=\&quot;tg-wrap\&quot;&gt;&lt;table class=\&quot;tg\&quot;&gt;&lt;thead&gt;\n &lt;tr&gt;\n &lt;th class=\&quot;tg-0pky\&quot;&gt;&lt;/th&gt;\n &lt;th class=\&quot;tg-0pky\&quot;&gt;&lt;/th&gt;\n &lt;th class=\&quot;tg-0pky\&quot;&gt;&lt;/th&gt;\n &lt;th class=\&quot;tg-0pky\&quot; colspan=\&quot;2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Sizes (bytes)&lt;/span&gt;&lt;/th&gt;\n &lt;th class=\&quot;tg-0pky\&quot; colspan=\&quot;2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;CPU time (lower is better)&lt;/span&gt;&lt;/th&gt;\n &lt;/tr&gt;&lt;/thead&gt;\n&lt;tbody&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Family&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Name variant&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Public key&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Signature&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Signing&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Verification&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Elliptic curves&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Ed25519&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;❌&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;32&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;64&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.15&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1.3&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Factoring&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-5ece\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;RSA&lt;small&gt; 2048&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;❌&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;256&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;256&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;80&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.4&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Lattices&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;ML-DSA &lt;small&gt;44&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;✅&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-mrls\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1,312&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-boa2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;2,420&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1 (baseline)&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1 (baseline)&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot; rowspan=\&quot;3\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Symmetric&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;SLH-DSA &lt;small&gt;128s&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;✅&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;32&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;7,856&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;14,000&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;40&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;SLH-DSA &lt;small&gt;128f&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;✅&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;32&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;17,088&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-boa2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;720&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-boa2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;110&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;LMS &lt;small&gt;M4_H20_W8&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;✅&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;48&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-mrls\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1,112&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-d0am\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;2.9&lt;/span&gt; ⚠️&lt;/td&gt;\n &lt;td class=\&quot;tg-d0am\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;8.4&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Lattices&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Falcon &lt;small&gt;512&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;📝&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;897&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;666&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-d0am\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;3 ⚠️&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.7&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot; rowspan=\&quot;2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Codebased&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;CROSS &lt;small&gt;R-SDP(G)1 small&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;38&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;7,956&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;20&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;35&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;LESS &lt;small&gt;1s&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;97,484&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;5,120&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-boa2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;620&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;1800&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot; rowspan=\&quot;5\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;MPC in the head&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Mirath &lt;small&gt;Mirith Ia fast&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;129&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;7,877&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;25&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;60&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;MQOM &lt;small&gt;L1-gf251-fast&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;59&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;7,850&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;35&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-boa2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;85&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;PERK &lt;small&gt;I-fast5&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;240&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;8,030&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;20&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;40&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;RYDE &lt;small&gt;128F&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;86&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;7,446&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;15&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;40&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;SDitH &lt;small&gt;gf251-L1-hyp&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;132&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;8,496&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;30&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-boa2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;80&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;VOLE in the head&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;FAEST &lt;small&gt;EM-128f&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;32&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;5,696&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-d0am\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;6&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-d0am\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;18&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Lattices&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;HAWK &lt;small&gt;512&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1,024&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;555&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.25&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1.2&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Isogeny&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;SQISign &lt;small&gt;I&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;64&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;177&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;17,000&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;900&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-jxgv\&quot; rowspan=\&quot;8\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;Multivariate&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;MAYO &lt;small&gt;one&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1,168&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;321&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1.4&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1.4&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;MAYO &lt;small&gt;two&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;5,488&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;180&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1.7&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.8&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;QR-UOV &lt;small&gt;I-(31,165,60,3)&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;23,657&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;157&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;75&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-boa2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;125&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;SNOVA &lt;small&gt;(24,5,4)&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-hqhz\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1,016&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;248&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.9&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1.4&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;SNOVA &lt;small&gt;(25,8,3)&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-boa2\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;2,320&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;165&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.9&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1.8&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;SNOVA &lt;small&gt;(37,17,2)&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;9,842&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;106&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;1.2&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;UOV &lt;small&gt;Is-pkc&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;66,576&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;96&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.3&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;2.3&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n &lt;tr&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;UOV &lt;small&gt;Ip-pkc&lt;/small&gt;&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-0pky\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;🤔&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-4zxv\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#FFF;background-color:transparent\&quot;&gt;43,576&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;128&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.3&lt;/span&gt;&lt;/td&gt;\n &lt;td class=\&quot;tg-ezr1\&quot;&gt;&lt;span style=\&quot;font-weight:400;font-style:normal;text-decoration:none;color:#000;background-color:transparent\&quot;&gt;0.8&lt;/span&gt;&lt;/td&gt;\n &lt;/tr&gt;\n&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Some notes about the table. It compares selected variants of the submissions progressed to the second round of the NIST PQC signature on ramp with earlier existing traditional and post-quantum schemes at the security level of AES-128. CPU times are taken from the &lt;a href=\&quot;https://pqshield.github.io/nist-sigs-zoo/\&quot;&gt;&lt;u&gt;signatures zoo&lt;/u&gt;&lt;/a&gt;, which collected them from the submission documents and some later advances. CPU performance varies significantly by platform and implementation, and should only be taken as a rough indication. We are early in the competition, and the on-ramp schemes will evolve: some will improve drastically (both in compute and size), whereas others will regress to counter new attacks. Check out &lt;a href=\&quot;https://pqshield.github.io/nist-sigs-zoo/\&quot;&gt;&lt;u&gt;the zoo&lt;/u&gt;&lt;/a&gt; for the latest numbers. We marked Falcon signing with a &lt;i&gt;⚠️&lt;/i&gt;, as Falcon signing is hard to implement in a fast and timing side-channel secure manner. LMS signing has a ⚠️, as secure LMS signing requires keeping a state and the listed signing time assumes a 32MB cache. This will be discussed later on.&lt;/p&gt;&lt;p&gt;These are a lot of algorithms, and we didn’t even list all variants. One thing is clear: none of them perform as well as classical elliptic curve signatures across the board. Let’s start with NIST’s 2022 picks.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;ml-dsa-slh-dsa-and-falcon\&quot;&gt;ML-DSA, SLH-DSA, and Falcon&lt;/h3&gt;\n &lt;a href=\&quot;#ml-dsa-slh-dsa-and-falcon\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;The most viable general purpose post-quantum signature scheme standardized today is the lattice-based &lt;b&gt;ML-DSA&lt;/b&gt; (&lt;a href=\&quot;https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.204.pdf\&quot;&gt;&lt;u&gt;FIPS 204&lt;/u&gt;&lt;/a&gt;), which started its life as &lt;a href=\&quot;https://pq-crystals.org/dilithium/index.shtml\&quot;&gt;&lt;u&gt;Dilithium&lt;/u&gt;&lt;/a&gt;. It’s light on the CPU and reasonably straightforward to implement. The big downside is that its signatures and public keys are large: 2.4kB and 1.3kB respectively. Here and for the balance of the blog post, we will only consider the variants at the AES-128 security level unless stated otherwise. Adding ML-DSA, adds 14.7kB to the TLS handshake (two 1312-byte public keys plus five 2420-byte signatures).&lt;/p&gt;&lt;p&gt;&lt;b&gt;SLH-DSA&lt;/b&gt; (&lt;a href=\&quot;https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.205.pdf\&quot;&gt;&lt;u&gt;FIPS 205&lt;/u&gt;&lt;/a&gt;, née &lt;a href=\&quot;https://sphincs.org/\&quot;&gt;&lt;u&gt;SPHINCS&lt;/u&gt;&lt;u&gt;&lt;sup&gt;+&lt;/sup&gt;&lt;/u&gt;&lt;/a&gt;) looks strictly worse, adding 39kB and significant computational overhead for both signing and verification. The advantage of SLH-DSA, being solely based on hashes, is that its security is much better understood than ML-DSA. The lowest security level of SLH-DSA is generally more trusted than the highest security levels of many other schemes.&lt;/p&gt;&lt;p&gt;&lt;a href=\&quot;https://falcon-sign.info/\&quot;&gt;&lt;b&gt;&lt;u&gt;Falcon&lt;/u&gt;&lt;/b&gt;&lt;/a&gt; (to be renamed &lt;a href=\&quot;https://www.nist.gov/news-events/news/2024/08/nist-releases-first-3-finalized-post-quantum-encryption-standards\&quot;&gt;&lt;u&gt;FN-DSA&lt;/u&gt;&lt;/a&gt;) seems much better than SLH-DSA and ML-DSA if you look only at the numbers in the table. There is a catch though. For fast signing, Falcon requires fast floating-point arithmetic, which turns out to be &lt;a href=\&quot;https://blog.cloudflare.com/nist-post-quantum-surprise/#digital-signatures\&quot;&gt;&lt;u&gt;difficult to implement securely&lt;/u&gt;&lt;/a&gt;. Signing can be performed securely with emulated floating-point arithmetic, but that makes it roughly twenty times slower. This makes Falcon ill-suited for online signatures. Furthermore, the signing procedure of Falcon is complicated to implement. On the other hand, Falcon verification is simple and doesn’t require floating-point arithmetic.&lt;/p&gt;&lt;p&gt;Leaning into Falcon’s strength, by using ML-DSA for the handshake signature, and Falcon for the rest, we’re only adding 7.3kB (at security level of AES-128).&lt;/p&gt;&lt;p&gt;There is one more difficulty with Falcon worth mentioning: it’s missing a middle security level. That means that if Falcon-512 (which we considered so far) turns out to be weaker than expected, then the next one up is Falcon-1024, which has double signature and public key size. That amounts to adding about 11kB.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;stateful-hash-based-signatures\&quot;&gt;Stateful hash-based signatures&lt;/h3&gt;\n &lt;a href=\&quot;#stateful-hash-based-signatures\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;The very first post-quantum signature algorithms standardized are the stateful hash-based &lt;a href=\&quot;https://datatracker.ietf.org/doc/html/rfc8391\&quot;&gt;&lt;u&gt;XMSS&lt;/u&gt;&lt;u&gt;&lt;sup&gt;(MT)&lt;/sup&gt;&lt;/u&gt;&lt;/a&gt; and &lt;a href=\&quot;https://datatracker.ietf.org/doc/html/rfc8554#page-45\&quot;&gt;&lt;u&gt;LMS/HSS&lt;/u&gt;&lt;/a&gt;. These are hash-based signatures, similar to SLH-DSA, and so we have a lot of trust in their security. They come with a big drawback: when creating a keypair you prepare a finite number of &lt;i&gt;signature slots&lt;/i&gt;. For the variant listed in the table, there are about one million slots. Each slot can only be used once. If by accident a slot is used twice, then anyone can (&lt;a href=\&quot;https://eprint.iacr.org/2016/1042\&quot;&gt;&lt;u&gt;probably&lt;/u&gt;&lt;/a&gt;) use those two signatures to forge any new signature from that slot and break into the connection the certificate is supposed to protect. Remembering which slots have been used, is the &lt;i&gt;state&lt;/i&gt; in &lt;i&gt;stateful&lt;/i&gt; hash-based signature. Certificate authorities might be able to keep the state, but for general use, Adam Langley calls keeping the state a &lt;a href=\&quot;https://www.imperialviolet.org/2013/07/18/hashsig.html\&quot;&gt;&lt;u&gt;huge foot-cannon&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;There are more quirks to keep in mind for stateful hash-based signatures. To start, during key generation, each slot needs to be prepared. Preparing each slot takes approximately the same amount of time as verifying a signature. Preparing all million takes a couple of hours on a single core. For intermediate certificates of a popular certificate authority, a million slots are not enough. Indeed, Let’s Encrypt issues more than &lt;a href=\&quot;https://letsencrypt.org/stats/\&quot;&gt;&lt;u&gt;four million certificates per day&lt;/u&gt;&lt;/a&gt;. Instead of increasing the number of slots directly, we can use an extra intermediate. This is what XMSS&lt;sup&gt;MT&lt;/sup&gt; and HSS do internally. A final quirk of stateful hash-based signatures is that their security is bottlenecked on non-repudiation: the listed LMS instance has 192 bits of security against forgery, but only 96 bits against the signer themselves creating a single signature that verifies two different messages.&lt;/p&gt;&lt;p&gt;Even when stateful hash-based signatures or Falcon can be used, we are still adding a lot of bytes on the wire. From &lt;a href=\&quot;https://blog.cloudflare.com/sizing-up-post-quantum-signatures/\&quot;&gt;&lt;u&gt;earlier experiments&lt;/u&gt;&lt;/a&gt; we know that that will impact performance significantly. We summarize those findings later in this blog post, and share some new data. The short of it: it would be nice to have a post-quantum signature scheme that outperforms Falcon, or at least outperforms ML-DSA and is easier to deploy. This is one of the reasons NIST is running the second competition.&lt;/p&gt;&lt;p&gt;With that in mind, let’s have a look at the candidates.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;structured-lattice-alternatives\&quot;&gt;Structured lattice alternatives&lt;/h3&gt;\n &lt;a href=\&quot;#structured-lattice-alternatives\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;With only performance in mind, it is surprising that half of the candidates do worse than ML-DSA. There is a good reason for it: NIST is worried that we’re putting all our eggs in the structured lattices basket. SLH-DSA is an alternative to lattices today, but it doesn’t perform well enough for many applications. As such, NIST &lt;a href=\&quot;https://csrc.nist.gov/csrc/media/Projects/pqc-dig-sig/documents/call-for-proposals-dig-sig-sept-2022.pdf\&quot;&gt;&lt;u&gt;would primarily like to standardize&lt;/u&gt;&lt;/a&gt; another general purpose signature algorithm that is not based on structured lattices, and that outperforms SLH-DSA. We will briefly touch upon these schemes here.&lt;/p&gt;&lt;h4&gt;Code-based&lt;/h4&gt;&lt;p&gt;&lt;a href=\&quot;https://www.cross-crypto.com/\&quot;&gt;&lt;u&gt;CROSS&lt;/u&gt;&lt;/a&gt; and &lt;a href=\&quot;https://www.less-project.com/#:~:text=LESS%20(Linear%20Equivalence%20Signature%20Scheme,the%20Linear%20Code%20Equivalence%20problem.\&quot;&gt;&lt;u&gt;LESS&lt;/u&gt;&lt;/a&gt; are two&lt;b&gt; code-based signature&lt;/b&gt; schemes. &lt;b&gt;CROSS&lt;/b&gt; is based on a variant of the traditional syndrome decoding problem. Its signatures are about as large as SLH-DSA, but its edge over SLH-DSA is the much better signing times. &lt;b&gt;LESS&lt;/b&gt; is based on the novel &lt;a href=\&quot;https://eprint.iacr.org/2023/847\&quot;&gt;&lt;u&gt;linear equivalence problem&lt;/u&gt;&lt;/a&gt;. It only outperforms SLH-DSA on signature size, requiring larger public keys in return. For use in TLS, the high verification times of LESS are especially problematic. Given that LESS is based on a new approach, it will be interesting to see how much it can improve going forward.&lt;/p&gt;&lt;h4&gt;Multi-party computation in the head&lt;/h4&gt;&lt;p&gt;Five of the submissions (&lt;a href=\&quot;https://pqc-mira.org/\&quot;&gt;&lt;u&gt;Mira&lt;/u&gt;&lt;/a&gt;&lt;a href=\&quot;https://pqc-mirith.org/\&quot;&gt;&lt;u&gt;th&lt;/u&gt;&lt;/a&gt;, &lt;a href=\&quot;https://mqom.org/\&quot;&gt;&lt;u&gt;MQOM&lt;/u&gt;&lt;/a&gt;, &lt;a href=\&quot;https://pqc-perk.org/\&quot;&gt;&lt;u&gt;PERK&lt;/u&gt;&lt;/a&gt;, &lt;a href=\&quot;https://pqc-ryde.org/\&quot;&gt;&lt;u&gt;RYDE&lt;/u&gt;&lt;/a&gt;, &lt;a href=\&quot;https://sdith.org/\&quot;&gt;&lt;u&gt;SDitH&lt;/u&gt;&lt;/a&gt;) use the &lt;b&gt;Multi-Party Computation in the Head&lt;/b&gt; (MPCitH) paradigm.&lt;/p&gt;&lt;p&gt;It has been exciting to see the developments in this field. To explain a bit about it, let’s go back to &lt;a href=\&quot;https://microsoft.github.io/Picnic/\&quot;&gt;&lt;u&gt;Picnic&lt;/u&gt;&lt;/a&gt;. Picnic was an MPCitH submission to the previous NIST PQC competition. In essence, its private key is a random key &lt;i&gt;x&lt;/i&gt;, and its public key is the hash &lt;i&gt;H(x)&lt;/i&gt;. A signature is a zero-knowledge proof demonstrating that the signer knows &lt;i&gt;x&lt;/i&gt;. So far, it’s pretty similar in shape to other signature schemes that use zero knowledge proofs. The difference is in how that proof is created. We have to talk about multi-party computation (MPC) first. MPC starts with splitting the key &lt;i&gt;x&lt;/i&gt; into shares, using &lt;a href=\&quot;https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing\&quot;&gt;&lt;u&gt;Shamir secret sharing&lt;/u&gt;&lt;/a&gt; for instance, and giving each party one share. No single party knows the value of &lt;i&gt;x&lt;/i&gt; itself, but they can recover it by recombining. The insight of MPC is that these parties (with some communication) can perform arbitrary computation on the data they shared. In particular, they can compute a secret share of &lt;i&gt;H(x)&lt;/i&gt;. Now, we can use that to make a zero-knowledge proof as follows. The signer simulates all parties in the multi-party protocol to compute and recombine &lt;i&gt;H(x)&lt;/i&gt;. The signer then reveals part of the intermediate values of the computation using &lt;a href=\&quot;https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic\&quot;&gt;&lt;u&gt;Fiat–Shamir&lt;/u&gt;&lt;/a&gt;: enough so that none of the parties could have cheated on any of the steps, but not enough that it allows the verifier to figure out &lt;i&gt;x&lt;/i&gt; themselves.&lt;/p&gt;&lt;p&gt;For &lt;i&gt;H&lt;/i&gt;, Picnic uses &lt;a href=\&quot;https://lowmc.github.io/\&quot;&gt;&lt;u&gt;LowMC&lt;/u&gt;&lt;/a&gt;, a block cipher for which it’s easy to do the multi-party computation. The initial submission of Picnic performed poorly compared to SLH-DSA with 32kB signatures. For the second round, Picnic was improved considerably, boasting 12kB signatures. SLH-DSA won out with smaller signatures, and more conservative security assumptions: Picnic relies on LowMC which didn’t receive as much study as the hashes on which SLH-DSA is based.&lt;/p&gt;&lt;p&gt;Back to the MPCitH candidates that progressed. All of them have variants (listed in the table) with similar or better signature sizes as SLH-DSA, while outperforming SLH-DSA considerably in signing time. There are variants with even smaller signatures, but their verification performance is significantly higher. The difference between the MPCitH candidates is the underlying &lt;a href=\&quot;https://en.wikipedia.org/wiki/Trapdoor_function\&quot;&gt;&lt;u&gt;trapdoor&lt;/u&gt;&lt;/a&gt; they use. In Picnic the trapdoor was LowMC. For both RYDE and SDiTH, the trapdoors used are based on variants of &lt;a href=\&quot;https://en.wikipedia.org/wiki/Decoding_methods#Syndrome_decoding\&quot;&gt;&lt;u&gt;syndrome decoding&lt;/u&gt;&lt;/a&gt;, and could be classified as code-based cryptography.&lt;/p&gt;&lt;p&gt;Over the years, MPCitH schemes have seen remarkable improvements in performance, and we don’t seem to have reached the end of it yet. There is still some way to go before these schemes would be competitive in TLS: signature size needs to be reduced without sacrificing the currently borderline acceptable verification performance. On top of that, not all underlying trapdoors of the various schemes have seen enough scrutiny.&lt;/p&gt;&lt;h4&gt;FAEST&lt;/h4&gt;&lt;p&gt;&lt;a href=\&quot;https://faest.info/\&quot;&gt;&lt;u&gt;FAEST&lt;/u&gt;&lt;/a&gt; is a peek into the future. It’s similar to the MPCitH candidates in that its security reduces to an underlying trapdoor. It is quite different from those in that FAEST’s underlying trapdoor is AES. That means that, given the security analysis of FAEST is correct, it’s on the same footing as SLH-DSA. Despite the conservative trapdoor, FAEST beats the MPCitH candidates in performance. It also beats SLH-DSA on all metrics.&lt;/p&gt;&lt;p&gt;At the AES-128 security level, FAEST’s signatures are larger than ML-DSA. For those that want to hedge against improvements in lattice attacks, and would only consider higher security levels of ML-DSA, FAEST becomes an attractive alternative. ML-DSA-65 has a combined public key and signature size of 5.2kB, which is similar to FAEST EM-128f. ML-DSA-65 still has a slight edge in performance.&lt;/p&gt;&lt;p&gt;FAEST is based on the 2023 &lt;a href=\&quot;https://eprint.iacr.org/2023/996.pdf\&quot;&gt;&lt;u&gt;VOLE in the Head&lt;/u&gt;&lt;/a&gt; paradigm. These are new ideas, and it seems likely their full potential has not been realized yet. It is likely that FAEST will see improvements.&lt;/p&gt;&lt;p&gt;The VOLE in the Head techniques can and probably will be adopted by some of the MPCitH submissions. It will be interesting to see how far VOLEitH can be pushed when applied to less conservative trapdoors. Surpassing ML-DSA seems in reach, but Falcon? We will see.&lt;/p&gt;&lt;p&gt;Now, let’s move on to the submissions that surpass ML-DSA today.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;hawk\&quot;&gt;HAWK&lt;/h3&gt;\n &lt;a href=\&quot;#hawk\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;&lt;a href=\&quot;https://hawk-sign.info/\&quot;&gt;&lt;u&gt;HAWK&lt;/u&gt;&lt;/a&gt; is similar to Falcon, but improves upon it in a few key ways. Most importantly, it doesn’t rely on floating point arithmetic. Furthermore, its signing procedure is simpler and much faster. This makes HAWK suitable for online signatures. Using HAWK adds 4.8kB. Apart from size and speed, it’s beneficial to rely on only a single scheme: using multiple schemes increases the attack surface for algorithmic weaknesses and implementation mistakes.&lt;/p&gt;&lt;p&gt;Similar to Falcon, HAWK is missing a middle security level. Using HAWK-1024 doubles sizes (9.6kB).&lt;/p&gt;&lt;p&gt;There is one downside to HAWK over Falcon: HAWK relies on a new security assumption, the &lt;a href=\&quot;https://eprint.iacr.org/2021/1332.pdf\&quot;&gt;&lt;u&gt;lattice isomorphism problem&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;sqisign\&quot;&gt;SQISign&lt;/h3&gt;\n &lt;a href=\&quot;#sqisign\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;&lt;a href=\&quot;https://sqisign.org/\&quot;&gt;&lt;u&gt;SQISign&lt;/u&gt;&lt;/a&gt; is based on &lt;a href=\&quot;https://blog.cloudflare.com/sidh-go/\&quot;&gt;&lt;u&gt;isogenies&lt;/u&gt;&lt;/a&gt;. Famously, SIKE, another isogeny-based scheme in the previous competition, got &lt;a href=\&quot;https://eprint.iacr.org/2022/975.pdf\&quot;&gt;&lt;u&gt;broken badly&lt;/u&gt;&lt;/a&gt; late into the competition. SQISign is based on a different problem, though. SQISign is remarkable for having very small signatures and public keys: it even beats RSA-2048. The glaring downside is that it is computationally very expensive to compute and verify a signature. Isogeny-based signature schemes is a very active area of research with many advances over the years.&lt;/p&gt;&lt;p&gt;It seems unlikely that any future SQISign variant will sign fast enough for the TLS handshake signature. Furthermore, SQISign signing seems to be hard to implement in a timing side-channel secure manner. What about the other signatures of TLS? The bottleneck is verification time. It would be acceptable for SQISign to have larger signatures, if that allows it to have faster verification time.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;uov\&quot;&gt;UOV&lt;/h3&gt;\n &lt;a href=\&quot;#uov\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;&lt;a href=\&quot;https://www.uovsig.org/\&quot;&gt;&lt;u&gt;UOV&lt;/u&gt;&lt;/a&gt; (unbalanced oil and vinegar) is an old multivariate scheme with large public keys (67kB), but small signatures (96 bytes). Furthermore, it has excellent signing and verification performance. These interesting size tradeoffs make it quite suited for use cases where the public key is known in advance.&lt;/p&gt;&lt;p&gt;If we use UOV in TLS for the SCTs and root CA, whose public keys are not transmitted when setting up the connection, together with ML-DSA for the others, we’re looking at 7.2kB. That’s a clear improvement over using ML-DSA everywhere, and a tad better than combining ML-DSA with Falcon.&lt;/p&gt;&lt;p&gt;When combining UOV with HAWK instead of ML-DSA, we’re looking at adding only 3.4kB. That’s better again, but only a marginal improvement over using HAWK everywhere (4.8kB). The relative advantage of UOV improves if the certificate transparency ecosystem moves towards requiring more SCTs.&lt;/p&gt;&lt;p&gt;For SCTs, the size of UOV public keys seems acceptable, as there are not that many certificate transparency logs at the moment. Shipping a UOV public key for hundreds of root CAs is more painful, but within reason. Even with &lt;a href=\&quot;https://blog.cloudflare.com/pq-2024/#leaving-out-intermediate-certificates\&quot;&gt;&lt;u&gt;intermediate suppression&lt;/u&gt;&lt;/a&gt;, using UOV in each of the thousands of intermediate certificates does not make sense.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h3 id=\&quot;structured-multivariate\&quot;&gt;Structured multivariate&lt;/h3&gt;\n &lt;a href=\&quot;#structured-multivariate\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;Since the original UOV, over the decades, many attempts have been made to add additional structure UOV, to get a better balance between the size of the signature and public key. Unfortunately many of these &lt;i&gt;structured multivariate&lt;/i&gt; schemes, which include GeMMS and Rainbow, have been broken.&lt;/p&gt;&lt;p&gt;Let’s have a look at the multivariate candidates. The most interesting variant of &lt;b&gt;QR-UOV&lt;/b&gt; for TLS has 24kB public keys and 157 byte signatures. The current verification times are unacceptably high, but there seems to be plenty of room for an improved implementation. There is also a variant with a 12kB public key, but its verification time needs to come down even further. In any case, the combined size QR-UOV’s public key and signatures remain large enough that it’s not a competitor of ML-DSA or Falcon. Instead, QR-UOV competes with UOV, where UOV’s public keys are unwieldy. Although QR-UOV hasn’t seen a direct attack yet, a similar scheme has recently been &lt;a href=\&quot;https://link.springer.com/chapter/10.1007/978-3-031-62746-0_9\&quot;&gt;&lt;u&gt;weakened&lt;/u&gt;&lt;/a&gt; and another &lt;a href=\&quot;https://link.springer.com/chapter/10.1007/978-3-030-44223-1_18\&quot;&gt;&lt;u&gt;broken&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Finally, we get to&lt;b&gt; &lt;/b&gt;&lt;a href=\&quot;https://snova.pqclab.org/\&quot;&gt;&lt;b&gt;&lt;u&gt;SNOVA&lt;/u&gt;&lt;/b&gt;&lt;/a&gt; and &lt;a href=\&quot;https://pqmayo.org/\&quot;&gt;&lt;b&gt;&lt;u&gt;MAYO&lt;/u&gt;&lt;/b&gt;&lt;/a&gt;. Although they’re based on a different technique, they have a lot of properties in common. To start, they have the useful property that they allow for a granular tradeoff between public key and signature size. This allows us to use a different variant optimized for whether we’re transmitting the public in the connection or not. Using MAYO&lt;sub&gt;one&lt;/sub&gt; for the leaf and intermediate, and MAYO&lt;sub&gt;two&lt;/sub&gt; for the others, adds 3.5kB. Similarly with SNOVA, we add 2.8kB. On top of that, both schemes have excellent signing and verification performance.&lt;/p&gt;&lt;p&gt;The elephant in the room is the security. During the end of the first round, a new &lt;a href=\&quot;https://www.jstage.jst.go.jp/article/jsiaml/15/0/15_53/_article\&quot;&gt;&lt;u&gt;generic attack&lt;/u&gt;&lt;/a&gt; on underdefined multivariate systems prompted the MAYO team to &lt;a href=\&quot;https://groups.google.com/a/list.nist.gov/g/pqc-forum/c/jEKfDYUgdec/m/0UP_GNKSAwAJ\&quot;&gt;&lt;u&gt;tweak their parameters&lt;/u&gt;&lt;/a&gt; slightly. SNOVA has been hit a bit harder by three attacks (&lt;a href=\&quot;https://dl.acm.org/doi/10.1145/3659467.3659900\&quot;&gt;&lt;u&gt;1&lt;/u&gt;&lt;/a&gt;, &lt;a href=\&quot;https://eprint.iacr.org/2024/1297\&quot;&gt;&lt;u&gt;2&lt;/u&gt;&lt;/a&gt;, &lt;a href=\&quot;https://eprint.iacr.org/2024/1770.pdf\&quot;&gt;&lt;u&gt;3&lt;/u&gt;&lt;/a&gt;), but so far it seems that SNOVA’s parameters can be adjusted to compensate.&lt;/p&gt;&lt;p&gt;Ok, we had a look at all the candidates. What did we learn? There are some very promising algorithms that will reduce the number of bytes required on the wire compared to ML-DSA and Falcon. None of the practical ones will prevent us from adding any extra bytes to TLS. So, given that we must add some bytes: how many extra bytes are too many?&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;how-many-added-bytes-are-too-many-for-tls\&quot;&gt;How many added bytes are too many for TLS?&lt;/h2&gt;\n &lt;a href=\&quot;#how-many-added-bytes-are-too-many-for-tls\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;On average, around 15 million TLS connections are established with Cloudflare per second. Upgrading each to ML-DSA, would take 1.8Tbps, which is 0.6% of our current total network capacity. No problem so far. The question is how these extra bytes affect performance.&lt;/p&gt;&lt;p&gt;Back in 2021, we &lt;a href=\&quot;https://blog.cloudflare.com/sizing-up-post-quantum-signatures/\&quot;&gt;&lt;u&gt;ran a large-scale experiment&lt;/u&gt;&lt;/a&gt; to measure the impact of big post-quantum certificate chains on connections to Cloudflare’s network over the open Internet. There were two important results. First, we saw a steep increase in the rate of client and middlebox failures when we added more than 10kB to existing certificate chains. Secondly, when adding less than 9kB, the slowdown in TLS handshake time would be approximately 15%. We felt the latter is workable, but far from ideal: such a slowdown is noticeable and people might hold off deploying post-quantum certificates before it’s too late.&lt;/p&gt;&lt;p&gt;Chrome is more cautious and set 10% as their target for maximum TLS handshake time regression. They &lt;a href=\&quot;https://dadrian.io/blog/posts/pqc-signatures-2024/#fnref:3\&quot;&gt;&lt;u&gt;report&lt;/u&gt;&lt;/a&gt; that deploying post-quantum key agreement has already incurred a 4% slowdown in TLS handshake time, for the extra 1.1kB from server-to-client and 1.2kB from client-to-server. That slowdown is proportionally larger than the 15% we found for 9kB, but that could be explained by slower upload speeds than download speeds.&lt;/p&gt;&lt;p&gt;There has been pushback against the focus on TLS handshake times. One argument is that session resumption alleviates the need for sending the certificates again. A second argument is that the data required to visit a typical website dwarfs the additional bytes for post-quantum certificates. One example is this &lt;a href=\&quot;https://www.amazon.science/publications/the-impact-of-data-heavy-post-quantum-tls-1-3-on-the-time-to-last-byte-of-real-world-connections\&quot;&gt;&lt;u&gt;2024 publication&lt;/u&gt;&lt;/a&gt;, where Amazon researchers have simulated the impact of large post-quantum certificates on data-heavy TLS connections. They argue that typical connections transfer multiple requests and hundreds of kilobytes, and for those the TLS handshake slowdown disappears in the margin.&lt;/p&gt;&lt;p&gt;Are session resumption and hundreds of kilobytes over a connection typical though? We’d like to share what we see. We focus on QUIC connections, which are likely initiated by browsers or browser-like clients. Of all QUIC connections with Cloudflare that carry at least one HTTP request, 37% are &lt;a href=\&quot;https://blog.cloudflare.com/even-faster-connection-establishment-with-quic-0-rtt-resumption/\&quot;&gt;&lt;u&gt;resumptions&lt;/u&gt;&lt;/a&gt;, meaning that key material from a previous TLS connection is reused, avoiding the need to transmit certificates. The median number of bytes transferred from server-to-client over a resumed QUIC connection is 4.4kB, while the average is 395kB. For non-resumptions the median is 7.8kB and average is 551kB. This vast difference between median and average indicates that a small fraction of data-heavy connections skew the average. In fact, only 15.8% of all QUIC connections transfer more than 100kB.&lt;/p&gt;&lt;p&gt;The median certificate chain today (with compression) is &lt;a href=\&quot;https://datatracker.ietf.org/doc/html/draft-ietf-tls-cert-abridge-02#section-4\&quot;&gt;&lt;u&gt;3.2kB&lt;/u&gt;&lt;/a&gt;. That means that almost 40% of all data transferred from server to client on more than half of the non-resumed QUIC connections are just for the certificates, and this only gets worse with post-quantum algorithms. For the majority of QUIC connections, using ML-DSA as a drop-in replacement for classical signatures would more than double the number of transmitted bytes over the lifetime of the connection.&lt;/p&gt;&lt;p&gt;It sounds quite bad if the vast majority of data transferred for a typical connection is just for the post-quantum certificates. It’s still only a proxy for what is actually important: the effect on metrics relevant to the end-user, such as the browsing experience (e.g. &lt;a href=\&quot;https://web.dev/articles/optimize-lcp\&quot;&gt;&lt;u&gt;largest contentful paint&lt;/u&gt;&lt;/a&gt;) and the amount of data those certificates take from a user’s monthly data cap. We will continue to investigate and get a better understanding of the impact.&lt;/p&gt;\n &lt;div class=\&quot;flex anchor relative\&quot;&gt;\n &lt;h2 id=\&quot;zooming-out\&quot;&gt;Zooming out&lt;/h2&gt;\n &lt;a href=\&quot;#zooming-out\&quot; aria-hidden=\&quot;true\&quot; class=\&quot;relative sm:absolute sm:-left-5\&quot;&gt;\n &lt;svg width=\&quot;16\&quot; height=\&quot;16\&quot; viewBox=\&quot;0 0 24 24\&quot;&gt;&lt;path fill=\&quot;currentcolor\&quot; d=\&quot;m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z\&quot;&gt;&lt;/path&gt;&lt;/svg&gt;\n &lt;/a&gt;\n &lt;/div&gt;\n &lt;p&gt;That was a lot — let’s step back.&lt;/p&gt;&lt;p&gt;It’s great to see how much better the post-quantum signature algorithms are today in almost every family than they were in &lt;a href=\&quot;https://blog.cloudflare.com/sizing-up-post-quantum-signatures/\&quot;&gt;&lt;u&gt;2021&lt;/u&gt;&lt;/a&gt;. The improvements haven’t slowed down either. Many of the algorithms that do not improve over ML-DSA for TLS today could still do so in the third round. Looking back, we are also cautioned: several algorithms considered in 2021 have since been broken.&lt;/p&gt;&lt;p&gt;From an implementation and performance perspective for TLS today, HAWK, SNOVA, and MAYO are all clear improvements over ML-DSA and Falcon. They are also very new, and presently we cannot depend on them without a &lt;a href=\&quot;https://blog.cloudflare.com/pq-2024/#way-forward\&quot;&gt;&lt;u&gt;plan B&lt;/u&gt;&lt;/a&gt;. UOV has been around a lot longer. Due to its large public key, it will not work on its own, but be a very useful complement to another general purpose signature scheme.&lt;/p&gt;&lt;p&gt;Even with the best performers out of the competition, the way we see TLS connections used today, suggest that drop-in post-quantum certificates will have a big impact on at least half of them.&lt;/p&gt;&lt;p&gt;In the meantime, we can also make plan B our plan A: there are several ways in which we can reduce the number of signatures used in TLS. We can leave out intermediate certificates (&lt;a href=\&quot;https://datatracker.ietf.org/doc/html/draft-kampanakis-tls-scas-latest\&quot;&gt;&lt;u&gt;1&lt;/u&gt;&lt;/a&gt;, &lt;a href=\&quot;https://datatracker.ietf.org/doc/draft-ietf-tls-cert-abridge/\&quot;&gt;&lt;u&gt;2&lt;/u&gt;&lt;/a&gt;, &lt;a href=\&quot;https://datatracker.ietf.org/doc/html/draft-davidben-tls-trust-expr-04#name-intermediate-elision\&quot;&gt;&lt;u&gt;3&lt;/u&gt;&lt;/a&gt;). Another is to use a KEM &lt;a href=\&quot;https://kemtls.org/\&quot;&gt;&lt;u&gt;instead of a signature&lt;/u&gt;&lt;/a&gt; for handshake authentication. We can even get rid of all the offline signatures with a more &lt;a href=\&quot;https://datatracker.ietf.org/doc/html/draft-davidben-tls-merkle-tree-certs-03\&quot;&gt;&lt;u&gt;ambitious redesign&lt;/u&gt;&lt;/a&gt; for the &lt;a href=\&quot;https://www.youtube.com/watch?v=f8unMB2Qjho\&quot;&gt;&lt;u&gt;vast majority&lt;/u&gt;&lt;/a&gt; of visits: a post-quantum Internet with fewer bytes on the wire! We’ve discussed these ideas at more length in a &lt;a href=\&quot;https://blog.cloudflare.com/pq-2024/#way-forward\&quot;&gt;&lt;u&gt;previous blog post&lt;/u&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;So what does this mean for the coming years? We will continue to work with browsers to understand the end user impact of large drop-in post-quantum certificates. When certificate authorities support them (our guess: 2026), we will add support for ML-DSA certificates &lt;a href=\&quot;https://blog.cloudflare.com/post-quantum-crypto-should-be-free/\&quot;&gt;&lt;u&gt;for free&lt;/u&gt;&lt;/a&gt;. This will be opt-in until cryptographically relevant quantum computers are imminent, to prevent undue performance regression. In the meantime, we will continue to pursue larger changes to the WebPKI, so that we can bring full post-quantum security to the Internet without performance compromise.&lt;/p&gt;&lt;p&gt;We’ve talked a lot about certificates, but what we need to care about today is encryption. Along with many across industry, including the major browsers, we have deployed the post-quantum key agreement X25519MLKEM768 across the board, and you can make sure your connections with Cloudflare are already secured against harvest-now/decrypt-later. Visit &lt;a href=\&quot;http://pq.cloudflareresearch.com\&quot;&gt;&lt;u&gt;pq.cloudflareresearch.com&lt;/u&gt;&lt;/a&gt; to learn how.&lt;/p&gt;&quot;],&quot;published_at&quot;:[0,&quot;2024-11-07T14:00+00:00&quot;],&quot;updated_at&quot;:[0,&quot;2025-01-01T19:03:47.257Z&quot;],&quot;feature_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/14aKzVYYFvVhoxquNWWg1I/5da91d29f43ab0f6b5e08cf812c55aae/unnamed.png&quot;],&quot;tags&quot;:[1,[[0,{&quot;id&quot;:[0,&quot;6bIo7ayy56Fzdrtf9z2EWy&quot;],&quot;name&quot;:[0,&quot;Post-Quantum&quot;],&quot;slug&quot;:[0,&quot;post-quantum&quot;]}],[0,{&quot;id&quot;:[0,&quot;1x7tpPmKIUCt19EDgM1Tsl&quot;],&quot;name&quot;:[0,&quot;Research&quot;],&quot;slug&quot;:[0,&quot;research&quot;]}],[0,{&quot;id&quot;:[0,&quot;1QsJUMpv0QBSLiVZLLQJ3V&quot;],&quot;name&quot;:[0,&quot;Cryptography&quot;],&quot;slug&quot;:[0,&quot;cryptography&quot;]}],[0,{&quot;id&quot;:[0,&quot;56vA0Z6hqev6QaJBQmO2J8&quot;],&quot;name&quot;:[0,&quot;TLS&quot;],&quot;slug&quot;:[0,&quot;tls&quot;]}]]],&quot;relatedTags&quot;:[0],&quot;authors&quot;:[1,[[0,{&quot;name&quot;:[0,&quot;Bas Westerbaan&quot;],&quot;slug&quot;:[0,&quot;bas&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4KeBG8XO1ADZHuEV9v5Hwz/6c90a96cfb1373b208bffc35f3fd71f4/bas.png&quot;],&quot;location&quot;:[0,&quot;The Netherlands&quot;],&quot;website&quot;:[0,&quot;https://bas.westerbaan.name&quot;],&quot;twitter&quot;:[0,&quot;@bwesterb&quot;],&quot;facebook&quot;:[0,null]}],[0,{&quot;name&quot;:[0,&quot;Luke Valenta&quot;],&quot;slug&quot;:[0,&quot;luke&quot;],&quot;bio&quot;:[0,null],&quot;profile_image&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2FfvsynZPyK2w2lJSIXdqr/f8758aa6638121491d233932a076770e/luke.jpg&quot;],&quot;location&quot;:[0,&quot;Austin, TX&quot;],&quot;website&quot;:[0,&quot;https://lukevalenta.com/&quot;],&quot;twitter&quot;:[0,&quot;@lukevalenta&quot;],&quot;facebook&quot;:[0,null]}]]],&quot;meta_description&quot;:[0,&quot;NIST has standardized four post-quantum signature schemes so far, and they’re not done yet: there are fourteen new candidates in the running for standardization. In this blog post we take measure of them and discover why we ended up with so many PQ signatures.&quot;],&quot;primary_author&quot;:[0,{}],&quot;localeList&quot;:[0,{&quot;name&quot;:[0,&quot;blog-english-only&quot;],&quot;enUS&quot;:[0,&quot;English for Locale&quot;],&quot;zhCN&quot;:[0,&quot;No Page for Locale&quot;],&quot;zhHansCN&quot;:[0,&quot;No Page for Locale&quot;],&quot;zhTW&quot;:[0,&quot;No Page for Locale&quot;],&quot;frFR&quot;:[0,&quot;No Page for Locale&quot;],&quot;deDE&quot;:[0,&quot;No Page for Locale&quot;],&quot;itIT&quot;:[0,&quot;No Page for Locale&quot;],&quot;jaJP&quot;:[0,&quot;No Page for Locale&quot;],&quot;koKR&quot;:[0,&quot;No Page for Locale&quot;],&quot;ptBR&quot;:[0,&quot;No Page for Locale&quot;],&quot;esLA&quot;:[0,&quot;No Page for Locale&quot;],&quot;esES&quot;:[0,&quot;No Page for Locale&quot;],&quot;enAU&quot;:[0,&quot;No Page for Locale&quot;],&quot;enCA&quot;:[0,&quot;No Page for Locale&quot;],&quot;enIN&quot;:[0,&quot;No Page for Locale&quot;],&quot;enGB&quot;:[0,&quot;No Page for Locale&quot;],&quot;idID&quot;:[0,&quot;No Page for Locale&quot;],&quot;ruRU&quot;:[0,&quot;No Page for Locale&quot;],&quot;svSE&quot;:[0,&quot;No Page for Locale&quot;],&quot;viVN&quot;:[0,&quot;No Page for Locale&quot;],&quot;plPL&quot;:[0,&quot;No Page for Locale&quot;],&quot;arAR&quot;:[0,&quot;No Page for Locale&quot;],&quot;nlNL&quot;:[0,&quot;No Page for Locale&quot;],&quot;thTH&quot;:[0,&quot;No Page for Locale&quot;],&quot;trTR&quot;:[0,&quot;No Page for Locale&quot;],&quot;heIL&quot;:[0,&quot;No Page for Locale&quot;],&quot;lvLV&quot;:[0,&quot;No Page for Locale&quot;],&quot;etEE&quot;:[0,&quot;No Page for Locale&quot;],&quot;ltLT&quot;:[0,&quot;No Page for Locale&quot;]}],&quot;url&quot;:[0,&quot;https://blog.cloudflare.com/another-look-at-pq-signatures&quot;],&quot;metadata&quot;:[0,{&quot;title&quot;:[0,&quot;A look at the latest post-quantum signature standardization candidates&quot;],&quot;description&quot;:[0,&quot;NIST has standardized four post-quantum signature schemes so far, and they’re not done yet: there are fourteen new candidates in the running for standardization. In this blog post we take measure of them and discover why we ended up with so many PQ signatures.&quot;],&quot;imgPreview&quot;:[0,&quot;https://cf-assets.www.cloudflare.com/zkvhlag99gkb/GJ6NG49gvA8D9v05bCZIC/79a0d682bf29db033ca86c4d7bfc5485/A_look_at_the_latest_post-quantum_signature_standardization_candidates_-OG.png&quot;]}]}]]],&quot;locale&quot;:[0,&quot;en-us&quot;],&quot;translations&quot;:[0,{&quot;posts.by&quot;:[0,&quot;By&quot;],&quot;footer.gdpr&quot;:[0,&quot;GDPR&quot;],&quot;lang_blurb1&quot;:[0,&quot;This post is also available in {lang1}.&quot;],&quot;lang_blurb2&quot;:[0,&quot;This post is also available in {lang1} and {lang2}.&quot;],&quot;lang_blurb3&quot;:[0,&quot;This post is also available in {lang1}, {lang2} and {lang3}.&quot;],&quot;footer.press&quot;:[0,&quot;Press&quot;],&quot;header.title&quot;:[0,&quot;The Cloudflare Blog&quot;],&quot;search.clear&quot;:[0,&quot;Clear&quot;],&quot;search.filter&quot;:[0,&quot;Filter&quot;],&quot;search.source&quot;:[0,&quot;Source&quot;],&quot;footer.careers&quot;:[0,&quot;Careers&quot;],&quot;footer.company&quot;:[0,&quot;Company&quot;],&quot;footer.support&quot;:[0,&quot;Support&quot;],&quot;footer.the_net&quot;:[0,&quot;theNet&quot;],&quot;search.filters&quot;:[0,&quot;Filters&quot;],&quot;footer.our_team&quot;:[0,&quot;Our team&quot;],&quot;footer.webinars&quot;:[0,&quot;Webinars&quot;],&quot;page.more_posts&quot;:[0,&quot;More posts&quot;],&quot;posts.time_read&quot;:[0,&quot;{time} min read&quot;],&quot;search.language&quot;:[0,&quot;Language&quot;],&quot;footer.community&quot;:[0,&quot;Community&quot;],&quot;footer.resources&quot;:[0,&quot;Resources&quot;],&quot;footer.solutions&quot;:[0,&quot;Solutions&quot;],&quot;footer.trademark&quot;:[0,&quot;Trademark&quot;],&quot;header.subscribe&quot;:[0,&quot;Subscribe&quot;],&quot;footer.compliance&quot;:[0,&quot;Compliance&quot;],&quot;footer.free_plans&quot;:[0,&quot;Free plans&quot;],&quot;footer.impact_ESG&quot;:[0,&quot;Impact/ESG&quot;],&quot;posts.follow_on_X&quot;:[0,&quot;Follow on X&quot;],&quot;footer.help_center&quot;:[0,&quot;Help center&quot;],&quot;footer.network_map&quot;:[0,&quot;Network Map&quot;],&quot;header.please_wait&quot;:[0,&quot;Please Wait&quot;],&quot;page.related_posts&quot;:[0,&quot;Related posts&quot;],&quot;search.result_stat&quot;:[0,&quot;Results &lt;strong&gt;{search_range}&lt;/strong&gt; of &lt;strong&gt;{search_total}&lt;/strong&gt; for &lt;strong&gt;{search_keyword}&lt;/strong&gt;&quot;],&quot;footer.case_studies&quot;:[0,&quot;Case Studies&quot;],&quot;footer.connect_2024&quot;:[0,&quot;Connect 2024&quot;],&quot;footer.terms_of_use&quot;:[0,&quot;Terms of Use&quot;],&quot;footer.white_papers&quot;:[0,&quot;White Papers&quot;],&quot;footer.cloudflare_tv&quot;:[0,&quot;Cloudflare TV&quot;],&quot;footer.community_hub&quot;:[0,&quot;Community Hub&quot;],&quot;footer.compare_plans&quot;:[0,&quot;Compare plans&quot;],&quot;footer.contact_sales&quot;:[0,&quot;Contact Sales&quot;],&quot;header.contact_sales&quot;:[0,&quot;Contact Sales&quot;],&quot;header.email_address&quot;:[0,&quot;Email Address&quot;],&quot;page.error.not_found&quot;:[0,&quot;Page not found&quot;],&quot;footer.developer_docs&quot;:[0,&quot;Developer docs&quot;],&quot;footer.privacy_policy&quot;:[0,&quot;Privacy Policy&quot;],&quot;footer.request_a_demo&quot;:[0,&quot;Request a demo&quot;],&quot;page.continue_reading&quot;:[0,&quot;Continue reading&quot;],&quot;footer.analysts_report&quot;:[0,&quot;Analyst reports&quot;],&quot;footer.for_enterprises&quot;:[0,&quot;For enterprises&quot;],&quot;footer.getting_started&quot;:[0,&quot;Getting Started&quot;],&quot;footer.learning_center&quot;:[0,&quot;Learning Center&quot;],&quot;footer.project_galileo&quot;:[0,&quot;Project Galileo&quot;],&quot;pagination.newer_posts&quot;:[0,&quot;Newer Posts&quot;],&quot;pagination.older_posts&quot;:[0,&quot;Older Posts&quot;],&quot;posts.social_buttons.x&quot;:[0,&quot;Discuss on X&quot;],&quot;search.icon_aria_label&quot;:[0,&quot;Search&quot;],&quot;search.source_location&quot;:[0,&quot;Source/Location&quot;],&quot;footer.about_cloudflare&quot;:[0,&quot;About Cloudflare&quot;],&quot;footer.athenian_project&quot;:[0,&quot;Athenian Project&quot;],&quot;footer.become_a_partner&quot;:[0,&quot;Become a partner&quot;],&quot;footer.cloudflare_radar&quot;:[0,&quot;Cloudflare Radar&quot;],&quot;footer.network_services&quot;:[0,&quot;Network services&quot;],&quot;footer.trust_and_safety&quot;:[0,&quot;Trust &amp; Safety&quot;],&quot;header.get_started_free&quot;:[0,&quot;Get Started Free&quot;],&quot;page.search.placeholder&quot;:[0,&quot;Search Cloudflare&quot;],&quot;footer.cloudflare_status&quot;:[0,&quot;Cloudflare Status&quot;],&quot;footer.cookie_preference&quot;:[0,&quot;Cookie Preferences&quot;],&quot;header.valid_email_error&quot;:[0,&quot;Must be valid email.&quot;],&quot;search.result_stat_empty&quot;:[0,&quot;Results &lt;strong&gt;{search_range}&lt;/strong&gt; of &lt;strong&gt;{search_total}&lt;/strong&gt;&quot;],&quot;footer.connectivity_cloud&quot;:[0,&quot;Connectivity cloud&quot;],&quot;footer.developer_services&quot;:[0,&quot;Developer services&quot;],&quot;footer.investor_relations&quot;:[0,&quot;Investor relations&quot;],&quot;page.not_found.error_code&quot;:[0,&quot;Error Code: 404&quot;],&quot;search.autocomplete_title&quot;:[0,&quot;Insert a query. Press enter to send&quot;],&quot;footer.logos_and_press_kit&quot;:[0,&quot;Logos &amp; press kit&quot;],&quot;footer.application_services&quot;:[0,&quot;Application services&quot;],&quot;footer.get_a_recommendation&quot;:[0,&quot;Get a recommendation&quot;],&quot;posts.social_buttons.reddit&quot;:[0,&quot;Discuss on Reddit&quot;],&quot;footer.sse_and_sase_services&quot;:[0,&quot;SSE and SASE services&quot;],&quot;page.not_found.outdated_link&quot;:[0,&quot;You may have used an outdated link, or you may have typed the address incorrectly.&quot;],&quot;footer.report_security_issues&quot;:[0,&quot;Report Security Issues&quot;],&quot;page.error.error_message_page&quot;:[0,&quot;Sorry, we can&#39;t find the page you are looking for.&quot;],&quot;header.subscribe_notifications&quot;:[0,&quot;Subscribe to receive notifications of new posts:&quot;],&quot;footer.cloudflare_for_campaigns&quot;:[0,&quot;Cloudflare for Campaigns&quot;],&quot;header.subscription_confimation&quot;:[0,&quot;Subscription confirmed. Thank you for subscribing!&quot;],&quot;posts.social_buttons.hackernews&quot;:[0,&quot;Discuss on Hacker News&quot;],&quot;footer.diversity_equity_inclusion&quot;:[0,&quot;Diversity, equity &amp; inclusion&quot;],&quot;footer.critical_infrastructure_defense_project&quot;:[0,&quot;Critical Infrastructure Defense Project&quot;]}],&quot;localesAvailable&quot;:[1,[[0,&quot;zh-cn&quot;],[0,&quot;zh-tw&quot;]]],&quot;footerBlurb&quot;:[0,&quot;Cloudflare&#39;s connectivity cloud protects &lt;a target=&#39;_blank&#39; href=&#39;https://www.cloudflare.com/network-services/&#39; rel=&#39;noreferrer&#39;&gt;entire corporate networks&lt;/a&gt;, helps customers build &lt;a target=&#39;_blank&#39; href=&#39;https://workers.cloudflare.com/&#39; rel=&#39;noreferrer&#39;&gt;Internet-scale applications efficiently&lt;/a&gt;, accelerates any &lt;a target=&#39;_blank&#39; href=&#39;https://www.cloudflare.com/performance/accelerate-internet-applications/&#39; rel=&#39;noreferrer&#39;&gt;website or Internet application&lt;/a&gt;, &lt;a target=&#39;_blank&#39; href=&#39;https://www.cloudflare.com/ddos/&#39; rel=&#39;noreferrer&#39;&gt;wards off DDoS attacks&lt;/a&gt;, keeps &lt;a target=&#39;_blank&#39; href=&#39;https://www.cloudflare.com/application-security/&#39; rel=&#39;noreferrer&#39;&gt;hackers at bay&lt;/a&gt;, and can help you on &lt;a target=&#39;_blank&#39; href=&#39;https://www.cloudflare.com/products/zero-trust/&#39; rel=&#39;noreferrer&#39;&gt;your journey to Zero Trust&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Visit &lt;a target=&#39;_blank&#39; href=&#39;https://one.one.one.one/&#39; rel=&#39;noreferrer&#39;&gt;1.1.1.1&lt;/a&gt; from any device to get started with our free app that makes your Internet faster and safer.&lt;br/&gt;&lt;br/&gt;To learn more about our mission to help build a better Internet, &lt;a target=&#39;_blank&#39; href=&#39;https://www.cloudflare.com/learning/what-is-cloudflare/&#39; rel=&#39;noreferrer&#39;&gt;start here&lt;/a&gt;. If you&amp;apos;re looking for a new career direction, check out &lt;a target=&#39;_blank&#39; href=&#39;http://www.cloudflare.com/careers&#39; rel=&#39;noreferrer&#39;&gt;our open positions&lt;/a&gt;.&quot;]}" ssr client="load" opts="{&quot;name&quot;:&quot;Post&quot;,&quot;value&quot;:true}" await-children><main id="post" class="flex flex-row flex-wrap items-center justify-center pt2 pt4-l"><article class="post-full mw-100 ph3 ph0-l fs-20px"><h1 class="f6 f7-l fw4 gray1 pt1 pt3-l mb1">Privacy Pass: upgrading to the latest protocol version</h1><p class="f3 fw5 gray5 db di-l mt2">2024-01-04</p><ul class="author-lists flex pl0 mt4"><li class="list flex items-center pr2 mb1-ns"><a href="/author/thibault/" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1CqrdcRymVgEs1zRfSE6Xr/b8182164b0a8435b162bdd1246b7e91f/thibault.png" alt="Thibault Meunier" width="62" height="62"/></a><div class="author-name-tooltip"><a href="/author/thibault/" class="fw4 f3 no-underline black mr3">Thibault Meunier</a></div></li><li class="list flex items-center pr2 mb1-ns"><a href="/author/cdrubin/" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5EziMHboXmLjqS6FXaUODB/8f0daa841b1c260fae1be2a9d863457f/cdrubin.png" alt="Cefan Daniel Rubin" width="62" height="62"/></a><div class="author-name-tooltip"><a href="/author/cdrubin/" class="fw4 f3 no-underline black mr3">Cefan Daniel Rubin</a></div></li><li class="list flex items-center pr2 mb1-ns"><a href="/author/armfazh/" class="static-avatar pr1"><img class="author-profile-image br-100 mr2" src="https://blog.cloudflare.com/cdn-cgi/image/format=auto,dpr=3,width=64,height=64,gravity=face,fit=crop,zoom=0.5/https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1KZECWa5TCEPjjcRmbx9iT/c62263899934ff326df2b6864e42b104/armfazh.png" alt="Armando Faz-Hernández" width="62" height="62"/></a><div class="author-name-tooltip"><a href="/author/armfazh/" class="fw4 f3 no-underline black mr3">Armando Faz-Hernández</a></div></li></ul><section class="post-full-content"><div class="mb2 gray5">16 min read</div><div class="mt4">This post is also available in <a href="/zh-cn/privacy-pass-standard">简体中文</a> and <a href="/zh-tw/privacy-pass-standard">繁體中文</a>.</div><div class="post-content lh-copy gray1"><p></p> <figure class="kg-card kg-image-card "> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2LZJxp89GI8PxGwGSPRQJL/9cfe61e756369dcad6cb78f5ad89ec1f/image9.png" alt="Privacy Pass: Upgrading to the latest protocol version" class="kg-image" width="1800" height="1013" loading="lazy"/> </figure> <div class="flex anchor relative"> <h2 id="enabling-anonymous-access-to-the-web-with-privacy-preserving-cryptography">Enabling anonymous access to the web with privacy-preserving cryptography</h2> <a href="#enabling-anonymous-access-to-the-web-with-privacy-preserving-cryptography" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>The challenge of telling humans and bots apart is almost as old as the web itself. From online ticket vendors to dating apps, to ecommerce and finance — there are many legitimate reasons why you&#39;d want to know if it&#39;s a person or a machine knocking on the front door of your website.</p><p>Unfortunately, the tools for the web have traditionally been clunky and sometimes involved a bad user experience. None more so than the CAPTCHA — an irksome solution that humanity wastes a <a href="/introducing-cryptographic-attestation-of-personhood/">staggering</a> amount of time on. A more subtle but intrusive approach is IP tracking, which uses IP addresses to identify and take action on suspicious traffic, but that too can come with <a href="/consequences-of-ip-blocking/">unforeseen consequences</a>.</p><p>And yet, the problem of distinguishing legitimate human requests from automated bots remains as vital as ever. This is why for years Cloudflare has invested in the Privacy Pass protocol — a novel approach to establishing a user’s identity by relying on cryptography, rather than crude puzzles — all while providing a streamlined, privacy-preserving, and often frictionless experience to end users.</p><p>Cloudflare began <a href="/cloudflare-supports-privacy-pass/">supporting Privacy Pass</a> in 2017, with the release of browser extensions for Chrome and Firefox. Web admins with their sites on Cloudflare would have Privacy Pass enabled in the Cloudflare Dash; users who installed the extension in their browsers would see fewer CAPTCHAs on websites they visited that had Privacy Pass enabled.</p><p>Since then, Cloudflare <a href="/end-cloudflare-captcha/">stopped issuing CAPTCHAs</a>, and Privacy Pass has come a long way. Apple uses a version of Privacy Pass for its <a href="https://developer.apple.com/news/?id=huqjyh7k">Private Access Tokens</a> system which works in tandem with a device’s secure enclave to attest to a user’s humanity. And Cloudflare uses Privacy Pass as an important signal in our Web Application Firewall and Bot Management products — which means millions of websites natively offer Privacy Pass.</p><p>In this post, we explore the latest changes to Privacy Pass protocol. We are also excited to introduce a public implementation of the latest IETF draft of the <a href="https://www.ietf.org/archive/id/draft-ietf-privacypass-protocol-16.html">Privacy Pass protocol</a> — including a <a href="https://github.com/cloudflare?q=pp-&type=all&language=&sort=#org-repositories">set of open-source templates</a> that can be used to implement Privacy Pass <a href="https://github.com/cloudflare/pp-origin"><i>Origins</i></a><i>,</i> <a href="https://github.com/cloudflare/pp-issuer"><i>Issuers</i></a>, and <a href="https://github.com/cloudflare/pp-attester"><i>Attesters</i></a>. These are based on Cloudflare Workers, and are the easiest way to get started with a new deployment of Privacy Pass.</p><p>To complement the updated implementations, we are releasing a new version of our Privacy Pass browser extensions (<a href="https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/">Firefox</a>, <a href="https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi">Chrome</a>), which are rolling out with the name: <i>Silk - Privacy Pass Client</i>. Users of these extensions can expect to see fewer bot-checks around the web, and will be contributing to research about privacy preserving signals via a set of trusted attesters, which can be configured in the extension’s settings panel.</p><p>Finally, we will discuss how Privacy Pass can be used for an array of scenarios beyond differentiating bot from human traffic.</p><p><b>Notice to our users</b></p><ul><li><p>If you use the Privacy Pass API that controls Privacy Pass configuration on Cloudflare, you can remove these calls. This API is no longer needed since Privacy Pass is now included by default in our Challenge Platform. Out of an abundance of caution for our customers, we are doing a <a href="https://developers.cloudflare.com/fundamentals/api/reference/deprecations/">four-month deprecation notice</a>.</p></li><li><p>If you have the Privacy Pass extension installed, it should automatically update to <i>Silk - Privacy Pass Client</i> (<a href="https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/">Firefox</a>, <a href="https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi">Chrome</a>) over the next few days. We have renamed it to keep the distinction clear between the protocol itself and a client of the protocol.</p></li></ul> <div class="flex anchor relative"> <h2 id="brief-history">Brief history</h2> <a href="#brief-history" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>In the last decade, we&#39;ve seen the <a href="/next-generation-privacy-protocols/">rise of protocols</a> with privacy at their core, including <a href="/building-privacy-into-internet-standards-and-how-to-make-your-app-more-private-today/">Oblivious HTTP (OHTTP)</a>, <a href="/deep-dive-privacy-preserving-measurement/">Distributed aggregation protocol (DAP)</a>, and <a href="/unlocking-quic-proxying-potential/">MASQUE</a>. These protocols improve privacy when browsing and interacting with services online. By protecting users&#39; privacy, these protocols also ask origins and website owners to revise their expectations around the data they can glean from user traffic. This might lead them to reconsider existing assumptions and mitigations around suspicious traffic, such as <a href="/consequences-of-ip-blocking/">IP filtering</a>, which often has unintended consequences.</p><p>In 2017, Cloudflare announced <a href="/cloudflare-supports-privacy-pass/">support for Privacy Pass</a>. At launch, this meant improving content accessibility for web users who would see a lot of interstitial pages (such as <a href="https://www.cloudflare.com/learning/bots/how-captchas-work/">CAPTCHAs</a>) when browsing websites protected by Cloudflare. Privacy Pass tokens provide a signal about the user’s capabilities to website owners while protecting their privacy by ensuring each token redemption is unlinkable to its issuance context. Since then, the technology has turned into a <a href="https://datatracker.ietf.org/wg/privacypass/documents/">fully fledged protocol</a> used by millions thanks to academic and industry effort. The existing browser extension accounts for hundreds of thousands of downloads. During the same time, Cloudflare has dramatically evolved the way it allows customers to challenge their visitors, being <a href="/end-cloudflare-captcha/">more flexible about the signals</a> it receives, and <a href="/turnstile-ga/">moving away from CAPTCHA</a> as a binary legitimacy signal.</p><p>Deployments of this research have led to a broadening of use cases, opening the door to different kinds of attestation. An attestation is a cryptographically-signed data point supporting facts. This can include a signed token indicating that the user has successfully solved a CAPTCHA, having a user’s hardware attest it’s untampered, or a piece of data that an attester can verify against another data source.</p><p>For example, in 2022, Apple hardware devices began to offer Privacy Pass tokens to websites who wanted to reduce how often they show CAPTCHAs, by using the hardware itself as an attestation factor. Before showing images of buses and fire hydrants to users, CAPTCHA providers can request a <a href="https://developer.apple.com/news/?id=huqjyh7k">Private Access Token</a> (PAT). This native support does not require installing extensions, or any user action to benefit from a smoother and more private web browsing experience.</p><p>Below is a brief overview of changes to the protocol we participated in:</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3YImfph78oDPj3kgEcyvV6/37bcd89ffcfff8b636b00c8e931f3218/image8.png" alt="" class="kg-image" width="1808" height="631" loading="lazy"/> </figure><p>The timeline presents cryptographic changes, community inputs, and industry collaborations. These changes helped shape better standards for the web, such as VOPRF (<a href="https://www.rfc-editor.org/rfc/rfc9497">RFC 9497</a>), or RSA Blind Signatures (<a href="https://www.rfc-editor.org/rfc/rfc9474">RFC 9474</a>). In the next sections, we dive in the Privacy Pass protocol to understand its ins and outs.</p> <div class="flex anchor relative"> <h2 id="anonymous-credentials-in-real-life">Anonymous credentials in real life</h2> <a href="#anonymous-credentials-in-real-life" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>Before explaining the protocol in more depth, let&#39;s use an analogy. You are at a music festival. You bought your ticket online with a student discount. When you arrive at the gates, an agent scans your ticket, checks your student status, and gives you a yellow wristband and two drink tickets.</p><p>During the festival, you go in and out by showing your wristband. When a friend asks you to grab a drink, you pay with your tickets. One for your drink and one for your friend. You give your tickets to the bartender, they check the tickets, and give you a drink. The characteristics that make this interaction private is that the drinks tickets cannot be traced back to you or your payment method, but they can be verified as having been unused and valid for purchase of a drink.</p><p>In the web use case, the Internet is a festival. When you arrive at the gates of a website, an agent scans your request, and gives you a session cookie as well as two Privacy Pass tokens. They could have given you just one token, or more than two, but in our example ‘two tokens’ is the given website’s policy. You can use these tokens to attest your humanity, to authenticate on certain websites, or even to confirm the legitimacy of your hardware.</p><p>Now, you might wonder if this is a technique we have been using for years, why do we need fancy cryptography and standardization efforts? Well, unlike at a real-world music festival where most people don’t carry around photocopiers, on the Internet it is pretty easy to copy tokens. For instance, how do we stop people using a token twice? We could put a unique number on each token, and check it is not spent twice, but that would allow the gate attendant to tell the bartender which numbers were linked to which person. So, we need cryptography.</p><p>When another website presents a challenge to you, you provide your Privacy Pass token and are then allowed to view a gallery of beautiful cat pictures. The difference with the festival is this challenge might be interactive, which would be similar to the bartender giving you a numbered ticket which would have to be signed by the agent before getting a drink. The website owner can verify that the token is valid but has no way of tracing or connecting the user back to the action that provided them with the Privacy Pass tokens. With Privacy Pass terminology, you are a Client, the website is an Origin, the agent is an Attester, and the bar an Issuer. The next section goes through these in more detail.</p> <div class="flex anchor relative"> <h2 id="privacy-pass-protocol">Privacy Pass protocol</h2> <a href="#privacy-pass-protocol" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>Privacy Pass specifies an extensible protocol for creating and redeeming anonymous and transferable tokens. In fact, Apple has their own implementation with Private Access Tokens (PAT), and later we will describe another implementation with the Silk browser extension. Given PAT was the first to implement the IETF defined protocol, Privacy Pass is sometimes referred to as PAT in the literature.</p><p>The protocol is generic, and defines four components:</p><ul><li><p>Client: Web user agent with a Privacy Pass enabled browser. This could be your <a href="/eliminating-captchas-on-iphones-and-macs-using-new-standard/">Apple device with PAT</a>, or your web browser with <a href="https://github.com/cloudflare/pp-browser-extension">the Silk extension installed</a>. Typically, this is the actor who is requesting content and is asked to share some attribute of themselves.</p></li><li><p>Origin: Serves content requested by the Client. The Origin trusts one or more Issuers, and presents Privacy Pass challenges to the Client. For instance, Cloudflare Managed Challenge is a Privacy Pass origin serving two Privacy Pass challenges: one for Apple PAT Issuer, one for Cloudflare Research Issuer.</p></li><li><p>Issuer: Signs Privacy Pass tokens upon request from a trusted party, either an Attester or a Client depending on the deployment model. Different Issuers have their own set of trusted parties, depending on the security level they are looking for, as well as their privacy considerations. An Issuer validating device integrity should use different methods that vouch for this attribute to acknowledge the diversity of Client configurations.</p></li><li><p>Attester: Verifies an attribute of the Client and when satisfied requests a signed Privacy Pass token from the Issuer to pass back to the Client. Before vouching for the Client, an Attester may ask the Client to complete a specific task. This task could be a CAPTCHA, a location check, or age verification or some other check that will result in a single binary result. The Privacy Pass token will then share this one-bit of information in an unlinkable manner.</p></li></ul><p>They interact as illustrated below.</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7tX1xRQv6Ltif1NRj2fCOa/eeb412fa39d73e2232f4b062d95cd708/Frame-699-1-.png" alt="" class="kg-image" width="1492" height="780" loading="lazy"/> </figure><p>Let&#39;s dive into what&#39;s really happening with an example. The User wants to access an Origin, say store.example.com. This website has suffered attacks or abuse in the past, and the site is using Privacy Pass to help avoid these going forward. To that end, the Origin returns <a href="https://www.rfc-editor.org/rfc/rfc9110#field.www-authenticate">an authentication request</a> to the Client: <code>WWW-Authenticate: PrivateToken challenge=&quot;A==&quot;,token-key=&quot;B==&quot;</code>. In this way, the Origin signals that it accepts tokens from the Issuer with public key “B==” to satisfy the challenge. That Issuer in turn trusts reputable Attesters to vouch for the Client not being an attacker by means of the presence of a cookie, CAPTCHA, Turnstile, or <a href="/introducing-cryptographic-attestation-of-personhood/">CAP challenge</a> for example. For accessibility reasons for our example, let us say that the Client likely prefers the Turnstile method. The User’s browser prompts them to solve a Turnstile challenge. On success, it contacts the Issuer “B==” with that solution, and then replays the initial requests to store.example.com, this time sending along the token header <code>Authorization: PrivateToken token=&quot;C==&quot;</code>, which the Origin accepts and returns your desired content to the Client. And that’s it.</p><p>We’ve described the Privacy Pass authentication protocol. While Basic authentication (<a href="https://www.rfc-editor.org/rfc/rfc7617">RFC 7671</a>) asks you for a username and a password, the PrivateToken authentication scheme allows the browser to be more flexible on the type of check, while retaining privacy. The Origin store.example.com does not know your attestation method, they just know you are reputable according to the token issuer. In the same spirit, the Issuer &quot;B==&quot; does not see your IP, nor the website you are visiting. This separation between issuance and redemption, also referred to as unlinkability, is what <a href="https://www.ietf.org/archive/id/draft-ietf-privacypass-architecture-16.html">makes Privacy Pass private</a>.</p> <div class="flex anchor relative"> <h2 id="demo-time">Demo time</h2> <a href="#demo-time" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>To put the above in practice, let’s see how the protocol works with Silk, a browser extension providing Privacy Pass support. First, download the relevant <a href="https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi">Chrome</a> or <a href="https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/">Firefox</a> extension.</p><p>Then, head to <a href="https://demo-pat.research.cloudflare.com/login">https://demo-pat.research.cloudflare.com/login</a>. The page returns a 401 Privacy Pass Token not presented. In fact, the origin expects you to perform a PrivateToken authentication. If you don’t have the extension installed, the flow stops here. If you have the extension installed, the extension is going to orchestrate the flow required to get you a token requested by the Origin.</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2ZPDrhytZNVoB81Q7RILu5/7c115c9ed069aa09694373ec1adcc4d0/image10.png" alt="" class="kg-image" width="1596" height="1105" loading="lazy"/> </figure><p>With the extension installed, you are directed to a new tab <a href="https://pp-attester-turnstile.research.cloudflare.com/challenge">https://pp-attester-turnstile.research.cloudflare.com/challenge</a>. This is a page provided by an Attester able to deliver you a token signed by the Issuer request by the Origin. In this case, the Attester checks you’re able to solve a Turnstile challenge.</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7fmDWo3548oMK8jgZ7V0Kd/94ee9ab9bc1df6fee6e6a76dc4fb3e02/image2.png" alt="" class="kg-image" width="1596" height="1105" loading="lazy"/> </figure><p>You click, and that’s it. The Turnstile challenge solution is sent to the Attester, which upon validation, sends back a token from the requested Issuer. This page appears for a very short time, as once the extension has the token, the challenge page is no longer needed.</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3KROIlp9njiXlfceDzRU7W/d1e306da3012c949e3fa5b80934f83a4/image11.png" alt="" class="kg-image" width="1596" height="1105" loading="lazy"/> </figure><p>The extension, now having a token requested by the Origin, sends your initial request for a second time, with an Authorization header containing a valid Issuer PrivateToken. Upon validation, the Origin allows you in with a 200 Privacy Pass Token valid!</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3qOSkMc5wIqS50CuNNNoZY/b36b88ba01ffa1c5f4d78727e602062f/image3.png" alt="" class="kg-image" width="1596" height="1105" loading="lazy"/> </figure><p>If you want to check behind the scenes, you can right-click on the extension logo and go to the preference/options page. It contains a list of attesters trusted by the extension, one per line. You can add your own attestation method (API described below). This allows the Client to decide on their preferred attestation methods.</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/78BCHYQuOBC2aFlnPshu83/c6ee6b54d1d24b6f92f34577267a1146/image7.png" alt="" class="kg-image" width="1596" height="1105" loading="lazy"/> </figure> <div class="flex anchor relative"> <h2 id="privacy-pass-protocol-extended">Privacy Pass protocol — extended</h2> <a href="#privacy-pass-protocol-extended" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>The Privacy Pass protocol is new and not a standard yet, which implies that it’s not uniformly supported on all platforms. To improve flexibility beyond the existing standard proposal, we are introducing two mechanisms: an API for Attesters, and a replay API for web clients. The API for attesters allows developers to build new attestation methods, which only need to provide their URL to interface with the Silk browser extension. The replay API for web clients is a mechanism to enable websites to cooperate with the extension to make PrivateToken authentication work on browsers with Chrome user agents.</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2TLz1CPx9OHczqLabCRmyc/c54b0b4bb637a97812c637ca0eebc78c/image12.png" alt="" class="kg-image" width="1999" height="1119" loading="lazy"/> </figure><p>Because more than one Attester may be supported on your machine, your Client needs to understand which Attester to use depending on the requested Issuer. As mentioned before, you as the Client do not communicate directly with the Issuer because you don’t necessarily know their relation with the attester, so you cannot retrieve its public key. To this end, the Attester API exposes all Issuers reachable by the said Attester via an endpoint: /v1/private-token-issuer-directory. This way, your client selects an appropriate Attester - one in relation with an Issuer that the Origin trusts, before triggering a validation.</p><p>In addition, we propose a replay API. Its goal is to allow clients to fetch a resource a second time if the first response presented a Privacy pass challenge. Some platforms do this automatically, like Silk on Firefox, but some don’t. That’s the case with the Silk Chrome extension for instance, which in its support of <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/manifest_version">manifest v3</a> cannot block requests and only supports Basic authentication in the onAuthRequired extension event. The Privacy Pass Authentication scheme proposes the request to be sent once to get a challenge, and then a second time to get the actual resource. Between these requests to the Origin, the platform orchestrates the issuance of a token. To keep clients informed about the state of this process, we introduce a <code>private-token-client-replay: UUID header</code> alongside WWW-Authenticate. Using a platform defined endpoint, this UUID informs web clients of the current state of authentication: pending, fulfilled, not-found.</p><p>To learn more about how you can use these today, and to deploy your own attestation method, read on.</p> <div class="flex anchor relative"> <h2 id="how-to-use-privacy-pass-today">How to use Privacy Pass today?</h2> <a href="#how-to-use-privacy-pass-today" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>As seen in the section above, Privacy Pass is structured around four components: Origin, Client, Attester, Issuer. That’s why we created four repositories: <a href="https://github.com/cloudflare/pp-origin">cloudflare/pp-origin</a>, <a href="https://github.com/cloudflare/pp-browser-extension">cloudflare/pp-browser-extension</a>, <a href="https://github.com/cloudflare/pp-attester">cloudflare/pp-attester</a>, <a href="https://github.com/cloudflare/pp-issuer">cloudflare/pp-issuer</a>. In addition, the underlying cryptographic libraries are available <a href="https://github.com/cloudflare/privacypass-ts">cloudflare/privacypass-ts</a>, <a href="https://github.com/cloudflare/blindrsa-ts">cloudflare/blindrsa-ts</a>, and <a href="https://github.com/cloudflare/voprf-ts">cloudflare/voprf-ts</a>. In this section, we dive into how to use each one of these depending on your use case.</p><blockquote><p>Note: All examples below are designed in JavaScript and targeted at Cloudflare Workers. Privacy Pass is also implemented in <a href="https://github.com/ietf-wg-privacypass/base-drafts#existing-implementations">other languages</a> and can be deployed with a configuration that suits your needs.</p></blockquote> <div class="flex anchor relative"> <h3 id="as-an-origin-website-owners-service-providers">As an Origin - website owners, service providers</h3> <a href="#as-an-origin-website-owners-service-providers" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>You are an online service that people critically rely upon (health or messaging for instance). You want to provide private payment options to users to maintain your users’ privacy. You only have one subscription tier at $10 per month. You have <a href="https://datatracker.ietf.org/doc/html/draft-davidson-pp-architecture-00#autoid-60">heard</a> people are making privacy preserving apps, and want to use the latest version of Privacy Pass.</p><p>To access your service, users are required to prove they&#39;ve paid for the service through a payment provider of their choosing (that you deem acceptable). This payment provider acknowledges the payment and requests a token for the user to access the service. As a sequence diagram, it looks as follows:</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3CDt5NsDY4c2DuYbggdleT/c2084b1b7cb141a8b528de78392833b3/image4.png" alt="" class="kg-image" width="1615" height="903" loading="lazy"/> </figure><p>To implement it in Workers, we rely on the <a href="https://www.npmjs.com/package/@cloudflare/privacypass-ts"><code>@cloudflare/privacypass-ts</code></a> library, which can be installed by running:</p> <pre class="language-bash"><code class="language-bash">npm i @cloudflare/privacypass-ts</pre></code> <p>This section is going to focus on the Origin work. We assume you have an Issuer up and running, which is described in a later section.</p><p>The Origin defines two flows:</p><ol><li><p>User redeeming token</p></li><li><p>User requesting a token issuance</p></li></ol> <pre class="language-javascript"><code class="language-javascript">import { Client } from &#039;@cloudflare/privacypass-ts&#039; const issuer = &#039;static issuer key&#039; const handleRedemption =&gt; (req) =&gt; { const token = TokenResponse.parse(req.headers.get(&#039;authorization&#039;)) const isValid = token.verify(issuer.publicKey) } const handleIssuance = () =&gt; { return new Response(&#039;Please pay to access the service&#039;, { status: 401, headers: { &#039;www-authenticate&#039;: &#039;PrivateToken challenge=, token-key=, max-age=300&#039; } }) } const handleAuth = (req) =&gt; { const authorization = req.headers.get(&#039;authorization&#039;) if (authorization.startsWith(`PrivateToken token=`)) { return handleRedemption(req) } return handleIssuance(req) } export default { fetch(req: Request) { return handleAuth(req) } }</pre></code> <p>From the user’s perspective, the overhead is minimal. Their client (possibly the Silk browser extension) receives a WWW-Authenticate header with the information required for a token issuance. Then, depending on their client configuration, they are taken to the payment provider of their choice to validate their access to the service.</p><p>With a successful response to the PrivateToken challenge a session is established, and the traditional web service flow continues.</p> <div class="flex anchor relative"> <h3 id="as-an-attester-captcha-providers-authentication-provider">As an Attester - CAPTCHA providers, authentication provider</h3> <a href="#as-an-attester-captcha-providers-authentication-provider" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>You are the author of a new attestation method, such as <a href="/introducing-cryptographic-attestation-of-personhood/">CAP,</a> a new CAPTCHA mechanism, or a new way to validate cookie consent. You know that website owners already use Privacy Pass to trigger such challenges on the user side, and an Issuer is willing to trust your method because it guarantees a high security level. In addition, because of the Privacy Pass protocol you never see which website your attestation is being used for.</p><p>So you decide to expose your attestation method as a Privacy Pass Attester. An Issuer with public key B== trusts you, and that&#39;s the Issuer you are going to request a token from. You can check that with the Yes/No Attester below, whose code is on <a href="https://cloudflareworkers.com/#eedc5a7a6560c44b23a24cc1414b29d7:https://tutorial.cloudflareworkers.com/v1/challenge">Cloudflare Workers playground</a></p> <pre class="language-javascript"><code class="language-javascript">const ISSUER_URL = &#039;https://pp-issuer-public.research.cloudflare.com/token-request&#039; const b64ToU8 = (b) =&gt; Uint8Array.from(atob(b), c =&gt; c.charCodeAt(0)) const handleGetChallenge = (req) =&gt; { return new Response(` &lt;html&gt; &lt;head&gt; &lt;title&gt;Challenge Response&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;button onclick=&quot;sendResponse(&#039;Yes&#039;)&quot;&gt;Yes&lt;/button&gt; &lt;button onclick=&quot;sendResponse(&#039;No&#039;)&quot;&gt;No&lt;/button&gt; &lt;/body&gt; &lt;script&gt; function sendResponse(choice) { fetch(location.href, { method: &#039;POST&#039;, headers: { &#039;private-token-attester-data&#039;: choice } }) } &lt;/script&gt; &lt;/html&gt; `, { status: 401, headers: { &#039;content-type&#039;: &#039;text/html&#039; } }) } const handlePostChallenge = (req) =&gt; { const choice = req.headers.get(&#039;private-token-attester-data&#039;) if (choice !== &#039;Yes&#039;) { return new Response(&#039;Unauthorised&#039;, { status: 401 }) } // hardcoded token request // debug here https://pepe-debug.research.cloudflare.com/?challenge=PrivateToken%20challenge=%22AAIAHnR1dG9yaWFsLmNsb3VkZmxhcmV3b3JrZXJzLmNvbSBE-oWKIYqMcyfiMXOZpcopzGBiYRvnFRP3uKknYPv1RQAicGVwZS1kZWJ1Zy5yZXNlYXJjaC5jbG91ZGZsYXJlLmNvbQ==%22,token-key=%22MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAQ8AMIIBCgKCAQEApqzusqnywE_3PZieStkf6_jwWF-nG6Es1nn5MRGoFSb3aXJFDTTIX8ljBSBZ0qujbhRDPx3ikWwziYiWtvEHSLqjeSWq-M892f9Dfkgpb3kpIfP8eBHPnhRKWo4BX_zk9IGT4H2Kd1vucIW1OmVY0Z_1tybKqYzHS299mvaQspkEcCo1UpFlMlT20JcxB2g2MRI9IZ87sgfdSu632J2OEr8XSfsppNcClU1D32iL_ETMJ8p9KlMoXI1MwTsI-8Kyblft66c7cnBKz3_z8ACdGtZ-HI4AghgW-m-yLpAiCrkCMnmIrVpldJ341yR6lq5uyPej7S8cvpvkScpXBSuyKwIDAQAB%22 const body = b64ToU8(&#039;AALoAYM+fDO53GVxBRuLbJhjFbwr0uZkl/m3NCNbiT6wal87GEuXuRw3iZUSZ3rSEqyHDhMlIqfyhAXHH8t8RP14ws3nQt1IBGE43Q9UinwglzrMY8e+k3Z9hQCEw7pBm/hVT/JNEPUKigBYSTN2IS59AUGHEB49fgZ0kA6ccu9BCdJBvIQcDyCcW5LCWCsNo57vYppIVzbV2r1R4v+zTk7IUDURTa4Mo7VYtg1krAWiFCoDxUOr+eTsc51bWqMtw2vKOyoM/20Wx2WJ0ox6JWdPvoBEsUVbENgBj11kB6/L9u2OW2APYyUR7dU9tGvExYkydXOfhRFJdKUypwKN70CiGw==&#039;) // You can perform some check here to confirm the body is a valid token request console.log(&#039;requesting token for tutorial.cloudflareworkers.com&#039;) return fetch(ISSUER_URL, { method: &#039;POST&#039;, headers: { &#039;content-type&#039;: &#039;application/private-token-request&#039; }, body: body, }) } const handleIssuerDirectory = async () =&gt; { // These are fake issuers // Issuer data can be fetch at https://pp-issuer-public.research.cloudflare.com/.well-known/private-token-issuer-directory const TRUSTED_ISSUERS = { &quot;issuer1&quot;: { &quot;token-keys&quot;: [{ &quot;token-type&quot;: 2, &quot;token-key&quot;: &quot;A==&quot; }] }, &quot;issuer2&quot;: { &quot;token-keys&quot;: [{ &quot;token-type&quot;: 2, &quot;token-key&quot;: &quot;B==&quot; }] }, } return new Response(JSON.stringify(TRUSTED_ISSUERS), { headers: { &quot;content-type&quot;: &quot;application/json&quot; } }) } const handleRequest = (req) =&gt; { const pathname = new URL(req.url).pathname console.log(pathname, req.url) if (pathname === &#039;/v1/challenge&#039;) { if (req.method === &#039;POST&#039;) { return handlePostChallenge(req) } return handleGetChallenge(req) } if (pathname === &#039;/v1/private-token-issuer-directory&#039;) { return handleIssuerDirectory() } return new Response(&#039;Not found&#039;, { status: 404 }) } addEventListener(&#039;fetch&#039;, event =&gt; { event.respondWith(handleRequest(event.request)) })</pre></code> <p>The validation method above is simply checking if the user selected yes. Your method might be more complex, the wrapping stays the same.</p> <figure class="kg-card kg-image-card "> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5PnBuinoRKUpYjrBsHQbn/966c266e7de411503c5bf9a5dc9a184d/Screenshot-2024-01-04-at-10.30.04.png" alt="" class="kg-image" width="1356" height="206" loading="lazy"/> </figure><p><i>Screenshot of the Yes/No Attester example</i></p><p>Because users might have multiple Attesters configured for a given Issuer, we recommend your Attester implements one additional endpoint exposing the keys of the issuers you are in contact with. You can try this code on <a href="https://cloudflareworkers.com/#4eeeef2fa895e519addb3ae442ee351d:https://tutorial.cloudflareworkers.com/v1/private-token-issuer-directory">Cloudflare Workers playground</a>.</p> <pre class="language-javascript"><code class="language-javascript">const handleIssuerDirectory = () =&gt; { const TRUSTED_ISSUERS = { &quot;issuer1&quot;: { &quot;token-keys&quot;: [{ &quot;token-type&quot;: 2, &quot;token-key&quot;: &quot;A==&quot; }] }, &quot;issuer2&quot;: { &quot;token-keys&quot;: [{ &quot;token-type&quot;: 2, &quot;token-key&quot;: &quot;B==&quot; }] }, } return new Response(JSON.stringify(TRUSTED_ISSUERS), { headers: { &quot;content-type&quot;: &quot;application/json&quot; } }) } export default { fetch(req: Request) { const pathname = new URL(req.url).pathname if (pathname === &#039;/v1/private-token-issuer-directory&#039;) { return handleIssuerDirectory() } } }</pre></code> <p>Et voilà. You have an Attester that can be used directly with the Silk browser extension (<a href="https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/">Firefox</a>, <a href="https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi">Chrome</a>). As you progress through your deployment, it can also be directly integrated into your applications.</p><p>If you would like to have a more advanced Attester and deployment pipeline, look at <a href="https://github.com/cloudflare/pp-attester">cloudflare/pp-attester</a> template.</p> <div class="flex anchor relative"> <h3 id="as-an-issuer-foundation-consortium">As an Issuer - foundation, consortium</h3> <a href="#as-an-issuer-foundation-consortium" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>We&#39;ve mentioned the Issuer multiple times already. The role of an Issuer is to select a set of Attesters it wants to operate with, and communicate its public key to Origins. The whole cryptographic behavior of an Issuer is specified <a href="https://www.ietf.org/archive/id/draft-ietf-privacypass-protocol-16.html">by the IETF</a> draft. In contrast to the Client and Attesters which have discretionary behavior, the Issuer is fully standardized. Their opportunity is to choose a signal that is strong enough for the Origin, while preserving privacy of Clients.</p><p>Cloudflare Research is operating a public Issuer for experimental purposes to use on <a href="https://pp-issuer-public.research.cloudflare.com">https://pp-issuer-public.research.cloudflare.com</a>. It is the simplest solution to start experimenting with Privacy Pass today. Once it matures, you can consider joining a production Issuer, or deploying your own.</p><p>To deploy your own, you should:</p> <pre class="language-bash"><code class="language-bash">git clone github.com/cloudflare/pp-issuer</pre></code> <p>Update wrangler.toml with your Cloudflare Workers account id and zone id. The open source Issuer API works as follows:</p><ul><li><p>/.well-known/private-token-issuer-directory returns the issuer configuration. Note it does not expose non-standard token-key-legacy</p></li><li><p>/token-request returns a token. This endpoint should be gated (by Cloudflare Access for instance) to only allow trusted attesters to call it</p></li><li><p>/admin/rotate to generate a new public key. This should only be accessible by your team, and be called prior to the issuer being available.</p></li></ul><p>Then, <code>wrangler publish</code>, and you&#39;re good to onboard Attesters.</p> <div class="flex anchor relative"> <h2 id="development-of-silk-extension">Development of Silk extension</h2> <a href="#development-of-silk-extension" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>Just like the protocol, the browser technology on which Privacy Pass was proven viable has changed as well. For 5 years, the protocol got deployed along with a browser extension for Chrome and Firefox. In 2021, Chrome released a new version of extension configurations, usually referred to as <a href="https://developer.chrome.com/docs/extensions/mv3/intro/platform-vision/">Manifest version 3</a> (MV3). Chrome also started enforcing this new configuration for all newly released extensions.</p><p>Privacy Pass <i>the extension</i> is based on an agreed upon Privacy Pass <a href="https://datatracker.ietf.org/doc/draft-ietf-privacypass-auth-scheme/"><i>authentication protocol</i></a>. Briefly looking at <a href="https://developer.chrome.com/docs/extensions/reference/webRequest/">Chrome’s API documentation</a>, we should be able to use the onAuthRequired event. However, with PrivateToken authentication not yet being standard, there are no hooks provided by browsers for extensions to add logic to this event.</p> <figure class="kg-card kg-image-card kg-width-wide"> <Image src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1iQsRopHuLfmHqjsppwImc/1a379a0cdd3de3e17de04811b1c08ac0/Screenshot-2024-01-04-at-10.32.44.png" alt="" class="kg-image" width="2000" height="932" loading="lazy"/> </figure><p><i>Image available under CC-BY-SA 4.0 provided by</i> <a href="https://developer.chrome.com/docs/extensions/reference/webRequest/"><i>Google For Developers</i></a></p><p>The approach we decided to use is to define a client side replay API. When a response comes with 401 WWW-Authenticate PrivateToken, the browser lets it through, but triggers the private token redemption flow. The original page is notified when a token has been retrieved, and replays the request. For this second request, the browser is able to attach an authorization token, and the request succeeds. This is an active replay performed by the client, rather than a transparent replay done by the platform. A specification is available on <a href="https://github.com/cloudflare/pp-browser-extension#chrome-support-via-client-replay-api">GitHub</a>.</p><p>We are looking forward to the standard progressing, and simplifying this part of the project. This should improve diversity in attestation methods. As we see in the next section, this is key to identifying new signals that can be leveraged by origins.</p> <div class="flex anchor relative"> <h2 id="a-standard-for-anonymous-credentials">A standard for anonymous credentials</h2> <a href="#a-standard-for-anonymous-credentials" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>IP remains as a key identifier in the anti abuse system. At the same time, IP fingerprinting techniques have become a bigger concern and platforms have started to remove some of these ways of tracking users. To enable anti abuse systems to not rely on IP, while ensuring user privacy, Privacy Pass offers a reasonable alternative to deal with potentially abusive or suspicious traffic. The attestation methods vary and can be chosen as needed for a particular deployment. For example, Apple decided to back their attestation with hardware when using Privacy Pass as the authorization technology for iCloud Private Relay. Another example is Cloudflare Research which decided to deploy a Turnstile attester to signal a successful solve for Cloudflare’s challenge platform.</p><p>In all these deployments, Privacy Pass-like technology has allowed for specific bits of information to be shared. Instead of sharing your location, past traffic, and possibly your name and phone number simply by connecting to a website, your device is able to prove specific information to a third party in a privacy preserving manner. Which user information and attestation methods are sufficient to prevent abuse is an open question. We are looking to empower researchers with the release of this software to help in the quest for finding these answers. This could be via new experiments such as testing out new attestation methods, or fostering other privacy protocols by providing a framework for specific information sharing.</p> <div class="flex anchor relative"> <h2 id="future-recommendations">Future recommendations</h2> <a href="#future-recommendations" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>Just as we expect this latest version of Privacy Pass to lead to new applications and ideas we also expect further evolution of the standard and the clients that use it. Future development of Privacy Pass promises to cover topics like batch token issuance and rate limiting. From our work building and deploying this version of Privacy Pass we have encountered limitations that we expect to be resolved in the future as well.</p><p>The division of labor between Attesters and Issuers and the clear directions of trust relationships between the Origin and Issuer, and the Issuer and Attester make reasoning about the implications of a breach of trust clear. Issuers can trust more than one Attester, but since many current deployments of Privacy Pass do not identify the Attester that lead to issuance, a breach of trust in one Attester would render all tokens issued by any Issuer that trusts the Attester untrusted. This is because it would not be possible to tell which Attester was involved in the issuance process. Time will tell if this promotes a 1:1 correspondence between Attesters and Issuers.</p><p>The process of developing a browser extension supported by both Firefox and Chrome-based browsers can at times require quite baroque (and brittle) code paths. Privacy Pass the protocol seems a good fit for an extension of the <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onAuthRequired">webRequest.onAuthRequired</a> browser event. Just as Privacy Pass appears as an alternate authentication message in the WWW-Authenticate HTTP header, browsers could fire the onAuthRequired event for Private Token authentication too and include and allow request blocking support within the onAuthRequired event. This seems a natural evolution of the use of this event which currently is limited to the now rather long-in-the-tooth Basic authentication.</p> <div class="flex anchor relative"> <h2 id="conclusion">Conclusion</h2> <a href="#conclusion" aria-hidden="true" class="relative sm:absolute sm:-left-5"> <svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg> </a> </div> <p>Privacy Pass provides a solution to one of the longstanding challenges of the web: anonymous authentication. By leveraging cryptography, the protocol allows websites to get the information they need from users, and solely this information. It&#39;s already used by millions to help distinguish human requests from automated bots in a manner that is privacy protective and often seamless. We are excited by the protocol’s broad and growing adoption, and by the novel use cases that are unlocked by this latest version.</p><p>Cloudflare’s Privacy Pass implementations are available on GitHub, and are compliant with the standard. We have open-sourced a <a href="https://github.com/cloudflare?q=pp-&type=all&language=&sort=#org-repositories">set of templates</a> that can be used to implement Privacy Pass <a href="https://github.com/cloudflare/pp-origin"><i>Origins</i></a><i>,</i> <a href="https://github.com/cloudflare/pp-issuer"><i>Issuers</i></a>, and <a href="https://github.com/cloudflare/pp-attester"><i>Attesters</i></a>, which leverage Cloudflare Workers to get up and running quickly.</p><p>For those looking to try Privacy Pass out for themselves right away, download the <i>Silk - Privacy Pass Client</i> browser extensions (<a href="https://addons.mozilla.org/en-US/firefox/addon/privacy-pass/">Firefox</a>, <a href="https://chromewebstore.google.com/detail/privacy-pass/ajhmfdgkijocedmfjonnpjfojldioehi">Chrome</a>, <a href="https://github.com/cloudflare/pp-browser-extension">GitHub</a>) and start browsing a web with fewer bot checks today.</p></div></section><section class="post-full-content flex flex-row flex-wrap mw7 center mb4"><div class="post-content lh-copy w-100 gray1 bt b--gray8 pt4">Cloudflare's connectivity cloud protects <a target='_blank' href='https://www.cloudflare.com/network-services/' rel='noreferrer'>entire corporate networks</a>, helps customers build <a target='_blank' href='https://workers.cloudflare.com/' rel='noreferrer'>Internet-scale applications efficiently</a>, accelerates any <a target='_blank' href='https://www.cloudflare.com/performance/accelerate-internet-applications/' rel='noreferrer'>website or Internet application</a>, <a target='_blank' href='https://www.cloudflare.com/ddos/' rel='noreferrer'>wards off DDoS attacks</a>, keeps <a target='_blank' href='https://www.cloudflare.com/application-security/' rel='noreferrer'>hackers at bay</a>, and can help you on <a target='_blank' href='https://www.cloudflare.com/products/zero-trust/' rel='noreferrer'>your journey to Zero Trust</a>.<br/><br/>Visit <a target='_blank' href='https://one.one.one.one/' rel='noreferrer'>1.1.1.1</a> from any device to get started with our free app that makes your Internet faster and safer.<br/><br/>To learn more about our mission to help build a better Internet, <a target='_blank' href='https://www.cloudflare.com/learning/what-is-cloudflare/' rel='noreferrer'>start here</a>. If you&apos;re looking for a new career direction, check out <a target='_blank' href="https://www.cloudflare.com/careers" rel='noreferrer'>our open positions</a>.</div></section><div class="pv2 ph0-l mw7 center" id="social-buttons"><div class="mt5-l mt2 mb4 f2 flex flex-row-ns flex-column flex-wrap"><a id="social-button-hn" title="Discuss on Hacker News" href="https://news.ycombinator.com/submitlink?u=https://blog.cloudflare.com/privacy-pass-standard" target="_blank" rel="noreferrer" class="mr2-ns mb0-l white link b pv3 ph3 mb3 " style="background-color:#0055DC"><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 512 512" class="mr2"><g><path d="M31,31v450h450V31H31z M270.1,287.6v94.9h-28.1v-94.9L165,143.5h31.9L256,254.3l59.1-110.8H347 C347,143.5,270.1,287.6,270.1,287.6z"></path></g></svg><span class="v-mid">Discuss on Hacker News</span></a></div></div><iframe sandbox="allow-scripts allow-popups allow-popups-to-escape-sandbox" title="cloudflare-tv-live-link" id="cloudflare-tv-embed" src="https://cloudflare.tv/embed/live.html" loading="lazy"></iframe><a href="/tag/research/" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1">Research</a><a href="/tag/privacy-pass/" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1">Privacy Pass</a><a href="/tag/firefox/" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1">Firefox</a><a href="/tag/chrome/" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1">Chrome</a><a href="/tag/privacy/" class="dib pl2 pr2 pt1 pb1 mb2 bg-gray8 no-underline blue3 f2 mr1">Privacy</a></article></main><div class="ph3 pv3"><div class=" flex flex-row flex-wrap mw7 center"><div class="w-100 bt b--gray8"><p class="black fw5 f4 mt4">Follow on X</p></div><div class="w-100 pb2"><span>Thibault Meunier</span><span class="ph1">|</span><a href="https://x.com/@thibmeu" class="no-underline">@thibmeu</a></div><div class="w-100 pb2"><span>Armando Faz-Hernández</span><span class="ph1">|</span><a href="https://x.com/@armfazh" class="no-underline">@armfazh</a></div><div class="w-100 pb2"><span>Cloudflare</span><span class="ph2">|</span><a href="https://x.com/@cloudflare" class="no-underline">@cloudflare</a></div></div></div><div data-testid="related-posts-section" class="pv4 ph3 ph0-l flex flex-row flex-wrap mw7 center"><div class="w-100 bt b--gray8"><p class="orange fw5 f4 mt4 ttu">Related posts</p></div><article data-testid="related-posts-article" class="w-100 w-100-m w-50-l ph3 mb4"><p data-testid="related-posts-article-date" class="f3 fw5 gray5" data-iso-date="2025-01-28T00:00+00:00">January 28, 2025 12:00 AM</p><a data-testid="related-posts-article-title" href="/cloudflare-cbpr-a-global-privacy-first/" class="no-underline gray1 f4 fw5"><h2 class="gray1 f4 fw5 mt2">Cloudflare meets new Global Cross-Border Privacy (CBPR) standards</h2></a><p data-testid="related-posts-article-excerpt" class="gray1 lh-copy">Cloudflare is the first organization globally to announce having been successfully audited against the ‘Global Cross-Border Privacy Rules’ system and ‘Global Privacy Recognition for Processors’.<!-- -->...</p><ul class="flex pl0 fw6 f2 mb3 flex flex-wrap"><span>By<!-- --> </span><li class="list flex items-center" style="white-space:nowrap"><div class="author-name-tooltip"><a href="/author/rory/" class="fw5 f2 black no-underline">Rory Malone</a><span class="fw5 f2 black no-underline">, </span></div></li><li class="list flex items-center" style="white-space:nowrap"><div class="author-name-tooltip"><a href="/author/emily-hancock/" class="fw5 f2 black no-underline">Emily Hancock</a></div></li></ul><div data-testid="related-posts-article-tags" class="flex flex-row flex-wrap"><span><a href="/tag/certification/" class="no-underline f1 fw2 blue3 underline-hover">Certification<!-- -->,</a> </span><span><a href="/tag/privacy/" class="no-underline f1 fw2 blue3 underline-hover">Privacy<!-- -->,</a> </span><span><a href="/tag/compliance/" class="no-underline f1 fw2 blue3 underline-hover">Compliance<!-- -->,</a> </span><span><a href="/tag/security/" class="no-underline f1 fw2 blue3 underline-hover">Security<!-- -->,</a> </span><span><a href="/tag/policy/" class="no-underline f1 fw2 blue3 underline-hover">Policy &amp; Legal</a> </span></div></article><article data-testid="related-posts-article" class="w-100 w-100-m w-50-l ph3 mb4"><p data-testid="related-posts-article-date" class="f3 fw5 gray5" data-iso-date="2024-12-26T14:00+00:00">December 26, 2024 2:00 PM</p><a data-testid="related-posts-article-title" href="/sometimes-i-cache/" class="no-underline gray1 f4 fw5"><h2 class="gray1 f4 fw5 mt2">Sometimes I cache: implementing lock-free probabilistic caching</h2></a><p data-testid="related-posts-article-excerpt" class="gray1 lh-copy">If you want to know what cache revalidation is, how it works, and why it can involve rolling a die, read on. This blog post presents a lock-free probabilistic approach to cache revalidation, along <!-- -->...</p><ul class="flex pl0 fw6 f2 mb3 flex flex-wrap"><span>By<!-- --> </span><li class="list flex items-center" style="white-space:nowrap"><div class="author-name-tooltip"><a href="/author/thibault/" class="fw5 f2 black no-underline">Thibault Meunier</a></div></li></ul><div data-testid="related-posts-article-tags" class="flex flex-row flex-wrap"><span><a href="/tag/research/" class="no-underline f1 fw2 blue3 underline-hover">Research<!-- -->,</a> </span><span><a href="/tag/cache/" class="no-underline f1 fw2 blue3 underline-hover">Cache<!-- -->,</a> </span><span><a href="/tag/workers/" class="no-underline f1 fw2 blue3 underline-hover">Cloudflare Workers<!-- -->,</a> </span><span><a href="/tag/developer-platform/" class="no-underline f1 fw2 blue3 underline-hover">Developer Platform</a> </span></div></article><article data-testid="related-posts-article" class="w-100 w-100-m w-50-l ph3 mb4"><p data-testid="related-posts-article-date" class="f3 fw5 gray5" data-iso-date="2024-11-08T14:00+00:00">November 08, 2024 2:00 PM</p><a data-testid="related-posts-article-title" href="/topaz-policy-engine-design/" class="no-underline gray1 f4 fw5"><h2 class="gray1 f4 fw5 mt2">How we prevent conflicts in authoritative DNS configuration using formal verification</h2></a><p data-testid="related-posts-article-excerpt" class="gray1 lh-copy">We describe how Cloudflare uses a custom Lisp-like programming language and formal verifier (written in Racket and Rosette) to prevent logical contradictions in our authoritative DNS nameserver’s behavior.<!-- -->...</p><ul class="flex pl0 fw6 f2 mb3 flex flex-wrap"><span>By<!-- --> </span><li class="list flex items-center" style="white-space:nowrap"><div class="author-name-tooltip"><a href="/author/james-larisch/" class="fw5 f2 black no-underline">James Larisch</a><span class="fw5 f2 black no-underline">, </span></div></li><li class="list flex items-center" style="white-space:nowrap"><div class="author-name-tooltip"><a href="/author/suleman/" class="fw5 f2 black no-underline">Suleman Ahmad</a><span class="fw5 f2 black no-underline">, </span></div></li><li class="list flex items-center" style="white-space:nowrap"><div class="author-name-tooltip"><a href="/author/marwan/" class="fw5 f2 black no-underline">Marwan Fayed</a></div></li></ul><div data-testid="related-posts-article-tags" class="flex flex-row flex-wrap"><span><a href="/tag/dns/" class="no-underline f1 fw2 blue3 underline-hover">DNS<!-- -->,</a> </span><span><a href="/tag/research/" class="no-underline f1 fw2 blue3 underline-hover">Research<!-- -->,</a> </span><span><a href="/tag/addressing/" class="no-underline f1 fw2 blue3 underline-hover">Addressing<!-- -->,</a> </span><span><a href="/tag/formal-methods/" class="no-underline f1 fw2 blue3 underline-hover">Formal Methods</a> </span></div></article><article data-testid="related-posts-article" class="w-100 w-100-m w-50-l ph3 mb4"><p data-testid="related-posts-article-date" class="f3 fw5 gray5" data-iso-date="2024-11-07T14:00+00:00">November 07, 2024 2:00 PM</p><a data-testid="related-posts-article-title" href="/another-look-at-pq-signatures/" class="no-underline gray1 f4 fw5"><h2 class="gray1 f4 fw5 mt2">A look at the latest post-quantum signature standardization candidates</h2></a><p data-testid="related-posts-article-excerpt" class="gray1 lh-copy">NIST has standardized four post-quantum signature schemes so far, and they’re not done yet: there are fourteen new candidates in the running for standardization.<!-- -->...</p><ul class="flex pl0 fw6 f2 mb3 flex flex-wrap"><span>By<!-- --> </span><li class="list flex items-center" style="white-space:nowrap"><div class="author-name-tooltip"><a href="/author/bas/" class="fw5 f2 black no-underline">Bas Westerbaan</a><span class="fw5 f2 black no-underline">, </span></div></li><li class="list flex items-center" style="white-space:nowrap"><div class="author-name-tooltip"><a href="/author/luke/" class="fw5 f2 black no-underline">Luke Valenta</a></div></li></ul><div data-testid="related-posts-article-tags" class="flex flex-row flex-wrap"><span><a href="/tag/post-quantum/" class="no-underline f1 fw2 blue3 underline-hover">Post-Quantum<!-- -->,</a> </span><span><a href="/tag/research/" class="no-underline f1 fw2 blue3 underline-hover">Research<!-- -->,</a> </span><span><a href="/tag/cryptography/" class="no-underline f1 fw2 blue3 underline-hover">Cryptography<!-- -->,</a> </span><span><a href="/tag/tls/" class="no-underline f1 fw2 blue3 underline-hover">TLS</a> </span></div></article></div><!--astro:end--></astro-island><footer class="pt4 pb4 pl1 pr1 main-footer"><div class="mw8 center dn db-l ph3"><div class="flex flex-row justify-between"><div class="main-footer__menu-group"><ul id="getting-started-menu" class="list pl0"><li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="getting-started-menu">Getting Started<i class="icon-caret-down"></i></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/plans/free/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="free-plans" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Free plans</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/enterprise/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="enterprise" class="f1 blue3 no-underline underline-hover" rel="noreferrer">For enterprises</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/plans/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="compare-plans" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Compare plans</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/about-your-website/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="get-a-recommendation" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Get a recommendation</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/plans/enterprise/demo/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="request-a-demo" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Request a demo</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/plans/enterprise/contact/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="contact-sales" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Contact Sales</a></li></ul></div><div class="main-footer__menu-group"><ul id="company-menu" class="list pl0"><li class="pt1 pb1 f1" data-submenu="company-menu">Resources<i class="icon-caret-down"></i></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/learning/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="learning-center" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Learning Center</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/analysts/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="analysts-report" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Analyst reports</a></li><li class="pt1 pb1"><a href="https://radar.cloudflare.com/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="overview" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Cloudflare Radar</a></li><li class="pt1 pb1"><a href="https://cloudflare.tv/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="tv" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Cloudflare TV</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/case-studies/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="case-studies" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Case Studies</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/resource-hub/?resourcetype=Webinar" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="webinars" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Webinars</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/resource-hub/?resourcetype=Whitepaper" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="white-papers" class="f1 blue3 no-underline underline-hover" rel="noreferrer">White Papers</a></li><li class="pt1 pb1"><a href="https://developers.cloudflare.com" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="developer-docs" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Developer docs</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/the-net/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="theNet" class="f1 blue3 no-underline underline-hover" rel="noreferrer">theNet</a></li></ul></div><div class="main-footer__menu-group"><ul id="sales-menu" class="list pl0"><li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="sales-menu">Solutions<i class="icon-caret-down"></i></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/connectivity-cloud/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="connectivity-cloud" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Connectivity cloud</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/zero-trust/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="zero-trust" class="f1 blue3 no-underline underline-hover" rel="noreferrer">SSE and SASE services</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/application-services/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="application-services" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Application services</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/network-services/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="network-services" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Network services</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/developer-platform/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="developer-services" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Developer services</a></li></ul></div><div class="main-footer__menu-group"><ul id="community-menu" class="list pl0"><li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="community-menu">Community<i class="icon-caret-down"></i></li><li class="pt1 pb1"><a href="https://community.cloudflare.com" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="community_hub" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Community Hub</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/galileo/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="galileo" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Project Galileo</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/athenian/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="athenian" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Athenian Project</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/campaigns/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="cloudflare-for-campaigns" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Cloudflare for Campaigns</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/partners/technology-partners/cidp/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="critical-infrastructure-defense-project" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Critical Infrastructure Defense Project</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/connect2024/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="connect-2024" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Connect 2024</a></li></ul></div><div class="main-footer__menu-group"><ul id="support-menu" class="list pl0"><li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="support-menu">Support<i class="icon-caret-down"></i></li><li class="pt1 pb1"><a href="https://support.cloudflare.com" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="help-center" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Help center</a></li><li class="pt1 pb1"><a href="https://www.cloudflarestatus.com" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="status" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Cloudflare Status</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/compliance/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="compliance" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Compliance</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/gdpr/introduction/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="gdpr" class="f1 blue3 no-underline underline-hover" rel="noreferrer">GDPR</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/trust-hub/abuse-approach/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="trust-and-safety" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Trust &amp; Safety</a></li></ul></div><div class="main-footer__menu-group"><ul id="company-menu" class="list pl0"><li class="pt1 pb1 f1 main-footer__menu-group__header js-toggle-footer-group" data-submenu="company-menu">Company<i class="icon-caret-down"></i></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/about-overview/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="overview" class="f1 blue3 no-underline underline-hover" rel="noreferrer">About Cloudflare</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/people/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="our_team" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Our team</a></li><li class="pt1 pb1"><a href="https://cloudflare.net/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="investor-relations" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Investor relations</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/press/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="press" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Press</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/careers/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="careers" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Careers</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/diversity-equity-and-inclusion/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="diversity-equity-inclusion" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Diversity, equity &amp; inclusion</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/impact/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="impact-ESG" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Impact/ESG</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/network/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="network_map" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Network Map</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/press-kit/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="press-kit" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Logos &amp; press kit</a></li><li class="pt1 pb1"><a href="https://www.cloudflare.com/partners/" target="_blank" data-tracking-category="footer" data-tracking-action="click" data-tracking-label="partners" class="f1 blue3 no-underline underline-hover" rel="noreferrer">Become a partner</a></li></ul></div></div></div><div class="mw8 center ph3"><div class="flex flex-row flex-wrap justify-center md:justify-between items-center pt4"><div class="flex flex-row space-x-4 items-start w-25-l pb4 pb0-l"><a target="_blank" rel="noreferrer" href="https://www.facebook.com/Cloudflare/" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/facebook.svg" alt="facebook"/></a><a target=" _blank" rel="noreferrer" href="https://x.com/Cloudflare" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/twitter.svg" alt="X"/></a><a target="_blank" rel="noreferrer" href="https://www.linkedin.com/company/cloudflare" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/linkedin.svg" alt="linkedin"/></a><a target="_blank" rel="noreferrer" href="https://www.youtube.com/cloudflare" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/youtube.svg" alt="youtube"/></a><a target="_blank" rel="noreferrer" href="https://www.instagram.com/cloudflare" class="w-8"><img class="w-8" src="https://www.cloudflare.com/img/footer/instagram.svg" alt="instagram"/></a></div><div class="w-70-l tr-l tl-ns"><div><span class="main-footer__copyright f1">© <!-- -->2025<!-- --> Cloudflare, Inc.<!-- --> </span><span class="main-footer__copyright f1">|</span><a href="https://www.cloudflare.com/privacypolicy/" target="_blank" class="main-footer__copyright f1 no-underline underline-hover" rel="noreferrer"> <!-- -->Privacy Policy<!-- --> </a><span class="main-footer__copyright f1">|</span><a href="https://www.cloudflare.com/website-terms/" target="_blank" class="main-footer__copyright f1 no-underline underline-hover" rel="noreferrer"> <!-- -->Terms of Use<!-- --> </a><span class="main-footer__copyright f1">|</span><a href="https://www.cloudflare.com/disclosure/" target="_blank" class="main-footer__copyright f1 no-underline underline-hover" rel="noreferrer"> <!-- -->Report Security Issues<!-- --> </a><span class="main-footer__copyright f1">|</span><img class="mw2 ph1" src="/images/privacy-options.svg" alt="Privacy Options"/><a href="#cookie-settings" id="ot-sdk-btn" class="ot-sdk-show-settings main-footer__copyright f1 no-underline underline-hover"><span class="brandGray5">Cookie Preferences</span> </a><span class="main-footer__copyright f1">|</span><a href="https://www.cloudflare.com/trademark/" target="_blank" class="main-footer__copyright f1 no-underline underline-hover" rel="noreferrer"> <!-- -->Trademark<!-- --> </a></div></div></div></div></footer></html>