Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,5 @@ cython_debug/
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)

config*.toml
keys/*
keys/*
deps/
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "qosst-alice"
version = "0.10.0"
version = "0.10.0.dev+post-processing2"
description = "Alice submodule of QOSST, containing the server and DSP of Alice."
authors = [
"Yoann Piétri <Yoann.Pietri@lip6.fr>",
Expand All @@ -25,9 +25,11 @@ classifiers = [
[tool.poetry.dependencies]
python = ">=3.9,<3.14"
#qosst-core = "^0.10.0"
qosst-core = { git = "https://github.com/qosst/qosst-core", branch = "dev" }
qosst-core = { git = "https://github.com/qosst/qosst-core", branch = "post-processing" }
#qosst-hal = "^0.10.0"
qosst-hal = { git = "https://github.com/qosst/qosst-hal", branch = "dev" }
qosst-pp = { git = "https://github.com/qosst/qosst-pp", branch = "main" }
zmq = {version = "^0.0.0", extras = ["remote-pp"]}


[tool.poetry.scripts]
Expand Down
2 changes: 1 addition & 1 deletion qosst_alice/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
"""
QOSST package for Alice. It contains the DSP of Alice and the code for the server.
"""
__version__ = "0.10.0"
__version__ = "0.10.0.dev+post-processing2"
179 changes: 124 additions & 55 deletions qosst_alice/alice.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# qosst-alice - Alice module of the Quantum Open Software for Secure Transmissions.
# Copyright (C) 2021-2024 Yoann Piétri
# Copyright (C) 2021-2025 Yoann Piétri

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -30,7 +30,7 @@
import time
import traceback
from pathlib import Path
from typing import Optional
from typing import Optional, List

import numpy as np

Expand All @@ -40,7 +40,7 @@
from qosst_hal.voa import GenericVOA
from qosst_hal.modulator_bias_control import GenericModulatorBiasController

from qosst_core.utils import eph
from qosst_core.utils import eph, complex_to_real
from qosst_core.logging import create_loggers
from qosst_core.configuration import Configuration
from qosst_core.configuration.exceptions import InvalidConfiguration
Expand All @@ -49,11 +49,20 @@
from qosst_core.control_protocol.codes import QOSSTCodes, QOSSTErrorCodes
from qosst_core.infos import get_script_infos

from qosst_pp.reconciliation.reconciliation import reconcile_alice
from qosst_pp.privacy_amplification import privacy_amplification_alice

from qosst_alice import __version__
from qosst_alice.dsp import dsp_alice

logger = logging.getLogger(__name__)

try:
import zmq
except ImportError:
logger.warning("zmq was not imported.")
zmq = None


# pylint: disable=too-many-instance-attributes,too-many-return-statements,too-many-boolean-expressions,too-many-branches,too-many-statements
class QOSSTAlice:
Expand Down Expand Up @@ -81,6 +90,16 @@ class QOSSTAlice:
] #: An array with the quantum sequence, of the current frame.
symbols: Optional[np.ndarray] #: An array with the symbols, of the current frame.
photon_number: float #: The mean photon number of the current frame.
indices: List[int] #: Array of indices requested by Bob for PE.
raw_key_material: (
np.ndarray
) #: Raw key material before reconcilation, after parameter estimation.
reconciled_key: Optional[
List[int]
] #: Variable to store key after error correction.
final_key: Optional[
List[int]
] #: Variable to store key after privacy amplification.

# Hardware
dac: GenericDAC #: The DAC of Alice.
Expand All @@ -106,24 +125,13 @@ def __init__(self, config_path: str):
# State initialization
self.client_connected = False
self.client_initialized = False
self.frame_uuid = None
self.frame_prepared = False
self.frame_sent = False
self.frame_ended = False
self.pe_ended = False
self.ec_initialized = False
self.ec_ended = False
self.pa_ended = False

# Useful variables initialization
self.quantum_sequence = None
self.symbols = None
self.photon_number = 0

# Configuration initialization
self.config_path = config_path
self.config = None

self._reset()

self._load_config()

self._init_hardware()
Expand Down Expand Up @@ -260,8 +268,6 @@ def _reset(self) -> None:
Completly reset the state of the server.
"""
logger.info("Resetting state of the server.")
self.client_connected = False
self.client_initialized = False
self.frame_uuid = None
self.frame_prepared = False
self.frame_sent = False
Expand All @@ -271,9 +277,13 @@ def _reset(self) -> None:
self.ec_ended = False
self.pa_ended = False

# Useful variables initialization
self.quantum_sequence = None
self.symbols = None
self.photon_number = 0
self.reconciled_key = None
self.final_key = None
self.indices = []

def _interruption_handler(self, _signum, _frame) -> None:
"""The interruption handler of the script.
Expand Down Expand Up @@ -363,26 +373,14 @@ def _check_code(self, code: QOSSTCodes) -> bool:
and self.frame_ended
)

if code == QOSSTCodes.EC_INITIALIZATION:
if code in (QOSSTCodes.EC_INITIALIZATION, QOSSTCodes.EC_INITIALIZATION_REMOTE):
return (
self.client_connected
and self.client_initialized
and self.frame_uuid is not None
and self.pe_ended
)

if code in (
QOSSTCodes.EC_BLOCK,
QOSSTCodes.EC_REMAINING,
QOSSTCodes.EC_VERIFICATION,
):
return (
self.client_connected
and self.client_initialized
and self.frame_uuid is not None
and self.ec_initialized
)

if code == QOSSTCodes.PA_REQUEST:
return (
self.client_connected
Expand Down Expand Up @@ -732,6 +730,7 @@ def serve(self):
continue

indices = np.array(data["indices"])
self.indices += data["indices"]
logger.debug("Indices: %s.", str(indices))

logger.info("Sending symbols.")
Expand Down Expand Up @@ -792,43 +791,107 @@ def serve(self):
continue

logger.info("Parameters estimation is approved.")
logger.info(
"Removing symbols used for parameters estimation from raw key material"
)
mask = np.ones(
len(self.symbols), dtype=bool
) # Create an array of True, same length of quantum symbols
mask[np.array(self.indices)] = False # Set False where to remove
self.raw_key_material = np.copy(self.symbols[mask])
logger.info("%i raw key symbols", len(self.raw_key_material))

self.pe_ended = True
self.socket.send(QOSSTCodes.PE_APPROVED)

if code in (
QOSSTCodes.EC_INITIALIZATION,
QOSSTCodes.EC_BLOCK,
QOSSTCodes.EC_REMAINING,
QOSSTCodes.EC_VERIFICATION,
):
logger.error("Error correction is not implemented yet.")
if code == QOSSTCodes.EC_INITIALIZATION:
logger.info("Received EC initialization request")
# Symbols must be normalized in shot noise units
self.reconciled_key = reconcile_alice(
self.socket,
complex_to_real(
self.raw_key_material
* np.sqrt(
self.photon_number / np.mean(np.abs(self.symbols) ** 2)
)
),
self.config.post_processing.reconciliation.dimension,
data,
)
self.ec_ended = True

self.socket.send(QOSSTCodes.UNEXPECTED_COMMAND)
if code == QOSSTCodes.EC_INITIALIZATION_REMOTE:
assert self.config.post_processing.reconciliation.remote
logger.info("Using remote reconciliation.")
assert zmq is not None

if code == QOSSTCodes.PA_REQUEST:
logger.error("Privacy amplification is not implemented yet.")
logger.info("Creating ZMQ socket.")
zmq_context = zmq.Context()
zmq_socket = zmq_context.socket(zmq.REQ)

self.socket.send(QOSSTCodes.UNEXPECTED_COMMAND)
endpoint = self.config.post_processing.reconciliation.remote_endpoint
logger.info("Connecting ZMQ socket to %s", endpoint)
zmq_socket.connect(endpoint)

logger.info("Sending symbols")
zmq_socket.send_json(
{
"alice_symbols": list(
complex_to_real(
self.raw_key_material
* np.sqrt(
self.photon_number
/ np.mean(np.abs(self.symbols) ** 2)
)
)
),
"mdr_dimension": self.config.post_processing.reconciliation.dimension,
}
)

logger.info("Sending ACK to Bob.")
self.socket.send(QOSSTCodes.EC_INITIALIZATION_REMOTE_ACK)

logger.info("Wait for answer from the remote worker.")
data = zmq_socket.recv_json()

logger.info("Received key.")
self.reconciled_key = data["key"]

logger.info("Closing ZMQ socket.")
zmq_socket.close()
zmq_context.term()

self.ec_ended = True
if code == QOSSTCodes.PA_REQUEST:
logger.info("Received PA request.")
self.final_key = privacy_amplification_alice(
self.socket,
self.reconciled_key,
self.config.post_processing.privacy_amplification.extractor,
data,
)
self.pa_ended = True

if code == QOSSTCodes.FRAME_ENDED:
logger.info("Frame %s ended.", str(self.frame_uuid))
self.socket.send(
QOSSTCodes.FRAME_ENDED_ACK, {"frame_uuid": str(self.frame_uuid)}
)
if self.final_key:
if self.config.pushkey:
logger.info("Pushing key to KMS")
self.config.pushkey.interface(
self.frame_uuid,
self.final_key,
**self.config.pushkey.kwargs,
)
else:
logger.warning(
"No pushkey interface is configured. Discarding key."
)
logger.info("Resetting frame values")
self.client_initialized = False
self.frame_uuid = None
self.frame_prepared = False
self.frame_sent = False
self.frame_ended = False
self.pe_ended = False
self.ec_initialized = False
self.ec_ended = False
self.pa_ended = False

self.quantum_sequence = None
self.symbols = None
self.photon_number = 0
self._reset()

def _do_dsp(self) -> bool:
"""
Expand Down Expand Up @@ -903,6 +966,12 @@ def _estimate_photon_number(self) -> float:
"""
assert self.config is not None
assert self.config.alice is not None
if self.config.alice.override_photon_number:
logger.warning(
"Override photon number is not zero. Using this value (%f)",
self.config.alice.override_photon_number,
)
return self.config.alice.override_photon_number
assert self.config.frame is not None
assert self.quantum_sequence is not None
self.dac.set_emission_parameters(
Expand Down