From 2ae8359445e963949bbfe25b4f9d0f6efce2de4f Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 28 May 2026 17:32:28 +0200 Subject: [PATCH 1/2] 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). --- lib/std/crypto/salsa20.zig | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig index 36793eae2e755f28c4adff01eaee064414240b47..892c30c669d7a5ae009eb2ceff545b6864247cf6 100644 --- a/lib/std/crypto/salsa20.zig +++ b/lib/std/crypto/salsa20.zig @@ -378,9 +378,17 @@ pub const XSalsa20Poly1305 = struct { /// c: ciphertext: output buffer should be of size m.len /// tag: authentication tag: output MAC /// m: message - /// ad: Associated Data + /// ad: Associated Data (see below) /// npub: public nonce /// k: private key + /// + /// With this construction, if the associated data is not empty, + /// it must only contain fixed-length information, such as + /// session identifiers and sequence numbers. + /// + /// Since there is no separation between the associated data and + /// the ciphertext, its length must not contain be under an adversary's + /// control. pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void { debug.assert(c.len == m.len); const extended = extend(rounds, k, npub); @@ -399,11 +407,19 @@ pub const XSalsa20Poly1305 = struct { /// `m`: Message /// `c`: Ciphertext /// `tag`: Authentication tag - /// `ad`: Associated data + /// `ad`: Associated data (see below) /// `npub`: Public nonce /// `k`: Private key /// Asserts `c.len == m.len`. /// + /// With this construction, if the associated data is not empty, + /// it must only contain fixed-length information, such as + /// sessions identifiers and sequence numbers. + /// + /// Since there is no separation between the associated data and + /// the ciphertext, its length must not be under an adversary's + /// control. + /// /// Contents of `m` are undefined if an error is returned. 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 { debug.assert(c.len == m.len); -- 2.54.0 From ed6d0eea9781b73bd6fab43fb0c1dbe8bc4102d7 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 28 May 2026 23:44:31 +0200 Subject: [PATCH 2/2] Typo --- lib/std/crypto/salsa20.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig index 892c30c669d7a5ae009eb2ceff545b6864247cf6..3d4ec106063cd16f820be464df9e053c21d376b0 100644 --- a/lib/std/crypto/salsa20.zig +++ b/lib/std/crypto/salsa20.zig @@ -387,7 +387,7 @@ pub const XSalsa20Poly1305 = struct { /// session identifiers and sequence numbers. /// /// Since there is no separation between the associated data and - /// the ciphertext, its length must not contain be under an adversary's + /// the ciphertext, its length must not be under an adversary's /// control. pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void { debug.assert(c.len == m.len); -- 2.54.0