コード例 #1
0
ファイル: hashtreap.go プロジェクト: pylls/balloon
// Verify verifies a prune proof for the provided arguments. Returns true if valid,
// otherwise false.
func (p *PruneProof) Verify(keys [][]byte, answer bool, root []byte) (valid bool) {
	// always possible to insert no keys
	if len(keys) == 0 {
		return answer // same as answer == true
	}

	// create a pruned hash treap out of all nodes in the proof
	proofTree := NewHashTreap()
	if len(p.Nodes) > 0 {
		// linearly build the proof tree
		// this involves no verification of the nodes
		proofTree.newPrunedHashTreap(p.Nodes)
	}

	if !util.Equal(root, proofTree.Root()) {
		return false
	}

	// go over each key, looking if it exists in the proof
	for _, k := range keys {
		value, valid := proofTree.verifiablyGet(k)
		// at least one invalid node in the proof
		if !valid {
			return false
		}
		// the key exists
		if value != nil {
			return !answer && util.Equal(k, p.Key)
		}
	}

	return answer
}
コード例 #2
0
ファイル: balloon.go プロジェクト: pylls/balloon
// Update updates balloon with the slice of events, producing the next snapshot.
// Run by the author on trusted input.
func (balloon *Balloon) Update(events []Event, current *Snapshot,
	sk []byte) (next *Snapshot, err error) {
	if len(events) == 0 {
		return nil, errors.New("you need to add at least one event")
	}
	if !util.Equal(current.Roots.History, balloon.history.Root()) ||
		!util.Equal(current.Roots.Treap, balloon.treap.Root()) {
		return nil, errors.New("provided snapshot is not current")
	}

	sort.Sort(ByKey(events))

	// attempt to add events
	treap := balloon.treap
	ht := balloon.history.Clone()
	for i := 0; i < len(events); i++ {
		// add the hash of the entire event to the history tree
		_, err = ht.Add(util.Hash(append(events[i].Key, events[i].Value...)))
		if err != nil {
			return
		}

		// add to the treap the hash of the key pointing to the index (version) of the
		// hash of the event in the history tree
		treap, err = treap.Add(util.Hash(events[i].Key), util.Itob(ht.LatestVersion()))
		if err != nil {
			return
		}
	}

	// attempt to create next snapshot
	next = new(Snapshot)
	next.Index = current.Index + 1
	next.Roots.History = ht.Root()
	next.Roots.Treap = treap.Root()
	next.Roots.Version = ht.LatestVersion()
	next.Previous = current.Signature
	signature, err := util.Sign(balloon.sk,
		append(append([]byte("snapshot"), next.Roots.History...), append(next.Roots.Treap, next.Previous...)...))
	if err != nil {
		panic(err)
	}
	next.Signature = signature

	// all is OK, save result
	err = balloon.Storage.Store(events, *next)
	if err != nil {
		return nil, err
	}
	balloon.latestsnapshot = *next
	if len(events) > 0 {
		balloon.latesteventkey = events[len(events)-1].Key
	}
	balloon.treap = treap
	balloon.history = ht

	return
}
コード例 #3
0
ファイル: balloon.go プロジェクト: pylls/balloon
// Equal determines if another snapshot is equal to this snapshot.
func (snap *Snapshot) Equal(other *Snapshot) bool {
	if snap == nil || other == nil ||
		snap.Index != other.Index ||
		!util.Equal(snap.Signature, other.Signature) ||
		!util.Equal(snap.Roots.History, other.Roots.History) ||
		!util.Equal(snap.Roots.Treap, other.Roots.Treap) ||
		snap.Roots.Version != other.Roots.Version {
		return false
	}

	return true
}
コード例 #4
0
ファイル: hashtreap.go プロジェクト: pylls/balloon
// Verify verifies a membership query for a provided key from an expected
// root hash that fixes a hash treap. Returns true if the proof is valid ,
// false otherwise.
func (p *QueryProof) Verify(key, root []byte) (valid bool) {
	if len(p.Nodes) == 0 {
		// an empty hash treap shows non-membership for any key
		return p.Value == nil && root == nil
	} else if !util.Equal(p.Nodes[0].Hash, root) {
		return false
	}

	// build a pruned
	proofTree := NewHashTreap()
	proofTree.newPrunedHashTreap(p.Nodes)
	value, valid := proofTree.verifiablyGet(key)

	return valid && util.Equal(value, p.Value) && util.Equal(key, p.Key)
}
コード例 #5
0
ファイル: hashtreap.go プロジェクト: pylls/balloon
func (t *HashTreap) verifiablyGet(key []byte) (value []byte, valid bool) {
	n := t.root
	for n != nil {
		// verify the node we are at
		var left, right []byte
		left = NoNodeHash
		right = NoNodeHash
		if n.left != nil {
			left = n.left.hash
		}
		if n.right != nil {
			right = n.right.hash
		}
		// verify the hash
		if !util.Equal(n.hash, util.Hash(n.key, n.value, left, right)) {
			return nil, false
		}

		c := bytes.Compare(key, n.key)
		if c < 0 {
			n = n.left
		} else if c > 0 {
			n = n.right
		} else {
			return n.value, true
		}
	}
	return nil, true
}
コード例 #6
0
ファイル: balloon.go プロジェクト: pylls/balloon
// QueryPrune performs a prune query for a slice of events. Returns an answer indicating
// if the events can be added and a proof.
// Run by the server on untrusted input. The minimalProof flag trades a small amount
// of computation for a significantly smaller proof (the more events, the bigger the reduction).
func (balloon *Balloon) QueryPrune(events []Event, vk []byte,
	minimalProof bool) (answer bool, proof PruneProof) {
	// query hash treap
	treapKeys := make([][]byte, len(events))
	for i := 0; i < len(events); i++ {
		treapKeys[i] = util.Hash(events[i].Key)
	}
	answer, proof.TreapProof = balloon.treap.QueryPrune(treapKeys, minimalProof)

	// if we found a member among the keys, then the proof is done shows that it is not
	// posssible to insert the set of events
	if !answer {
		return
	}

	// we only need/can extract the latest event, whose membership query fixes the history
	// tree, if there is at least one event in the Balloon
	if balloon.Size() > 0 {
		members, qevent, qproof, err := balloon.QueryMembership(balloon.latesteventkey,
			&balloon.latestsnapshot, vk)
		if err != nil {
			panic(err)
		}
		if !members {
			panic("an event that should be a member is not")
		}
		if !util.Equal(balloon.latesteventkey, qevent.Key) {
			panic("events differ")
		}
		proof.QueryProof = qproof
		proof.Event = *qevent
	}

	return
}
コード例 #7
0
ファイル: balloon.go プロジェクト: pylls/balloon
// Verify verifies a proof and answer from QueryMembership. Returns true if the
// answer and proof are correct and consistent, otherwise false.
// Run by a client on input that should be verified.
func (proof *QueryProof) Verify(key []byte, queried, current *Snapshot,
	answer bool, event *Event, vk []byte) (valid bool) {

	// verify the snapshots
	if !util.Verify(vk,
		append(append([]byte("snapshot"), queried.Roots.History...),
			append(queried.Roots.Treap, queried.Previous...)...),
		queried.Signature) {
		return false
	}
	if !util.Verify(vk,
		append(append([]byte("snapshot"), current.Roots.History...),
			append(current.Roots.Treap, current.Previous...)...),
		current.Signature) {
		return false
	}

	// check the authenticated path in the hash treap
	if !proof.TreapProof.Verify(util.Hash(key), current.Roots.Treap) {
		return false
	}

	// a non-membership proof where there is no event in the treap
	if !answer && proof.TreapProof.Value == nil && event == nil {
		return true
	}

	// a non-membership proof where the event was added _after_ the queried for snapshot
	index := util.Btoi(proof.TreapProof.Value)
	if !answer && index > queried.Roots.Version {
		return true
	}

	// a membership proof
	if answer && event != nil && util.Equal(event.Key, key) && proof.HistoryProof.Verify() &&
		proof.HistoryProof.Index == index && proof.HistoryProof.Version == queried.Roots.Version &&
		util.Equal(proof.HistoryProof.Root, queried.Roots.History) &&
		util.Equal(proof.HistoryProof.Event, util.Hash(event.Key, event.Value)) {
		return true
	}

	// otherwise the proof is invalid
	return false
}
コード例 #8
0
ファイル: historytree_test.go プロジェクト: pylls/balloon
func TestClone(t *testing.T) {
	// create a tree
	size := 128
	tree := NewTree()
	events := make([][]byte, size)
	for i := 0; i < size; i++ {
		events[i] = []byte("event " + string(i))
		_, err := tree.Add(events[i])
		if err != nil {
			t.Fatalf("failed to add event: %s", err)
		}
	}

	// create a clone
	clone := tree.Clone()
	if !util.Equal(clone.Root(), tree.Root()) {
		t.Fatal("expected equal roots")
	}

	// update original tree
	for i := 0; i < size; i++ {
		events[i] = []byte("event " + string(i))
		_, err := tree.Add(events[i])
		if err != nil {
			t.Fatalf("failed to add event: %s", err)
		}
	}
	if util.Equal(clone.Root(), tree.Root()) {
		t.Fatal("expected different roots")
	}

	// update clone
	for i := 0; i < size; i++ {
		events[i] = []byte("event " + string(i))
		_, err := clone.Add(events[i])
		if err != nil {
			t.Fatalf("failed to add event: %s", err)
		}
	}
	if !util.Equal(clone.Root(), tree.Root()) {
		t.Fatal("expected equal roots")
	}
}
コード例 #9
0
ファイル: historytree.go プロジェクト: pylls/balloon
// Verify verifies a membership proof
func (p *MembershipProof) Verify() (correct bool) {
	if p.Root == nil || p.Event == nil || p.Index < 0 ||
		p.Version < 0 {
		return false
	}

	proofTree := NewTree()
	for _, n := range p.Nodes {
		proofTree.frozen = proofTree.frozen.Set(n.Position.toString(), n.Hash)
	}
	proofTree.events = proofTree.events.Set(strconv.Itoa(p.Index), p.Event)

	c, err := proofTree.getHashedNode(0, proofTree.calculateDepth(p.Version+1), p.Version, true)
	if err != nil {
		return false
	}

	return util.Equal(c, p.Root)
}
コード例 #10
0
ファイル: balloon_test.go プロジェクト: pylls/balloon
func TestBalloon(t *testing.T) {
	/*
		Basics
	*/

	sk, vk, err := Genkey()
	if err != nil {
		t.Fatalf("failed to generate keys: %s", err)
	}

	// setup for an initially empty Balloon for the author
	author, s0, err := Setup(nil, sk, vk, NewTestEventStorage())
	if err != nil {
		t.Fatalf("failed to setup balloon: %s", err)
	}

	// update the Balloon with some events
	size := 10
	events := make([]Event, size)
	for i := 0; i < size; i++ {
		k := make([]byte, util.HashOutputLen)
		_, err = rand.Read(k)
		if err != nil {
			t.Fatalf("failed to read random bytes: %s", err)
		}
		events[i].Key = k
		events[i].Value = util.Hash(k)
	}
	s1, err := author.Update(events, s0, sk)
	if err != nil {
		t.Fatalf("failed to update to s1: %s", err)
	}

	// for the server, create an empty balloon
	server := NewBalloon(NewTestEventStorage())

	// refresh with the initial empty events
	err = server.Refresh(nil, nil, s0, vk)
	if err != nil {
		t.Fatalf("failed to do initial refresh: %s", err)
	}

	// refresh with events
	err = server.Refresh(events, s0, s1, vk)
	if err != nil {
		t.Fatalf("failed to do refresh from s0 to s1: %s", err)
	}

	// create some more events, update, and refresh
	for i := 0; i < size; i++ {
		k := make([]byte, util.HashOutputLen)
		_, err = rand.Read(k)
		if err != nil {
			t.Fatalf("failed to read random bytes: %s", err)
		}
		events[i].Key = k
		events[i].Value = util.Hash(k)
	}
	s2, err := author.Update(events[:1], s1, sk)
	if err != nil {
		t.Fatalf("failed to update to 2: %s", err)
	}
	// refresh with events
	err = server.Refresh(events[:1], s1, s2, vk)
	if err != nil {
		t.Fatalf("failed to do refresh from s1 to s2: %s", err)
	}

	/*
		Membership queries
	*/

	// check if the event we just inserted into the Balloon exists in the latest snapshot
	answer, event, proof, err := server.QueryMembership(events[0].Key, s2, vk)
	if err != nil {
		t.Fatalf("failed to perform a membership query: %s", err)
	}
	if !answer {
		t.Fatal("got a false answer for a membership query that should have answered true")
	}
	if event == nil {
		t.Fatal("got an empty event for a membership query that should have returned an event")
	}
	if !proof.Verify(events[0].Key, s2, s2, answer, event, vk) {
		t.Fatal("failed to verify valid membership proof")
	}
	if proof.Size() == 0 {
		t.Fatal("expected a non-zero sized membership proof")
	}

	// query for same event in previous snapshot, where it should not exist
	answer, event, proof, err = server.QueryMembership(events[0].Key, s1, vk)
	if err != nil {
		t.Fatalf("failed to perform a membership query: %s", err)
	}
	if answer {
		t.Fatal("got a true answer for a membership query that should have answered false")
	}
	if event != nil {
		t.Fatal("got an event for a membership query that should have not returned an event")
	}
	if !proof.Verify(events[0].Key, s1, s2, answer, event, vk) {
		t.Fatal("failed to verify valid membership proof")
	}

	// query for a random key in latest snapshot
	key := make([]byte, util.HashOutputLen)
	_, err = rand.Read(key)
	if err != nil {
		t.Fatalf("failed to read random bytes: %s", err)
	}
	answer, event, proof, err = server.QueryMembership(key, s2, vk)
	if err != nil {
		t.Fatalf("failed to perform a membership query: %s", err)
	}
	if answer {
		t.Fatal("got a true answer for a membership query that should have answered false")
	}
	if event != nil {
		t.Fatal("got an event for a membership query that should have not returned an event")
	}
	if !proof.Verify(key, s2, s2, answer, event, vk) {
		t.Fatal("failed to verify valid membership proof")
	}

	// query for the random key in a prior snapshot
	answer, event, proof, err = server.QueryMembership(key, s1, vk)
	if err != nil {
		t.Fatalf("failed to perform a membership query: %s", err)
	}
	if answer {
		t.Fatal("got a true answer for a membership query that should have answered false")
	}
	if event != nil {
		t.Fatal("got an event for a membership query that should have not returned an event")
	}
	if !proof.Verify(key, s1, s2, answer, event, vk) {
		t.Fatal("failed to verify valid membership proof")
	}

	/*
		Pruned queries
	*/

	// start with a prune query for events already inserted
	answer, pproof := server.QueryPrune(events, vk, true)
	if !pproof.Verify(events, answer, s2, vk) {
		t.Fatal("failed to verify a valid query prune proof")
	}
	if answer {
		t.Fatal("got a true reply to a prune query with old events")
	}
	if pproof.Size() == 0 {
		t.Fatal("expected a non-zero sized membership proof")
	}

	// randomise events
	for i := 0; i < size; i++ {
		k := make([]byte, util.HashOutputLen)
		_, err = rand.Read(k)
		if err != nil {
			t.Fatalf("failed to read random bytes: %s", err)
		}
		events[i].Key = k
		events[i].Value = util.Hash(k)
	}
	// query again
	answer, pproof = server.QueryPrune(events, vk, true)
	if !pproof.Verify(events, answer, s2, vk) {
		t.Fatal("failed to verify a valid query prune proof")
	}
	if !answer {
		t.Fatal("got a false reply to a prune query with random events")
	}

	/*
		Update using pruned queries
	*/

	s3u, err := pproof.Update(events, s2, sk)
	if err != nil {
		t.Fatalf("failed to update using prune: %s", err)
	}
	s3, err := author.Update(events, s2, sk)
	if err != nil {
		t.Fatalf("failed to update to 3: %s", err)
	}

	if !util.Equal(s3.Roots.History, s3u.Roots.History) {
		t.Fatal("Update (prune) produced a different history tree root")
	}
	if !util.Equal(s3.Roots.Treap, s3u.Roots.Treap) {
		t.Fatal("Update (prune) produced a different hash treap root")
	}
	if s3.Roots.Version != s3u.Roots.Version {
		t.Fatal("Update (prune) produced a different version")
	}

	/*
		Use RefreshVerify at the server
	*/

	if !server.RefreshVerify(events, s2, s3, vk) {
		t.Fatal("failed to refresh verify at server from 2 to 3")
	}
}
コード例 #11
0
ファイル: balloon_test.go プロジェクト: pylls/balloon
func TestBalloonDetails(t *testing.T) {
	// More specific tests for test coverage
	// setup for an initially empty Balloon for an author
	sk, vk, err := Genkey()
	if err != nil {
		t.Fatalf("failed to generate keys: %s", err)
	}
	author, s0, err := Setup(nil, sk, vk, NewTestEventStorage())
	if err != nil {
		t.Fatalf("failed to setup balloon: %s", err)
	}

	// update the Balloon with some events
	size := 10
	events := make([]Event, size)
	for i := 0; i < size; i++ {
		k := make([]byte, util.HashOutputLen)
		_, err = rand.Read(k)
		if err != nil {
			t.Fatalf("failed to read random bytes: %s", err)
		}
		events[i].Key = k
		events[i].Value = util.Hash(k)
	}
	snapUpdate, err := author.Update(events, s0, sk)
	if err != nil {
		t.Fatalf("failed to update to s1: %s", err)
	}

	// create a second balloon, but use Setup directly
	_, snapSetup, err := Setup(events, sk, vk, NewTestEventStorage())
	if err != nil {
		t.Fatalf("failed to setup balloon: %s", err)
	}
	if snapUpdate.Roots.Version != snapSetup.Roots.Version ||
		!util.Equal(snapUpdate.Roots.History, snapSetup.Roots.History) ||
		!util.Equal(snapUpdate.Roots.Treap, snapSetup.Roots.Treap) {
		t.Fatal("snapshots using Update and Setup differ")
	}

	// Refresh
	server := NewBalloon(NewTestEventStorage())
	err = server.Refresh(nil, nil, s0, vk)
	if err != nil {
		t.Fatalf("failed to do initial refresh: %s", err)
	}
	err = server.Refresh(events, nil, snapUpdate, vk)
	if err == nil {
		t.Fatalf("did Refresh with wrong current snapshot")
	}
	err = server.Refresh(events, s0, snapUpdate, vk)
	if err != nil {
		t.Fatalf("failed to refresh from s0 to snapSetup: %s", err)
	}
	err = server.Refresh(events, nil, snapUpdate, vk)
	if err == nil {
		t.Fatalf("did Refresh with wrong current snapshot")
	}
	err = server.Refresh(events, snapUpdate, snapUpdate, vk)
	if err == nil {
		t.Fatalf("did Refresh with wrong current snapshot")
	}
	err = server.Refresh(events, s0, snapUpdate, vk)
	if err == nil {
		t.Fatalf("did Refresh with wrong current snapshot")
	}

	// Setup
	events = make([]Event, size)
	_, _, err = Setup(events, sk, vk, NewTestEventStorage())
	if err == nil {
		t.Fatal("successfully Setup with nil event keys and values")
	}

	// Update
	_, err = author.Update(events, snapUpdate, sk)
	if err == nil {
		t.Fatal("successfully Update with nil event keys and values")
	}
	_, err = author.Update(events, s0, sk)
	if err == nil {
		t.Fatal("successfully Update with old snapshot")
	}
	events = make([]Event, 0, size)
	_, err = author.Update(events, snapUpdate, sk)
	if err == nil {
		t.Fatal("successfully Update with no events")
	}

	// QueryMembership
	_, _, _, err = author.QueryMembership([]byte("too short key"), s0, vk)
	if err == nil {
		t.Fatalf("membership query for too short key: %s", err)
	}
	// flip every byte in the signature of the snapshot
	for i := range s0.Signature {
		s0.Signature[i] ^= 0x40
		_, _, _, err = author.QueryMembership(util.Hash([]byte("a valid key")), s0, vk)
		if err == nil {
			t.Fatalf("membership query for invalid signature: %s", err)
		}
		s0.Signature[i] ^= 0x40
	}
}
コード例 #12
0
ファイル: balloon.go プロジェクト: pylls/balloon
// Refresh updates balloon with the slice of events. Returns an error if the update
// fails to produce a snapshot identical to the provided next one.
// Run by the server on input (events and next) that will be verified.
func (balloon *Balloon) Refresh(events []Event, current, next *Snapshot,
	vk []byte) (err error) {
	// current can be empty on the first run of Refresh
	if current == nil && balloon.Size() > 0 {
		return errors.New("current snapshot required for non-zero Balloon")
	}

	if current != nil {
		if !util.Equal(current.Roots.History, balloon.history.Root()) ||
			!util.Equal(current.Roots.Treap, balloon.treap.Root()) {
			return errors.New("provided snapshot is not current")
		}
		if current.Roots.Version == -1 && next.Roots.Version != len(events)-1 {
			return errors.New("unexpected version in next snapshot")
		}
		if current.Roots.Version != -1 && current.Roots.Version+len(events) != next.Roots.Version {
			return errors.New("version in next snapshot inconsistent with current")
		}
		if !util.Verify(vk,
			append(append([]byte("snapshot"), current.Roots.History...),
				append(current.Roots.Treap, current.Previous...)...),
			current.Signature) {
			return errors.New("invalid signature in current snapshot")
		}
	}

	treap := balloon.treap
	ht := balloon.history.Clone()
	if len(events) > 0 {
		sort.Sort(ByKey(events))

		// attempt to add events
		for i := 0; i < len(events); i++ {
			// add the hash of the entire event to the history tree
			_, err = ht.Add(util.Hash(append(events[i].Key, events[i].Value...)))
			if err != nil {
				return
			}

			// add to the treap the hash of the key pointing to the index (version) of the
			// hash of the event in the history tree
			treap, err = treap.Add(util.Hash(events[i].Key), util.Itob(ht.LatestVersion()))
			if err != nil {
				return
			}
		}
	}

	var prev []byte
	if current != nil {
		prev = current.Signature
	}

	// compare snapshot with next snapshot
	if !util.Equal(ht.Root(), next.Roots.History) ||
		!util.Equal(treap.Root(), next.Roots.Treap) ||
		ht.LatestVersion() != next.Roots.Version ||
		!util.Equal(prev, next.Previous) {
		return errors.New("roots or version mismatch")
	}

	if current == nil {
		if next.Index != 0 {
			return errors.New("index not what expected in next snapshot")
		}
	} else if current.Index+1 != next.Index {
		return errors.New("index not what expected in next snapshot")
	}

	if !util.Verify(vk,
		append(append([]byte("snapshot"), next.Roots.History...), append(next.Roots.Treap, next.Previous...)...),
		next.Signature) {
		return errors.New("invalid signature")
	}

	// all is OK, store results
	err = balloon.Storage.Store(events, *next)
	if err != nil {
		return err
	}
	balloon.latestsnapshot = *next
	if len(events) > 0 {
		balloon.latesteventkey = events[len(events)-1].Key
	}
	balloon.treap = treap
	balloon.history = ht

	return
}