diff --git a/verifcid/allowlist.go b/verifcid/allowlist.go deleted file mode 100644 index 419400e29..000000000 --- a/verifcid/allowlist.go +++ /dev/null @@ -1,58 +0,0 @@ -package verifcid - -import ( - "github.com/ipfs/go-cid" - mh "github.com/multiformats/go-multihash" -) - -// Allowlist defines an interface containing list of allowed multihashes. -type Allowlist interface { - // IsAllowed checks for multihash allowance by the code. - IsAllowed(code uint64) bool - // ValidateCid validates multihash allowance behind given CID. - ValidateCid(c cid.Cid) error -} - -// NewAllowlist constructs new Allowlist from the given map set. -func NewAllowlist(allowset map[uint64]bool) Allowlist { - return &allowlist{allowset: allowset} -} - -type allowlist struct { - allowset map[uint64]bool -} - -func (ghr *allowlist) IsAllowed(code uint64) bool { - good, found := ghr.allowset[code] - if good { - return true - } - - if !found { - if code >= mh.BLAKE2B_MIN+19 && code <= mh.BLAKE2B_MAX { - return true - } - if code >= mh.BLAKE2S_MIN+19 && code <= mh.BLAKE2S_MAX { - return true - } - } - - return false -} - -func (ghr *allowlist) ValidateCid(c cid.Cid) error { - pref := c.Prefix() - if !ghr.IsAllowed(pref.MhType) { - return ErrPossiblyInsecureHashFunction - } - - if pref.MhType != mh.IDENTITY && pref.MhLength < minimumHashLength { - return ErrBelowMinimumHashLength - } - - if pref.MhType != mh.IDENTITY && pref.MhLength > maximumHashLength { - return ErrAboveMaximumHashLength - } - - return nil -} diff --git a/verifcid/validate.go b/verifcid/validate.go index c37960d44..ea552e68b 100644 --- a/verifcid/validate.go +++ b/verifcid/validate.go @@ -11,28 +11,43 @@ var ErrPossiblyInsecureHashFunction = fmt.Errorf("potentially insecure hash func var ErrBelowMinimumHashLength = fmt.Errorf("hashes must be at least %d bytes long", minimumHashLength) var ErrAboveMaximumHashLength = fmt.Errorf("hashes must be at most %d bytes long", maximumHashLength) -const minimumHashLength = 20 -const maximumHashLength = 128 +// Allowlist defines an interface containing list of allowed multihashes. +type Allowlist interface { + // IsAllowed checks for multihash allowance by the code. + IsAllowed(code uint64) bool +} + +// NewAllowlist constructs new [Allowlist] from the given map set. +func NewAllowlist(allowset map[uint64]bool) Allowlist { + return allowlist{allowset: allowset} +} + +// NewOverdingAllowlist is like [NewAllowlist] but it will fallback to an other [AllowList] if keys are missing. +// If override is nil it will return unsecure for unknown things. +func NewOverdingAllowlist(override Allowlist, allowset map[uint64]bool) Allowlist { + return allowlist{override, allowset} +} -var goodset = map[uint64]bool{ - mh.SHA2_256: true, - mh.SHA2_512: true, - mh.SHA3_224: true, - mh.SHA3_256: true, - mh.SHA3_384: true, - mh.SHA3_512: true, - mh.SHAKE_256: true, - mh.DBL_SHA2_256: true, - mh.KECCAK_224: true, - mh.KECCAK_256: true, - mh.KECCAK_384: true, - mh.KECCAK_512: true, - mh.BLAKE3: true, - mh.IDENTITY: true, - - mh.SHA1: true, // not really secure but still useful +type allowlist struct { + override Allowlist + allowset map[uint64]bool } +func (ghr allowlist) IsAllowed(code uint64) bool { + if good, found := ghr.allowset[code]; found { + return good + } + + if ghr.override != nil { + return ghr.override.IsAllowed(code) + } + + return false +} + +const minimumHashLength = 20 +const maximumHashLength = 128 + // IsGoodHash checks for multihash allowance by the given code. func IsGoodHash(code uint64) bool { return DefaultAllowlist.IsAllowed(code) @@ -40,8 +55,54 @@ func IsGoodHash(code uint64) bool { // ValidateCid validates multihash allowance behind given CID. func ValidateCid(c cid.Cid) error { - return DefaultAllowlist.ValidateCid(c) + return ValidateCidWithAllowlist(DefaultAllowlist, c) +} + +func ValidateCidWithAllowlist(allowlist Allowlist, c cid.Cid) error { + pref := c.Prefix() + // don't use IsAllowed to avoid recursing twice + if !allowlist.IsAllowed(pref.MhType) { + return ErrPossiblyInsecureHashFunction + } + + if pref.MhType != mh.IDENTITY && pref.MhLength < minimumHashLength { + return ErrBelowMinimumHashLength + } + + if pref.MhType != mh.IDENTITY && pref.MhLength > maximumHashLength { + return ErrAboveMaximumHashLength + } + + return nil } // DefaultAllowlist is the default list of hashes allowed in IPFS. -var DefaultAllowlist = NewAllowlist(goodset) +var DefaultAllowlist defaultAllowlist + +type defaultAllowlist struct{} + +func (defaultAllowlist) IsAllowed(code uint64) bool { + switch code { + case mh.SHA2_256, mh.SHA2_512, + mh.SHAKE_256, + mh.DBL_SHA2_256, + mh.BLAKE3, + mh.IDENTITY, + + mh.SHA3_224, mh.SHA3_256, mh.SHA3_384, mh.SHA3_512, + mh.KECCAK_224, mh.KECCAK_256, mh.KECCAK_384, mh.KECCAK_512, + + mh.SHA1: // not really secure but still useful for git + return true + default: + if code >= mh.BLAKE2B_MIN+19 && code <= mh.BLAKE2B_MAX { + return true + } + if code >= mh.BLAKE2S_MIN+19 && code <= mh.BLAKE2S_MAX { + return true + } + + return false + } + +}