tiny.quic.tls.schedule
Defined in tls.
API (10)
Actions
Public operations.
Schedule.applicationSchedule.eraseDerivationSecretsSchedule.eraseHandshakeTrafficSecretsSchedule.eraseMasterSecretSchedule.initfinishedKeyfinishedVerifyData
Types and contracts
Public types and contracts.
Source
Source: lib/quic/src/tls/root.zig:38
zig
pub const schedule = @import("schedule.zig");Source: lib/quic/src/tls/schedule.zig
zig
const std = @import("std");const Hkdf = std.crypto.kdf.hkdf.HkdfSha256;const Hmac = std.crypto.auth.hmac.sha2.HmacSha256;const Sha256 = std.crypto.hash.sha2.Sha256;const tls = std.crypto.tls;pub const Secret = [Sha256.digest_length]u8;pub const Application = struct { client: Secret, server: Secret, exporter: Secret,};pub const Schedule = struct { early: Secret, handshake: Secret, client_handshake: Secret, server_handshake: Secret, master: Secret, pub fn init(shared_secret: Secret, hello_hash: Secret) Schedule { const zero: Secret = @splat(0); var early = Hkdf.extract(&zero, &zero); defer std.crypto.secureZero(u8, &early); const empty_hash = tls.emptyHash(Sha256); var early_derived = tls.hkdfExpandLabel( Hkdf, early, "derived", &empty_hash, @sizeOf(Secret), ); defer std.crypto.secureZero(u8, &early_derived); var handshake = Hkdf.extract(&early_derived, &shared_secret); defer std.crypto.secureZero(u8, &handshake); var client = derive(handshake, "c hs traffic", hello_hash); defer std.crypto.secureZero(u8, &client); var server = derive(handshake, "s hs traffic", hello_hash); defer std.crypto.secureZero(u8, &server); var handshake_derived = tls.hkdfExpandLabel( Hkdf, handshake, "derived", &empty_hash, @sizeOf(Secret), ); defer std.crypto.secureZero(u8, &handshake_derived); var master = Hkdf.extract(&handshake_derived, &zero); defer std.crypto.secureZero(u8, &master); return .{ .early = early, .handshake = handshake, .client_handshake = client, .server_handshake = server, .master = master, }; } pub fn application(self: *const Schedule, transcript_hash: Secret) Application { return .{ .client = derive(self.master, "c ap traffic", transcript_hash), .server = derive(self.master, "s ap traffic", transcript_hash), .exporter = derive(self.master, "exp master", transcript_hash), }; } pub fn eraseDerivationSecrets(self: *Schedule) void { std.crypto.secureZero(u8, &self.early); std.crypto.secureZero(u8, &self.handshake); } pub fn eraseHandshakeTrafficSecrets(self: *Schedule) void { std.crypto.secureZero(u8, &self.client_handshake); std.crypto.secureZero(u8, &self.server_handshake); } pub fn eraseMasterSecret(self: *Schedule) void { std.crypto.secureZero(u8, &self.master); }};pub fn finishedKey(traffic_secret: Secret) Secret { return tls.hkdfExpandLabel(Hkdf, traffic_secret, "finished", &.{}, @sizeOf(Secret));}pub fn finishedVerifyData(traffic_secret: Secret, transcript_hash: Secret) Secret { return tls.hmac(Hmac, &transcript_hash, finishedKey(traffic_secret));}fn derive(secret: Secret, label: []const u8, transcript_hash: Secret) Secret { return tls.hkdfExpandLabel(Hkdf, secret, label, &transcript_hash, @sizeOf(Secret));}fn hexBytes(comptime length: usize, comptime value: []const u8) [length]u8 { var result: [length]u8 = undefined; _ = std.fmt.hexToBytes(&result, value) catch unreachable; return result;}fn rfc8448ClientHello() [196]u8 { return hexBytes(196, "010000c00303cb34ecb1e78163ba1c38c6dacb196a6dffa21a8d9912ec18a2" ++ "ef6283024dece7000006130113031302010000910000000b0009000006736572" ++ "766572ff01000100000a00140012001d00170018001901000101010201030104" ++ "00230000003300260024001d002099381de560e4bd43d23d8e435a7dbafeb3c0" ++ "6e51c13cae4d5413691e529aaf2c002b0003020304000d0020001e0403050306" ++ "03020308040805080604010501060102010402050206020202002d0002010100" ++ "1c00024001");}fn rfc8448ServerHello() [90]u8 { return hexBytes(90, "020000560303a6af06a4121860dc5e6e60249cd34c95930c8ac5cb1434dac155" ++ "772ed3e2692800130100002e00330024001d0020c9828876112095fe66762bdb" ++ "f7c672e156d6cc253b833df1dd69b1b04e751f0f002b00020304");}fn rfc8448HelloHash() Secret { var transcript = Sha256.init(.{}); transcript.update(&rfc8448ClientHello()); transcript.update(&rfc8448ServerHello()); var digest: Secret = undefined; transcript.final(&digest); return digest;}test "RFC 8448 section 3 X25519 and TLS 1.3 handshake schedule" { const client_private = hexBytes( 32, "49af42ba7f7994852d713ef2784bcbcaa7911de26adc5642cb634540e7ea5005", ); const server_private = hexBytes( 32, "b1580eeadf6dd589b8ef4f2d5652578cc810e9980191ec8d058308cea216a21e", ); const client_public = try std.crypto.dh.X25519.recoverPublicKey(client_private); const server_public = try std.crypto.dh.X25519.recoverPublicKey(server_private); const shared = try std.crypto.dh.X25519.scalarmult(server_private, client_public); const client_shared = try std.crypto.dh.X25519.scalarmult(client_private, server_public); try std.testing.expectEqual(shared, client_shared); try std.testing.expectEqual( hexBytes(32, "8bd4054fb55b9d63fdfbacf9f04b9f0d35e6d63f537563efd46272900f89492d"), shared, ); const hello_hash = rfc8448HelloHash(); try expectSecret( "860c06edc07858ee8e78f0e7428c58edd6b43f2ca3e6e95f02ed063cf0e1cad8", hello_hash, ); const schedule = Schedule.init(shared, hello_hash); try expectSecret( "33ad0a1c607ec03b09e6cd9893680ce210adf300aa1f2660e1b22e10f170f92a", schedule.early, ); try expectSecret( "1dc826e93606aa6fdc0aadc12f741b01046aa6b99f691ed221a9f0ca043fbeac", schedule.handshake, ); try expectSecret( "b3eddb126e067f35a780b3abf45e2d8f3b1a950738f52e9600746a0e27a55a21", schedule.client_handshake, ); try expectSecret( "b67b7d690cc16c4e75e54213cb2d37b4e9c912bcded9105d42befd59d391ad38", schedule.server_handshake, ); try expectSecret( "18df06843d13a08bf2a449844c5f8a478001bc4d4c627984d5a41da8d0402919", schedule.master, );}test "RFC 8448 section 3 application schedule and server Finished" { const master = hexBytes( 32, "18df06843d13a08bf2a449844c5f8a478001bc4d4c627984d5a41da8d0402919", ); const base = Schedule{ .early = undefined, .handshake = undefined, .client_handshake = undefined, .server_handshake = hexBytes( 32, "b67b7d690cc16c4e75e54213cb2d37b4e9c912bcded9105d42befd59d391ad38", ), .master = master, }; const application = base.application(hexBytes( 32, "9608102a0f1ccc6db6250b7b7e417b1a000eaada3daae4777a7686c9ff83df13", )); try expectSecret( "9e40646ce79a7f9dc05af8889bce6552875afa0b06df0087f792ebb7c17504a5", application.client, ); try expectSecret( "a11af9f05531f856ad47116b45a950328204b4f44bfb6b3a4b4f1f3fcb631643", application.server, ); try expectSecret( "fe22f881176eda18eb8f44529e6792c50c9a3f89452f68d8ae311b4309d3cf50", application.exporter, ); const verify_data = finishedVerifyData(base.server_handshake, hexBytes( 32, "edb7725fa7a3473b031ec8ef65a2485493900138a2b91291407d7951a06110ed", )); try expectSecret( "9b9b141d906337fbd2cbdce71df4deda4ab42c309572cb7fffee5454b78f0718", verify_data, );}fn expectSecret(comptime expected: []const u8, actual: Secret) !void { try std.testing.expectEqual(hexBytes(32, expected), actual);}Audit
| Definitions | 11 |
|---|---|
| Public names | 11 |
| Members | 8 |
| Version | 26.7.0 |
| Revision | daab053ee433 |