def __init__(self, ID_S: Identifier, dao: DAOServer, filename_private_key: str = "output/private_key." + SIGN_KEY_FORMAT): """ Instantiate a server object. :param ID_S: The server's identifier :type ID_S: Identifier :param filename_private_key: The file containing the server's private key (used to sign) :type filename_private_key: str :param dao: DAO for the server :type dao: DAOServer """ assert isinstance(ID_S, Identifier) and isinstance(dao, DAOServer) assert isinstance(filename_private_key, str) self.ID_S = ID_S self.dao = dao self.signer = SignVerify() self.logger = logging.getLogger(__name__ + '.' + self.__class__.__name__) try: self.signer.import_private_keys(filename_private_key) logging.debug( "Using private key in file '{}'".format(filename_private_key)) except FileNotFoundError: self.logger.error( "%s does not exist, generating and exporting keys", filename_private_key) self.signer.generate_keys() filename_public_key = filename_private_key.replace( "private", "public", 1) self.signer.export_keys(filename_public_key, filename_private_key)
def __init__(self, ID_S: Identifier, dao: DAOServer, filename_private_key: str="output/private_key."+SIGN_KEY_FORMAT): """ Instantiate a server object. :param ID_S: The server's identifier :type ID_S: Identifier :param filename_private_key: The file containing the server's private key (used to sign) :type filename_private_key: str :param dao: DAO for the server :type dao: DAOServer """ assert isinstance(ID_S, Identifier) and isinstance(dao, DAOServer) assert isinstance(filename_private_key, str) self.ID_S = ID_S self.dao = dao self.signer = SignVerify() self.logger = logging.getLogger(__name__ + '.' + self.__class__.__name__) try: self.signer.import_private_keys(filename_private_key) logging.debug("Using private key in file '{}'".format(filename_private_key)) except FileNotFoundError: self.logger.error("%s does not exist, generating and exporting keys", filename_private_key) self.signer.generate_keys() filename_public_key = filename_private_key.replace("private", "public", 1) self.signer.export_keys(filename_public_key, filename_private_key)
class TestSignVerify(TestCase): signverify = SignVerify() def test_0_generate_keys(self): TestSignVerify.signverify.generate_keys() # type: SignVerify self.assertIsNotNone(TestSignVerify.signverify.key) self.assertIsNotNone(TestSignVerify.signverify.signer_verifier) def test_1_export_keys(self): TestSignVerify.signverify.export_keys(filename_public_key, filename_private_key) self.assertTrue(path.isfile(filename_private_key)) self.assertTrue(path.isfile(filename_public_key)) def test_3_import_private_keys(self): TestSignVerify.signverify.import_private_keys(filename_private_key) self.assertIsInstance(TestSignVerify.signverify.signer_verifier, PKCS115_SigScheme) self.assertTrue(TestSignVerify.signverify.signer_verifier.can_sign()) def test_4_import_public_keys(self): TestSignVerify.signverify.import_public_keys(filename_public_key) self.assertIsInstance(TestSignVerify.signverify.signer_verifier, PKCS115_SigScheme) self.assertFalse(TestSignVerify.signverify.signer_verifier.can_sign()) def test_5_sign(self): global signed_resp, signed_msg TestSignVerify.signverify.import_private_keys(filename_private_key) x = hash_factory(data=b'ABCD') t_resp = TimestampResponse(x, Identifier("server"), Identifier("client"), datetime.utcnow(), KSIErrorCodes.NO_ERROR) signed_msg, signed_resp = TestSignVerify.signverify.sign(t_resp) def test_6_verify(self): global signed_resp, signed_msg TestSignVerify.signverify.import_public_keys(filename_public_key) self.assertTrue( TestSignVerify.signverify.verify(signed_msg, signed_resp)) self.assertFalse(TestSignVerify.signverify.verify( b'1234', signed_resp))
# LOGGER_DEFAULT_OUTFILE = LOGGER_DIR + LOGGER_NAME + '.log' LOGGER_SAVE_OUTFILE = LOGGER_DIR + 'benchmark-{}-{}-{}.log' if __name__ == '__main__': assert PERFORM_BENCHMARKS is True # This switch need to be enabled! assert BENCHMARK_MOTIF == "ksi.signverify" add_logger() logging.basicConfig(level=logging.INFO) hash = hash_factory(data=b'ABCD') for i in [1024, 2048, 4096]: open(LOGGER_DEFAULT_OUTFILE, 'w+').close() signer = SignVerify() signer.generate_keys(i) resp = TimestampResponse(hash, Identifier('server'), Identifier('client'), datetime.utcnow(), KSIErrorCodes.NO_ERROR) # Benchmark start m, sig = signer.sign(resp) assert signer.verify(m, sig) is True # 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('Sign', '', 'key-len_' + str(i)))
class KSIServer: """ The KSI server. It receive, check and sign time stamping requests. User certificates are kept in a dict for the moment (on the long term they should be kept in a database). A dict of all signed requests is kept (self.signed) as a dict indexed with the signed message and with the signature as value. """ def __init__(self, ID_S: Identifier, dao: DAOServer, filename_private_key: str = "output/private_key." + SIGN_KEY_FORMAT): """ Instantiate a server object. :param ID_S: The server's identifier :type ID_S: Identifier :param filename_private_key: The file containing the server's private key (used to sign) :type filename_private_key: str :param dao: DAO for the server :type dao: DAOServer """ assert isinstance(ID_S, Identifier) and isinstance(dao, DAOServer) assert isinstance(filename_private_key, str) self.ID_S = ID_S self.dao = dao self.signer = SignVerify() self.logger = logging.getLogger(__name__ + '.' + self.__class__.__name__) try: self.signer.import_private_keys(filename_private_key) logging.debug( "Using private key in file '{}'".format(filename_private_key)) except FileNotFoundError: self.logger.error( "%s does not exist, generating and exporting keys", filename_private_key) self.signer.generate_keys() filename_public_key = filename_private_key.replace( "private", "public", 1) self.signer.export_keys(filename_public_key, filename_private_key) @benchmark_decorator def get_timestamp_response(self, request: TimestampRequest, callback) -> Signature: """ Send a timestamp response for a given request, check if the fields of TimestampRequest object are valid. :param request: The request to answer to :type request: TimestampRequest :param callback: The function to callback once the TimestampResponse is computed. This callback have the following signature: callback(response: TimestampResponse) -> Signature :return: The Signature object returned by the callback :rtype: Signature """ assert isinstance(request, TimestampRequest) self.logger.info("Received timestamp request: %s", str(request)) t = datetime.utcnow().replace( microsecond=0) # We take the time at the reception of the request status_code = self.__client_certificate_is_valid__(request.ID_C, t) response = TimestampResponse(request.x, self.ID_S, request.ID_C, t, status_code) if status_code is KSIErrorCodes.NO_ERROR: msg, response = self.signer.sign(response) assert self.dao.publish_signed_request(msg, response) self.logger.info("Responding with St: %s", str(response)) return callback(response) @benchmark_decorator def __client_certificate_is_valid__( self, ID_C: Identifier, current_time: datetime) -> KSIErrorCodes: """ Check if the client identified by ID_C have a valid certificate. :param ID_C: The client identifier to match to the server's database of user certificates :type ID_C: Identifier :param current_time: The current time (at which the time stamping request was received) :type current_time: datetime :return: KSIErrorCodes.NO_ERROR if the certificate for ID_C is valid, KSIErrorCodes.CERTIFICATE_EXPIRED if the client certificate expired or KSIErrorCodes.UNSPECIFIED_ERROR for other unexpected error(s) :rtype: KSIErrorCodes """ assert isinstance(ID_C, Identifier) and isinstance( current_time, datetime) res = KSIErrorCodes.UNSPECIFIED_ERROR cert = None try: cert = self.dao.get_user_certificate(ID_C) except KeyError: res = KSIErrorCodes.UNKNOWN_CERTIFICATE if cert: if cert.id_server == self.ID_S: if current_time < cert.t_0: res = KSIErrorCodes.CERTIFICATE_TOO_EARLY elif current_time <= cert.t_0 + timedelta(seconds=cert.l): res = KSIErrorCodes.NO_ERROR else: res = KSIErrorCodes.CERTIFICATE_EXPIRED return res
class KSIServer: """ The KSI server. It receive, check and sign time stamping requests. User certificates are kept in a dict for the moment (on the long term they should be kept in a database). A dict of all signed requests is kept (self.signed) as a dict indexed with the signed message and with the signature as value. """ def __init__(self, ID_S: Identifier, dao: DAOServer, filename_private_key: str="output/private_key."+SIGN_KEY_FORMAT): """ Instantiate a server object. :param ID_S: The server's identifier :type ID_S: Identifier :param filename_private_key: The file containing the server's private key (used to sign) :type filename_private_key: str :param dao: DAO for the server :type dao: DAOServer """ assert isinstance(ID_S, Identifier) and isinstance(dao, DAOServer) assert isinstance(filename_private_key, str) self.ID_S = ID_S self.dao = dao self.signer = SignVerify() self.logger = logging.getLogger(__name__ + '.' + self.__class__.__name__) try: self.signer.import_private_keys(filename_private_key) logging.debug("Using private key in file '{}'".format(filename_private_key)) except FileNotFoundError: self.logger.error("%s does not exist, generating and exporting keys", filename_private_key) self.signer.generate_keys() filename_public_key = filename_private_key.replace("private", "public", 1) self.signer.export_keys(filename_public_key, filename_private_key) @benchmark_decorator def get_timestamp_response(self, request: TimestampRequest, callback) -> Signature: """ Send a timestamp response for a given request, check if the fields of TimestampRequest object are valid. :param request: The request to answer to :type request: TimestampRequest :param callback: The function to callback once the TimestampResponse is computed. This callback have the following signature: callback(response: TimestampResponse) -> Signature :return: The Signature object returned by the callback :rtype: Signature """ assert isinstance(request, TimestampRequest) self.logger.info("Received timestamp request: %s", str(request)) t = datetime.utcnow().replace(microsecond=0) # We take the time at the reception of the request status_code = self.__client_certificate_is_valid__(request.ID_C, t) response = TimestampResponse(request.x, self.ID_S, request.ID_C, t, status_code) if status_code is KSIErrorCodes.NO_ERROR: msg, response = self.signer.sign(response) assert self.dao.publish_signed_request(msg, response) self.logger.info("Responding with St: %s", str(response)) return callback(response) @benchmark_decorator def __client_certificate_is_valid__(self, ID_C: Identifier, current_time: datetime) -> KSIErrorCodes: """ Check if the client identified by ID_C have a valid certificate. :param ID_C: The client identifier to match to the server's database of user certificates :type ID_C: Identifier :param current_time: The current time (at which the time stamping request was received) :type current_time: datetime :return: KSIErrorCodes.NO_ERROR if the certificate for ID_C is valid, KSIErrorCodes.CERTIFICATE_EXPIRED if the client certificate expired or KSIErrorCodes.UNSPECIFIED_ERROR for other unexpected error(s) :rtype: KSIErrorCodes """ assert isinstance(ID_C, Identifier) and isinstance(current_time, datetime) res = KSIErrorCodes.UNSPECIFIED_ERROR cert = None try: cert = self.dao.get_user_certificate(ID_C) except KeyError: res = KSIErrorCodes.UNKNOWN_CERTIFICATE if cert: if cert.id_server == self.ID_S: if current_time < cert.t_0: res = KSIErrorCodes.CERTIFICATE_TOO_EARLY elif current_time <= cert.t_0 + timedelta(seconds=cert.l): res = KSIErrorCodes.NO_ERROR else: res = KSIErrorCodes.CERTIFICATE_EXPIRED return res