lib/quic/src/connection/keys.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const quic = @import("../root.zig");
  3 
  4 pub const State = struct {
  5     storage: *quic.connection.Storage,
  6     initial_write: ?quic.crypto.Keys = null,
  7     initial_read: ?quic.crypto.Keys = null,
  8     handshake_write: ?quic.crypto.Keys = null,
  9     handshake_read: ?quic.crypto.Keys = null,
 10     application_write: ?quic.crypto.Keys = null,
 11     application_read_previous: ?quic.crypto.Keys = null,
 12     application_read_current: ?quic.crypto.Keys = null,
 13     application_read_next: ?quic.crypto.Keys = null,
 14     application_write_secret: quic.crypto.Secret = @splat(0),
 15     application_read_secret: quic.crypto.Secret = @splat(0),
 16     application_read_next_secret: quic.crypto.Secret = @splat(0),
 17     send_phase: bool = false,
 18     receive_phase: bool = false,
 19     previous_read_valid: bool = false,
 20     first_current_read_packet: u62 = 0,
 21     receive_update_acknowledged: bool = true,
 22     previous_read_largest: ?u62 = null,
 23     update_requested: bool = false,
 24     update_awaiting_ack: bool = false,
 25     update_first_packet: ?u62 = null,
 26 
 27     pub const InstallError = quic.crypto.Keys.InitError || error{UnsupportedCipherSuite};
 28 
 29     pub fn init(storage: *quic.connection.Storage) State {
 30         return .{ .storage = storage };
 31     }
 32 
 33     pub fn installInitial(
 34         self: *State,
 35         role: quic.tls.Role,
 36         destination: []const u8,
 37     ) InstallError!void {
 38         std.debug.assert(self.initial_write == null);
 39         std.debug.assert(self.initial_read == null);
 40         const secrets = quic.crypto.initial.secrets(destination);
 41         const write_secret = if (role == .client) secrets.client else secrets.server;
 42         const read_secret = if (role == .client) secrets.server else secrets.client;
 43         self.initial_write = try quic.crypto.Keys.derive(
 44             self.storage.keys(.initial_write),
 45             .aes_128_gcm_sha256,
 46             write_secret,
 47         );
 48         self.initial_read = try quic.crypto.Keys.derive(
 49             self.storage.keys(.initial_read),
 50             .aes_128_gcm_sha256,
 51             read_secret,
 52         );
 53     }
 54 
 55     pub fn installTls(self: *State, engine: *const quic.tls.Engine) InstallError!void {
 56         const suite = try selectedSuite(engine);
 57         if (self.handshake_write == null) {
 58             if (engine.secret(.handshake, .write)) |secret| {
 59                 self.handshake_write = try quic.crypto.Keys.derive(
 60                     self.storage.keys(.handshake_write),
 61                     suite,
 62                     secret,
 63                 );
 64             }
 65         }
 66         if (self.handshake_read == null) {
 67             if (engine.secret(.handshake, .read)) |secret| {
 68                 self.handshake_read = try quic.crypto.Keys.derive(
 69                     self.storage.keys(.handshake_read),
 70                     suite,
 71                     secret,
 72                 );
 73             }
 74         }
 75         try self.installApplicationWrite(engine, suite);
 76         try self.installApplicationRead(engine, suite);
 77     }
 78 
 79     fn installApplicationWrite(
 80         self: *State,
 81         engine: *const quic.tls.Engine,
 82         suite: quic.crypto.Suite,
 83     ) InstallError!void {
 84         if (self.application_write != null) return;
 85         const secret = engine.secret(.one_rtt, .write) orelse return;
 86         self.application_write_secret = secret;
 87         self.application_write = try quic.crypto.Keys.derive(
 88             self.storage.keys(.application_write),
 89             suite,
 90             secret,
 91         );
 92     }
 93 
 94     fn installApplicationRead(
 95         self: *State,
 96         engine: *const quic.tls.Engine,
 97         suite: quic.crypto.Suite,
 98     ) InstallError!void {
 99         if (self.application_read_current != null) return;
100         const secret = engine.secret(.one_rtt, .read) orelse return;
101         self.application_read_secret = secret;
102         self.application_read_next_secret = quic.crypto.Keys.next(suite, secret);
103         self.application_read_previous = try quic.crypto.Keys.derive(
104             self.storage.keys(.application_read_previous),
105             suite,
106             secret,
107         );
108         self.application_read_current = try quic.crypto.Keys.derive(
109             self.storage.keys(.application_read_current),
110             suite,
111             secret,
112         );
113         self.application_read_next = try quic.crypto.Keys.derive(
114             self.storage.keys(.application_read_next),
115             suite,
116             secret,
117         );
118         self.application_read_next.?.update(self.application_read_next_secret);
119     }
120 
121     pub fn write(self: *State, kind: quic.connection.SpaceKind) ?*quic.crypto.Keys {
122         return switch (kind) {
123             .initial => if (self.initial_write) |*keys| keys else null,
124             .handshake => if (self.handshake_write) |*keys| keys else null,
125             .application => if (self.application_write) |*keys| keys else null,
126         };
127     }
128 
129     pub fn read(self: *State, kind: quic.connection.SpaceKind) ?*quic.crypto.Keys {
130         return switch (kind) {
131             .initial => if (self.initial_read) |*keys| keys else null,
132             .handshake => if (self.handshake_read) |*keys| keys else null,
133             .application => if (self.application_read_current) |*keys| keys else null,
134         };
135     }
136 
137     pub fn selectApplicationRead(
138         self: *State,
139         header: quic.crypto.packet.Header,
140     ) ?*quic.crypto.Keys {
141         const phase = header.key_phase orelse return null;
142         if (phase == self.receive_phase) {
143             return if (self.application_read_current) |*keys| keys else null;
144         }
145         if (self.previous_read_valid and header.packet_number < self.first_current_read_packet) {
146             return if (self.application_read_previous) |*keys| keys else null;
147         }
148         return if (self.application_read_next) |*keys| keys else null;
149     }
150 
151     pub fn promoteApplicationRead(
152         self: *State,
153         packet_number: u62,
154         next_send: u62,
155     ) error{ConsecutiveUpdate}!void {
156         std.debug.assert(self.application_read_previous != null);
157         std.debug.assert(self.application_read_current != null);
158         std.debug.assert(self.application_read_next != null);
159         const peer_initiated = self.send_phase == self.receive_phase;
160         if (peer_initiated and !self.receive_update_acknowledged) return error.ConsecutiveUpdate;
161         std.mem.swap(
162             quic.crypto.Keys,
163             &self.application_read_previous.?,
164             &self.application_read_current.?,
165         );
166         std.mem.swap(
167             quic.crypto.Keys,
168             &self.application_read_current.?,
169             &self.application_read_next.?,
170         );
171         self.application_read_secret = self.application_read_next_secret;
172         const suite = self.application_read_current.?.selectedSuite();
173         self.application_read_next_secret = quic.crypto.Keys.next(
174             suite,
175             self.application_read_secret,
176         );
177         self.application_read_next.?.update(self.application_read_next_secret);
178         self.previous_read_valid = true;
179         self.receive_phase = !self.receive_phase;
180         self.first_current_read_packet = packet_number;
181         self.previous_read_largest = null;
182         self.receive_update_acknowledged = false;
183         if (peer_initiated) self.updateWrite(next_send);
184         std.debug.assert(self.send_phase == self.receive_phase);
185     }
186 
187     pub fn requestUpdate(self: *State) void {
188         self.update_requested = true;
189     }
190 
191     pub fn prepareSend(self: *State, confirmed: bool, next_packet: u62) void {
192         const keys = if (self.application_write) |*value| value else return;
193         if (!confirmed) return;
194         if (!self.update_requested and !keys.needsUpdate()) return;
195         if (self.update_awaiting_ack) return;
196         self.updateWrite(next_packet);
197         self.update_requested = false;
198     }
199 
200     fn updateWrite(self: *State, next_packet: u62) void {
201         const keys = &self.application_write.?;
202         self.application_write_secret = quic.crypto.Keys.next(
203             keys.selectedSuite(),
204             self.application_write_secret,
205         );
206         keys.update(self.application_write_secret);
207         self.send_phase = !self.send_phase;
208         self.update_awaiting_ack = true;
209         self.update_first_packet = next_packet;
210     }
211 
212     /// Frees the state that holds a further rotation back, `update_awaiting_ack`, once the peer has
213     /// acknowledged something protected by the write keys now in use. The connection hands every
214     /// ACK frame that arrives in the application space to this function, so a second key update can
215     /// follow the first, because `prepareSend` tests that field before it rotates the write keys
216     /// again. An ACK whose largest number falls under the first packet protected by those keys
217     /// leaves the state as it was. The call returns at once when write key rotation is already
218     /// complete.
219     pub fn observeAck(self: *State, value: quic.frame.Ack) void {
220         const first = self.update_first_packet orelse return;
221         std.debug.assert(self.update_awaiting_ack);
222         if (value.largest < first) return;
223         self.update_awaiting_ack = false;
224         self.update_first_packet = null;
225     }
226 
227     /// Marks the peer's most recent phase change as covered by an acknowledgment this endpoint has
228     /// now sent. The connection calls this function after it puts an ACK into a 1-RTT packet, so a
229     /// peer-driven key update can be followed by another, because `promoteApplicationRead` reads
230     /// that mark and turns a second peer-driven rotation away when the mark is missing, answering
231     /// `ConsecutiveUpdate`. The number handed in sits at or above the first packet of the phase
232     /// being read.
233     pub fn observeAckSent(self: *State, largest_received: u62) void {
234         std.debug.assert(largest_received >= self.first_current_read_packet);
235         self.receive_update_acknowledged = true;
236     }
237 
238     /// Pulls the phase boundary down to the number of a packet that arrived behind its neighbors.
239     /// That boundary is `first_current_read_packet`, the lowest number the current keys have been
240     /// seen to open. The connection reports every 1-RTT packet the current read keys opened to this
241     /// function, so the boundary between the two key generations follows the peer's numbering,
242     /// which the order of arrival can disturb. The call answers `PreviousKeysAboveCurrent` when the
243     /// older keys have opened something higher since the last rotation, because a newer generation
244     /// covering a lower number would break the order the peer promised. A number the older keys
245     /// handled already leaves the boundary in place, which holds `previous_read_largest` strictly
246     /// under the boundary.
247     pub fn observeCurrentRead(
248         self: *State,
249         packet_number: u62,
250     ) error{PreviousKeysAboveCurrent}!void {
251         std.debug.assert(self.application_read_current != null);
252         if (self.previous_read_largest) |largest| {
253             std.debug.assert(largest < self.first_current_read_packet);
254             if (packet_number < largest) return error.PreviousKeysAboveCurrent;
255             if (packet_number == largest) return;
256         }
257         if (packet_number >= self.first_current_read_packet) return;
258         self.first_current_read_packet = packet_number;
259     }
260 
261     /// Keeps the highest number the older keys have opened since the last rotation. The connection
262     /// reports every 1-RTT packet the older read keys opened to this function, so
263     /// `observeCurrentRead` compares a later packet against that number to separate a tolerable
264     /// reordering from one it refuses. The number handed in falls under the phase boundary.
265     pub fn observePreviousRead(self: *State, packet_number: u62) void {
266         std.debug.assert(self.previous_read_valid);
267         std.debug.assert(packet_number < self.first_current_read_packet);
268         const largest = self.previous_read_largest orelse packet_number;
269         self.previous_read_largest = @max(largest, packet_number);
270     }
271 
272     pub fn discardInitial(self: *State) void {
273         discard(&self.initial_write);
274         discard(&self.initial_read);
275     }
276 
277     pub fn discardHandshake(self: *State) void {
278         discard(&self.handshake_write);
279         discard(&self.handshake_read);
280     }
281 
282     pub fn deinit(self: *State) void {
283         discard(&self.initial_write);
284         discard(&self.initial_read);
285         discard(&self.handshake_write);
286         discard(&self.handshake_read);
287         discard(&self.application_write);
288         discard(&self.application_read_previous);
289         discard(&self.application_read_current);
290         discard(&self.application_read_next);
291         std.crypto.secureZero(u8, &self.application_write_secret);
292         std.crypto.secureZero(u8, &self.application_read_secret);
293         std.crypto.secureZero(u8, &self.application_read_next_secret);
294     }
295 };
296 
297 fn selectedSuite(engine: *const quic.tls.Engine) State.InstallError!quic.crypto.Suite {
298     const suite = engine.cipherSuite() orelse return error.UnsupportedCipherSuite;
299     return switch (suite) {
300         .AES_128_GCM_SHA256 => .aes_128_gcm_sha256,
301         .CHACHA20_POLY1305_SHA256 => .chacha20_poly1305_sha256,
302         else => error.UnsupportedCipherSuite,
303     };
304 }
305 
306 fn discard(keys: *?quic.crypto.Keys) void {
307     if (keys.*) |*value| _ = value.deinit();
308     keys.* = null;
309 }