Exemple #1
0
    def test_genKeys(self):
        assert HASH_ALGO == 'sha3_256'
        seed = b'ABCD'  # Do _not_ change the seed value or the following asserts will fail!

        keys = Keys(4, seed)

        # All nodes in the hash tree are checked
        # z_0
        assert str(
            keys.keys[0]
        ) == '86fcf391e5741c8c72bf67c99d50d0157fb9b02db4e3e12ff9e87cbaebf7aa3e'
        # z_1_2 = h(z_1 || z_2)
        assert str(
            keys.hash_tree_root.left_child
        ) == '387fec1a51aa8e02509065abf2903fdca4d72c59ce6a06b9c626b5b82d93b8d0'
        # z_3_4 = h(z_3 || z_4)
        assert str(
            keys.hash_tree_root.right_child
        ) == '11553e32125fb3743c7f541e9b9dc7d088436f6cf1bfcd2e158e470df16138d5'
        # z_1_2_3_4 = h(z_1_2 || z_3_4)
        assert str(
            keys.hash_tree_root
        ) == '79151d0a354254f666c9652003ec58a667cec715bab1f09fb5080ab5c31d9673'

        assert keys.hash_tree_root.right_child.left_child.is_leaf() is True

        # Merkle tree diagram
        keys.hash_tree_root.clear_mark()  # This is for code coverage
        g = graphviz.Digraph(name="merkle tree",
                             directory="./output",
                             format="dot",
                             node_attr={"shape": "box"})
        g = keys.hash_tree_root.to_graphviz(g)
        g.render()

        # z_i diagram
        g_z = graphviz.Digraph(name="z_i",
                               directory="./output",
                               format="dot",
                               node_attr={"shape": "box"},
                               edge_attr={"color": "red"})
        last_idx = len(keys.keys) - 1
        g_z.node(str(keys.keys[last_idx].uuid),
                 label="z_" + str(last_idx) + " : " +
                 keys.keys[last_idx].short_hex())

        for i in range(last_idx, 0, -1):
            g_z.node(str(keys.keys[i - 1].uuid),
                     label="z_" + str(i - 1) + " : " +
                     keys.keys[i - 1].short_hex())
            g_z.edge(str(keys.keys[i].uuid), str(keys.keys[i - 1].uuid))

        g_z.node(seed.hex(), label="seed : " + str(seed))
        g_z.edge(seed.hex(), str(keys.keys[last_idx].uuid))
        g_z.render()
Exemple #2
0
    def test_3_missing_client_certificate(self):
        # This test works because the DAO memory share certificates among the client and server DAO
        dao_factory = factory(DAOMemoryFactory)
        server = KSIServer(self.id_server, dao_factory.get_server())
        l = 8
        keys = Keys(l=l, seed=b'SEED')
        client = KSIClient(server, dao_factory.get_client(), keys=keys)

        client.sign(b'AAAA')
        assert len(dao_factory.get_client().signatures) == 1 and len(
            dao_factory.get_server().signed) == 1
Exemple #3
0
    def test_default(self):
        id_client = Identifier("client")
        id_server = Identifier("server")
        seed = b'\xde\xad\xbe\xef'
        keys = Keys(l=4, seed=seed)
        z0 = keys.keys[0].hash
        r = keys.hash_tree_root.hash
        t0 = datetime.now()

        cert = Certificate(id_client, z0, r, t0, id_server)
        print(cert)

        # There isn't much of testing to be done here as Certificate is a convenience class...
        assert cert is not None
Exemple #4
0
    def test_all(self):
        dao_factory = factory(DAOMongoFactory)
        server = KSIServer(Identifier('server'), dao_factory.get_server())
        keys = Keys(l=256, seed=b'SEED')
        client = KSIClient(server, dao_factory.get_client(), keys=keys)
        ref_cert = client.certificate

        mongo_cert = server.dao.get_user_certificate(
            client.certificate.id_client)
        self.assertTrue(ref_cert == mongo_cert)

        ref_msg = hash_factory(data=b'DATA').digest()
        sig = client.sign(ref_msg)
        client.verify(sig, client.certificate, ref_msg)
Exemple #5
0
    def test_verify(self):
        dao_factory = factory(DAOMemoryFactory)
        l = 512
        keys = Keys(l=l, seed=b'SEED')
        client = KSIClient(KSIServer(Identifier("server"),
                                     dao_factory.get_server()),
                           dao_factory.get_client(),
                           keys=keys)
        message = b'DDDD'
        sig = client.sign(message)

        self.assertTrue(client.verify(sig, client.certificate, sig.message))

        # Tampering with the hash chain
        sig.c_i.right_child.hash = b'1234567890'  # This is an arbitrary value, any other than the original one would do
        self.assertFalse(client.verify(sig, client.certificate, sig.message))
Exemple #6
0
    def test_4_client_certificate_too_early(self):
        dao_factory = factory(DAOMemoryFactory)
        dao_memory_server = dao_factory.get_server()  # type: DAOMemoryServer
        server = KSIServer(self.id_server, dao_factory.get_server())
        l = 8
        keys = Keys(l=l, seed=b'SEED2')
        client2 = KSIClient(server,
                            dao_factory.get_client(),
                            keys=keys,
                            ID_C_str='client2')
        dao_memory_server.client_certificates[str(
            client2.certificate.id_client)].t_0 += timedelta(seconds=100)

        with self.assertRaises(ValueError):
            client2.sign(b'AAAA')

        assert len(dao_factory.get_client().signatures) == 1 and len(
            dao_memory_server.signed) == 1
Exemple #7
0
 def test_genKeysRandom(self):
     # Test with random seed
     random_keys = Keys(2**4)
Exemple #8
0
    def __init__(self,
                 server: KSIServer,
                 dao: DAOClient,
                 certificate: Certificate = None,
                 keys: Keys = None,
                 ID_C_str: str = "client",
                 api_user: str = "",
                 api_password: str = "",
                 api_ID_S: str = "",
                 public_key_filename: str = "output/public_key." +
                 SIGN_KEY_FORMAT):
        """
        Create a new client with the provided parameters.
        This constructor support 2 "modes": local and API. API uses the REST API (requires a server). The local variant
        is the "legacy" version, although it is great for testing purposes (i.e. with travis or unit-testing).
        To use the local version you must set the server argument and leave api_* arguments. To use the API you do the
        opposite, that is set server to None and fill api_*.
        :param server: The server to ask for timestamps
        :type server: KSIServer
        :param certificate: The client's certificate. It can be None in which case it is filled with self.keys.
        :type certificate: Certificate
        :param keys: The client's keys. It can be None in which case it is generated with the default values.
        :type keys: Keys
        :param ID_C_str: The client's identifier string
        :type ID_C_str: str
        :param api_user: The client's username for the API HTTP Basic Auth
        :type api_user: str
        :param api_password: The client's password for the API HTTP Basic Auth
        :type api_password: str
        :param api_ID_S: The ID_S for the server if you are using the API (i.e. server must be None)
        :type api_ID_S: str
        :param public_key_filename: Path to the public key used to verify signed timestamps
        :type public_key_filename: str
        """
        assert isinstance(dao, DAOClient)
        assert isinstance(api_user, str) and isinstance(
            api_password, str) and isinstance(api_ID_S, str)
        self.server = server

        if self.server:
            assert isinstance(
                server, KSIServer
            ) and api_ID_S == "" and api_user == "" and api_password == ""
            self.server_id = server.ID_S
        else:
            assert api_ID_S != "" and api_user != "" and api_password != ""
            self.server_id = Identifier(api_ID_S)

        self.dao = dao
        self.keys = keys
        self.api_user = api_user
        self.api_password = api_password

        # Generate keys with default values
        if not self.keys:
            self.keys = Keys()

        self.certificate = certificate

        # Configure the user's certificate from self.keys, t_0 is the current UTC time
        if not self.certificate:
            assert isinstance(self.keys, Keys)

            z_0 = self.keys.keys[0].hash
            r = self.keys.hash_tree_root.hash
            t_0 = datetime.utcnow().replace(microsecond=0)
            self.certificate = Certificate(Identifier(ID_C_str), z_0, r, t_0,
                                           self.server_id, self.keys.l)

        assert dao.publish_certificate(self.certificate) is True

        # Dictionary of requests made (indexed by x)
        self.requests = {}
        self.logger = logging.getLogger(__name__ + '.' +
                                        self.__class__.__name__)

        # Asymmetric signature verifier
        self.verifier = SignVerify()
        self.verifier.import_public_keys(public_key_filename)

        logging.debug(
            "Using public key in file '{}'".format(public_key_filename))
Exemple #9
0
# REST API client example.
# This file is executable as a "standalone" script.
#

logging.basicConfig(level=logging.DEBUG)

# Filter messages to come only from the client's logger
for handler in logging.root.handlers:
    handler.addFilter(logging.Filter("ksi.ksi_client.KSIClient"))

if __name__ == "__main__":

    dao_factory = factory(DAOMongoFactory)
    client = KSIClient(None,
                       dao_factory.get_client(),
                       keys=Keys(l=8, seed=b'SEED2'),
                       ID_C_str="client2",
                       api_user="******",
                       api_password="******",
                       api_ID_S="server",
                       public_key_filename="/tmp/public_key." +
                       SIGN_KEY_FORMAT)

    sig = client.sign(b'EFGH', use_rest_api=True)

    dao_client = dao_factory.get_client()  # type: DAOMongoClient
    r = requests.get(API_HOST_PORT + API_ROUTE_BASE + 'signed')
    assert str(client.certificate.id_client) in r.json()['signed_timestamps']

    assert client.verify(sig, client.certificate, sig.message) is True
Exemple #10
0
if __name__ == '__main__':
    add_logger()
    logging.basicConfig(level=logging.INFO)
    assert PERFORM_BENCHMARKS is True  # This switch need to be enabled!
    assert BENCHMARK_MOTIF == "ksi.keys"

    l_nums = list(map(lambda i: 2**i, [i for i in range(2, MAX_L_POW + 1)]))
    _max_l = 2**MAX_L_POW

    for l in l_nums:
        open(LOGGER_DEFAULT_OUTFILE, 'w+').close()
        print("Computing time: \t{} / {}".format(l, _max_l))

        # Benchmark Start
        keys = Keys(l=l)
        # Benchmark End

        logger = logging.getLogger(LOGGER_NAME)

        for h in logger.handlers:
            h.flush()
            logger.removeHandler(h)

        os.rename(LOGGER_DEFAULT_OUTFILE,
                  LOGGER_SAVE_OUTFILE.format("Keys", HASH_ALGO, "l_" + str(l)))

        # Generating keys is quite long, we wish to restore them at a latter time (with the hash function of our choice)
        with shelve.open(KEY_SHELVE_OUTFILE.format("Keys", HASH_ALGO,
                                                   "l_" + str(l)),
                         flag='c') as save:
Exemple #11
0
    def test_sign(self):
        dao_factory = factory(DAOMemoryFactory)
        dao_memory_client = dao_factory.get_client()  # type: DAOMemoryClient

        logging.basicConfig(level=logging.DEBUG)

        server_id = Identifier("server")
        server = KSIServer(server_id, dao_factory.get_server())
        l = 8
        keys = Keys(l=l, seed=b'SEED')
        client = KSIClient(server, dao_memory_client, keys=keys)

        # dao_memory_server.client_certificates[str(client.certificate.id_client)] = client.certificate
        sleep_counter = 2

        sleep(sleep_counter)
        client.sign(b'AAAA')

        # Compute graphviz on the whole merkle graph
        g1 = graphviz.Digraph(name="after sign",
                              directory="./output",
                              format="dot",
                              node_attr={"shape": "box"})
        g1 = keys.hash_tree_root.to_graphviz(g1)
        g1.render()

        # Compute graphviz only the hash chain
        print("Signatures: ")
        for k, v in client.dao.signatures.items(
        ):  # type: _sha3.SHA3, Signature
            print("[{k}] = {v}".format(k=k.hex(), v=v))
            assert v.S_t.status_code == KSIErrorCodes.NO_ERROR
            g2 = graphviz.Digraph(name="hash chain",
                                  directory="./output",
                                  format="dot",
                                  node_attr={"shape": "box"})
            g2 = v.c_i.to_graphviz(g2)
            g2.render()

        # +1 for "the sleep before publishing the signature" mechanism
        sleep_counter += 2 + 1
        client.signatures = {}
        sleep(2)
        client.sign(b'BBBB')

        # Compute graphviz on the whole merkle graph
        g3 = graphviz.Digraph(name="after sign 2",
                              directory="./output",
                              format="dot",
                              node_attr={"shape": "box"})
        g3 = keys.hash_tree_root.to_graphviz(g3)
        g3.render()

        # Compute graphviz only the hash chain
        print("Signatures: ")
        for k, v in client.dao.signatures.items(
        ):  # type: _sha3.SHA3, Signature
            print("[{k}] = {v}".format(k=k.hex(), v=v))
            g4 = graphviz.Digraph(name="hash chain 2",
                                  directory="./output",
                                  format="dot",
                                  node_attr={"shape": "box"})
            g4 = v.c_i.to_graphviz(g4)
            g4.render()

        sleep(l - sleep_counter - 1)

        with self.assertRaises(ValueError):
            client.sign(b'CCCC')