Skip to content

Latest commit

 

History

History
 
 

40_PopularCurves

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
title tags
40. 常用椭圆曲线
zk
abstract algebra
elliptic curve
group theory
finite field
pairing

WTF zk 教程第 40 讲:常用椭圆曲线

这一讲,我们将介绍以太坊上常用的三条椭圆曲线:secp256k1bn128,和 bls12_381,其中 secp256k1 也被比特币采用。

1. secp256k1

我们在介绍椭圆曲线离散对数问题(ECDLP)的时候介绍了 secp256k1 曲线,这里再简单介绍一遍。secp256k1 由SECG(Standards for Efficient Cryptography Group)标准化,也是它名字的由来。

secp256k1 的椭圆曲线方程为:

$$ y^2 = x^3 + 7 \mod p $$

它定义在一个特定的有限域 $\mathbb{F}_p$ 上,其中 $p$ 是一个非常大的质数: $p = 2^{256} - 2^{32} - 977$

secp256k1 椭圆曲线上的点群形成了一个循环群,阶为 115792089237316195423570985008687907852837564279074904382605163141518161494337,包含的点非常多,保障了算法的安全性。点 $G(G_x, G_y)$secp256k1 的一个基点/生成元,可以生成所有点,其中:

Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424

secp256k1 被用于比特币和以太坊的公钥生成和数字签名,下面我们利用 py_ecc 库给出代码示例。

1.1 公钥生成

使用 secp256k1 进行公钥生成非常简单,我们先随机生成一个私钥 $x$,然后计算公钥 $y = xG$,其中点 $G$ 是椭圆曲线的基点:

# 椭圆曲线 secp256k1 示例
# 利用标量乘法,从私钥生成公钥
from py_ecc.secp256k1 import secp256k1
import os 

def generate_public_key(private_key):
    """
    使用secp256k1椭圆曲线,根据给定的私钥生成公钥。
    
    参数:
    private_key (int): 私钥,一个大整数。
    
    返回:
    (int, int): 公钥,椭圆曲线上的点。
    """
    # secp256k1的基点
    G = secp256k1.G
    
    # 计算公钥
    public_key = secp256k1.multiply(G, private_key)
    
    return public_key

# 示例:使用一个随机的私钥
private_key = int(os.urandom(32).hex(), 16)

# 生成公钥
public_key = generate_public_key(private_key)

# 打印结果
print(f"Private Key: {private_key}")
print(f"Public Key: {public_key}")

# 示例输出
# Private Key: 40871478222817722377012551921323657605236631423958081783403470740144884256441
# Public Key: (18814187692496112820586797121940816605467606938301853840004393937958984136992, 72833048843328294920821861725991661253504985018641366317346599320677055943891)

1.2 数字签名

我们可以利用 py_ecc.secp256k1 中的 ecdsa_raw_signecdsa_raw_recover 来实现椭圆曲线数字签名算法(ECDSA)。我们之后会更详细的介绍 ECDSA 算法。

# secp256k1 数字签名
from py_ecc.secp256k1 import secp256k1
import os
import hashlib

def sign_message(private_key, message):
    """
    使用私钥对消息进行签名。
    """
    message_hash = hashlib.sha256(message.encode()).digest()
    private_key_bytes = private_key.to_bytes(32, "big")
    signature = secp256k1.ecdsa_raw_sign(message_hash, private_key_bytes)
    return signature

def verify_signature(message, signature, public_key):
    """
    使用公钥验证消息的签名。
    """
    message_hash = hashlib.sha256(message.encode()).digest()
    recovered_public_key = secp256k1.ecdsa_raw_recover(message_hash, signature)
    return recovered_public_key == public_key

# 示例使用
private_key = int.from_bytes(os.urandom(32), 'big')
public_key = secp256k1.multiply(secp256k1.G, private_key) # 计算公钥

print(f"Private Key: {private_key}")
print(f"Public Key: {public_key}")

message = "Hello, blockchain world!"
signature = sign_message(private_key, message) # 签名消息
is_valid = verify_signature(message, signature, public_key) # 尝试从签名恢复公钥

# 比较原始公钥和恢复的公钥
print(f"Signature valid: {is_valid}")

# 示例输出
# Private Key: 100160191408028635805410835424804882729758587641862022398559246101084514055515
# Public Key: (94753041202778772959486517607721465828067708966050249355074253521404789962176, 108824044826657285606250531715029407748756362423778933890891533480553307901806)
# Signature valid: True

2. bn128

bn_128曲线,全称 Barreto-Naehrig 128位曲线,是一种在密码学中广泛使用的配对友好椭圆曲线。它的嵌入度为 12,兼顾加密安全与配对效率,常被用于零知识证明算法中。

bn_128 的椭圆曲线 $E(\mathbb{F}_p)$ 方程为:

$$ y^2 = x^3 + 3 \mod p $$

它定义在一个特定的有限域 $\mathbb{F}_p$ 上,其中 p 是一个大素数。

p = 21888242871839275222246405745257275088548364400416034343698204186575808495617

bn_128 椭圆曲线上的点群形成了一个循环群,阶为 21888242871839275222246405745257275088548364400416034343698204186575808495617,包含的点非常多,保障了算法的安全性。点 $G_1(1, 2)$bn_128 的一个基点/生成元,可以生成所有点。

在构造 Ate 配对时,配对映射 $\hat{\tau}: C_2 \times C_1 \to G_T$,其中 $C_1 = E(\mathbb{F}p), C_2 = E(\mathbb{F}{p^2}), G_T = \mathbb{F}{p^{12}}$。$C_2$ 是为了支持配对操作特别定义在扩域 $\mathbb{F}{p^2}$ 上的曲线,每个点的坐标都由复数表示,基点 $G_2$ 形式为 $(a+bi, c+di)$,其中:

a = 10857046999023057135944570762232829481370756359578518086990519993285655852781
b = 11559732032986387107991004021392285783925812861821192530917403151452391805634
c = 8495653923123431417604973247489272438418190587263600148770280649306958101930
d = 4082367875863433681332203403145435568316851327593401208105741076214120093531

2.1 公钥生成

使用 bn128 曲线生成公钥的过程和 secp256k1 类似。

# bn128 生成公钥
from py_ecc.bn128 import bn128_curve
import os

def generate_bn128_public_key(private_key):
    """
    使用bn_128曲线和给定的私钥生成公钥。
    
    参数:
    private_key (int): 私钥,一个大整数。
    
    返回:
    (int, int): 公钥,bn_128曲线上的点。
    """
    # BN128_G1是bn_128曲线的基点
    public_key = bn128_curve.multiply(bn128_curve.G1, private_key)
    return public_key

# 示例:使用一个随机的私钥
private_key = int.from_bytes(os.urandom(32), 'big')

# 生成公钥
public_key = generate_bn128_public_key(private_key)

# 打印结果
print(f"Private Key: {private_key}")
print(f"Public Key: {public_key}")

# 示例输出
# Private Key: 98359178994781335595533648854802231427270090895769248482397491373685555850978
# Public Key: (3661113004864472419130070831996330893639690693841499262022550248113059694488, 16647856845341024716707355890250833951196099189567973816478113518219363325204)

2.2 双线性配对

我们在第38讲中介绍了如何用 bn128 实现配对,要注意一点是配对是在 $C_2 \times C_1 \to G_T$ 进行的,因此第一个点是 $G_2$ 的倍点,第二个点是 $G_1$ 的倍点,不然会报错。

# bn128 双线性配对
from py_ecc.bn128 import G1, G2, pairing, add, multiply, eq

print(G1)
print(G2)
print("\n")

a = 69
b = 420
c = a * b
A = multiply(G2, a)
B = multiply(G1, b)
pair_A_B = pairing(A, B)
print("Pairing of points A and B: ",pair_A_B)
print("\n")

C = multiply(G2, c)
pair_G2_C = pairing(C, G1)
print("Pairing of points G2 and C: ",pair_G2_C)
print("\n")

print("Is pair_A_B == pair_G2_C? ", pair_A_B == pair_G2_C)

# 输出
# (1, 2)
# ((10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634), (8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531))


# Pairing of points A and B:  (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)


# Pairing of points G2 and C:  (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)


# Is pair_A_B == pair_G2_C?  True

3. bls12_381

bls12_381 属于 Barreto-Lynn-Scott (BLS) 曲线族的一员,是一种配对友好的椭圆曲线。它的嵌入度为 12,配对高效且安全,被以太坊广泛采用,比如POS的节点签名。

bls12_381 的椭圆曲线 $E(\mathbb{F}_p)$ 方程为:

$$ y^2 = x^3 + 4 \mod p $$

它定义在一个特定的有限域 $\mathbb{F}_p$ 上,其中 p 是一个大素数。

p = 21888242871839275222246405745257275088696311157297823662689037894645226208583

bls12_381 椭圆曲线上的点群形成了一个循环群,阶为 52435875175126190479447740508185965837690552500527637822603658699938581184513,包含的点非常多,保障了算法的安全性。点 $G_1(x_1, x_2)$bls12_381 的一个基点/生成元,可以生成所有点。

其中:

x1 = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507
x2 = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569

在构造 Ate 配对时,配对映射 $\hat{\tau}: C_2 \times C_1 \to G_T$,其中 $C_1 = E(\mathbb{F}p), C_2 = E(\mathbb{F}{p^2}), G_T = \mathbb{F}{p^{12}}$。$C_2$ 是为了支持配对操作特别定义在扩域 $\mathbb{F}{p^2}$ 上的曲线,每个点的坐标都由复数表示,基点 $G_2$ 形式为 $(a+bi, c+di)$,其中:

a = 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160
b = 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758
c = 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905
d = 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582

3.1 公钥生成

使用 bls12_381 曲线生成公钥的过程和 bn128 类似。

# bls12_381 生成公钥
from py_ecc.bls12_381 import bls12_381_curve
import os

def generate_bn12_381_public_key(private_key):
    """
    使用bn_128曲线和给定的私钥生成公钥。
    
    参数:
    private_key (int): 私钥,一个大整数。
    
    返回:
    (int, int): 公钥,bn_128曲线上的点。
    """
    # BN128_G1是bn_128曲线的基点
    public_key = bls12_381_curve.multiply(bls12_381_curve.G1, private_key)
    return public_key

# 示例:使用一个随机的私钥
private_key = int.from_bytes(os.urandom(32), 'big')

# 生成公钥
public_key = generate_bn12_381_public_key(private_key)

# 打印结果
print(f"Private Key: {private_key}")
print(f"Public Key: {public_key}")

# 示例输出
# Private Key: 56832202591799069674370871859151631253659339730808373097707650526306669655451
# Public Key: (2672943242084458229690202553507767493858110823696659228443909079159465919837314837610879707240986236828893077890320, 1516123302208562362629191397278119903430202415903098718379575356530260147717671392463098304288800407793122740332702)

3.2 双线性配对

bls12_381 的配对实现和 bn128 类似,同样要注意一点是配对是在 $C_2 \times C_1 \to G_T$ 进行的,因此第一个点是 $G_2$ 的倍点,第二个点是 $G_1$ 的倍点,不然会报错。

# bls12_381 双线性配对
from py_ecc.bls12_381 import G1, G2, pairing, add, multiply, eq

print(G1)
print(G2)
print("\n")

a = 69
b = 420
c = a * b
A = multiply(G2, a)
B = multiply(G1, b)
pair_A_B = pairing(A, B)
print("Pairing of points A and B: ",pair_A_B)
print("\n")

C = multiply(G2, c)
pair_G2_C = pairing(C, G1)
print("Pairing of points G2 and C: ",pair_G2_C)
print("\n")

print("Is pair_A_B == pair_G2_C? ", pair_A_B == pair_G2_C)

# 输出
# (3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507, 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569)
# ((352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160, 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758), (1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905, 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582))


# Pairing of points A and B:  (559875907953044229256999964908655596470329614681804657067074917348889299656574983763205735263906508658423626306192, 3251387332700024622230711712327939415422447046055926038630362608193945095062996827680637432749053965474170688846803, 1858978301495685148589092187900391308425211794249943939941796868269658435530364461212323465553496690057506398710958, 2821404563389658506867792213698964607416184520400354580766817454079710851304957653749944402097411780097914172680501, 3371394908151563362208243796489511825354821020573987752522791881868987441291678825694507113985550194783443672326288, 2297984649797609740520111469542560058998637859756636710986648527547199594538622957148043186058263511759636104385884, 3480762570907741510541555641785158658308333937734941283300383364697580261225975907843679022209374260264933745191828, 3240728397255321363427476858948130370605249287048803384220203355573990507150386888156524321441752887099266166133108, 2376364160413810038009747479244195484637259359228646277111417852748902100064937901534264975269224589631629799823776, 505616797063036550970852124247959597244795697098930755073121309023649013438395471915981324345265013483679297150493, 283646809217572597374969288842854659843926065997224794730305413934017744924705963601539908633134851567640611976585, 208748941951544416005406635656082534817221797560780462964136211816030039051269393961107822344678318294491041226308)


# Pairing of points G2 and C:  (559875907953044229256999964908655596470329614681804657067074917348889299656574983763205735263906508658423626306192, 3251387332700024622230711712327939415422447046055926038630362608193945095062996827680637432749053965474170688846803, 1858978301495685148589092187900391308425211794249943939941796868269658435530364461212323465553496690057506398710958, 2821404563389658506867792213698964607416184520400354580766817454079710851304957653749944402097411780097914172680501, 3371394908151563362208243796489511825354821020573987752522791881868987441291678825694507113985550194783443672326288, 2297984649797609740520111469542560058998637859756636710986648527547199594538622957148043186058263511759636104385884, 3480762570907741510541555641785158658308333937734941283300383364697580261225975907843679022209374260264933745191828, 3240728397255321363427476858948130370605249287048803384220203355573990507150386888156524321441752887099266166133108, 2376364160413810038009747479244195484637259359228646277111417852748902100064937901534264975269224589631629799823776, 505616797063036550970852124247959597244795697098930755073121309023649013438395471915981324345265013483679297150493, 283646809217572597374969288842854659843926065997224794730305413934017744924705963601539908633134851567640611976585, 208748941951544416005406635656082534817221797560780462964136211816030039051269393961107822344678318294491041226308)


# Is pair_A_B == pair_G2_C?  True

4. 总结

这一讲,我们介绍了比特币和以太坊上的常用椭圆曲线:secp256k1bn128,和bls12_381,并基于ecc_py写了简单示例。secp256k1主要用于比特币和以太坊上的公钥生成和数字签名,而bn128bls12_381是配对友好的曲线,可以被用于签名和零知识证明算法。