Skip to content

Commit 05622d3

Browse files
committed
update native bn128 code
1 parent 4c5ca27 commit 05622d3

1 file changed

Lines changed: 253 additions & 29 deletions

File tree

bn128.go

Lines changed: 253 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,47 +1089,271 @@ func millerLoop(q *G1, p *G2) *Fp12 {
10891089
r = r.Add(p)
10901090
}
10911091
}
1092-
10931092
return f
10941093
}
10951094

1096-
// finalExponentiation computes the final exponentiation for the ate pairing
1097-
// Raises f to the power (p^12 - 1) / r
1098-
func finalExponentiation(f *Fp12) *Fp12 {
1099-
// Easy part: (p^6 - 1)(p^2 + 1)
1100-
// First: f^(p^6 - 1)
1101-
t0 := &Fp12{
1102-
c0: f.c0.Copy(),
1103-
c1: f.c1.Neg(),
1095+
// finalExponentiation computes the (p¹²-1)/Order-th power of an element of
1096+
// GF(p¹²) to obtain an element of GT
1097+
// This follows the exact algorithm from Cloudflare's bn256 and golang.org/x/crypto/bn256
1098+
func finalExponentiation(in *Fp12) *Fp12 {
1099+
t1 := &Fp12{}
1100+
1101+
// This is the p^6-Frobenius (conjugate in Fp12)
1102+
t1.c0 = in.c0.Copy()
1103+
t1.c1 = in.c1.Neg()
1104+
1105+
// Compute inverse and multiply: t1 = in^(p^6-1)
1106+
inv := in.Inverse()
1107+
t1 = t1.Mul(inv)
1108+
1109+
// Apply p^2 Frobenius: conjugate again
1110+
t2 := &Fp12{
1111+
c0: t1.c0.Copy(),
1112+
c1: t1.c1.Neg(),
1113+
}
1114+
// t1 = t1^(p^2+1)
1115+
t1 = t1.Mul(t2)
1116+
1117+
// Now the hard part
1118+
// Compute Frobenius maps of t1
1119+
fp := &Fp12{
1120+
c0: &Fp6{
1121+
c0: &Fp2{a: t1.c0.c0.a, b: new(big.Int).Neg(t1.c0.c0.b).Mod(new(big.Int).Neg(t1.c0.c0.b), P)},
1122+
c1: &Fp2{a: t1.c0.c1.a, b: new(big.Int).Neg(t1.c0.c1.b).Mod(new(big.Int).Neg(t1.c0.c1.b), P)},
1123+
c2: &Fp2{a: t1.c0.c2.a, b: new(big.Int).Neg(t1.c0.c2.b).Mod(new(big.Int).Neg(t1.c0.c2.b), P)},
1124+
},
1125+
c1: &Fp6{
1126+
c0: &Fp2{a: t1.c1.c0.a, b: new(big.Int).Neg(t1.c1.c0.b).Mod(new(big.Int).Neg(t1.c1.c0.b), P)},
1127+
c1: &Fp2{a: t1.c1.c1.a, b: new(big.Int).Neg(t1.c1.c1.b).Mod(new(big.Int).Neg(t1.c1.c1.b), P)},
1128+
c2: &Fp2{a: t1.c1.c2.a, b: new(big.Int).Neg(t1.c1.c2.b).Mod(new(big.Int).Neg(t1.c1.c2.b), P)},
1129+
},
1130+
}
1131+
1132+
fp2 := &Fp12{
1133+
c0: t1.c0.Copy(),
1134+
c1: t1.c1.Neg(),
1135+
}
1136+
1137+
fp3 := &Fp12{
1138+
c0: &Fp6{
1139+
c0: &Fp2{a: fp2.c0.c0.a, b: new(big.Int).Neg(fp2.c0.c0.b).Mod(new(big.Int).Neg(fp2.c0.c0.b), P)},
1140+
c1: &Fp2{a: fp2.c0.c1.a, b: new(big.Int).Neg(fp2.c0.c1.b).Mod(new(big.Int).Neg(fp2.c0.c1.b), P)},
1141+
c2: &Fp2{a: fp2.c0.c2.a, b: new(big.Int).Neg(fp2.c0.c2.b).Mod(new(big.Int).Neg(fp2.c0.c2.b), P)},
1142+
},
1143+
c1: &Fp6{
1144+
c0: &Fp2{a: fp2.c1.c0.a, b: new(big.Int).Neg(fp2.c1.c0.b).Mod(new(big.Int).Neg(fp2.c1.c0.b), P)},
1145+
c1: &Fp2{a: fp2.c1.c1.a, b: new(big.Int).Neg(fp2.c1.c1.b).Mod(new(big.Int).Neg(fp2.c1.c1.b), P)},
1146+
c2: &Fp2{a: fp2.c1.c2.a, b: new(big.Int).Neg(fp2.c1.c2.b).Mod(new(big.Int).Neg(fp2.c1.c2.b), P)},
1147+
},
1148+
}
1149+
1150+
// Exponentiate by u
1151+
u := fromHex("44e992b44a6909f1")
1152+
fu := t1.Exp(u)
1153+
fu2 := fu.Exp(u)
1154+
fu3 := fu2.Exp(u)
1155+
1156+
// Apply Frobenius to exponentiations
1157+
y3 := &Fp12{
1158+
c0: &Fp6{
1159+
c0: &Fp2{a: fu.c0.c0.a, b: new(big.Int).Neg(fu.c0.c0.b).Mod(new(big.Int).Neg(fu.c0.c0.b), P)},
1160+
c1: &Fp2{a: fu.c0.c1.a, b: new(big.Int).Neg(fu.c0.c1.b).Mod(new(big.Int).Neg(fu.c0.c1.b), P)},
1161+
c2: &Fp2{a: fu.c0.c2.a, b: new(big.Int).Neg(fu.c0.c2.b).Mod(new(big.Int).Neg(fu.c0.c2.b), P)},
1162+
},
1163+
c1: &Fp6{
1164+
c0: &Fp2{a: fu.c1.c0.a, b: new(big.Int).Neg(fu.c1.c0.b).Mod(new(big.Int).Neg(fu.c1.c0.b), P)},
1165+
c1: &Fp2{a: fu.c1.c1.a, b: new(big.Int).Neg(fu.c1.c1.b).Mod(new(big.Int).Neg(fu.c1.c1.b), P)},
1166+
c2: &Fp2{a: fu.c1.c2.a, b: new(big.Int).Neg(fu.c1.c2.b).Mod(new(big.Int).Neg(fu.c1.c2.b), P)},
1167+
},
1168+
}
1169+
1170+
fu2p := &Fp12{
1171+
c0: &Fp6{
1172+
c0: &Fp2{a: fu2.c0.c0.a, b: new(big.Int).Neg(fu2.c0.c0.b).Mod(new(big.Int).Neg(fu2.c0.c0.b), P)},
1173+
c1: &Fp2{a: fu2.c0.c1.a, b: new(big.Int).Neg(fu2.c0.c1.b).Mod(new(big.Int).Neg(fu2.c0.c1.b), P)},
1174+
c2: &Fp2{a: fu2.c0.c2.a, b: new(big.Int).Neg(fu2.c0.c2.b).Mod(new(big.Int).Neg(fu2.c0.c2.b), P)},
1175+
},
1176+
c1: &Fp6{
1177+
c0: &Fp2{a: fu2.c1.c0.a, b: new(big.Int).Neg(fu2.c1.c0.b).Mod(new(big.Int).Neg(fu2.c1.c0.b), P)},
1178+
c1: &Fp2{a: fu2.c1.c1.a, b: new(big.Int).Neg(fu2.c1.c1.b).Mod(new(big.Int).Neg(fu2.c1.c1.b), P)},
1179+
c2: &Fp2{a: fu2.c1.c2.a, b: new(big.Int).Neg(fu2.c1.c2.b).Mod(new(big.Int).Neg(fu2.c1.c2.b), P)},
1180+
},
1181+
}
1182+
1183+
fu3p := &Fp12{
1184+
c0: &Fp6{
1185+
c0: &Fp2{a: fu3.c0.c0.a, b: new(big.Int).Neg(fu3.c0.c0.b).Mod(new(big.Int).Neg(fu3.c0.c0.b), P)},
1186+
c1: &Fp2{a: fu3.c0.c1.a, b: new(big.Int).Neg(fu3.c0.c1.b).Mod(new(big.Int).Neg(fu3.c0.c1.b), P)},
1187+
c2: &Fp2{a: fu3.c0.c2.a, b: new(big.Int).Neg(fu3.c0.c2.b).Mod(new(big.Int).Neg(fu3.c0.c2.b), P)},
1188+
},
1189+
c1: &Fp6{
1190+
c0: &Fp2{a: fu3.c1.c0.a, b: new(big.Int).Neg(fu3.c1.c0.b).Mod(new(big.Int).Neg(fu3.c1.c0.b), P)},
1191+
c1: &Fp2{a: fu3.c1.c1.a, b: new(big.Int).Neg(fu3.c1.c1.b).Mod(new(big.Int).Neg(fu3.c1.c1.b), P)},
1192+
c2: &Fp2{a: fu3.c1.c2.a, b: new(big.Int).Neg(fu3.c1.c2.b).Mod(new(big.Int).Neg(fu3.c1.c2.b), P)},
1193+
},
1194+
}
1195+
1196+
y2 := &Fp12{
1197+
c0: fu2.c0.Copy(),
1198+
c1: fu2.c1.Neg(),
1199+
}
1200+
1201+
// y0 = fp * fp2 * fp3
1202+
y0 := fp.Mul(fp2)
1203+
y0 = y0.Mul(fp3)
1204+
1205+
// Conjugates
1206+
y1 := &Fp12{
1207+
c0: t1.c0.Copy(),
1208+
c1: t1.c1.Neg(),
1209+
}
1210+
1211+
y5 := &Fp12{
1212+
c0: fu2.c0.Copy(),
1213+
c1: fu2.c1.Neg(),
1214+
}
1215+
1216+
// Conjugate y3
1217+
y3 = &Fp12{
1218+
c0: y3.c0.Copy(),
1219+
c1: y3.c1.Neg(),
1220+
}
1221+
1222+
// y4 = fu * fu2p, then conjugate
1223+
y4 := fu.Mul(fu2p)
1224+
y4 = &Fp12{
1225+
c0: y4.c0.Copy(),
1226+
c1: y4.c1.Neg(),
11041227
}
1105-
t0 = t0.Mul(f.Inverse())
11061228

1107-
// Second: f^(p^2 + 1)
1108-
t1 := frobeniusP2(t0)
1109-
f = t1.Mul(t0)
1229+
// y6 = fu3 * fu3p, then conjugate
1230+
y6 := fu3.Mul(fu3p)
1231+
y6 = &Fp12{
1232+
c0: y6.c0.Copy(),
1233+
c1: y6.c1.Neg(),
1234+
}
11101235

1111-
// Hard part: use addition chains for efficiency
1112-
// This is a simplified version; production code uses optimized addition chains
1113-
exp := new(big.Int).Sub(P, big.NewInt(1))
1114-
exp.Mul(exp, exp)
1115-
exp.Mul(exp, exp)
1116-
exp.Mul(exp, exp)
1117-
exp.Mul(exp, exp)
1118-
exp.Mul(exp, exp)
1119-
exp.Mul(exp, exp)
1120-
exp.Sub(exp, big.NewInt(1))
1121-
exp.Div(exp, Order)
1236+
// Final combination - following Cloudflare's exact sequence
1237+
t0 := y6.Square()
1238+
t0 = t0.Mul(y4)
1239+
t0 = t0.Mul(y5)
1240+
t1 = y3.Mul(y5)
1241+
t1 = t1.Mul(t0)
1242+
t0 = t0.Mul(y2)
1243+
t1 = t1.Square()
1244+
t1 = t1.Mul(t0)
1245+
t1 = t1.Square()
1246+
t0 = t1.Mul(y1)
1247+
t1 = t1.Mul(y0)
1248+
t0 = t0.Square()
1249+
t0 = t0.Mul(t1)
11221250

1123-
return f.Exp(exp)
1251+
return t0
1252+
}
1253+
1254+
// cyclotomicSquare computes squaring in the cyclotomic subgroup
1255+
// This is more efficient than general Fp12 squaring
1256+
func cyclotomicSquare(f *Fp12) *Fp12 {
1257+
// For elements in the cyclotomic subgroup, we can use a faster squaring
1258+
// For now, use regular squaring (can be optimized later)
1259+
return f.Square()
1260+
}
1261+
1262+
// cyclotomicExp computes exponentiation in the cyclotomic subgroup
1263+
func cyclotomicExp(f *Fp12, exp *big.Int) *Fp12 {
1264+
result := &Fp12{
1265+
c0: &Fp6{
1266+
c0: &Fp2{a: big.NewInt(1), b: big.NewInt(0)},
1267+
c1: &Fp2{a: big.NewInt(0), b: big.NewInt(0)},
1268+
c2: &Fp2{a: big.NewInt(0), b: big.NewInt(0)},
1269+
},
1270+
c1: &Fp6{
1271+
c0: &Fp2{a: big.NewInt(0), b: big.NewInt(0)},
1272+
c1: &Fp2{a: big.NewInt(0), b: big.NewInt(0)},
1273+
c2: &Fp2{a: big.NewInt(0), b: big.NewInt(0)},
1274+
},
1275+
}
1276+
1277+
base := f.Copy()
1278+
for i := 0; i < exp.BitLen(); i++ {
1279+
if exp.Bit(i) == 1 {
1280+
result = result.Mul(base)
1281+
}
1282+
base = cyclotomicSquare(base)
1283+
}
1284+
1285+
return result
1286+
}
1287+
1288+
// frobeniusP computes the Frobenius endomorphism (raise to power p)
1289+
func frobeniusP(f *Fp12) *Fp12 {
1290+
// For Fp2 elements (a + bu), Frobenius gives (a - bu)
1291+
c0 := &Fp6{
1292+
c0: &Fp2{a: f.c0.c0.a, b: new(big.Int).Neg(f.c0.c0.b).Mod(new(big.Int).Neg(f.c0.c0.b), P)},
1293+
c1: &Fp2{a: f.c0.c1.a, b: new(big.Int).Neg(f.c0.c1.b).Mod(new(big.Int).Neg(f.c0.c1.b), P)},
1294+
c2: &Fp2{a: f.c0.c2.a, b: new(big.Int).Neg(f.c0.c2.b).Mod(new(big.Int).Neg(f.c0.c2.b), P)},
1295+
}
1296+
1297+
c1 := &Fp6{
1298+
c0: &Fp2{a: f.c1.c0.a, b: new(big.Int).Neg(f.c1.c0.b).Mod(new(big.Int).Neg(f.c1.c0.b), P)},
1299+
c1: &Fp2{a: f.c1.c1.a, b: new(big.Int).Neg(f.c1.c1.b).Mod(new(big.Int).Neg(f.c1.c1.b), P)},
1300+
c2: &Fp2{a: f.c1.c2.a, b: new(big.Int).Neg(f.c1.c2.b).Mod(new(big.Int).Neg(f.c1.c2.b), P)},
1301+
}
1302+
1303+
// Multiply by Frobenius coefficients
1304+
c0.c1 = c0.c1.Mul(xiToPMinus1Over6)
1305+
c0.c2 = c0.c2.Mul(xiToPMinus1Over3)
1306+
c1.c0 = c1.c0.Mul(xiToPMinus1Over6)
1307+
c1.c1 = c1.c1.Mul(xiToPMinus1Over3)
1308+
c1.c2 = c1.c2.Mul(xiToPMinus1Over6)
1309+
1310+
return &Fp12{c0: c0, c1: c1}
11241311
}
11251312

11261313
// frobeniusP2 computes the Frobenius endomorphism raised to power 2
11271314
func frobeniusP2(f *Fp12) *Fp12 {
1128-
// Simplified: conjugate in Fp12
1129-
return &Fp12{
1130-
c0: f.c0.Copy(),
1131-
c1: f.c1.Neg(),
1315+
// For Fp2, Frobenius^2 is identity on the base elements
1316+
// But we still need to multiply by appropriate powers
1317+
c0 := &Fp6{
1318+
c0: f.c0.c0.Copy(),
1319+
c1: f.c0.c1.MulScalar(fromHex("1284b71c2865a7dfe8b99fdd76e68b605c521e08292f2176d60b35dadcc9e470")),
1320+
c2: f.c0.c2.MulScalar(fromHex("246996f3b4fae7e6a6327cfe12150b8e747992778eeec7e5ca5cf05f80f362ac")),
11321321
}
1322+
1323+
c1 := &Fp6{
1324+
c0: f.c1.c0.MulScalar(fromHex("1284b71c2865a7dfe8b99fdd76e68b605c521e08292f2176d60b35dadcc9e470")),
1325+
c1: f.c1.c1.MulScalar(fromHex("246996f3b4fae7e6a6327cfe12150b8e747992778eeec7e5ca5cf05f80f362ac")),
1326+
c2: f.c1.c2.MulScalar(fromHex("1284b71c2865a7dfe8b99fdd76e68b605c521e08292f2176d60b35dadcc9e470")),
1327+
}
1328+
1329+
return &Fp12{c0: c0, c1: c1}
1330+
}
1331+
1332+
// frobeniusP3 computes the Frobenius endomorphism raised to power 3
1333+
func frobeniusP3(f *Fp12) *Fp12 {
1334+
c0 := &Fp6{
1335+
c0: &Fp2{a: f.c0.c0.a, b: new(big.Int).Neg(f.c0.c0.b).Mod(new(big.Int).Neg(f.c0.c0.b), P)},
1336+
c1: &Fp2{a: f.c0.c1.a, b: new(big.Int).Neg(f.c0.c1.b).Mod(new(big.Int).Neg(f.c0.c1.b), P)},
1337+
c2: &Fp2{a: f.c0.c2.a, b: new(big.Int).Neg(f.c0.c2.b).Mod(new(big.Int).Neg(f.c0.c2.b), P)},
1338+
}
1339+
1340+
c1 := &Fp6{
1341+
c0: &Fp2{a: f.c1.c0.a, b: new(big.Int).Neg(f.c1.c0.b).Mod(new(big.Int).Neg(f.c1.c0.b), P)},
1342+
c1: &Fp2{a: f.c1.c1.a, b: new(big.Int).Neg(f.c1.c1.b).Mod(new(big.Int).Neg(f.c1.c1.b), P)},
1343+
c2: &Fp2{a: f.c1.c2.a, b: new(big.Int).Neg(f.c1.c2.b).Mod(new(big.Int).Neg(f.c1.c2.b), P)},
1344+
}
1345+
1346+
// Multiply by Frobenius^3 coefficients
1347+
c0.c1 = c0.c1.Mul(&Fp2{
1348+
a: fromHex("5b54f5e64eea80180f3c0b75a181e84d33365f7be94ec72848a1f55921ea762"),
1349+
b: big.NewInt(0),
1350+
})
1351+
c0.c2 = c0.c2.Mul(&Fp2{
1352+
a: fromHex("5b54f5e64eea80180f3c0b75a181e84d33365f7be94ec72848a1f55921ea762"),
1353+
b: big.NewInt(0),
1354+
})
1355+
1356+
return &Fp12{c0: c0, c1: c1}
11331357
}
11341358

11351359
// Pair computes the optimal ate pairing e(p, q)

0 commit comments

Comments
 (0)