authorgravatar for bas@westerbaan.nameBas Westerbaan <bas@westerbaan.name> 2023-03-17 17:51:24+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-03-17 17:51:24+01:00
log2089b3f193beaef6debc5671ca6d1f6fc028db8d
tree4ac4d924578a68383a9fc89e73f4e65f93594736
parente0dd20b02ea026e125b36b3674f20aa20ff043e3
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

tls: use post-quantum secure key exchange (#14920)


2 files changed, 31 insertions(+), 8 deletions(-)

lib/std/crypto/tls.zig+4
...@@ -215,6 +215,10 @@ pub const NamedGroup = enum(u16) {...@@ -215,6 +215,10 @@ pub const NamedGroup = enum(u16) {
215 ffdhe6144 = 0x0103,215 ffdhe6144 = 0x0103,
216 ffdhe8192 = 0x0104,216 ffdhe8192 = 0x0104,
217217
218 // Hybrid post-quantum key agreements
219 x25519_kyber512d00 = 0xFE30,
220 x25519_kyber768d00 = 0xFE31,
221
218 _,222 _,
219};223};
220224
lib/std/crypto/tls/Client.zig+27-8
...@@ -158,6 +158,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -158,6 +158,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
158 // Only possible to happen if the private key is all zeroes.158 // Only possible to happen if the private key is all zeroes.
159 error.IdentityElement => return error.InsufficientEntropy,159 error.IdentityElement => return error.InsufficientEntropy,
160 };160 };
161 const kyber768_kp = crypto.kem.kyber_d00.Kyber768.KeyPair.create(null) catch {};
161162
162 const extensions_payload =163 const extensions_payload =
163 tls.extension(.supported_versions, [_]u8{164 tls.extension(.supported_versions, [_]u8{
...@@ -175,6 +176,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -175,6 +176,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
175 .rsa_pkcs1_sha512,176 .rsa_pkcs1_sha512,
176 .ed25519,177 .ed25519,
177 })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{178 })) ++ tls.extension(.supported_groups, enum_array(tls.NamedGroup, &.{
179 .x25519_kyber768d00,
178 .secp256r1,180 .secp256r1,
179 .x25519,181 .x25519,
180 })) ++ tls.extension(182 })) ++ tls.extension(
...@@ -182,7 +184,9 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -182,7 +184,9 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
182 array(1, int2(@enumToInt(tls.NamedGroup.x25519)) ++184 array(1, int2(@enumToInt(tls.NamedGroup.x25519)) ++
183 array(1, x25519_kp.public_key) ++185 array(1, x25519_kp.public_key) ++
184 int2(@enumToInt(tls.NamedGroup.secp256r1)) ++186 int2(@enumToInt(tls.NamedGroup.secp256r1)) ++
185 array(1, secp256r1_kp.public_key.toUncompressedSec1())),187 array(1, secp256r1_kp.public_key.toUncompressedSec1()) ++
188 int2(@enumToInt(tls.NamedGroup.x25519_kyber768d00)) ++
189 array(1, x25519_kp.public_key ++ kyber768_kp.public_key.toBytes())),
186 ) ++190 ) ++
187 int2(@enumToInt(tls.ExtensionType.server_name)) ++191 int2(@enumToInt(tls.ExtensionType.server_name)) ++
188 int2(host_len + 5) ++ // byte length of this extension payload192 int2(host_len + 5) ++ // byte length of this extension payload
...@@ -274,7 +278,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -274,7 +278,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
274 const extensions_size = hsd.decode(u16);278 const extensions_size = hsd.decode(u16);
275 var all_extd = try hsd.sub(extensions_size);279 var all_extd = try hsd.sub(extensions_size);
276 var supported_version: u16 = 0;280 var supported_version: u16 = 0;
277 var shared_key: [32]u8 = undefined;281 var shared_key: []const u8 = undefined;
278 var have_shared_key = false;282 var have_shared_key = false;
279 while (!all_extd.eof()) {283 while (!all_extd.eof()) {
280 try all_extd.ensure(2 + 2);284 try all_extd.ensure(2 + 2);
...@@ -295,14 +299,29 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -295,14 +299,29 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
295 const key_size = extd.decode(u16);299 const key_size = extd.decode(u16);
296 try extd.ensure(key_size);300 try extd.ensure(key_size);
297 switch (named_group) {301 switch (named_group) {
302 .x25519_kyber768d00 => {
303 const xksl = crypto.dh.X25519.public_length;
304 const hksl = xksl + crypto.kem.kyber_d00.Kyber768.ciphertext_length;
305 if (key_size != hksl)
306 return error.TlsIllegalParameter;
307 const server_ks = extd.array(hksl);
308
309 shared_key = &((crypto.dh.X25519.scalarmult(
310 x25519_kp.secret_key,
311 server_ks[0..xksl].*,
312 ) catch return error.TlsDecryptFailure) ++ (kyber768_kp.secret_key.decaps(
313 server_ks[xksl..hksl],
314 ) catch return error.TlsDecryptFailure));
315 },
298 .x25519 => {316 .x25519 => {
299 if (key_size != 32) return error.TlsIllegalParameter;317 const ksl = crypto.dh.X25519.public_length;
300 const server_pub_key = extd.array(32);318 if (key_size != ksl) return error.TlsIllegalParameter;
319 const server_pub_key = extd.array(ksl);
301320
302 shared_key = crypto.dh.X25519.scalarmult(321 shared_key = &(crypto.dh.X25519.scalarmult(
303 x25519_kp.secret_key,322 x25519_kp.secret_key,
304 server_pub_key.*,323 server_pub_key.*,
305 ) catch return error.TlsDecryptFailure;324 ) catch return error.TlsDecryptFailure);
306 },325 },
307 .secp256r1 => {326 .secp256r1 => {
308 const server_pub_key = extd.slice(key_size);327 const server_pub_key = extd.slice(key_size);
...@@ -314,7 +333,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -314,7 +333,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
314 const mul = pk.p.mulPublic(secp256r1_kp.secret_key.bytes, .Big) catch {333 const mul = pk.p.mulPublic(secp256r1_kp.secret_key.bytes, .Big) catch {
315 return error.TlsDecryptFailure;334 return error.TlsDecryptFailure;
316 };335 };
317 shared_key = mul.affineCoordinates().x.toBytes(.Big);336 shared_key = &mul.affineCoordinates().x.toBytes(.Big);
318 },337 },
319 else => {338 else => {
320 return error.TlsIllegalParameter;339 return error.TlsIllegalParameter;
...@@ -358,7 +377,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -358,7 +377,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
358 const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes);377 const early_secret = P.Hkdf.extract(&[1]u8{0}, &zeroes);
359 const empty_hash = tls.emptyHash(P.Hash);378 const empty_hash = tls.emptyHash(P.Hash);
360 const hs_derived_secret = hkdfExpandLabel(P.Hkdf, early_secret, "derived", &empty_hash, P.Hash.digest_length);379 const hs_derived_secret = hkdfExpandLabel(P.Hkdf, early_secret, "derived", &empty_hash, P.Hash.digest_length);
361 p.handshake_secret = P.Hkdf.extract(&hs_derived_secret, &shared_key);380 p.handshake_secret = P.Hkdf.extract(&hs_derived_secret, shared_key);
362 const ap_derived_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "derived", &empty_hash, P.Hash.digest_length);381 const ap_derived_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "derived", &empty_hash, P.Hash.digest_length);
363 p.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes);382 p.master_secret = P.Hkdf.extract(&ap_derived_secret, &zeroes);
364 const client_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length);383 const client_secret = hkdfExpandLabel(P.Hkdf, p.handshake_secret, "c hs traffic", &hello_hash, P.Hash.digest_length);