
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Fri, 10 Apr 2026 00:54:04 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Anonymous credentials: rate-limiting bots and agents without compromising privacy]]></title>
            <link>https://blog.cloudflare.com/private-rate-limiting/</link>
            <pubDate>Thu, 30 Oct 2025 13:00:00 GMT</pubDate>
            <description><![CDATA[ As AI agents change how the Internet is used, they create a challenge for security. We explore how Anonymous Credentials can rate limit agent traffic and block abuse without tracking users or compromising their privacy. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>The way we interact with the Internet is changing. Not long ago, ordering a pizza meant visiting a website, clicking through menus, and entering your payment details. Soon, you might just <a href="https://www.cnet.com/tech/services-and-software/i-had-chatgpt-order-me-a-pizza-this-could-change-everything/"><u>ask your phone</u></a> to order a pizza that matches your preferences. A program on your device or on a remote server, which we call an <a href="https://developers.cloudflare.com/agents/concepts/what-are-agents/"><u>AI agent</u></a>, would visit the website and orchestrate the necessary steps on your behalf.</p><p>Of course, agents can do much more than order pizza. Soon we might use them to buy concert tickets, plan vacations, or even write, review, and merge pull requests. While some of these tasks will eventually run locally, for now, most are powered by massive AI models running in the biggest datacenters in the world. As agentic AI increases in popularity, we expect to see a large increase in traffic from these AI platforms and a corresponding drop in traffic from more conventional sources (like your phone).</p><p>This shift in traffic patterns has prompted us to assess how to keep our customers online and secure in the AI era. On one hand, the nature of requests are changing: Websites optimized for human visitors will have to cope with faster, and potentially greedier, agents. On the other hand, AI platforms may soon become a significant source of attacks, originating from malicious users of the platforms themselves.</p><p>Unfortunately, existing tools for managing such (mis)behavior are likely too coarse-grained to manage this transition. For example, <a href="https://blog.cloudflare.com/per-customer-bot-defenses/"><u>when Cloudflare detects that a request is part of a known attack pattern</u></a>, the best course of action often is to block all subsequent requests from the same source. When the source is an AI agent platform, this could mean inadvertently blocking all users of the same platform, even honest ones who just want to order pizza. We started addressing this problem <a href="https://blog.cloudflare.com/web-bot-auth/"><u>earlier this year</u></a>. But as agentic AI grows in popularity, we think the Internet will need more fine-grained mechanisms of managing agents without impacting honest users.</p><p>At the same time, we firmly believe that any such security mechanism must be designed with user privacy at its core. In this post, we'll describe how to use <b>anonymous credentials (AC)</b> to build these tools. Anonymous credentials help website operators to enforce a wide range of security policies, like rate-limiting users or blocking a specific malicious user, without ever having to identify any user or track them across requests.</p><p>Anonymous credentials are <a href="https://mailarchive.ietf.org/arch/msg/privacy-pass/--JXbGvkHnLq1iHQKJAnfn5eH9A/"><u>under development at IETF</u></a> in order to provide a standard that can work across websites, browsers, platforms. It's still in its early stages, but we believe this work will play a critical role in keeping the Internet secure and private in the AI era. We will be contributing to this process as we work towards real-world deployment. This is still early days. If you work in this space, we hope you will follow along and contribute as well.</p>
    <div>
      <h2>Let’s build a small agent</h2>
      <a href="#lets-build-a-small-agent">
        
      </a>
    </div>
    <p>To help us discuss how AI agents are affecting web servers, let’s build an agent ourselves. Our goal is to have an agent that can order a pizza from a nearby pizzeria. Without an agent, you would open your browser, figure out which pizzeria is nearby, view the menu and make selections, add any extras (double pepperoni), and proceed to checkout with your credit card. With an agent, it’s the same flow —except the agent is opening and orchestrating the browser on your behalf.</p><p>In the traditional flow, there’s a human all along the way, and each step has a clear intent: list all pizzerias within 3 Km of my current location; pick a pizza from the menu; enter my credit card; and so on. An agent, on the other hand, has to infer each of these actions from the prompt "order me a pizza."</p><p>In this section, we’ll build a simple program that takes a prompt and can make outgoing requests. Here’s an example of a simple <a href="https://workers.cloudflare.com/"><u>Worker</u></a> that takes a specific prompt and generates an answer accordingly. You can find the code on <a href="https://github.com/cloudflareresearch/mini-ai-agent-demo"><u>GitHub</u></a>:</p>
            <pre><code>export default {
   async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise&lt;Response&gt; {
       const out = await env.AI.run("@cf/meta/llama-3.1-8b-instruct-fp8", {
           prompt: `I'd like to order a pepperoni pizza with extra cheese.
                    Please deliver it to Cloudflare Austin office.
                    Price should not be more than $20.`,
       });


       return new Response(out.response);
   },
} satisfies ExportedHandler&lt;Env&gt;;</code></pre>
            <p>In this context, the LLM provides its best answer. It gives us a plan and instruction, but does not perform the action on our behalf. You and I are able to take a list of instructions and act upon it because we have agency and can affect the world. To allow our agent to interact with more of the world, we’re going to give it control over a web browser.</p><p>Cloudflare offers a <a href="https://developers.cloudflare.com/browser-rendering"><u>Browser Rendering</u></a> service that can bind directly into our Worker. Let’s do that. The following code uses <a href="https://www.stagehand.dev/"><u>Stagehand</u></a>, an automation framework that makes it simple to control the browser. We pass it an instance of Cloudflare remote browser, as well as a client for <a href="https://developers.cloudflare.com/workers-ai/"><u>Workers AI</u></a>.</p>
            <pre><code>import { Stagehand } from "@browserbasehq/stagehand";
import { endpointURLString } from "@cloudflare/playwright";
import { WorkersAIClient } from "./workersAIClient"; // wrapper to convert cloudflare AI


export default {
   async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise&lt;Response&gt; {
       const stagehand = new Stagehand({
           env: "LOCAL",
           localBrowserLaunchOptions: { cdpUrl: endpointURLString(env.BROWSER) },
           llmClient: new WorkersAIClient(env.AI),
           verbose: 1,
       });
       await stagehand.init();


       const page = stagehand.page;
       await page.goto("https://mini-ai-agent.cloudflareresearch.com/llm");


       const { extraction } = await page.extract("what are the pizza available on the menu?");
       return new Response(extraction);
   },
} satisfies ExportedHandler&lt;Env&gt;;</code></pre>
            <p>You can access that code for yourself on <a href="https://mini-ai-agent.cloudflareresearch.com/llm"><i><u>https://mini-ai-agent.cloudflareresearch.com/llm</u></i></a>. Here’s the response we got on October 10, 2025:</p>
            <pre><code>Margherita Classic: $12.99
Pepperoni Supreme: $14.99
Veggie Garden: $13.99
Meat Lovers: $16.99
Hawaiian Paradise: $15.49</code></pre>
            <p>Using the screenshot API of browser rendering, we can also inspect what the agent is doing. Here's how the browser renders the page in the example above:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6lXTePCTUORCyyOWNNcwZ8/5978abd1878f78107a2c9606c3a1ef51/image4.png" />
          </figure><p>Stagehand allows us to identify components on the page, such as <code>page.act(“Click on pepperoni pizza”)</code> and <code>page.act(“Click on Pay now”)</code>. This eases interaction between the developer and the browser.</p><p>To go further, and instruct the agent to perform the whole flow autonomously, we have to use the appropriately named <a href="https://docs.stagehand.dev/basics/agent"><u>agent</u></a> mode of Stagehand. This feature is not yet supported by Cloudflare Workers, but is provided below for completeness.</p>
            <pre><code>import { Stagehand } from "@browserbasehq/stagehand";
import { endpointURLString } from "@cloudflare/playwright";
import { WorkersAIClient } from "./workersAIClient";


export default {
   async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise&lt;Response&gt; {
       const stagehand = new Stagehand({
           env: "LOCAL",
           localBrowserLaunchOptions: { cdpUrl: endpointURLString(env.BROWSER) },
           llmClient: new WorkersAIClient(env.AI),
           verbose: 1,
       });
       await stagehand.init();
       
       const agent = stagehand.agent();
       const result = await agent.execute(`I'd like to order a pepperoni pizza with extra cheese.
                                           Please deliver it to Cloudflare Austin office.
                                           Price should not be more than $20.`);


       return new Response(result.message);
   },
} satisfies ExportedHandler&lt;Env&gt;;</code></pre>
            <p>We can see that instead of adding step-by-step instructions, the agent is provided control. To actually pay, it would need access to a payment method such as a <a href="https://en.wikipedia.org/wiki/Controlled_payment_number"><u>virtual credit card</u></a>.</p><p>The prompt had some subtlety in that we’ve scoped the location to Cloudflare’s Austin office. This is because while the agent responds to us, it needs to understand our context. In this case, the agent operates out of Cloudflare edge, a location remote to us. This implies we are unlikely to pick up a pizza from this <a href="https://www.cloudflare.com/learning/cdn/glossary/data-center/"><u>data center</u></a> if it was ever delivered.</p><p>The more capabilities we provide to the agent, the more it has the ability to create some disruption. Instead of someone having to make 5 clicks at a slow rate of 1 request per 10 seconds, they’d have a program running in a data center possibly making all 5 requests in a second.</p><p>This agent is simple, but now imagine many thousands of these — some benign, some not — running at datacenter speeds. This is the challenge origins will face.</p>
    <div>
      <h2>Protecting origins</h2>
      <a href="#protecting-origins">
        
      </a>
    </div>
    <p>For humans to interact with the online world, they need a web browser and some peripherals with which to direct the behavior of that browser. Agents are another way of directing a browser, so it may be tempting to think that not much is actually changing from the origin's point of view. Indeed, the most obvious change from the origin's point of view is merely where traffic comes from:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/304j2MNDUNwAaipqmH2Jbt/35beb792bda327a6cf0db3b642bbc4d6/unnamed-1.png" />
          </figure><p>The reason this change is significant has to do with the tools the server has to manage traffic. Websites generally try to be as permissive as possible, but they also need to manage finite resources (bandwidth, CPU, memory, storage, and so on). There are a few basic ways to do this:</p><ol><li><p><b>Global security policy</b>: A server may opt to slow down, CAPTCHA, or even temporarily block requests from all users. This policy may be applied to an entire site, a specific resource, or to requests classified as being part of a known or likely attack pattern. Such mechanisms may be deployed in reaction to an observed spike in traffic, as in a DDoS attack, or in anticipation of a spike in legitimate traffic, as in <a href="https://developers.cloudflare.com/waiting-room/"><u>Waiting Room</u></a>.</p></li><li><p><b>Incentives</b>: Servers sometimes try to incentivize users to use the site when more resources are available. For instance, a server price may be lower depending on the location or request time. This could be implemented with a <a href="https://developers.cloudflare.com/rules/snippets/when-to-use/"><u>Cloudflare Snippet</u></a>.</p></li></ol><p>While both tools can be effective, they also sometimes cause significant collateral damage. For example, while rate limiting a website's login endpoint <a href="https://developers.cloudflare.com/waf/rate-limiting-rules/best-practices/#protecting-against-credential-stuffing"><u>can help prevent credential stuffing attacks</u></a>, it also degrades the user experience for non-attackers. Before resorting to such measures, servers will first try to apply the security policy (whether a rate limit, a CAPTCHA, or an outright block) to individual users or groups of users.</p><p>However, in order to apply a security policy to individuals, the server needs some way of identifying them. Historically, this has been done via some combination of IP addresses, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent"><u>User-Agent</u></a>, an account tied to the user identity (if available), and other fingerprints. Like most cloud service providers, Cloudflare has a <a href="https://developers.cloudflare.com/waf/rate-limiting-rules/best-practices/"><u>dedicated offering</u></a> for per-user rate limits based on such heuristics.</p><p>Fingerprinting works for the most part. However, it's unequitably distributed. On mobile, users <a href="https://blog.cloudflare.com/eliminating-captchas-on-iphones-and-macs-using-new-standard/#captchas-dont-work-in-mobile-environments-pats-remove-the-need-for-them"><u>have an especially difficult time solving CAPTCHA</u></a>s, when using a VPN they’re <a href="https://arstechnica.com/tech-policy/2023/07/meta-blocking-vpn-access-to-threads-in-eu/"><u>more</u></a> <a href="https://help.netflix.com/en/node/277"><u>likely</u></a> <a href="https://www.theregister.com/2015/10/19/bbc_cuts_off_vpn_to_iplayer/"><u>to</u></a> <a href="https://torrentfreak.com/hulu-blocks-vpn-users-over-piracy-concerns-140425/"><u>be</u></a> <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/troubleshooting/common-issues/"><u>blocked</u></a>, and when using <a href="https://www.peteresnyder.com/static/papers/speedreader-www2019.pdf"><u>reading mode</u></a> they can mess up their fingerprint, preventing rendering of the page.</p><p>Likewise, agentic AI only exacerbates the limitations of fingerprinting. Not only will more traffic be concentrated on a smaller source IP range, the agents themselves will run the same software and hardware platform, making it harder to distinguish honest from malicious users.</p><p>Something that could help is <a href="https://blog.cloudflare.com/web-bot-auth/"><u>Web Bot Auth</u></a>, which would allow agents to identify to the origin which platform they're operated by. However, we wouldn't want to extend this mechanism — intended for identifying the platform itself — to identifying individual users of the platforms, as this would create an unacceptable privacy risk for these users.</p><p>We need some way of implementing security controls for individual users without identifying them. But how? The Privacy Pass protocol provides <a href="https://blog.cloudflare.com/eliminating-captchas-on-iphones-and-macs-using-new-standard/#captchas-dont-work-in-mobile-environments-pats-remove-the-need-for-them"><u>a partial solution</u></a>.</p>
    <div>
      <h2>Privacy Pass and its limitations</h2>
      <a href="#privacy-pass-and-its-limitations">
        
      </a>
    </div>
    <p>Today, one of the most prominent use cases for Privacy Pass is to <a href="https://www.cloudflare.com/learning/bots/what-is-rate-limiting/"><u>rate limit</u></a> requests from a user to an origin, as we have <a href="https://blog.cloudflare.com/privacy-pass-standard/#privacy-pass-protocol"><u>discussed before</u></a>. The protocol works roughly as follows. The client is <b>issued</b> a number of <b>tokens</b>. Each time it wants to make a request, it <b>redeems</b> one of its tokens to the origin; the origin allows the request through only if the token is <b>fresh</b>, i.e., has never been observed before by the origin.</p><p>In order to use Privacy Pass for per-user rate-limiting, it's necessary to limit the number of tokens issued to each user (e.g., 100 tokens per user per hour). To rate limit an AI agent, this role would be fulfilled by the AI platform. To obtain tokens, the user would log in with the platform, and said platform would allow the user to get tokens from the issuer. The AI platform fulfills the <b>attester</b> role in Privacy Pass <a href="https://datatracker.ietf.org/doc/html/rfc9576"><u>parlance</u></a>. The attester is the party guaranteeing the per-user property of the rate limit. The AI platform, as an attester, is incentivized to enforce this token distribution as it stakes its reputation: Should it allow for too many tokens to be issued, the issuer could distrust them.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/62Rz5eS1UMm2pKorpowEGg/4949220bdf2fa3c39ccfa17d4df70fff/token__1_.png" />
          </figure><p>The issuance and redemption protocols are designed to have two properties:</p><ul><li><p>Tokens are <b>unforgeable</b>: only the issuer can issue valid tokens.</p></li><li><p>Tokens are <b>unlinkable: </b>no party, including the issuer, attester, or origin, can tell which user a token was issued to. </p></li></ul><p>These properties can be achieved using a <a href="https://csrc.nist.gov/glossary/term/cryptographic_primitive"><u>cryptographic primitive</u></a> called a <a href="https://blog.cloudflare.com/privacy-pass-the-math/"><u>blind signature</u></a><b> </b>scheme. In a conventional signature scheme, the signer uses its <b>private key</b> to produce a signature for a message. Later on, a verifier can use the signer’s <b>public key</b> to verify the signature. Blind signature schemes work in the same way, except that the message to be signed is blinded such that the signer doesn't know the message it's signing. The client “blinds” the message to be signed and sends it to the server, which then computes a blinded signature over the blinded message. The client obtains the final signature by unblinding the signature.  </p><p>This is exactly how the standardised Privacy Pass issuance protocols are defined by <a href="https://www.rfc-editor.org/rfc/rfc9578"><u>RFC 9578</u></a>:</p><ul>
  <li>
    <strong>Issuance:</strong> The user generates a random message 
    <strong>$k$</strong> 
    which we call the 
    <strong>nullifier</strong>. Concretely, this is just a random, 32-byte string. It then blinds the nullifier and sends it to the issuer. The issuer replies with a blind signature. Finally, the user unblinds the signature to get 
    <strong>$\sigma$</strong>, 
    a signature for the nullifier 
    <strong>$k$</strong>. The token is the pair 
    <strong>$(k, \sigma)$</strong>.
  </li>
  <li>
    <strong>Redemption:</strong> When the user presents 
    <strong>$(k, \sigma)$</strong>, 
    the origin checks that 
    <strong>$\sigma$</strong> 
    is a valid signature for the nullifier 
    <strong>$k$</strong> 
    and that 
    <strong>$k$</strong> 
    is fresh. If both conditions hold, then it accepts and lets the request through.
  </li>
</ul><p>Blind signatures are simple, cheap, and perfectly suited for many applications. However, they have some limitations that make them unsuitable for our use case.</p><p>First, the communication cost of the issuance protocol is too high. For each token issued, the user sends a 256-byte, blinded nullifier and the issuer replies with a 256-byte blind signature (assuming RSA-2048 is used). That's 0.5KB of additional communication per request, or 500KB for every 1,000 requests. This is manageable as we’ve seen in a <a href="https://eprint.iacr.org/2023/414.pdf"><u>previous experiment</u></a> for Privacy Pass, but not ideal. Ideally, the bandwidth would be sublinear in the rate limit we want to enforce. An alternative to blind signatures with lower compute time are Oblivious Pseudorandom Functions (<a href="https://datatracker.ietf.org/doc/rfc9497/"><u>VOPRF</u></a>), but the bandwidth is still asymptotically linear. We’ve <a href="https://blog.cloudflare.com/privacy-pass-the-math/"><u>discussed them in the past</u></a>, as they served as the basis for early deployments of Privacy Pass.</p><p>Second, blind signatures can't be used to rate-limit on a per-origin basis. Ideally, when issuing $N$ tokens to the client, the client would be able to redeem at most $N$ tokens at any origin server that can verify the token's validity. However, the client can't safely redeem the same token at more than one server because it would be possible for the servers to link those redemptions to the same client. What's needed is some mechanism for what we'll call <b>late origin-binding</b>: transforming a token for redemption at a particular origin in a way that's unlinkable to other redemptions of the same token.</p><p>Third, once a token is issued, it can't be revoked: it remains valid as long as the issuer's public key is valid. This makes it impossible for an origin to block a specific user if it detects an attack, or if its tokens are compromised. The origin can block the offending request, but the user can continue to make requests using its remaining token budget.</p>
    <div>
      <h2>Anonymous credentials and the future of Privacy Pass</h2>
      <a href="#anonymous-credentials-and-the-future-of-privacy-pass">
        
      </a>
    </div>
    <p>As noted by <a href="https://dl.acm.org/doi/pdf/10.1145/4372.4373"><u>Chaum</u></a> in 1985, an <b>anonymous credential</b> system allows users to obtain a credential from an issuer, and later prove possession of this credential, in an unlinkable way, without revealing any additional information. Also, it is possible to demonstrate that some attributes are attached to the credential.</p><p>One way to think of an anonymous credential is as a kind of blind signature with some additional capabilities: late-binding (link a token to an origin after issuance), multi-show (generate multiple tokens from a single issuer response), and expiration distinct from key rotation (token validity decoupled of the issuer cryptographic key validity). In the redemption flow for Privacy Pass, the client presents the unblinded message and signature to the server. To accept the redemption, the server needs to verify the signature. In an AC system, the client only presents a <b>part of the message</b>. In order for the server to accept the request, the client needs to prove to the server that it knows a valid signature for the entire message without revealing the whole thing.</p><p>The flow we described above would therefore include this additional <b>presentation</b> step. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7pb3ZDoAHbDLEt0mxtf67T/b6be11710a7df7a4df7d1c89788285a7/credentials__2_.png" />
          </figure><p>Note that the tokens generated through blind signatures or VOPRFs can only be used once, so they can be regarded as <i>single-use tokens</i>. However, there exists a type of anonymous credentials that allows tokens to be used multiple times. For this to work, the issuer grants a <i>credential</i> to the user, who can later derive at most <i>N</i> many single-use tokens for redemption. Therefore, the user can send multiple requests, at the expense of a single issuance session.  </p><p>The table below describes how blind signatures and anonymous credentials provide features of interest to rate limiting.</p><table><tr><td><p><b>Feature</b></p></td><td><p><b>Blind Signature</b></p></td><td><p><b>Anonymous Credential</b></p></td></tr><tr><td><p><b>Issuing Cost</b></p></td><td><p>Linear complexity: issuing 10 signatures is 10x as expensive as issuing one signature</p></td><td><p>Sublinear complexity: signing 10 attributes is cheaper than 10 individual signatures</p></td></tr><tr><td><p><b>Proof Capability</b></p></td><td><p>Only prove that a message has been signed</p></td><td><p>Allow efficient proving of partial statements (i.e., attributes)</p></td></tr><tr><td><p><b>State Management</b></p></td><td><p>Stateless</p></td><td><p>Stateful</p></td></tr><tr><td><p><b>Attributes</b></p></td><td><p>No attributes</p></td><td><p>Public (e.g. expiry time) and private state</p></td></tr></table><p>
  Let's see how a simple anonymous credential scheme works. The client's message consists of the pair 
  <strong>$(k, C)$</strong>, 
  where 
  <strong>$k$</strong> 
  is a 
  <strong>nullifier</strong> and 
  <strong>$C$</strong> 
  is a 
  <strong>counter</strong> representing the remaining number of times the client can access a resource. The value of the counter is controlled by the server: when the client redeems its credential, it presents both the nullifier and the counter. In response, the server checks that signature of the message is valid and that the nullifier is fresh, as before. Additionally, the server also
</p><ol><li><p>checks that the counter is greater than zero; and</p></li><li><p>decrements the counter issuing a new credential for the updated counter and a fresh nullifier.</p></li></ol><p>A blind signature could be used to meet this functionality. However, whereas the nullifier can be blinded as before, it would be necessary to handle the counter in plaintext so that the server can check that the counter is valid (Step 1) and update it (Step 2). This creates an obvious privacy risk since the server, which is in control of the counter, can use it to link multiple presentations by the same client. For example, when you reach out to buy a pepperoni pizza, the origin could assign you a special counter value, which eases fingerprinting when you present it a second time. Fortunately, there exist anonymous credentials designed to close this kind of privacy gap.</p><p>The scheme above is a simplified version of Anonymous Credit Tokens (<a href="https://datatracker.ietf.org/doc/draft-schlesinger-cfrg-act/"><u>ACT</u></a>), one of the anonymous credential schemes being considered for adoption by the <a href="https://datatracker.ietf.org/wg/privacypass/about/"><u>Privacy Pass working group</u></a> at IETF. The key feature of ACT is its <b>statefulness</b>: upon successful redemption, the server re-issues a new credential with updated nullifier and counter values. This creates a feedback loop between the client and server that can be used to express a variety of security policies.</p><p>By design, it's not possible to present ACT credentials multiple times simultaneously: the first presentation must be completed so that the re-issued credential can be presented in the next request. <b>Parallelism </b>is the key feature of Anonymous Rate-limited Credential (<a href="https://datatracker.ietf.org/doc/html/draft-yun-cfrg-arc-00"><u>ARC</u></a>), another scheme under discussion at the Privacy Pass working group. ARCs can be presented across multiple requests in parallel up to the presentation limit determined during issuance.</p><p>Another important feature of ARC is its support for late origin-binding: when a client is issued an ARC with presentation limit $N$, it can safely use its credential to present up to $N$ times to any origin that can verify the credential.</p><p>These are just examples of relevant features of some anonymous credentials. Some applications may benefit from a subset of them; others may need additional features. Fortunately, both ACT and ARC can be constructed from a small set of cryptographic primitives that can be easily adapted for other purposes.</p>
    <div>
      <h2>Building blocks for anonymous credentials</h2>
      <a href="#building-blocks-for-anonymous-credentials">
        
      </a>
    </div>
    <p>ARC and ACT share two primitives in common: <a href="https://eprint.iacr.org/2013/516.pdf"><b><u>algebraic MACs</u></b></a>, which provide for limited computations on the blinded message; and <a href="https://en.wikipedia.org/wiki/Zero-knowledge_proof"><b><u>zero-knowledge proofs (ZKP)</u></b></a> for proving validity of the part of the message not revealed to the server. Let's take a closer look at each.</p>
    <div>
      <h3>Algebraic MACs</h3>
      <a href="#algebraic-macs">
        
      </a>
    </div>
    <p>A Message Authenticated Code (MAC) is a cryptographic tag used to verify a message's authenticity (that it comes from the claimed sender) and integrity (that it has not been altered). Algebraic MACs are built from mathematical structures like <a href="https://en.wikipedia.org/wiki/Group_action"><u>group actions</u></a>. The algebraic structure gives them some additional functionality, one of them being a <i>homomorphism</i> that we can blind easily to conceal the actual value of the MAC. Adding a random value on an algebraic MAC blinds the value.</p><p>Unlike blind signatures, both ACT and ARC are only <i>privately</i> verifiable, meaning the issuer and the origin must both have the issuer's private key. Taking Cloudflare as an example, this means that a credential issued by Cloudflare can only be redeemed by an origin behind Cloudflare. Publicly verifiable variants of both are possible, but at an additional cost.</p>
    <div>
      <h3>Zero-Knowledge Proofs for linear relations</h3>
      <a href="#zero-knowledge-proofs-for-linear-relations">
        
      </a>
    </div>
    <p>Zero knowledge proofs (ZKP) allow us to prove a statement is true without revealing the exact value that makes the statement true. The ZKP is constructed by a prover in such a way that it can only be generated by someone who actually possesses the secret. The verifier can then run a quick mathematical check on this proof. If the check passes, the verifier is convinced that the prover's initial statement is valid. The crucial property is that the proof itself is just data that confirms the statement; it contains no other information that could be used to reconstruct the original secret.</p><p>For ARC and ACT, we want to prove <i>linear relations</i> of secrets. In ARC, a user needs to prove that different tokens are linked to the same original secret credential. For example, a user can generate a proof showing that a <i>request token</i> was derived from a valid <i>issued credential</i>. The system can verify this proof to confirm the tokens are legitimately connected, all without ever learning the underlying secret credential that ties them together. This allows the system to validate user actions while guaranteeing their privacy.</p><p>Proving simple linear relations can be extended to prove a number of powerful statements, for example that a number is in range. For example, this is useful to prove that you have a positive balance on your account. To prove your balance is positive, you prove that you can encode your balance in binary. Let’s say you can at most have 1024 credits in your account. To prove your balance is non-zero when it is, for example, 12, you prove two things simultaneously: first, that you have a set of binary bits, in this case 12=(1100)<sub>2</sub>, and second, that a linear equation using these bits (8*1 + 4*1 + 2*0 + 1*0) correctly adds up to your total committed balance. This convinces the verifier that the number is validly constructed without them learning the exact value. This is how it works for powers of two, but it can <a href="https://github.com/chris-wood/draft-arc/pull/38"><u>easily be extended to arbitrary ranges</u></a>.</p><p>The mathematical structure of algebraic MACs allows easy blinding and evaluation. The structure also allows for an easy proof that a MAC has been evaluated with the private key without revealing the MAC. In addition, ARC could use ZKPs to prove that a nonce has not been spent before. In contrast, ACT uses ZKPs to prove we have enough of a balance left on our token. The balance is subtracted homomorphically using more group structure.</p>
    <div>
      <h2>How much does this all cost?</h2>
      <a href="#how-much-does-this-all-cost">
        
      </a>
    </div>
    <p>Anonymous credentials allow for more flexibility, and have the potential to reduce the communication cost, compared to blind signatures in certain applications. To identify such applications, we need to measure the concrete communication cost of these new protocols. In addition, we need to understand how their CPU usage compares to blind signatures and oblivious pseudorandom functions.</p><p>We measure the time that each participant spends at each stage of some AC schemes. We also report the size of messages transmitted across the network. For ARC, ACT, and VOPRF, we'll use <a href="https://doi.org/10.17487/RFC9496"><u>ristretto255</u></a> as the prime group and SHAKE128 for hashing. For Blind RSA, we'll use a 2048-bit modulus and SHA-384 for hashing.</p><p>Each algorithm was implemented in Go, on top of the <a href="https://github.com/cloudflare/circl"><u>CIRCL</u></a> library. We plan to open source the code once the specifications of ARC and ACT begin to stabilize.</p><p>Let’s take a look at the most widely used deployment in Privacy Pass: Blind RSA. Redemption time is low, and most of the cost lies with the server at issuance time. Communication cost is mostly constant and in the order of 256 bytes.</p>
<div><table><thead>
  <tr>
    <th><span>Blind RSA</span><br /><a href="https://doi.org/10.17487/RFC9474"><span>RFC9474</span></a><span>(RSA-2048+SHA384)</span></th>
    <th><span>1 Token</span></th>
  </tr>
  <tr>
    <th><span>Time</span></th>
    <th><span>Message Size</span></th>
  </tr></thead>
<tbody>
  <tr>
    <td><span>Issuance</span></td>
    <td><span>Client (Blind)</span></td>
    <td><span>63 µs</span></td>
    <td><span>256 B</span></td>
  </tr>
  <tr>
    <td><span>Server (Evaluate)</span></td>
    <td><span>2.69 ms</span></td>
    <td><span>256 B</span></td>
  </tr>
  <tr>
    <td><span>Client (Finalize)</span></td>
    <td><span>37 µs</span></td>
    <td><span>256 B</span></td>
  </tr>
  <tr>
    <td><span>Redemption</span></td>
    <td><span>Client</span></td>
    <td><span> –</span></td>
    <td><span>300 B</span></td>
  </tr>
  <tr>
    <td><span>Server</span></td>
    <td><span>37 µs</span></td>
    <td><span>–</span></td>
  </tr>
</tbody></table></div><p>When looking at VOPRF, verification time on the server is slightly higher than for Blind RSA, but communication cost and issuance are much faster. Evaluation time on the server is 10x faster for 1 token, and more than 25x faster when using <a href="https://datatracker.ietf.org/doc/draft-ietf-privacypass-batched-tokens/"><u>amortized token issuance</u></a>. Communication cost per token is also more appealing, with a message size at least 3x lower.</p>
<div><table><thead>
  <tr>
    <th><span>VOPRF</span><br /><a href="https://doi.org/10.17487/RFC9497"><span>RFC9497</span></a><span>(Ristretto255+SHA512)</span></th>
    <th><span>1 Token</span></th>
    <th><span>1000 Amortized issuances</span></th>
  </tr>
  <tr>
    <th><span>Time</span></th>
    <th><span>Message Size</span></th>
    <th><span>Time </span><br /><span>(per token)</span></th>
    <th><span>Message Size </span><br /><span>(per token)</span></th>
  </tr></thead>
<tbody>
  <tr>
    <td><span>Issuance</span></td>
    <td><span>Client (Blind)</span></td>
    <td><span>54 µs</span></td>
    <td><span>32 B</span></td>
    <td><span>54 µs</span></td>
    <td><span>32 B</span></td>
  </tr>
  <tr>
    <td><span>Server (Evaluate)</span></td>
    <td><span>260 µs</span></td>
    <td><span>96 B</span></td>
    <td><span>99 µs</span></td>
    <td><span>32.064 B</span></td>
  </tr>
  <tr>
    <td><span>Client (Finalize)</span></td>
    <td><span>376 µs</span></td>
    <td><span>64 B</span></td>
    <td><span>173 µs</span></td>
    <td><span>64 B</span></td>
  </tr>
  <tr>
    <td><span>Redemption</span></td>
    <td><span>Client</span></td>
    <td><span> –</span></td>
    <td><span>96 B</span></td>
    <td><span>–</span></td>
  </tr>
  <tr>
    <td><span>Server</span></td>
    <td><span>57 µs</span></td>
    <td><span>–</span></td>
  </tr>
</tbody></table></div><p>This makes VOPRF tokens appealing for applications requiring a lot of tokens that can accept a slightly higher redemption cost, and that don’t need public verifiability.</p><p>Now, let’s take a look at the figures for ARC and ACT anonymous credential schemes. For both schemes we measure the time to issue a credential that can be presented at most $N=1000$ times.</p>
<div><table><thead>
  <tr>
    <th><span>Issuance</span><br /><span>Credential Generation</span></th>
    <th><span>ARC</span></th>
    <th><span>ACT</span></th>
  </tr>
  <tr>
    <th><span>Time</span></th>
    <th><span>Message Size</span></th>
    <th><span>Time</span></th>
    <th><span>Message Size</span></th>
  </tr></thead>
<tbody>
  <tr>
    <td><span>Client (Request)</span></td>
    <td><span>323 µs</span></td>
    <td><span>224 B</span></td>
    <td><span>64 µs</span></td>
    <td><span>141 B</span></td>
  </tr>
  <tr>
    <td><span>Server (Response)</span></td>
    <td><span>1349 µs</span></td>
    <td><span>448 B</span></td>
    <td><span>251 µs</span></td>
    <td><span>176 B</span></td>
  </tr>
  <tr>
    <td><span>Client (Finalize)</span></td>
    <td><span>1293 µs</span></td>
    <td><span>128 B</span></td>
    <td><span>204 µs</span></td>
    <td><span>176 B</span></td>
  </tr>
  <tr>
    <td></td>
  </tr>
  <tr>
    <td><span>Redemption</span><br /><span>Credential Presentation</span></td>
    <td><span>ARC</span></td>
    <td><span>ACT</span></td>
  </tr>
  <tr>
    <td><span>Time</span></td>
    <td><span>Message Size</span></td>
    <td><span>Time</span></td>
    <td><span>Message Size</span></td>
  </tr>
  <tr>
    <td><span>Client (Present)</span></td>
    <td><span>735 µs</span></td>
    <td><span>288 B</span></td>
    <td><span> 1740 µs</span></td>
    <td><span>1867 B</span></td>
  </tr>
  <tr>
    <td><span>Server (Verify/Refund)</span></td>
    <td><span>740 µs</span></td>
    <td><span>–</span></td>
    <td><span>1785 µs</span></td>
    <td><span>141 B</span></td>
  </tr>
  <tr>
    <td><span>Client (Update)</span></td>
    <td><span>–</span></td>
    <td><span>–</span></td>
    <td><span>508 µs</span></td>
    <td><span>176 B</span></td>
  </tr>
</tbody></table></div><p>As we would hope, the communication cost and the server’s runtime is much lower than a batched issuance with either Blind RSA or VOPRF. For example, a VOPRF issuance of 1000 tokens takes 99 ms (99 µs per token) <i>vs</i> 1.35 ms for issuing one ARC credential that allows for 1000 presentations. This is about 70x faster. The trade-off is that presentation is more expensive, both for the client and server.</p><p>How about ACT? Like ARC, we would expect the communication cost of issuance grows much slower with respect to the credits issued. Our implementation bears this out. However, there are some interesting performance differences between ARC and ACT: issuance is much cheaper for ACT than it is for ARC, but redemption is the opposite.</p><p>What's going on? The answer has largely to do with what each party needs to prove with ZKPs at each step. For example, during ACT redemption, the client proves to the server (in zero-knowledge) that its counter $C$ is in the desired range, i.e., $0 \leq C \leq N$. The proof size is on the order of $\log_{2} N$, which accounts for the larger message size. In the current version, ARC redemption does not involve range proofs, but a range proof may be added in a <a href="http://mailarchive.ietf.org/arch/msg/privacy-pass/A3VHUdHqhslwBzYEQjcaXzYQAxQ/"><u>future version</u></a>. Meanwhile, the statements the client and server need to prove during ARC issuance are a bit more complicated than for ARC presentation, which accounts for the difference in runtime there.</p><p>The advantage of anonymous credentials, as discussed in the previous sections, is that issuance only has to be performed once. When a server evaluates its cost, it takes into account the cost of all issuances and the cost of all verifications. At present, only accounting for credentials costs, it’s cheaper for a server to issue and verify tokens than verify an anonymous credential presentation.</p><p>The advantage of multiple-use anonymous credentials is that instead of the issuer generating $N$ tokens, the bulk of computation is offloaded to the clients. This is more scoped. Late origin binding allows them to work for multiple origins/namespace, range proof to decorrelate expiration from key rotation, and refund to provide a dynamic rate limit. Their current applications are dictated by the limitation of single-use token based schemes, more than by the added efficiency they provide. This seems to be an exciting area to explore, and see if closing the gap is possible.</p>
    <div>
      <h2>Managing agents with anonymous credentials</h2>
      <a href="#managing-agents-with-anonymous-credentials">
        
      </a>
    </div>
    <p>Managing agents will likely require features from both ARC and ACT.</p><p>ARC already has much of the functionality we need: it supports rate limiting, is communication-efficient, and it supports late origin-binding. Its main downside is that, once an ARC credential is issued, it can't be revoked. A malicious user can always make up to <i>N</i> requests to any origin it wants.</p><p>We can allow for a limited form of revocation by pairing ARC with blind signatures (or VOPRF). Each presentation of the ARC credential is accompanied by a Privacy Pass token: upon successful presentation, the client is issued another Privacy Pass token it can use during the next presentation. To revoke a credential, the server would simply not re-issue the token:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6EiHmkbLef6kXsQU473fcX/d1d4018eaf2abd42b9690ae5d01494dc/image1.png" />
          </figure><p>This scheme is already quite useful. However, it has some important limitations:</p><ul><li><p>Parallel presentation across origins is not possible: the client must wait for the request to one origin to succeed before it can initiate a request to a second origin.</p></li><li><p>Revocation is <i>global</i> rather than per-origin, meaning the credential is not only revoked for the origin to whom it was presented, but for every origin it can be presented to. We suspect this will be undesirable in some cases. For example, an origin may want to revoke if a request violates its <code>robots.txt</code> policy; but the same request may have been accepted by other origins.  </p></li></ul><p>A more fundamental limitation of this design is that the decision to revoke can only be made on the basis of a single request — the one in which the credential was presented. It may be risky to decide to block a user on the basis of a single request; in practice, attack patterns may only emerge across many requests. ACT's statefulness enables at least a rudimentary form of this kind of defense. Consider the following scheme:</p><ul><li><p><b>Issuance: </b>The client is issued an ARC with presentation limit $N=1$.</p></li><li><p><b>Presentation:</b></p><ul><li><p>When the client presents its ARC credential to an origin for the first time, the server issues an ACT credential with a valid initial state.</p></li><li><p>When the client presents an ACT with valid state (e.g., credit counter greater than 0), the origin either:</p><ul><li><p>refuses to issue a new ACT, thereby revoking the credential. It would only do so if it had high confidence that the request was part of an attack; or</p></li><li><p>issues a new ACT with state updated to reduce the ACT credit by the amount of resources consumed while processing the request.</p></li></ul></li></ul></li></ul><p>Benign requests wouldn't change the state by much (if at all), but suspicious requests might impact the state in a way that gets the user closer to their rate limit much faster.</p>
    <div>
      <h2>Demo</h2>
      <a href="#demo">
        
      </a>
    </div>
    <p>To see how this idea works in practice, let's look at a working example that uses the <a href="https://developers.cloudflare.com/agents/model-context-protocol/"><u>Model Context Protocol</u></a>. The demo below is built using<a href="https://developers.cloudflare.com/agents/model-context-protocol/tools/"> <u>MCP Tools</u></a>. <a href="https://modelcontextprotocol.info/tools/"><u>Tools</u></a> are extensions the AI agent can call to extend its capabilities. They don't need to be integrated at release time within the MCP client. This provides a nice and easy prototyping avenue for anonymous credentials.</p><p>Tools are offered by the server via an MCP compatible interface. You can see details on how to build such MCP servers in a <a href="https://blog.cloudflare.com/remote-model-context-protocol-servers-mcp/"><u>previous blog</u></a>.</p><p>In our pizza context, this could look like a pizzeria that offers you a voucher. Each voucher gets you 3 pizza slices. Mocking a design, an integration within a chat application could look as follows:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5WD5MYoSMYGyRW2biwe6j4/bde101967276a72d48d9e494a23db5fa/image5.png" />
          </figure>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5SEqaVpwFxS1D21oyjjbN8/80dde2484f43c15e206ecfda991c286a/image9.png" />
          </figure><p>The first panel presents all tools exposed by the MCP server. The second one showcases an interaction performed by the agent calling these tools.</p><p>To look into how such a flow would be implemented, let’s write the MCP tools, offer them in an MCP server, and manually orchestrate the calls with the <a href="https://modelcontextprotocol.io/docs/tools/inspector"><u>MCP Inspector</u></a>.</p><p>The MCP server should provide two tools:</p><ul><li><p><code>act-issue </code>which issues an ACT credential valid for 3 requests. The code used here is an earlier version of the IETF draft which has some limitations.</p></li><li><p><code>act-redeem</code> makes a presentation of the local credential, and fetches our pizza menu.</p></li></ul><p>First, we run <code>act-issue</code>. At this stage, we could ask the agent to run an<a href="https://modelcontextprotocol.info/specification/draft/basic/authorization/"> <u>OAuth flow</u></a>, fetch an internal authentication endpoint, or to compute a proof of work.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6sLS7jMfTHPjVW5vMvsTWX/2d2b10fdb12c64f0e33fee89e09eab85/image10.png" />
          </figure><p>This gives us 3 credits to spend against an origin. Then, we run <code>act-redeem</code></p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1YTc0Wohrsqw3hizAOmJjU/4534cccbc490ad0aa09522a3875693af/image8.png" />
          </figure><p>Et voilà. If we run <code>act-redeem</code> once more, we see we have one fewer credit.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/a0zmBfl46hX33hWoXyGyX/86649d9f435562c95a85ec72fbf33022/image3.png" />
          </figure><p>You can test it yourself, here are the <a href="https://github.com/cloudflareresearch/anonymous-credentials-agent-demo"><u>source codes</u></a> available. The MCP server is written in<a href="https://github.com/modelcontextprotocol/rust-sdk/"> <u>Rust</u></a> to integrate with the <a href="https://github.com/SamuelSchlesinger/anonymous-credit-tokens/stargazers"><u>ACT rust</u></a> library. The <a href="https://act-client-demo.cloudflareresearch.com/"><u>browser-based client</u></a> works similarly, check it out.</p>
    <div>
      <h2>Moving further</h2>
      <a href="#moving-further">
        
      </a>
    </div>
    <p>In this post, we’ve presented a concrete approach to rate limit agent traffic. It is in full control of the client, and is built to protect the user's privacy. It uses emerging standards for anonymous credentials, integrates with MCP, and can be readily deployed on Cloudflare Workers.</p><p>We're on the right track, but there are still questions that remain. As we touched on before, a notable limitation of both ARC and ACT is that they are only <i>privately verifiable</i>. This means that the issuer and origin need to share a private key, for issuing and verifying the credential respectively. There are likely to be deployment scenarios for which this isn't possible. Fortunately, there may be a path forward for these cases using<i> pairing-</i>based cryptography, as in the <a href="https://datatracker.ietf.org/doc/draft-irtf-cfrg-bbs-signatures/"><u>BBS signature specification</u></a> making its way through IETF. We’re also exploring post-quantum implications in a <a href="https://blog.cloudflare.com/pq-anonymous-credentials/"><u>concurrent post</u></a>.</p><p>If you are an agent platform, an agent developer, or a browser, all our code is available on <a href="https://github.com/cloudflareresearch/anonymous-credentials-agent-demo"><u>GitHub</u></a> for you to experiment. Cloudflare is actively working on vetting this approach for real-world use cases.</p><p>The specification and discussion are happening within the IETF and W3C. This ensures the protocols are built in the open, and receive participation from experts. Improvements are still to be made to clarify the right performance-to-privacy tradeoff, or even the story to deploy on the open web.</p><p>If you’d like to help us, <a href="https://blog.cloudflare.com/cloudflare-1111-intern-program/"><u>we’re hiring 1,111 interns</u></a> over the course of next year, and have <a href="https://www.cloudflare.com/careers/early-talent/"><u>open positions</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Research]]></category>
            <category><![CDATA[Rate Limiting]]></category>
            <guid isPermaLink="false">1znqOjDHsm8kxWujPMhsgA</guid>
            <dc:creator>Thibault Meunier</dc:creator>
            <dc:creator>Christopher Patton</dc:creator>
            <dc:creator>Lena Heimberger</dc:creator>
            <dc:creator>Armando Faz-Hernández</dc:creator>
        </item>
        <item>
            <title><![CDATA[Policy, privacy and post-quantum: anonymous credentials for everyone]]></title>
            <link>https://blog.cloudflare.com/pq-anonymous-credentials/</link>
            <pubDate>Thu, 30 Oct 2025 13:00:00 GMT</pubDate>
            <description><![CDATA[ The world is adopting anonymous credentials for digital privacy, but these systems are vulnerable to quantum computers. This post explores the cryptographic challenges and promising research paths toward building new, quantum-resistant credentials from the ground up. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>The Internet is in the midst of one of the most complex transitions in its history: the migration to <a href="https://www.cloudflare.com/en-gb/pqc/"><u>post-quantum (PQ) cryptography.</u></a> Making a system safe against quantum attackers isn't just a matter of replacing elliptic curves and RSA with PQ alternatives, such as <a href="https://csrc.nist.gov/pubs/fips/203/final"><u>ML-KEM</u></a> and <a href="https://csrc.nist.gov/pubs/fips/204/final"><u>ML-DSA</u></a>. These algorithms have higher costs than their classical counterparts, making them unsuitable as drop-in replacements in many situations.</p><p>Nevertheless, we're <a href="https://blog.cloudflare.com/pq-2025/"><u>making steady progress</u></a> on the most important systems. As of this writing, <a href="https://radar.cloudflare.com/adoption-and-usage#post-quantum-encryption"><u>about 50%</u></a> of <a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/"><u>TLS connections</u></a> to Cloudflare's edge are safe against <a href="https://en.wikipedia.org/wiki/Harvest_now,_decrypt_later"><u>store-now/harvest-later attacks</u></a>. Quantum safe authentication is further out, as it will require more significant changes to how certificates work. Nevertheless, this year we've <a href="https://blog.cloudflare.com/bootstrap-mtc/"><u>taken a major step</u></a> towards making TLS deployable at scale with PQ certificates.</p><p>That said, TLS is only the lowest hanging fruit. There are <a href="https://github.com/fancy-cryptography/fancy-cryptography"><u>many more ways</u></a> we have come to rely on cryptography than key exchange and authentication and which aren’t as easy to migrate. In this blog post, we'll take a look at <b>Anonymous Credentials (ACs)</b>.</p><p>ACs solve a common privacy dilemma: how to prove a specific fact (for example that one has had a valid driver’s license for more than three years) without over-sharing personal information (like the place of birth)? Such problems are fundamental to a number of use cases, and ACs may provide the foundation we need to make these applications as private as possible.</p><p>Just like for TLS, the central question for ACs is whether there are drop-in, PQ replacements for its classical primitives that will work at the scale required, or will it be necessary to re-engineer the application to mitigate the cost of PQ.</p><p>We'll take a stab at answering this question in this post. We'll focus primarily on an emerging use case for ACs described in a <a href="https://blog.cloudflare.com/private-rate-limiting/"><u>concurrent post</u></a>: rate-limiting requests from agentic AI platforms and users. This demanding, high-scale use case is the perfect lens through which to evaluate the practical readiness of today's post-quantum research. We'll use it as our guiding problem to measure each cryptographic approach.</p><p>We'll first explore the current landscape of classical AC adoption across the tech industry and the public sector. Then, we’ll discuss what cryptographic researchers are currently looking into on the post-quantum side. Finally, we’ll take a look at what it'll take to bridge the gap between theory and real-world applications.</p><p>While anonymous credentials are only seeing their first real-world deployments in recent years, it is critical to start thinking about the post-quantum challenge concurrently. This isn’t a theoretical, too-soon problem given the store-now decrypt-later threat. If we wait for mass adoption before solving post-quantum anonymous credentials, ACs risk being dead on arrival. Fortunately, our survey of the state of the art shows the field is close to a practical solution. Let’s start by reviewing real-world use-cases of ACs. </p>
    <div>
      <h2>Real world (classical) anonymous credentials</h2>
      <a href="#real-world-classical-anonymous-credentials">
        
      </a>
    </div>
    <p>In 2026, the European Union is <a href="https://eur-lex.europa.eu/eli/reg/2024/1183/oj"><u>set to launch its digital identity wallet</u></a>, a system that will allow EU citizens, residents and businesses to digitally attest to their personal attributes. This will enable them, for example, to display their driver’s license on their phone or <a href="https://educatedguesswork.org/posts/age-verification-id/"><u>perform age</u></a> <a href="https://soatok.blog/2025/07/31/age-verification-doesnt-need-to-be-a-privacy-footgun/"><u>verification</u></a>. Cloudflare's use cases for ACs are a bit different and revolve around keeping our customers secure by, for example, rate limiting bots and humans as we <a href="https://blog.cloudflare.com/privacy-pass-standard/"><u>currently do with Privacy Pass</u></a>. The EU wallet is a massive undertaking in identity provisioning, and our work operates at a massive scale of traffic processing. Both initiatives are working to solve a shared fundamental problem: allowing an entity to prove a specific attribute about themselves without compromising their privacy by revealing more than they have to.</p><p>The EU's goal is a fully mobile, secure, and user-friendly digital ID. The current technical plan is ambitious, as laid out in the <a href="https://ec.europa.eu/digital-building-blocks/sites/spaces/EUDIGITALIDENTITYWALLET/pages/900014854/Version+2.0+of+the+Architecture+and+Reference+Framework+now+available"><u>Architecture Reference Framework (ARF)</u></a>. It defines the key privacy goals of unlinkability to guarantee that if a user presents attributes multiple times, the recipients cannot link these separate presentations to conclude that they concern the same user. However, currently proposed solutions fail to achieve this. The framework correctly identifies the core problem: attestations contain <i>unique, fixed elements such as hash values, […], public keys, and signatures</i> that colluding entities could store and compare to track individuals.</p><p>In its present form, the ARF's recommendation to mitigate cross-session linkability is <i>limited-time attestations</i>. The framework acknowledges in the text that this would <i>only partially mitigate Relying Party linkability</i>. An alternative proposal that would mitigate linkability risks are single-use credentials. They are not considered at the moment due to <i>complexity and management overhead</i>. The framework therefore leans on <i>organisational and enforcement measure</i>s to deter collusion instead of providing a stronger guarantee backed by cryptography.</p><p>This reliance on trust assumptions could become problematic, especially in the sensitive context of digital identity. When asked for feedback, c<a href="https://github.com/eu-digital-identity-wallet/eudi-doc-architecture-and-reference-framework/issues/200"><u>ryptographic researchers agree</u></a> that the proper solution would be to adopt anonymous credentials. However, this solution presents a long-term challenge. Well-studied methods for anonymous credentials, such as those based on <a href="https://datatracker.ietf.org/doc/draft-irtf-cfrg-bbs-signatures/"><u>BBS signatures</u></a>, are vulnerable to quantum computers. While some <a href="https://datatracker.ietf.org/doc/rfc9474/"><u>anonymous</u></a> <a href="https://datatracker.ietf.org/doc/draft-schlesinger-cfrg-act/"><u>schemes</u></a> are PQ-unlinkable, meaning that user privacy is preserved even when cryptographically relevant quantum computers exist, new credentials could be forged. This may be an attractive target for, say, a nation state actor.</p><p>New cryptography also faces deployment challenges: in the EU, only approved cryptographic primitives, as listed in the <a href="https://www.sogis.eu/documents/cc/crypto/SOGIS-Agreed-Cryptographic-Mechanisms-1.3.pdf"><u>SOG-IS catalogue,</u></a> can be used. At the time of writing, this catalogue is limited to established algorithms such as RSA or ECDSA. But when it comes to post-quantum cryptography, SOG-IS is <a href="https://www.sogis.eu/documents/cc/crypto/SOGIS-Agreed-Cryptographic-Mechanisms-1.3.pdf"><u>leaving the problem wide open</u></a>.</p><p>The wallet's first deployment will not be quantum-secure. However, with the transition to post-quantum algorithms being ahead of us, as soon as 2030 for high-risk use cases per <a href="https://digital-strategy.ec.europa.eu/en/library/coordinated-implementation-roadmap-transition-post-quantum-cryptography"><u>the EU roadma</u></a>p, research in a post-quantum compatible alternative for anonymous credentials is critical. This will encompass<b> </b><i>standardizing more cryptography.</i></p><p>Regarding existing large scale deployments, the US has allowed digital ID on smartphones since 2024. They <a href="https://www.tsa.gov/digital-id/participating-states"><u>can be used at TSA checkpoints</u></a> for instance. The <a href="https://www.dhs.gov/science-and-technology/privacy-preserving-digital-credential-wallets-verifiers"><u>Department of Homeland Security lists funding for six privacy-preserving digital credential wallets and verifiers on their website.</u></a> This early exploration and engagement is a positive sign, and highlights the need to plan for privacy-preserving presentations. </p><p>Finally, ongoing efforts at the Internet Engineering Task Force (IETF)<b> </b>aim<b> </b>to build a more private Internet by standardizing advanced cryptographic techniques. Active individual drafts (i.e., not yet adopted by a working group), such as <a href="https://datatracker.ietf.org/doc/draft-google-cfrg-libzk/"><u>Longfellow</u></a> and Anonymous Credit Tokens (<a href="https://datatracker.ietf.org/doc/draft-schlesinger-cfrg-act/"><u>ACT</u></a>), and adopted drafts like Anonymous Rate-limited Credentials (<a href="https://datatracker.ietf.org/doc/draft-yun-privacypass-crypto-arc/"><u>ARC</u></a>), propose more flexible multi-show anonymous credentials that incorporate developments over the last several years. At IETF 117 in 2023, <a href="https://www.irtf.org/anrw/2023/slides-117-anrw-sessc-not-so-low-hanging-fruit-security-and-privacy-research-opportunities-for-ietf-protocols-00.pdf"><u>post-quantum anonymous credentials and deployable generic anonymous credentials were presented as a research opportunity</u></a>. Check out our <a href="https://blog.cloudflare.com/private-rate-limiting/"><u>post on rate limiting agents</u></a> for details.</p><p>Before we get into the state-of-the-art for PQ, allow us to try to crystalize a set of requirements for real world applications.</p>
    <div>
      <h3>Requirements</h3>
      <a href="#requirements">
        
      </a>
    </div>
    <p>Given the diversity of use cases, adoption of ACs will be made easier by the fact that they can be built from a handful of powerful primitives. (More on this in our <a href="https://blog.cloudflare.com/private-rate-limiting/"><u>concurrent post</u></a>.) As we'll see in the next section, we don't yet have drop-in, PQ alternatives for these kinds of primitives. The "building blocks" of PQ ACs are likely to look quite different, and we're going to know something about what we're building towards.</p><p>For our purposes, we can think of an anonymous credential as a kind of fancy <a href="https://en.wikipedia.org/wiki/Blind_signature"><b><u>blind signature</u></b></a>. What's that you ask? A blind signature scheme has two phases: <b>issuance</b>, in which the server signs a message chosen by the client; and <b>presentation</b>, in which the client reveals the message and the signature to the server. The scheme should be <b>unlinkable</b> in the sense that the server can't link any message and signature to the run of the issuance protocol in which it was produced. It should also be <b>unforgeable</b> in the sense that no client can produce a valid signature without interacting with the server.</p><p>The key difference between ACs and blind signatures is that, during presentation of an AC, the client only presents <i>part of the message</i> in plaintext; the rest of the message is kept secret. Typically, the message has three components:</p><ol><li><p>Private <b>state</b>, such as a counter that, for example, keeps track of the number of times the credential was presented. The client would prove to the server that the state is "valid", for example, a counter with value $0 \leq C \leq N$, without revealing $C$. In many situations, it's desirable to allow the server to update this state upon successful presentation, for example, by decrementing the counter. In the context of rate limiting, this is the number of how many requests are left for a credential.</p></li><li><p>A random value called the <b>nullifier</b> that is revealed to the server during presentation. In rate-limiting, the nullifier prevents a user from spending a credential with a given state more than once.</p></li><li><p>Public <b>attributes</b> known to both the client and server that bind the AC to some application context. For example, this might represent the window of time in which the credential is valid (without revealing the exact time it was issued).</p></li></ol><p>Such ACs are well-suited for rate limiting requests made by the client. Here the idea is to prevent the client from making more than some maximum number of requests during the credential's lifetime. For example, if the presentation limit is 1,000 and the validity window is one hour, then the clients can make up to 0.27 requests/second on average before it gets throttled.</p><p>It's usually desirable to enforce rate limits on a <b>per-origin</b> basis. This means that if the presentation limit is 1,000, then the client can make at most 1,000 requests to any website that can verify the credential. Moreover, it can do so safely, i.e., without breaking unlinkability across these sites.</p><p>The current generation of ACs being considered for standardization at IETF are only <b>privately verifiable,</b> meaning the server issuing the credential (the <b>issuer</b>) must share a private key with the server verifying the credential (the <b>origin</b>). This will be sufficient for some deployment scenarios, but many will require <b>public verifiability</b>, where the origin only needs the issuer's public key. This is possible with BBS-based credentials, for example.</p><p>Finally, let us say a few words about round complexity. An AC is <b>round optimal</b> if issuance and presentation both complete in a single HTTP request and response. In our survey of PQ ACs, we found a number of papers that discovered neat tricks that reduce bandwidth (the total number of bits transferred between the client and server) at the cost of additional rounds. However, for use cases like ours, <b>round optimality</b> is an absolute necessity, especially for presentation. Not only do multiple rounds have a high impact on latency, they also make the implementation far more complex.</p><p>Within these constraints, our goal is to develop PQ ACs that have as low communication cost (i.e., bandwidth consumption) and runtime as possible in the context of rate-limiting.</p>
    <div>
      <h2>"Ideal world" (PQ) anonymous credentials</h2>
      <a href="#ideal-world-pq-anonymous-credentials">
        
      </a>
    </div>
    <p>The academic community has produced a number of promising post-quantum ACs. In our survey of the state of the art, we evaluated several leading schemes, scoring them on their underlying primitives and performance to determine which are truly ready for the Internet. To understand the challenges, it is essential to first grasp the cryptographic building blocks used in ACs today. We’ll now discuss some of the core concepts that frequently appear in the field.</p>
    <div>
      <h3>Relevant cryptographic paradigms</h3>
      <a href="#relevant-cryptographic-paradigms">
        
      </a>
    </div>
    
    <div>
      <h4>Zero-knowledge proofs</h4>
      <a href="#zero-knowledge-proofs">
        
      </a>
    </div>
    <p>Zero-knowledge proofs (ZKPs) are a cryptographic protocol that allows a <i>prover</i> to convince a <i>verifier</i> that a statement is true without revealing the secret information, or <i>witness</i>. ZKPs play a central role in ACs: they allow proving statements of the secret part of the credential's state without revealing the state itself. This is achieved by transforming the statement into a mathematical representation, such as a set of polynomial equations over a finite field. The prover then generates a proof by performing complex operations on this representation, which can only be completed correctly if they possess the valid witness.</p><p>General-purpose ZKP systems, like <a href="https://eprint.iacr.org/2018/046"><u>Scalable Transparent Arguments of Knowledge (STARKs)</u></a>, can prove the integrity of <i>any</i> computation up to a certain size. In a STARK-based system, the computational trace is represented as a <i>set of polynomials</i>. The prover then constructs a proof by evaluating these polynomials and committing to them using cryptographic hash functions. The verifier can then perform a quick probabilistic check on this proof to confirm that the original computation was executed correctly. Since the proof itself is just a collection of hashes and sampled polynomial values, it is secure against quantum computers, providing a statistically sound guarantee that the claimed result is valid.</p>
    <div>
      <h4>Cut-and-Choose</h4>
      <a href="#cut-and-choose">
        
      </a>
    </div>
    <p>Cut-and-choose is a cryptographic technique designed to ensure a prover’s honest behaviour by having a verifier check a random subset of their work. The prover first commits to multiple instances of a computation, after which the verifier randomly chooses a portion to be <i>cut open</i> by revealing the underlying secrets for inspection. If this revealed subset is correct, the verifier gains high statistical confidence that the remaining, un-opened instances are also correct.</p><p>This technique is important because while it is a generic tool used to build protocols secure against malicious adversaries, it also serves as a crucial case study. Its security is not trivial; for example, practical attacks on cut-and-choose schemes built with (post-quantum) homomorphic encryption have succeeded by <a href="https://eprint.iacr.org/2025/1890.pdf"><u>attacking the algebraic structure of the encoding</u></a>, not the encryption itself. This highlights that even generic constructions must be carefully analyzed in their specific implementation to prevent subtle vulnerabilities and information leaks.</p>
    <div>
      <h4>Sigma Protocols</h4>
      <a href="#sigma-protocols">
        
      </a>
    </div>
    <p><a href="https://datatracker.ietf.org/doc/draft-irtf-cfrg-sigma-protocols/01/"><u>Sigma protocols</u></a> follow a more structured approach that does not require us to throw away any computations. The <a href="https://pages.cs.wisc.edu/~mkowalcz/628.pdf"><u>three-move protocol</u></a> starts with a <i>commitment</i> phase where the prover generates some randomness<i>,</i> which is added to the input to generate the commitment, and sends the commitment to the verifier. Then, the verifier <i>challenges </i>the prover with an unpredictable challenge. To finish the proof, the prover provides a <i>response</i> in which they combine the initial randomness with the verifier’s challenge in a way that is only possible if the secret value, such as the solution to a discrete logarithm problem, is known.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ihEZ5KhWBQ0PZF5pTc0Bi/e35de03a89af0c2254bcc114041f6904/image4.png" />
          </figure><p><sup>Depiction of a Sigma protocol flow, where the prover commits to their witness $w$, the verifier challenges the prover to prove knowledge about $w$, and the prover responds with a mathematical statement that the verifier can either accept or reject.</sup></p><p>In practice, the prover and verifier don't run this interactive protocol. Instead, they make it non-interactive using a technique known as the <a href="https://link.springer.com/content/pdf/10.1007/3-540-47721-7_12.pdf"><u>Fiat-Shamir transformation</u></a>. The idea is that the prover generates the challenge <i>itself</i>, by deriving it from its own commitment. It may sound a bit odd, but it works quite well. In fact, it's the basis of signatures like ECDSA and even PQ signatures like ML-DSA.</p>
    <div>
      <h4>MPC in the head</h4>
      <a href="#mpc-in-the-head">
        
      </a>
    </div>
    <p>Multi-party computation (MPC) is a cryptographic tool that allows multiple parties to jointly compute a function over their inputs without revealing their individual inputs to the other parties. <a href="https://web.cs.ucla.edu/~rafail/PUBLIC/77.pdf"><u>MPC in the Head</u></a> (MPCitH) is a technique to generate zero-knowledge proofs by simulating a multi-party protocol <i>in the head</i> of the prover.</p><p>The prover simulates the state and communication for each virtual party, commits to these simulations, and shows the commitments to the verifier. The verifier then challenges the prover to open a subset of these virtual parties. Since MPC protocols are secure even if a minority of parties are dishonest, revealing this subset doesn't leak the secret, yet it convinces the verifier that the overall computation was correct. </p><p>This paradigm is particularly useful to us because it's a flexible way to build post-quantum secure ZKPs. MPCitH constructions build their security from symmetric-key primitives (like hash functions). This approach is also transparent, requiring no trusted setup. While STARKs share these post-quantum and transparent properties, MPCitH often offers faster prover times for many computations. Its primary trade-off, however, is that its proofs scale linearly with the size of the circuit to prove, while STARKs are succinct, meaning their proof size grows much slower.</p>
    <div>
      <h4>Rejection sampling</h4>
      <a href="#rejection-sampling">
        
      </a>
    </div>
    <p>When a randomness source is biased or outputs numbers outside the desired range, rejection sampling can correct the distribution. For example, imagine you need a random number between 1 and 10, but your computer only gives you random numbers between 0 and 255. (Indeed, this is the case!) The rejection sampling algorithm calls the RNG until it outputs a number below 11 and above 0: </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/ogslPSn4DJYx3R5jGZ3mi/7ab640864dc26d6e1e2eb53c25f628ea/image6.png" />
          </figure><p>Calling the generator over and over again may seem a bit wasteful. An efficient implementation can be realized with an eXtendable Output Function (XOF). A XOF takes an input, for example a seed, and computes an arbitrarily-long output. An example is the SHAKE family (part of the <a href="https://csrc.nist.gov/pubs/fips/202/final"><u>SHA3 standard</u></a>), and the recently proposed round-reduced version of SHAKE called <a href="https://datatracker.ietf.org/doc/rfc9861/"><u>TurboSHAKE</u></a>.</p><p>Let’s imagine you want to have three numbers between 1 and 10. Instead of calling the XOF over and over, you can also ask the XOF for several bytes of output. Since each byte has a probability of 3.52% to be in range, asking the XOF for 174 bytes is enough to have a greater than 99% chance of finding at least three usable numbers. In fact, we can be even smarter than this: 10 fits in four bits, so we can split the output bytes into lower and higher <a href="https://en.wikipedia.org/wiki/Nibble"><u>nibbles</u></a>. The probability of a nibble being in the desired range is now 56.4%:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4W98tjgA7gIkaM7A5LBMyi/7b12bbfd22e53b84439a7c9e690605d9/image2.png" />
          </figure><p><sup>Rejection sampling by batching queries. </sup></p><p>Rejection sampling is a part of many cryptographic primitives, including many we'll discuss in the schemes we look at below.</p>
    <div>
      <h3>Building post-quantum ACs</h3>
      <a href="#building-post-quantum-acs">
        
      </a>
    </div>
    <p>Classical anonymous credentials (ACs), such as ARC and ACT, are built from algebraic groups- specifically, elliptic curves, which are very efficient. Their security relies on the assumption that certain mathematical problems over these groups are computationally hard. The premise of post-quantum cryptography, however, is that quantum computers can solve these supposedly hard problems. The most intuitive solution is to replace elliptic curves with a post-quantum alternative. In fact, cryptographers have been working on a replacement for a number of years: <a href="https://eprint.iacr.org/2018/383"><u>CSIDH</u></a>. </p><p>This raises the key question: can we simply adapt a scheme like ARC by replacing its elliptic curves with CSIDH? The short answer is <b>no</b>, due to a critical roadblock in constructing the necessary zero-knowledge proofs. While we can, in theory, <a href="https://eprint.iacr.org/2023/1614"><u>build the required Sigma protocols or MPC-in-the-Head (MPCitH) proofs from CSIDH</u></a>, they have a prerequisite that makes them unusable in practice: they require a <b>trusted setup</b> to ensure the prover cannot cheat. This requirement is a non-starter, as <a href="https://eprint.iacr.org/2022/518"><u>no algorithm for performing a trusted setup in CSIDH exists</u></a>. The trusted setup for sigma protocols can be replaced by a combination of <a href="https://eprint.iacr.org/2016/505"><u>generic techniques from multi-party computation</u></a> and cut-and-choose protocols, but that adds significant computation cost to the already computationally expensive isogeny operations.</p><p>This specific difficulty highlights a more general principle. The high efficiency of classical credentials like ARC is deeply tied to the rich algebraic structure of elliptic curves. Swapping this component for a post-quantum alternative, or moving to generic constructions, fundamentally alters the design and its trade-offs. We must therefore accept that post-quantum anonymous credentials cannot be a simple "lift-and-shift" of today's schemes. They will require new designs built from different cryptographic primitives, such as lattices or hash functions.</p>
    <div>
      <h3>Prefabricated schemes from generic approaches</h3>
      <a href="#prefabricated-schemes-from-generic-approaches">
        
      </a>
    </div>
    <p>At Cloudflare, we explored a <a href="https://eprint.iacr.org/2023/414"><u>post-quantum privacy pass construction in 2023</u></a> that closely resembles the functionality needed for anonymous credentials. The main result is a generic construction that composes separate, quantum-secure building blocks: a digital signature scheme and a general-purpose ZKP system:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4dpmFzSv7HG5JHEEqu7D9o/ea1f02c37c0e36dc0972dfd1044fa9a3/image8.png" />
          </figure><p>The figure shows a cryptographic protocol divided into two main phases: (1.) Issuance: The user commits to a message (without revealing it) and sends the commitment to the server. The server signs the commitment and returns this signed commitment, which serves as a token. The user verifies the server's signature. (2.) Redemption: To use the token, the user presents it and constructs a proof. This proof demonstrates they have a valid signature on the commitment and opens the commitment to reveal the original message. If the server validates the proof, the user and server continue (e.g., to access a rate-limited origin).</p><p>The main appeal of this modular design is its flexibility. The experimental <a href="https://github.com/guruvamsi-policharla/zkdilithium"><u>implementation</u></a> uses a modified version of the signature ML-DSA signatures and STARKs, but the components can be easily swapped out. The design provides strong, composable security guarantees derived directly from the underlying parts. A significant speedup for the construction came from replacing the hash function SHA3 in ML-DSA with the zero-knowledge friendly <a href="https://eprint.iacr.org/2019/458"><u>Poseidon</u></a>.</p><p>However, the modularity of our post-quantum Privacy Pass construction <a href="https://zkdilithium.cloudflareresearch.com/index.html"><u>incurs a significant performance overhead</u></a> demonstrated in a clear trade-off between proof generation time and size: a fast 300 ms proof generation requires a large 173 kB signature, while a 4.8s proof generation time cuts the size of the signature nearly in half. A balanced parameter set, which serves as a good benchmark for any dedicated solution to beat, took 660 ms to sign and resulted in a 112 kB signature. The implementation is currently a proof of concept, with perhaps some room for optimization. Alternatively, a different signature like <a href="https://datatracker.ietf.org/doc/draft-ietf-cose-falcon/"><u>FN-DSA</u></a> could offer speed improvements: while its issuance is more complex, its verification is far more straightforward, boiling down to a simple hash-to-lattice computation and a norm check.</p><p>However, while this construction gives a functional baseline, these figures highlight the performance limitations for a real-time rate limiting system, where every millisecond counts. The 660 ms signing time strongly motivates the development of <i>dedicated</i> cryptographic constructions that trade some of the modularity for performance.</p>
    <div>
      <h3>Solid structure: Lattices</h3>
      <a href="#solid-structure-lattices">
        
      </a>
    </div>
    <p><a href="https://blog.cloudflare.com/lattice-crypto-primer/"><u>Lattices</u></a> are a natural starting point when discussing potential post-quantum AC candidates. NIST standardized ML-DSA and ML-KEM as signature and KEM algorithms, both of which are based on lattices. So, are lattices the answer to post-quantum anonymous credentials?</p><p>The answer is a bit nuanced. While explicit anonymous credential schemes from lattices exist, they have shortcomings that prevent real-world deployment: for example, a <a href="https://eprint.iacr.org/2023/560.pdf"><u>recent scheme</u></a> sacrifices round-optimality for smaller communication size, which is unacceptable for a service like Privacy Pass where every second counts. Given that our RTT is 100ms or less for the majority of users, each extra communication round adds tangible latency especially for those on slower Internet connections. When the final credential size is still over 100 kB, the trade-offs are hard to justify. So, our search continues. We expand our horizon by looking into <i>blind signatures </i>and whether we can adapt them for anonymous credentials.</p>
    <div>
      <h4>Two-step approach: Hash-and-sign</h4>
      <a href="#two-step-approach-hash-and-sign">
        
      </a>
    </div>
    <p>A prominent paradigm in lattice-based signatures is the <i>hash-and-sign</i> construction. Here, the message is first hashed to a point in the lattice. Then, the signer uses their secret key, a <a href="https://eprint.iacr.org/2007/432"><u>lattice trapdoor</u></a>, to generate a vector that, when multiplied with the private key, evaluates to the hashed point in the lattice. This is the core mechanism behind signature schemes like FN-DSA.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/66hA0KmluGoGO4I2SHAGTv/1a465c6c810e4f17df3112b96ed816da/image1.png" />
          </figure><p>Adapting hash-and-sign for blind signatures is tricky, since the signer may not learn the message. This introduces a significant security challenge: If the user can request signatures on arbitrary points, they can mount an attack to extract the trapdoor by repeatedly requesting signatures for carefully chosen arbitrary points. These points can be used to reconstruct a short basis, which is equivalent to a key recovery. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1lyCHqOTL477mFGSWjH3dv/48ffe46acfbe81b692c2ba30f383634b/image9.png" />
          </figure><p>The standard defense against this attack is to require the user to prove in zero-knowledge that the point they are asking to be signed is the blinded output of the specified hash function. However, proving hash preimages leads to the same problem as in the generic post-quantum privacy pass paper: proving a conventional hash function (like SHA3) inside a ZKP is computationally expensive and has a large communication complexity.</p><p>This difficult trade-off is at the heart of recent academic work. The <a href="https://eprint.iacr.org/2023/077.pdf"><u>state-of-the-art paper</u></a> presents two lattice-based blind signature schemes with small signature sizes of 22 KB for a signature and 48 kB for a privately-verifiable protocol that may be more useful in a setting like anonymous credential. However, this focus on the final signature size comes at the cost of an impractical <i>issuance</i>. The user must provide ZKPs for the correct hash and lattice relations that, by the paper’s own analysis, can add to<i> several hundred kilobytes</i> and take<i> 20 seconds to generate and 10 seconds to verify</i>.</p><p>While these results are valuable for advancing the field, this trade-off is a significant barrier for any large-scale, practical system. For our use case, a protocol that increases the final signature size moderately in exchange for a more efficient and lightweight issuance process would be a more suitable and promising direction.</p>
    <div>
      <h4>Best of two signatures: Hash-and-sign with aborts</h4>
      <a href="#best-of-two-signatures-hash-and-sign-with-aborts">
        
      </a>
    </div>
    <p>A promising technique for blind signatures combines the hash-and-sign paradigm with <i>Fiat-Shamir with aborts</i>, a method that relies on rejection sampling signatures. In this approach, the signer repeatedly attempts to generate a signature and aborts any result that may leak information about the secret key. This process ensures the final signature is statistically independent of the key and is used in modern signatures like ML-DSA. The <a href="https://eprint.iacr.org/2014/1027"><u>Phoenix signature</u></a> scheme uses <i>hash-and-sign with aborts</i>, where a message is first hashed into the lattice and signed, with rejection sampling employed to break the dependency between the signature and the private key.</p><p>Building on this foundation is an <a href="https://eprint.iacr.org/2024/131"><u>anonymous credential scheme for hash-and-sign with aborts</u></a>. The main improvement over hash-and-sign anonymous credentials is that, instead of proving the validity of a hash, the user commits to their attributes, which avoids costly zero-knowledge proofs.</p><p>The scheme is <a href="https://github.com/Chair-for-Security-Engineering/lattice-anonymous-credentials"><u>fully implemented</u></a> and credentials with attribute proofs just under 80 KB and signatures under 7 kB. The scheme takes less than 400 ms for issuance and 500 ms for showing the credential. The protocol also has a lot of features necessary for anonymous credentials, allowing users to prove relations between attributes and request pseudonyms for different instances.</p><p>This research presents a compelling step towards real-world deployability by combining state-of-the-art techniques to achieve a much healthier balance between performance and security. While the underlying mathematics are a bit more complex, the scheme is fully implemented and with a proof of knowledge of a signature at 40 kB and a prover time under a second, the scheme stands out as a great contender. However, for practical deployment, these figures would likely need a significant speedup to be usable in real-time systems. An improvement seems plausible, given recent <a href="https://eprint.iacr.org/2024/1952"><u>advances in lattice samplers</u></a>. Though the exact scale we can achieve is unclear. Still, we think it would be worthwhile to nudge the underlying design paradigm a little closer to our use cases.</p>
    <div>
      <h3>Do it yourself: MPC-in-the-head </h3>
      <a href="#do-it-yourself-mpc-in-the-head">
        
      </a>
    </div>
    <p>While the lattice-based hash-and-sign with aborts scheme provides one path to post-quantum signatures, an alternative approach is emerging from the MPCitH variant VOLE-in-the-Head <a href="https://eprint.iacr.org/2023/996"><u>(VOLEitH)</u></a>. </p><p>This scheme builds on <a href="https://eprint.iacr.org/2017/617"><u>Vector Oblivious Linear Evaluation (VOLE)</u></a>, an interactive protocol where one party's input vector is processed with another's secret value <i>delta</i>, creating a <i>correlation</i>. This VOLE correlation is used as a cryptographic commitment to the prover’s input. The system provides a zero-knowledge proof because the prover is bound by this correlation and cannot forge a solution without knowing the secret delta. The verifier, in turn, just has to verify that the final equation holds when the commitment is opened. This system is <i>linearly homomorphic</i>, which means that two commitments can be combined. This property is ideal for the <i>commit-and-prove</i> paradigm, where the prover first commits to the witnesses and then proves the validity of the circuit gate by gate. The primary trade-off is that the proofs are linear in the size of the circuit, but they offer substantially better runtimes. We also use linear-sized proofs for ARC and ACT.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6o073F0y7J7RxxHuDb4BSY/1ac0c4fc8b154dd77a8d3294016cbd32/image4.png" />
          </figure><p><sup>Example of evaluating a circuit gate by first committing to each wire and then proving the composition. This is easy for linear gates.</sup></p><p>This commit-and-prove approach allows <a href="https://link.springer.com/chapter/10.1007/978-3-031-91134-7_14"><u>VOLEitH</u></a> to efficiently prove the evaluation of symmetric ciphers, which are quantum-resistant. The transformation to a non-interactive protocol follows the standard MPCitH method: the prover commits to all secret values, a challenge is used to select a subset to reveal, and the prover proves consistency.</p><p>Efficient implementations operate over two mathematical fields (binary and prime) simultaneously, allowing these ZK circuits to handle both arithmetic and bitwise functions (like XORs) efficiently. Based on this foundation, a <a href="https://www.youtube.com/watch?v=VMeaF9xgbcw"><u>recent talk</u></a> teased the potential for blind signatures from the multivariate quadratic signature scheme <a href="https://pqmayo.org/about/"><u>MAYO</u></a> with sizes of just 7.5 kB and signing/verification times under 50 ms.</p><p>The VOLEitH approach, as a general-purpose solution system, represents a promising new direction for performant constructions. There are a <a href="https://pqc-mirath.org"><u>number</u></a> <a href="https://mqom.org"><u>of</u></a> <a href="https://pqc-perk.org"><u>competing</u></a> <a href="https://sdith.org"><u>in-the-head</u></a> schemes in the <a href="https://csrc.nist.gov/projects/pqc-dig-sig"><u>NIST competition for additional signature schemes</u></a>, including <a href="https://faest.info/authors.html"><u>one based on VOLEitH</u></a>. The current VOLEitH literature focuses on high-performance digital signatures, and an explicit construction for a full anonymous credential system has not yet been proposed. This means that features standard to ACs, such as multi-show unlinkability or the ability to prove relations between attributes, are not yet part of the design, whereas they are explicitly supported by the lattice construction. However, the preliminary results show great potential for performance, and it will be interesting to see the continued cryptanalysis and feature development from this line of VOLEitH in the area of anonymous credentials, especially since the general-purpose construction allows adding features easily.
</p><table><tr><td><p><b>Approach</b></p></td><td><p><b>Pros</b></p></td><td><p><b>Cons</b></p></td><td><p><b>Practical Viability</b></p></td></tr><tr><td><p><a href="https://eprint.iacr.org/2023/414"><u>Generic Composition</u></a></p></td><td><p>Flexible construction, strong security</p></td><td><p>Large signatures (112 kB), slow (660 ms)</p></td><td><p>Low: Performance is not great</p></td></tr><tr><td><p><a href="https://eprint.iacr.org/2023/077.pdf"><u>Hash-and-sign</u></a></p></td><td><p>Potentially tiny signatures, lots of optimization potential</p></td><td><p>Current implementation large and slow</p></td><td><p>Low: Performance is not great</p></td></tr><tr><td><p><a href="https://eprint.iacr.org/2024/131"><u>Hash-and-sign with aborts</u></a></p></td><td><p>Full AC system, good balance in communication</p></td><td><p>Slow runtimes (1s)</p></td><td><p>Medium: promising but performance would need to improve</p></td></tr><tr><td><p><a href="https://www.youtube.com/watch?v=VMeaF9xgbcw"><u>VOLEitH</u></a></p></td><td><p>Excellent potential performance (&lt;50ms, 7.5 kB)</p></td><td><p>not a full AC system, not peer-reviewed</p></td><td><p>Medium: promising research direction, no full solution available so far</p></td></tr></table>
    <div>
      <h2>Closing the gap</h2>
      <a href="#closing-the-gap">
        
      </a>
    </div>
    <p>My (that is Lena's) internship focused on a critical question: what should we look at next to build ACs for the Internet? For us, "the right direction" means developing protocols that can be integrated with real world applications, and developed collaboratively at the IETF. To make these a reality, we need researchers to look beyond blind signatures; we need a complete privacy-preserving protocol that combines blind signatures with efficient zero-knowledge proofs and properties like multi-show credentials that have an internal state. The issuance should also be sublinear in communication size with the number of presentations.</p><p>So, with the transition to post-quantum cryptography on the horizon, what are our thoughts on the current IETF proposals? A 2022 NIST presentation on the current state of anonymous credentials states that <a href="https://csrc.nist.gov/csrc/media/Presentations/2022/stppa4-revoc-decent/images-media/20221121-stppa4--baldimtsi--anon-credentials-revoc-decentral.pdf"><u>efficient post-quantum secure solutions are basically non-existent</u></a>. We argue that the last three years show nice developments in lattices and MPCitH anonymous credentials, but efficient post-quantum protocols still need work. Moving protocols into a post-quantum world isn't just a matter of swapping out old algorithms for new ones. A common approach on constructing post-quantum versions of classical protocols is swapping out the building blocks for their quantum-secure counterpart. </p><p>We believe this approach is essential, but not forward-looking. In addition to identifying how modern concerns can be accommodated on old cryptographic designs, we should be building new, post-quantum native protocols.</p><ul><li><p>For ARC, the conceptual path to a post-quantum construction seems relatively straightforward. The underlying cryptography follows a similar structure as the lattice-based anonymous credentials, or, when accepting a protocol with fewer features, the <a href="https://eprint.iacr.org/2023/414"><u>generic post-quantum privacy-pass</u></a> construction. However, we need to support per-origin rate-limiting, which allows us to transform a token at an origin without leaking us being able to link the redemption to redemptions at other origins, a feature that none of the post-quantum anonymous credential protocols or blind signatures support. Also, ARC is sublinear in communication size with respect to the number of tokens issued, which so far only the hash-and-sign with abort lattices achieve, although the notion of “limited shows” is not present in the current proposal. In addition, it would be great to gauge efficient implementations, especially for blind signatures, as well as looking into efficient zero-knowledge proofs. </p></li><li><p>For ACT, we need the protocols for ARC and an additional state. Even for the simplest counter, we need the ability to homomorphically subtract from that balance within the credential itself. This is a much more complex cryptographic requirement. It would also be interesting to see a post-quantum double-spend prevention that enforces the sequential nature of ACT. </p></li></ul><p>Working on ACs and other privacy-preserving cryptography inevitably leads to a major bottleneck: efficient zero-knowledge proofs, or to be more exact, efficiently proving hash function evaluations. In a ZK circuit, multiplications are expensive. Each wire in the circuit that performs a multiplication requires a cryptographic commitment, which adds communication overhead. In contrast, other operations like XOR can be virtually "free." This makes a huge difference in performance. For example, SHAKE (the primitive used in ML-DSA) can be orders of magnitude slower than arithmetization-friendly hash functions inside a ZKP. This is why researchers and implementers are already using <a href="https://eprint.iacr.org/2019/458"><u>Poseidon</u></a> or <a href="https://eprint.iacr.org/2023/323"><u>Poseidon2</u></a> to make their protocols faster.</p><p>Currently, <a href="https://www.poseidon-initiative.info/"><u>Ethereum</u></a> is <a href="https://x.com/VitalikButerin/status/1894681713613164888"><u>seriously considering migrating Ethereum to the Poseidon hash</u></a> and calls for cryptanalysis, but there is no indication of standardization. This is a problem: papers increasingly use different instantiations of Poseidon to fit their use-case, and there <a href="https://eprint.iacr.org/2016/492"><u>are</u></a> <a href="https://eprint.iacr.org/2023/323"><u>more</u></a> <a href="https://eprint.iacr.org/2022/840"><u>and</u></a> <a href="https://eprint.iacr.org/2025/1893"><u>more</u></a> <a href="https://eprint.iacr.org/2025/926"><u>zero</u></a>-<a href="https://eprint.iacr.org/2020/1143"><u>knowledge</u></a> <a href="https://eprint.iacr.org/2019/426"><u>friendly</u></a> <a href="https://eprint.iacr.org/2023/1025"><u>hash</u></a> <a href="https://eprint.iacr.org/2021/1038"><u>functions</u></a> <a href="https://eprint.iacr.org/2022/403"><u>coming</u></a> <a href="https://eprint.iacr.org/2025/058"><u>out</u></a>, tailored to different use-cases. We would like to see at least one XOF and one hash each for a prime field and for a binary field, ideally with some security levels. And also, is Poseidon the best or just the most well-known ZK friendly cipher? Is it always secure against quantum computers (like we believe AES to be), and are there other attacks like the <a href="https://eprint.iacr.org/2025/950"><u>recent</u></a> <a href="https://eprint.iacr.org/2025/937"><u>attacks</u></a> on round-reduced versions?</p><p>Looking at algebra and zero-knowledge brings us to a fundamental debate in modern cryptography. Imagine a line representing the spectrum of research: On one end, you have protocols built on very well-analyzed standard assumptions like the <a href="https://blog.cloudflare.com/lattice-crypto-primer/#breaking-lattice-cryptography-by-finding-short-vectors"><u>SIS problem</u></a> on lattices or the collision resistance of SHA3. On the other end, you have protocols that gain massive efficiency by using more algebraic structure, which in turn relies on newer, stronger cryptographic assumptions. Breaking novel hash functions is somewhere in the middle. </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2BMtbDoVnrmKeTvhCyfOjK/616438127351eedf6ff41db282a0511e/image7.png" />
          </figure><p>The answer for the Internet can’t just be to relent and stay at the left end of our graph to be safe. For the ecosystem to move forward, we need to have confidence in both. We need more research to validate the security of ZK-friendly primitives like Poseidon, and we need more scrutiny on the stronger assumptions that enable efficient algebraic methods.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>As we’ve explored, the cryptographic properties that make classical ACs efficient, particularly the rich structure of elliptic curves, do not have direct post-quantum equivalents. Our survey of the state of the art from generic compositions using STARKs, to various lattice-based schemes, and promising new directions like MPC-in-the-head, reveals a field full of potential but with no clear winner. The trade-offs between communication cost, computational cost, and protocol rounds remain a significant barrier to practical, large-scale deployment, especially in comparison to elliptic curve constructions.</p><p>To bridge this gap, we must move beyond simply building post-quantum blind signatures. We challenge our colleagues in academia and industry to develop complete, post-quantum native protocols that address real-world needs. This includes supporting essential features like the per-origin rate-limiting required for ARC or the complex stateful credentials needed for ACT.</p><p>A critical bottleneck for all these approaches is the lack of efficient, standardized, and well-analyzed zero-knowledge-friendly hash functions. We need to research zero-knowledge friendly primitives and build industry-wide confidence to enable efficient post-quantum privacy.</p><p>If you’re working on these problems, or you have experience in the management and deployment of classical credentials, now is the time to engage. The world is rapidly adopting credentials for everything from digital identity to bot management, and it is our collective responsibility to ensure these systems are private and secure for a post-quantum future. We can tell for certain that there are more discussions to be had, and if you’re interested in helping to build this more secure and private digital world, we’re hiring 1,111 interns over the course of next year, and have open positions!</p> ]]></content:encoded>
            <category><![CDATA[AI Bots]]></category>
            <category><![CDATA[Post-Quantum]]></category>
            <category><![CDATA[IETF]]></category>
            <category><![CDATA[European Union]]></category>
            <category><![CDATA[Elliptic Curves]]></category>
            <category><![CDATA[Cryptography]]></category>
            <guid isPermaLink="false">JA04hlqr6TaeGhkvyutbt</guid>
            <dc:creator>Lena Heimberger</dc:creator>
            <dc:creator>Christopher Patton</dc:creator>
        </item>
        <item>
            <title><![CDATA[Keeping the Internet fast and secure: introducing Merkle Tree Certificates]]></title>
            <link>https://blog.cloudflare.com/bootstrap-mtc/</link>
            <pubDate>Tue, 28 Oct 2025 13:00:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare is launching an experiment with Chrome to evaluate fast, scalable, and quantum-ready Merkle Tree Certificates, all without degrading performance or changing WebPKI trust relationships. ]]></description>
            <content:encoded><![CDATA[ <p>The world is in a race to build its first quantum computer capable of solving practical problems not feasible on even the largest conventional supercomputers. While the quantum computing paradigm promises many benefits, it also threatens the security of the Internet by breaking much of the cryptography we have come to rely on.</p><p>To mitigate this threat, Cloudflare is helping to migrate the Internet to Post-Quantum (PQ) cryptography. Today, <a href="https://radar.cloudflare.com/adoption-and-usage#post-quantum-encryption"><u>about 50%</u></a> of traffic to Cloudflare's edge network is protected against the most urgent threat: an attacker who can intercept and store encrypted traffic today and then decrypt it in the future with the help of a quantum computer. This is referred to as the <a href="https://en.wikipedia.org/wiki/Harvest_now,_decrypt_later"><u>harvest now, decrypt later</u></a><i> </i>threat.</p><p>However, this is just one of the threats we need to address. A quantum computer can also be used to crack a server's <a href="https://www.cloudflare.com/application-services/products/ssl/">TLS certificate</a>, allowing an attacker to impersonate the server to unsuspecting clients. The good news is that we already have PQ algorithms we can use for quantum-safe authentication. The bad news is that adoption of these algorithms in TLS will require significant changes to one of the most complex and security-critical systems on the Internet: the Web Public-Key Infrastructure (WebPKI).</p><p>The central problem is the sheer size of these new algorithms: signatures for ML-DSA-44, one of the most performant PQ algorithms standardized by NIST, are 2,420 bytes long, compared to just 64 bytes for ECDSA-P256, the most popular non-PQ signature in use today; and its public keys are 1,312 bytes long, compared to just 64 bytes for ECDSA. That's a roughly 20-fold increase in size. Worse yet, the average TLS handshake includes a number of public keys and signatures, adding up to 10s of kilobytes of overhead per handshake. This is enough to have a <a href="https://blog.cloudflare.com/another-look-at-pq-signatures/#how-many-added-bytes-are-too-many-for-tls"><u>noticeable impact</u></a> on the performance of TLS.</p><p>That makes drop-in PQ certificates a tough sell to enable today: they don’t bring any security benefit before Q-day — the day a cryptographically relevant quantum computer arrives — but they do degrade performance. We could sit and wait until Q-day is a year away, but that’s playing with fire. Migrations always take longer than expected, and by waiting we risk the security and privacy of the Internet, which is <a href="https://developers.cloudflare.com/ssl/edge-certificates/universal-ssl/"><u>dear to us</u></a>.</p><p>It's clear that we must find a way to make post-quantum certificates cheap enough to deploy today by default for everyone — not just those that can afford it. In this post, we'll introduce you to the plan we’ve brought together with industry partners to the <a href="https://datatracker.ietf.org/group/plants/about/"><u>IETF</u></a> to redesign the WebPKI in order to allow a smooth transition to PQ authentication with no performance impact (and perhaps a performance improvement!). We'll provide an overview of one concrete proposal, called <a href="https://datatracker.ietf.org/doc/draft-davidben-tls-merkle-tree-certs/"><u>Merkle Tree Certificates (MTCs)</u></a>, whose goal is to whittle down the number of public keys and signatures in the TLS handshake to the bare minimum required.</p><p>But talk is cheap. We <a href="https://blog.cloudflare.com/experiment-with-pq/"><u>know</u></a> <a href="https://blog.cloudflare.com/announcing-encrypted-client-hello/"><u>from</u></a> <a href="https://blog.cloudflare.com/why-tls-1-3-isnt-in-browsers-yet/"><u>experience</u></a> that, as with any change to the Internet, it's crucial to test early and often. <b>Today we're announcing our intent to deploy MTCs on an experimental basis in collaboration with Chrome Security.</b> In this post, we'll describe the scope of this experiment, what we hope to learn from it, and how we'll make sure it's done safely.</p>
    <div>
      <h2>The WebPKI today — an old system with many patches</h2>
      <a href="#the-webpki-today-an-old-system-with-many-patches">
        
      </a>
    </div>
    <p>Why does the TLS handshake have so many public keys and signatures?</p><p>Let's start with Cryptography 101. When your browser connects to a website, it asks the server to <b>authenticate</b> itself to make sure it's talking to the real server and not an impersonator. This is usually achieved with a cryptographic primitive known as a digital signature scheme (e.g., ECDSA or ML-DSA). In TLS, the server signs the messages exchanged between the client and server using its <b>secret key</b>, and the client verifies the signature using the server's <b>public key</b>. In this way, the server confirms to the client that they've had the same conversation, since only the server could have produced a valid signature.</p><p>If the client already knows the server's public key, then only <b>1 signature</b> is required to authenticate the server. In practice, however, this is not really an option. The web today is made up of around a billion TLS servers, so it would be unrealistic to provision every client with the public key of every server. What's more, the set of public keys will change over time as new servers come online and existing ones rotate their keys, so we would need some way of pushing these changes to clients.</p><p>This scaling problem is at the heart of the design of all PKIs.</p>
    <div>
      <h3>Trust is transitive</h3>
      <a href="#trust-is-transitive">
        
      </a>
    </div>
    <p>Instead of expecting the client to know the server's public key in advance, the server might just send its public key during the TLS handshake. But how does the client know that the public key actually belongs to the server? This is the job of a <b>certificate</b>.</p><p>A certificate binds a public key to the identity of the server — usually its DNS name, e.g., <code>cloudflareresearch.com</code>. The certificate is signed by a Certification Authority (CA) whose public key is known to the client. In addition to verifying the server's handshake signature, the client verifies the signature of this certificate. This establishes a chain of trust: by accepting the certificate, the client is trusting that the CA verified that the public key actually belongs to the server with that identity.</p><p>Clients are typically configured to trust many CAs and must be provisioned with a public key for each. Things are much easier however, since there are only 100s of CAs instead of billions. In addition, new certificates can be created without having to update clients.</p><p>These efficiencies come at a relatively low cost: for those counting at home, that's <b>+1</b> signature and <b>+1</b> public key, for a total of <b>2 signatures and 1 public key</b> per TLS handshake.</p><p>That's not the end of the story, however. As the WebPKI has evolved, so have these chains of trust grown a bit longer. These days it's common for a chain to consist of two or more certificates rather than just one. This is because CAs sometimes need to rotate<b> </b>their keys, just as servers do. But before they can start using the new key, they must distribute the corresponding public key to clients. This takes time, since it requires billions of clients to update their trust stores. To bridge the gap, the CA will sometimes use the old key to issue a certificate for the new one and append this certificate to the end of the chain.</p><p>That's<b> +1</b> signature and<b> +1</b> public key, which brings us to<b> 3 signatures and 2 public keys</b>. And we still have a little ways to go.</p>
    <div>
      <h3>Trust but verify</h3>
      <a href="#trust-but-verify">
        
      </a>
    </div>
    <p>The main job of a CA is to verify that a server has control over the domain for which it’s requesting a certificate. This process has evolved over the years from a high-touch, CA-specific process to a standardized, <a href="https://datatracker.ietf.org/doc/html/rfc8555/"><u>mostly automated process</u></a> used for issuing most certificates on the web. (Not all CAs fully support automation, however.) This evolution is marked by a number of security incidents in which a certificate was <b>mis-issued </b>to a party other than the server, allowing that party to impersonate the server to any client that trusts the CA.</p><p>Automation helps, but <a href="https://en.wikipedia.org/wiki/DigiNotar#Issuance_of_fraudulent_certificates"><u>attacks</u></a> are still possible, and mistakes are almost inevitable. <a href="https://blog.cloudflare.com/unauthorized-issuance-of-certificates-for-1-1-1-1/"><u>Earlier this year</u></a>, several certificates for Cloudflare's encrypted 1.1.1.1 resolver were issued without our involvement or authorization. This apparently occurred by accident, but it nonetheless put users of 1.1.1.1 at risk. (The mis-issued certificates have since been revoked.)</p><p>Ensuring mis-issuance is detectable is the job of the Certificate Transparency (CT) ecosystem. The basic idea is that each certificate issued by a CA gets added to a public <b>log</b>. Servers can audit these logs for certificates issued in their name. If ever a certificate is issued that they didn't request itself, the server operator can prove the issuance happened, and the PKI ecosystem can take action to prevent the certificate from being trusted by clients.</p><p>Major browsers, including Firefox and Chrome and its derivatives, require certificates to be logged before they can be trusted. For example, Chrome, Safari, and Firefox will only accept the server's certificate if it appears in at least two logs the browser is configured to trust. This policy is easy to state, but tricky to implement in practice:</p><ol><li><p>Operating a CT log has historically been fairly expensive. Logs ingest billions of certificates over their lifetimes: when an incident happens, or even just under high load, it can take some time for a log to make a new entry available for auditors.</p></li><li><p>Clients can't really audit logs themselves, since this would expose their browsing history (i.e., the servers they wanted to connect to) to the log operators.</p></li></ol><p>The solution to both problems is to include a signature from the CT log along with the certificate. The signature is produced immediately in response to a request to log a certificate, and attests to the log's intent to include the certificate in the log within 24 hours.</p><p>Per browser policy, certificate transparency adds <b>+2</b> signatures to the TLS handshake, one for each log. This brings us to a total of <b>5 signatures and 2 public keys</b> in a typical handshake on the public web.</p>
    <div>
      <h3>The future WebPKI</h3>
      <a href="#the-future-webpki">
        
      </a>
    </div>
    <p>The WebPKI is a living, breathing, and highly distributed system. We've had to patch it a number of times over the years to keep it going, but on balance it has served our needs quite well — until now.</p><p>Previously, whenever we needed to update something in the WebPKI, we would tack on another signature. This strategy has worked because conventional cryptography is so cheap. But <b>5 signatures and 2 public keys </b>on average for each TLS handshake is simply too much to cope with for the larger PQ signatures that are coming.</p><p>The good news is that by moving what we already have around in clever ways, we can drastically reduce the number of signatures we need.</p>
    <div>
      <h3>Crash course on Merkle Tree Certificates</h3>
      <a href="#crash-course-on-merkle-tree-certificates">
        
      </a>
    </div>
    <p><a href="https://datatracker.ietf.org/doc/draft-davidben-tls-merkle-tree-certs/"><u>Merkle Tree Certificates (MTCs)</u></a> is a proposal for the next generation of the WebPKI that we are implementing and plan to deploy on an experimental basis. Its key features are as follows:</p><ol><li><p>All the information a client needs to validate a Merkle Tree Certificate can be disseminated out-of-band. If the client is sufficiently up-to-date, then the TLS handshake needs just <b>1 signature, 1 public key, and 1 Merkle tree inclusion proof</b>. This is quite small, even if we use post-quantum algorithms.</p></li><li><p>The MTC specification makes certificate transparency a first class feature of the PKI by having each CA run its own log of exactly the certificates they issue.</p></li></ol><p>Let's poke our head under the hood a little. Below we have an MTC generated by one of our internal tests. This would be transmitted from the server to the client in the TLS handshake:</p>
            <pre><code>-----BEGIN CERTIFICATE-----
MIICSzCCAUGgAwIBAgICAhMwDAYKKwYBBAGC2ksvADAcMRowGAYKKwYBBAGC2ksv
AQwKNDQzNjMuNDguMzAeFw0yNTEwMjExNTMzMjZaFw0yNTEwMjgxNTMzMjZaMCEx
HzAdBgNVBAMTFmNsb3VkZmxhcmVyZXNlYXJjaC5jb20wWTATBgcqhkjOPQIBBggq
hkjOPQMBBwNCAARw7eGWh7Qi7/vcqc2cXO8enqsbbdcRdHt2yDyhX5Q3RZnYgONc
JE8oRrW/hGDY/OuCWsROM5DHszZRDJJtv4gno2wwajAOBgNVHQ8BAf8EBAMCB4Aw
EwYDVR0lBAwwCgYIKwYBBQUHAwEwQwYDVR0RBDwwOoIWY2xvdWRmbGFyZXJlc2Vh
cmNoLmNvbYIgc3RhdGljLWN0LmNsb3VkZmxhcmVyZXNlYXJjaC5jb20wDAYKKwYB
BAGC2ksvAAOB9QAAAAAAAAACAAAAAAAAAAJYAOBEvgOlvWq38p45d0wWTPgG5eFV
wJMhxnmDPN1b5leJwHWzTOx1igtToMocBwwakt3HfKIjXYMO5CNDOK9DIKhmRDSV
h+or8A8WUrvqZ2ceiTZPkNQFVYlG8be2aITTVzGuK8N5MYaFnSTtzyWkXP2P9nYU
Vd1nLt/WjCUNUkjI4/75fOalMFKltcc6iaXB9ktble9wuJH8YQ9tFt456aBZSSs0
cXwqFtrHr973AZQQxGLR9QCHveii9N87NXknDvzMQ+dgWt/fBujTfuuzv3slQw80
mibA021dDCi8h1hYFQAA
-----END CERTIFICATE-----</code></pre>
            <p>Looks like your average PEM encoded certificate. Let's decode it and look at the parameters:</p>
            <pre><code>$ openssl x509 -in merkle-tree-cert.pem -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 531 (0x213)
        Signature Algorithm: 1.3.6.1.4.1.44363.47.0
        Issuer: 1.3.6.1.4.1.44363.47.1=44363.48.3
        Validity
            Not Before: Oct 21 15:33:26 2025 GMT
            Not After : Oct 28 15:33:26 2025 GMT
        Subject: CN=cloudflareresearch.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:70:ed:e1:96:87:b4:22:ef:fb:dc:a9:cd:9c:5c:
                    ef:1e:9e:ab:1b:6d:d7:11:74:7b:76:c8:3c:a1:5f:
                    94:37:45:99:d8:80:e3:5c:24:4f:28:46:b5:bf:84:
                    60:d8:fc:eb:82:5a:c4:4e:33:90:c7:b3:36:51:0c:
                    92:6d:bf:88:27
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Subject Alternative Name:
                DNS:cloudflareresearch.com, DNS:static-ct.cloudflareresearch.com
    Signature Algorithm: 1.3.6.1.4.1.44363.47.0
    Signature Value:
        00:00:00:00:00:00:02:00:00:00:00:00:00:00:02:58:00:e0:
        44:be:03:a5:bd:6a:b7:f2:9e:39:77:4c:16:4c:f8:06:e5:e1:
        55:c0:93:21:c6:79:83:3c:dd:5b:e6:57:89:c0:75:b3:4c:ec:
        75:8a:0b:53:a0:ca:1c:07:0c:1a:92:dd:c7:7c:a2:23:5d:83:
        0e:e4:23:43:38:af:43:20:a8:66:44:34:95:87:ea:2b:f0:0f:
        16:52:bb:ea:67:67:1e:89:36:4f:90:d4:05:55:89:46:f1:b7:
        b6:68:84:d3:57:31:ae:2b:c3:79:31:86:85:9d:24:ed:cf:25:
        a4:5c:fd:8f:f6:76:14:55:dd:67:2e:df:d6:8c:25:0d:52:48:
        c8:e3:fe:f9:7c:e6:a5:30:52:a5:b5:c7:3a:89:a5:c1:f6:4b:
        5b:95:ef:70:b8:91:fc:61:0f:6d:16:de:39:e9:a0:59:49:2b:
        34:71:7c:2a:16:da:c7:af:de:f7:01:94:10:c4:62:d1:f5:00:
        87:bd:e8:a2:f4:df:3b:35:79:27:0e:fc:cc:43:e7:60:5a:df:
        df:06:e8:d3:7e:eb:b3:bf:7b:25:43:0f:34:9a:26:c0:d3:6d:
        5d:0c:28:bc:87:58:58:15:00:00</code></pre>
            <p>While some of the parameters probably look familiar, others will look unusual. On the familiar side, the subject and public key are exactly what we might expect: the DNS name is <code>cloudflareresearch.com</code> and the public key is for a familiar signature algorithm, ECDSA-P256. This algorithm is not PQ, of course — in the future we would put ML-DSA-44 there instead.</p><p>On the unusual side, OpenSSL appears to not recognize the signature algorithm of the issuer and just prints the raw OID and bytes of the signature. There's a good reason for this: the MTC does not have a signature in it at all! So what exactly are we looking at?</p><p>The trick to leave out signatures is that a Merkle Tree Certification Authority (MTCA) produces its <i>signatureless</i> certificates <i>in batches</i> rather than individually. In place of a signature, the certificate has an <b>inclusion proof</b> of the certificate in a batch of certificates signed by the MTCA.</p><p>To understand how inclusion proofs work, let's think about a slightly simplified version of the MTC specification. To issue a batch, the MTCA arranges the unsigned certificates into a data structure called a <b>Merkle tree</b> that looks like this:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4LGhISsS07kbpSgDkqx8p2/68e3b36deeca7f97139654d2c769df68/image3.png" />
          </figure><p>Each leaf of the tree corresponds to a certificate, and each inner node is equal to the hash of its children. To sign the batch, the MTCA uses its secret key to sign the head of the tree. The structure of the tree guarantees that each certificate in the batch was signed by the MTCA: if we tried to tweak the bits of any one of the certificates, the treehead would end up having a different value, which would cause the signature to fail.</p><p>An inclusion proof for a certificate consists of the hash of each sibling node along the path from the certificate to the treehead:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4UZZHkRwsBLWXRYeop4rXv/8598cde48c27c112bc4992889f3d5799/image1.gif" />
          </figure><p>Given a validated treehead, this sequence of hashes is sufficient to prove inclusion of the certificate in the tree. This means that, in order to validate an MTC, the client also needs to obtain the signed treehead from the MTCA.</p><p>This is the key to MTC's efficiency:</p><ol><li><p>Signed treeheads can be disseminated to clients out-of-band and validated offline. Each validated treehead can then be used to validate any certificate in the corresponding batch, eliminating the need to obtain a signature for each server certificate.</p></li><li><p>During the TLS handshake, the client tells the server which treeheads it has. If the server has a signatureless certificate covered by one of those treeheads, then it can use that certificate to authenticate itself. That's <b>1 signature,1 public key and 1 inclusion proof</b> per handshake, both for the server being authenticated.</p></li></ol><p>Now, that's the simplified version. MTC proper has some more bells and whistles. To start, it doesn’t create a separate Merkle tree for each batch, but it grows a single large tree, which is used for better transparency. As this tree grows, periodically (sub)tree heads are selected to be shipped to browsers, which we call <b>landmarks</b>. In the common case browsers will be able to fetch the most recent landmarks, and servers can wait for batch issuance, but we need a fallback: MTC also supports certificates that can be issued immediately and don’t require landmarks to be validated, but these are not as small. A server would provision both types of Merkle tree certificates, so that the common case is fast, and the exceptional case is slow, but at least it’ll work.</p>
    <div>
      <h2>Experimental deployment</h2>
      <a href="#experimental-deployment">
        
      </a>
    </div>
    <p>Ever since early designs for MTCs emerged, we’ve been eager to experiment with the idea. In line with the IETF principle of “<a href="https://www.ietf.org/runningcode/"><u>running code</u></a>”, it often takes implementing a protocol to work out kinks in the design. At the same time, we cannot risk the security of users. In this section, we describe our approach to experimenting with aspects of the Merkle Tree Certificates design <i>without</i> changing any trust relationships.</p><p>Let’s start with what we hope to learn. We have lots of questions whose answers can help to either validate the approach, or uncover pitfalls that require reshaping the protocol — in fact, an implementation of an early MTC draft by <a href="https://www.cs.ru.nl/masters-theses/2025/M_Pohl___Implementation_and_Analysis_of_Merkle_Tree_Certificates_for_Post-Quantum_Secure_Authentication_in_TLS.pdf"><u>Maximilian Pohl</u></a> and <a href="https://www.ietf.org/archive/id/draft-davidben-tls-merkle-tree-certs-07.html#name-acknowledgements"><u>Mia Celeste</u></a> did exactly this. We’d like to know:</p><p><b>What breaks?</b> Protocol ossification (the tendency of implementation bugs to make it harder to change a protocol) is an ever-present issue with deploying protocol changes. For TLS in particular, despite having built-in flexibility, time after time we’ve found that if that flexibility is not regularly used, there will be buggy implementations and middleboxes that break when they see things they don’t recognize. TLS 1.3 deployment <a href="https://blog.cloudflare.com/why-tls-1-3-isnt-in-browsers-yet/"><u>took years longer</u></a> than we hoped for this very reason. And more recently, the rollout of PQ key exchange in TLS caused the Client Hello to be split over multiple TCP packets, something that many middleboxes <a href="https://tldr.fail/"><u>weren't ready for</u></a>.</p><p><b>What is the performance impact?</b> In fact, we expect MTCs to <i>reduce </i>the size of the handshake, even compared to today's non-PQ certificates. They will also reduce CPU cost: ML-DSA signature verification is about as fast as ECDSA, and there will be far fewer signatures to verify. We therefore expect to see a <i>reduction in latency</i>. We would like to see if there is a measurable performance improvement.</p><p><b>What fraction of clients will stay up to date? </b>Getting the performance benefit of MTCs requires the clients and servers to be roughly in sync with one another. We expect MTCs to have fairly short lifetimes, a week or so. This means that if the client's latest landmark is older than a week, the server would have to fallback to a larger certificate. Knowing how often this fallback happens will help us tune the parameters of the protocol to make fallbacks less likely.</p><p>In order to answer these questions, we are implementing MTC support in our TLS stack and in our certificate issuance infrastructure. For their part, Chrome is implementing MTC support in their own TLS stack and will stand up infrastructure to disseminate landmarks to their users.</p><p>As we've done in past experiments, we plan to enable MTCs for a subset of our free customers with enough traffic that we will be able to get useful measurements. Chrome will control the experimental rollout: they can ramp up slowly, measuring as they go and rolling back if and when bugs are found.</p><p>Which leaves us with one last question: who will run the Merkle Tree CA?</p>
    <div>
      <h3>Bootstrapping trust from the existing WebPKI</h3>
      <a href="#bootstrapping-trust-from-the-existing-webpki">
        
      </a>
    </div>
    <p>Standing up a proper CA is no small task: it takes years to be trusted by major browsers. That’s why Cloudflare isn’t going to become a “real” CA for this experiment, and Chrome isn’t going to trust us directly.</p><p>Instead, to make progress on a reasonable timeframe, without sacrificing due diligence, we plan to "mock" the role of the MTCA. We will run an MTCA (on <a href="https://github.com/cloudflare/azul/"><u>Workers</u></a> based on our <a href="https://blog.cloudflare.com/azul-certificate-transparency-log/"><u>StaticCT logs</u></a>), but for each MTC we issue, we also publish an existing certificate from a trusted CA that agrees with it. We call this the <b>bootstrap certificate</b>. When Chrome’s infrastructure pulls updates from our MTCA log, they will also pull these bootstrap certificates, and check whether they agree. Only if they do, they’ll proceed to push the corresponding landmarks to Chrome clients. In other words, Cloudflare is effectively just “re-encoding” an existing certificate (with domain validation performed by a trusted CA) as an MTC, and Chrome is using certificate transparency to keep us honest.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>With almost 50% of our traffic already protected by post-quantum encryption, we’re halfway to a fully post-quantum secure Internet. The second part of our journey, post-quantum certificates, is the hardest yet though. A simple drop-in upgrade has a noticeable performance impact and no security benefit before Q-day. This means it’s a hard sell to enable today by default. But here we are playing with fire: migrations always take longer than expected. If we want to keep an ubiquitously private and secure Internet, we need a post-quantum solution that’s performant enough to be enabled by default <b>today</b>.</p><p>Merkle Tree Certificates (MTCs) solves this problem by reducing the number of signatures and public keys to the bare minimum while maintaining the WebPKI's essential properties. We plan to roll out MTCs to a fraction of free accounts by early next year. This does not affect any visitors that are not part of the Chrome experiment. For those that are, thanks to the bootstrap certificates, there is no impact on security.</p><p>We’re excited to keep the Internet fast <i>and</i> secure, and will report back soon on the results of this experiment: watch this space! MTC is evolving as we speak, if you want to get involved, please join the IETF <a href="https://mailman3.ietf.org/mailman3/lists/plants@ietf.org/"><u>PLANTS mailing list</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Post-Quantum]]></category>
            <category><![CDATA[Research]]></category>
            <category><![CDATA[Cryptography]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[TLS]]></category>
            <category><![CDATA[Chrome]]></category>
            <category><![CDATA[Google]]></category>
            <category><![CDATA[IETF]]></category>
            <category><![CDATA[Transparency]]></category>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Open Source]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <guid isPermaLink="false">4jURWdZzyjdrcurJ4LlJ1z</guid>
            <dc:creator>Luke Valenta</dc:creator>
            <dc:creator>Christopher Patton</dc:creator>
            <dc:creator>Vânia Gonçalves</dc:creator>
            <dc:creator>Bas Westerbaan</dc:creator>
        </item>
        <item>
            <title><![CDATA[Prepping for post-quantum: a beginner’s guide to lattice cryptography]]></title>
            <link>https://blog.cloudflare.com/lattice-crypto-primer/</link>
            <pubDate>Fri, 21 Mar 2025 13:00:00 GMT</pubDate>
            <description><![CDATA[ This post is a beginner's guide to lattices, the math at the heart of the transition to post-quantum (PQ) cryptography. It explains how to do lattice-based encryption and authentication from scratch. ]]></description>
            <content:encoded><![CDATA[ <p>The cryptography that secures the Internet is evolving, and it's time to catch up. This post is a tutorial on lattice cryptography, the paradigm at the heart of the post-quantum (PQ) transition.</p><p>Twelve years ago (in 2013), the <a href="https://en.wikipedia.org/wiki/Edward_Snowden"><u>revelation of mass surveillance in the US</u></a> kicked off the widespread adoption of <a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/"><u>TLS</u></a> for encryption and authentication on the web. This transition was buoyed by the standardization and implementation of new, more efficient public-key cryptography based on <a href="https://blog.cloudflare.com/a-relatively-easy-to-understand-primer-on-elliptic-curve-cryptography/"><u>elliptic curves</u></a>. Elliptic curve cryptography was both faster and required less communication than its predecessors, including <a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)"><u>RSA</u></a> and <a href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange"><u>Diffie-Hellman</u></a> over finite fields.</p><p>Today's transition to <a href="https://www.cloudflare.com/learning/ssl/quantum/what-is-post-quantum-cryptography/"><u>PQ cryptography</u></a> addresses a looming threat for TLS and beyond: once built, a sufficiently large quantum computer can be used to break all <a href="https://www.cloudflare.com/learning/ssl/how-does-public-key-encryption-work/">public-key cryptography</a> in use today. And we continue to see <a href="https://blog.google/technology/research/google-willow-quantum-chip/"><u>advancements</u></a> in quantum-computer engineering that bring us closer to this threat becoming a reality.</p><p>Fortunately, this transition is well underway. The research and standards communities have spent the last several years developing alternatives that resist quantum cryptanalysis. For its part, Cloudflare has contributed to this process and is an early adopter of newly developed schemes. In fact, PQ encryption has been available at our edge <a href="https://blog.cloudflare.com/post-quantum-for-all/"><u>since 2022</u></a> and is <a href="https://radar.cloudflare.com/adoption-and-usage#post-quantum-encryption-adoption"><u>used in over 35% of non-automated HTTPS traffic today (2025)</u></a>. And this year we're beginning a major push towards PQ authentication for the TLS ecosystem.</p><p>Lattice-based cryptography is the first paradigm that will replace <a href="https://blog.cloudflare.com/a-relatively-easy-to-understand-primer-on-elliptic-curve-cryptography/"><u>elliptic curves</u></a>. Apart from being PQ secure, lattices are often as fast, and sometimes faster, in terms of CPU time. However, this new paradigm for public key crypto has one major cost: <b>lattices require much more communication than elliptic curves.</b> For example, establishing an encryption key using lattices requires 2272 bytes of communication between the client and the server (<a href="https://csrc.nist.gov/pubs/fips/203/final"><u>ML-KEM-768</u></a>), compared to just 64 bytes for a key exchange using a modern elliptic-curve-based scheme (<a href="https://datatracker.ietf.org/doc/html/rfc7748"><u>X25519</u></a>). Accommodating such costs requires a significant amount of engineering, from dealing with <a href="https://blog.cloudflare.com/sizing-up-post-quantum-signatures/"><u>TCP packet fragmentation</u></a>, to reworking <a href="https://blog.cloudflare.com/pq-2024/#two-migrations"><u>TLS and its public key infrastructure</u></a>. Thus, the PQ transition is going to require the participation of a large number of people with a variety of backgrounds, not just cryptographers.</p><p>The primary audience for this blog post is those who find themselves involved in the PQ transition and want to better understand what's going on under the hood. However, more fundamentally, we think it's important for everyone to understand lattice cryptography on some level, especially if we're going to trust it for our security and privacy.</p><p>We'll assume you have a software-engineering background and some familiarity with concepts like TLS, encryption, and authentication. We'll see that the math behind lattice cryptography is, at least at the highest level, not difficult to grasp. Readers with a crypto-engineering background who want to go deeper might want to start with the excellent <a href="https://eprint.iacr.org/2024/1287"><u>tutorial by Vadim Lyubashevsky</u></a> on which this blog post is based. We also recommend <a href="https://keymaterial.net/2023/09/01/learning-but-with-errors/"><u>Sophie Schmieg's blog</u></a> on this subject.</p><p>While the transition to lattice cryptography incurs costs, it also creates opportunities. Many things we can build with elliptic curves we can also build with lattices, though not always as efficiently; but there are also things we can do with lattices that we don't know how to do efficiently with anything else. We'll touch on some of these applications at the very end.</p><p>We're going to cover a lot of ground in this post. If you stick with it, we hope you'll come away feeling empowered, not only to tackle the engineering challenges the PQ transition entails, but to solve problems you didn't know how to solve before.</p><p>Strap in — let's have some fun!</p>
    <div>
      <h3>Encryption</h3>
      <a href="#encryption">
        
      </a>
    </div>
    <p>The most pressing problem for the PQ transition is to ensure that tomorrow's quantum computers don't break today's encryption. An attacker today can store the packets exchanged between your laptop and a website you visit, and then, some time in the future, decrypt those packets with the help of a quantum computer. This means that much of the sensitive information transiting the Internet today — everything from <a href="https://blog.cloudflare.com/https-only-for-cloudflare-apis-shutting-the-door-on-cleartext-traffic/"><u>API tokens</u></a> and passwords to database encryption keys — may one day be unlocked by a quantum computer.</p><p>In fact, today's encryption in TLS is <i>mostly</i> PQ secure: <b>what's at risk is the process by which your browser and a server establish an encryption key</b>. Today this is usually done with elliptic-curve-based schemes, which are not PQ secure; our goal for this section is to understand how to do key exchange with lattices-based schemes, which are.</p><p>We will work through and implement a simplified version of <a href="https://csrc.nist.gov/pubs/fips/203/final"><u>ML-KEM</u></a>, a.k.a. Kyber, the most widely deployed PQ key exchange in use today. Our code will be less efficient and secure than a <a href="https://pkg.go.dev/github.com/cloudflare/circl@v1.6.0/kem/mlkem"><u>spec-compliant, production-quality implementation</u></a>, but will be good enough to grasp the main ideas.</p><p>Our starting point is a protocol that looks an awful lot like <a href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange"><u>Diffie-Hellman (DH)</u></a> key exchange. For those readers unacquainted with DH, the goal is for Alice and Bob to establish a shared secret over an insecure network. To do so, each picks a random secret number, computes the corresponding "key share", and sends the key share to the other:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5yWJO1Sem97PfhwqiKZRLm/bfacc44ca4f7187f4212b7b4616dd86d/image7.png" />
          </figure><p>Alice's secret number is $s$ and her key share is $g^s$; Bob's secret number is $r$ and his key share is $g^r$. Then given their secret and their peer's key share, each can compute $g^{rs}$. The security of this protocol comes from how we choose $g$, $s$, and $r$ and how we do arithmetic. The most efficient instantiation of DH uses elliptic curves.</p><p>In ML-KEM we replace operations on elliptic curves with <b>matrix </b>operations. It's not quite a drop-in replacement, so we'll need a little linear algebra to make sense of it. But don't worry: we're going to work with Python so we have running code to play with, and we'll use <a href="https://numpy.org/"><u>NumPy</u></a> to keep things high level.</p>
    <div>
      <h4>All the math we'll need</h4>
      <a href="#all-the-math-well-need">
        
      </a>
    </div>
    <p>A matrix is just a two-dimensional array of numbers. In NumPy, we can create a matrix as follows (importing <code>numpy</code> as <code>np</code>):</p>
            <pre><code>A = np.matrix([[1, 2, 3],
               [4, 5, 6],
               [7, 8, 9]])</code></pre>
            <p>This defines <code>A</code> to be the <code>3</code>-by-<code>3</code> matrix with entries <code>A[0,0]==1, A[0,1]==2, A[0,2]==3, A[1,0]==4</code>, and so on.</p><p>For the purposes of this post, the entries of our matrices will always be integers. Furthermore, whenever we add, subtract, or multiply two integers, we then <b>reduce</b> the result, just like we do with hours on a clock, so that we end up with a number in <code>range(Q)</code> for some positive number <code>Q</code>, called the modulus. The exact value doesn’t really matter now, but for ML-KEM it’s <code>Q=3329</code>, so let's go with that for now. (The modulus for a clock would be <code>Q=12</code>.)</p><p>In Python, we write multiplication of integers <code>a</code> and <code>b</code> modulo <code>Q</code> as<code> c = a*b % Q</code>. Here we compute <code>a*b</code>, divide the result by <code>Q</code>, then set <code>c</code> to the remainder. For example, <code>42*1337</code> <code>% Q</code> is equal to <code>2890</code> rather than <code>56154</code>. Modular addition and subtraction are done analogously. For the rest of this blog, we will sometimes omit "<code>% Q</code>" when it's clear in context that we mean modular arithmetic.</p><p>Next, we'll need three operations on matrices.</p><p>The first is <b>matrix transpose</b>, written <code>A.T </code>in NumPy. This operation flips the matrix along its diagonal so that <code>A.T[j,i] == A[i,j]</code> for all rows <code>i</code> and columns <code>j</code>:</p>
            <pre><code>print(A.T)
# [[1 4 7]
#  [2 5 8]
#  [3 6 9]]</code></pre>
            <p>To visualize this, imagine writing down a matrix on a translucent piece of paper. Draw a line from the top left corner to the bottom right corner of that paper, then rotate the paper 180° around that line:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2x3qNDptBldEKCgQBbmNPr/ad174561910e6c90753024625f16fd68/image3.png" />
          </figure><p>The second operation we'll need is <b>matrix multiplication</b>. Normally, we will multiply a matrix by a <b>column vector</b>, which is just a matrix with one column. For example, the following <code>3</code>-by-<code>1</code> matrix is a column vector:</p>
            <pre><code>s = np.matrix([[0],
               [1],
               [0]])</code></pre>
            <p>We can also write <code>s </code>more concisely as <code>np.matrix([[0,1,0]]).T</code>.  To multiply a square matrix <code>A</code> by a column vector <code>s</code>, we compute the <b>dot product</b> of each row of <code>A</code> with <code>s</code>. That is, if <code>t = A*s % Q</code>, then <code>t[i] == (A[i,0]*s[0,0] + A[i,1]*s[1,0] + A[i,2]*s[2,0]) % Q </code>for each row <code>i</code>. The output will always be a column vector:</p>
            <pre><code>print(A*s % Q)
# [[2]
#  [5]
#  [8]]</code></pre>
            <p>The number of rows of this column vector is equal to the number of rows of the matrix on the left hand side. In particular, if we take our column vector <code>s</code>, transpose it into a <code>1</code>-by-<code>3</code> matrix, and multiply it by a <code>3</code>-by-<code>1</code> matrix <code>r</code>, then we end up with a <code>1</code>-by-<code>1 </code>matrix:</p>
            <pre><code>r = np.matrix([[1,2,3]]).T
print(s.T*r % Q)
# [[2]]</code></pre>
            <p>The final matrix operation we'll need is <b>matrix addition</b>. If <code>A</code> and <code>B</code> are both <code>N</code>-by-<code>M</code> matrices, then <code>C = (A+B) % Q</code> is the <code>N</code>-by-<code>M</code> matrix for which <code>C[i,j] == (A[i,j]+B[i,j]) % Q</code>. Of course, this only works if the matrices we're adding have the same dimensions.</p>
    <div>
      <h4>Warm up</h4>
      <a href="#warm-up">
        
      </a>
    </div>
    <p>Enough maths — let's get to exchanging some keys. We start with the DH diagram from before and swap out the computations with matrix operations. Note that this protocol is not secure, but will be the basis of a secure key exchange mechanism we'll develop in the next section:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3qRFXuCjStyX8IdRgHogJi/0ea04a675e6b8b52fc74a2375827890c/image6.png" />
          </figure><ul><li><p>Alice and Bob agree on a public, <code>N</code>-by-<code>N</code> matrix <code>A</code>. This is analogous to the number $g$ that Alice and Bob agree on in the DH diagram.</p></li><li><p>Alice chooses a random length<code>-N</code> vector <code>s</code> and sends <code>t = A*s % Q</code> to Bob.</p></li><li><p>Bob chooses a random length<code>-N</code>  vector <code>r</code> and sends <code>u = r.T*A % Q</code> to Alice. You can also compute this as <code>(A.T*r).T % Q.</code></p></li></ul><p>The vectors <code>t</code> and <code>u</code> are analogous to DH key shares. After the exchange of these key shares, Alice and Bob can compute a shared secret. Alice computes the shared secret as <code>u*s % Q</code> and Bob computes the shared secret as <code>r.T*t % Q</code>. To see why they compute the same key, notice that <code>u*s == (r.T*A)*s == r.T*(A*s) == r.T*t.</code></p><p>In fact, this key exchange is essentially what happens in ML-KEM. However, we don't use this directly, but rather as part of a <b>public key encryption scheme</b>. Public key encryption involves three algorithms:</p><ul><li><p><code>key_gen():</code> The key generation algorithm that outputs a public encryption key <code>pk</code> and the corresponding secret decryption key <code>sk</code>.</p></li><li><p><code>encrypt()</code>: The encryption algorithm that takes the public key and a plaintext and outputs a ciphertext.</p></li><li><p><code>decrypt()</code>: The decryption algorithm that takes the secret key and a ciphertext and outputs the underlying plaintext. That is, <code>decrypt(sk, encrypt(pk, ptxt)) == ptxt</code> for any plaintext <code>ptxt</code>.</p></li></ul><p>We'll say the scheme is secure if, given a ciphertext and the public key used to encrypt it, no attacker can discern any information about the underlying plaintext without knowledge of the secret key. Once we have this encryption scheme, we then transform it into a <b>key-encapsulation mechanism</b> (the "KEM" in "ML-KEM") in the last step. A KEM is very similar to encryption except that the plaintext is always a randomly generated key.</p><p>Our encryption scheme is as follows:</p><ul><li><p><code>key_gen()</code>: To generate a key pair, we choose a random, square matrix <code>A</code> and a random column vector <code>s</code>. We set our public key to <code>(A,t=A*s % Q)</code> and our secret key to <code>s</code>. Notice that <code>t </code>is Alice's  key share from the key exchange protocol above.</p></li><li><p><code>encrypt()</code>: Suppose our plaintext <code>ptxt </code>is an integer in <code>range(Q)</code>. To encrypt <code>ptxt</code>, Bob generates his key share <code>u</code>. He then derives the shared secret and adds it to <code>ptxt</code>. The ciphertext has two components:</p></li></ul><blockquote><p><code>u = r.T*A % Q</code></p><p><code>v = (r.T*t + m) % Q</code></p></blockquote><p>Here <code>m </code>is a <code>1</code>-by-<code>1 </code>matrix containing the plaintext, i.e., <code>m = np.matrix([[ptxt]])</code>, and <code>r</code> is a random column vector.</p><ul><li><p><code>decrypt()</code>: To decrypt, Alice computes the shared secret and subtracts it from <code>v</code>:</p></li></ul><blockquote><p><code>m = (v - u*s) % Q</code></p></blockquote><p>Some readers will notice that this looks an awful lot like <a href="https://en.wikipedia.org/wiki/ElGamal_encryption"><u>El Gamal</u></a> encryption. This isn't a coincidence. Good cryptographers roll their own crypto; great cryptographers steal from good cryptographers.</p><p>Let's now put this together into code. The last thing we'll need is a method of generating random matrices and column vectors. We call this function <code>gen_mat() </code>below. Take a crack at implementing this yourself. Our scheme has two parameters: the modulus <code>Q</code>; and the dimension of <code>N</code> of the matrix and column vectors. The choice of <code>N</code> matters for security, but for now feel free to pick whatever value you want.</p>
            <pre><code>def key_gen():
    # Here `gen_mat()` returns an N-by-N matrix with entries
    # randomly chosen from `range(0, Q)`.
    A = gen_mat(N, N, 0, Q)
    # Like above except the matrix is N-by-1.
    s = gen_mat(N, 1, 0, Q)
    t = A*s % Q
    return ((A, t), s)

def encrypt(pk, ptxt):
    (A, t) = pk
    m = np.matrix([[ptxt]])
    r = gen_mat(N, 1, 0, Q)
    u = r.T*A % Q
    v = (r.T*t + m) % Q
    return (u, v)

def decrypt(sk, ctxt):
    s = sk
    (u, v) = ctxt
    m = (v - u*s) % Q
    return m[0,0]

# Test
assert decrypt(sk, encrypt(pk, 1)) == 1</code></pre>
            
    <div>
      <h4>Making the scheme secure (or "What is a lattice?")</h4>
      <a href="#making-the-scheme-secure-or-what-is-a-lattice">
        
      </a>
    </div>
    <p>By now, you might be wondering what on Earth a lattice even is. We promise we'll define it, but before we do, it'll help to understand why our warm-up scheme is insecure and what it'll take to fix it.</p><p>Readers familiar with linear algebra may already see the problem: in order for this scheme to be secure, it should be impossible for the attacker to recover the secret key <code>s</code>; but given the public <code>(A,t)</code>, we can immediately solve for <code>s</code> using <a href="https://en.wikipedia.org/wiki/Gaussian_elimination"><u>Gaussian elimination</u></a>.</p><p>In more detail, if <code>A</code> is invertible, we can write the secret key as <code>A</code><code><sup>-1</sup></code><code>*t == A</code><code><sup>-1</sup></code><code>*(A*s) == (A</code><code><sup>-1</sup></code><code>*A)*s == s,</code> where <code>A</code><code><sup>-1</sup></code> is the inverse of <code>A</code>. (When you multiply a matrix by its inverse, you get the identity matrix <code>I</code>, which simply takes a column vector to itself, i.e., <code>I*s == s.</code>) We can use Gaussian elimination to compute this matrix. Intuitively, all we're doing is solving a set of linear equations, where the entries of <code>s</code> are the unknown variables. (Note that this is possible even if <code>A</code> is not invertible.)</p><p>In order to make this encryption scheme secure, we need to make it a little... “messier”.</p>
    <div>
      <h5>Let's get messy</h5>
      <a href="#lets-get-messy">
        
      </a>
    </div>
    <p>For starters, we need to make it hard to recover the secret key from the public key. Let's try the following: generate another random vector <code>e</code> and add it into <code>A*</code>s. Our key generation algorithm becomes:</p>
            <pre><code>def key_gen():
    A = gen_mat(N, N, 0, Q)
    s = gen_mat(N, 1, 0, Q)
    e = gen_mat(N, 1, 0, Q)
    t = (A*s + e) % Q
    return ((A, t), s)</code></pre>
            <p>Our formula for the column vector component of the public key, <code>t</code>, now includes an additive term <code>e</code>, which we'll call the <b>error</b>. Like the secret key, the error is just a random vector. </p><p>Notice that the previous attack no longer works: since <code>A</code><code><sup>-1</sup></code><code>*t == A</code><code><sup>-1</sup></code><code>*(A*s + e) == A</code><code><sup>-1</sup></code><code>*(A*s) + A</code><code><sup>-1</sup></code><code>*e == s + A</code><code><sup>-1</sup></code><code>*e</code>, we need to know <code>e</code> in order to compute <code>s</code>.</p><p>Great, but this patch creates another problem. Take a second to plug in this new key generation algorithm into your implementation and test it out. What happens?</p><p>You should see that <code>decrypt()</code> now outputs garbage. We can see why using a little algebra:</p><blockquote><p>	<code>(v - u*s) == (r.T*t + m) - (r.T*A)*s</code></p><p><code>                == r.T*(A*s + e) + m - (r.T*A)*s</code></p><p><code>                == r.T*(A*s) + r.T*e + m - r.T*(A*s)</code></p><p><code>                == r.T*e + m</code></p></blockquote><p>The entries of <code>r</code> and <code>e</code> are sampled randomly, so <code>r.T*e</code> is also uniformly random. It's as if we encrypted <code>m</code> with a <a href="https://en.wikipedia.org/wiki/One-time_pad"><u>one-time pad</u></a>, then threw away the one-time pad!</p><h6>Handling decryption errors</h6><p>What can we do about this? First, it would help if <code>r.T*e</code> were small so that decryption yields something that's close to the plaintext. Imagine we could generate <code>r</code> and<code> e</code> in such a way that <code>r.T*e</code> were in <code>range(-epsilon, epsilon+1)</code> for some small <code>epsilon</code>. Then <code>decrypt</code> would output a number in <code>range(ptxt-epsilon, ptxt+epsilon+1)</code>, which would be pretty close to the actual plaintext.</p><p>However, we need to do better than get close. Imagine your browser failing to load your favorite website one-third of the time because of a decryption error. Nobody has time for that.</p><p>ML-KEM reduces the probability of decryption errors by being clever about how we encode the plaintext. Suppose all we want to do is encrypt a single bit, i.e., <code>ptxt </code>is either <code>0</code> or <code>1</code>. Consider the numbers in <code>range(Q)</code>, and split the number line into four chunks of roughly equal length:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3xojaMtl881io3pH4BdXNe/d47aede96c751942b82fedf0ca396450/image2.png" />
          </figure><p>Here we've labeled the region around zero (<code>-Q/4</code> to <code>Q/4</code> modulo <code>Q</code>) with <code>ptxt=0</code> and the region far away from zero with <code>ptxt=1</code>. To encode the bit, we set it to the integer corresponding to the middle of its range, i.e., <code>m = np.matrix([[ptxt * Q//2]])</code>. (Note the double "<code>//</code>" — this denotes integer division in Python.) To decode, we choose the <code>ptxt</code> corresponding to whatever range<code> m[0,0] </code>is in. That way if the decryption error is small, then we're highly likely to end up in the correct range.</p><p>Now all that's left is to ensure the decryption error, <code>r.T*e</code>, is small. We do this by sampling <b>short vectors </b><code>r</code> and <code>e</code>. By "short" we mean the entries of these vectors are sampled from a range that is much smaller than <code>range(Q)</code>. In particular, we'll pick some small positive integer <code>beta</code> and sample entries <code>range(-beta,beta+1)</code>.</p><p>How do we choose <code>beta</code>? Well, it should be small enough that decryption succeeds with overwhelming probability, but not so small that <code>r</code> and <code>e</code> are easy to guess and our scheme is broken. Take a minute or two to play with this. The parameters we can vary are:</p><ul><li><p>the modulus <code>Q</code></p></li><li><p>the dimension of the column vectors <code>N</code></p></li><li><p>the shortness parameter <code>beta</code></p></li></ul><p>For what ranges of these parameters is the decryption error low but the secret vectors are hard to guess? For what ranges is our scheme most efficient, in terms of runtime and communication cost (size of the public key plus the ciphertext)? We'll give a concrete answer at the end of this section, but in the meantime, we encourage you to play with this a bit.</p><h6>Gauss strikes back</h6><p>At this point, we have a working encryption scheme that mitigates at least one key-recovery attack. We've come pretty far, but we have at least one more problem.</p><p>Take another look at our formula for the ciphertext  <code>ctxt = (u,v)</code>. What would happen if we managed to recover the random vector <code>r</code>? That would be catastrophic, since <code>v == r.T*t + m</code>, and we already know <code>t</code> (part of the public key) and<code> v </code>(part of the ciphertext).</p><p>Just as we were able to compute the secret key from the public key in our initial scheme, we can recover the encryption randomness <code>r</code> from the ciphertext component <code>u</code> using Gaussian elimination. Again, this is just because <code>r</code> is the solution to a system of linear equations.</p><p>We can mitigate this plaintext-recovery attack just as before, by adding some noise. In particular, we'll generate a short vector according to <code>gen_mat(N,1,-beta,beta+1)</code> and add it into <code>u</code>. We also need to add noise to <code>v</code> in the same way, for reasons that we'll discuss in the next section.</p><p>Once again, adding noise increases the probability of a decryption error, but this time the magnitude of the error also depends on the secret key <code>s</code>. To see this, recall that during decryption, we multiply <code>u</code> by <code>s</code> (to compute the shared secret), and the error vector is an additive term. We'll therefore need <code>s</code> to be a short vector as well.</p><p>Let's now put together everything we've learned into an updated encryption scheme. Our scheme now has three parameters, <code>Q</code>, <code>N</code>, and <code>beta</code>, and can be used to encrypt a single bit:</p>
            <pre><code>def key_gen():
    A = gen_mat(N, N, 0, Q)
    s = gen_mat(N, 1, -beta, beta+1)
    e1 = gen_mat(N, 1, -beta, beta+1)
    t = (A*s + e1) % Q
    return ((A, t), s)

def encrypt(pk, ptxt):
    (A, t) = pk
    m = np.matrix([[ptxt*(Q//2) % Q]])
    r = gen_mat(N, 1, -beta, beta+1)
    e2 = gen_mat(N, 1, -beta, beta+1)
    e3 = gen_mat(1, 1, -beta, beta+1)
    u = (r.T*A + e2) % Q
    v = (r.T*t + e3 + m) % Q
    return (u, v)

def decrypt(sk, ctxt):
    s = sk
    (u, v) = ctxt
    m = (v - u*s) % Q
    if m[0,0] in range(Q//4, 3*Q//4):
        return 1
    return 0

# Test
assert decrypt(sk, encrypt(pk, 0)) == 0
assert decrypt(sk, encrypt(pk, 1)) == 1</code></pre>
            <p>Before moving on, try to find parameters for which the scheme works and for which the secret and error vectors seem hard to guess.</p>
    <div>
      <h5>Learning with errors</h5>
      <a href="#learning-with-errors">
        
      </a>
    </div>
    <p>So far we have a functioning encryption scheme for which we've mitigated two attacks, one a key-recovery attack and the other a plaintext-recovery attack. There seems to be no other obvious way of breaking our scheme, unless we choose parameters that are so weak that an attacker can easily guess the secret key s or ciphertext randomness r. Again, these vectors need to be short in order to prevent decryption errors, but not so short that they are easy to guess. (Likewise for the error terms.)</p><p>Still, there may be other attacks that require a little more sophistication to pull off. For instance, there might be some mathematical analysis we can do to recover, or at least make a good guess of, a portion of the ciphertext randomness. This raises a more fundamental question: in general, how do we establish that cryptosystems like this are actually secure?</p><p>As a first step, cryptographers like to try and reduce the attack surface. <b>Modern cryptosystems are designed so that the problem of attacking the scheme reduces to solving some other problem that is easier to reason about.</b></p><p>Our public key encryption scheme is an excellent illustration of this idea. Think back to the key- and plaintext-recovery attacks from the previous section. What do these attacks have in common?</p><p>In both instances, the attacker knows some public vector that allowed it to recover a secret vector:</p><ul><li><p>In the key-recovery attack, the attacker knew <code>t</code> for which <code>A*s == t.</code></p></li><li><p>In the plaintext-recovery attack, the attacker knew <code>u</code> for which <code>r.T*A == u </code>(or, equivalently,<code> A.T*r == u.T</code>).</p></li></ul><p>The fix in both cases was to construct the public vector in such a manner that it is hard to solve for the secret, namely, by adding an error term. However, ideally the public vector would reveal no information about the secret whatsoever. This ideal is formalized by the <b>Learning With Errors (LWE)</b> problem.</p><p>The LWE problem asks the attacker to distinguish between two distributions. Concretely, imagine we flip a coin, and if it comes up heads, we sample from the first distribution and give the sample to the attacker; and if the coin comes up tails, we sample from the second distribution and give the sample to the attacker. The distributions are as follows:</p><ul><li><p><code>(A,t=A*s + e</code>) where <code>A</code> is a random matrix generated with <code>gen_mat(N,N,0,Q)</code> and <code>s</code> and <code>e</code> are short vectors generated with <code>gen_mat(N,1,-beta,beta+1)</code>.</p></li><li><p><code>(A,t) </code>where <code>A</code> is a random matrix generated with <code>gen_mat(N,N,0,Q)</code> and <code>t</code> is a random vector generated with <code>gen_mat(N,1,0,Q)</code>.</p></li></ul><p>The first distribution corresponds to what we actually do in the encryption scheme; in the second, <code>t</code> is just a random vector, and no longer a secret vector at all. We say that the LWE problem is "hard" if no attacker is able to guess the coin flip with probability significantly better than one-half.</p><p>Our encryption is <i>passively </i>secure — meaning the ciphertext doesn't leak any information about the plaintext — if the LWE problem is hard for the parameters we chose. To see why, notice that both the public key and ciphertext look like LWE instances; if we can replace each instance with an instance of the random distribution, then the ciphertext would be completely independent of the plaintext and therefore leak no information about it at all. Note that, for this argument to go through, we also have to add the error term <code>e3</code> to the ciphertext component <code>v</code>.</p>
    <div>
      <h5>Choosing the parameters</h5>
      <a href="#choosing-the-parameters">
        
      </a>
    </div>
    <p>We've established that if solving the LWE problem is hard for parameters <code>N</code>, <code>Q</code>, and <code>beta</code>, then so is breaking our public key encryption scheme. What's left for us to do is tune the parameters so that solving LWE is beyond the reach of any attacker we can think of. This is where lattices come in.</p><h6>Lattices</h6><p>A <b>lattice </b>is an infinite grid of points in high-dimensional space. A two-dimensional lattice might look something like this:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Js1SkG7bWyCNwPxKaYf3i/277a665f0e44ec7594e7e11b35958bc8/image4.png" />
          </figure><p>The points always follow a clear pattern that resembles "lattice work" you might see in a garden:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5kOimwlM1guY2YMj9Gn9xu/62ee11ddde1e18ba0cf3151a0b87092e/image5.jpg" />
          </figure><p><sup><i>(Source: https://picryl.com/media/texture-wood-vintage-backgrounds-textures-8395bb)</i></sup></p><p>For cryptography, we care about a special class of lattices, those defined by a matrix <code>P</code> that "recognizes" points in the lattice. That is, the lattice recognized by <code>P</code> is the set of vectors <code>v</code> for which <code>P*v == 0</code>, where "<code>0</code>" denotes the all-zero vector. The all-zero vector is <code>np.zeros((N,1), dtype=int)</code> in NumPy.</p><p>Readers familiar with linear algebra may have a different definition of lattices in mind: in general, a lattice is the set of points obtained by taking linear combinations of some basis. Our lattices can also be formulated in this way, i.e., for a matrix <code>P</code> that recognizes a lattice, we can compute the basis vectors that generate the lattice. However, we don't much care about this representation here.</p><p>The LWE problem boils down to distinguishing a set of points that are "close to" the lattice from a set of points that are "far away from" the lattice. We construct these points from an LWE instance and a random <code>(A,t)</code> respectively. Here we have an LWE sample (left) and a sample from the random distribution (right):</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/45bmYu3jdn8g7cI1i7WC3t/c82617991463169ecd83b1b944aa879a/image10.png" />
          </figure><p>What this shows is that the points of the LWE instance are much closer to the lattice than the random instance. This is indeed the case on average. However, while distinguishing LWE instances from random is easy in two dimensions, it gets harder in higher dimensions.</p><p>Let's take a look at how we construct these points. First, let's take an LWE instance <code>(A,t=(A*s + e) % Q</code>) and consider the lattice recognized by the matrix <code>P</code> we get by concatenating <code>A</code> with the identity matrix <code>I</code>. This might look something like this (<code>N=3</code>):</p>
            <pre><code>A = gen_mat(N, N, 0, Q)
P = np.concatenate((A, np.identity(N, dtype=int)), axis=1)
print(P)
# [[1570  634  161	1	0	0]
#  [1522 1215  861	0	1	0]
#  [ 344 2651 1889	0	0	1]]</code></pre>
            <p>Notice that we can compute <code>t</code> by multiplying <code>P</code> by the vector we get by concatenating <code>s</code> and <code>e</code> (<code>beta=2</code>):</p>
            <pre><code>s = gen_mat(N, 1, -beta, beta+1)
e = gen_mat(N, 1, -beta, beta+1)
t = (A*s + e) % Q
z = np.concatenate((s, e))
print(z)
# [[-2]
#  [ 0]
#  [-2]
#  [ 0]
#  [-1]
#  [ 2]]
assert np.array_equal(t, P*z % Q)</code></pre>
            <p>Let <code>z</code> denote this vector and consider the set of points <code>v</code> for which <code>P*v == t</code>. By definition, we say this set of points is "close to" the lattice because <code>z</code> is a short vector. (Remember: by "short" we mean its entries are bounded around <code>0</code> by <code>beta</code>.)</p><p>Now consider a random<code> (A,t)</code> and consider the set of points <code>v</code> for which <code>P*v == t</code>. We won't prove it, but it is a fact that this set of points is likely to be "far away from" the lattice in the sense that there is no short vector <code>z</code> for which<code> P*z == t.</code></p><p>Intuitively, solving LWE gets harder as <code>z</code> gets longer. Indeed, increasing the average length of <code>z</code> (by making <code>beta</code> larger) increases the average distance to the lattice, making it look more like a random instance: </p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4oN8fydfP54b6EDLIOTYjI/85813f716b48164077b8f6d27586f768/unnamed__1_.png" />
          </figure><p>On the other hand, making <code>z</code> too long creates another problem.</p><h6>Breaking lattice cryptography by finding short vectors</h6><p>Given a random matrix <code>A</code>, the <b>Short Integer Solution (SIS)</b> problem is to find short vectors (i.e., whose entries are bounded by <code>beta</code>) <code>z1</code> and <code>z2</code> for which <code>(A*z1 + z2) % Q</code> is zero. Notice that this is equivalent to finding a short vector <code>z</code> in the lattice recognized by <code>P</code>:</p>
            <pre><code>z = np.concatenate((z1, z2))
assert np.array_equal((A*z1 + z2) % Q, P*z % Q)</code></pre>
            <p>If we had a (quantum) computer program for solving SIS, then we could use this program to solve LWE as well: if <code>(A,t) </code>is an LWE instance, then <code>z1.T*t </code>will be small; otherwise, if <code>(A,t)</code> is random, then <code>z1.T*t </code>will be uniformly random. (You can convince yourself of this using a little algebra.) Therefore, in order for our encryption scheme to be secure, it must be hard to find short vectors in the lattice defined by those parameters.</p><p>Intuitively, finding long vectors in the lattice is easier than finding short ones, which means that solving the SIS problem gets easier as <code>beta </code>gets closer to <code>Q</code>. On the other hand, as <code>beta </code> gets closer to <code>0</code>, it gets easier to distinguish LWE instances from random!</p><p><b>This suggests a kind of Goldilocks zone for LWE-based encryption</b>: if the secret and noise vectors are too short, then LWE is easy; but if the secret and noise vectors are too long, then SIS is easy. The optimal choice is somewhere in the middle.</p><h6>Enough math, just give me my parameters!</h6><p>To tune our encryption scheme, we want to choose parameters for which the most efficient known algorithms (quantum or classical) for solving LWE are out of reach for any attacker with as many resources as we can imagine (and then some, in case new algorithms are discovered). But how do we know which attacks to look out for?</p><p>Fortunately, the community of expert lattice cryptographers and cryptanalysts maintains a tool called <a href="https://github.com/malb/lattice-estimator"><u>lattice-estimator</u></a> that estimates the complexity of the best known (quantum) algorithms for lattice problems relevant to cryptography. Here's what we get when we run this tool for ML-KEM (this requires <a href="https://www.sagemath.org/"><u>Sage</u></a> to run):</p>
            <pre><code>sage: from estimator import *
sage: res = LWE.estimate.rough(schemes.Kyber768)
usvp        :: rop: ≈2^182.2, red: ≈2^182.2, δ: 1.002902, β: 624, d: 1427, tag: usvp
dual_hybrid :: rop: ≈2^174.3, red: ≈2^174.3, guess: ≈2^162.5, β: 597, p: 4, ζ: 10, t: 60, β': 597, N: ≈2^122.7, m: 768</code></pre>
            <p>The number that we're most interested in is "<code>rop</code>", which estimates the amount of computation the attack would consume. Playing with this tool a bit, we eventually find some parameters for our scheme for which the "<code>usvp</code>" and "<code>dual_hybrid</code>" attacks have comparable complexity. However, lattice-estimator identifies an attack it calls "<code>arora-gb</code>" that applies to our scheme, but not to ML-KEM, that has much lower complexity.  (<code>N=600</code>, <code>Q=3329</code>, and <code>beta=4</code>):</p>
            <pre><code>sage: res = LWE.estimate.rough(LWE.Parameters(n=600, q=3329, Xs=ND.Uniform(-4,4), Xe=ND.Uniform(-4,4)))
usvp        :: rop: ≈2^180.2, red: ≈2^180.2, δ: 1.002926, β: 617, d: 1246, tag: usvp
dual_hybrid :: rop: ≈2^226.2, red: ≈2^225.4, guess: ≈2^224.9, β: 599, p: 3, ζ: 10, t: 0, β': 599, N: ≈2^174.8, m: 600
arora-gb    :: rop: ≈2^129.4, dreg: 9, mem: ≈2^129.4, t: 4, m: ≈2^64.7</code></pre>
            <p>We'd have to bump the parameters even further to the scheme to a regime that has comparable security to ML-KEM.</p><p>Finally, a word of warning: when designing lattice cryptography, determining whether our scheme is secure requires a lot more than estimating the cost of generic attacks on our LWE parameters. In the absence of a mathematical proof of security in a realistic adversarial model, we can't rule out other ways of breaking our scheme. Tread lightly, fair traveler, and bring a friend along for the journey.</p>
    <div>
      <h4>Making the scheme efficient</h4>
      <a href="#making-the-scheme-efficient">
        
      </a>
    </div>
    <p>Now that we understand how to encrypt with LWE, let's take a quick look at how to make our scheme efficient.</p><p>The main problem with our scheme is that we can only encrypt a bit at a time. This is because we had to split the  <code>range(Q)</code> into two chunks, one that encodes <code>1</code> and another that encodes <code>0</code>. We could improve the bit rate by splitting the range into more chunks, but this would make decryption errors more likely.</p><p>Another problem with our scheme is that the runtime depends heavily on our security parameters. Encryption requires <code>O(N</code><code><sup>2</sup></code><code>)</code> multiplications (multiplication is the most expensive part of a secure implementation of modular arithmetic), and in order for our scheme to be secure, we need to make <code>N</code> quite large.</p><p>ML-KEM solves both of these problems by replacing modular arithmetic with arithmetic over a <a href="https://en.wikipedia.org/wiki/Polynomial_ring"><b><u>polynomial ring</u></b></a>. This means the entries of our matrices will be polynomials rather than integers. We need to define what it means to add, subtract, and multiply polynomials, but once we've done that, everything else about the encryption scheme is the same.</p><p>In fact, you probably learned polynomial arithmetic in grade school. The only thing you might not be familiar with is polynomial modular reduction. To multiply two polynomials $f(X)$ and $g(X)$, we start by multiplying $f(X)\cdot g(X)$ as usual. Then we're going to <b>divide </b>$f(X)\cdot g(X)$ by some special polynomial — ML-KEM uses $X^{256}+1$ — and take the remainder. We won't try to explain this algorithm, but the takeaway is that the result is a polynomial with $256$ coefficients, each of which is an integer in <code>range(Q)</code>.</p><p>The main advantage of using a polynomial ring for arithmetic is that we can pack more bits into the ciphertext. Our formula for the ciphertext is exactly the same (<code>u=r.T*A + e2, v=r.T*t + e3 + m</code>), but this time the plaintext <code>m </code>encodes a polynomial. Each coefficient of the polynomial encodes a bit, and we'll handle decryption errors just as we did before, by splitting <code>range(Q)</code> into two chunks, one that encodes <code>1</code> and another that encodes <code>0</code>. This allows us to reliably encrypt 256 bits (32 bytes) per ciphertext.</p><p>Another advantage of using polynomials is that it significantly reduces the dimension of the matrix without impacting security. Concretely, the most widely used variant of ML-KEM, ML-KEM-768, uses a <code>3</code>-by-<code>3</code> matrix <code>A</code>, so just <code>9 </code>polynomials in total. (Note that $256 \cdot 3 = 768$, hence the name "ML-KEM-768".) However, note that we have to be careful in how we choose the modulus: $X^{256}+1$ is special in that it does not exhibit any algebraic structure that is known to permit attacks.</p><p>The choices of <code>Q=3329</code> for the coefficient modulus and $X^{256}+1$ for the polynomial modulus have one more benefit. They allow polynomial multiplication to be carried out using the <a href="https://eprint.iacr.org/2024/585"><u>NTT algorithm</u></a>, which massively reduces the number of multiplications and additions we have to perform. In fact, this optimization is a major reason why ML-KEM is sometimes faster in terms of CPU time than key exchange with elliptic curves.</p><p>We won't get into how NTT works here, except to say that the algorithm will look familiar to you if you've ever implemented RSA. In both cases we use the <a href="https://en.wikipedia.org/wiki/Chinese_remainder_theorem"><u>Chinese Remainder Theorem</u></a> to split multiplication up into multiple, cheaper multiplications with smaller moduli.</p>
    <div>
      <h4>From public key encryption to ML-KEM</h4>
      <a href="#from-public-key-encryption-to-ml-kem">
        
      </a>
    </div>
    <p>The last step to build ML-KEM is to make the scheme secure against chosen ciphertext attacks (CCA). Currently, it's only secure against chosen plaintext attacks (CPA), which basically means that the ciphertext leaks no information about the plaintext, regardless of the distribution of plaintexts. CCA security is stronger in that it gives the attacker access to <a href="https://en.wikipedia.org/wiki/Adaptive_chosen-ciphertext_attack#Practical_attacks"><u>decryptions of ciphertexts of its choosing</u></a>. (Of course, it's not allowed to decrypt the target ciphertext itself.) The specific transform used in ML-KEM results in a CCA-secure KEM ("Key-Encapsulation Mechanism").</p><p>Chosen ciphertext attacks might seem a bit abstract, but in fact they formalize a realistic threat model for many applications of KEMs (and public key encryption for that matter). For example, suppose we use the scheme in a protocol in which the server authenticates itself to a client by proving it was able to decrypt a ciphertext generated by the client. In this kind of protocol, the server acts as a sort of "decryption oracle" in which its responses to clients depend on the secret key. Unless the scheme is CCA secure, this oracle can be abused by an attacker to leak information about the secret key over time, allowing it to eventually impersonate the server.</p><p>ML-KEM incorporates several more optimizations to make it as fast and as compact as possible. For example, instead of generating a random matrix <code>A</code>, we can derive it from a random, 32-byte string (called a "seed") using a hash-based primitive called a XOF ("eXtendable Output Function"), in the case of ML-KEM this XOF is <a href="https://pycryptodome.readthedocs.io/en/latest/src/hash/shake128.html"><u>SHAKE128</u></a>. This significantly reduces the size of the public key.</p><p>Another interesting optimization is that the polynomial coefficients (integers in <code>range(Q)</code>) in the ciphertext are compressed by rounding off the least significant bits of each coefficient, thereby reducing the overall size of the ciphertext.</p><p>All told, for the most widely deployed parameters (ML-KEM-768), the public key is 1184 bytes and the ciphertext is 1088 bytes. There's no obvious way to reduce this, except by reducing the size of the encapsulated key or the size of the public matrix <code>A</code>. The former would make ML-KEM useful for fewer applications, and the latter would reduce the security margin.</p><p>Note that there are <a href="https://eprint.iacr.org/2022/031"><u>other lattice schemes</u></a> that are smaller, but they are based on different hardness assumptions and are still undergoing analysis.</p>
    <div>
      <h3>Authentication</h3>
      <a href="#authentication">
        
      </a>
    </div>
    <p>In the previous section, we learned about ML-KEM, the algorithm already in use to make encryption PQ-secure. However, encryption is only one piece of the puzzle: establishing a secure connection also requires <b>authenticating</b> the server — and sometimes the client, depending on the application.</p><p>Authentication is usually provided by a <b>digital signature scheme</b>, which uses a secret key to sign a message and a public key to verify the signature. The signature schemes used today aren't PQ-secure: a quantum computer can be used to compute the secret key corresponding to a server's public key, then use this key to impersonate the server.</p><p>While this threat is less urgent than the threat to encryption, mitigating it is going to be more complicated. Over the years, we've bolted a number of signatures onto the <a href="https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/">TLS handshake</a> in order to meet the evolving requirements of the web <a href="https://en.wikipedia.org/wiki/Public_key_infrastructure"><u>PKI</u></a>. We have PQ alternatives for these signatures, one of which we'll study in this section, but so far these signatures and their public keys are too large (i.e., take up too many bytes) to make comfortable replacements for today's schemes. Barring some breakthrough in <a href="https://blog.cloudflare.com/another-look-at-pq-signatures/"><u>NIST's ongoing standardization effort</u></a>, we will have to re-engineer TLS and the web PKI to use fewer signatures.</p><p>For now, let's dive into the PQ signature scheme we're likely to see deployed first: <a href="https://csrc.nist.gov/pubs/fips/204/final"><u>ML-DSA</u></a>, a.k.a. Dillithium. The design of ML-DSA follows a similar template as ML-KEM. We start by building some intermediate primitive, then we transform that primitive into the primitive we want, in this case a signature scheme.</p><p>ML-DSA is quite a bit more involved than ML-KEM, so we're going to try to boil it down even further and just try to get across the main ideas.</p>
    <div>
      <h4>Warm up</h4>
      <a href="#warm-up">
        
      </a>
    </div>
    <p>Whereas ML-KEM is basically El Gamal encryption with elliptic curves replaced with lattices, ML-DSA is basically the <a href="https://www.zkdocs.com/docs/zkdocs/zero-knowledge-protocols/schnorr/"><u>Schnorr identification protocol</u></a> with elliptic curves replaced with lattices. Schnorr's protocol is used by a <b>prover </b>to convince a <b>verifier</b> that it knows the secret key associated with its public key without revealing the secret key itself. The protocol has three moves and is executed with four algorithms:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7lV5fU3j9rRQ2j2Y4lpW8r/a12908c078ec46e0fa35ef674fcfdb07/image8.png" />
          </figure><ol><li><p><code>initialize()</code>: The prover initializes the protocol and sends a <b>commitment</b> to the verifier</p></li><li><p><code>challenge()</code>:  The verifier receives the commitment and sends the prover a <b>challenge</b></p></li><li><p><code>finish()</code>: The prover receives the challenge and sends the verifier the <b>proof</b> </p></li><li><p><code>verify()</code>:  Finally, the verifier uses the proof to decide whether the prover knows the secret key</p></li></ol><p>We get the high-level structure of ML-DSA by making this protocol non-interactive. In particular, the prover derives the challenge itself by hashing the commitment together with the message to be signed. The signature consists of the commitment and proof: to verify the signature, the verifier recomputes the challenge from the commitment and message and runs <code>verify()</code>as usual.</p><p>Let's jump right in to building Schnorr's identification protocol from lattices. If you've never seen this protocol before, then this will look a little like black magic at first. We'll go through it slowly enough to see how and why it works.</p><p>Just like for ML-KEM, our public key is an LWE instance <code>(A,t=A*s1 + s2)</code>. However, this time our secret key is the <i>pair</i> of short vectors <code>(s1,s2)</code>, i.e., it includes the error term. Otherwise, key generation is exactly the same:</p>
            <pre><code>def key_gen():
    A = gen_mat(N, N, 0, Q)
    s1 = gen_mat(N, 1, -beta, beta+1)
    s2 = gen_mat(N, 1, -beta, beta+1)
    t = (A*s1 + s2) % Q
    return ((A, t), (s1, s2))</code></pre>
            <p>To initialize the protocol, the prover generates another LWE instance <code>(A,w=A*y1 + y2)</code>. You'll see why in just a moment. The prover sends the <i>hash</i> of <code>w</code> as its commitment:</p>
            <pre><code>def initialize(A):
    y1 = gen_mat(N, 1, -beta, beta+1)
    y2 = gen_mat(N, 1, -beta, beta+1)
    w = (A*y1 + y2) % Q
    return (H(w), (y1, y2))</code></pre>
            <p>Here <code>H </code>is some cryptographic hash function, like <a href="https://en.wikipedia.org/wiki/SHA-3"><u>SHA-3</u></a>. The prover stores the secret vectors<code> (y1,y2)</code> for use in its next move.</p><p>Now it's time for the verifier's challenge. The challenge is just an integer, but we need to be careful about how we choose it. For now let's just pick it at random:</p>
            <pre><code>def challenge():
    return random.randrange(0, Q)</code></pre>
            <p>Remember: when we turn this protocol into a digital signature, the challenge is derived from the commitment, <code>H(w)</code>, and the message. The range of this hash function must be the same as the set of outputs of <code>challenge()</code>.</p><p>Now comes the fun part. The proof is a pair of vectors<code> (z1,z2)</code> satisfying <code>A*z1 + z2 == c*t + w</code>. We can easily produce this proof if we know the secret key:</p><p>	<code>z1 = (c*s1 + y1) % Q</code></p><p>        <code>z2 = (c*s2 + y2) % Q</code></p><p>Then <code>A*z1 + z2 == A*(c*s1 + y1) + (c*s2 + y2) == c*(A*s1 + s2) + (A*y1 + y2) == c*t + w</code>. Our goal is to design the protocol such that it's hard to come up with <code>(z1,z2)</code> without knowing <code>(s1,s2)</code>, even after observing many executions of the protocol.</p><p>Here are the <code>finish() </code>and <code>verify() </code>algorithms for completeness:</p>
            <pre><code>def finish(s1, s2, y1, y2, c):
    z1 = (c*s1 + y1) % Q
    z2 = (c*s2 + y2) % Q
    return (z1, z2)

def verify(A, t, hw, c, z1, z2):
	return H((A*z1 + z2 - c*t) % Q) == hw

# Test
((A, t), (s1, s2)) = key_gen()
(hw, (y1, y2)) = initialize(A)        # hw: prover -&gt; verifier
c = challenge()                       # c: verifier -&gt; prover
(z1, z2) = finish(s1, s2, y1, y2, c)  # (z1, z2): prover -&gt; verifier
assert verify(A, t, hw, c, z1, z2)    # verifier</code></pre>
            <p>Notice that the verifier doesn't actually check <code>A*z1 + z2 == c*t + w</code> directly; we have to rearrange the equation so that we can set the commitment to <code>H(w)</code> rather than <code>w</code>. We'll explain the need for hashing in the next section.</p>
    <div>
      <h4>Making this scheme secure</h4>
      <a href="#making-this-scheme-secure">
        
      </a>
    </div>
    <p>The question of whether this protocol is secure boils down to whether it's possible to impersonate the prover without knowledge of the secret key. Let's put our attacker hat on and poke around.</p><p>Perhaps there's a way to compute the secret key, either from the public key directly or by eavesdropping on executions of the protocol with the honest prover. If LWE is hard, then clearly there's no way we're going to extract the secret key from the public key <code>t</code>. Likewise, the commitment <code>H(w)</code>doesn't leak any information that would help us extract the secret key from the proof<code> (z1,z2)</code>.</p><p>Let's take a closer look at the proof. Notice that the vectors <code>(y1,y2)</code> "mask" the secret key vectors, sort of how the shared secret masks the plaintext in ML-KEM. However, there's one big exception: we also scale the secret key vectors by the challenge <code>c</code>.</p><p>What's the effect of scaling these vectors? If we squint at a few proofs, we start to see a pattern emerge. Let's look at <code>z1</code> first (<code>N=3, Q=3329, beta=4</code>):</p>
            <pre><code>((A, t), (s1, s2)) = key_gen()
print('s1={}'.format(s1.T % Q))
for _ in range(10):
    (w, (y1, y2)) = initialize(A)
    c = challenge()
    (z1, z2) = finish(s1, s2, y1, y2, c)
    print('c={}, z1={}'.format(c, z1.T))
# s1=[[   1	0 3326]]
# c=1123, z1=[[1121 3327 3287]]
# c=1064, z1=[[1060	4  137]]
# c=1885, z1=[[1884 3327  999]]
# c=269, z1=[[ 270 3325 2524]]
# c=1506, z1=[[1510 3325 2141]]
# c=3147, z1=[[3149	4  547]]
# c=703, z1=[[ 700	4 1219]]
# c=1518, z1=[[1518 3327 2104]]
# c=1726, z1=[[1726	0 1478]]
# c=2591, z1=[[2589	4 2217]]</code></pre>
            <p>Indeed, with enough proof samples, we should be able to make a pretty good guess of the value of <code>s1</code>. In fact, for these parameters, there is a simple statistical analysis we can do to compute <code>s1</code> exactly. (Hint: <code>Q</code> is a prime number, which means <code>c*pow(c,-1,Q)==1 </code>whenever <code>c&gt;0</code>.) We can also apply this analysis to <code>s2</code>, or compute it directly from <code>t</code>, <code>s1</code>, and <code>A</code>.</p><p>The main flaw in our protocol is that, although our secret vectors are short, scaling them makes them so long that they're not completely masked by<code> (y1,y2)</code>. Since <code>c</code> spans the entire <code>range(Q)</code>, so do the entries of <code>c*s1.</code> and <code>c*s2,</code> which means in order to mask these entries, we need the entries of <code>(y1,y2) </code>to span <code>range(Q)</code> as well. However, doing this would make solving LWE for <code>(A,w)</code> easy, by solving SIS. We somehow need to strike a balance between the length of the vectors of our LWE instances and the leakage induced by the challenge.</p><p>Here's where things get tricky. Let's refer to the set of possible outputs of <code>challenge() </code>as the <b>challenge space. </b>We need the challenge space to be fairly large, large enough that the probability of outputting the same challenge twice is negligible.</p><p>Why would such a collision be a problem? It's a little easier to see in the context of digital signatures. Let's say an attacker knows a valid signature for a message <code>m</code>. The signature includes the commitment <code>H(m)</code>, so the attacker also knows the challenge is <code>c == H(H(w),m)</code>. Suppose it manages to find a different message <code>m</code><code><sup>*</sup></code> for which <code>c == H(H(w),m</code><code><sup>*</sup></code><code>)</code>. Then the signature is also valid for m! And this attack is easy to pull off if the challenge space, that is, the set of possible outputs of <code>H</code>, is too small.</p><p>Unfortunately, we can't make the challenge space larger simply by increasing the size of the modulus <code>Q</code>: the larger the challenge might be, the more information we'd leak about the secret key. We need a new idea.</p>
    <div>
      <h5>The best of both worlds</h5>
      <a href="#the-best-of-both-worlds">
        
      </a>
    </div>
    <p>Remember that the hardness of LWE depends on the ratio between <code>beta</code> and <code>Q</code>. This means that <code>y1</code> and <code>y2</code> don't need to be short in absolute terms, but short relative to random vectors.</p><p>With that in mind, consider the following idea. Let's take a larger modulus, say <code>Q=2**31 - 1</code>, and we'll continue to sample from the same challenge space, <code>range(2**16)</code>.</p><p>First, notice that <code>z1</code> is now "relatively" short, since its entries are now in <code>range(-gamma, gamma+1)</code>, where <code>gamma = beta*(2**16-1),</code> rather than uniform over <code>range(Q)</code>. Let's also modify <code>initialize()</code> to sample the entries of <code>(y1,y2)</code> from the same range and see what happens:</p>
            <pre><code>def initialize(A):
	y1 = gen_mat(N, 1, -gamma, gamma+1)
	y2 = gen_mat(N, 1, -gamma, gamma+1)
	w = (A*y1 + y2) % Q
	return (H(w), (y1, y2))

((A, t), (s1, s2)) = key_gen()
print('s1={}'.format(s1.T % Q))
for _ in range(10):
    (w, (y1, y2)) = initialize(A)
    c = challenge()
    (z1, z2) = finish(s1, s2, y1, y2, c)
    print('c={}, z1={}'.format(c, z1.T))
# s1=[[3 0 1]]
# c=31476, z1=[[175933 141954  93186]]
# c=27360, z1=[[    136404 2147438807     283758]]
# c=33536, z1=[[2147430945 2147377022     190671]]
# c=23283, z1=[[186516  73400   4955]]
# c=24756, z1=[[    328377 2147438906 2147388768]]
# c=12428, z1=[[2147340715     188675      90282]]
# c=24266, z1=[[    175498 2147261581 2147301553]]
# c=45331, z1=[[357595 185269 177155]]
# c=45641, z1=[[     21592 2147249191 2147446200]]
# c=57893, z1=[[297750 113335 144894]]</code></pre>
            <p>This is definitely going in the right direction, since there are no obvious correlations between <code>z1</code> and <code>s1</code>. (Likewise for <code>z2</code> and <code>s2</code>.) However, we're not quite there.</p><p>One problem is that the challenge space is still quite small. With only <code>2**16 </code>challenges to choose from, we're likely to see a collision even after only a handful of protocol executions. We need the challenge space to be much, much larger, say around <code>2**256</code>. But then <code>Q</code> has to be an insanely large number in order for the <code>beta</code> to <code>Q</code> ratio to be secure.</p><p>ML-DSA is able to side step this problem due to its use of arithmetic over polynomial rings. It uses the same modulus polynomial as ML-KEM, so the challenge is a polynomial with 256 coefficients. The coefficients are chosen carefully so that the challenge space is large, but multiplication by the challenge scales the secret vector by a small amount. Note that we still end up using a slightly larger modulus (<code>Q=8380417</code>) for ML-DSA than for ML-KEM, but only by about twelve bits.</p><p>However, there is a more fundamental problem here, which is that we haven't completely ruled out that signatures may leak information about the secret key.</p>
    <div>
      <h5>Cause and effect</h5>
      <a href="#cause-and-effect">
        
      </a>
    </div>
    <p>Suppose we run the protocol a number of times, and in each run, we happen to choose a relatively small value for some entry of <code>y1</code>. After enough runs, this would eventually allow us to reconstruct the corresponding entry of <code>s1</code>. To rule this out as a possibility, we need to make <code>y1 </code>even longer. (Likewise for <code>y2</code>.) But how long?</p><p>Suppose we know that the entries of <code>z1</code> and <code>z2</code> are always in <code>range(-beta_loose,beta_loose+1) </code>for some <code>beta_loose &gt; beta</code>. Then we can <b>simulate </b>an honest run of the protocol as follows:</p>
            <pre><code>def simulate(A, t):
    z1 = gen_mat(N, 1, -beta_loose, beta_loose+1)
    z2 = gen_mat(N, 1, -beta_loose, beta_loose+1)
    c = challenge()
    w = (A*z1 + z2 - c*t) % Q
    return (H(w), c, (z1, z2))

# Test
((A, t), (s1, s2)) = key_gen()
(hw, c, (z1, z2)) = simulate(A, t)
assert verify(A, t, hw, c, z1, z2)</code></pre>
            <p>This procedure perfectly simulates honest runs of the protocol, in the sense that the output of <code>simulate() </code>is indistinguishable from the transcript of a real run of the protocol with the honest prover. To see this, notice that the <code>w</code>,<code> c</code>, <code>z1</code>, and <code>z2 </code>all have the same mathematical relationship (the verification equation still holds) and have the same distribution.</p><p>And here's the punch line: since this procedure doesn't use the secret key, it follows that the attacker learns nothing from eavesdropping on the honest prover that it can't compute from the public key itself. Pretty neat!</p><p>What's left to do is arrange for <code>z1</code> and <code>z2</code> to fall in this range. First, we modify <code>initialize() </code>by increasing the range of <code>y1 </code>and<code> y2 </code>by<code> beta_loose</code>:</p>
            <pre><code>def initialize(A):
    y1 = gen_mat(N, 1, -gamma+beta_loose, gamma+beta_loose+1)
    y2 = gen_mat(N, 1, -gamma+beta_loose, gamma+beta_loose+1)
    w = (A*y1 + y2) % Q
    return (H(w), (y1, y2))</code></pre>
            <p>This ensures the proof vectors <code>z1</code> and <code>z2 </code>are roughly uniform over r<code>ange(-beta_loose, beta_loose+1)</code>. However, they may fall slightly outside of this range, so need to modify <code>finalize() </code>to <b>abort </b>if not. Correspondingly, <code>verify() </code>should reject proof vectors that are out of range:</p>
            <pre><code>def finish(s1, s2, y1, y2, c):
    z1 = (c*s1 + y1) % Q
    z2 = (c*s2 + y2) % Q
    if not in_range(z1, beta_loose) or not in_range(z2, beta_loose):
        return (None, None)
    return (z1, z2)

def verify(A, t, hw, c, z1, z2):
    if not in_range(z1, beta_loose) or not in_range(z2, beta_loose):
        return False
    return H((A*z1 + z2 - c*t) % Q) == hw</code></pre>
            <p>If <code>finish()</code> returns <code>(None,None)</code>, then the prover and verifier are meant to abort the protocol and retry until the protocol succeeds:</p>
            <pre><code>((A, t), (s1, s2)) = key_gen()
while True:
    (hw, (y1, y2)) = initialize(A)        # hw: prover -&gt; verifier
    c = challenge()                       # c: verifier -&gt; prover
    (z1, z2) = finish(s1, s2, y1, y2, c)  # (z1, z2): prover -&gt; verifier
    if z1 is not None and z2 is not None:
        break
assert verify(A, t, hw, c, z1, z2)</code></pre>
            <p>Interestingly, we should expect aborts to be quite common. The parameters of ML-DSA are tuned so that the protocol runs five times on average before it succeeds.</p><p>Another interesting point is that the security proof requires us to simulate not only successful protocol runs, but aborted protocol runs as well. More specifically, the protocol simulator must abort with the same probability as the real protocol, which implies that the rejection probability is <i>independent </i>of the secret key.</p><p>The simulator also needs to be able to produce realistic looking commitments for aborted transcripts. This is exactly why the prover commits to the <i>hash</i> of <code>w </code>rather than <code>w </code> itself: in the security proof, we can easily simulate hashes of random inputs.</p>
    <div>
      <h4>Making this scheme efficient</h4>
      <a href="#making-this-scheme-efficient">
        
      </a>
    </div>
    <p>ML-DSA benefits from many of the same optimizations as ML-KEM, including using polynomial rings, NTT for polynomial multiplication, and encoding polynomials with a fixed number of bits. However, ML-DSA has a few more tricks to make things smaller.</p><p>First, in ML-DSA, instead of the pair of short vectors <code>z1 </code>and <code>z2</code>, the proof consists of a single vector <code>z=c*s1 + y,</code> where <code>y</code> was committed to in the previous step. In turn, we only end up with a single proof vector <code>z</code> rather than two as before. Getting this to work requires a special encoding of the commitment so that we can't compute <code>y</code> from it. ML-DSA uses a related trick to reduce the size of the <code>t</code> vector of the public key, but the details are more complicated.</p><p>For the parameters we expect to deploy first (ML-DSA-44), the public key is 1312 bytes long and the signature is a whopping 2420 bytes. In contrast to ML-KEM, it is possible to shave off some more bytes. This does not come for free and requires complicating the scheme. An example is <a href="https://eprint.iacr.org/2023/624.pdf"><u>HAETAE</u></a>, which changes the distributions used. <a href="https://github.com/pornin/rust-fn-dsa"><u>Falcon</u></a> takes it a step further with even smaller signatures, using a completely different approach, which although elegant is also more complex to implement.</p>
    <div>
      <h3>Wrap up</h3>
      <a href="#wrap-up">
        
      </a>
    </div>
    <p>Lattice cryptography underpins the first generation of PQ algorithms to get widely deployed on the Internet. ML-KEM is already widely used today to protect encryption from quantum computers, and in the coming years we expect to see ML-DSA deployed to get ahead of the threat of quantum computers to authentication.</p><p>Lattices are also the basis of a new frontier for cryptography: computing on encrypted data.</p><p>Suppose you wanted to aggregate some metrics submitted by clients without learning the metrics themselves. <a href="https://eprint.iacr.org/2024/936"><u>With LWE-based encryption</u></a>, you can arrange for each client to encrypt their metrics before submission, aggregate the ciphertexts, then decrypt to get the aggregate.</p><p>Suppose instead that a server has a database that it wants to provide clients access to without revealing to the server which rows of the database the client wants to query. <a href="https://eprint.iacr.org/2022/949"><u>LWE-based encryption</u></a> allows the database to be encoded in a manner that permits encrypted queries.</p><p>These applications are special cases of a paradigm known as <a href="https://en.wikipedia.org/wiki/Homomorphic_encryption"><u>FHE</u></a> ("Fully Homomorphic Encryption"), which allows for arbitrary computations on encrypted data. FHE is an extremely powerful primitive, and the only way we know how to build it today is with lattices. However, for most applications, FHE is far less practical than a special-purpose protocol would be (lattice-based or not). Still, over the years we've seen FHE get better and better, and for many applications it is already a decent option. Perhaps we'll dig into this and other lattice schemes in a future blog post.</p><p>We hope you enjoyed this whirlwind tour of lattices. Thanks for reading!</p> ]]></content:encoded>
            <category><![CDATA[Security Week]]></category>
            <category><![CDATA[Cryptography]]></category>
            <category><![CDATA[Post-Quantum]]></category>
            <category><![CDATA[Research]]></category>
            <guid isPermaLink="false">01euRoOpkvsq16eKrMZ6hu</guid>
            <dc:creator>Christopher Patton</dc:creator>
            <dc:creator>Peter Schwabe (Guest Author)</dc:creator>
        </item>
        <item>
            <title><![CDATA[An early look at cryptographic watermarks for AI-generated content]]></title>
            <link>https://blog.cloudflare.com/an-early-look-at-cryptographic-watermarks-for-ai-generated-content/</link>
            <pubDate>Wed, 19 Mar 2025 13:00:00 GMT</pubDate>
            <description><![CDATA[ It's hard to tell the difference between web content produced by humans and web content produced by AI. We're taking new approach to making AI content distinguishable without impacting performance.  ]]></description>
            <content:encoded><![CDATA[ <p><a href="https://www.cloudflare.com/learning/ai/what-is-generative-ai/">Generative AI</a> is reshaping many aspects of our lives, from how we work and learn, to how we play and interact. Given that it's Security Week, it's a good time to think about some of the unintended consequences of this information revolution and the role that we play in bringing them about.</p><p>Today's web is full of AI-generated content: text, code, <a href="https://www.cloudflare.com/learning/ai/ai-image-generation/">images</a>, audio, and video can all be generated by machines, normally based on a prompt from a human. Some models have become so sophisticated that distinguishing their artifacts — that is, the text, audio, and video they generate — from everything else can be quite difficult, <a href="https://originality.ai/blog/openai-text-classifier-review"><u>even for machines themselves</u></a>. This difficulty creates a number of challenges. On the one hand, those who train and deploy generative AI need to be able to identify AI-created artifacts they scrape from websites in order <a href="https://www.cloudflare.com/learning/ai/how-to-secure-training-data-against-ai-data-leaks/"><u>to avoid polluting their training data</u></a>. On the other hand, the origin of these artifacts may be intentionally misrepresented, creating <a href="https://www.gov.ca.gov/2024/09/19/governor-newsom-signs-bills-to-crack-down-on-sexually-explicit-deepfakes-require-ai-watermarking/"><u>myriad problems</u></a> for society writ large.</p><p>Part of the solution to this problem might be <b>watermarking</b>. The basic idea of watermarking is to modify the training process, the inference process, or both so that an artifact of the model embeds some identifying information of the model from which it originates. This way a model operator, or potentially the consumer of the content themselves, can determine whether some artifact came from the model by checking for the presence of the watermark.</p><p>Watermarking shares many of the same goals as the <a href="https://c2pa.org/"><u>C2PA initiative</u></a>. C2PA seeks to add provenance of media from a variety of sources, not just AI. Think of it as a chain of digital signatures, where each link in the chain corresponds to some modification of the artifact. For example, if you're a Cloudflare customer using <a href="https://www.cloudflare.com/developer-platform/products/cloudflare-images/"><u>Images</u></a> to serve C2PA-tagged content, <a href="https://blog.cloudflare.com/preserve-content-credentials-with-cloudflare-images/"><u>you can opt in to preserve the provenance</u></a> by extending this signature chain, even after the image is compressed on our network.</p><p>The challenge of this approach is that it requires participation by each entity in the chain of custody of the artifact. Watermarking has the potential to make C2PA more robust by preserving the origin of the artifact even after unattributed modification. Whereas the C2PA signature is encoded in an image’s metadata, a watermark is embedded in the pixels of the image itself.</p><p>In this post, we're going to take a look at an emerging paradigm for AI watermarking. Based on cryptography, these new watermarks aim to provide rigorous, mathematical guarantees of quality preservation and robustness to modification of the content. This field is, as of 2025, only a couple of years old, and we don't yet know if it will yield schemes that are practical to deploy. Nevertheless, we believe this is a promising area of research, and we hope this post inspires someone to come up with the next big idea.</p>
    <div>
      <h3>The case for cryptography</h3>
      <a href="#the-case-for-cryptography">
        
      </a>
    </div>
    <p>It's often said that cryptography is necessary but not sufficient for security. In other words, cryptographers make certain assumptions about the state of the system, like that a key is kept secret from the attacker, or that some computational puzzle is hard to solve. When these assumptions hold, a good cryptosystem provides a mathematical proof of security against the class of attacks it is designed to prevent.</p><p>Artifact watermarking usually has three security goals:</p><ol><li><p>Robustness: Users of the model should not be able to easily misrepresent the origin of its artifacts. The watermark should be verifiable even after the artifact is modified to some extent.</p></li><li><p>Undetectability: Watermarking should have negligible impact on the quality of the model output. In particular, watermarked artifacts should be indistinguishable from non-watermarked artifacts of the same model.</p></li><li><p>Unforgeability: It should be impossible for anyone but the model operator to produce watermarked artifacts. No one should be able to convince the model operator that an artifact was generated by the model when it wasn't.</p></li></ol><p>Today's state-of-the-art watermarks, including Google's <a href="https://deepmind.google/technologies/synthid/"><u>SynthID</u></a> and Meta's <a href="https://aidemos.meta.com/videoseal/"><u>Video Seal</u></a>, are often based on <a href="https://www.cloudflare.com/learning/ai/what-is-deep-learning/"><u>deep learning</u></a>. These schemes involve training a <a href="https://www.cloudflare.com/learning/ai/what-is-machine-learning/">machine learning model</a>, typically one with an encoder–decoder architecture where the encoder encodes a signature into an artifact and the decoder decodes the signature:</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/wk6vE9ArPOK7EXP2otytf/f847db73272c932ba546d8a6b3176480/1.png" />
          </figure><p><sup><i>Figure 1: Illustration of the process of training an encoder-decoder watermarking model.</i></sup></p><p>The training process involves subjecting the watermarked artifact to a series of known attacks. The more attacks the model thwarts, the higher the model quality. For example, the trainer would alter the artifact in various ways and run the decoder on the outputs: the model scores high if the decoder manages to correctly output the signature most of the time.</p><p>This idea is quite beautiful. It's like a scaled up version of <a href="https://www.cloudflare.com/learning/security/glossary/what-is-penetration-testing/">penetration testing</a>, an essential practice of security engineering whereby the system is subjected to a suite of known attacks until all known vulnerabilities are patched. Of course, there will always be new attack variants or new attack strategies that the model was not trained on and that may evade the model.</p><p>And so ensues the proverbial game of cat-and-mouse that consumes so much time in security engineering. Coping with new attacks on robustness, undetectability, or unforgeability of deep-learning based watermarks requires continual intelligence gathering and re-training of deployed models to keep up with attackers.</p><p>The promise of cryptography is that it helps us break out of these kinds of cat-and-mouse games. Cryptography reduces the attack surface by focusing the attacker's attention on breaking some narrow aspect of the system that is easier to reason about. This might be gaining access to some secret key, or solving some (seemingly unrelated) computational puzzle that is believed to be impossible to solve.</p>
    <div>
      <h3>Pseudorandom codes</h3>
      <a href="#pseudorandom-codes">
        
      </a>
    </div>
    <p>To the best of our knowledge, the first cryptographic AI watermark was proposed by Scott Aaronson <a href="https://scottaaronson.blog/?p=6823"><u>in the summer of 2022 while he was working at OpenAI</u></a>. Tailored specifically for chatbots, Aaronson's simple scheme was both undetectable and unforgeable. However, it was susceptible to some simple attacks on robustness.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4XcSE2infymYTGywob2XlS/b79819f48b58c26a7c1713a89879d412/2.png" />
          </figure><p><sup><i>Figure 2: The "emoji attack": Ask the chatbot to embed a simple pattern in its response, then remove the pattern manually. This is sufficient to remove some cryptographic watermarks from the model output.</i></sup></p><p>In the year or so that followed, other cryptographic watermarks were proposed, all making different trade-offs between detectability and robustness. Two years later, in a paper that appeared at <a href="https://eprint.iacr.org/2024/235"><u>CRYPTO 2024</u></a>, Miranda Christ and Sam Gunn articulated a new framework for watermarks that, if properly instantiated, would provide all three properties simultaneously.</p><p>Along with the prompt provided by the user, generative AI models typically take as input some randomness generated by the model operator. For many such models, it is often possible to run the model "in reverse" such that, given an artifact of the model, one can recover <b>an approximation of</b> the initial randomness used to generate it. We'll see why this is important in a moment.</p><p>The starting point for Christ-Gunn-2024 is a mathematical tool called an <a href="https://en.wikipedia.org/wiki/Error_correction_code"><u>error correcting code</u></a>. These codes are normally used to transmit messages over noisy channels and are found in just about every system we rely on, including fiber optics, satellites, the data bus on your motherboard, and even quantum computers.</p><p>To transmit a message <code>m</code>, one first encodes it into a <b>codeword</b> <code>c=encode(m)</code>. Then the receiver attempts to decode the codeword as <code>decode(c)</code>. Error correcting codes are designed to tolerate some fraction of the codeword bits being flipped: if too many bits are flipped, then decoding will fail.</p><p>Now, ignoring undetectability and unforgeability for a moment, we can use an error correcting code to make a robust watermark as follows:</p><ol><li><p>Generate the initial randomness.</p></li><li><p>Embed a codeword <code>c=encode(m)</code> into the randomness in some manner, by overwriting bits of randomness with bits of <code>c</code>. The message <code>m</code> can be whatever we want, for example a short string identifying the version of our model.</p></li><li><p>Run the model with the modified randomness.</p></li></ol><p>To verify the watermark, we:</p><ol><li><p>Run the model "in reverse" on the artifact, obtaining an approximation of the initial randomness.</p></li><li><p>Extract the codeword <code>c</code><code><sup>*</sup></code> from the randomness.</p></li><li><p>If <code>decode(c</code><code><sup>*</sup></code><code>) </code>succeeds, then the watermark is present.</p></li></ol><p>Why does this work? Since c is a codeword, we can verify the watermark even if our approximation of the initial randomness isn't perfect. Some of the bits will be flipped, but the error correcting property of the code allows us to compensate for this. In fact, this is also what makes the watermark robust, since we can also tolerate bit flips caused by an attacker munging the artifact. Of course, the better our approximation of the initial randomness, the more robust our watermark will be, since we'll be able to correct for more bit flips.</p><p>To see why this watermark is detectable, notice that overwriting bits of the randomness with a fixed codeword (<code>c=encode(m)</code>) biases the randomness and thereby the output of the model. Thus, the distribution of watermarked artifacts will be slightly different from unwatermarked artifacts, perhaps even noticeably so. This watermark is also forgeable, since the encoding algorithm is public and can be run by anyone.</p><p>The challenge then is to design error-correcting codes for which codewords look random, and generating codewords requires knowledge of a secret key held by the model operator. Christ-Gunn-2024 names these <b>pseudorandom error-correcting codes</b>, or simply <b>pseudorandom codes</b>.</p><p>A pseudorandom code consists of three algorithms:</p><ul><li><p><code>k = key_gen()</code>: the key generation algorithm. Let's call <code>k</code> the <b>watermarking key</b>.</p></li><li><p><code>c = encode(k,m)</code>: the encoding algorithm takes in a message <code>m</code> and outputs a codeword <code>c</code>.</p></li><li><p><code>m = decode(k,c)</code>: the decoding algorithm takes in a codeword <code>c</code> and outputs the underlying message <code>m</code>, or an indication that decoding failed.</p></li></ul><p>The term "pseudorandom" refers to the fact that codewords aren't technically random bit strings. Intuitively, an attacker can distinguish a codeword from a random string if it manages to guess the watermarking key. Thus, our goal is to choose parameters for the code such that distinguishing codewords from random — for example, by guessing the watermarking key — is hard for any computationally bounded attacker.</p><p>To use a pseudorandom code for watermarking, the operator first generates a watermarking key <code>k</code>. Then each time it gets a prompt from a user, it generates the initial randomness, embeds <code>c=encode(k,m)</code> into the initial randomness, and runs the model. To verify the watermark, the operator runs the model in reverse to get the inverted randomness, extracts the inverted codeword, <code>c</code><code><sup>*</sup></code>, and computes <code>decode(k,c</code><code><sup>*</sup></code><code>)</code>: if decoding succeeds, then the watermark is present.</p><p>In order for this watermark to be undetectable, we need to pick an embedding of the codeword that doesn't change the distribution of the initial randomness. The details of this embedding depend on the model. Let's take a look at <a href="https://en.wikipedia.org/wiki/Stable_Diffusion"><u>Stable Diffusion</u></a> as an example.</p>
    <div>
      <h3>A watermark for Stable Diffusion</h3>
      <a href="#a-watermark-for-stable-diffusion">
        
      </a>
    </div>
    <p><a href="https://en.wikipedia.org/wiki/Stable_Diffusion"><u>Stable Diffusion</u></a> is a model used for image generation that takes as input a tensor of normally distributed floating point numbers called a <b>latent</b>. The model uses the user's prompt to "denoise" the latent tensor over a number of iterations, then converts the final version of the latent to an image.</p>
    <div>
      <h4>Approximating the initial latent</h4>
      <a href="#approximating-the-initial-latent">
        
      </a>
    </div>
    <p>Diffusion inversion is an iterative process that returns an exact or approximate initial latent by reversing the sampling process that generated an image. Inversion for text to image diffusion models is a relatively new area of research. A common application of diffusion inversion is <a href="https://arxiv.org/abs/2211.09794"><u>editing images by text prompts</u></a>.</p><p><a href="https://arxiv.org/abs/2010.02502"><u>Denoising Diffusion Implicit Models (DDIMs)</u></a> are iterative, implicit probabilistic models that can generate high quality images using a faster sampling process than other approaches, as it only requires a relatively small number of timesteps to generate a sample. This makes <a href="https://arxiv.org/abs/2211.09794"><u>DDIM Inversion</u></a> a popular inversion technique because it is computationally fast, as it requires only a few timesteps to return an approximate initial latent of a generated image. Despite its popularity, it has some known limitations and can be <a href="https://arxiv.org/abs/2312.12540v5"><u>problematic to use for tasks where exact image reproduction is required</u></a>. These limitations have led researchers to explore techniques that produce <a href="https://arxiv.org/abs/2311.18387"><u>exact initial latents</u></a>. However, since watermarks based on pseudorandom codes can tolerate errors, it's worth investigating whether DDIM Inversion suffices for our purposes.</p><p>Before we can generate an approximate initial latent, we need a generated image. To do this we use a pretrained Stable Diffusion model that uses a DDIM scheduler. The scheduler performs the “denoising” process that generates an image from a random noise seed (initial latent). By default, the pipeline computes random latents; when embedding a watermark we will generate this latent ourselves as described in the next section. The Stable Diffusion pipelines in the code snippets below sets the number of inference steps to 50. This parameter controls the number of steps the denoising process takes. 50 steps provides a nice balance between speed and image quality.</p>
            <pre><code>from stable_diffusion.utils import build_stable_diffusion_pipeline
from stable_diffusion.schedulers import ddim_scheduler

# Instantiate Stable Diffusion pipeline
model_cache_path = './model_cache'
model = 'stabilityai/stable-diffusion-2-1-base'
scheduler, _ = ddim_scheduler()
pipe, device = build_stable_diffusion_pipeline(model, model_cache_path, scheduler)

# Generate image
prompt = 'grainy photo of a UFO at night'
image, _ = pipe(prompt, num_inference_steps=50, return_dict=False)</code></pre>
            <p>To compute the approximate initial latent for the image we generated, we run the sampling process backwards. We could include the prompt, but in the case of verifying a watermark, we will usually not know the initial prompt, so we instead just set it to the empty string:</p>
            <pre><code>from PIL import Image
from stable_diffusion.utils import build_stable_diffusion_pipeline,   
                                   convert_pil_to_latents
from stable_diffusion.schedulers import ddim_inverse_scheduler

# Load image
img = Image.open(image_path)

# Instantiate Stable Diffusion pipeline with DDIM Inverse scheduler
model_cache_path = './model_cache'
model = 'stabilityai/stable-diffusion-2-1-base'
scheduler, _= ddim_inverse_scheduler()
pipe, _ = build_stable_diffusion_pipeline(model, model_cache_path, scheduler)

# Convert the input image to latent space
image_latent = convert_pil_to_latents(pipe, img)

# Invert the sampling process that generated the image with an empty prompt
inverted_latent, _ = pipe('', output_type='latent', latents=image_latent,  
                          num_inference_steps=50, return_dict=False)</code></pre>
            
    <div>
      <h4>Embedding the code</h4>
      <a href="#embedding-the-code">
        
      </a>
    </div>
    <p>The initial latent used for stable diffusion consists of a bunch of floating point numbers, each independently and <a href="https://en.wikipedia.org/wiki/Normal_distribution"><u>normally</u></a> distributed with a mean of zero.</p><p>The following observation is from a <a href="https://arxiv.org/pdf/2410.07369"><u>recent evaluation</u></a> of the watermark of Christ and Gunn for stable diffusion. (There they used a more sophisticated but expensive inversion method than DDIM.) Observe that the probability that each number is negative is equal to the probability that the number is positive. Likewise, if the code is indeed pseudorandom, then each bit of the codeword is computationally indistinguishable from a bit that is one with probability ½ and zero with probability ½.</p><p>To embed the codeword in the latent, we just set the sign of each number according to the corresponding bit of the codeword:</p>
            <pre><code>from stable_diffusion.utils import build_stable_diffusion_pipeline
from stable_diffusion.schedulers import ddim_scheduler
import numpy as np
import torch

# Generate a normally distributed latent. For the default image
# size of 512x512 pixels, the latent shape is `[1, 4, 64, 64]`.
initial_latent = np.abs(np.random.randn(*LATENTS_SHAPE))

with np.nditer(initial_latent, op_flags=['readwrite']) as it:
    	codeword = encode(k, m)
    	for (i, x) in enumerate(it):
             # `codeword[i]` is a `bool` representing the `i`-th bit of
             # the codeword.
             x *= 1 if codeword[i] else -1

watermarked_latent = torch.from_numpy(initial_latent).to(dtype=torch.float32)

# Instantiate Stable Diffusion pipeline
model_cache_path = './model_cache'
model = 'stabilityai/stable-diffusion-2-1-base'
scheduler, _ = ddim_scheduler()
pipe, _ = build_stable_diffusion_pipeline(model, model_cache_path, scheduler)

# Generate watermarked image
prompt = 'grainy photo of a UFO at night'
watermarked_image, _ = pipe(prompt, num_inference_steps=50,
                                              latents=watermarked_latent, 
                                              return_dict=False)</code></pre>
            <p>To verify this watermark, we compute the inverted latent, extract the codeword, and attempt to decode:</p>
            <pre><code>from PIL import Image
from stable_diffusion.utils import build_stable_diffusion_pipeline,   
                                   convert_pil_to_latents
from stable_diffusion.schedulers import ddim_inverse_scheduler
import numpy as np

# Load image
img = Image.open(image_path)

# Instantiate Stable Diffusion pipeline with DDIM Inverse scheduler
model_cache_path = './model_cache'
model = 'stabilityai/stable-diffusion-2-1-base'
scheduler, _ = ddim_inverse_scheduler()
pipe, _ = build_stable_diffusion_pipeline(model, model_cache_path, scheduler)

# Convert the input image to latent space
image_latent = convert_pil_to_latents(pipe, img)

# Invert the sampling process that generated the image
inverted_latent, _ = pipe('', output_type='latent', latents=image_latent,     
                         num_inference_steps=50, return_dict=False)

watermark_verified = False
with np.nditer(inverted_latent.cpu().numpy()) as it:
    inverted_codeword = []
    for x in it:
         inverted_codeword.append(x &gt; 0)

    if decode(k, inverted_codeword) == m:
         watermark_verified = True</code></pre>
            <p>This should work in theory given the error-correcting properties of the code. But does it work in practice?</p>
    <div>
      <h4>Evaluation</h4>
      <a href="#evaluation">
        
      </a>
    </div>
    <p>A good approximate initial latent is one that is very similar to the original latent that generated an image.  Given our embedding of a codeword into the latent, we define similarity as latents that have a high percentage of overlapping or matching signs.</p><p>To get a feel for this difference, we can visualize it by comparing a generated image to the same image, but with the inverted latent (these images are unwatermarked):</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/ZFAAipEvnp6u79lcQrk5t/0d06e07468c05d707289a322937e9253/1.png" />
          </figure><p><sup><i>Figure 3: Image generated with prompt 'grainy photo of a UFO at night' (left) and the same image generated using the inverted latent (right).</i></sup></p><p>To evaluate how good the approximate latents are for preserving the robustness of watermarks, we randomly sampled 1,000 prompts from the <a href="https://github.com/google-research/parti?tab=readme-ov-file#partiprompts-benchmark"><u>PartiPrompts benchmark dataset</u></a>. For each of these prompts we generated initial latent and inverted latent pairs. We then computed our similarity metric for each pair. We found that on average, 82% of the signs matched for all initial latent and inverted latent pairs, and at least 75% of signs matched for 90% of the pairs.</p><p>We were pleasantly surprised with how accurate the approximation was on average. If 75% of the signs are preserved, then this gives us a decent margin for correcting for errors introduced by an attacker attempting to remove the watermark. Of course a better approximation would give us a better robustness margin. More study is required to fully understand the strengths and limitations of using DDIM Inversion for watermark decoding.</p>
    <div>
      <h3>Candidate pseudorandom codes</h3>
      <a href="#candidate-pseudorandom-codes">
        
      </a>
    </div>
    <p>Now that we have a feel for how to apply pseudorandom codes, let's take a look at how we actually build them. Although the field is barely a year old, we already have a handful of candidates.</p><p>One obvious idea to try is to compose a plain error-correcting code with some cryptographic primitive to make the code pseudorandom. For instance, we might use some standard authenticated encryption scheme, like <a href="https://datatracker.ietf.org/doc/html/rfc8452"><u>AES-GCM-SIV</u></a>, to encrypt <code>m</code>, then apply an error correcting code to the ciphertext. (The watermarking key would be the encryption key.) This "encrypt-then-encode" composition seems natural because encryption schemes are already designed so that their ciphertexts are pseudorandom. Unfortunately, error correcting codes are generally highly structured, and this structure would be betrayed by the codeword, even when applied to a (pseudo)random input.</p><p>The dual composition, "encode-then-encrypt", also doesn't work. If the ciphertext is non-malleable, as in AES-GCM-SIV, then we wouldn't be able to tolerate any number of bit flips. On the other hand, if the ciphertext were malleable, as in <a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)"><u>AES-CTR</u></a>, then the attacker would be able to forge codewords by manipulating a known codeword in a targeted manner.</p><p>The strategy of Christ-Gunn-2024 is to modify an existing error-correcting code to make it pseudorandom.</p>
    <div>
      <h4>Pseudorandom LDPC codes</h4>
      <a href="#pseudorandom-ldpc-codes">
        
      </a>
    </div>
    <p>Their starting point is the widely used <a href="https://en.wikipedia.org/wiki/Low-density_parity-check_code"><u>Low-Density Parity-Check (LDPC) code</u></a>. This code is defined by a <b>parity check</b> matrix <code>P</code> with and a corresponding <b>generator</b> matrix <code>G</code>. The parity check matrix has bit entries and might look something like this:</p>
            <pre><code>import numpy as np
P = np.matrix([[1, 0, 1],
               [0, 0, 1],
               [0, 1, 1],
               [1, 1, 1],
               [1, 0, 0],
               [0, 1, 0]])</code></pre>
            <p>This matrix is used to check if a given bit string is a codeword. By definition, a codeword is any bit string c for which the weight of <code>P*c</code> (the number ones in the output of <code>P*c</code>) is small. (Note that arithmetic is modulo <code>2</code> here.) The generator matrix <code>G</code> is constructed from <code>P </code>so that it can be used as the encoder. In particular, for any bit string <code>m</code>, <code>c=G*m </code>is a codeword. The performance of this code depends in large part on the sparsity of the parity check matrix: roughly speaking, the more zero entries the matrix has, the more the bit flips the code can tolerate.</p><p>The main idea of Christ-Gunn-2024 is to tune the parameters of the LDPC code (the dimensions of the parity check matrix and its density) so that when the parity-check matrix <code>P</code> is chosen at random, the generator matrix <code>G</code> is pseudorandom. This means that, intuitively, when we encode a random input <code>m</code> as <code>c=G*m</code>, the codeword <code>c</code> is also pseudorandom. (There is a bit more that goes into constructing the input <code>m</code>, but this is roughly the idea.)</p><p>It's easy to see that a watermark based on this construction is robust, as it follows immediately from the capacity of LDPC to tolerate bit flips. Ensuring the watermark remains undetectable is more delicate, as it relies on relatively strong and understudied computational assumptions. As a result, it's not clear today for what parameter ranges this scheme is concretely secure. (There has been some progress here: <a href="https://eprint.iacr.org/2024/1425"><u>a recent preprint</u></a> by Surendra Ghentiyala and Venkatesan Guruswami showed that the pseudorandomness of Christ-Gunn-2024 can be proved with slightly weaker assumptions.)</p><p>To get a feel for how things might go wrong, consider what happens if the attacker manages to guess one of the rows of the parity check matrix<code> P</code>. When we take the dot product of this row with a codeword, then the output will be 0 with high probability. (By definition, <code>c</code> is a codeword if the sum of the dot products of <code>c </code>with each row of <code>P</code> is small.) But if we take the dot product of this row with a random bit string, then we should expect to see 0 with probability roughly ½. This gives us a way of distinguishing codewords from random bit strings.</p><p>Guessing a row of <code>P</code> is easy if the matrix is too sparse. In the extreme case, if each row has only one bit set, then there are only <code>n</code> possible values for that row, where <code>n</code> is the number of columns of <code>P</code>. On the other hand, making <code>P</code> too dense will degrade the code's ability to detect bit flips.</p><p>Similarly, it may be easy to guess a row of <code>P</code> if the length of the codeword itself (<code>n</code>) is too small. Thus, in order for this code to be pseudorandom, it is necessary (but not sufficient) for the number of possible parity check matrices to be so large that exhaustively searching for <code>P</code> is not feasible. This can be done by increasing the size of the codeword or tolerating fewer bit flips.</p>
    <div>
      <h4>Pseudorandom codes from PRFs</h4>
      <a href="#pseudorandom-codes-from-prfs">
        
      </a>
    </div>
    <p>Another approach to constructing pseudorandom codes comes from <a href="https://eprint.iacr.org/2024/898"><u>a 2024 preprint</u></a> from Noah Golowich and Ankur Moitra. Their starting point is a common cryptographic primitive called a pseudorandom function (PRF). They require a PRF that takes as input a key <code>k</code>, a bit string <code>x</code> of length <code>m</code> and outputs a bit, denoted <code>F(k,m)</code>.</p><p>Suppose our codewords are <code>x</code><code><sub>1</sub></code>, ⁣<code>w</code><code><sub>1</sub></code><code>=F(k,x</code><code><sub>1</sub></code><code>)</code>, …,<code> x</code><code><sub>n</sub></code>,<code> w</code><code><sub>n</sub></code><code>=F(k,x</code><code><sub>n</sub></code><code>) </code>where <code>x</code><code><sub>1</sub></code>, …, <code>x</code><code><sub>n</sub></code> are random <code>m</code>-bit strings. (Notice that the codeword length is <code>(m+1)*n</code>.) To verify if a string is a codeword, we parse the codeword into <code>x</code><code><sub>1</sub></code>, <code>w</code><code><sub>1</sub></code>, …, <code>x</code><code><sub>n</sub></code>, <code>w</code><code><sub>n</sub></code><code> </code>and check if <code>w</code><code><sub>i</sub></code><code> = F(k,x</code><code><sub>i</sub></code><code>)</code> for all <code>i</code>. If the number of checks that pass is sufficiently high, then the string is likely a codeword.</p><p>It's easy to see that this code is pseudorandom if the output of <code>F</code> is pseudorandom. However, it's not very robust: to make the <code>i</code>-th check fail, we just need to flip a single bit, <code>w</code><code><sub>i</sub></code>. The attacker just needs to flip a sufficient number of these bits to cause verification to fail. To defeat this attack, the encoder permutes the bits of the codeword with a secret, random permutation. That way the attacker has to guess the position of a sufficient number of <code>w</code><code><sub>i</sub></code>s in the permuted bit string. (A bit more is required to make this scheme provably robust, but this is the idea.)</p><p>Note that the number of bit flips we can tolerate with this scheme depends significantly on the number of PRF checks. This in turn determines the length of the codeword, so we may only get a reasonable degree of robustness for longer codewords. Note that we can increase the number of PRF checks by decreasing the length <code>m</code> of the <code>x</code><code><sub>i</sub></code>s, but making these strings too short is detrimental for pseudorandomness. (What happens if we happen to randomly pick <code>x</code><code><sub>i</sub></code><code>==x</code><code><sub>j</sub></code><code> </code>for <code>i!=j</code>?)</p>
    <div>
      <h4>Are these schemes practical?</h4>
      <a href="#are-these-schemes-practical">
        
      </a>
    </div>
    <p>In our own experiments with Stable Diffusion, we were able to tune the LDPC code to tolerate up to 33% of the codeword bits being mangled, which is likely more than sufficient for robustness in practice. However, achieving this required making the parity check matrix so sparse that the code is not strongly pseudorandom. Thus, the resulting watermark cannot be considered cryptographically undetectable. Among the parameter sets for which the code is plausibly pseudorandom, we didn't find any for which the code tolerates more than 5% bit flips.</p><p>Our findings were similar for the PRF-based code: with plausibly pseudorandom parameters, we couldn't tune the code to tolerate more than 1% bit flips. Like the LDPC code, we can crank this higher by sacrificing pseudorandomness, but we weren't able to crack 5% with any parameters we tried.</p><p>There are a few ways to think about this.</p><p>First, for both codes, robustness improves as the codeword gets larger. In particular, if the latent space for Stable Diffusion were larger, then we'd expect to be able to tolerate more bit flips. In general, cryptographic watermarks of all kinds perform better when there is more randomness to work with. For example, short responses produced by chatbots are especially hard for any watermarking strategy, including pseudorandom codes.</p><p>Another takeaway is that we need a better approximation of the initial latent than provided by DDIM. Indeed, <a href="https://arxiv.org/abs/2410.07369"><u>in their own evaluation</u></a> of the LDPC-based code, Sam Gunn, Xuandong Zhao, and Dawn Song chose a much more sophisticated inversion method, which exhibited better results, albeit at a higher computational cost.</p><p>A third view is that, as a practical matter, cryptographic undetectability might not be all that important for some applications. For instance, we might decide the watermark is good enough on the basis of statistical tests to check for biases within, or correlations across, codewords. Of course, such tests can't rule out the possibility of perceptible differences between watermarked and unwatermarked artifacts.</p>
          <figure>
          <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7BVEy8oI4HZUU6u7zUgW9G/20c48669c252dfe99a689dd0860a54a7/2.png" />
          </figure><p><sup><i>Figure 4: Images with verified LDPC watermarks generated with prompt 'grainy photo of a UFO at night'.</i></sup></p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>The sign of a good abstraction boundary is that it allows folks to collaborate across disciplines. With pseudorandom codes, it seems like we've landed on the right abstraction for AI watermarking: it's up to cryptography experts to figure out how to instantiate them; and it's up to AI/ML experts to figure out how to embed them in their applications. We believe this separation of concerns has the potential to make watermarking easier to deploy, especially for operators like Cloudflare's <a href="https://www.cloudflare.com/developer-platform/products/workers-ai/"><u>Workers AI</u></a>, who don't train and maintain the models themselves.</p><p>After spending a few weeks playing around with this stuff, we're excited by the potential of pseudorandom codes to make strong watermarks for generative AI. However, we feel it will take some time for this field to yield practical schemes.</p><p>Existing candidates will require further study to determine the parameter ranges for which they provide good security. It is also worthwhile to investigate new approaches to building pseudorandom codes, perhaps starting with some other error correcting code besides LDPC. We should also examine what is even theoretically possible in this space: perhaps there is a fundamental tension between detectability and robustness that can't be resolved for some parameter regimes.</p><p>It's also going to be important for watermarks based on pseudorandom codes to be publicly verifiable, as some other cryptographic watermarks are. Concretely, the LDPC code is sort of analogous to public key encryption, where the ciphertext corresponds to a codeword. It might be possible to flip this paradigm around and make a digital signature where the signature is a codeword. Of course, this only works when the weights of the model are also publicly available.</p><p>On the AI/ML side, we need to look closer at methods of approximating the initial randomness for different types of models. This blog looked at what is perhaps the simplest possible method for Stable Diffusion, and while this seems to work pretty well, it's obvious that we can do a lot better. It's just a matter of keeping costs low. A good rule of thumb might be that verifying the watermark should not be more expensive than watermarked inference.</p><p>Pseudorandom codes may also have applications beyond watermarking. When we circulated this blog post internally, an idea that came up a lot was to somehow apply this technology to non-AI content, to embed in the content its provenance. Indeed, this is the idea behind the <a href="https://blog.cloudflare.com/preserve-content-credentials-with-cloudflare-images/"><u>C2PA integration</u></a>. Pseudorandom codes aren't immediately applicable, but may be in the future. Wherever you have a source of randomness in the process of generating some artifact, like digital photography, you can embed in that randomness a codeword.</p><p>Thanks for reading! We hope we've managed to pique your interest in this field. We certainly will be following along. If you'd like to play with the code we used to produce the numbers in this blog, or just make some cool watermarked AI content, you can find our demo on <a href="https://github.com/cloudflareresearch/poc-watermark"><u>GitHub</u></a>.</p> ]]></content:encoded>
            <category><![CDATA[Security Week]]></category>
            <category><![CDATA[AI]]></category>
            <category><![CDATA[Cryptography]]></category>
            <category><![CDATA[Research]]></category>
            <guid isPermaLink="false">VxbhII1JF1Bnb4w9D1kEQ</guid>
            <dc:creator>Teresa Brooks-Mejia</dc:creator>
            <dc:creator>Christopher Patton</dc:creator>
        </item>
        <item>
            <title><![CDATA[Post-quantum cryptography goes GA]]></title>
            <link>https://blog.cloudflare.com/post-quantum-cryptography-ga/</link>
            <pubDate>Fri, 29 Sep 2023 13:05:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare announces Post-Quantum Cryptography as a Generally Available system ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/CftKQnuBYwGI69XmAFsVq/d0577dd4455257096f07d478ddaf5bdd/image2-28.png" />
            
            </figure><p>Over the last twelve months, we have been talking about the new baseline of encryption on the Internet: <a href="https://www.cloudflare.com/learning/ssl/quantum/what-is-post-quantum-cryptography/">post-quantum cryptography</a>. During Birthday Week last year we announced that our <a href="/post-quantum-for-all/">beta of Kyber was available for testing,</a> and that <a href="/post-quantum-tunnel/">Cloudflare Tunnel</a> could be enabled with post-quantum cryptography. Earlier this year, we made our stance clear that this foundational technology should be available to <a href="/post-quantum-crypto-should-be-free/">everyone for free, forever</a>.</p><p>Today, we have hit a milestone after six years and <a href="/searchresults/#q=post%20quantum%20crypto&amp;sort=relevancy&amp;f:@customer_facing_source=%5BBlog%5D&amp;f:@language=%5BEnglish%5D">31 blog posts</a> in the making: we’re starting to roll out <a href="/post-quantum-to-origins/">General Availability</a><sup>1</sup> of post-quantum cryptography support to our customers, services, and internal systems as described more fully below. This includes products like <a href="/post-quantum-to-origins/">Pingora</a> for origin connectivity, 1.1.1.1, <a href="https://www.cloudflare.com/developer-platform/r2/">R2</a>, Argo Smart Routing, Snippets, and so many more.</p><p>This is a milestone for the Internet. We don't yet know when quantum computers will have enough scale to break today's cryptography, but the benefits of upgrading to post-quantum cryptography now are clear. <a href="/the-tls-post-quantum-experiment/">Fast connections and future-proofed</a> security are all possible today because of the advances made by Cloudflare, Google, Mozilla, the National Institutes of Standards and Technology in the United States, the Internet Engineering Task Force, and numerous academic institutions</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6oRJ1q0ib6rCSfgBJPJyuU/8e2847b56b058eb6267d1b9303a74883/image1-40.png" />
            
            </figure><p>What does General Availability mean? In October 2022 <a href="/post-quantum-for-all/">we enabled <i>X25519+Kyber</i> as a beta for all websites and APIs</a> served through Cloudflare. However, it takes two to tango: the connection is only secured if the browser also supports post-quantum cryptography. Starting August 2023, <a href="https://blog.chromium.org/2023/08/protecting-chrome-traffic-with-hybrid.html">Chrome</a> is slowly enabling <i>X25519+Kyber</i> by default.</p><p>The user’s request is routed through Cloudflare’s network (2). We have upgraded many of these internal connections to use post-quantum cryptography, and expect to be done upgrading all of our internal connections by the end of 2024. That leaves as the final link the connection (3) between us and the <i>origin server</i>.</p><p>We are happy to announce that <b>we are rolling out support for X25519+Kyber for most inbound and outbound connections</b> <b>as Generally Available</b> for use including <i>origin servers</i> and <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> <code>fetch()</code>es.</p>
<table>
<thead>
  <tr>
    <th><span>Plan</span></th>
    <th><span>Support for post-quantum outbound connections</span></th>
  </tr>
</thead>
<tbody>
  <tr>
    <td><span>Free</span></td>
    <td><span>Started roll-out. Aiming for 100% by the end of the October.</span></td>
  </tr>
  <tr>
    <td><span>Pro and business</span></td>
    <td><span>Aiming for 100% by the end of year.</span></td>
  </tr>
  <tr>
    <td><span>Enterprise</span></td>
    <td><span>Roll-out begins February 2024. 100% by March 2024.</span></td>
  </tr>
</tbody>
</table><p>For our Enterprise customers, we will be sending out additional information regularly over the course of the next six months to help prepare you for the roll-out. Pro, Business, and Enterprise customers can skip the roll-out and opt-in within your zone today, or opt-out ahead of time using an API described in our companion blog post on <a href="/post-quantum-to-origins/">post-quantum cryptography</a>. Before rolling out for Enterprise in February 2024, we will add a toggle on the dashboard to opt out.</p><p>If you're excited to get started now, <a href="/post-quantum-to-origins/">check out our blog with the technical details and flip on post-quantum cryptography support via the API</a>!</p>
    <div>
      <h3>What’s included and what is next?</h3>
      <a href="#whats-included-and-what-is-next">
        
      </a>
    </div>
    <p>With an upgrade of this magnitude, we wanted to focus on our most used products first and then expand outward to cover our edge cases. This process has led us to include the following products and systems in this roll out:</p><table>
<thead>
  <tr>
    <td>1.1.1.1</td>
  </tr>
</thead>
<tbody>
  <tr>
    <td>AMP</td>
  </tr>
  <tr>
    <td>API Gateway</td>
  </tr>
  <tr>
    <td>Argo Smart Routing</td>
  </tr>
  <tr>
    <td>Auto Minify</td>
  </tr>
  <tr>
    <td>Automatic Platform Optimization</td>
  </tr>
  <tr>
    <td>Automatic Signed Exchange</td>
  </tr>
  <tr>
    <td>Cloudflare Egress</td>
  </tr>
  <tr>
    <td>Cloudflare Images</td>
  </tr>
  <tr>
    <td>Cloudflare Rulesets</td>
  </tr>
  <tr>
    <td>Cloudflare Snippets</td>
  </tr>
  <tr>
    <td>Cloudflare Tunnel</td>
  </tr>
  <tr>
    <td>Custom Error Pages</td>
  </tr>
  <tr>
    <td>Flow Based Monitoring</td>
  </tr>
  <tr>
    <td>Health checks</td>
  </tr>
  <tr>
    <td>Hermes</td>
  </tr>
  <tr>
    <td>Host Head Checker</td>
  </tr>
  <tr>
    <td>Magic Firewall</td>
  </tr>
  <tr>
    <td>Magic Network Monitoring</td>
  </tr>
  <tr>
    <td>Network Error Logging</td>
  </tr>
  <tr>
    <td>Project Flame</td>
  </tr>
  <tr>
    <td>Quicksilver</td>
  </tr>
  <tr>
    <td>R2 Storage</td>
  </tr>
  <tr>
    <td>Request Tracer</td>
  </tr>
  <tr>
    <td>Rocket Loader</td>
  </tr>
  <tr>
    <td>Speed on Cloudflare Dash</td>
  </tr>
  <tr>
    <td>SSL/TLS</td>
  </tr>
  <tr>
    <td>Traffic Manager</td>
  </tr>
  <tr>
    <td>WAF, Managed Rules</td>
  </tr>
  <tr>
    <td>Waiting Room</td>
  </tr>
  <tr>
    <td>Web Analytics</td>
  </tr>
</tbody>
</table><p>If a product or service you use is not listed here, we have not started rolling out post-quantum cryptography to it yet. We are actively working on rolling out post-quantum cryptography to all products and services including our Zero Trust products. Until we have achieved post-quantum cryptography support in all of our systems, we will publish an update blog in every Innovation Week that covers which products we have rolled out post-quantum cryptography to, the products that will be getting it next, and what is still on the horizon.</p><p>Products we are working on bringing post-quantum cryptography support to soon:</p><table>
<thead>
  <tr>
    <td>Cloudflare Gateway</td>
  </tr>
</thead>
<tbody>
  <tr>
    <td>Cloudflare DNS</td>
  </tr>
  <tr>
    <td>Cloudflare Load Balancer</td>
  </tr>
  <tr>
    <td>Cloudflare Access</td>
  </tr>
  <tr>
    <td>Always Online</td>
  </tr>
  <tr>
    <td>Zaraz</td>
  </tr>
  <tr>
    <td>Logging</td>
  </tr>
  <tr>
    <td>D1</td>
  </tr>
  <tr>
    <td>Cloudflare Workers</td>
  </tr>
  <tr>
    <td>Cloudflare WARP</td>
  </tr>
  <tr>
    <td>Bot Management</td>
  </tr>
</tbody>
</table>
    <div>
      <h3>Why now?</h3>
      <a href="#why-now">
        
      </a>
    </div>
    <p>As we announced earlier this year, post-quantum cryptography will be included for free in all Cloudflare products and services that can support it. The best encryption technology should be accessible to everyone - free of charge - to help support privacy and human rights globally.</p><p>As we <a href="/post-quantum-crypto-should-be-free/">mentioned</a> in March:</p><p><i>“What was once an experimental frontier has turned into the underlying fabric of modern society. It runs in our most critical infrastructure like power systems, hospitals, airports, and banks. We trust it with our most precious memories. We trust it with our secrets. That’s why the Internet needs to be private by default. It needs to be secure by default.”</i></p><p>Our work on post-quantum cryptography is driven by the thesis that quantum computers that can break conventional cryptography create a similar problem to the Year 2000 bug. We know there is going to be a problem in the future that could have catastrophic consequences for users, businesses, and even nation states. The difference this time is we don’t know how the date and time that this break in the computational paradigm will occur. Worse, any traffic captured today could be decrypted in the future. We need to prepare today to be ready for this threat.</p><p>We are excited for everyone to adopt post-quantum cryptography into their systems. To follow the latest developments of our deployment of post-quantum cryptography and third-party client/server support, check out <a href="https://pq.cloudflareresearch.com/">pq.cloudflareresearch.com</a> and keep an eye on this blog.</p><p>***</p><p><sup>1</sup>We are using a <a href="https://datatracker.ietf.org/doc/draft-tls-westerbaan-xyber768d00/">preliminary version</a> of Kyber, NIST’s pick for post-quantum key agreement. Kyber has not been finalized. We expect a final standard to be published in 2024 under the name ML-KEM, which we will then adopt promptly while deprecating support for X25519Kyber768Draft00.</p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[General Availability]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Research]]></category>
            <category><![CDATA[Post-Quantum]]></category>
            <guid isPermaLink="false">6BFLGzTX8jguAgFnyAFCib</guid>
            <dc:creator>Wesley Evans</dc:creator>
            <dc:creator>Bas Westerbaan</dc:creator>
            <dc:creator>Christopher Patton</dc:creator>
            <dc:creator>Peter Wu</dc:creator>
            <dc:creator>Vânia Gonçalves</dc:creator>
        </item>
        <item>
            <title><![CDATA[Privacy-preserving measurement and machine learning]]></title>
            <link>https://blog.cloudflare.com/deep-dive-privacy-preserving-measurement/</link>
            <pubDate>Fri, 29 Sep 2023 13:00:45 GMT</pubDate>
            <description><![CDATA[ Cloudflare is implementing DAP (Distributed Aggregation Protocol) – a way of aggregating data without exposing individual measurements that uses  multi-party computation ]]></description>
            <content:encoded><![CDATA[ <p></p><p>In 2023, data-driven approaches to making decisions are the norm. We use data for everything from analyzing x-rays to translating thousands of languages to directing autonomous cars. However, when it comes to building these systems, the conventional approach has been to collect as much data as possible, and worry about privacy as an afterthought.</p><p>The problem is, data can be sensitive and used to identify individuals – even when explicit <a href="https://dataprivacylab.org/projects/identifiability/paper1.pdf">identifiers are removed</a> or noise is added.</p><p>Cloudflare Research has been interested in exploring different approaches to this question: is there a <i>truly private</i> way to perform data collection, especially for some of the most sensitive (but incredibly useful!) technology?</p><p>Some of the use cases we’re thinking about include: training federated <a href="https://www.cloudflare.com/learning/ai/what-is-machine-learning/">machine learning models</a> for predictive keyboards without collecting every user’s keystrokes; performing <a href="https://www.census.gov/data/academy/webinars/2021/disclosure-avoidance-series/simulated-reconstruction-abetted-re-identification-attack-on-the-2010-census.html">a census</a> without storing data about individuals’ responses; <a href="https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ENPA_White_Paper.pdf">providing healthcare authorities with data about COVID-19 exposures without tracking peoples’ locations en masse</a>; figuring out the most common errors browsers are experiencing without reporting which websites are visiting.  </p><p>It’s with those use cases in mind that we’ve been participating in the Privacy Preserving Measurement <a href="https://datatracker.ietf.org/group/ppm/about/">working group at the IETF</a> whose goal is to develop systems for collecting and using this data while minimizing the amount of per-user information exposed to the data collector.</p><p>So far, the most promising standard in this space is <a href="https://datatracker.ietf.org/doc/draft-ietf-ppm-dap/">DAP – Distributed Aggregation Protocol</a> – a clever way to use <a href="https://en.wikipedia.org/wiki/Secure_multi-party_computation">multi-party computation</a> to aggregate data without exposing individual measurements. Early versions of the algorithms used by DAP have been implemented by Google and Apple for <a href="https://covid19-static.cdn-apple.com/applications/covid19/current/static/contact-tracing/pdf/ENPA_White_Paper.pdf">exposure notifications</a>.</p><p>In this blog post, we’ll do a deep dive into the fundamental concepts behind the DAP protocol and give an example of how we’ve implemented it into <a href="https://github.com/cloudflare/daphne">Daphne</a>, our open source aggregator server. We hope this will inspire others to collaborate with us and get involved in this space!</p>
    <div>
      <h3>The principles behind DAP, an open standard for privacy preserving measurement</h3>
      <a href="#the-principles-behind-dap-an-open-standard-for-privacy-preserving-measurement">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/Y8EEQM4c3MgIDKhYnp09B/c7626358f569efcd509c4571ff3fc409/MAnxtCSIuR-Y9c_2OkGchGPEHA_U4feb9db_mXD1BOWpc5cMy25ggAgcGg_Ir-8lkU6kCXkLIyq8M25cxxBmPksZL1EIrlsHErLD7rpZXvMxnRdeLmWdavhLIGww.png" />
            
            </figure><p>At a high level, using the DAP protocol forces us to think in terms of <i>data minimization</i><b>:</b> collect only the data that we use and nothing more. Abstractly, our goal is to devise a system with which a data collector can compute some function \( f(m_{1},...,m_{N}) \) of measurements \( m_{1},...,m_{N} \) uploaded by users without observing the measurements in the clear.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/qDmuIUgKzPXskT39UdfKf/17309b04fd740935c53fe01be8f4b11c/image12-3.png" />
            
            </figure><p><i>Alice wants to know some aggregate statistic – like the average salary of the people at the party – without knowing how much each individual person makes.</i></p><p>This may at first seem like an impossible task: to compute on data without knowing the data we're computing on. Nevertheless, —and, as is often the case in cryptography— once we've properly constrained the problem, solutions begin to emerge.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5nMLQ0GblS8fPnWGtfm2oM/36cc8266ab7f74a7240d3aa266d26790/image9-2.png" />
            
            </figure><p><i>Strawperson solution: delegate the calculation to a trusted third party, Bob. The problem with this is that Bob can see the private inputs in the clear</i></p><p>In an ideal world (see above), there would be some server somewhere on the Internet that we could trust to consume measurements, aggregate them, and send the result to the data collector without ever disclosing anything else. However, in reality there's no reason for users to trust such a server more than the data collector; Indeed, both are subject to the usual assortment of attacks that can lead to a data breach.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6axqzCL6WF50qNFJu3qMT5/3781cd288e80cd53be27ad090ad52082/image1-42.png" />
            
            </figure><p>_MPC solution: secret-share the inputs across multiple parties, a.k.a. Bob and Daphne. If at least one person is honest, Alice gets the aggregate result without anyone knowing individual inputs in the clear._‌ ‌</p><p>Instead, what we do in DAP is <i>distribute</i> the computation across the servers such that no single server has a complete measurement. The key idea that makes this possible is <i>secret sharing</i>.</p>
    <div>
      <h2>Computing on secret shared data</h2>
      <a href="#computing-on-secret-shared-data">
        
      </a>
    </div>
    <p>To set things up, let's make the problem a little more concrete. Suppose each measurement \( m_{i} \) is a number and our goal is to compute the sum of the measurements. That is, \( f(m_{1},...,m_{N}) = m_{1} + \cdots + m_{N} \). Our goal is to use secret sharing to allow two servers, which we'll call <i>aggregators</i>, to jointly compute this sum.</p><p>To understand secret sharing, we're going to need a tiny bit of math—modular arithmetic. The expression \(  X + Y  (\textrm{mod})  \textit{q} \) means "add \(  X  \) and \(  Y  \), then divide the sum by \(  q  \) and return the remainder". For now the modulus \(  q  \) can be any large number, as long as it's larger than any sum we'd ever want to compute (\(  2 ^{64}  \), say). In the remainder of this section, we'll omit \(  q  \) and simply write \(  X  + Y \) for addition modulo \(  q  \).</p><p>The goal of secret sharing is to shard a measurement (i.e., a "secret") into two "shares" such that (i) the measurement can be recovered by combining the shares together and (ii) neither share leaks any information about the measurement. To secret share each \(  m_{i} \), we choose a random number \( R_{i} \in \lbrace  0,...,q - 1\rbrace \), set the first share to be \(X_{i} = m_{i} - R_{i} \) and set the other share to be \( Y_{i} = R_{i} \). To recover the measurement, we simply add the shares together. This works because \( X_{i} + Y_{i} = (m_{i} - R_{i}) + R_{i} = m_{i} \). Moreover, each share is indistinguishable from a random number: For example, \( 1337 \) might be secret-shared into \( 11419752798245067454 \) and \( 7026991275464485499 \) (modulo \( q = 2^{64} \)).</p><p>With this scheme we can devise a simple protocol for securely computing the sum:</p><ol><li><p>Each client shards its measurement \( m_{i} \) into \( X_{i} \) and \( Y_{i} \) and sends one share to each server.</p></li><li><p>The first aggregator computes \( X = X_{1} + \cdots + X_{N} \) and reveals \( X \) to the data collector. The second aggregator computes \( Y = Y_{1} + \cdots + Y_{N} \) and reveals \( Y \) to the data collector.</p></li><li><p>The data collector unshards the result as \( r = X + Y \).</p></li></ol><p>This works because the secret shares are additive, and the order in which we add things up is irrelevant to the function we're computing:</p><p>\( r = m_{1} + \cdots + m_{N} \) // by definition\( r = (m_{1} - R_{1}) + R_{1} + \cdots (m_{N} - R_{N}) + R_{N} \) // apply sharding\( r = (m_{1} - R_{1}) + \cdots + (m_{N} - R_{N}) + R_{1} + \cdots R_{N} \) // rearrange the sum\( r = X + Y \) // apply aggregation</p>
    <div>
      <h3>Rich data types</h3>
      <a href="#rich-data-types">
        
      </a>
    </div>
    <p>This basic template for secure aggregation was described in a paper from Henry Corrigan-Gibbs and Dan Boneh called <a href="https://www.usenix.org/conference/nsdi17/technical-sessions/presentation/corrigan-gibbs">"Prio: Private, Robust, and Scalable Computation of Aggregate Statistics"</a> (NSDI 2017). This paper is a critical milestone in DAP's history, as it showed that a wide variety of aggregation tasks (not just sums) can be solved within one, simple protocol framework, Prio. With DAP, our goal in large part is to bring this framework to life.</p><p>All Prio tasks are instances of the same template. Measurements are encoded in a form that allows the aggregation function to be expressed as the sum of (shares of) the encoded measurements. For example:</p><ol><li><p>To get arithmetic mean, we just divide the sum by the number of measurements.</p></li><li><p>Variance and standard deviation can be expressed as a linear function of the sum and the sum of squares (i.e., \( m_{i}, m_{i}^{2} \) for each \( i \)).</p></li><li><p>Quantiles (e.g., median) can be estimated reasonably well by mapping the measurements into buckets and aggregating the histogram.</p></li><li><p>Linear regression (i.e., finding a line of best fit through a set of data points) is a bit more complicated, but can also be expressed in the Prio framework.</p></li></ol><p>This degree of flexibility is essential for wide-spread adoption because it allows us to get the most value we can out of a relatively small amount of software. However, there are a couple problems we still need to overcome, both of which entail the need for some form of interaction.</p>
    <div>
      <h3>Input validation</h3>
      <a href="#input-validation">
        
      </a>
    </div>
    <p>The first problem is <i>input validation</i>. Software engineers, especially those of us who operate web services, know in our bones that validating inputs we get from clients is of paramount importance. (Never, <i>ever</i> stick a raw input you got from a client into an SQL query!) But if the inputs are secret shared, then there is no way for an aggregator to discern even a single bit of the measurement, let alone check that it has an expected value. (A secret share of a valid measurement and a number sampled randomly from \( \lbrace 0,...,q - 1 \rbrace \) look identical.) At least, not on its own.</p><p>The solution adopted by Prio (and the <a href="https://datatracker.ietf.org/doc/draft-irtf-cfrg-vdaf/">standard</a>, with some improvements), is a special kind of <a href="/introducing-zero-knowledge-proofs-for-private-web-attestation-with-cross-multi-vendor-hardware/"><i>zero-knowledge proof (ZKP)</i> system</a> designed to operate on secret shared data. The goal is for a prover to convince a verifier that a statement about some data it has committed to is true (e.g., the user has a valid hardware key), without revealing the data itself (e.g. which hardware key is in-use).</p><p>Our setting is exactly the same, except that we're working on secret-shared data rather than committed data. Along with the measurement shares, the client sends shares of a validity proof; then during aggregation, the aggregators interact with one another in order to check and verify the proof. (One round-trip over the network is required.)</p><p>A happy consequence of working with secret shared data is that proof generation and verification are much faster than for committed (or encrypted) data. This is mainly because we avoid the use of public-key cryptography (i.e., <a href="/a-relatively-easy-to-understand-primer-on-elliptic-curve-cryptography/">elliptic curves</a>) and are less constrained in how we choose cryptographic parameters. (We require the modulus \( q \) to be a prime number with a particular structure, but such primes are not hard to find.)</p>
    <div>
      <h3>Non-linear aggregation</h3>
      <a href="#non-linear-aggregation">
        
      </a>
    </div>
    <p>There are a variety of aggregation tasks for which Prio is not well-suited, in particular those that are non-linear. One such task is to find the "heavy hitters" among the set of measurements. The heavy hitters are the subset of the measurements that occur most frequently, say at least \( t \) times for some threshold \( t \). For example, the measurements might be the URLs visited on a given day by users of a web browser; the heavy hitters would be the set of URLs that were visited by at least \( t \) users.</p><p>This computation can be expressed as a simple program:</p>
            <pre><code>def heavy_hitters(measurements: list[bytes], t: int) -&gt; set[bytes]:
    hh = defaultdict(lambda: 0)
    for measurement in measurements:
        hh[measurement] += 1
    return set(map(lambda x: x[0], filter(lambda x: x[1] &gt;= t, hh.items())))</code></pre>
            <p>However, it cannot be expressed as a linear function, at least not efficiently (with sub-exponential space). This would be required to perform this computation on secret-shared measurements.</p><p>In order to enable non-linear computation on secret shared data, it is necessary to introduce some form of interaction. There are a few possibilities. For the heavy hitters problem in particular, Henry Corrigan-Gibbs and others devised a protocol called <a href="https://ieeexplore.ieee.org/document/9519492">Poplar</a> (IEEE Security &amp; Privacy 2021) in which several rounds of aggregation and unsharding are performed, where in each round, information provided by the collector is used to "query" the measurements to obtain a refined aggregate result.</p>
    <div>
      <h3>Helping to build a world of multi-party computation</h3>
      <a href="#helping-to-build-a-world-of-multi-party-computation">
        
      </a>
    </div>
    <p>Protocols like Prio or Poplar that enable computation over secret shared data fit into a rich tradition in cryptography known as <i>multi-party computation (MPC)</i>. MPC is at once an active research area in theoretical computer science and a class of protocols that are beginning to see real-world use—in our case, to minimize the amount of privacy-sensitive information we collect in order to keep the Internet moving.</p><p>The PPM working group at IETF represents a significant effort, by Cloudflare and others, to standardize MPC techniques for privacy preserving measurement. This work has three main prongs:</p><ol><li><p>To identify the types of problems that need to be solved.</p></li><li><p>To provide cryptography researchers from academia, industry, and the public sector with "templates" for solutions that we know how to deploy. One such template is called a <a href="https://datatracker.ietf.org/doc/draft-irtf-cfrg-vdaf/">"Verifiable Distributed Aggregation Function (VDAF)"</a>, which specifies a kind of "API boundary" between protocols like Prio and Poplar and the systems that are built around them. Cloudflare Research is leading development of the standard, contributing to <a href="https://github.com/divviup/libprio-rs">implementations</a>, and providing <a href="https://petsymposium.org/popets/2023/popets-2023-0126.pdf">security analysis</a>.</p></li><li><p>To provide a deployment roadmap for emerging protocols. <a href="https://datatracker.ietf.org/doc/draft-ietf-ppm-dap/">DAP</a> is one such roadmap: it specifies execution of a generic VDAF over HTTPS and attends to the various operational considerations that arise as deployments progress. As well as contributing to the standard itself, Cloudflare has developed its own implementation designed for our own infrastructure (see below).</p></li></ol><p>The IETF is working on its first set of drafts (DAP/VDAF). These drafts are mature enough to deploy, and a number of deployments are scaling up as we speak. Our hope is that we have initiated positive feedback between theorists and practitioners: as new cryptographic techniques emerge, more practitioners will begin to work with them, which will lead to identifying new problems to solve, leading to new techniques, and so on.</p>
    <div>
      <h3>Daphne: Cloudflare’s implementation of a DAP Aggregation Server</h3>
      <a href="#daphne-cloudflares-implementation-of-a-dap-aggregation-server">
        
      </a>
    </div>
    <p>Our emerging technology group has been working on <a href="https://github.com/cloudflare/daphne">Daphne</a>, our Rust-based implementation of a DAP aggregator server. This is only half of a deployment – DAP architecture requires two aggregator servers to interoperate, both operated by different parties. Our current version only implements the DAP Helper role; the other role is the DAP Leader. Plans are in the works to implement the Leader as well, which will open us up to deploy Daphne for more use cases.</p><p>We made two big decisions in our implementation here: using Rust and using Workers. Rust has been skyrocketing in popularity in the past few years due to its performance and memory management – a favorite of cryptographers for similar reasons. <a href="https://workers.cloudflare.com/">Workers</a> is Cloudflare’s serverless execution environment that allows developers to easily deploy applications globally across our network – making it a favorite tool to prototype with at Cloudflare. This allows for easy integration with our Workers-based storage solutions like: <a href="https://developers.cloudflare.com/durable-objects/">Durable Objects</a>, which we’re using for storing various data artifacts as required by the DAP protocol; and <a href="https://www.cloudflare.com/developer-platform/workers-kv/">KV</a>, which we’re using for managing aggregation task configuration. We’ve learned a lot from our interop tests and deployment, which has helped improve our own Workers products and which we have also fed back into the PPM working group to help improve the DAP standard.</p><p>If you’re interested in learning more about Daphne or collaborating with us in this space, you can fill out <a href="https://www.cloudflare.com/lp/privacy-edge/">this form</a>. If you’d like to get involved in the DAP standard, you can check out the <a href="https://datatracker.ietf.org/wg/ppm/about/">working group</a>.</p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Privacy]]></category>
            <category><![CDATA[Machine Learning]]></category>
            <category><![CDATA[Research]]></category>
            <guid isPermaLink="false">2vQ2io72Fczi8H9Eh8HrWF</guid>
            <dc:creator>Christopher Patton</dc:creator>
            <dc:creator>Mari Galicer</dc:creator>
        </item>
        <item>
            <title><![CDATA[Experiment with post-quantum cryptography today]]></title>
            <link>https://blog.cloudflare.com/experiment-with-pq/</link>
            <pubDate>Thu, 04 Aug 2022 13:00:00 GMT</pubDate>
            <description><![CDATA[ The future is post quantum. Enable post-quantum key agreement on your test zone today and get a headstart ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Practically all data sent over the Internet today is at <a href="/the-quantum-menace/">risk</a> in the future if a sufficiently large and stable quantum computer is created. Anyone who captures data now could decrypt it.</p><p>Luckily, there is a solution: we can switch to so-called <a href="https://www.cloudflare.com/learning/ssl/quantum/what-is-post-quantum-cryptography/"><i>post-quantum (PQ) cryptography</i></a>, which is designed to be secure against attacks of quantum computers. After a six-year worldwide selection process, in July 2022, NIST <a href="/nist-post-quantum-surprise/">announced</a> they will standardize <a href="https://pq-crystals.org/kyber/index.shtml">Kyber</a>, a post-quantum key agreement scheme. The standard will be ready in 2024, but we want to help drive the adoption of post-quantum cryptography.</p><p>Today we have added support for the <i>X25519Kyber512Draft00</i> and <i>X25519Kyber768Draft00</i> hybrid post-quantum key agreements to a number of test domains, including <a href="https://pq.cloudflareresearch.com/">pq.cloudflareresearch.com</a>.</p><p><i>Do you want to experiment with post-quantum on your test website for free? Mail</i> <a><i>ask-research@cloudflare.com</i></a> <i>to enroll your test website, but read the fine-print below.</i></p>
    <div>
      <h2>What does it mean to enable post-quantum on your website?</h2>
      <a href="#what-does-it-mean-to-enable-post-quantum-on-your-website">
        
      </a>
    </div>
    <p>If you enroll your website to the post-quantum beta, we will add support for these two extra key agreements <b>alongside</b> the existing classical encryption schemes such as X25519. If your browser doesn’t support these post-quantum key agreements (and none at the time of writing do), then your browser will continue working with a classically secure, but not quantum-resistant, connection.</p>
    <div>
      <h3>Then how to test it?</h3>
      <a href="#then-how-to-test-it">
        
      </a>
    </div>
    <p>We have open-sourced a fork of <a href="https://github.com/cloudflare/boringssl-pq">BoringSSL</a> and <a href="https://github.com/cloudflare/go">Go</a> that has support for these post-quantum key agreements. With those and an enrolled test domain, you can check how your application performs with post-quantum key exchanges. We are working on support for more libraries and languages.</p>
    <div>
      <h3>What to look for?</h3>
      <a href="#what-to-look-for">
        
      </a>
    </div>
    <p>Kyber and classical key agreements such as X25519 have different performance characteristics: Kyber requires less computation, but has bigger keys and requires a bit more RAM to compute. It could very well make the connection faster if used on its own.</p><p>We are not using Kyber on its own though, but are using <b>hybrids</b>. That means we are doing both an X25519 <i>and</i> Kyber key agreement such that the connection is still classically secure if either is broken. That also means that connections will be a bit slower. In our experiments, the difference is <a href="/the-tls-post-quantum-experiment/">very</a> <a href="/post-quantumify-cloudflare/">small</a>, but it’s best to check for yourself.</p>
    <div>
      <h2>The fine-print</h2>
      <a href="#the-fine-print">
        
      </a>
    </div>
    <p>Cloudflare’s post-quantum cryptography support is a beta service for experimental use only. Enabling post-quantum on your website will subject the website to Cloudflare’s Beta Services terms and will impact other Cloudflare services on the website as described below.</p>
    <div>
      <h3>No stability or support guarantees</h3>
      <a href="#no-stability-or-support-guarantees">
        
      </a>
    </div>
    <p>Over the coming months, both Kyber and the way it’s integrated into <a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/">TLS</a> will change for several reasons, including:</p><ol><li><p>Kyber will see small, but backward-incompatible changes in the coming months.</p></li><li><p>We want to be compatible with other early adopters and will change our integration accordingly.</p></li><li><p>As, together with the cryptography community, we find issues, we will add workarounds in our integration.</p></li></ol><p>We will update our forks accordingly, but cannot guarantee any long-term stability or continued support. PQ support may become unavailable at any moment. We will post updates on <a href="https://pq.cloudflareresearch.com">pq.cloudflareresearch.com</a>.</p>
    <div>
      <h3>Features in enrolled domains</h3>
      <a href="#features-in-enrolled-domains">
        
      </a>
    </div>
    <p>For the moment, we are running enrolled zones on a slightly different infrastructure for which not all features, notably QUIC, are available.</p><p>With that out of the way, it’s…</p>
    <div>
      <h2>Demo time!</h2>
      <a href="#demo-time">
        
      </a>
    </div>
    
    <div>
      <h3>BoringSSL</h3>
      <a href="#boringssl">
        
      </a>
    </div>
    <p>With the following commands build our <a href="https://github.com/cloudflare/boringssl-pq">fork of BoringSSL</a> and create a TLS connection with pq.cloudflareresearch.com using the compiled <code>bssl</code> tool. Note that we do not enable the post-quantum key agreements by default, so you have to pass the <code>-curves</code> flag.</p>
            <pre><code>$ git clone https://github.com/cloudflare/boringssl-pq
[snip]
$ cd boringssl-pq &amp;&amp; mkdir build &amp;&amp; cd build &amp;&amp; cmake .. -GNinja &amp;&amp; ninja 
[snip]
$ ./tool/bssl client -connect pq.cloudflareresearch.com -server-name pq.cloudflareresearch.com -curves Xyber512D00
	Connecting to [2606:4700:7::a29f:8a55]:443
Connected.
  Version: TLSv1.3
  Resumed session: no
  Cipher: TLS_AES_128_GCM_SHA256
  ECDHE curve: X25519Kyber512Draft00
  Signature algorithm: ecdsa_secp256r1_sha256
  Secure renegotiation: yes
  Extended master secret: yes
  Next protocol negotiated: 
  ALPN protocol: 
  OCSP staple: no
  SCT list: no
  Early data: no
  Encrypted ClientHello: no
  Cert subject: CN = *.pq.cloudflareresearch.com
  Cert issuer: C = US, O = Let's Encrypt, CN = E1</code></pre>
            
    <div>
      <h3>Go</h3>
      <a href="#go">
        
      </a>
    </div>
    <p>Our <a href="https://github.com/cloudflare/go">Go fork</a> doesn’t enable the post-quantum key agreement by default. The following simple Go program enables PQ by default for the http package and GETs pq.cloudflareresearch.com.</p>
            <pre><code>package main

import (
    "context"
    "crypto/tls"
    "fmt"
    "net/http"
)

func main() {
    req, err := http.NewRequestWithContext(
        context.WithValue(
            context.Background(),
            tls.CFEventHandlerContextKey{},
            func(ev tls.CFEvent) {
                switch e := ev.(type) {
                case tls.CFEventTLS13HRR:
                    fmt.Printf("HelloRetryRequest\n")
                case tls.CFEventTLS13NegotiatedKEX:
                    switch e.KEX {
                    case tls.X25519Kyber512Draft00:
                        fmt.Printf("Used X25519Kyber512Draft00\n")
                    default:
                        fmt.Printf("Used %d\n", e.KEX)
                    }
                }
            },
        ),
        "GET",
        "https://pq.cloudflareresearch.com",
        nil,
    )
    if err != nil {
        panic(err)
    }

    http.DefaultTransport.(*http.Transport).TLSClientConfig = &amp;tls.Config{
        CurvePreferences: []tls.CurveID{tls.X25519Kyber512Draft00, tls.X25519},
    }

    if _, err = (&amp;http.Client{}).Do(req); err != nil {
        fmt.Println(err)
    }
}</code></pre>
            <p>To run we need to compile our <a href="https://github.com/cloudflare/go">Go fork</a>:</p>
            <pre><code>$ git clone https://github.com/cloudflare/go
[snip]
$ cd go/src &amp;&amp; ./all.bash
[snip]
$ ../bin/go run path/to/example.go
Used X25519Kyber512Draft00</code></pre>
            
    <div>
      <h3>On the wire</h3>
      <a href="#on-the-wire">
        
      </a>
    </div>
    <p>So what does this look like on the wire? With <a href="https://www.wireshark.org/">Wireshark</a> we can capture the packet flow. First a non-post quantum HTTP/2 connection with X25519:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4xAkcteIQZp4rEkXNhJpEb/537b6cfe73ad478336fdc74d14dda0cd/image1-8.png" />
            
            </figure><p>This is a normal <a href="https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/">TLS 1.3 handshake</a>: the client sends a ClientHello with an X25519 keyshare, which fits in a single packet. In return, the server sends its own 32 byte X25519 keyshare. It also sends various other messages, such as the certificate chain, which requires two packets in total.</p><p>Let’s check out Kyber:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4tczvgQiEaYXtanEjGy6Wb/466ef092c38058c34c7370770a126d17/image3-2.png" />
            
            </figure><p>As you can see the ClientHello is a bit bigger, but still fits within a single packet. The response takes three packets now, instead of two, because of the larger server keyshare.</p>
    <div>
      <h2>Under the hood</h2>
      <a href="#under-the-hood">
        
      </a>
    </div>
    <p>Want to add client support yourself? We are using a <a href="https://www.ietf.org/archive/id/draft-ietf-tls-hybrid-design-04.txt">hybrid</a> of <a href="https://datatracker.ietf.org/doc/html/rfc7748">X25519</a> and Kyber <a href="https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf">version 3.02</a>. We are writing out the details of the latter in <a href="https://github.com/bwesterb/draft-schwabe-cfrg-kyber">version 00 of this CRFG IETF draft</a>, hence the name. We are using TLS <a href="https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8">group identifiers</a> <code>0xfe30</code> and <code>0xfe31</code> for <i>X25519Kyber512Draft00</i> and <i>X25519Kyber768Draft00</i> respectively.</p><p>There are some differences between our Go and BoringSSL forks that are interesting to compare.</p><ul><li><p>Our <a href="https://github.com/cloudflare/go">Go fork</a> uses our fast <a href="https://github.com/cloudflare/circl/tree/main/kem/kyber">AVX2 optimized implementation of Kyber</a> from <a href="/introducing-circl/">CIRCL</a>. In contrast, our BoringSSL fork uses the simpler <a href="https://github.com/pq-crystals/kyber/tree/master/ref">portable reference implementation</a>. Without the AVX2 optimisations it’s easier to evaluate. The downside is that it’s slower. Don’t be mistaken: it is still very fast, but you can check yourself.</p></li><li><p>Our Go fork only sends one keyshare. If the server doesn’t support it, it will respond with a HelloRetryRequest message and the client will fallback to one the server does support. This adds a roundtrip.Our BoringSSL fork, on the other hand, will send two keyshares: the post-quantum hybrid and a classical one (if a classical key agreement is still enabled). If the server doesn’t recognize the first, it will be able to use the second. In this way we avoid a roundtrip if the server does not support the post-quantum key agreement.</p></li></ul>
    <div>
      <h2>Looking ahead</h2>
      <a href="#looking-ahead">
        
      </a>
    </div>
    <p>The quantum future is here. In the coming years the Internet will move to post-quantum cryptography. Today we are offering our customers the tools to get a headstart and test post-quantum key agreements. We love to hear your feedback: e-mail it to <a>ask-research@cloudflare.com</a>.</p><p>This is just a small, but important first step. We will continue our efforts to move towards a secure and private quantum-secure Internet. Much more to come — watch this space.</p> ]]></content:encoded>
            <category><![CDATA[Post-Quantum]]></category>
            <category><![CDATA[Cryptography]]></category>
            <category><![CDATA[TLS]]></category>
            <category><![CDATA[Research]]></category>
            <guid isPermaLink="false">2XdVJ2OPv7P9zFIhVjxmU0</guid>
            <dc:creator>Bas Westerbaan</dc:creator>
            <dc:creator>Christopher Patton</dc:creator>
            <dc:creator>Peter Wu</dc:creator>
        </item>
        <item>
            <title><![CDATA[Handshake Encryption: Endgame (an ECH update)]]></title>
            <link>https://blog.cloudflare.com/handshake-encryption-endgame-an-ech-update/</link>
            <pubDate>Tue, 12 Oct 2021 12:59:22 GMT</pubDate>
            <description><![CDATA[ In this post, we’ll dig into ECH details and describe what this protocol does to move the needle to help build a better Internet. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Privacy and security are fundamental to Cloudflare, and we believe in and champion the use of cryptography to help provide these fundamentals for customers, end-users, and the Internet at large. In the past, we helped <a href="/rfc-8446-aka-tls-1-3/">specify, implement, and ship TLS 1.3</a>, the latest version of the transport security protocol underlying the web, to all of our users. TLS 1.3 vastly improved upon prior versions of the protocol with respect to security, privacy, and performance: simpler cryptographic algorithms, more handshake encryption, and fewer round trips are just a few of the many great features of this protocol.</p><p>TLS 1.3 was a tremendous improvement over TLS 1.2, but there is still room for improvement. Sensitive metadata relating to application or user intent is still visible in plaintext on the wire. In particular, all client parameters, including the name of the target server the client is connecting to, are visible in plaintext. For obvious reasons, this is problematic from a privacy perspective: Even if your application traffic to crypto.cloudflare.com is encrypted, the fact you’re visiting crypto.cloudflare.com can be quite revealing.</p><p>And so, in collaboration with other participants in the standardization community and members of industry, <a href="/esni/">we embarked towards a solution</a> for encrypting all sensitive TLS metadata in transit. The result: <a href="https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-13">TLS Encrypted ClientHello (ECH)</a>, an extension to protect this sensitive metadata during connection establishment.</p><p>Last year, <a href="/encrypted-client-hello/">we described the current status of this standard</a> and its relation to the TLS 1.3 standardization effort, as well as ECH's predecessor, Encrypted SNI (ESNI). The protocol has come a long way since then, but when will we know when it's ready? There are many ways by which one can measure a protocol. Is it implementable? Is it easy to enable? Does it seamlessly integrate with existing protocols or applications? In order to assess these questions and see if the Internet is ready for ECH, the community needs deployment experience. Hence, for the past year, we’ve been focused on making the protocol stable, interoperable, and, ultimately, deployable. And today, we’re pleased to announce that we’ve begun our initial deployment of TLS ECH.</p><p>What does ECH mean for connection security and privacy on the network? How does it relate to similar technologies and concepts such as domain fronting? In this post, we’ll dig into ECH details and describe what this protocol does to move the needle to help build a better Internet.</p>
    <div>
      <h3>Connection privacy</h3>
      <a href="#connection-privacy">
        
      </a>
    </div>
    <p>For most Internet users, connections are made to perform some type of task, such as loading a web page, sending a message to a friend, purchasing some items online, or accessing bank account information. Each of these connections reveals some limited information about user behavior. For example, a connection to a messaging platform reveals that one might be trying to send or receive a message. Similarly, a connection to a bank or financial institution reveals when the user typically makes financial transactions. Individually, this metadata might seem harmless. But consider what happens when it accumulates: does the set of websites you visit on a regular basis uniquely identify you as a user? The safe answer is: yes.</p><p>This type of metadata is privacy-sensitive, and ultimately something that should only be known by two entities: the user who initiates the connection, and the service which accepts the connection. However, the reality today is that this metadata is known to more than those two entities.</p><p>Making this information private is no easy feat. The nature or intent of a connection, i.e., the name of the service such as crypto.cloudflare.com, is revealed in multiple places during the course of connection establishment: during DNS resolution, wherein clients map service names to IP addresses; and during connection establishment, wherein clients indicate the service name to the target server. (Note: there are other small leaks, though DNS and TLS are the primary problems on the Internet today.)</p><p>As is common in recent years, the solution to this problem is encryption. DNS-over-HTTPS (DoH) is a protocol for encrypting DNS queries and responses to hide this information from onpath observers. Encrypted Client Hello (ECH) is the complementary protocol for TLS.</p><p>The TLS handshake begins when the client sends a ClientHello message to the server over a TCP connection (or, in the context of <a href="https://datatracker.ietf.org/doc/html/rfc9000">QUIC</a>, over UDP) with relevant parameters, including those that are sensitive. The server responds with a ServerHello, encrypted parameters, and all that’s needed to finish the handshake.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6B6iYtQ5u2IZkww1qmL2SP/5d8eccfa2749e6907d2c4851bd741a9d/image1-28.png" />
            
            </figure><p>The goal of ECH is as simple as its name suggests: to encrypt the ClientHello so that privacy-sensitive parameters, such as the service name, are unintelligible to anyone listening on the network. The client encrypts this message using a public key it learns by making a DNS query for a special record known as the <a href="https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-svcb-https-07">HTTPS resource record</a>. This record advertises the server's various TLS and HTTPS capabilities, including ECH support. The server decrypts the encrypted ClientHello using the corresponding secret key.</p><p>Conceptually, DoH and ECH are somewhat similar. With DoH, clients establish an encrypted connection (HTTPS) to a DNS recursive resolver such as 1.1.1.1 and, within that connection, perform DNS transactions.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6uBrhlpXqeiYY1TVOxo6Wb/d1c4048e7fabb347af1e0e18e862ab06/image3-18.png" />
            
            </figure><p>With ECH, clients establish an encrypted connection to a TLS-terminating server such as crypto.cloudflare.com, and within that connection, request resources for an authorized domain such as cloudflareresearch.com.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5EIiZ1q7pBHrnNXSMHy5NJ/273f38bb845b3c6546bf71a6f8b6b80f/image5-14.png" />
            
            </figure><p>There is one very important difference between DoH and ECH that is worth highlighting. Whereas a DoH recursive resolver is specifically designed to allow queries for any domain, a TLS server is configured to allow connections for a select set of authorized domains. Typically, the set of authorized domains for a TLS server are those which appear on its certificate, as these constitute the set of names for which the server is authorized to terminate a connection.</p><p>Basically, this means the DNS resolver is <i>open</i>, whereas the ECH client-facing server is <i>closed</i>. And this closed set of authorized domains is informally referred to as the anonymity set. (This will become important later on in this post.) Moreover, the anonymity set is assumed to be public information. Anyone can query DNS to discover what domains map to the same client-facing server.</p><p>Why is this distinction important? It means that one cannot use ECH for the purposes of <i>connecting</i> to an authorized domain and then <i>interacting</i> with a different domain, a practice commonly referred to as <i>domain fronting</i>. When a client connects to a server using an authorized domain but then tries to interact with a different domain <i>within</i> that connection, e.g., by sending HTTP requests for an origin that does not match the domain of the connection, the request will fail.</p><p>From a high level, encrypting names in DNS and TLS may seem like a simple feat. However, as we’ll show, ECH demands a different look at security and an updated threat model.</p>
    <div>
      <h3>A changing threat model and design confidence</h3>
      <a href="#a-changing-threat-model-and-design-confidence">
        
      </a>
    </div>
    <p>The typical threat model for TLS is known as the <a href="https://en.wikipedia.org/wiki/Dolev%E2%80%93Yao_model">Dolev-Yao</a> model, in which an active network attacker can read, write, and delete packets from the network. This attacker’s goal is to derive the shared session key. There has been <a href="https://ieeexplore.ieee.org/document/7958594">a</a> <a href="https://eprint.iacr.org/2020/1044.pdf">tremendous</a> <a href="https://dl.acm.org/doi/10.1145/3133956.3134063">amount</a> <a href="https://www.research-collection.ethz.ch/bitstream/handle/20.500.11850/452409/main.pdf?sequence=8">of</a> <a href="https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.738.9908&amp;rep=rep1&amp;type=pdf">research</a> <a href="https://hal.inria.fr/hal-01674096/file/record.pdf">analyzing</a> <a href="https://link.springer.com/chapter/10.1007/978-3-662-53018-4_10">the</a> <a href="https://ieeexplore.ieee.org/abstract/document/7546517">security</a> of TLS to gain confidence that the protocol achieves this goal.</p><p>The threat model for ECH is somewhat stronger than considered in previous work. Not only should it be hard to derive the session key, it should also be hard for the attacker to determine the identity of the server from a <i>known anonymity set</i>. That is, ideally, it should have no more advantage in identifying the server than if it simply guessed from the set of servers in the anonymity set. And recall that the attacker is free to read, write, and modify any packet as part of the TLS connection. This means, for example, that an attacker can replay a ClientHello and observe the server’s response. It can also extract pieces of the ClientHello — including the ECH extension — and use them in its own modified ClientHello.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2UDOt4nL1dZujWFI6uVzLI/c17e28d83342592427b17ad55e208d61/image2-20.png" />
            
            </figure><p>The design of ECH ensures that this sort of attack is virtually impossible by ensuring the server certificate can only be decrypted by either the client or client-facing server.</p><p>Something else an attacker might try is masquerade as the server and actively interfere with the client to observe its behavior. If the client reacted differently based on whether the server-provided certificate was correct, this would allow the attacker to test whether a given connection using ECH was for a particular name.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4BSHqa7Vz8fRJqffZTtKht/efeb05714d785273db9571c73160b1a4/image4-18.png" />
            
            </figure><p>ECH also defends against this attack by ensuring that an attacker without access to the private ECH key material cannot actively inject anything into the connection.</p><p>The attacker can also be entirely passive and try to infer encrypted information from other visible metadata, such as packet sizes and timing. (Indeed, traffic analysis is an open problem for ECH and in general for TLS and related protocols.) Passive attackers simply sit and listen to TLS connections, and use what they see and, importantly, what they know to make determinations about the connection contents. For example, if a passive attacker knows that the name of the client-facing server is crypto.cloudflare.com, and it sees a ClientHello with ECH to crypto.cloudflare.com, it can conclude, with reasonable certainty, that the connection is to some domain in the anonymity set of crypto.cloudflare.com.</p><p>The number of potential attack vectors is astonishing, and something that the TLS working group has tripped over in prior iterations of the ECH design. Before any sort of real world deployment and experiment, we needed confidence in the design of this protocol. To that end, we are working closely with external researchers on a formal analysis of the ECH design which captures the following security goals:</p><ol><li><p>Use of ECH does not weaken the security properties of TLS without ECH.</p></li><li><p>TLS connection establishment to a host in the client-facing server’s anonymity set is indistinguishable from a connection to any other host in that anonymity set.</p></li></ol><p>We’ll write more about the model and analysis when they’re ready. Stay tuned!</p><p>There are plenty of other subtle security properties we desire for ECH, and some of these drill right into the most important question for a privacy-enhancing technology: Is this deployable?</p>
    <div>
      <h3>Focusing on deployability</h3>
      <a href="#focusing-on-deployability">
        
      </a>
    </div>
    <p>With confidence in the security and privacy properties of the protocol, we then turned our attention towards deployability. In the past, significant protocol changes to fundamental Internet protocols such as <a href="https://conferences.sigcomm.org/imc/2011/docs/p181.pdf">TCP</a> or <a href="https://datatracker.ietf.org/doc/html/rfc8446#appendix-D.4">TLS</a> have been complicated by some form of benign interference. Network software, like any software, is prone to bugs, and sometimes these bugs manifest in ways that we only detect when there’s a change elsewhere in the protocol. For example, TLS 1.3 unveiled middlebox ossification bugs that ultimately led to the <a href="https://datatracker.ietf.org/doc/html/rfc8446#appendix-D.4">middlebox compatibility mode</a> for TLS 1.3.</p><p>While itself just an extension, the risk of ECH exposing (or introducing!) similar bugs is real. To combat this problem, ECH supports a variant of <a href="https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-13#section-6.2">GREASE</a> whose goal is to ensure that all ECH-capable clients produce <i>syntactically equivalent</i> ClientHello messages. In particular, if a client supports ECH but does not have the corresponding ECH configuration, it uses GREASE. Otherwise, it produces a ClientHello with real ECH support. In both cases, the syntax of the ClientHello messages is equivalent.</p><p>This hopefully avoids network bugs that would otherwise trigger upon real or fake ECH. Or, in other words, it helps ensure that all ECH-capable client connections are treated similarly in the presence of benign network bugs or otherwise passive attackers. Interestingly, active attackers can easily distinguish -- with some probability -- between real or fake ECH. Using GREASE, the ClientHello carries an ECH extension, though its contents are effectively randomized, whereas a real ClientHello using ECH has information that will match what is contained in DNS. This means an active attacker can simply compare the ClientHello against what’s in the DNS. Indeed, anyone can query DNS and use it to determine if a ClientHello is real or fake:</p>
            <pre><code>$ dig +short crypto.cloudflare.com TYPE65
\# 134 0001000001000302683200040008A29F874FA29F884F000500480046 FE0D0042D500200020E3541EC94A36DCBF823454BA591D815C240815 77FD00CAC9DC16C884DF80565F0004000100010013636C6F7564666C 6172652D65736E692E636F6D00000006002026064700000700000000 0000A29F874F260647000007000000000000A29F884F</code></pre>
            <p>Despite this obvious distinguisher, the end result isn’t that interesting. If a server is capable of ECH and a client is capable of ECH, then the connection most likely used ECH, and whether clients and servers are capable of ECH is assumed public information. Thus, GREASE is primarily intended to ease deployment against benign network bugs and otherwise passive attackers.</p><p>Note, importantly, that GREASE (or fake) ECH ClientHello messages are semantically different from real ECH ClientHello messages. This presents a real problem for networks such as enterprise settings or school environments that otherwise use plaintext TLS information for the purposes of implementing various features like filtering or parental controls. (Encrypted DNS protocols like DoH also encountered similar obstacles in their deployment.) Fundamentally, this problem reduces to the following: How can networks securely disable features like DoH and ECH? Fortunately, there are a number of approaches that might work, with the more promising one centered around DNS discovery. In particular, if clients could securely discover encrypted recursive resolvers that can perform filtering in lieu of it being done at the TLS layer, then TLS-layer filtering might be wholly unnecessary. (Other approaches, such as the use of <a href="https://support.mozilla.org/en-US/kb/configuring-networks-disable-dns-over-https">canary domains</a> to give networks an opportunity to signal that certain features are not permitted, may work, though it’s not clear if these could or would be abused to disable ECH.)</p><p>We are eager to collaborate with browser vendors, network operators, and other stakeholders to find a feasible deployment model that works well for users without ultimately stifling connection privacy for everyone else.</p>
    <div>
      <h3>Next steps</h3>
      <a href="#next-steps">
        
      </a>
    </div>
    <p>ECH is rolling out for some FREE zones on our network in select geographic regions. We will continue to expand the set of zones and regions that support ECH slowly, monitoring for failures in the process. Ultimately, the goal is to work with the rest of the TLS working group and IETF towards updating the specification based on this experiment in hopes of making it safe, secure, usable, and, ultimately, deployable for the Internet.</p><p>ECH is one part of the connection privacy story. Like a leaky boat, it’s important to look for and plug all the gaps before taking on lots of passengers! Cloudflare Research is committed to these narrow technical problems and their long-term solutions. Stay tuned for more updates on this and related protocols.</p> ]]></content:encoded>
            <category><![CDATA[Research]]></category>
            <category><![CDATA[Protocols]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Privacy]]></category>
            <category><![CDATA[TLS]]></category>
            <guid isPermaLink="false">796U3yTtzV0G33HROsW9Nu</guid>
            <dc:creator>Christopher Wood</dc:creator>
            <dc:creator>Christopher Patton</dc:creator>
        </item>
        <item>
            <title><![CDATA[Good-bye ESNI, hello ECH!]]></title>
            <link>https://blog.cloudflare.com/encrypted-client-hello/</link>
            <pubDate>Tue, 08 Dec 2020 12:00:00 GMT</pubDate>
            <description><![CDATA[ A deep dive into the Encrypted Client Hello, a standard that encrypts privacy-sensitive parameters sent by the client, as part of the TLS handshake. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Most communication on the modern Internet is encrypted to ensure that its content is intelligible only to the endpoints, i.e., client and server. Encryption, however, requires a key and so the endpoints must agree on an encryption key without revealing the key to would-be attackers. The most widely used cryptographic protocol for this task, called <i>key exchange,</i> is the <a href="https://www.cloudflare.com/learning/ssl/transport-layer-security-tls/"><i>Transport Layer Security</i> (TLS)</a> handshake.</p><p>In this post we'll dive into <a href="https://tools.ietf.org/html/draft-ietf-tls-esni-08"><i>Encrypted Client Hello</i> (ECH)</a>, a new extension for TLS that promises to significantly enhance the privacy of this critical Internet protocol. Today, a number of privacy-sensitive parameters of the TLS connection are negotiated in the clear. This leaves a trove of metadata available to network observers, including the endpoints' identities, how they use the connection, and so on.</p><p>ECH encrypts the full handshake so that this metadata is kept secret. Crucially, this closes <a href="/esni/">a long-standing privacy leak</a> by protecting the <a href="https://www.cloudflare.com/learning/ssl/what-is-sni/"><i>Server Name Indication</i> (SNI)</a> from eavesdroppers on the network. Encrypting the SNI is important because it is the clearest signal of which server a given client is communicating with. However, and perhaps more significantly, ECH also lays the groundwork for adding future security features and performance enhancements to TLS while minimizing their impact on the privacy of end users.</p><p>ECH is the product of close collaboration, facilitated by the IETF, between academics and the tech industry leaders, including Cloudflare, our friends at Fastly and Mozilla (both of whom are the affiliations of co-authors of the standard), and many others. This feature represents a significant upgrade to the TLS protocol, one that builds on bleeding edge technologies, like <a href="/dns-encryption-explained/">DNS-over-HTTPS</a>, that are only now coming into their own. As such, the protocol is not yet ready for Internet-scale deployment. This article is intended as a sign post on the road to full handshake encryption.</p>
    <div>
      <h2>Background</h2>
      <a href="#background">
        
      </a>
    </div>
    <p>The story of TLS is the story of the Internet. As our reliance on the Internet has grown, so the protocol has evolved to address ever-changing operational requirements, use cases, and threat models. The client and server don't just exchange a key. They negotiate a wide variety of features and parameters: the exact method of key exchange; the encryption algorithm; who is authenticated and how; which application layer protocol to use after the handshake; and much, much more. All of these parameters impact the security properties of the communication channel in one way or another.</p><p>SNI is a prime example of a parameter that impacts the channel's security. <a href="https://tools.ietf.org/html/rfc6066">The SNI extension</a> is used by the client to indicate to the server the website it wants to reach. This is essential for the modern Internet, as it's common nowadays for many origin servers to sit behind a single TLS operator. In this setting, the operator uses the SNI to determine who will authenticate the connection: without it, there would be no way of knowing which <a href="https://www.cloudflare.com/application-services/products/ssl/">TLS certificate</a> to present to the client. The problem is that SNI leaks to the network the identity of the origin server the client wants to connect to, potentially allowing eavesdroppers to infer a lot of information about their communication. (Of course, there are other ways for a network observer to identify the origin — the origin's IP address, for example. But co-locating with other origins on the same IP address makes it much harder to use this metric to determine the origin than it is to simply inspect the SNI.)</p><p>Although protecting SNI is the impetus for ECH, it is by no means the only privacy-sensitive handshake parameter that the client and server negotiate. Another is the <a href="https://tools.ietf.org/html/rfc7301">ALPN extension</a>, which is used to decide which application-layer protocol to use once the TLS connection is established. The client sends the list of applications it supports — whether it's HTTPS, email, instant messaging, or the myriad other applications that use TLS for transport security — and the server selects one from this list, and sends its selection to the client. By doing so, the client and server leak to the network a clear signal of their capabilities and what the connection might be used for.</p><p>Some features are so privacy-sensitive that their inclusion in the handshake is a non-starter. <a href="https://tools.ietf.org/html/draft-barnes-tls-pake-04">One idea that has been floated</a> is to replace the key exchange at the heart of TLS with <a href="/opaque-oblivious-passwords"><i>password-authenticated</i> key-exchange (PAKE)</a>. This would allow password-based authentication to be used alongside (or in lieu of) certificate-based authentication, making TLS more robust and suitable for a wider range of applications. The privacy issue here is analogous to SNI: servers typically associate a unique identifier to each client (e.g., a username or email address) that is used to retrieve the client's credentials; and the client must, somehow, convey this identity to the server during the course of the handshake. If sent in the clear, then this personally identifiable information would be easily accessible to any network observer.</p><p>A necessary ingredient for addressing all of these privacy leaks is <i>handshake encryption</i>, i.e., the encryption of handshake messages in addition to application data. Sounds simple enough, but this solution presents another problem: how do the client and server pick an encryption key if, after all, the handshake is itself a means of exchanging a key? Some parameters <i>must</i> be sent in the clear, of course, so the goal of ECH is to encrypt all handshake parameters except those that are essential to completing the key exchange.</p><p>In order to understand ECH and the design decisions underpinning it, it helps to understand a little bit about the history of handshake encryption in TLS.</p>
    <div>
      <h3>Handshake encryption in TLS</h3>
      <a href="#handshake-encryption-in-tls">
        
      </a>
    </div>
    <p>TLS had no handshake encryption at all prior to the latest version, <a href="https://tools.ietf.org/html/rfc8446">TLS 1.3</a>. In the wake of the Snowden revelations in 2013, the IETF community <a href="https://tools.ietf.org/html/rfc7258">began to consider ways</a> of countering the threat that mass surveillance posed to the open Internet. When the process of standardizing TLS 1.3 began in 2014, one of its design goals was to encrypt as much of the handshake as possible. Unfortunately, the final standard falls short of full handshake encryption, and several parameters, including SNI, are still sent in the clear. Let's take a closer look to see why.</p><p>The TLS 1.3 protocol flow is illustrated in Figure 1. Handshake encryption begins as soon as the client and server compute a fresh shared secret. To do this, the client sends a <i>key share</i> in its ClientHello message, and the server responds in its ServerHello with its own key share. Having exchanged these shares, the client and server can derive a shared secret. Each subsequent handshake message is encrypted using the <i>handshake traffic key</i> derived from the shared secret. Application data is encrypted using a different key, called the <i>application traffic key</i>, which is also derived from the shared secret. These derived keys have different security properties: to emphasize this, they are illustrated with different colors.</p><p>The first handshake message that is encrypted is the server's EncryptedExtensions. The purpose of this message is to protect the server's sensitive handshake parameters, including the server's ALPN extension, which contains the application selected from the client's ALPN list. Key-exchange parameters are sent unencrypted in the ClientHello and ServerHello.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Dya9l0Am8pUE5Ii0u58az/7eed911113649bdb11d583be8629795c/image4-11.png" />
            
            </figure><p><b>Figure 1:</b> The TLS 1.3 handshake.</p><p>All of the client's handshake parameters, sensitive or not, are sent in the ClientHello. Looking at Figure 1, you might be able to think of ways of reworking the handshake so that some of them can be encrypted, perhaps at the cost of additional latency (i.e., more <a href="https://www.cloudflare.com/learning/cdn/glossary/round-trip-time-rtt/">round trips</a> over the network). However, extensions like SNI create a kind of "chicken-and-egg" problem.</p><p>The client doesn't encrypt anything until it has verified the server's identity (this is the job of the Certificate and CertificateVerify messages) and the server has confirmed that it knows the shared secret (the job of the Finished message). These measures ensure the key exchange is <i>authenticated</i>, thereby preventing <a href="/monsters-in-the-middleboxes/">monster-in-the-middle (MITM)</a> attacks in which the adversary impersonates the server to the client in a way that allows it to decrypt messages sent by the client.  Because SNI is needed by the server to select the certificate, it needs to be transmitted before the key exchange is authenticated.</p><p>In general, ensuring confidentiality of handshake parameters used for authentication is only possible if the client and server <i>already share an encryption key</i>. But where might this key come from?</p><p><b>Full handshake encryption in the early days of TLS 1.3.</b> Interestingly, full handshake encryption was once proposed as a core feature of TLS 1.3. In early versions of the protocol (<a href="https://tools.ietf.org/html/draft-ietf-tls-tls13-10#section-6.2.2">draft-10</a>, circa 2015), the server would offer the client a long-lived public key during the handshake, which the client would use for encryption in subsequent handshakes. (This design came from a protocol called <a href="https://eprint.iacr.org/2015/978">OPTLS</a>, which in turn was borrowed from <a href="https://docs.google.com/document/d/1g5nIXAIkN_Y-7XJW5K45IblHd_L2f5LTaDUDwvZ5L6g/edit#heading=h.s0zksnx7d9oi">the original QUIC proposal</a>.) Called "0-RTT", the primary purpose of this mode was to allow the client to begin sending application data prior to completing a handshake. In addition, it would have allowed the client to encrypt its first flight of handshake messages following the ClientHello, including its own EncryptedExtensions, which might have been used to protect the client's sensitive handshake parameters.</p><p>Ultimately this feature was not included in the final standard (<a href="https://tools.ietf.org/html/rfc8446">RFC 8446</a>, published in 2018), mainly because its usefulness was outweighed by its added complexity. In particular, it does nothing to protect the initial handshake in which the client learns the server's public key. Parameters that are required for server authentication of the initial handshake, like SNI, would still be transmitted in the clear.</p><p>Nevertheless, this scheme is notable as the forerunner of other handshake encryption mechanisms, like ECH, that use public key encryption to protect sensitive ClientHello parameters. The main problem these mechanisms must solve is <i>key distribution</i>.</p>
    <div>
      <h3>Before ECH there was (and is!) ESNI</h3>
      <a href="#before-ech-there-was-and-is-esni">
        
      </a>
    </div>
    <p>The immediate predecessor of ECH was the <a href="https://tools.ietf.org/html/draft-ietf-tls-esni-02"><i>Encrypted SNI</i> (ESNI)</a> extension. As its name implies, the goal of ESNI was to provide confidentiality of the SNI. To do so, the client would encrypt its SNI extension under the server's public key and send the ciphertext to the server. The server would attempt to decrypt the ciphertext using the secret key corresponding to its public key. If decryption were to succeed, then the server would proceed with the connection using the decrypted SNI. Otherwise, it would simply abort the handshake. The high-level flow of this simple protocol is illustrated in Figure 2.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/24LqleKycQgsXAe0qKK6Yk/1bab0083757f0dab8e049f9bd0dbed45/image9-1.png" />
            
            </figure><p><b>Figure 2:</b> The TLS 1.3 handshake with the ESNI extension. It is identical to the TLS 1.3 handshake, except the SNI extension has been replaced with ESNI.</p><p>For key distribution, ESNI relied on another critical protocol: <a href="https://www.cloudflare.com/learning/dns/what-is-dns/"><i>Domain Name Service</i> (DNS)</a>. In order to use ESNI to connect to a website, the client would piggy-back on its standard A/AAAA queries a request for a TXT record with the ESNI public key. For example, to get the key for crypto.dance, the client would request the TXT record of _esni.crypto.dance:</p>
            <pre><code>$ dig _esni.crypto.dance TXT +short
"/wGuNThxACQAHQAgXzyda0XSJRQWzDG7lk/r01r1ZQy+MdNxKg/mAqSnt0EAAhMBAQQAAAAAX67XsAAAAABftsCwAAA="</code></pre>
            <p>The base64-encoded blob contains an ESNI public key and related parameters such as the encryption algorithm.</p><p>But what's the point of encrypting SNI if we're just going to leak the server name to network observers via a plaintext DNS query? Deploying ESNI this way became feasible with the introduction of <a href="/dns-encryption-explained/"><i>DNS-over-HTTPS</i></a> (DoH), which enables encryption of DNS queries to resolvers that provide the DoH service (1.1.1.1 is an example of such a service.). Another crucial feature of DoH is that it provides an authenticated channel for transmitting the ESNI public key from the DoH server to the client. This prevents <a href="/sad-dns-explained/">cache-poisoning attacks</a> that originate from the client's local network: in the absence of DoH, a local attacker could prevent the client from offering the ESNI extension by returning an empty TXT record, or coerce the client into using ESNI with a key it controls.</p><p>While ESNI took a significant step forward, it falls short of our goal of achieving full handshake encryption. Apart from being incomplete — it only protects SNI — it is vulnerable to <a href="https://tools.ietf.org/html/draft-ietf-tls-esni-08#section-10.10">a handful of sophisticated attacks</a>, which, while hard to pull off, point to theoretical weaknesses in the protocol's design that need to be addressed.</p><p>ESNI was <a href="/encrypted-sni/">deployed by Cloudflare</a> and <a href="https://blog.mozilla.org/security/2018/10/18/encrypted-sni-comes-to-firefox-nightly/">enabled by Firefox</a>, on an opt-in basis, in 2018, an  experience that laid bare some of the challenges with relying on DNS for key distribution. Cloudflare rotates its ESNI key every hour in order to minimize the collateral damage in case a key ever gets compromised. DNS artifacts are sometimes cached for much longer, the result of which is that there is a decent chance of a client having a stale public key. While Cloudflare's ESNI service tolerates this to a degree, every key must eventually expire. The question that the ESNI protocol left open is how the client should proceed if decryption fails <i>and</i> it can't access the current public key, via DNS or otherwise.</p><p>Another problem with relying on DNS for key distribution is that several endpoints might be authoritative for the same origin server, but have different capabilities. For example, a request for the A record of "example.com" might return one of two different IP addresses, each operated by a different <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/">CDN</a>. The TXT record for "_esni.example.com" would contain the public key for one of these CDNs, but certainly not both. The DNS protocol does not provide a way of atomically tying together resource records that correspond to the same endpoint. In particular, it's possible for a client to inadvertently offer the ESNI extension to an endpoint that doesn't support it, causing the handshake to fail. Fixing this problem requires changes to the DNS protocol. (More on this below.)</p><p><b>The future of ESNI.</b> In the next section, we'll describe the ECH specification and how it addresses the shortcomings of ESNI. Despite its limitations, however, the practical privacy benefit that ESNI provides is significant. Cloudflare intends to continue its support for ESNI until ECH is production-ready.</p>
    <div>
      <h2>The ins and outs of ECH</h2>
      <a href="#the-ins-and-outs-of-ech">
        
      </a>
    </div>
    <p>The goal of ECH is to encrypt the entire ClientHello, thereby closing the gap left in TLS 1.3 and ESNI by protecting all privacy-sensitive handshake-parameters. Similar to ESNI, the protocol uses a public key, distributed via DNS and obtained using DoH, for encryption during the client's first flight. But ECH has improvements to key distribution that make the protocol more robust to DNS cache inconsistencies. Whereas the ESNI server aborts the connection if decryption fails, the ECH server attempts to complete the handshake and supply the client with a public key it can use to retry the connection.</p><p>But how can the server complete the handshake if it's unable to decrypt the ClientHello? As illustrated in Figure 3, the ECH protocol actually involves <i>two</i> ClientHello messages: the ClientHelloOuter, which is sent in the clear, as usual; and the ClientHelloInner, which is encrypted and sent as an extension of the ClientHelloOuter. The server completes the handshake with just one of these ClientHellos: if decryption succeeds, then it proceeds with the ClientHelloInner; otherwise, it proceeds with the ClientHelloOuter.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3C9ceBTx5AQXu8tS0lgzdF/55ea89f5a56843db15296b2b47f7b1c2/image3-17.png" />
            
            </figure><p><b>Figure 3:</b> The TLS 1.3 handshake with the ECH extension.</p><p>The ClientHelloInner is composed of the handshake parameters the client wants to use for the connection. This includes sensitive values, like the SNI of the origin server it wants to reach (called the <i>backend server</i> in ECH parlance), the ALPN list, and so on. The ClientHelloOuter, while also a fully-fledged ClientHello message, is not used for the intended connection. Instead, the handshake is completed by the ECH service provider itself (called the <i>client-facing server</i>), signaling to the client that its intended destination couldn't be reached due to decryption failure. In this case, the service provider also sends along the correct ECH public key with which the client can retry handshake, thereby "correcting" the client's configuration. (This mechanism is similar to how the server distributed its public key for 0-RTT mode in the early days of TLS 1.3.)</p><p>At a minimum, both ClientHellos must contain the handshake parameters that are required for a server-authenticated key-exchange. In particular, while the ClientHelloInner contains the real SNI, the ClientHelloOuter also contains an SNI value, which the client expects to verify in case of ECH decryption failure (i.e., the client-facing server). If the connection is established using the ClientHelloOuter, then the client is expected to immediately abort the connection and retry the handshake with the public key provided by the server. It's not necessary that the client specify an ALPN list in the ClientHelloOuter, nor any other extension used to guide post-handshake behavior. All of these parameters are encapsulated by the encrypted ClientHelloInner.</p><p>This design resolves — quite elegantly, I think — most of the challenges for securely deploying handshake encryption encountered by earlier mechanisms. Importantly, the design of ECH was not conceived in a vacuum. The protocol reflects the diverse perspectives of the IETF community, and its development dovetails with other IETF standards that are crucial to the success of ECH.</p><p>The first is an important new DNS feature known as the <a href="https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-02">HTTPS resource record type</a>. At a high level, this record type is intended to allow multiple HTTPS endpoints that are authoritative for the same <a href="https://www.cloudflare.com/learning/dns/glossary/what-is-a-domain-name/">domain name</a> to advertise different capabilities for TLS. This makes it possible to rely on DNS for key distribution, resolving one of the deployment challenges uncovered by the initial ESNI deployment. For a deep dive into this new record type and what it means for the Internet more broadly, check out Alessandro Ghedini's recent blog post on <a href="/speeding-up-https-and-http-3-negotiation-with-dns/">speeding up HTTPS with DNS</a>.</p><p>The second is the CFRG's <a href="https://tools.ietf.org/html/draft-irtf-cfrg-hpke-06"><i>Hybrid Public Key Encryption</i> (HPKE)</a> standard, which specifies an extensible framework for building public key encryption schemes suitable for a wide variety of applications. In particular, ECH delegates all of the details of its handshake encryption mechanism to HPKE, resulting in a much simpler and easier-to-analyze specification. (Incidentally, HPKE is also one of the main ingredients of <a href="/oblivious-dns">Oblivious DNS-over-HTTPS</a>.)</p>
    <div>
      <h2>The road ahead</h2>
      <a href="#the-road-ahead">
        
      </a>
    </div>
    <p>The current ECH specification is the culmination of a multi-year collaboration. At this point, the overall design of the protocol is fairly stable. In fact, the next draft of the specification will be the first to be targeted for interop testing among implementations. Still, there remain a number of details that need to be sorted out. Let's end this post with a brief overview of the road ahead.</p>
    <div>
      <h3>Resistance to traffic analysis</h3>
      <a href="#resistance-to-traffic-analysis">
        
      </a>
    </div>
    <p>Ultimately, the goal of ECH is to ensure that TLS connections made to different origin servers behind the same ECH service provider are indistinguishable from one another. In other words, when you connect to an origin behind, say, Cloudflare, no one on the network between you and Cloudflare should be able to discern which origin you reached, or which privacy-sensitive handshake-parameters you and the origin negotiated. Apart from an immediate privacy boost, this property, if achieved, paves the way for the deployment of new features for TLS without compromising privacy.</p><p>Encrypting the ClientHello is an important step towards achieving this goal, but we need to do a bit more. An important attack vector we haven't discussed yet is <a href="https://tools.ietf.org/html/draft-irtf-pearg-website-fingerprinting-01"><i>traffic analysis</i></a>. This refers to the collection and analysis of properties of the communication channel that betray part of the ciphertext's contents, but without cracking the underlying encryption scheme. For example, the <i>length</i> of the encrypted ClientHello might leak enough information about the SNI for the adversary to make an educated guess as to its value (this risk is especially high for domain names that are either particularly short or particularly long). It is therefore crucial that the length of each ciphertext is independent of the values of privacy-sensitive parameters. The current ECH specification provides some mitigations, but their coverage is incomplete. Thus, improving ECH's resistance to traffic analysis is an important direction for future work.</p>
    <div>
      <h3>The spectre of ossification</h3>
      <a href="#the-spectre-of-ossification">
        
      </a>
    </div>
    <p>An important open question for ECH is the impact it will have on network operations.</p><p>One of the lessons learned from the deployment of TLS 1.3 is that upgrading a core Internet protocol can trigger unexpected network behavior. Cloudflare was one of the first major TLS operators <a href="/introducing-tls-1-3/">to deploy TLS 1.3 at scale</a>; when browsers like Firefox and Chrome began to enable it on an experimental basis, <a href="/why-tls-1-3-isnt-in-browsers-yet/">they observed</a> a significantly higher rate of connection failures compared to TLS 1.2. The root cause of these failures was network <i>ossification</i>, i.e., the tendency of <i>middleboxes</i> — network appliances between clients and servers that monitor and sometimes intercept traffic — to write software that expects traffic to look and behave a certain way. Changing the protocol before middleboxes had the chance to update their software led to middleboxes trying to parse packets they didn't recognize, triggering software bugs that, in some instances, caused connections to be dropped completely.</p><p>This problem was so widespread that, instead of waiting for network operators to update their software, the design of TLS 1.3 was altered in order to mitigate the impact of network ossification. The ingenious solution was to make TLS 1.3 "look like" another protocol that middleboxes are known to tolerate. Specifically, the wire format and even the contents of handshake messages were made to resemble TLS 1.2. These two protocols aren't identical, of course — a curious network observer can still distinguish between them — but they look and behave similar enough to ensure that the majority of existing middleboxes don't treat them differently. Empirically, <a href="https://datatracker.ietf.org/meeting/100/materials/slides-100-tls-sessa-tls13/">it was found</a> that this strategy reduced the connection failure rate enough to make deployment of TLS 1.3 viable.</p><p>Once again, ECH represents a significant upgrade for TLS for which the spectre of network ossification looms large. The ClientHello contains parameters, like SNI, that have existed in the handshake for a long time, and we don't yet know what the impact will be of encrypting them. In anticipation of the deployment issues ossification might cause, the ECH protocol has been designed to look as much like a standard TLS 1.3 handshake as possible. The most notable difference is the ECH extension itself: if middleboxes ignore it — as they should, if they are compliant with the TLS 1.3 standard — then the rest of the handshake will look and behave very much as usual.</p><p>It remains to be seen whether this strategy will be enough to ensure the wide-scale deployment of ECH. If so, it is notable that this new feature will help to mitigate the impact of future TLS upgrades on network operations. Encrypting the full handshake reduces the risk of ossification since it means that there are less visible protocol features for software to ossify on. We believe this will be good for the health of the Internet overall.</p>
    <div>
      <h2>Conclusion</h2>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>The old TLS handshake is (unintentionally) leaky. Operational requirements of both the client and server have led to privacy-sensitive parameters, like SNI, being negotiated completely in the clear and available to network observers. The ECH extension aims to close this gap by enabling encryption of the full handshake. This represents a significant upgrade to TLS, one that will help preserve end-user privacy as the protocol continues to evolve.</p><p>The ECH standard is a work-in-progress. As this work continues, Cloudflare is committed to doing its part to ensure this important upgrade for TLS reaches Internet-scale deployment.</p> ]]></content:encoded>
            <category><![CDATA[Privacy]]></category>
            <category><![CDATA[Privacy Week]]></category>
            <category><![CDATA[Research]]></category>
            <category><![CDATA[Protocols]]></category>
            <category><![CDATA[DNS]]></category>
            <category><![CDATA[TLS]]></category>
            <guid isPermaLink="false">7niJq6DaoM1Up9qzRLkwDQ</guid>
            <dc:creator>Christopher Patton</dc:creator>
        </item>
        <item>
            <title><![CDATA[Roughtime: Securing Time with Digital Signatures]]></title>
            <link>https://blog.cloudflare.com/roughtime/</link>
            <pubDate>Fri, 21 Sep 2018 12:00:00 GMT</pubDate>
            <description><![CDATA[ When you visit a secure website, it offers you a TLS certificate that asserts its identity. Every certificate has an expiration date, and when it’s passed due, it is no longer valid. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>When you visit a secure website, it offers you a <a href="https://www.cloudflare.com/application-services/products/ssl/">TLS certificate</a> that asserts its identity. Every certificate has an expiration date, and when it’s passed due, it is no longer valid. The idea is almost as old as the web itself: limiting the lifetime of certificates is meant to reduce the risk in case a TLS server’s secret key is compromised.</p><p>Certificates aren’t the only cryptographic artifacts that expire. When you visit a site protected by Cloudflare, we also tell you whether its certificate has been revoked (see our blog post on <a href="/high-reliability-ocsp-stapling/">OCSP stapling</a>) — for example, due to the secret key being compromised — and this value (a so-called OCSP staple) has an expiration date, too.</p><p>Thus, to determine if a certificate is valid and hasn’t been revoked, your system needs to know the current time. Indeed, time is crucial for the security of TLS and myriad other protocols. To help keep clocks in sync, we are announcing a free, high-availability, and low-latency authenticated time service called <a href="https://roughtime.googlesource.com/roughtime">Roughtime</a>, available at <a href="https://roughtime.cloudflare.com">roughtime.cloudflare.com</a> on port 2002.</p>
    <div>
      <h2>Time is tricky</h2>
      <a href="#time-is-tricky">
        
      </a>
    </div>
    <p>It may surprise you to learn that, in practice, clients’ clocks are heavily skewed. A <a href="https://acmccs.github.io/papers/p1407-acerA.pdf">recent study of Chrome users</a> showed that a significant fraction of reported <a href="https://www.cloudflare.com/learning/ssl/common-errors/">TLS-certificate errors</a> are caused by client-clock skew. During the period in which error reports were collected, 6.7% of client-reported times were behind by more than 24 hours. (0.05% were ahead by more than 24 hours.) This skew was a causal factor for at least 33.5% of the sampled reports from Windows users, 8.71% from Mac OS, 8.46% from Android, and 1.72% from Chrome OS. These errors are usually presented to users as warnings that the user can click through to get to where they’re going. However, showing too many warnings makes users grow accustomed to clicking through them; <a href="https://en.wikipedia.org/wiki/Alarm_fatigue">this is risky</a>, since these warnings are meant to keep users away from malicious websites.</p><p>Clock skew also holds us back from improving the security of certificates themselves. We’d like to issue certificates with shorter lifetimes because the less time the certificate is valid, the lower the risk of the secret key being exposed. (This is why Let’s Encrypt issues certificates valid for just <a href="https://letsencrypt.org/2015/11/09/why-90-days.html">90 days by default</a>.) But the long tail of skewed clocks limits the effective lifetime of certificates; shortening the lifetime too much would only lead to more warnings.</p><p>Endpoints on the Internet often synchronize their clocks using a protocol like the <a href="https://en.wikipedia.org/wiki/Network_Time_Protocol">Network Time Protocol</a> (NTP). NTP aims for precise synchronization, and even accounts for network latency. However, it is usually deployed without security features, as the added overhead on high-load servers <a href="https://www.usenix.org/conference/usenixsecurity16/technical-sessions/presentation/dowling">degrades precision significantly</a>. As a result, a man-in-the-middle attacker between the client and server can easily influence the client’s clock. By moving the client back in time, the attacker can force it to accept expired (and possibly compromised) certificates; by moving forward in time, it can force the client to accept a certificate that is <i>not yet</i> valid.</p><p>Fortunately, for settings in which both security and precision are paramount, workable solutions are <a href="https://www.usenix.org/conference/usenixsecurity16/technical-sessions/presentation/dowling">on the horizon</a>. But for many applications, precise network time isn’t essential; it suffices to be <i>accurate</i>, say, within 10 seconds of real time. This observation is the primary motivation of Google’s <a href="https://roughtime.googlesource.com/roughtime">Roughtime</a> protocol, a simple protocol by which clients can synchronize their clocks with one or more authenticated servers. Roughtime lacks the precision of NTP, but aims to be accurate enough for cryptographic applications, and since the responses are authenticated, man-in-the-middle attacks aren’t possible.</p><p>The protocol is designed to be simple and flexible. A client can get Roughtime from just one server it trusts, or it may contact many servers to make its calculation more robust. But its most distinctive feature is that it adds <i>accountability</i> to time servers. If a server misbehaves by providing the wrong time, then the protocol allows clients to produce publicly verifiable, cryptographic proof of this misbehavior. Making servers auditable in this manner makes them accountable to provide accurate time.</p><p>We are deploying a Roughtime service for two reasons.</p><p>First, the clock we use for this service is the same as the clock we use to determine whether our customers’ certificates are valid and haven’t been revoked; as a result, exposing this service makes us accountable for the validity of TLS artifacts we serve to clients on behalf of our customers.</p><p>Second, Roughtime is a great idea whose time has come. But it is only useful if several independent organizations participate; the more Roughtime servers there are, the more robust the ecosystem becomes. Our hope is that putting our weight behind it will help the Roughtime ecosystem grow.</p>
    <div>
      <h2>The Roughtime protocol</h2>
      <a href="#the-roughtime-protocol">
        
      </a>
    </div>
    <p>At its most basic level, Roughtime is a one-round protocol in which the client requests the current time and the server sends a signed response. The response is comprised of a timestamp (the number of microseconds since the Unix epoch) and a <i>radius</i> (in microseconds) used to indicate the server’s certainty about the reported time. For example, a radius of 1,000,000μs means the server is reasonably sure that the true time is within one second of the reported time.</p><p>The server proves freshness of its response as follows. The request consists of a short, random string commonly called a <i>nonce</i> (pronounced /<a href="https://www.merriam-webster.com/dictionary/nonce">nän(t)s</a>/, or sometimes /ˈen wən(t)s/). The server incorporates the nonce into its signed response so that it’s needed to verify the signature. If the nonce is sufficiently long (say, 16 bytes), then the number of possible nonces is so large that it’s extremely unlikely the server has encountered (or will ever encounter) a request with the same nonce. Thus, a valid signature serves as cryptographic proof that the response is fresh.</p><p>The client uses the server’s <i>root public key</i> to verify the signature. (The key is obtained out-of-band; you can get our key <a href="https://developers.cloudflare.com/time-services/roughtime/usage/">here</a>.) When the server starts, it generates an online public/secret key pair; the root secret key is used to create a delegation for the online public key, and the online secret key is used to sign the response. The delegation serves the same function as a traditional <a href="https://en.wikipedia.org/wiki/X.509">X.509</a> certificate on the web: as illustrated in the figure below, the client first uses the root public key to verify the delegation, then uses the online public key to verify the response. This allows for operational separation of the delegator and the server and limits exposure of the root secret key.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/52glM0EDJ0cO6EzChb6oz4/611386837f9e99dfa650d562a850d7ed/Cloudflare-Roughtime-1.png" />
            
            </figure><p>Simplified Roughtime (without delegation)</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3G51im5eHgZt2KlZ3sSaRm/a29f0ede0d24251022a85f719f0c6ff5/Cloudflare-Roughtime-2.png" />
            
            </figure><p>Roughtime with delegation</p><hr /><p>Roughtime offers two features designed to make it scalable.  First, when the volume of requests is high, the server may batch-sign a number of clients’ requests by constructing a <a href="https://en.wikipedia.org/wiki/Merkle_tree">Merkle tree</a> from the nonces. The server signs the root of the tree and sends in its response the information needed to prove to the client that its request is in the tree. (The data structure is a binary tree, so the amount of information is proportional to the base-2 logarithm of the number of requests in the batch; see the figure below) Second, the protocol is executed over UDP. In order to prevent the Roughtime server from being an amplifier for <a href="https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/">DDoS attacks</a>, the request is padded to 1KB; if the UDP packet is too short, then it’s dropped without further processing. Check out <a href="https://int08h.com/post/to-catch-a-lying-timeserver/">this blog post</a> for a more in-depth discussion.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7qbWjYHoaWRrXuc0as57rf/0cc40418c06f19279f23d3cb68d8a5f9/Cloudflare-Roughtime-3.png" />
            
            </figure><p>Roughtime with batching</p><hr />
    <div>
      <h3>Using Roughtime</h3>
      <a href="#using-roughtime">
        
      </a>
    </div>
    <p>The protocol is flexible enough to support a variety of use cases. A web browser could use a Roughtime server to proactively synchronize its clock when validating TLS certificates. It could also be used retroactively to avoid showing the user too many warnings: when a certificate validation error occurs — in particular, when the browser believes it’s expired or not yet valid — Roughtime could be used to determine if the clock skew was the root cause. Instead of telling the user the certificate is invalid, it could tell the user that their clock is incorrect.</p><p>Using just one server is sufficient if that server is trustworthy, but a security-conscious user could make requests to many servers; the delta might be computed by eliminating outliers and averaging the responses, or by some <a href="https://roughtime.googlesource.com/roughtime/+/master/go/client/">more sophisticated method</a>. This makes the calculation robust to one or more of the servers misbehaving.</p>
    <div>
      <h3>Making servers accountable</h3>
      <a href="#making-servers-accountable">
        
      </a>
    </div>
    <p>The real power of Roughtime is that it’s auditable. Consider the following mode of operation. The client has a list of servers it will query in a particular order. The client generates a random string — called a blind in the parlance of Roughtime — hashes it, and uses the output as the nonce for its request to the server. For subsequent requests, it computes the nonce as follows: generate a blind, compute the hash of this string and the response from the previous server (including the timestamp and signature), and use this hash as the nonce for the next request.</p><hr />
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4Tud1Kf1Y3Epq0Cpqd6Zvc/8effcbc470799ce34d0f976e8caeb2ec/Cloudflare-Roughtime-4.png" />
            
            </figure><p>Chaining multiple Roughtime servers</p><hr /><p>Creating a chain of timestamps in this way binds each response to the response that precedes it. Thus, the sequence of blinds and signatures constitute a publicly verifiable, cryptographic proof that the timestamps were requested in order (a “clockchain” if you will ?). If the servers are roughly synchronized, then we expect that the sequence to monotonically increase, at least roughly. If one of the servers were consistently behind or ahead of the others, then this would be evident in the sequence. Suppose you get the following sequence of timestamps, each from different servers:</p>
<table>
    <tbody>
        <tr>
            <th>Server
            </th><th>Timestamp
        </th></tr>
        <tr>
            <td>ServerA-Roughtime</td>
            <td>2018-08-29 14:51:50 -0700 PDT</td>
        </tr>
        <tr>
            <td>ServerB-Roughtime</td>
            <td>2018-08-29 14:51:51 -0700 PDT +0:00:01</td>
        </tr>
        <tr>
            <td>Cloudflare-Roughtime</td>
            <td>2018-08-29 12:51:52 -0700 PDT -1:59:59</td>
        </tr>
        <tr>
            <td>ServerC-Roughtime</td>
            <td>2018-08-29 14:51:53 -0700 PDT +2:00:01</td>
        </tr>

    </tbody>
</table>
<p>Servers B and C corroborate the time given by server A, but — oh no! Cloudflare is two hours behind! Unless servers A, B, and C are in cahoots, it’s likely that the time offered by Cloudflare is incorrect. Moreover, you have verifiable, cryptographic proof. In this way, the Roughtime protocol makes our server (and all Roughtime servers) accountable to provide accurate time, or, at least, to be in sync with the others.</p>
    <div>
      <h2>The Roughtime ecosystem</h2>
      <a href="#the-roughtime-ecosystem">
        
      </a>
    </div>
    <p>The infrastructure for monitoring and auditing the <a href="https://roughtime.googlesource.com/roughtime/+/HEAD/ECOSYSTEM.md">Roughtime ecosystem</a> hasn’t been built yet. Right now there’s only a handful of servers: in addition to Cloudflare’s and <a href="https://roughtime.googlesource.com/roughtime/+/master/roughtime-servers.json">Google’s</a>, there’s also a really nice <a href="https://github.com/int08h/roughenough">Rust implementation</a>. The more diversity there is, the healthier the ecosystem becomes. We hope to see more organizations adopt this protocol.</p>
    <div>
      <h3>Cloudflare’s Roughtime service</h3>
      <a href="#cloudflares-roughtime-service">
        
      </a>
    </div>
    <p>For the initial deployment of this service, our primary goals are to ensure high availability and minimal maintenance overhead. Each machine at each Cloudflare location executes an instance of the service and responds to queries using its system clock. The server signs each request individually rather than batch-signing them as described above; we rely on our load balancer to ensure no machine is overwhelmed. There are three ways in which we envision this service could be used:</p><ol><li><p><i>TLS authentication</i>. When a TLS application (a web browser for example) starts, it could make a request to roughtime.cloudflare.com and compute the difference between the reported time and its system time. Whenever it authenticates a TLS server, it would add this difference to the system time to get the current time.</p></li><li><p><i>Roughtime daemon</i>. One could implement an OS daemon that periodically requests the current time. If the reported time differs from the system time by more than a second, it might issue an alert.</p></li><li><p><i>Server auditing</i>. As the <a href="https://roughtime.googlesource.com/roughtime/+/HEAD/ECOSYSTEM.md">Roughtime ecosystem</a> grows, it will be important to ensure that all of the servers are in sync. Individuals or organizations may take it upon themselves to monitor the ecosystem and ensure that the servers are in sync with one another.</p></li></ol><p>The service is reachable wherever you are via our anycast network. This is important for a service like Roughtime, because minimizing network latency helps improve accuracy. For information about how to configure a client to use Cloudflare-Roughtime, check out the <a href="https://developers.cloudflare.com/time-services/roughtime/">developer documentation</a>. Note that our initial release is somewhat experimental. As such, our root public key may change in the future. See the developer docs for information on obtaining the current public key.</p><p>If you want to see what time our Roughtime server returns, click the button below!</p><p>function getTime() { document.getElementById("time-txt-box").innerHTML = "Loading..." fetch("/cdn-cgi/trace").then((res) =&gt; res.text()).then((txt) =&gt; { let ts = txt.match(/ts=([0-9\.]+)/)[1] let str = new Date(parseFloat(ts) * 1000) document.getElementById("time-txt-box").innerHTML = str }) .catch((err) =&gt; { console.log(err) document.getElementById("time-txt-box").innerHTML = "Request failed." }) }</p><p>Get Time! </p><p><a href="/subscribe/"><i>Subscribe to the blog</i></a><i> for daily updates on our announcements.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3kjD9adVSlGmVPxJvtg0VJ/ba8c654b3f2cfeef827719c8dcd79ca4/Crypto-Week-1-1-3.png" />
            
            </figure><p></p> ]]></content:encoded>
            <category><![CDATA[Crypto Week]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[TLS]]></category>
            <category><![CDATA[OCSP]]></category>
            <category><![CDATA[Research]]></category>
            <category><![CDATA[Cryptography]]></category>
            <guid isPermaLink="false">7kCyQ6aExp7N5vYW6kc3gY</guid>
            <dc:creator>Christopher Patton</dc:creator>
        </item>
    </channel>
</rss>