本节摘要:BFV 方案把明文规定为"对一个小模数取余的整数",用缩放因子的取整编码把它藏进密文系数的高位,解密就是一次带取整的内积。本节讲清编码结构、解密的取整条件与明文模数的定量边界(能表示多大整数、能撑多少层乘法),给出四千零九十六维度下的完整尺寸账单与一段可直接套用的加密运算代码骨架,并解释打包槽位如何摊销成本。
BGV 解决了"怎么算得动",BFV 解决的是"算的东西长什么样"。它的设计目标很朴素:让密文里的明文语义就是整数对一个小模数取余——计数、求和、比较的中间量都能直接放进去。实现手段是一次"放大再取整"的编码:设系数模数为大模数,明文模数为小模数,缩放因子取两者之比的整数部分;加密时把明文乘上缩放因子,让明文信息稳稳落在系数的高位段,噪声留在低位段。解密就是计算密文与私钥的内积、对大模数取余、再除以缩放因子取整——只要噪声小于缩放因子的一半,取整就把噪声整体抹掉,明文完整落出。
这条"噪声小于缩放因子一半"的取整条件,是 BFV 全部参数权衡的出发点。它给出一个三方拉扯:明文模数越大,能表示的整数越大(位数约等于明文模数的比特数),但缩放因子越小、噪声容许区间越小,可支撑的乘法层数越少;系数模数越大,噪声预算越厚,但安全强度越低(上一章的比值规则),维度必须跟着加大。实用配置在这三点之间找平衡,一组常用的社区标准组合是:维度四千零九十六、系数模数约一百零九比特、明文模数二十比特——能表示百万量级的整数,支撑两三层纯乘法或配合模交换五层上下。
下面这段骨架代码展示典型库的调用形态(以流行的 Python 封装库接口为参考,概念通用):
# BFV 加密整数向量运算的调用骨架(接口形态参考主流 Python 封装库) import tenseal as ts # 建立上下文:维度 4096,明文模数取二十比特素数 ctx = ts.context( ts.SCHEME_TYPE.BFV, poly_modulus_degree=4096, plain_modulus=1032193, # 约二十比特的素数 ) sk = ctx.secret_key() # 私钥留在本地 vec1 = [60000, 40000, 9000, 100] * 512 # 打包 2048 个槽 vec2 = [3, 3, 3, 3] * 512 ct1 = ts.bfv_vector(ctx, vec1) # 加密:一个密文装下全部 2048 个整数 ct2 = ts.bfv_vector(ctx, vec2) ct_sum = ct1 + ct2 # 密文加法:槽内逐位相加 ct_prod = ct1 * ct2 # 密文乘法:槽内逐位相乘(需要重线性化密钥) print(ct_sum.decrypt(sk)[:4]) # [60003, 40003, 9003, 103] print(ct_prod.decrypt(sk)[:4]) # [180000, 120000, 27000, 300]
注意两件事。其一,一个密文装下了两千零四十八个整数槽(维度之半),加法与乘法都是槽内逐位进行——这就是打包,一个密文运算等于两千多次明文运算。其二,所有运算在明文模数的世界里进行:槽内结果超过约一百零三万就会回绕。上一章 Paillier 算例里的"二百六十变三十九"在这里以更大规模重演,工程上的标准对策是把需要精确表示的量预先缩放编码,或选更大的明文模数。

账单里最值得咀嚼的是最后一行:一百一十千字节的密文装五千字节的明文,膨胀二十二倍——听起来昂贵,但换个角度算:两千零四十八个槽摊下来,每个槽的有效膨胀只有二十二倍,而如果不打包(第一代的一比特时代),膨胀是六位数。打包的原理是中国剩余定理:把明文多项式环里"对多个互素模数取余"的结构对齐到密文的多项式结构上,槽与槽天然独立,加乘互不串扰。它带来两个推论:其一,密文运算的摊销成本骤降,这是同态加密从"演示"走向"负载"的第一张门票;其二,槽间通信成为新问题——想做向量旋转、矩阵乘向量这类跨槽运算,需要旋转密钥与对角线技术,第四章会展开。
BFV 与 BGV 常被并称为一个家族,区别主要在编码姿态:BGV 把明文放在特定编码位置逐层折算,BFV 用大缩放因子一次放大。两者在近似配置下可以互相模拟,工程库通常同时提供。选择建议很简单:整数精确语义(计数、金额分单位、求交哈希桶计数)优先 BFV;浮点负载直接看下一节的 CKKS;比特级灵活控制等本家族最后一节。
⚠️ 常见坑:明文模数选成合数时,槽内运算在零因子处会出现"乘出零"的诡异现象(不可逆元素的乘法塌缩);工程实践一律取素数,或在明确知道自己在做什么时才用二的幂(换取取模运算加速,代价是放弃乘法可逆性)。
给 BFV 补几条写代码时会反复用到的实战直觉。其一,明文编码的空间会计:批量编码把一个多项式槽位摊成成对的复数槽,槽位数量等于多项式次数除以二——这意味着一次乘法的有效并行度先天由参数决定,想扩大批量先升参数,而升参数的代价是全链路算力与内存,槽位会计是所有 CKKS 与 BFV 工程的第一课。其二,缩放因子的守恒:BFV 的明文模数与缩放因子要在乘法后保持整数世界的自洽,跨参数的乘法要靠重缩放找回平衡——把它想成浮点数的对阶,工程师的直觉立刻就通了。其三,明文空间的选择:模一个素数适合整数与布尔编码(比特切片),模二的三十二次方适合批量整数,选择错了后面的每一步都别扭。其四,与 CKKS 的分工口诀:要精确整数走 BFV,要近似实数走 CKKS——两者共享大部分参数设施,工程上常在一套系统里按算子切换。带着这四条直觉读任何 BFV 的示例代码,你会发现自己第一次真正看懂了那些参数为什么那么取。