1. 项目概述为什么用Python模拟BB84协议如果你对量子计算或者量子通信感兴趣那么“量子密钥分发”这个词你一定不陌生。它被誉为“理论上无条件安全”的通信方式听起来既神秘又高深。但理论归理论对于咱们程序员和开发者来说最实在的莫过于亲手把它“跑”起来看看这“无条件安全”的密钥到底是怎么从一堆量子态里蹦出来的。这就是我们今天要干的事儿抛开复杂的物理实验装置用你最熟悉的Python在代码世界里完整地模拟一遍BB84协议。BB84协议是量子密钥分发QKD的奠基之作由Bennett和Brassard在1984年提出。它的核心思想极其巧妙利用量子力学的基本原理特别是海森堡测不准原理和量子不可克隆定理来确保密钥分发的安全性。任何窃听者Eve的测量行为都会不可避免地扰动量子态从而在通信双方Alice和Bob的后续比对中被发现。这个项目的目的就是让你通过代码直观地理解这个“发现窃听”的过程并亲手生成一段共享的安全密钥。你可能会问没有量子计算机没有单光子源怎么模拟这正是Python这类通用编程语言的魅力所在。我们可以用随机数生成来模拟Alice的随机选择和编码用概率来模拟量子态的传输和测量用经典的后处理步骤来提炼出最终的密钥。整个过程你只需要一台能运行Python的电脑。通过这个项目你不仅能深刻理解BB84协议每一步的逻辑还能掌握将抽象物理协议转化为可执行代码的思维方法这对于理解更复杂的量子通信协议如测量设备无关QKD、双场QKD也大有裨益。2. 核心原理拆解BB84协议到底在做什么在动手写代码之前我们必须把BB84协议的“游戏规则”彻底吃透。整个协议可以清晰地分为四个阶段制备与发送、测量、基矢比对筛选和窃听检测误码率估计。理解每个阶段的目的和实现方式是成功模拟的关键。2.1 量子比特与编码基BB84协议的核心载体是量子比特。在物理实现中这可以是光子的偏振态水平/垂直或45°/-45°、相位等。在我们的模拟中我们用一个抽象的二维向量或状态来表示它。协议使用了两组非正交的基Z基或 rectilinear basis通常对应|0和|1态。在我们的模拟中我们可以简单地用比特0和1直接表示并约定在Z基下测量0就是01就是1。X基或 diagonal basis通常对应| (|0|1)/√2 和|- (|0-|1)/√2 态。在X基下原来的比特信息0和1被编码成了叠加态。关键理解为什么非要两组非正交的基这是安全性的根源。如果只有一组基窃听者Eve只要用同样的基测量就能完美获取信息而不被发现。使用两组非正交基后当Eve猜错基时概率50%她的测量会随机坍缩量子态到该基下的一个本征态从而有50%的概率改变原始信息。这个“扰动”就是Alice和Bob后来能发现她的证据。在我们的Python模拟中我们不需要真的去计算叠加态的系数。我们可以用一种更直观的方式来模拟我们仍然用0和1来表示Alice想要发送的经典信息比特。我们引入一个“基”的概念。Alice随机选择用Z基还是X基来“包装”她的比特。对于接收方Bob他也随机选择用Z基还是X基来“拆开”这个包装即测量。只有当Alice和Bob“碰巧”选择了相同的基时Bob的测量结果才会确定性地等于Alice发送的比特。如果基不同Bob的测量结果将是完全随机的0或1各50%概率。2.2 协议四步走流程让我们一步步拆解第一步Alice的制备与发送Alice随机生成一个长的原始密钥比特串比如1000个比特每个比特是0或1。对于每一个比特Alice再随机选择一个基Z或X来编码它。这样每个比特都附带了一个“编码基”的信息。Alice将这些“用特定基编码后的量子态”发送给Bob。在模拟中“发送”这个动作就是把这个比特 编码基的对子传递给下一个处理环节可能经过Eve。第二步Bob的测量Bob收到模拟收到每一个量子态。对于每一个态Bob完全随机地选择一个测量基Z或X进行测量。测量规则如果Bob的测量基等于Alice的编码基那么Bob的测量结果100%等于Alice发送的原始比特。如果Bob的测量基不等于Alice的编码基那么Bob的测量结果是完全随机的有50%概率是050%概率是1与Alice发送的比特无关。Bob记录下他的测量结果和他所使用的测量基。第三步基矢比对筛选协议进行一段时间后Alice和Bob通过一个公开的经典信道比如打电话、发邮件这个信道可以被窃听但不会被篡改来互相公布他们每一步所使用的基注意只公布基不公布比特值。双方对比每一个位置上的基。只保留那些双方使用了相同基的位置。这些位置上Bob的测量结果理论上应该和Alice发送的比特完全一致。这些被保留下来的比特就构成了“原始密钥”或“筛选后密钥”。双方丢弃掉那些基不匹配的位置上的所有数据包括比特值。因为那些位置上的数据是随机的没有信息量。第四步窃听检测与纠错误码率估计为了检查是否有窃听Alice和Bob从筛选后的密钥中随机抽取一部分比如20%比特通过公开信道比对它们的值。如果信道完美且无窃听这部分比对的误码率应该为0或者接近0考虑到模拟中可能引入的极小误差。如果存在窃听者Eve她每次窃听测量都有概率引入错误。双方计算误码率。如果误码率低于一个预先约定的安全阈值例如11%则认为通信基本安全可以继续。如果误码率过高则说明信道不安全必须丢弃整段密钥重新开始。信息协商与隐私放大在确认误码率可接受后双方需要对剩下的密钥进行纠错使用一些经典纠错码协议确保双方的密钥完全一致然后进行隐私放大通过一个公开的哈希函数将密钥压缩以消除Eve可能通过窃听获得的部分信息。最终得到一段长度稍短但安全性极高的最终共享密钥。3. Python模拟实战从零构建BB84现在我们进入最核心的实操环节。我们将使用纯Python和NumPy库来构建整个模拟。确保你的环境中已安装NumPy (pip install numpy)。我们将模拟一个包含窃听者Eve的完整场景。3.1 环境准备与基础定义首先我们定义一些常量和基础函数。我们将用数字0代表Z基数字1代表X基。import numpy as np import hashlib # 用于后续可能的隐私放大演示 # 定义常量 Z_BASIS 0 # 标准基 X_BASIS 1 # 对角基3.2 模拟无窃听的信道我们先实现一个没有窃听者的理想情况帮助你建立信心。def generate_random_bits(length): 生成指定长度的随机比特序列0或1。 return np.random.randint(0, 2, sizelength) def generate_random_bases(length): 生成指定长度的随机基序列0代表Z基1代表X基。 return np.random.randint(0, 2, sizelength) def simulate_measurement(alice_bits, alice_bases, bob_bases): 模拟Bob的测量过程。 参数: alice_bits: Alice发送的比特数组 alice_bases: Alice使用的编码基数组 bob_bases: Bob使用的测量基数组 返回: bob_bits: Bob测量得到的比特数组 bob_bits np.empty_like(alice_bits) for i in range(len(alice_bits)): if alice_bases[i] bob_bases[i]: # 基相同完美测量 bob_bits[i] alice_bits[i] else: # 基不同测量结果完全随机 (0或1等概率) bob_bits[i] np.random.randint(0, 2) return bob_bits def sift_keys(alice_bits, alice_bases, bob_bits, bob_bases): 执行基矢比对筛选出双方基相同的比特。 参数: alice_bits, alice_bases, bob_bits, bob_bases 返回: sifted_alice_key, sifted_bob_key: 筛选后的密钥 # 找到基相同的位置索引 same_basis_indices np.where(alice_bases bob_bases)[0] # 提取这些位置上的比特 sifted_alice_key alice_bits[same_basis_indices] sifted_bob_key bob_bits[same_basis_indices] return sifted_alice_key, sifted_bob_key, same_basis_indices def estimate_error_rate(alice_key, bob_key, sample_ratio0.2): 从筛选后的密钥中随机抽样一部分进行比对估计误码率。 参数: alice_key, bob_key: 筛选后的密钥 sample_ratio: 抽样比例 返回: error_rate: 估计的误码率 sample_indices: 被抽样的索引用于后续从密钥中移除这些测试比特 key_length len(alice_key) sample_size int(key_length * sample_ratio) # 随机选择测试样本的索引 sample_indices np.random.choice(key_length, sizesample_size, replaceFalse) # 计算错误比特数 errors np.sum(alice_key[sample_indices] ! bob_key[sample_indices]) error_rate errors / sample_size if sample_size 0 else 0.0 return error_rate, sample_indices def remove_samples(key, indices_to_remove): 从密钥中移除指定索引的比特。 mask np.ones(len(key), dtypebool) mask[indices_to_remove] False return key[mask] # 主模拟流程无窃听 def simulate_bb84_no_eve(key_length1000): print( 开始模拟BB84协议无窃听 ) # 1. Alice准备 alice_raw_bits generate_random_bits(key_length) alice_bases generate_random_bases(key_length) print(fAlice生成了 {key_length} 个原始比特和随机基。) # 2. Bob准备测量基并“测量” bob_bases generate_random_bases(key_length) bob_raw_bits simulate_measurement(alice_raw_bits, alice_bases, bob_bases) print(fBob随机选择了测量基并完成了‘测量’。) # 3. 基矢比对筛选 sifted_alice_key, sifted_bob_key, _ sift_keys(alice_raw_bits, alice_bases, bob_raw_bits, bob_bases) print(f基矢比对后筛选出 {len(sifted_alice_key)} 个比特的原始密钥。) # 4. 窃听检测误码率估计 error_rate, test_indices estimate_error_rate(sifted_alice_key, sifted_bob_key) print(f抽样估计的误码率为: {error_rate:.4f} ({error_rate*100:.2f}%)) # 5. 移除测试比特得到最终密钥这里暂不进行纠错和隐私放大 final_alice_key remove_samples(sifted_alice_key, test_indices) final_bob_key remove_samples(sifted_bob_key, test_indices) print(f移除测试比特后最终共享密钥长度为: {len(final_alice_key)} 比特。) print(fAlice和Bob的最终密钥是否完全一致 {np.array_equal(final_alice_key, final_bob_key)}) print(*50) return final_alice_key, final_bob_key, error_rate # 运行一次无窃听模拟 final_alice_key, final_bob_key, error_rate simulate_bb84_no_eve(500)运行这段代码你会看到在无窃听情况下筛选后的密钥长度大约是原始长度的一半因为基匹配的概率是50%并且估计的误码率应该非常接近于0。最终Alice和Bob的密钥是完全一致的。3.3 引入窃听者Eve现在让我们加入一个窃听者Eve看看她是如何被发现的。Eve的策略很简单拦截Alice发送给Bob的量子态自己进行测量同样需要随机选择基然后将她测量后的态或者根据测量结果重新制备的态发送给Bob。这个过程被称为“拦截-重发”攻击。def eavesdrop_intercept_resend(alice_bits, alice_bases): 模拟窃听者Eve的‘拦截-重发’攻击。 Eve拦截量子态随机选择基进行测量然后将她认为的态发送给Bob。 参数: alice_bits: Alice的原始比特 alice_bases: Alice的编码基 返回: eve_bits: Eve测量得到的比特 eve_bases: Eve随机选择的测量基 resend_bits: Eve重发给Bob的比特她测量得到的结果 resend_bases: Eve重发给Bob的基她测量时使用的基 length len(alice_bits) eve_bases generate_random_bases(length) # Eve随机选择测量基 # Eve进行测量规则与Bob相同 eve_bits simulate_measurement(alice_bits, alice_bases, eve_bases) # Eve将她测量得到的比特和**她使用的基**发送给Bob。 # 注意在真实量子世界中Eve发送的是她测量后坍缩的量子态。 # 在我们的模拟中我们用eve_bits, eve_bases这个对子来表示她“重发”的东西。 # 对于Bob来说他收到的“发送方”变成了Eve。 return eve_bits, eve_bases def simulate_bb84_with_eve(key_length1000, eve_presenceTrue): 模拟包含窃听者Eve的BB84协议。 eve_presence: 布尔值控制Eve是否存在。 print(f 开始模拟BB84协议Eve窃听: {eve_presence} ) # 1. Alice准备 alice_raw_bits generate_random_bits(key_length) alice_bases generate_random_bases(key_length) # 2. 信道传输可能经过Eve if eve_presence: print(警告信道中存在窃听者Eve) # Eve进行拦截-重发攻击 eve_bits, eve_bases eavesdrop_intercept_resend(alice_raw_bits, alice_bases) # Bob实际接收到的是Eve重发的东西 received_bits, received_bases eve_bits, eve_bases else: # 无窃听Bob直接收到Alice的 received_bits, received_bases alice_raw_bits, alice_bases # 3. Bob的测量 bob_bases generate_random_bases(key_length) # Bob对“收到”的态进行测量。注意此时发送方是Eve或有窃听或Alice无窃听。 bob_raw_bits simulate_measurement(received_bits, received_bases, bob_bases) # 4. 基矢比对Alice和Bob公开讨论基 sifted_alice_key, sifted_bob_key, _ sift_keys(alice_raw_bits, alice_bases, bob_raw_bits, bob_bases) print(f基矢比对后筛选出 {len(sifted_alice_key)} 个比特的原始密钥。) # 5. 窃听检测 error_rate, test_indices estimate_error_rate(sifted_alice_key, sifted_bob_key) print(f抽样估计的误码率为: {error_rate:.4f} ({error_rate*100:.2f}%)) # 6. 安全判断与后续处理 security_threshold 0.11 # 一个典型的安全误码率阈值例如11% if error_rate security_threshold: print(f误码率 {error_rate*100:.2f}% 高于安全阈值 {security_threshold*100}%) print(检测到潜在窃听本次通信密钥不安全应被丢弃。) final_alice_key final_bob_key np.array([], dtypeint) else: print(f误码率 {error_rate*100:.2f}% 低于安全阈值通信基本安全。) # 移除测试比特得到最终密钥模拟未做纠错和隐私放大 final_alice_key remove_samples(sifted_alice_key, test_indices) final_bob_key remove_samples(sifted_bob_key, test_indices) print(f移除测试比特后最终共享密钥长度为: {len(final_alice_key)} 比特。) # 注意即使误码率低于阈值由于Eve的窃听Alice和Bob的密钥也可能不一致。 # 这需要通过后续的“信息协商”纠错步骤来解决。 print(fAlice和Bob的最终密钥是否完全一致 {np.array_equal(final_alice_key, final_bob_key)}) print(*50) return final_alice_key, final_bob_key, error_rate # 运行模拟有窃听 vs 无窃听 print(\n模拟1: 无窃听者) key_a1, key_b1, err1 simulate_bb84_with_eve(1000, eve_presenceFalse) print(\n模拟2: 存在窃听者Eve拦截-重发攻击) key_a2, key_b2, err2 simulate_bb84_with_eve(1000, eve_presenceTrue)多次运行上面的代码你会发现无窃听时误码率极低理论上应为0模拟中因随机性可能有个别错误但比例极低Alice和Bob的最终密钥一致。有窃听时误码率会显著升高大约在25%左右这正是Eve被暴露的关键。为什么是25%我们可以简单计算一下Eve有50%的概率选错基与Alice不同当她选错基时她的测量会随机扰动比特导致她发给Bob的态有50%的概率是错的。而对于Bob他也有50%的概率选对基与Eve相同。综合起来Eve引入错误的概率是 0.5 * 0.5 0.25。所以Alice和Bob在比对测试比特时会发现高达25%的错误率远高于安全阈值从而断定信道不安全。3.4 可视化与深入分析为了更直观地理解整个过程我们可以添加一些简单的统计和可视化使用matplotlib需安装pip install matplotlib。import matplotlib.pyplot as plt def run_multiple_simulations(num_simulations100, key_length200): 多次运行模拟统计有/无窃听下的误码率分布。 errors_no_eve [] errors_with_eve [] for i in range(num_simulations): _, _, err_no simulate_bb84_with_eve(key_length, eve_presenceFalse) errors_no_eve.append(err_no) _, _, err_with simulate_bb84_with_eve(key_length, eve_presenceTrue) errors_with_eve.append(err_with) # 绘制误码率分布直方图 plt.figure(figsize(10, 6)) plt.hist(errors_no_eve, bins20, alpha0.7, label无窃听 (No Eve), colorgreen, edgecolorblack) plt.hist(errors_with_eve, bins20, alpha0.7, label有窃听 (With Eve), colorred, edgecolorblack) plt.axvline(x0.11, colorblue, linestyle--, linewidth2, labelf安全阈值 (~11%)) plt.xlabel(误码率 (QBER)) plt.ylabel(出现次数) plt.title(fBB84协议误码率分布模拟 ({num_simulations} 次运行, 密钥长度{key_length})) plt.legend() plt.grid(True, alpha0.3) plt.show() print(f无窃听平均误码率: {np.mean(errors_no_eve):.4f}) print(f有窃听平均误码率: {np.mean(errors_with_eve):.4f} (理论值~0.25)) # 运行100次模拟观察误码率分布 run_multiple_simulations(num_simulations100, key_length200)这张图会清晰地展示在有Eve窃听时误码率会聚集在25%附近而无窃听时误码率则集中在0附近。两者被一条安全阈值线例如11%明显分开直观地证明了BB84协议检测窃听的能力。4. 关键参数、问题排查与进阶思考通过上面的模拟你已经掌握了BB84的核心流程。但在实际理解或未来进行更复杂的模拟时以下几个要点和常见问题需要特别注意。4.1 核心参数与安全阈值筛选效率由于基匹配概率是50%所以原始密钥经过基矢比对后长度大约会减半。这是协议固有的效率损失。窃听引入的误码率对于“拦截-重发”攻击理论误码率是25%。这是最经典的一种攻击。实际上Eve可能有更聪明的攻击策略如相干攻击但BB84协议的安全性分析证明只要误码率低于某个阈值总可以通过隐私放大提取出安全密钥。这个阈值取决于具体的安全证明和协议变种对于BB84协议在理想单光子源下这个阈值大约是11%更精确的值是约12.6%或更低取决于模型。在我们的模拟中我们使用了11%作为判断依据。最终密钥生成率并非所有筛选后的比特都能成为最终密钥。我们需要扣除测试比特用于估计误码率的部分例如20%。纠错开销为了纠正剩余比特中的错误双方需要交换一些纠错信息通过公开信道这部分信息会被窃听者听到因此需要从密钥长度中扣除相应的信息量。隐私放大压缩为了消除Eve可能拥有的任何信息需要将密钥通过一个通用哈希函数压缩到一个更短的长度。压缩的比例与估计的误码率有关误码率越高压缩得越厉害。4.2 模拟中常见问题与排查误码率不为零无窃听时在无窃听的模拟中由于我们使用了随机数并且“测量”函数在基不匹配时生成随机比特这本身不会在Alice和Bob之间引入错误因为筛选时只保留基匹配的比特。你看到的极低非零误码率可能源于极小的浮点误差或随机数生成的极端情况。如果误码率显著高于0比如1%请检查sift_keys函数逻辑确保它正确地只筛选了alice_bases bob_bases的位置并且simulate_measurement函数在基匹配时是直接复制比特值。有窃听时误码率远低于25%确保你的Eve模拟函数eavesdrop_intercept_resend工作正确。关键点是Eve测量后发送给Bob的是她测量得到的比特和她测量时使用的基。Bob随后会根据他自己随机选择的基对Eve发来的这个比特基对进行“测量”。这个逻辑链条必须准确。最终密钥不一致即使在误码率低于阈值的情况下由于信道噪声我们模拟中暂未加入或Eve的窃听筛选后密钥中仍可能存在未被测试样本检测到的错误比特。这就是为什么真实的QKD协议在误码率检测后必须进行信息协商纠错步骤例如使用Cascade或LDPC码等协议确保双方密钥完全一致。我们的模拟省略了这一步所以最终密钥可能不一致。要模拟一个完整的流程你需要在此之后添加纠错模块。随机数生成器的质量numpy.random.randint对于教学模拟足够了。但在对安全性要求极高的真实场景或加密演示中需要使用密码学安全的随机数生成器如secrets模块。4.3 从模拟到现实局限性认知我们的Python模拟极大地简化了现实理想单光子源我们假设Alice每次都能完美地发送一个量子比特。现实中制备理想的单光子源非常困难弱相干光源激光器衰减是更常用的方案但这会引入光子数分离攻击等安全漏洞需要更复杂的协议如诱骗态协议来弥补。理想探测器我们假设Bob的探测器是完美的效率100%无暗计数。现实中的单光子探测器有效率限制、暗计数和后脉冲等噪声。无损耗信道我们假设量子态在信道中传输没有损耗。真实的光纤或自由空间信道有衰减随着距离增加信号指数衰减这严重限制了成码率和传输距离。简单的窃听模型我们只模拟了“拦截-重发”攻击。现实中Eve可以使用更复杂的量子攻击手段。BB84协议的安全性证明可以抵御所有量子攻击但我们的简化模拟无法体现这一点。尽管有这些简化这个模拟项目已经成功地揭示了BB84协议最精髓的思想利用非正交基和量子测量的扰动特性来检测窃听。它是你深入量子密码学殿堂的一块绝佳敲门砖。5. 项目扩展与更深度的模拟挑战如果你已经成功完成了基础模拟并想进一步挑战这里有几个方向可以深入实现纠错与隐私放大纠错实现一个简单的奇偶校验纠错协议如Cascade协议的简化版。双方将密钥分成块比对块的奇偶性如果不同则二分查找错误比特。这个过程需要在公开信道交换一些信息因此会泄露部分信息量。隐私放大在纠错后双方共享一个一致的密钥串但Eve可能拥有部分信息。使用一个公开的通用哈希函数如SHA-256对密钥进行压缩。例如如果估计Eve最多掌握了密钥的t比特信息那么就将n比特的密钥哈希成n - t - s比特的输出其中s是安全参数。这样就能以极高的概率消除Eve的知识。模拟非理想器件的影响信道损耗在simulate_measurement函数中引入一个损耗概率loss_prob。对于每个量子态以该概率让Bob“探测不到”任何信号从而没有测量结果。这会影响筛选后的密钥长度和成码率。探测器效率与暗计数模拟Bob的探测器不是100%效率。同时即使没有信号输入探测器也有一定概率产生一个“暗计数”误触发。这会在无窃听时引入本底误码率。模拟诱骗态BB84协议这是现实中最常用的QKD协议之一用于对抗使用弱相干光源带来的安全漏洞。你需要模拟Alice随机发送三种不同强度的光脉冲信号态用于生成密钥、诱骗态用于探测光子数分离攻击和真空态。协议的后处理分析会更加复杂需要估计单光子成分的贡献。可视化交互界面使用tkinter或streamlit创建一个简单的图形界面让用户可以调整参数如密钥长度、窃听开关、信道损耗等并实时看到密钥生成过程、误码率变化和最终密钥的一致性对比。完成这个Python模拟项目你收获的不仅仅是一段代码而是对量子密码学核心思想——利用物理定律而非计算复杂度来保障安全——的一次深刻实践。下次当你听到“量子密钥分发”时你脑海中浮现的将不再是晦涩的公式而是一行行清晰的代码逻辑以及那在随机基比对中浮现出的、确保安全的25%误码率警戒线。这正是动手实践的魅力所在。
Python模拟BB84协议:量子密钥分发原理与代码实现
1. 项目概述为什么用Python模拟BB84协议如果你对量子计算或者量子通信感兴趣那么“量子密钥分发”这个词你一定不陌生。它被誉为“理论上无条件安全”的通信方式听起来既神秘又高深。但理论归理论对于咱们程序员和开发者来说最实在的莫过于亲手把它“跑”起来看看这“无条件安全”的密钥到底是怎么从一堆量子态里蹦出来的。这就是我们今天要干的事儿抛开复杂的物理实验装置用你最熟悉的Python在代码世界里完整地模拟一遍BB84协议。BB84协议是量子密钥分发QKD的奠基之作由Bennett和Brassard在1984年提出。它的核心思想极其巧妙利用量子力学的基本原理特别是海森堡测不准原理和量子不可克隆定理来确保密钥分发的安全性。任何窃听者Eve的测量行为都会不可避免地扰动量子态从而在通信双方Alice和Bob的后续比对中被发现。这个项目的目的就是让你通过代码直观地理解这个“发现窃听”的过程并亲手生成一段共享的安全密钥。你可能会问没有量子计算机没有单光子源怎么模拟这正是Python这类通用编程语言的魅力所在。我们可以用随机数生成来模拟Alice的随机选择和编码用概率来模拟量子态的传输和测量用经典的后处理步骤来提炼出最终的密钥。整个过程你只需要一台能运行Python的电脑。通过这个项目你不仅能深刻理解BB84协议每一步的逻辑还能掌握将抽象物理协议转化为可执行代码的思维方法这对于理解更复杂的量子通信协议如测量设备无关QKD、双场QKD也大有裨益。2. 核心原理拆解BB84协议到底在做什么在动手写代码之前我们必须把BB84协议的“游戏规则”彻底吃透。整个协议可以清晰地分为四个阶段制备与发送、测量、基矢比对筛选和窃听检测误码率估计。理解每个阶段的目的和实现方式是成功模拟的关键。2.1 量子比特与编码基BB84协议的核心载体是量子比特。在物理实现中这可以是光子的偏振态水平/垂直或45°/-45°、相位等。在我们的模拟中我们用一个抽象的二维向量或状态来表示它。协议使用了两组非正交的基Z基或 rectilinear basis通常对应|0和|1态。在我们的模拟中我们可以简单地用比特0和1直接表示并约定在Z基下测量0就是01就是1。X基或 diagonal basis通常对应| (|0|1)/√2 和|- (|0-|1)/√2 态。在X基下原来的比特信息0和1被编码成了叠加态。关键理解为什么非要两组非正交的基这是安全性的根源。如果只有一组基窃听者Eve只要用同样的基测量就能完美获取信息而不被发现。使用两组非正交基后当Eve猜错基时概率50%她的测量会随机坍缩量子态到该基下的一个本征态从而有50%的概率改变原始信息。这个“扰动”就是Alice和Bob后来能发现她的证据。在我们的Python模拟中我们不需要真的去计算叠加态的系数。我们可以用一种更直观的方式来模拟我们仍然用0和1来表示Alice想要发送的经典信息比特。我们引入一个“基”的概念。Alice随机选择用Z基还是X基来“包装”她的比特。对于接收方Bob他也随机选择用Z基还是X基来“拆开”这个包装即测量。只有当Alice和Bob“碰巧”选择了相同的基时Bob的测量结果才会确定性地等于Alice发送的比特。如果基不同Bob的测量结果将是完全随机的0或1各50%概率。2.2 协议四步走流程让我们一步步拆解第一步Alice的制备与发送Alice随机生成一个长的原始密钥比特串比如1000个比特每个比特是0或1。对于每一个比特Alice再随机选择一个基Z或X来编码它。这样每个比特都附带了一个“编码基”的信息。Alice将这些“用特定基编码后的量子态”发送给Bob。在模拟中“发送”这个动作就是把这个比特 编码基的对子传递给下一个处理环节可能经过Eve。第二步Bob的测量Bob收到模拟收到每一个量子态。对于每一个态Bob完全随机地选择一个测量基Z或X进行测量。测量规则如果Bob的测量基等于Alice的编码基那么Bob的测量结果100%等于Alice发送的原始比特。如果Bob的测量基不等于Alice的编码基那么Bob的测量结果是完全随机的有50%概率是050%概率是1与Alice发送的比特无关。Bob记录下他的测量结果和他所使用的测量基。第三步基矢比对筛选协议进行一段时间后Alice和Bob通过一个公开的经典信道比如打电话、发邮件这个信道可以被窃听但不会被篡改来互相公布他们每一步所使用的基注意只公布基不公布比特值。双方对比每一个位置上的基。只保留那些双方使用了相同基的位置。这些位置上Bob的测量结果理论上应该和Alice发送的比特完全一致。这些被保留下来的比特就构成了“原始密钥”或“筛选后密钥”。双方丢弃掉那些基不匹配的位置上的所有数据包括比特值。因为那些位置上的数据是随机的没有信息量。第四步窃听检测与纠错误码率估计为了检查是否有窃听Alice和Bob从筛选后的密钥中随机抽取一部分比如20%比特通过公开信道比对它们的值。如果信道完美且无窃听这部分比对的误码率应该为0或者接近0考虑到模拟中可能引入的极小误差。如果存在窃听者Eve她每次窃听测量都有概率引入错误。双方计算误码率。如果误码率低于一个预先约定的安全阈值例如11%则认为通信基本安全可以继续。如果误码率过高则说明信道不安全必须丢弃整段密钥重新开始。信息协商与隐私放大在确认误码率可接受后双方需要对剩下的密钥进行纠错使用一些经典纠错码协议确保双方的密钥完全一致然后进行隐私放大通过一个公开的哈希函数将密钥压缩以消除Eve可能通过窃听获得的部分信息。最终得到一段长度稍短但安全性极高的最终共享密钥。3. Python模拟实战从零构建BB84现在我们进入最核心的实操环节。我们将使用纯Python和NumPy库来构建整个模拟。确保你的环境中已安装NumPy (pip install numpy)。我们将模拟一个包含窃听者Eve的完整场景。3.1 环境准备与基础定义首先我们定义一些常量和基础函数。我们将用数字0代表Z基数字1代表X基。import numpy as np import hashlib # 用于后续可能的隐私放大演示 # 定义常量 Z_BASIS 0 # 标准基 X_BASIS 1 # 对角基3.2 模拟无窃听的信道我们先实现一个没有窃听者的理想情况帮助你建立信心。def generate_random_bits(length): 生成指定长度的随机比特序列0或1。 return np.random.randint(0, 2, sizelength) def generate_random_bases(length): 生成指定长度的随机基序列0代表Z基1代表X基。 return np.random.randint(0, 2, sizelength) def simulate_measurement(alice_bits, alice_bases, bob_bases): 模拟Bob的测量过程。 参数: alice_bits: Alice发送的比特数组 alice_bases: Alice使用的编码基数组 bob_bases: Bob使用的测量基数组 返回: bob_bits: Bob测量得到的比特数组 bob_bits np.empty_like(alice_bits) for i in range(len(alice_bits)): if alice_bases[i] bob_bases[i]: # 基相同完美测量 bob_bits[i] alice_bits[i] else: # 基不同测量结果完全随机 (0或1等概率) bob_bits[i] np.random.randint(0, 2) return bob_bits def sift_keys(alice_bits, alice_bases, bob_bits, bob_bases): 执行基矢比对筛选出双方基相同的比特。 参数: alice_bits, alice_bases, bob_bits, bob_bases 返回: sifted_alice_key, sifted_bob_key: 筛选后的密钥 # 找到基相同的位置索引 same_basis_indices np.where(alice_bases bob_bases)[0] # 提取这些位置上的比特 sifted_alice_key alice_bits[same_basis_indices] sifted_bob_key bob_bits[same_basis_indices] return sifted_alice_key, sifted_bob_key, same_basis_indices def estimate_error_rate(alice_key, bob_key, sample_ratio0.2): 从筛选后的密钥中随机抽样一部分进行比对估计误码率。 参数: alice_key, bob_key: 筛选后的密钥 sample_ratio: 抽样比例 返回: error_rate: 估计的误码率 sample_indices: 被抽样的索引用于后续从密钥中移除这些测试比特 key_length len(alice_key) sample_size int(key_length * sample_ratio) # 随机选择测试样本的索引 sample_indices np.random.choice(key_length, sizesample_size, replaceFalse) # 计算错误比特数 errors np.sum(alice_key[sample_indices] ! bob_key[sample_indices]) error_rate errors / sample_size if sample_size 0 else 0.0 return error_rate, sample_indices def remove_samples(key, indices_to_remove): 从密钥中移除指定索引的比特。 mask np.ones(len(key), dtypebool) mask[indices_to_remove] False return key[mask] # 主模拟流程无窃听 def simulate_bb84_no_eve(key_length1000): print( 开始模拟BB84协议无窃听 ) # 1. Alice准备 alice_raw_bits generate_random_bits(key_length) alice_bases generate_random_bases(key_length) print(fAlice生成了 {key_length} 个原始比特和随机基。) # 2. Bob准备测量基并“测量” bob_bases generate_random_bases(key_length) bob_raw_bits simulate_measurement(alice_raw_bits, alice_bases, bob_bases) print(fBob随机选择了测量基并完成了‘测量’。) # 3. 基矢比对筛选 sifted_alice_key, sifted_bob_key, _ sift_keys(alice_raw_bits, alice_bases, bob_raw_bits, bob_bases) print(f基矢比对后筛选出 {len(sifted_alice_key)} 个比特的原始密钥。) # 4. 窃听检测误码率估计 error_rate, test_indices estimate_error_rate(sifted_alice_key, sifted_bob_key) print(f抽样估计的误码率为: {error_rate:.4f} ({error_rate*100:.2f}%)) # 5. 移除测试比特得到最终密钥这里暂不进行纠错和隐私放大 final_alice_key remove_samples(sifted_alice_key, test_indices) final_bob_key remove_samples(sifted_bob_key, test_indices) print(f移除测试比特后最终共享密钥长度为: {len(final_alice_key)} 比特。) print(fAlice和Bob的最终密钥是否完全一致 {np.array_equal(final_alice_key, final_bob_key)}) print(*50) return final_alice_key, final_bob_key, error_rate # 运行一次无窃听模拟 final_alice_key, final_bob_key, error_rate simulate_bb84_no_eve(500)运行这段代码你会看到在无窃听情况下筛选后的密钥长度大约是原始长度的一半因为基匹配的概率是50%并且估计的误码率应该非常接近于0。最终Alice和Bob的密钥是完全一致的。3.3 引入窃听者Eve现在让我们加入一个窃听者Eve看看她是如何被发现的。Eve的策略很简单拦截Alice发送给Bob的量子态自己进行测量同样需要随机选择基然后将她测量后的态或者根据测量结果重新制备的态发送给Bob。这个过程被称为“拦截-重发”攻击。def eavesdrop_intercept_resend(alice_bits, alice_bases): 模拟窃听者Eve的‘拦截-重发’攻击。 Eve拦截量子态随机选择基进行测量然后将她认为的态发送给Bob。 参数: alice_bits: Alice的原始比特 alice_bases: Alice的编码基 返回: eve_bits: Eve测量得到的比特 eve_bases: Eve随机选择的测量基 resend_bits: Eve重发给Bob的比特她测量得到的结果 resend_bases: Eve重发给Bob的基她测量时使用的基 length len(alice_bits) eve_bases generate_random_bases(length) # Eve随机选择测量基 # Eve进行测量规则与Bob相同 eve_bits simulate_measurement(alice_bits, alice_bases, eve_bases) # Eve将她测量得到的比特和**她使用的基**发送给Bob。 # 注意在真实量子世界中Eve发送的是她测量后坍缩的量子态。 # 在我们的模拟中我们用eve_bits, eve_bases这个对子来表示她“重发”的东西。 # 对于Bob来说他收到的“发送方”变成了Eve。 return eve_bits, eve_bases def simulate_bb84_with_eve(key_length1000, eve_presenceTrue): 模拟包含窃听者Eve的BB84协议。 eve_presence: 布尔值控制Eve是否存在。 print(f 开始模拟BB84协议Eve窃听: {eve_presence} ) # 1. Alice准备 alice_raw_bits generate_random_bits(key_length) alice_bases generate_random_bases(key_length) # 2. 信道传输可能经过Eve if eve_presence: print(警告信道中存在窃听者Eve) # Eve进行拦截-重发攻击 eve_bits, eve_bases eavesdrop_intercept_resend(alice_raw_bits, alice_bases) # Bob实际接收到的是Eve重发的东西 received_bits, received_bases eve_bits, eve_bases else: # 无窃听Bob直接收到Alice的 received_bits, received_bases alice_raw_bits, alice_bases # 3. Bob的测量 bob_bases generate_random_bases(key_length) # Bob对“收到”的态进行测量。注意此时发送方是Eve或有窃听或Alice无窃听。 bob_raw_bits simulate_measurement(received_bits, received_bases, bob_bases) # 4. 基矢比对Alice和Bob公开讨论基 sifted_alice_key, sifted_bob_key, _ sift_keys(alice_raw_bits, alice_bases, bob_raw_bits, bob_bases) print(f基矢比对后筛选出 {len(sifted_alice_key)} 个比特的原始密钥。) # 5. 窃听检测 error_rate, test_indices estimate_error_rate(sifted_alice_key, sifted_bob_key) print(f抽样估计的误码率为: {error_rate:.4f} ({error_rate*100:.2f}%)) # 6. 安全判断与后续处理 security_threshold 0.11 # 一个典型的安全误码率阈值例如11% if error_rate security_threshold: print(f误码率 {error_rate*100:.2f}% 高于安全阈值 {security_threshold*100}%) print(检测到潜在窃听本次通信密钥不安全应被丢弃。) final_alice_key final_bob_key np.array([], dtypeint) else: print(f误码率 {error_rate*100:.2f}% 低于安全阈值通信基本安全。) # 移除测试比特得到最终密钥模拟未做纠错和隐私放大 final_alice_key remove_samples(sifted_alice_key, test_indices) final_bob_key remove_samples(sifted_bob_key, test_indices) print(f移除测试比特后最终共享密钥长度为: {len(final_alice_key)} 比特。) # 注意即使误码率低于阈值由于Eve的窃听Alice和Bob的密钥也可能不一致。 # 这需要通过后续的“信息协商”纠错步骤来解决。 print(fAlice和Bob的最终密钥是否完全一致 {np.array_equal(final_alice_key, final_bob_key)}) print(*50) return final_alice_key, final_bob_key, error_rate # 运行模拟有窃听 vs 无窃听 print(\n模拟1: 无窃听者) key_a1, key_b1, err1 simulate_bb84_with_eve(1000, eve_presenceFalse) print(\n模拟2: 存在窃听者Eve拦截-重发攻击) key_a2, key_b2, err2 simulate_bb84_with_eve(1000, eve_presenceTrue)多次运行上面的代码你会发现无窃听时误码率极低理论上应为0模拟中因随机性可能有个别错误但比例极低Alice和Bob的最终密钥一致。有窃听时误码率会显著升高大约在25%左右这正是Eve被暴露的关键。为什么是25%我们可以简单计算一下Eve有50%的概率选错基与Alice不同当她选错基时她的测量会随机扰动比特导致她发给Bob的态有50%的概率是错的。而对于Bob他也有50%的概率选对基与Eve相同。综合起来Eve引入错误的概率是 0.5 * 0.5 0.25。所以Alice和Bob在比对测试比特时会发现高达25%的错误率远高于安全阈值从而断定信道不安全。3.4 可视化与深入分析为了更直观地理解整个过程我们可以添加一些简单的统计和可视化使用matplotlib需安装pip install matplotlib。import matplotlib.pyplot as plt def run_multiple_simulations(num_simulations100, key_length200): 多次运行模拟统计有/无窃听下的误码率分布。 errors_no_eve [] errors_with_eve [] for i in range(num_simulations): _, _, err_no simulate_bb84_with_eve(key_length, eve_presenceFalse) errors_no_eve.append(err_no) _, _, err_with simulate_bb84_with_eve(key_length, eve_presenceTrue) errors_with_eve.append(err_with) # 绘制误码率分布直方图 plt.figure(figsize(10, 6)) plt.hist(errors_no_eve, bins20, alpha0.7, label无窃听 (No Eve), colorgreen, edgecolorblack) plt.hist(errors_with_eve, bins20, alpha0.7, label有窃听 (With Eve), colorred, edgecolorblack) plt.axvline(x0.11, colorblue, linestyle--, linewidth2, labelf安全阈值 (~11%)) plt.xlabel(误码率 (QBER)) plt.ylabel(出现次数) plt.title(fBB84协议误码率分布模拟 ({num_simulations} 次运行, 密钥长度{key_length})) plt.legend() plt.grid(True, alpha0.3) plt.show() print(f无窃听平均误码率: {np.mean(errors_no_eve):.4f}) print(f有窃听平均误码率: {np.mean(errors_with_eve):.4f} (理论值~0.25)) # 运行100次模拟观察误码率分布 run_multiple_simulations(num_simulations100, key_length200)这张图会清晰地展示在有Eve窃听时误码率会聚集在25%附近而无窃听时误码率则集中在0附近。两者被一条安全阈值线例如11%明显分开直观地证明了BB84协议检测窃听的能力。4. 关键参数、问题排查与进阶思考通过上面的模拟你已经掌握了BB84的核心流程。但在实际理解或未来进行更复杂的模拟时以下几个要点和常见问题需要特别注意。4.1 核心参数与安全阈值筛选效率由于基匹配概率是50%所以原始密钥经过基矢比对后长度大约会减半。这是协议固有的效率损失。窃听引入的误码率对于“拦截-重发”攻击理论误码率是25%。这是最经典的一种攻击。实际上Eve可能有更聪明的攻击策略如相干攻击但BB84协议的安全性分析证明只要误码率低于某个阈值总可以通过隐私放大提取出安全密钥。这个阈值取决于具体的安全证明和协议变种对于BB84协议在理想单光子源下这个阈值大约是11%更精确的值是约12.6%或更低取决于模型。在我们的模拟中我们使用了11%作为判断依据。最终密钥生成率并非所有筛选后的比特都能成为最终密钥。我们需要扣除测试比特用于估计误码率的部分例如20%。纠错开销为了纠正剩余比特中的错误双方需要交换一些纠错信息通过公开信道这部分信息会被窃听者听到因此需要从密钥长度中扣除相应的信息量。隐私放大压缩为了消除Eve可能拥有的任何信息需要将密钥通过一个通用哈希函数压缩到一个更短的长度。压缩的比例与估计的误码率有关误码率越高压缩得越厉害。4.2 模拟中常见问题与排查误码率不为零无窃听时在无窃听的模拟中由于我们使用了随机数并且“测量”函数在基不匹配时生成随机比特这本身不会在Alice和Bob之间引入错误因为筛选时只保留基匹配的比特。你看到的极低非零误码率可能源于极小的浮点误差或随机数生成的极端情况。如果误码率显著高于0比如1%请检查sift_keys函数逻辑确保它正确地只筛选了alice_bases bob_bases的位置并且simulate_measurement函数在基匹配时是直接复制比特值。有窃听时误码率远低于25%确保你的Eve模拟函数eavesdrop_intercept_resend工作正确。关键点是Eve测量后发送给Bob的是她测量得到的比特和她测量时使用的基。Bob随后会根据他自己随机选择的基对Eve发来的这个比特基对进行“测量”。这个逻辑链条必须准确。最终密钥不一致即使在误码率低于阈值的情况下由于信道噪声我们模拟中暂未加入或Eve的窃听筛选后密钥中仍可能存在未被测试样本检测到的错误比特。这就是为什么真实的QKD协议在误码率检测后必须进行信息协商纠错步骤例如使用Cascade或LDPC码等协议确保双方密钥完全一致。我们的模拟省略了这一步所以最终密钥可能不一致。要模拟一个完整的流程你需要在此之后添加纠错模块。随机数生成器的质量numpy.random.randint对于教学模拟足够了。但在对安全性要求极高的真实场景或加密演示中需要使用密码学安全的随机数生成器如secrets模块。4.3 从模拟到现实局限性认知我们的Python模拟极大地简化了现实理想单光子源我们假设Alice每次都能完美地发送一个量子比特。现实中制备理想的单光子源非常困难弱相干光源激光器衰减是更常用的方案但这会引入光子数分离攻击等安全漏洞需要更复杂的协议如诱骗态协议来弥补。理想探测器我们假设Bob的探测器是完美的效率100%无暗计数。现实中的单光子探测器有效率限制、暗计数和后脉冲等噪声。无损耗信道我们假设量子态在信道中传输没有损耗。真实的光纤或自由空间信道有衰减随着距离增加信号指数衰减这严重限制了成码率和传输距离。简单的窃听模型我们只模拟了“拦截-重发”攻击。现实中Eve可以使用更复杂的量子攻击手段。BB84协议的安全性证明可以抵御所有量子攻击但我们的简化模拟无法体现这一点。尽管有这些简化这个模拟项目已经成功地揭示了BB84协议最精髓的思想利用非正交基和量子测量的扰动特性来检测窃听。它是你深入量子密码学殿堂的一块绝佳敲门砖。5. 项目扩展与更深度的模拟挑战如果你已经成功完成了基础模拟并想进一步挑战这里有几个方向可以深入实现纠错与隐私放大纠错实现一个简单的奇偶校验纠错协议如Cascade协议的简化版。双方将密钥分成块比对块的奇偶性如果不同则二分查找错误比特。这个过程需要在公开信道交换一些信息因此会泄露部分信息量。隐私放大在纠错后双方共享一个一致的密钥串但Eve可能拥有部分信息。使用一个公开的通用哈希函数如SHA-256对密钥进行压缩。例如如果估计Eve最多掌握了密钥的t比特信息那么就将n比特的密钥哈希成n - t - s比特的输出其中s是安全参数。这样就能以极高的概率消除Eve的知识。模拟非理想器件的影响信道损耗在simulate_measurement函数中引入一个损耗概率loss_prob。对于每个量子态以该概率让Bob“探测不到”任何信号从而没有测量结果。这会影响筛选后的密钥长度和成码率。探测器效率与暗计数模拟Bob的探测器不是100%效率。同时即使没有信号输入探测器也有一定概率产生一个“暗计数”误触发。这会在无窃听时引入本底误码率。模拟诱骗态BB84协议这是现实中最常用的QKD协议之一用于对抗使用弱相干光源带来的安全漏洞。你需要模拟Alice随机发送三种不同强度的光脉冲信号态用于生成密钥、诱骗态用于探测光子数分离攻击和真空态。协议的后处理分析会更加复杂需要估计单光子成分的贡献。可视化交互界面使用tkinter或streamlit创建一个简单的图形界面让用户可以调整参数如密钥长度、窃听开关、信道损耗等并实时看到密钥生成过程、误码率变化和最终密钥的一致性对比。完成这个Python模拟项目你收获的不仅仅是一段代码而是对量子密码学核心思想——利用物理定律而非计算复杂度来保障安全——的一次深刻实践。下次当你听到“量子密钥分发”时你脑海中浮现的将不再是晦涩的公式而是一行行清晰的代码逻辑以及那在随机基比对中浮现出的、确保安全的25%误码率警戒线。这正是动手实践的魅力所在。