samjanny/entangled-api

GitHub: samjanny/entangled-api

Entangled协议的Rust参考实现,用于构建和验证签名文档。

Stars: 0 | Forks: 0

# 混沌 API [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/874482f747064501.svg)](https://github.com/samjanny/entangled-api/actions/workflows/ci.yml) [![许可证:MIT 或 Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license) [![MSRV](https://img.shields.io/badge/MSRV-1.88-orange.svg)](#install) Rust 实现的混沌 v1.0 协议:类型化签名文档、封闭模式验证、JCS 正规化、Ed25519 签名和验证、发布者身份短语推导、Tor v3 原始地址绑定、金丝雀检查、客户端状态助手。 混沌是一个用于在敌对或匿名导向的载体网络上发布签名、结构化文档的协议。它旨在为小型内容站点设计,其中读者应能够验证发布者身份,同时客户端故意保持渲染攻击面狭窄。 使用混沌构建的站点不是一个网络应用程序。它是一组签名 JSON 文档,通过载体(如 Tor v3)提供并由专用客户端渲染。没有 JavaScript、没有 DOM 脚本、没有 HTML、没有 cookie、没有环境浏览器存储,也没有发布者控制的客户端界面。 ## 状态 `entangled-api` 当前包含一个 Rust 包: - [`entangled-core`](./entangled-core): 协议核心库。 当前包版本:`0.10.0`。 在 `entangled-core` 中实现: - 清单、内容和事务文档类型。 - 混沌 v1.0 传输格式的封闭模式验证。 - 十一种签名内容块类型。 - 签名输入的 JCS 正规化。 - Ed25519 签名和严格验证。 - 清单、内容和事务的签名域分离。 - 发布者身份短语推导和恢复。 - Tor v3 葱地址解析和原始地址绑定。 - 清单类型状态验证管道。 - 金丝雀结构检查和金丝雀状态计算。 - 发布者历史检查的降级助手。 - 带策略感知助手的客户端状态存储。 - 提交体构建和验证。 此包的范围不包括: - 网络传输。 - HTTP 客户端/服务器实现。 - 完整的混沌浏览器/客户端 UI。 - 信任状态持久化和 UI 面板。 - 发布者历史存储。 - 同意提示 UI。 - 图像解码和渲染。 这些预计将存在于更高级别的包或应用程序中。 ## 混沌存在的理由 混沌将通常在网络上纠缠在一起的四个关注点分开: 1. **发布者身份** - 长期离线 Ed25519 身份密钥。 2. **载体可达性** - 如 Tor v3 葱服务之类的地址。 3. **常规发布签名** - 定期轮换的运行时密钥。 4. **文档渲染** - 由客户端约束的语法渲染。 目标是让读者验证文档是否属于同一发布者,即使服务器被破坏、原始地址轮换或载体迁移,同时避免通用浏览器运行时的攻击面。 混沌不是一个匿名层、网络替代品、分布式存储系统或否认机制。它依赖于所选的载体网络进行路由、可达性和任何网络层匿名。 ## 仓库布局 ``` . ├── Cargo.toml # Workspace manifest ├── Cargo.lock # Locked dependency set ├── deny.toml # cargo-deny policy ├── CHANGELOG.md ├── LICENSE-MIT ├── LICENSE-APACHE └── entangled-core/ # Rust core implementation ├── Cargo.toml ├── README.md ├── src/ └── tests/ ``` 协议规范本身位于单独的仓库中, [github.com/samjanny/entangled](https://github.com/samjanny/entangled), 在下面的 [规范](#specification) 部分中引用。 ## 安装 将核心包添加到 Rust 项目中: ``` [dependencies] entangled-core = "0.10" ``` 或者,在开发此仓库时: ``` [dependencies] entangled-core = { path = "entangled-core" } ``` 最低支持的 Rust 版本:`1.88`。 ## 快速入门 从发布者公钥推导发布者身份短语并从短语中恢复密钥: ``` use entangled_core::crypto::{derive_pip, pip_to_pubkey, PublisherSigningKey}; let publisher = PublisherSigningKey::from_seed(&[0x42; 32]); let publisher_pubkey = publisher.verifying_key(); let pip = derive_pip(&publisher_pubkey); assert_eq!(pip.split_whitespace().count(), 24); let recovered = pip_to_pubkey(&pip).unwrap(); assert_eq!(recovered, publisher_pubkey); ``` PIP 是公开的。它不是种子短语、密码、恢复密钥或私钥。它是发布者身份密钥的人可读指纹。 ## 构建和签名文档 `document` 模块使用 `Unsigned*` 对应物(`UnsignedManifest`、`UnsignedContent`、`UnsignedTransaction`)和 `build_*` 函数镜像每个签名线类型。构建器验证未签名的值是否与封闭模式一致,对其进行正规化(JCS),使用角色适当的密钥对正规化有效负载进行签名,并返回签名结构及其精确的序列化线字节。清单由 `K_publisher` 签名;内容和事务文档由 `K_runtime` 签名。 内容文档由运行时密钥签名: ``` use entangled_core::crypto::RuntimeSigningKey; use entangled_core::document::{build_content, UnsignedContent}; use entangled_core::types::blocks::Block; use entangled_core::types::inline::{InlineElement, TextMark}; use entangled_core::types::keys::SpecVersion; use entangled_core::types::meta::Meta; use entangled_core::types::path::EntangledPath; use entangled_core::types::timestamp::EntangledTimestamp; # fn demo() -> Result<(), entangled_core::document::DocumentError> { let runtime = RuntimeSigningKey::from_seed(&[0x01; 32]); let unsigned = UnsignedContent { spec_version: SpecVersion, path: EntangledPath::try_from("/articles/first-post") .expect("valid content path"), meta: Meta { title: "First post".to_owned(), published_at: EntangledTimestamp::try_from("2026-05-07T00:00:00Z") .expect("valid timestamp"), }, blocks: vec![Block::Paragraph { content: vec![InlineElement::Text { value: "Hello, world.".to_owned(), marks: Vec::::new(), }], }], // Optional content sequence number; required only when the manifest // declares `content_root` and the path is indexed (see the content // index section). seq: None, }; let (content, wire_bytes) = build_content(&unsigned, &runtime)?; // `wire_bytes` is the exact byte sequence to serve at `content.path`. # let _ = (content, wire_bytes); # Ok(()) # } ``` `build_manifest` 与此类似,它还接受当前时间以在构建时强制执行 `updated` 时钟偏移量界限。`UnsignedManifest` 包含嵌套的 `origin` 和 `canary` 块;一旦组装,签名只需一个调用: ``` use entangled_core::crypto::PublisherSigningKey; use entangled_core::document::{build_manifest, UnsignedManifest}; use entangled_core::types::timestamp::EntangledTimestamp; # // The full UnsignedManifest construction (origin, canary, state_policy, ...) # // is elided here; see tests/tor/integration_full.rs for a complete value. # fn demo(unsigned: &UnsignedManifest) -> Result<(), entangled_core::document::DocumentError> { let publisher = PublisherSigningKey::from_seed(&[0x42; 32]); let now = EntangledTimestamp::try_from("2026-05-07T00:00:00Z") .expect("valid timestamp"); let (manifest, wire_bytes) = build_manifest(unsigned, &publisher, &now)?; // `wire_bytes` is the exact byte sequence to serve at `/manifest.json`. # let _ = (manifest, wire_bytes); # Ok(()) # } ``` `build_transaction` 类似(由 `K_runtime` 签名,接受 `UnsignedTransaction`)。在失败时,构建器返回 `DocumentError`;其 `DocumentError::Validation(Diagnostic)` 变体携带规范性诊断,因此调用者可以匹配包含的 `Diagnostic.code`(每个规范的第 11 节的 `DiagnosticCode`)。产生的 `wire_bytes` 完全往返:将它们放回 `parse_and_verify_*`(以下)中可以重新生成签名结构。 ## 安全模型概述 混沌使用三个关键角色: | 密钥 | 角色 | 暴露配置文件 | | --- | --- | --- | | `K_publisher` | 长期发布者身份 | 离线;仅用于发布者仪式 | | `K_origin` | 载体端点身份 | 在线或近在线;对于 Tor v3,洋葱服务密钥 | | `K_runtime` | 常规文档签名 | 在线;通过清单金丝雀定期轮换 | 发布者密钥签名清单。清单授权当前原始地址和运行时密钥。内容和事务文档由运行时密钥签名。 服务器被破坏可能会暴露 `K_origin` 和 `K_runtime`,但只要 `K_publisher` 保持离线且未受损害,发布者身份就应该能够生存服务器被破坏。 ## 验证管道 `entangled-core` 实现了 Entangled 客户端管道的静态验证和签名验证部分: 1. 输入字节大小检查。 2. UTF-8 和 BOM 检查。 3. 带结构限制的 JSON 解析。 4. 文档类型区分。 5. 封闭模式验证。 6. 签名验证。 7. 清单类型状态过渡到金丝雀和原始地址检查。 信任状态查找、TOFU 锚定、外部验证 PIP 状态、发布者历史持久化和客户端 UI 行为仍然是嵌入客户端的责任。 ## 清单验证 清单解析返回类型状态包装器而不是裸 `Manifest`。这迫使调用者显式继续或有意退出后续验证阶段。 ``` use entangled_core::document::parse_and_verify_manifest; use entangled_core::types::{EntangledTimestamp, OnionAddress}; # fn verify_manifest_bytes( # manifest_bytes: &[u8], # now: &EntangledTimestamp, # fetched_onion: &OnionAddress, # content_index_bytes: Option<&[u8]>, # ) -> Result<(), entangled_core::validation::Diagnostic> { let verified = parse_and_verify_manifest(manifest_bytes, now)?; let (manifest, canary_state, content_index) = verified .verify_canary(now)? .verify_origin(fetched_onion, now)? .verify_content_index(content_index_bytes)? .into_parts(); let runtime_pubkey = manifest.canary.runtime_pubkey; # let _ = canary_state; # let _ = content_index; # let _ = runtime_pubkey; # Ok(()) # } ``` `verify_content_index` 在清单声明 `content_root` 时强制执行第 09:116 硬失败模型:调用者必须提供 `/content_index.json` 响应体字节,这些字节与 `content_root` 进行哈希验证,并结构化验证。省略 `content_root` 的清单在此处接受 `None` 并产生 `content_index = None`。 如果调用者正在构建离线工具、一致性测试或另一个上下文,其中金丝雀/原始地址/内容索引检查有意不适用,API 提供显式退出方法,例如 `skip_canary_check`、`skip_origin_check` 和 `skip_content_index_check`。 ## 金丝雀状态和过期的用户覆盖合同 `verify_canary` 返回 `ManifestCanaryChecked` 清单并将分类的 `CanaryState` 通过 `canary_state()` 暴露出来。库不根据状态操作:渲染策略存在于嵌入客户端。 规范第 08:183 是规范性必须:当观察到 `CanaryState::Expired` 时,客户端必须拒绝渲染当前内容。内容区域必须是空白或客户端生成的占位符;发布者控制的内容不得出现。 规范第 08:185 将第二个必须附加到渲染块:客户端必须提供具有以下属性的会话范围用户覆盖功能: - 一个肯定行动的铬控制(按钮、键组合或等效功能)的语义是“接受风险并继续”;被动事件不得算作接受; - 覆盖仅适用于当前会话中受影响的站点,不跨会话持久化,不修改金丝雀状态,也不抑制铬警告; - 覆盖活动时,一个持久、不易取消的警告必须保持可见。 第 11 节诊断代码 `E_CANARY_EXPIRED` 在 `error` 严重性下编目(rc.23 N64;该代码在 rc.10 到 rc.22 之间为 `W_CANARY_EXPIRED` 在 `warning` 严重性下,rc.23 通过重命名和提升解决了编目-行为不匹配)。现在,编目与第 08:183 规范性必须保持一致,即阻止当前内容的渲染。第 08:185 的会话范围用户覆盖功能和第 08 的许可金丝雀模式是规范定义的宽松策略 carve-outs,与第 11:87 客户端端重新分类严重性不同。`entangled-core` 识别金丝雀,暴露 `CanaryState::Expired` 并以 `error` 严重性发出诊断。覆盖状态、铬功能以及会话范围的持久性都存在于嵌入客户端。 ## 内容验证 内容文档与由验证的清单授权的运行时密钥进行验证: ``` use entangled_core::document::parse_and_verify_content; use entangled_core::types::RuntimePubkey; # fn verify_content_bytes( # content_bytes: &[u8], # runtime_pubkey: &RuntimePubkey, # ) -> Result<(), entangled_core::validation::Diagnostic> { let content = parse_and_verify_content(content_bytes, runtime_pubkey)?; // Higher-level clients should also bind `content.path` to the path that was fetched. # let _ = content; # Ok(()) # } ``` ## 事务验证 事务文档也由运行时密钥签名: ``` use entangled_core::document::parse_and_verify_transaction; use entangled_core::types::RuntimePubkey; # fn verify_transaction_bytes( # transaction_bytes: &[u8], # runtime_pubkey: &RuntimePubkey, # ) -> Result<(), entangled_core::validation::Diagnostic> { let transaction = parse_and_verify_transaction(transaction_bytes, runtime_pubkey)?; // Higher-level clients should bind `transaction.in_response_to` to the submit path. # let _ = transaction; # Ok(()) # } ``` ## 核心模块 | 模块 | 目的 | | --- | --- | | `types` | 清单、内容、事务、块、链接、表单、路径、时间戳和密钥的线格式类型 | | `canon` | JCS 正规化和签名输入构建 | | `crypto` | Ed25519 包装器、签名助手、SHA-256 助手和 PIP 推导 | | `validation` | 输入检查、封闭模式验证、诊断代码、金丝雀检查、状态策略检查和提交验证 | | `document` | 高级构建器、解析器和清单类型状态包装器 | | `state` | 客户端状态存储和提交体构建助手 | | `tor` | Tor v3 葱地址解析、校验和验证和原始地址绑定 | ## 开发 运行测试套件: ``` cargo test --all --locked ``` 运行格式化和 lint 检查: ``` cargo fmt --all --check cargo clippy --all-targets --all-features -- -D warnings ``` 如果已安装 `cargo-deny`,则运行依赖项/许可证/建议检查: ``` cargo deny check advisories licenses bans sources ``` 推荐的本地预发布检查是: ``` cargo fmt --all --check cargo test --all --locked cargo clippy --all-targets --all-features -- -D warnings cargo deny check advisories licenses bans sources ``` ## 安全态势 核心包禁止在包根目录中使用不安全 Rust: ``` #![forbid(unsafe_code)] ``` 一些传递依赖项可能内部使用 `unsafe` 进行加密数学或 SIMD 优化。这些是依赖级别实现细节,不是此包中的不安全代码。 与安全相关的设计选择包括: - 严格的 Ed25519 验证; - 每个文档家族的单独签名域; - 限制输入大小和结构验证; - JSON 解析期间的重复键拒绝; - 带未知字段拒绝的封闭模式; - 签名和验证之前的确定性正规化; - 明确的 Tor v3 原始地址绑定; - 基于PIP的发布者身份模型; - 通过清单金丝雀运行时密钥轮换。 如果您报告安全漏洞,请包括: - 受影响的包/版本或提交; - 如果有的话,最小重现程序; - 该问题是否影响签名验证、正规化、解析、状态处理、原始地址绑定或 API 故意使用。 ## 规范 协议规范位于单独的仓库中: [github.com/samjanny/entangled](https://github.com/samjanny/entangled). - [`00-overview.md`](https://github.com/samjanny/entangled/blob/main/specs/00-overview.md) - [`01-glossary.md`](https://github.com/samjanny/entangled/blob/main/specs/01-glossary.md) - [`02-document-schema.md`](https://github.com/samjanny/entangled/blob/main/specs/02-document-schema.md) - [`03-block-types.md`](https://github.com/samjanny/entangled/blob/main/specs/03-block-types.md) - [`04-canonicalization.md`](https://github.com/samjanny/entangled/blob/main/specs/04-canonicalization.md) - [`05-keys-and-signing.md`](https://github.com/samjanny/entangled/blob/main/specs/05-keys-and-signing.md) - [`06-manifest.md`](https://github.com/samjanny/entangled/blob/main/specs/06-manifest.md) - [`07-state.md`](https://github.com/samjanny/entangled/blob/main/specs/07-state.md) - [`08-canary.md`](https://github.com/samjanny/entangled/blob/main/specs/08-canary.md) - [`09-transport.md`](https://github.com/samjanny/entangled/blob/main/specs/09-transport.md) - [`10-client-behavior.md`](https://github.com/samjanny/entangled/blob/main/specs/10-client-behavior.md) - [`11-errors-and-versioning.md`](https://github.com/samjanny/entangled/blob/main/specs/11-errors-and-versioning.md) 有关操作指南,请参阅 [`docs/operator-playbook.md`](https://github.com/samjanny/entangled/blob/main/docs/operator-playbook.md). ## 许可证 代码在以下任一许可证下双许可: - MIT 许可证 - Apache 许可证,版本 2.0 协议/规范文档根据规范仓库中声明的许可证单独受保护。 [`LICENSE.md`](https://github.com/samjanny/entangled/blob/main/LICENSE.md).
标签:Apache许可证, CVE, Ed25519, JSON, Rust, Tor网络, 内容分发, 加密协议, 协议实现, 协议库, 可视化界面, 安全可观测性, 客户端渲染, 开源协议, 开源框架, 持续集成, 数字签名, 文档验证, 无cookies, 无DOM脚本, 无HTML, 无JavaScript, 版本控制, 状态管理, 签名文档, 结构化数据, 网络匿名, 网络安全, 网络安全, 网络流量审计, 通知系统, 隐私保护, 隐私保护