Tendermint crypto 密码学包完全指南:密钥接口、签名算法与 Amino 兼容 JSON 编码
区块链共识算法【免费下载链接】tendermint⟁ Tendermint Core (BFT Consensus) in Go项目地址https://gitcode.com/gh_mirrors/te/tendermint点击查看免费下载本文基于 crypto/README.md 展开深入 Tendermint Core 的密码学模块crypto系统讲解其抽象接口设计、三种签名算法Ed25519、Secp256k1、Sr25519的实现细节、二进制Protobuf与 JSON 两种编码规范并结合仓库源码给出可运行的实战用法。读完本文你将能正确导入并使用 Tendermint 的密钥接口完成密钥生成、签名、验签与地址计算并理解为什么 Tendermint 的 JSON 里会同时出现type与value两个字段。一、crypto 包是什么为 Tendermint 量身裁剪的密码学工具集crypto是 Tendermint 为自身使用场景适配、封装的密码学包。它在 Go 标准库与第三方密码学库如golang.org/x/crypto、btcec、go-schnorrkel之上抽象出一套统一、稳定的密钥与密码原语接口使得共识、P2P、ABCI 等上层模块可以不关心具体算法只依赖统一的PubKey/PrivKey接口完成签名与验证。从源码结构看crypto包crypto/由以下几部分组成子包 / 文件职责crypto.go核心接口定义PubKey、PrivKey、Symmetric、Addressed25519/Ed25519 签名算法实现Tendermint 默认验证人密钥算法secp256k1/Secp256k1 ECDSA 实现兼容 Bitcoin 地址风格sr25519/Sr25519 Schnorr 签名实现基于 Ristretto与 Polkadot 生态兼容encoding/密钥与 ProtobufPublicKey消息的双向转换armor/ASCII Armor 编解码类似 PGP 的-----BEGIN ...-----包装xsalsa20symmetric/基于 NaClsecretbox的对称加解密tmhash/SHA256 及截断到 20 字节的哈希工具merkle/确定性最小高度 Merkle 树哈希RFC 6962 风格此外crypto/version.go 声明了当前包版本Version 0.9.0-devcrypto/doc.go 给出了包的整体设计意图它是对 Go 标准库等价功能的包装便于 Tendermint 统一使用。二、导入方式接口包与算法子包的分层设计crypto/README.md明确给出了两种导入路径区分了「接口」与「具体算法」两个抽象层次// 1. 只需要接口PubKey / PrivKey / Symmetric / Address时导入根包 import github.com/tendermint/tendermint/crypto // 2. 需要具体算法实现时导入对应子包 import github.com/tendermint/tendermint/crypto/ed25519 import github.com/tendermint/tendermint/crypto/secp256k1 import github.com/tendermint/tendermint/crypto/sr25519这种分层设计的好处是上层业务代码如验证人管理、签名服务可以只依赖crypto根包的接口写逻辑把具体算法作为运行时注入的依赖而需要做密钥生成、类型断言或算法切换时才引入具体子包。三、核心接口PubKey、PrivKey 与 Addresscrypto包的核心抽象定义在 crypto/crypto.go 中。三种接口分别对应非对称公钥、非对称私钥与对称密码原语type PubKey interface { Address() Address Bytes() []byte VerifySignature(msg []byte, sig []byte) bool Equals(PubKey) bool Type() string } type PrivKey interface { Bytes() []byte Sign(msg []byte) ([]byte, error) PubKey() PubKey Equals(PrivKey) bool Type() string } type Symmetric interface { Keygen() []byte Encrypt(plaintext []byte, secret []byte) (ciphertext []byte) Decrypt(ciphertext []byte, secret []byte) (plaintext []byte, err error) }理解这几个接口是掌握整个crypto包的钥匙PubKey.Bytes()/PrivKey.Bytes()返回密钥的原始字节序列供序列化、落盘与网络传输使用VerifySignature(msg, sig)/Sign(msg)签名与验签的核心入口消息直接以字节数组形式传入Type()返回算法类型字符串ed25519、secp256k1、sr25519用于在 JSON/Protobuf 编码时区分具体实现Equals密钥相等性比较。值得注意的实现细节是各算法子包的Equals都使用crypto/subtle.ConstantTimeCompare如 ed25519.go或bytes.Equal进行常数时间比较以避免因比较耗时差异引发时序侧信道攻击。Address更紧凑的标识符Address是公钥的紧凑标识在 Tendermint 中被广泛用作验证人地址。其定义位于 crypto/crypto.goconst ( // AddressSize is the size of a pubkey address. AddressSize tmhash.TruncatedSize // 20 字节 ) type Address bytes.HexBytes func AddressHash(bz []byte) Address { return Address(tmhash.SumTruncated(bz)) }关键点AddressSize固定为 20 字节tmhash.TruncatedSize见 crypto/tmhash/hash.goAddress是bytes.HexBytes的类型别名即使在 JSON 中也以十六进制字符串形式编码这是 Tendermint 对地址类字节数据的一贯约定AddressHash是通用的「SHA256 取前 20 字节」地址计算函数具体算法的Address()方法会在此基础上叠加各自的差异见下文。四、三种签名算法实现细节与差异对照4.1 Ed25519Tendermint 默认的验证人签名算法实现位于 crypto/ed25519/ed25519.go基于标准库crypto/ed25519RFC 8032。其类型常量一目了然const ( PrivKeyName tendermint/PrivKeyEd25519 PubKeyName tendermint/PubKeyEd25519 PubKeySize 32 PrivateKeySize 64 SignatureSize 64 SeedSize 32 KeyType ed25519 )私钥 64 字节前 32 字节为随机种子后 32 字节为拼接的压缩公钥Sign与PubKey()均依赖这一布局见 ed25519.go公钥 32 字节压缩 Edwards25519 点签名 64 字节压缩点 域元素各 32 字节。地址计算规则为SHA256(pubkey) 截取前 20 字节对应 ed25519.gofunc (pubKey PubKey) Address() crypto.Address { if len(pubKey) ! PubKeySize { panic(pubkey is incorrect size) } return crypto.Address(tmhash.SumTruncated(pubKey)) }密钥生成有两种方式ed25519.goGenPrivKey()从操作系统随机源crypto.CReader()即crypto/rand.Reader读取 32 字节种子生成GenPrivKeyFromSecret(secret)对秘密做crypto.Sha256得到 32 字节种子后生成。源码注释特别提醒若该 secret 来自用户口令应先经过 KDF如 bcrypt处理避免直接用弱口令。4.2 Secp256k1兼容 Bitcoin 生态的 ECDSA实现位于 crypto/secp256k1/secp256k1.go底层基于github.com/btcsuite/btcd/btcec。它与 Ed25519 在多个维度上存在显著差异公钥采用压缩格式固定 33 字节PubKeySize 331 字节前缀 0x02/0x03 标识 y 坐标奇偶性 32 字节 x 坐标见 secp256k1.go私钥 32 字节PrivKeySize 32地址采用 Bitcoin 风格RIPEMD160(SHA256(pubkey))见 secp256k1.go签名是 R || S 的 64 字节定长序列先对消息做crypto.Sha256再做 ECDSA签名序列化时 R、S 各自左补零到 32 字节见 secp256k1.go 与serializeSig。该实现还内建了防签名延展性malleability保护验签时若S secp256k1halfN即 S 不在 lower-S 形式直接拒绝这与 libsecp256k1 的行为保持一致见 secp256k1.go。密钥生成同样提供随机生成GenPrivKey()与确定性生成GenPrivKeySecp256k1(secret)。确定性版本严格遵循 FIPS 186-3 附录 A.2.1 的方法保证私钥是合法的域元素// c sha256(secret) // k (c mod (n − 1)) 1其中 n 为曲线阶4.3 Sr25519Schnorr 签名Ristretto 群实现位于 crypto/sr25519/底层基于github.com/ChainSafe/go-schnorrkel与 Polkadot/Substrate 生态使用的签名算法同源。其常量定义在 crypto/sr25519/privkey.go 与 crypto/sr25519/pubkey.goPrivKeySize 32PubKeySize 32SignatureSize 64crypto/sr25519/encoding.go类型标识为sr25519JSON 类型名为tendermint/PrivKeySr25519、tendermint/PubKeySr25519。签名流程privkey.go私钥经NewMiniSecretKeyFromRaw→ExpandEd25519()扩展为正式密钥再在签名上下文NewSigningContext带空上下文下对消息签名。地址计算与 Ed25519 相同SHA256(pubkey)[:20]pubkey.go。4.4 三种算法速查表维度Ed25519Secp256k1Sr25519算法类型Edwards25519 曲线签名ECDSA椭圆曲线 secp256k1SchnorrRistretto 群公钥长度32 字节33 字节压缩32 字节私钥长度64 字节seed 32 pubkey 3232 字节32 字节签名长度64 字节64 字节R||S64 字节地址算法SHA256(pubkey)[:20]RIPEMD160(SHA256(pubkey))SHA256(pubkey)[:20]防延展性天然具备要求 lower-S天然具备底层依赖标准库crypto/ed25519btcd/btcecChainSafe/go-schnorrkel说明地址/签名的十六进制与 base64 形态请对照 crypto/README.md 中的 JSON 示例见下文第六节更完整的地址与签名规范描述见 spec/core/encoding.md。五、二进制编码Protobuf 与定长字节约定crypto/README.md指出二进制编码遵循Tendermint 编码规范即仓库内的 spec/core/encoding.md。该规范的核心约定包括所有数据结构使用 Protocol Buffersproto3序列化spec/core/encoding.md字节数组 UVarint 长度前缀 原始字节例如字节数组[0xA, 0xB]编码为0x020A0Bspec/core/encoding.md哈希统一使用 SHA256且「先序列化、后哈希」SHA256(obj)等价于SHA256(ProtoEncoding(obj))spec/core/encoding.md。针对公钥Protobuf 中通过Oneof 区分不同算法类型。仓库内的 proto/tendermint/crypto/keys.proto 定义如下// PublicKey defines the keys available for use with Tendermint Validators message PublicKey { option (gogoproto.compare) true; option (gogoproto.equal) true; oneof sum { bytes ed25519 1; bytes secp256k1 2; } }对应的 Go 转换实现位于 crypto/encoding/codec.goPubKeyToProto(k crypto.PubKey) (pc.PublicKey, error)把crypto.PubKey按具体类型包装进PublicKey的 oneof 字段codec.goPubKeyFromProto(k pc.PublicKey) (crypto.PubKey, error)反向转换并严格校验长度——Ed25519 公钥必须是 32 字节、Secp256k1 必须是 33 字节否则返回错误codec.goinit()中通过json.RegisterType注册了tendermint.crypto.PublicKey等 Protobuf 类型的 JSON 类型名codec.go。六、JSON 编码Amino 兼容的 type/value 包装6.1 为什么要用内部 JSON 编码器crypto/README.md明确指出JSON 编码使用 Tendermint 内部的 JSON 编码器其设计文档即 libs/json/doc.go。该包的目标是提供与 Amino JSON 编码向后兼容的序列化行为与标准库encoding/json主要有两点差异64 位整数编码为字符串而非数字避免 JavaScript 等 64 位浮点只有 53 位精度的问题int32(32) // Output: 32 int64(64) // Output: 64 uint64(64) // Output: 64对应实现见 libs/json/encoder.go接口类型使用{type:类型名,value:值}包装即所谓的类型信封type envelope。6.2 类型注册机制类型信封的名称来自全局类型注册表。json.RegisterTypelibs/json/types.go把 Go 类型与字符串名绑定并要求只能在init()中调用重名或重复注册会 panic。各算法子包正是通过init()完成注册例如crypto/ed25519/ed25519.go 注册tendermint/PrivKeyEd25519、tendermint/PubKeyEd25519crypto/secp256k1/secp256k1.go 注册tendermint/PrivKeySecp256k1、tendermint/PubKeySecp256k1crypto/sr25519/encoding.go 注册tendermint/PrivKeySr25519、tendermint/PubKeySr25519。编码时encodeReflectInterface会查找具体类型的注册名并输出信封libs/json/encoder.go字节切片则按 base64 编码libs/json/encoder.go这就是密钥 JSON 中value字段是 base64 字符串的原因。6.3 README 中的完整 JSON 编码示例以下六条示例来自 crypto/README.md 原文全部为仓库内真实可验证的编码形态ed25519.PrivKey - {type:tendermint/PrivKeyEd25519,value:EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ} ed25519.PubKey - {type:tendermint/PubKeyEd25519,value:AT/aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE} sr25519.PrivKeySr25519 - {type:tendermint/PrivKeySr25519,value:xtYVH8UCIqfrY8FIFc0QEpAEBShSG4NT0zlEOVSZ2w4} sr25519.PubKeySr25519 - {type:tendermint/PubKeySr25519,value:8sKBLKQ/OoXMcAJVxBqz1U7TyxRFQ5cmliuHy4MrF0s} crypto.PrivKeySecp256k1 - {type:tendermint/PrivKeySecp256k1,value:zx4Pnh67Ng2V5vZbQzEyRerX9c4ccNZOVzM9RvJ0Y} crypto.PubKeySecp256k1 - {type:tendermint/PubKeySecp256k1,value:A8lPKJXcNl5VHt1FK8a244K9EJuS4WX1hFBnwisi0IJx}可以验证的规律每个对象的type字段就是init()中注册的类型名value字段是密钥原始字节的 base64 编码Ed25519 与 Secp256k1 公钥的value长度分别对应 32 与 33 字节展开后的 base64前者的 base64 以结尾提示原始长度恰为 32 字节的倍数后者 33 字节则无填充符号同一算法体系的私钥value比公钥长Ed25519 私钥 64 字节、Secp256k1 私钥 32 字节但示例对应公钥 33 字节编码形态不同与上文第四节的密钥长度表完全吻合。关于信封格式的通用定义spec/core/encoding.md 也给出了权威说明注册类型统一编码为{type:type name,value:JSON}value即公钥原始字节的 base64。七、实战用法生成、签名、验签与哈希7.1 密钥生成与派生import ( github.com/tendermint/tendermint/crypto github.com/tendermint/tendermint/crypto/ed25519 ) // 1. 随机生成使用操作系统熵源 crypto/rand.Reader privKey : ed25519.GenPrivKey() pubKey : privKey.PubKey() // 2. 从秘密确定性派生秘密应来自 KDF如 bcrypt 输出 privKey2 : ed25519.GenPrivKeyFromSecret([]byte(some-kdf-output)) // 3. 拿到公钥地址20 字节hex 编码 addr : pubKey.Address()三种算法子包都提供GenPrivKey()与GenPrivKeyFromSecret()签名接口完全一致可无缝替换。7.2 签名与验签msg : []byte(This is Tendermint) sig, err : privKey.Sign(msg) // PrivKey.Sign ok : pubKey.VerifySignature(msg, sig) // PubKey.VerifySignature注意算法差异对调用方的影响Ed25519/Sr25519 直接对消息字节签名验签Secp256k1 内部会先对消息做 SHA256 再签名见 secp256k1.go并强制要求签名长度为 64 字节、S 为 lower-S 形式否则验签直接返回false。7.3 哈希工具根包提供 SHA256 便捷封装crypto/hash.gosum : crypto.Sha256([]byte(This is Tendermint)) fmt.Printf(%x\n, sum) // f91afb642f3d1c87c17eb01aae5cb65c242dfdbe7cf1066cc260f4ce5d33b94e该输出正是仓库内 crypto/example_test.go 的测试断言结果可直接作为自测基准。需要 20 字节截断哈希时使用tmhash.SumTruncatedcrypto/tmhash/hash.go它是所有公钥地址计算的基础。7.4 对称加密与 ASCII Armor对于需要对称加密的场景crypto提供基于 NaClsecretbox的 XSalsa20-Poly1305 实现crypto/xsalsa20symmetric/symmetric.goEncryptSymmetric(plaintext, secret)要求 secret 恰好 32 字节输出 24 字节随机 nonce 密文总长比明文多secretbox.Overhead 24字节DecryptSymmetric(ciphertext, secret)反向解密密文过短或认证失败均返回错误源码注释建议 secret 由Sha256(Bcrypt(passphrase))之类的方式从口令派生。而 crypto/armor/armor.go 提供了EncodeArmor/DecodeArmor用于把密钥字节包装为可读的 ASCII 块可携带 headers 元数据适合密钥导出、备份与文本传输场景。7.5 随机数工具crypto/random.go 提供仅依赖 OS 熵源的随机数接口CRandBytes(numBytes)返回加密安全随机字节CRandHex(numDigits)返回十六进制字符串文档注释指出CRandHex(24)提供 96 位随机性通常足够大多数用途CReader()返回crypto/rand.Reader即各GenPrivKey的默认熵源。八、小结Tendermint 的crypto包是一个「接口统一、算法可插拔、编码规范严谨」的密码学基础设施接口层crypto根包定义了PubKey/PrivKey/Symmetric与 20 字节Address让上层代码与具体算法解耦算法层ed25519、secp256k1、sr25519子包分别提供 64/33/32 字节公钥、不同地址算法SHA256 截断 vs Bitcoin RIPEMD160、不同签名语义并通过Type()与注册名区分编码层二进制走 Protobufproto/tendermint/crypto/keys.proto crypto/encoding/codec.goJSON 走 Amino 兼容的内部编码器libs/json/密钥统一以{type:tendermint/...,value:base64}形态出现在日志、RPC 与配置中。掌握这些约定无论是解析节点日志中的公钥字段、编写验证人密钥管理工具还是为 Tendermint 上层应用接入签名服务都能做到心中有数、手到擒来。更多编码细节可继续研读 spec/core/encoding.md 与各子包的测试文件如 crypto/ed25519/ed25519_test.go、crypto/secp256k1/secp256k1_test.go。赞分享区块链共识算法【免费下载链接】tendermint⟁ Tendermint Core (BFT Consensus) in Go项目地址https://gitcode.com/gh_mirrors/te/tendermint点击查看免费下载相关推荐Crypto公钥密码学RSA加密与签名实践终极指南Crypto公钥密码学RSA加密与签名实践终极指南 Crypto是一个强大的免费C加密库提供了完整的RSA公钥密码学实现。RSA加密是现代安全通密码学后端cryptography 椭圆曲线密码学EC完整指南密钥生成、ECDSA 签名与 ECDH 密钥交换cryptography 椭圆曲线密码学EC完整指南密钥生成、ECDSA 签名与 ECDH 密钥交换 本文基于 cryptography 官方文档 ec.密码学cryptography 库 RSA 公钥密码完全指南密钥生成、序列化、签名与加密实战cryptography 库 RSA 公钥密码完全指南密钥生成、序列化、签名与加密实战 RSA 是 cryptography 库中历史最悠久、使用最广泛的非对密码学上一篇终极指南PHPExcel多工作表操作的完整技巧创建、复制与删除下一篇Linux Wallpaper Engine JSON配置文件详解从场景对象到相机设置创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考