// 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 }
// Setup creates a new balloon based on the slice of events (may be empty or nil). Returns the // first snapshot. Run by the author on trusted input. func Setup(events []Event, sk, vk []byte, storage EventStorage) (balloon *Balloon, snap *Snapshot, err error) { balloon = NewBalloon(storage) balloon.sk = sk balloon.vk = vk // do same as update, allow events to be empty if len(events) > 0 { sort.Sort(ByKey(events)) // add events for i := 0; i < len(events); i++ { // add the hash of the entire event to the history tree _, err = balloon.history.Add(util.Hash(append(events[i].Key, events[i].Value...))) if err != nil { return nil, nil, err } // add to the treap the hash of the key pointing to the index (version) of the // hash of the event in the history tree balloon.treap, err = balloon.treap.Add(util.Hash(events[i].Key), util.Itob(balloon.history.LatestVersion())) if err != nil { return nil, nil, err } } } // create first snapshot snap = new(Snapshot) snap.Index = 0 snap.Roots.History = balloon.history.Root() snap.Roots.Treap = balloon.treap.Root() snap.Roots.Version = balloon.history.LatestVersion() snap.Previous = nil signature, err := util.Sign(balloon.sk, append(append([]byte("snapshot"), snap.Roots.History...), append(snap.Roots.Treap, snap.Previous...)...)) if err != nil { panic(err) } snap.Signature = signature // actually store events err = balloon.Storage.Store(events, *snap) if err != nil { return nil, nil, err } balloon.latestsnapshot = *snap if len(events) > 0 { balloon.latesteventkey = events[len(events)-1].Key } return }
func TestEqualSnapshot(t *testing.T) { size := 10 sk, vk, err := Genkey() if err != nil { t.Fatalf("failed to generate keys: %s", err) } balloon, s0, err := Setup(nil, sk, vk, NewTestEventStorage()) if err != nil { t.Fatalf("failed to setup balloon: %s", err) } 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 := balloon.Update(events, s0, sk) if err != nil { t.Fatalf("failed to update to s1: %s", err) } if !s1.Equal(s1) { t.Fatal("the smae snapshot is not equal with itself") } if s1.Equal(s0) { t.Fatal("two different snapshots are equal") } }
// Verify verifies a proof and answer from QueryPrune. Returns true if the answer // and proof is correct, otherwise false. Run by the author. func (proof *PruneProof) Verify(events []Event, answer bool, current *Snapshot, vk []byte) (valid bool) { valid = true if !util.Verify(vk, append(append([]byte("snapshot"), current.Roots.History...), append(current.Roots.Treap, current.Previous...)...), current.Signature) { return false } // always possible to add no events if len(events) == 0 { return answer // same as answer == true } // verify the prune proof in the hash treap treapKeys := make([][]byte, len(events)) for i := 0; i < len(events); i++ { treapKeys[i] = util.Hash(events[i].Key) } valid = proof.TreapProof.Verify(treapKeys, answer, current.Roots.Treap) if !valid { return } // check the query if needed if answer && current.Roots.Version >= 0 { valid = proof.QueryProof.Verify(proof.Event.Key, current, current, true, &proof.Event, vk) } return valid }
// 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 }
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 }
func (t *HashTreap) upsert(key, value []byte) *HashTreap { r := t.union(t.root, &node{ key: key, value: value, priority: util.Hash(key), taint: true, }) return &HashTreap{root: r, size: t.Size() + 1} }
// Update creates the next snapshot from adding the provided events in the balloon // fixed by the current snapshot. This function depends on that the provided prune proof has // been successfully verified for the answer true. // Run by the author on input that should have been verified before by using Verify. func (proof *PruneProof) Update(events []Event, current *Snapshot, sk []byte) (next *Snapshot, err error) { sort.Sort(ByKey(events)) // calculate balloon internal keys and values based on events startIndex := current.Roots.Version + 1 values := make([][]byte, len(events)) treapKeys := make([][]byte, len(events)) treapValues := make([][]byte, len(events)) for i := 0; i < len(events); i++ { values[i] = util.Hash(append(events[i].Key, events[i].Value...)) treapKeys[i] = util.Hash(events[i].Key) treapValues[i] = util.Itob(startIndex + i) } // calculate updated commitment on the history tree c, version, err := proof.QueryProof.HistoryProof.Update(values) if err != nil { return nil, err } // calculate updated hash treap root root, err := proof.TreapProof.Update(treapKeys, treapValues) if err != nil { return nil, err } next = new(Snapshot) next.Index = current.Index + 1 next.Roots.History = c next.Roots.Treap = root next.Roots.Version = version next.Previous = current.Signature signature, err := util.Sign(sk, append(append([]byte("snapshot"), next.Roots.History...), append(next.Roots.Treap, next.Previous...)...)) if err != nil { panic(err) } next.Signature = signature return }
// 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 }
// layeR, Index, Version // See Figure 5 in "Efficient Data Structures for Tamper-Evident Logging" func (t *Tree) getHashedNode(index, layer, version int, proofMode bool) (value []byte, err error) { // always prefer frozen hashes, if we have calculated them if proofMode || version >= index+util.Pow(2, layer)-1 { if t.isFrozenHash(index, layer) { return t.getFrozenHash(index, layer) } } // special case for child nodes if layer == 0 && version >= index { event, err := t.getEvent(index) if err != nil { return nil, errors.New("no event with the provided index") } value = util.Hash(prefixZero, event) // have version determine if the right node is there or not } else if version >= index+util.Pow(2, layer-1) { a1, err := t.getHashedNode(index, layer-1, version, proofMode) if err != nil { return nil, errors.New("failed to get internal node with index " + strconv.Itoa(index)) } a2, err := t.getHashedNode(index+util.Pow(2, layer-1), layer-1, version, proofMode) if err != nil { return nil, errors.New("failed to get internal node with index " + strconv.Itoa(index)) } value = util.Hash(prefixOne, a1, a2) } else { a, err := t.getHashedNode(index, layer-1, version, proofMode) if err != nil { return nil, errors.New("failed to get internal node with index " + strconv.Itoa(index)) } value = util.Hash(prefixOne, a) } // should we add this to the frozen hash cache? if version >= index+util.Pow(2, layer)-1 { t.setFrozenHash(index, layer, value) } return }
func TestPrune(t *testing.T) { size := 3 runs := 4 sk, vk, err := Genkey() if err != nil { t.Fatalf("failed to generate keys: %s", err) } _, current, err := Setup(nil, sk, vk, NewTestEventStorage()) if err != nil { t.Fatalf("failed to setup balloon: %s", err) } server := NewBalloon(NewTestEventStorage()) if !server.RefreshVerify(nil, nil, current, vk) { t.Fatal("failed to refresh to s0") } for i := 1; i <= runs; i++ { events := make([]Event, size) for j := 0; j < size; j++ { k := make([]byte, util.HashOutputLen) _, err = rand.Read(k) if err != nil { t.Fatalf("failed to read random bytes: %s", err) } events[j].Key = k events[j].Value = util.Hash(k) } answer, proof := server.QueryPrune(events, vk, true) if !answer { t.Fatalf("cannot update a prune proof on run %d", i) } if !proof.Verify(events, answer, current, vk) { t.Fatalf("failed to verify prune proof on run %d", i) } next, err := proof.Update(events, current, sk) if err != nil { t.Fatalf("failed to update on run %d, error %s", i, err) } if !server.RefreshVerify(events, current, next, vk) { t.Fatalf("failed to refresh on run %d", i) } current = next } }
// QueryMembership queries for an event with a key in a particular snapshot in the balloon. // Returns an answer (is member?), an event (if the answer is true), and a proof (always). // Run by the server with untrusted input (key and queried). func (balloon *Balloon) QueryMembership(key []byte, queried *Snapshot, vk []byte) (answer bool, event *Event, proof QueryProof, err error) { answer = false // verify the queried snapshot if !util.Verify(vk, append(append([]byte("snapshot"), queried.Roots.History...), append(queried.Roots.Treap, queried.Previous...)...), queried.Signature) { return answer, nil, proof, errors.New("invalid signature") } // verify the length of the key if len(key) != util.HashOutputLen { return answer, nil, proof, errors.New("invalid key length") } // check hash treap for hash of key proof.TreapProof = balloon.treap.MembershipQuery(util.Hash(key)) // if not in treap, then a non-membership is done if proof.TreapProof.Value == nil { return } // the position (index) of the event in the history tree index := util.Btoi(proof.TreapProof.Value) // was the event added _after_ the queried for snapshot was created? if index > queried.Roots.Version { return } // ok, now we know it's a membership proof, so we query the history tree proof.HistoryProof, err = balloon.history.MembershipProof(index, queried.Roots.Version) if err != nil { panic(err) } // get the event from storage e, err := balloon.Storage.LookupEvent(key) if err != nil { return } return true, e, proof, nil }
func (t *HashTreap) update(node *node) { if node.taint { left := NoNodeHash right := NoNodeHash if node.left != nil { t.update(node.left) left = node.left.hash } if node.right != nil { t.update(node.right) right = node.right.hash } node.hash = util.Hash(node.key, node.value, left, right) node.taint = false } }
func (t *HashTreap) addProofNode(n *node, proofNode *ProofNode) *node { if n == nil { t.size++ return &node{ key: proofNode.Key, value: proofNode.Value, hash: proofNode.Hash, priority: util.Hash(proofNode.Key), taint: false, } } c := bytes.Compare(proofNode.Key, n.key) if c < 0 { n.left = t.addProofNode(n.left, proofNode) } else if c > 0 { n.right = t.addProofNode(n.right, proofNode) } return n }
// Update updates the root hash of the hash treap to cover all nodes // in the treap. func (t *HashTreap) Update() { if t.root == nil { return } if t.root.left != nil { t.update(t.root.left) } if t.root.right != nil { t.update(t.root.right) } left := NoNodeHash right := NoNodeHash if t.root.left != nil { left = t.root.left.hash } if t.root.right != nil { right = t.root.right.hash } t.root.hash = util.Hash(t.root.key, t.root.value, left, right) t.root.taint = false }
func TestClone(t *testing.T) { size := 10 sk, vk, err := Genkey() if err != nil { t.Fatalf("failed to generate keys: %s", err) } balloon, s0, err := Setup(nil, sk, vk, NewTestEventStorage()) if err != nil { t.Fatalf("failed to setup balloon: %s", err) } clone0 := balloon.Clone() if !clone0.latestsnapshot.Equal(s0) { t.Fatal("clone has different snapshot") } 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 := balloon.Update(events, s0, sk) if err != nil { t.Fatalf("failed to update to s1: %s", err) } s1c, err := clone0.Update(events, s0, sk) if err != nil { t.Fatalf("failed to update to s1: %s", err) } if !s1c.Equal(s1) { t.Fatal("clone has different snapshot") } }
func TestPruneQueryProofFlip(t *testing.T) { /* Create a Balloon with size events. */ size := 10 sk, vk, err := Genkey() if err != nil { t.Fatalf("failed to generate keys: %s", err) } balloon, s0, err := Setup(nil, sk, vk, NewTestEventStorage()) if err != nil { t.Fatalf("failed to setup balloon: %s", err) } 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 := balloon.Update(events, s0, sk) if err != nil { t.Fatalf("failed to update to s1: %s", err) } // a prune query for new random events, to get paths in both the hash treap // and the history tree 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) } answer, proof := balloon.QueryPrune(events, vk, true) if !proof.Verify(events, answer, s1, vk) { t.Fatal("failed to verify a valid query prune proof") } if !answer { t.Fatal("got a false reply to a prune query with old events") } /* OK, now we start systematically tampering with the proof and arguments to Verify to make sure that any changes are detected */ // flip answer if proof.Verify(events, !answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } // flip every byte in the root of the history tree in the snapshot for i := range s1.Roots.History { s1.Roots.History[i] ^= 0x40 if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Roots.History[i] ^= 0x40 } // flip every byte in the root of the hash treap in the snapshot for i := range s1.Roots.Treap { s1.Roots.Treap[i] ^= 0x40 if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Roots.Treap[i] ^= 0x40 } // flip every byte in the signature of the snapshot for i := range s1.Signature { s1.Signature[i] ^= 0x40 if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Signature[i] ^= 0x40 } // flip every byte in the previous signature of the snapshot for i := range s1.Previous { s1.Previous[i] ^= 0x40 if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Previous[i] ^= 0x40 } // change the version of the snapshot s1.Roots.Version++ if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Roots.Version-- // flip every byte in the verification key for i := range vk { vk[i] ^= 0x40 if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } vk[i] ^= 0x40 } // flip every byte in the event key in the proof for i := range proof.Event.Key { proof.Event.Key[i] ^= 0x40 if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.Event.Key[i] ^= 0x40 } // flip every byte in the event value in the proof for i := range proof.Event.Value { proof.Event.Value[i] ^= 0x40 if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.Event.Value[i] ^= 0x40 } // flip every byte in the key of the treap part of the proof for i := range proof.TreapProof.Key { proof.TreapProof.Key[i] ^= 0x40 if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.TreapProof.Key[i] ^= 0x40 } // for each node in the hash treap proof for i := range proof.TreapProof.Nodes { // flip every byte in the hash of the node for j := range proof.TreapProof.Nodes[i].Hash { proof.TreapProof.Nodes[i].Hash[j] ^= 0x40 if proof.Verify(events, answer, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.TreapProof.Nodes[i].Hash[j] ^= 0x40 } // Note that we _cannot_ detect flips in a proof node's key and value when // the node's key and value are only used to place the node's hash either // left or right along the verified authenticated path in the hash treap. // This is not a security issue for the queries that make up Balloon, // but very well may be for other types of queries not specified in the // design of Balloon. } // always possible to add no events if !proof.Verify(nil, true, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } }
// 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 }
func TestAdd(t *testing.T) { tree := NewTree() event0 := util.Hash([]byte("event 0")) event1 := util.Hash([]byte("event 1")) event2 := util.Hash([]byte("event 2")) event3 := util.Hash([]byte("event 3")) event4 := util.Hash([]byte("event 4")) c, err := tree.Add(event0) if err != nil { t.Fatalf("failed to add event to empty tree, error: %s", err) } // should be that c == H(0||event0) if !bytes.Equal(c, util.Hash(prefixZero, event0)) { t.Fatalf("the returned root for the first event is invalid") } c, err = tree.Add(event1) if err != nil { t.Fatalf("failed to add event to tree of size %d, error: %s", tree.Size(), err) } // should be that c == H(1||H(0||event0)||H(0||event1)) if !bytes.Equal(c, util.Hash(prefixOne, util.Hash(prefixZero, event0), util.Hash(prefixZero, event1))) { t.Fatalf("the returned root for the second event is invalid, %d, %d", tree.getDepth(), tree.Size()-1) } c, err = tree.Add(event2) if err != nil { t.Fatalf("failed to add event to tree of size %d, error: %s", tree.Size(), err) } // should be that c == H(1||H(1||H(0||event0)||H(0||event1))||H(1||H(0||event2))) if !bytes.Equal(c, util.Hash(prefixOne, util.Hash(prefixOne, util.Hash(prefixZero, event0), util.Hash(prefixZero, event1)), util.Hash(prefixOne, util.Hash(prefixZero, event2)))) { t.Fatalf("the returned root for the third event is invalid, %d, %d", tree.getDepth(), tree.Size()-1) } c, err = tree.Add(event3) if err != nil { t.Fatalf("failed to add event to tree of size %d, error: %s", tree.Size(), err) } // should be that c == H(1||H(1||H(0||event0)||H(0||event1)) // ||H(1||H(0||event2)||H(0||event3))) if !bytes.Equal(c, util.Hash(prefixOne, util.Hash(prefixOne, util.Hash(prefixZero, event0), util.Hash(prefixZero, event1)), util.Hash(prefixOne, util.Hash(prefixZero, event2), util.Hash(prefixZero, event3)))) { t.Fatalf("the returned root for the fourth event is invalid, %d, %d", tree.getDepth(), tree.Size()-1) } c, err = tree.Add(event4) if err != nil { t.Fatalf("failed to add event to tree of size %d, error: %s", tree.Size(), err) } // should be that c == H(1||x||y), where // x = H(1||H(1||H(0||event0)||H(0||event1)) // ||H(1||H(0||event2)||H(0||event3))) // y = H(1||H(1||H(0||event4))) x := util.Hash(prefixOne, util.Hash(prefixOne, util.Hash(prefixZero, event0), util.Hash(prefixZero, event1)), util.Hash(prefixOne, util.Hash(prefixZero, event2), util.Hash(prefixZero, event3))) y := util.Hash(prefixOne, util.Hash(prefixOne, util.Hash(prefixZero, event4))) if !bytes.Equal(c, util.Hash(prefixOne, x, y)) { t.Fatalf("failed to add event to tree of size %d, error: %s", tree.Size(), err) } }
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 } }
func TestMembershipQueryProofFlip(t *testing.T) { /* Create a Balloon with size events. */ size := 10 sk, vk, err := Genkey() if err != nil { t.Fatalf("failed to generate keys: %s", err) } balloon, s0, err := Setup(nil, sk, vk, NewTestEventStorage()) if err != nil { t.Fatalf("failed to setup balloon: %s", err) } 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 := balloon.Update(events, s0, sk) if err != nil { t.Fatalf("failed to update to s1: %s", err) } // create a membership query for an event that exists, such that // the proof consists of nodes in both the hash treap and the // history tree answer, event, proof, err := balloon.QueryMembership(events[0].Key, s1, vk) if err != nil { t.Fatalf("failed to perform membership query: %s", err) } if !proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("failed to verify valid membership proof and arguments") } /* OK, now we start systematically tampering with the proof and arguments to Verify to make sure that any changes are detected */ // flip every byte in the key for i := range events[0].Key { events[0].Key[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } events[0].Key[i] ^= 0x40 } // flip every byte in the root of the history tree in the snapshot for i := range s1.Roots.History { s1.Roots.History[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Roots.History[i] ^= 0x40 } // flip every byte in the root of the hash treap in the snapshot for i := range s1.Roots.Treap { s1.Roots.Treap[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Roots.Treap[i] ^= 0x40 } // flip every byte in the signature of the snapshot for i := range s1.Signature { s1.Signature[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } if proof.Verify(events[0].Key, s0, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Signature[i] ^= 0x40 } // flip every byte in the previous signature of the snapshot for i := range s1.Previous { s1.Previous[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } if proof.Verify(events[0].Key, s0, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Previous[i] ^= 0x40 } // change the version of the snapshot s1.Roots.Version++ if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Roots.Version-- // flip answer if proof.Verify(events[0].Key, s1, s1, !answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } // flip every byte in the event key for i := range event.Key { event.Key[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } event.Key[i] ^= 0x40 } // flip every byte in the event value for i := range event.Value { event.Value[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } event.Value[i] ^= 0x40 } // flip every byte in the verification key for i := range vk { vk[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } vk[i] ^= 0x40 } // flip every byte in event of the history tree proof for i := range proof.HistoryProof.Event { proof.HistoryProof.Event[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.HistoryProof.Event[i] ^= 0x40 } // flip every byte in root of the history tree proof for i := range proof.HistoryProof.Root { proof.HistoryProof.Root[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.HistoryProof.Root[i] ^= 0x40 } // flip the index history tree proof proof.HistoryProof.Index++ if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.HistoryProof.Index-- // flip the version history tree proof proof.HistoryProof.Version++ if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.HistoryProof.Version-- // for each node in the history tree proof for i := range proof.HistoryProof.Nodes { // flip each byte in the hash of the node for j := range proof.HistoryProof.Nodes[i].Hash { proof.HistoryProof.Nodes[i].Hash[j] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.HistoryProof.Nodes[i].Hash[j] ^= 0x40 } // flip index in position proof.HistoryProof.Nodes[i].Position.Index++ if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.HistoryProof.Nodes[i].Position.Index-- // flip layer in position proof.HistoryProof.Nodes[i].Position.Layer++ if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.HistoryProof.Nodes[i].Position.Layer-- } // flip every byte in the key of the hash treap proof for i := range proof.TreapProof.Key { proof.TreapProof.Key[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.TreapProof.Key[i] ^= 0x40 } // flip every byte in the value of the hash treap proof for i := range proof.TreapProof.Value { proof.TreapProof.Value[i] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.TreapProof.Value[i] ^= 0x40 } // for each node in the hash treap proof for i := range proof.TreapProof.Nodes { // flip every byte in the hash of the node for j := range proof.TreapProof.Nodes[i].Hash { proof.TreapProof.Nodes[i].Hash[j] ^= 0x40 if proof.Verify(events[0].Key, s1, s1, answer, event, vk) { t.Fatal("verified an invalid proof and/or argument") } proof.TreapProof.Nodes[i].Hash[j] ^= 0x40 } // Note that we _cannot_ detect flips in a proof node's key and value when // the node's key and value are only used to place the node's hash either // left or right along the verified authenticated path in the hash treap. // This is not a security issue for the queries that make up Balloon, // but very well may be for other types of queries not specified in the // design of Balloon. } }
func TestAdd(t *testing.T) { value := util.Itob(42) tree := NewHashTreap() k0 := util.Itob(0) // priority 1b7409ccf0d5a34d3a77eaabfa9fe27427655be9297127ee9522aa1bf4046d4f t.Logf("priority of k0 is %s", hex.EncodeToString(util.Hash(k0))) k1 := util.Itob(1) // priority aa826d9d5f563309b4a9043987ff0d87e82d32d2ddc813aab7defe15f9062911 t.Logf("priority of k1 is %s", hex.EncodeToString(util.Hash(k1))) k2 := util.Itob(2) // priority 8c7471bddfd31fa1e83a761a2f5bc2fc772a5567c85b3a753d3b8a2e8259386f t.Logf("priority of k2 is %s", hex.EncodeToString(util.Hash(k2))) k3 := util.Itob(3) // priority f6ec9d10ae03d32f6b31ae37322b629ec7caad83dd3c5dd870df8e6fd13a5a51 t.Logf("priority of k3 is %s", hex.EncodeToString(util.Hash(k3))) k4 := util.Itob(4) // priority 2a364b052431533577431129ac1acce106dd019245de82ad97663290b5dbb9cc t.Logf("priority of k4 is %s", hex.EncodeToString(util.Hash(k4))) tree.Update() if tree.Root() != nil { t.Fatal("empty tree returned non-nil root") } tree, err := tree.Add(k0, value) if err != nil { t.Fatalf("failed to add key in empty tree: %s", err) } // should be that root == H(k0||value||NoNodeHash||NoNodeHash) if !bytes.Equal(tree.Root(), util.Hash(k0, value, NoNodeHash, NoNodeHash)) { t.Fatalf("the root of the tree of size %d is invalid", tree.Size()) } tree, err = tree.Add(k1, value) if err != nil { t.Fatalf("failed to add key in tree of length %d: %s", tree.Size(), err) } tree.Update() // k1 > k0, and k0 has a smaller priority than k1, so the tree is: // k1 // / // k0 // it should be that root = H(k1||value|| H(k0||value||NoNodeHash||NoNodeHash)|| NoNodeHash) if !bytes.Equal(tree.Root(), util.Hash(k1, value, util.Hash(k0, value, NoNodeHash, NoNodeHash), NoNodeHash)) { t.Fatalf("the root of the tree of size %d is invalid", tree.Size()) } tree, err = tree.Add(k2, value) if err != nil { t.Fatalf("failed to add key in tree of length %d: %s", tree.Size(), err) } tree.Update() // k2 > k1 > k0, and priority is k1 > k2 > k0, so the tree is: // k1 // / \ // k0 k2 // it should be that root = H(k1||value|| // H(k0||value||NoNodeHash||NoNodeHash)|| // H(k2||value||NoNodeHash||NoNodeHash)) if !bytes.Equal(tree.Root(), util.Hash(k1, value, util.Hash(k0, value, NoNodeHash, NoNodeHash), util.Hash(k2, value, NoNodeHash, NoNodeHash))) { t.Fatalf("the root of the tree of size %d is invalid", tree.Size()) } tree, err = tree.Add(k3, value) if err != nil { t.Fatalf("failed to add key in tree of length %d: %s", tree.Size(), err) } tree.Update() // k3 > k2 > k1 > k0, and priority is k3 > k1 > k2 > k0, so the tree is: // k3 // / // k1 // / \ // k0 k2 // it should be that root = H(k3||value|| // H(k1||value|| // H(k0||value||NoNodeHash||NoNodeHash)|| // H(k2||value||NoNodeHash||NoNodeHash))|| // NoNodeHash) if !bytes.Equal(tree.Root(), util.Hash(k3, value, util.Hash(k1, value, util.Hash(k0, value, NoNodeHash, NoNodeHash), util.Hash(k2, value, NoNodeHash, NoNodeHash)), NoNodeHash)) { t.Fatalf("the root of the tree of size %d is invalid", tree.Size()) } tree, err = tree.Add(k4, value) if err != nil { t.Fatalf("failed to add key in tree of length %d: %s", tree.Size(), err) } tree.Update() // k4 > k3 > k2 > k1 > k0, and priority is k3 > k1 > k2 > k4 > k0, so the tree is: // k3 // / \ // k1 k4 // / \ // k0 k2 // it should be that root = H(k3||value|| // H(k1||value|| // H(k0||value||NoNodeHash||NoNodeHash)|| // H(k2||value||NoNodeHash||NoNodeHash))|| // H(k4||value||NoNodeHash||NoNodeHash)) if !bytes.Equal(tree.Root(), util.Hash(k3, value, util.Hash(k1, value, util.Hash(k0, value, NoNodeHash, NoNodeHash), util.Hash(k2, value, NoNodeHash, NoNodeHash)), util.Hash(k4, value, NoNodeHash, NoNodeHash))) { t.Fatalf("the root of the tree of size %d is invalid", tree.Size()) } _, err = tree.Add(nil, nil) if err == nil { t.Fatal("added (nil,nil) without an error") } _, err = tree.Add(k4, value) if err == nil { t.Fatal("successfully added a node with the same key twice") } if tree.Get([]byte("a key not there")) != nil { t.Fatal("got value for key not in the hash treap") } }
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") } }
func TestPruneQuery(t *testing.T) { count := 1024 treap := NewHashTreap() ints := mrand.Perm(count) var err error for i := 0; i < len(ints); i++ { treap, err = treap.Add(util.Itob(i), util.Itob(i)) if err != nil { t.Fatalf("failed to add to hash treap: %s", err) } } treap.Update() // size new random events size := 42 keys := make([][]byte, size) values := make([][]byte, size) for i := 0; i < size; i++ { keys[i] = make([]byte, util.HashOutputLen) _, err = rand.Read(keys[i]) if err != nil { t.Fatalf("failed to read random bytes: %s", err) } values[i] = util.Hash(keys[i]) } // query for new events possible, proof := treap.QueryPrune(keys, true) if !possible { t.Fatal("not possible to add random keys, highly unlikely") } if !proof.Verify(keys, possible, treap.Root()) { t.Fatal("failed to verify valid proof") } if proof.Size() <= 0 { t.Fatal("size of proof too small") } root, err := proof.Update(keys, values) if err != nil { t.Fatalf("failed to calculate an updated root from a pruned proof: %s", err) } // calculate root to compare with for i := 0; i < len(keys); i++ { treap, err = treap.Add(keys[i], values[i]) if err != nil { t.Fatalf("failed to add to hash treap: %s", err) } } treap.Update() if !bytes.Equal(treap.Root(), root) { t.Fatal("pruned root and actual root differs") } // test when the prune query should prove that it is not possible possible, proof = treap.QueryPrune(keys, true) if possible { t.Fatal("possible to add key that was just added") } if !proof.Verify(keys, possible, treap.Root()) { t.Fatal("failed to verify valid proof") } if proof.Size() <= 0 { t.Fatal("size of proof too small") } // ignore what the proof said, try to update _, err = proof.Update(keys, values) if err == nil { t.Fatal("managed to calculate updated root when adding key already in place") } // all proofs show that we can add no keys if !proof.Verify(nil, true, treap.Root()) { t.Fatal("failed to verify valid proof") } // flip every byte in the key of the proof for i := range proof.Key { proof.Key[i] ^= 0x40 if proof.Verify(keys, possible, treap.Root()) { t.Fatal("verified an invalid proof and/or argument") } proof.Key[i] ^= 0x40 } // treap.Root() and the hash in the first proof node point to the same slice, // so we need to make a copy, otherwise we change both... root = make([]byte, len(treap.Root())) copy(root, treap.Root()) for i := range proof.Nodes { // flip every byte in the hash of the node for j := range proof.Nodes[i].Hash { proof.Nodes[i].Hash[j] ^= 0x40 if proof.Verify(keys, possible, root) { t.Fatal("verified an invalid proof and/or argument") } proof.Nodes[i].Hash[j] ^= 0x40 } } }
func TestRefreshVerifyFlip(t *testing.T) { size := 10 sk, vk, err := Genkey() if err != nil { t.Fatalf("failed to generate keys: %s", err) } balloon, s0, err := Setup(nil, sk, vk, NewTestEventStorage()) if err != nil { t.Fatalf("failed to setup balloon: %s", err) } 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 := balloon.Update(events, s0, sk) if err != nil { t.Fatalf("failed to update to s1: %s", err) } server := NewBalloon(NewTestEventStorage()) if !server.RefreshVerify(nil, nil, s0, vk) { t.Fatalf("failed to do initial refresh: %s", err) } clone := server.Clone() if !server.RefreshVerify(events, s0, s1, vk) { t.Fatalf("failed to do initial refresh: %s", err) } // flip events for i := range events { for j := range events[i].Key { // we need to do this since sort in RefreshVerify // may change the order of the slice based on key or value key := events[i].Key key[j] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } key[j] ^= 0x40 } for j := range events[i].Value { value := events[i].Value value[j] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } value[j] ^= 0x40 } } // flip snapshot s0 s0.Index++ if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s0.Index-- s0.Roots.Version++ if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s0.Roots.Version-- for i := range s0.Roots.History { s0.Roots.History[i] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s0.Roots.History[i] ^= 0x40 } for i := range s0.Roots.Treap { s0.Roots.Treap[i] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s0.Roots.Treap[i] ^= 0x40 } for i := range s0.Signature { s0.Signature[i] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s0.Signature[i] ^= 0x40 } // flip snapshot s1 s1.Index++ if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Index-- s1.Roots.Version++ if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Roots.Version-- for i := range s1.Roots.History { s1.Roots.History[i] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Roots.History[i] ^= 0x40 } for i := range s1.Roots.Treap { s1.Roots.Treap[i] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Roots.Treap[i] ^= 0x40 } for i := range s1.Signature { s1.Signature[i] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Signature[i] ^= 0x40 } for i := range s1.Previous { s1.Previous[i] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } s1.Previous[i] ^= 0x40 } // flip verification key for i := range vk { vk[i] ^= 0x40 if clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("verified an invalid proof and/or argument") } vk[i] ^= 0x40 } // see that the clone works if !clone.RefreshVerify(events, s0, s1, vk) { t.Fatal("broken clone") } }