this repo has no description
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}