zig icon indicating copy to clipboard operation
zig copied to clipboard

tls: send post-quantum secure keyshare

Open bwesterb opened this issue 2 years ago • 2 comments

Sends post-quantum secure hybrid key share X25519+Kyber768Draft00, deployed by Cloudflare.

A few notes:

  1. The method of hybridisation is as per this IETF TLS WG draft version -06.
  2. Kyber will probably change before final standardisation by NIST. When it does, we'll use a new TLS codepoint and update. The current version is v3.02 of the specification and version -00 of this I-D.
  3. The only other big deployment of post-quantum kex is CECPQ2 (ref1, ref2) by Google, but they'll move to Kyber. It is not clear though whether they'll adopt a preliminary version such as this one.
  4. Zig already generates and sends two key shares: P-256 and X25519. We generate a Kyber768 on top of that and reuse the X25519 share to make the hybrid. Kyber768 is fast — faster than X25519 — even with the currently unoptimised implementation:
            x25519:      33753 exchanges/s
       kyber768d00:      49095 encaps/s
       kyber768d00:      62798 decaps/s
       kyber768d00:      43287 keygen/s
    
  5. Kyber's keyshare is bigger: 1184 bytes for the client keyshare and 1088 bytes for the server keyshare. This will typically split the ClientHello into two TCP segments. The standards allow this, but it might well be that some middle boxes don't expect this.

To test

const std = @import("std");

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    const hdrs = std.http.Client.Request.Headers{};
    const opts = std.http.Client.Request.Options{};
    var buf: [1000]u8 = undefined;
    const uri = try std.Uri.parse("https://cloudflare.com/cdn-cgi/trace");
    var client = std.http.Client{
        .allocator= arena.allocator(),
    };
    var req = try client.request(uri, hdrs, opts);
    try req.finish();
    const read = try req.readAll(&buf);
    const stdout = std.io.getStdOut().writer();
    try stdout.print("{s}\n", .{buf[0..read]});
}

Output:

fl=555f40
h=cloudflare.com
ip=[snip]
ts=1678868428.064
visit_scheme=https
uag=zig (std.http)
colo=AMS
sliver=none
http=http/1.1
loc=NL
tls=TLSv1.3
sni=plaintext
warp=off
gateway=off
rbi=off
kex=X25519Kyber768Draft00

bwesterb avatar Mar 15 '23 08:03 bwesterb

@jedisct1

bwesterb avatar Mar 15 '23 08:03 bwesterb

Seeing that is amazing, well done Bas!

jedisct1 avatar Mar 15 '23 10:03 jedisct1

@jedisct1 build passed.

bwesterb avatar Mar 17 '23 09:03 bwesterb