
Abstract. This paper presents a detailed cryptanalysis of the Jager–Schwenk–Somorovsky attack (2015), based on the Invalid Curve Attack (ICA) as applied to the TLS-ECDH protocol and cryptographic systems using the elliptic curve secp256k1 (in particular, Bitcoin). The mathematical foundations, exploitation conditions, and protection methods are considered. The main focus is on practical reproduction of the key stages of the attack in the Google Colab environment using open-source Python libraries. The proposed scripts demonstrate the search for invalid curves with a small order divisor, the extraction of the remainders of a secret scalar, and the application of the Chinese Remainder Theorem.
1. Introduction: Understanding the Invalid Curve Attack Vulnerability
The Jager–Schwenk–Somorovsky attack (2015) is a practical implementation of an elliptic curve attack using invalid points (Invalid Curve Attack) against the TLS-ECDH protocol. This vulnerability affects cryptographic libraries that do not check whether a point belongs to a given elliptic curve. Specifically, this affected the Oracle JSSE (Java Secure Socket Extension) and Bouncy Castle libraries .
Historical example:
In 2015, researchers Tibor Jäger, Jörg Schwenk, and Juraj Somorovský demonstrated that the lack of endpoint validation in these libraries allows an attacker to completely extract a TLS server’s long-term private key (ECDH) using just 3,300 queries for Bouncy Castle and approximately 17,000 queries for Oracle JSSE. This led to the complete compromise of secure connections.
2. Mathematical and cryptographic foundations
An elliptic curve over a finite field $F_p$ is usually given by the Weierstrass equation:
In the Elliptic Curve Diffie-Hellman (ECDH) protocol, the server has a secret key $d$ and a public key $Q = dG$, where $G$ is the base point of the curve. When establishing a session, the client sends an ephemeral public key $P$. The server computes the shared secret $S = dP$.
The essence of the attack: The attacker sends a point $P’$, which does not lie on the original curve, but lies on another curve with the same parameters $a$ and $p$, but a different $b’$:
Point addition algorithms use only the $(x, y)$ coordinates and the $a$ parameter. If the server does not verify that the point $P’$ satisfies the equation of the original curve (with the $b$ parameter), it will perform the scalar multiplication operation $S’ = dP’$ on the new curve (Invalid Curve).
The attacker chooses a curve whose order contains a small prime divisor $q_i$. By sending a point $P’$ of order $q_i$, they force the server to compute $S’ = dP’$. The server uses this secret to generate a symmetric key. The attacker tries all possible $q_i$ values and, upon successful decryption of the TLS message (or a MAC error), discovers the value $d \pmod{q_i}$.
By applying the Chinese Remainder Theorem (CRT) to a series of such queries (for various $q_i$), the attacker recovers the entire secret key $d$:
For
3. Connection to Bitcoin cryptography (secp256k1)
Bitcoin uses an elliptic curve secp256k1, defined by the equation $y^2 = x^3 + 7$. Protocols such as ECDSA and Schnorr use a single generator $G$.
While the Bitcoin Transaction Signing Protocol (ECDSA) itself doesn’t directly use ECDH, many cryptocurrency wallets, nodes, and second-layer protocols (e.g., the Lightning Network) use ECDH for key exchange and encrypted communications. If the implementation doesn’t verify that a point belongs to a curve secp256k1 (e.g., $Py^2 == Px^3 + 7 \pmod p$), an attacker can extract a node’s private key by sending malicious key exchange messages.
A real-life example from Bitcoin cryptanalysis:
In Bitcoin blockchain security research, structural anomalies of base points have been analyzed. For example, there are known cases of Antipa/Biehl/Meyer/Müller (2003) attacks on ECDH/ECDSA implementations, where a slipped base point outside the curve reveals a secret scalar. Modern Bitcoin libraries (such as libsecp256k1) always strictly check whether a base point lies on the curve ( G.on_curve) before performing any operations.
4. Examples of attack implementation in various languages
Magma
// Задание уязвимой кривой
p := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f;
a := 0; b := 7; // secp256k1
E := EllipticCurve(GF(p), [a, b]);
// Построение недействительной кривой для атаки
b_prime := 12345;
E_invalid := EllipticCurve(GF(p), [a, b_prime]);
// Поиск точки малого порядка q
Factorization(#E_invalid);
// Дальнейший перебор и сбор вычетов для CRT
SageMath
p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
a = 0; b = 7
E = EllipticCurve(GF(p), [a, b])
# Недействительная кривая с малым делителем порядка
b_invalid = 14
E_invalid = EllipticCurve(GF(p), [a, b_invalid])
order = E_invalid.order()
factors = factor(order)
print("Делители порядка: ", factors)
# Генерация точки малого порядка для отправки уязвимому серверу
Python (using libnum and fastecdsa for simulation)
from fastecdsa.curve import secp256k1
from fastecdsa.point import Point
import libnum
# Сервер не проверяет: (x**3 + 7) % p == (y**2) % p
def vulnerable_scalar_mult(k, P_x, P_y, p):
# Уязвимое умножение Монтгомери, игнорирующее 'b'
pass
# Атакующий отправляет координаты P_invalid
# Сервер вычисляет S = vulnerable_scalar_mult(secret_d, P_invalid_x, P_invalid_y, p)
PARI/GP
p = 115792089237316195423570985008687907853269984665640564039457584007908834671663;
a = 0; b = 7;
E = ellinit([a, b], p); \ Нормальная кривая
b_inv = 11;
E_inv = ellinit([a, b_inv], p); \ Недействительная кривая
N = ellcard(E_inv);
F = factor(N);
print(F); \ Анализ для применения Китайской теоремы об остатках
5. Parameters, laws and rules of protection
- Mandatory Point Validation: Before performing the scalar multiplication $Q = kP$, the receiver must check:
- $P eq \mathcal{O}$ (is not a point at infinity).
- The coordinates $x, y$ are in the correct range $[0, p-1]$.
- The coordinates satisfy the equation of an elliptic curve: $y^2 \equiv x^3 + ax + b \pmod p$.
- (Optional for cofactor > 1) The point lies in a subgroup of the correct order: $nP = \mathcal{O}$.
- Impact on libraries: The vulnerability (CVE-2015-6939) was closed in Bouncy Castle (in version >= 1.51) and in Oracle Java SE.
- Bitcoin Key Format: In Bitcoin, public keys are transmitted in a compressed format (only the x-coordinate and y-sign). In this case, the receiver automatically calculates y from the curve equation. This automatically protects against Invalid Curve Attacks, since if the x-coordinate does not correspond to a point on a valid curve, the equation will have no solutions (the square root will not be extracted), and the point will be rejected during the unpacking process.
4. Hands-on demonstration in Google Colab
Below are Python scripts that can be run in Google Colab (or locally) to illustrate the key stages of the attack. We use the ecdsa, sympy, numpy and built-in tools for working with final fields.
▶ Code for Colab All scripts are completely self-contained and demonstrate:
- Construction of an incorrect curve and search for a small divisor of the order.
- Simulation of sending a low-order point and extracting the remainder of the secret.
- Recovering a full key from multiple residues using CRT.
4.1. Finding an invalid curve with a small divisor
For the curve secp256k1 () we go through various and calculate the order of the resulting curve. We are interested in curves whose order contains a small prime factor (for example, 2, 3, 5, 7, …).
# ========== БЛОК 1: ПОИСК НЕДЕЙСТВИТЕЛЬНОЙ КРИВОЙ ==========
import sympy as sp
from sympy.ntheory import factorint
p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
a = 0
# Перебираем небольшие b' (в реальной атаке используются десятки тысяч)
for b_prime in range(2, 100):
# Уравнение кривой: y^2 = x^3 + a*x + b_prime (mod p)
# Используем формулу для порядка кривой через след Фробениуса (для демонстрации – упрощённо)
# В реальных вычислениях используется алгоритм SEA, но здесь для малых p' мы используем sympy.
# Поскольку p огромно, мы не можем вычислить порядок напрямую.
# Вместо этого мы создаём "игрушечную" кривую над малым полем для иллюстрации.
# Но для Colab мы используем встроенную библиотеку SageMath (если доступна),
# либо моделируем с помощью ecdsa.
pass
# Для Colab рекомендуется использовать SageMath, но мы приведём альтернативу с ecdsa.
# Имитация: ищем малые делители среди простых чисел.
small_primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
print("Потенциальные малые делители для анализа:", small_primes)
print("В реальной атаке строятся кривые с порядком, кратным этим числам.")
4.2 Simulation of the extraction of the remaining secret
We simulate a server that performs scalar multiplication on a dummy point order The attacker is picking candidates and determines the correct remainder.
# ========== БЛОК 2: ИЗВЛЕЧЕНИЕ ОСТАТКА (d mod q) ==========
from ecdsa import ellipticcurve, numbertheory
from ecdsa.curves import SECP256k1
import random
# Используем библиотеку ecdsa для работы с secp256k1
curve = SECP256k1.curve
G = SECP256k1.generator
n = SECP256k1.order
# Секретный ключ сервера (d) – случайный
d = random.randrange(1, n)
# Создаём недействительную кривую (для демонстрации используем другую кривую с a=0, b'=14)
# В реальности мы должны найти кривую с малым делителем, здесь просто имитация.
# ВНИМАНИЕ: код ниже является упрощённой моделью, не выполняющей реальный криптоанализ,
# но иллюстрирующей принцип перебора остатков.
# Имитируем точку P' порядка q (q – небольшое простое число)
q = 7 # например, порядок подгруппы
# Для простоты генерируем точку на некоторой кривой с порядком q (это не secp256k1)
# Мы просто создаём точку P' как точку на фиктивной кривой, где скалярное умножение даёт S' = d*P'
# В реальном сценарии сервер вычисляет S' = d * P' (на некорректной кривой)
# Мы же для демонстрации считаем, что сервер возвращает некоторый оракул (успешность MAC).
# Атакующий перебирает возможные d mod q.
print("Симуляция: секретный ключ d =", d % q, "(mod", q, ")")
# Перебор кандидатов r от 0 до q-1
for r in range(q):
# Проверка: если бы сервер использовал r, то MAC совпал бы.
# В реальной атаке атакующий отправляет зашифрованное сообщение и проверяет ошибку.
if r == d % q:
print(f"✓ Найден правильный остаток: d ≡ {r} (mod {q})")
else:
print(f"✗ Кандидат {r} не подходит")
4.3. Key recovery via CRT
After receiving several comparisons We use CRT to find modulo the product . If , the key is restored unambiguously.
# ========== БЛОК 3: КИТАЙСКАЯ ТЕОРЕМА ОБ ОСТАТКАХ ==========
from sympy.ntheory.modular import solve_congruence
# Предположим, что мы получили следующие остатки (результат предыдущего этапа)
congruences = [
(d % 2, 2),
(d % 3, 3),
(d % 5, 5),
(d % 7, 7),
]
# Решаем систему сравнений
result = solve_congruence(*congruences)
if result:
d_crt, mod = result
print(f"Восстановленное значение d ≡ {d_crt} (mod {mod})")
print(f"Истинное значение d = {d}")
print(f"Совпадение? {d % mod == d_crt}")
else:
print("Не удалось решить систему.")
Please note that for a real attack you need:
- Find the invalid curve with an order that has a small prime divisor This is done using the SEA (Schoof–Elkies–Atkin) algorithm or built-in SageMath functions.
- Generate a point order on .
- Send multiple times server and analyze the responses (MAC success/failure) to determine .
- Repeat for a set of mutually prime numbers , so that the product exceeds the order of the curve.
For working with real cryptosystems, it is recommended to use SageMath , which provides ready-made functions for working with elliptic curves over finite fields and computing the order.
5. Defense against Invalid Curve attack
Main countermeasures:
- Point validation: Before any scalar multiplication, check that , coordinates correct and .
- Subgroup membership test: For curves with cofactor > 1, it is also necessary to check that , Where — the order of the main subgroup.
- Using compressed keys (like in Bitcoin) is an automatic protection.
- Library updates: Vulnerabilities in Bouncy Castle and Oracle JSSE have been fixed in Java SE versions 1.51 and related updates.
In modern TLS 1.3 implementations and in libsecp256k1, all of these checks are performed by default, making the Invalid Curve Attack infeasible.
6. Conclusion and findings
The Jager–Schwenk–Somorovsky attack is a striking example of how a seemingly minor implementation error—a missing endpoint validation—can lead to the complete disclosure of a long-term secret key. It highlights the need for strict adherence to cryptographic protocol specifications and the use of only proven libraries.
In the context of Bitcoin and other blockchain systems, although the main transaction chain is not vulnerable to this attack, supporting services (nodes and ECDH-enabled wallets) must be protected similarly. The proposed Colab scripts allow researchers and developers to visually demonstrate the attack’s principles and test defense mechanisms in an isolated environment.
7. Appendix. Complete code for Google Colab
Below is a single block of code that can be copied and executed in a Colab notebook. It includes all three stages (curve search with divisor – simulation, remainder search, CRT).
# ==================== ПОЛНЫЙ КОД ДЛЯ GOOGLE COLAB ====================
# Установка необходимых библиотек (если не установлены)
!pip install ecdsa sympy
import random
from ecdsa import ellipticcurve, numbertheory
from ecdsa.curves import SECP256k1
from sympy.ntheory.modular import solve_congruence
# 1. Параметры кривой secp256k1
curve = SECP256k1.curve
G = SECP256k1.generator
n = SECP256k1.order
# 2. Секретный ключ (случайный)
d = random.randrange(1, n)
print(f"Секретный ключ d (в десятичном виде): {d}")
# 3. Симуляция оракула, возвращающего остаток d mod q
# В реальной атаке мы бы отправляли точки и анализировали ответы.
# Здесь мы просто вычисляем d mod q для демонстрации.
def oracle_mod(q):
"""Возвращает d mod q (имитация оракула)."""
return d % q
# 4. Собираем сравнения для нескольких малых q
congruences = []
small_primes = [2, 3, 5, 7, 11, 13, 17, 19]
for q in small_primes:
residue = oracle_mod(q)
congruences.append((residue, q))
print(f"d ≡ {residue} (mod {q})")
# 5. Восстанавливаем d по CRT
result = solve_congruence(*congruences)
if result:
d_crt, mod = result
print(f"\nВосстановленное значение: d ≡ {d_crt} (mod {mod})")
if mod > n:
print("Произведение модулей превышает порядок кривой → ключ восстановлен однозначно.")
else:
print("Произведение модулей меньше порядка кривой → требуется больше сравнений.")
print(f"Проверка: d % mod = {d % mod}, d_crt = {d_crt}")
print("Совпадает!" if d % mod == d_crt else "Ошибка восстановления.")
else:
print("Система сравнений неразрешима.")
By running this code, you will see how the original value is restored from a set of residuals by small modules. (or part of it). This clearly demonstrates the mathematical basis of the attack.
Important: The code presented is a training simulation . It does not perform real cryptanalysis of TLS or Bitcoin, as this requires a full implementation of working with points on invalid curves and MAC oracle analysis. However, it accurately reproduces the CRT logic and the principle of extracting remainders, which is the core of the attack.
Acknowledgments and Literature
- Jager, T., Schwenk, J., & Somorovsky, J. (2015). Practical Invalid Curve Attacks on TLS-ECDH . ESORICS.
- Antipa, A., et al. (2003). Validation of Elliptic Curve Public Keys . P.K.C.
- Biehl, I., Meyer, B., & Müller, V. (2003). Differential Fault Attacks on Elliptic Curve Cryptosystems . CRYPTO.
