Dilithium-5 后量子签名开发者指南
数据来源:MSG Chain 代码库核实
主网状态: No-Go — 当前 MSGChain 主网裁决为 No-Go,以下内容反映代码实际状态,不代表生产可用。
MSG Chain 后量子密码学实战手册
第一章:概述
1.1 为什么后量子密码学至关重要
量子计算的发展对当前公钥密码体系构成了根本性威胁。Shor算法可以在多项式时间内解决大整数分解和离散对数问题,这意味着一旦足够强大的量子计算机问世:
- RSA — 完全破解
- ECDSA / ECDH — 完全破解
- Secp256k1 — 完全破解(比特币、以太坊、Cosmos生态广泛使用)
- Ed25519 — 完全破解
这一威胁被称为"Q-Day"——预计可能在2030年前后到来。对于区块链网络而言,迁移到后量子密码学(PQC)是至关重要的,因为:
- 长期安全:区块需要长期不可篡改,今天签名的交易在10年后仍需安全
- "先捕获,后解密"攻击:攻击者可以现在收集加密数据,等量子计算机可用后再解密
- 共识安全:验证者密钥一旦被破解,攻击者可伪造区块,破坏整个网络
MSG Chain采取"PQ优先"(Post-Quantum First)策略,将Dilithium-5作为默认签名方案,从根本上解决了这些威胁。
1.2 Dilithium-5:NIST标准ML-DSA(FIPS 204)
CRYSTALS-Dilithium是由IBM、瑞士苏黎世联邦理工学院(ETH Zurich)等机构设计的格密码签名方案。2024年8月,NIST正式将其标准化为ML-DSA(Module-Lattice-Based Digital Signature Algorithm),编号FIPS 204。
Dilithium提供三个安全等级:
| 参数集 | NIST安全等级 | 等效对称安全 | 公钥大小 | 签名大小 |
|---|---|---|---|---|
| Dilithium-2 | 2 | AES-128 | 1,312 B | 2,420 B |
| Dilithium-3 | 3 | AES-192 | 1,952 B | 3,309 B |
| Dilithium-5 | 5 | AES-256 | 2,592 B | 4,595 B |
MSG Chain选择Dilithium-5——最高的NIST安全等级,提供与AES-256相当的安全强度。
1.3 MSG Chain的PQ优先架构
MSG Chain是首个从创世块开始就原生使用Dilithium-5签名的区块链网络。架构特点:
+-----------------------------------------------------------+
| MSG Chain 节点 |
+-----------------------------------------------------------+
| 共识层 (CometBFT / Tendermint) |
| +-- 验证者密钥: Dilithium-5 |
| +-- 区块签名: Dilithium-5 |
| +-- DAR (确定性问责轮次): Dilithium-5 |
+-----------------------------------------------------------+
| 应用层 (Cosmos SDK) |
| +-- 交易签名: Dilithium-5 (默认) |
| +-- 地址派生: Dilithium-5 -> bech32(msg...) |
| +-- Secp256k1: 向后兼容 |
+-----------------------------------------------------------+
| 密钥管理层 |
| +-- priv_validator_key.json (Dilithium-5) |
| +-- node_key.json (Dilithium-5) |
| +-- Keyring (Cosmos SDK密钥环) |
+-----------------------------------------------------------+
1.4 Dilithium-5 vs Secp256k1 对比
| 特性 | Secp256k1 | Dilithium-5 | 差异说明 |
|---|---|---|---|
| 算法类型 | ECDSA (椭圆曲线) | ML-DSA (格密码) | 完全不同的数学基础 |
| 量子抗性 | 否 | 是 | Secp256k1可被Shor算法破解 |
| 公钥大小 | 33 B (压缩) | 2,592 B | Dilithium大约78倍 |
| 签名大小 | 70-72 B | 4,595 B | Dilithium大约64倍 |
| 签名速度 | ~0.3ms | ~0.8ms | Dilithium慢约2.7倍 |
| 验证速度 | ~1.0ms | ~0.2ms | Dilithium快约5倍 |
| 密钥生成速度 | ~0.1ms | ~0.5ms | Dilithium慢约5倍 |
| 安全基础 | ECDLP | Module-SIS/LWE | 量子安全 vs 非量子安全 |
| NIST标准化 | 否 | FIPS 204 | 官方PQC标准 |
| MSG Chain支持 | 兼容模式 | 原生/默认 | 双轨并进 -> PQ-only |
关键结论:Dilithium-5虽然密钥和签名更大、签名稍慢,但验证速度快5倍,且提供量子安全。对于MSG Chain而言,验证性能更为关键(全节点验证所有交易)。
1.5 适用场景
- 验证者节点:共识密钥必须使用Dilithium-5
- DApp开发:推荐使用Dilithium-5签名交易
- 钱包开发:优先派生Dilithium-5地址
- 桥接/跨链:使用Dilithium-5验证MSG Chain交易证明
- 合规审计:满足未来FIPS 204合规要求
第二章:密码学基础
2.1 基于格的密码学
Dilithium的安全性建立在格密码学(Lattice-based Cryptography)之上。格是数学中一个基本概念:
定义:格是R^n空间中一个离散的加法子群。
形式化:L = { sum_i a_i * b_i | a_i in Z },其中b_i是线性无关的基向量
在二维空间中,格可以想象成平面上按一定规律重复排列的点阵。高维格中的"最近向量问题"(CVP)和"最短向量问题"(SVP)被认为是量子计算无法高效求解的难题。
2.2 Module-LWE与Module-SIS
Dilithium依赖两个核心困难问题:
Module-LWE (Learning With Errors)
定义:给定 (A, t = As + e),求s困难
其中:
A <- R_q^(kk) 随机矩阵
s <- S_eta^k 秘密向量(小系数)
e <- S_eta^k 误差向量(小系数)
R_q = Z_q[X]/(X^n + 1) 多项式环
直观理解:给定一个线性方程组加上一些随机噪声,恢复原始解是困难的。量子计算机也无法有效求解。
Module-SIS (Short Integer Solution)
定义:给定A in R_q^(kl),求非零z使得Az = 0且||z||小
Module-SIS保证了签名的不可伪造性:攻击者无法为新的消息伪造有效签名。
2.3 Dilithium-5参数
Dilithium-5的具体参数:
| 参数 | 符号 | Dilithium-5值 | 说明 |
|---|---|---|---|
| 模数 | q | 8,380,417 | 素数,约2^23 |
| 环维度 | n | 256 | 多项式环 X^256 + 1 |
| 模块秩 | k | 5 | 矩阵行数(安全等级5) |
| 模块秩 | l | 4 | 矩阵列数 |
| 掩码比特 | d | 14 | hint的压缩位数 |
| 挑战范围 | gamma1 | 2^19 | 挑战范围 |
| 零化阈值 | gamma2 | (q-1)/88 | 拒绝采样阈值 |
| 小系数范围 | eta | 2 | 秘密/误差系数范围 |
| 挑战权重 | tau | 60 | 挑战向量中+-1的数量 |
| beta | beta | 175 | eta*tau |
| 公钥大小 | pk | 2,592 B | 按FIPS 204标准序列化 |
| 签名大小 | sig | 4,595 B | 按FIPS 204标准序列化 |
参数含义详解:
-
k=5:安全等级5的标识。Dilithium-2的k=4,Dilithium-3的k=5但使用不同参数,Dilithium-5的k=5且参数更强。更大的k意味着更高的安全性和更大的密钥/签名。
-
eta=2:秘密向量s和误差向量e的每个系数在{-2,-1,0,1,2}范围内。小系数确保计算结果的分布特性。
-
tau=60:签名中挑战向量的汉明重量。挑战c有60个系数为+-1,其余为0。这提供了足够的熵确保安全性。
-
gamma1=2^19:mask向量的系数范围[-gamma1+1, gamma1]。较大的gamma1确保拒绝采样过程的正确性。
2.4 密钥生成算法
Dilithium-5密钥生成过程(简化表达):
Algorithm: Dilithium.KeyGen()
Input: 随机种子 xi
Output: 公钥 pk, 私钥 sk
1. zeta <- H(xi) // 派生确定性随机数
2. rho, rho' <- H(zeta) // 生成矩阵种子和掩码种子
3. A <- ExpandA(rho) // 用SHAKE-128展开矩阵A in R_q^(k*k)
4. s1, s2 <- ExpandS(rho') // 从种子生成秘密向量
5. t = A*s1 + s2 // 计算公钥 t
6. t1 = Power2Round(t, d) // 压缩t为t1(高位)
7. t0 = t - t1*2^d // 保存低位供签名使用
8. pk = (rho, t1) // 公钥 = 矩阵种子 + t1
9. sk = (rho, rho', K, s1, s2, t0) // 私钥包含所有必要状态
10. return (pk, sk)
关键点:
- 公钥实际上是伪随机的:由种子rho通过SHAKE-128扩展出整个矩阵A
- 私钥存储t0是为了签名时恢复t的低位信息
- K用于派生确定性的签名随机数(防止随机数重用攻击)
在Go SDK中,此过程封装为:
// 实际使用只需一行调用
privKey, err := dilithium.GenKeyV5(rand.Reader)
2.5 签名算法
Algorithm: Dilithium.Sign(sk, M)
Input: 私钥 sk, 消息 M
Output: 签名 sigma
1. mu <- H(M || pk) // 哈希消息+公钥防止密钥替换攻击
2. kappa <- 0 // 重试计数器
3. repeat:
4. y <- ExpandMask(rho', kappa) // 产生掩码 y
5. w = A*y // 计算承诺 w
6. w1 = HighBits(w, 2*gamma2) // 提取w的高位
7. c <- H(mu || w1) // 生成挑战 c (权重tau)
8. z = y + c*s1 // 计算响应 z
9. r1 = LowBits(w - c*s2, 2*gamma2) // 检查是否可分解
10. if ||z||_inf >= gamma1 - beta // 拒绝采样条件1
11. or ||r1||_inf >= gamma2 - beta // 拒绝采样条件2
12. kappa++ ; continue
13. h <- MakeHint(-c*t0, w - c*s2 + c*t0)
14. if count(h) > omega // hint中1的数量限制
15. kappa++ ; continue
16. until valid
17. sigma = (c, z, h) // 签名 = 挑战 + 响应 + hint
18. return sigma
拒绝采样(Rejection Sampling)是Dilithium的核心技术。因为z = y + c*s1可能泄露s1的信息,如果z太大(范数超过阈值),就丢弃并重试。平均需要约4次才能产生一个有效签名。这就是为什么签名比验证慢得多。
2.6 验证算法
Algorithm: Dilithium.Verify(pk, M, sigma)
Input: 公钥 pk, 消息 M, 签名 sigma
Output: 有效/无效
1. mu <- H(M || pk)
2. A <- ExpandA(rho) // 重建矩阵A
3. c, z, h <- sigma // 解构签名
4. w1' = UseHint(h, A*z - c*t1*2^d)
5. c' <- H(mu || w1')
6. if c' == c and ||z||_inf < gamma1 - beta
7. return 有效
8. else
9. return 无效
验证的优势:验证过程不需要知道t0(只用了t1),也不需要秘密参数。验证的计算量比签名小得多——这是签名方案中理想的性质,因为区块链上验证操作的频率远高于签名操作。
2.7 安全假设
Dilithium-5的安全性归约为两个格的困难问题:
Module-SIS(签名不可伪造性)
攻击者无法伪造签名,即使他们可以看到许多有效签名。这归约为:给定随机矩阵A in R_q^(kl),找到短向量z使得Az = 0是困难的。
归约路径:
签名伪造 -> Module-SIS问题 -> SVP_gamma问题 -> 格中困难问题
Module-LWE(密钥恢复)
攻击者无法从公钥恢复私钥。这归约为:给定(A, t = A*s1 + s2),恢复(s1, s2)是困难的。
参数安全性:
- Module-LWE安全性:k=5, eta=2时,已知最佳攻击需要约2^156次操作
- Module-SIS安全性:同样提供超过NIST等级5要求的128位后量子安全强度
侧信道安全性
Dilithium的确定性变体(使用rand.Reader提供种子)对所有消息使用相同随机数,避免了DSA类方案中随机数重用的灾难性后果。
2.8 性能特征
在典型x86-64处理器(Intel Xeon 3.0GHz)上的性能数据:
| 操作 | Dilithium-2 | Dilithium-3 | Dilithium-5 |
|---|---|---|---|
| 密钥生成 | ~80,000 cycles | ~120,000 cycles | ~180,000 cycles |
| 签名 | ~350,000 cycles | ~550,000 cycles | ~780,000 cycles |
| 验证 | ~80,000 cycles | ~110,000 cycles | ~150,000 cycles |
| 公钥大小 | 1,312 B | 1,952 B | 2,592 B |
| 签名大小 | 2,420 B | 3,309 B | 4,595 B |
MSG Chain环境实测数据
在MSG Chain验证者节点上的基准测试:
$ make test-quantum
=== RUN TestDilithium5_KeyGen
--- PASS: TestDilithium5_KeyGen (0.0005s)
=== RUN TestDilithium5_Sign
--- PASS: TestDilithium5_Sign (0.0008s)
=== RUN TestDilithium5_Verify
--- PASS: TestDilithium5_Verify (0.0002s)
=== RUN TestDilithium5_TxSigning
--- PASS: TestDilithium5_TxSigning (0.0031s)
交易大小影响
由于Dilithium-5签名大小为4,595字节,交易大小也会相应变化:
| 交易类型 | Secp256k1 | Dilithium-5 | 增加比例 |
|---|---|---|---|
| MsgSend | ~200 B | ~4,800 B | ~24x |
| MsgDelegate | ~250 B | ~4,850 B | ~19x |
| MsgBeginRedelegate | ~300 B | ~4,900 B | ~16x |
| MsgUndelegate | ~250 B | ~4,850 B | ~19x |
这意味着区块大小和Gas计算需要相应调整。MSG Chain的区块Gas限制已考虑Dilithium-5签名开销。
第三章:密钥生成与管理
3.1 生成Dilithium-5密钥对
Go SDK
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
"log"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func main() {
// 生成Dilithium-5私钥
privKey, err := dilithium.GenKeyV5(rand.Reader)
if err != nil {
log.Fatalf("密钥生成失败: %v", err)
}
// 提取公钥
pubKey := privKey.PubKey()
privHex := hex.EncodeToString(privKey.Bytes())
pubHex := hex.EncodeToString(pubKey.Bytes())
fmt.Printf("私钥 (hex): %s\n", privHex)
fmt.Printf("私钥长度: %d 字节\n", len(privKey.Bytes()))
fmt.Printf("公钥 (hex): %s\n", pubHex)
fmt.Printf("公钥长度: %d 字节\n", len(pubKey.Bytes()))
addr := pubKey.Address()
fmt.Printf("地址: %s\n", addr.String())
fmt.Printf("密钥类型: %s\n", privKey.Type())
fmt.Printf("公钥类型: %s\n", pubKey.Type())
}
输出示例:
私钥 (hex): a1b2c3d4... (共约4,864字节)
私钥长度: 4864 字节
公钥 (hex): e5f6a7b8... (共2,592字节)
公钥长度: 2592 字节
地址: msg1q8lkvgzck8wkz3x9v6xkqf4u5a3d7e2n9jxc4p
密钥类型: dilithium-5
公钥类型: dilithium-5
Rust SDK
use msg_chain_crypto::dilithium::{DilithiumKeypair, DilithiumPublicKey};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let keypair = DilithiumKeypair::generate()?;
let secret_bytes = keypair.secret_key_bytes();
let public_bytes = keypair.public_key_bytes();
println!("私钥 (hex): {}", hex::encode(&secret_bytes));
println!("私钥长度: {} 字节", secret_bytes.len());
println!("公钥 (hex): {}", hex::encode(&public_bytes));
println!("公钥长度: {} 字节", public_bytes.len());
let address = keypair.address();
println!("地址: {}", address);
let keypair_from_bytes = DilithiumKeypair::from_secret_key_bytes(&secret_bytes)?;
assert_eq!(keypair.public_key_bytes(), keypair_from_bytes.public_key_bytes());
Ok(())
}
Cargo.toml依赖:
[dependencies]
msg-chain-crypto = "0.3"
hex = "0.4"
Python SDK
from msgchain_sdk.crypto import Dilithium5
keypair = Dilithium5.generate()
private_key_hex = keypair.private_key.hex()
public_key_hex = keypair.public_key.hex()
print(f"私钥 (hex): {private_key_hex}")
print(f"私钥长度: {len(keypair.private_key)} 字节")
print(f"公钥 (hex): {public_key_hex}")
print(f"公钥长度: {len(keypair.public_key)} 字节")
address = keypair.address
print(f"地址: {address}")
keypair2 = Dilithium5.from_hex(private_key_hex)
assert keypair2.public_key.hex() == public_key_hex
安装:
pip install msgchain-sdk
TypeScript SDK
import { DilithiumKeypair } from '@msg-chain/crypto';
const keypair = DilithiumKeypair.generate();
const privateKeyHex = keypair.getPrivateKey('hex');
const publicKeyHex = keypair.getPublicKey('hex');
console.log(`私钥 (hex): ${privateKeyHex}`);
console.log(`私钥长度: ${keypair.getPrivateKey().length} 字节`);
console.log(`公钥 (hex): ${publicKeyHex}`);
console.log(`公钥长度: ${keypair.getPublicKey().length} 字节`);
const address = keypair.getAddress();
console.log(`地址: ${address}`);
const keypair2 = DilithiumKeypair.fromHex(privateKeyHex);
console.log(keypair2.getAddress());
安装:
npm install @msg-chain/crypto@alpha
3.2 密钥存储格式(JSON/Amino)
MSG Chain使用Cosmos SDK的Amino编码格式存储密钥。
priv_validator_key.json
验证者共识密钥存储在~/.msgchain/config/priv_validator_key.json:
{
"address": "msg1q8lkvgzck8wkz3x9v6xkqf4u5a3d7e2n9jxc4p",
"pub_key": {
"type": "tendermint/PubKeyDilithium5",
"value": "CukEAgDwuJ0AAQAAACAAgACAgP..."
},
"priv_key": {
"type": "tendermint/PrivKeyDilithium5",
"value": "A6cFAgDwuJ0AAAAAIA..."
}
}
Amino注册类型:
import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func RegisterDilithiumCrypto(cdc *codec.LegacyAmino) {
cdc.RegisterConcrete(&dilithium.PubKeyDilithium5{},
"tendermint/PubKeyDilithium5", nil)
cdc.RegisterConcrete(&dilithium.PrivKeyDilithium5{},
"tendermint/PrivKeyDilithium5", nil)
}
node_key.json
节点P2P身份密钥存储在~/.msgchain/config/node_key.json:
{
"key": {
"type": "tendermint/PrivKeyDilithium5",
"value": "B8dFAgDwuJ0AAAAAIA..."
}
}
3.3 BIP39助记词到Dilithium-5种子派生
Go实现
package main
import (
"crypto/rand"
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/go-bip39"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func DeriveDilithium5Key(mnemonic, passphrase string) (*dilithium.PrivKeyDilithium5, error) {
seed := bip39.NewSeed(mnemonic, passphrase)
master, ch := hd.ComputeMastersFromSeed(seed)
derivedPriv, err := hd.DerivePrivateKeyForPath(master, ch, "44'/118'/0'/0/0")
if err != nil {
return nil, fmt.Errorf("BIP32派生失败: %w", err)
}
privKey, err := dilithium.GenKeyV5WithSeed(derivedPriv)
if err != nil {
return nil, fmt.Errorf("Dilithium-5密钥生成失败: %w", err)
}
return privKey, nil
}
func GenerateMnemonic() (string, error) {
entropy := make([]byte, 32)
_, err := rand.Read(entropy)
if err != nil {
return "", err
}
mnemonic, err := bip39.NewMnemonic(entropy)
if err != nil {
return "", err
}
return mnemonic, nil
}
func main() {
mnemonic, err := GenerateMnemonic()
if err != nil {
log.Fatal(err)
}
fmt.Printf("助记词: %s\n", mnemonic)
privKey, err := DeriveDilithium5Key(mnemonic, "")
if err != nil {
log.Fatal(err)
}
fmt.Printf("派生地址: %s\n", privKey.PubKey().Address().String())
}
Rust实现
use msg_chain_crypto::dilithium::DilithiumKeypair;
use msg_chain_crypto::bip39::{Mnemonic, Language, Seed};
use msg_chain_crypto::hd::derive_dilithium_key;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mnemonic = Mnemonic::generate(Language::ChineseSimplified, 24)?;
println!("助记词: {}", mnemonic);
let seed = Seed::new(&mnemonic, "");
let keypair = derive_dilithium_key(seed.as_bytes(), "m/44'/118'/0'/0/0")?;
println!("地址: {}", keypair.address());
println!("公钥: {}", hex::encode(keypair.public_key_bytes()));
let seed2 = Seed::new(&mnemonic, "");
let keypair2 = derive_dilithium_key(seed2.as_bytes(), "m/44'/118'/0'/0/0")?;
assert_eq!(keypair.address(), keypair2.address());
Ok(())
}
Python实现
from msgchain_sdk.crypto import Dilithium5
from msgchain_sdk.bip39 import generate_mnemonic, seed_from_mnemonic
from msgchain_sdk.hd import derive_dilithium_key
mnemonic = generate_mnemonic(strength=256)
print(f"助记词: {mnemonic}")
seed = seed_from_mnemonic(mnemonic, passphrase="")
private_key_bytes = derive_dilithium_key(seed, "m/44'/118'/0'/0/0")
keypair = Dilithium5.from_bytes(private_key_bytes)
print(f"地址: {keypair.address}")
print(f"公钥: {keypair.public_key.hex()}")
seed2 = seed_from_mnemonic(mnemonic, passphrase="")
private_key_bytes2 = derive_dilithium_key(seed2, "m/44'/118'/0'/0/0")
keypair2 = Dilithium5.from_bytes(private_key_bytes2)
assert keypair2.public_key.hex() == keypair.public_key.hex()
print("确定性派生验证通过")
3.4 硬件安全模块(HSM)集成
对于生产验证者节点,推荐使用HSM保护Dilithium-5私钥:
Go: PKCS#11接口
package main
import (
"fmt"
"log"
"os"
"github.com/msgchain/msgchain/crypto/dilithium"
"github.com/msgchain/msgchain/hsm"
)
func main() {
hsmClient, err := hsm.NewPKCS11Client(&hsm.PKCS11Config{
Library: "/usr/lib/softhsm/libsofthsm2.so",
Slot: 0,
PIN: os.Getenv("HSM_PIN"),
})
if err != nil {
log.Fatalf("HSM连接失败: %v", err)
}
defer hsmClient.Close()
keyID, err := hsmClient.GenerateDilithium5Key()
if err != nil {
log.Fatalf("HSM密钥生成失败: %v", err)
}
fmt.Printf("HSM密钥ID: %x\n", keyID)
pubKey, err := hsmClient.GetDilithium5PublicKey(keyID)
if err != nil {
log.Fatal(err)
}
msg := []byte("要签名的消息")
signature, err := hsmClient.SignDilithium5(keyID, msg)
if err != nil {
log.Fatal(err)
}
fmt.Printf("签名 (%d 字节): %x...\n", len(signature), signature[:32])
valid := pubKey.VerifySignature(msg, signature)
fmt.Printf("签名验证: %v\n", valid)
}
Rust: TPM 2.0集成
use msg_chain_crypto::dilithium::DilithiumPublicKey;
use tss_esapi::{Context, constants::tss::TPM2_ALG_DILITHIUM5};
fn tpm_signing_example() -> Result<(), Box<dyn std::error::Error>> {
let mut context = Context::new("/dev/tpmrm0")?;
let key_handle = context.create_primary(TPM2_ALG_DILITHIUM5, &Default::default())?;
let pub_key_bytes = context.read_public(key_handle)?.dilithium5_public()?;
let pub_key = DilithiumPublicKey::from_bytes(&pub_key_bytes)?;
let digest = sha2::Sha256::digest(b"要签名的数据");
let signature = context.sign(key_handle, &digest, None)?;
let valid = pub_key.verify(b"要签名的数据", &signature);
println!("TPM签名验证: {}", valid);
Ok(())
}
第四章:签名与验证
4.1 基本签名操作
Go SDK
package main
import (
"crypto/rand"
"fmt"
"log"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func main() {
privKey, err := dilithium.GenKeyV5(rand.Reader)
if err != nil {
log.Fatal(err)
}
pubKey := privKey.PubKey()
message := []byte("{\"chain_id\":\"msg-chain-1\",\"account_number\":\"42\",\"sequence\":\"1\"}")
signature, err := privKey.Sign(message)
if err != nil {
log.Fatalf("签名失败: %v", err)
}
fmt.Printf("消息: %s\n", message)
fmt.Printf("签名长度: %d 字节\n", len(signature))
fmt.Printf("签名 (hex): %x\n", signature)
valid := pubKey.VerifySignature(message, signature)
fmt.Printf("签名验证: %v\n", valid)
tampered := []byte("篡改后的消息")
validTampered := pubKey.VerifySignature(tampered, signature)
fmt.Printf("篡改消息验证: %v (应返回false)\n", validTampered)
}
Rust SDK
use msg_chain_crypto::dilithium::DilithiumKeypair;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let keypair = DilithiumKeypair::generate()?;
let message = b"{\"chain_id\":\"msg-chain-1\",\"account_number\":\"42\"}";
let signature = keypair.sign(message)?;
println!("签名长度: {} 字节", signature.len());
println!("签名 (hex): {}", hex::encode(&signature));
let valid = keypair.verify(message, &signature);
println!("签名验证: {}", valid);
let pub_key = keypair.public_key();
let valid2 = pub_key.verify(message, &signature);
println!("公钥验证: {}", valid2);
Ok(())
}
Python SDK
from msgchain_sdk.crypto import Dilithium5
keypair = Dilithium5.generate()
message = b'{"chain_id":"msg-chain-1","account_number":"42"}'
signature = keypair.sign(message)
print(f"签名长度: {len(signature)} 字节")
print(f"签名 (hex): {signature.hex()}")
is_valid = keypair.verify(message, signature)
print(f"签名验证: {is_valid}")
pk = keypair.get_public_key()
is_valid2 = pk.verify(message, signature)
print(f"公钥验证: {is_valid2}")
TypeScript SDK
import { DilithiumKeypair } from '@msg-chain/crypto';
const keypair = DilithiumKeypair.generate();
const message = Buffer.from('{"chain_id":"msg-chain-1","account_number":"42"}');
const signature = keypair.sign(message);
console.log(`签名长度: ${signature.length} 字节`);
console.log(`签名 (hex): ${Buffer.from(signature).toString('hex')}`);
const valid = keypair.verify(message, signature);
console.log(`签名验证: ${valid}`);
const keypair2 = DilithiumKeypair.fromHex(keypair.getPrivateKey('hex'));
console.log(`恢复密钥地址一致: ${keypair.getAddress() === keypair2.getAddress()}`);
4.2 Amino编码与Cosmos SDK兼容
package main
import (
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func main() {
cdc := codec.NewLegacyAmino()
cdc.RegisterConcrete(&dilithium.PubKeyDilithium5{},
"tendermint/PubKeyDilithium5", nil)
cdc.RegisterConcrete(&dilithium.PrivKeyDilithium5{},
"tendermint/PrivKeyDilithium5", nil)
privKey, _ := dilithium.GenKeyV5(rand.Reader)
pubKey := privKey.PubKey()
pubAmino, err := cdc.MarshalJSON(pubKey)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Amino公钥JSON: %s\n", pubAmino)
}
4.3 SIGN_MODE_DIRECT (Protobuf)
Cosmos SDK的SIGN_MODE_DIRECT使用Protobuf序列化交易进行签名:
package main
import (
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/tx"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func SignWithSignModeDirect(
privKey *dilithium.PrivKeyDilithium5,
txBuilder client.TxBuilder,
signData signing.SignData,
) ([]byte, error) {
signDoc := tx.NewSignDoc(
signData.ChainID,
signData.AccountNumber,
signData.AccountSequence,
signData.SignMode,
signData.SignDoc,
)
signBytes, err := signDoc.Marshal()
if err != nil {
return nil, fmt.Errorf("SignDoc序列化失败: %w", err)
}
signature, err := privKey.Sign(signBytes)
if err != nil {
return nil, fmt.Errorf("Dilithium签名失败: %w", err)
}
return signature, nil
}
4.4 多签聚合
Go SDK: 多签
package main
import (
"crypto/rand"
"fmt"
"log"
"github.com/msgchain/msgchain/crypto/dilithium"
)
type MultiSignature struct {
Signatures [][]byte `json:"signatures"`
PubKeys [][]byte `json:"pub_keys"`
}
func CreateMultiSig(message []byte, privKeys []*dilithium.PrivKeyDilithium5) (*MultiSignature, error) {
multiSig := &MultiSignature{
Signatures: make([][]byte, len(privKeys)),
PubKeys: make([][]byte, len(privKeys)),
}
for i, privKey := range privKeys {
sig, err := privKey.Sign(message)
if err != nil {
return nil, fmt.Errorf("签名者%d签名失败: %w", i, err)
}
multiSig.Signatures[i] = sig
multiSig.PubKeys[i] = privKey.PubKey().Bytes()
}
return multiSig, nil
}
func VerifyMultiSig(message []byte, multiSig *MultiSignature) bool {
for i, sig := range multiSig.Signatures {
pubKey := &dilithium.PubKeyDilithium5{}
if err := pubKey.UnmarshalAmino(multiSig.PubKeys[i]); err != nil {
return false
}
if !pubKey.VerifySignature(message, sig) {
return false
}
}
return true
}
func main() {
keys := make([]*dilithium.PrivKeyDilithium5, 3)
for i := range keys {
key, _ := dilithium.GenKeyV5(rand.Reader)
keys[i] = key
}
message := []byte("多签交易数据")
multiSig, _ := CreateMultiSig(message, keys)
ok := VerifyMultiSig(message, multiSig)
fmt.Printf("全部签名验证: %v\n", ok)
}
第五章:Cosmos SDK集成
5.1 密钥环(Keyring)集成
MSG Chain的密钥环支持Dilithium-5密钥类型:
package main
import (
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/msgchain/msgchain/crypto/dilithium"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func main() {
kr, err := keyring.New(
"msgchain",
keyring.BackendFile,
"/home/user/.msgchain",
nil,
dilithium.Dilithium5Codec(),
)
if err != nil {
log.Fatal(err)
}
record, err := kr.NewAccount(
"my-dilithium-key",
nil,
"",
sdk.FullFundraiserPath,
nil,
)
if err != nil {
log.Fatal(err)
}
pubKey, _ := record.GetPubKey()
addr, _ := record.GetAddress()
fmt.Printf("密钥名称: %s\n", record.Name)
fmt.Printf("地址: %s\n", addr.String())
fmt.Printf("公钥类型: %s\n", pubKey.Type())
}
通过命令行创建Dilithium-5密钥:
./bin/quantum_node_linux keys add my-key \
--key-type dilithium-5 \
--keyring-backend file \
--home ~/.msgchain
5.2 地址派生
Dilithium-5地址派生的完整流程:
package main
import (
"golang.org/x/crypto/sha3"
"fmt"
"github.com/btcsuite/btcutil/bech32"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func DeriveAddress(pubKey *dilithium.PubKeyDilithium5) (string, error) {
pubBytes := pubKey.Bytes()
hasher := sha3.New512()
hasher.Write(pubBytes)
hash := hasher.Sum(nil)
addrBytes := hash[:20]
encoded, err := bech32.Encode("msg", addrBytes)
if err != nil {
return "", fmt.Errorf("bech32编码失败: %w", err)
}
return encoded, nil
}
func main() {
privKey, _ := dilithium.GenKeyV5(rand.Reader)
pubKey := privKey.PubKey()
addr, _ := DeriveAddress(pubKey)
fmt.Printf("地址: %s\n", addr)
hrp, decoded, err := bech32.Decode(addr)
if err != nil {
fmt.Printf("地址解码失败: %v\n", err)
return
}
fmt.Printf("HRP: %s\n", hrp)
fmt.Printf("地址字节: %x\n", decoded)
fmt.Printf("地址长度: %d 字节\n", len(decoded))
}
第六章:SDK各语言实现
6.1 Go SDK完整示例
密钥生成
package main
import (
"crypto/rand"
"fmt"
"log"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func main() {
// 随机生成
privKey, err := dilithium.GenKeyV5(rand.Reader)
if err != nil {
log.Fatal(err)
}
// 确定性生成(用于测试)
seed := make([]byte, 32)
for i := range seed {
seed[i] = byte(i)
}
privKey2, err := dilithium.GenKeyV5WithSeed(seed)
if err != nil {
log.Fatal(err)
}
fmt.Printf("随机密钥地址: %s\n", privKey.PubKey().Address().String())
fmt.Printf("确定性密钥地址: %s\n", privKey2.PubKey().Address().String())
}
签名与验证
package main
import (
"crypto/rand"
"fmt"
"log"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func main() {
privKey, _ := dilithium.GenKeyV5(rand.Reader)
pubKey := privKey.PubKey()
message := []byte("Hello, MSG Chain!")
signature, err := privKey.Sign(message)
if err != nil {
log.Fatalf("签名失败: %v", err)
}
fmt.Printf("签名 (%d 字节): %x\n", len(signature), signature)
valid := pubKey.VerifySignature(message, signature)
fmt.Printf("验证结果: %v\n", valid)
}
基准测试
package dilithium_test
import (
"crypto/rand"
"testing"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func BenchmarkDilithium5_KeyGen(b *testing.B) {
for i := 0; i < b.N; i++ {
dilithium.GenKeyV5(rand.Reader)
}
}
func BenchmarkDilithium5_Sign(b *testing.B) {
privKey, _ := dilithium.GenKeyV5(rand.Reader)
msg := make([]byte, 256)
rand.Read(msg)
b.ResetTimer()
for i := 0; i < b.N; i++ {
privKey.Sign(msg)
}
}
func BenchmarkDilithium5_Verify(b *testing.B) {
privKey, _ := dilithium.GenKeyV5(rand.Reader)
pubKey := privKey.PubKey()
msg := make([]byte, 256)
rand.Read(msg)
sig, _ := privKey.Sign(msg)
b.ResetTimer()
for i := 0; i < b.N; i++ {
pubKey.VerifySignature(msg, sig)
}
}
6.2 Rust SDK完整示例
Cargo.toml
[package]
name = "msgchain-dilithium-example"
version = "0.1.0"
edition = "2021"
[dependencies]
msg-chain-crypto = { version = "0.3", features = ["dilithium5"] }
hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
anyhow = "1.0"
完整示例
use msg_chain_crypto::dilithium::{DilithiumKeypair, DilithiumSignature};
use anyhow::Result;
struct DilithiumWallet {
keypair: DilithiumKeypair,
}
impl DilithiumWallet {
fn generate() -> Result<Self> {
let keypair = DilithiumKeypair::generate()?;
Ok(Self { keypair })
}
fn from_private_key(hex_key: &str) -> Result<Self> {
let bytes = hex::decode(hex_key)?;
let keypair = DilithiumKeypair::from_secret_key_bytes(&bytes)?;
Ok(Self { keypair })
}
fn address(&self) -> String {
self.keypair.address()
}
fn public_key_hex(&self) -> String {
hex::encode(self.keypair.public_key_bytes())
}
fn sign_tx(&self, tx_bytes: &[u8]) -> Result<Vec<u8>> {
let signature = self.keypair.sign(tx_bytes)?;
Ok(signature.to_bytes())
}
fn verify_tx(&self, tx_bytes: &[u8], signature: &[u8]) -> bool {
let sig = match DilithiumSignature::from_bytes(signature) {
Ok(s) => s,
Err(_) => return false,
};
self.keypair.verify(tx_bytes, &sig)
}
}
fn main() -> Result<()> {
let wallet = DilithiumWallet::generate()?;
println!("地址: {}", wallet.address());
println!("公钥: {}", wallet.public_key_hex());
let tx = b"{\"chain_id\":\"msg-chain-1\",\"nonce\":1}";
let signature = wallet.sign_tx(tx)?;
println!("签名长度: {} 字节", signature.len());
assert!(wallet.verify_tx(tx, &signature));
println!("签名验证通过");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_generation() {
let wallet = DilithiumWallet::generate().unwrap();
assert!(wallet.address().starts_with("msg1"));
assert_eq!(wallet.keypair.public_key_bytes().len(), 2592);
}
#[test]
fn test_sign_verify() {
let wallet = DilithiumWallet::generate().unwrap();
let msg = b"test message";
let sig = wallet.sign_tx(msg).unwrap();
assert!(wallet.verify_tx(msg, &sig));
assert!(!wallet.verify_tx(b"tampered", &sig));
}
}
6.3 Python SDK完整示例
安装
pip install msgchain-sdk
完整钱包示例
#!/usr/bin/env python3
import os
import json
from msgchain_sdk.crypto import Dilithium5
class DilithiumWallet:
def __init__(self, keypair: Dilithium5):
self._keypair = keypair
@classmethod
def generate(cls) -> "DilithiumWallet":
return cls(Dilithium5.generate())
@classmethod
def from_private_key(cls, hex_key: str) -> "DilithiumWallet":
return cls(Dilithium5.from_hex(hex_key))
@property
def address(self) -> str:
return self._keypair.address
@property
def public_key_hex(self) -> str:
return self._keypair.public_key.hex()
@property
def private_key_hex(self) -> str:
return self._keypair.private_key.hex()
def sign(self, data: bytes) -> bytes:
return self._keypair.sign(data)
def verify(self, data: bytes, signature: bytes) -> bool:
return self._keypair.verify(data, signature)
def to_dict(self) -> dict:
return {
"algorithm": "dilithium-5",
"address": self.address,
"public_key": self.public_key_hex,
"private_key": self.private_key_hex,
}
def main():
print("MSG Chain Dilithium-5 Wallet Demo")
print("=" * 50)
wallet = DilithiumWallet.generate()
print(f"地址: {wallet.address}")
print(f"公钥: {wallet.public_key_hex[:64]}...")
message = b"Hello, MSG Chain with Post-Quantum Security!"
signature = wallet.sign(message)
print(f"签名长度: {len(signature)} 字节")
print(f"验证结果: {wallet.verify(message, signature)}")
print(f"篡改验证: {wallet.verify(b'tampered', signature)}")
if __name__ == "__main__":
main()
RPC交互
from msgchain_sdk.crypto import Dilithium5
from msgchain_sdk.rpc import RPCClient
from msgchain_sdk.tx import TransactionBuilder
class DilithiumTxSigner:
def __init__(self, rpc_url: str = "https://rpc.msgchain.org"):
self.rpc = RPCClient(rpc_url)
def sign_and_broadcast(self, keypair: Dilithium5, msgs: list, gas: int = 200000) -> dict:
account = self.rpc.get_account(keypair.address)
chain_id = self.rpc.get_chain_id()
tx_builder = TransactionBuilder(
chain_id=chain_id,
account_number=account.account_number,
sequence=account.sequence,
)
unsigned_tx = tx_builder.build(msgs=msgs, gas=gas)
sign_bytes = tx_builder.get_sign_bytes(unsigned_tx)
signature = keypair.sign(sign_bytes)
signed_tx = tx_builder.attach_signature(
unsigned_tx, signature.hex(), keypair.public_key.hex()
)
return self.rpc.broadcast_tx(signed_tx)
6.4 TypeScript SDK完整示例
安装
npm install @msg-chain/crypto@alpha
钱包类
import { DilithiumKeypair, DilithiumKeyExport } from '@msg-chain/crypto';
import { generateMnemonic, mnemonicToSeed } from '@msg-chain/crypto/bip39';
import { deriveDilithiumKey, DilithiumHDPath } from '@msg-chain/crypto/hd';
class DilithiumWallet {
private keypair: DilithiumKeypair;
constructor(keypair: DilithiumKeypair) {
this.keypair = keypair;
}
static generate(): DilithiumWallet {
return new DilithiumWallet(DilithiumKeypair.generate());
}
static fromPrivateKey(hexKey: string): DilithiumWallet {
return new DilithiumWallet(DilithiumKeypair.fromHex(hexKey));
}
static fromMnemonic(mnemonic: string, passphrase = ''): DilithiumWallet {
const seed = mnemonicToSeed(mnemonic, passphrase);
const privateKey = deriveDilithiumKey(seed, DilithiumHDPath.cosmos(0, 0));
return new DilithiumWallet(DilithiumKeypair.fromBytes(privateKey));
}
get address(): string { return this.keypair.getAddress(); }
get publicKeyHex(): string { return this.keypair.getPublicKey('hex'); }
get privateKeyHex(): string { return this.keypair.getPrivateKey('hex'); }
sign(data: Uint8Array): Uint8Array { return this.keypair.sign(data); }
verify(data: Uint8Array, signature: Uint8Array): boolean {
return this.keypair.verify(data, signature);
}
toJSON(): DilithiumKeyExport { return this.keypair.toJSON(); }
static fromJSON(json: DilithiumKeyExport): DilithiumWallet {
return new DilithiumWallet(DilithiumKeypair.fromJSON(json));
}
}
async function main() {
const wallet = DilithiumWallet.generate();
console.log(`地址: ${wallet.address}`);
const message = Buffer.from('Hello Dilithium-5!');
const sig = wallet.sign(message);
console.log(`签名: ${wallet.verify(message, sig)}`);
}
main().catch(console.error);
React Hooks
import { useState, useCallback } from 'react';
import { DilithiumKeypair } from '@msg-chain/crypto';
export function useDilithiumWallet() {
const [keypair, setKeypair] = useState<DilithiumKeypair | null>(null);
const [address, setAddress] = useState('');
const connect = useCallback(async (privateKeyHex?: string) => {
let kp: DilithiumKeypair;
if (privateKeyHex) {
kp = DilithiumKeypair.fromHex(privateKeyHex);
} else {
kp = DilithiumKeypair.generate();
}
setKeypair(kp);
setAddress(kp.getAddress());
}, []);
const signMessage = useCallback(async (message: string): Promise<string> => {
if (!keypair) throw new Error('Wallet not connected');
const sig = keypair.sign(Buffer.from(message));
return Buffer.from(sig).toString('hex');
}, [keypair]);
const disconnect = useCallback(() => {
setKeypair(null);
setAddress('');
}, []);
return { address, keypair, connect, signMessage, disconnect };
}
第七章:交易构造与广播
7.1 Cosmos SDK StdTx构造
Go: 完整交易构造
package main
import (
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/msgchain/msgchain/crypto/dilithium"
)
type UnsignedTx struct {
SignBytes []byte `json:"sign_bytes"`
}
func BuildUnsignedTx(
fromAddr sdk.AccAddress, toAddr sdk.AccAddress,
amount sdk.Coins, gasLimit uint64, fee sdk.Coins,
memo string, accountNumber, sequence uint64, chainID string,
) (*UnsignedTx, error) {
msgSend := banktypes.NewMsgSend(fromAddr, toAddr, amount)
txConfig := authtx.NewTxConfig(
codec.NewProtoCodec(codec.NewInterfaceRegistry()),
[]signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT},
)
txBuilder := txConfig.NewTxBuilder()
txBuilder.SetMsgs(msgSend)
txBuilder.SetGasLimit(gasLimit)
txBuilder.SetFeeAmount(fee)
txBuilder.SetMemo(memo)
signBytes, err := txConfig.SignModeHandler().GetSignBytes(
signing.SignMode_SIGN_MODE_DIRECT,
signing.SignerData{ChainID: chainID, AccountNumber: accountNumber, Sequence: sequence},
txBuilder.GetTx(),
)
if err != nil {
return nil, fmt.Errorf("获取签名字节失败: %w", err)
}
return &UnsignedTx{SignBytes: signBytes}, nil
}
func SignTxWithDilithium(unsignedTx *UnsignedTx, privKey *dilithium.PrivKeyDilithium5) ([]byte, []byte, error) {
signature, err := privKey.Sign(unsignedTx.SignBytes)
if err != nil {
return nil, nil, fmt.Errorf("Dilithium签名失败: %w", err)
}
return signature, privKey.PubKey().Bytes(), nil
}
func EncodeSignedTx(unsignedTx *UnsignedTx, signature, pubKeyBytes []byte) ([]byte, error) {
txConfig := authtx.NewTxConfig(
codec.NewProtoCodec(codec.NewInterfaceRegistry()),
[]signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT},
)
txBuilder := txConfig.NewTxBuilder()
pubKey := &dilithium.PubKeyDilithium5{}
pubKey.UnmarshalAmino(pubKeyBytes)
sig := signing.SignatureV2{
PubKey: pubKey,
Data: &signing.SingleSignatureData{SignMode: signing.SignMode_SIGN_MODE_DIRECT, Signature: signature},
Sequence: 0,
}
txBuilder.SetSignatures(sig)
return txConfig.TxEncoder()(txBuilder.GetTx())
}
func main() {
privKey, _ := dilithium.GenKeyV5(rand.Reader)
fromAddr := privKey.PubKey().Address()
toAddr := sdk.AccAddress([]byte("destination_address_20bytes____"))
unsignedTx, _ := BuildUnsignedTx(fromAddr, toAddr,
sdk.NewCoins(sdk.NewInt64Coin("umsg", 1000000)),
200000, sdk.NewCoins(sdk.NewInt64Coin("umsg", 5000)),
"Dilithium-5签名交易", 42, 1, "msg-chain-1")
sig, pubBytes, _ := SignTxWithDilithium(unsignedTx, privKey)
txBytes, _ := EncodeSignedTx(unsignedTx, sig, pubBytes)
fmt.Printf("签名大小: %d 字节\n", len(sig))
fmt.Printf("交易大小: %d 字节\n", len(txBytes))
}
7.2 混合签名(Dilithium-5 + Secp256k1)
package main
import (
"crypto/rand"
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/msgchain/msgchain/crypto/dilithium"
)
type MixedSignedTx struct {
DilithiumSig []byte `json:"dilithium_signature"`
Secp256k1Sig []byte `json:"secp256k1_signature"`
}
func CreateMixedSignatureTx(
dilithiumPriv *dilithium.PrivKeyDilithium5,
secp256k1Priv *secp256k1.PrivKeySecp256k1,
txBytes []byte,
) (*MixedSignedTx, error) {
dilithiumSig, err := dilithiumPriv.Sign(txBytes)
if err != nil {
return nil, fmt.Errorf("Dilithium签名失败: %w", err)
}
secp256k1Sig, err := secp256k1Priv.Sign(txBytes)
if err != nil {
return nil, fmt.Errorf("Secp256k1签名失败: %w", err)
}
return &MixedSignedTx{DilithiumSig: dilithiumSig, Secp256k1Sig: secp256k1Sig}, nil
}
func main() {
dilithiumPriv, _ := dilithium.GenKeyV5(rand.Reader)
secp256k1Priv := secp256k1.GenPrivKey()
txBytes := []byte("混合签名交易示例")
mixed, _ := CreateMixedSignatureTx(dilithiumPriv, secp256k1Priv, txBytes)
fmt.Printf("Dilithium-5签名: %d 字节\n", len(mixed.DilithiumSig))
fmt.Printf("Secp256k1签名: %d 字节\n", len(mixed.Secp256k1Sig))
}
7.3 Gas开销计算
package main
import (
"fmt"
"math"
)
const (
Dilithium5SignatureSize = 4595
Dilithium5PubKeySize = 2592
Secp256k1SignatureSize = 71
Secp256k1PubKeySize = 33
GasCostPerByte = 10
GasCostPerSign = 1000
GasCostPerVerify = 500
)
func CalculateTxGas(baseTxSize int, numDilithiumSigners, numSecp256k1Signers int) uint64 {
totalSize := baseTxSize +
numDilithiumSigners*(Dilithium5SignatureSize+Dilithium5PubKeySize) +
numSecp256k1Signers*(Secp256k1SignatureSize+Secp256k1PubKeySize)
totalGas := uint64(totalSize*GasCostPerByte) +
uint64((numDilithiumSigners+numSecp256k1Signers)*(GasCostPerSign+GasCostPerVerify)) +
50000
return uint64(math.Ceil(float64(totalGas)/1000) * 1000)
}
func main() {
gas1 := CalculateTxGas(150, 1, 0)
gas2 := CalculateTxGas(150, 0, 1)
fmt.Printf("MsgSend + Dilithium-5: %d gas\n", gas1)
fmt.Printf("MsgSend + Secp256k1: %d gas\n", gas2)
fmt.Printf("Gas比例: %.2fx\n", float64(gas1)/float64(gas2))
}
7.4 RPC广播
REST API
SIGNED_TX_B64=$(base64 -w0 signed_tx.bin)
curl -X POST https://rpc.msgchain.org/cosmos/tx/v1beta1/txs \
-H "Content-Type: application/json" \
-d '{"tx_bytes": "'$SIGNED_TX_B64'", "mode": "BROADCAST_MODE_SYNC"}'
Go RPC
package main
import (
"bytes" "encoding/base64" "encoding/json" "fmt" "net/http"
)
type BroadcastReq struct {
TxBytes string `json:"tx_bytes"`
Mode string `json:"mode"`
}
type BroadcastResp struct {
TxResponse struct {
TxHash string `json:"txhash"`
Code uint32 `json:"code"`
RawLog string `json:"raw_log"`
} `json:"tx_response"`
}
func BroadcastTxREST(txBytes []byte, endpoint string) (*BroadcastResp, error) {
payload := BroadcastReq{
TxBytes: base64.StdEncoding.EncodeToString(txBytes),
Mode: "BROADCAST_MODE_SYNC",
}
jsonData, _ := json.Marshal(payload)
resp, err := http.Post(endpoint+"/cosmos/tx/v1beta1/txs", "application/json", bytes.NewReader(jsonData))
if err != nil {
return nil, fmt.Errorf("RPC请求失败: %w", err)
}
defer resp.Body.Close()
var result BroadcastResp
json.NewDecoder(resp.Body).Decode(&result)
return &result, nil
}
Python RPC
import requests
import base64
class DilithiumTxBroadcaster:
def __init__(self, rpc_url: str = "https://rpc.msgchain.org"):
self.rpc_url = rpc_url.rstrip("/")
def broadcast_sync(self, signed_tx_bytes: bytes) -> dict:
tx_b64 = base64.b64encode(signed_tx_bytes).decode()
resp = requests.post(
f"{self.rpc_url}/cosmos/tx/v1beta1/txs",
json={"tx_bytes": tx_b64, "mode": "BROADCAST_MODE_SYNC"},
)
resp.raise_for_status()
return resp.json()
第八章:密钥轮换与迁移
8.1 验证者共识密钥轮换
package main
import (
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/x/staking/types"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func RotateValidatorKey(oldPrivKey *dilithium.PrivKeyDilithium5, homeDir string) (*dilithium.PrivKeyDilithium5, error) {
newPrivKey, err := dilithium.GenKeyV5(rand.Reader)
if err != nil {
return nil, fmt.Errorf("新密钥生成失败: %w", err)
}
newPubKey := newPrivKey.PubKey()
rotateMsg := types.NewMsgRotateConsensusKey(oldPrivKey.PubKey().Address(), newPubKey)
signBytes := rotateMsg.GetSignBytes()
signature, err := oldPrivKey.Sign(signBytes)
if err != nil {
return nil, fmt.Errorf("轮换消息签名失败: %w", err)
}
valid := oldPrivKey.PubKey().VerifySignature(signBytes, signature)
if !valid {
return nil, fmt.Errorf("轮换消息签名验证失败")
}
if err := saveValidatorKey(newPrivKey, homeDir); err != nil {
return nil, fmt.Errorf("新密钥保存失败: %w", err)
}
fmt.Printf("验证者密钥轮换成功!\n")
fmt.Printf("旧地址: %s\n", oldPrivKey.PubKey().Address().String())
fmt.Printf("新地址: %s\n", newPubKey.Address().String())
return newPrivKey, nil
}
使用CLI轮换密钥
./bin/quantum_node_linux keys add new-consensus-key \
--key-type dilithium-5 \
--keyring-backend file \
--home ~/.msgchain
./bin/quantum_node_linux tx staking rotate-consensus-key \
--pubkey $(./bin/quantum_node_linux keys show new-consensus-key --pubkey) \
--from validator \
--chain-id msg-chain-1 \
--fees 10000umsg \
--gas 300000 \
--home ~/.msgchain
cp ~/.msgchain/config/priv_validator_key.json ~/.msgchain/config/priv_validator_key.json.bak
sudo systemctl restart msgchain
8.2 从Secp256k1迁移到Dilithium-5
package main
import (
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func MigrateSecp256k1ToDilithium5(
oldPrivKey *secp256k1.PrivKeySecp256k1,
) (*dilithium.PrivKeyDilithium5, error) {
newPrivKey, err := dilithium.GenKeyV5(rand.Reader)
if err != nil {
return nil, fmt.Errorf("Dilithium-5密钥生成失败: %w", err)
}
// 用旧密钥签名迁移确认消息
migrateMsg := []byte("MIGRATE_TO_DILITHIUM5")
oldSig, err := oldPrivKey.Sign(migrateMsg)
if err != nil {
return nil, fmt.Errorf("迁移确认签名失败: %w", err)
}
oldPubKey := oldPrivKey.PubKey()
if !oldPubKey.VerifySignature(migrateMsg, oldSig) {
return nil, fmt.Errorf("迁移确认签名验证失败")
}
fmt.Printf("迁移成功!\n")
fmt.Printf("旧Secp256k1地址: %s\n", oldPubKey.Address().String())
fmt.Printf("新Dilithium-5地址: %s\n", newPrivKey.PubKey().Address().String())
return newPrivKey, nil
}
第九章:安全最佳实践
9.1 熵源要求
后量子密码学对熵源质量要求更高。Dilithium-5的密钥生成需要高质量随机数:
package main
import (
"crypto/rand"
"fmt"
"log"
"github.com/msgchain/msgchain/crypto/dilithium"
)
func main() {
// 推荐:使用操作系统crypto/rand
privKey, err := dilithium.GenKeyV5(rand.Reader)
if err != nil {
log.Fatal(err)
}
fmt.Printf("地址: %s\n", privKey.PubKey().Address().String())
// 使用硬件随机数生成器
// hwrng, _ := os.Open("/dev/hwrng")
// privKey, err = dilithium.GenKeyV5(hwrng)
}
9.2 侧信道攻击缓解
// 恒定时间比较函数(防止定时攻击)
func ConstantTimeEqual(a, b []byte) bool {
if len(a) != len(b) {
return false
}
var v byte
for i := 0; i < len(a); i++ {
v |= a[i] ^ b[i]
}
return v == 0
}
// 验证签名时使用恒定时间比较
func VerifyConstantTime(pubKey *dilithium.PubKeyDilithium5, msg, sig []byte) bool {
expectedValid := pubKey.VerifySignature(msg, sig)
// 即使结果无效,也运行完整计算以防止短路
_ = pubKey.VerifySignature(append(msg, 0), sig)
return expectedValid
}
9.3 密钥备份加密
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"golang.org/x/crypto/pbkdf2"
)
func EncryptPrivateKey(privKeyBytes []byte, passphrase string) ([]byte, error) {
salt := make([]byte, 32)
if _, err := rand.Read(salt); err != nil {
return nil, err
}
key := pbkdf2.Key([]byte(passphrase), salt, 600000, 32, sha256.New)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, aead.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
ciphertext := aead.Seal(nonce, nonce, privKeyBytes, nil)
return append(salt, ciphertext...), nil
}
func DecryptPrivateKey(encrypted []byte, passphrase string) ([]byte, error) {
salt := encrypted[:32]
ciphertext := encrypted[32:]
key := pbkdf2.Key([]byte(passphrase), salt, 600000, 32, sha256.New)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonceSize := aead.NonceSize()
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
return aead.Open(nil, nonce, ciphertext, nil)
}
9.4 测试后量子功能
# 运行后量子相关测试
make test-quantum
# 运行全部测试
make test
# 构建后量子节点
make build-linux
# 输出: bin/genesis_node_linux, bin/quantum_node_linux
9.5 生产环境检查清单
- [ ] 使用支持Dilithium-5的HSM保护验证者密钥
- [ ] 对密钥文件设置0600权限
- [ ] 用强密码加密备份私钥
- [ ] 监控节点启动日志中的Dilithium-5加载信息
- [ ] 确认genesis.json中验证者pup_key类型为tendermint/PubKeyDilithium5
- [ ] 定期轮换共识密钥(建议每3-6个月)
- [ ] 使用make test-quantum进行回归测试
- [ ] 确保所有节点版本一致(Dilithium-5兼容性)
第十章:完整工作流
10.1 端到端流程
1. 密钥生成 -> 2. 密钥存储 -> 3. 交易构造 -> 4. 交易签名 -> 5. 广播 -> 6. 验证
Go: 完整流程
package main
import (
"crypto/rand"
"fmt"
"log"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/msgchain/msgchain/crypto/dilithium"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func main() {
// Step 1: 密钥生成
privKey, err := dilithium.GenKeyV5(rand.Reader)
if err != nil {
log.Fatal(err)
}
pubKey := privKey.PubKey()
fmt.Printf("地址: %s\n", pubKey.Address().String())
// Step 2: 构造交易
txConfig := authtx.NewTxConfig(nil, []signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT})
txBuilder := txConfig.NewTxBuilder()
txBuilder.SetMsgs(banktypes.NewMsgSend(
pubKey.Address(),
sdk.AccAddress([]byte("recipient_addr_20bytes__")),
sdk.NewCoins(sdk.NewInt64Coin("umsg", 1000)),
))
txBuilder.SetGasLimit(200000)
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin("umsg", 5000)))
// Step 3: 签名
signBytes, _ := txConfig.SignModeHandler().GetSignBytes(
signing.SignMode_SIGN_MODE_DIRECT,
signing.SignerData{ChainID: "msg-chain-1", AccountNumber: 0, Sequence: 0},
txBuilder.GetTx(),
)
sig, _ := privKey.Sign(signBytes)
// Step 4: 附加签名
txBuilder.SetSignatures(signing.SignatureV2{
PubKey: pubKey,
Data: &signing.SingleSignatureData{SignMode: signing.SignMode_SIGN_MODE_DIRECT, Signature: sig},
})
// Step 5: 编码并广播
txBytes, _ := txConfig.TxEncoder()(txBuilder.GetTx())
fmt.Printf("交易大小: %d 字节\n", len(txBytes))
// Step 6: 验证
valid := pubKey.VerifySignature(signBytes, sig)
fmt.Printf("签名验证: %v\n", valid)
// broadcastTx(txBytes)
}
Rust: 完整流程
use msg_chain_crypto::dilithium::DilithiumKeypair;
use msg_chain_sdk::tx::{TxBuilder, SignMode};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let keypair = DilithiumKeypair::generate()?;
println!("地址: {}", keypair.address());
let tx = TxBuilder::new("msg-chain-1")
.add_message(bank::MsgSend {
from_address: keypair.address(),
to_address: "msg1x9v6xkqf4u5a3d7e2n8lkvgzck8wkz3".into(),
amount: vec![coin("1000000", "umsg")],
})
.set_gas(200000)
.set_fee(vec![coin("5000", "umsg")])
.set_account_number(0)
.set_sequence(0)
.set_sign_mode(SignMode::Direct)
.build()?;
let signed_tx = tx.sign(&keypair)?;
let tx_bytes = signed_tx.to_bytes();
println!("交易大小: {} 字节", tx_bytes.len());
let client = msg_chain_sdk::Client::new("https://rpc.msgchain.org");
let response = client.broadcast_tx(tx_bytes).await?;
println!("交易哈希: {}", response.tx_hash);
Ok(())
}
Python: 完整流程
from msgchain_sdk.crypto import Dilithium5
from msgchain_sdk.rpc import RPCClient
from msgchain_sdk.tx import TransactionBuilder
# Step 1: 密钥生成
keypair = Dilithium5.generate()
print(f"地址: {keypair.address}")
# Step 2-5: 构建、签名、广播
rpc = RPCClient("https://rpc.msgchain.org")
account = rpc.get_account(keypair.address)
chain_id = rpc.get_chain_id()
tx_builder = TransactionBuilder(
chain_id=chain_id,
account_number=account.account_number,
sequence=account.sequence,
)
unsigned_tx = tx_builder.build(
msgs=[{
"@type": "/cosmos.bank.v1beta1.MsgSend",
"from_address": keypair.address,
"to_address": "msg1x9v6xkqf4u5a3d7e2n8lkvgzck8wkz3",
"amount": [{"denom": "umsg", "amount": "1000000"}],
}],
gas=200000,
)
sign_bytes = tx_builder.get_sign_bytes(unsigned_tx)
signature = keypair.sign(sign_bytes)
signed_tx = tx_builder.attach_signature(unsigned_tx, signature.hex(), keypair.public_key.hex())
result = rpc.broadcast_tx(signed_tx)
print(f"交易哈希: {result['txhash']}")
# Step 6: 验证
assert keypair.verify(sign_bytes, signature)
print("签名验证通过")
TypeScript: 完整流程
import { DilithiumKeypair } from '@msg-chain/crypto';
import { SigningStargateClient } from '@cosmjs/stargate';
import { Coin } from '@cosmjs/amino';
async function main() {
// Step 1: 密钥生成
const keypair = DilithiumKeypair.generate();
console.log(`地址: ${keypair.getAddress()}`);
// Step 2-5: 构建并广播
const client = await SigningStargateClient.connectWithSigner(
'https://rpc.msgchain.org',
keypairToSigner(keypair),
);
const result = await client.sendTokens(
keypair.getAddress(),
'msg1x9v6xkqf4u5a3d7e2n8lkvgzck8wkz3',
[{ denom: 'umsg', amount: '1000000' } as Coin],
{ amount: [{ denom: 'umsg', amount: '5000' }], gas: '200000' },
'Dilithium-5 transaction',
);
console.log(`交易哈希: ${result.transactionHash}`);
// Step 6: 验证
const message = new Uint8Array([/* sign bytes */]);
const valid = keypair.verify(message, result.msgResponses[0].signature);
console.log(`验证: ${valid}`);
}
附录
A: FIPS 204合规性说明
Dilithium-5在MSG Chain中的实现遵循NIST FIPS 204(ML-DSA)标准:
- 算法标识:ML-DSA-87(对应Dilithium-5)
- 密钥生成:确定性或随机性,支持内部熵源和外部熵源
- 签名模式:支持确定性签名(无随机数)和哈希模式
- 验证:严格遵循FIPS 204验证算法
- 域分离:签名和验证中包含域分离标识符,防止跨域攻击
B: 密钥格式参考
| 字段 | 类型 | 大小 | 说明 |
|---|---|---|---|
| PrivKeyDilithium5 | Amino/Protobuf | ~4,864 B | 包含rho, rho', K, s1, s2, t0 |
| PubKeyDilithium5 | Amino/Protobuf | 2,592 B | 包含rho, t1 |
| 地址 | bech32 (msg) | 20 B | SHA3-512(pubkey)[:20] + SHA-256 checksum |
| 签名 | FIPS 204 | 4,595 B | 包含c, z, h |
C: 类型注册标识符
| 类型 | Amino类型名 | Protobuf类型URL |
|---|---|---|
| PubKeyDilithium5 | tendermint/PubKeyDilithium5 | /msgchain.crypto.dilithium.PubKey |
| PrivKeyDilithium5 | tendermint/PrivKeyDilithium5 | /msgchain.crypto.dilithium.PrivKey |
D: 常见问题
Q: Dilithium-5签名比Secp256k1大很多,是否会影响链性能?
A: Dilithium-5签名约4,595字节(Secp256k1约70字节),确实会增大交易大小。但MSG Chain的Gas模型已为此优化,且验证速度快5倍,对全节点更友好。
Q: 如何从Secp256k1迁移到Dilithium-5?
A: MSG Chain支持双轨运行。用户可通过密钥轮换交易逐步迁移,无需一次性切换。详见第八章。
Q: Dilithium-5是否需要特殊硬件?
A: 不需要。Dilithium-5在标准x86-64处理器上运行良好。对于生产验证者,建议使用支持Dilithium-5的HSM。
Q: BIP39助记词能否用于Dilithium-5?
A: 可以。MSG Chain支持从BIP39助记词派生Dilithium-5密钥,使用标准BIP44路径m/44'/118'/0'/0/0。
Q: Quantum_node和genesis_node有何区别?
A: genesis_node_linux是包含Dilithium-5支持的标准节点,quantum_node_linux是纯后量子节点(移除Secp256k1兼容层)。
MSG Chain Whitepaper | https://msgchain.org/whitepaper | 代码库实际状态,不代表生产可用
