Astrid 加密审计链(The Cryptographic Audit Chain):不可变、链式链接、密码学签名的防篡改审计账本 文档教程【免费下载链接】bookThe canonical reference for Astrid: kernel, capsules, host ABI, IPC, and the security model.项目地址https://gitcode.com/gh_mirrors/book269/book点击查看免费下载导读本文深入讲解 Unicity Astrid OS 的审计子系统——core/crates/astrid-audit所实现的加密审计链。审计日志不是被动的日志文件而是一个不可变、链式链接、密码学签名的防篡改账本对历史条目的任何修改都会破坏链条并在验证时被检测。读完本文你将掌握AuditEntry的数据模型与确定性签名构造、BLAKE3 链式哈希与 Ed25519 签名如何协同、按 principal 拆分链的设计、三种篡改场景的验证算法以及密钥轮换key rotation如何做到透明安全。概览审计子系统的三层架构Astrid 代表用户执行的每一个动作都会被记录为一条不可变、链式链接、密码学签名的审计条目。审计日志不是一个被动的日志文件它是一个防篡改账本对历史条目的任何修改都会破坏链条并在验证时被检测。整个审计子系统由三层组成Entryentry.rs单个事件的已签名、链式链接记录。Loglog.rs高层 API负责创建、存储和验证条目。Storagestorage.rs可插拔的AuditStoragetrait生产环境由SurrealKV作为后端。该 crate 从astrid-capabilities重新导出AuditEntryId因此所有调用方引用的是同一个规范化 ID 类型。[AuditLog] |-- append() / append_with_principal() | \-- AuditEntry::create / ::create_with_principal | \-- runtime_key.sign(signing_data) |-- verify_chain() / verify_principal_chain() |-- storage: Boxdyn AuditStorage \-- SurrealKvAuditStorage (production) \-- SurrealKvAuditStorage::in_memory() (tests, backed by MemoryKvStore)这套审计子系统与 Astrid 的安全模型深度耦合。在 The Five-Layer Security Gate 中审计是第五层fail-closed 层每个动作无论通过还是被拒都必须先写入审计日志intercept()才会返回审计写入失败本身就是硬错误。在 Policy, Budget, Approval, and Audit 中审计条目还承担着同意链consent chain的角色UserApproval::approval_entry_id会回溯到批准该动作的那条审计记录。AuditEntry 结构签名覆盖所有字段entry.rs:16定义的AuditEntry是审计链的基本单元pub struct AuditEntry { pub id: AuditEntryId, pub timestamp: Timestamp, pub session_id: SessionId, pub principal: OptionPrincipalId, // None system action pub action: AuditAction, pub authorization: AuthorizationProof, pub outcome: AuditOutcome, pub previous_hash: ContentHash, // BLAKE3 hash of previous entry pub runtime_key: PublicKey, // embedded at write time pub signature: Signature, // ed25519 over all fields above }关键设计点从id到runtime_key的每一个字段都参与签名。嵌入的runtime_key是条目创建时处于活动状态的 runtime 密钥对的公钥半部分。这就是密钥轮换之所以安全的核心机制验证时永远使用条目内记录的公钥而不是AuditLog当前持有的密钥。principal: OptionPrincipalId表示该动作是否归属于某个用户None表示系统动作如会话启动Some(principal)表示由具体用户principal发起。这与 Astrid 的多用户模型对应——同一会话内 Alice 和 Bob 的工具调用会形成各自独立的审计链。密码学原语BLAKE3 Ed25519哈希BLAKE3astrid-crypto/src/hash.rs:16定义了ContentHashpub struct ContentHash([u8; 32]); impl ContentHash { pub fn hash(data: [u8]) - Self { Self(*blake3::hash(data).as_bytes()) } pub const fn zero() - Self { Self([0u8; 32]) } pub fn is_zero(self) - bool { self.0 [0u8; 32] } }ContentHash::zero()是创世条目genesis entry的哨兵值一条链的第一个条目具有previous_hash ContentHash::zero()。验证算法会显式检查这一不变量。签名Ed25519astrid-crypto/src/keypair.rs:19定义了KeyPair#[derive(ZeroizeOnDrop)] pub struct KeyPair { #[zeroize(skip)] verifying_key: VerifyingKey, // ed25519-dalek signing_key: SigningKey, }ZeroizeOnDrop派生在KeyPair结构体本身。verifying_key字段带有#[zeroize(skip)]因为VerifyingKey不实现Zeroize。KeyPair::generate()从OsRng汲取熵——没有静态或硬编码的密钥内核在启动时生成全新的密钥对。签名验证astrid-crypto/src/signature.rs:89中的verifypub fn verify(self, message: [u8], public_key: [u8; 32]) - CryptoResult() { let verifying_key VerifyingKey::from_bytes(public_key)?; let sig DalekSignature::from_bytes(self.0); verifying_key.verify(message, sig) .map_err(|_| CryptoError::SignatureVerificationFailed) }注意verify接收的公钥是普通的字节切片而不是KeyPair。AuditEntry::verify_signature直接提供entry.runtime_key.as_bytes()因此每个条目在不接触活动签名密钥的情况下即可自验证。签名数据的确定性构造签名载荷由AuditEntry::signing_data()entry.rs:123确定性地构造。字段顺序与编码是固定的字段编码id16 字节 UUID 原始字节timestampi64 Unix 秒小端序session_id16 字节 UUID 原始字节principal0xFF u32 长度 LE UTF-8 字节或0x00actionserde_json::to_vecauthorizationserde_json::to_vecoutcome1 字节布尔值成功 1失败 0previous_hash32 原始字节runtime_key32 原始字节principal 字段使用带长度的编码并显式给出存在标记0xFF/0x00以避免principal 不存在与零长度 principal 紧邻下一字段之间的歧义。条目的内容哈希是ContentHash::hash(entry.signing_data())——这个值正是存储在下一条目的previous_hash中的内容pub fn content_hash(self) - ContentHash { ContentHash::hash(self.signing_data()) }条目创建create 与 create_with_principalAuditEntry::createentry.rs:67是主构造函数它接收前一条的哈希和当前活动的KeyPair作为参数pub fn create( session_id: SessionId, action: AuditAction, authorization: AuthorizationProof, outcome: AuditOutcome, previous_hash: ContentHash, runtime_key: KeyPair, ) - Self { let mut entry Self::new_unsigned( session_id, action, authorization, outcome, previous_hash, runtime_key.export_public_key(), // snapshot the public key ); let signing_data entry.signing_data(); entry.signature runtime_key.sign(signing_data); entry }new_unsigned将signature字段置为[0u8; 64]占位符。真正的签名在条目返回前替换它。占位符永远不会被持久化create返回的是完全签名的条目。AuditEntry::create_with_principal与上述逻辑相同但在计算签名数据之前额外设置entry.principal Some(principal)。因为principal参与载荷签名一个没有 principal 创建的条目无法在事后被追溯归属到某个用户而不使签名失效。这是审计归因attribution不可篡改的核心保证。链不变量Chain Invariant一条链是一系列满足以下条件的条目序列第一个条目的previous_hash是ContentHash::zero()创世。每个后续条目的previous_hash等于前一条目的content_hash()。每个条目的signature能用自己的内嵌runtime_key验证通过。follows方法编码了条件 2pub fn follows(self, previous: AuditEntry) - bool { self.previous_hash previous.content_hash() }任何乱序插入、删除或字段修改都会破坏至少一个上述不变量。按 Principal 拆分链会话内的独立链单个会话可能涉及多个 principal用户以及系统动作。与其把所有条目交错进一条链审计日志维护以(SessionId, OptionPrincipalId)为键的独立链。log.rs:20type ChainKey (SessionId, OptionPrincipalId);AuditLog中的chain_heads映射缓存每条链当前的头作为ContentHash。每次 append 时先查找相关链的头用作previous_hash然后更新为新条目的哈希// log.rs:106 let chain_key: ChainKey (session_id.clone(), principal.clone()); let previous_hash self.get_previous_hash(chain_key)?;get_previous_hash先检查内存缓存然后回退到存储后端的get_chain_head后者在audit:chain_heads命名空间下存储每条链最新的AuditEntryId。存储键格式为系统链{session_uuid}Principal 链{session_uuid}:{principal}storage.rs:150这意味着即使在同一个会话中Alice 的工具调用与 Bob 的工具调用也形成完全独立的链。Alice 链中的被篡改条目不会影响 Bob 的链或系统链的验证结果。这一设计与 PrincipalId and Per-Invocation Isolation 的多用户隔离模型一致也与五层安全门中的principal 隔离属性相呼应。AuditLog API从构造到查询构造// Production: SurrealKV on disk let log AuditLog::open(/var/lib/astrid/audit, runtime_key)?; // Tests: in-memory MemoryKvStore let log AuditLog::in_memory(runtime_key);生产路径将审计数据持久化到 SurrealKV与 KV Storage 中描述的SurrealKvStore同源测试路径使用MemoryKvStoreHashMapString, Vecu8加RwLock。追加条目// System action (no principal) let id log.append( session_id, AuditAction::SessionStarted { user_id, platform: cli.to_string() }, AuthorizationProof::System { reason: session start.to_string() }, AuditOutcome::success(), )?; // Principal-attributed action let id log.append_with_principal( session_id, alice_principal, AuditAction::FileWrite { path: /tmp/out.txt.into(), content_hash }, AuthorizationProof::Capability { token_id, token_hash }, AuditOutcome::success(), )?;从调用方角度看两个方法都是同步的。存储后端在block_on内执行 I/O它在多线程运行时上使用tokio::task::block_in_place生产环境在单线程运行时上使用作用域线程测试。参见storage.rs:106的三路分发逻辑。检索条目// Single entry by ID let entry: OptionAuditEntry log.get(entry_id)?; // All entries in a session (insertion order) let entries: VecAuditEntry log.get_session_entries(session_id)?; // Entries for one chain only let alice_entries log.get_principal_entries(session_id, Some(alice))?; let system_entries log.get_principal_entries(session_id, None)?;验证算法verify_chain、verify_principal_chain、verify_allverify_chain三种检查、完整收集AuditLog::verify_chain验证会话中的每一条链。它按principal分组条目按时间戳排序并依次检查三个性质创世chain_entries[0].previous_hash.is_zero()签名对每个条目调用entry.verify_signature()链接对每对相邻条目检查curr.follows(prev)pub fn verify_chain(self, session_id: SessionId) - AuditResultChainVerificationResult结果类型携带valid、entries_verified和一个VecChainIssuepub enum ChainIssue { InvalidGenesis { entry_id: AuditEntryId }, InvalidSignature { entry_id: AuditEntryId }, BrokenLink { entry_id: AuditEntryId, expected_previous: ContentHash, actual_previous: ContentHash, }, }verify_chain不会在第一次失败时短路——它收集所有问题让审计者能看到篡改的全貌。verify_principal_chain定向验证单条链// Verify Alices chain only let result log.verify_principal_chain(session_id, Some(alice))?; // Verify the system chain let result log.verify_principal_chain(session_id, None)?;这等价于对一个只包含指定 principal 条目的会话调用verify_chain。同样的三项检查序列适用。verify_all全库扫描let results: Vec(SessionId, ChainVerificationResult) log.verify_all()?;遍历storage.list_sessions()返回的每个会话并分别调用verify_chain。适合后台完整性扫描或启动自检。篡改检测实战三种互不重叠的失败模式log_tests.rs中的测试套件演示了三种篡改场景签名被破坏test_verify_detects_tampered_signature对条目标签的第一个字节做 XOR。verify_chain为该条目报告ChainIssue::InvalidSignature。链接断裂test_verify_detects_broken_link覆盖previous_hash并用原始密钥重新签名因此签名有效。verify_chain仅报告ChainIssue::BrokenLink不报告InvalidSignature。这证明两项检查相互独立且有效的签名并不蕴含链完整性。创世无效test_verify_detects_invalid_genesis在首条目上设置非零previous_hash并重新签名。verify_chain仅报告ChainIssue::InvalidGenesis。三种失败模式截然不同、互不重叠。密钥轮换嵌入公钥的透明机制条目在创建时嵌入公钥runtime_key: PublicKey。验证调用entry.runtime_key.verify(signing_data, entry.signature)使用的是条目自身的嵌入密钥而不是AuditLog当前的runtime_key字段。这意味着在密钥 A 下写入的条目在运行时轮换到密钥 B 后仍然可验证。测试test_key_rotation_entries_verify_via_embedded_pubkeylog_tests.rs:233明确演示了这一点// Write 3 entries signed by key A. let log_a AuditLog::in_memory(keypair_a); append_test_entries(log_a, session_id, 3); // Move entries into a log that holds key B. let log_b AuditLog::in_memory(keypair_b); for entry in log_a.get_session_entries(session_id)? { log_b.storage.store(entry)?; } // Verification succeeds because each entry carries its own public key. let result log_b.verify_chain(session_id)?; assert!(result.valid);审计者读取链时能看到哪条目由哪个 runtime 密钥签名。如果某个密钥在特定日期被泄露审计者能识别出哪些条目是在该密钥下产生的哪些是在其继任密钥下产生的。注意验证逻辑依赖的KeyPair生成本身同样来自OsRng每次内核启动都是全新密钥对因此启动即轮换是天然支持的场景。AuditAction 目录记录哪些安全相关事件AuditAction枚举entry.rs:190覆盖每一类安全相关事件。选录变体变体隐私说明McpToolCall { server, tool, args_hash }参数被哈希不存储明文CapsuleToolCall { capsule_id, tool, args_hash }相同的哈希策略FileWrite { path, content_hash }内容被哈希不存储CapabilityCreated { token_id, resource, permissions, scope }完整的授权细节ApprovalGranted { action, resource, scope }同意链SubAgentSpawned { parent_session_id, child_session_id, description }父子链接AdminRequest { method, required_capability, target_principal, params }取证参数字段SecurityViolation { violation_type, details }策略执行args_hash模式让链能够证明某个特定工具以特定参数被调用通过出示原像而无需在审计存储中保存可能敏感的参数值。这与 Capabilities, Tokens, and Delegation 中CapabilityToken::content_hash()的做法一脉相承敏感内容只存哈希原像由调用方按需出示。AuthorizationProof记录为什么被允许每个条目携带一个AuthorizationProof记录动作被许可的原因pub enum AuthorizationProof { User { user_id: [u8; 8], message_id: String }, Capability { token_id: TokenId, token_hash: ContentHash }, UserApproval { user_id: [u8; 8], approval_entry_id: OptionAuditEntryId }, NotRequired { reason: String }, System { reason: String }, Denied { reason: String }, }Capability::token_hash将审计记录链接到授权该动作的确切能力令牌。这与 Capabilities, Tokens, and Delegation 中描述的approval_audit_id机制相呼应——令牌与审批条目互链审计链成为谁批准了什么、何时批准的 ground truth。UserApproval::approval_entry_id链接回同一条链中的ApprovalGranted条目在同意事件与其授权的动作之间创建交叉引用。在 Policy, Budget, Approval, and Audit 中ApproveAlways铸造的CapabilityToken携带approval_audit_id未来由该令牌授权的每次调用都会携带UserApproval证明从而在审批事件与它覆盖的所有后续调用之间建立链式链接。被拒绝的动作使用AuthorizationProof::Denied配合AuditOutcome::failure。这意味着拒绝行为与成功行为享有相同的链式链接保证——审计日志对发生了什么和什么被拒绝了一视同仁。存储布局三个命名空间只追加不更新SurrealKvAuditStorage后端在astrid-storage的KvStore中使用三个命名空间命名空间键值audit:entries{entry_uuid}JSON 序列化的AuditEntryaudit:session_index{session_uuid}AuditEntryId的 JSON 数组插入顺序audit:chain_heads{session_uuid}或{session_uuid}:{principal}该链最新条目的 UTF-8 UUID会话索引是一个会话内所有条目 ID 的有序列表无论它们属于哪条链。链特有的顺序通过在entry.principal上过滤并按时间戳排序来恢复。存储层从不删除或更新条目它只追加。这是防篡改账本能长期成立的物理基础——写入路径只有 append历史数据天然不可覆盖。错误处理AuditResult 与 AuditError所有公开方法返回AuditResultT即ResultT, AuditErrorpub enum AuditError { StorageError(String), SerializationError(String), EntryNotFound { entry_id: String }, IntegrityViolation { entry_id: String, reason: String }, InvalidSignature { entry_id: String }, SessionNotFound { session_id: String }, CryptoError(#[from] astrid_crypto::CryptoError), }IntegrityViolation在检索过程中发现结构性问题时由存储层返回。作为AuditError的InvalidSignature与ChainIssue::InvalidSignature不同AuditError变体由AuditEntry::verify_signature返回仅在独立调用验证时冒泡而在verify_chain过程中问题被收集到VecChainIssue而不是作为错误返回因此调用者能看到完整图景。在五层安全门的语境中这些错误有更重的分量AuditLog::append的任何错误都会由拦截器转换为ApprovalError::AuditFailedfail-closed动作被视为被拒绝绝不带着缺失的审计记录继续执行参见 The Five-Layer Security Gate。总结三层式保证审计链提供了三层式保证条目级完整性覆盖所有字段的 ed25519 签名意味着对已存储条目的任何单字节修改都会被verify_signature检测到。顺序完整性BLAKE3previous_hash链接意味着插入、删除和重排序都会被verify_chain检测到。归因清晰性按 principal 拆分链意味着 Alice 的链与 Bob 的链可独立验证一条链中被破坏的条目不会污染另一条链的验证结果。密钥轮换是透明处理的因为公钥在写入时嵌入到每个条目中而verify_signature只使用那个嵌入的密钥。这使得 Astrid 的审计日志同时满足防篡改、可追溯、多租户隔离和长期可维护四项要求——它是整个安全模型策略、能力令牌、预算、审批的最终仲裁者。相关文档The Five-Layer Security GateCapabilities, Tokens, and DelegationPolicy, Budget, Approval, and AuditKV Storage赞分享文档教程【免费下载链接】bookThe canonical reference for Astrid: kernel, capsules, host ABI, IPC, and the security model.项目地址https://gitcode.com/gh_mirrors/book269/book点击查看免费下载相关推荐Astrid 审计链锚定Audit Chain Anchoring独立签名审计日志如何锚入主存储实现可考古性与不可伪造性Astrid 审计链锚定Audit Chain Anchoring独立签名审计日志如何锚入主存储实现可考古性与不可伪造性 导读 本文围绕 AstridAstrid 审计子系统深度解析Ed25519 签名 BLAKE3 哈希链的可篡改检测事件日志Astrid 审计子系统深度解析Ed25519 签名 BLAKE3 哈希链的可篡改检测事件日志 Astrid 是一套可移植、具备能力安全capabiliprotect-mcp 审计链Audit Chain校验实战用 /audit-chain 验证 Ed25519 签名与哈希链完整性的完整指南protect mcp 审计链Audit Chain校验实战用 /audit chain 验证 Ed25519 签名与哈希链完整性的完整指南 /auditAI 插件AI 技能开发工具上一篇10 分钟跑通数字产品销售全流程Gumroad 本地部署保姆级指南下一篇Android-Sunflower中Dagger Hilt与KSP注解处理器的完整集成指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考