Source code for tno.quantum.communication.qkd_key_rate.classical.privacy_amplification

r"""Privacy amplification module.

The :py:class:`~tno.quantum.communication.qkd_key_rate.classical.privacy_amplification.PrivacyAmplification`
class can be used to compute the hash of an error corrected string. The length of the
hashed string equals the remaining entropy.

Typical usage example:

    >>> from tno.quantum.communication.qkd_key_rate.classical import Message
    >>> from tno.quantum.communication.qkd_key_rate.classical.privacy_amplification import (
    ...     PrivacyAmplification,
    ... )
    >>>
    >>> message = Message.random_message(message_length=100)
    >>> privacy = PrivacyAmplification(message.length, error_rate_basis_x=0)
    >>> entropy = privacy.get_entropy_estimate(error_correction_loss=10)
    >>> privacy.do_hash(message, entropy)  # doctest: +SKIP
    '110101100010101010011011111111101010000111101010111001011111010000000110101001000001111110'
"""  # noqa: E501

import hashlib

from tno.quantum.communication.qkd_key_rate._utils import (
    one_minus_binary_entropy as one_minus_h,
)
from tno.quantum.communication.qkd_key_rate.classical._message import Message


[docs]class PrivacyAmplification: """Privacy amplification. A hash of an error corrected string is computed and returned. The length of the hashed string equals the remaining entropy. """
[docs] def __init__(self, observed_pulses_basis_x: int, error_rate_basis_x: float) -> None: """Init of PrivacyAmplification. Args: observed_pulses_basis_x: Number of pulses received in the X-basis error_rate_basis_x: Error rate in the pulses received in the X-basis """ self.observed_pulses_basis_x = observed_pulses_basis_x self.error_rate_basis_x = error_rate_basis_x
[docs] def get_entropy_estimate(self, error_correction_loss: int = 0) -> float: """Estimate the amount of entropy. Uses the key-rate estimation functions to determine the key-rate and obtains the number of secure bits by multiplying this by the number of pulses sent. Adjusts the remaining entropy for losses due to the error-correction. Args: error_correction_loss: Number of corrected errors Returns: The amount of entropy in bits. """ entropy = self.observed_pulses_basis_x * float( one_minus_h(self.error_rate_basis_x)[0] ) entropy -= error_correction_loss return entropy
[docs] def do_hash(self, message: Message, entropy: float) -> str: """Computes the hash of a given bit message, returned as a bit string. The length of the hash equals the number of bits of entropy. Args: message: The message to hash. entropy: The amount of entropy in bits, used to determines size of the hash. Returns: The hashed digest of the given message (as a string of bits). """ if entropy < 0: error_msg = "Entropy is smaller than 0. Secure hash cannot be computed" raise ValueError(error_msg) hash_function = hashlib.shake_256() hash_function.update(bytes(message)) entropy_bits = int(entropy) entropy_bytes = (entropy_bits // 8) + 1 hash_bytes = hash_function.digest(entropy_bytes) return "".join(f"{byte:08b}" for byte in hash_bytes)[:entropy_bits]