Bitcoin Forum
January 29, 2026, 02:43:29 PM *
News: Latest Bitcoin Core release: 30.2 [Torrent]
 
   Home   Help Search Login Register More  
Pages: « 1 2 [3]  All
  Print  
Author Topic: Legendre Symbol Oracle Breaks ECC  (Read 398 times)
rdenkye (OP)
Jr. Member
*
Offline Offline

Activity: 40
Merit: 1


View Profile
January 28, 2026, 05:27:44 PM
 #41

Although it's not exactly like this here, even without knowing the scalar, it's possible to find 10 other points related to a single point.

All the other points on the curve are related to a single point as well. Just keep adding.

The whole idea of an endomorphism is to not do any EC operations at all. What I see in your code, is that you are actually doing EC math to get to other points, and moreover, solving DLP instances.

If you analyze the real secp256k1, you'll find that the number of points divides by 2, 3, and then, IIRC, 147, 631, etc. However, the only real endomorphism only has 2*3=6 points, the rest (like 147 points per orbit) generate points without any common X or Y, which means: they cannot be used as an attack vector, just for iterating the orbiting points (and this requires EC scalar multiplication).



The new 10 points have a unique relationship.
For example, 4 points have no duplicates.
Therefore, it is not possible to accept your criticism of "Just keep adding."

Everything I mentioned in this section are summary parts of a topic that I will eventually connect and explain as a whole.
rdenkye (OP)
Jr. Member
*
Offline Offline

Activity: 40
Merit: 1


View Profile
January 28, 2026, 08:16:52 PM
Last edit: January 28, 2026, 08:34:12 PM by rdenkye
 #42

I will also add the scalar values that cause the anomaly on the secp256k1 field prime p, i.e., the cases where p equals n. That will probably be a first as well.



Code:
from math import isqrt

def extract_small_square_factor(n: int, bound: int = 20000):
    sf = 1
    m = n
    sieve = [True] * (bound + 1)
    primes = []
    for i in range(2, bound + 1):
        if sieve[i]:
            primes.append(i)
            ii = i * i
            if ii <= bound:
                step = i
                sieve[ii:bound + 1:step] = [False] * (((bound - ii) // step) + 1)

    for q in primes:
        q2 = q * q
        if q2 > m:
            break
        while m % q2 == 0:
            m //= q2
            sf *= q

    return sf, m  

def report_anomalous_difficulty(p: int, bound: int = 20000):
    Da = 4 * p - 1  # = -(1 - 4p)
    sf, rem = extract_small_square_factor(Da, bound=bound)
    print(f"p = {p}")
    print(f"Delta = 1 - 4p = -({Da})")
    print(f"small square factor: {sf * sf}  (sf={sf})")
    print(f"remaining cofactor bit length (Da / sf^2): {rem.bit_length()}")
    sp = isqrt(p)
    print(f"expected count of order==p cases ~ sqrt(p)/4 ≈ {sp // 4}")
    print(f"hit probability for random k ~ 1/(4*sqrt(p)) ≈ 1/{4 * sp}")
    print()

for P in (
              79,
              967,
              1303,
              115792089237316195423570985008687907853269984665640564039457584007908834671663,
          ):    report_anomalous_difficulty(P)


Code:
p = 79
Delta = 1 - 4p = -(315)
small square factor: 9  (sf=3)
remaining cofactor bit length (Da / sf^2): 6
expected count of order==p cases ~ sqrt(p)/4 ≈ 2
hit probability for random k ~ 1/(4*sqrt(p)) ≈ 1/32

p = 967
Delta = 1 - 4p = -(3867)
small square factor: 1  (sf=1)
remaining cofactor bit length (Da / sf^2): 12
expected count of order==p cases ~ sqrt(p)/4 ≈ 7
hit probability for random k ~ 1/(4*sqrt(p)) ≈ 1/124

p = 1303
Delta = 1 - 4p = -(5211)
small square factor: 9  (sf=3)
remaining cofactor bit length (Da / sf^2): 10
expected count of order==p cases ~ sqrt(p)/4 ≈ 9
hit probability for random k ~ 1/(4*sqrt(p)) ≈ 1/144

p = 115792089237316195423570985008687907853269984665640564039457584007908834671663
Delta = 1 - 4p = -(463168356949264781694283940034751631413079938662562256157830336031635338686651)
small square factor: 9  (sf=3)
remaining cofactor bit length (Da / sf^2): 255
expected count of order==p cases ~ sqrt(p)/4 ≈ 85070591730234615865843651857942052863
hit probability for random k ~ 1/(4*sqrt(p)) ≈ 1/1361129467683753853853498429727072845820


This script gives a quick, practical summary of how rare and difficult the “order equals p” (anomalous) target is for each prime p you test.
In the output,The “square factor” line shows whether there is any small square factor hiding inside the relevant value tied to p (some primes have a small square factor, some do not).
The “bit length” line indicates how large the remaining cofactor is; the larger it is, the more complex the structure typically is.
The “expected count” line is a rough estimate of how many such special cases you might expect for that p.
The “hit probability for random k” line shows how unlikely it is to hit this special case by choosing k at random.
Overall; for small p it can be feasible to search, but for huge p (like the secp field prime) random searching becomes practically hopeless.

I have never come across a curve with order 115792089237316195423570985008687907853269984665640564039457584007908834671663!
I think it has never been shared.
Curves of type p=n are vulnerable to various attack methods, both known and not widely known, such as Smart's Attack (p-adic math). These curves are not used because they are known to be weak. However, this will continue until a bridging method is discovered between the weak curve and the original curve.



Code:
import random
from math import isqrt

def legendre(a, p):
    a %= p
    if a == 0:
        return 0
    return 1 if pow(a, (p - 1) // 2, p) == 1 else -1

def tonelli_shanks(n, p):
    n %= p
    if n == 0:
        return 0
    if pow(n, (p - 1) // 2, p) != 1:
        return None
    if p % 4 == 3:
        return pow(n, (p + 1) // 4, p)

    q = p - 1
    s = 0
    while q % 2 == 0:
        s += 1
        q //= 2

    z = 2
    while pow(z, (p - 1) // 2, p) != p - 1:
        z += 1

    m = s
    c = pow(z, q, p)
    t = pow(n, q, p)
    r = pow(n, (q + 1) // 2, p)

    while t != 1:
        i = 1
        t2i = (t * t) % p
        while i < m and t2i != 1:
            t2i = (t2i * t2i) % p
            i += 1
        b = pow(c, 1 << (m - i - 1), p)
        r = (r * b) % p
        t = (t * b * b) % p
        c = (b * b) % p
        m = i
    return r

def singular_ks(p, a=1):
    # 4a^3 + 27k^2 ≡ 0 (mod p)  => k^2 ≡ -4a^3 * inv(27)
    inv27 = pow(27, -1, p)
    rhs = (-4 * (a**3) * inv27) % p
    r = tonelli_shanks(rhs, p)
    if r is None:
        return []  
    return sorted({r % p, (-r) % p})

def anomalous_ks_small_p(p, a=1):
    base_count = [0] * p
    for x in range(p):
        v = (x * x % p * x + a * x) % p
        base_count[v] += 1

    chi = [0] * p
    for r in range(p):
        chi[r] = legendre(r, p)

    hits = []
    singular = []

    for k in range(p):
        if (4 * (a ** 3) + 27 * (k * k)) % p == 0:
            singular.append(k)
            continue
        s = 0
        for v, c in enumerate(base_count):
            if c:
                s += c * chi[(v + k) % p]
        if s == -1:  # order == p
            hits.append(k)

    return hits, singular

def anomalous_ks_for_y2_eq_x3_plus_ax_plus_k(p, a=1, small_p_limit=2_000_000):
    if p <= small_p_limit:
        return anomalous_ks_small_p(p, a=a)
    sing = singular_ks(p, a=a)
    return [], sing

Ps = (79, 967, 1303, 115792089237316195423570985008687907853269984665640564039457584007908834671663)
for p in Ps:
    hits, sing = anomalous_ks_for_y2_eq_x3_plus_ax_plus_k(p, a=1)
    print(f"p={p}\n  order==p k'ları: {hits}\n  singular: {sing}\n")



Code:
p=79
  order==p k'ları: [14, 43, 53]
  singular: [28, 51]

p=967
  order==p k'ları: [451, 549, 587, 703, 705, 888]
  singular: [259, 708]

p=1303
  order==p k'ları: [35, 47, 142, 154, 240, 395, 503, 579, 754, 764, 841, 941, 1095, 1124, 1175, 1202, 1215, 1223]
  singular: [332, 971]

p=115792089237316195423570985008687907853269984665640564039457584007908834671663
  order==p k'ları: []
  singular: [11842912595111486228085625214058158124919313799234703649739607102117954628595, 103949176642204709195485359794629749728350670866405860389717976905790880043068]



This code cannot be used to determine the b coefficient of large curves. I have developed a different and faster technique for this purpose; I am keeping the method and the b coefficients I found confidential at this stage, but I will share these values ​​at the end of the process.
jovica888
Member
**
Offline Offline

Activity: 63
Merit: 17


View Profile
January 28, 2026, 11:33:21 PM
 #43

p=97
  order==p k'ları: [1, 3, 12, 85, 94, 96]
  singular: [5, 92]

I checked here
https://graui.de/code/elliptic2/

and I can not make 96 for order....
4 11 22 44 - that is what I get

Oh I see now you ment
y^2 = x^3 + 1x + 92 mod 97
y^2 = x^3 + 1x + 5 mod 97

Yeah they are singulars
Also this
y^2 = x^3 + 66x + 31 mod 97
And I think this is better because this curve has infinity point and 96 other points
rdenkye (OP)
Jr. Member
*
Offline Offline

Activity: 40
Merit: 1


View Profile
Today at 08:47:24 AM
 #44

p=97
  order==p k'ları: [1, 3, 12, 85, 94, 96]
  singular: [5, 92]

I checked here
https://graui.de/code/elliptic2/

and I can not make 96 for order....
4 11 22 44 - that is what I get

Oh I see now you ment
y^2 = x^3 + 1x + 92 mod 97
y^2 = x^3 + 1x + 5 mod 97

Yeah they are singulars
Also this
y^2 = x^3 + 66x + 31 mod 97
And I think this is better because this curve has infinity point and 96 other points

p=97
  order==p k'ları: [1, 3, 12, 85, 94, 96]
  singular: [5, 92]

Code:
from sage.all import *
import multiprocessing as mp
import traceback

p = 97
a = 1

def _worker(k, q):
    try:
        b = k

        if (4*(a**3) + 27*(b**2)) % p == 0:
            q.put(("SINGULAR(Δ=0)", k, None))
            return

        E = EllipticCurve(GF(p), [a, b])
        order = int(E.order())  

        q.put(("OK", k, order))
    except Exception:
        q.put(("HATA", k, traceback.format_exc()))


ctx = mp.get_context("fork") if "fork" in mp.get_all_start_methods() else mp.get_context()

TIMEOUT = 28  

for k in range(1, 97 ):
    q = ctx.Queue()
    proc = ctx.Process(target=_worker, args=(k, q))
    proc.start()
    proc.join(TIMEOUT)

    if proc.is_alive():
        proc.terminate()
        proc.join()
        print(f"{k}\tTIMEOUT({TIMEOUT}s) -> atlandı")
        continue

    if q.empty():
        print(f"{k}\tHATA: sonuç dönmedi (queue boş)")
        continue

    status, kk, payload = q.get()

    if status == "OK":
        order = payload
        found=""
        if order == p:
             found ="✅ order == p "        
        print(f"{kk}\t{order} {found}")

    elif status.startswith("SINGULAR"):
        print(f"{kk}\t{status} -> atlandı")
    else:  # HATA
        print(f"{kk}\tHATA:\n{payload}")



p=97 a=1 b={List Value}

1   97 ✅ order == p
2   104
3   97 ✅ order == p
4   89
5   SINGULAR(Δ=0) -> atlandı
6   116
7   81
8   116
9   90
10   96
11   105
12   97 ✅ order == p
13   93
14   108
15   89
16   96
17   100
18   103
19   96
20   84
21   94
22   98
23   82
24   91
25   87
26   108
27   115
28   92
29   100
30   100
31   106
32   112
33   108
34   105
35   100
36   90
37   110
38   96
39   100
40   86
41   114
42   106
43   94
44   114
45   84
46   85
47   85
48   95
49   95
50   85
51   85
52   84
53   114
54   94
55   106
56   114
57   86
58   100
59   96
60   110
61   90
62   100
63   105
64   108
65   112
66   106
67   100
68   100
69   92
70   115
71   108
72   87
73   91
74   82
75   98
76   94
77   84
78   96
79   103
80   100
81   96
82   89
83   108
84   93
85   97 ✅ order == p
86   105
87   96
88   90
89   116
90   81
91   116
92   SINGULAR(Δ=0) -> atlandı
93   89
94   97 ✅ order == p
95   104
96   97 ✅ order == p
ActiveC
Copper Member
Newbie
*
Offline Offline

Activity: 4
Merit: 2


View Profile
Today at 02:17:10 PM
 #45

Again, drop this dead, all this gibberish won't work on the secp256k1.

Your explanations are misleading, you are confusing people for nothing...
Pages: « 1 2 [3]  All
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!