技术探秘:在RISC Zero中验证FHE——由隐藏到证明:FHE验证的ZK路径(1)

1. 引言

开源代码实现见:

  • https://github.com/hashcloak/fhe_risc0_zkvm(Rust)
  • https://github.com/weikengchen/vfhe-profiled(Rust)
  • https://github.com/l2iterative/vfhe0(Rust)

L2IV Research团队近期尝试用ZKP来验证FHE,原因在于如下2大应用场景的出现:

  • 1)fhEVM的链下计算:Fhenix和Inco,正基于Zama的fhEVM构建在L1链上具有全同态加密增强的EVM。其中fhEVM全称为fully homomorphic Ethereum Virtual Machine (EVM)。链下计算,使得L1 Validators无需rerun FHE计算,从而具备可扩展性,同时,可能可进一步隐藏函数,从而具备隐私性。
  • 2)FHE mining:受Aleo的proof of succinct work (PoSW) 和 ZPrize 启发,认为FHE mining是一个值得注意的未来方向,以鼓励FHE的ASIC制造,并激励FHE矿工成为fhEVM网络的Validators。FHE mining的核心任务是为FHE上下文中的PoSW开发ZKP系统。

FHE方案有多种,L2IV Research团队尤其关注Zama所使用TFHE。其实现的TFHE使用模 p = 2 64 p=2^{64} p=264——其可在现代CPU和其它硬件平台上高效计算。L2IV Research团队对验证FHE的兴趣源于其在fhEVM中的直接适用性。

但是,使用模 p = 2 64 p=2^{64} p=264,对ZKP来说挑战不小,因仅有有限的ZKP系统可高效兼容该模:

  • 1)大多数ZKP系统都设计为某Field内运算,但模 p = 2 64 p=2^{64} p=264不构成a field,因为其缺少对2的modular inverse,其仅仅是a ring。现有的2021年论文Rinocchio: SNARKs for Ring Arithmetic 是针对ring设计的,但其不能用于本场景,因其仅支持:
    • 若该ZKP为designed-verifier的,可支持任意rings,但这不适于区块链应用场景。
    • rings与安全、composite-order pairing-friendly curves关联使用,其与 p = 2 64 p=2^{64} p=264不兼容。
  • 2)使用合适的域来模拟64位整数计算,这是可选项,尽管其由自己的开销。仅有少量VMs设计为64位整数的,如RISC Zero和Aleo的Leo。也可使用32位域来模拟64位整数计算,如Polygon Miden 和 Valida(使用Plonky3)。

L2IV Research团队选择RISC Zero的原因有3:

  • 1)其工具链和开发生态相对稳定和成熟。
  • 2)RISC Zero的证明生成性能,尤其是在Apple M2 chips上的性能,相当不错。
  • 3)通过Bonsai API,可将证明生成处理,卸载给RISC Zero的专用Bonsai服务器,从而避免需在本地生成proof。
    在这里插入图片描述
    本文重点展示功能而暂未优化的原型。后续将做一系列优化实现。未来也将探索不同的方向。

本文概述了FHE和RISC Zero,详细介绍了将现有Rust代码用于RISC Zero的过程,介绍了RISC Zero中一种新的数据加载优化技巧,并演示了使用Ghidra对RISC-V代码进行分解和分析,以确定进一步的优化机会。

2023年12月14日更新:我们注意到“include_bytes”可能无法正确对齐数据,并可能导致对齐错误。因此,选择使用include_bytes_aligned crate中的include_bytes_aligned

2. 何为TFHE?

fully homomorphic encryption (FHE) 为加密算法,表示为 E E E,用于做数据加密。如已知明文 a a a,经FHE加密后获得密文 E ( a ) E(a) E(a)
a → E ( a ) a \rightarrow E(a) aE(a)

全同态性,意味着可基于密文做加法、减法和乘法运算:

  • 加法: Addition:  E ( a ) , E ( b ) → E ( a + b ) \text{Addition:}~E(a),~E(b)\rightarrow E(a+b) Addition: E(a), E(b)E(a+b)
  • 减法: Subtraction:  E ( a ) , E ( b ) → E ( a − b ) \text{Subtraction:}~E(a),~E(b)\rightarrow E(a-b) Subtraction: E(a), E(b)E(ab)
  • 乘法: Multiplication:  E ( a ) , E ( b ) → E ( a × b ) \text{Multiplication:}~E(a),~E(b)\rightarrow E(a \times b) Multiplication: E(a), E(b)E(a×b)

当明文为二进制位(0和1)时,可使用FHE来表示所有二进制逻辑门。包括XOR和AND这样的基础门,因其构成了FHE中任意二进制逻辑运算的基础:

  • XOR:  E ( a ) , E ( b ) → E ( a + b − a × b ) \text{XOR:}~E(a),~E(b)\rightarrow E(a + b - a \times b) XOR: E(a), E(b)E(a+ba×b)
  • AND:  E ( a ) , E ( b ) → E ( a × b ) \text{AND:}~E(a),~E(b)\rightarrow E(a \times b) AND: E(a), E(b)E(a×b)

由于其可表示所有二进制门,FHE可执行bounded size内的任意计算。在区块链应用中,FHE因在去中心化金融(DeFi)应用中实现隐私而引起了极大的兴趣。例如,在隐私增强的去中心化交易所(DEX)中,FHE可以秘密地为自动做市商(AMM)处理计算。

FHE的大部分计算开销归因于管理和减轻“噪声(noise)”。所有现有的FHE构建都依赖于learning-with error(LWE)假设或其变种——构成了这些密码学系统的基础。对于计算的每一步,输出——如 E ( a + b − a × b ) E(a+b-a\times b) E(a+ba×b)——将比输入 E ( a ) E(a) E(a) E ( b ) E(b) E(b)具有更多的噪声,并且该输出可能成为后续步骤的输入。随着计算的进行,密文积累的噪声量越来越大。如下图所示,一旦密文中的噪声达到某个阈值,就会使密文不可解密。【下图摘自2021年Zama团队Marc Joye论文《Guide to Fully Homomorphic Encryption over the [Discretized] Torus》。】
在这里插入图片描述
为促进FHE中的无限计算,必须找到一种在噪声变得过大之前清除噪声的方法。这种技术被称为“自举(bootstrapping)”——由Craig Gentry于2009年通过其关于FHE的开创性论文首次引入。bootstrapping包括使用FHE secret key的加密版本,通常被称为“bootstrapping key”,来解密并刷新有噪声的密文,这会产生包含相同数据但噪声较小的新密文。

可以想象,对于密文来说,FHE计算是一项非常累人的工作——就像马拉松一样,密文需要休息以避免“精疲力竭(burn out)”。如下图所示,将FHE bootstrapping看成是某人需要休息以避免在马拉松中精疲力尽。
在这里插入图片描述
在不同的全同态加密(FHE)算法中,TFHE引起了人们的极大关注,因为TFHE中的自举是有效的,并且TFHE非常适合于评估加密数据上的布尔电路。Zama、Fhenix、Inco都在使用TFHE。

因此,验证FHE的主要挑战在于准确地验证自举过程。在TFHE中,自举包括使用自举密钥根据被自举的密文“盲目地旋转(blindly rotate)”多项式,随后从这个旋转后的多项式中提取刷新的密文。

虽然这最初看起来像是对高级密码学的一次尝试,但值得注意的是,这个过程主要围绕着操作多项式和矩阵,如下图所示,其摘自2021年Zama团队Marc Joye论文《Guide to Fully Homomorphic Encryption over the [Discretized] Torus》。对于那些热衷于深入研究的人,我们强烈推荐Marc Joye关于TFHE的这本入门书,对于那些只对线性代数有基本了解的人来说,这本书很容易上手。
在这里插入图片描述

3. 何为RISC Zero?

RISC Zero是专门为RISC-V体系结构设计的通用零知识证明系统。换句话说,任何可通过riscv32im(用于整数乘法和除法的,具有“M”扩展的RISC-V 32位)编译成ELF(executable and linkable format)程序的程序都与RISC Zero兼容。在VM执行时,RISC Zero生成该执行的零知识证明,称为“receipt”。具体如下图所示。
在这里插入图片描述
人们常问的一个问题是:RISC Zero为什么选择RISC-V而不是其它指令集?其主要原因有2方面:

  • 1)RISC-V为简单通用指令集。RISC Zero仅需支持riscv32im中的如下46种指令:
    LB, LH, LW, LBU, LHU, ADDI, SLLI, SLTI, SLTIU, XORI, SRLI, SRAI, ORI, ANDI, AUIPC, SB, SH, AW, ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND, MUL, MULH, MULSU, MULU, DIV, DIVU, REM, REMU, LUI, BEQ, BNE, BLT, BGE, BGEU, JALR, JAL, ECALL, EBREAK
    
    相比于现代Intel x86的1131条指令,以及现代ARM的数百条指令,其要简单得多。同时,RISC-V也与其他极简主义指令集没有太大区别——MIPS(MIPS公司后来转型为RISC-V)、WASM和早期几代ARM,如ARMv4T。
  • 2)由于有LLVM,很容易将各种语言编译为RISC-V。
    LLVM是一个编译工具链,它实现了后端(“ISAs”)和前端(“编程语言”)之间的中间层(称为“intermediate representation(IR)”)。由于RISC-V是支持的后端之一,LLVM允许将许多前端(包括C、C++、Haskell、Ruby、Rust、Swift等)编译成RISC-V。
    见Chris Lattner书籍The Architecture of Open Source Applications (Volume 1) LLVM,LLVM的3阶段设计实现,其中RISC-V也是可用的LLVM后端之一。
    在这里插入图片描述

换句话说,通过自下而上的方法,RISC Zero:

  • 能够支持使用现有的Web2编程语言编写的程序。
  • 还能针对ZKP domain-specific language(DSL),如ZoKrates、Cairo、Noir和Circom,并创建一个编译器将它们转换为RISC-V。
    对于直接编译到RISC-V困难的DSL语言,另一种方法是首先将DSL编译成像C/C++这样的中间语言,然后使用现有的LLVM编译器最终转换到RISC-V。

人们会问的另一个问题是,尽管RISC-V是一个有利的选择,为什么以虚拟机方式启动?难道不能在现有的ZK-specific DSL中编写“约束系统”吗,比如ZoKrates、Cairo、Noir和Circom?
主要原因有二:

  • 1)使用RISC Zero可大幅降低开发时长。
    传统上,为ZKP构建约束系统是一项艰巨的任务,只有熟练的开发人员才能完成,而且可能需要数周时间。更糟糕的是,调试约束系统可能需要更长的时间,而且很难确保约束系统没有错误。这是限制零知识证明的开发和采用的因素之一,因为它很难构建应用程序。
    对于建立验证比特币或以太坊历史的historical proofs,或者在零知识的情况下证明某AI的输出,这些曾经是雄心勃勃的创业项目。现在,有了RISC Zero,曾经雄心勃勃的努力即使作为一个黑客马拉松项目也变得可行。例如,验证Zama的FHE——一个非常复杂的应用程序,以前从来没有人为它编写过ZKP约束系统——可以在RISC Zero中用几行代码完成。
    这种转变也简化了招聘开发人员的过程。一个有过Rust经验的人可以很容易地将现有的Rust代码迁移到RISC Zero中,而这个人甚至不需要很多Rust知识。
  • 2)RISC Zero甚至可能在某些方面超过人类的性能。RISC Zero的关键优势在于,它经过专门优化,可以进行32位和64位整数计算,还可以管理大型存储和内存,即使是熟练的ZKP工程师也无法击败它。
    在RISC Zero之前,执行这样的优化需要顶尖的ZKP研究人员和工程师,因为这样的技术是最新的,从未被记录在案。优化至少需要几个月的时间来开发,因为它可能需要最有创意地使用lookup arguments。
    RISC Zero已将这些尖端技术封装到其Bonsai框架中,使其既可访问又可负担。如果密码学就像烹饪,那么RISC Zero就是一个微波炉。

接下来将探讨如何利用RISC Zero以零知识验证Zama的FHE计算。看起来,所需要的只是重用Zama团队的一些代码,然后添加几行代码。

4. 以RISC Zero来验证Zama的FHE计算

4.1 验证FHE密文的bootstrapping

首先,需展示如何来验证某FHE密文bootstrapping的主要步骤:

  • 使用bootstrapping key来对多项式做“blind rotation”,具体如下:
#![no_main]
risc0_zkvm::guest::entry!(main);// load the toy FHE Rust library from Louis Tremblay Thibault (Zama)
use ttfhe::{N,ggsw::{cmux, GgswCiphertext},glwe::GlweCiphertext,lwe::LweCiphertext
};// load the bootstrapping key and the ciphertext to be bootstrapped
static BSK_BYTES: &[u8] = include_bytes_aligned!(8, "../../../bsk");
static C_BYTES: &[u8] = include_bytes_aligned!(8, "../../../c");pub fn main() {// a zero-copy trick to load the key and the ciphertext into RISC Zerolet bsk = unsafe {std::mem::transmute::<&u8, &[GgswCiphertext; N]>(&BSK_BYTES[0])};let c = unsafe {std::mem::transmute::<&u8, &LweCiphertext>(&C_BYTES[0])};// initialize the polynomial to be blindly rotatedlet mut c_prime = GlweCiphertext::trivial_encrypt_lut_poly();   c_prime.rotate_trivial((2 * N as u64) - c.body);// perform the blind rotationfor i in 0..N {c_prime = cmux(&bsk[i], &c_prime, &c_prime.rotate(c.mask[i]));}eprintln!("test res: {}", c_prime.body.coefs[0]);
}

以上代码中,除加载依赖或常量数据等trivial操作代码行之外,实际的关键代码仅5行——包括多项式的初始化及其blindly rotating:

let mut c_prime = GlweCiphertext::trivial_encrypt_lut_poly();   
c_prime.rotate_trivial((2 * N as u64) - c.body);for i in 0..N {c_prime = cmux(&bsk[i], &c_prime, &c_prime.rotate(c.mask[i]));
}

执行FHE steps的关键函数和算法,均源自Zama团队Louis Tremblay Thibault所开发https://github.com/tremblaythibaultl/ttfhe/ Rust库,具体关键函数和算法有:

  • trivial_encrypt_lut_poly
  • rotate_trivial
  • cmux

使用RISC Zero,可为该RISC-V程序的执行生成proof。RISC Zero的如下代码会执行该RISC-V程序(为ELF格式的可执行文件),并生成认证该执行的proof(称为“receipt”):

let env = ExecutorEnv::builder().build().unwrap();
let prover = default_prover();let receipt = prover.prove_elf(env, METHOD_NAME_ELF).unwrap();
receipt.verify(METHOD_NAME_ID).unwrap();

可将receipt发送给第三方,第三方可验证该RISC-V程序的执行情况,而无需访问其详细工作。对于需要更紧凑证明格式的情况,RISC Zero还能够生成succinct proof,在更简洁的同时保留其可验证性。

4.2 验证FHE(VFHE)

以Zama团队Louis Tremblay Thibault所开发https://github.com/tremblaythibaultl/ttfhe/ toy FHE Rust库为例,来演示如何使用RISC Zero来验证FHE。选择这个库的原因有二:

  • 1)其非常接近于Zama生产fhEVM中所使用的库
  • 2)其以Rust编写,便于直接编译到RISC-V。

在这里插入图片描述
该toy FHE Rust库是极简主义的——它只有6个文件,包含800行代码——但它完全支持将使用的三种不同类型的FHE密文:

  • 1)LWE密文(lwe.rs):其结构为1024个64位整数组成的向量。
  • 2)General LWE(GLWE)密文(glwe.rs):其结构也是1024个64位整数组成的向量。
  • 3)General Gentry–Sahai–Waters(GGSW)密文(ggsw):其结构为由64位整数组成的size为 4 × 1024 4\times 1024 4×1024的矩阵。

这就足以启动使用RISC Zero的开发,因为主要需求是一个高效的Rust实现,其可无缝编译到RISC-V。Louis Tremblay Thibault已经在VFHE库中开发了这些概念的初步版本(https://github.com/tremblaythibaultl/vfhe),作为基本出发点:

#![no_main]
use risc0_zkvm::guest::env;
use ttfhe::{ggsw::BootstrappingKey, glwe::GlweCiphertext, lwe::LweCiphertext};
risc0_zkvm::guest::entry!(main);pub fn main() {// bincode can serialize `bsk` into an blob that weighs 39.9MB on disk.// This `env::read()` call doesn't seem to stop - memory is allocated until the process goes OOM.let (c, bsk): (LweCiphertext, BootstrappingKey) = env::read();let lut = GlweCiphertext::trivial_encrypt_lut_poly();// `blind_rotate` is a quite heavy computation that takes ~2s to perform on a M2 MBP.// Maybe this is why the process is running OOM?let blind_rotated_lut = lut.blind_rotate(c, &bsk);let res_ct = blind_rotated_lut.sample_extract();env::commit(&res_ct);
}

不过,在上述代码的注释中,明确指出了其存在的2个主要问题:

  • 1)第一个问题与数据加载有关。当前面临着一个尚未解决的挑战,即如何有效地将自举密钥和密文加载到RISC-V程序中,因二者size可能很大。
    Louis的方法是使用RISC Zero的env::read channel,这是在proof生成期间将数据从外部输入RISC-V机器的标准方法。然而,正如Louis所指出的,这种方法并不是最优的,主要是由于其显著的内存需求和仅用于数据加载所需的大量VM CPU周期,导致了内存不足(OOM)问题。RISC Zero的Parker Thompson承认,这可能是问题的根源:“通常,向client读取大块数据的成本相当高。”
    作为避免这种数据加载开销的初步解决方案,建议将数据直接嵌入RISC-V程序中。在Rust中,一个典型的解决方案为使用include_bytes_aligned!宏,指示编译器将数据集成到RISC-V可执行文件中。随后,可从byte反序列化这些数据,例如,使用bincode::deserialize。代码如下所示:
    static BSK_BYTES: &[u8] = include_bytes_aligned!(8, ("../../../bsk");
    let bsk: BootstrappingKey = bincode::deserialize(BSK_BYTES);
    
    然而,重大挑战,源自,RISC-V程序为整个64MB自举密钥分配内存和复制数据所需的大量cycles。基准测试表明,仅证明密钥的正确加载就需要至少2个小时。
    本文揭示了RISC zero中解决这个问题的“零拷贝(zero-copy)”技巧。它允许实现几乎零CPU周期的自举密钥。本文将深入研究这项技术的细节。
  • 2)第二个主要问题涉及计算。正如Louis对代码所评论的那样,blind rotation盲旋转(这是自举的主要步骤)的效率可能是一个问题,因为它本身不是一个轻量级的计算(“在M2 MBP上执行需要2秒”)。这是整个“在RISC Zero中验证FHE”的更大挑战。
    L2IV Research团队在RISC Zero中设计并实现了许多技巧和技术来优化这一部分。在RISC Zero中优化FHE计算的过程是广泛的,计划在本系列中专门发表几篇文章来彻底解释每种技巧和技术。

4.2.1 加载大量数据的zero-copy trick

L2IV Research团队深入研究了克服RISC-V程序中数据加载挑战的方法,强调了显著减少RISC-V CPU周期的方法。
核心思想是避免在RISC-V中复制数据。这一点至关重要,因为在RISC-V中复制64MB的数据集将需要超过5000万条指令——一条指令读取数据,一条指令写入数据,以及一条指令更新指针。所有这些指令在某种程度上都是不必要的,因为Rust编译器已经将数据作为RISC-V程序的一部分包含在内,因此数据已经可用。
由于其固有的内存安全设计,在Rust中实现这一点具有挑战性。Rust中的标准实践涉及通过一个严格的过程初始化数据结构:在stack或heap中分配数据结构,将数据结构的内存归零(通过在整个内存中学以致用地填充零),然后一个接一个地复制数据。可以看出,Rust在计算周期上的花费甚至更大,因为将内存归零至少需要另外3400万条指令。
L2IV Research团队的解决方案采用了某些低级Rust原语,能够“绕过”Rust LLVM编译器施加的限制,从而更有效地编程RISC-V。

在与Greater Heat合作进行Aleo mining的过程中,学到了一项有价值的技术,涉及std::mem::transmite。这是一个特殊的Rust函数,可以将一种类型的bits重新解释为另一种类型。特别是,它可以用来修改指针的类型。
在本应用中,可直接在RISC-V文件中显式地嵌入(或者更准确地说,硬编码)自举密钥(BSK_bytes)和要自举的密文(C_bytes。为了避免复制数据,可直接操作指针的类型,如下所示:

let bsk = unsafe { std::mem::transmute::<&u8, &[GgswCiphertext; N]>(&BSK_BYTES[0]) 
}; let c = unsafe { std::mem::transmute::<&u8, &LweCiphertext>(&C_BYTES[0]) 
};

如前一段代码所示,获得了指向硬编码数据的ELF段指针,最初是一个字节指针(&u8)。然后,将其转换为指向自举密钥的指针(&[GgswCiphertext; N])或指向LWE密文的指针(&LweCiphertext)。此外,有必要将此代码封装在“不安全”括号中,因为Rust将此低级函数归类为不安全,并要求通过不安全明确承认其潜在风险。这种不安全的使用本身并不意味着危险;相反,这意味着需要专门的专业知识来处理这种低级操作。

对于熟悉C/C++的人来说,这个过程可以比作typecasting。在C/C++中,等效代码如下所示:

/* C */
BootstrappingKey *bsk = (BootstrappingKey*) &BSK_BYTES[0];
LweCiphertext *c = (LweCiphertext*) &C_BYTES[0];/* C++ */
BootstrappingKey *bsk = reinterpret_cast<BootstrappingKey*>(&BSK_BYTES[0]);
LweCiphertext *c = reinterpret_cast<LweCiphertext*>(&C_BYTES[0]);

实验结果表明,使用这种方法实际上消除了数据加载通常需要的cycles数。后续分析将通过使用RISC-V反编译器进一步验证这种零拷贝操作。

5. RISC-V一览

L2IV Research团队已经解决了来自Zama的Louis在使用RISC Zero进行FHE自举验证时遇到的最初挑战。在本系列即将发表的文章中,将深入探讨性能改进及其细微差别这一主题。

本节关注,使用RISC-V反编译器在零知识上下文中检查RISC Zero所验证的程序。目标有两个:

  • 1)确认L2IV Research团队的技术在RISC-V汇编级别上有效地实现了零拷贝。
  • 2)全面了解RISC-V程序的总体结构。

为此,使用https://github.com/NationalSecurityAgency/ghidra——由US National Security Agency (NSA)开发的全面、免费使用的逆向工程框架,其支持RISC-V。
在这里插入图片描述
上图展示了对RISC Zero所证明的RISC-V程序的Ghidra CodeBrowser。

Ghidra同时支持:

  • 展示RISC-V汇编代码,见上图中间窗格。
  • 以及反编译的代码(以C表示)——见上图右侧窗格。回顾之前提到的RISC-V的46条指令,值得注意的是,正在分析的汇编代码使用了这些精确的指令。

先专注于自动生成的反编译代码,如下所示:

/* method_name::main */void method_name::main(void){uint *puVar1;uint *puVar2;int iVar3;undefined auStack_c018 [8192];undefined auStack_a018 [8192];undefined *local_8018;code *local_8014;int *local_4018 [2];undefined1 *local_4010;undefined4 local_400c;undefined **local_4008;undefined4 local_4004;gp = &__global_pointer$;ttfhe::glwe::GlweCiphertext::trivial_encrypt_lut_poly(auStack_c018);ttfhe::glwe::GlweCiphertext::rotate_trivial((int)auStack_c018,0x600);puVar2 = &anon.874983810a662adbf4687c54e184621b.1.llvm.4718791565163837729;puVar1 = (uint *)&anon.874983810a662adbf4687c54e184621b.0.llvm.4718791565163837729;iVar3 = 0x400;do {ttfhe::glwe::GlweCiphertext::rotate(local_4018,(int)auStack_c018,*puVar2);ttfhe::ggsw::cmux(&local_8018,puVar1,(int)auStack_c018,(int)local_4018);memcpy(auStack_c018,&local_8018,0x4000);puVar2 = puVar2 + 2;iVar3 = iVar3 + -1;puVar1 = puVar1 + 0x4000;} while (iVar3 != 0);local_8018 = auStack_a018;local_8014 = u64>::fmt;local_4010 = anon.874983810a662adbf4687c54e184621b.4.llvm.4718791565163837729;local_400c = 2;local_4018[0] = (int *)0x0;local_4008 = &local_8018;local_4004 = 1;std::io::stdio::_eprint(local_4018);return;
}

初步可确认,其数据加载流程确实实现了zero-copy efficiency。该代码片段使用std::mem::transmute来将数据加载编译进16-byte sequence of RISC-V machine codes:

37 c5 20 04 93 04 c5 4c 37 c5 20 00 13 04 c5 4c

反编译展示了4条汇编指令,负责将指针值存储到s1和s0寄存器中。本质上,该代码分配0x420c4cc值给s1寄存器,分配0x020c4cc值给s0寄存器:

        00200844 37 c5 20 04     lui        a0,0x420c00200848 93 04 c5 4c     addi      s1,a0,0x4cc0020084c 37 c5 20 00     lui        a0,0x20c00200850 13 04 c5 4c     addi      s0,a0,0x4cc

可进一步将该汇编代码反编译为C类似格式,以有更清晰的理解,如下所示:

uint *puVar1;
uint *puVar2;puVar2 = &anon.874983810a662adbf4687c54e184621b.1.llvm.4718791565163837729;
puVar1 = (uint *)&anon.874983810a662adbf4687c54e184621b.0.llvm.4718791565163837729;

在该反编译代码中,首个label anon.874983810a662adbf4687c54e184621b.1.llvm.4718791565163837729 明确指出了密文字节的位置,表示为C_BYTES。利用Ghidra,可直接观察该代码中的这些密文字节:
在这里插入图片描述
上图,展示了0x420c4cc位置的ELF可执行文件的数据,即s1寄存器的初始值,用于待自举的密文。

C_BYTE包含在名为"c"的文件中。通过使用Hex Fiend,一种用于十六进制编辑的工具,可检查该文件的内容。如下所述,检查确认了数据的一致性。

static C_BYTES: &[u8] = include_bytes!("../../../c");

在这里插入图片描述
如上十六进制编辑器中所示,"c"文件中存储了待自举的密文。
类似地,对第二个label——anon.874983810a662adbf4687c54e184621b.0.llvm.4718791565163837729,可定位对应自举密钥的字节,称为BSK_BYTES
在这里插入图片描述
上图,展示了0x020c4cc位置的ELF可执行文件的数据,即s0寄存器的初始值,用于自举密钥。
此外,可通过将该数据与其源文件“bsk”进行交叉检查来验证该数据,确保其与上述信息一致。
在这里插入图片描述
上图,在十六进制编辑器中,展示了“bsk”文件中存储了该自举密钥。

接下来,将展示如何在程序中使用该自举密钥和密文。Rust源代码中,通过明确调用cmux函数,在for循环中同时集成该自举密钥和密文:

// perform the blind rotation
for i in 0..N {c_prime = cmux(&bsk[i], &c_prime, &c_prime.rotate(c.mask[i]));
}

然后,借助Ghidra来定位并检查相应的反编译代码:

puVar2 = &anon.874983810a662adbf4687c54e184621b.1.llvm.4718791565163837729;
puVar1 = (uint *)&anon.874983810a662adbf4687c54e184621b.0.llvm.4718791565163837729;
iVar3 = 0x400;
do {ttfhe::glwe::GlweCiphertext::rotate(local_4018,(int)auStack_c018,*puVar2);ttfhe::ggsw::cmux(&local_8018,puVar1,(int)auStack_c018,(int)local_4018);memcpy(auStack_c018,&local_8018,0x4000);puVar2 = puVar2 + 2;iVar3 = iVar3 + -1;puVar1 = puVar1 + 0x4000;
} while (iVar3 != 0);

该反编译代码中包含不同组件:

  • 1)Cursor Assignments:puVar2用作指向c的当前指针,而puVar1为指向bsk的当前指针。此设置便于通过密文和自举密钥进行导航。
  • 2)Loop Mechanics:该循环使用iVar3作为从1024的自减计数器,当其值为0时接收循环。在每个迭代过程中,会做如下操作:
    • 2.1)Ciphertext Manipulation:c_prime,存储在stack的auStack_c018中,首先与 c.mask[i](称为 *puVar2) rotate生成的结果存于local_4018中。
    • 2.2)cmux Function Invocation:cmux 关键函数,以bsk[i] (puVar1)、原始c_prime、和已旋转的c_prime 为输入,输出的结果到local_8018中。
    • 2.3)Optimization Opportunity:一个有趣的方面是local_8018的处理,它被视为更新的c_prime。它被复制回c_prime变量中,暗示潜在的优化。通过就地执行cmux消除此拷贝可以提高效率。
    • 2.4)Cursor Updates:该循环包含同时更新puVar2和puVar1。puVar2向前移动两个dwords(64位)到下一个c.mask[i],而puVar1向前移动16384个dwords(524288位)到随后的bsk[i]。

当iVar3值为0时,以上循环结束,这些steps共同表示在RISC-V程序中处理FHE自举的过程。

使用Ghidra的进一步分析能够仔细审查该计划的其他部分,从而深入了解潜在的优化机会。这个过程有助于评估Rust RISC-V编译器是否按预期生成RISC-V指令。

如,检查cmux函数的反编译代码。为了提供上下文,将首先考虑原始的Rust代码,如下所示:

/// Ciphertext multiplexer. If `ctb` is an encryption of `1`, return `ct1`. Else, return `ct2`.
pub fn cmux(ctb: &GgswCiphertext, ct1: &GlweCiphertext, ct2: &GlweCiphertext) -> GlweCiphertext {let mut res = ct2.sub(ct1);res = ctb.external_product(&res);res = res.add(ct1);res
}

其反编译代码表明,对“sub”和“add”的函数调用在编译过程中被有效地内联了。这种内联在代码中产生可见的循环,这些循环负责模拟64位整数运算。此外,该代码还使用了对memsetmemcpy的几个调用。值得注意的是,memset的一些实例用于归零内存,这可能并不总是必要的。这一观察结果开辟了潜在的优化途径,特别是在消除不必要的memset调用方面。
在这里插入图片描述
上图展示了cmux函数的反编译RISC-V指令代码。

参考资料

[1] L2IV Research团队2023年11月16日博客 Tech Deep Dive: Verifying FHE in RISC Zero, Part I:From Hidden to Proven: The ZK Path of FHE Validation

RISC Zero系列博客

  • RISC0:Towards a Unified Compilation Framework for Zero Knowledge
  • Risc Zero ZKVM:zk-STARKs + RISC-V
  • 2023年 ZK Hack以及ZK Summit 9 亮点记
  • RISC Zero zkVM 白皮书
  • Risc0:使用Continunations来证明任意EVM交易
  • Zeth:首个Type 0 zkEVM
  • RISC Zero项目简介
  • RISC Zero zkVM性能指标
  • Continuations:扩展RISC Zero zkVM支持(无限)大计算
  • A summary on the FRI low degree test前2页导读
  • Reed-Solomon Codes及其与RISC Zero zkVM的关系
  • RISC Zero zkVM架构
  • RISC-V与RISC Zero zkVM的关系
  • 有限域的Fast Multiplication和Modular Reduction算法实现
  • RISC Zero的Bonsai证明服务
  • RISC Zero ZKP协议中的商多项式
  • FRI的Commit、Query以及FRI Batching内部机制
  • RISC Zero的手撕STARK
  • RISC Zero zkVM guest程序优化技巧 及其 与物理CPU的关键差异
  • ZK*FM:RISC Zero zkVM的形式化验证
  • Zirgen MLIR:RISC-Zero的ZK-circuits形式化验证
  • 以RISC Zero ZK Fraud Proof赋能Optimistic Rollups
  • zkSummit10 亮点记

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/232402.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于AT89C52单片机的计算器设计与仿真

点击链接获取Keil源码与Project Backups仿真图&#xff1a; https://download.csdn.net/download/qq_64505944/88637995?spm1001.2014.3001.5503 源码获取 B 源码仿真图课程设计51 摘 要 计算器一般是指“电子计算器”,能进行数学运算的手持机器&#xff0c;拥有集成电路芯…

整合SpringSecurity

目录 前言 数据库设计 用户表 角色表 用户角色表 权限表 角色权限表 插入数据 表的实体类 用户表实体类 角色表实体类 权限表实体类 mapper层接口 UserMapper RoleMapper AuthorityMapper 封装登录信息 统一响应结果 上下文相关类 jwt令牌工具类 依赖导入…

2023.12.17 关于 Redis 的特性和应用场景

目录 引言 Redis 特性 内存中存储数据 可编程性 可扩展性 持久化 支持集群 高可用性 Redis 优势 Redis 用作数据库 Redis 相较于 MySQL 优势 Redis 相较于 MySQL 劣势 Redis 用作缓存 典型场景 Redis 存储 session 信息 Redis 用作消息队列 初心 消息队列的…

智能优化算法应用:基于适应度相关算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于适应度相关算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于适应度相关算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.适应度相关算法4.实验参数设定5.算法…

构建高效统一的音视频联动融合通信调度平台

在信息技术日益高度发展的今天&#xff0c;越来越多的企业或者组织机构重视如何提高内外部的工作效率&#xff0c;但由于传统的通信方式如电话、邮件、短信、传真等方式无法满足企业高效、快速的沟通要求&#xff0c;因此需要一个可以将各种通信方式结合在一起的通信系统来满足…

驭见大模型 智领新征程丨泰迪智能科技荣登2023年度广东省人工智能应用项目风云榜

12月15日&#xff0c;由广东省科学技术厅、广东省工业和信息化厅、广东省人力资源和社会保障厅、广东省政务服务数据管理局、广东省科学技术协会指导&#xff0c;广东省人工智能产业协会主办的2023年粤港澳大湾区人工智能产业大会正式举办&#xff0c;大会以“驭见大模型、智领…

PTFE四氟托盘应用于化工、医药、食品行业

PTFE托盘是一种广泛应用于化工、医药、食品等行业的特种托盘&#xff0c;采用聚四氟乙烯&#xff08;PTFE&#xff09;材料制造而成。PTFE是一种具有出色耐腐蚀性、高温稳定性和优异物理特性的材料。 PTFE托盘在化工领域中被广泛应用&#xff0c;主要用于处理腐蚀性的化学物质。…

【leetcode876】链表的中间结点Java代码讲解

12.19 链表的中间结点 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个中间结点&a…

工作纪实38-排查cpu彪高

昨天晚上上线了一个服务&#xff0c;第二天发现CPU持续飙高到70&#xff5e;90%&#xff0c;触发平台的自动扩容&#xff0c;后定位出问题后降低到3% 怀疑部分代码使用的线程在持续工作没有释放&#xff08;死循环&#xff09;进入机器&#xff0c;使用top -H 找出系统中使用C…

接口自动化测试难点:数据库验证解决方案

接口自动化中的数据库验证&#xff1a;确保数据的一致性和准确性 接口自动化测试是现代软件开发中不可或缺的一环&#xff0c;而数据库验证则是确保接口返回数据与数据库中的数据一致性的重要步骤。本文将介绍接口自动化中的数据库验证的原理、步骤以及示例代码&#xff0c;帮…

机器学习 | 贝叶斯方法

不同于KNN最近邻算法的空间思维&#xff0c;线性算法的线性思维&#xff0c;决策树算法的树状思维&#xff0c;神经网络的网状思维&#xff0c;SVM的升维思维。 贝叶斯方法强调的是 先后的因果思维。 监督式模型分为判别式模型和生成式模型。 判别模型和生成模型的区别&#xf…

【LeetCode刷题-树】--257.二叉树的所有路径

257.二叉树的所有路径 方法&#xff1a;深度优先搜索 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, T…

win环境下启动kafka Port already in use: 6688; nested exception is

背景 zk启动成功后&#xff0c;接下来启动kafka&#xff0c;再启动kafka后一直说端口被占用。 端口占用解决办法: netstat -aon|findstr 9092 taskkill -f -pid 7780 杀掉后&#xff0c;再次启动kafka时&#xff0c;问题并未解决 后来修改了批处理文件kafka-run-class.bat中…

深度剖析知识图谱:方法、工具与实战案例

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 知识图谱作为一种强大的知识表示和关联技术&am…

SAP 特殊采购类80简介

前面我们已经测试特殊采购类40、70,我们今天测试一下特殊采购类80。 特殊采购类80:在替代工厂生产,在成品层维护特殊采购类,需求和收货在计划工厂完成,成品生产和组件采购在生产工厂完成。 80采购类也是我们在SAP系统中实现跨工厂需求传递的一种方式。它具有传递方式简单、…

Python tkinter控件全集之组合选择框 ttk.ComboBox

Tkinter标准库 Tkinter是Python的标准GUI库&#xff0c;也是最常用的Python GUI库之一&#xff0c;提供了丰富的组件和功能&#xff0c;包括窗口、按钮、标签、文本框、列表框、滚动条、画布、菜单等&#xff0c;方便开发者进行图形界面的开发。Tkinter库基于Tk for Unix/Wind…

Sui限定版冬季任务来袭,100万份礼物和50万SUI在等你!

在这个渐趋寒冷但节日氛围却日渐浓厚的冬日&#xff0c;Mysten Labs非常高兴地向大家宣布&#xff0c;限定版冬季任务已于北京时间12月19日凌晨3点正式启动啦&#xff01;这次冒险任务充满刺激&#xff0c;奖励丰厚&#xff0c;快来Mysten Labs任务网站体验吧&#xff01; 对于…

查看git的帮助信息

说明 在cmd窗口、或者git Bash shell下执行git --help或者git -h命令&#xff0c;可以查看git的帮助信息。 执行git <command> --help命令可以查看某个命令的帮助信息&#xff0c;其中<command>表示某个具体的命令。 示例1&#xff1a;在git Bash shell下运行git…

Linux文本三剑客grep练习

1、显示/etc/rc.d/rc.sysinit文件中以不区分大小的h开头的行&#xff1b; [rootshell ~]# grep "^[hH]" /etc/rc.d/rc.rc.sysinit [rootshell ~]# grep -i "h" /etc/rc.d/rc.sysinit 2、显示/etc/passwd中以sh结尾的行; [rootshell ~]# grep "sh$&quo…

windows10 固定电脑IP地址操作说明

windows10 固定电脑IP地址操作说明 一、无线网络的IP地址设置方法二、有线网络的IP地址设置方法 本文主要介绍&#xff0c;windows10操作系统下&#xff0c;不同的网络类型&#xff0c;对应的电脑IP地址设置方法。 一、无线网络的IP地址设置方法 在桌面右下角&#xff0c;点击…