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)
def sign(self, message: bytes, use_rest_api=False) -> Signature: """ Ask the server to sign the message. At the end of this function self.requests will contain an entry at x for self.sign_callback(). :param message: The message to sign :type message: bytes :param use_rest_api: Set to True if you want to use the remote REST API, False otherwise (e.g. travis) :type use_rest_api: bool :return: The signature returned by the self.sign_callback() :rtype: Signature """ assert isinstance(message, bytes) and isinstance(use_rest_api, bool) current_time = datetime.utcnow().replace(microsecond=0) self.logger.debug("Sign request at %s for message: %s", current_time.isoformat(), message) # Assert we have a z_i to sign this message if not (current_time < self.certificate.t_0 + timedelta(seconds=self.keys.l)): self.logger.error("Attempt to sign with an outdated certificate!") raise ValueError # Compute the time difference between now and t_0 of the certificate time_delta = current_time - self.certificate.t_0 # type: timedelta # time_delta + 1 is meant to correct the index time_delta_offset = int(time_delta.total_seconds()) + 1 if time_delta_offset < 0: self.logger.error( "Attempt to sign with a certificate beginning in the future!") raise ValueError # Take the appropriate z_i from the list (exclude the first item being z_0) z_i = self.keys.keys[time_delta_offset] # type: Node self.logger.debug("\tz_i used to sign: %s", z_i.hash.hex()) # x = hash(message || z_i) x = hash_factory(data=bytes(message) + z_i.hash) self.logger.debug("\tx computed: %s", x.hexdigest()) # Insert x in self.requests self.requests[x.hexdigest()] = (z_i, time_delta_offset, x, message) request = TimestampRequest(x.digest(), self.certificate.id_client) # Create the request and send it to the server to get S_t if use_rest_api: assert self.api_user != "" and self.api_password != "" r = requests.post(API_HOST_PORT + API_ROUTE_BASE + 'sign', data=request.to_json(), auth=(self.api_user, self.api_password), headers={'Content-Type': 'application/json'}) response = TimestampResponse.from_json_dict(r.json()) return self.sign_callback(response) else: return self.server.get_timestamp_response( request, lambda _response: self.sign_callback(_response))
def sign(self, message: bytes, use_rest_api=False) -> Signature: """ Ask the server to sign the message. At the end of this function self.requests will contain an entry at x for self.sign_callback(). :param message: The message to sign :type message: bytes :param use_rest_api: Set to True if you want to use the remote REST API, False otherwise (e.g. travis) :type use_rest_api: bool :return: The signature returned by the self.sign_callback() :rtype: Signature """ assert isinstance(message, bytes) and isinstance(use_rest_api, bool) current_time = datetime.utcnow().replace(microsecond=0) self.logger.debug("Sign request at %s for message: %s", current_time.isoformat(), message) # Assert we have a z_i to sign this message if not (current_time < self.certificate.t_0 + timedelta(seconds=self.keys.l)): self.logger.error("Attempt to sign with an outdated certificate!") raise ValueError # Compute the time difference between now and t_0 of the certificate time_delta = current_time - self.certificate.t_0 # type: timedelta # time_delta + 1 is meant to correct the index time_delta_offset = int(time_delta.total_seconds()) + 1 if time_delta_offset < 0: self.logger.error("Attempt to sign with a certificate beginning in the future!") raise ValueError # Take the appropriate z_i from the list (exclude the first item being z_0) z_i = self.keys.keys[time_delta_offset] # type: Node self.logger.debug("\tz_i used to sign: %s", z_i.hash.hex()) # x = hash(message || z_i) x = hash_factory(data=bytes(message) + z_i.hash) self.logger.debug("\tx computed: %s", x.hexdigest()) # Insert x in self.requests self.requests[x.hexdigest()] = (z_i, time_delta_offset, x, message) request = TimestampRequest(x.digest(), self.certificate.id_client) # Create the request and send it to the server to get S_t if use_rest_api: assert self.api_user != "" and self.api_password != "" r = requests.post( API_HOST_PORT + API_ROUTE_BASE + "sign", data=request.to_json(), auth=(self.api_user, self.api_password), headers={"Content-Type": "application/json"}, ) response = TimestampResponse.from_json_dict(r.json()) return self.sign_callback(response) else: return self.server.get_timestamp_response(request, lambda _response: self.sign_callback(_response))
def sign(self, sig: TimestampResponse, base64_encode: bool=True) -> (bytes, TimestampResponse): """ Fill the field sig.signature by signing the message. This is a proxy method for self._sign(). :param sig: The TimestampResponse object to fill :type sig: TimestampResponse :param base64_encode: True to encode the signature in base64 (standard) :type base64_encode: bool :return: (message, filled TimestampResponse) """ assert isinstance(sig, TimestampResponse) msg = self.msg(sig) sig.signature = self._sign(msg, base64_encode) return msg, sig
def from_json(json_obj: dict): """ :param json_obj: A JSON representation of the Signature object :type json_obj: dict :return: A new Signature built from the json_obj parameter :rtype: Signature """ assert 'ID_C' in json_obj assert 'i' in json_obj assert 'z_i' in json_obj assert 'c_i' in json_obj assert 'msg' in json_obj assert 'S_t' in json_obj ID_C = Identifier(json_obj['ID_C'][len(IDENTIFIER_BASE_NAME):]) i = json_obj['i'] z_i = standard_b64decode(json_obj['z_i']) c_i = pickle.loads(standard_b64decode(json_obj['c_i'])) msg = standard_b64decode(json_obj['msg']) S_t = TimestampResponse.from_json_dict(json_obj['S_t']) return Signature(ID_C, i, z_i, c_i, S_t, msg)
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)))