Add Ed25519 keys and EdDSA signatures

This commit is contained in:
Helder Eijs 2022-04-15 00:15:48 +02:00
parent db731bb3a7
commit 764c1e81b9
73 changed files with 6840 additions and 629 deletions

View file

@ -33,7 +33,10 @@ class MockLib(object):
ec_ws_new_context = lambda *x: 0 ec_ws_new_context = lambda *x: 0
ec_free_context = lambda *x: None ec_free_context = lambda *x: None
ec_ws_new_point = lambda *x: 0 ec_ws_new_point = lambda *x: 0
ec_free_point = lambda *x: None ec_ws_free_point = lambda *x: None
ed25519_new_point = lambda *x: 0
ed25519_free_point = lambda *x: None
ed25519_new_point = lambda *x: 0
_raw_api.load_pycryptodome_raw_lib = lambda name, cdec: MockLib() _raw_api.load_pycryptodome_raw_lib = lambda name, cdec: MockLib()

28
Doc/reqs.txt Normal file
View file

@ -0,0 +1,28 @@
alabaster==0.7.12
Babel==2.9.1
certifi==2021.5.30
chardet==4.0.0
commonmark==0.8.1
docutils==0.17.1
future==0.18.2
idna==2.10
imagesize==1.2.0
Jinja2==2.11.3
MarkupSafe==1.1.1
mock==1.0.1
packaging==20.9
Pillow
Pygments==2.5.2
pyparsing==2.4.7
pytz==2021.1
readthedocs-sphinx-ext==2.1.4
recommonmark==0.5.0
requests==2.26.0
six==1.16.0
snowballstemmer==2.1.0
Sphinx==1.8.5
sphinx-rtd-theme==0.4.3
sphinxcontrib-websupport==1.1.2
#typing==3.10.0.0
typing
urllib3==1.26.7

View file

@ -17,7 +17,7 @@ using widely supported formats like PEM or DER.
.. _curve_names: .. _curve_names:
.. csv-table:: .. csv-table::
:header: Curve, Possible identifiers :header: Curve, Strings accepted for the ``curve`` API parameter
:widths: 20, 80 :widths: 20, 80
"NIST P-192", "``'NIST P-192'``, ``'p192'``, ``'P-192'``, ``'prime192v1'``, ``'secp192r1'``" "NIST P-192", "``'NIST P-192'``, ``'p192'``, ``'P-192'``, ``'prime192v1'``, ``'secp192r1'``"
@ -25,9 +25,12 @@ using widely supported formats like PEM or DER.
"NIST P-256", "``'NIST P-256'``, ``'p256'``, ``'P-256'``, ``'prime256v1'``, ``'secp256r1'``" "NIST P-256", "``'NIST P-256'``, ``'p256'``, ``'P-256'``, ``'prime256v1'``, ``'secp256r1'``"
"NIST P-384", "``'NIST P-384'``, ``'p384'``, ``'P-384'``, ``'prime384v1'``, ``'secp384r1'``" "NIST P-384", "``'NIST P-384'``, ``'p384'``, ``'P-384'``, ``'prime384v1'``, ``'secp384r1'``"
"NIST P-521", "``'NIST P-521'``, ``'p521'``, ``'P-521'``, ``'prime521v1'``, ``'secp521r1'``" "NIST P-521", "``'NIST P-521'``, ``'p521'``, ``'P-521'``, ``'prime521v1'``, ``'secp521r1'``"
"Ed25519", "``'ed25519'``, ``'Ed25519'``"
For more information about each NIST curve see `FIPS 186-4`_, Section D.1.2. For more information about each NIST curve see `FIPS 186-4`_, Section D.1.2.
The Ed25519 curve is defined in RFC8032_.
The following example demonstrates how to generate a new ECC key, export it, The following example demonstrates how to generate a new ECC key, export it,
and subsequently reload it back into the application:: and subsequently reload it back into the application::
@ -42,11 +45,12 @@ and subsequently reload it back into the application::
>>> f = open('myprivatekey.pem','rt') >>> f = open('myprivatekey.pem','rt')
>>> key = ECC.import_key(f.read()) >>> key = ECC.import_key(f.read())
The ECC key can be used to perform or verify ECDSA signatures, using the module The ECC key can be used to perform or verify ECDSA signatures, using the modules
:mod:`Crypto.Signature.DSS`. :mod:`Crypto.Signature.DSS` (NIST curves only) or :mod:`Crypto.Signature.eddsa` (Ed25519 curve only).
.. _ECC: http://andrea.corbellini.name/2015/05/17/elliptic-curve-cryptography-a-gentle-introduction/ .. _ECC: http://andrea.corbellini.name/2015/05/17/elliptic-curve-cryptography-a-gentle-introduction/
.. _`FIPS 186-4`: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf .. _`FIPS 186-4`: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
.. _RFC8032: https://datatracker.ietf.org/doc/html/rfc8032
.. automodule:: Crypto.PublicKey.ECC .. automodule:: Crypto.PublicKey.ECC
:members: :members:

View file

@ -43,5 +43,7 @@ Available mechanisms
* :doc:`pkcs1_pss` * :doc:`pkcs1_pss`
* :doc:`eddsa`
* :doc:`dsa` * :doc:`dsa`

View file

@ -92,7 +92,7 @@ def wrap(private_key, key_oid, passphrase=None, protection=None,
| | value is 1. | | | value is 1. |
+------------------+-----------------------------------------------+ +------------------+-----------------------------------------------+
key_params (DER object): key_params (DER object or Null):
The algorithm parameters associated to the private key. The algorithm parameters associated to the private key.
It is required for algorithms like DSA, but not for others like RSA. It is required for algorithms like DSA, but not for others like RSA.
@ -106,9 +106,6 @@ def wrap(private_key, key_oid, passphrase=None, protection=None,
The PKCS#8-wrapped private key (possibly encrypted), as a byte string. The PKCS#8-wrapped private key (possibly encrypted), as a byte string.
""" """
if key_params is None:
key_params = DerNull()
# #
# PrivateKeyInfo ::= SEQUENCE { # PrivateKeyInfo ::= SEQUENCE {
# version Version, # version Version,
@ -117,12 +114,14 @@ def wrap(private_key, key_oid, passphrase=None, protection=None,
# attributes [0] IMPLICIT Attributes OPTIONAL # attributes [0] IMPLICIT Attributes OPTIONAL
# } # }
# #
if key_params is None:
algorithm = DerSequence([DerObjectId(key_oid)])
else:
algorithm = DerSequence([DerObjectId(key_oid), key_params])
pk_info = DerSequence([ pk_info = DerSequence([
0, 0,
DerSequence([ algorithm,
DerObjectId(key_oid),
key_params
]),
DerOctetString(private_key) DerOctetString(private_key)
]) ])
pk_info_der = pk_info.encode() pk_info_der = pk_info.encode()
@ -185,11 +184,12 @@ def unwrap(p8_private_key, passphrase=None):
if not found: if not found:
raise ValueError("Error decoding PKCS#8 (%s)" % error_str) raise ValueError("Error decoding PKCS#8 (%s)" % error_str)
pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4)) pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4, 5))
if len(pk_info) == 2 and not passphrase: if len(pk_info) == 2 and not passphrase:
raise ValueError("Not a valid clear PKCS#8 structure " raise ValueError("Not a valid clear PKCS#8 structure "
"(maybe it is encrypted?)") "(maybe it is encrypted?)")
# RFC5208, PKCS#8, version is v1(0)
# #
# PrivateKeyInfo ::= SEQUENCE { # PrivateKeyInfo ::= SEQUENCE {
# version Version, # version Version,
@ -197,22 +197,27 @@ def unwrap(p8_private_key, passphrase=None):
# privateKey PrivateKey, # privateKey PrivateKey,
# attributes [0] IMPLICIT Attributes OPTIONAL # attributes [0] IMPLICIT Attributes OPTIONAL
# } # }
# Version ::= INTEGER
if pk_info[0] != 0:
raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
# PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
# #
# EncryptedPrivateKeyInfo ::= SEQUENCE { # RFC5915, Asymmetric Key Package, version is v2(1)
# encryptionAlgorithm EncryptionAlgorithmIdentifier, #
# encryptedData EncryptedData # OneAsymmetricKey ::= SEQUENCE {
# version Version,
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
# privateKey PrivateKey,
# attributes [0] Attributes OPTIONAL,
# ...,
# [[2: publicKey [1] PublicKey OPTIONAL ]],
# ...
# } # }
# EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
# AlgorithmIdentifier ::= SEQUENCE { if pk_info[0] == 0:
# algorithm OBJECT IDENTIFIER, if len(pk_info) not in (3, 4):
# parameters ANY DEFINED BY algorithm OPTIONAL raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
# } elif pk_info[0] == 1:
if len(pk_info) not in (3, 4, 5):
raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
else:
raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
algo = DerSequence().decode(pk_info[1], nr_elements=(1, 2)) algo = DerSequence().decode(pk_info[1], nr_elements=(1, 2))
algo_oid = DerObjectId().decode(algo[0]).value algo_oid = DerObjectId().decode(algo[0]).value
@ -225,7 +230,9 @@ def unwrap(p8_private_key, passphrase=None):
except: except:
algo_params = algo[1] algo_params = algo[1]
# EncryptedData ::= OCTET STRING # PrivateKey ::= OCTET STRING
private_key = DerOctetString().decode(pk_info[2]).payload private_key = DerOctetString().decode(pk_info[2]).payload
# We ignore attributes and (for v2 only) publickey
return (algo_oid, private_key, algo_params) return (algo_oid, private_key, algo_params)

View file

@ -51,12 +51,12 @@ class IntegerBase(ABC):
pass pass
@abc.abstractmethod @abc.abstractmethod
def to_bytes(self, block_size=0): def to_bytes(self, block_size=0, byteorder='big'):
pass pass
@staticmethod @staticmethod
@abc.abstractmethod @abc.abstractmethod
def from_bytes(byte_string): def from_bytes(byte_string, byteorder='big'):
pass pass
# Relations # Relations
@ -228,7 +228,7 @@ class IntegerBase(ABC):
@abc.abstractmethod @abc.abstractmethod
def jacobi_symbol(a, n): def jacobi_symbol(a, n):
pass pass
@staticmethod @staticmethod
def _tonelli_shanks(n, p): def _tonelli_shanks(n, p):
"""Tonelli-shanks algorithm for computing the square root """Tonelli-shanks algorithm for computing the square root

View file

@ -7,9 +7,9 @@ class IntegerBase:
def __int__(self) -> int: ... def __int__(self) -> int: ...
def __str__(self) -> str: ... def __str__(self) -> str: ...
def __repr__(self) -> str: ... def __repr__(self) -> str: ...
def to_bytes(self, block_size: Optional[int]=0) -> bytes: ... def to_bytes(self, block_size: Optional[int]=0, byteorder: str= ...) -> bytes: ...
@staticmethod @staticmethod
def from_bytes(byte_string: bytes) -> IntegerBase: ... def from_bytes(byte_string: bytes, byteorder: Optional[str] = ...) -> IntegerBase: ...
def __eq__(self, term: object) -> bool: ... def __eq__(self, term: object) -> bool: ...
def __ne__(self, term: object) -> bool: ... def __ne__(self, term: object) -> bool: ...
def __lt__(self, term: Union[IntegerBase, int]) -> bool: ... def __lt__(self, term: Union[IntegerBase, int]) -> bool: ...

View file

@ -57,7 +57,14 @@ implementation = {"library": "custom", "api": backend}
class IntegerCustom(IntegerNative): class IntegerCustom(IntegerNative):
@staticmethod @staticmethod
def from_bytes(byte_string): def from_bytes(byte_string, byteorder='big'):
if byteorder == 'big':
pass
elif byteorder == 'little':
byte_string = bytearray(byte_string)
byte_string.reverse()
else:
raise ValueError("Incorrect byteorder")
return IntegerCustom(bytes_to_long(byte_string)) return IntegerCustom(bytes_to_long(byte_string))
def inplace_pow(self, exponent, modulus=None): def inplace_pow(self, exponent, modulus=None):

View file

@ -35,7 +35,7 @@ from Crypto.Util.py3compat import tobytes, is_native_int
from Crypto.Util._raw_api import (backend, load_lib, from Crypto.Util._raw_api import (backend, load_lib,
get_raw_buffer, get_c_string, get_raw_buffer, get_c_string,
null_pointer, create_string_buffer, null_pointer, create_string_buffer,
c_ulong, c_size_t) c_ulong, c_size_t, c_uint8_ptr)
from ._IntegerBase import IntegerBase from ._IntegerBase import IntegerBase
@ -225,7 +225,7 @@ class IntegerGMP(IntegerBase):
def __index__(self): def __index__(self):
return int(self) return int(self)
def to_bytes(self, block_size=0): def to_bytes(self, block_size=0, byteorder='big'):
"""Convert the number into a byte string. """Convert the number into a byte string.
This method encodes the number in network order and prepends This method encodes the number in network order and prepends
@ -236,6 +236,8 @@ class IntegerGMP(IntegerBase):
block_size : integer block_size : integer
The exact size the output byte string must have. The exact size the output byte string must have.
If zero, the string has the minimal length. If zero, the string has the minimal length.
byteorder : string
'big' for big-endian integers (default), 'little' for litte-endian.
:Returns: :Returns:
A byte string. A byte string.
:Raise ValueError: :Raise ValueError:
@ -252,6 +254,7 @@ class IntegerGMP(IntegerBase):
" of prescribed length") " of prescribed length")
buf = create_string_buffer(buf_len) buf = create_string_buffer(buf_len)
_gmp.mpz_export( _gmp.mpz_export(
buf, buf,
null_pointer, # Ignore countp null_pointer, # Ignore countp
@ -261,20 +264,39 @@ class IntegerGMP(IntegerBase):
c_size_t(0), # No nails c_size_t(0), # No nails
self._mpz_p) self._mpz_p)
return b'\x00' * max(0, block_size - buf_len) + get_raw_buffer(buf) result = b'\x00' * max(0, block_size - buf_len) + get_raw_buffer(buf)
if byteorder == 'big':
pass
elif byteorder == 'little':
result = bytearray(result)
result.reverse()
result = bytes(result)
else:
raise ValueError("Incorrect byteorder")
return result
@staticmethod @staticmethod
def from_bytes(byte_string): def from_bytes(byte_string, byteorder='big'):
"""Convert a byte string into a number. """Convert a byte string into a number.
:Parameters: :Parameters:
byte_string : byte string byte_string : byte string
The input number, encoded in network order. The input number, encoded in network order.
It can only be non-negative. It can only be non-negative.
byteorder : string
'big' for big-endian integers (default), 'little' for litte-endian.
:Return: :Return:
The ``Integer`` object carrying the same value as the input. The ``Integer`` object carrying the same value as the input.
""" """
result = IntegerGMP(0) result = IntegerGMP(0)
if byteorder == 'big':
pass
elif byteorder == 'little':
byte_string = bytearray(byte_string)
byte_string.reverse()
else:
raise ValueError("Incorrect byteorder")
_gmp.mpz_import( _gmp.mpz_import(
result._mpz_p, result._mpz_p,
c_size_t(len(byte_string)), # Amount of words to read c_size_t(len(byte_string)), # Amount of words to read
@ -282,7 +304,7 @@ class IntegerGMP(IntegerBase):
c_size_t(1), # Each word is 1 byte long c_size_t(1), # Each word is 1 byte long
0, # Endianess within a word - not relevant 0, # Endianess within a word - not relevant
c_size_t(0), # No nails c_size_t(0), # No nails
byte_string) c_uint8_ptr(byte_string))
return result return result
# Relations # Relations

View file

@ -62,16 +62,31 @@ class IntegerNative(IntegerBase):
def __index__(self): def __index__(self):
return int(self._value) return int(self._value)
def to_bytes(self, block_size=0): def to_bytes(self, block_size=0, byteorder='big'):
if self._value < 0: if self._value < 0:
raise ValueError("Conversion only valid for non-negative numbers") raise ValueError("Conversion only valid for non-negative numbers")
result = long_to_bytes(self._value, block_size) result = long_to_bytes(self._value, block_size)
if len(result) > block_size > 0: if len(result) > block_size > 0:
raise ValueError("Value too large to encode") raise ValueError("Value too large to encode")
if byteorder == 'big':
pass
elif byteorder == 'little':
result = bytearray(result)
result.reverse()
result = bytes(result)
else:
raise ValueError("Incorrect byteorder")
return result return result
@classmethod @classmethod
def from_bytes(cls, byte_string): def from_bytes(cls, byte_string, byteorder='big'):
if byteorder == 'big':
pass
elif byteorder == 'little':
byte_string = bytearray(byte_string)
byte_string.reverse()
else:
raise ValueError("Incorrect byteorder")
return cls(bytes_to_long(byte_string)) return cls(bytes_to_long(byte_string))
# Relations # Relations

File diff suppressed because it is too large Load diff

View file

@ -62,3 +62,4 @@ def construct(**kwargs: Union[str, int]) -> EccKey: ...
def import_key(encoded: Union[bytes, str], def import_key(encoded: Union[bytes, str],
passphrase: Optional[str]=None, passphrase: Optional[str]=None,
curve_name:Optional[str]=None) -> EccKey: ... curve_name:Optional[str]=None) -> EccKey: ...
def _import_ed25519_public_key(encoded: bytes) -> EccKey: ...

View file

@ -37,7 +37,7 @@ import struct
from Crypto import Random from Crypto import Random
from Crypto.Util.py3compat import tobytes, bord, tostr from Crypto.Util.py3compat import tobytes, bord, tostr
from Crypto.Util.asn1 import DerSequence from Crypto.Util.asn1 import DerSequence, DerNull
from Crypto.Math.Numbers import Integer from Crypto.Math.Numbers import Integer
from Crypto.Math.Primality import (test_probable_prime, from Crypto.Math.Primality import (test_probable_prime,
@ -339,19 +339,22 @@ class RsaKey(object):
if format == 'PEM' and protection is None: if format == 'PEM' and protection is None:
key_type = 'PRIVATE KEY' key_type = 'PRIVATE KEY'
binary_key = PKCS8.wrap(binary_key, oid, None) binary_key = PKCS8.wrap(binary_key, oid, None,
key_params=DerNull())
else: else:
key_type = 'ENCRYPTED PRIVATE KEY' key_type = 'ENCRYPTED PRIVATE KEY'
if not protection: if not protection:
protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC' protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'
binary_key = PKCS8.wrap(binary_key, oid, binary_key = PKCS8.wrap(binary_key, oid,
passphrase, protection) passphrase, protection,
key_params=DerNull())
passphrase = None passphrase = None
else: else:
key_type = "PUBLIC KEY" key_type = "PUBLIC KEY"
binary_key = _create_subject_public_key_info(oid, binary_key = _create_subject_public_key_info(oid,
DerSequence([self.n, DerSequence([self.n,
self.e]) self.e]),
DerNull()
) )
if format == 'DER': if format == 'DER':

View file

@ -60,17 +60,16 @@ def _expand_subject_public_key_info(encoded):
return algo_oid.value, spk, algo_params return algo_oid.value, spk, algo_params
def _create_subject_public_key_info(algo_oid, secret_key, params=None): def _create_subject_public_key_info(algo_oid, public_key, params):
if params is None: if params is None:
params = DerNull() algorithm = DerSequence([DerObjectId(algo_oid)])
else:
algorithm = DerSequence([DerObjectId(algo_oid), params])
spki = DerSequence([ spki = DerSequence([algorithm,
DerSequence([ DerBitString(public_key)
DerObjectId(algo_oid), ])
params]),
DerBitString(secret_key)
])
return spki.encode() return spki.encode()

View file

@ -39,6 +39,8 @@ from binascii import unhexlify
from Crypto.Util.py3compat import * from Crypto.Util.py3compat import *
from Crypto.IO import PKCS8 from Crypto.IO import PKCS8
from Crypto.Util.asn1 import DerNull
oid_key = '1.2.840.113549.1.1.1' oid_key = '1.2.840.113549.1.1.1'
# Original RSA key (in DER format) # Original RSA key (in DER format)
@ -407,7 +409,7 @@ class PKCS8_Decrypt(unittest.TestCase):
b("TestTest"), b("TestTest"),
protection=t[0], protection=t[0],
prot_params=params, prot_params=params,
key_params=None, key_params=DerNull(),
randfunc=rng) randfunc=rng)
self.assertEqual(wrapped, t[4]) self.assertEqual(wrapped, t[4])

View file

@ -114,27 +114,42 @@ class TestIntegerBase(unittest.TestCase):
v1 = Integer(0x17) v1 = Integer(0x17)
self.assertEqual(b("\x17"), v1.to_bytes()) self.assertEqual(b("\x17"), v1.to_bytes())
v2 = Integer(0xFFFF) v2 = Integer(0xFFFE)
self.assertEqual(b("\xFF\xFF"), v2.to_bytes()) self.assertEqual(b("\xFF\xFE"), v2.to_bytes())
self.assertEqual(b("\x00\xFF\xFF"), v2.to_bytes(3)) self.assertEqual(b("\x00\xFF\xFE"), v2.to_bytes(3))
self.assertRaises(ValueError, v2.to_bytes, 1) self.assertRaises(ValueError, v2.to_bytes, 1)
self.assertEqual(b("\xFE\xFF"), v2.to_bytes(byteorder='little'))
self.assertEqual(b("\xFE\xFF\x00"), v2.to_bytes(3, byteorder='little'))
v3 = Integer(-90) v3 = Integer(-90)
self.assertRaises(ValueError, v3.to_bytes) self.assertRaises(ValueError, v3.to_bytes)
self.assertRaises(ValueError, v3.to_bytes, byteorder='bittle')
def test_conversion_from_bytes(self): def test_conversion_from_bytes(self):
Integer = self.Integer Integer = self.Integer
v1 = Integer.from_bytes(b("\x00")) v1 = Integer.from_bytes(b"\x00")
self.assertTrue(isinstance(v1, Integer)) self.assertTrue(isinstance(v1, Integer))
self.assertEqual(0, v1) self.assertEqual(0, v1)
v2 = Integer.from_bytes(b("\x00\x00")) v2 = Integer.from_bytes(b"\x00\x01")
self.assertEqual(0, v2) self.assertEqual(1, v2)
v3 = Integer.from_bytes(b("\xFF\xFF")) v3 = Integer.from_bytes(b"\xFF\xFF")
self.assertEqual(0xFFFF, v3) self.assertEqual(0xFFFF, v3)
v4 = Integer.from_bytes(b"\x00\x01", 'big')
self.assertEqual(1, v4)
v5 = Integer.from_bytes(b"\x00\x01", byteorder='big')
self.assertEqual(1, v5)
v6 = Integer.from_bytes(b"\x00\x01", byteorder='little')
self.assertEqual(0x0100, v6)
self.assertRaises(ValueError, Integer.from_bytes, b'\x09', 'bittle')
def test_inequality(self): def test_inequality(self):
# Test Integer!=Integer and Integer!=int # Test Integer!=Integer and Integer!=int
v1, v2, v3, v4 = self.Integers(89, 89, 90, -8) v1, v2, v3, v4 = self.Integers(89, 89, 90, -8)

View file

@ -24,31 +24,29 @@
"""Self-test for public-key crypto""" """Self-test for public-key crypto"""
__revision__ = "$Id$" import unittest
from Crypto.SelfTest.PublicKey import (test_DSA, test_RSA,
test_ECC_NIST, test_ECC_25519,
test_import_DSA, test_import_RSA,
test_import_ECC, test_ElGamal)
import os
def get_tests(config={}): def get_tests(config={}):
tests = [] tests = []
from Crypto.SelfTest.PublicKey import test_DSA; tests += test_DSA.get_tests(config=config) tests += test_DSA.get_tests(config=config)
from Crypto.SelfTest.PublicKey import test_RSA; tests += test_RSA.get_tests(config=config) tests += test_RSA.get_tests(config=config)
from Crypto.SelfTest.PublicKey import test_ECC; tests += test_ECC.get_tests(config=config) tests += test_ECC_NIST.get_tests(config=config)
tests += test_ECC_25519.get_tests(config=config)
from Crypto.SelfTest.PublicKey import test_import_DSA tests += test_import_DSA.get_tests(config=config)
tests +=test_import_DSA.get_tests(config=config)
from Crypto.SelfTest.PublicKey import test_import_RSA
tests += test_import_RSA.get_tests(config=config) tests += test_import_RSA.get_tests(config=config)
from Crypto.SelfTest.PublicKey import test_import_ECC
tests += test_import_ECC.get_tests(config=config) tests += test_import_ECC.get_tests(config=config)
from Crypto.SelfTest.PublicKey import test_ElGamal; tests += test_ElGamal.get_tests(config=config) tests += test_ElGamal.get_tests(config=config)
return tests return tests
if __name__ == '__main__':
import unittest
suite = lambda: unittest.TestSuite(get_tests())
unittest.main(defaultTest='suite')
# vim:set ts=4 sw=4 sts=4 expandtab: if __name__ == '__main__':
def suite():
return unittest.TestSuite(get_tests())
unittest.main(defaultTest='suite')

View file

@ -0,0 +1,327 @@
# ===================================================================
#
# Copyright (c) 2022, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
import unittest
from binascii import unhexlify
from Crypto.SelfTest.st_common import list_test_cases
from Crypto.SelfTest.loader import load_test_vectors
from Crypto.PublicKey import ECC
from Crypto.PublicKey.ECC import EccPoint, _curves, EccKey
from Crypto.Math.Numbers import Integer
from Crypto.Hash import SHAKE128
class TestEccPoint_Ed25519(unittest.TestCase):
Gxy = {"x": 15112221349535400772501151409588531511454012693041857206046113283949847762202,
"y": 46316835694926478169428394003475163141307993866256225615783033603165251855960}
G2xy = {"x": 24727413235106541002554574571675588834622768167397638456726423682521233608206,
"y": 15549675580280190176352668710449542251549572066445060580507079593062643049417}
G3xy = {"x": 46896733464454938657123544595386787789046198280132665686241321779790909858396,
"y": 8324843778533443976490377120369201138301417226297555316741202210403726505172}
pointG = EccPoint(Gxy['x'], Gxy['y'], curve="Ed25519")
pointG2 = EccPoint(G2xy['x'], G2xy['y'], curve="Ed25519")
pointG3 = EccPoint(G3xy['x'], G3xy['y'], curve="Ed25519")
def test_init_xy(self):
EccPoint(self.Gxy['x'], self.Gxy['y'], curve="Ed25519")
# Neutral point
pai = EccPoint(0, 1, curve="Ed25519")
self.assertEqual(pai.x, 0)
self.assertEqual(pai.y, 1)
self.assertEqual(pai.xy, (0, 1))
# G
bp = self.pointG.copy()
self.assertEqual(bp.x, 15112221349535400772501151409588531511454012693041857206046113283949847762202)
self.assertEqual(bp.y, 46316835694926478169428394003475163141307993866256225615783033603165251855960)
self.assertEqual(bp.xy, (bp.x, bp.y))
# 2G
bp2 = self.pointG2.copy()
self.assertEqual(bp2.x, 24727413235106541002554574571675588834622768167397638456726423682521233608206)
self.assertEqual(bp2.y, 15549675580280190176352668710449542251549572066445060580507079593062643049417)
self.assertEqual(bp2.xy, (bp2.x, bp2.y))
# 5G
EccPoint(x=33467004535436536005251147249499675200073690106659565782908757308821616914995,
y=43097193783671926753355113395909008640284023746042808659097434958891230611693,
curve="Ed25519")
# Catch if point is not on the curve
self.assertRaises(ValueError, EccPoint, 34, 35, curve="Ed25519")
def test_set(self):
pointW = EccPoint(0, 1, curve="Ed25519")
pointW.set(self.pointG)
self.assertEqual(pointW.x, self.pointG.x)
self.assertEqual(pointW.y, self.pointG.y)
def test_copy(self):
pointW = self.pointG.copy()
self.assertEqual(pointW.x, self.pointG.x)
self.assertEqual(pointW.y, self.pointG.y)
def test_equal(self):
pointH = self.pointG.copy()
pointI = self.pointG2.copy()
self.assertEqual(self.pointG, pointH)
self.assertNotEqual(self.pointG, pointI)
def test_pai(self):
pai = EccPoint(0, 1, curve="Ed25519")
self.failUnless(pai.is_point_at_infinity())
self.assertEqual(pai, pai.point_at_infinity())
def test_negate(self):
negG = -self.pointG
sum = self.pointG + negG
self.failUnless(sum.is_point_at_infinity())
def test_addition(self):
self.assertEqual(self.pointG + self.pointG2, self.pointG3)
self.assertEqual(self.pointG2 + self.pointG, self.pointG3)
self.assertEqual(self.pointG2 + self.pointG.point_at_infinity(), self.pointG2)
self.assertEqual(self.pointG.point_at_infinity() + self.pointG2, self.pointG2)
G5 = self.pointG2 + self.pointG3
self.assertEqual(G5.x, 33467004535436536005251147249499675200073690106659565782908757308821616914995)
self.assertEqual(G5.y, 43097193783671926753355113395909008640284023746042808659097434958891230611693)
def test_inplace_addition(self):
pointH = self.pointG.copy()
pointH += self.pointG
self.assertEqual(pointH, self.pointG2)
pointH += self.pointG
self.assertEqual(pointH, self.pointG3)
pointH += self.pointG.point_at_infinity()
self.assertEqual(pointH, self.pointG3)
def test_doubling(self):
pointH = self.pointG.copy()
pointH.double()
self.assertEqual(pointH.x, self.pointG2.x)
self.assertEqual(pointH.y, self.pointG2.y)
# 2*0
pai = self.pointG.point_at_infinity()
pointR = pai.copy()
pointR.double()
self.assertEqual(pointR, pai)
def test_scalar_multiply(self):
d = 0
pointH = d * self.pointG
self.assertEqual(pointH.x, 0)
self.assertEqual(pointH.y, 1)
d = 1
pointH = d * self.pointG
self.assertEqual(pointH.x, self.pointG.x)
self.assertEqual(pointH.y, self.pointG.y)
d = 2
pointH = d * self.pointG
self.assertEqual(pointH.x, self.pointG2.x)
self.assertEqual(pointH.y, self.pointG2.y)
d = 3
pointH = d * self.pointG
self.assertEqual(pointH.x, self.pointG3.x)
self.assertEqual(pointH.y, self.pointG3.y)
d = 4
pointH = d * self.pointG
self.assertEqual(pointH.x, 14582954232372986451776170844943001818709880559417862259286374126315108956272)
self.assertEqual(pointH.y, 32483318716863467900234833297694612235682047836132991208333042722294373421359)
d = 5
pointH = d * self.pointG
self.assertEqual(pointH.x, 33467004535436536005251147249499675200073690106659565782908757308821616914995)
self.assertEqual(pointH.y, 43097193783671926753355113395909008640284023746042808659097434958891230611693)
d = 10
pointH = d * self.pointG
self.assertEqual(pointH.x, 43500613248243327786121022071801015118933854441360174117148262713429272820047)
self.assertEqual(pointH.y, 45005105423099817237495816771148012388779685712352441364231470781391834741548)
d = 20
pointH = d * self.pointG
self.assertEqual(pointH.x, 46694936775300686710656303283485882876784402425210400817529601134760286812591)
self.assertEqual(pointH.y, 8786390172762935853260670851718824721296437982862763585171334833968259029560)
d = 255
pointH = d * self.pointG
self.assertEqual(pointH.x, 36843863416400016952258312492144504209624961884991522125275155377549541182230)
self.assertEqual(pointH.y, 22327030283879720808995671630924669697661065034121040761798775626517750047180)
d = 256
pointH = d * self.pointG
self.assertEqual(pointH.x, 42740085206947573681423002599456489563927820004573071834350074001818321593686)
self.assertEqual(pointH.y, 6935684722522267618220753829624209639984359598320562595061366101608187623111)
def test_sizes(self):
self.assertEqual(self.pointG.size_in_bits(), 255)
self.assertEqual(self.pointG.size_in_bytes(), 32)
class TestEccKey_Ed25519(unittest.TestCase):
def test_private_key(self):
seed = unhexlify("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
Px = 38815646466658113194383306759739515082307681141926459231621296960732224964046
Py = 11903303657706407974989296177215005343713679411332034699907763981919547054807
key = EccKey(curve="Ed25519", seed=seed)
self.assertEqual(key.seed, seed)
self.assertEqual(key.d, 36144925721603087658594284515452164870581325872720374094707712194495455132720)
self.assertTrue(key.has_private())
self.assertEqual(key.pointQ.x, Px)
self.assertEqual(key.pointQ.y, Py)
point = EccPoint(Px, Py, "ed25519")
key = EccKey(curve="Ed25519", seed=seed, point=point)
self.assertEqual(key.d, 36144925721603087658594284515452164870581325872720374094707712194495455132720)
self.assertTrue(key.has_private())
self.assertEqual(key.pointQ, point)
# Other names
key = EccKey(curve="ed25519", seed=seed)
# Must not accept d parameter
self.assertRaises(ValueError, EccKey, curve="ed25519", d=1)
def test_public_key(self):
point = EccPoint(_curves['ed25519'].Gx, _curves['ed25519'].Gy, curve='ed25519')
key = EccKey(curve="ed25519", point=point)
self.assertFalse(key.has_private())
self.assertEqual(key.pointQ, point)
def test_public_key_derived(self):
priv_key = EccKey(curve="ed25519", seed=b'H'*32)
pub_key = priv_key.public_key()
self.assertFalse(pub_key.has_private())
self.assertEqual(priv_key.pointQ, pub_key.pointQ)
def test_invalid_seed(self):
self.assertRaises(ValueError, lambda: EccKey(curve="ed25519", seed=b'H' * 31))
def test_equality(self):
private_key = ECC.construct(seed=b'H'*32, curve="Ed25519")
private_key2 = ECC.construct(seed=b'H'*32, curve="ed25519")
private_key3 = ECC.construct(seed=b'C'*32, curve="Ed25519")
public_key = private_key.public_key()
public_key2 = private_key2.public_key()
public_key3 = private_key3.public_key()
self.assertEqual(private_key, private_key2)
self.assertNotEqual(private_key, private_key3)
self.assertEqual(public_key, public_key2)
self.assertNotEqual(public_key, public_key3)
self.assertNotEqual(public_key, private_key)
class TestEccModule_Ed25519(unittest.TestCase):
def test_generate(self):
key = ECC.generate(curve="Ed25519")
self.assertTrue(key.has_private())
point = EccPoint(_curves['Ed25519'].Gx, _curves['Ed25519'].Gy, curve="Ed25519") * key.d
self.assertEqual(key.pointQ, point)
# Always random
key2 = ECC.generate(curve="Ed25519")
self.assertNotEqual(key, key2)
# Other names
ECC.generate(curve="Ed25519")
# Random source
key1 = ECC.generate(curve="Ed25519", randfunc=SHAKE128.new().read)
key2 = ECC.generate(curve="Ed25519", randfunc=SHAKE128.new().read)
self.assertEqual(key1, key2)
def test_construct(self):
seed = unhexlify("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
Px = 38815646466658113194383306759739515082307681141926459231621296960732224964046
Py = 11903303657706407974989296177215005343713679411332034699907763981919547054807
d = 36144925721603087658594284515452164870581325872720374094707712194495455132720
point = EccPoint(Px, Py, curve="Ed25519")
# Private key only
key = ECC.construct(curve="Ed25519", seed=seed)
self.assertEqual(key.pointQ, point)
self.assertTrue(key.has_private())
# Public key only
key = ECC.construct(curve="Ed25519", point_x=Px, point_y=Py)
self.assertEqual(key.pointQ, point)
self.assertFalse(key.has_private())
# Private and public key
key = ECC.construct(curve="Ed25519", seed=seed, point_x=Px, point_y=Py)
self.assertEqual(key.pointQ, point)
self.assertTrue(key.has_private())
# Other names
key = ECC.construct(curve="ed25519", seed=seed)
def test_negative_construct(self):
coord = dict(point_x=10, point_y=4)
coordG = dict(point_x=_curves['ed25519'].Gx, point_y=_curves['ed25519'].Gy)
self.assertRaises(ValueError, ECC.construct, curve="Ed25519", **coord)
self.assertRaises(ValueError, ECC.construct, curve="Ed25519", d=2, **coordG)
self.assertRaises(ValueError, ECC.construct, curve="Ed25519", seed=b'H'*31)
def get_tests(config={}):
tests = []
tests += list_test_cases(TestEccPoint_Ed25519)
tests += list_test_cases(TestEccKey_Ed25519)
tests += list_test_cases(TestEccModule_Ed25519)
return tests
if __name__ == '__main__':
def suite():
return unittest.TestSuite(get_tests())
unittest.main(defaultTest='suite')

View file

@ -29,6 +29,8 @@
# =================================================================== # ===================================================================
import unittest import unittest
from binascii import unhexlify
from Crypto.SelfTest.st_common import list_test_cases from Crypto.SelfTest.st_common import list_test_cases
from Crypto.SelfTest.loader import load_test_vectors from Crypto.SelfTest.loader import load_test_vectors
@ -190,7 +192,7 @@ class TestEccPoint_NIST_P192(unittest.TestCase):
self.assertEqual(pointR.x, pointRx) self.assertEqual(pointR.x, pointRx)
self.assertEqual(pointR.y, pointRy) self.assertEqual(pointR.y, pointRy)
def test_joing_scalar_multiply(self): def test_joint_scalar_multiply(self):
d = 0xa78a236d60baec0c5dd41b33a542463a8255391af64c74ee d = 0xa78a236d60baec0c5dd41b33a542463a8255391af64c74ee
e = 0xc4be3d53ec3089e71e4de8ceab7cce889bc393cd85b972bc e = 0xc4be3d53ec3089e71e4de8ceab7cce889bc393cd85b972bc
pointRx = 0x019f64eed8fa9b72b7dfea82c17c9bfa60ecb9e1778b5bde pointRx = 0x019f64eed8fa9b72b7dfea82c17c9bfa60ecb9e1778b5bde
@ -993,6 +995,9 @@ class TestEccKey_P256(unittest.TestCase):
key = EccKey(curve="secp256r1", d=1) key = EccKey(curve="secp256r1", d=1)
key = EccKey(curve="prime256v1", d=1) key = EccKey(curve="prime256v1", d=1)
# Must not accept d parameter
self.assertRaises(ValueError, EccKey, curve="p256", seed=b'H'*32)
def test_public_key(self): def test_public_key(self):
point = EccPoint(_curves['p256'].Gx, _curves['p256'].Gy) point = EccPoint(_curves['p256'].Gx, _curves['p256'].Gy)

View file

@ -148,6 +148,14 @@ def create_ref_keys_p521():
return (ECC.construct(curve="P-521", d=private_key_d), return (ECC.construct(curve="P-521", d=private_key_d),
ECC.construct(curve="P-521", point_x=public_key_x, point_y=public_key_y)) ECC.construct(curve="P-521", point_x=public_key_x, point_y=public_key_y))
def create_ref_keys_ed25519():
key_lines = load_file("ecc_ed25519.txt").splitlines()
seed = compact(key_lines[5:8])
key = ECC.construct(curve="Ed25519", seed=seed)
return (key, key.public_key())
# Create reference key pair # Create reference key pair
# ref_private, ref_public = create_ref_keys_p521() # ref_private, ref_public = create_ref_keys_p521()
@ -199,10 +207,10 @@ class TestImport_P192(unittest.TestCase):
key = ECC.import_key(key_file, curve_name='P192') key = ECC.import_key(key_file, curve_name='P192')
self.assertEqual(self.ref_public, key) self.assertEqual(self.ref_public, key)
def test_import_private_der(self): def test_import_rfc5915_der(self):
key_file = load_file("ecc_p192_private.der") key_file = load_file("ecc_p192_private.der")
key = ECC._import_private_der(key_file, None) key = ECC._import_rfc5915_der(key_file, None)
self.assertEqual(self.ref_private, key) self.assertEqual(self.ref_private, key)
key = ECC._import_der(key_file, None) key = ECC._import_der(key_file, None)
@ -309,10 +317,10 @@ class TestImport_P224(unittest.TestCase):
key = ECC.import_key(key_file, curve_name='P224') key = ECC.import_key(key_file, curve_name='P224')
self.assertEqual(self.ref_public, key) self.assertEqual(self.ref_public, key)
def test_import_private_der(self): def test_import_rfc5915_der(self):
key_file = load_file("ecc_p224_private.der") key_file = load_file("ecc_p224_private.der")
key = ECC._import_private_der(key_file, None) key = ECC._import_rfc5915_der(key_file, None)
self.assertEqual(self.ref_private, key) self.assertEqual(self.ref_private, key)
key = ECC._import_der(key_file, None) key = ECC._import_der(key_file, None)
@ -419,10 +427,10 @@ class TestImport_P256(unittest.TestCase):
key = ECC.import_key(key_file, curve_name='P256') key = ECC.import_key(key_file, curve_name='P256')
self.assertEqual(self.ref_public, key) self.assertEqual(self.ref_public, key)
def test_import_private_der(self): def test_import_rfc5915_der(self):
key_file = load_file("ecc_p256_private.der") key_file = load_file("ecc_p256_private.der")
key = ECC._import_private_der(key_file, None) key = ECC._import_rfc5915_der(key_file, None)
self.assertEqual(self.ref_private, key) self.assertEqual(self.ref_private, key)
key = ECC._import_der(key_file, None) key = ECC._import_der(key_file, None)
@ -559,10 +567,10 @@ class TestImport_P384(unittest.TestCase):
key = ECC.import_key(key_file, curve_name='P384') key = ECC.import_key(key_file, curve_name='P384')
self.assertEqual(self.ref_public, key) self.assertEqual(self.ref_public, key)
def test_import_private_der(self): def test_import_rfc5915_der(self):
key_file = load_file("ecc_p384_private.der") key_file = load_file("ecc_p384_private.der")
key = ECC._import_private_der(key_file, None) key = ECC._import_rfc5915_der(key_file, None)
self.assertEqual(self.ref_private, key) self.assertEqual(self.ref_private, key)
key = ECC._import_der(key_file, None) key = ECC._import_der(key_file, None)
@ -694,10 +702,10 @@ class TestImport_P521(unittest.TestCase):
key = ECC.import_key(key_file, curve_name='P521') key = ECC.import_key(key_file, curve_name='P521')
self.assertEqual(self.ref_public, key) self.assertEqual(self.ref_public, key)
def test_import_private_der(self): def test_import_rfc5915_der(self):
key_file = load_file("ecc_p521_private.der") key_file = load_file("ecc_p521_private.der")
key = ECC._import_private_der(key_file, None) key = ECC._import_rfc5915_der(key_file, None)
self.assertEqual(self.ref_private, key) self.assertEqual(self.ref_private, key)
key = ECC._import_der(key_file, None) key = ECC._import_der(key_file, None)
@ -840,10 +848,10 @@ class TestExport_P192(unittest.TestCase):
value = extract_bitstring_from_spki(key_file_compressed_ref) value = extract_bitstring_from_spki(key_file_compressed_ref)
self.assertEqual(value, encoded) self.assertEqual(value, encoded)
def test_export_private_der(self): def test_export_rfc5915_private_der(self):
key_file = load_file("ecc_p192_private.der") key_file = load_file("ecc_p192_private.der")
encoded = self.ref_private._export_private_der() encoded = self.ref_private._export_rfc5915_private_der()
self.assertEqual(key_file, encoded) self.assertEqual(key_file, encoded)
# --- # ---
@ -1094,10 +1102,10 @@ class TestExport_P224(unittest.TestCase):
value = extract_bitstring_from_spki(key_file_compressed_ref) value = extract_bitstring_from_spki(key_file_compressed_ref)
self.assertEqual(value, encoded) self.assertEqual(value, encoded)
def test_export_private_der(self): def test_export_rfc5915_private_der(self):
key_file = load_file("ecc_p224_private.der") key_file = load_file("ecc_p224_private.der")
encoded = self.ref_private._export_private_der() encoded = self.ref_private._export_rfc5915_private_der()
self.assertEqual(key_file, encoded) self.assertEqual(key_file, encoded)
# --- # ---
@ -1348,10 +1356,10 @@ class TestExport_P256(unittest.TestCase):
value = extract_bitstring_from_spki(key_file_compressed_ref) value = extract_bitstring_from_spki(key_file_compressed_ref)
self.assertEqual(value, encoded) self.assertEqual(value, encoded)
def test_export_private_der(self): def test_export_rfc5915_private_der(self):
key_file = load_file("ecc_p256_private.der") key_file = load_file("ecc_p256_private.der")
encoded = self.ref_private._export_private_der() encoded = self.ref_private._export_rfc5915_private_der()
self.assertEqual(key_file, encoded) self.assertEqual(key_file, encoded)
# --- # ---
@ -1627,10 +1635,10 @@ class TestExport_P384(unittest.TestCase):
value = extract_bitstring_from_spki(key_file_compressed_ref) value = extract_bitstring_from_spki(key_file_compressed_ref)
self.assertEqual(value, encoded) self.assertEqual(value, encoded)
def test_export_private_der(self): def test_export_rfc5915_private_der(self):
key_file = load_file("ecc_p384_private.der") key_file = load_file("ecc_p384_private.der")
encoded = self.ref_private._export_private_der() encoded = self.ref_private._export_rfc5915_private_der()
self.assertEqual(key_file, encoded) self.assertEqual(key_file, encoded)
# --- # ---
@ -1911,10 +1919,10 @@ class TestExport_P521(unittest.TestCase):
value = extract_bitstring_from_spki(key_file_compressed_ref) value = extract_bitstring_from_spki(key_file_compressed_ref)
self.assertEqual(value, encoded) self.assertEqual(value, encoded)
def test_export_private_der(self): def test_export_rfc5915_private_der(self):
key_file = load_file("ecc_p521_private.der") key_file = load_file("ecc_p521_private.der")
encoded = self.ref_private._export_private_der() encoded = self.ref_private._export_rfc5915_private_der()
self.assertEqual(key_file, encoded) self.assertEqual(key_file, encoded)
# --- # ---
@ -2156,6 +2164,231 @@ vv6oYkMIIi7r5oQWAiQDrR2mlrrFDL9V7GH/r8SWQw==
self.assertEqual(low16, 0x9643) self.assertEqual(low16, 0x9643)
class TestImport_Ed25519(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(TestImport_Ed25519, self).__init__(*args, **kwargs)
self.ref_private, self.ref_public = create_ref_keys_ed25519()
def test_import_public_der(self):
key_file = load_file("ecc_ed25519_public.der")
key = ECC._import_subjectPublicKeyInfo(key_file)
self.assertEqual(self.ref_public, key)
key = ECC._import_der(key_file, None)
self.assertEqual(self.ref_public, key)
key = ECC.import_key(key_file)
self.assertEqual(self.ref_public, key)
def test_import_pkcs8_der(self):
key_file = load_file("ecc_ed25519_private.der")
key = ECC._import_der(key_file, None)
self.assertEqual(self.ref_private, key)
key = ECC.import_key(key_file)
self.assertEqual(self.ref_private, key)
def test_import_private_pkcs8_encrypted_1(self):
key_file = load_file("ecc_ed25519_private_p8.der")
key = ECC._import_der(key_file, "secret")
self.assertEqual(self.ref_private, key)
key = ECC.import_key(key_file, "secret")
self.assertEqual(self.ref_private, key)
def test_import_private_pkcs8_encrypted_2(self):
key_file = load_file("ecc_ed25519_private_p8.pem")
key = ECC.import_key(key_file, "secret")
self.assertEqual(self.ref_private, key)
def test_import_x509_der(self):
key_file = load_file("ecc_ed25519_x509.der")
key = ECC._import_der(key_file, None)
self.assertEqual(self.ref_public, key)
key = ECC.import_key(key_file)
self.assertEqual(self.ref_public, key)
def test_import_public_pem(self):
key_file = load_file("ecc_ed25519_public.pem")
key = ECC.import_key(key_file)
self.assertEqual(self.ref_public, key)
def test_import_private_pem(self):
key_file = load_file("ecc_ed25519_private.pem")
key = ECC.import_key(key_file)
self.assertEqual(self.ref_private, key)
def test_import_private_pem_encrypted(self):
for algo in "des3", "aes128", "aes192", "aes256":
key_file = load_file("ecc_ed25519_private_enc_%s.pem" % algo)
key = ECC.import_key(key_file, "secret")
self.assertEqual(self.ref_private, key)
key = ECC.import_key(tostr(key_file), b"secret")
self.assertEqual(self.ref_private, key)
def test_import_x509_pem(self):
key_file = load_file("ecc_ed25519_x509.pem")
key = ECC.import_key(key_file)
self.assertEqual(self.ref_public, key)
def test_import_openssh_public(self):
key_file = load_file("ecc_ed25519_public_openssh.txt")
key = ECC._import_openssh_public(key_file)
self.failIf(key.has_private())
key = ECC.import_key(key_file)
self.failIf(key.has_private())
def test_import_openssh_private_clear(self):
key_file = load_file("ecc_ed25519_private_openssh.pem")
key = ECC.import_key(key_file)
def test_import_openssh_private_password(self):
key_file = load_file("ecc_p521_private_openssh_pwd.pem")
key = ECC.import_key(key_file, b"password")
class TestExport_Ed25519(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(TestExport_Ed25519, self).__init__(*args, **kwargs)
self.ref_private, self.ref_public = create_ref_keys_ed25519()
def test_export_public_der(self):
key_file = load_file("ecc_ed25519_public.der")
encoded = self.ref_public._export_subjectPublicKeyInfo(True)
self.assertEqual(key_file, encoded)
encoded = self.ref_public.export_key(format="DER")
self.assertEqual(key_file, encoded)
encoded = self.ref_public.export_key(format="DER", compress=False)
self.assertEqual(key_file, encoded)
def test_export_public_sec1(self):
self.assertRaises(ValueError, self.ref_public.export_key, format="SEC1")
def test_export_private_pkcs8_clear(self):
key_file = load_file("ecc_ed25519_private.der")
encoded = self.ref_private._export_pkcs8()
self.assertEqual(key_file, encoded)
# ---
encoded = self.ref_private.export_key(format="DER")
self.assertEqual(key_file, encoded)
self.assertRaises(ValueError, self.ref_private.export_key,
format="DER", use_pkcs8=False)
def test_export_private_pkcs8_encrypted(self):
encoded = self.ref_private._export_pkcs8(passphrase="secret",
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC")
# This should prove that the output is password-protected
self.assertRaises(ValueError, ECC._import_pkcs8, encoded, None)
decoded = ECC._import_pkcs8(encoded, "secret")
self.assertEqual(self.ref_private, decoded)
# ---
encoded = self.ref_private.export_key(format="DER",
passphrase="secret",
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC")
decoded = ECC.import_key(encoded, "secret")
self.assertEqual(self.ref_private, decoded)
def test_export_public_pem(self):
key_file_ref = load_file("ecc_ed25519_public.pem", "rt").strip()
key_file = self.ref_public.export_key(format="PEM").strip()
self.assertEqual(key_file_ref, key_file)
def test_export_private_pem_clear(self):
key_file = load_file("ecc_ed25519_private.pem", "rt").strip()
encoded = self.ref_private.export_key(format="PEM").strip()
self.assertEqual(key_file, encoded)
def test_export_private_pem_encrypted(self):
encoded = self.ref_private.export_key(format="PEM",
passphrase=b"secret",
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC")
# This should prove that the output is password-protected
self.assertRaises(ValueError, ECC.import_key, encoded)
assert "ENCRYPTED PRIVATE KEY" in encoded
decoded = ECC.import_key(encoded, "secret")
self.assertEqual(self.ref_private, decoded)
def test_export_openssh(self):
key_file = load_file("ecc_ed25519_public_openssh.txt", "rt")
public_key = ECC.import_key(key_file)
key_file = " ".join(key_file.split(' ')[:2]) # remove comment
encoded = public_key._export_openssh(False)
self.assertEqual(key_file, encoded.strip())
encoded = public_key.export_key(format="OpenSSH")
self.assertEqual(key_file, encoded.strip())
def test_prng(self):
# Test that password-protected containers use the provided PRNG
encoded1 = self.ref_private.export_key(format="PEM",
passphrase="secret",
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC",
randfunc=get_fixed_prng())
encoded2 = self.ref_private.export_key(format="PEM",
passphrase="secret",
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC",
randfunc=get_fixed_prng())
self.assertEqual(encoded1, encoded2)
def test_byte_or_string_passphrase(self):
encoded1 = self.ref_private.export_key(format="PEM",
passphrase="secret",
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC",
randfunc=get_fixed_prng())
encoded2 = self.ref_private.export_key(format="PEM",
passphrase=b"secret",
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC",
randfunc=get_fixed_prng())
self.assertEqual(encoded1, encoded2)
def test_error_params1(self):
# Unknown format
self.assertRaises(ValueError, self.ref_private.export_key, format="XXX")
# Missing 'protection' parameter when PKCS#8 is used
self.assertRaises(ValueError, self.ref_private.export_key, format="PEM",
passphrase="secret")
# Empty password
self.assertRaises(ValueError, self.ref_private.export_key, format="PEM",
passphrase="", use_pkcs8=False)
self.assertRaises(ValueError, self.ref_private.export_key, format="PEM",
passphrase="",
protection="PBKDF2WithHMAC-SHA1AndAES128-CBC")
# No private keys with OpenSSH
self.assertRaises(ValueError, self.ref_private.export_key, format="OpenSSH",
passphrase="secret")
def get_tests(config={}): def get_tests(config={}):
tests = [] tests = []
tests += list_test_cases(TestImport) tests += list_test_cases(TestImport)
@ -2165,12 +2398,15 @@ def get_tests(config={}):
tests += list_test_cases(TestImport_P256) tests += list_test_cases(TestImport_P256)
tests += list_test_cases(TestImport_P384) tests += list_test_cases(TestImport_P384)
tests += list_test_cases(TestImport_P521) tests += list_test_cases(TestImport_P521)
tests += list_test_cases(TestImport_Ed25519)
tests += list_test_cases(TestExport_P192) tests += list_test_cases(TestExport_P192)
tests += list_test_cases(TestExport_P224) tests += list_test_cases(TestExport_P224)
tests += list_test_cases(TestExport_P256) tests += list_test_cases(TestExport_P256)
tests += list_test_cases(TestExport_P384) tests += list_test_cases(TestExport_P384)
tests += list_test_cases(TestExport_P521) tests += list_test_cases(TestExport_P521)
tests += list_test_cases(TestExport_Ed25519)
except MissingTestVectorException: except MissingTestVectorException:
pass pass
return tests return tests

View file

@ -22,18 +22,20 @@
"""Self-test for signature modules""" """Self-test for signature modules"""
import os import unittest
from . import test_pkcs1_15, test_pss, test_dss, test_eddsa
def get_tests(config={}): def get_tests(config={}):
tests = [] tests = []
from . import test_pkcs1_15; tests += test_pkcs1_15.get_tests(config=config) tests += test_pkcs1_15.get_tests(config=config)
from . import test_pss; tests += test_pss.get_tests(config=config) tests += test_pss.get_tests(config=config)
from . import test_dss; tests += test_dss.get_tests(config=config) tests += test_dss.get_tests(config=config)
tests += test_eddsa.get_tests(config=config)
return tests return tests
if __name__ == '__main__':
import unittest
suite = lambda: unittest.TestSuite(get_tests())
unittest.main(defaultTest='suite')
# vim:set ts=4 sw=4 sts=4 expandtab: if __name__ == '__main__':
def suite():
return unittest.TestSuite(get_tests())
unittest.main(defaultTest='suite')

View file

@ -251,6 +251,10 @@ class FIPS_ECDSA_Tests(unittest.TestCase):
self.assertRaises(ValueError, signer.sign, hash_obj) self.assertRaises(ValueError, signer.sign, hash_obj)
self.assertRaises(ValueError, signer.verify, hash_obj, b"\x00" * 40) self.assertRaises(ValueError, signer.verify, hash_obj, b"\x00" * 40)
def test_negative_eddsa_key(self):
key = ECC.generate(curve="ed25519")
self.assertRaises(ValueError, DSS.new, key, 'fips-186-3')
def test_sign_verify(self): def test_sign_verify(self):
"""Verify public/private method""" """Verify public/private method"""

View file

@ -0,0 +1,355 @@
#
# Copyright (c) 2022, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
import unittest
from binascii import unhexlify
from Crypto.PublicKey import ECC
from Crypto.Signature import eddsa
from Crypto.Hash import SHA512
from Crypto.SelfTest.st_common import list_test_cases
from Crypto.SelfTest.loader import load_test_vectors_wycheproof
from Crypto.Util.number import bytes_to_long
rfc8032_tv_str = (
# 7.1 Ed25519
(
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
"",
None,
"",
"e5564300c360ac729086e2cc806e828a"
"84877f1eb8e5d974d873e06522490155"
"5fb8821590a33bacc61e39701cf9b46b"
"d25bf5f0595bbe24655141438e7a100b"
),
(
"4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
"3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
"72",
None,
"",
"92a009a9f0d4cab8720e820b5f642540"
"a2b27b5416503f8fb3762223ebdb69da"
"085ac1e43e15996e458f3613d0f11d8c"
"387b2eaeb4302aeeb00d291612bb0c00"
),
(
"c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
"fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
"af82",
None,
"",
"6291d657deec24024827e69c3abe01a3"
"0ce548a284743a445e3680d7db5ac3ac"
"18ff9b538d16f290ae67f760984dc659"
"4a7c15e9716ed28dc027beceea1ec40a"
),
(
"f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
"278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
"08b8b2b733424243760fe426a4b54908"
"632110a66c2f6591eabd3345e3e4eb98"
"fa6e264bf09efe12ee50f8f54e9f77b1"
"e355f6c50544e23fb1433ddf73be84d8"
"79de7c0046dc4996d9e773f4bc9efe57"
"38829adb26c81b37c93a1b270b20329d"
"658675fc6ea534e0810a4432826bf58c"
"941efb65d57a338bbd2e26640f89ffbc"
"1a858efcb8550ee3a5e1998bd177e93a"
"7363c344fe6b199ee5d02e82d522c4fe"
"ba15452f80288a821a579116ec6dad2b"
"3b310da903401aa62100ab5d1a36553e"
"06203b33890cc9b832f79ef80560ccb9"
"a39ce767967ed628c6ad573cb116dbef"
"efd75499da96bd68a8a97b928a8bbc10"
"3b6621fcde2beca1231d206be6cd9ec7"
"aff6f6c94fcd7204ed3455c68c83f4a4"
"1da4af2b74ef5c53f1d8ac70bdcb7ed1"
"85ce81bd84359d44254d95629e9855a9"
"4a7c1958d1f8ada5d0532ed8a5aa3fb2"
"d17ba70eb6248e594e1a2297acbbb39d"
"502f1a8c6eb6f1ce22b3de1a1f40cc24"
"554119a831a9aad6079cad88425de6bd"
"e1a9187ebb6092cf67bf2b13fd65f270"
"88d78b7e883c8759d2c4f5c65adb7553"
"878ad575f9fad878e80a0c9ba63bcbcc"
"2732e69485bbc9c90bfbd62481d9089b"
"eccf80cfe2df16a2cf65bd92dd597b07"
"07e0917af48bbb75fed413d238f5555a"
"7a569d80c3414a8d0859dc65a46128ba"
"b27af87a71314f318c782b23ebfe808b"
"82b0ce26401d2e22f04d83d1255dc51a"
"ddd3b75a2b1ae0784504df543af8969b"
"e3ea7082ff7fc9888c144da2af58429e"
"c96031dbcad3dad9af0dcbaaaf268cb8"
"fcffead94f3c7ca495e056a9b47acdb7"
"51fb73e666c6c655ade8297297d07ad1"
"ba5e43f1bca32301651339e22904cc8c"
"42f58c30c04aafdb038dda0847dd988d"
"cda6f3bfd15c4b4c4525004aa06eeff8"
"ca61783aacec57fb3d1f92b0fe2fd1a8"
"5f6724517b65e614ad6808d6f6ee34df"
"f7310fdc82aebfd904b01e1dc54b2927"
"094b2db68d6f903b68401adebf5a7e08"
"d78ff4ef5d63653a65040cf9bfd4aca7"
"984a74d37145986780fc0b16ac451649"
"de6188a7dbdf191f64b5fc5e2ab47b57"
"f7f7276cd419c17a3ca8e1b939ae49e4"
"88acba6b965610b5480109c8b17b80e1"
"b7b750dfc7598d5d5011fd2dcc5600a3"
"2ef5b52a1ecc820e308aa342721aac09"
"43bf6686b64b2579376504ccc493d97e"
"6aed3fb0f9cd71a43dd497f01f17c0e2"
"cb3797aa2a2f256656168e6c496afc5f"
"b93246f6b1116398a346f1a641f3b041"
"e989f7914f90cc2c7fff357876e506b5"
"0d334ba77c225bc307ba537152f3f161"
"0e4eafe595f6d9d90d11faa933a15ef1"
"369546868a7f3a45a96768d40fd9d034"
"12c091c6315cf4fde7cb68606937380d"
"b2eaaa707b4c4185c32eddcdd306705e"
"4dc1ffc872eeee475a64dfac86aba41c"
"0618983f8741c5ef68d3a101e8a3b8ca"
"c60c905c15fc910840b94c00a0b9d0",
None,
"",
"0aab4c900501b3e24d7cdf4663326a3a"
"87df5e4843b2cbdb67cbf6e460fec350"
"aa5371b1508f9f4528ecea23c436d94b"
"5e8fcd4f681e30a6ac00a9704a188a03"
),
# 7.2 Ed25519ctx
(
"0305334e381af78f141cb666f6199f57"
"bc3495335a256a95bd2a55bf546663f6",
"dfc9425e4f968f7f0c29f0259cf5f9ae"
"d6851c2bb4ad8bfb860cfee0ab248292",
"f726936d19c800494e3fdaff20b276a8",
None,
"666f6f",
"55a4cc2f70a54e04288c5f4cd1e45a7b"
"b520b36292911876cada7323198dd87a"
"8b36950b95130022907a7fb7c4e9b2d5"
"f6cca685a587b4b21f4b888e4e7edb0d"
),
(
"0305334e381af78f141cb666f6199f57"
"bc3495335a256a95bd2a55bf546663f6",
"dfc9425e4f968f7f0c29f0259cf5f9ae"
"d6851c2bb4ad8bfb860cfee0ab248292",
"f726936d19c800494e3fdaff20b276a8",
None,
"626172",
"fc60d5872fc46b3aa69f8b5b4351d580"
"8f92bcc044606db097abab6dbcb1aee3"
"216c48e8b3b66431b5b186d1d28f8ee1"
"5a5ca2df6668346291c2043d4eb3e90d"
),
(
"0305334e381af78f141cb666f6199f57"
"bc3495335a256a95bd2a55bf546663f6",
"dfc9425e4f968f7f0c29f0259cf5f9ae"
"d6851c2bb4ad8bfb860cfee0ab248292",
"508e9e6882b979fea900f62adceaca35",
None,
"666f6f",
"8b70c1cc8310e1de20ac53ce28ae6e72"
"07f33c3295e03bb5c0732a1d20dc6490"
"8922a8b052cf99b7c4fe107a5abb5b2c"
"4085ae75890d02df26269d8945f84b0b"
),
(
"ab9c2853ce297ddab85c993b3ae14bca"
"d39b2c682beabc27d6d4eb20711d6560",
"0f1d1274943b91415889152e893d80e9"
"3275a1fc0b65fd71b4b0dda10ad7d772",
"f726936d19c800494e3fdaff20b276a8",
None,
"666f6f",
"21655b5f1aa965996b3f97b3c849eafb"
"a922a0a62992f73b3d1b73106a84ad85"
"e9b86a7b6005ea868337ff2d20a7f5fb"
"d4cd10b0be49a68da2b2e0dc0ad8960f"
),
# 7.3 Ed25519ph
(
"833fe62409237b9d62ec77587520911e"
"9a759cec1d19755b7da901b96dca3d42",
"ec172b93ad5e563bf4932c70e1245034"
"c35467ef2efd4d64ebf819683467e2bf",
"616263",
SHA512,
"",
"98a70222f0b8121aa9d30f813d683f80"
"9e462b469c7ff87639499bb94e6dae41"
"31f85042463c2a355a2003d062adf5aa"
"a10b8c61e636062aaad11c2a26083406"
)
)
rfc8032_tv_bytes = []
for tv_str in rfc8032_tv_str:
rfc8032_tv_bytes.append([unhexlify(i) if isinstance(i, str) else i for i in tv_str])
class TestEdDSA(unittest.TestCase):
def test_sign(self):
for sk, _, msg, hashmod, ctx, exp_signature in rfc8032_tv_bytes:
key = eddsa.import_private_key(sk)
signer = eddsa.new(key, 'rfc8032', context=ctx)
if hashmod is None:
# PureEdDSA
signature = signer.sign(msg)
else:
# HashEdDSA
hashobj = hashmod.new(msg)
signature = signer.sign(hashobj)
self.assertEqual(exp_signature, signature)
def test_verify(self):
for _, pk, msg, hashmod, ctx, exp_signature in rfc8032_tv_bytes:
key = eddsa.import_public_key(pk)
verifier = eddsa.new(key, 'rfc8032', context=ctx)
if hashmod is None:
# PureEdDSA
verifier.verify(msg, exp_signature)
else:
# HashEdDSA
hashobj = hashmod.new(msg)
verifier.verify(hashobj, exp_signature)
def test_negative(self):
key = ECC.generate(curve="ed25519")
self.assertRaises(ValueError, eddsa.new, key, 'rfc9999')
nist_key = ECC.generate(curve="p256")
self.assertRaises(ValueError, eddsa.new, nist_key, 'rfc8032')
class TestExport_Ed25519(unittest.TestCase):
def test_raw(self):
key = ECC.generate(curve="Ed25519")
x, y = key.pointQ.xy
raw = bytearray(key._export_ed25519())
sign_x = raw[31] >> 7
raw[31] &= 0x7F
yt = bytes_to_long(raw[::-1])
self.assertEqual(y, yt)
self.assertEqual(x & 1, sign_x)
key = ECC.construct(point_x=0, point_y=1, curve="Ed25519")
out = key._export_ed25519()
self.assertEqual(b'\x01' + b'\x00' * 31, out)
class TestImport_Ed25519(unittest.TestCase):
def test_raw(self):
Px = 24407857220263921307776619664228778204996144802740950419837658238229122415920
Py = 56480760040633817885061096979765646085062883740629155052073094891081309750690
encoded = b'\xa2\x05\xd6\x00\xe1 \xe1\xc0\xff\x96\xee?V\x8e\xba/\xd3\x89\x06\xd7\xc4c\xe8$\xc2d\xd7a1\xfa\xde|'
key = eddsa.import_public_key(encoded)
self.assertEqual(Py, key.pointQ.y)
self.assertEqual(Px, key.pointQ.x)
encoded = b'\x01' + b'\x00' * 31
key = eddsa.import_public_key(encoded)
self.assertEqual(1, key.pointQ.y)
self.assertEqual(0, key.pointQ.x)
class TestVectorsEdDSAWycheproof(unittest.TestCase):
def add_tests(self, filename):
def pk(group):
elem = group['key']['pk']
return unhexlify(elem)
def sk(group):
elem = group['key']['sk']
return unhexlify(elem)
result = load_test_vectors_wycheproof(("Signature", "wycheproof"),
filename,
"Wycheproof ECDSA signature (%s)"
% filename,
group_tag={'pk': pk, 'sk': sk})
self.tv += result
def setUp(self):
self.tv = []
self.add_tests("eddsa_test.json")
def test_sign(self, tv):
if not tv.valid:
return
self._id = "Wycheproof EdDSA Sign Test #%d (%s, %s)" % (tv.id, tv.comment, tv.filename)
key = eddsa.import_private_key(tv.sk)
signer = eddsa.new(key, 'rfc8032')
signature = signer.sign(tv.msg)
self.assertEqual(signature, tv.sig)
def test_verify(self, tv):
self._id = "Wycheproof EdDSA Verify Test #%d (%s, %s)" % (tv.id, tv.comment, tv.filename)
key = eddsa.import_public_key(tv.pk)
verifier = eddsa.new(key, 'rfc8032')
try:
verifier.verify(tv.msg, tv.sig)
except ValueError:
assert not tv.valid
else:
assert tv.valid
def runTest(self):
for tv in self.tv:
self.test_sign(tv)
self.test_verify(tv)
def get_tests(config={}):
tests = []
tests += list_test_cases(TestExport_Ed25519)
tests += list_test_cases(TestImport_Ed25519)
tests += list_test_cases(TestEdDSA)
tests += [TestVectorsEdDSAWycheproof()]
return tests
if __name__ == '__main__':
def suite():
return unittest.TestSuite(get_tests())
unittest.main(defaultTest='suite')

View file

@ -379,6 +379,8 @@ def new(key, mode, encoding='binary', randfunc=None):
if isinstance(key, EccKey): if isinstance(key, EccKey):
order = key._curve.order order = key._curve.order
private_key_attr = 'd' private_key_attr = 'd'
if key._curve.name == "ed25519":
raise ValueError("ECC key is not on a NIST P curve")
elif isinstance(key, DsaKey): elif isinstance(key, DsaKey):
order = Integer(key.q) order = Integer(key.q)
private_key_attr = 'x' private_key_attr = 'x'

View file

@ -33,4 +33,4 @@
A collection of standardized protocols to carry out digital signatures. A collection of standardized protocols to carry out digital signatures.
""" """
__all__ = ['PKCS1_v1_5', 'PKCS1_PSS', 'DSS', 'pkcs1_15', 'pss'] __all__ = ['PKCS1_v1_5', 'PKCS1_PSS', 'DSS', 'pkcs1_15', 'pss', 'eddsa']

View file

@ -0,0 +1,229 @@
# ===================================================================
#
# Copyright (c) 2022, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto.Math.Numbers import Integer
from Crypto.Hash import SHA512
from Crypto.Util.py3compat import bchr, is_bytes
from Crypto.PublicKey.ECC import EccKey, construct, _import_ed25519_public_key
def import_public_key(encoded):
"""Import an EdDSA ECC public key, when encoded as raw ``bytes`` as described
in RFC8032.
Args:
encoded (bytes):
The EdDSA public key to import.
It must be 32 bytes long for Ed25519.
Returns:
:class:`Crypto.PublicKey.EccKey` : a new ECC key object.
Raises:
ValueError: when the given key cannot be parsed.
"""
x, y = _import_ed25519_public_key(encoded)
return construct(curve="Ed25519", point_x=x, point_y=y)
def import_private_key(encoded):
"""Import an EdDSA ECC private key, when encoded as raw ``bytes`` as described
in RFC8032.
Args:
encoded (bytes):
The EdDSA private key to import. It must be 32 bytes long for Ed25519.
Returns:
:class:`Crypto.PublicKey.EccKey` : a new ECC key object.
Raises:
ValueError: when the given key cannot be parsed.
"""
if len(encoded) != 32:
raise ValueError("Incorrect length. Only Ed25519 private keys are supported.")
# Note that the private key is truly a sequence of random bytes,
# so we cannot check its correctness in any way.
return construct(seed=encoded, curve="ed25519")
class EdDSASigScheme(object):
"""An EdDSA signature object.
Do not instantiate directly.
Use :func:`Crypto.Signature.eddsa.new`.
"""
def __init__(self, key, context):
"""Create a new EdDSA object.
Do not instantiate this object directly,
use `Crypto.Signature.DSS.new` instead.
"""
self._key = key
self._context = context
self._A = key._export_ed25519()
self._order = key._curve.order
def can_sign(self):
"""Return ``True`` if this signature object can be used
for signing messages."""
return self._key.has_private()
def sign(self, msg_or_hash):
"""Compute the EdDSA signature of a message.
Args:
msg_or_hash (bytes or a :class:`Crypto.Hash.SHA512` object):
The message to verify (*PureEdDSA*) or
the hash that was carried out over the message (*HashEdDSA*).
:return: The signature as ``bytes``
:raise TypeError: if the EdDSA key has no private half
"""
if not self._key.has_private():
raise TypeError("Private key is needed to sign")
ph = isinstance(msg_or_hash, SHA512.SHA512Hash)
if not (ph or is_bytes(msg_or_hash)):
raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash")
if self._context or ph:
flag = int(ph)
dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \
bchr(len(self._context)) + self._context
else:
dom2 = b''
PHM = msg_or_hash.digest() if ph else msg_or_hash
# See RFC 8032, section 5.1.6
# Step 2
r_hash = SHA512.new(dom2 + self._key._prefix + PHM).digest()
r = Integer.from_bytes(r_hash, 'little') % self._order
# Step 3
R_pk = EccKey(point=r * self._key._curve.G)._export_ed25519()
# Step 4
k_hash = SHA512.new(dom2 + R_pk + self._A + PHM).digest()
k = Integer.from_bytes(k_hash, 'little') % self._order
# Step 5
s = (r + k * self._key.d) % self._order
return R_pk + s.to_bytes(32, 'little')
def verify(self, msg_or_hash, signature):
"""Check if an EdDSA signature is authentic.
Args:
msg_or_hash (bytes or a :class:`Crypto.Hash.SHA512` object):
The message to verify (*PureEdDSA*) or
the hash that was carried out over the message (*HashEdDSA*).
signature (``bytes``):
The signature that needs to be validated.
:raise ValueError: if the signature is not authentic
"""
if len(signature) != 64:
raise ValueError("The signature is not authentic (length)")
ph = isinstance(msg_or_hash, SHA512.SHA512Hash)
if not (ph or is_bytes(msg_or_hash)):
raise TypeError("'msg_or_hash' must be bytes of a SHA-512 hash")
if self._context or ph:
flag = int(ph)
dom2 = b'SigEd25519 no Ed25519 collisions' + bchr(flag) + \
bchr(len(self._context)) + self._context
else:
dom2 = b''
PHM = msg_or_hash.digest() if ph else msg_or_hash
# Step 1
try:
R = import_public_key(signature[:32]).pointQ
except ValueError:
raise ValueError("The signature is not authentic (R)")
s = Integer.from_bytes(signature[32:], 'little')
if s > self._order:
raise ValueError("The signature is not authentic (S)")
# Step 2
k_hash = SHA512.new(dom2 + signature[:32] + self._A + PHM).digest()
k = Integer.from_bytes(k_hash, 'little') % self._order
# Step 3
point1 = s * 8 * self._key._curve.G
# OPTIMIZE: with double-scalar multiplication, with no SCA
# countermeasures because it is public values
point2 = 8 * R + k * 8 * self._key.pointQ
if point1 != point2:
raise ValueError("The signature is not authentic")
def new(key, mode, context=None):
"""Create a signature object :class:`EdDSASigScheme` that
can perform or verify an EdDSA signature.
Args:
key (:class:`Crypto.PublicKey.ECC` on curve ``Ed25519``):
The key to use for computing the signature (*private* keys only)
or for verifying one.
mode (string):
This parameter must be ``'rfc8032'``.
context (bytes):
Up to 255 bytes of `context <https://datatracker.ietf.org/doc/html/rfc8032#page-41>`_,
which is a constant byte string to segregate different protocols or
uses of the same key.
Do not specify a context, if you want a pure Ed25519 signature.
"""
if not isinstance(key, EccKey) or key._curve.name != 'ed25519':
raise ValueError("EdDSA can only be used with Ed25519 keys")
if mode != 'rfc8032':
raise ValueError("Mode must be 'rfc8032'")
if context is None:
context = b''
elif len(context) > 255:
raise ValueError("Context for EdDSA must not be longer than 255 bytes")
return EdDSASigScheme(key, context)

View file

@ -0,0 +1,18 @@
from typing import Union, Optional
from typing_extensions import Protocol
from Crypto.PublicKey.ECC import EccKey
class Hash(Protocol):
def digest(self) -> bytes: ...
def import_public_key(encoded: bytes) -> EccKey: ...
def import_private_key(encoded: bytes) -> EccKey: ...
class EdDSASigScheme(object):
def __init__(self, key: EccKey, context: bytes) -> None: ...
def can_sign(self) -> bool: ...
def sign(self, msg_or_hash: Union[bytes, Hash]) -> bytes: ...
def verify(self, msg_or_hash: Union[bytes, Hash], signature: bytes) -> None: ...
def new(key: EccKey, mode: bytes, context: Optional[bytes]=None) -> EdDSASigScheme: ...

View file

@ -13,6 +13,7 @@ def bytestring(x: Any) -> bool: ...
def is_native_int(s: Any) -> bool: ... def is_native_int(s: Any) -> bool: ...
def is_string(x: Any) -> bool: ... def is_string(x: Any) -> bool: ...
def is_bytes(x: Any) -> bool: ...
def BytesIO(b: bytes) -> IO[bytes]: ... def BytesIO(b: bytes) -> IO[bytes]: ...
def StringIO(s: str) -> IO[str]: ... def StringIO(s: str) -> IO[str]: ...

View file

@ -1,6 +1,6 @@
__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature', __all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature',
'IO', 'Math'] 'IO', 'Math']
version_info = (3, 15, '0', 'dev') version_info = (3, 15, '0dev')
__version__ = ".".join([str(x) for x in version_info]) __version__ = ".".join([str(x) for x in version_info])

View file

@ -443,6 +443,14 @@ ext_modules = [
'src/p521_table.c'], 'src/p521_table.c'],
py_limited_api=True, py_limited_api=True,
), ),
Extension("Crypto.PublicKey._x255219",
include_dirs=['src/'],
sources=['src/x25519.c']
),
Extension("Crypto.PublicKey._ed25519",
include_dirs=['src/'],
sources=['src/ed25519.c']
),
# Math # Math
Extension("Crypto.Math._modexp", Extension("Crypto.Math._modexp",

View file

@ -28,5 +28,8 @@ p384_table.c: make_ecc_table.py
p521_table.c: make_ecc_table.py p521_table.c: make_ecc_table.py
python make_ecc_table.py p521 4 p521_table python make_ecc_table.py p521 4 p521_table
x25519: x25519.c multiply_64.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DPROFILE
clean:: clean::
rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp x25519

282
src/bignum.c Normal file
View file

@ -0,0 +1,282 @@
#ifndef __BIGNUM_C
#define __BIGNUM_C
#include "common.h"
#include "multiply.h"
#if defined(USE_SSE2)
#if defined(HAVE_INTRIN_H)
#include <intrin.h>
#elif defined(HAVE_X86INTRIN_H)
#include <x86intrin.h>
#elif defined(HAVE_EMMINTRIN_H)
#include <xmmintrin.h>
#include <emmintrin.h>
#endif
#endif
static inline unsigned is_odd(uint64_t x)
{
return 1 == (x & 1);
}
static inline unsigned is_even(uint64_t x)
{
return !is_odd(x);
}
/**
* Check if a multi-word integer x is greater than or equal to y.
*
* @param x The first term
* @param y The second term
* @param nw The number of words that make up x and y
* @return 1 if x>=y, 0 if x<y
*/
STATIC int ge(const uint64_t *x, const uint64_t *y, size_t nw)
{
unsigned mask = (unsigned)-1;
unsigned result = 0;
size_t i, j;
i = nw - 1;
for (j=0; j<nw; j++, i--) {
unsigned greater, lower;
greater = x[i] > y[i];
lower = x[i] < y[i];
result |= mask & (greater | (lower << 1));
mask &= (greater ^ lower) - 1;
}
return result<2;
}
/*
* Subtract a multi-word integer b from a.
*
* @param out The location where the multi-word result is stored
* @param a Number to subtract from
* @param b Number to subtract
* @param nw The number of words of both a and b
* @result 0 if there is no borrow, 1 otherwise
*/
STATIC unsigned sub(uint64_t *out, const uint64_t *a, const uint64_t *b, size_t nw)
{
size_t i;
unsigned borrow1 , borrow2;
borrow2 = 0;
for (i=0; i<nw; i++) {
borrow1 = b[i] > a[i];
out[i] = a[i] - b[i];
borrow1 |= borrow2 > out[i];
out[i] -= borrow2;
borrow2 = borrow1;
}
return borrow2;
}
/*
* Multiply a multi-word integer a by a 64-bit scalar k and
* then add the result to the multi-word integer t.
*
* @param t The multi-word integer accumulator
* @param tw The number of words of t
* @param a The multi-word integer to multiply with the scalar
* @param aw The number of words of a
* @param k The 64-bit scalar multiplier
*/
STATIC void addmul(uint64_t *t, size_t tw, const uint64_t *a, size_t aw, uint64_t k)
{
size_t i;
uint64_t carry;
carry = 0;
for (i=0; i<aw; i++) {
uint64_t prod_lo, prod_hi;
DP_MULT(a[i], k, prod_lo, prod_hi);
prod_lo += carry;
prod_hi += prod_lo < carry;
t[i] += prod_lo;
prod_hi += t[i] < prod_lo;
carry = prod_hi;
}
for (; carry; i++) {
t[i] += carry;
carry = t[i] < carry;
}
assert(i <= tw);
}
/**
* Multiply two multi-word integers.
*
* @param t The location where the result is stored. It is twice as big as
* either a (or b). It is an array of 2*nw words).
* @param scratchpad Temporary area. It is an array of 3*nw words.
* @param a The first term, array of nw words.
* @param b The second term, array of nw words.
* @param nw The number of words of both a and b.
*
*/
STATIC void product(uint64_t *t, uint64_t *scratchpad, const uint64_t *a, const uint64_t *b, size_t nw)
{
size_t i;
memset(t, 0, 2*sizeof(uint64_t)*nw);
for (i=0; i<(nw ^ (nw & 1)); i+=2) {
addmul128(&t[i], scratchpad, a, b[i], b[i+1], 2*nw-i, nw);
}
if (is_odd(nw)) {
addmul(&t[nw-1], nw+2, a, nw, b[nw-1]);
}
}
/*
* Select a number out of two, in constant time.
*
* @param out The location where the multi-word result is stored
* @param a The first choice, selected if cond is true (non-zero)
* @param b The second choice, selected if cond is false (zero)
* @param cond The flag that drives the selection
* @param words The number of words of a, b, and out
* @return 0 for success, the appropriate code otherwise.
*/
STATIC FUNC_SSE2 int mod_select(uint64_t *out, const uint64_t *a, const uint64_t *b, unsigned cond, size_t words)
{
uint64_t mask;
#if defined(USE_SSE2)
unsigned pairs, i;
__m128i r0, r1, r2, r3, r4, r5;
pairs = (unsigned)words / 2;
mask = (uint64_t)((cond != 0) - 1); /* 0 for a, 1s for b */
#if SYSBITS == 64
r0 = _mm_set1_epi64x(mask);
#else
r0 = _mm_loadl_epi64((__m128i*)&mask);
r0 = _mm_unpacklo_epi64(r0, r0);
#endif
for (i=0; i<pairs; i++, a+=2, b+=2, out+=2) {
r1 = _mm_loadu_si128((__m128i const*)b);
r2 = _mm_loadu_si128((__m128i const*)a);
r3 = _mm_and_si128(r0, r1);
r4 = _mm_andnot_si128(r0, r2);
r5 = _mm_or_si128(r3, r4);
_mm_storeu_si128((__m128i*)out, r5);
}
if (words & 1) {
*out = (*b & mask) ^ (*a & ~mask);
}
#else
unsigned i;
mask = (uint64_t)((cond != 0) - 1);
for (i=0; i<words; i++) {
*out++ = (*b++ & mask) ^ (*a++ & ~mask);
}
#endif
return 0;
}
/*
* Add two multi-word numbers with modulo arithmetic.
*
* @param out The locaton where the multi-word result (nw words) is stored
* @param a The first term (nw words)
* @param b The second term (nw words)
* @param modulus The modulus (nw words)
* @param tmp1 A temporary area (nw words)
* @param tmp2 A temporary area (nw words)
* @param nw The number of 64-bit words in all parameters
*/
STATIC void add_mod(uint64_t* out, const uint64_t* a, const uint64_t* b, const uint64_t *modulus, uint64_t *tmp1, uint64_t *tmp2, size_t nw)
{
unsigned i;
unsigned carry, borrow1, borrow2;
/*
* Compute sum in tmp1[], and subtract modulus[]
* from tmp1[] into tmp2[].
*/
borrow2 = 0;
for (i=0, carry=0; i<nw; i++) {
tmp1[i] = a[i] + carry;
carry = tmp1[i] < carry;
tmp1[i] += b[i];
carry += tmp1[i] < b[i];
borrow1 = modulus[i] > tmp1[i];
tmp2[i] = tmp1[i] - modulus[i];
borrow1 |= borrow2 > tmp2[i];
tmp2[i] -= borrow2;
borrow2 = borrow1;
}
/*
* If there is no borrow or if there is carry,
* tmp1[] is larger than modulus, so we must return tmp2[].
*/
mod_select(out, tmp2, tmp1, carry | (borrow2 ^ 1), nw);
}
/*
* Subtract two multi-word numbers with modulo arithmetic.
*
* @param out The locaton where the multi-word result (nw words) is stored
* @param a The number it will be subtracted from (nw words)
* @param b The number to subtract (nw wordS)
* @param modulus The modulus (nw words)
* @param tmp1 A temporary area (nw words)
* @param tmp2 A temporary area (nw words)
* @param nw The number of 64-bit words in all parameters
* @return 0 for success, the relevant error code otherwise
*/
int sub_mod(uint64_t *out, const uint64_t *a, const uint64_t *b, const uint64_t *modulus, uint64_t *tmp1, uint64_t *tmp2, size_t nw)
{
unsigned i;
unsigned carry, borrow1 , borrow2;
/*
* Compute difference in tmp1[], and add modulus[]
* to tmp1[] into tmp2[].
*/
borrow2 = 0;
carry = 0;
for (i=0; i<nw; i++) {
borrow1 = b[i] > a[i];
tmp1[i] = a[i] - b[i];
borrow1 |= borrow2 > tmp1[i];
tmp1[i] -= borrow2;
borrow2 = borrow1;
tmp2[i] = tmp1[i] + carry;
carry = tmp2[i] < carry;
tmp2[i] += modulus[i];
carry += tmp2[i] < modulus[i];
}
/*
* If there is no borrow, tmp[] is smaller than modulus.
*/
mod_select(out, tmp2, tmp1, borrow2, nw);
return 0;
}
#endif /* __BIGNUM_C **/

View file

@ -42,7 +42,7 @@ EXPORT_SYM int ec_ws_new_point(EcPoint **pecp,
const uint8_t *y, const uint8_t *y,
size_t len, size_t len,
const EcContext *ec_ctx); const EcContext *ec_ctx);
EXPORT_SYM void ec_free_point(EcPoint *ecp); EXPORT_SYM void ec_ws_free_point(EcPoint *ecp);
EXPORT_SYM int ec_ws_get_xy(uint8_t *x, EXPORT_SYM int ec_ws_get_xy(uint8_t *x,
uint8_t *y, uint8_t *y,
size_t len, size_t len,

View file

@ -36,6 +36,9 @@
#include "multiply.h" #include "multiply.h"
#include "mont.h" #include "mont.h"
#include "ec.h" #include "ec.h"
#include "p256_table.h"
#include "p384_table.h"
#include "p521_table.h"
#include "p256_table.h" #include "p256_table.h"
#include "p384_table.h" #include "p384_table.h"
@ -986,7 +989,7 @@ EXPORT_SYM void ec_free_context(EcContext *ec_ctx)
* Create a new EC point on the given EC curve. * Create a new EC point on the given EC curve.
* *
* @param pecp The memory area where the pointer to the newly allocated EC * @param pecp The memory area where the pointer to the newly allocated EC
* point will be stored. Use ec_free_point() for deallocating it. * point will be stored. Use ec_ws_free_point() for deallocating it.
* @param x The X-coordinate (affine, big-endian) * @param x The X-coordinate (affine, big-endian)
* @param y The Y-coordinate (affine, big-endian) * @param y The Y-coordinate (affine, big-endian)
* @param len The length of x and y in bytes * @param len The length of x and y in bytes
@ -1062,7 +1065,7 @@ cleanup:
return res; return res;
} }
EXPORT_SYM void ec_free_point(EcPoint *ecp) EXPORT_SYM void ec_ws_free_point(EcPoint *ecp)
{ {
if (NULL == ecp) if (NULL == ecp)
return; return;
@ -1172,42 +1175,6 @@ EXPORT_SYM int ec_ws_add(EcPoint *ecpa, EcPoint *ecpb)
return 0; return 0;
} }
/*
* Normalize the projective representation of a point
* so that Z=1 or Z=0.
*/
EXPORT_SYM int ec_ws_normalize(EcPoint *ecp)
{
MontContext *ctx;
Workplace *wp = NULL;
if (NULL == ecp)
return ERR_NULL;
ctx = ecp->ec_ctx->mont_ctx;
wp = new_workplace(ctx);
if (NULL == wp)
return ERR_MEMORY;
if (!mont_is_zero(ecp->z, ctx)) {
ec_projective_to_affine(ecp->x, ecp->y,
ecp->x, ecp->y, ecp->z,
wp, ctx);
mont_set(ecp->z, 1, ctx);
}
free_workplace(wp);
return 0;
}
EXPORT_SYM int ec_ws_is_pai(EcPoint *ecp)
{
if (NULL == ecp)
return FALSE;
return mont_is_zero(ecp->z, ecp->ec_ctx->mont_ctx);
}
/* /*
* Blind the scalar factor to be used in an EC multiplication * Blind the scalar factor to be used in an EC multiplication
* *
@ -1473,22 +1440,6 @@ cleanup:
return res; return res;
} }
EXPORT_SYM int ec_ws_copy(EcPoint *ecp1, const EcPoint *ecp2)
{
MontContext *ctx;
if (NULL == ecp1 || NULL == ecp2)
return ERR_NULL;
ctx = ecp2->ec_ctx->mont_ctx;
ecp1->ec_ctx = ecp2->ec_ctx;
mont_copy(ecp1->x, ecp2->x, ctx);
mont_copy(ecp1->y, ecp2->y, ctx);
mont_copy(ecp1->z, ecp2->z, ctx);
return 0;
}
/* /*
* Compare two EC points and return 0 if they match * Compare two EC points and return 0 if they match
*/ */
@ -1550,3 +1501,18 @@ EXPORT_SYM int ec_ws_neg(EcPoint *p)
return 0; return 0;
} }
EXPORT_SYM int ec_ws_copy(EcPoint *ecp1, const EcPoint *ecp2)
{
MontContext *ctx;
if (NULL == ecp1 || NULL == ecp2)
return ERR_NULL;
ctx = ecp2->ec_ctx->mont_ctx;
ecp1->ec_ctx = ecp2->ec_ctx;
mont_copy(ecp1->x, ecp2->x, ctx);
mont_copy(ecp1->y, ecp2->y, ctx);
mont_copy(ecp1->z, ecp2->z, ctx);
return 0;
}

View file

@ -116,8 +116,8 @@ int main(void)
printf("\n"); printf("\n");
#endif #endif
ec_free_point(gp); ec_ws_free_point(gp);
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
return 0; return 0;

View file

@ -118,8 +118,8 @@ int main(void)
printf("\n"); printf("\n");
#endif #endif
ec_free_point(gp); ec_ws_free_point(gp);
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
return 0; return 0;

View file

@ -122,8 +122,8 @@ int main(void)
printf("\n"); printf("\n");
#endif #endif
ec_free_point(gp); ec_ws_free_point(gp);
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
return 0; return 0;

308
src/ed25519.c Normal file
View file

@ -0,0 +1,308 @@
/*
* Twisted Edward curve with equation:
*
* ax² + y² = 1 + dx²y²
*
* where a = -1 and d = - 121665/121666 over the prime field 2² - 19.
*
* Points (x, y) can be represented as extended homogeneous coordinates
* (X, Y, Z, T) with x = X/Z, y = Y/Z, and x*y = T/Z for a non-zero Z.
*
* The PAI (or neutral point) is (0, Z, Z, 0) or equivalently (0, 1).
* The point (x, y) can be obtained by normalizing to (x, y, 1, x*y).
*/
#include "common.h"
#include "endianess.h"
#include "mod25519.c"
typedef struct Point {
uint32_t X[10];
uint32_t Y[10];
uint32_t Z[10];
uint32_t T[10];
} Point;
/*
* P3 can be P1 or P2
*/
STATIC void ed25519_add_internal(Point *P3, const Point *P1, const Point *P2)
{
uint32_t A[10], B[10], C[10], D[10];
/* d = 37095705934669439343138083508754565189542113879843219016388785533085940283555 */
/* k = 2 * d (mod 2²⁵⁵ - 19) */
static const uint32_t k[10] = { 0x2B2F159, 0x1A6E509, 0x22ADD7A, 0xD4141D, 0x38052,
0xF3D130, 0x3407977, 0x19CE331, 0x1C56DFF, 0x901B67 };
/* https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3 */
sub32(A, P1->Y, P1->X); /* (Y1-X1) each limb < 2²⁷ */
sub32(B, P2->Y, P2->X); /* (Y2-X2) < 2²⁷ */
mul_25519(A, A, B); /* A < 2²⁶ */
add32(B, P1->Y, P1->X); /* (Y1+X1) < 2²⁷ */
add32(C, P2->Y, P2->X); /* (Y2+X2) < 2²⁷ */
mul_25519(B, B, C); /* B < 2²⁶ */
mul_25519(C, P1->T, P2->T); /* T1*T2 < 2²⁶ */
mul_25519(C, C, k); /* C < 2²⁶ */
mul_25519(D, P1->Z, P2->Z); /* Z1*Z2 < 2²⁶ */
add_25519(D, D, D); /* D < 2²⁶ */
sub32(P3->T, B, A); /* E=B-A < 2²⁷ */
sub32(P3->Z, D, C); /* F=D-C < 2²⁷ */
add32(D, D, C); /* G=D+C < 2²⁷ */
add32(B, B, A); /* H=B+A < 2²⁷ */
mul_25519(P3->X, P3->T, P3->Z); /* X3=E*F < 2²⁶ */
mul_25519(P3->Y, D, B); /* Y3=G*H < 2²⁶ */
mul_25519(P3->T, P3->T, B); /* T3=E*H < 2²⁶ */
mul_25519(P3->Z, P3->Z, D); /* Z3=F*G < 2²⁶ */
}
STATIC void ed25519_double_internal(Point *P3, const Point *P1)
{
uint32_t A[10], B[10], C[10], D[10];
mul_25519(A, P1->X, P1->X); /* X1^2 each limb < 2²⁶ */
mul_25519(B, P1->Y, P1->Y); /* Y1^2 < 2²⁶ */
mul_25519(C, P1->Z, P1->Z); /* Z1^2 < 2²⁶ */
add_25519(C, C, C); /* C=2*Z1^2 < 2²⁶ */
add32(D, A, B); /* H=A+B < 2²⁷ */
add32(P3->T, P1->X, P1->Y); /* X1+Y1 < 2²⁷ */
mul_25519(P3->T, P3->T, P3->T); /* (X1+Y1)^2 < 2²⁶ */
sub32(P3->T, D, P3->T); /* E=H-(X1+Y1)^2 < 2²⁷ */
sub32(P3->Z, A, B); /* G=A-B < 2²⁷ */
add_25519(A, C, P3->Z); /* F=C+G < 2²⁶ */
mul_25519(P3->X, P3->T, A); /* X3=E*F < 2²⁶ */
mul_25519(P3->Y, P3->Z, D); /* Y3=G*H < 2²⁶ */
mul_25519(P3->T, P3->T, D); /* T3=E*H < 2²⁶ */
mul_25519(P3->Z, A, P3->Z); /* Z3=F*G < 2²⁶ */
}
#if 0
static void print_point_le8(const Point *p)
{
uint8_t bin[32];
unsigned i;
uint32_t tmp[32];
uint32_t invz[32];
invert_25519(invz, p->Z);
mul_25519(tmp, p->X, invz);
convert_le25p5_to_le8(bin, tmp);
printf("X=");
for (i=0; i<32; i++)
printf("%02X", bin[i]);
printf("\n");
invert_25519(invz, p->Z);
mul_25519(tmp, p->Y, invz);
convert_le25p5_to_le8(bin, tmp);
printf("Y=");
for (i=0; i<32; i++)
printf("%02X", bin[i]);
printf("\n");
}
#endif
/*
* Scalar multiplication Q = k*B
*
* @param[out] xout The X-coordinate of the resulting point Q.
* @param[out] yout The Y-coordinate of the resulting point Q.
* @param[in] k The scalar encoded in little-endian mode.
* @param[in] len Length of the scalar in bytes.
* @param[in] xin The X-coordinate of the input point B.
* @param[in] yin The Y-coordinate of the input point B.
*/
STATIC void ed25519_scalar_internal(Point *Pout,
const uint8_t *k, size_t len,
const Point *Pin)
{
Point R0, R1;
unsigned bit_idx, swap;
size_t scan;
/* Point R0 */
memset(&R0, 0, sizeof R0);
R0.Y[0] = R0.Z[0] = 1;
/* Point R1 */
R1 = *Pin;
/* https://eprint.iacr.org/2020/956.pdf */
/* OPTIMIZE: with pre-computed tables in case of fixed-point multiplication */
/* Scan all bits from MSB to LSB */
bit_idx = 7;
swap = 0;
scan = 0;
while (scan<len) {
unsigned bit;
bit = (k[scan] >> bit_idx) & 1;
swap ^= bit;
cswap(R0.X, R0.Y, R1.X, R1.Y, swap);
cswap(R0.Z, R0.T, R1.Z, R1.T, swap);
ed25519_add_internal(&R1, &R0, &R1); /* R1 <-- R0 + R1 */
ed25519_double_internal(&R0, &R0); /* R0 <-- 2R0 */
swap = bit;
if (bit_idx-- == 0) {
bit_idx = 7;
scan++;
}
}
cswap(R0.X, R0.Y, R1.X, R1.Y, swap);
cswap(R0.Z, R0.T, R1.Z, R1.T, swap);
*Pout = R0;
}
/* ---- */
int ed25519_new_point(Point **out,
uint8_t x[32], uint8_t y[32],
size_t modsize, void *context)
{
uint32_t A[10], B[10], C[10];
const char d[] = "52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3";
if ((NULL == out) || (NULL == x) || (NULL == y))
return ERR_NULL;
if (modsize != 32)
return ERR_MODULUS;
*out = calloc(1, sizeof(Point));
if (NULL == *out)
return ERR_MEMORY;
convert_be8_to_le25p5((*out)->X, x);
convert_be8_to_le25p5((*out)->Y, y);
(*out)->Z[0] = 1;
mul_25519((*out)->T, (*out)->X, (*out)->Y);
/** Verify that the point is on the Ed25519 curve **/
mul_25519(A, (*out)->X, (*out)->X); /* x² */
mul_25519(B, (*out)->Y, (*out)->Y); /* y² */
convert_behex_to_le25p5(C, d); /* d */
mul_25519(C, C, B); /* dy² */
mul_25519(C, C, A); /* dx²y² */
add_25519(C, C, A); /* dx²y² - ax² */
memset(A, 0, sizeof A);
A[0] = 1;
add_25519(C, C, A); /* 1 + dx²y² - ax² */
reduce_25519_le25p5(B);
reduce_25519_le25p5(C);
if (0 != memcmp(B, C, sizeof B)) {
free(*out);
*out = NULL;
return ERR_EC_POINT;
}
return 0;
}
int ed25519_clone(Point **P, const Point *Q)
{
if ((NULL == P) || (NULL == Q))
return ERR_NULL;
*P = calloc(1, sizeof(Point));
if (NULL == *P)
return ERR_MEMORY;
**P = *Q;
return 0;
}
void ed25519_free_point(Point *p)
{
if (p)
free(p);
}
int ed25519_cmp(const Point *p1, const Point *p2)
{
uint32_t tmp[10];
uint8_t bin1[32], bin2[32];
int res = 0;
mul_25519(tmp, p1->X, p2->Z);
convert_le25p5_to_le8(bin1, tmp);
mul_25519(tmp, p2->X, p1->Z);
convert_le25p5_to_le8(bin2, tmp);
for (unsigned i=0; i<sizeof bin1; i++) {
res |= bin1[i] != bin2[i];
}
mul_25519(tmp, p1->Y, p2->Z);
convert_le25p5_to_le8(bin1, tmp);
mul_25519(tmp, p2->Y, p1->Z);
convert_le25p5_to_le8(bin2, tmp);
for (unsigned i=0; i<sizeof bin1; i++) {
res |= bin1[i] != bin2[i];
}
return res;
}
int ed25519_neg(Point *p)
{
const uint32_t zero[10] = { 0 };
sub32(p->X, zero, p->X);
reduce_25519_le25p5(p->X);
return 0;
}
int ed25519_get_xy(uint8_t *xb, uint8_t *yb, size_t modsize, Point *p)
{
uint32_t invz[10], tmp[10];
if ((NULL == xb) || (NULL == yb) || (NULL == p))
return ERR_NULL;
if (modsize != 32)
return ERR_MODULUS;
invert_25519(invz, p->Z);
mul_25519(tmp, p->X, invz);
convert_le25p5_to_be8(xb, tmp);
mul_25519(tmp, p->Y, invz);
convert_le25p5_to_be8(yb, tmp);
return 0;
}
int ed25519_double(Point *p)
{
if (NULL == p)
return ERR_NULL;
ed25519_double_internal(p, p);
return 0;
}
int ed25519_add(Point *P1, const Point *P2)
{
if ((NULL == P1) || (NULL == P2))
return ERR_NULL;
ed25519_add_internal(P1, P1, P2);
return 0;
}
int ed25519_scalar(Point *P, uint8_t *scalar, size_t scalar_len, uint64_t seed)
{
if ((NULL == P) || (NULL == scalar))
return ERR_NULL;
ed25519_scalar_internal(P, scalar, scalar_len, P);
return 0;
}

111
src/make_p256_table.c Normal file
View file

@ -0,0 +1,111 @@
#include "common.h"
#include "ec.h"
#include "endianess.h"
#define BITS 256
#define BYTES BITS/8
#define WORDS BITS/64
static void print_64bit_array(uint64_t *x, unsigned len)
{
unsigned i, j;
for (i=0; i<len; i++) {
printf("0x");
for (j=0; j<8; j++) {
printf("%02X", (uint8_t)(x[i] >> ((7-j)*8)));
}
printf("ULL");
if (i!=(len-1))
printf(",");
}
}
int main(void)
{
const uint8_t p256_mod[32] = "\xff\xff\xff\xff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
const uint8_t b[32] = "\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b";
const uint8_t order[32] = "\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xbc\xe6\xfa\xad\xa7\x17\x9e\x84\xf3\xb9\xca\xc2\xfc\x63\x25\x51";
const uint8_t p256_Gx[32] = "\x6b\x17\xd1\xf2\xe1\x2c\x42\x47\xf8\xbc\xe6\xe5\x63\xa4\x40\xf2\x77\x03\x7d\x81\x2d\xeb\x33\xa0\xf4\xa1\x39\x45\xd8\x98\xc2\x96";
const uint8_t p256_Gy[32] = "\x4f\xe3\x42\xe2\xfe\x1a\x7f\x9b\x8e\xe7\xeb\x4a\x7c\x0f\x9e\x16\x2b\xce\x33\x57\x6b\x31\x5e\xce\xcb\xb6\x40\x68\x37\xbf\x51\xf5";
uint8_t xz[32] = { 0 }, yz[32] = { 0 };
EcContext *ec_ctx;
EcPoint *g = NULL;
EcPoint **window = NULL;
int i, j;
unsigned n_tables, points_per_table, window_size;
ec_ws_new_context(&ec_ctx, p256_mod, b, order, 32, 0);
ec_ws_new_point(&g, p256_Gx, p256_Gy, 32, ec_ctx);
/** TODO: accept this as input **/
window_size = 5;
points_per_table = 1U << window_size;
n_tables = (256+window_size-1)/window_size;
/** Create table with points 0, G, 2G, 3G, .. (2**window_size-1)G **/
window = (EcPoint**)calloc(points_per_table, sizeof(EcPoint*));
ec_ws_new_point(&window[0], xz, yz, 32, ec_ctx);
for (i=1; i<points_per_table; i++) {
ec_ws_clone(&window[i], window[i-1]);
ec_ws_add(window[i], g);
}
printf("/* This file was automatically generated, do not edit */\n");
printf("#include \"common.h\"\n");
printf("#include \"p256_table.h\"\n");
printf("const unsigned p256_n_tables = %d;\n", n_tables);
printf("const unsigned p256_window_size = %d;\n", window_size);
printf("const unsigned p256_points_per_table = %d;\n", points_per_table);
printf("/* Affine coordinates in Montgomery form */\n");
printf("/* Table size: %u kbytes */\n", (unsigned)(n_tables*points_per_table*2*WORDS*sizeof(uint64_t)));
printf("const uint64_t p256_tables[%d][%d][2][4] = {\n", n_tables, points_per_table);
for (i=0; i<n_tables; i++) {
printf(" { /* Table #%u */\n", i);
for (j=0; j<points_per_table; j++) {
uint64_t xw[4], yw[4];
if (j == 0) {
memcpy(xw, xz, sizeof xw);
memcpy(yw, yz, sizeof yw);
} else {
ec_ws_normalize(window[j]);
memcpy(xw, window[j]->x, sizeof xw);
memcpy(yw, window[j]->y, sizeof yw);
}
printf(" { /* Point #%d */\n", j);
printf(" { ");
print_64bit_array(xw, 4);
printf(" },\n");
printf(" { ");
print_64bit_array(yw, 4);
printf(" }\n");
printf(" }%s\n", j==points_per_table-1 ? "" : ",");
}
printf(" }%s\n", i==n_tables-1 ? "" : ",");
/* Move from G to G*2^{w} */
for (j=0; j<window_size; j++)
ec_ws_double(g);
for (j=1; j<points_per_table; j++) {
ec_ws_copy(window[j], window[j-1]);
ec_ws_add(window[j], g);
}
}
printf("};\n");
for (i=0; i<points_per_table; i++) {
ec_ws_free_point(window[i]);
}
free(window);
ec_ws_free_point(g);
ec_free_context(ec_ctx);
return 0;
}

111
src/make_p384_table.c Normal file
View file

@ -0,0 +1,111 @@
#include "common.h"
#include "ec.h"
#include "endianess.h"
#define BITS 384
#define BYTES BITS/8
#define WORDS BITS/64
static void print_64bit_array(uint64_t *x, unsigned len)
{
unsigned i, j;
for (i=0; i<len; i++) {
printf("0x");
for (j=0; j<8; j++) {
printf("%02X", (uint8_t)(x[i] >> ((7-j)*8)));
}
printf("ULL");
if (i!=(len-1))
printf(",");
}
}
int main(void)
{
const uint8_t p384_mod[BYTES] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff";
const uint8_t b[BYTES] = "\xb3\x31\x2f\xa7\xe2\x3e\xe7\xe4\x98\x8e\x05\x6b\xe3\xf8\x2d\x19\x18\x1d\x9c\x6e\xfe\x81\x41\x12\x03\x14\x08\x8f\x50\x13\x87\x5a\xc6\x56\x39\x8d\x8a\x2e\xd1\x9d\x2a\x85\xc8\xed\xd3\xec\x2a\xef";
const uint8_t order[BYTES] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc7\x63\x4d\x81\xf4\x37\x2d\xdf\x58\x1a\x0d\xb2\x48\xb0\xa7\x7a\xec\xec\x19\x6a\xcc\xc5\x29\x73";
const uint8_t p384_Gx[BYTES] = "\xaa\x87\xca\x22\xbe\x8b\x05\x37\x8e\xb1\xc7\x1e\xf3\x20\xad\x74\x6e\x1d\x3b\x62\x8b\xa7\x9b\x98\x59\xf7\x41\xe0\x82\x54\x2a\x38\x55\x02\xf2\x5d\xbf\x55\x29\x6c\x3a\x54\x5e\x38\x72\x76\x0a\xb7";
const uint8_t p384_Gy[BYTES] = "\x36\x17\xde\x4a\x96\x26\x2c\x6f\x5d\x9e\x98\xbf\x92\x92\xdc\x29\xf8\xf4\x1d\xbd\x28\x9a\x14\x7c\xe9\xda\x31\x13\xb5\xf0\xb8\xc0\x0a\x60\xb1\xce\x1d\x7e\x81\x9d\x7a\x43\x1d\x7c\x90\xea\x0e\x5f";
uint8_t xz[BYTES] = { 0 }, yz[BYTES] = { 0 };
EcContext *ec_ctx;
EcPoint *g = NULL;
EcPoint **window = NULL;
int i, j;
unsigned n_tables, points_per_table, window_size;
ec_ws_new_context(&ec_ctx, p384_mod, b, order, BYTES, 0);
ec_ws_new_point(&g, p384_Gx, p384_Gy, BYTES, ec_ctx);
/** TODO: accept this as input **/
window_size = 5;
points_per_table = 1U << window_size;
n_tables = (BITS+window_size-1)/window_size;
/** Create table with points 0, G, 2G, 3G, .. (2**window_size-1)G **/
window = (EcPoint**)calloc(points_per_table, sizeof(EcPoint*));
ec_ws_new_point(&window[0], xz, yz, BYTES, ec_ctx);
for (i=1; i<points_per_table; i++) {
ec_ws_clone(&window[i], window[i-1]);
ec_ws_add(window[i], g);
}
printf("/* This file was automatically generated, do not edit */\n");
printf("#include \"common.h\"\n");
printf("#include \"p384_table.h\"\n");
printf("const unsigned p384_n_tables = %d;\n", n_tables);
printf("const unsigned p384_window_size = %d;\n", window_size);
printf("const unsigned p384_points_per_table = %d;\n", points_per_table);
printf("/* Affine coordinates in Montgomery form */\n");
printf("/* Table size: %u kbytes */\n", (unsigned)(n_tables*points_per_table*2*WORDS*sizeof(uint64_t)));
printf("const uint64_t p384_tables[%d][%d][2][%d] = {\n", n_tables, points_per_table, WORDS);
for (i=0; i<n_tables; i++) {
printf(" { /* Table #%u */\n", i);
for (j=0; j<points_per_table; j++) {
uint64_t xw[WORDS], yw[WORDS];
if (j == 0) {
memcpy(xw, xz, sizeof xw);
memcpy(yw, yz, sizeof yw);
} else {
ec_ws_normalize(window[j]);
memcpy(xw, window[j]->x, sizeof xw);
memcpy(yw, window[j]->y, sizeof yw);
}
printf(" { /* Point #%d */\n", j);
printf(" { ");
print_64bit_array(xw, 6);
printf(" },\n");
printf(" { ");
print_64bit_array(yw, 6);
printf(" }\n");
printf(" }%s\n", j==points_per_table-1 ? "" : ",");
}
printf(" }%s\n", i==n_tables-1 ? "" : ",");
/* Move from G to G*2^{w} */
for (j=0; j<window_size; j++)
ec_ws_double(g);
for (j=1; j<points_per_table; j++) {
ec_ws_copy(window[j], window[j-1]);
ec_ws_add(window[j], g);
}
}
printf("};\n");
for (i=0; i<points_per_table; i++) {
ec_ws_free_point(window[i]);
}
free(window);
ec_ws_free_point(g);
ec_free_context(ec_ctx);
return 0;
}

111
src/make_p521_table.c Normal file
View file

@ -0,0 +1,111 @@
#include "common.h"
#include "ec.h"
#include "endianess.h"
#define BITS 521
#define BYTES 66
#define WORDS 9
static void print_64bit_array(uint64_t *x, unsigned len)
{
unsigned i, j;
for (i=0; i<len; i++) {
printf("0x");
for (j=0; j<8; j++) {
printf("%02X", (uint8_t)(x[i] >> ((7-j)*8)));
}
printf("ULL");
if (i!=(len-1))
printf(",");
}
}
int main(void)
{
const uint8_t p521_mod[BYTES] = "\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
const uint8_t b[BYTES] = "\x00\x51\x95\x3e\xb9\x61\x8e\x1c\x9a\x1f\x92\x9a\x21\xa0\xb6\x85\x40\xee\xa2\xda\x72\x5b\x99\xb3\x15\xf3\xb8\xb4\x89\x91\x8e\xf1\x09\xe1\x56\x19\x39\x51\xec\x7e\x93\x7b\x16\x52\xc0\xbd\x3b\xb1\xbf\x07\x35\x73\xdf\x88\x3d\x2c\x34\xf1\xef\x45\x1f\xd4\x6b\x50\x3f\x00";
const uint8_t order[BYTES] = "\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfa\x51\x86\x87\x83\xbf\x2f\x96\x6b\x7f\xcc\x01\x48\xf7\x09\xa5\xd0\x3b\xb5\xc9\xb8\x89\x9c\x47\xae\xbb\x6f\xb7\x1e\x91\x38\x64\x09";
const uint8_t p521_Gx[BYTES] = "\x00\xc6\x85\x8e\x06\xb7\x04\x04\xe9\xcd\x9e\x3e\xcb\x66\x23\x95\xb4\x42\x9c\x64\x81\x39\x05\x3f\xb5\x21\xf8\x28\xaf\x60\x6b\x4d\x3d\xba\xa1\x4b\x5e\x77\xef\xe7\x59\x28\xfe\x1d\xc1\x27\xa2\xff\xa8\xde\x33\x48\xb3\xc1\x85\x6a\x42\x9b\xf9\x7e\x7e\x31\xc2\xe5\xbd\x66";
const uint8_t p521_Gy[BYTES] = "\x01\x18\x39\x29\x6a\x78\x9a\x3b\xc0\x04\x5c\x8a\x5f\xb4\x2c\x7d\x1b\xd9\x98\xf5\x44\x49\x57\x9b\x44\x68\x17\xaf\xbd\x17\x27\x3e\x66\x2c\x97\xee\x72\x99\x5e\xf4\x26\x40\xc5\x50\xb9\x01\x3f\xad\x07\x61\x35\x3c\x70\x86\xa2\x72\xc2\x40\x88\xbe\x94\x76\x9f\xd1\x66\x50";
uint8_t xz[WORDS*8] = { 0 }, yz[WORDS*8] = { 0 };
EcContext *ec_ctx;
EcPoint *g = NULL;
EcPoint **window = NULL;
int i, j;
unsigned n_tables, points_per_table, window_size;
ec_ws_new_context(&ec_ctx, p521_mod, b, order, BYTES, 0);
ec_ws_new_point(&g, p521_Gx, p521_Gy, BYTES, ec_ctx);
/** TODO: accept this as input **/
window_size = 4;
points_per_table = 1U << window_size;
n_tables = (BITS+window_size-1)/window_size;
/** Create table with points 0, G, 2G, 3G, .. (2**window_size-1)G **/
window = (EcPoint**)calloc(points_per_table, sizeof(EcPoint*));
ec_ws_new_point(&window[0], xz, yz, sizeof xz, ec_ctx);
for (i=1; i<points_per_table; i++) {
ec_ws_clone(&window[i], window[i-1]);
ec_ws_add(window[i], g);
}
printf("/* This file was automatically generated, do not edit */\n");
printf("#include \"common.h\"\n");
printf("#include \"p521_table.h\"\n");
printf("const unsigned p521_n_tables = %d;\n", n_tables);
printf("const unsigned p521_window_size = %d;\n", window_size);
printf("const unsigned p521_points_per_table = %d;\n", points_per_table);
printf("/* Affine coordinates in plain form (not Montgomery) */\n");
printf("/* Table size: %u kbytes */\n", (unsigned)(n_tables*points_per_table*2*WORDS*sizeof(uint64_t)));
printf("const uint64_t p521_tables[%d][%d][2][%d] = {\n", n_tables, points_per_table, WORDS);
for (i=0; i<n_tables; i++) {
printf(" { /* Table #%u */\n", i);
for (j=0; j<points_per_table; j++) {
uint64_t xw[WORDS], yw[WORDS];
if (j == 0) {
memcpy(xw, xz, sizeof xw);
memcpy(yw, yz, sizeof yw);
} else {
ec_ws_normalize(window[j]);
memcpy(xw, window[j]->x, sizeof xw);
memcpy(yw, window[j]->y, sizeof yw);
}
printf(" { /* Point #%d */\n", j);
printf(" { ");
print_64bit_array(xw, WORDS);
printf(" },\n");
printf(" { ");
print_64bit_array(yw, WORDS);
printf(" }\n");
printf(" }%s\n", j==points_per_table-1 ? "" : ",");
}
printf(" }%s\n", i==n_tables-1 ? "" : ",");
/* Move from G to G*2^{w} */
for (j=0; j<window_size; j++)
ec_ws_double(g);
for (j=1; j<points_per_table; j++) {
ec_ws_copy(window[j], window[j-1]);
ec_ws_add(window[j], g);
}
}
printf("};\n");
for (i=0; i<points_per_table; i++) {
ec_ws_free_point(window[i]);
}
free(window);
ec_ws_free_point(g);
ec_free_context(ec_ctx);
return 0;
}

581
src/mod25519.c Normal file
View file

@ -0,0 +1,581 @@
#ifndef __MOD25519_C
#define __MOD25519_C
#include "common.h"
#include "endianess.h"
#include "bignum.c"
/*
* Operations over the field of integers modulo 2² - 19
*/
#if defined(MASK26) || defined(MASK25) || defined(MASK13) || defined(MASK12)
#error Necessary macros are already defined
#endif
#define MASK26 ((1ULL<<26)-1)
#define MASK25 ((1ULL<<25)-1)
#define MASK13 ((1ULL<<13)-1)
#define MASK12 ((1ULL<<12)-1)
STATIC void reduce_25519_le64(uint64_t x[4]);
/*
* Convert a 256-bit number in radix 2 and little-endian mode,
* to mixed radix 2²/2², also little-endian.
*/
STATIC void convert_le64_to_le25p5(uint32_t out[10], const uint64_t in[4])
{
/** MSB of in[3] will be ignored */
out[0] = in[0] & MASK26; /* Fill 26 bits: 38 bits left in in[0] */
out[1] = (in[0] >> 26) & MASK25; /* Fill 25 bits: 13 bits left in in[0] */
out[2] = (uint32_t)((in[0] >> 51) | (in[1] & MASK13) << 13); /* Fill 26 bits: 51 bits left in in[1] */
out[3] = (in[1] >> 13) & MASK25; /* Fill 25 bits: 26 bits left in in[1] */
out[4] = (uint32_t)(in[1] >> 38); /* Fill 26 bits: no bits left in in[1] */
out[5] = in[2] & MASK25; /* Fill 25 bits: 39 bits left in in[2] */
out[6] = (in[2] >> 25) & MASK26; /* Fill 26 bits: 13 bits left in in[2] */
out[7] = (uint32_t)((in[2] >> 51) | (in[3] & MASK12) << 13); /* Fill 25 bits: 52 bits left in in[3] */
out[8] = (in[3] >> 12) & MASK26; /* Fill 26 bits: 26 bits left in in[3] */
out[9] = (uint32_t)(in[3] >> 38); /* Fill 26 bits in theory, 25 in practice */
}
/*
* Convert a 256-bit number in mixed radix 2²/2² and little-endian mode,
* to radix 2, also little-endian mode.
*
* The input limbs must fulfill the conditions:
* in[i] < 2² for even i (0, 2, 4, etc)
* in[j] < 2² for odd j<9 (1, 3, 5, etc)
* in[9] < 2²
*/
STATIC void convert_le25p5_to_le64(uint64_t out[4], const uint32_t in[10])
{
/** We assume that the 6 or 7 upper bits of in[] words is set to 0 */
assert(in[0] >> 26 == 0);
assert(in[1] >> 25 == 0);
assert(in[2] >> 26 == 0);
assert(in[3] >> 25 == 0);
assert(in[4] >> 26 == 0);
assert(in[5] >> 25 == 0);
assert(in[6] >> 26 == 0);
assert(in[7] >> 25 == 0);
assert(in[8] >> 26 == 0);
assert(in[9] >> 26 == 0);
out[0] = in[0] | (uint64_t)in[1] << 26 | ((uint64_t)in[2] & MASK13) << 51; /* 64 = 26 + 25 + 13: 13 bits left in in[2] */
out[1] = in[2] >> 13 | (uint64_t)in[3] << 13 | (uint64_t)in[4] << 38; /* 64 = 13 + 25 + 26: no bits left in[4] */
out[2] = in[5] | (uint64_t)in[6] << 25 | ((uint64_t)in[7] & MASK13) << 51; /* 64 = 25 + 26 + 13: 12 bits left in in[7] */
out[3] = in[7] >> 13 | (uint64_t)in[8] << 12 | (uint64_t)in[9] << 38; /* 64 = 12 + 26 + 26(!) */
}
/*
* Deserialize a 255-bit little-endian integer
* to mixed radix 2²/2², also little-endian.
*/
STATIC void convert_le8_to_le25p5(uint32_t out[10], const uint8_t in[32])
{
uint64_t in64[4];
unsigned i;
for (i=0; i<4; i++) {
in64[i] = LOAD_U64_LITTLE(&in[i*8]);
}
convert_le64_to_le25p5(out, in64);
}
/*
* Serialize a 256-bit mixed-radix 2²/2² little-endian integer
* into a little-endian byte string.
*
* The input limbs must fulfill the conditions:
* in[i] < 2² for even i (0, 2, 4, etc)
* in[j] < 2² for odd j<9 (1, 3, 5, etc)
* in[9] < 2²
*/
STATIC void convert_le25p5_to_le8(uint8_t out[32], const uint32_t in[10])
{
uint64_t out64[4];
unsigned i;
convert_le25p5_to_le64(out64, in);
reduce_25519_le64(out64);
for (i=0; i<4; i++) {
STORE_U64_LITTLE(&out[i*8], out64[i]);
}
}
/*
* Deserialize a 255-bit big-endian integer
* to mixed radix 2²/2² little-endian.
*/
STATIC void convert_be8_to_le25p5(uint32_t out[10], const uint8_t in[32])
{
uint64_t in64[4];
unsigned i;
for (i=0; i<4; i++) {
in64[3-i] = LOAD_U64_BIG(&in[i*8]);
}
convert_le64_to_le25p5(out, in64);
}
/*
* Serialize a 256-bit mixed-radix 2²/2² little-endian integer
* into a big-endian byte string.
*
* The input limbs must fulfill the conditions:
* in[i] < 2² for even i (0, 2, 4, etc)
* in[j] < 2² for odd j<9 (1, 3, 5, etc)
* in[9] < 2²
*/
STATIC void convert_le25p5_to_be8(uint8_t out[32], const uint32_t in[10])
{
uint64_t out64[4];
unsigned i;
convert_le25p5_to_le64(out64, in);
reduce_25519_le64(out64);
for (i=0; i<4; i++) {
STORE_U64_BIG(&out[i*8], out64[3-i]);
}
}
static int hex2bin(char in)
{
if ((in >= '0') && (in <= '9'))
return in - '0';
if ((in >= 'A') && (in <= 'F'))
return in - 'A' + 10;
if ((in >= 'a') && (in <= 'f'))
return in - 'a' + 10;
return -1;
}
static char bin2hex(uint8_t b)
{
if (b < 10)
return '0' + b;
return 'a' + b - 10;
}
/*
* Convert a big-endian hexadecimal number (up to 256 bits, and as a zero-terminated ASCII string)
* to mixed radix 2²/2², also little-endian.
*/
STATIC int convert_behex_to_le25p5(uint32_t out[10], const char *in)
{
size_t len;
uint8_t bin[32];
unsigned i;
if (in == NULL)
return ERR_NULL;
len = strlen(in);
if (len > 64)
return ERR_MAX_DATA;
if (len & 1)
return ERR_BLOCK_SIZE;
memset(bin, 0, sizeof bin);
for (i=0; i<len; i+=2) {
int c1, c2;
c1 = hex2bin(in[len-1-i]);
c2 = hex2bin(in[len-1-i-1]);
if ((c1 < 0) || (c2 < 0))
return ERR_VALUE;
bin[i/2] = c2 * 16 + c1;
}
convert_le8_to_le25p5(out, bin);
return 0;
}
/*
* Convert a number in mixed radix 2²/2², little-endian form
* into a new ASCII hex, zero-terminated string. The caller
* is responsible for deallocating the string.
*/
STATIC int convert_le25p5_to_behex(char **out, uint32_t in[10])
{
uint8_t bin[32];
unsigned i;
if (NULL == out)
return ERR_NULL;
convert_le25p5_to_le8(bin, in);
*out = calloc(64 + 1, 1);
for (i=0; i<32; i++) {
*(*out+64-1-i*2) = bin2hex(bin[i] & 0xF);
*(*out+64-2-i*2) = bin2hex(bin[i] >> 4);
}
return 0;
}
#if 0
static void print_le25p5(char *str, const uint32_t n[10])
{
uint8_t bin[32];
convert_le25p5_to_le8(bin, n);
printf("%s", str);
for (unsigned i=0; i<32; i++) {
printf("%02X", bin[i]);
}
printf("\n");
}
#endif
/*
* Reduce a 256-bit number (2 radix, litte-endian) modulo 2² - 19.
*/
STATIC void reduce_25519_le64(uint64_t x[4])
{
unsigned borrow;
uint64_t tmp1[4], tmp2[4];
static const uint64_t modulus[4] = { 0xffffffffffffffedULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL, 0x7fffffffffffffffULL };
borrow = sub(tmp1, x, modulus, 4);
mod_select(tmp2, x, tmp1, borrow, 4);
borrow = sub(tmp1, tmp2, modulus, 4);
mod_select(x, tmp2, tmp1, borrow, 4);
}
/*
* Multiply f[] and g[] modulo 2² - 19.
*
* The inputs f[] and g[] are encoded in mixed radix 2²/2² with limbs < 2².
*
* The result out[] is encoded in also mixed radix 2²/2² such that:
* f[i] < 2² for even i
* f[j] < 2² for odd j<9
* f[9] < 2²
*/
STATIC void mul_25519(uint32_t out[10], const uint32_t f[10], const uint32_t g[10])
{
uint64_t h0, h1, h2, h3, h4, h5, h6, h7, h8, h9;
uint64_t f0, f1, f2, f3, f4, f5, f6, f7, f8, f9;
uint64_t f1_38, f2_19, f3_19, f4_19, f5_19, f6_19, f7_19, f8_19, f9_19;
uint64_t g0, g1, g2, g3, g4, g5, g6, g7, g8, g9;
uint64_t carry;
f0 = f[0]; f1 = f[1]; f2 = f[2]; f3 = f[3]; f4 = f[4]; f5 = f[5]; f6 = f[6]; f7 = f[7]; f8 = f[8]; f9 = f[9];
g0 = g[0]; g1 = g[1]; g2 = g[2]; g3 = g[3]; g4 = g[4]; g5 = g[5]; g6 = g[6]; g7 = g[7]; g8 = g[8]; g9 = g[9];
f1_38 = (uint64_t)38*f[1];
f2_19 = (uint64_t)19*f[2];
f3_19 = (uint64_t)19*f[3];
f4_19 = (uint64_t)19*f[4];
f5_19 = (uint64_t)19*f[5];
f6_19 = (uint64_t)19*f[6];
f7_19 = (uint64_t)19*f[7];
f8_19 = (uint64_t)19*f[8];
f9_19 = (uint64_t)19*f[9];
/** input terms can the result of at most 4 additions **/
h0 = f0*g0 + f1_38*g9 + f2_19*g8 + 2*f3_19*g7 + f4_19*g6 +
2*f5_19*g5 + f6_19*g4 + 2*f7_19*g3 + f8_19*g2 + 2*f9_19*g1;
h1 = f0*g1 + f1*g0 + f2_19*g9 + f3_19*g8 + f4_19*g7 +
f5_19*g6 + f6_19*g5 + f7_19*g4 + f8_19*g3 + f9_19*g2;
h2 = f0*g2 + 2*f1*g1 + f2*g0 + 2*f3_19*g9 + f4_19*g8 +
2*f5_19*g7 + f6_19*g6 + 2*f7_19*g5 + f8_19*g4 + 2*f9_19*g3;
h3 = f0*g3 + f1*g2 + f2*g1 + f3*g0 + f4_19*g9 +
f5_19*g8 + f6_19*g7 + f7_19*g6 + f8_19*g5 + f9_19*g4;
h4 = f0*g4 + 2*f1*g3 + f2*g2 + 2*f3*g1 + f4*g0 +
2*f5_19*g9 + f6_19*g8 + 2*f7_19*g7 + f8_19*g6 + 2*f9_19*g5;
h5 = f0*g5 + f1*g4 + f2*g3 + f3*g2 + f4*g1 +
f5*g0 + f6_19*g9 + f7_19*g8 + f8_19*g7 + f9_19*g6;
h6 = f0*g6 + 2*f1*g5 + f2*g4 + 2*f3*g3 + f4*g2 +
2*f5*g1 + f6*g0 + 2*f7_19*g9 + f8_19*g8 + 2*f9_19*g7;
h7 = f0*g7 + f1*g6 + f2*g5 + f3*g4 + f4*g3 +
f5*g2 + f6*g1 + f7*g0 + f8_19*g9 + f9_19*g8;
h8 = f0*g8 + 2*f1*g7 + f2*g6 + 2*f3*g5 + f4*g4 +
2*f5*g3 + f6*g2 + 2*f7*g1 + f8*g0 + 2*f9_19*g9;
h9 = f0*g9 + f1*g8 + f2*g7 + f3*g6 + f4*g5 +
f5*g4 + f6*g3 + f7*g2 + f8*g1 + f9*g0;
/* h0..h9 < 2⁶³ */
carry = h8 >> 26;
h8 &= MASK26;
/* carry < 2³⁷ */
h9 += carry;
carry = (h9 >> 25)*19;
h9 &= MASK25;
/* carry < 2⁴⁴ */
h0 += carry;
carry = h0 >> 26;
h0 &= MASK26;
/* carry < 2³⁸ */
h1 += carry;
carry = h1 >> 25;
h1 &= MASK25;
/* carry < 2³⁹ */
h2 += carry;
carry = h2 >> 26;
h2 &= MASK26;
/* carry < 2³⁸ */
h3 += carry;
carry = h3 >> 25;
h3 &= MASK25;
/* carry < 2³⁹ */
h4 += carry;
carry = h4 >> 26;
h4 &= MASK26;
/* carry < 2³⁸ */
h5 += carry;
carry = h5 >> 25;
h5 &= MASK25;
/* carry < 2³⁹ */
h6 += carry;
carry = h6 >> 26;
h6 &= MASK26;
/* carry < 2³⁸ */
h7 += carry;
carry = h7 >> 25;
h7 &= MASK25;
/* carry < 2³⁹ */
h8 += carry;
carry = h8 >> 26;
h8 &= MASK26;
/* carry < 2¹⁴ */
h9 += carry;
/* h9 < 2²⁶ */
out[0] = (uint32_t)h0;
out[1] = (uint32_t)h1;
out[2] = (uint32_t)h2;
out[3] = (uint32_t)h3;
out[4] = (uint32_t)h4;
out[5] = (uint32_t)h5;
out[6] = (uint32_t)h6;
out[7] = (uint32_t)h7;
out[8] = (uint32_t)h8;
out[9] = (uint32_t)h9;
}
/*
* Compute addition for mixed-radix 2²/2².
* If the biggest input limb fits into x bits (x<32), the biggest output limb will fit into (x+1) bits.
*/
STATIC void add32(uint32_t out[10], const uint32_t a[10], const uint32_t b[10])
{
unsigned i;
for (i=0; i<10; i++) {
out[i] = a[i] + b[i];
}
}
/*
* Carry out subtraction a[] - b[] for mixed-radix 2²/2² where all limbs are < 2².
* Output limbs are < 2².
*/
STATIC void sub32(uint32_t out[10], const uint32_t a[10], const uint32_t b[10])
{
/*
* We pre-sum a number which is >= 2²-1 for each limb, and which is congruent to zero modulo 2²-19
*/
static const uint32_t modulus_32[10] = { 0x7ffffda, 0x3fffffe, 0x7fffffe, 0x3fffffe, 0x7fffffe, 0x3fffffe, 0x7fffffe, 0x3fffffe, 0x7fffffe, 0x3fffffe };
unsigned i;
for (i=0; i<10; i++) {
out[i] = modulus_32[i] + a[i] - b[i];
}
}
/*
* Swap arguments a/c and b/d when condition is NOT ZERO.
* If the condition IS ZERO, no swapping takes place.
*/
STATIC void cswap(uint32_t a[10], uint32_t b[10], uint32_t c[10], uint32_t d[10], unsigned swap)
{
uint32_t mask, i, e, f;
mask = (uint32_t)(0 - (swap!=0)); /* 0 if swap is 0, all 1s if swap is !=0 */
for (i=0; i<10; i++) {
e = mask & (a[i] ^ c[i]);
a[i] ^= e;
c[i] ^= e;
f = mask & (b[i] ^ d[i]);
b[i] ^= f;
d[i] ^= f;
}
}
/*
* Compute x¹ in prime field 2² - 19
*
* The input x[] is encoded in mixed radix 2²/2² with limbs < 2².
*
* The result out[] is encoded in also mixed radix 2²/2² such that:
* out[i] < 2² for even i
* out[j] < 2² for odd j<9
* out[9] < 2²
*/
STATIC void invert_25519(uint32_t out[10], const uint32_t x[10])
{
uint32_t a[10], x1[10], x3p0[10], x3p1p0[10], x5m0[10];
uint32_t x10m0[10], x20m0[10], x50m0[10], x100m0[10];
unsigned i;
#define sqr_25519(a,b) mul_25519(a,b,b)
sqr_25519(x1, x); /* 2¹ */
sqr_25519(a, x1); /* 2² */
sqr_25519(a, a); /* 2³ */
mul_25519(x3p0, a, x); /* 2³ + 2⁰ */
mul_25519(x3p1p0, x3p0, x1);/* 2³ + 2¹ + 2⁰ = 11 */
sqr_25519(a, x3p1p0); /* 2⁴ + 2² + 2¹ */
mul_25519(x5m0, a, x3p0); /* 2⁴ + 2³ + 2² + 2¹ + 2⁰ = 2⁵ - 2⁰ */
sqr_25519(a, x5m0); /* 2⁶ - 2¹ */
sqr_25519(a, a); /* 2⁷ - 2² */
sqr_25519(a, a); /* 2⁸ - 2³ */
sqr_25519(a, a); /* 2⁹ - 2⁴ */
sqr_25519(a, a); /* 2¹⁰ - 2⁵ */
mul_25519(a, a, x5m0); /* 2¹⁰ - 2⁰ */
memcpy(x10m0, a, sizeof a);
for (i=0; i<10; i++) {
sqr_25519(a, a);
} /* 2²⁰ - 2¹⁰ */
mul_25519(a, a, x10m0); /* 2²⁰ - 2⁰ */
memcpy(x20m0, a, sizeof a);
for (i=0; i<20; i++) {
sqr_25519(a, a);
} /* 2⁴⁰ - 2²⁰ */
mul_25519(a, a, x20m0); /* 2⁴⁰ - 2⁰ */
for (i=0; i<10; i++) {
sqr_25519(a, a);
} /* 2⁵⁰ - 2¹⁰ */
mul_25519(a, a, x10m0); /* 2⁵⁰ - 2⁰ */
memcpy(x50m0, a, sizeof a);
for (i=0; i<50; i++) {
sqr_25519(a, a);
} /* 2¹⁰⁰ - 2⁵⁰ */
mul_25519(a, a, x50m0); /* 2¹⁰⁰ - 2⁰ */
memcpy(x100m0, a, sizeof a);
for (i=0; i<100; i++) {
sqr_25519(a, a);
} /* 2²⁰⁰ - 2¹⁰⁰ */
mul_25519(a, a, x100m0); /* 2²⁰⁰ - 2⁰ */
for (i=0; i<50; i++) {
sqr_25519(a, a);
} /* 2²⁵⁰ - 2⁵⁰ */
mul_25519(a, a, x50m0); /* 2²⁵⁰ - 2⁰ */
sqr_25519(a, a); /* 2²⁵¹ - 2¹ */
sqr_25519(a, a); /* 2²⁵² - 2² */
sqr_25519(a, a); /* 2²⁵³ - 2³ */
sqr_25519(a, a); /* 2²⁵⁴ - 2⁴ */
sqr_25519(a, a); /* 2²⁵⁵ - 2⁵ = 2²⁵⁵ - 32 */
mul_25519(out, a, x3p1p0); /* 2²⁵⁵ - 21 */
}
/*
* Add f[] and g[] modulo 2² - 19.
*
* f[] and g[] are encoded in radix 2²/2² and each limb is < 2².
*
* The result out[] is encoded in also radix 2²/2² such that:
* out[i] < 2² for even i
* out[j] < 2² for odd j<9
* out[9] < 2²
*/
STATIC void add_25519(uint32_t out[10], const uint32_t f[10], const uint32_t g[10])
{
uint64_t h0, h1, h2, h3, h4, h5, h6, h7, h8, h9;
uint64_t carry;
h0 = f[0] + g[0];
h1 = f[1] + g[1];
h2 = f[2] + g[2];
h3 = f[3] + g[3];
h4 = f[4] + g[4];
h5 = f[5] + g[5];
h6 = f[6] + g[6];
h7 = f[7] + g[7];
h8 = f[8] + g[8];
h9 = f[9] + g[9];
/* h0..h9 < 2²⁸ */
carry = h8 >> 26;
h8 &= MASK26;
/* carry < 2² */
h9 += carry;
carry = (h9 >> 25)*19;
h9 &= MASK25;
/* carry < 2⁹ */
h0 += carry;
carry = h0 >> 26;
h0 &= MASK26;
/* carry < 2³ */
h1 += carry;
carry = h1 >> 25;
h1 &= MASK25;
/* carry < 2⁴ */
h2 += carry;
carry = h2 >> 26;
h2 &= MASK26;
/* carry < 2³ */
h3 += carry;
carry = h3 >> 25;
h3 &= MASK25;
/* carry < 2⁴ */
h4 += carry;
carry = h4 >> 26;
h4 &= MASK26;
/* carry < 2³ */
h5 += carry;
carry = h5 >> 25;
h5 &= MASK25;
/* carry < 2⁴ */
h6 += carry;
carry = h6 >> 26;
h6 &= MASK26;
/* carry < 2³ */
h7 += carry;
carry = h7 >> 25;
h7 &= MASK25;
/* carry < 2⁴ */
h8 += carry;
carry = h8 >> 26;
h8 &= MASK26;
/* carry < 2¹ */
h9 += carry;
/* h9 < 2²⁶ */
out[0] = (uint32_t)h0;
out[1] = (uint32_t)h1;
out[2] = (uint32_t)h2;
out[3] = (uint32_t)h3;
out[4] = (uint32_t)h4;
out[5] = (uint32_t)h5;
out[6] = (uint32_t)h6;
out[7] = (uint32_t)h7;
out[8] = (uint32_t)h8;
out[9] = (uint32_t)h9;
}
/*
* Reduce a 256-bit number (mixed radix 2²/², litte-endian) modulo 2² - 19.
*
* Each limb of x[] in input is < 2² (for instance, as result of sub32).
*
* The result x[] in output is such that:
* x[i] < 2² for even i
* x[j] < 2² for odd j<9
* x[9] < 2²
*/
STATIC void reduce_25519_le25p5(uint32_t x[10])
{
uint32_t zero[10] = { 0 };
add_25519(x, x, zero);
assert((x[0] >> 26) == 0);
}
#endif /* __MOD25519_C */

View file

@ -58,15 +58,7 @@
#endif #endif
#endif #endif
static inline unsigned is_odd(uint64_t x) #include "bignum.c"
{
return 1 == (x & 1);
}
static inline unsigned is_even(uint64_t x)
{
return !is_odd(x);
}
/** /**
* Compute the inverse modulo 2 of a 64-bit odd integer. * Compute the inverse modulo 2 of a 64-bit odd integer.
@ -88,61 +80,6 @@ STATIC uint64_t inverse64(uint64_t a)
return x; return x;
} }
/**
* Check if a multi-word integer x is greater than or equal to y.
*
* @param x The first term
* @param y The second term
* @param nw The number of words that make up x and y
* @return 1 if x>=y, 0 if x<y
*/
STATIC int ge(const uint64_t *x, const uint64_t *y, size_t nw)
{
unsigned mask = (unsigned)-1;
unsigned result = 0;
size_t i, j;
i = nw - 1;
for (j=0; j<nw; j++, i--) {
unsigned greater, lower;
greater = x[i] > y[i];
lower = x[i] < y[i];
result |= mask & (greater | (lower << 1));
mask &= (greater ^ lower) - 1;
}
return result<2;
}
/*
* Subtract a multi-word integer b from a.
*
* @param out The location where the multi-word result is stored
* @param a Number to subtract from
* @param b Number to subtract
* @param nw The number of words of both a and b
* @result 0 if there is no borrow, 1 otherwise
*/
STATIC unsigned sub(uint64_t *out, const uint64_t *a, const uint64_t *b, size_t nw)
{
size_t i;
unsigned borrow1 , borrow2;
borrow2 = 0;
for (i=0; i<nw; i++) {
borrow1 = b[i] > a[i];
out[i] = a[i] - b[i];
borrow1 |= borrow2 > out[i];
out[i] -= borrow2;
borrow2 = borrow1;
}
return borrow2;
}
/* /*
* Compute R² mod N, where R is the smallest power of 2 larger than N. * Compute R² mod N, where R is the smallest power of 2 larger than N.
* *
@ -183,161 +120,6 @@ STATIC void rsquare(uint64_t *r2_mod_n, uint64_t *n, size_t nw)
} }
} }
/*
* Multiply a multi-word integer a by a 64-bit scalar k and
* then add the result to the multi-word integer t.
*
* @param t The multi-word integer accumulator
* @param tw The number of words of t
* @param a The multi-word integer to multiply with the scalar
* @param aw The number of words of a
* @param k The 64-bit scalar multiplier
*/
STATIC void addmul(uint64_t *t, size_t tw, const uint64_t *a, size_t aw, uint64_t k)
{
size_t i;
uint64_t carry;
carry = 0;
for (i=0; i<aw; i++) {
uint64_t prod_lo, prod_hi;
DP_MULT(a[i], k, prod_lo, prod_hi);
prod_lo += carry;
prod_hi += prod_lo < carry;
t[i] += prod_lo;
prod_hi += t[i] < prod_lo;
carry = prod_hi;
}
for (; carry; i++) {
t[i] += carry;
carry = t[i] < carry;
}
assert(i <= tw);
}
/**
* Multiply two multi-word integers.
*
* @param t The location where the result is stored. It is twice as big as
* either a (or b). It is an array of 2*nw words).
* @param scratchpad Temporary area. It is an array of 3*nw words.
* @param a The first term, array of nw words.
* @param b The second term, array of nw words.
* @param nw The number of words of both a and b.
*
*/
STATIC void product(uint64_t *t, uint64_t *scratchpad, const uint64_t *a, const uint64_t *b, size_t nw)
{
size_t i;
memset(t, 0, 2*sizeof(uint64_t)*nw);
for (i=0; i<(nw ^ (nw & 1)); i+=2) {
addmul128(&t[i], scratchpad, a, b[i], b[i+1], 2*nw-i, nw);
}
if (is_odd(nw)) {
addmul(&t[nw-1], nw+2, a, nw, b[nw-1]);
}
}
/*
* Select a number out of two, in constant time.
*
* @param out The location where the multi-word result is stored
* @param a The first choice, selected if cond is true (non-zero)
* @param b The second choice, selected if cond is false (zero)
* @param cond The flag that drives the selection
* @param words The number of words of a, b, and out
* @return 0 for success, the appropriate code otherwise.
*/
STATIC FUNC_SSE2 int mont_select(uint64_t *out, const uint64_t *a, const uint64_t *b, unsigned cond, size_t words)
{
uint64_t mask;
#if defined(USE_SSE2)
unsigned pairs, i;
__m128i r0, r1, r2, r3, r4, r5;
pairs = (unsigned)words / 2;
mask = (uint64_t)((cond != 0) - 1); /* 0 for a, 1s for b */
#if SYSBITS == 64
r0 = _mm_set1_epi64x(mask);
#else
r0 = _mm_loadl_epi64((__m128i*)&mask);
r0 = _mm_unpacklo_epi64(r0, r0);
#endif
for (i=0; i<pairs; i++, a+=2, b+=2, out+=2) {
r1 = _mm_loadu_si128((__m128i const*)b);
r2 = _mm_loadu_si128((__m128i const*)a);
r3 = _mm_and_si128(r0, r1);
r4 = _mm_andnot_si128(r0, r2);
r5 = _mm_or_si128(r3, r4);
_mm_storeu_si128((__m128i*)out, r5);
}
if (words & 1) {
*out = (*b & mask) ^ (*a & ~mask);
}
#else
unsigned i;
mask = (uint64_t)((cond != 0) - 1);
for (i=0; i<words; i++) {
*out++ = (*b++ & mask) ^ (*a++ & ~mask);
}
#endif
return 0;
}
/*
* Add two multi-word numbers with modulo arithmetic.
*
* @param out The locaton where the multi-word result (nw words) is stored
* @param a The first term (nw words)
* @param b The second term (nw words)
* @param modulus The modulus (nw words)
* @param tmp1 A temporary area (nw words)
* @param tmp2 A temporary area (nw words)
* @param nw The number of 64-bit words in all parameters
*/
void add_mod(uint64_t* out, const uint64_t* a, const uint64_t* b, const uint64_t *modulus, uint64_t *tmp1, uint64_t *tmp2, size_t nw)
{
unsigned i;
unsigned carry, borrow1, borrow2;
/*
* Compute sum in tmp1[], and subtract modulus[]
* from tmp1[] into tmp2[].
*/
borrow2 = 0;
for (i=0, carry=0; i<nw; i++) {
tmp1[i] = a[i] + carry;
carry = tmp1[i] < carry;
tmp1[i] += b[i];
carry += tmp1[i] < b[i];
borrow1 = modulus[i] > tmp1[i];
tmp2[i] = tmp1[i] - modulus[i];
borrow1 |= borrow2 > tmp2[i];
tmp2[i] -= borrow2;
borrow2 = borrow1;
}
/*
* If there is no borrow or if there is carry,
* tmp1[] is larger than modulus, so we must return tmp2[].
*/
mont_select(out, tmp2, tmp1, carry | (borrow2 ^ 1), nw);
}
/* /*
* Montgomery modular multiplication, that is a*b*R mod N. * Montgomery modular multiplication, that is a*b*R mod N.
* *
@ -413,7 +195,7 @@ STATIC void mont_mult_generic(uint64_t *out, const uint64_t *a, const uint64_t *
/** Divide by R and possibly subtract n **/ /** Divide by R and possibly subtract n **/
sub(t2, &t[nw], n, nw); sub(t2, &t[nw], n, nw);
cond = (unsigned)(t[2*nw] | (uint64_t)ge(&t[nw], n, nw)); cond = (unsigned)(t[2*nw] | (uint64_t)ge(&t[nw], n, nw));
mont_select(out, t2, &t[nw], cond, (unsigned)nw); mod_select(out, t2, &t[nw], cond, (unsigned)nw);
} }
STATIC void mont_mult_p256(uint64_t *out, const uint64_t *a, const uint64_t *b, const uint64_t *n, uint64_t m0, uint64_t *tmp, size_t nw) STATIC void mont_mult_p256(uint64_t *out, const uint64_t *a, const uint64_t *b, const uint64_t *n, uint64_t m0, uint64_t *tmp, size_t nw)
@ -552,7 +334,7 @@ STATIC void mont_mult_p256(uint64_t *out, const uint64_t *a, const uint64_t *b,
/** Divide by R and possibly subtract n **/ /** Divide by R and possibly subtract n **/
sub(t2, &t[nw], n, WORDS_64); sub(t2, &t[nw], n, WORDS_64);
cond = (unsigned)(t[PREDIV_WORDS_64-1] | (uint64_t)ge(&t[WORDS_64], n, WORDS_64)); cond = (unsigned)(t[PREDIV_WORDS_64-1] | (uint64_t)ge(&t[WORDS_64], n, WORDS_64));
mont_select(out, t2, &t[WORDS_64], cond, WORDS_64); mod_select(out, t2, &t[WORDS_64], cond, WORDS_64);
#undef WORDS_64 #undef WORDS_64
#undef PREDIV_WORDS_64 #undef PREDIV_WORDS_64
@ -735,7 +517,7 @@ STATIC void mont_mult_p384(uint64_t *out, const uint64_t *a, const uint64_t *b,
/** Divide by R and possibly subtract n **/ /** Divide by R and possibly subtract n **/
sub(t2, &t[WORDS_64], n, WORDS_64); sub(t2, &t[WORDS_64], n, WORDS_64);
cond = (unsigned)(t[PREDIV_WORDS_64-1] | (uint64_t)ge(&t[WORDS_64], n, WORDS_64)); cond = (unsigned)(t[PREDIV_WORDS_64-1] | (uint64_t)ge(&t[WORDS_64], n, WORDS_64));
mont_select(out, t2, &t[WORDS_64], cond, WORDS_64); mod_select(out, t2, &t[WORDS_64], cond, WORDS_64);
#undef WORDS_64 #undef WORDS_64
#undef PREDIV_WORDS_64 #undef PREDIV_WORDS_64
@ -1044,40 +826,10 @@ int mont_mult(uint64_t* out, const uint64_t* a, const uint64_t *b, uint64_t *tmp
*/ */
int mont_sub(uint64_t *out, const uint64_t *a, const uint64_t *b, uint64_t *tmp, const MontContext *ctx) int mont_sub(uint64_t *out, const uint64_t *a, const uint64_t *b, uint64_t *tmp, const MontContext *ctx)
{ {
unsigned i;
unsigned carry, borrow1 , borrow2;
uint64_t *scratchpad;
if (NULL == out || NULL == a || NULL == b || NULL == tmp || NULL == ctx) if (NULL == out || NULL == a || NULL == b || NULL == tmp || NULL == ctx)
return ERR_NULL; return ERR_NULL;
scratchpad = tmp + ctx->words; return sub_mod(out, a, b, ctx->modulus, tmp, tmp + ctx->words, ctx->words);
/*
* Compute difference in tmp[], and add modulus[]
* to tmp[] into scratchpad[].
*/
borrow2 = 0;
carry = 0;
for (i=0; i<ctx->words; i++) {
borrow1 = b[i] > a[i];
tmp[i] = a[i] - b[i];
borrow1 |= borrow2 > tmp[i];
tmp[i] -= borrow2;
borrow2 = borrow1;
scratchpad[i] = tmp[i] + carry;
carry = scratchpad[i] < carry;
scratchpad[i] += ctx->modulus[i];
carry += scratchpad[i] < ctx->modulus[i];
}
/*
* If there is no borrow, tmp[] is smaller than modulus.
*/
mont_select(out, scratchpad, tmp, borrow2, ctx->words);
return 0;
} }
/* /*

View file

@ -6,7 +6,8 @@ CFLAGS += -O3 -g -fstrict-aliasing -Wall -Werror
TAPPS:=tests_addmul128_32 tests_addmul128_64 tests_square_32 tests_square_64 tests_product tests_addmul test_endianess\ TAPPS:=tests_addmul128_32 tests_addmul128_64 tests_square_32 tests_square_64 tests_product tests_addmul test_endianess\
test_poly1305_reduce test_poly1305_load_r test_poly1305_load_m test_poly1305_multiply test_poly1305_accumulate\ test_poly1305_reduce test_poly1305_load_r test_poly1305_load_m test_poly1305_multiply test_poly1305_accumulate\
test_mont tests_mont_mult tests_ec_ws_64 tests_ec_ws_32 test_pkcs1 test_mont tests_mont_mult tests_ec_ws_64 tests_ec_ws_32 test_pkcs1\
test_mod25519 test_x25519 tests_mul_25519 tests_ladder_step test_ed25519
ifneq (,$(filter $(shell uname -m),x86_64 i386 i686)) ifneq (,$(filter $(shell uname -m),x86_64 i386 i686))
CPPFLAGS += -DHAVE_X86INTRIN_H -DUSE_SSE2 -DHAVE_WMMINTRIN_H -DHAVE_TMMINTRIN_H CPPFLAGS += -DHAVE_X86INTRIN_H -DUSE_SSE2 -DHAVE_WMMINTRIN_H -DHAVE_TMMINTRIN_H
@ -33,7 +34,7 @@ build:
all: ${$TGTS} all: ${$TGTS}
clean: clean:
rm -fr build common.pyc rm -fr build common.pyc __pycache__
# utils # utils
@ -44,7 +45,6 @@ build/modexp_utils.o: ../modexp_utils.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $^ $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $^
# ECC # ECC
TABLES = build/p256_table.o build/p384_table.o build/p521_table.o TABLES = build/p256_table.o build/p384_table.o build/p521_table.o
build/p256_table.o: ../p256_table.c build/p256_table.o: ../p256_table.c
@ -164,3 +164,25 @@ build/tests_mont_mult: build/tests_mont_mult.c build/mont_32.o $(UTILS)
# pkcs1 # pkcs1
build/test_pkcs1: test_pkcs1.c ../common.h ../pkcs1_decode.c build/test_pkcs1: test_pkcs1.c ../common.h ../pkcs1_decode.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $^
# Curve25519
build/test_mod25519: test_mod25519.c ../mod25519.c ../multiply_32.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
build/tests_mul_25519.c: make_tests_mul_25519.py
$(PYTHON) $^ > $@
build/tests_mul_25519: build/tests_mul_25519.c ../mod25519.c ../multiply_32.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
build/test_x25519: test_x25519.c ../x25519.c ../multiply_32.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
build/tests_ladder_step.c: make_tests_ladder_step.py
$(PYTHON) $^ > $@
build/tests_ladder_step: build/tests_ladder_step.c ../x25519.c ../multiply_32.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
build/test_ed25519: test_ed25519.c ../ed25519.c ../multiply_32.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^

View file

@ -19,7 +19,7 @@ def make_test(t, a, b0, b1):
# Computation does not depend on zero terms # Computation does not depend on zero terms
result_len = max(len(result), 2 + len(a)) result_len = max(len(result), 2 + len(a))
# Pad the output vector with as many padding zeroes as needed # Pad the output vector with as many padding zeroes as needed
for x in range(result_len - len(t_in)): for x in range(result_len - len(t_in)):
t_in.append("0") t_in.append("0")

View file

@ -0,0 +1,182 @@
"""Make unit test for ladder_step() in x25519.c"""
from common import counter, make_main, split64, bin2int
from hashlib import sha256
import struct
def ref(x2, z2, x3, z3, x1):
mod = 2**255 - 19
x4 = (x2**2 - z2**2)**2
z4 = 4*x2*z2*(x2**2 + 486662*x2*z2 + z2**2)
x5 = 4*((x2*x3 - z2*z3)**2)
z5 = 4*((x2*z3 - z2*x3)**2)*x1
return x4 % mod, z4 % mod, x5 % mod, z5 % mod
def make_test_max():
v = ["0x%08X" % (2**26-1)] * 10
modulus = 2**255 - 19
base = [0, 26, 51, 77, 102, 128, 153, 179, 204, 230]
n = 0
for i in range(10):
n += (2**26 - 1) * (2**(base[i]))
n %= modulus
x2_out, z2_out, x3_out, z3_out = ref(n, n, n, n, n)
# Output
words = split64(x2_out)
x2outx = words + ["0"] * (4 - len(words))
words = split64(z2_out)
z2outx = words + ["0"] * (4 - len(words))
words = split64(x3_out)
x3outx = words + ["0"] * (4 - len(words))
words = split64(z3_out)
z3outx = words + ["0"] * (4 - len(words))
print("")
print("void test_%d() {" % next(counter))
print(" uint32_t x2[10] = { " + ",".join(v) + " };")
print(" uint32_t z2[10] = { " + ",".join(v) + " };")
print(" uint32_t x3[10] = { " + ",".join(v) + " };")
print(" uint32_t z3[10] = { " + ",".join(v) + " };")
print(" uint32_t xp[10] = { " + ",".join(v) + " };")
print(" const uint64_t x2_out_ref[4] = {" + ", ".join(x2outx) + "};")
print(" const uint64_t z2_out_ref[4] = {" + ", ".join(z2outx) + "};")
print(" const uint64_t x3_out_ref[4] = {" + ", ".join(x3outx) + "};")
print(" const uint64_t z3_out_ref[4] = {" + ", ".join(z3outx) + "};")
print(" uint64_t x2_out[4] = { 0 };")
print(" uint64_t z2_out[4] = { 0 };")
print(" uint64_t x3_out[4] = { 0 };")
print(" uint64_t z3_out[4] = { 0 };")
print("")
print(" ladder_step(x2, z2, x3, z3, xp);")
print(" convert_le25p5_to_le64(x2_out, x2);")
print(" convert_le25p5_to_le64(z2_out, z2);")
print(" convert_le25p5_to_le64(x3_out, x3);")
print(" convert_le25p5_to_le64(z3_out, z3);")
print(" reduce_25519_le64(x2_out);")
print(" reduce_25519_le64(z2_out);")
print(" reduce_25519_le64(x3_out);")
print(" reduce_25519_le64(z3_out);")
print("")
print(" assert(0 == memcmp(x2_out, x2_out_ref, sizeof x2_out));")
print(" assert(0 == memcmp(z2_out, z2_out_ref, sizeof z2_out));")
print(" assert(0 == memcmp(x3_out, x3_out_ref, sizeof x3_out));")
print(" assert(0 == memcmp(z3_out, z3_out_ref, sizeof z3_out));")
print("}")
def make_test(x2, z2, x3, z3, xp):
x2_out, z2_out, x3_out, z3_out = ref(x2, z2, x3, z3, xp)
# Input
words = split64(x2)
x2x = words + ["0"] * (4 - len(words))
words = split64(z2)
z2x = words + ["0"] * (4 - len(words))
words = split64(x3)
x3x = words + ["0"] * (4 - len(words))
words = split64(z3)
z3x = words + ["0"] * (4 - len(words))
words = split64(xp)
xpx = words + ["0"] * (4 - len(words))
# Output
words = split64(x2_out)
x2outx = words + ["0"] * (4 - len(words))
words = split64(z2_out)
z2outx = words + ["0"] * (4 - len(words))
words = split64(x3_out)
x3outx = words + ["0"] * (4 - len(words))
words = split64(z3_out)
z3outx = words + ["0"] * (4 - len(words))
print("")
print("void test_%d() {" % next(counter))
print(" const uint64_t x2_in[4] = {" + ", ".join(x2x) + "};")
print(" const uint64_t z2_in[4] = {" + ", ".join(z2x) + "};")
print(" const uint64_t x3_in[4] = {" + ", ".join(x3x) + "};")
print(" const uint64_t z3_in[4] = {" + ", ".join(z3x) + "};")
print(" const uint64_t xp_in[4] = {" + ", ".join(xpx) + "};")
print(" const uint64_t x2_out_ref[4] = {" + ", ".join(x2outx) + "};")
print(" const uint64_t z2_out_ref[4] = {" + ", ".join(z2outx) + "};")
print(" const uint64_t x3_out_ref[4] = {" + ", ".join(x3outx) + "};")
print(" const uint64_t z3_out_ref[4] = {" + ", ".join(z3outx) + "};")
print(" uint32_t x2[10] = { 0 };")
print(" uint32_t z2[10] = { 0 };")
print(" uint32_t x3[10] = { 0 };")
print(" uint32_t z3[10] = { 0 };")
print(" uint32_t xp[10] = { 0 };")
print(" uint64_t x2_out[4] = { 0 };")
print(" uint64_t z2_out[4] = { 0 };")
print(" uint64_t x3_out[4] = { 0 };")
print(" uint64_t z3_out[4] = { 0 };")
print("")
print(" convert_le64_to_le25p5(x2, x2_in);")
print(" convert_le64_to_le25p5(z2, z2_in);")
print(" convert_le64_to_le25p5(x3, x3_in);")
print(" convert_le64_to_le25p5(z3, z3_in);")
print(" convert_le64_to_le25p5(xp, xp_in);")
print(" ladder_step(x2, z2, x3, z3, xp);")
print(" convert_le25p5_to_le64(x2_out, x2);")
print(" convert_le25p5_to_le64(z2_out, z2);")
print(" convert_le25p5_to_le64(x3_out, x3);")
print(" convert_le25p5_to_le64(z3_out, z3);")
print(" reduce_25519_le64(x2_out);")
print(" reduce_25519_le64(z2_out);")
print(" reduce_25519_le64(x3_out);")
print(" reduce_25519_le64(z3_out);")
print("")
print(" assert(0 == memcmp(x2_out, x2_out_ref, sizeof x2_out));")
print(" assert(0 == memcmp(z2_out, z2_out_ref, sizeof z2_out));")
print(" assert(0 == memcmp(x3_out, x3_out_ref, sizeof x3_out));")
print(" assert(0 == memcmp(z3_out, z3_out_ref, sizeof z3_out));")
print("}")
def make_limb(seed):
result = bin2int(sha256(struct.pack(">I", seed)).digest()) & ((2**255)-1)
return result
print("#include <assert.h>")
print("#include <string.h>")
print("#include <stdint.h>")
print("#include <stdio.h>")
print("void convert_le25p5_to_le64(uint64_t out[4], const uint32_t in[10]);")
print("void convert_le64_to_le25p5(uint32_t out[10], const uint64_t in[4]);")
print("void reduce_25519_le64(uint64_t x[4]);")
print("void ladder_step(uint32_t x2[10], uint32_t z2[10], uint32_t x3[10], uint32_t z3[10], const uint32_t xp[10]);")
make_test_max()
make_test(0, 0, 0, 0, 0)
make_test(1, 1, 1, 1, 1)
make_test(6000, 10, 1000, 19999, 18888)
for x in range(50):
x2 = make_limb(1000 + x)
z2 = make_limb(2000 + x)
x3 = make_limb(3000 + x)
z3 = make_limb(4000 + x)
xp = make_limb(5000 + x)
make_test(x2, z2, x3, z3, xp)
make_main()

View file

@ -0,0 +1,107 @@
"""Make unit test for mul_25519() in x25519.c"""
from common import counter, make_main, split64, bin2int
from hashlib import sha256
import struct
def make_test(f, g):
assert(len(f) == 10)
assert(len(g) == 10)
for i in range(10):
assert(f[i] < 2**27)
assert(g[i] < 2**27)
fx = ["0x%08X" % x for x in f]
gx = ["0x%08X" % x for x in g]
max26 = hex(2**26 - 1)
max25 = hex(2**25 - 1)
modulus = 2**255 - 19
base = [0, 26, 51, 77, 102, 128, 153, 179, 204, 230]
fv = 0
gv = 0
for i in range(10):
fv += f[i] * (2**(base[i]))
gv += g[i] * (2**(base[i]))
canonical = (fv * gv) % modulus
results = [canonical, canonical + modulus]
if canonical < 38:
results.append(canonical + modulus * 2)
# Turn results[] into arrays of 64-bit words
results_hex = []
for result in results:
words = split64(result)
words = words + ["0"] * (4 - len(words))
results_hex.append(words)
print("")
print("void test_%d() {" % next(counter))
print(" const uint32_t f[10] = {" + ", ".join(fx) + "};")
print(" const uint32_t g[10] = {" + ", ".join(gx) + "};")
print(" uint32_t out[10];")
print(" uint64_t out64[4];")
print(" uint64_t exp[%d][4] = {" % len(results))
print(" { " + ",".join(results_hex[0]) + " },")
print(" { " + ",".join(results_hex[1]) + " }")
if len(results_hex) == 3:
print(" ,{ " + ",".join(results_hex[2]) + " }")
print(" };")
print(" unsigned match;")
print("")
print(" mul_25519(out, f, g);")
print(" assert(out[0] <= " + max26 + ");")
print(" assert(out[1] <= " + max25 + ");")
print(" assert(out[2] <= " + max26 + ");")
print(" assert(out[3] <= " + max25 + ");")
print(" assert(out[4] <= " + max26 + ");")
print(" assert(out[5] <= " + max25 + ");")
print(" assert(out[6] <= " + max26 + ");")
print(" assert(out[7] <= " + max25 + ");")
print(" assert(out[8] <= " + max26 + ");")
print(" assert(out[9] <= " + max26 + ");")
print(" convert_le25p5_to_le64(out64, out);")
print(" match = 0;")
print(" match |= !memcmp(exp[0], out64, 32);")
print(" match |= !memcmp(exp[1], out64, 32);")
if len(results_hex) == 3:
print(" match |= !memcmp(exp[2], out64, 32);")
print(" assert(match);")
print("}")
def make_limb(seed):
result = bin2int(sha256(struct.pack(">I", seed)).digest()) & ((2**27)-1)
return result
print("#include <assert.h>")
print("#include <string.h>")
print("#include <stdint.h>")
print("#include <stdio.h>")
print("void convert_le25p5_to_le64(uint64_t out[4], const uint32_t in[10]);")
print("void mul_25519(uint32_t out[10], const uint32_t f[10], const uint32_t g[10]);")
modulus = [0x3ffffed, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff]
modulus_m1 = [0x3ffffec, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff]
modulus_m2 = [0x3ffffeb, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff]
modulus_m40 = [0x3ffffc5, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff]
make_test([0]*10, [0]*10)
make_test([1] + [0]*9, [1] + [0]*9)
make_test([30] + [0]*9, [30] + [0]*9)
make_test([0x7ffffed] + [0]*9, [0x7ffffed] + [0]*9)
make_test(modulus, modulus)
make_test(modulus_m1, modulus_m1)
make_test(modulus_m2, modulus_m2)
make_test(modulus, modulus_m2)
make_test(modulus_m40, modulus_m40)
for x in range(100):
f = [make_limb(1000*x + y) for y in range(10)]
g = [make_limb(2000*x + y) for y in range(10)]
make_test(f, g)
make_main()

View file

@ -572,11 +572,11 @@ void test_ec_ws_new_point(void)
res = ec_ws_new_point(&ecp, Gx, Gy, 32, ec_ctx); res = ec_ws_new_point(&ecp, Gx, Gy, 32, ec_ctx);
assert(res == 0); assert(res == 0);
ec_free_point(ecp); ec_ws_free_point(ecp);
res = ec_ws_new_point(&ecp, zero, zero, 32, ec_ctx); res = ec_ws_new_point(&ecp, zero, zero, 32, ec_ctx);
assert(res == 0); assert(res == 0);
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
} }
@ -614,7 +614,7 @@ void test_ec_ws_get_xy(void)
assert(0 == memcmp(bufx, Gx, 32)); assert(0 == memcmp(bufx, Gx, 32));
assert(0 == memcmp(bufy, Gy, 32)); assert(0 == memcmp(bufy, Gy, 32));
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
} }
@ -642,7 +642,7 @@ void test_ec_ws_double_p256(void)
assert(0 == memcmp(bufx, "\x7c\xf2\x7b\x18\x8d\x03\x4f\x7e\x8a\x52\x38\x03\x04\xb5\x1a\xc3\xc0\x89\x69\xe2\x77\xf2\x1b\x35\xa6\x0b\x48\xfc\x47\x66\x99\x78", 32)); assert(0 == memcmp(bufx, "\x7c\xf2\x7b\x18\x8d\x03\x4f\x7e\x8a\x52\x38\x03\x04\xb5\x1a\xc3\xc0\x89\x69\xe2\x77\xf2\x1b\x35\xa6\x0b\x48\xfc\x47\x66\x99\x78", 32));
assert(0 == memcmp(bufy, "\x07\x77\x55\x10\xdb\x8e\xd0\x40\x29\x3d\x9a\xc6\x9f\x74\x30\xdb\xba\x7d\xad\xe6\x3c\xe9\x82\x29\x9e\x04\xb7\x9d\x22\x78\x73\xd1", 32)); assert(0 == memcmp(bufy, "\x07\x77\x55\x10\xdb\x8e\xd0\x40\x29\x3d\x9a\xc6\x9f\x74\x30\xdb\xba\x7d\xad\xe6\x3c\xe9\x82\x29\x9e\x04\xb7\x9d\x22\x78\x73\xd1", 32));
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
} }
@ -670,7 +670,7 @@ void test_ec_ws_double_p521(void)
assert(0 == memcmp(bufx, "\x01\x28\x79\x44\x2F\x24\x50\xC1\x19\xE7\x11\x9A\x5F\x73\x8B\xE1\xF1\xEB\xA9\xE9\xD7\xC6\xCF\x41\xB3\x25\xD9\xCE\x6D\x64\x31\x06\xE9\xD6\x11\x24\xA9\x1A\x96\xBC\xF2\x01\x30\x5A\x9D\xEE\x55\xFA\x79\x13\x6D\xC7\x00\x83\x1E\x54\xC3\xCA\x4F\xF2\x64\x6B\xD3\xC3\x6B\xC6", 66)); assert(0 == memcmp(bufx, "\x01\x28\x79\x44\x2F\x24\x50\xC1\x19\xE7\x11\x9A\x5F\x73\x8B\xE1\xF1\xEB\xA9\xE9\xD7\xC6\xCF\x41\xB3\x25\xD9\xCE\x6D\x64\x31\x06\xE9\xD6\x11\x24\xA9\x1A\x96\xBC\xF2\x01\x30\x5A\x9D\xEE\x55\xFA\x79\x13\x6D\xC7\x00\x83\x1E\x54\xC3\xCA\x4F\xF2\x64\x6B\xD3\xC3\x6B\xC6", 66));
assert(0 == memcmp(bufy, "\x01\x98\x64\xA8\xB8\x85\x5C\x24\x79\xCB\xEF\xE3\x75\xAE\x55\x3E\x23\x93\x27\x1E\xD3\x6F\xAD\xFC\x44\x94\xFC\x05\x83\xF6\xBD\x03\x59\x88\x96\xF3\x98\x54\xAB\xEA\xE5\xF9\xA6\x51\x5A\x02\x1E\x2C\x0E\xEF\x13\x9E\x71\xDE\x61\x01\x43\xF5\x33\x82\xF4\x10\x4D\xCC\xB5\x43", 66)); assert(0 == memcmp(bufy, "\x01\x98\x64\xA8\xB8\x85\x5C\x24\x79\xCB\xEF\xE3\x75\xAE\x55\x3E\x23\x93\x27\x1E\xD3\x6F\xAD\xFC\x44\x94\xFC\x05\x83\xF6\xBD\x03\x59\x88\x96\xF3\x98\x54\xAB\xEA\xE5\xF9\xA6\x51\x5A\x02\x1E\x2C\x0E\xEF\x13\x9E\x71\xDE\x61\x01\x43\xF5\x33\x82\xF4\x10\x4D\xCC\xB5\x43", 66));
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
} }
@ -702,8 +702,8 @@ void test_ec_ws_add(void)
assert(0 == memcmp(bufx, "\x5e\xcb\xe4\xd1\xa6\x33\x0a\x44\xc8\xf7\xef\x95\x1d\x4b\xf1\x65\xe6\xc6\xb7\x21\xef\xad\xa9\x85\xfb\x41\x66\x1b\xc6\xe7\xfd\x6c", 32)); assert(0 == memcmp(bufx, "\x5e\xcb\xe4\xd1\xa6\x33\x0a\x44\xc8\xf7\xef\x95\x1d\x4b\xf1\x65\xe6\xc6\xb7\x21\xef\xad\xa9\x85\xfb\x41\x66\x1b\xc6\xe7\xfd\x6c", 32));
assert(0 == memcmp(bufy, "\x87\x34\x64\x0c\x49\x98\xff\x7e\x37\x4b\x06\xce\x1a\x64\xa2\xec\xd8\x2a\xb0\x36\x38\x4f\xb8\x3d\x9a\x79\xb1\x27\xa2\x7d\x50\x32", 32)); assert(0 == memcmp(bufy, "\x87\x34\x64\x0c\x49\x98\xff\x7e\x37\x4b\x06\xce\x1a\x64\xa2\xec\xd8\x2a\xb0\x36\x38\x4f\xb8\x3d\x9a\x79\xb1\x27\xa2\x7d\x50\x32", 32));
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_point(ecp2); ec_ws_free_point(ecp2);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
} }
@ -733,7 +733,7 @@ void test_ec_ws_scalar(void)
assert(0 == memcmp(bufx, "\xf2\x49\x10\x4d\x0e\x6f\x8f\x29\xe6\x01\x62\x77\x78\x0c\xda\x84\xdc\x84\xb8\x3b\xc3\xd8\x99\xdf\xb7\x36\xca\x08\x31\xfb\xe8\xcf", 32)); assert(0 == memcmp(bufx, "\xf2\x49\x10\x4d\x0e\x6f\x8f\x29\xe6\x01\x62\x77\x78\x0c\xda\x84\xdc\x84\xb8\x3b\xc3\xd8\x99\xdf\xb7\x36\xca\x08\x31\xfb\xe8\xcf", 32));
assert(0 == memcmp(bufy, "\xb5\x7e\x12\xfc\xdb\x03\x1f\x59\xca\xb8\x1b\x1c\x6b\x1e\x1c\x07\xe4\x51\x2e\x52\xce\x83\x2f\x1a\x0c\xed\xef\xff\x8b\x43\x40\xe9", 32)); assert(0 == memcmp(bufy, "\xb5\x7e\x12\xfc\xdb\x03\x1f\x59\xca\xb8\x1b\x1c\x6b\x1e\x1c\x07\xe4\x51\x2e\x52\xce\x83\x2f\x1a\x0c\xed\xef\xff\x8b\x43\x40\xe9", 32));
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
} }
@ -761,7 +761,7 @@ void test_ec_ws_neg(void)
assert(0 == memcmp(bufx, "\x6b\x17\xd1\xf2\xe1\x2c\x42\x47\xf8\xbc\xe6\xe5\x63\xa4\x40\xf2\x77\x03\x7d\x81\x2d\xeb\x33\xa0\xf4\xa1\x39\x45\xd8\x98\xc2\x96", 32)); assert(0 == memcmp(bufx, "\x6b\x17\xd1\xf2\xe1\x2c\x42\x47\xf8\xbc\xe6\xe5\x63\xa4\x40\xf2\x77\x03\x7d\x81\x2d\xeb\x33\xa0\xf4\xa1\x39\x45\xd8\x98\xc2\x96", 32));
assert(0 == memcmp(bufy, "\xb0\x1c\xbd\x1c\x01\xe5\x80\x65\x71\x18\x14\xb5\x83\xf0\x61\xe9\xd4\x31\xcc\xa9\x94\xce\xa1\x31\x34\x49\xbf\x97\xc8\x40\xae\x0a", 32)); assert(0 == memcmp(bufy, "\xb0\x1c\xbd\x1c\x01\xe5\x80\x65\x71\x18\x14\xb5\x83\xf0\x61\xe9\xd4\x31\xcc\xa9\x94\xce\xa1\x31\x34\x49\xbf\x97\xc8\x40\xae\x0a", 32));
ec_free_point(ecp); ec_ws_free_point(ecp);
ec_free_context(ec_ctx); ec_free_context(ec_ctx);
} }

169
src/test/test_ed25519.c Normal file
View file

@ -0,0 +1,169 @@
#include "endianess.h"
#include <assert.h>
typedef struct Point {
uint32_t X[10];
uint32_t Y[10];
uint32_t Z[10];
uint32_t T[10];
} Point;
void ed25519_add_internal(Point *P3, const Point *P1, const Point *P2);
void ed25519_double_internal(Point *P3, const Point *P1);
void mul_25519(uint32_t out[10], const uint32_t f[10], const uint32_t g[10]);
void invert_25519(uint32_t out[10], const uint32_t x[10]);
int convert_behex_to_le25p5(uint32_t out[10], const char *in);
int convert_le25p5_to_behex(char **out, uint32_t in[10]);
void convert_le25p5_to_le8(uint8_t out[32], const uint32_t in[10]);
void convert_le8_to_le25p5(uint32_t out[10], const uint8_t in[32]);
void ed25519_scalar_internal(Point *Pout,
const uint8_t *k, size_t len,
const Point *Pin);
void test_point_add(void)
{
char Gx_hex[] = "216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a";
char Gy_hex[] = "6666666666666666666666666666666666666666666666666666666666666658";
char G2x_hex[] = "36ab384c9f5a046c3d043b7d1833e7ac080d8e4515d7a45f83c5a14e2843ce0e";
char G2y_hex[] = "2260cdf3092329c21da25ee8c9a21f5697390f51643851560e5f46ae6af8a3c9";
Point G, G2;
uint32_t invz[10];
char *c;
memset(&G, 0, sizeof G);
convert_behex_to_le25p5(G.X, Gx_hex);
convert_behex_to_le25p5(G.Y, Gy_hex);
G.Z[0] = 1;
mul_25519(G.T, G.X, G.Y);
ed25519_add_internal(&G2, &G, &G);
invert_25519(invz, G2.Z);
/* Check X */
mul_25519(G2.X, G2.X, invz);
convert_le25p5_to_behex(&c, G2.X);
assert(0 == strcmp(c, G2x_hex));
free(c);
/* Check Y */
mul_25519(G2.Y, G2.Y, invz);
convert_le25p5_to_behex(&c, G2.Y);
assert(0 == strcmp(c, G2y_hex));
free(c);
}
void test_point_double(void)
{
char Gx_hex[] = "216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a";
char Gy_hex[] = "6666666666666666666666666666666666666666666666666666666666666658";
char G2x_hex[] = "36ab384c9f5a046c3d043b7d1833e7ac080d8e4515d7a45f83c5a14e2843ce0e";
char G2y_hex[] = "2260cdf3092329c21da25ee8c9a21f5697390f51643851560e5f46ae6af8a3c9";
Point G, G2;
uint32_t invz[10];
char *c;
memset(&G, 0, sizeof G);
convert_behex_to_le25p5(G.X, Gx_hex);
convert_behex_to_le25p5(G.Y, Gy_hex);
G.Z[0] = 1;
mul_25519(G.T, G.X, G.Y);
ed25519_double_internal(&G2, &G);
invert_25519(invz, G2.Z);
/* Check X */
mul_25519(G2.X, G2.X, invz);
convert_le25p5_to_behex(&c, G2.X);
assert(0 == strcmp(c, G2x_hex));
free(c);
/* Check Y */
mul_25519(G2.Y, G2.Y, invz);
convert_le25p5_to_behex(&c, G2.Y);
assert(0 == strcmp(c, G2y_hex));
free(c);
}
void from_affine(Point *P, const uint8_t x[32], const uint8_t y[32])
{
memset(P, 0, sizeof *P);
convert_le8_to_le25p5(P->X, x);
convert_le8_to_le25p5(P->Y, y);
P->Z[0] = 1;
mul_25519(P->T, P->X, P->Y);
}
void to_affine(uint8_t x[32], uint8_t y[32], const Point *P)
{
uint32_t invz[10];
uint32_t tmp[10];
invert_25519(invz, P->Z);
mul_25519(tmp, P->X, invz);
convert_le25p5_to_le8(x, tmp);
mul_25519(tmp, P->Y, invz);
convert_le25p5_to_le8(y, tmp);
}
void test_scalar_mult(void)
{
uint8_t xout[32], yout[32];
uint8_t G0x[32] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
uint8_t G0y[32] = "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
uint8_t Gx[32] = "\x1a\xd5\x25\x8f\x60\x2d\x56\xc9\xb2\xa7\x25\x95\x60\xc7\x2c\x69\x5c\xdc\xd6\xfd\x31\xe2\xa4\xc0\xfe\x53\x6e\xcd\xd3\x36\x69\x21";
uint8_t Gy[32] = "\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66";
uint8_t G2x[32] = "\x0e\xce\x43\x28\x4e\xa1\xc5\x83\x5f\xa4\xd7\x15\x45\x8e\x0d\x08\xac\xe7\x33\x18\x7d\x3b\x04\x3d\x6c\x04\x5a\x9f\x4c\x38\xab\x36";
uint8_t G2y[32] = "\xc9\xa3\xf8\x6a\xae\x46\x5f\x0e\x56\x51\x38\x64\x51\x0f\x39\x97\x56\x1f\xa2\xc9\xe8\x5e\xa2\x1d\xc2\x29\x23\x09\xf3\xcd\x60\x22";
uint8_t G5y[32] = "\xED\xC8\x76\xD6\x83\x1F\xD2\x10\x5D\x0B\x43\x89\xCA\x2E\x28\x31\x66\x46\x92\x89\x14\x6E\x2C\xE0\x6F\xAE\xFE\x98\xB2\x25\x48\x5F";
uint8_t Gry[32] = "\xC9\x72\x8D\x51\x1D\xF5\xB3\x05\x12\xD4\x81\xCC\x41\xDE\x72\x0E\x73\x90\xF1\x53\xFE\xF6\xF0\x59\xDC\xF4\xB8\xAF\xEE\x92\x77\x16";
Point P, Q;
/* 0 */
from_affine(&P, Gx, Gy);
ed25519_scalar_internal(&Q, (uint8_t*)"\x00", 1, &P);
to_affine(xout, yout, &Q);
assert(0 == memcmp(G0x, xout, sizeof G0x));
assert(0 == memcmp(G0y, yout, sizeof G0y));
/* 1 */
from_affine(&P, Gx, Gy);
ed25519_scalar_internal(&Q, (uint8_t*)"\x01", 1, &P);
to_affine(xout, yout, &Q);
assert(0 == memcmp(Gx, xout, sizeof Gx));
assert(0 == memcmp(Gy, yout, sizeof Gy));
/* 2 */
from_affine(&P, Gx, Gy);
ed25519_scalar_internal(&Q, (uint8_t*)"\x02", 1, &P);
to_affine(xout, yout, &Q);
assert(0 == memcmp(G2x, xout, sizeof G2x));
assert(0 == memcmp(G2y, yout, sizeof G2y));
/* 5 */
from_affine(&P, Gx, Gy);
ed25519_scalar_internal(&Q, (uint8_t*)"\x05", 1, &P);
to_affine(xout, yout, &Q);
assert(0 == memcmp(G5y, yout, sizeof G5y));
/* random */
uint8_t r[32] = "\x08\x68\xba\x7a\x34\x73\x4f\x3e\x93\xdd\x24\x26\x32\x7f\x0f\x34\x14\x5c\xd9\x43\x02\xe4\xd5\xdd\x95\x00\xee\x1b\x57\x11\x39\xdd";
from_affine(&P, Gx, Gy);
ed25519_scalar_internal(&Q, r, 32, &P);
to_affine(xout, yout, &Q);
assert(0 == memcmp(Gry, yout, sizeof Gry));
}
int main(void)
{
test_point_add();
test_point_double();
test_scalar_mult();
return 0;
}

374
src/test/test_mod25519.c Normal file
View file

@ -0,0 +1,374 @@
#include "endianess.h"
#include <assert.h>
/** Test for multiplication are in a separate unit **/
void convert_le64_to_le25p5(uint32_t out[9], const uint64_t in[4]);
void convert_le25p5_to_le64(uint64_t out[4], const uint32_t in[9]);
int convert_behex_to_le25p5(uint32_t out[10], const char *in);
int convert_le25p5_to_behex(char **out, uint32_t in[10]);
void reduce_25519_le64(uint64_t x[4]);
void cswap(uint32_t a[10], uint32_t b[10], uint32_t c[10], uint32_t d[10], unsigned cond);
void invert_25519(uint32_t out[10], const uint32_t x[10]);
void add_25519(uint32_t out[10], const uint32_t f[10], const uint32_t g[10]);
static const uint64_t modulus[4] = { 0xffffffffffffffedULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL, 0x7fffffffffffffffULL };
static const uint64_t modulus2[4] = { 0xffffffffffffffdaULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL, 0xffffffffffffffffULL };
static const uint64_t hundhund[4] = { 0xe08063f1e8753fb4ULL, 0x29e492f797f6605cULL, 0x1f6de7b30d1327efULL, 0x534a930de945ebf3ULL };
static const uint32_t modulus_32[10] = { 0x3ffffed, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff, 0x3ffffff, 0x1ffffff };
static const uint32_t hundhund_32[10] = { 0x753fb4, 0x18fc7a, 0xb9c10, 0x1bcbfb3, 0xa7924b, 0x11327ef, 0x2f3d986, 0x17e63ed, 0xde945e, 0x14d2a4c };
void test_le64_tole25p5(void)
{
uint32_t out[10];
uint64_t in[4];
memset(out, 0xAA, sizeof out);
memcpy(in, modulus, sizeof modulus);
convert_le64_to_le25p5(out, in);
assert(out[0] == 0x3ffffed);
assert(out[1] == 0x1ffffff);
assert(out[2] == 0x3ffffff);
assert(out[3] == 0x1ffffff);
assert(out[4] == 0x3ffffff);
assert(out[5] == 0x1ffffff);
assert(out[6] == 0x3ffffff);
assert(out[7] == 0x1ffffff);
assert(out[8] == 0x3ffffff);
assert(out[9] == 0x1ffffff);
memset(out, 0xAA, sizeof out);
memcpy(in, hundhund, sizeof hundhund);
convert_le64_to_le25p5(out, in);
assert(out[0] == 0x753fb4);
assert(out[1] == 0x18fc7a);
assert(out[2] == 0xb9c10);
assert(out[3] == 0x1bcbfb3);
assert(out[4] == 0xa7924b);
assert(out[5] == 0x11327ef);
assert(out[6] == 0x2f3d986);
assert(out[7] == 0x17e63ed);
assert(out[8] == 0xde945e);
assert(out[9] == 0x14d2a4c);
in[0] = 0xAAAAAAAAAAAAAAAA;
in[1] = 0xBBBBBBBBBBBBBBBB;
in[2] = 0xCCCCCCCCCCCCCCCC;
in[3] = 0xDDDDDDDDDDDDDDDD;
convert_le64_to_le25p5(out, in);
assert(out[0] == 0x2aaaaaa);
assert(out[1] == 0xaaaaaa);
assert(out[2] == 0x3777555);
assert(out[3] == 0x1dddddd);
assert(out[4] == 0x2eeeeee);
assert(out[5] == 0xcccccc);
assert(out[6] == 0x2666666);
assert(out[7] == 0x1bbb999);
assert(out[8] == 0x1dddddd);
assert(out[9] == 0x3777777);
}
void test_le25p5_to_le64(void)
{
uint64_t out[4];
memset(out, 0xAA, sizeof out);
convert_le25p5_to_le64(out, modulus_32);
assert(out[0] == modulus[0]);
assert(out[1] == modulus[1]);
assert(out[2] == modulus[2]);
assert(out[3] == modulus[3]);
memset(out, 0xAA, sizeof out);
convert_le25p5_to_le64(out, hundhund_32);
assert(out[0] == hundhund[0]);
assert(out[1] == hundhund[1]);
assert(out[2] == hundhund[2]);
}
void test_behex_tole25p5(void)
{
uint32_t out[10];
char in1[] = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed";
char in2[] = "534a930de945ebf31f6de7b30d1327ef29e492f797f6605ce08063f1e8753fb4";
char in3[] = "DDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA";
char in4[] = "AA";
int ret;
memset(out, 0xAA, sizeof out);
ret = convert_behex_to_le25p5(out, in1);
assert(ret == 0);
assert(out[0] == 0x3ffffed);
assert(out[1] == 0x1ffffff);
assert(out[2] == 0x3ffffff);
assert(out[3] == 0x1ffffff);
assert(out[4] == 0x3ffffff);
assert(out[5] == 0x1ffffff);
assert(out[6] == 0x3ffffff);
assert(out[7] == 0x1ffffff);
assert(out[8] == 0x3ffffff);
assert(out[9] == 0x1ffffff);
memset(out, 0xAA, sizeof out);
ret = convert_behex_to_le25p5(out, in2);
assert(ret == 0);
assert(out[0] == 0x753fb4);
assert(out[1] == 0x18fc7a);
assert(out[2] == 0xb9c10);
assert(out[3] == 0x1bcbfb3);
assert(out[4] == 0xa7924b);
assert(out[5] == 0x11327ef);
assert(out[6] == 0x2f3d986);
assert(out[7] == 0x17e63ed);
assert(out[8] == 0xde945e);
assert(out[9] == 0x14d2a4c);
memset(out, 0xAA, sizeof out);
ret = convert_behex_to_le25p5(out, in3);
assert(ret == 0);
assert(out[0] == 0x2aaaaaa);
assert(out[1] == 0xaaaaaa);
assert(out[2] == 0x3777555);
assert(out[3] == 0x1dddddd);
assert(out[4] == 0x2eeeeee);
assert(out[5] == 0xcccccc);
assert(out[6] == 0x2666666);
assert(out[7] == 0x1bbb999);
assert(out[8] == 0x1dddddd);
assert(out[9] == 0x3777777);
memset(out, 0xAA, sizeof out);
ret = convert_behex_to_le25p5(out, in4);
assert(ret == 0);
assert(out[0] == 0xaa);
assert(out[1] == 0);
assert(out[2] == 0);
assert(out[3] == 0);
assert(out[4] == 0);
assert(out[5] == 0);
assert(out[6] == 0);
assert(out[7] == 0);
assert(out[8] == 0);
/* Negative test cases */
ret = convert_behex_to_le25p5(out, "A");
assert(ret != 0);
ret = convert_behex_to_le25p5(out, NULL);
assert(ret != 0);
ret = convert_behex_to_le25p5(out, "te");
assert(ret != 0);
}
void test_tole25p5_to_behex(void)
{
char *out;
uint32_t in[10];
int res;
in[0] = 0x3ffffed;
in[1] = 0x1ffffff;
in[2] = 0x3ffffff;
in[3] = 0x1ffffff;
in[4] = 0x3ffffff;
in[5] = 0x1ffffff;
in[6] = 0x3ffffff;
in[7] = 0x1ffffff;
in[8] = 0x3ffffff;
in[9] = 0x1ffffff;
convert_le25p5_to_behex(&out, in);
res = strcmp(out, "0000000000000000000000000000000000000000000000000000000000000000");
assert(res == 0);
}
void test_reduce(void)
{
uint64_t x[4];
/** 0 **/
x[0] = x[1] = x[2] = x[3] = 0;
reduce_25519_le64(x);
assert(x[0] == 0);
assert(x[1] == 0);
assert(x[2] == 0);
assert(x[3] == 0);
memcpy(x, modulus, sizeof x);
reduce_25519_le64(x);
assert(x[0] == 0);
assert(x[1] == 0);
assert(x[2] == 0);
assert(x[3] == 0);
memcpy(x, modulus2, sizeof x);
reduce_25519_le64(x);
assert(x[0] == 0);
assert(x[1] == 0);
assert(x[2] == 0);
assert(x[3] == 0);
/** 1 **/
x[0] = 1;
x[1] = x[2] = x[3] = 0;
reduce_25519_le64(x);
assert(x[0] == 1);
assert(x[1] == 0);
assert(x[2] == 0);
assert(x[3] == 0);
memcpy(x, modulus, sizeof x);
x[0]++;
reduce_25519_le64(x);
assert(x[0] == 1);
assert(x[1] == 0);
assert(x[2] == 0);
assert(x[3] == 0);
memcpy(x, modulus2, sizeof x);
x[0]++;
reduce_25519_le64(x);
assert(x[0] == 1);
assert(x[1] == 0);
assert(x[2] == 0);
assert(x[3] == 0);
/** 38 **/
x[0] = 38;
x[1] = x[2] = x[3] = 0;
reduce_25519_le64(x);
assert(x[0] == 38);
assert(x[1] == 0);
assert(x[2] == 0);
assert(x[3] == 0);
x[0] = 0x13;
x[1] = x[2] = 0;
x[3] = 0x8000000000000000ULL;
reduce_25519_le64(x);
assert(x[0] == 38);
assert(x[1] == 0);
assert(x[2] == 0);
assert(x[3] == 0);
}
int check(uint32_t x[10], uint32_t s)
{
unsigned i;
for (i=0; i<10; i++)
if (x[i] != s) {
return 0;
}
return 1;
}
void test_cswap(void)
{
uint32_t a[10];
uint32_t b[10];
uint32_t c[10];
uint32_t d[10];
memset(a, 0xAA, sizeof a);
memset(b, 0x55, sizeof b);
memset(c, 0x77, sizeof c);
memset(d, 0x11, sizeof d);
cswap(a, b, c, d, 0);
assert(check(a, 0xAAAAAAAA));
assert(check(b, 0x55555555));
assert(check(c, 0x77777777));
assert(check(d, 0x11111111));
cswap(a, b, c, d, 1);
assert(check(a, 0x77777777));
assert(check(b, 0x11111111));
assert(check(c, 0xAAAAAAAA));
assert(check(d, 0x55555555));
}
void test_invert(void)
{
uint64_t in[4];
uint32_t x[10];
uint64_t out[4];
in[0] = 1;
in[1] = in[2] = in[3] = 0;
convert_le64_to_le25p5(x, in);
invert_25519(x, x);
convert_le25p5_to_le64(out, x);
reduce_25519_le64(out);
assert(out[0] == 1);
assert(out[1] == 0);
assert(out[2] == 0);
assert(out[3] == 0);
in[0] = 2;
in[1] = in[2] = in[3] = 0;
convert_le64_to_le25p5(x, in);
invert_25519(x, x);
convert_le25p5_to_le64(out, x);
reduce_25519_le64(out);
assert(out[0] == 0xfffffffffffffff7);
assert(out[1] == 0xffffffffffffffff);
assert(out[2] == 0xffffffffffffffff);
assert(out[3] == 0x3fffffffffffffff);
in[0] = 0xAAAAAAAAAAAAAAAA;
in[1] = 0xBBBBBBBBBBBBBBBB;
in[2] = 0xCCCCCCCCCCCCCCCC;
in[3] = 0xDDDDDDDDDDDDDDDD;
convert_le64_to_le25p5(x, in);
invert_25519(x, x);
convert_le25p5_to_le64(out, x);
reduce_25519_le64(out);
assert(out[0] == 0x6cf8847ba332c4c7);
assert(out[1] == 0x984028b39c0b5e92);
assert(out[2] == 0x2404af2276fdd005);
assert(out[3] == 0x22336ebc77628108);
}
void test_add(void)
{
uint64_t in[4];
uint32_t x[10];
uint64_t out[4];
in[0] = 0xFFFFFFFFFFFFFFFF;
in[1] = 0xFFFFFFFFFFFFFFFF;
in[2] = 0xFFFFFFFFFFFFFFFF;
in[3] = 0xFFFFFFFFFFFFFFFF;
convert_le64_to_le25p5(x, in);
/* x[8] and x[9] have 26 bits set */
add_25519(x, x, x);
convert_le25p5_to_le64(out, x);
assert(out[0] == 0x0000000000000037);
assert(out[1] == 0x0000000000000000);
assert(out[2] == 0x0000000000000000);
assert(out[3] == 0x8000000000000000);
}
int main(void)
{
test_le64_tole25p5();
test_le25p5_to_le64();
test_behex_tole25p5();
test_tole25p5_to_behex();
test_reduce();
test_cswap();
test_invert();
test_add();
return 0;
}

View file

@ -5,7 +5,7 @@
int ge(const uint64_t *x, const uint64_t *y, size_t nw); int ge(const uint64_t *x, const uint64_t *y, size_t nw);
unsigned sub(uint64_t *out, const uint64_t *a, const uint64_t *b, size_t nw); unsigned sub(uint64_t *out, const uint64_t *a, const uint64_t *b, size_t nw);
void rsquare(uint64_t *r2, uint64_t *n, size_t nw); void rsquare(uint64_t *r2, uint64_t *n, size_t nw);
int mont_select(uint64_t *out, const uint64_t *a, const uint64_t *b, unsigned cond, size_t words); int mod_select(uint64_t *out, const uint64_t *a, const uint64_t *b, unsigned cond, unsigned words);
void test_ge(void) void test_ge(void)
{ {
@ -371,7 +371,7 @@ void test_mont_set(void)
mont_context_free(ctx); mont_context_free(ctx);
} }
void test_mont_select() void test_mod_select()
{ {
int res; int res;
MontContext *ctx; MontContext *ctx;
@ -387,17 +387,17 @@ void test_mont_select()
mont_context_init(&ctx, modulusA, 16); mont_context_init(&ctx, modulusA, 16);
memset(c, 0, sizeof c); memset(c, 0, sizeof c);
res = mont_select(c, a, b, 1, ctx->words); res = mod_select(c, a, b, 1, ctx->words);
assert(res == 0); assert(res == 0);
assert(memcmp(a, c, sizeof c) == 0); assert(memcmp(a, c, sizeof c) == 0);
memset(c, 0, sizeof c); memset(c, 0, sizeof c);
res = mont_select(c, a, b, 10, ctx->words); res = mod_select(c, a, b, 10, ctx->words);
assert(res == 0); assert(res == 0);
assert(memcmp(a, c, sizeof c) == 0); assert(memcmp(a, c, sizeof c) == 0);
memset(c, 0, sizeof c); memset(c, 0, sizeof c);
res = mont_select(c, a, b, 0, ctx->words); res = mod_select(c, a, b, 0, ctx->words);
assert(res == 0); assert(res == 0);
assert(memcmp(b, c, sizeof c) == 0); assert(memcmp(b, c, sizeof c) == 0);
@ -408,12 +408,12 @@ void test_mont_select()
mont_context_init(&ctx, modulusB, 17); mont_context_init(&ctx, modulusB, 17);
memset(f, 0, sizeof f); memset(f, 0, sizeof f);
res = mont_select(f, d, e, 1, ctx->words); res = mod_select(f, d, e, 1, ctx->words);
assert(res == 0); assert(res == 0);
assert(memcmp(d, f, sizeof f) == 0); assert(memcmp(d, f, sizeof f) == 0);
memset(f, 0, sizeof f); memset(f, 0, sizeof f);
res = mont_select(f, d, e, 0, ctx->words); res = mod_select(f, d, e, 0, ctx->words);
assert(res == 0); assert(res == 0);
assert(memcmp(e, f, sizeof f) == 0); assert(memcmp(e, f, sizeof f) == 0);
} }
@ -429,6 +429,6 @@ int main(void) {
test_mont_sub(); test_mont_sub();
test_mont_inv_prime(); test_mont_inv_prime();
test_mont_set(); test_mont_set();
test_mont_select(); test_mod_select();
return 0; return 0;
} }

24
src/test/test_x25519.c Normal file
View file

@ -0,0 +1,24 @@
#include "endianess.h"
#include <assert.h>
void ladder(uint8_t shared_secret[32], const uint8_t *k, size_t len, const uint8_t pubkey[32]);
void test_ladder(void)
{
uint8_t scalar[32] = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4";
uint8_t pubkey[32] = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c";
uint8_t expout[32] = "\xc3\xda\x55\x37\x9d\xe9\xc6\x90\x8e\x94\xea\x4d\xf2\x8d\x08\x4f\x32\xec\xcf\x03\x49\x1c\x71\xf7\x54\xb4\x07\x55\x77\xa2\x85\x52";
uint8_t out[32];
scalar[0] &= 248;
scalar[31] &= 127;
scalar[31] |= 64;
ladder(out, scalar, 32, pubkey);
assert(0 == memcmp(out, expout, 32));
}
int main(void)
{
test_ladder();
return 0;
}

133
src/x25519.c Normal file
View file

@ -0,0 +1,133 @@
#include "common.h"
#include "endianess.h"
FAKE_INIT(x25519)
/*
* Fast variable-base scalar multiplication for the Montgomery curve Curve25519
*
* y² = x³ + 486662x² + x
*
* over the prime field 2² - 19.
*/
#include "mod25519.c"
/*
* Execute the step in the Montgomery ladder.
*
* x2/z2 is updated with the doubling of P
* x3/z3 is updated with the sum P+P
*
* @param[in,out] x2 The projective X-coordinate of P (< 2²)
* @param[in,out] z2 The projective Z-coordinate of P (< 2²)
* @param[in,out] x3 The projective X-coordinate of P (< 2²)
* @param[in,out] z3 The projective Z-coordinate of P (< 2²)
* @param[in] xp The affine X-coordinate of P-P (< 2²)
*/
STATIC void ladder_step(uint32_t x2[10], uint32_t z2[10], uint32_t x3[10], uint32_t z3[10], const uint32_t xp[10])
{
uint32_t t0[10], t1[10];
static const uint32_t nr_121666[10] = { 121666 };
/* https://www.hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#ladder-mladd-1987-m */
sub32(t0, x3, z3); /* t0 = D < 2^28 */
sub32(t1, x2, z2); /* t1 = B < 2^28 */
add32(x2, x2, z2); /* x2 = A < 2^27 */
add32(z2, x3, z3); /* z2 = C < 2^27 */
mul_25519(z3, t0, x2); /* z3 = DA < 2^26 */
mul_25519(z2, z2, t1); /* z2 = CB < 2^26 */
add32(x3, z3, z2); /* x3 = DA+CB < 2^27 */
sub32(z2, z3, z2); /* z2 = DA-CB < 2^28 */
mul_25519(x3, x3, x3); /* x3 = X5 < 2^26 */
mul_25519(z2, z2, z2); /* z2 = (DA-CB)² < 2^26 */
mul_25519(t0, t1, t1); /* t0 = BB < 2^26 */
mul_25519(t1, x2, x2); /* t1 = AA < 2^26 */
sub32(x2, t1, t0); /* x2 = E < 2^28 */
mul_25519(z3, xp, z2); /* z3 = Z5 < 2^26 */
mul_25519(z2, x2, nr_121666); /* z2 = a24*E < 2^26 */
add32(z2, t0, z2); /* z2 = BB+a24*E < 2^27 */
mul_25519(z2, x2, z2); /* z2 = Z4 < 2^26 */
mul_25519(x2, t1, t0); /* x2 = X4 < 2^26 */
}
/*
* Variable-base scalar multiplication on Curve25519.
*
* @param[out] ssecret The X-coordinate of the resulting point.
* @param[in] k The scalar encoded in little-endian mode.
* It must have been already clamped.
* @param[in] len Length of the scalar in bytes.
* @param[in] pubkey The X-coordinate of the point to multiply, encoded in
* little-endian mode.
*/
void ladder(uint8_t ssecret[32], const uint8_t *k, size_t len, const uint8_t pubkey[32])
{
uint32_t R0x[10] = { 1 };
uint32_t R0z[10] = { 0 };
uint32_t R1x[10];
uint32_t R1z[10] = { 1 };
uint32_t xp[10];
uint32_t invz[10];
uint32_t affx[10];
uint64_t tmp_64[4];
unsigned bit_idx, swap;
unsigned i;
for (i=0; i<4; i++) {
tmp_64[i] = LOAD_U64_LITTLE(&pubkey[i*8]);
}
convert_le64_to_le25p5(xp, tmp_64);
memcpy(R1x, xp, sizeof R1x);
bit_idx = 7;
swap = 0;
/* https://eprint.iacr.org/2020/956.pdf */
while (len>0) {
unsigned bit;
bit = (k[len-1] >> bit_idx) & 1;
swap ^= bit;
cswap(R0x, R0z, R1x, R1z, swap);
ladder_step(R0x, R0z, R1x, R1z, xp);
swap = bit;
if (bit_idx-- == 0) {
bit_idx = 7;
len--;
}
}
cswap(R0x, R0z, R1x, R1z, swap);
invert_25519(invz, R0z);
mul_25519(affx, R0x, invz);
convert_le25p5_to_le64(tmp_64, affx);
reduce_25519_le64(tmp_64);
for (i=0; i<4; i++) {
STORE_U64_LITTLE(&ssecret[i*8], tmp_64[i]);
}
}
#ifdef PROFILE
int main(void)
{
uint8_t pubkey[32];
uint8_t secret[32];
uint8_t out[32];
unsigned i;
secret[0] = pubkey[0] = 0xAA;
for (i=1; i<32; i++) {
secret[i] = pubkey[i] = (uint8_t)((secret[i-1] << 1) | (secret[i-1] >> 7));
}
for (i=0; i<10000; i++) {
ladder(out, secret, sizeof secret, pubkey);
}
}
#endif

View file

@ -0,0 +1,12 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIAX0BvLDTI71TpzUoZ+rzXFdgm0WVN+ndcu2UuG4lz47
-----END PRIVATE KEY-----
ED25519 Private-Key:
priv:
05:f4:06:f2:c3:4c:8e:f5:4e:9c:d4:a1:9f:ab:cd:
71:5d:82:6d:16:54:df:a7:75:cb:b6:52:e1:b8:97:
3e:3b
pub:
bc:85:b8:cf:58:5d:20:a4:de:47:e8:4d:1c:b6:18:
3f:63:d9:ba:96:22:3f:cb:c8:86:e3:63:ff:de:a2:
0c:ff

View file

@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIAX0BvLDTI71TpzUoZ+rzXFdgm0WVN+ndcu2UuG4lz47
-----END PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAiwCrCpOhjU8wICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEAQIEEOk0cNotyf4edYcdUnl6GNAEQFEf
NUiZ3jfT8HYD0FqIjwZ9UL5T5rUmM34LCqipAuSPu+f23i2OuFlWIvfXtF4Qy79v
ysv4OQa6DTDGopAHaL4=
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAiX7Eh6OAEHXAICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEARYEEJpUDxSZpCzf7OI60qPsYU0EQG0u
aMz2AkSXtRU3+ATTEDtb4slSdtivYBkUCfcAw8/d0PZN56wjPStu585VgRTlGTXW
Ty3Qe6eABSTTtEfBAhU=
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAh8KD2HGLrgSAICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEENKrZJ2LKxhTgbSfXCWBKuMEQFiS
O8+y8UlvtuAAHhDd0TBu7sGVG2M4tzpvhnMV82QXfCRDkJAUqQNtXQBLWjdO44Ui
EaYvk2g9GUDQVDfx25E=
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -0,0 +1,5 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGKME4GCSqGSIb3DQEFDTBBMCkGCSqGSIb3DQEFDDAcBAjS0vLA/pEM8AICCAAw
DAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIdHVzfnJXMDwEOFN1knabShDOAvnI
M8NhzBnlqPf0YK3HeJ4JZY0jAfLBGyKafLPA94+KbGBGRLB+bFIrVRpBrkN4
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB5VzUBr/gix80anEf1muNYHTnCByK7eInlDM5QrqilUAAAAIiXPO6Alzzu
gAAAAAtzc2gtZWQyNTUxOQAAACB5VzUBr/gix80anEf1muNYHTnCByK7eInlDM5QrqilUA
AAAEBjRlj3swElydFqbfy2FD2dgmwGMDLyhkPJ6HesGcJC4XlXNQGv+CLHzRqcR/Wa41gd
OcIHIrt4ieUMzlCuqKVQAAAABHRlc3QB
-----END OPENSSH PRIVATE KEY-----

View file

@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDjWCfp8z
xIlHc7MwYdCIthAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAICHNC69L0IPdGK1V
hVBpRNoxcBOoDKZpGZ98pFEBo1wNAAAAkKTQjCRhdNO9Zv027lR5uG3cGePQK+98oRA6Wy
wcrAmwahxGoBo8y49yPMCIt0e9KmDY7+Utw0FBfdldKuYzRV732kd7bQgr0z13sc+70oix
rT8S6ufYMZHyT5sRvjaM8jlM6nuyBix4xLc7pjyNqojZfLnoELNWQ9hrN6DK8C4yQ2rUwU
UtOxazNEKJBnmLqg==
-----END OPENSSH PRIVATE KEY-----

View file

@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAh2gZRW4+DZnQICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEENs1bbPFWltnsWl6mwI1bf8EQH0K
0QOWF+Y2c24tojkepMuuKq3JRht2vCY/bxaJO74lk0p0DLsa8kIxUfd6oDA0+Mf2
K8LxR/qXZCIMRYzRyjA=
-----END ENCRYPTED PRIVATE KEY-----

View file

@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAvIW4z1hdIKTeR+hNHLYYP2PZupYiP8vIhuNj/96iDP8=
-----END PUBLIC KEY-----

View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHlXNQGv+CLHzRqcR/Wa41gdOcIHIrt4ieUMzlCuqKVQ test

View file

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBWzCCAQ2gAwIBAgIUCvoAagsRlhJ+PYMH2nrNafycn1UwBQYDK2VwMCMxCzAJ
BgNVBAYTAkdCMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0yMjA1MDcxODMxMzFa
Fw0yMzA1MDcxODMxMzFaMCMxCzAJBgNVBAYTAkdCMRQwEgYDVQQDDAtleGFtcGxl
LmNvbTAqMAUGAytlcAMhALyFuM9YXSCk3kfoTRy2GD9j2bqWIj/LyIbjY//eogz/
o1MwUTAdBgNVHQ4EFgQU8Yevx2AVIt0UoJy8mJSdH7lBHF0wHwYDVR0jBBgwFoAU
8Yevx2AVIt0UoJy8mJSdH7lBHF0wDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQB/
vYInhoHQyJYoNuPWHQt0AkZwHTZNNFBSz/+Vty2SfkTZgsyKtqNdeVlc1VfVDjU7
Sa3zerNfRQGz/qMLtKkP
-----END CERTIFICATE-----

View file

@ -0,0 +1,39 @@
#!/bin/sh
# Note that openssl's ec command does not support ed25519; you must use the (gen)pkey command
set -e
set -x
openssl version | tee openssl_version_ed25519.txt
# Private key (PKCS#8)
openssl genpkey -algorithm ed25519 -outform PEM -out ecc_ed25519_private.pem
openssl pkey -inform PEM -in ecc_ed25519_private.pem -outform DER -out ecc_ed25519_private.der
openssl pkey -in ecc_ed25519_private.pem -text -out ecc_ed25519.txt
# Encrypted private key
# Traditional format (PEM enveloped) is unsupported for ed25519, so we only use encrypted PKCS#8
openssl pkcs8 -in ecc_ed25519_private.der -inform DER -passout 'pass:secret' -out ecc_ed25519_private_p8.der -outform DER -topk8
openssl pkcs8 -in ecc_ed25519_private.der -inform DER -passout 'pass:secret' -out ecc_ed25519_private_p8.pem -outform PEM -topk8
openssl pkey -in ecc_ed25519_private.pem -des3 -out ecc_ed25519_private_enc_des3.pem -passout 'pass:secret' -outform PEM
openssl pkey -in ecc_ed25519_private.pem -aes128 -out ecc_ed25519_private_enc_aes128.pem -passout 'pass:secret' -outform PEM
openssl pkey -in ecc_ed25519_private.pem -aes192 -out ecc_ed25519_private_enc_aes192.pem -passout 'pass:secret' -outform PEM
openssl pkey -in ecc_ed25519_private.pem -aes256 -out ecc_ed25519_private_enc_aes256.pem -passout 'pass:secret' -outform PEM
# GCM is not supported by openssl in this case...
#openssl pkey -in ecc_ed25519_private.pem -aes-256-gcm -out ecc_ed25519_private_enc_aes256_gcm.pem -passout 'pass:secret' -outform PEM
# Public key
openssl pkey -in ecc_ed25519_private.pem -pubout -out ecc_ed25519_public.pem
openssl pkey -pubin -in ecc_ed25519_public.pem -outform DER -out ecc_ed25519_public.der
# X.509 cert
openssl req -new -key ecc_ed25519_private.pem -days 365 -x509 -out ecc_ed25519_x509.pem -subj '/C=GB/CN=example.com'
openssl x509 -in ecc_ed25519_x509.pem -out ecc_ed25519_x509.der -outform DER
# OpenSSH - also the .pem.pub files are created
# Unfortunately, it does not seem possible to reuse ecc_ed25519_private.pem, we need to regenerate
ssh-keygen -t ed25519 -f ecc_ed25519_private_openssh.pem -P "" -C test
mv ecc_ed25519_private_openssh.pem.pub ecc_ed25519_public_openssh.txt
ssh-keygen -t ed25519 -f ecc_ed25519_private_openssh_pwd.pem -P "password" -C test
rm ecc_ed25519_private_openssh_pwd.pem.pub

View file

@ -0,0 +1 @@
OpenSSL 1.1.1n FIPS 15 Mar 2022

File diff suppressed because it is too large Load diff

View file

@ -1 +1 @@
__version__ = "1.0.7" __version__ = "1.0.8"