Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
3.1 kB · 104 lines
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105// Package bdasl implements BDASL content identifiers (CIDs) using BLAKE3.//// A BDASL CID is a DASL CID with BLAKE3 as the hash function://// string form: "b" + base32lower(binary)// binary form: [0x01 version] [0x55 raw codec] [0x1e BLAKE3] [0x20 size] [32-byte digest]//// See https://dasl.ing/bdasl.html and https://dasl.ing/cid.htmlpackage bdasl
import ( "encoding/base32" "fmt" "io" "strings"
"lukechampine.com/blake3")
// base32 lowercase encoder per RFC 4648 §6var b32 = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)
// CID computes a BDASL CID (BLAKE3, raw codec) for the given data.func CID(data []byte) string { digest := blake3.Sum256(data) return encodeCID(digest[:])}
// encodeCID wraps a 32-byte BLAKE3 digest in the BDASL multibase/multihash// framing. The caller is responsible for ensuring digest is exactly 32// bytes long.func encodeCID(digest []byte) string { var bin [36]byte bin[0] = 0x01 // version bin[1] = 0x55 // raw codec bin[2] = 0x1e // BLAKE3 bin[3] = 0x20 // 32-byte hash copy(bin[4:], digest) return "b" + b32.EncodeToString(bin[:])}
// Writer is an io.Writer that incrementally BLAKE3-hashes everything// written to it and exposes the final BDASL CID via CID(). It's intended// for streaming pipelines that need to compute a content identifier for a// payload too large to hold in memory.type Writer struct { h *blake3.Hasher}
// NewWriter returns a fresh streaming BDASL hasher.func NewWriter() *Writer { return &Writer{h: blake3.New(32, nil)}}
// Write feeds bytes into the running BLAKE3 hash. Never returns a short// write or error.func (w *Writer) Write(p []byte) (int, error) { return w.h.Write(p)}
// CID returns the BDASL CID for everything written so far. Calling it// does not finalize the hasher — further Write calls are valid and a// subsequent CID call returns the updated identifier.func (w *Writer) CID() string { return encodeCID(w.h.Sum(nil))}
var _ io.Writer = (*Writer)(nil)
// Verify checks that data matches the given CID.func Verify(cid string, data []byte) error { expected := CID(data) if cid != expected { return fmt.Errorf("CID mismatch: got %s, expected %s", cid, expected) } return nil}
// Parse extracts the 32-byte BLAKE3 digest from a BDASL CID string.// Returns an error if the CID is malformed or uses an unsupported hash type.func Parse(cid string) ([32]byte, error) { var digest [32]byte if !strings.HasPrefix(cid, "b") { return digest, fmt.Errorf("unsupported CID prefix: %q", cid[:1]) } bin, err := b32.DecodeString(cid[1:]) if err != nil { return digest, fmt.Errorf("base32 decode: %w", err) } if len(bin) != 36 { return digest, fmt.Errorf("unexpected CID length: %d", len(bin)) } if bin[0] != 0x01 { return digest, fmt.Errorf("unsupported CID version: %d", bin[0]) } if bin[2] != 0x1e { return digest, fmt.Errorf("unsupported hash type: 0x%02x (expected 0x1e BLAKE3)", bin[2]) } if bin[3] != 0x20 { return digest, fmt.Errorf("unsupported hash size: %d", bin[3]) } copy(digest[:], bin[4:]) return digest, nil}