 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			297 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package blake3 implements the BLAKE3 cryptographic hash function.
 | |
| package blake3 // import "lukechampine.com/blake3"
 | |
| 
 | |
| import (
 | |
| 	"encoding/binary"
 | |
| 	"errors"
 | |
| 	"hash"
 | |
| 	"io"
 | |
| 	"math"
 | |
| 	"math/bits"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	flagChunkStart = 1 << iota
 | |
| 	flagChunkEnd
 | |
| 	flagParent
 | |
| 	flagRoot
 | |
| 	flagKeyedHash
 | |
| 	flagDeriveKeyContext
 | |
| 	flagDeriveKeyMaterial
 | |
| 
 | |
| 	blockSize = 64
 | |
| 	chunkSize = 1024
 | |
| 
 | |
| 	maxSIMD = 16 // AVX-512 vectors can store 16 words
 | |
| )
 | |
| 
 | |
| 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 parentNode(left, right [8]uint32, key [8]uint32, flags uint32) node {
 | |
| 	n := node{
 | |
| 		cv:       key,
 | |
| 		counter:  0,         // counter is reset for parents
 | |
| 		blockLen: blockSize, // block is full
 | |
| 		flags:    flags | flagParent,
 | |
| 	}
 | |
| 	copy(n.block[:8], left[:])
 | |
| 	copy(n.block[8:], right[:])
 | |
| 	return n
 | |
| }
 | |
| 
 | |
| // Hasher implements hash.Hash.
 | |
| type Hasher struct {
 | |
| 	key   [8]uint32
 | |
| 	flags uint32
 | |
| 	size  int // output size, for Sum
 | |
| 
 | |
| 	// log(n) set of Merkle subtree roots, at most one per height.
 | |
| 	stack   [50][8]uint32 // 2^50 * maxSIMD * chunkSize = 2^64
 | |
| 	counter uint64        // number of buffers hashed; also serves as a bit vector indicating which stack elems are occupied
 | |
| 
 | |
| 	buf    [maxSIMD * chunkSize]byte
 | |
| 	buflen int
 | |
| }
 | |
| 
 | |
| func (h *Hasher) hasSubtreeAtHeight(i int) bool {
 | |
| 	return h.counter&(1<<i) != 0
 | |
| }
 | |
| 
 | |
| func (h *Hasher) pushSubtree(cv [8]uint32) {
 | |
| 	// seek to first open stack slot, merging subtrees as we go
 | |
| 	i := 0
 | |
| 	for h.hasSubtreeAtHeight(i) {
 | |
| 		cv = chainingValue(parentNode(h.stack[i], cv, h.key, h.flags))
 | |
| 		i++
 | |
| 	}
 | |
| 	h.stack[i] = cv
 | |
| 	h.counter++
 | |
| }
 | |
| 
 | |
| // rootNode computes the root of the Merkle tree. It does not modify the
 | |
| // stack.
 | |
| func (h *Hasher) rootNode() node {
 | |
| 	n := compressBuffer(&h.buf, h.buflen, &h.key, h.counter*maxSIMD, h.flags)
 | |
| 	for i := bits.TrailingZeros64(h.counter); i < bits.Len64(h.counter); i++ {
 | |
| 		if h.hasSubtreeAtHeight(i) {
 | |
| 			n = parentNode(h.stack[i], chainingValue(n), h.key, h.flags)
 | |
| 		}
 | |
| 	}
 | |
| 	n.flags |= flagRoot
 | |
| 	return n
 | |
| }
 | |
| 
 | |
| // Write implements hash.Hash.
 | |
| func (h *Hasher) Write(p []byte) (int, error) {
 | |
| 	lenp := len(p)
 | |
| 	for len(p) > 0 {
 | |
| 		if h.buflen == len(h.buf) {
 | |
| 			n := compressBuffer(&h.buf, h.buflen, &h.key, h.counter*maxSIMD, h.flags)
 | |
| 			h.pushSubtree(chainingValue(n))
 | |
| 			h.buflen = 0
 | |
| 		}
 | |
| 		n := copy(h.buf[h.buflen:], p)
 | |
| 		h.buflen += n
 | |
| 		p = p[n:]
 | |
| 	}
 | |
| 	return lenp, nil
 | |
| }
 | |
| 
 | |
| // Sum implements hash.Hash.
 | |
| func (h *Hasher) Sum(b []byte) (sum []byte) {
 | |
| 	// We need to append h.Size() bytes to b. Reuse b's capacity if possible;
 | |
| 	// otherwise, allocate a new slice.
 | |
| 	if total := len(b) + h.Size(); cap(b) >= total {
 | |
| 		sum = b[:total]
 | |
| 	} else {
 | |
| 		sum = make([]byte, total)
 | |
| 		copy(sum, b)
 | |
| 	}
 | |
| 	// Read into the appended portion of sum. Use a low-latency-low-throughput
 | |
| 	// path for small digests (requiring a single compression), and a
 | |
| 	// high-latency-high-throughput path for large digests.
 | |
| 	if dst := sum[len(b):]; len(dst) <= 64 {
 | |
| 		var out [64]byte
 | |
| 		wordsToBytes(compressNode(h.rootNode()), &out)
 | |
| 		copy(dst, out[:])
 | |
| 	} else {
 | |
| 		h.XOF().Read(dst)
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Reset implements hash.Hash.
 | |
| func (h *Hasher) Reset() {
 | |
| 	h.counter = 0
 | |
| 	h.buflen = 0
 | |
| }
 | |
| 
 | |
| // BlockSize implements hash.Hash.
 | |
| func (h *Hasher) BlockSize() int { return 64 }
 | |
| 
 | |
| // Size implements hash.Hash.
 | |
| func (h *Hasher) Size() int { return h.size }
 | |
| 
 | |
| // XOF returns an OutputReader initialized with the current hash state.
 | |
| func (h *Hasher) XOF() *OutputReader {
 | |
| 	return &OutputReader{
 | |
| 		n: h.rootNode(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newHasher(key [8]uint32, flags uint32, size int) *Hasher {
 | |
| 	return &Hasher{
 | |
| 		key:   key,
 | |
| 		flags: flags,
 | |
| 		size:  size,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // New returns a Hasher for the specified digest size and key. If key is nil,
 | |
| // the hash is unkeyed. Otherwise, len(key) must be 32.
 | |
| func New(size int, key []byte) *Hasher {
 | |
| 	if key == nil {
 | |
| 		return newHasher(iv, 0, size)
 | |
| 	}
 | |
| 	var keyWords [8]uint32
 | |
| 	for i := range keyWords {
 | |
| 		keyWords[i] = binary.LittleEndian.Uint32(key[i*4:])
 | |
| 	}
 | |
| 	return newHasher(keyWords, flagKeyedHash, size)
 | |
| }
 | |
| 
 | |
| // Sum256 and Sum512 always use the same hasher state, so we can save some time
 | |
| // when hashing small inputs by constructing the hasher ahead of time.
 | |
| var defaultHasher = New(64, nil)
 | |
| 
 | |
| // Sum256 returns the unkeyed BLAKE3 hash of b, truncated to 256 bits.
 | |
| func Sum256(b []byte) (out [32]byte) {
 | |
| 	out512 := Sum512(b)
 | |
| 	copy(out[:], out512[:])
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Sum512 returns the unkeyed BLAKE3 hash of b, truncated to 512 bits.
 | |
| func Sum512(b []byte) (out [64]byte) {
 | |
| 	var n node
 | |
| 	if len(b) <= blockSize {
 | |
| 		hashBlock(&out, b)
 | |
| 		return
 | |
| 	} else if len(b) <= chunkSize {
 | |
| 		n = compressChunk(b, &iv, 0, 0)
 | |
| 		n.flags |= flagRoot
 | |
| 	} else {
 | |
| 		h := *defaultHasher
 | |
| 		h.Write(b)
 | |
| 		n = h.rootNode()
 | |
| 	}
 | |
| 	wordsToBytes(compressNode(n), &out)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // DeriveKey derives a subkey from ctx and srcKey. ctx should be hardcoded,
 | |
| // globally unique, and application-specific. A good format for ctx strings is:
 | |
| //
 | |
| //	[application] [commit timestamp] [purpose]
 | |
| //
 | |
| // e.g.:
 | |
| //
 | |
| //	example.com 2019-12-25 16:18:03 session tokens v1
 | |
| //
 | |
| // The purpose of these requirements is to ensure that an attacker cannot trick
 | |
| // two different applications into using the same context string.
 | |
| func DeriveKey(subKey []byte, ctx string, srcKey []byte) {
 | |
| 	// construct the derivation Hasher
 | |
| 	const derivationIVLen = 32
 | |
| 	h := newHasher(iv, flagDeriveKeyContext, 32)
 | |
| 	h.Write([]byte(ctx))
 | |
| 	derivationIV := h.Sum(make([]byte, 0, derivationIVLen))
 | |
| 	var ivWords [8]uint32
 | |
| 	for i := range ivWords {
 | |
| 		ivWords[i] = binary.LittleEndian.Uint32(derivationIV[i*4:])
 | |
| 	}
 | |
| 	h = newHasher(ivWords, flagDeriveKeyMaterial, 0)
 | |
| 	// derive the subKey
 | |
| 	h.Write(srcKey)
 | |
| 	h.XOF().Read(subKey)
 | |
| }
 | |
| 
 | |
| // An OutputReader produces an seekable stream of 2^64 - 1 pseudorandom output
 | |
| // bytes.
 | |
| type OutputReader struct {
 | |
| 	n   node
 | |
| 	buf [maxSIMD * blockSize]byte
 | |
| 	off uint64
 | |
| }
 | |
| 
 | |
| // Read implements io.Reader. Callers may assume that Read returns len(p), nil
 | |
| // unless the read would extend beyond the end of the stream.
 | |
| func (or *OutputReader) Read(p []byte) (int, error) {
 | |
| 	if or.off == math.MaxUint64 {
 | |
| 		return 0, io.EOF
 | |
| 	} else if rem := math.MaxUint64 - or.off; uint64(len(p)) > rem {
 | |
| 		p = p[:rem]
 | |
| 	}
 | |
| 	lenp := len(p)
 | |
| 	for len(p) > 0 {
 | |
| 		if or.off%(maxSIMD*blockSize) == 0 {
 | |
| 			or.n.counter = or.off / blockSize
 | |
| 			compressBlocks(&or.buf, or.n)
 | |
| 		}
 | |
| 		n := copy(p, or.buf[or.off%(maxSIMD*blockSize):])
 | |
| 		p = p[n:]
 | |
| 		or.off += uint64(n)
 | |
| 	}
 | |
| 	return lenp, nil
 | |
| }
 | |
| 
 | |
| // Seek implements io.Seeker.
 | |
| func (or *OutputReader) Seek(offset int64, whence int) (int64, error) {
 | |
| 	off := or.off
 | |
| 	switch whence {
 | |
| 	case io.SeekStart:
 | |
| 		if offset < 0 {
 | |
| 			return 0, errors.New("seek position cannot be negative")
 | |
| 		}
 | |
| 		off = uint64(offset)
 | |
| 	case io.SeekCurrent:
 | |
| 		if offset < 0 {
 | |
| 			if uint64(-offset) > off {
 | |
| 				return 0, errors.New("seek position cannot be negative")
 | |
| 			}
 | |
| 			off -= uint64(-offset)
 | |
| 		} else {
 | |
| 			off += uint64(offset)
 | |
| 		}
 | |
| 	case io.SeekEnd:
 | |
| 		off = uint64(offset) - 1
 | |
| 	default:
 | |
| 		panic("invalid whence")
 | |
| 	}
 | |
| 	or.off = off
 | |
| 	or.n.counter = uint64(off) / blockSize
 | |
| 	if or.off%(maxSIMD*blockSize) != 0 {
 | |
| 		compressBlocks(&or.buf, or.n)
 | |
| 	}
 | |
| 	// NOTE: or.off >= 2^63 will result in a negative return value.
 | |
| 	// Nothing we can do about this.
 | |
| 	return int64(or.off), nil
 | |
| }
 | |
| 
 | |
| // ensure that Hasher implements hash.Hash
 | |
| var _ hash.Hash = (*Hasher)(nil)
 |