Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

XSalsa20Poly1305

The XSalsa stream cipher, combined with the Poly1305 MAC

salsa20.XSalsa20Poly1305
pub const XSalsa20Poly1305 = struct

File

lib/std/crypto/salsa20.zig:368

Code

pub const XSalsa20Poly1305 = struct {
    /// Authentication tag length in bytes.
    pub const tag_length = Poly1305.mac_length;
    /// Nonce length in bytes.
    pub const nonce_length = XSalsa20.nonce_length;
    /// Key length in bytes.
    pub const key_length = XSalsa20.key_length;

    const rounds = 20;

    /// c: ciphertext: output buffer should be of size m.len
    /// tag: authentication tag: output MAC
    /// m: message
    /// 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 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);
        var block0: [64]u8 = @splat(0);
        const mlen0 = @min(32, m.len);
        @memcpy(block0[32..][0..mlen0], m[0..mlen0]);
        Salsa20.xor(block0[0..], block0[0..], 0, extended.key, extended.nonce);
        @memcpy(c[0..mlen0], block0[32..][0..mlen0]);
        Salsa20.xor(c[mlen0..], m[mlen0..], 1, extended.key, extended.nonce);
        var mac = Poly1305.init(block0[0..32]);
        mac.update(ad);
        mac.update(c);
        mac.final(tag);
    }

    /// `m`: Message
    /// `c`: Ciphertext
    /// `tag`: Authentication tag
    /// `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);
        const extended = extend(rounds, k, npub);
        var block0: [64]u8 = @splat(0);
        const mlen0 = @min(32, c.len);
        @memcpy(block0[32..][0..mlen0], c[0..mlen0]);
        Salsa20.xor(block0[0..], block0[0..], 0, extended.key, extended.nonce);
        var mac = Poly1305.init(block0[0..32]);
        mac.update(ad);
        mac.update(c);
        var computed_tag: [tag_length]u8 = undefined;
        mac.final(&computed_tag);

        const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
        if (!verify) {
            crypto.secureZero(u8, &computed_tag);
            @memset(m, undefined);
            return error.AuthenticationFailed;
        }
        @memcpy(m[0..mlen0], block0[32..][0..mlen0]);
        Salsa20.xor(m[mlen0..], c[mlen0..], 1, extended.key, extended.nonce);
    }
}