Skip to content

Protocols

ecutils.protocols

Key exchange protocols built on top of the core elliptic curve primitives.

DiffieHellman dataclass

Elliptic Curve Diffie-Hellman key exchange.

Protocol
  1. Alice computes her public key: H_A = d_A · G
  2. Bob computes his public key: H_B = d_B · G
  3. Shared secret: S = d_A · H_B = d_B · H_A = d_A · d_B · G

Security relies on the Elliptic Curve Discrete Logarithm Problem (ECDLP): given G and Q = d·G, it is computationally infeasible to recover the private scalar d.

Attributes:

Name Type Description
private_key int

The private scalar (integer).

curve_name str

Name of the curve (e.g. "secp256k1").

Source code in ecutils/protocols/diffie_hellman.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@dataclass(frozen=True)
class DiffieHellman:
    """Elliptic Curve Diffie-Hellman key exchange.

    Protocol:
        1. Alice computes her public key: H_A = d_A · G
        2. Bob computes his public key:   H_B = d_B · G
        3. Shared secret: S = d_A · H_B = d_B · H_A = d_A · d_B · G

    Security relies on the Elliptic Curve Discrete Logarithm Problem
    (ECDLP): given G and Q = d·G, it is computationally infeasible to
    recover the private scalar *d*.

    Attributes:
        private_key: The private scalar (integer).
        curve_name:  Name of the curve (e.g. ``"secp256k1"``).
    """

    private_key: int
    curve_name: str = "secp256k1"

    # ----- derived properties -----

    @property
    def _G(self) -> Point:
        return get_generator(self.curve_name)

    @property
    def public_key(self) -> Point:
        """Compute the public key: ``private_key * G``."""
        return self.private_key * self._G

    # ----- protocol -----

    def compute_shared_secret(self, other_public_key: Point) -> Point:
        """Compute the shared secret from another party's public key.

        Args:
            other_public_key: The other party's public key point.

        Returns:
            The shared secret point ``private_key * other_public_key``.
        """
        return self.private_key * other_public_key

public_key: Point property

Compute the public key: private_key * G.

compute_shared_secret(other_public_key)

Compute the shared secret from another party's public key.

Parameters:

Name Type Description Default
other_public_key Point

The other party's public key point.

required

Returns:

Type Description
Point

The shared secret point private_key * other_public_key.

Source code in ecutils/protocols/diffie_hellman.py
59
60
61
62
63
64
65
66
67
68
def compute_shared_secret(self, other_public_key: Point) -> Point:
    """Compute the shared secret from another party's public key.

    Args:
        other_public_key: The other party's public key point.

    Returns:
        The shared secret point ``private_key * other_public_key``.
    """
    return self.private_key * other_public_key

MasseyOmura dataclass

Massey-Omura three-pass protocol.

Three-pass message exchange without shared keys:

C1 = e_A · M           (Alice encrypts)
C2 = e_B · C1          (Bob encrypts)
C3 = e_A⁻¹ · C2        (Alice removes her encryption)
M  = e_B⁻¹ · C3        (Bob recovers the message)

The protocol works because scalar multiplication on elliptic curves is commutative: e_A · (e_B · M) = e_B · (e_A · M).

Requirement: gcd(private_key, n) = 1 so that the modular inverse e⁻¹ mod n exists.

Attributes:

Name Type Description
private_key int

The private scalar (integer), must be coprime with n.

curve_name str

Name of the curve (e.g. "secp521r1").

Source code in ecutils/protocols/massey_omura.py
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
@dataclass(frozen=True)
class MasseyOmura:
    """Massey-Omura three-pass protocol.

    Three-pass message exchange without shared keys:

        C1 = e_A · M           (Alice encrypts)
        C2 = e_B · C1          (Bob encrypts)
        C3 = e_A⁻¹ · C2        (Alice removes her encryption)
        M  = e_B⁻¹ · C3        (Bob recovers the message)

    The protocol works because scalar multiplication on elliptic curves
    is commutative: e_A · (e_B · M) = e_B · (e_A · M).

    Requirement: gcd(private_key, n) = 1 so that the modular inverse
    e⁻¹ mod n exists.

    Attributes:
        private_key: The private scalar (integer), must be coprime with n.
        curve_name:  Name of the curve (e.g. ``"secp521r1"``).
    """

    private_key: int
    curve_name: str = "secp521r1"

    # ----- derived properties -----

    @property
    def _curve(self):
        return get_curve(self.curve_name)

    @property
    def _inverse_key(self) -> int:
        """Modular inverse of the private key mod n."""
        return pow(self.private_key, -1, self._curve.n)

    # ----- protocol -----

    def encrypt(self, point: Point) -> Point:
        """Encrypt (multiply) a point with the private key.

        Args:
            point: A curve point (message or partially encrypted).

        Returns:
            ``private_key * point``
        """
        return self.private_key * point

    def decrypt(self, point: Point) -> Point:
        """Decrypt (multiply) a point with the inverse of the private key.

        Args:
            point: A curve point to decrypt.

        Returns:
            ``private_key⁻¹ * point``
        """
        return self._inverse_key * point

decrypt(point)

Decrypt (multiply) a point with the inverse of the private key.

Parameters:

Name Type Description Default
point Point

A curve point to decrypt.

required

Returns:

Type Description
Point

private_key⁻¹ * point

Source code in ecutils/protocols/massey_omura.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def decrypt(self, point: Point) -> Point:
    """Decrypt (multiply) a point with the inverse of the private key.

    Args:
        point: A curve point to decrypt.

    Returns:
        ``private_key⁻¹ * point``
    """
    return self._inverse_key * point

encrypt(point)

Encrypt (multiply) a point with the private key.

Parameters:

Name Type Description Default
point Point

A curve point (message or partially encrypted).

required

Returns:

Type Description
Point

private_key * point

Source code in ecutils/protocols/massey_omura.py
80
81
82
83
84
85
86
87
88
89
def encrypt(self, point: Point) -> Point:
    """Encrypt (multiply) a point with the private key.

    Args:
        point: A curve point (message or partially encrypted).

    Returns:
        ``private_key * point``
    """
    return self.private_key * point