该文章翻译自CKKS EXPLAINED, PART 2: FULL ENCODING AND DECODING,主要介绍CKKS方案中是如何编码和解码的(如何将复数向量转成整数多项式,以及如何求逆运算)

介绍

在前一篇文章《CKKS:第1部分,普通编码和解码》中,我们了解到,要在CKKS方案中实现加密复数向量的计算,必须首先构建一个编码和解码,将复数向量转换为多项式。

这个编/解码步骤是必要的,因为加密、解密和其他机制在多项式环上工作。因此,有必要找到一种将复数向量转换成多项式的方法。

我们还了解到,通过使用标准嵌入σ,即通过在\(X^N+1\)的根上计算多项式来解码多项式,我们能够在\(ℂ^N —>ℂ[X] /(X^N+1)\)。然而,因为我们希望我们的编码器输出多项式\(ℤ[X] /(X^N+1)\),为了利用多项式整数环的结构,我们需要修改前一个文章中的普通编码器,以便能够输出“右环的多项式”。(不太懂,应该是能够输出不带i的多项式)

因此,在本文中,我们将探讨如何实现原始论文《Homomorphic Encryption for Arithmetic of Approximate Numbers》中使用的编码和解码,这将是我们从头开始实现CKK的第一步。

CKKS编码

与前一篇文章的不同之处在于,编码多项式的明文空间现在是\(R=Z\left[ X \right]/X^{N}+1\)而不是\(\mbox{C}\left[ X \right]/X^{N}+1\),所以编码值多项式的系数必须是整数系数,然而当我们把一个向量编码成\(C^N\)时,我们已经了解到它的编码不一定是整数系数(有的是复数系数)。

为了解决这个问题,让我们来看看标准嵌入σ在R上的图像。

因为多项式R上是整数系数,即实数系数,我们在复数根上计算它们,其中一半是另一半的共轭项(参见上一章),我们有\(\sigma \left( R \right)\in H=z\in \mbox{C}^{N}:z_{j}=\neg z_{-j}\)。

像上一章的M=8:

从上面的照片看,\(\omega ^{1}=-\omega ^{7}\; and\; \omega ^{3}=-\omega ^{5}\),一般来说,我们用\(X^N+1\)的根计算一个多项式,对于任何多项式\(m\left( x \right)\in R,m\left( \xi ^{j} \right)=-m\left( \xi ^{-j} \right)=m\left( -\xi ^{-j} \right)\),因此,\(σ(R)\)上的任何元素实际上是在一个N/2的空间中,而不是N。因此,如果我们在CKKS中编码向量时使用大小为N/2的复数向量,我们需要通过复制其共轭根的来扩展它的另一半。

这个操作需要将\(ℍ\)投射到\(ℂ^{N/2}\),在CKKS论文中该操作称为π。请注意,这也定义了同构。

现在我们可以从\(z∈ℂ^{N/2}\)开始,用\(π^{−1}\)展开(注意π是映射,\(π^{−1}\)是扩展),我们可以得到\(π^{−1}(z)∈ℍ\).

我们面临的一个问题是,我们不能直接使用\(σ: R=ℤ[X]/(X^N+1)→σ(R)⊆ℍ\),因为ℍ 不一定在σ(R)中. σ确实定义了同构,但仅从R到σ(R). 为了证明σ(R)不等于ℍ, 你可以注意到R是可数的(??)因此σ(R) 也是,但是ℍ 不是,因为它与ℂ同构。

这个细节很重要,因为这意味着我们必须找到一种在σ(R)上的映射\(π^{−1}(z)\),为此,我们将使用一种称为“coordinate-wise random rounding, 坐标随机舍入”的技术,该技术在 A Toolkit for Ring-LWE Cryptography中定义。这种舍入技术允许将实数x舍入到⌊x⌋ 或⌊x⌋+1,我们将不深入讨论这个算法的细节,尽管我们将实现它。

想法很简单,有一个正交基ℤ:\(1,X,....,X^{N−1}\),假设σ是同构的,σ(R) 有一个正交基: \(β=(b1,b2,…,bN)=(σ(1),σ(X),...,σ(X^{N−1}))\). 因此,对于任何z∈ℍ, 我们将简单地将其投射到β上:$$z=\sum_{i=1}^{N}{z_{i}b_{i},z_{i}=\frac{<z,b_{i}>}{\left| \left| b_{i} \right| \right|^{2}}}$$

因为基要么是正交的,要么不是正交的,所以\(z_{I} =\frac{<z,b_{i}>}{\left| \left| b_{i} \right| \right|^{2}}\), 请注意,我们在这里使用的是hermitian积(厄米乘积):\(<x,y>=\sum_{i=1}^{N}{x_{i}\left( -y_{i} \right)}\), 厄米乘积给出了真正的输出,因为我们它是在ℍ上, 你可以通过计算来证明,或者注意到,你可以在ℍ 和\(ℝ^N\)之间找到同构关系,所以在ℍ上的内积将是实际的输出。

最后,一旦我们有了\(z_i\),我们只需要使用“coordinate-wise random rounding, 坐标随机舍入”将它们随机舍入到更高或更低的最接近整数。这样我们就得到了一个多项式,它的基坐标为整数\((σ(1),σ(X),...,σ(X^N)−1) )\),因此该多项式将属于σ(R) 。

一旦我们有了映射关系σ(R), 我们可以用\(σ^{−1}\)的输出,这正是我们想要的!

最后一个细节:因为舍入可能会破坏一些重要的数字,我们实际上需要在编码中乘以Δ>0,在解码中除以Δ以保持1/Δ的精度。要了解其工作原理,请假设您想要将x=1.4四舍五入,但不想将其四舍五入到最接近的整数,而是要将其四舍五入到最接近的0.25倍,以保持一定的精度。然后,您需要设置刻度Δ=4,其精度为1Δ=0.25。的确,现在当我们\(\left\lfloor \Delta x \right\rfloor=\left\lfloor 4\cdot 1.4 \right\rfloor=\left\lfloor 5.6 \right\rfloor=6\)一旦我们将其除以相同的Δ,我们得到1.5,这实际上是x=1.4的最接近倍数0.25。

所以最后的编码过程是:

以\(z∈ℂ^{N/2}\)为例

将其扩展到\(π^{-1}∈H\);

将其乘以Δ以保证精度

映射:\(\left\lfloor \Delta \pi ^{-1}\left( z \right) \right\rfloor_{\sigma \left( R \right)}\in \sigma \left( R \right)\)

使用σ:\(m\left( x \right)=\sigma ^{-1}\left( \left\lfloor \Delta \pi ^{-1}\left( z \right) \right\rfloor_{\sigma \left( R \right)} \right)\in R\)对其进行编码

解码过程要简单得多,从多项式m(X)我们只得到\(z=π∘σ(Δ^{−1}.m)\)。

实现

现在我们终于看到了完整的CKKS编码和解码是如何工作的,让我们来实现它吧!我们将使用之前用于Vanilla编码器和解码器的代码。代码可以在这里

在本文的其余部分中,让我们重构并构建我们在上一篇文章中创建的CKKSEncoder类。在笔记本电脑环境中,我们不需要每次添加或更改方法时都重新定义类,而只需使用Fastai的fastcore包中的patch_to。这使我们能够对已经定义的对象进行修补。使用patch_to纯粹是为了方便,您可以使用添加的方法在每个单元重新定义CKKSEncoder。

# !pip3 install fastcore

from fastcore.foundation import patch_to
@patch_to(CKKSEncoder)
def pi(self, z: np.array) -> np.array:
"""Projects a vector of H into C^{N/2}.""" N = self.M // 4
return z[:N] @patch_to(CKKSEncoder)
def pi_inverse(self, z: np.array) -> np.array:
"""Expands a vector of C^{N/2} by expanding it with its
complex conjugate.""" z_conjugate = z[::-1]
z_conjugate = [np.conjugate(x) for x in z_conjugate]
return np.concatenate([z, z_conjugate]) # We can now initialize our encoder with the added methods
encoder = CKKSEncoder(M)
z = np.array([0,1])
encoder.pi_inverse(z)

输出:array([0, 1, 1, 0])

@patch_to(CKKSEncoder)
def create_sigma_R_basis(self):
"""Creates the basis (sigma(1), sigma(X), ..., sigma(X** N-1)).""" self.sigma_R_basis = np.array(self.vandermonde(self.xi, self.M)).T @patch_to(CKKSEncoder)
def __init__(self, M):
"""Initialize with the basis"""
self.xi = np.exp(2 * np.pi * 1j / M)
self.M = M
self.create_sigma_R_basis() encoder = CKKSEncoder(M)

我们现在可以看看基:\(\sigma \left( 1 \right),\sigma \left( X \right),\sigma \left( X^{2} \right),\sigma \left( X^{3} \right)\)

encoder.sigma_R_basis

\(array([[ 1.00000000e+00+0.j, 1.00000000e+00+0.j,1.00000000e+00+0.j, 1.00000000e+00+0.j],[ 7.07106781e-01+0.70710678j, -7.07106781e-01+0.70710678j, -7.07106781e-01-0.70710678j, 7.07106781e-01-0.70710678j],[ 2.22044605e-16+1.j, -4.44089210e-16-1.j, 1.11022302e-15+1.j, -1.38777878e-15-1.j], [-7.07106781e-01+0.70710678j, 7.07106781e-01+0.70710678j,7.07106781e-01-0.70710678j, -7.07106781e-01-0.70710678j]])\)

这里我们将检查ℤ(σ(1)、σ(X)、σ(X2)、σ(X3))的元素是否被编码为整数多项式。

# Here we simply take a vector whose coordinates are (1,1,1,1) in the lattice basis
coordinates = [1,1,1,1] b = np.matmul(encoder.sigma_R_basis.T, coordinates)
b

\(array([1.+2.41421356j, 1.+0.41421356j, 1.-0.41421356j, 1.-2.41421356j])\)

现在我们可以检查它是否编码为整数多项式。

p = encoder.sigma_inverse(b)
p

\(x↦(1+2.220446049250313e-16j)+((1+0j))x+((0.9999999999999998+2.7755575615628716e-17j))x^2+((1+2.220446049250313e-16j))x^3\)

@patch_to(CKKSEncoder)
def compute_basis_coordinates(self, z):
"""Computes the coordinates of a vector with respect to the orthogonal lattice basis."""
output = np.array([np.real(np.vdot(z, b) / np.vdot(b,b)) for b in self.sigma_R_basis])
return output def round_coordinates(coordinates):
"""Gives the integral rest."""
coordinates = coordinates - np.floor(coordinates)
return coordinates def coordinate_wise_random_rounding(coordinates):
"""Rounds coordinates randonmly."""
r = round_coordinates(coordinates)
f = np.array([np.random.choice([c, c-1], 1, p=[1-c, c]) for c in r]).reshape(-1) rounded_coordinates = coordinates - f
rounded_coordinates = [int(coeff) for coeff in rounded_coordinates]
return rounded_coordinates @patch_to(CKKSEncoder)
def sigma_R_discretization(self, z):
"""Projects a vector on the lattice using coordinate wise random rounding."""
coordinates = self.compute_basis_coordinates(z) rounded_coordinates = coordinate_wise_random_rounding(coordinates)
y = np.matmul(self.sigma_R_basis.T, rounded_coordinates)
return y encoder = CKKSEncoder(M)

最后,因为在舍入步骤中可能会损失精度,所以我们使用刻度参数Δ来达到固定的精度水平。

@patch_to(CKKSEncoder)
def __init__(self, M:int, scale:float):
"""Initializes with scale."""
self.xi = np.exp(2 * np.pi * 1j / M)
self.M = M
self.create_sigma_R_basis()
self.scale = scale @patch_to(CKKSEncoder)
def encode(self, z: np.array) -> Polynomial:
"""Encodes a vector by expanding it first to H,
scale it, project it on the lattice of sigma(R), and performs
sigma inverse.
"""
pi_z = self.pi_inverse(z)
scaled_pi_z = self.scale * pi_z
rounded_scale_pi_zi = self.sigma_R_discretization(scaled_pi_z)
p = self.sigma_inverse(rounded_scale_pi_zi) # We round it afterwards due to numerical imprecision
coef = np.round(np.real(p.coef)).astype(int)
p = Polynomial(coef)
return p @patch_to(CKKSEncoder)
def decode(self, p: Polynomial) -> np.array:
"""Decodes a polynomial by removing the scale,
evaluating on the roots, and project it on C^(N/2)"""
rescaled_p = p / self.scale
z = self.sigma(rescaled_p)
pi_z = self.pi(z)
return pi_z scale = 64 encoder = CKKSEncoder(M, scale)

我们现在可以立刻看到它,CKKS使用的完整编码器:

z = np.array([3 +4j, 2 - 1j])
z

输出:array([3.+4.j, 2.-1.j])

现在我们有一个整数多项式作为我们的编码。

p = encoder.encode(z)
p

\(x↦160.0+90.0x+160.0x^2+45.0x^3\)

而且它实际上解码得很好!

encoder.decode(p)
array([2.99718446+3.99155337j, 2.00281554-1.00844663j])

我希望你们喜欢这篇关于将复数向量编码成多项式进行同态加密的小介绍。我们将在下面的文章中进一步深入探讨这一点,敬请期待!

CKKS Part2: CKKS的编码和解码的更多相关文章

  1. CKKS Part3: CKKS的加密和解密

    本篇文章翻译于CKKS EXPLAINED, PART 3: ENCRYPTION AND DECRYPTION,主要介绍CKKS方案的加密和解密. 介绍 在上一篇 CKKS Part2: CKKS的 ...

  2. CKKS Part5: CKKS的重缩放

    本文翻译于 CKKS EXPLAINED, PART 5: RESCALING,主要介绍CKKS方案中最重要的技术- rescaling,重缩放技术 介绍 在CKKS的前一篇文章 CKKS Part4 ...

  3. java编码原理,java编码和解码问题

    java的编码方式原理 java的JVM的缺省编码方式由系统的“本地语言环境”设置确定,和操作系统的类型无关 . 在JAVA源文件-->JAVAC-->Class-->Java--& ...

  4. IO(六)--- 编码和解码

    编码: 把看得懂的字符变成看不懂码值这个过程我们称作为编码. 解码: 把码值查找对应的字符,我们把这个过程称作为解码. 注意: 以后编码与解码一般我们都使用统一的码表.否则非常容易出乱码. 常用码表: ...

  5. RapidJSON 代码剖析(三):Unicode 的编码与解码

    根据 RFC-7159: 8.1 Character Encoding JSON text SHALL be encoded in UTF-8, UTF-16, or UTF-32. The defa ...

  6. BASE64编码和解码(VC源代码) 并 内存加载 CImage 图像

      BASE64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本.完整的BASE64定义可见 RFC1421和 RFC2045.编码后的数据比原始数据略长,为原来的4/3.在电子 ...

  7. Android 中的编码与解码

    前言:今天遇到一个问题,一个用户在登录的时候,出现登录失败.但是其他用户登录都是正常的,经过调试发现登录失败的用户的密码中有两个特殊字符: * .#  . 特殊符号在提交表单的时候,出现了编码不一样的 ...

  8. base64编码、解码的C语言实现

    转自:http://www.cnblogs.com/yejianfei/archive/2013/04/06/3002838.html base64是一种基于64个可打印字符来表示二进制数据的表示方法 ...

  9. android Java BASE64编码和解码二:图片的编码和解码

    1.准备工作 (1)在项目中集成 Base64 代码,集成方法见第一篇博文:android Java BASE64编码和解码一:基础 (2)添加 ImgHelper 工具类 package com.a ...

随机推荐

  1. YAPTCHA(hdu2973)

    YAPTCHA Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  2. Web前端面试题整合,持续更新【可以收藏】

    饭后闲来无事,把这几年带学生用的一些面试题整合一下,供上!拿走,不客气!应付一般公司的二面基本上是够用了.祝你早日拿到心仪的offer. css相关 1. 万能居中 1.margin: 0 auto; ...

  3. uniapp云打包之后华为手机推送角标不显示(有推送没角标)

    小米手机上有角标,华为和OPPO没有角标 解决方法: 华为手机添加权限(可通过反编译或者离线打包添加) < uses - permission android:name="com.hu ...

  4. Linux磁盘分区与挂载

    原理介绍 在Linux世界中,一切皆目录,每一块硬盘分区对应Linux的一个目录,所以我们可以通过管理目录来管理硬盘分区,而将硬盘分区与文件目录关联的操作就成为"挂载"[mount ...

  5. 基于Spring MVC + Spring + MyBatis的【图书资源管理系统】

    资源下载:https://download.csdn.net/download/weixin_44893902/45598347 练习点设计:模糊查询.删除.新增 一.语言和环境 实现语言:JAVA语 ...

  6. Windows10中同时安装MySQL5和MySQL8

    Windows10中同时安装MySQL5和MySQL8 同时安装的话,在执行mysql install要注意加名字,比如:mysqld --install MYSQL5 MySQL-5.5.54 ZI ...

  7. [数据结构]FHQ-Treap

    前言(个人评价FHQ-Treap) 这是一个巨佬(名叫范浩强)在冬令营交流的时候提出的数据结构(FHQ:\(\text{你干嘛非要旋转呢?Think Functional!}\))(可以看出FHQ大佬 ...

  8. .NET 微服务——CI/CD(3):镜像自动分发

    如何通过Jenkins完成镜像分发?基本做法是:打包镜像→上传镜像到仓库→脚本分发.镜像仓库也有很多,比如docker hub.Harbor等,今天这一篇讲一下基于阿里云镜像仓库的操作. 首先,准备一 ...

  9. 字母 Letters

    D. 字母 Letters 内存限制:256 MiB 时间限制:500 ms 标准输入输出 题目类型:传统 评测方式:文本比较 题目描述 给定两个长度相同且由大写英文字母组成的字符串 A 和 B,保证 ...

  10. Redis OM .NET Redis对象映射框架

    Redis OM Redis OM 是 Redis 官方推出的对象映射框架,即:Object Mapping.让开发人员更简单.方便的操作 Redis 数据.Redis 存储的数据抽象为对象映射,支持 ...