authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-05-28 17:32:28+02:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-07-05 12:23:52+02:00
log2ae8359445e963949bbfe25b4f9d0f6efce2de4f
tree5fcdd1867a048890472c892f43bfd9641b43862a
parent7b029dedd946cdf251e6af29175f9e92d63d66a5

crypto.xsalsa20poly1305: add a comment about AD usage restrictions

There is no domain separation between the AD and the ciphertext, and no trailers with their lengths. This is generally fine unless a protocol allows an adversary to set the length of the associated data and the length of the ciphertext (the delimitation can then be shifted without affecting the MAC). Clarify in the documentation comment that with this construction, the AD length must not be controlled by an adversary. This is the sane way to use it (usually to bind to a sequence number, or to some session identifier).

1 files changed, 18 insertions(+), 2 deletions(-)

lib/std/crypto/salsa20.zig+18-2
...@@ -378,9 +378,17 @@ pub const XSalsa20Poly1305 = struct {...@@ -378,9 +378,17 @@ pub const XSalsa20Poly1305 = struct {
378 /// c: ciphertext: output buffer should be of size m.len378 /// c: ciphertext: output buffer should be of size m.len
379 /// tag: authentication tag: output MAC379 /// tag: authentication tag: output MAC
380 /// m: message380 /// m: message
381 /// ad: Associated Data381 /// ad: Associated Data (see below)
382 /// npub: public nonce382 /// npub: public nonce
383 /// k: private key383 /// k: private key
384 ///
385 /// With this construction, if the associated data is not empty,
386 /// it must only contain fixed-length information, such as
387 /// session identifiers and sequence numbers.
388 ///
389 /// Since there is no separation between the associated data and
390 /// the ciphertext, its length must not contain be under an adversary's
391 /// control.
384 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {392 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
385 debug.assert(c.len == m.len);393 debug.assert(c.len == m.len);
386 const extended = extend(rounds, k, npub);394 const extended = extend(rounds, k, npub);
...@@ -399,11 +407,19 @@ pub const XSalsa20Poly1305 = struct {...@@ -399,11 +407,19 @@ pub const XSalsa20Poly1305 = struct {
399 /// `m`: Message407 /// `m`: Message
400 /// `c`: Ciphertext408 /// `c`: Ciphertext
401 /// `tag`: Authentication tag409 /// `tag`: Authentication tag
402 /// `ad`: Associated data410 /// `ad`: Associated data (see below)
403 /// `npub`: Public nonce411 /// `npub`: Public nonce
404 /// `k`: Private key412 /// `k`: Private key
405 /// Asserts `c.len == m.len`.413 /// Asserts `c.len == m.len`.
406 ///414 ///
415 /// With this construction, if the associated data is not empty,
416 /// it must only contain fixed-length information, such as
417 /// sessions identifiers and sequence numbers.
418 ///
419 /// Since there is no separation between the associated data and
420 /// the ciphertext, its length must not be under an adversary's
421 /// control.
422 ///
407 /// Contents of `m` are undefined if an error is returned.423 /// Contents of `m` are undefined if an error is returned.
408 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void {424 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void {
409 debug.assert(c.len == m.len);425 debug.assert(c.len == m.len);