news 2026/9/18 22:31:57

aptos-core 中的 diem-crypto 组件:哈希、签名与密钥派生原语全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
aptos-core 中的 diem-crypto 组件:哈希、签名与密钥派生原语全解析

aptos-core 中的 diem-crypto 组件:哈希、签名与密钥派生原语全解析

【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-core

导读

本文以third_party/move/move-examples/diem-framework/crates/crypto/README.md为骨架,系统讲解 Aptos 核心仓库中所继承的diem-crypto密码学组件:SHA-3 哈希、基于 RFC 5869 的 HKDF 密钥派生、类型安全的 traits 密码学 API、Ed25519 / MultiEd25519 签名,以及用于节点间安全通信的 X25519 + Noise 协议封装。读完本文,你将掌握该组件的模块划分、底层依赖、安全设计动机,并能在当前仓库中定位对应实现与测试,为深入阅读或二次开发提供地图。

组件定位:一条密码学原语链

diem-crypto是随 Move 生态一并内嵌于本仓库的密码学 crate(Cargo 包名为diem-crypto,见 Cargo.toml)。它承载了 Diem(Aptos 的前身架构)中使用的全部密码学原语实现:哈希(hashing)、签名(signing)与密钥派生/生成(key derivation/generation)。其中,基于traits.rs构建的库部分提供了强制类型安全的密码学 API,并实现了可验证随机函数所需的 EdDSA 与 MultiEdDSA 签名。

从仓库源码看,该 crate 的入口 lib.rs 对外暴露了compated25519errorhashhkdfmulti_ed25519noisetraitsvalidatablex25519等模块,并声明了#![forbid(unsafe_code)](全局禁止 unsafe)与#![deny(missing_docs)](强制文档完整性)两条 crate 级安全纪律,这体现了密码学库对内存安全与可审计性的严格要求。

使用的密码学算法总览

README 明确列出了该组件依赖的核心算法,逐一说明如下:

算法用途底层实现
SHA-3主要哈希函数,标准见 FIPS 202tiny-keccak(Cargo.toml中启用sha3feature)
HKDFHMAC-based Extract-and-Expand 密钥派生函数,标准见 RFC 5869hkdfcrate,配合sha2提供 SHA-256
Ed25519基于新 API 设计的签名方案,含额外安全校验(如防可锻性 malleability)ed25519-dalek(fork 版ed25519-dalek-fiat
X25519密钥交换,用于保护验证器之间的通信x25519-dalek(fork 版x25519-dalek-fiat),配合 Noise Protocol Framework

值得注意的细节是:Cargo.toml中这三个 dalek 系列依赖均指向*-fiatfork 包,且在 lib.rs 中通过compile_error!强制必须且只能启用fiatu64u32三种算术后端之一(默认fiat)。fiat 后端采用经过形式化验证的算术实现(fiat-crypto),这正是注释中所说的 "We use formally verified arithmetic"。

模块组织方式

README 给出的模块结构如下,仓库中实际文件与之一一对应:

crypto/src ├── hash.rs # Hash function (SHA-3) ├── hkdf.rs # HKDF implementation (HMAC-based Extract-and-Expand Key Derivation Function based on RFC 5869) ├── macros/ # Derivations for SilentDebug and SilentDisplay ├── utils.rs # Serialization utility functions ├── lib.rs ├── ed25519.rs # Ed25519 implementation of the signing/verification API in traits.rs ├── multi_ed25519.rs # MultiEd25519 implementation of the signing/verification API in traits.rs ├── x25519.rs # X25519 wrapper ├── test_utils.rs ├── traits.rs # New API design and the necessary abstractions └── unit_tests/ # Tests

对照当前仓库实际布局(见 crypto 目录),模块集合基本一致,但做了进一步拆分:error.rsvalidatable.rsnoise.rstags.rscompat.rs是新增或独立出来的文件;macros/目录对应的宏实现(SilentDebugSilentDisplay等 derive)位于相邻的crypto-derivecrate(diem-crypto-derive = { path = "../crypto-derive" }),用于生成DeserializeKeySerializeKeySilentDebugSilentDisplayCryptoHasherBCSCryptoHash等派生实现。unit_tests/下则包含hash_test.rshkdf_test.rsed25519_test.rsmulti_ed25519_test.rsnoise_test.rscompat_test.rscryptohasher.rs以及compilation/子目录中的派生宏编译测试。此外还有benches/ed25519.rsbenches/noise.rs两个 Criterion 基准测试,以及test_vectors/noise_cacophony.txt官方向量文件。

README 还特别提示:该 crate 历史上曾支持 BLS12381、ECVRF 与 SlIP-0010,后因缺乏使用而被移除,移除前最后一个 git 修订为00301524。当前仓库中确实已不存在这三个模块,traits.rs中仅实现了 Ed25519 与 MultiEd25519 的Sealed标记(见下文)。

SHA-3 哈希:HashValue 与类型安全哈希

hash.rs(源码)基于tiny_keccak的 SHA-3 实现构建,核心输出类型为HashValue:固定 32 字节(LENGTH: usize = 32)的哈希值,通过newfrom_slice等构造方式从字节数组生成,并对序列化、十六进制编解码提供完整支持。

防两类现实攻击的设计动机

hash.rs的模块文档解释了这套设计的初衷——防御两类真实世界攻击:

  1. 语义歧义(Semantic Ambiguity):Alice 在同一把私钥下使用 X、Y 两个应用。X 请她签署 "I am Alice",但在 Y 应用中 "I" 开头可能代表转账、"Alice" 可能被解释为地址。如果不做域隔离,同一签名可能被跨应用误读。解决办法是让每一种被哈希、被签名的 Rust 类型都有唯一语义。
  2. 格式歧义(Format Ambiguity):如果程序用a + "||" + b拼接后哈希,那么("foo||", "bar")("foo", "||bar")会产生相同哈希,形成碰撞。解决办法是统一使用 BCS(Binary Canonical Serialization)作为写入哈希器的推荐序列化方式。

CryptoHasher:用盐(salt)隔离类型域

针对上述问题,库提供了CryptoHasher抽象:每个哈希类型都带有独特的种子(seed/salt),保证类型MyNewStruct的哈希永远不会与其它类型的哈希碰撞。实现细节在 hash.rs:所有可哈希结构的盐都以全局前缀DIEM::DIEM_HASH_PREFIX)开头,再拼接该结构的序列化名称,从而在二进制层面做域分离。

推荐的用法是通过diem_crypto_derive的派生宏,一键获得哈希能力:

use diem_crypto::hash::CryptoHash; use diem_crypto_derive::{CryptoHasher, BCSCryptoHash}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)] struct MyNewStruct { /*...*/ } let value = MyNewStruct { /*...*/ }; value.hash();

其背后会自动生成MyNewStructHasherCryptoHashertrait 的实现)以及基于 BCS 的CryptoHash实现。若想自定义盐的名称,可用 serde 的rename属性——盐将基于OptionalCustomSerdeName而非默认的类型名:

use diem_crypto_derive::CryptoHasher; use serde::Deserialize; #[derive(Deserialize, CryptoHasher)] #[serde(rename = "OptionalCustomSerdeName")] struct MyNewStruct { /*...*/ }

对于特殊场景,库也提供define_hasher!宏直接定义定制 hasher,以及TestOnlyHasher这类测试用 hasher(可直接update/finish)。README 与源码均明确警告:新代码除非明确知道后果,否则不要使用后两种方式,派生宏才是推荐路径。对应测试位于 hash_test.rs 与 cryptohasher.rs。

HKDF:基于 RFC 5869 的密钥派生

hkdf.rs(源码)实现了 RFC 5869 定义的 HMAC-based Extract-and-Expand Key Derivation Function,遵循经典的"extract-then-expand" 两阶段范式

  • Extract 阶段:从输入密钥材料(IKM,即 seed)中"提取"出固定长度的伪随机密钥 PRK;
  • Expand 阶段:将 PRK"扩展"为若干额外的伪随机密钥(KDF 输出)。

该实现与输出 256 位及以上的哈希函数兼容(如 SHA-256、SHA3-256、SHA-512),源码中通过类型约束D::OutputSize: IsGreaterOrEqual<DMinimumSize, Output = True>DMinimumSize = U32)在编译期拒绝 SHA-1 这类短输出哈希。

典型应用场景

HKDF 的模块文档列出了五类典型应用,均可在当前仓库语境下理解:

  • a) 从高熵主种子派生密钥——Diem 中生成密钥的推荐方式,尤其在没有真随机数发生器(TRNG)时;
  • b) 在密钥协商协议中,从共享 Diffie-Hellman 值派生会话密钥;
  • c) 组合多个随机源(系统事件、用户击键、/dev/urandom 等)的熵,再用组合种子生成账户、网络与交易签名密钥;
  • d) 类似比特币 BIP32 的分层私钥派生,便于密钥管理;
  • e) 混合密钥生成:主种子叠加 PRNG 输出,防止主种子泄露或 PRNG 熵不足带来的风险。

使用建议与安全约束

  • Salt(可选):使用随机 salt 能增强 HKDF 强度,保证哈希函数不同用途之间的独立性;salt 应与输入密钥材料相互独立,且不能被攻击者选择或操纵。
  • Application info(可选):用于把派生密钥绑定到应用与上下文特定信息(协议号、算法标识、BIP32 中的子密钥号等),唯一技术要求是与种子独立。
  • 两个步骤都用:除非完全清楚自己在做什么,否则应同时使用 extract 与 expand 两步。

代码示例与最小安全长度

use diem_crypto::hkdf::Hkdf; use sha2::Sha256; // some bytes required for this example. let raw_bytes = [2u8; 10]; // define salt let salt = Some(&raw_bytes[0..4]); // define seed - in production this is recommended to be a 32 bytes or longer random seed. let seed = [3u8; 32]; // define application info let info = Some(&raw_bytes[4..10]); // HKDF extract-then-expand 64-bytes output let derived_bytes = Hkdf::<Sha256>::extract_then_expand(salt, &seed, info, 64); assert_eq!(derived_bytes.unwrap().len(), 64)

源码中还有几处关键的安全护栏值得注意(hkdf.rs):

  • 种子(IKM)长度不得小于 16 字节(MINIMUM_SEED_LENGTH: usize = 16),这是防止 HKDF 误用的预防性措施——128 位是当今多数应用避免暴力破解的最小种子熵;而对 Ed25519 密钥,官方建议随机种子至少 32 字节;
  • HKDF-Expand 的输出长度不能为 0,且受 RFC 5869 的MAX_OUTPUT_LENGTH <= 255 * HashLen限制,超出会返回HkdfError::InvalidOutputLengthError
  • extract_then_expand_no_ikm是不带 IKM 输入的特例 API,非完全符合 RFC(RFC 始终要求非零 ikm),目前仅供 Noise 协议的 HKDF 规范使用,普通代码应优先使用extract_then_expand

对应的单元测试位于 hkdf_test.rs,其中覆盖了HkdfError各类错误分支。

traits.rs:类型安全的密码学 API 抽象

traits.rs(源码)是整个组件"新 API 设计"的核心,用一组相互关联的 Rust trait 把密钥、签名、验证串联成强类型体系:

  • ValidCryptoMaterial:具有字节校验概念的密钥/密码学材料类型族,要求实现TryFrom<&[u8], Error = CryptoMaterialError>SerializeDeserializeOwnedto_bytes();配套的ValidCryptoMaterialStringExt提供基于 hex 的from_encoded_string/to_encoded_string编解码。
  • PrivateKey/PublicKey:公私钥类型通过关联类型互相绑定(type PublicKeyMaterial: PublicKey<PrivateKeyMaterial = Self>),PublicKey还要求存在From<&PrivateKeyMaterial>转换,保证从私钥到公钥的确定性、规范化构造。
  • SigningKey/VerifyingKey/Signature:签名方、验证方与签名三者通过关联类型闭环(SigningKeyVerifyingKeyMaterialSignatureMaterial分别被VerifyingKeySignature反向引用)。SigningKey::sign接受任意T: CryptoHash + Serialize的消息对象,内部先取T::Hasher的域分离种子再 BCS 序列化(见signing_message函数);Signature提供verifyverify_arbitrary_msgto_bytes,并给出去默认逐个验证的batch_verify,允许各方案覆写为更高效的批量验证实现。
  • Uniform:从CryptoRng生成密钥材料的方案类型族,附赠generate_for_testing()(基于共享TEST_SEED的确定性生成)。
  • Genesis:按惯例产生创世私钥的类型族。

类型安全的一个关键机制是pub(crate) mod private中的Sealed密封 trait(traits.rs):只有本 crate 内被显式实现Sealed的类型(Ed25519PrivateKey/PublicKey/SignatureMultiEd25519PrivateKey/PublicKey/Signature)才能实现SigningKeyVerifyingKeySignature,从编译期杜绝了外部 crate 伪造签名方案实现的可能。

此外,CryptoMaterialError枚举集中定义了密钥/签名摄入失败的原因分类:序列化失败、反序列化失败、验证失败、长度错误、非规范化表示(可锻性)、小群元素、点不在曲线上、BitVec 错误等——这些错误类型在 Ed25519 与 MultiEd25519 的实现中被广泛复用。

Ed25519 与 MultiEd25519 签名

Ed25519:RFC 8032 纯 EdDSA

ed25519.rs(源码)基于ed25519-dalek(fork 版ed25519-dalek-fiat)实现 RFC 8032 定义的 twisted Edwards 曲线纯 EdDSA 签名。三个核心类型Ed25519PrivateKeyEd25519PublicKeyEd25519Signature均以 dalek 对应类型做内部封装,长度常量直接透传:私钥、公钥、签名长度分别对应ed25519_dalekSECRET_KEY_LENGTHPUBLIC_KEY_LENGTHSIGNATURE_LENGTH(均为 32 / 32 / 64 字节)。

模块文档强调:签名验证还会检查并拒绝非规范化(non-canonical)签名——这正是 README 所说的"additional security checks (e.g. for malleability)",源码中的L常量(ed25519 群的阶)即用于规范化校验。此外,私钥默认不可Cloneassert-private-keys-not-cloneablefeature 下用static_assertions在编译期断言),仅在测试/fuzzing/cloneable-private-keys等 feature 下提供克隆能力,防止私钥意外复制传播。

用法示例(摘自习代码文档):

use diem_crypto_derive::{CryptoHasher, BCSCryptoHash}; use diem_crypto::{ ed25519::*, traits::{Signature, SigningKey, Uniform}, }; use rand::{rngs::StdRng, SeedableRng}; use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)] pub struct TestCryptoDocTest(String); let message = TestCryptoDocTest("Test message".to_string()); let mut rng: StdRng = SeedableRng::from_seed([0; 32]); let private_key = Ed25519PrivateKey::generate(&mut rng); let public_key: Ed25519PublicKey = (&private_key).into(); let signature = private_key.sign(&message); assert!(signature.verify(&message, &public_key).is_ok());

注意示例中的密钥生成走的是仅供测试的路径,生产代码应使用安全的密钥生成与托管方案。

MultiEd25519:可问责阈值多签

multi_ed25519.rs(源码)实现了基于 ed25519 曲线的**可问责阈值多签(accountable threshold multi-sig)**方案:

  • MultiEd25519PrivateKey/MultiEd25519PublicKey:由私钥/公钥向量加threshold: u8组成;
  • MultiEd25519Signature:由Vec<Ed25519Signature>加 4 字节位图(bitmap: [u8; 4])组成,位图用于把各签名映射到对应公钥(位从左到右读取,如[0b0001_0000, 0b0000_0000, 0b0000_0000, 0b0000_0001]表示第 3 与第 31 位被置位);
  • 构造约束:threshold不能为 0、密钥数量不能少于阈值(否则ValidationError)、密钥数量上限为 32(超过则WrongLengthError),常量MAX_NUM_OF_KEYS: usize = 32

测试覆盖见 multi_ed25519_test.rs,ed25519 相关测试见 ed25519_test.rs;此外还有针对 BCS 序列化往返与跨 trait 对象兼容性的 bcs_test.rs 与 compat_test.rs。

X25519 与 Noise:验证器间安全通信

X25519:Diffie-Hellman 密钥交换

x25519.rs(源码)是对x25519-dalek(fork 版x25519-dalek-fiat)的薄封装,用于 Diffie-Hellman 密钥交换。私钥、公钥、共享秘密长度均为 32 字节(PRIVATE_KEY_SIZEPUBLIC_KEY_SIZESHARED_SECRET_SIZE)。模块文档给出的建议是:整个代码库应尽量只使用x25519::PrivateKeyx25519::PublicKey,直到字节真正进入密码学运算为止——即用强类型包装隔离原始字节,防止误用。

十六进制编解码用法(摘自习代码文档):

use diem_crypto::{x25519, Uniform, test_utils::TEST_SEED}; use rand::{rngs::StdRng, SeedableRng}; // Derive an X25519 private key for testing. let mut rng: StdRng = SeedableRng::from_seed(TEST_SEED); let private_key = x25519::PrivateKey::generate(&mut rng); let public_key = private_key.public_key(); // Deserialize a hexadecimal private or public key use diem_crypto::traits::ValidCryptoMaterialStringExt; let private_key = "404acc8ec6a0f18df7359a6ee7823f19dd95616b10fed8bdb0de030e891b945a"; let private_key = x25519::PrivateKey::from_encoded_string(&private_key)?; let public_key = "080e287879c918794170e258bfaddd75acac5b3e350419044655e4983a487120"; let public_key = x25519::PublicKey::from_encoded_string(&public_key)?;

Noise:IK 握手协议的裁剪实现

noise.rs(源码)实现了Noise Protocol Framework的一个精简版本Noise_IK_25519_AESGCM_SHA256,即只实现 IK 握手所需部分,用于在 Diem 网络中加密和认证节点间通信。README 中"用于保护验证器之间通信"的表述在源码中得到印证:noise.rs明确写到 "We use in Diem to encrypt and authenticate communications between nodes of the network"。

模块还提示:若想利用 AES 硬件加速,需以RUSTFLAGS="-Ctarget-cpu=skylake -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3"编译本 crate。握手的完整用法示例(发起方 initiate_connection → 响应方 respond_to_client_and_finalize → 发起方 finalize_connection → 双方 write/read_message_in_place)见 noise.rs,测试与基准分别在 noise_test.rs、noise_cacophony.txt(官方向量)与 benches/noise.rs。

工程实践:feature 开关、编译护栏与测试体系

算术后端 feature(见 Cargo.toml):

  • 默认fiat:启用fiat_u64_backend,使用 fiat-crypto 形式化验证算术;
  • u64/u32:非 fiat 的 64/32 位后端;
  • fuzzing:启用proptestproptest-derivecloneable-private-keys
  • assert-private-keys-not-cloneable/cloneable-private-keys:控制私钥是否允许 Clone。

lib.rs通过compile_error!保证"必须恰好启用一个后端",否则编译直接失败,从源头杜绝误配。

密钥处理纪律:默认禁止私钥 Clone、SilentDebug/SilentDisplay派生宏避免私钥被日志打印泄露、测试路径与生产路径(如from_bytes_uncheckedgenerate_for_testing)明确分离,代码文档反复强调生产环境必须采用硬件或等效安全方案生成与存储私钥。

测试与基准unit_tests/覆盖哈希、HKDF、Ed25519、MultiEd25519、Noise、BCS 往返、兼容性及派生宏编译测试;benches/提供 ed25519 与 noise 的 Criterion 基准;test_vectors/noise_cacophony.txt保留官方互操作向量,用于验证协议实现的正确性。

小结

diem-crypto组件以traits.rs的类型安全 API 为骨架,串联起 SHA-3 哈希(含CryptoHasher域分离机制)、RFC 5869 HKDF 密钥派生、Ed25519/MultiEd25519 签名与 X25519+Noise 安全信道四类能力。它既是 Move 框架自带的密码学工具箱,也是理解 Aptos 账户密钥体系、网络层安全握手与链上多签方案的底层参照。读者可按本文给出的模块地图,在 third_party/move/move-examples/diem-framework/crates/crypto/ 下逐文件深入研读实现与测试。

【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-core

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 22:29:29

代码审查实战:从原则到落地,构建高效Code Review流程

1. 代码审查到底在审什么&#xff1a;先想清楚这件事值不值得做代码审查&#xff08;Code Review&#xff09;这词儿&#xff0c;但凡是写代码的&#xff0c;基本都听过。有些人觉得它是形式主义&#xff0c;走个过场点个赞就完事&#xff1b;有些人觉得它是团队里最有价值的一…

作者头像 李华
网站建设 2026/9/18 22:27:09

Flutter OHOS 端内存与 GPU 问题定位:从原理到实战排查指南

用 Flutter 做 OHOS 端应用&#xff0c;你迟早会撞上内存和 GPU 这两堵墙。我见过太多团队&#xff0c;功能都跑通了&#xff0c;一到真机压测就露馅&#xff1a;内存曲线一路涨不回头&#xff0c;列表滑两页开始掉帧&#xff0c;GPU 占用高得离谱&#xff0c;翻来覆去不知道从…

作者头像 李华
网站建设 2026/9/18 22:25:17

IDEA插件精选指南:提升开发效率的实用组合与避坑技巧

1. 装插件之前&#xff0c;先说说我的筛选标准每次看到有人晒 IDEA 界面&#xff0c;密密麻麻全是插件图标&#xff0c;我就觉得挺有意思——装插件这事&#xff0c;跟买工具很像&#xff0c;看着什么都想要&#xff0c;真正天天用的其实就那么几个。我前后用过至少上百款 IDEA…

作者头像 李华
网站建设 2026/9/18 22:24:42

ArcGIS地理配准与矢量化全流程实操指南

简介&#xff1a;本资源是一份完整的GIS专业本科生实验报告&#xff0c;面向地理信息科学、资源环境类相关专业初学者&#xff0c;系统覆盖ArcGIS Desktop核心操作技能训练。报告包含六大实验模块&#xff1a;ArcMap与ArcGlobe基础认知、影像地理配准&#xff08;含控制点选取与…

作者头像 李华
网站建设 2026/9/18 22:22:31

Spring Data JPA分页优化:用Slice替代Page,告别count查询

做后端的人应该都有过这种经历&#xff1a;一个接口平时跑得还行&#xff0c;一到列表页就发烫&#xff0c;查慢SQL日志&#xff0c;发现真正拖后腿的不是那几条业务查询&#xff0c;而是Spring Data JPA顺手帮你执行的那条count。我之前优化一个用户文章列表接口&#xff0c;业…

作者头像 李华