std.crypto: improve KT documentation, use key_length for B3 key length (#25807)

It was not obvious that the KT128/KT256 customization string can be
used to set a key, or what it was designed to be used for at all.

Also properly use key_length and not digest_length for the BLAKE3
key length (no practical changes as they are both 32, but that was
confusing).

Remove unneeded simd_degree copies by the way, and that doesn't need
to be in the public interface.
This commit is contained in:
Frank Denis 2025-11-07 08:20:04 +01:00 committed by GitHub
parent 2e8f8afc83
commit 4b593a6c24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 45 additions and 15 deletions

View file

@ -12,8 +12,8 @@ const Vec16 = @Vector(16, u32);
const chunk_length = 1024; const chunk_length = 1024;
const max_depth = 54; const max_depth = 54;
pub const simd_degree = std.simd.suggestVectorLength(u32) orelse 1; const simd_degree = std.simd.suggestVectorLength(u32) orelse 1;
pub const max_simd_degree = simd_degree; const max_simd_degree = simd_degree;
const max_simd_degree_or_2 = if (max_simd_degree > 2) max_simd_degree else 2; const max_simd_degree_or_2 = if (max_simd_degree > 2) max_simd_degree else 2;
/// Threshold for switching to parallel processing. /// Threshold for switching to parallel processing.
@ -502,9 +502,7 @@ fn hashManySimd(
var out_ptr = out.ptr; var out_ptr = out.ptr;
var cnt = counter; var cnt = counter;
const simd_deg = comptime simd_degree; if (simd_degree >= 16) {
if (comptime simd_deg >= 16) {
while (remaining >= 16) { while (remaining >= 16) {
const sixteen_inputs = [16][*]const u8{ const sixteen_inputs = [16][*]const u8{
inp[0], inp[1], inp[2], inp[3], inp[0], inp[1], inp[2], inp[3],
@ -525,7 +523,7 @@ fn hashManySimd(
} }
} }
if (comptime simd_deg >= 8) { if (simd_degree >= 8) {
while (remaining >= 8) { while (remaining >= 8) {
const eight_inputs = [8][*]const u8{ const eight_inputs = [8][*]const u8{
inp[0], inp[1], inp[2], inp[3], inp[0], inp[1], inp[2], inp[3],
@ -544,7 +542,7 @@ fn hashManySimd(
} }
} }
if (comptime simd_deg >= 4) { if (simd_degree >= 4) {
while (remaining >= 4) { while (remaining >= 4) {
const four_inputs = [4][*]const u8{ const four_inputs = [4][*]const u8{
inp[0], inp[0],
@ -571,7 +569,7 @@ fn hashManySimd(
} }
fn hashMany(inputs: [][*]const u8, num_inputs: usize, blocks: usize, key: [8]u32, counter: u64, increment_counter: bool, flags: Flags, flags_start: Flags, flags_end: Flags, out: []u8) void { fn hashMany(inputs: [][*]const u8, num_inputs: usize, blocks: usize, key: [8]u32, counter: u64, increment_counter: bool, flags: Flags, flags_start: Flags, flags_end: Flags, out: []u8) void {
if (comptime max_simd_degree >= 4) { if (max_simd_degree >= 4) {
hashManySimd(inputs, num_inputs, blocks, key, counter, increment_counter, flags, flags_start, flags_end, out); hashManySimd(inputs, num_inputs, blocks, key, counter, increment_counter, flags, flags_start, flags_end, out);
} else { } else {
hashManyPortable(inputs, num_inputs, blocks, key, counter, increment_counter, flags, flags_start, flags_end, out); hashManyPortable(inputs, num_inputs, blocks, key, counter, increment_counter, flags, flags_start, flags_end, out);
@ -909,7 +907,7 @@ pub const Blake3 = struct {
pub const digest_length = 32; pub const digest_length = 32;
pub const key_length = 32; pub const key_length = 32;
pub const Options = struct { key: ?[digest_length]u8 = null }; pub const Options = struct { key: ?[key_length]u8 = null };
pub const KdfOptions = struct {}; pub const KdfOptions = struct {};
key: [8]u32, key: [8]u32,

View file

@ -840,7 +840,21 @@ fn KTHash(
/// The block length, or rate, in bytes. /// The block length, or rate, in bytes.
pub const block_length = Variant.rate; pub const block_length = Variant.rate;
/// Options for KangarooTwelve can include a customization string for domain separation. /// Configuration options for KangarooTwelve hashing.
///
/// Options include an optional customization string that provides domain separation,
/// ensuring that identical inputs with different customization strings
/// produce completely distinct hash outputs.
///
/// This prevents hash collisions when the same data is hashed in different contexts.
///
/// Customization strings can be of any length.
///
/// Common options for customization::
///
/// - Key derivation or MAC: 16-byte secret for KT128, 32-byte secret for KT256
/// - Context Separation: domain-specific strings (e.g., "email", "password", "session")
/// - Composite Keys: concatenation of secret key + context string
pub const Options = struct { pub const Options = struct {
customization: ?[]const u8 = null, customization: ?[]const u8 = null,
}; };
@ -864,7 +878,20 @@ fn KTHash(
pending_count: usize, // Number of complete chunks in pending_chunks pending_count: usize, // Number of complete chunks in pending_chunks
/// Initialize a KangarooTwelve hashing context. /// Initialize a KangarooTwelve hashing context.
/// The customization string is optional and used for domain separation. ///
/// Options include an optional customization string that provides domain separation,
/// ensuring that identical inputs with different customization strings
/// produce completely distinct hash outputs.
///
/// This prevents hash collisions when the same data is hashed in different contexts.
///
/// Customization strings can be of any length.
///
/// Common options for customization::
///
/// - Key derivation or MAC: 16-byte secret for KT128, 32-byte secret for KT256
/// - Context Separation: domain-specific strings (e.g., "email", "password", "session")
/// - Composite Keys: concatenation of secret key + context string
pub fn init(options: Options) Self { pub fn init(options: Options) Self {
const custom = options.customization orelse &[_]u8{}; const custom = options.customization orelse &[_]u8{};
return .{ return .{
@ -971,7 +998,13 @@ fn KTHash(
} }
/// Finalize the hash and produce output. /// Finalize the hash and produce output.
/// After calling this, the context should not be reused. ///
/// Unlike traditional hash functions, the output can be of any length.
///
/// When using as a regular hash function, use the recommended `digest_length` value (32 bytes for KT128, 64 bytes for KT256).
///
/// After calling this method, the context should not be reused. However, the structure can be cloned before finalizing
/// to compute multiple hashes with the same prefix.
pub fn final(self: *Self, out: []u8) void { pub fn final(self: *Self, out: []u8) void {
const cv_size = Variant.cv_size; const cv_size = Variant.cv_size;
@ -1063,12 +1096,11 @@ fn KTHash(
} }
/// Hash a message using sequential processing with SIMD acceleration. /// Hash a message using sequential processing with SIMD acceleration.
/// Best performance for inputs under 10MB. Never allocates memory.
/// ///
/// Parameters: /// Parameters:
/// - message: Input data to hash (any length) /// - message: Input data to hash (any length)
/// - out: Output buffer (any length, arbitrary output sizes supported) /// - out: Output buffer (any length, arbitrary output sizes supported, `digest_length` recommended for standard use)
/// - options: Optional settings including customization string for domain separation /// - options: Optional settings to include a secret key or a context separation string
pub fn hash(message: []const u8, out: []u8, options: Options) !void { pub fn hash(message: []const u8, out: []u8, options: Options) !void {
const custom = options.customization orelse &[_]u8{}; const custom = options.customization orelse &[_]u8{};