// Package multihash is the Go implementation of // https://github.com/multiformats/multihash, or self-describing // hashes. package multihash import ( "encoding/binary" "encoding/hex" "errors" "fmt" "math" b58 "github.com/mr-tron/base58/base58" ) // errors var ( ErrUnknownCode = errors.New("unknown multihash code") ErrTooShort = errors.New("multihash too short. must be >= 2 bytes") ErrTooLong = errors.New("multihash too long. must be < 129 bytes") ErrLenNotSupported = errors.New("multihash does not yet support digests longer than 127 bytes") ErrInvalidMultihash = errors.New("input isn't valid multihash") ErrVarintBufferShort = errors.New("uvarint: buffer too small") ErrVarintTooLong = errors.New("uvarint: varint too big (max 64bit)") ) // ErrInconsistentLen is returned when a decoded multihash has an inconsistent length type ErrInconsistentLen struct { dm *DecodedMultihash } func (e ErrInconsistentLen) Error() string { return fmt.Sprintf("multihash length inconsistent: %v", e.dm) } // constants const ( ID = 0x00 SHA1 = 0x11 SHA2_256 = 0x12 SHA2_512 = 0x13 SHA3_224 = 0x17 SHA3_256 = 0x16 SHA3_384 = 0x15 SHA3_512 = 0x14 SHA3 = SHA3_512 KECCAK_224 = 0x1A KECCAK_256 = 0x1B KECCAK_384 = 0x1C KECCAK_512 = 0x1D SHAKE_128 = 0x18 SHAKE_256 = 0x19 BLAKE2B_MIN = 0xb201 BLAKE2B_MAX = 0xb240 BLAKE2S_MIN = 0xb241 BLAKE2S_MAX = 0xb260 MD5 = 0xd5 DBL_SHA2_256 = 0x56 MURMUR3 = 0x22 X11 = 0x1100 ) func init() { // Add blake2b (64 codes) for c := uint64(BLAKE2B_MIN); c <= BLAKE2B_MAX; c++ { n := c - BLAKE2B_MIN + 1 name := fmt.Sprintf("blake2b-%d", n*8) Names[name] = c Codes[c] = name DefaultLengths[c] = int(n) } // Add blake2s (32 codes) for c := uint64(BLAKE2S_MIN); c <= BLAKE2S_MAX; c++ { n := c - BLAKE2S_MIN + 1 name := fmt.Sprintf("blake2s-%d", n*8) Names[name] = c Codes[c] = name DefaultLengths[c] = int(n) } } // Names maps the name of a hash to the code var Names = map[string]uint64{ "id": ID, "sha1": SHA1, "sha2-256": SHA2_256, "sha2-512": SHA2_512, "sha3": SHA3_512, "sha3-224": SHA3_224, "sha3-256": SHA3_256, "sha3-384": SHA3_384, "sha3-512": SHA3_512, "dbl-sha2-256": DBL_SHA2_256, "murmur3": MURMUR3, "keccak-224": KECCAK_224, "keccak-256": KECCAK_256, "keccak-384": KECCAK_384, "keccak-512": KECCAK_512, "shake-128": SHAKE_128, "shake-256": SHAKE_256, "x11": X11, "md5": MD5, } // Codes maps a hash code to it's name var Codes = map[uint64]string{ ID: "id", SHA1: "sha1", SHA2_256: "sha2-256", SHA2_512: "sha2-512", SHA3_224: "sha3-224", SHA3_256: "sha3-256", SHA3_384: "sha3-384", SHA3_512: "sha3-512", DBL_SHA2_256: "dbl-sha2-256", MURMUR3: "murmur3", KECCAK_224: "keccak-224", KECCAK_256: "keccak-256", KECCAK_384: "keccak-384", KECCAK_512: "keccak-512", SHAKE_128: "shake-128", SHAKE_256: "shake-256", X11: "x11", MD5: "md5", } // DefaultLengths maps a hash code to it's default length var DefaultLengths = map[uint64]int{ ID: -1, SHA1: 20, SHA2_256: 32, SHA2_512: 64, SHA3_224: 28, SHA3_256: 32, SHA3_384: 48, SHA3_512: 64, DBL_SHA2_256: 32, KECCAK_224: 28, KECCAK_256: 32, MURMUR3: 4, KECCAK_384: 48, KECCAK_512: 64, SHAKE_128: 32, SHAKE_256: 64, X11: 64, MD5: 16, } func uvarint(buf []byte) (uint64, []byte, error) { n, c := binary.Uvarint(buf) if c == 0 { return n, buf, ErrVarintBufferShort } else if c < 0 { return n, buf[-c:], ErrVarintTooLong } else { return n, buf[c:], nil } } // DecodedMultihash represents a parsed multihash and allows // easy access to the different parts of a multihash. type DecodedMultihash struct { Code uint64 Name string Length int // Length is just int as it is type of len() opearator Digest []byte // Digest holds the raw multihash bytes } // Multihash is byte slice with the following form: // . // See the spec for more information. type Multihash []byte // HexString returns the hex-encoded representation of a multihash. func (m *Multihash) HexString() string { return hex.EncodeToString([]byte(*m)) } // String is an alias to HexString(). func (m *Multihash) String() string { return m.HexString() } // FromHexString parses a hex-encoded multihash. func FromHexString(s string) (Multihash, error) { b, err := hex.DecodeString(s) if err != nil { return Multihash{}, err } return Cast(b) } // B58String returns the B58-encoded representation of a multihash. func (m Multihash) B58String() string { return b58.Encode([]byte(m)) } // FromB58String parses a B58-encoded multihash. func FromB58String(s string) (m Multihash, err error) { b, err := b58.Decode(s) if err != nil { return Multihash{}, ErrInvalidMultihash } return Cast(b) } // Cast casts a buffer onto a multihash, and returns an error // if it does not work. func Cast(buf []byte) (Multihash, error) { dm, err := Decode(buf) if err != nil { return Multihash{}, err } if !ValidCode(dm.Code) { return Multihash{}, ErrUnknownCode } return Multihash(buf), nil } // Decode parses multihash bytes into a DecodedMultihash. func Decode(buf []byte) (*DecodedMultihash, error) { if len(buf) < 2 { return nil, ErrTooShort } var err error var code, length uint64 code, buf, err = uvarint(buf) if err != nil { return nil, err } length, buf, err = uvarint(buf) if err != nil { return nil, err } if length > math.MaxInt32 { return nil, errors.New("digest too long, supporting only <= 2^31-1") } dm := &DecodedMultihash{ Code: code, Name: Codes[code], Length: int(length), Digest: buf, } if len(dm.Digest) != dm.Length { return nil, ErrInconsistentLen{dm} } return dm, nil } // Encode a hash digest along with the specified function code. // Note: the length is derived from the length of the digest itself. func Encode(buf []byte, code uint64) ([]byte, error) { if !ValidCode(code) { return nil, ErrUnknownCode } start := make([]byte, 2*binary.MaxVarintLen64, 2*binary.MaxVarintLen64+len(buf)) spot := start n := binary.PutUvarint(spot, code) spot = start[n:] n += binary.PutUvarint(spot, uint64(len(buf))) return append(start[:n], buf...), nil } // EncodeName is like Encode() but providing a string name // instead of a numeric code. See Names for allowed values. func EncodeName(buf []byte, name string) ([]byte, error) { return Encode(buf, Names[name]) } // ValidCode checks whether a multihash code is valid. func ValidCode(code uint64) bool { _, ok := Codes[code] return ok }