-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Adds the ed25519 functions to the python binding. #8257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| // Copyright James Keane 2026. Use, modification and distribution is | ||
| // subject to the Boost Software License, Version 1.0. (See accompanying | ||
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
|
|
||
| #include "boost_python.hpp" | ||
| #include <libtorrent/kademlia/ed25519.hpp> | ||
| #include "bytes.hpp" | ||
|
|
||
| using namespace boost::python; | ||
| using namespace lt; | ||
|
|
||
| namespace { | ||
|
|
||
| bytes create_seed() | ||
| { | ||
| auto const seed = dht::ed25519_create_seed(); | ||
| return bytes(seed.data(), seed.size()); | ||
| } | ||
|
|
||
| tuple create_keypair(bytes const& seed) | ||
| { | ||
| if (seed.arr.size() != 32) | ||
| throw std::invalid_argument("seed must be 32 bytes"); | ||
|
|
||
| std::array<char, 32> s; | ||
| std::copy(seed.arr.begin(), seed.arr.end(), s.begin()); | ||
|
|
||
| auto const [pk, sk] = dht::ed25519_create_keypair(s); | ||
| return make_tuple( | ||
| bytes(pk.bytes.data(), pk.bytes.size()), | ||
| bytes(sk.bytes.data(), sk.bytes.size())); | ||
| } | ||
|
|
||
| bytes sign(bytes const& msg, bytes const& pk, bytes const& sk) | ||
| { | ||
| if (pk.arr.size() != 32) | ||
| throw std::invalid_argument("public key must be 32 bytes"); | ||
| if (sk.arr.size() != 64) | ||
| throw std::invalid_argument("secret key must be 64 bytes"); | ||
|
|
||
| dht::public_key public_key(pk.arr.data()); | ||
| dht::secret_key secret_key(sk.arr.data()); | ||
|
|
||
| auto const sig = dht::ed25519_sign( | ||
| {msg.arr.data(), static_cast<std::ptrdiff_t>(msg.arr.size())}, | ||
| public_key, secret_key); | ||
| return bytes(sig.bytes.data(), sig.bytes.size()); | ||
| } | ||
|
|
||
| bool verify(bytes const& sig, bytes const& msg, bytes const& pk) | ||
| { | ||
| if (sig.arr.size() != 64) | ||
| throw std::invalid_argument("signature must be 64 bytes"); | ||
| if (pk.arr.size() != 32) | ||
| throw std::invalid_argument("public key must be 32 bytes"); | ||
|
|
||
| dht::signature signature(sig.arr.data()); | ||
| dht::public_key public_key(pk.arr.data()); | ||
|
|
||
| return dht::ed25519_verify(signature, | ||
| {msg.arr.data(), static_cast<std::ptrdiff_t>(msg.arr.size())}, | ||
| public_key); | ||
| } | ||
|
|
||
| bytes add_scalar_public(bytes const& pk, bytes const& scalar) | ||
| { | ||
| if (pk.arr.size() != 32) | ||
| throw std::invalid_argument("public key must be 32 bytes"); | ||
| if (scalar.arr.size() != 32) | ||
| throw std::invalid_argument("scalar must be 32 bytes"); | ||
|
|
||
| dht::public_key public_key(pk.arr.data()); | ||
| std::array<char, 32> s; | ||
| std::copy(scalar.arr.begin(), scalar.arr.end(), s.begin()); | ||
|
|
||
| auto const result = dht::ed25519_add_scalar(public_key, s); | ||
| return bytes(result.bytes.data(), result.bytes.size()); | ||
| } | ||
|
|
||
| bytes add_scalar_secret(bytes const& sk, bytes const& scalar) | ||
| { | ||
| if (sk.arr.size() != 64) | ||
| throw std::invalid_argument("secret key must be 64 bytes"); | ||
| if (scalar.arr.size() != 32) | ||
| throw std::invalid_argument("scalar must be 32 bytes"); | ||
|
|
||
| dht::secret_key secret_key(sk.arr.data()); | ||
| std::array<char, 32> s; | ||
| std::copy(scalar.arr.begin(), scalar.arr.end(), s.begin()); | ||
|
|
||
| auto const result = dht::ed25519_add_scalar(secret_key, s); | ||
| return bytes(result.bytes.data(), result.bytes.size()); | ||
| } | ||
|
|
||
| bytes key_exchange(bytes const& pk, bytes const& sk) | ||
| { | ||
| if (pk.arr.size() != 32) | ||
| throw std::invalid_argument("public key must be 32 bytes"); | ||
| if (sk.arr.size() != 64) | ||
| throw std::invalid_argument("secret key must be 64 bytes"); | ||
|
|
||
| dht::public_key public_key(pk.arr.data()); | ||
| dht::secret_key secret_key(sk.arr.data()); | ||
|
|
||
| auto const secret = dht::ed25519_key_exchange(public_key, secret_key); | ||
| return bytes(secret.data(), secret.size()); | ||
| } | ||
|
|
||
| } // anonymous namespace | ||
|
|
||
| void bind_ed25519() | ||
| { | ||
| def("ed25519_create_seed", &create_seed); | ||
| def("ed25519_create_keypair", &create_keypair); | ||
| def("ed25519_sign", &sign); | ||
| def("ed25519_verify", &verify); | ||
| def("ed25519_add_scalar_public", &add_scalar_public); | ||
| def("ed25519_add_scalar_secret", &add_scalar_secret); | ||
| def("ed25519_key_exchange", &key_exchange); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,141 @@ | ||||||
| import unittest | ||||||
|
|
||||||
| import libtorrent as lt | ||||||
|
|
||||||
|
|
||||||
| class Ed25519CreateSeedTest(unittest.TestCase): | ||||||
| def test_returns_32_bytes(self) -> None: | ||||||
| seed = lt.ed25519_create_seed() | ||||||
| self.assertEqual(len(seed), 32) | ||||||
|
|
||||||
| def test_seeds_are_unique(self) -> None: | ||||||
| seed1 = lt.ed25519_create_seed() | ||||||
| seed2 = lt.ed25519_create_seed() | ||||||
| self.assertNotEqual(seed1, seed2) | ||||||
|
Comment on lines
+11
to
+14
|
||||||
|
|
||||||
|
|
||||||
| class Ed25519CreateKeypairTest(unittest.TestCase): | ||||||
| def test_keypair_sizes(self) -> None: | ||||||
| seed = lt.ed25519_create_seed() | ||||||
| pk, sk = lt.ed25519_create_keypair(seed) | ||||||
| self.assertEqual(len(pk), 32) | ||||||
| self.assertEqual(len(sk), 64) | ||||||
|
|
||||||
| def test_deterministic(self) -> None: | ||||||
| seed = lt.ed25519_create_seed() | ||||||
| pk1, sk1 = lt.ed25519_create_keypair(seed) | ||||||
| pk2, sk2 = lt.ed25519_create_keypair(seed) | ||||||
| self.assertEqual(pk1, pk2) | ||||||
| self.assertEqual(sk1, sk2) | ||||||
|
|
||||||
| def test_different_seeds_produce_different_keys(self) -> None: | ||||||
| pk1, _ = lt.ed25519_create_keypair(lt.ed25519_create_seed()) | ||||||
| pk2, _ = lt.ed25519_create_keypair(lt.ed25519_create_seed()) | ||||||
| self.assertNotEqual(pk1, pk2) | ||||||
|
|
||||||
| def test_invalid_seed_length(self) -> None: | ||||||
| with self.assertRaises(Exception): | ||||||
|
||||||
| with self.assertRaises(Exception): | |
| with self.assertRaises(ValueError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file uses std::array, std::copy, std::invalid_argument, and std::ptrdiff_t but doesn’t include the corresponding standard headers. Relying on transitive includes can break builds depending on include order/toolchain. Add explicit includes for the standard library facilities used (e.g., , , , and ).