Security layer
Selected by the security= component, per protocol family. Shot-noise units,
Devetak–Winter
| Rate | |
| reconciliation efficiency | |
| Holevo bound on Eve's information about Bob's key, Eve purifying the channel | |
| Arithmetic | f64 on the CPU; the subtraction is cancellation-sensitive (precision, not device) |
Added-noise bookkeeping
| Term | Value, referred to the channel input |
|---|---|
Mutual information
| Detection | |
|---|---|
| Homodyne | |
| Heterodyne |
The Holevo bound
Gaussian modulation of coherent states is equivalent to Alice holding one mode of an EPR state of variance
| from an analogous pair | |
| Where the detector enters | trusted: |
Departures from the literal closed form, checked against a 60-digit evaluation (conditioning exam):
| Departure | Detail |
|---|---|
| near | |
the literal form returns exactly zero once x + 1 == x | |
| The guard | on the eigenvalues, |
Trusted vs untrusted is a security model
Whether Eve controls Bob's trusted= flag on the detector.
| Trusted | Untrusted | |
|---|---|---|
| Assumption | detector inside Bob's secure lab | Eve controls detector imperfections |
| Detector noise | held out of Eve's information | attributed to Eve, inflates |
| Key rate | higher, longer reach | conservative |
| Requires | a calibration argument that | nothing |
- The flag belongs to the rate function, not to the receiver component carrying it.
- Discrete modulation: the analytic bound refuses
trusted=True(why); the certified bound carries it. - No click family carries the split: a threshold detector's
and dark rate enter the gain and error rate, with no second labelling.
Untrusted is a substitution: fold the detector into the channel, give Bob a perfect one.
| Substitution | |
| Source | Laudenbach & Pacher, Sec. 2, Eqs. (13)/(14), in input-referred form; Laudenbach 2018, Eqs. (7.38)–(7.41) and (9.92)–(9.97) |
| No | vacuum loss, carried by the smaller |
| Invariant | test/consistency.py asserts it |
import qkd as q
def rate(trusted):
return q.Link(
modulation=q.GaussianModulation(v_a=5.0),
channel=q.Channel(T=0.316, xi=0.01, ref="input"),
bob=q.Bob(detector=q.Heterodyne(eta=0.6, v_el=0.1, trusted=trusted)),
security=q.Asymptotic(beta=0.95),
).run()
rate(True).key_rate # 0.087859 i_ab = 0.516558, chi_BE = 0.402871
rate(False).key_rate # 0.0 i_ab = 0.516558, chi_BE = 0.929099
# (raw value -0.438369)key_rate clamps at zero; the suite asserts untrusted never exceeds trusted. The substitution is the only place modelling the split: budget.assemble() has no vel and no trusted (why).
Finite size
Leverrier, Grosshans & Grangier (2010), collective attacks.
| Quantity | At failure probability |
|---|---|
| two-sided Gaussian tail; at | |
for n in (1e5, 1e7, 1e9, 1e12):
res = q.Link(
modulation=q.GaussianModulation(v_a=5.0),
channel=q.Channel(T=0.316, xi=0.01, ref="input"),
bob=q.Bob(detector=q.Heterodyne(eta=0.6, v_el=0.1, trusted=True)),
security=q.FiniteSize(beta=0.95, eps=1e-10, n=n, pe_fraction=0.5),
).run()
print(n, res.key_rate, res.t_min, res.xi_max, res.delta)key_rate | t_min | xi_max | delta | |
|---|---|---|---|---|
| 0.000000 | 0.288757 | 0.530750 | 0.184454 | |
| 0.018441 | 0.313220 | 0.058010 | 0.018326 | |
| 0.040998 | 0.315721 | 0.014763 | 0.001831 | |
| 0.043834 | 0.315991 | 0.010150 | 0.000058 | |
| asymptotic | 0.087859 | — | — | — |
At pe_fraction recovers it at wider intervals.
cv_bounds(t_hat, sigma2_hat, m, va, eps) | |
|---|---|
| Takes | the measured |
| Cost of the idealisation | overstates the key |
| Plane | the pipeline reports cv_bounds needs it and where it is exactly |
| Same hardware, two planes | de-embedded, |
| Epsilon convention | |
|---|---|
| Decomposition (Leverrier 2010, Eq. (5)) | |
FiniteSize(eps=…) | sets |
| not modelled: qkd runs no reconciliation. A published four-term |
The count the interval is built on
Frame errors
FiniteSize(fer=…) | scales the whole rate: |
fer=None | not modelled, never a measured zero |
| Pinned versus simulated | the table above is a pinned channel; on the simulated path see estimates versus the oracle |
| Attack class | collective; general attacks: the Gaussian de Finetti reduction |
Click protocols use different mathematics
A covariance matrix yields a Holevo bound only under a Gaussian measurement; a threshold click is a non-Gaussian POVM. The optics simulation is shared, security= is per family, and Link refuses a mismatched pair by name.
The WTY individual-attack bound
Waks, Takesue & Yamamoto (2006).
| Collision probability per bit | |
| Rate | |
| the photon-splitting concession for a weak-coherent source of mean | |
| Domain | Behaviour |
|---|---|
| Eq. (34) saturates and proves nothing; the rate is | |
| the concession consumes the key; the rate is | |
Anchors: DPS exam. The QBER is derived from the click train or pinned on IndividualAttack(qber=…); explain() labels which ran.
Every security component, and the family it belongs to
| Modulation | Security component | Eve priced by |
|---|---|---|
GaussianModulation | Asymptotic, FiniteSize | the Holevo bound on a Gaussian state |
PhaseShiftKeying | Asymptotic (analytic bound, untrusted only), CertifiedBound | the Holevo bound on a finite constellation; the relative-entropy proof |
DifferentialPhase | IndividualAttack | the WTY collision probability |
BasisKeying | SplittingAttack: GLLP (GLLP04) over the decoy bounds | the tagged multiphoton fraction plus |
BasisKeying(bases=3) | the same | the same, on a tomographically complete estimate |
BasisKeying(announce="pair") | the same | the same, plus a two-photon term BB84 concedes |
IntensityKeying | PhaseBound, e_phase required | a phase error nothing in the record estimates |
TwoStateKeying | DiscriminationBound | a phase error derived from the loss, or supplied |
PairSource (q.PairLink) | SymmetryBound | the phase error, from the conjugate basis |
| the same | ViolationBound | the observed CHSH violation, no source-state assumption |
BellAnalyser (MDI-BB84, q.Swap) | TestBasisBound | the |
PhaseBound refuses an e_phase below the multiphoton fraction
What the qubit-family bounds assume
Most share the Shor–Preskill shape
| Family | Phase error from | Tolerance and cost |
|---|---|---|
BB84 (BasisKeying) | the conjugate basis; two error rates leave Eve one free Bell-diagonal parameter | tolerable QBER 11.003% |
Six-state (bases=3) | three error rates, fixing all four Bell-diagonal weights | 12.6193%, paid in sifting: |
SARG04 (announce="pair") | the conjugate basis, on conclusive exclusions of an announced pair | 9.689% at one photon, 2.710% at two; sifting |
B92 (TwoStateKeying) | derived from the loss on the plain branch | the rate reaches zero at transmittance equal to the state overlap, the noiseless boundary. At overlap 0.68125 the crossing falls to 0.61884, 0.52015 and 0.42221 at depolarising rates 0.001, 0.005 and 0.01. reference=True has no closed form and prices a supplied bound |
BBM92 (SymmetryBound) | the conjugate basis of an entangled pair | 11.003% |
E91 (ViolationBound) | none: | 7.149%. The gap from 11.003% is what the state assumption is worth |
MDI-BB84 (TestBasisBound) | the test basis, through the joint decoy inversion | the detector is Eve's by construction |
RRDPS (q.rates) | nothing measured: packet length | rises without limit in |
Mode pairing (q.rates) | Ma & Razavi's mdi.rs | pairs arrive at |
Nothing here is device independent (why).
Finite key for BB84-WCP
Lim, Curty, Walenta, Xu & Zbinden (2014), through q.SplittingAttack(block=q.KeyBlock(…)); without a block the asymptotic GLLP rate returns byte for byte. res.key_length is bits for the block, res.key_rate is key_length / n; res.s0, res.s1, res.phi, res.n_key carry the pieces. Example.
Quote this caveat with every number from this path. Tupkary et al.: Lim's phase-error estimate "goes through a Taylor approximation step. Thus the estimate is not a true bound", while "the main essence of the result remains true after suitable fixes". qkd evaluates the bound as published, with E6's load-bearing printing error corrected; no fix for the Taylor step is implemented.
What is different from the continuous-variable path
| Gaussian modulation | BB84-WCP | |
|---|---|---|
| Estimated | integer detection and error counts, per intensity and basis | |
| Interval | two-sided Gaussian tail, | Hoeffding, |
| Unit | bits per symbol | bits for the block, reported beside the per-pulse rate |
| Smoothing | six explicit | |
| Component | q.FiniteSize | q.KeyBlock on the family's security component |
The components are not shared: FiniteSize.n counts symbols and KeyBlock.n emitted pulses; beta pe_fraction has no meaning where the split is over intensities and bases.
The pieces
| Quantity | |
|---|---|
decoy_counts: lower bounds on vacuum and single-photon detections, clamped at zero | |
decoy_errors: upper bound on single-photon bit errors | |
bb84_phase: | |
bb84_length: |
The basis split is derived, not dialled
BasisKeying(sift=…) is sift below sift = 1 too (no test basis). run_basis names the basis with the draw that decided sifting, so key_sifted + test_sifted == sifted exactly.
What it costs, and why biasing is worth doing
GYS receiver, 50 km,
key_rate | ratio to asymptotic | |
|---|---|---|
| 0.000000 | 0.000 | |
| 0.153 | ||
| 0.356 | ||
| 0.379 | ||
| asymptotic | — |
The rate settles near 0.38 of asymptotic. The residual is the test basis and the decoy lines, not a finite-size penalty. Biasing recovers most of it at an interior optimum:
sift | asymptotic | finite, | ratio |
|---|---|---|---|
| 0.5 | 0.356 | ||
| 0.7 | 0.646 | ||
| 0.8 | 0.625 | ||
| 0.9 | 0.499 | ||
| 0.98 | 0 | 0 |
Past the optimum the test basis starves and
Epsilon convention
| Composition | |
KeyBlock(eps_sec=…) | FiniteSize(eps=…) |
KeyBlock(eps_cor=…) | eps_sec: no simulated counterpart, qkd running no reconciliation |
KeyBlock(fer=…) | scales the whole length; None is not modelled |
Closed form or counted
explain()["counts"] says which ran.
| Path | Selected by | Counts |
|---|---|---|
| Closed form | alice=None | expected, from the analytic gains at KeyBlock(n=…) |
| Sampled | alice=q.Alice(…) | measured, integers off run_basis; run(symbols=…) must equal KeyBlock(n=…) |
Expected counts are a typical case, not a bound: a block whose signal line falls low returns a shorter key. The two paths agree to about 1% at
Source-preparation flaws
A qubit source whose phase modulator applies
flaw_tolerant | flaw_standard | |
|---|---|---|
| Phase error | read exactly, by inverting observed yields against Alice's known Bloch vectors | bounded through a quantum coin whose imbalance Eve amplifies by the whole channel loss |
| Fourth return | the phase error rate | the coin imbalance |
| At zero flaw | agree to round-off; both reduce to the single-photon BB84 rate | |
| Smaller published flaw, tilt | distils to 57.775328 dB | stops at 28.766343 dB |
The 29.008984 dB gap is the price of the worst-case assumption, not a security margin. The two tuples do not line up slot for slot; never subtract them.
| Loss tolerance | |
|---|---|
| Mechanism | the single-photon components stay in a two-dimensional Hilbert space, so loss opens no side channel. Three states whose Bloch vectors form a triangle span |
| Numerically | the phase error barely moves across forty decibels of loss; the dark count moves it |
| Scope | |
|---|---|
| Source | a qubit only: no side channel, no Trojan-horse leakage, no mode dependency. Wang's |
| Signals | asymptotic, single photons; a weak-coherent source reaches it through the decoy layer, which supplies |
| Not composed | with attacks.mismatch_rate (why); the unreconciled line of Tamaki's Appendix C: gaps |
Certifying COW′'s phase error
Plain COW's e_phase is supplied. COW′ adds the two-pulse vacuum decoy sequence, and src/sdp.rs certifies its phase error as the value of a semidefinite programme (problem). Method, refusals and the other two solvers: three cones.
The certified value is returned raw: at or above q.Link reaches it: q.IntensityKeying describes a three-sequence source and sdp.rs reads four. The routes are q.rates("cow-vacuum") and q.security.keylength.
Everything else is asymptotic, and says so
| Finite-key route | Families |
|---|---|
| A component | Gaussian modulation, q.FiniteSize; BB84-WCP, q.KeyBlock; MDI-BB84, q.TestBasisBound(block=q.RelayBlock(…)); CV-MDI, q.TwoModeBound(block=q.GaussianBlock(…)) |
q.security.keylength, by name | six-state, SARG04, RRDPS, mode pairing, COW′, discrete modulation, the pair link |
| Its own module | DPS, qkd.dps.finite(..., detector=…) |
Every other request refuses by name; the reasons are What qkd will not do today. What that table does not carry:
| Family | |
|---|---|
| Discrete modulation | analytic bound: Lupo & Ouyang (2022), a heterodyne confined to _core.dm_length refuses source="certified" twice: the equality set sits inside the relaxed one, and the two are written over different observables, |
| DPS | qkd.dps.finite, Mizutani, Takeuchi & Tamaki (2023), reproduces their detector="threshold" names four gaps: the detected event is a photon number, not a click; the protocol is block-wise where run_clicks is a continuous train; ClickOut has no code/sample coin; the analysis wants one symmetric detector pair |
| COW | Korzh et al. 2015 published |
| Six-state | the pooled monitor statistic is the worst case, not an approximation: |
| SARG04 | sarg_length is Nian, Nie, Zhang & Lu, Commun. Theor. Phys. 76, 065101 (2024), Eq. (5), on Rusca et al., Appl. Phys. Lett. 112, 171104 (2018). No complementary-basis sample: |
| E91 and BBM92 | qkd.pairs.finite (Tomamichel & Leverrier (2017), Theorem 3) reproduces the four points their own examples.py prints exactly. _core.ekert_finite refuses and names four missing pieces. q.PairLink carries no block-size component |
| RRDPS, mode pairing | no component tree; epsilons: register |
| The relay | q.TwoModeBound(block=q.GaussianBlock(…)) (Papanastasiou, Ottaviani & Pirandola (2017)): sigma is a coefficient in standard deviations, not a probability, with no default; its epsilons do not compose; attack= refuses "collective" and "coherent" by name. q.RelayBlock is not a q.KeyBlock: n counts pulse pairs, the length is per announced Bell state summed at a summed budget, and the secrecy budget splits into 266 shares against Lim's 21 |
qkd.security, the epsilon register
No two shipped families mean the same thing by "eps". A registry, not a derivation: every row is read off the engine it names.
| Family | What the declared parameter is | Rule | form | Equal shares |
|---|---|---|---|---|
cv | per term. q.FiniteSize.eps is handed to eps_pe, eps_smooth and eps_pa alike, so the composed secrecy parameter is | sum, a union bound over three independent failure events | sum | none |
bb84 | the total | sum: secrecy plus correctness | sum | 21 |
sarg | the total | sum: secrecy plus correctness | sum | 18 |
pairing | the total | sum: secrecy plus correctness | sum | 24 |
pair | the total, over one correctness term and two secrecy | sum, plus a hash length in bits | sum | none |
cvmdi | uncomposed: POP17 add none of these together, and state parameter estimation as a coefficient in standard deviations | the secrecy half is a sum of the two modelled terms; no total composes | sum | none |
mdi | the total per announced Bell state | per-state sum | per-state | 266 |
sixstate | eps_ec sits inside eps | nested | nested | 3 |
cow-vacuum | four terms on one common share | sum at Li's weights 2/1/6/1, each weight counting the bounds that term is spent on | weighted | 10 |
rrdps | Takesue's | max, with a square root inside one branch | max | none |
dmcs | five terms | outer sum over a maximum: | outer-max | none |
dps | four terms, two of them hash lengths | root over a weighted sum, MTT23 Eq. (50), two terms charged three times each | root | none |
families() lists the registered families; describe(family) returns one record. Both refuse an unregistered name.
rule is a sentence; form is the same composition as a token from security.FORMS, and Ledger.secrecy dispatches on it. A row whose form names an arm it does not reach refuses rather than falling through: the plain sum reads zero for six-state, half the parameter for MDI and above one for RRDPS and DPS. The token names the arm of the secrecy half, not the total — hence cvmdi carries sum while its rule composes nothing.
Three kinds, and a hash length is not a probability
| Kind | Is |
|---|---|
eps | a failure probability in src/std.rs::check_eps |
bits | a hash length |
count | whole repetitions of a whole analysis, as Curty's sum over announced Bell states |
A term also carries a role: secrecy, correctness, authentication, or structure, which is not charged but multiplies.
Verify or compute, and only one of them is available per family
compose(family, **terms) | verifies a stated budget. Every term is required; the refusal lists what the family accepts |
allocate(family, target) | computes terms by the family's own published assignment. rrdps only; every other family refuses by name, having an equal-share divisor or none, and dividing a target misreports a budget that composes as a maximum, nests, or counts repetitions |
Both return a Ledger. .secrecy and .correctness are the two halves; .total is their sum plus any authentication term. Each refuses a term the family does not model, naming the file that would produce it. cv models no correctness parameter — a frame error rate scales the rate and is not a probability that two keys differ undetected — and rrdps charges error-correction leakage as a count of bits sent, with no confidence interval.
authenticate(ledger, messages, eps) adds the forgery probability qkd.reconcile's net_length subtracts authentication in bits and never touches the parameter. Never charge both.
A privacy-amplification term — eps_pa, eps_amp, eps_hash — is accepted only by a family whose engine takes one as a separate argument; elsewhere it is refused by name, the family's secrecy parameter already charging the hashing.
Attacks outside every bound on this page
Every rate above bounds Eve given the observables. Under every qkd.attacks attack the observables stay at values an unattacked link would give, so no bound here sees them.
| Attack | Family | Mechanism |
|---|---|---|
Saturation | homodyne CV | the covariance matrix is invariant under a shift of the quadrature mean, which no CV-QKD estimator monitors: Eve intercept-resends every pulse and displaces Bob into his clipping region. Qin, Kumar & Alléaume (2016) |
Calibration | homodyne CV | the shot-noise unit is over-estimated; the reported excess noise is the true one divided by that ratio, and past the zero-noise ratio it is negative, returned raw. Jouguet, Kunz-Jacques & Diamanti (2013) |
Blinding | threshold detectors | under detector control the gain and QBER are whatever Eve reproduces, while she holds one bit per sifted bit. Lydersen et al. (2010); a detector set violating their Eq. (1) is refused, not modelled as a weaker attack |
Mismatch | threshold detectors | a time shift routes the pulse through a long or short path unmeasured, so the QBER carries no trace at any mismatch. Qi, Fung, Lo & Ma (2007); surviving rate Fung, Tamaki, Qi, Lo & Ma (2009), Eqs. (32)–(34) |
Blanking | threshold detectors | a bright pulse in the dead interval blinds the next gate and leaves gain and QBER unchanged. Weier et al. (2011). The q.DeadTime spends on rate, this attack spends on security |
A Reading carries observed and eve and no key rate: key_rate, rate, key, secure, safe and margin raise. Never subtract the two. qkd's asymptotic rate is positive on the channel the attacked estimator reports and negative on the channel that is there. Module reference: Impairments.
The two engines that do not compose
src/flaws.rs and attacks.mismatch_rate may not be chained. Both loss-tolerant analyses require Bob's inconclusive operator
References
| Key | Citation |
|---|---|
| Fung, Tamaki, Qi, Lo & Ma (2009) | QIC 9, 131 (2009), arXiv:0802.3788 |
| Gao et al. (2022) | Opt. Express 30, 23783 (2022), arXiv:2107.09329 |
| GLLP04 | Gottesman, Lo, Lütkenhaus & Preskill, QIC 5, 325 (2004) |
| Jouguet, Kunz-Jacques & Diamanti (2013) | PRA 87, 062313 (2013), arXiv:1304.7024 |
| Kanitschar, George, Lin, Upadhyaya & Lütkenhaus (2023) | PRX Quantum 4, 040306 (2023), arXiv:2301.08686 |
| Laudenbach & Pacher | arXiv:1904.01970 |
| Laudenbach 2018 | arXiv:1703.09278 |
| Leverrier, Grosshans & Grangier (2010) | PRA 81, 062343 (2010) |
| Li, Cao, Xie, Yin & Chen (2024) | Phys. Rev. Research 6, 013022 (2024) |
| Lim, Curty, Walenta, Xu & Zbinden (2014) | Phys. Rev. A 89, 022307 (2014), arXiv:1311.7129 |
| Lupo & Ouyang (2022) | PRX Quantum 3, 010341 (2022), arXiv:2108.00428 |
| Lydersen et al. (2010) | Nat. Photonics 4, 686 (2010), arXiv:1008.4593 |
| Mizutani, Takeuchi & Tamaki (2023), MTT23 | Phys. Rev. Research 5, 023132 (2023), arXiv:2301.09844 |
| Papanastasiou, Ottaviani & Pirandola (2017), POP17 | Phys. Rev. A 96, 042332 (2017), arXiv:1707.04599 |
| Pereira, Curty & Tamaki (2019) | npj Quantum Information 5, 62 (2019), arXiv:1902.02126 |
| Qi, Fung, Lo & Ma (2007) | QIC 7, 73 (2007), quant-ph/0512080 |
| Qin, Kumar & Alléaume (2016) | PRA 94, 012325 (2016), arXiv:1511.01007 |
| Scarani & Renner (2008) | Phys. Rev. Lett. 100, 200501 (2008), arXiv:0708.0709 |
| Tamaki, Curty, Kato, Lo & Azuma (2014) | PRA 90, 052314 (2014), arXiv:1312.3514 |
| Tomamichel & Leverrier (2017) | Quantum 1, 14 (2017), arXiv:1506.08458 |
| Tupkary et al. | arXiv:2502.10340v3 |
| Waks, Takesue & Yamamoto (2006) | PRA 73, 012344 (2006) |
| Weier et al. (2011) | New J. Phys. 13, 073024 (2011), arXiv:1101.5289 |