diff --git a/Makefile b/Makefile
index fcebe0d..28a4582 100644
--- a/Makefile
+++ b/Makefile
@@ -11,4 +11,5 @@ lintroll:
 	flake8 $(FILES_TO_LINT)
 
 protobufs:
+	cd libp2p/crypto/pb && protoc --python_out=. crypto.proto
 	cd libp2p/pubsub/pb && protoc --python_out=. rpc.proto
diff --git a/libp2p/__init__.py b/libp2p/__init__.py
index 6dd42cd..af72cb8 100644
--- a/libp2p/__init__.py
+++ b/libp2p/__init__.py
@@ -1,8 +1,7 @@
 import asyncio
 from typing import Mapping, Sequence
 
-from Crypto.PublicKey import RSA
-
+from libp2p.crypto.rsa import create_new_key_pair
 from libp2p.host.basic_host import BasicHost
 from libp2p.kademlia.network import KademliaServer
 from libp2p.kademlia.storage import IStorage
@@ -34,9 +33,10 @@ async def cleanup_done_tasks() -> None:
         await asyncio.sleep(3)
 
 
-def generate_id() -> ID:
-    new_key = RSA.generate(2048, e=65537).publickey().export_key("DER")
-    new_id = ID.from_pubkey(new_key)
+def generate_peer_id_from_rsa_identity() -> ID:
+    new_key_pair = create_new_key_pair()
+    new_public_key = new_key_pair.public_key
+    new_id = ID.from_pubkey(new_public_key)
     return new_id
 
 
@@ -53,7 +53,7 @@ def initialize_default_kademlia_router(
     :return: return a default kademlia instance
     """
     if not id_opt:
-        id_opt = generate_id()
+        id_opt = generate_peer_id_from_rsa_identity()
 
     node_id = id_opt.to_bytes()
     # ignore type for Kademlia module
@@ -83,7 +83,7 @@ def initialize_default_swarm(
     """
 
     if not id_opt:
-        id_opt = generate_id()
+        id_opt = generate_peer_id_from_rsa_identity()
 
     # TODO parse transport_opt to determine transport
     transport_opt = transport_opt or ["/ip4/127.0.0.1/tcp/8001"]
@@ -124,7 +124,7 @@ async def new_node(
     """
 
     if not id_opt:
-        id_opt = generate_id()
+        id_opt = generate_peer_id_from_rsa_identity()
 
     if not swarm_opt:
         swarm_opt = initialize_default_swarm(
diff --git a/libp2p/crypto/__init__.py b/libp2p/crypto/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/libp2p/crypto/keys.py b/libp2p/crypto/keys.py
new file mode 100644
index 0000000..9751705
--- /dev/null
+++ b/libp2p/crypto/keys.py
@@ -0,0 +1,82 @@
+from abc import ABC, abstractmethod
+from dataclasses import dataclass
+from enum import Enum, unique
+
+from .pb import crypto_pb2 as protobuf
+
+
+@unique
+class KeyType(Enum):
+    RSA = 0
+    Ed25519 = 1
+    Secp256k1 = 2
+    ECDSA = 3
+
+
+class Key:
+    """
+    A ``Key`` represents a cryptographic key.
+    """
+
+    @abstractmethod
+    def to_bytes(self) -> bytes:
+        """
+        Returns the byte representation of this key.
+        """
+        ...
+
+    @abstractmethod
+    def get_type(self) -> KeyType:
+        """
+        Returns the ``KeyType`` for ``self``.
+        """
+        ...
+
+
+class PublicKey(ABC, Key):
+    """
+    A ``PublicKey`` represents a cryptographic public key.
+    """
+
+    @abstractmethod
+    def verify(self, data: bytes, signature: bytes) -> bool:
+        """
+        Verify that ``signature`` is the cryptographic signature of the hash of ``data``.
+        """
+        ...
+
+    def serialize_to_protobuf(self) -> protobuf.PublicKey:
+        _type = self.get_type()
+        data = self.to_bytes()
+        protobuf_key = protobuf.PublicKey()
+        protobuf_key.key_type = _type.value
+        protobuf_key.data = data
+        return protobuf_key
+
+
+class PrivateKey(ABC, Key):
+    """
+    A ``PrivateKey`` represents a cryptographic private key.
+    """
+
+    @abstractmethod
+    def sign(self, data: bytes) -> bytes:
+        ...
+
+    @abstractmethod
+    def get_public_key(self) -> PublicKey:
+        ...
+
+    def serialize_to_protobuf(self) -> protobuf.PrivateKey:
+        _type = self.get_type()
+        data = self.to_bytes()
+        protobuf_key = protobuf.PrivateKey()
+        protobuf_key.key_type = _type.value
+        protobuf_key.data = data
+        return protobuf_key
+
+
+@dataclass(frozen=True)
+class KeyPair:
+    private_key: PrivateKey
+    public_key: PublicKey
diff --git a/libp2p/crypto/pb/crypto.proto b/libp2p/crypto/pb/crypto.proto
new file mode 100644
index 0000000..a12f7b6
--- /dev/null
+++ b/libp2p/crypto/pb/crypto.proto
@@ -0,0 +1,20 @@
+syntax = "proto2";
+
+package crypto.pb;
+
+enum KeyType {
+  RSA = 0;
+  Ed25519 = 1;
+  Secp256k1 = 2;
+  ECDSA = 3;
+}
+
+message PublicKey {
+  required KeyType key_type = 1;
+  required bytes data = 2;
+}
+
+message PrivateKey {
+  required KeyType key_type = 1;
+  required bytes data = 2;
+}
\ No newline at end of file
diff --git a/libp2p/crypto/pb/crypto_pb2.py b/libp2p/crypto/pb/crypto_pb2.py
new file mode 100644
index 0000000..0e4aa1f
--- /dev/null
+++ b/libp2p/crypto/pb/crypto_pb2.py
@@ -0,0 +1,162 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: crypto.proto
+
+import sys
+_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
+from google.protobuf.internal import enum_type_wrapper
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
+from google.protobuf import symbol_database as _symbol_database
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor.FileDescriptor(
+  name='crypto.proto',
+  package='crypto.pb',
+  syntax='proto2',
+  serialized_options=None,
+  serialized_pb=_b('\n\x0c\x63rypto.proto\x12\tcrypto.pb\"?\n\tPublicKey\x12$\n\x08key_type\x18\x01 \x02(\x0e\x32\x12.crypto.pb.KeyType\x12\x0c\n\x04\x64\x61ta\x18\x02 \x02(\x0c\"@\n\nPrivateKey\x12$\n\x08key_type\x18\x01 \x02(\x0e\x32\x12.crypto.pb.KeyType\x12\x0c\n\x04\x64\x61ta\x18\x02 \x02(\x0c*9\n\x07KeyType\x12\x07\n\x03RSA\x10\x00\x12\x0b\n\x07\x45\x64\x32\x35\x35\x31\x39\x10\x01\x12\r\n\tSecp256k1\x10\x02\x12\t\n\x05\x45\x43\x44SA\x10\x03')
+)
+
+_KEYTYPE = _descriptor.EnumDescriptor(
+  name='KeyType',
+  full_name='crypto.pb.KeyType',
+  filename=None,
+  file=DESCRIPTOR,
+  values=[
+    _descriptor.EnumValueDescriptor(
+      name='RSA', index=0, number=0,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='Ed25519', index=1, number=1,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='Secp256k1', index=2, number=2,
+      serialized_options=None,
+      type=None),
+    _descriptor.EnumValueDescriptor(
+      name='ECDSA', index=3, number=3,
+      serialized_options=None,
+      type=None),
+  ],
+  containing_type=None,
+  serialized_options=None,
+  serialized_start=158,
+  serialized_end=215,
+)
+_sym_db.RegisterEnumDescriptor(_KEYTYPE)
+
+KeyType = enum_type_wrapper.EnumTypeWrapper(_KEYTYPE)
+RSA = 0
+Ed25519 = 1
+Secp256k1 = 2
+ECDSA = 3
+
+
+
+_PUBLICKEY = _descriptor.Descriptor(
+  name='PublicKey',
+  full_name='crypto.pb.PublicKey',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='key_type', full_name='crypto.pb.PublicKey.key_type', index=0,
+      number=1, type=14, cpp_type=8, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='data', full_name='crypto.pb.PublicKey.data', index=1,
+      number=2, type=12, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=27,
+  serialized_end=90,
+)
+
+
+_PRIVATEKEY = _descriptor.Descriptor(
+  name='PrivateKey',
+  full_name='crypto.pb.PrivateKey',
+  filename=None,
+  file=DESCRIPTOR,
+  containing_type=None,
+  fields=[
+    _descriptor.FieldDescriptor(
+      name='key_type', full_name='crypto.pb.PrivateKey.key_type', index=0,
+      number=1, type=14, cpp_type=8, label=2,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='data', full_name='crypto.pb.PrivateKey.data', index=1,
+      number=2, type=12, cpp_type=9, label=2,
+      has_default_value=False, default_value=_b(""),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
+  ],
+  extensions=[
+  ],
+  nested_types=[],
+  enum_types=[
+  ],
+  serialized_options=None,
+  is_extendable=False,
+  syntax='proto2',
+  extension_ranges=[],
+  oneofs=[
+  ],
+  serialized_start=92,
+  serialized_end=156,
+)
+
+_PUBLICKEY.fields_by_name['key_type'].enum_type = _KEYTYPE
+_PRIVATEKEY.fields_by_name['key_type'].enum_type = _KEYTYPE
+DESCRIPTOR.message_types_by_name['PublicKey'] = _PUBLICKEY
+DESCRIPTOR.message_types_by_name['PrivateKey'] = _PRIVATEKEY
+DESCRIPTOR.enum_types_by_name['KeyType'] = _KEYTYPE
+_sym_db.RegisterFileDescriptor(DESCRIPTOR)
+
+PublicKey = _reflection.GeneratedProtocolMessageType('PublicKey', (_message.Message,), dict(
+  DESCRIPTOR = _PUBLICKEY,
+  __module__ = 'crypto_pb2'
+  # @@protoc_insertion_point(class_scope:crypto.pb.PublicKey)
+  ))
+_sym_db.RegisterMessage(PublicKey)
+
+PrivateKey = _reflection.GeneratedProtocolMessageType('PrivateKey', (_message.Message,), dict(
+  DESCRIPTOR = _PRIVATEKEY,
+  __module__ = 'crypto_pb2'
+  # @@protoc_insertion_point(class_scope:crypto.pb.PrivateKey)
+  ))
+_sym_db.RegisterMessage(PrivateKey)
+
+
+# @@protoc_insertion_point(module_scope)
diff --git a/libp2p/crypto/rsa.py b/libp2p/crypto/rsa.py
new file mode 100644
index 0000000..ceea008
--- /dev/null
+++ b/libp2p/crypto/rsa.py
@@ -0,0 +1,50 @@
+import Crypto.PublicKey.RSA as RSA
+from Crypto.PublicKey.RSA import RsaKey
+
+from libp2p.crypto.keys import KeyPair, KeyType, PrivateKey, PublicKey
+
+
+class RSAPublicKey(PublicKey):
+    def __init__(self, impl: RsaKey) -> None:
+        self.impl = impl
+
+    def to_bytes(self) -> bytes:
+        return self.impl.export_key("DER")
+
+    def get_type(self) -> KeyType:
+        return KeyType.RSA
+
+    def verify(self, data: bytes, signature: bytes) -> bool:
+        raise NotImplementedError
+
+
+class RSAPrivateKey(PrivateKey):
+    def __init__(self, impl: RsaKey) -> None:
+        self.impl = impl
+
+    @classmethod
+    def new(cls, bits: int = 2048, e: int = 65537) -> "RSAPrivateKey":
+        private_key_impl = RSA.generate(bits, e=e)
+        return cls(private_key_impl)
+
+    def to_bytes(self) -> bytes:
+        return self.impl.export_key("DER")
+
+    def get_type(self) -> KeyType:
+        return KeyType.RSA
+
+    def sign(self, data: bytes) -> bytes:
+        raise NotImplementedError
+
+    def get_public_key(self) -> PublicKey:
+        return RSAPublicKey(self.impl.publickey())
+
+
+def create_new_key_pair(bits: int = 2048, e: int = 65537) -> KeyPair:
+    """
+    Returns a new RSA keypair with the requested key size (``bits``) and the given public
+    exponent ``e``. Sane defaults are provided for both values.
+    """
+    private_key = RSAPrivateKey.new(bits, e)
+    public_key = private_key.get_public_key()
+    return KeyPair(private_key, public_key)
diff --git a/libp2p/crypto/secp256k1.py b/libp2p/crypto/secp256k1.py
new file mode 100644
index 0000000..79ffc9d
--- /dev/null
+++ b/libp2p/crypto/secp256k1.py
@@ -0,0 +1,52 @@
+import coincurve
+
+from libp2p.crypto.keys import KeyPair, KeyType, PrivateKey, PublicKey
+
+
+class Secp256k1PublicKey(PublicKey):
+    def __init__(self, impl: coincurve.PublicKey) -> None:
+        self.impl = impl
+
+    def to_bytes(self) -> bytes:
+        return self.impl.format()
+
+    def get_type(self) -> KeyType:
+        return KeyType.Secp256k1
+
+    def verify(self, data: bytes, signature: bytes) -> bool:
+        raise NotImplementedError
+
+
+class Secp256k1PrivateKey(PrivateKey):
+    def __init__(self, impl: coincurve.PrivateKey) -> None:
+        self.impl = impl
+
+    @classmethod
+    def new(cls, secret: bytes = None) -> "Secp256k1PrivateKey":
+        private_key_impl = coincurve.PrivateKey(secret)
+        return cls(private_key_impl)
+
+    def to_bytes(self) -> bytes:
+        return self.impl.secret
+
+    def get_type(self) -> KeyType:
+        return KeyType.Secp256k1
+
+    def sign(self, data: bytes) -> bytes:
+        raise NotImplementedError
+
+    def get_public_key(self) -> PublicKey:
+        public_key_impl = coincurve.PublicKey.from_secret(self.impl.secret)
+        return Secp256k1PublicKey(public_key_impl)
+
+
+def create_new_key_pair(secret: bytes = None) -> KeyPair:
+    """
+    Returns a new Secp256k1 keypair derived from the provided ``secret``,
+    a sequence of bytes corresponding to some integer between 0 and the group order.
+
+    A valid secret is created if ``None`` is passed.
+    """
+    private_key = Secp256k1PrivateKey.new(secret)
+    public_key = private_key.get_public_key()
+    return KeyPair(private_key, public_key)
diff --git a/libp2p/peer/id.py b/libp2p/peer/id.py
index 9e39cf0..bfb78d0 100644
--- a/libp2p/peer/id.py
+++ b/libp2p/peer/id.py
@@ -4,6 +4,15 @@ from typing import Union
 import base58
 import multihash
 
+from libp2p.crypto.keys import PublicKey
+
+
+def _serialize_public_key(key: PublicKey) -> bytes:
+    """
+    Serializes ``key`` in the way expected to form valid peer ids.
+    """
+    return key.serialize_to_protobuf().SerializeToString()
+
 
 class ID:
 
@@ -28,10 +37,10 @@ class ID:
             self._b58_str = base58.b58encode(self._bytes).decode()
         return self._b58_str
 
-    def __bytes__(self) -> bytes:
-        return self._bytes
+    def __repr__(self) -> str:
+        return "<libp2p.peer.id.ID 0x" + self._bytes.hex() + ">"
 
-    __repr__ = __str__ = pretty = to_string = to_base58
+    __str__ = pretty = to_string = to_base58
 
     def __eq__(self, other: object) -> bool:
         if isinstance(other, str):
@@ -53,9 +62,10 @@ class ID:
         return pid
 
     @classmethod
-    def from_pubkey(cls, key: bytes) -> "ID":
+    def from_pubkey(cls, key: PublicKey) -> "ID":
+        serialized_key = _serialize_public_key(key)
         algo = multihash.Func.sha2_256
-        mh_digest = multihash.digest(key, algo)
+        mh_digest = multihash.digest(serialized_key, algo)
         return cls(mh_digest.encode())
 
 
diff --git a/setup.py b/setup.py
index 779a704..f91ec65 100644
--- a/setup.py
+++ b/setup.py
@@ -38,6 +38,7 @@ setuptools.setup(
         "rpcudp>=3.0.0,<4.0.0",
         "lru-dict>=1.1.6",
         "protobuf==3.9.0",
+        "coincurve==12.0.0",
     ],
     extras_require=extras_require,
     packages=setuptools.find_packages(exclude=["tests", "tests.*"]),

`Peer.ID` implementation only offers `RSA` right now
The peer identity spec supports several different cryptosystems for identity generation; we should at least make it straightforward to use `secp256k1` keys.
diff --git a/tests/peer/test_interop.py b/tests/peer/test_interop.py
new file mode 100644
index 0000000..17dcb4c
--- /dev/null
+++ b/tests/peer/test_interop.py
@@ -0,0 +1,43 @@
+import base64
+
+import Crypto.PublicKey.RSA as RSA
+
+from libp2p.crypto.pb import crypto_pb2 as pb
+from libp2p.crypto.rsa import RSAPrivateKey
+from libp2p.peer.id import ID
+
+# ``PRIVATE_KEY_PROTOBUF_SERIALIZATION`` is a protobuf holding an RSA private key.
+PRIVATE_KEY_PROTOBUF_SERIALIZATION = """
+CAAS4AQwggJcAgEAAoGBAL7w+Wc4VhZhCdM/+Hccg5Nrf4q9NXWwJylbSrXz/unFS24wyk6pEk0zi3W
+7li+vSNVO+NtJQw9qGNAMtQKjVTP+3Vt/jfQRnQM3s6awojtjueEWuLYVt62z7mofOhCtj+VwIdZNBo
+/EkLZ0ETfcvN5LVtLYa8JkXybnOPsLvK+PAgMBAAECgYBdk09HDM7zzL657uHfzfOVrdslrTCj6p5mo
+DzvCxLkkjIzYGnlPuqfNyGjozkpSWgSUc+X+EGLLl3WqEOVdWJtbM61fewEHlRTM5JzScvwrJ39t7o6
+CCAjKA0cBWBd6UWgbN/t53RoWvh9HrA2AW5YrT0ZiAgKe9y7EMUaENVJ8QJBAPhpdmb4ZL4Fkm4OKia
+NEcjzn6mGTlZtef7K/0oRC9+2JkQnCuf6HBpaRhJoCJYg7DW8ZY+AV6xClKrgjBOfERMCQQDExhnzu2
+dsQ9k8QChBlpHO0TRbZBiQfC70oU31kM1AeLseZRmrxv9Yxzdl8D693NNWS2JbKOXl0kMHHcuGQLMVA
+kBZ7WvkmPV3aPL6jnwp2pXepntdVnaTiSxJ1dkXShZ/VSSDNZMYKY306EtHrIu3NZHtXhdyHKcggDXr
+qkBrdgErAkAlpGPojUwemOggr4FD8sLX1ot2hDJyyV7OK2FXfajWEYJyMRL1Gm9Uk1+Un53RAkJneqp
+JGAzKpyttXBTIDO51AkEA98KTiROMnnU8Y6Mgcvr68/SMIsvCYMt9/mtwSBGgl80VaTQ5Hpaktl6Xbh
+VUt5Wv0tRxlXZiViCGCD1EtrrwTw==
+""".replace(
+    "\n", ""
+)
+
+EXPECTED_PEER_ID = "QmRK3JgmVEGiewxWbhpXLJyjWuGuLeSTMTndA1coMHEy5o"
+
+
+# NOTE: this test checks that we can recreate the expected peer id given a private key
+# serialization, taken from the Go implementation of libp2p.
+def test_peer_id_interop():
+    private_key_protobuf_bytes = base64.b64decode(PRIVATE_KEY_PROTOBUF_SERIALIZATION)
+    private_key_protobuf = pb.PrivateKey()
+    private_key_protobuf.ParseFromString(private_key_protobuf_bytes)
+
+    private_key_data = private_key_protobuf.data
+
+    private_key_impl = RSA.import_key(private_key_data)
+    private_key = RSAPrivateKey(private_key_impl)
+    public_key = private_key.get_public_key()
+
+    peer_id = ID.from_pubkey(public_key)
+    assert peer_id == EXPECTED_PEER_ID
diff --git a/tests/peer/test_peerid.py b/tests/peer/test_peerid.py
index 65b3c06..30b74c1 100644
--- a/tests/peer/test_peerid.py
+++ b/tests/peer/test_peerid.py
@@ -1,32 +1,26 @@
 import random
 
-from Crypto.PublicKey import RSA
 import base58
 import multihash
-import pytest
 
+from libp2p.crypto.rsa import create_new_key_pair
 from libp2p.peer.id import ID
 
 ALPHABETS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
 
 
-def test_init():
+def test_eq_impl_for_bytes():
     random_id_string = ""
     for _ in range(10):
-        random_id_string += random.SystemRandom().choice(ALPHABETS)
+        random_id_string += random.choice(ALPHABETS)
     peer_id = ID(random_id_string.encode())
     assert peer_id == random_id_string.encode()
 
 
-def test_no_init_value():
-    with pytest.raises(Exception):
-        ID()
-
-
 def test_pretty():
     random_id_string = ""
     for _ in range(10):
-        random_id_string += random.SystemRandom().choice(ALPHABETS)
+        random_id_string += random.choice(ALPHABETS)
     peer_id = ID(random_id_string.encode())
     actual = peer_id.pretty()
     expected = base58.b58encode(random_id_string).decode()
@@ -37,7 +31,7 @@ def test_pretty():
 def test_str_less_than_10():
     random_id_string = ""
     for _ in range(5):
-        random_id_string += random.SystemRandom().choice(ALPHABETS)
+        random_id_string += random.choice(ALPHABETS)
     peer_id = base58.b58encode(random_id_string).decode()
     expected = peer_id
     actual = ID(random_id_string.encode()).__str__()
@@ -48,7 +42,7 @@ def test_str_less_than_10():
 def test_str_more_than_10():
     random_id_string = ""
     for _ in range(10):
-        random_id_string += random.SystemRandom().choice(ALPHABETS)
+        random_id_string += random.choice(ALPHABETS)
     peer_id = base58.b58encode(random_id_string).decode()
     expected = peer_id
     actual = ID(random_id_string.encode()).__str__()
@@ -59,7 +53,7 @@ def test_str_more_than_10():
 def test_eq_true():
     random_id_string = ""
     for _ in range(10):
-        random_id_string += random.SystemRandom().choice(ALPHABETS)
+        random_id_string += random.choice(ALPHABETS)
     peer_id = ID(random_id_string.encode())
 
     assert peer_id == base58.b58encode(random_id_string).decode()
@@ -74,21 +68,10 @@ def test_eq_false():
     assert peer_id != other
 
 
-def test_hash():
-    random_id_string = ""
-    for _ in range(10):
-        random_id_string += random.SystemRandom().choice(ALPHABETS)
-
-    expected = hash(random_id_string.encode())
-    actual = ID(random_id_string.encode()).__hash__()
-
-    assert actual == expected
-
-
 def test_id_to_base58():
     random_id_string = ""
     for _ in range(10):
-        random_id_string += random.SystemRandom().choice(ALPHABETS)
+        random_id_string += random.choice(ALPHABETS)
     expected = base58.b58encode(random_id_string).decode()
     actual = ID(random_id_string.encode()).to_base58()
 
@@ -98,7 +81,7 @@ def test_id_to_base58():
 def test_id_from_base58():
     random_id_string = ""
     for _ in range(10):
-        random_id_string += random.SystemRandom().choice(ALPHABETS)
+        random_id_string += random.choice(ALPHABETS)
     expected = ID(base58.b58decode(random_id_string))
     actual = ID.from_base58(random_id_string.encode())
 
@@ -106,12 +89,14 @@ def test_id_from_base58():
 
 
 def test_id_from_public_key():
-    bits_list = [1024, 1280, 1536, 1536, 2048]
-    key = RSA.generate(random.choice(bits_list))
-    key_bin = key.exportKey("DER")
+    key_pair = create_new_key_pair()
+    public_key = key_pair.public_key
+
+    key_bin = public_key.serialize_to_protobuf().SerializeToString()
     algo = multihash.Func.sha2_256
     mh_digest = multihash.digest(key_bin, algo)
     expected = ID(mh_digest.encode())
-    actual = ID.from_pubkey(key_bin)
+
+    actual = ID.from_pubkey(public_key)
 
     assert actual == expected

pip install -e .
parse_log_pytest
pytest
pytest pytest-mpl pytest-cov pytest-asyncio
apt-get update apt-get install -y libsamplerate0
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
base58==1.0.3
contourpy==1.3.0
coverage==7.8.0
cycler==0.12.1
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
fonttools==4.56.0
importlib_resources==6.5.2
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
Jinja2==3.1.6
kiwisolver==1.4.7
-e git+https://github.com/libp2p/py-libp2p.git@3736592a7acf9194b863f407f87efc1d89c66e95#egg=libp2p
lru-dict==1.3.0
MarkupSafe==3.0.2
matplotlib==3.9.4
multiaddr==0.0.9
netaddr==1.3.0
numpy==2.0.2
packaging @ file:///croot/packaging_1734472117206/work
pillow==11.1.0
pluggy @ file:///croot/pluggy_1733169602837/work
protobuf==3.9.0
pycryptodome==3.22.0
pymultihash==0.8.2
pyparsing==3.2.3
pytest @ file:///croot/pytest_1738938843180/work
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-mpl==0.17.0
python-dateutil==2.9.0.post0
rpcudp==3.0.0
six==1.17.0
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions==4.13.0
u-msgpack-python==2.8.0
varint==1.0.2
zipp==3.21.0

name: py-libp2p
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - base58==1.0.3
      - contourpy==1.3.0
      - coverage==7.8.0
      - cycler==0.12.1
      - fonttools==4.56.0
      - importlib-resources==6.5.2
      - jinja2==3.1.6
      - kiwisolver==1.4.7
      - libp2p==0.0.1
      - lru-dict==1.3.0
      - markupsafe==3.0.2
      - matplotlib==3.9.4
      - multiaddr==0.0.9
      - netaddr==1.3.0
      - numpy==2.0.2
      - pillow==11.1.0
      - protobuf==3.9.0
      - pycryptodome==3.22.0
      - pymultihash==0.8.2
      - pyparsing==3.2.3
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-mpl==0.17.0
      - python-dateutil==2.9.0.post0
      - rpcudp==3.0.0
      - six==1.17.0
      - typing-extensions==4.13.0
      - u-msgpack-python==2.8.0
      - varint==1.0.2
      - zipp==3.21.0
prefix: /opt/conda/envs/py-libp2p

tests/peer/test_interop.py::test_peer_id_interop tests/peer/test_peerid.py::test_eq_impl_for_bytes tests/peer/test_peerid.py::test_pretty tests/peer/test_peerid.py::test_str_less_than_10 tests/peer/test_peerid.py::test_str_more_than_10 tests/peer/test_peerid.py::test_eq_true tests/peer/test_peerid.py::test_eq_false tests/peer/test_peerid.py::test_id_to_base58 tests/peer/test_peerid.py::test_id_from_base58 tests/peer/test_peerid.py::test_id_from_public_key
libp2p/crypto/__init__.py Makefile libp2p/crypto/keys.py libp2p/crypto/pb/crypto_pb2.py setup.py libp2p/peer/id.py libp2p/crypto/secp256k1.py libp2p/crypto/pb/crypto.proto libp2p/crypto/rsa.py libp2p/__init__.py
libp2p/crypto/__init__.py Makefile libp2p/crypto/keys.py libp2p/crypto/pb/crypto_pb2.py setup.py libp2p/peer/id.py libp2p/crypto/secp256k1.py libp2p/crypto/pb/crypto.proto libp2p/crypto/rsa.py libp2p/__init__.py