Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weird case where Decode() gives corrupted data without returning an error #12

Open
darkvertex opened this issue Jan 7, 2021 · 0 comments

Comments

@darkvertex
Copy link

Hi! Thank you for creating this library. It seems very useful. I was playing around with the example in the README and playing with corrupting the data.

Eventually I succeeded at corrupting it enough that it would not recover, but it would also not error, thus recovering faulty data... Try this:

package main

import (
	"fmt"

	"github.com/vivint/infectious"
)

func main() {
	const (
		required = 8
		total    = 14
	)

	// Create a *FEC, which will require required pieces for reconstruction at
	// minimum, and generate total total pieces.
	f, err := infectious.NewFEC(required, total)
	if err != nil {
		panic(err)
	}

	// Prepare to receive the shares of encoded data.
	shares := make([]infectious.Share, total)
	output := func(s infectious.Share) {
		// the memory in s gets reused, so we need to make a deep copy
		shares[s.Number] = s.DeepCopy()
	}

	// the data to encode must be padded to a multiple of required, hence the
	// underscores.
	text := "hello, world! __"
	err = f.Encode([]byte(text), output)
	if err != nil {
		panic(err)
	}

	fmt.Println("----------------")
	fmt.Println("Generated shares:")
	// we now have total shares.
	for _, share := range shares {
		fmt.Printf("%d: %#v\n", share.Number, string(share.Data))
	}

	// Let's reconstitute with shares 0-5 missing and 1 piece corrupted.
	shares = shares[6:]
	shares[2].Data[1] = '!' // mutate some data

	fmt.Println("----------------")
	fmt.Println("Fucked shares:")
	for _, share := range shares {
		fmt.Printf("%d: %#v\n", share.Number, string(share.Data))
	}

	err = f.Correct(shares)
	if err != nil {
		panic(err)
	}

	result, err := f.Decode(nil, shares)
	if err != nil {
		panic(err)
	}

	// we have the original data!
	fmt.Println("----------------")
	fmt.Println("Fixed shares:")
	fmt.Printf("original text:  %#v\n", string(text))
	fmt.Printf("recovered text: %#v\n", string(result))
}

You can try the snippet above in the playground: https://play.golang.org/p/ueV5p2O6QE5

It does not panic at all; printing:

----------------
Generated shares:
0: "he"
1: "ll"
2: "o,"
3: " w"
4: "or"
5: "ld"
6: "! "
7: "__"
8: " n"
9: "P\\"
10: "\xceS"
11: "\xf28"
12: "\x94\xdc"
13: "\xd9y"
----------------
Fucked shares:
6: "! "
7: "__"
8: " !"
9: "P\\"
10: "\xceS"
11: "\xf28"
12: "\x94\xdc"
13: "\xd9y"
----------------
Fixed shares:
original text:  "hello, world! __"
recovered text: "h`l\x84oR \xd0o\xfdl\xfd! __"

In this example I require 8 "good" shares to recover. I have 8 shares, but 1 of them is corrupt, so technically I only have 7 good shares, which is not enough to decode this... yet no panic.

That's weird, no? I guess there's something I'm not understanding but I would have expected an error value if the data was too corrupt.

Now if I replace:

	// Let's reconstitute with shares 0-5 missing and 1 piece corrupted.
	shares = shares[6:]
	shares[2].Data[1] = '!' // mutate some data

...with a few mutations too many:

	// Let's corrupt shares 0-3 through mutation:
	shares[0].Data[1] = '!'
	shares[1].Data[1] = '!'
	shares[2].Data[1] = '!'
	shares[3].Data[1] = '!'

I get: panic: too many errors to reconstruct which is the kind of error I'd expect to get with my first snippet instead of decoding garbage data.

Is it a bug? or a weird edge case where the algorithm breaks?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant