Skip to content

Commit 1948dba

Browse files
Add the method to compute Jacobi symbol
1 parent add89e3 commit 1948dba

File tree

1 file changed

+134
-39
lines changed
  • Src/Autarkysoft.Bitcoin/Cryptography/EllipticCurve

1 file changed

+134
-39
lines changed

Src/Autarkysoft.Bitcoin/Cryptography/EllipticCurve/ModInv32.cs

+134-39
Original file line numberDiff line numberDiff line change
@@ -246,15 +246,15 @@ private static int DivSteps30(int zeta, uint f0, uint g0, out ModInv32Trans2x2 t
246246

247247

248248
//https://github.com/bitcoin-core/secp256k1/blob/1a81df826e2a24a1656fc28fc3076b62562216d9/src/util.h#L305
249-
static readonly byte[] DeBruijn = new byte[32]
249+
private static readonly byte[] DeBruijn = new byte[32]
250250
{
251251
0x00, 0x01, 0x02, 0x18, 0x03, 0x13, 0x06, 0x19, 0x16, 0x04, 0x14, 0x0A,
252252
0x10, 0x07, 0x0C, 0x1A, 0x1F, 0x17, 0x12, 0x05, 0x15, 0x09, 0x0F, 0x0B,
253253
0x1E, 0x11, 0x08, 0x0E, 0x1D, 0x0D, 0x1C, 0x1B
254254
};
255255
// Determine the number of trailing zero bits in a (non-zero) 32-bit x.
256256
[MethodImpl(MethodImplOptions.AggressiveInlining)]
257-
static int Ctz32Var(uint x)
257+
private static int Ctz32Var(uint x)
258258
{
259259
// TODO: dotnet 3.0+ have: BitOperations.TrailingZeroCount()
260260
Debug.Assert(x != 0);
@@ -272,7 +272,7 @@ static int Ctz32Var(uint x)
272272
// Return: final eta
273273
//
274274
// Implements the divsteps_n_matrix_var function from the explanation.
275-
static int DivSteps30Var(int eta, uint f0, uint g0, out ModInv32Trans2x2 t)
275+
private static int DivSteps30Var(int eta, uint f0, uint g0, out ModInv32Trans2x2 t)
276276
{
277277
// Transformation matrix; see comments in DivSteps30().
278278
uint u = 1, v = 0, q = 0, r = 1;
@@ -343,7 +343,7 @@ static int DivSteps30Var(int eta, uint f0, uint g0, out ModInv32Trans2x2 t)
343343
/// Jacobi tracking requires knowing (f mod 8) rather than just (f mod 2).
344344
/// </summary>
345345
/// <remarks>
346-
/// (*jacp & 1) is bitflipped if and only if the Jacobi symbol of (f | g) changes sign
346+
/// (*jacp &#38; 1) is bitflipped if and only if the Jacobi symbol of (f | g) changes sign
347347
/// by applying the returned transformation matrix to it. The other bits of *jacp may
348348
/// change, but are meaningless.
349349
/// </remarks>
@@ -353,9 +353,9 @@ static int DivSteps30Var(int eta, uint f0, uint g0, out ModInv32Trans2x2 t)
353353
/// <param name="t">transition matrix</param>
354354
/// <param name="jacp"></param>
355355
/// <returns>final eta</returns>
356-
static int PosDivSteps30Var(int eta, uint f0, uint g0, ModInv32Trans2x2 t, ref int jacp)
356+
private static int PosDivSteps30Var(int eta, uint f0, uint g0, out ModInv32Trans2x2 t, ref int jacp)
357357
{
358-
/* Transformation matrix. */
358+
// Transformation matrix.
359359
uint u = 1, v = 0, q = 0, r = 1;
360360
uint f = f0, g = g0, m;
361361
ushort w;
@@ -364,59 +364,62 @@ static int PosDivSteps30Var(int eta, uint f0, uint g0, ModInv32Trans2x2 t, ref i
364364

365365
while (true)
366366
{
367-
/* Use a sentinel bit to count zeros only up to i. */
367+
// Use a sentinel bit to count zeros only up to i.
368368
zeros = Ctz32Var(g | (uint.MaxValue << i));
369-
/* Perform zeros divsteps at once; they all just divide g by two. */
369+
// Perform zeros divsteps at once; they all just divide g by two.
370370
g >>= zeros;
371371
u <<= zeros;
372372
v <<= zeros;
373373
eta -= zeros;
374374
i -= zeros;
375-
/* Update the bottom bit of jac: when dividing g by an odd power of 2,
376-
* if (f mod 8) is 3 or 5, the Jacobi symbol changes sign. */
375+
// Update the bottom bit of jac: when dividing g by an odd power of 2,
376+
// if (f mod 8) is 3 or 5, the Jacobi symbol changes sign.
377377
jac ^= (int)(zeros & ((f >> 1) ^ (f >> 2)));
378-
/* We're done once we've done 30 posdivsteps. */
379-
if (i == 0) break;
378+
// We're done once we've done 30 posdivsteps.
379+
if (i == 0)
380+
{
381+
break;
382+
}
383+
380384
Debug.Assert((f & 1) == 1);
381385
Debug.Assert((g & 1) == 1);
382386
Debug.Assert((u * f0 + v * g0) == f << (30 - i));
383387
Debug.Assert((q * f0 + r * g0) == g << (30 - i));
384-
/* If eta is negative, negate it and replace f,g with g,f. */
388+
// If eta is negative, negate it and replace f,g with g,f.
385389
if (eta < 0)
386390
{
387391
uint tmp;
388392
eta = -eta;
389-
/* Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign
390-
* if both f and g are 3 mod 4. */
393+
// Update bottom bit of jac: when swapping f and g, the Jacobi symbol changes sign
394+
// if both f and g are 3 mod 4.
391395
jac ^= (int)((f & g) >> 1);
392396
tmp = f; f = g; g = tmp;
393397
tmp = u; u = q; q = tmp;
394398
tmp = v; v = r; r = tmp;
395399
}
396-
/* eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more
397-
* than i can be cancelled out (as we'd be done before that point), and no more than eta+1
398-
* can be done as its sign will flip once that happens. */
400+
// eta is now >= 0. In what follows we're going to cancel out the bottom bits of g. No more
401+
// than i can be cancelled out (as we'd be done before that point), and no more than eta+1
402+
// can be done as its sign will flip once that happens.
399403
limit = (eta + 1) > i ? i : (eta + 1);
400-
/* m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits). */
404+
// m is a mask for the bottom min(limit, 8) bits (our table only supports 8 bits).
401405
Debug.Assert(limit > 0 && limit <= 30);
402406
m = (uint.MaxValue >> (32 - limit)) & 255U;
403-
/* Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits. */
407+
// Find what multiple of f must be added to g to cancel its bottom min(limit, 8) bits.
404408
w = (ushort)((g * Inv256[(f >> 1) & 127]) & m);
405-
/* Do so. */
409+
// Do so.
406410
g += f * w;
407411
q += u * w;
408412
r += v * w;
409413
Debug.Assert((g & m) == 0);
410414
}
411-
/* Return data in t and return value. */
412-
t.u = (int)u;
413-
t.v = (int)v;
414-
t.q = (int)q;
415-
t.r = (int)r;
416-
/* The determinant of t must be a power of two. This guarantees that multiplication with t
417-
* does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
418-
* will be divided out again). As each divstep's individual matrix has determinant 2 or -2,
419-
* the aggregate of 30 of them will have determinant 2^30 or -2^30. */
415+
416+
// Return data in t and return value.
417+
t = new ModInv32Trans2x2(u, v, q, r);
418+
419+
// The determinant of t must be a power of two. This guarantees that multiplication with t
420+
// does not change the gcd of f and g, apart from adding a power-of-2 factor to it (which
421+
// will be divided out again). As each divstep's individual matrix has determinant 2 or -2,
422+
// the aggregate of 30 of them will have determinant 2^30 or -2^30.
420423
Debug.Assert((long)t.u * t.r - (long)t.v * t.q == ((long)1) << 30 ||
421424
(long)t.u * t.r - (long)t.v * t.q == -(((long)1) << 30));
422425
jacp = jac;
@@ -430,7 +433,7 @@ static int PosDivSteps30Var(int eta, uint f0, uint g0, ModInv32Trans2x2 t, ref i
430433
// in range (-2^30,2^30).
431434
//
432435
// This implements the update_de function from the explanation.
433-
internal static void UpdateDE30(ref ModInv32Signed30 d, ref ModInv32Signed30 e, ModInv32Trans2x2 t, in ModInv32ModInfo modinfo)
436+
private static void UpdateDE30(ref ModInv32Signed30 d, ref ModInv32Signed30 e, ModInv32Trans2x2 t, in ModInv32ModInfo modinfo)
434437
{
435438
const int M30 = (int)(uint.MaxValue >> 2);
436439
int di, ei, md, me, sd, se;
@@ -496,7 +499,7 @@ internal static void UpdateDE30(ref ModInv32Signed30 d, ref ModInv32Signed30 e,
496499
// Compute (t/2^30) * [f, g], where t is a transition matrix for 30 divsteps.
497500
//
498501
// This implements the update_fg function from the explanation.
499-
static void UpdateFG30(ref ModInv32Signed30 f, ref ModInv32Signed30 g, ModInv32Trans2x2 t)
502+
private static void UpdateFG30(ref ModInv32Signed30 f, ref ModInv32Signed30 g, ModInv32Trans2x2 t)
500503
{
501504
const int M30 = (int)(uint.MaxValue >> 2);
502505
int fi, gi;
@@ -535,7 +538,7 @@ static void UpdateFG30(ref ModInv32Signed30 f, ref ModInv32Signed30 g, ModInv32T
535538
// Version that operates on a variable number of limbs in f and g.
536539
//
537540
// This implements the update_fg function from the explanation in modinv64_impl.h.
538-
static void UpdateFG30Var(int len, ref ModInv32Signed30 f, ref ModInv32Signed30 g, ModInv32Trans2x2 t)
541+
private static void UpdateFG30Var(int len, ref ModInv32Signed30 f, ref ModInv32Signed30 g, ModInv32Trans2x2 t)
539542
{
540543
const int M30 = (int)(uint.MaxValue >> 2);
541544
int fi, gi;
@@ -665,10 +668,10 @@ public static void ComputeVar(ref ModInv32Signed30 x, ModInv32ModInfo modinfo)
665668
UpdateDE30(ref d, ref e, t, modinfo);
666669
// Update f,g using that transition matrix.
667670
# if DEBUG
668-
Debug.Assert(MulCmp30(f, len, modinfo.modulus, -1) > 0); /* f > -modulus */
669-
Debug.Assert(MulCmp30(f, len, modinfo.modulus, 1) <= 0); /* f <= modulus */
670-
Debug.Assert(MulCmp30(g, len, modinfo.modulus, -1) > 0); /* g > -modulus */
671-
Debug.Assert(MulCmp30(g, len, modinfo.modulus, 1) < 0); /* g < modulus */
671+
Debug.Assert(MulCmp30(f, len, modinfo.modulus, -1) > 0); // f > -modulus
672+
Debug.Assert(MulCmp30(f, len, modinfo.modulus, 1) <= 0); // f <= modulus
673+
Debug.Assert(MulCmp30(g, len, modinfo.modulus, -1) > 0); // g > -modulus
674+
Debug.Assert(MulCmp30(g, len, modinfo.modulus, 1) < 0); // g < modulus
672675
#endif
673676
UpdateFG30Var(len, ref f, ref g, t);
674677

@@ -684,7 +687,10 @@ public static void ComputeVar(ref ModInv32Signed30 x, ModInv32ModInfo modinfo)
684687
cond |= gv[j];
685688
}
686689
// If so, we're done.
687-
if (cond == 0) break;
690+
if (cond == 0)
691+
{
692+
break;
693+
}
688694
}
689695

690696
// Determine if len>1 and limb (len-1) of both f and g is 0 or -1.
@@ -698,7 +704,7 @@ public static void ComputeVar(ref ModInv32Signed30 x, ModInv32ModInfo modinfo)
698704
{
699705
fv[len - 2] |= fn << 30;
700706
gv[len - 2] |= gn << 30;
701-
--len;
707+
len--;
702708
}
703709

704710
f = new ModInv32Signed30(fv);
@@ -730,5 +736,94 @@ public static void ComputeVar(ref ModInv32Signed30 x, ModInv32ModInfo modinfo)
730736
int[] tempArr = f.GetArray();
731737
x = Normalize30(d, tempArr[len - 1], modinfo);
732738
}
739+
740+
741+
/// <summary>
742+
/// Compute the Jacobi symbol for (x | modinfo->modulus). x must be coprime with modulus (and thus
743+
/// cannot be 0, as modulus >= 3). All limbs of x must be non-negative. Returns 0 if the result
744+
/// cannot be computed.
745+
/// </summary>
746+
/// <param name="x"></param>
747+
/// <param name="modinfo"></param>
748+
/// <returns></returns>
749+
public static int Jacobi32MaybeVar(in ModInv32Signed30 x, in ModInv32ModInfo modinfo)
750+
{
751+
// Start with f=modulus, g=x, eta=-1.
752+
ModInv32Signed30 f = modinfo.modulus;
753+
ModInv32Signed30 g = x;
754+
int len = 9;
755+
// eta = -delta; delta is initially 1
756+
int eta = -1;
757+
int cond, fn, gn;
758+
int jac = 0;
759+
760+
// The input limbs must all be non-negative.
761+
Debug.Assert(g.v0 >= 0 && g.v1 >= 0 && g.v0 >= 0 && g.v3 >= 0 &&
762+
g.v4 >= 0 && g.v5 >= 0 && g.v6 >= 0 && g.v7 >= 0 && g.v8 >= 0);
763+
764+
// If x > 0, then if the loop below converges, it converges to f=g=gcd(x,modulus). Since we
765+
// require that gcd(x,modulus)=1 and modulus>=3, x cannot be 0. Thus, we must reach f=1 (or
766+
// time out).
767+
Debug.Assert((g.v0 | g.v1 | g.v2 | g.v3 | g.v4 | g.v5 | g.v6 | g.v7 | g.v8) != 0);
768+
769+
const int JACOBI32_ITERATIONS =
770+
#if DEBUG
771+
25;
772+
#else
773+
50;
774+
#endif
775+
for (int count = 0; count < JACOBI32_ITERATIONS; count++)
776+
{
777+
// Compute transition matrix and new eta after 30 posdivsteps.
778+
eta = PosDivSteps30Var(eta, (uint)f.v0 | ((uint)f.v1 << 30), (uint)g.v0 | ((uint)g.v1 << 30), out ModInv32Trans2x2 t, ref jac);
779+
#if DEBUG
780+
// Update f,g using that transition matrix.
781+
Debug.Assert(MulCmp30(f, len, modinfo.modulus, 0) > 0); // f > 0
782+
Debug.Assert(MulCmp30(f, len, modinfo.modulus, 1) <= 0); // f <= modulus
783+
Debug.Assert(MulCmp30(g, len, modinfo.modulus, 0) > 0); // g > 0
784+
Debug.Assert(MulCmp30(g, len, modinfo.modulus, 1) < 0); // g < modulus
785+
#endif
786+
UpdateFG30Var(len, ref f, ref g, t);
787+
788+
// If the bottom limb of f is 1, there is a chance that f=1.
789+
int[] fv = f.GetArray();
790+
int[] gv = g.GetArray();
791+
if (f.v0 == 1)
792+
{
793+
cond = 0;
794+
// Check if the other limbs are also 0.
795+
for (int j = 1; j < len; j++)
796+
{
797+
cond |= fv[j];
798+
}
799+
// If so, we're done. If f=1, the Jacobi symbol (g | f)=1.
800+
if (cond == 0)
801+
{
802+
return 1 - 2 * (jac & 1);
803+
}
804+
}
805+
806+
// Determine if len>1 and limb (len-1) of both f and g is 0.
807+
fn = fv[len - 1];
808+
gn = gv[len - 1];
809+
cond = (len - 2) >> 31;
810+
cond |= fn;
811+
cond |= gn;
812+
// If so, reduce length.
813+
if (cond == 0)
814+
{
815+
len--;
816+
}
817+
#if DEBUG
818+
Debug.Assert(MulCmp30(f, len, modinfo.modulus, 0) > 0); // f > 0
819+
Debug.Assert(MulCmp30(f, len, modinfo.modulus, 1) <= 0); // f <= modulus
820+
Debug.Assert(MulCmp30(g, len, modinfo.modulus, 0) > 0); // g > 0
821+
Debug.Assert(MulCmp30(g, len, modinfo.modulus, 1) < 0); // g < modulus
822+
#endif
823+
}
824+
825+
// The loop failed to converge to f=g after 1500 iterations. Return 0, indicating unknown result.
826+
return 0;
827+
}
733828
}
734829
}

0 commit comments

Comments
 (0)