k98kurz

ed25519.py

Jun 3rd, 2022 (edited)
1,097
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 18.56 KB | None | 0 0
  1. from __future__ import annotations
  2. from hashlib import new
  3. from nacl.signing import SigningKey, SignedMessage, VerifyKey
  4. from secrets import token_bytes
  5. import nacl.bindings
  6.  
  7.  
  8. """
  9. Demonstration of Twisted Edwards Curve 2^255-19 digital signature system, its
  10. properties, and its adaptations. See below sources for more details.
  11.  
  12. Ed25519 (RFC 8032): https://www.rfc-editor.org/rfc/rfc8032.html
  13. Adapters: https://medium.com/crypto-garage/adaptor-signature-schnorr-signature-and-ecdsa-da0663c2adc4
  14. AMHLs: https://eprint.iacr.org/2018/472
  15. """
  16.  
  17. # helper functions
  18. def clamp_scalar(scalar: bytes, from_private_key: bool = False) -> bytes:
  19.     """Make a clamped scalar."""
  20.     if type(scalar) is bytes and len(scalar) >= 32:
  21.         x_i = bytearray(scalar[:32])
  22.     elif type(scalar) is SigningKey:
  23.         x_i = bytearray(new('sha512', bytes(scalar)).digest()[:32])
  24.         from_private_key = True
  25.     else:
  26.         raise ValueError('not a SigningKey and not 32+ bytes scalar')
  27.  
  28.     if from_private_key:
  29.         # set bits 0, 1, and 2 to 0
  30.         # nb: lsb is right-indexed
  31.         x_i[0] &= 0b11111000
  32.         # set bit 254 to 1
  33.         x_i[31] |= 0b01000000
  34.  
  35.     # set bit 255 to 0
  36.     x_i[31] &= 0b01111111
  37.  
  38.     return bytes(x_i)
  39.  
  40. def H_big(*parts) -> bytes:
  41.     """The big, 64-byte hash function."""
  42.     return new('sha512', b''.join(parts)).digest()
  43.  
  44. def H_small(*parts) -> bytes:
  45.     """The small, 32-byte hash function."""
  46.     return nacl.bindings.crypto_core_ed25519_scalar_reduce(H_big(*parts))
  47.  
  48. def derive_key_from_seed(seed: bytes) -> bytes:
  49.     """Derive the scalar used for signing from a seed."""
  50.     return clamp_scalar(H_big(seed)[:32], True)
  51.  
  52. def aggregate_points(points: list) -> bytes:
  53.     """Aggregate points on the Ed25519 curve."""
  54.     # type checking inputs
  55.     for pt in points:
  56.         if type(pt) is not bytes and type(pt) is not VerifyKey:
  57.             raise TypeError('each point must be bytes or VerifyKey')
  58.  
  59.     # normalize points to bytes
  60.     points = [pt if type(pt) is bytes else bytes(pt) for pt in points]
  61.  
  62.     # raise an error for invalid points
  63.     for pt in points:
  64.         if not nacl.bindings.crypto_core_ed25519_is_valid_point(pt):
  65.             raise ValueError('each point must be a valid ed25519 point')
  66.  
  67.     # compute the sum
  68.     sum = points[0]
  69.     for i in range(1, len(points)):
  70.         sum = nacl.bindings.crypto_core_ed25519_add(sum, points[i])
  71.  
  72.     return sum
  73.  
  74. def xor(b1: bytes, b2: bytes) -> bytes:
  75.     """XOR two equal-length byte strings together."""
  76.     b3 = bytearray()
  77.     for i in range(len(b1)):
  78.         b3.append(b1[i] ^ b2[i])
  79.  
  80.     return bytes(b3)
  81.  
  82. def bytes_are_same(b1: bytes, b2: bytes) -> bool:
  83.     """Timing-attack safe bytes comparison."""
  84.     return len(b1) == len(b2) and int.from_bytes(xor(b1, b2), 'little') == 0
  85.  
  86. def hexify(thing):
  87.     """Convert a data structure to something printable."""
  88.     if type(thing) is dict:
  89.         new_thing = {}
  90.         for key in thing:
  91.             value = thing[key]
  92.             key = key.hex() if type(key) is bytes else key
  93.             new_thing[key] = hexify(value)
  94.         return new_thing
  95.  
  96.     if type(thing) is list:
  97.         return [hexify(t) for t in thing]
  98.  
  99.     if type(thing) is tuple:
  100.         return tuple([hexify(t) for t in thing])
  101.  
  102.     if type(thing) is bytes:
  103.         return thing.hex()
  104.  
  105.     return thing
  106.  
  107.  
  108. # adapter functions
  109. def public_tweak_adapter(seed: bytes, m: bytes, T: bytes) -> tuple[bytes]:
  110.     """Create a public tweak adapter signature. Return vbalue is (R, T, sa)."""
  111.     x = derive_key_from_seed(seed)
  112.     X = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(x) # G^x
  113.     nonce = H_big(seed)[32:]
  114.     r = clamp_scalar(H_small(H_big(nonce, m))) # H(nonce || m)
  115.     R = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(r) # G^r
  116.     RT = aggregate_points((R, T)) # R + t
  117.     ca = clamp_scalar(H_small(RT, X, m)) # H(R + T || X || m)
  118.     sa = nacl.bindings.crypto_core_ed25519_scalar_add(r, nacl.bindings.crypto_core_ed25519_scalar_mul(ca, x)) # r + H(R + T || X || m) * x
  119.     return (R, T, sa)
  120.  
  121. def verify_public_tweak_adapter(adapter: tuple[bytes], m: bytes, X: bytes) -> bool:
  122.     """Verify a public tweak adapter is valid for a given message."""
  123.     (R, T, sa) = adapter
  124.     sa_G = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(sa) # sa_G = G^sa
  125.     RT = aggregate_points((R, T)) # R + T
  126.     ca = clamp_scalar(H_small(RT, X, m)) # H(R + T || X || m)
  127.     caX = nacl.bindings.crypto_scalarmult_ed25519_noclamp(ca, X) # X^H(R + T || X || m)
  128.     RcaX = aggregate_points((R, caX)) # R + X^H(R + T || X || m)
  129.     return bytes_are_same(sa_G, RcaX) # G^sa == R + X^H(R + T || X || m)
  130.  
  131. def decrypt_public_tweak_adapter(adapter: tuple[bytes], m: bytes, t: bytes) -> SignedMessage:
  132.     """Decrypt a public tweak adapter signature."""
  133.     (R, T, sa) = adapter
  134.     RT = aggregate_points((R, T)) # R + T
  135.     s = nacl.bindings.crypto_core_ed25519_scalar_add(sa, t) # s = sa + t
  136.     sig = SignedMessage(RT + s + m)
  137.     return sig
  138.  
  139.  
  140. # AMHL functions
  141. # Messy conventions taken directly from the Anonymous Multi-Hop Lock paper
  142. def sample() -> bytes:
  143.     """Take 1 sample from the domain of the homomorphic one-way function."""
  144.     return clamp_scalar(token_bytes(32))
  145.  
  146. def samples(n: int) -> tuple[bytes]:
  147.     """Take n samples from the domain of the homomorphic one-way function."""
  148.     return tuple(sample() for i in range(n))
  149.  
  150. def oneway(scalar: bytes) -> bytes:
  151.     """Run the homomorphic one-way function on the input scalar."""
  152.     return nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(scalar)
  153.  
  154. def setup(n_users: int) -> tuple[tuple[bytes], tuple[bytes]]:
  155.     """Setup the lock inputs."""
  156.     y = samples(n_users)
  157.     Y = [oneway(y[0])]
  158.     for i, y_i in enumerate(y):
  159.         if i > 0:
  160.             Y.append(aggregate_points((Y[i-1], oneway(y_i))))
  161.     return (y, tuple(Y))
  162.  
  163. def scalar_sum(*scalars: tuple[bytes]) -> bytes:
  164.     """Compute the sum of the scalars."""
  165.     sum = scalars[0]
  166.     for i in range(1, len(scalars)):
  167.         sum = nacl.bindings.crypto_core_ed25519_scalar_add(sum, scalars[i])
  168.     return sum
  169.  
  170. def setup_for(s: tuple[tuple[bytes], tuple[bytes]], i: int) -> tuple[bytes]:
  171.     """Generate the setup for a particular user given the setup s and
  172.        the user index i.
  173.    """
  174.     if i == 0:
  175.         return (s[0][0],)
  176.  
  177.     if i == len(s[0]):
  178.         return ((s[1][i-1], 0, 0), scalar_sum(*s[0]))
  179.  
  180.     return (s[1][i-1], s[1][i], s[0][i])
  181.  
  182. def check_setup(s: tuple[bytes], i: int, n: int) -> bool:
  183.     """Verifies the setup for the ith of n users is valid."""
  184.     if i == 0:
  185.         return len(s) == 1 and isinstance(s[0], bytes)
  186.  
  187.     if i == n:
  188.         return len(s) == 2 and type(s[0]) is tuple and len(s[0]) == 3 and type(s[0][0]) is bytes and type(s[1]) is bytes
  189.  
  190.     Y_i = aggregate_points((s[0], oneway(s[2])))
  191.     return bytes_are_same(Y_i, s[1])
  192.  
  193. def lock(s: tuple) -> tuple[bytes, bool]:
  194.     """Create the lock from the setup."""
  195.     return (s[1], False)
  196.  
  197. def release(k: bytes, sI: tuple) -> bytes:
  198.     """Release a left lock given a key from a released intermediate lock."""
  199.     y = sI[2] if type(sI) is tuple else sI
  200.     return nacl.bindings.crypto_core_ed25519_scalar_sub(k, y)
  201.  
  202. def verify_lock_key(l: bytes, k: bytes) -> bool:
  203.     """Verify that a key opens a lock."""
  204.     return bytes_are_same(l, oneway(k))
  205.  
  206.  
  207. # tests
  208. def test_sign_and_verify():
  209.     """Replication of the maths involved in signing and verifying."""
  210.     # set seed and generate key pair from it
  211.     seed = token_bytes(32)
  212.     skey = SigningKey(seed)
  213.  
  214.     # derive key pair manually
  215.     x = derive_key_from_seed(seed) # clamp(H(seed)[:32])
  216.     X = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(x) # G^x
  217.     m = b'hello world'
  218.  
  219.     # create signature with nacl
  220.     sig = skey.sign(m)
  221.  
  222.     # create signature manually
  223.     nonce = H_big(seed)[32:]
  224.     r = clamp_scalar(H_small(nonce, m))
  225.     R = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(r) # G^r
  226.     c = clamp_scalar(H_small(R, X, m)) # clamp(H(R || X || m))
  227.     s = nacl.bindings.crypto_core_ed25519_scalar_add(r, nacl.bindings.crypto_core_ed25519_scalar_mul(c, x)) # r + c*x
  228.     nsig = SignedMessage(R + s + m)
  229.  
  230.     # G, X, R, s, and m are public values
  231.     # c can be derived: H(R || X || m)
  232.     # Therefore, G^s = R * X^c can be verified by anyone
  233.     # But only the holder of x and r can create the signature
  234.  
  235.     # verify both
  236.     assert bytes_are_same(bytes(sig), bytes(nsig))
  237.     assert skey.verify_key.verify(sig) # s = c*x + r
  238.     assert skey.verify_key.verify(nsig) # G^s = G^(c*x + r) = R * G^(x*c) = R * X^c
  239.  
  240.  
  241. def test_homomorphic_one_way():
  242.     """Test if Ed25519 meets the homomorphic one way condition."""
  243.     x1, x2 = clamp_scalar(token_bytes(32)), clamp_scalar(token_bytes(32))
  244.     y1 = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(x1) # G^x1
  245.     y2 = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(x2) # G^x2
  246.  
  247.     # test
  248.     y3_1 = nacl.bindings.crypto_core_ed25519_add(y1, y2) # G^x1 + G^x2
  249.     x3 = nacl.bindings.crypto_core_ed25519_scalar_add(x1, x2) # x1 + x2
  250.     y3_2 = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(x3) # G^(x1+x2)
  251.     assert y3_1 == y3_2 # G^x1 * G^x2 = G^(x1+x2) where * denotes group operator
  252.  
  253.  
  254. def test_adapter_signature():
  255.     """Test the construction, unlocking, and verification of adapters.
  256.        For an explanation and possible uses, see this medium article:
  257.        https://medium.com/crypto-garage/adaptor-signature-schnorr-signature-and-ecdsa-da0663c2adc4
  258.    """
  259.     # key pairs and message
  260.     seed1, seed2 = token_bytes(32), token_bytes(32)
  261.     skey = SigningKey(seed1)
  262.     x, t = derive_key_from_seed(seed1), derive_key_from_seed(seed2)
  263.     X = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(x) # G^x
  264.     T = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(t) # G^t
  265.     m = b'txn sending money to counterparty as part of payment route'
  266.  
  267.     # private key tweaking construction
  268.     # sa = t + r + H(R || X || m) * x
  269.     nonce = H_big(seed1)[32:]
  270.     r = clamp_scalar(H_small(nonce, m))
  271.     R = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(r) # G^r
  272.     c = clamp_scalar(H_small(R, X, m)) # clamp(H(R || X || m))
  273.     tr = nacl.bindings.crypto_core_ed25519_scalar_add(t, r)
  274.     sa = nacl.bindings.crypto_core_ed25519_scalar_add(tr, nacl.bindings.crypto_core_ed25519_scalar_mul(c, x)) # t + r + c*x
  275.  
  276.     # public values: R, sa, m, X, T
  277.     # sig = (T, R, sa, m)
  278.  
  279.     # private key tweaking adapter verification
  280.     # sa_G = G^sa
  281.     sa_G = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(sa)
  282.     c = clamp_scalar(H_small(R, X, m)) # clamp(H(R || X || m))
  283.     cX = nacl.bindings.crypto_scalarmult_ed25519_noclamp(c, X)
  284.     TRcX = aggregate_points((T, R, cX))
  285.     assert bytes_are_same(sa_G, TRcX)
  286.     # G^sa == T + R + X^H(R || X || m)
  287.  
  288.     # private key tweaking signature decryption and verification
  289.     # s = sa - t
  290.     s = nacl.bindings.crypto_core_ed25519_scalar_sub(sa, t)
  291.     sig = SignedMessage(R + s + m)
  292.     assert skey.verify_key.verify(sig)
  293.     # G^(sa - t) == T + R + X^H(R || X || m) - T
  294.     # G^s == R + X^H(R || X || m)
  295.  
  296.  
  297.     # public key tweaking construction
  298.     # sa = r + H(R + T || X || m) * x
  299.     RT = aggregate_points((R, T))
  300.     ca = clamp_scalar(H_small(RT, X, m))
  301.     sa = nacl.bindings.crypto_core_ed25519_scalar_add(r, nacl.bindings.crypto_core_ed25519_scalar_mul(ca, x))
  302.  
  303.     # public values: R, sa, m, X, T
  304.     # ca and RT can be derived from public values
  305.     # sig = (T, R, sa, m)
  306.  
  307.     # public key tweaking adapter verification
  308.     # sa_G = G^sa
  309.     sa_G = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(sa)
  310.     caX = nacl.bindings.crypto_scalarmult_ed25519_noclamp(ca, X)
  311.     RcaX = aggregate_points((R, caX))
  312.     assert bytes_are_same(sa_G, RcaX)
  313.     # G^sa == R + X^H(R + T || X || m)
  314.  
  315.     # public key tweaking signature decryption and verification
  316.     # s = sa + t
  317.     s = nacl.bindings.crypto_core_ed25519_scalar_add(sa, t)
  318.     sig = SignedMessage(RT + s + m)
  319.     assert skey.verify_key.verify(sig)
  320.     # G^(sa + t) == R + X^H(R + T || X || m) + T
  321.     # G^s == (R + T) + X^H((R + T) || X || m)
  322.     ...
  323.  
  324.  
  325. def test_AMHL():
  326.     """Test for setup, locking, and release of an Anonymous Multi-Hop
  327.        Lock using the homomorphic qualities of ed25519.
  328.    """
  329.     # first run the initial setup for 5 payers (4 intermediate)
  330.     n = 5
  331.     s = setup(n)
  332.  
  333.     # validate setups for each user
  334.     for i in range(len(s[0])):
  335.         assert check_setup(setup_for(s, i), i, n)
  336.  
  337.     # validate releasing of locks from right to left
  338.     s_n = setup_for(s, n)
  339.     k = s_n[1]
  340.  
  341.     for i in range(n-1, 1, -1):
  342.         s_i = setup_for(s, i)
  343.         r = release(k, s_i)
  344.         assert verify_lock_key(setup_for(s, i-1)[1], r)
  345.         k = r
  346.  
  347.  
  348. def test_AMHL_adapters():
  349.     """Test for combining the AMHL primitive with the adapter signature
  350.        primitive for atomic transaction locking/unlocking. In this case,
  351.        Alice pays Dave through Bob and Carla, with Bob and Carla each
  352.        taking a fee for faciliating the transaction.
  353.    """
  354.     # first run the initial setup for 3 payers (2 intermediate)
  355.     n = 3
  356.     s = setup(n)
  357.  
  358.     # validate setups for each user
  359.     for i in range(len(s[0])):
  360.         assert check_setup(setup_for(s, i), i, n)
  361.  
  362.     # Alice setup
  363.     Alice = {
  364.         'seed' : token_bytes(32),
  365.         'outbound_txn' : {
  366.             'm': b'Alice pays Bob 12'
  367.         },
  368.         'lock': setup_for(s, 0)
  369.     }
  370.     Alice['skey'] = SigningKey(Alice['seed'])
  371.     Alice['vkey'] = Alice['skey'].verify_key
  372.     Alice['x'] = derive_key_from_seed(Alice['seed'])
  373.     Alice['X'] = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(Alice['x'])
  374.     # create adapter
  375.     Alice['outbound_txn']['adapter'] = public_tweak_adapter(Alice['seed'], Alice['outbound_txn']['m'], oneway(Alice['lock'][0]))
  376.  
  377.     # Bob setup
  378.     Bob = {
  379.         'seed' : token_bytes(32),
  380.         'inbound_txn': Alice['outbound_txn'],
  381.         'outbound_txn' : {
  382.             'm': b'Bob pays Carla 11'
  383.         },
  384.         'lock': setup_for(s, 1)
  385.     }
  386.     Bob['skey'] = SigningKey(Bob['seed'])
  387.     Bob['vkey'] = Bob['skey'].verify_key
  388.     Bob['x'] = derive_key_from_seed(Bob['seed'])
  389.     Bob['X'] = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(Bob['x'])
  390.     # verify Bob's left lock is Alice's right lock
  391.     assert bytes_are_same(Bob['lock'][0], oneway(Alice['lock'][0]))
  392.     # verify Alice's adapter
  393.     assert verify_public_tweak_adapter(Bob['inbound_txn']['adapter'], Bob['inbound_txn']['m'], Alice['X'])
  394.     # create adapter
  395.     Bob['outbound_txn']['adapter'] = public_tweak_adapter(Bob['seed'], Bob['outbound_txn']['m'], Bob['lock'][1])
  396.  
  397.     # Carla setup
  398.     Carla = {
  399.         'seed' : token_bytes(32),
  400.         'inbound_txn' : Bob['outbound_txn'],
  401.         'outbound_txn': {
  402.             'm': b'Carla pays Dave 10'
  403.         },
  404.         'lock': setup_for(s, 2)
  405.     }
  406.     Carla['skey'] = SigningKey(Carla['seed'])
  407.     Carla['vkey'] = Carla['skey'].verify_key
  408.     Carla['x'] = derive_key_from_seed(Carla['seed'])
  409.     Carla['X'] = nacl.bindings.crypto_scalarmult_ed25519_base_noclamp(Carla['x'])
  410.     # verify Carla's left lock is Bob's right lock
  411.     assert bytes_are_same(Carla['lock'][0], Bob['lock'][1])
  412.     # verify Bob's adapter
  413.     assert verify_public_tweak_adapter(Carla['inbound_txn']['adapter'], Carla['inbound_txn']['m'], Bob['X'])
  414.     # create adapter
  415.     Carla['outbound_txn']['adapter'] = public_tweak_adapter(Carla['seed'], Carla['outbound_txn']['m'], Carla['lock'][1])
  416.  
  417.     # Dave setup
  418.     Dave = {
  419.         'seed' : token_bytes(32),
  420.         'inbound_txn' : Carla['outbound_txn'],
  421.         'lock': setup_for(s, 3)
  422.     }
  423.     # verify Dave's left lock is Carla's right lock
  424.     assert bytes_are_same(Dave['lock'][0][0], Carla['lock'][1])
  425.     # verify Carla's adapter
  426.     assert verify_public_tweak_adapter(Dave['inbound_txn']['adapter'], Dave['inbound_txn']['m'], Carla['X'])
  427.  
  428.     # decrypt adapter paying Dave
  429.     k = Dave['lock'][1]
  430.     Dave['inbound_txn']['signature'] = decrypt_public_tweak_adapter(Dave['inbound_txn']['adapter'], Dave['inbound_txn']['m'], k)
  431.     assert Carla['vkey'].verify(Dave['inbound_txn']['signature'])
  432.  
  433.     # release lock for Carla
  434.     r = release(k, Carla['lock'])
  435.     assert verify_lock_key(Carla['lock'][0], r) # Carla's left lock == Bob's right lock
  436.  
  437.     # decrypt adapter paying Carla
  438.     Carla['inbound_txn']['signature'] = decrypt_public_tweak_adapter(Carla['inbound_txn']['adapter'], Carla['inbound_txn']['m'], r)
  439.     assert Bob['vkey'].verify(Carla['inbound_txn']['signature'])
  440.  
  441.     # release lock for Bob
  442.     r = release(r, Bob['lock'])
  443.     assert verify_lock_key(Bob['lock'][0], r) # Bob's left lock == Alice's right lock
  444.  
  445.     # decrypt adapter paying Bob
  446.     Bob['inbound_txn']['signature'] = decrypt_public_tweak_adapter(Bob['inbound_txn']['adapter'], Bob['inbound_txn']['m'], r)
  447.     assert Alice['vkey'].verify(Bob['inbound_txn']['signature'])
  448.  
  449.     # Dave now has a signed transaction paying him from Carla
  450.     # Carla now has a signed transaction paying her from Bob
  451.     # Bob now has a signed transaction paying him from Alice
  452.     # Alice has paid Dave through Bob and Carla
  453.     ...
  454.  
  455.  
  456. def license():
  457.     """Copyleft (c) 2022 k98kurz
  458.  
  459.        Permission to use, copy, modify, and/or distribute this software
  460.        for any purpose with or without fee is hereby granted, provided
  461.        that the above copyleft notice and this permission notice appear in
  462.        all copies.
  463.  
  464.        THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
  465.        WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
  466.        WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
  467.        AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
  468.        CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  469.        OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  470.        NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  471.        CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  472.    """
  473.     return license.__doc__
  474.  
  475.  
  476. if __name__ == '__main__':
  477.     for i in range(128):
  478.         test_homomorphic_one_way()
  479.     print('homomorphic one-way function test passed')
  480.  
  481.     for i in range(128):
  482.         test_sign_and_verify()
  483.     print('signature creation and verification test passed')
  484.  
  485.     for i in range(128):
  486.         test_adapter_signature()
  487.     print('adapter signature test passed')
  488.  
  489.     for i in range(128):
  490.         test_AMHL()
  491.     print('anonymous multihop lock test passed')
  492.  
  493.     for i in range(128):
  494.         test_AMHL_adapters()
  495.     print('AMHL adapter (payment channel routing) test passed')
  496.  
Add Comment
Please, Sign In to add comment