Proof Without Sharing Source Code: SJV, SJP, and the Trust Boundary

Proof Without Sharing Source Code: SJV, SJP, and the Trust Boundary

无需共享源代码的证明:SJV、SJP 与信任边界

What if you need to demonstrate properties of a software module without giving the other party its source code? This comes up in real projects: proprietary libraries; supplier components; security-sensitive software; licensed IP; implementations shared between organizations with different trust boundaries. 如果你需要在不向对方提供源代码的情况下证明某个软件模块的属性,该怎么办?这在实际项目中经常出现:专有库、供应商组件、安全敏感型软件、许可知识产权,以及在不同信任边界的组织之间共享的实现。

The usual choices are not great. Either you disclose the implementation, or the other party has to trust a report saying that some internal verification succeeded. With Sekura JS, we are exploring a third option: separate the implementation from the contract and from the proof artifact. 通常的选择并不理想。要么你公开实现,要么对方必须信任一份声称内部验证已成功的报告。通过 Sekura JS,我们正在探索第三种选择:将实现与契约及证明工件分离开来。

The model uses three things: source code — stays with the developer; SJV — describes the properties that should hold; SJP — carries evidence produced by verification. The basic idea is: SJV says what should be proved. SJP carries the evidence. The source code does not have to travel with them. 该模型使用三样东西:源代码——保留在开发者手中;SJV——描述应成立的属性;SJP——携带验证产生的证据。基本思路是:SJV 说明需要证明什么,SJP 携带证据,而源代码无需随之传输。

There is an important limitation, though. A verifier can replay the mathematical obligations stored in an SJP without seeing the source code. But that alone does not prove that those obligations were correctly generated from the particular closed-source implementation claimed by the developer. That distinction is the interesting part. 不过,这里有一个重要的局限性。验证者可以在不查看源代码的情况下,重放 SJP 中存储的数学义务(mathematical obligations)。但这本身并不能证明这些义务确实是由开发者所声称的特定闭源实现生成的。这种区别正是最有趣的部分。

Separate the contract from the implementation

将契约与实现分离

Suppose two organizations exchange a binary component. The customer does not necessarily need to know how the component is implemented internally. They may instead care about properties such as: a value never exceeding a defined range; an invalid state being unreachable; a function preserving an invariant; an operation producing only allowed state transitions. Those properties can be written as an SJV contract. 假设两个组织交换一个二进制组件。客户不一定需要知道该组件在内部是如何实现的。他们可能更关心以下属性:数值永远不会超过定义的范围;无效状态不可达;函数保持不变性;操作仅产生允许的状态转换。这些属性可以编写为 SJV 契约。

Conceptually, the relationship looks like this: 从概念上讲,其关系如下:

private implementation
|
verified against
v
SJV contract
v
SJP

The implementation answers: How is the module built? The contract answers: What behavior are we claiming? The proof artifact answers: What verification evidence was produced for those claims? Keeping those questions separate is useful even when source code disclosure is not a problem. It becomes much more important when it is. 实现回答了:模块是如何构建的?契约回答了:我们声称具备什么行为?证明工件回答了:为这些声明产生了什么验证证据?即使在源代码披露不是问题的情况下,将这些问题分开也是有用的。而在源代码披露成为问题时,这一点就变得更加重要。

A proof is only as strong as its contract

证明的强度取决于其契约

Formal verification does not mean proving that a program is universally correct. You prove specific properties. If the SJV contract says that: 0 <= balance <= MAX_BALANCE then proving that property does not prove anything about authentication, timing behavior, memory safety, or business rules that were never included in the contract. 形式化验证并不意味着证明程序在全局范围内是正确的。你证明的是特定的属性。如果 SJV 契约规定 0 <= balance <= MAX_BALANCE,那么证明该属性并不能证明任何关于身份验证、时序行为、内存安全或契约中未包含的业务规则的内容。

This sounds obvious, but it is one of the easiest ways to overstate formal verification. A successful verification result means: the defined properties hold within the modeled scope. It does not mean: the module has no bugs. That is why the contract itself has to be reviewable by the recipient. Before asking whether a proof succeeded, the recipient should first ask: Are these actually the properties I care about? 这听起来很显而易见,但却是夸大形式化验证最容易的方式之一。成功的验证结果意味着:定义的属性在建模范围内成立。它并不意味着:该模块没有漏洞。这就是为什么契约本身必须可供接收方审查。在询问证明是否成功之前,接收方首先应该问:这些真的是我关心的属性吗?

What goes into an SJP?

SJP 包含什么?

After verification, Sekura JS can produce an SJP — Sekura Justified Proof. The package is designed to carry verification evidence separately from the implementation. An SJP may include: a verification manifest; information about inputs and configuration; verification results; SMT obligations; integrity data; a manifest signature. 验证完成后,Sekura JS 可以生成 SJP(Sekura Justified Proof,Sekura 证明)。该包旨在将验证证据与实现分离开来。SJP 可能包括:验证清单;输入和配置信息;验证结果;SMT 义务;完整性数据;清单签名。

The important part for independent verification is that the mathematical obligations can travel with the package. The recipient does not need to receive a statement like: “Trust us, Z3 returned SAT/UNSAT correctly.” Instead, the relevant SMT queries can be replayed. With the current toolchain, Z3 is used for verification, and CVC5 can be used for an additional cross-check where required. 对于独立验证而言,重要的是数学义务可以随包传输。接收方不需要收到类似“请相信我们,Z3 正确返回了 SAT/UNSAT”这样的声明。相反,相关的 SMT 查询可以被重放。在当前的工具链中,Z3 用于验证,必要时可以使用 CVC5 进行额外的交叉检查。

Conceptually: 从概念上讲:

Developer
|
SJV + SJP
v
Recipient
|
+--> inspect the contract
+--> check package integrity
+--> replay SMT obligations with Z3
+--> optionally cross-check with CVC5

No source files need to be part of that exchange. 交换过程中无需包含任何源文件。

What does verify-sjp actually verify?

verify-sjp 到底验证了什么?

This is where precise wording matters. In the current implementation, verify-sjp can check the SJP package and replay the SMT queries stored inside it. A successful verification can therefore establish things such as: the package is structurally valid; its integrity checks are valid; its signature is valid for a supplied public key, when signing is used; the stored SMT obligations reproduce the reported result. 这就是措辞精确性的重要之处。在当前的实现中,verify-sjp 可以检查 SJP 包并重放其中存储的 SMT 查询。因此,成功的验证可以确定以下事项:包在结构上是有效的;其完整性检查是有效的;在启用签名时,其签名对于提供的公钥是有效的;存储的 SMT 义务能够复现报告的结果。

That is already useful. But there is another question: Did these SMT obligations actually come from the exact private source code the developer claims they came from? That is a different claim. And replaying an SJP alone cannot establish it. 这已经很有用了。但还有一个问题:这些 SMT 义务是否确实来自开发者所声称的特定私有源代码?这是一个不同的主张。仅靠重放 SJP 是无法确立这一点的。

The trust boundary

信任边界

Consider these two statements: 考虑以下两个陈述:

  • Statement A: These SMT obligations are mathematically valid.
  • 陈述 A:这些 SMT 义务在数学上是有效的。
  • Statement B: These SMT obligations were correctly generated from version X of this private implementation.
  • 陈述 B:这些 SMT 义务是根据此私有实现的 X 版本正确生成的。

verify-sjp can help establish A. It does not independently establish B if the source code is unavailable. This creates an explicit trust boundary: verify-sjp 可以帮助确立 A。如果源代码不可用,它无法独立确立 B。这就产生了一个明确的信任边界:

private source code
|
proof generation
<-- trust boundary
v
SMT obligations
|
independently replayable
v
SJP

This boundary should not be hidden. In fact, making it explicit is more useful than pretending the proof artifact proves something it cannot. 这个边界不应被隐藏。事实上,将其明确化比假装证明工件能证明它无法证明的东西更有用。

What about signatures?

签名呢?

An SJP can also be signed using Ed25519. That gives us another independent property. A valid signature can establish: “This manifest matches a signature produced for this public key.” But it does not establish: “This public key belongs to Company X.” And it certainly does not establish: “Company X generated the proof correctly.” Those are separate trust questions. SJP 也可以使用 Ed25519 进行签名。这为我们提供了另一个独立的属性。有效的签名可以确立:“此清单与为此公钥生成的签名相匹配。”但它不能确立:“此公钥属于 X 公司。”它当然也不能确立:“X 公司正确生成了证明。”这些是独立的信任问题。

So there are at least four different layers here: 因此,这里至少有四个不同的层级:

  1. Contract: What properties are claimed?

  2. Mathematical proof: Do the stored obligations hold?

  3. Signature: Was this manifest signed by this key?

  4. Provenance: Did these obligations actually come from the claimed implementation?

  5. 契约:声称了哪些属性?

  6. 数学证明:存储的义务是否成立?

  7. 签名:此清单是否由该密钥签名?

  8. 来源:这些义务是否确实来自所声称的实现?

Mixing these layers leads to misleading security claims. Keeping them separate gives us a much cleaner verification model. 混淆这些层级会导致误导性的安全声明。将它们分开则为我们提供了一个更清晰的验证模型。