// Package guts provides a low-level interface to the BLAKE3 cryptographic hash // function.
package guts import ( ) // Various constants. const ( FlagChunkStart = 1 << iota FlagChunkEnd FlagParent FlagRoot FlagKeyedHash FlagDeriveKeyContext FlagDeriveKeyMaterial BlockSize = 64 ChunkSize = 1024 MaxSIMD = 16 // AVX-512 vectors can store 16 words ) // IV is the BLAKE3 initialization vector. var IV = [8]uint32{ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19, } // A Node represents a chunk or parent in the BLAKE3 Merkle tree. type Node struct { CV [8]uint32 // chaining value from previous node Block [16]uint32 Counter uint64 BlockLen uint32 Flags uint32 } // ParentNode returns a Node that incorporates the chaining values of two child // nodes. func (, [8]uint32, *[8]uint32, uint32) Node { := Node{ CV: *, Counter: 0, // counter is reset for parents BlockLen: BlockSize, // block is full Flags: | FlagParent, } copy(.Block[:8], [:]) copy(.Block[8:], [:]) return } // Eigentrees returns the sequence of eigentree heights that increment counter // to counter+chunks. func ( uint64, uint64) ( []int) { for := ; < +; { := min(bits.TrailingZeros64(), bits.Len64(+-)-1) = append(, ) += 1 << } return } // CompressEigentree compresses a buffer of 2^n chunks in parallel, returning // their root node. func ( []byte, *[8]uint32, uint64, uint32) Node { if := uint64(len() / ChunkSize); bits.OnesCount64() != 1 { panic("non-power-of-two eigentree size") } else if == 1 { return CompressChunk(, , , ) } else if <= MaxSIMD { := len() if cap() < MaxSIMD*ChunkSize { = append(, make([]byte, MaxSIMD*ChunkSize-len())...) } return CompressBuffer((*[MaxSIMD * ChunkSize]byte)([:MaxSIMD*ChunkSize]), , , , ) } else { := make([][8]uint32, /MaxSIMD) var sync.WaitGroup for := range { .Add(1) go func( uint64) { defer .Done() [] = ChainingValue(CompressBuffer((*[MaxSIMD * ChunkSize]byte)([*MaxSIMD*ChunkSize:]), MaxSIMD*ChunkSize, , +(MaxSIMD*), )) }(uint64()) } .Wait() var func( [][8]uint32) Node = func( [][8]uint32) Node { if len() == 2 { return ParentNode([0], [1], , ) } else if len() == MaxSIMD { return mergeSubtrees((*[MaxSIMD][8]uint32)(), MaxSIMD, , ) } return ParentNode(ChainingValue(([:len()/2])), ChainingValue(([len()/2:])), , ) } return () } }