this repo has no description
at main 61 lines 2.3 kB view raw
1const std = @import("std"); 2const Secp256k1 = std.crypto.ecc.Secp256k1; 3const Fe = @import("field.zig").Fe; 4const jacobian_mod = @import("jacobian.zig"); 5const AffinePoint = jacobian_mod.AffinePoint; 6const JacobianPoint = jacobian_mod.JacobianPoint; 7 8/// Cube root of unity in GF(p): beta^3 = 1 mod p. 9/// phi(x, y) = (beta * x, y) is equivalent to scalar multiplication by lambda, 10/// but costs only 1 field multiply instead of ~65 doublings. 11pub const beta = Fe.fromInt( 12 55594575648329892869085402983802832744385952214688224221778511981742606582254, 13); 14 15/// Apply the endomorphism to an affine point: (beta * x, y). 16pub fn phiAffine(p: AffinePoint) AffinePoint { 17 return .{ .x = p.x.mul(beta), .y = p.y }; 18} 19 20/// Apply the endomorphism to a Jacobian point: (beta * X, Y, Z). 21/// phi preserves the Z coordinate since it only scales the x-coordinate. 22pub fn phiJacobian(p: JacobianPoint) JacobianPoint { 23 return .{ .x = p.x.mul(beta), .y = p.y, .z = p.z }; 24} 25 26/// Re-export stdlib's GLV scalar decomposition. 27/// Decomposes k into (r1, r2) such that k = r1 + r2*lambda (mod n), 28/// where |r1|, |r2| < sqrt(n) (approximately 128 bits each). 29pub const splitScalar = Secp256k1.Endormorphism.splitScalar; 30pub const SplitScalar = Secp256k1.Endormorphism.SplitScalar; 31 32test "phi(G) matches lambda * G" { 33 const G = Secp256k1.basePoint; 34 const g_affine = G.affineCoordinates(); 35 const g26 = AffinePoint.fromStdlib(g_affine); 36 37 const phi_G = phiAffine(g26); 38 39 // lambda * G computed via scalar multiplication 40 const lambda_s = comptime s: { 41 var buf: [32]u8 = undefined; 42 std.mem.writeInt( 43 u256, 44 &buf, 45 37718080363155996902926221483475020450927657555482586988616620542887997980018, 46 .little, 47 ); 48 break :s buf; 49 }; 50 const lambda_G = try G.mulPublic(lambda_s, .little); 51 const lambda_G_affine = lambda_G.affineCoordinates(); 52 53 // compare via bytes 54 const phi_x = phi_G.x.normalize().toBytes(.big); 55 const lambda_x = Fe.fromStdlib(lambda_G_affine.x).normalize().toBytes(.big); 56 try std.testing.expectEqualSlices(u8, &lambda_x, &phi_x); 57 58 const phi_y = phi_G.y.normalize().toBytes(.big); 59 const lambda_y = Fe.fromStdlib(lambda_G_affine.y).normalize().toBytes(.big); 60 try std.testing.expectEqualSlices(u8, &lambda_y, &phi_y); 61}