以太坊密碼學基礎完整指南:橢圓曲線與數位簽章

深入探討以太坊的密碼學基礎,包括 secp256k1 橢圓曲線、ECDSA 簽章演算法、Keccak-256 雜湊函數,以及它們如何在以太坊中協同工作,提供完整的數學原理與程式碼範例。

以太坊密碼學基礎完整指南:橢圓曲線與數位簽章

概述

密碼學是以太坊安全性的基石。從帳戶地址生成到交易簽章驗證,從智慧合約狀態雜湊到共識機制,密碼學無處不在。理解這些底層技術不僅對於區塊鏈開發者至關重要,也是評估系統安全性的必要知識。

本文深入探討以太坊使用的密碼學技術,特別是 secp256k1 橢圓曲線、ECDSA 簽章演算法、Keccak-256 雜湊函數,以及它們如何在以太坊生態中協同工作。我們將從數學原理出發,結合實際程式碼範例,幫助讀者建立完整的密碼學知識體系。

一、橢圓曲線密碼學基礎

1.1 橢圓曲線數學原理

橢圓曲線密碼學(Elliptic Curve Cryptography, ECC)是一種基於橢圓曲線代數結構的公鑰密碼學系統。與傳統的 RSA 相比,ECC 以更短的密鑰長度提供同等的安全性,這使其特別適合資源受限的區塊鏈環境。

橢圓曲線的數學定義如下:

y² = x³ + ax + b (mod p)

其中 a 和 b 是曲線參數,p 是大質數。以太坊使用的 secp256k1 曲線參數為:

這個曲線在有限域 Fp 上定義,意味著所有運算都對質數 p 取模。曲線上的點 (x, y) 滿足上述方程,且存在一個特殊的無窮遠點 O(稱為零點或 identity element)。

1.2 橢圓曲線上的運算

點加法:給定曲線上兩個不同點 P 和 Q,存在唯一第三點 R = P + Q,其計算方式為:

λ = (y₂ - y₁) / (x₂ - x₁) (mod p)
x₃ = λ² - x₁ - x₂ (mod p)
y₃ = λ(x₁ - x₃) - y₁ (mod p)

倍點運算:給定曲線上一点 P,計算 nP(即 P 添加自身 n 次)稱為倍點運算。這是 ECC 安全性的核心——已知點 P 和 nP 計算 n 在計算上是不可行的(離散對數問題)。

λ = (3x₁² + a) / (2y₁) (mod p)
x₃ = λ² - 2x₁ (mod p)
y₃ = λ(x₁ - x₃) - y₁ (mod p)

1.3 橢圓曲線的安全性

橢圓曲線密碼學的安全性基於以下計算困難問題:

  1. 橢圓曲線離散對數問題(ECDLP):給定曲線上點 P 和 Q = nP,在計算上無法有效地確定 n。
  1. 橢圓曲線 Diffie-Hellman(ECDH):允許雙方在不安全通道上建立共享密鑰。
  1. 橢圓曲線數位簽章演算法(ECDSA):用於數位簽章和身份驗證。

這些問題的計算困難性使得 ECC 能夠以較短的密鑰提供與 RSA 相當的安全性。根據 NIST 建議,256 位 ECC 密鑰大致相當於 3072 位 RSA 密鑰的安全性。

二、secp256k1 曲線詳解

2.1 曲線參數

secp256k1 是以太坊採用的橢圓曲線,其標準化參數定義如下:

曲線方程:y² = x³ + 7

質數 p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F
       = 2^256 - 2^32 - 2^9 - 2^8 - 2^7 - 2^6 - 2^4 - 1

基點 G 的座標:
Gx = 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798
Gy = 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8

基點階 n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141

輔助因子 h = 1

選擇 secp256k1 的原因包括:

  1. 效率:曲線參數經過優化,點運算速度快
  2. 安全性:曲線結構已知是安全的,沒有已知弱點
  3. 簡潔:參數選擇簡單,易於實現
  4. 比特幣先例:比特幣已採用此曲線,有豐富的實作經驗

2.2 基點與生成元

基點 G 是曲線上的一個特殊點,所有其他點都可以通過重複倍點運算生成。基點的選擇必須確保其階 n 是質數,且 n 乘以任何小整數都不會得到零點。

在 secp256k1 中,基點 G 的選擇經過精心設計,確保:

2.3 點壓縮與表示

橢圓曲線點可以使用多種表示方式:

未壓縮表示:04 [X座標][Y座標],共 65 位元組

壓縮表示

共 33 位元組

以太坊地址生成中使用未壓壓縮表示,而交易簽章中則使用壓縮表示以節省空間。

三、ECDSA 數位簽章演算法

3.1 簽章生成過程

ECDSA(Elliptic Curve Digital Signature Algorithm)是以太坊使用的數位簽章標準。簽章過程如下:

輸入:私鑰 d、消息 m
輸出:簽章 (r, s)

1. 計算消息雜湊:e = SHA256(m) 或使用 Keccak-256

2. 生成隨機臨時密鑰 k(重要:必須隨機且每次不同)

3. 計算臨時公鑰:
   R = k * G
   r = Rx mod n(取 x 座標對 n 取模)

4. 計算 s:
   s = k⁻¹ * (e + r * d) mod n
   其中 k⁻¹ 是 k 在模 n 下的乘法逆元

5. 如果 r = 0 或 s = 0,重新選擇 k

返回值:(r, s)

3.2 簽章驗證過程

輸入:公鑰 Q、消息 m、簽章 (r, s)
輸出:有效/無效

1. 驗證 r, s 在有效範圍內:
   1 ≤ r < n
   1 ≤ s < n

2. 計算消息雜湊:e = SHA256(m) 或 Keccak-256

3. 計算 s 的逆元:s⁻¹ mod n

4. 計算:
   u₁ = e * s⁻¹ mod n
   u₂ = r * s⁻¹ mod n

5. 計算驗證點:
   P = u₁ * G + u₂ * Q

6. 驗證:
   如果 P 是無窮遠點,返回無效
   計算 v = Px mod n
   如果 v = r,返回有效
   否則返回無效

3.3 臨時密鑰 k 的重要性

臨時密鑰 k 的選擇對 ECDSA 安全性至關重要:

重用 k 的災難

如果相同的 k 用於簽章兩條不同消息,可以從兩個簽章中直接計算出私鑰:

假設使用相同 k 簽署消息 m1 和 m2:
s1 = k⁻¹ * (e1 + r * d) mod n
s2 = k⁻¹ * (e2 + r * d) mod n

通過代數運算:
d = (s1 * e1 - s2 * e2) * (s2 * r - s1 * r)⁻¹ mod n

比特幣和以太坊歷史上都發生過因 k 重用導致私鑰洩露的事件。2010 年比特幣客戶端曾因隨機數生成器問題導致私鑰洩露。

確定性簽章(RFC 6979)

為避免隨機數生成問題,RFC 6979 定義了確定性 ECDSA:

以太坊客戶端通常實現確定性簽章,確保安全性。

3.4 簽章類型:Replay Protection

以太坊使用交易的 RLP 編碼內容作為簽章消息,這自動提供了 replay protection:

四、以太坊地址生成

4.1 從私鑰到地址

以太坊地址生成過程如下:

1. 生成隨機私鑰:256 位隨機數 d(1 到 n-1 之間)

2. 生成公鑰:Q = d * G
   - 使用橢圓曲線倍點運算
   - 公鑰是曲線上的一個點 (x, y)

3. Keccak-256 雜湊公鑰:
   h = Keccak256(0x04 || Qx || Qy)
   注意:使用 0x04 前綴表示未壓縮公鑰

4. 取雜湊最後 20 位元組作為地址:
   address = "0x" || last20Bytes(h)

5. 通常轉換為 checksum 格式:
   EIP-55:對地址字母大小寫進行編碼

4.2 地址空間

以太坊地址是 20 位元組(160 位):

4.3 EOA 與合約帳戶

以太坊有兩種帳戶類型:

外部擁有帳戶(EOA)

合約帳戶

兩種帳戶類型共享相同的地址空間,但控制方式完全不同。

五、Keccak-256 雜湊函數

5.1 Keccak 演算法

Keccak 是 SHA-3 標準的基礎演算法,採用海綿結構(Sponge Construction):

┌────────────────────────────────────────────┐
│                                            │
│  Message Input ──▶│  f   │──▶  Hash Output │
│                    │      │                 │
│    (padding)       │      │   (optional)   │
│                    └──────┘                 │
│              Rate (r)   Capacity (c)       │
└────────────────────────────────────────────┘

Keccak-256 的參數:

5.2 以太坊中的 Keccak

以太坊使用 Keccak-256(而非標準化的 SHA-3-256)用於:

為什麼選擇 Keccak:

  1. SHA-3 標準化時以太坊已經使用 Keccak
  2. Keccak 已經過充分審計
  3. 避免與 NIST SHA-3 標準可能的關聯

5.3 Keccak vs SHA-3

雖然 Keccak 是 SHA-3 的基礎,但兩者不完全相同:

在以太坊生態中,引用 "sha3" 通常指的是 Keccak-256。

5.4 雜湊函數的安全性要求

密碼學雜湊函數必須滿足:

  1. 原像阻力:給定雜湊值 h,找到滿足 SHA256(m) = h 的 m 在計算上不可行
  2. 第二原像阻力:給定 m1,找到滿足 SHA256(m1) = SHA256(m2) 的 m2 ≠ m1 在計算上不可行
  3. 碰撞阻力:找到任意滿足 SHA256(m1) = SHA256(m2) 的 m1 ≠ m2 在計算上不可行
  4. 雪崩效應:輸入變化一位,輸出平均變化約 50%

Keccak-256 被認為具備所有這些安全特性。

六、簽章與交易驗證

6.1 交易類型

以太坊有不同類型的交易:

Legacy 交易(Type 0)

RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])

EIP-2930 交易(Type 1)

RLP([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList, v, r, s])

EIP-1559 交易(Type 2)

RLP([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, accessList, v, r, s])

6.2 簽章訊息

不同交易類型使用不同的簽章訊息:

Legacy 交易

msg = SHA3(RLP([nonce, gasPrice, gasLimit, to, value, data]))

EIP-1559 交易

msg = SHA3(0x02 || RLP([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, accessList]))

6.3 驗證流程

節點收到交易後的驗證流程:

1. 格式驗證:
   - RLP 解碼成功
   - 所有欄位類型正確
   - 無效字段為空

2. 範圍驗證:
   - nonce 是非負整數
   - gasLimit > 0
   - value >= 0
   - 簽章 (r, s) 在有效範圍內

3. 鏈 ID 驗證(EIP-155):
   - 從 v 值提取 chainId
   - 必須匹配當前鏈

4. 簽章驗證:
   - 恢復公鑰
   - 驗證簽章有效性

5. 帳戶狀態驗證:
   - nonce 正確
   - 餘額足夠支付 gas

6. Gas 估算:
   - 估算執行成本
   - 確保 gasLimit 足夠

6.4 ECRECOVER 與公鑰恢復

以太坊提供 ECRECOVER 操作碼用於從簽章恢復地址:

function ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)

工作原理:

  1. 使用 v, r, s 計算可能的公鑰點
  2. 從公鑰計算地址
  3. 返回地址

注意:ECRECOVER 在以下情況會返回零地址:

這是許多簽章相關漏洞的根源,開發者應注意處理這種情況。

七、常見漏洞與防護

7.1 簽章重放攻擊

問題:未經授權的第三方可以重放有效的交易。

以太坊防護

7.2 密鑰管理漏洞

問題:私鑰洩露導致資金被盜。

防護建議

7.3 隨機數生成漏洞

問題:弱隨機數導致私鑰或簽章可預測。

防護

7.4 橢圓曲線攻擊

問題:針對特定曲線參數的攻擊。

防護

八、程式碼範例

8.1 Python 實現橢圓曲線運算

import hashlib

# secp256k1 曲線參數
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
A = 0
B = 7
Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __repr__(self):
        return f"({hex(self.x)}, {hex(self.y)})"

# 零點(無窮遠點)
ZERO = Point(None, None)

def mod_inverse(a, m):
    """計算模逆元"""
    if a < 0:
        a = (a % m + m) % m
    g, x, _ = extended_gcd(a, m)
    if g != 1:
        raise Exception('Modular inverse does not exist')
    return x % m

def extended_gcd(a, b):
    if a == 0:
        return b, 0, 1
    gcd, x1, y1 = extended_gcd(b % a, a)
    x = y1 - (b // a) * x1
    y = x1
    return gcd, x, y

def point_add(p1, p2):
    """橢圓曲線點加法"""
    if p1 == ZERO:
        return p2
    if p2 == ZERO:
        return p1

    if p1.x == p2.x:
        if p1.y == p2.y:
            return point_double(p1)
        return ZERO

    # 計算斜率
    slope = ((p2.y - p1.y) * mod_inverse(p2.x - p1.x, P)) % P

    # 計算新點
    x3 = (slope * slope - p1.x - p2.x) % P
    y3 = (slope * (p1.x - x3) - p1.y) % P

    return Point(x3, y3)

def point_double(p):
    """橢圓曲線倍點"""
    if p == ZERO:
        return p

    # 計算斜率
    slope = ((3 * p.x * p.x + A) * mod_inverse(2 * p.y, P)) % P

    # 計算新點
    x3 = (slope * slope - 2 * p.x) % P
    y3 = (slope * (p.x - x3) - p.y) % P

    return Point(x3, y3)

def scalar_mult(k, p):
    """標量乘法(二進制法)"""
    result = ZERO
    while k:
        if k & 1:
            result = point_add(result, p)
        p = point_double(p)
        k >>= 1
    return result

8.2 地址生成範例

def keccak256(data):
    """Keccak-256 雜湊"""
    import sha3
    k = sha3.keccak_256()
    k.update(data)
    return k.digest()

def pubkey_to_address(pubkey_bytes):
    """從公鑰字節生成以太坊地址"""
    # Keccak-256 雜湊公鑰
    h = keccak256(pubkey_bytes)
    # 取最後 20 位元組
    return "0x" + h[-20:].hex()

def generate_address_from_private_key(private_key_hex):
    """從私鑰生成以太坊地址"""
    # 將私鑰轉換為整數
    d = int(private_key_hex, 16)

    # 生成公鑰
    G = Point(Gx, Gy)
    Q = scalar_mult(d, G)

    # 轉換為未壓縮格式(0x04 前綴)
    pubkey_bytes = b'\x04' + Q.x.to_bytes(32, 'big') + Q.y.to_bytes(32, 'big')

    # 生成地址
    return pubkey_to_address(pubkey_bytes)

# 測試
test_privkey = "0x" + "01" * 32  # 測試私鑰
address = generate_address_from_private_key(test_privkey)
print(f"測試地址: {address}")

8.3 ECDSA 簽章範例

def hash_message(message):
    """對消息進行 Keccak-256 雜湊"""
    if isinstance(message, str):
        message = message.encode('utf-8')
    return int.from_bytes(keccak256(message), 'big')

def sign_message(message, private_key):
    """ECDSA 簽章(確定性 RFC 6979)"""
    e = hash_message(message)
    d = private_key

    # RFC 6979 確定性 k 生成
    v = b'\x01' * 32
    k = b'\x00' * 32

    # 這裡省略 RFC 6979 完整實現
    # 實際實現需要複雜的確定性隨機數生成

    # 計算 r, s
    G = Point(Gx, Gy)
    R = scalar_mult(k, G)
    r = R.x % N

    s = (mod_inverse(k, N) * (e + r * d)) % N

    return (r, s)

def verify_signature(message, signature, public_key):
    """驗證 ECDSA 簽章"""
    r, s = signature

    # 驗證 r, s 範圍
    if not (1 <= r < N and 1 <= s < N):
        return False

    e = hash_message(message)
    s_inv = mod_inverse(s, N)

    u1 = (e * s_inv) % N
    u2 = (r * s_inv) % N

    G = Point(Gx, Gy)
    P = point_add(scalar_mult(u1, G), scalar_mult(u2, public_key))

    if P == ZERO:
        return False

    return (P.x % N) == r

九、未來發展:後量子密碼學

9.1 量子計算威脅

量子計算機的發展對現有密碼學構成潛在威脅:

Shor 演算法:可在多項式時間內分解大整數和計算離散對數

Grover 演算法:可加速雜湊函數暴力搜索

9.2 後量子密碼學候選

基於格(Lattice-based)

基於雜湊(Hash-based)

基於碼(Code-based)

9.3 以太坊的應對

以太坊社區正在評估後量子遷移策略:

  1. 短期:關注量子計算發展態勢
  2. 中期:準備遷移路徑
  3. 長期:可能需要硬分叉升級

預計在量子計算機實際威脅到 secp256k1 之前,以太坊有充足時間進行準備。

總結

以太坊的密碼學基礎建立在經過驗證的橢圓曲線密碼學之上。secp256k1 曲線、ECDSA 簽章和 Keccak-256 雜湊函數共同構成了以太坊安全性的基石。

理解這些底層技術對於:

至關重要。雖然這些技術對大多數用戶是透明的,但任何安全相關的決策都應該基於對這些基礎原理的理解。

隨著後量子密碼學的發展,以太坊密碼學棧也將持續演進。開發者和安全專業人員應持續關注這些領域的最新發展,確保系統的安全性能夠應對未來的挑戰。

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!