Skip to content

Commit c4f3670

Browse files
Kubuxukevina
authored andcommitted
Enforce the security rules of crypto-safe hashes and min length
See: ipfs/kubo#4371
1 parent 157fb41 commit c4f3670

File tree

4 files changed

+80
-47
lines changed

4 files changed

+80
-47
lines changed

cid-fmt/main.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,9 @@ func decode(v string) (mb.Encoding, *c.Cid, error) {
138138
}
139139

140140
if len(v) == 46 && v[:2] == "Qm" {
141-
hash, err := mh.FromB58String(v)
142-
if err != nil {
143-
return 0, nil, err
144-
}
141+
cid, err := c.Decode(v)
145142

146-
return mb.Base58BTC, c.NewCidV0(hash), nil
143+
return mb.Base58BTC, cid, err
147144
}
148145

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

275272
func toCidV1(cid *c.Cid) (*c.Cid, error) {
276-
return c.NewCidV1(cid.Type(), cid.Hash()), nil
273+
return c.NewCidV1(cid.Type(), cid.Hash())
277274
}

cid.go

+44-27
Original file line numberDiff line numberDiff line change
@@ -122,26 +122,45 @@ var CodecToStr = map[uint64]string{
122122
ZcashTx: "zcash-tx",
123123
}
124124

125+
// Cid represents a self-describing content adressed
126+
// identifier. It is formed by a Version, a Codec (which indicates
127+
// a multicodec-packed content type) and a Multihash.
128+
type Cid struct {
129+
version uint64
130+
codec uint64
131+
hash mh.Multihash
132+
}
133+
125134
// NewCidV0 returns a Cid-wrapped multihash.
126135
// They exist to allow IPFS to work with Cids while keeping
127136
// compatibility with the plain-multihash format used used in IPFS.
128137
// NewCidV1 should be used preferentially.
129-
func NewCidV0(mhash mh.Multihash) *Cid {
130-
return &Cid{
138+
func NewCidV0(mhash mh.Multihash) (*Cid, error) {
139+
if mhash[0] != mh.SHA2_256 && mhash[1] != byte(mh.DefaultLengths[mh.SHA2_256]) {
140+
return nil, errors.New("NewCidV0 accepts only SHA256 hashes of standard length")
141+
}
142+
143+
c := &Cid{
131144
version: 0,
132145
codec: DagProtobuf,
133146
hash: mhash,
134147
}
148+
return c, nil
135149
}
136150

137151
// NewCidV1 returns a new Cid using the given multicodec-packed
138152
// content type.
139-
func NewCidV1(codecType uint64, mhash mh.Multihash) *Cid {
140-
return &Cid{
153+
func NewCidV1(codecType uint64, mhash mh.Multihash) (*Cid, error) {
154+
c := &Cid{
141155
version: 1,
142156
codec: codecType,
143157
hash: mhash,
144158
}
159+
err := ValidateCid(c)
160+
if err != nil {
161+
return nil, err
162+
}
163+
return c, nil
145164
}
146165

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

168-
// Cid represents a self-describing content adressed
169-
// identifier. It is formed by a Version, a Codec (which indicates
170-
// a multicodec-packed content type) and a Multihash.
171-
type Cid struct {
172-
version uint64
173-
codec uint64
174-
hash mh.Multihash
175-
}
176-
177187
// Parse is a short-hand function to perform Decode, Cast etc... on
178188
// a generic interface{} type.
179189
func Parse(v interface{}) (*Cid, error) {
@@ -186,7 +196,7 @@ func Parse(v interface{}) (*Cid, error) {
186196
case []byte:
187197
return Cast(v2)
188198
case mh.Multihash:
189-
return NewCidV0(v2), nil
199+
return NewCidV0(v2)
190200
case *Cid:
191201
return v2, nil
192202
default:
@@ -217,7 +227,7 @@ func Decode(v string) (*Cid, error) {
217227
return nil, err
218228
}
219229

220-
return NewCidV0(hash), nil
230+
return NewCidV0(hash)
221231
}
222232

223233
_, data, err := mbase.Decode(v)
@@ -256,12 +266,7 @@ func Cast(data []byte) (*Cid, error) {
256266
if err != nil {
257267
return nil, err
258268
}
259-
260-
return &Cid{
261-
codec: DagProtobuf,
262-
version: 0,
263-
hash: h,
264-
}, nil
269+
return NewCidV0(h)
265270
}
266271

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

287-
return &Cid{
292+
c := &Cid{
288293
version: vers,
289294
codec: codec,
290295
hash: h,
291-
}, nil
296+
}
297+
err = ValidateCid(c)
298+
if err != nil {
299+
return nil, err
300+
}
301+
return c, nil
292302
}
293303

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

357367
func (c *Cid) bytesV1() []byte {
358-
// two 8 bytes (max) numbers plus hash
368+
// two 10 bytes (max) numbers plus hash
359369
buf := make([]byte, 2*binary.MaxVarintLen64+len(c.hash))
360370
n := binary.PutUvarint(buf, c.version)
361371
n += binary.PutUvarint(buf[n:], c.codec)
@@ -401,7 +411,14 @@ func (c *Cid) UnmarshalJSON(b []byte) error {
401411
c.version = out.version
402412
c.hash = out.hash
403413
c.codec = out.codec
404-
return nil
414+
err = ValidateCid(c)
415+
if err != nil {
416+
// Cleanup the struct so it is impossible to ignore the error
417+
c.version = 0
418+
c.hash = nil
419+
c.codec = 0
420+
}
421+
return err
405422
}
406423

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

460477
switch p.Version {
461478
case 0:
462-
return NewCidV0(hash), nil
479+
return NewCidV0(hash)
463480
case 1:
464-
return NewCidV1(p.Codec, hash), nil
481+
return NewCidV1(p.Codec, hash)
465482
default:
466483
return nil, fmt.Errorf("invalid cid version")
467484
}

cid_test.go

+24-8
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func TestTableForV0(t *testing.T) {
7070
}
7171

7272
func TestBasicMarshaling(t *testing.T) {
73-
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 4)
73+
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 20)
7474
if err != nil {
7575
t.Fatal(err)
7676
}
@@ -100,7 +100,7 @@ func TestBasicMarshaling(t *testing.T) {
100100
}
101101

102102
func TestBasesMarshaling(t *testing.T) {
103-
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 4)
103+
h, err := mh.Sum([]byte("TEST"), mh.SHA3, 20)
104104
if err != nil {
105105
t.Fatal(err)
106106
}
@@ -208,7 +208,10 @@ func TestNewPrefixV1(t *testing.T) {
208208
if err != nil {
209209
t.Fatal(err)
210210
}
211-
c2 := NewCidV1(DagCBOR, hash)
211+
c2, err := NewCidV1(DagCBOR, hash)
212+
if err != nil {
213+
t.Fatal(err)
214+
}
212215

213216
if !c1.Equals(c2) {
214217
t.Fatal("cids mismatch")
@@ -237,7 +240,10 @@ func TestNewPrefixV0(t *testing.T) {
237240
if err != nil {
238241
t.Fatal(err)
239242
}
240-
c2 := NewCidV0(hash)
243+
c2, err := NewCidV0(hash)
244+
if err != nil {
245+
t.Fatal(err)
246+
}
241247

242248
if !c1.Equals(c2) {
243249
t.Fatal("cids mismatch")
@@ -250,7 +256,10 @@ func TestNewPrefixV0(t *testing.T) {
250256
func TestPrefixRoundtrip(t *testing.T) {
251257
data := []byte("this is some test content")
252258
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
253-
c := NewCidV1(DagCBOR, hash)
259+
c, err := NewCidV1(DagCBOR, hash)
260+
if err != nil {
261+
t.Fatal(err)
262+
}
254263

255264
pref := c.Prefix()
256265

@@ -279,7 +288,10 @@ func TestPrefixRoundtrip(t *testing.T) {
279288
func Test16BytesVarint(t *testing.T) {
280289
data := []byte("this is some test content")
281290
hash, _ := mh.Sum(data, mh.SHA2_256, -1)
282-
c := NewCidV1(DagCBOR, hash)
291+
c, err := NewCidV1(DagCBOR, hash)
292+
if err != nil {
293+
t.Fatal(err)
294+
}
283295

284296
c.codec = 1 << 63
285297
_ = c.Bytes()
@@ -309,9 +321,13 @@ func TestParse(t *testing.T) {
309321
t.Fatal(err)
310322
}
311323

324+
c, err := NewCidV0(h)
325+
if err != nil {
326+
t.Fatal(err)
327+
}
312328
assertions := [][]interface{}{
313-
[]interface{}{NewCidV0(h), theHash},
314-
[]interface{}{NewCidV0(h).Bytes(), theHash},
329+
[]interface{}{c, theHash},
330+
[]interface{}{c.Bytes(), theHash},
315331
[]interface{}{h, theHash},
316332
[]interface{}{theHash, theHash},
317333
[]interface{}{"/ipfs/" + theHash, theHash},

validate_test.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@ func TestValidateCids(t *testing.T) {
3030

3131
assertFalse(IsGoodHash(mh.BLAKE2B_MIN + 5))
3232

33-
mhcid := func(code, length uint64) *Cid {
34-
p := NewPrefixV1(DagCBOR, code)
35-
p.MhLength = int(length)
36-
cid, err := p.Sum([]byte{})
33+
mhcid := func(code uint64, length int) *Cid {
34+
c := &Cid{
35+
version: 1,
36+
codec: DagCBOR,
37+
}
38+
mhash, err := mh.Sum([]byte{}, code, length)
3739
if err != nil {
3840
t.Fatal(err)
3941
}
40-
return cid
42+
c.hash = mhash
43+
return c
4144
}
4245

4346
cases := []struct {

0 commit comments

Comments
 (0)