Skip to content

Commit

Permalink
Enforce the security rules of crypto-safe hashes and min length
Browse files Browse the repository at this point in the history
  • Loading branch information
Kubuxu authored and kevina committed Jun 24, 2018
1 parent 157fb41 commit c4f3670
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 47 deletions.
11 changes: 4 additions & 7 deletions cid-fmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,9 @@ func decode(v string) (mb.Encoding, *c.Cid, error) {
}

if len(v) == 46 && v[:2] == "Qm" {
hash, err := mh.FromB58String(v)
if err != nil {
return 0, nil, err
}
cid, err := c.Decode(v)

return mb.Base58BTC, c.NewCidV0(hash), nil
return mb.Base58BTC, cid, err
}

base, data, err := mb.Decode(v)
Expand Down Expand Up @@ -269,9 +266,9 @@ func toCidV0(cid *c.Cid) (*c.Cid, error) {
if cid.Type() != c.DagProtobuf {
return nil, fmt.Errorf("can't convert non-protobuf nodes to cidv0")
}
return c.NewCidV0(cid.Hash()), nil
return c.NewCidV0(cid.Hash())
}

func toCidV1(cid *c.Cid) (*c.Cid, error) {
return c.NewCidV1(cid.Type(), cid.Hash()), nil
return c.NewCidV1(cid.Type(), cid.Hash())
}
71 changes: 44 additions & 27 deletions cid.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,26 +122,45 @@ var CodecToStr = map[uint64]string{
ZcashTx: "zcash-tx",
}

// Cid represents a self-describing content adressed
// identifier. It is formed by a Version, a Codec (which indicates
// a multicodec-packed content type) and a Multihash.
type Cid struct {
version uint64
codec uint64
hash mh.Multihash
}

// NewCidV0 returns a Cid-wrapped multihash.
// They exist to allow IPFS to work with Cids while keeping
// compatibility with the plain-multihash format used used in IPFS.
// NewCidV1 should be used preferentially.
func NewCidV0(mhash mh.Multihash) *Cid {
return &Cid{
func NewCidV0(mhash mh.Multihash) (*Cid, error) {
if mhash[0] != mh.SHA2_256 && mhash[1] != byte(mh.DefaultLengths[mh.SHA2_256]) {
return nil, errors.New("NewCidV0 accepts only SHA256 hashes of standard length")
}

c := &Cid{
version: 0,
codec: DagProtobuf,
hash: mhash,
}
return c, nil
}

// NewCidV1 returns a new Cid using the given multicodec-packed
// content type.
func NewCidV1(codecType uint64, mhash mh.Multihash) *Cid {
return &Cid{
func NewCidV1(codecType uint64, mhash mh.Multihash) (*Cid, error) {
c := &Cid{
version: 1,
codec: codecType,
hash: mhash,
}
err := ValidateCid(c)
if err != nil {
return nil, err
}
return c, nil
}

// NewPrefixV0 returns a CIDv0 prefix with the specified multihash type.
Expand All @@ -165,15 +184,6 @@ func NewPrefixV1(codecType uint64, mhType uint64) Prefix {
}
}

// Cid represents a self-describing content adressed
// identifier. It is formed by a Version, a Codec (which indicates
// a multicodec-packed content type) and a Multihash.
type Cid struct {
version uint64
codec uint64
hash mh.Multihash
}

// Parse is a short-hand function to perform Decode, Cast etc... on
// a generic interface{} type.
func Parse(v interface{}) (*Cid, error) {
Expand All @@ -186,7 +196,7 @@ func Parse(v interface{}) (*Cid, error) {
case []byte:
return Cast(v2)
case mh.Multihash:
return NewCidV0(v2), nil
return NewCidV0(v2)
case *Cid:
return v2, nil
default:
Expand Down Expand Up @@ -217,7 +227,7 @@ func Decode(v string) (*Cid, error) {
return nil, err
}

return NewCidV0(hash), nil
return NewCidV0(hash)
}

_, data, err := mbase.Decode(v)
Expand Down Expand Up @@ -256,12 +266,7 @@ func Cast(data []byte) (*Cid, error) {
if err != nil {
return nil, err
}

return &Cid{
codec: DagProtobuf,
version: 0,
hash: h,
}, nil
return NewCidV0(h)
}

vers, n := binary.Uvarint(data)
Expand All @@ -284,11 +289,16 @@ func Cast(data []byte) (*Cid, error) {
return nil, err
}

return &Cid{
c := &Cid{
version: vers,
codec: codec,
hash: h,
}, nil
}
err = ValidateCid(c)
if err != nil {
return nil, err
}
return c, nil
}

// Type returns the multicodec-packed content type of a Cid.
Expand Down Expand Up @@ -355,7 +365,7 @@ func (c *Cid) bytesV0() []byte {
}

func (c *Cid) bytesV1() []byte {
// two 8 bytes (max) numbers plus hash
// two 10 bytes (max) numbers plus hash
buf := make([]byte, 2*binary.MaxVarintLen64+len(c.hash))
n := binary.PutUvarint(buf, c.version)
n += binary.PutUvarint(buf[n:], c.codec)
Expand Down Expand Up @@ -401,7 +411,14 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
c.version = out.version
c.hash = out.hash
c.codec = out.codec
return nil
err = ValidateCid(c)
if err != nil {
// Cleanup the struct so it is impossible to ignore the error
c.version = 0
c.hash = nil
c.codec = 0
}
return err
}

// MarshalJSON procudes a JSON representation of a Cid, which looks as follows:
Expand Down Expand Up @@ -459,9 +476,9 @@ func (p Prefix) Sum(data []byte) (*Cid, error) {

switch p.Version {
case 0:
return NewCidV0(hash), nil
return NewCidV0(hash)
case 1:
return NewCidV1(p.Codec, hash), nil
return NewCidV1(p.Codec, hash)
default:
return nil, fmt.Errorf("invalid cid version")
}
Expand Down
32 changes: 24 additions & 8 deletions cid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestTableForV0(t *testing.T) {
}

func TestBasicMarshaling(t *testing.T) {
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 4)
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 20)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -100,7 +100,7 @@ func TestBasicMarshaling(t *testing.T) {
}

func TestBasesMarshaling(t *testing.T) {
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 4)
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 20)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -208,7 +208,10 @@ func TestNewPrefixV1(t *testing.T) {
if err != nil {
t.Fatal(err)
}
c2 := NewCidV1(DagCBOR, hash)
c2, err := NewCidV1(DagCBOR, hash)
if err != nil {
t.Fatal(err)
}

if !c1.Equals(c2) {
t.Fatal("cids mismatch")
Expand Down Expand Up @@ -237,7 +240,10 @@ func TestNewPrefixV0(t *testing.T) {
if err != nil {
t.Fatal(err)
}
c2 := NewCidV0(hash)
c2, err := NewCidV0(hash)
if err != nil {
t.Fatal(err)
}

if !c1.Equals(c2) {
t.Fatal("cids mismatch")
Expand All @@ -250,7 +256,10 @@ func TestNewPrefixV0(t *testing.T) {
func TestPrefixRoundtrip(t *testing.T) {
data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
c := NewCidV1(DagCBOR, hash)
c, err := NewCidV1(DagCBOR, hash)
if err != nil {
t.Fatal(err)
}

pref := c.Prefix()

Expand Down Expand Up @@ -279,7 +288,10 @@ func TestPrefixRoundtrip(t *testing.T) {
func Test16BytesVarint(t *testing.T) {
data := []byte("this is some test content")
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
c := NewCidV1(DagCBOR, hash)
c, err := NewCidV1(DagCBOR, hash)
if err != nil {
t.Fatal(err)
}

c.codec = 1 << 63
_ = c.Bytes()
Expand Down Expand Up @@ -309,9 +321,13 @@ func TestParse(t *testing.T) {
t.Fatal(err)
}

c, err := NewCidV0(h)
if err != nil {
t.Fatal(err)
}
assertions := [][]interface{}{
[]interface{}{NewCidV0(h), theHash},
[]interface{}{NewCidV0(h).Bytes(), theHash},
[]interface{}{c, theHash},
[]interface{}{c.Bytes(), theHash},
[]interface{}{h, theHash},
[]interface{}{theHash, theHash},
[]interface{}{"/ipfs/" + theHash, theHash},
Expand Down
13 changes: 8 additions & 5 deletions validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@ func TestValidateCids(t *testing.T) {

assertFalse(IsGoodHash(mh.BLAKE2B_MIN + 5))

mhcid := func(code, length uint64) *Cid {
p := NewPrefixV1(DagCBOR, code)
p.MhLength = int(length)
cid, err := p.Sum([]byte{})
mhcid := func(code uint64, length int) *Cid {
c := &Cid{
version: 1,
codec: DagCBOR,
}
mhash, err := mh.Sum([]byte{}, code, length)
if err != nil {
t.Fatal(err)
}
return cid
c.hash = mhash
return c
}

cases := []struct {
Expand Down

0 comments on commit c4f3670

Please sign in to comment.