高級
libsecp256k1
高效能橢圓曲線密碼學庫:比特幣簽名與驗證的核心引擎
20 分鐘
概述
libsecp256k1 是比特幣專用的高效能橢圓曲線密碼學庫, 由 Pieter Wuille 主導開發。它實現了 secp256k1 曲線上的 ECDSA、Schnorr 簽名和各種密碼學原語,是 Bitcoin Core 的核心依賴。
安全關鍵: 這個庫處理所有比特幣私鑰和簽名操作。 它經過廣泛的安全審計和形式化驗證,是經過實戰檢驗的密碼學實現。
secp256k1 曲線
曲線參數
secp256k1 橢圓曲線:
方程式: y² = x³ + 7 (mod p)
參數:
p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
= 2²⁵⁶ - 2³² - 977
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
(群的階)
G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)
(生成元)
特性:
- Koblitz 曲線(係數簡單)
- 大質數階(安全)
- 高效能運算
- 非 NIST 曲線(無後門疑慮) 為什麼選擇 secp256k1
中本聰選擇 secp256k1 的原因(推測):
1. 非 NIST 標準
- 避免潛在的政府後門
- 參數來源透明
2. 效能優勢
- 特殊曲線形式允許優化
- 係數 a=0, b=7 簡化運算
- 支持 GLV endomorphism 加速
3. 安全性
- 256 位安全等級
- 無已知弱點
- 經過廣泛密碼學分析 核心功能
模組列表
libsecp256k1 模組:
┌─────────────────────────────────────┐
│ 核心模組 │
├─────────────────────────────────────┤
│ ECDSA - 簽名與驗證 │
│ Schnorr - BIP-340 簽名 │
│ ECDH - 密鑰交換 │
│ Recovery - 公鑰恢復 │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 額外模組 │
├─────────────────────────────────────┤
│ MuSig2 - 聚合簽名 │
│ ElligatorSwift - 公鑰編碼 │
│ Extrakeys - x-only 公鑰 │
│ Generator - 自定義生成元 │
│ Bulletproofs - 範圍證明 (實驗性) │
└─────────────────────────────────────┘ ECDSA 簽名
簽名流程
ECDSA 簽名算法:
輸入: 私鑰 d, 訊息雜湊 z
1. 選擇隨機數 k ∈ [1, n-1]
2. 計算 (x, y) = k·G
3. r = x mod n
4. 如果 r = 0,返回步驟 1
5. s = k⁻¹(z + r·d) mod n
6. 如果 s = 0,返回步驟 1
7. 簽名 = (r, s)
DER 編碼:
30 [總長度]
02 [r 長度] [r 值]
02 [s 長度] [s 值]
典型大小: 70-72 bytes C API 使用
#include <secp256k1.h>
// 創建上下文
secp256k1_context *ctx = secp256k1_context_create(
SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY
);
// 生成密鑰對
unsigned char seckey[32];
// ... 填充隨機數據 ...
secp256k1_pubkey pubkey;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)) {
// 處理錯誤
}
// 序列化公鑰
unsigned char pubkey_serialized[33];
size_t pubkey_len = sizeof(pubkey_serialized);
secp256k1_ec_pubkey_serialize(
ctx, pubkey_serialized, &pubkey_len,
&pubkey, SECP256K1_EC_COMPRESSED
);
// ECDSA 簽名
unsigned char msg_hash[32];
// ... 計算訊息雜湊 ...
secp256k1_ecdsa_signature sig;
if (!secp256k1_ecdsa_sign(ctx, &sig, msg_hash, seckey, NULL, NULL)) {
// 處理錯誤
}
// 序列化簽名 (DER)
unsigned char sig_der[72];
size_t sig_len = sizeof(sig_der);
secp256k1_ecdsa_signature_serialize_der(ctx, sig_der, &sig_len, &sig);
// 驗證簽名
if (secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey)) {
printf("簽名有效\n");
}
// 清理
secp256k1_context_destroy(ctx); Schnorr 簽名
BIP-340 簽名
Schnorr 簽名算法 (BIP-340):
輸入: 私鑰 d, 訊息 m
1. 如果 d·G 的 y 座標為奇數,d = n - d
2. k = hash("BIP0340/aux", aux) XOR d
3. R = k·G
4. 如果 R 的 y 座標為奇數,k = n - k
5. e = hash("BIP0340/challenge", R.x || P.x || m)
6. s = k + e·d mod n
7. 簽名 = (R.x, s)
大小: 固定 64 bytes
優勢:
- 線性簽名允許聚合
- 無可塑性
- 批量驗證更快 Schnorr API
#include <secp256k1_schnorrsig.h>
#include <secp256k1_extrakeys.h>
// x-only 公鑰
secp256k1_xonly_pubkey xonly_pubkey;
secp256k1_keypair keypair;
// 創建密鑰對
secp256k1_keypair_create(ctx, &keypair, seckey);
secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey, NULL, &keypair);
// Schnorr 簽名
unsigned char sig[64];
unsigned char msg[32];
// ... 填充訊息 ...
if (!secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL)) {
// 處理錯誤
}
// 驗證
if (secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &xonly_pubkey)) {
printf("Schnorr 簽名有效\n");
} ECDH 密鑰交換
#include <secp256k1_ecdh.h>
// Alice 的密鑰對
unsigned char alice_seckey[32];
secp256k1_pubkey alice_pubkey;
// ... 生成 ...
// Bob 的密鑰對
unsigned char bob_seckey[32];
secp256k1_pubkey bob_pubkey;
// ... 生成 ...
// Alice 計算共享密鑰
unsigned char shared_secret_alice[32];
secp256k1_ecdh(ctx, shared_secret_alice, &bob_pubkey, alice_seckey, NULL, NULL);
// Bob 計算共享密鑰
unsigned char shared_secret_bob[32];
secp256k1_ecdh(ctx, shared_secret_bob, &alice_pubkey, bob_seckey, NULL, NULL);
// shared_secret_alice == shared_secret_bob 公鑰恢復
從簽名恢復公鑰:
ECDSA 簽名包含足夠信息來恢復公鑰(最多 4 個候選)
恢復 ID (recid):
- 0-3 的值
- 指示使用哪個候選公鑰
用途:
- 以太坊交易簽名
- 節省交易空間(不需發送公鑰)
- 訊息簽名驗證 #include <secp256k1_recovery.h>
// 可恢復簽名
secp256k1_ecdsa_recoverable_signature rec_sig;
secp256k1_ecdsa_sign_recoverable(ctx, &rec_sig, msg_hash, seckey, NULL, NULL);
// 序列化(包含 recovery id)
unsigned char sig_compact[64];
int recid;
secp256k1_ecdsa_recoverable_signature_serialize_compact(
ctx, sig_compact, &recid, &rec_sig
);
// 恢復公鑰
secp256k1_pubkey recovered_pubkey;
secp256k1_ecdsa_recover(ctx, &recovered_pubkey, &rec_sig, msg_hash); MuSig2 聚合簽名
#include <secp256k1_musig.h>
// 參與者公鑰
secp256k1_pubkey pubkeys[3];
const secp256k1_pubkey *pubkey_ptrs[3] = {
&pubkeys[0], &pubkeys[1], &pubkeys[2]
};
// 密鑰聚合
secp256k1_musig_keyagg_cache keyagg_cache;
secp256k1_xonly_pubkey agg_pubkey;
secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pubkey, &keyagg_cache, pubkey_ptrs, 3);
// Round 1: 生成 nonces
secp256k1_musig_secnonce secnonces[3];
secp256k1_musig_pubnonce pubnonces[3];
for (int i = 0; i < 3; i++) {
unsigned char session_id[32];
// ... 隨機填充 ...
secp256k1_musig_nonce_gen(
ctx, &secnonces[i], &pubnonces[i],
session_id, seckeys[i], &pubkeys[i],
msg, &keyagg_cache, NULL
);
}
// 聚合 nonces
const secp256k1_musig_pubnonce *pubnonce_ptrs[3] = {
&pubnonces[0], &pubnonces[1], &pubnonces[2]
};
secp256k1_musig_aggnonce agg_nonce;
secp256k1_musig_nonce_agg(ctx, &agg_nonce, pubnonce_ptrs, 3);
// Round 2: 部分簽名
secp256k1_musig_session session;
secp256k1_musig_nonce_process(ctx, &session, &agg_nonce, msg, &keyagg_cache, NULL);
secp256k1_musig_partial_sig partial_sigs[3];
for (int i = 0; i < 3; i++) {
secp256k1_musig_partial_sign(
ctx, &partial_sigs[i], &secnonces[i],
&keypairs[i], &keyagg_cache, &session
);
}
// 聚合簽名
const secp256k1_musig_partial_sig *partial_sig_ptrs[3] = {
&partial_sigs[0], &partial_sigs[1], &partial_sigs[2]
};
unsigned char final_sig[64];
secp256k1_musig_partial_sig_agg(ctx, final_sig, &session, partial_sig_ptrs, 3); ElligatorSwift
#include <secp256k1_ellswift.h>
// 編碼公鑰為 64 字節
unsigned char ellswift_encoded[64];
unsigned char random32[32];
// ... 填充隨機數據 ...
secp256k1_ellswift_create(ctx, ellswift_encoded, seckey, random32);
// 解碼回公鑰
secp256k1_pubkey decoded_pubkey;
secp256k1_ellswift_decode(ctx, &decoded_pubkey, ellswift_encoded);
// ECDH with ElligatorSwift
unsigned char shared_secret[32];
secp256k1_ellswift_xdh(
ctx, shared_secret,
our_ellswift, their_ellswift,
seckey, 0, // 0 = initiator, 1 = responder
secp256k1_ellswift_xdh_hash_function_bip324,
NULL
); 效能優化
優化技術
libsecp256k1 優化:
1. GLV Endomorphism
- 加速標量乘法 ~33%
- 利用曲線的特殊結構
- 將 256 位乘法拆分為兩個 128 位
2. 預計算表
- 生成元 G 的倍數預計算
- 加速簽名和驗證
3. 批量驗證
- 同時驗證多個簽名
- Schnorr: ~2.5x 加速 (n=100)
- ECDSA: 有限加速
4. 常數時間運算
- 防止時序攻擊
- 所有分支無關密鑰
5. 組裝優化
- x86_64 專用代碼
- ARM NEON 支持 基準測試
典型效能 (Intel i7):
ECDSA 簽名: ~45,000/秒
ECDSA 驗證: ~18,000/秒
Schnorr 簽名: ~45,000/秒
Schnorr 驗證: ~18,000/秒
Schnorr 批量驗證 (100): ~45,000/秒
密鑰生成: ~50,000/秒
ECDH: ~25,000/秒
對比 OpenSSL:
libsecp256k1 快 ~4-10 倍 安全特性
安全設計:
1. 常數時間
- 所有密鑰相關運算
- 防止時序側信道攻擊
2. 記憶體清理
- 敏感數據使用後清零
- 防止記憶體洩露
3. 隨機數安全
- RFC 6979 確定性 nonce
- 防止 nonce 重用攻擊
4. 驗證
- 輸入驗證
- 曲線點有效性檢查
5. 測試
- 廣泛的單元測試
- 模糊測試
- 交叉驗證 語言綁定
TypeScript/JavaScript
import * as secp256k1 from '@noble/secp256k1';
// 生成密鑰對
const privateKey = secp256k1.utils.randomPrivateKey();
const publicKey = secp256k1.getPublicKey(privateKey);
// ECDSA 簽名
const message = new TextEncoder().encode('Hello Bitcoin');
const msgHash = await secp256k1.utils.sha256(message);
const signature = await secp256k1.signAsync(msgHash, privateKey);
// 驗證
const isValid = secp256k1.verify(signature, msgHash, publicKey);
// Schnorr 簽名
const schnorrSig = await secp256k1.schnorr.sign(msgHash, privateKey);
const schnorrValid = await secp256k1.schnorr.verify(
schnorrSig, msgHash, publicKey.slice(1)
); Rust
use secp256k1::{Secp256k1, SecretKey, PublicKey, Message};
use secp256k1::rand::rngs::OsRng;
// 創建上下文
let secp = Secp256k1::new();
// 生成密鑰對
let (secret_key, public_key) = secp.generate_keypair(&mut OsRng);
// 簽名
let message = Message::from_digest_slice(&msg_hash)?;
let signature = secp.sign_ecdsa(&message, &secret_key);
// 驗證
secp.verify_ecdsa(&message, &signature, &public_key)?; Python
import secp256k1
# 生成私鑰
privkey = secp256k1.PrivateKey()
# 獲取公鑰
pubkey = privkey.pubkey
# 簽名
msg = b"Hello Bitcoin"
sig = privkey.ecdsa_sign(msg)
# 驗證
assert pubkey.ecdsa_verify(msg, sig) 編譯與配置
# 克隆倉庫
git clone https://github.com/bitcoin-core/secp256k1
cd secp256k1
# 配置(啟用所有模組)
./autogen.sh
./configure \
--enable-module-ecdh \
--enable-module-recovery \
--enable-module-schnorrsig \
--enable-module-musig \
--enable-module-ellswift
# 編譯
make
# 測試
make check
# 基準測試
./bench_internal
./bench_ecmult 最佳實踐
- 使用官方綁定:優先使用經過審計的語言綁定
- 隨機數安全:確保 PRNG 的高質量
- 記憶體安全:使用後清零敏感數據
- 版本更新:及時更新到最新版本
- 測試覆蓋:對密碼學代碼進行廣泛測試
相關資源
已複製連結