Exemplo n.º 1
0
    def assign_label(self):
        """ VOs have a different structure than skiplists, so their label
            assignment algorithm is completely different. If a node has a
            right neighbor, then that node must have been a plateau node; if
            not, then its right neighbor must have been a tower node.
        """

        if self.down is None:
            self.label = reduce(
                AuthNode.chash,
                [AuthNode._hash(x.serialize()) for x in reversed(self.elem)])
            return

        if isinstance(self.down, VONode):
            down_hash = self.down.label
        else:
            down_hash = self.down

        if self.right is None:
            # Just assign the bottom hash, since the right neighbor was a
            # tower node
            self.label = down_hash
            return

        if isinstance(self.right, VONode):
            right_hash = self.right.label
        else:
            right_hash = self.right

        self.label = AuthNode.chash(down_hash, right_hash)
        return
Exemplo n.º 2
0
    def test_non_membership_comp(self):
        """ Ensure that non-elements of the skip list are not in it,
            comparing the results to the authenticated skip list.
        """

        for i in xrange(0, 10 * self.num_iters):
            elems = map(IntElem, self.generate_elems(0, 25, 5))
            esl = EmbeddedSkipList.new(elems,
                                       IntElem(-201),
                                       IntElem(201),
                                       coin=HashCoin(),
                                       conn_info=None)
            asl = AuthSkipList.new(elems,
                                   IntElem(-201),
                                   IntElem(201),
                                   coin=HashCoin())

            bad_elem = IntElem(random.randint(-100, 100))
            while bad_elem in elems:
                bad_elem = IntElem(random.randint(-100, 100))

            bad_elems = [IntElem(-150), IntElem(150), bad_elem]

            for elem in bad_elems:

                evisited, eclosest = esl.root.search(elem)
                avisited, aclosest = asl.root.search(elem)

                new = []
                for n, f in evisited:
                    new.append((n, f))
                evisited = new

                eelems, eproof = esl.do_query(evisited, eclosest, elem)
                aelems, aproof = asl.do_query(avisited, aclosest, elem)

                efound = eelems[0] == elem
                afound = aelems[0] == elem

                eproof = [
                    AuthNode._hash(x.serialize()) for x in reversed(eelems)
                ] + eproof
                aproof = [
                    AuthNode._hash(x.serialize()) for x in reversed(aelems)
                ] + aproof

                self.assertEqual(efound, afound)
                self.assertTrue(not efound)

                self.assertEqual(AuthSkipList.verify(aproof), asl.root.label)
                self.assertEqual(EmbeddedSkipList.verify(eproof),
                                 esl.root.label)

                # Make sure the embedded one isn't cheating somehow
                self.assertEqual(esl.root.label, asl.root.label)
Exemplo n.º 3
0
    def test_comparison_to_auth(self):
        """ Test embedded list and authenticated list side-by-side to see where
            the former goes wrong. Mostly here for debugging.
        """

        for i in xrange(0, self.num_iters):
            elems = map(IntElem, self.generate_elems(0, 50, 10))
            esl = EmbeddedSkipList.new(elems,
                                       IntElem(-1),
                                       IntElem(101),
                                       coin=HashCoin(),
                                       conn_info=None)
            asl = AuthSkipList.new(elems,
                                   IntElem(-1),
                                   IntElem(101),
                                   coin=HashCoin())

            for elem in elems:
                evisited, eclosest = esl.root.search(elem)
                avisited, aclosest = asl.root.search(elem)

                new = []
                for n, f in evisited:
                    new.append((n, f))
                evisited = new

                eelems, eproof = esl.do_query(evisited, eclosest, elem)
                aelems, aproof = asl.do_query(avisited, aclosest, elem)

                efound = eelems[0] == elem
                afound = aelems[0] == elem

                eproof = [
                    AuthNode._hash(x.serialize()) for x in reversed(eelems)
                ] + eproof
                aproof = [
                    AuthNode._hash(x.serialize()) for x in reversed(aelems)
                ] + aproof

                # Make sure the embedded one isn't cheating somehow
                self.assertEqual(esl.root.label, asl.root.label)

                self.assertEqual(AuthSkipList.verify(aproof), asl.root.label)
                self.assertEqual(EmbeddedSkipList.verify(eproof),
                                 esl.root.label)

                # Make sure the embedded one isn't cheating somehow
                self.assertEqual(esl.root.label, asl.root.label)
    def do_query(self, visited, closest, elem):
        """ Given the input to a search for an element and that element, return
            a proof that either that element is in the skiplist or that it is
            not (so a proof that the elements on either side of it are adjacent
            in the skiplist.

            Arguments:
            visited - the nodes visited in the skiplist stored in reverse order,
                      paired with a flag indicating whether the next step was
                      to go right or down.
            closest - the node of the base list containing either the element
                      being queried or the greatest element in the skiplist
                      less than the element being queried.
            elem - the element being queried
            
            Returns:
            ret_elems - The elements in the base list starting from 'closest'
                        and going to (and including) the next tower node in the
                        base list.
            proof - A list of hash values that, when combined with ret_elems,
                    proves whether or not elem is in the skiplist.
        """
        proof = []

        last = closest
        found = (last.elem == elem)

        ret_elems = []

        ## First, return the elements of each node to the right of the node
        ## where the search ended until a tower node is reached. This provides
        ## enough information to verify a query in either the positive or
        ## negative direction, as well as enough information for a range
        ## query to be able to search for the next tower node in the base
        ## list.
        nxt = last
        ret_elems.append(nxt.elem)
        while (not nxt.right.tower) and nxt.right.right:
            ret_elems.append(nxt.right.elem)
            nxt = nxt.right
        ret_elems.append(nxt.right.elem)

        for v, flag in visited:
            if not v.right.tower:
                # its right neighbor is a plateau node, so add things
                if v.right is not last:
                    proof.append(v.right.label)
                elif v.down is None:
                    proof.append(AuthNode._hash(v.elem.serialize()))
                else:
                    proof.append(v.down.label)

            last = v

        # Return the elements from the end node in the base list to the next
        # tower node; this provides enough information for proof of membership
        # and proof of lack of membership, plus some extra information for
        # other functions that use do_query()
        return ret_elems, proof
Exemplo n.º 5
0
    def assign_label(self):
        """ One possible source of optimization for this function would
            be to figure out how to streamline/cache the lookups it
            performs.
        """
        node = self
        right = node.right

        if right is None:
            node.label = str(0)
            return

        if node.down is None:
            if right.tower:
                node.label = AuthNode.chash(
                                AuthNode._hash(node.raw_elem),
                                AuthNode._hash(right.raw_elem))
            else:
                node.label = AuthNode.chash(
                                AuthNode._hash(node.raw_elem),
                                right.label)
        else:
            if right.tower:
                node.label = node.down.label
            else:
                node.label = AuthNode.chash(node.down.label, right.label)
    def contains(self, elem):
        """ Override of super method. Now returns a proof that elem either is
            or is not in the skip list.

            NB: I have no idea what this algorithm is trying to do, why
                it's right, or how to verify it, I'm just transcribing it
                from the paper's pseudocode into Python.
        """
        visited, closest = self.root.search(elem)
        elems, proof = self.do_query(visited, closest, elem)

        return elems[0] == elem, [
            AuthNode._hash(x.serialize()) for x in reversed(elems)
        ] + proof
    def test_verified_insert(self):
        """ Test that verified insert actually allows the client to compute
            the new root label.
        """
        lower_bound = -1000
        upper_bound = 1000
        num_elems = 10
        num_new_elems = 150

        for i in range(0, self.num_iters):
            elems = gen_elems(lower_bound, upper_bound, num_elems)
            elems = set(elems)
            sl = AuthSkipList.new(elems, IntElem(lower_bound-1),
                                  IntElem(upper_bound+1))

            old_label = sl.root.label

            new_elems = gen_elems(lower_bound, upper_bound, num_new_elems)
            new_elems = [elem for elem in new_elems if elem not in elems]

            for elem in new_elems:
                print 'inserting elem %d' %elem.key
                print 'into list %s' %str(sl.to_list_of_lists())
                ret_elems, proof, proof_diff = sl.insert_with_diff(elem)
                print 'result list: %s' %str(sl.to_list_of_lists())

                self.assertEqual(
                    AuthSkipList.verify(
                        [AuthNode._hash(e.serialize())
                         for e in reversed(ret_elems)] + proof),
                        old_label)

                new_proof = AuthSkipList.update_query(
                    ret_elems, proof, proof_diff, elem)

                x, qproof = sl.contains(elem)
                self.assertTrue(x,
                    'Claims just-inserted element is not in list')
                self.assertEqual(AuthSkipList.verify(qproof), sl.root.label)

                np = AuthSkipList.verify(new_proof)
                print 'Root label: %s' %str(sl.root.label)
                print 'Recv label: %s' %str(np)
                self.assertEqual(np, sl.root.label)

                old_label = sl.root.label
    def _verify_range_query(proofs, lower, upper, root_label):
        """ Verifies a range query done with (the deprecated function)
            _range_query(). Also deprecated.
        """
        labels = [
            AuthSkipList.verify(
                [AuthNode._hash(x.serialize())
                 for x in reversed(elems)] + proof) for elems, proof in proofs
        ]

        if not labels:
            raise InvalidVerificationObjectException(
                'Empty list of proofs returned')

        all_elems = proofs[0][0]

        for elems, proof in proofs[1:]:
            all_elems.extend(elems[1:])

        ## Check to make sure the original list of elements is sorted
        if not (sorted(all_elems) == all_elems):
            raise InvalidVerificationObjectException(
                'Returned list of elements not sorted')

        ## There might be more than one upper boundary that got included, so
        ## make sure to get rid of all of them from the returned list
        while all_elems[-1] > upper:
            all_elems.pop()

        ## The lower boundary may or may not have been needed, so make sure to
        ## include it just in case
        if all_elems[0] < lower:
            all_elems = all_elems[1:]

        if not all([label == root_label for label in labels]):
            raise InvalidVerificationObjectException(
                'Invalid path exists in VO')

        if not all([lower <= elem <= upper for elem in all_elems]):
            raise InvalidVerificationObjectException(
                'Elements returned not in requested range')

        return all_elems
Exemplo n.º 9
0
    def newnode(vo, down, right, elem, assign_label=False):
        if isinstance(down, str) and isinstance(right, str):
            return AuthNode.chash(down, right)
        elif isinstance(down, str) and right is None:
            return down
        elif isinstance(right, str) and down is None:
            return right
        elif down is None and right is None:
            return super(VONode, VONode).newnode(vo, None, None, elem,
                                                 assign_label)

        if not isinstance(down, VONode):
            node = super(VONode, VONode).newnode(vo, None, right, elem,
                                                 assign_label)
            node.down = down
        else:
            node = super(VONode, VONode).newnode(vo, down, right, elem,
                                                 assign_label)
            down.tower = True

        return node
Exemplo n.º 10
0
    def verify(self, lower, upper):
        """ Verify that a node (including its subtree) is correct, recursively
            assigning labels along the way
        """
        # TODO: recursion won't work for nontrivial cases in python;
        #       rewrite iteratively at some point
        node = self

        if node.down is None:
            if node.right is not None:
                raise VerificationObjectException(
                    'Malformed VO: base VO nodes have no children')
            if len(node.elem) < 2:
                raise VerificationObjectException(
                    'Malformed VO: base element list must have at least 2 elements'
                )

            for i in range(len(node.elem) - 1):
                if node.elem[i] > node.elem[i + 1]:
                    raise VerificationObjectException(
                        'Malformed VO: elements in base must be in order')

            if node.elem[-1] < lower:
                raise VerificationObjectException(
                    'Malformed VO: element out of range (less than minimum)')

            # Don't want to check if right end is greater than maximum---might
            # have returned a base list whose first element is in the range,
            # but whose next element is the right boundary element
            if node.elem[0] > upper:
                raise VerificationObjectException(
                    'Malformed VO: element out of range (greater than maximum)'
                )

            node.label = reduce(
                AuthNode.chash,
                [AuthNode._hash(x.serialize()) for x in reversed(node.elem)])

            return node.elem

        # If it's not a base element (and therefore has a lower neighbor)
        else:
            if node.right is None:

                if isinstance(node.down, VONode):
                    if node.down.down and node.elem != node.down.elem:
                        raise VerificationObjectException(
                            'Malformed VO: element of lower node must be same as parent'
                        )
                    elif (node.down.down is None
                          and node.elem != node.down.elem[0]):
                        raise VerificationObjectException(
                            'Malformed VO: first element of leaf list must be same as parent'
                        )
                    down_elems = node.down.verify(lower, upper)
                    node.label = node.down.label

                    return down_elems
                else:
                    raise VerificationObjectException(
                        'Malformed VO: Branch not collapsed when it should have been (right neighbor None)'
                    )

            else:
                if not (isinstance(node.down, VONode)
                        or isinstance(node.right, VONode)):
                    raise VerificationObjectException(
                        'Malformed VO: Branch not collapsed when it should have been (neither neighbor VOnode)'
                    )

                down_elems = []
                right_elems = []

                down_label = node.down
                right_label = node.right

                if isinstance(node.down, VONode):
                    if node.down.down and node.elem != node.down.elem:
                        raise VerificationObjectException(
                            'Malformed VO: element of lower node must be same as parent'
                        )
                    elif (node.down.down is None
                          and node.elem != node.down.elem[0]):
                        raise VerificationObjectException(
                            'Malformed VO: element of lower node must be same as parent'
                        )

                    down_elems = node.down.verify(lower, upper)
                    down_label = node.down.label
                elif lower <= node.elem <= upper:
                    # This branch definitely should have been returned!
                    # This check keeps a malicious server from omitting
                    # branches in the middle of a range.
                    raise VerificationObjectException(
                        'Malformed VO: branch omitted that should not have been'
                    )

                if isinstance(node.right, VONode):
                    if node.right.elem < node.elem:
                        raise VerificationObjectException(
                            'Malformed VO: elements on right must be >= elements on left'
                        )

                    right_elems = node.right.verify(lower, upper)
                    right_label = node.right.label

                    # Cut off the overlap, if it exists
                    if down_elems:
                        down_elems = down_elems[:-1]

                node.label = AuthNode.chash(down_label, right_label)

                return down_elems + right_elems
    def update_query(base_elems, old_proof, proof_diff, elem):
        """ Updates a query of an element from an old skiplist without
            that element in it to a query of that element in the skiplist
            resulting from inserting it into the old one. Used to update
            the root label after an insert.
        """

        # No matter what, we're going to insert elem into the proof, so do it
        # first
        elem_loc = 1  # will always be immediately after the left neighbor
        base_elems.insert(elem_loc, elem)

        i = 0
        current = None

        # Recall that a proof is just a path from a base level node to the root
        # of the skip list. This partitions the path into three parts:
        #
        # - Everything from the base of the new path to the top of the new tower
        #   created by adding elem
        # - The label of the node below the node to the left of the new plateau
        #   node, if such a node exists (i.e. if at least one new non-base node
        #   was added). This is calculated from elements in the old proof.
        # - Everything further up in the proof than the new tower reached, whose
        #   order is unchanged.
        #
        # The element i keeps track of the difference between these locations:
        # everything to the left of i (not including i) is in the first group,
        # everything to the right of i (including i) is in the third group, and
        # the variable 'current' is the second group.

        for action in proof_diff:
            if action == 'MEET':
                # If we meet a plateau node on the right, we need to make sure
                # it occurs in the proof before the values we're accumulating
                # (since they need to get hashed into the overall value only
                # after the new tower stops & starts going left)
                i = i + 1
            elif action == 'UP':
                # This action should only occur once per insert, if the inserted
                # element's tower ends up going above the base list.

                # First, we accumulate all the elements in the base list to
                # the left of the new element (and including the new element
                # itself) that occur in the proof.
                current = AuthNode.chash(
                    AuthNode._hash(base_elems[0].serialize()),
                    AuthNode._hash(elem.serialize()))
                current = reduce(AuthNode.chash, old_proof[:i], current)

                # Next, we update the proof we're modifying. The first element
                # in it now needs to be the label of the bottom node in the
                # new tower.
                old_proof = old_proof[i:]
                i = 1

                # To compute this label, hash the elements from the inserted
                # element to the next base-level tower node, then accumulate
                # them together in order with the commutative hash.
                proof_order_hashes = [
                    AuthNode._hash(e.serialize())
                    for e in reversed(base_elems[elem_loc:])
                ]
                new_elem_node_label = reduce(AuthNode.chash,
                                             proof_order_hashes)
                old_proof = [new_elem_node_label] + old_proof

                # base_elems has been folded into old_proof, so we can set it
                # to the empty list.
                base_elems = []
            else:  # passed a plateau node
                # Accumulate the next 'acc_levels' elements of the old proof
                # into the current value.
                acc_levels = int(action)
                for z in range(acc_levels):
                    passed = old_proof.pop(i)
                    current = AuthNode.chash(passed, current)

        if current is not None:
            old_proof.insert(i, current)

        # Return the single list to feed in to verify()
        return ([AuthNode._hash(e.serialize())
                 for e in reversed(base_elems)] + old_proof)