eddsa-threshold 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. eddsa_threshold/__init__.py +1 -0
  2. eddsa_threshold/eddsa/__init__.py +1 -0
  3. eddsa_threshold/eddsa/algorithms/__init__.py +1 -0
  4. eddsa_threshold/eddsa/algorithms/ed25519.py +84 -0
  5. eddsa_threshold/eddsa/algorithms/ed25519ctx.py +23 -0
  6. eddsa_threshold/eddsa/algorithms/ed25519ph.py +23 -0
  7. eddsa_threshold/eddsa/algorithms/ed448.py +84 -0
  8. eddsa_threshold/eddsa/algorithms/ed448ph.py +23 -0
  9. eddsa_threshold/eddsa/curves/__init__.py +1 -0
  10. eddsa_threshold/eddsa/curves/base/__init__.py +1 -0
  11. eddsa_threshold/eddsa/curves/base/edwards_curve.py +91 -0
  12. eddsa_threshold/eddsa/curves/base/encoding.py +54 -0
  13. eddsa_threshold/eddsa/curves/base/field_ops.py +48 -0
  14. eddsa_threshold/eddsa/curves/base/scalar_ops.py +34 -0
  15. eddsa_threshold/eddsa/curves/ed25519/__init__.py +1 -0
  16. eddsa_threshold/eddsa/curves/ed25519/constants.py +37 -0
  17. eddsa_threshold/eddsa/curves/ed25519/ed25519_curve.py +79 -0
  18. eddsa_threshold/eddsa/curves/ed25519/encoding.py +80 -0
  19. eddsa_threshold/eddsa/curves/ed25519/field_ops.py +12 -0
  20. eddsa_threshold/eddsa/curves/ed25519/scalar_ops.py +12 -0
  21. eddsa_threshold/eddsa/curves/ed448/__init__.py +1 -0
  22. eddsa_threshold/eddsa/curves/ed448/constants.py +37 -0
  23. eddsa_threshold/eddsa/curves/ed448/ed448_curve.py +76 -0
  24. eddsa_threshold/eddsa/curves/ed448/encoding.py +77 -0
  25. eddsa_threshold/eddsa/curves/ed448/field_ops.py +12 -0
  26. eddsa_threshold/eddsa/curves/ed448/scalar_ops.py +12 -0
  27. eddsa_threshold/eddsa/keys/__init__.py +1 -0
  28. eddsa_threshold/eddsa/keys/ed25519_keypair.py +46 -0
  29. eddsa_threshold/eddsa/keys/ed448_keypair.py +45 -0
  30. eddsa_threshold/eddsa/keys/keypair.py +50 -0
  31. eddsa_threshold/eddsa/util/__init__.py +1 -0
  32. eddsa_threshold/eddsa/util/dom.py +17 -0
  33. eddsa_threshold/eddsa/util/hash_bindings.py +12 -0
  34. eddsa_threshold/frost/__init__.py +1 -0
  35. eddsa_threshold/frost/coordinator.py +185 -0
  36. eddsa_threshold/frost/core/__init__.py +1 -0
  37. eddsa_threshold/frost/core/base/__init__.py +1 -0
  38. eddsa_threshold/frost/core/base/frost_hashing.py +27 -0
  39. eddsa_threshold/frost/core/ed25519/__init__.py +1 -0
  40. eddsa_threshold/frost/core/ed25519/frost_hashing.py +26 -0
  41. eddsa_threshold/frost/core/ed448/__init__.py +1 -0
  42. eddsa_threshold/frost/core/ed448/frost_hashing.py +27 -0
  43. eddsa_threshold/frost/core/frost_types.py +53 -0
  44. eddsa_threshold/frost/core/polynomial.py +39 -0
  45. eddsa_threshold/frost/core/secrets/__init__.py +1 -0
  46. eddsa_threshold/frost/core/secrets/secret_sharing.py +26 -0
  47. eddsa_threshold/frost/core/secrets/shamir_secret_sharing.py +41 -0
  48. eddsa_threshold/frost/core/util.py +109 -0
  49. eddsa_threshold/frost/participant.py +140 -0
  50. eddsa_threshold/frost/trusted_dealer.py +72 -0
  51. eddsa_threshold-0.2.0.dist-info/METADATA +39 -0
  52. eddsa_threshold-0.2.0.dist-info/RECORD +55 -0
  53. eddsa_threshold-0.2.0.dist-info/WHEEL +5 -0
  54. eddsa_threshold-0.2.0.dist-info/licenses/LICENSE +14 -0
  55. eddsa_threshold-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
1
+ """EdDSA Threshold package."""
@@ -0,0 +1 @@
1
+ """EdDSA primitives."""
@@ -0,0 +1 @@
1
+ """EdDSA signing algorithms."""
@@ -0,0 +1,84 @@
1
+ from typing import Callable
2
+ from eddsa_threshold.eddsa.curves.ed25519.ed25519_curve import Ed25519Curve
3
+ from eddsa_threshold.eddsa.curves.ed25519.scalar_ops import Ed25519ScalarOps
4
+ from eddsa_threshold.eddsa.curves.ed25519.constants import SCALAR_SIZE
5
+ from eddsa_threshold.eddsa.keys.ed25519_keypair import Ed25519Keypair
6
+ from eddsa_threshold.eddsa.util.dom import dom2
7
+ from eddsa_threshold.eddsa.util.hash_bindings import sha512
8
+
9
+
10
+ class Ed25519():
11
+ """
12
+ EdDSA using the Ed25519 curve.
13
+ Implements signing and verification methods.
14
+ """
15
+
16
+ @staticmethod
17
+ def sign(message: bytes, keypair: Ed25519Keypair) -> bytes:
18
+ """Sign a message using the provided Ed25519 keypair."""
19
+
20
+ return Ed25519._sign(message, keypair, ph=lambda m: m, dom2=dom2(0, None))
21
+
22
+ @staticmethod
23
+ def verify(signature: bytes, message: bytes, public_key: bytes) -> bool:
24
+ """Verify a signature for a message using the provided Ed25519 public key."""
25
+
26
+ return Ed25519._verify(signature, message, public_key, ph=lambda m: m, dom2=dom2(0, None))
27
+
28
+ @staticmethod
29
+ def _sign(message: bytes, keypair: Ed25519Keypair, ph: Callable, dom2: bytes) -> bytes:
30
+ """Internal sign method as basis for subclasses."""
31
+
32
+ curve = Ed25519Curve()
33
+
34
+ # Sign message according to RFC 8032 Section 5.1.6
35
+ # 1. Get precomputed prefix
36
+ prefix = keypair.prefix
37
+
38
+ # 2. Compute the nonce
39
+ r = int.from_bytes(sha512(dom2 + prefix + ph(message)), byteorder='little')
40
+
41
+ # 3. Compute the R point
42
+ r = curve.scalar_ops.reduce(r)
43
+ R = curve.encode_extended_point(curve.scalar_mult(r))
44
+
45
+ # 4. Compute the challenge
46
+ k = int.from_bytes(sha512(dom2 + R + keypair.public_bytes + ph(message)), byteorder='little')
47
+
48
+ # 5. Compute the S value
49
+ k = curve.scalar_ops.reduce(k)
50
+ S = curve.scalar_ops.reduce(r + k * keypair.scalar)
51
+
52
+ return R + curve._encoding.encode_scalar(S)
53
+
54
+ @staticmethod
55
+ def _verify(signature: bytes, message: bytes, public_key: bytes, ph: Callable, dom2: bytes) -> bool:
56
+ """Internal verify method as basis for subclasses."""
57
+
58
+ curve = Ed25519Curve()
59
+
60
+ # Verify signature according to RFC 8032 Section 5.1.7
61
+ if len(signature) != 64:
62
+ return False
63
+
64
+ try:
65
+ # 1. Decode R and S from the signature
66
+ R = curve.decode_point(signature[:SCALAR_SIZE])
67
+ S = curve._encoding.decode_scalar(signature[SCALAR_SIZE:])
68
+ if S >= curve.scalar_ops.order or S < 0:
69
+ return False
70
+
71
+ A = curve.decode_point(public_key)
72
+
73
+ # 2. Compute the challenge
74
+ k = int.from_bytes(sha512(dom2 + signature[:SCALAR_SIZE] + public_key + ph(message)), byteorder='little')
75
+ k = curve.scalar_ops.reduce(k)
76
+
77
+ # 3. Verify the equation [S]B = R + [k]A
78
+ left = curve.scalar_mult(S)
79
+ right = curve.add(curve.affine_to_extended(R), curve.scalar_mult(k, curve.affine_to_extended(A)))
80
+
81
+ return curve.extended_to_affine(left) == curve.extended_to_affine(right)
82
+
83
+ except ValueError:
84
+ return False
@@ -0,0 +1,23 @@
1
+ from eddsa_threshold.eddsa.algorithms.ed25519 import Ed25519
2
+ from eddsa_threshold.eddsa.keys.ed25519_keypair import Ed25519Keypair
3
+ from eddsa_threshold.eddsa.util.dom import dom2
4
+ from eddsa_threshold.eddsa.util.hash_bindings import sha512
5
+
6
+
7
+ class Ed25519CTX(Ed25519):
8
+ """
9
+ EdDSA using the Ed25519 curve and context.
10
+ Implements signing and verification methods.
11
+ """
12
+
13
+ @staticmethod
14
+ def sign(message: bytes, keypair: Ed25519Keypair, context: bytes = b"") -> bytes:
15
+ """Sign a message using the provided Ed25519 keypair. Uses context (SHOULD not be emtpy)."""
16
+
17
+ return Ed25519CTX._sign(message, keypair, ph=lambda m: m, dom2=dom2(0, context))
18
+
19
+ @staticmethod
20
+ def verify(signature: bytes, message: bytes, public_key: bytes, context: bytes = b"") -> bool:
21
+ """Verify a signature for a message using the provided Ed25519 public key. Uses context (SHOULD not be emtpy)."""
22
+
23
+ return Ed25519CTX._verify(signature, message, public_key, ph=lambda m: m, dom2=dom2(0, context))
@@ -0,0 +1,23 @@
1
+ from eddsa_threshold.eddsa.algorithms.ed25519 import Ed25519
2
+ from eddsa_threshold.eddsa.keys.ed25519_keypair import Ed25519Keypair
3
+ from eddsa_threshold.eddsa.util.dom import dom2
4
+ from eddsa_threshold.eddsa.util.hash_bindings import sha512
5
+
6
+
7
+ class Ed25519PH(Ed25519):
8
+ """
9
+ EdDSA using the Ed25519 curve and pre-hashing.
10
+ Implements signing and verification methods.
11
+ """
12
+
13
+ @staticmethod
14
+ def sign(message: bytes, keypair: Ed25519Keypair, context: bytes = b"") -> bytes:
15
+ """Sign a message using the provided Ed25519 keypair. Uses pre-hashing and context (set to empty by default)."""
16
+
17
+ return Ed25519PH._sign(message, keypair, ph=sha512, dom2=dom2(1, context))
18
+
19
+ @staticmethod
20
+ def verify(signature: bytes, message: bytes, public_key: bytes, context: bytes = b"") -> bool:
21
+ """Verify a signature for a message using the provided Ed25519 public key. Uses pre-hashing and context (set to empty by default)."""
22
+
23
+ return Ed25519PH._verify(signature, message, public_key, ph=sha512, dom2=dom2(1, context))
@@ -0,0 +1,84 @@
1
+ from typing import Callable
2
+ from eddsa_threshold.eddsa.curves.ed448.constants import SCALAR_SIZE, SIGNATURE_SIZE
3
+ from eddsa_threshold.eddsa.curves.ed448.ed448_curve import Ed448Curve
4
+ from eddsa_threshold.eddsa.curves.ed448.scalar_ops import Ed448ScalarOps
5
+ from eddsa_threshold.eddsa.keys.ed448_keypair import Ed448Keypair
6
+ from eddsa_threshold.eddsa.util.dom import dom4
7
+ from eddsa_threshold.eddsa.util.hash_bindings import shake256
8
+
9
+
10
+ class Ed448():
11
+ """
12
+ EdDSA using the Ed448 curve.
13
+ Implements signing and verification methods.
14
+ """
15
+
16
+ @staticmethod
17
+ def sign(message: bytes, keypair: Ed448Keypair, context: bytes) -> bytes:
18
+ """Sign a message using the provided Ed448 keypair."""
19
+
20
+ return Ed448._sign(message, keypair, ph=lambda m: m, dom4=dom4(0, context))
21
+
22
+ @staticmethod
23
+ def verify(signature: bytes, message: bytes, public_key: bytes, context: bytes) -> bool:
24
+ """Verify a signature for a message using the provided Ed448 public key."""
25
+
26
+ return Ed448._verify(signature, message, public_key, ph=lambda m: m, dom4=dom4(0, context))
27
+
28
+ @staticmethod
29
+ def _sign(message: bytes, keypair: Ed448Keypair, ph: Callable, dom4: bytes) -> bytes:
30
+ """Internal sign method as basis for subclasses."""
31
+
32
+ curve = Ed448Curve()
33
+
34
+ # Sign message according to RFC 8032 Section 5.2.6
35
+ # 1. Get precomputed prefix
36
+ prefix = keypair.prefix
37
+
38
+ # 2. Compute the nonce
39
+ r = int.from_bytes(shake256(dom4 + prefix + ph(message), 114), byteorder='little')
40
+
41
+ # 3. Compute the R point
42
+ r = curve.scalar_ops.reduce(r)
43
+ R = curve.encode_extended_point(curve.scalar_mult(r))
44
+
45
+ # 4. Compute the challenge
46
+ k = int.from_bytes(shake256(dom4 + R + keypair.public_bytes + ph(message), 114), byteorder='little')
47
+
48
+ # 5. Compute the S value
49
+ k = curve.scalar_ops.reduce(k)
50
+ S = curve.scalar_ops.reduce(r + k * keypair.scalar)
51
+
52
+ return R + curve._encoding.encode_scalar(S)
53
+
54
+ @staticmethod
55
+ def _verify(signature: bytes, message: bytes, public_key: bytes, ph: Callable, dom4: bytes) -> bool:
56
+ """Internal verify method as basis for subclasses."""
57
+
58
+ curve = Ed448Curve()
59
+
60
+ # Verify signature according to RFC 8032 Section 5.2.7
61
+ if len(signature) != SIGNATURE_SIZE:
62
+ return False
63
+
64
+ try:
65
+ # 1. Decode R and S from the signature
66
+ R = curve.decode_point(signature[:SCALAR_SIZE])
67
+ S = curve._encoding.decode_scalar(signature[SCALAR_SIZE:])
68
+ if S >= curve.scalar_ops.order or S < 0:
69
+ return False
70
+
71
+ A = curve.decode_point(public_key)
72
+
73
+ # 2. Compute the challenge
74
+ k = int.from_bytes(shake256(dom4 + signature[:SCALAR_SIZE] + public_key + ph(message), 114), byteorder='little')
75
+ k = curve.scalar_ops.reduce(k)
76
+
77
+ # 3. Verify the equation [S]B = R + [k]A
78
+ left = curve.scalar_mult(S)
79
+ right = curve.add(curve.affine_to_extended(R), curve.scalar_mult(k, curve.affine_to_extended(A)))
80
+
81
+ return curve.extended_to_affine(left) == curve.extended_to_affine(right)
82
+
83
+ except ValueError:
84
+ return False
@@ -0,0 +1,23 @@
1
+ from eddsa_threshold.eddsa.algorithms.ed448 import Ed448
2
+ from eddsa_threshold.eddsa.keys.ed448_keypair import Ed448Keypair
3
+ from eddsa_threshold.eddsa.util.dom import dom4
4
+ from eddsa_threshold.eddsa.util.hash_bindings import shake256
5
+
6
+
7
+ class Ed448PH(Ed448):
8
+ """
9
+ EdDSA using the Ed448 curve and pre-hashing.
10
+ Implements signing and verification methods.
11
+ """
12
+
13
+ @staticmethod
14
+ def sign(message: bytes, keypair: Ed448Keypair, context: bytes = b"") -> bytes:
15
+ """Sign a message using the provided Ed448 keypair."""
16
+
17
+ return Ed448PH._sign(message, keypair, ph=lambda m: shake256(m, 64), dom4=dom4(1, context))
18
+
19
+ @staticmethod
20
+ def verify(signature: bytes, message: bytes, public_key: bytes, context: bytes = b"") -> bool:
21
+ """Verify a signature for a message using the provided Ed448 public key."""
22
+
23
+ return Ed448PH._verify(signature, message, public_key, ph=lambda m: shake256(m, 64), dom4=dom4(1, context))
@@ -0,0 +1 @@
1
+ """Curve implementations."""
@@ -0,0 +1 @@
1
+ """Base curve abstractions."""
@@ -0,0 +1,91 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Tuple
3
+
4
+ from eddsa_threshold.eddsa.curves.base.encoding import Encoding
5
+ from eddsa_threshold.eddsa.curves.base.field_ops import FieldOps
6
+ from eddsa_threshold.eddsa.curves.base.scalar_ops import ScalarOps
7
+
8
+
9
+ class EdwardsCurve(ABC):
10
+ """
11
+ Abstract base class for Edwards curves.
12
+
13
+ Provides methods for point encoding/decoding, coordinate conversions, scalar multiplication, and negation.
14
+
15
+ Concrete implementations must provide field operations, encoding, the base point, and point addition/doubling.
16
+ """
17
+
18
+ @property
19
+ @abstractmethod
20
+ def field(self) -> FieldOps:
21
+ """Return the FieldOps implementation."""
22
+ raise NotImplementedError
23
+
24
+ @property
25
+ @abstractmethod
26
+ def encoding(self) -> Encoding:
27
+ """Return the Encoding implementation."""
28
+ raise NotImplementedError
29
+
30
+ @property
31
+ @abstractmethod
32
+ def scalar_ops(self) -> ScalarOps:
33
+ """Return the ScalarOps implementation."""
34
+ raise NotImplementedError
35
+
36
+ @property
37
+ @abstractmethod
38
+ def base_point(self) -> Tuple:
39
+ """Return affine base point as (x, y)."""
40
+ raise NotImplementedError
41
+
42
+ def encode_extended_point(self, P: Tuple) -> bytes:
43
+ """Encode point from (X, Y, Z, T) to bytes."""
44
+ return self.encode_affine_point(self.extended_to_affine(P))
45
+
46
+ def encode_affine_point(self, P: Tuple) -> bytes:
47
+ """Encode point from (x, y) to bytes."""
48
+ return self.encoding.encode_point(P)
49
+
50
+ def decode_point(self, data: bytes) -> Tuple:
51
+ """Decode point from bytes to (x, y)."""
52
+ return self.encoding.decode_point(data)
53
+
54
+ def affine_to_extended(self, P: Tuple):
55
+ """Convert (x, y) → (X, Y, Z, T)."""
56
+ x, y = P
57
+ return (x, y, 1, self.field.mul(x, y))
58
+
59
+ def extended_to_affine(self, P: Tuple):
60
+ """Convert (X, Y, Z, T) → (x, y)."""
61
+ X, Y, Z, _ = P
62
+ z_inv = self.field.inv(Z)
63
+ return (self.field.mul(X, z_inv), self.field.mul(Y, z_inv))
64
+
65
+ @abstractmethod
66
+ def add(self, P: Tuple, Q: Tuple) -> Tuple:
67
+ """Add points P and Q."""
68
+ raise NotImplementedError
69
+
70
+ @abstractmethod
71
+ def double(self, P: Tuple) -> Tuple:
72
+ """Double point P."""
73
+ raise NotImplementedError
74
+
75
+ def scalar_mult(self, k: int, P=None) -> Tuple:
76
+ """Multiply point P by scalar k using double-and-add algorithm. If P is None, use the base point."""
77
+ if P is None:
78
+ P = self.affine_to_extended(self.base_point)
79
+
80
+ Q = (0, 1, 1, 0) # Neutral element in extended coordinates
81
+ for bit in reversed(bin(k)[2:]):
82
+ if bit == '1':
83
+ Q = self.add(Q, P)
84
+ P = self.double(P)
85
+
86
+ return Q
87
+
88
+ def negate(self, P: Tuple) -> Tuple:
89
+ """Negate point P."""
90
+ X, Y, Z, T = P
91
+ return (self.field.neg(X), Y, Z, self.field.neg(T))
@@ -0,0 +1,54 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Tuple
3
+
4
+ from eddsa_threshold.eddsa.curves.base.field_ops import FieldOps
5
+
6
+
7
+ class Encoding(ABC):
8
+ """
9
+ Abstract byte encoding/decoding layer.
10
+ """
11
+
12
+ @abstractmethod
13
+ def __init__(self, FieldOps: FieldOps):
14
+ """Initialize with field operations."""
15
+ raise NotImplementedError
16
+
17
+ @property
18
+ @abstractmethod
19
+ def scalar_size(self) -> int:
20
+ """Size in bytes of a private scalar."""
21
+ raise NotImplementedError
22
+
23
+ @property
24
+ @abstractmethod
25
+ def group_order(self) -> int:
26
+ """Group order of the field."""
27
+ raise NotImplementedError
28
+
29
+ @property
30
+ @abstractmethod
31
+ def point_size(self) -> int:
32
+ """Size in bytes of an encoded public key point."""
33
+ raise NotImplementedError
34
+
35
+ @abstractmethod
36
+ def encode_point(self, P: Tuple) -> bytes:
37
+ raise NotImplementedError
38
+
39
+ @abstractmethod
40
+ def decode_point(self, data: bytes) -> Tuple:
41
+ raise NotImplementedError
42
+
43
+ def encode_scalar(self, x: int) -> bytes:
44
+ """Serialize a scalar to bytes (little-endian)."""
45
+ return x.to_bytes(self.scalar_size, byteorder='little')
46
+
47
+ def decode_scalar(self, data: bytes) -> int:
48
+ """Deserialize bytes to a scalar (little-endian)."""
49
+ if len(data) != self.scalar_size:
50
+ raise ValueError(f"Invalid scalar size: expected {self.scalar_size} bytes")
51
+ value = int.from_bytes(data, byteorder='little')
52
+ if value < 0 or value >= self.group_order:
53
+ raise ValueError("Deserialized scalar is out of range")
54
+ return value
@@ -0,0 +1,48 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class FieldOps(ABC):
5
+ """
6
+ Abstract base class for finite field operations modulo a prime p.
7
+
8
+ All operations return integers representing field elements.
9
+ """
10
+
11
+ @property
12
+ @abstractmethod
13
+ def p(self) -> int:
14
+ """Prime modulus for the field GF(p)."""
15
+ raise NotImplementedError
16
+
17
+ def add(self, x: int, y: int) -> int:
18
+ """Addition in the field GF(p)."""
19
+ return (x + y) % self.p
20
+
21
+ def sub(self, x: int, y: int) -> int:
22
+ """Subtraction in the field GF(p)."""
23
+ return (x - y) % self.p
24
+
25
+ def mul(self, x: int, y: int) -> int:
26
+ """Multiplication in the field GF(p)."""
27
+ return (x * y) % self.p
28
+
29
+ def neg(self, x: int) -> int:
30
+ """Negation in the field GF(p)."""
31
+ return -x % self.p
32
+
33
+ def inv(self, x: int) -> int:
34
+ """Multiplicative inverse in the field GF(p)."""
35
+ # https://en.wikipedia.org/wiki/Finite_field_arithmetic
36
+ return pow(x, self.p - 2, self.p)
37
+
38
+ def sqr(self, x: int) -> int:
39
+ """Squaring in the field GF(p)."""
40
+ return (x * x) % self.p
41
+
42
+ def pow(self, x: int, e: int) -> int:
43
+ """Exponentiation in the field GF(p)."""
44
+ return pow(x, e, self.p)
45
+
46
+ def reduce(self, x: int) -> int:
47
+ """Reduce x modulo p."""
48
+ return x % self.p
@@ -0,0 +1,34 @@
1
+ from abc import ABC, abstractmethod
2
+ from secrets import randbelow
3
+
4
+
5
+ class ScalarOps(ABC):
6
+ """
7
+ Abstract class representing scalar-related math modulo the group order.
8
+
9
+ All operations return integers representing scalars.
10
+ """
11
+
12
+ @property
13
+ @abstractmethod
14
+ def order(self) -> int:
15
+ raise NotImplementedError
16
+
17
+ @property
18
+ @abstractmethod
19
+ def identity(self) -> int:
20
+ """Return the identity scalar."""
21
+ raise NotImplementedError
22
+
23
+ def inv(self, x: int) -> int:
24
+ """Multiplicative inverse in the field GF(order)."""
25
+ # https://en.wikipedia.org/wiki/Finite_field_arithmetic
26
+ return pow(x, self.order - 2, self.order)
27
+
28
+ def reduce(self, k: int) -> int:
29
+ """Reduce any integer modulo the group order."""
30
+ return k % self.order
31
+
32
+ def random_scalar(self) -> int:
33
+ """Fine for this project; NOT PRODUCTION SECURE."""
34
+ return randbelow(self.order)
@@ -0,0 +1 @@
1
+ """Ed25519 curve implementation."""
@@ -0,0 +1,37 @@
1
+ """
2
+ Constants for Ed25519
3
+
4
+ These values are taken from RFC 8032.
5
+ RFC: https://datatracker.ietf.org/doc/html/rfc8032
6
+ """
7
+
8
+ # Finite field modulus p
9
+ p = 2**255 - 19
10
+
11
+ # Bit size of the field
12
+ b = 256
13
+
14
+ # base 2 logarithm of cofactor
15
+ c = 3
16
+
17
+ n = 254
18
+
19
+ # Curve Parameters d/a
20
+ # d = -121665 * pow(121666, -1, p) % p
21
+ d = 37095705934669439343138083508754565189542113879843219016388785533085940283555
22
+ a = -1
23
+
24
+ # Base point of the curve
25
+ BASE_X = 15112221349535400772501151409588531511454012693041857206046113283949847762202
26
+ BASE_Y = 46316835694926478169428394003475163141307993866256225615783033603165251855960
27
+ BASE = (BASE_X, BASE_Y)
28
+ IDENTITY = (0, 1)
29
+
30
+ # Order of ed25519
31
+ L = 2**252+27742317777372353535851937790883648493
32
+
33
+ # Encoding sizes (in bytes)
34
+ SCALAR_SIZE = 32 # size of private scalar
35
+ PUBLIC_KEY_SIZE = 32 # size of encoded public key
36
+ SIGNATURE_SIZE = 64 # R || S
37
+ SEED_SIZE = 32 # seed length specified by RFC 8032
@@ -0,0 +1,79 @@
1
+ from typing import Tuple
2
+ from eddsa_threshold.eddsa.curves.base.edwards_curve import EdwardsCurve
3
+ from eddsa_threshold.eddsa.curves.base.encoding import Encoding
4
+ from eddsa_threshold.eddsa.curves.base.field_ops import FieldOps
5
+ from eddsa_threshold.eddsa.curves.base.scalar_ops import ScalarOps
6
+ from .scalar_ops import Ed25519ScalarOps
7
+ from .encoding import Ed25519Encoding
8
+ from .field_ops import Ed25519FieldOps
9
+ from .constants import d, BASE
10
+
11
+
12
+ class Ed25519Curve(EdwardsCurve):
13
+ """
14
+ Ed25519 curve implementation.
15
+
16
+ See base class EdwardsCurve for method descriptions.
17
+ """
18
+
19
+ def __init__(self):
20
+ self._field_ops = Ed25519FieldOps()
21
+ self._encoding = Ed25519Encoding(self._field_ops)
22
+ self._scalar_ops = Ed25519ScalarOps()
23
+ self.d = d
24
+
25
+ @property
26
+ def field(self) -> FieldOps:
27
+ return self._field_ops
28
+
29
+ @property
30
+ def encoding(self) -> Encoding:
31
+ return self._encoding
32
+
33
+ @property
34
+ def scalar_ops(self) -> ScalarOps:
35
+ return self._scalar_ops
36
+
37
+ @property
38
+ def base_point(self) -> Tuple:
39
+ return BASE
40
+
41
+ # Point addition
42
+ def add(self, P: Tuple, Q: Tuple) -> Tuple:
43
+ X1, Y1, Z1, T1 = P
44
+ X2, Y2, Z2, T2 = Q
45
+
46
+ A = (Y1 - X1) * (Y2 - X2)
47
+ B = (Y1 + X1) * (Y2 + X2)
48
+ C = T1 * 2 * self.d * T2
49
+ D = Z1 * 2 * Z2
50
+ E = B - A
51
+ F = D - C
52
+ G = D + C
53
+ H = B + A
54
+
55
+ X3 = self._field_ops.mul(E, F)
56
+ Y3 = self._field_ops.mul(G, H)
57
+ T3 = self._field_ops.mul(E, H)
58
+ Z3 = self._field_ops.mul(F, G)
59
+
60
+ return (X3, Y3, Z3, T3)
61
+
62
+ # Point doubling
63
+ def double(self, P: Tuple) -> Tuple:
64
+ X1, Y1, Z1, T1 = P
65
+
66
+ A = X1**2
67
+ B = Y1**2
68
+ C = 2 * Z1**2
69
+ H = A + B
70
+ E = H - (X1 + Y1)**2
71
+ G = A - B
72
+ F = C + G
73
+
74
+ X3 = self._field_ops.mul(E, F)
75
+ Y3 = self._field_ops.mul(G, H)
76
+ T3 = self._field_ops.mul(E, H)
77
+ Z3 = self._field_ops.mul(F, G)
78
+
79
+ return (X3, Y3, Z3, T3)