mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2026-02-11 07:30:55 +00:00
Add abstraction for a cryptographic key
This commit is contained in:
1
Makefile
1
Makefile
@ -11,4 +11,5 @@ lintroll:
|
|||||||
flake8 $(FILES_TO_LINT)
|
flake8 $(FILES_TO_LINT)
|
||||||
|
|
||||||
protobufs:
|
protobufs:
|
||||||
|
cd libp2p/crypto/pb && protoc --python_out=. crypto.proto
|
||||||
cd libp2p/pubsub/pb && protoc --python_out=. rpc.proto
|
cd libp2p/pubsub/pb && protoc --python_out=. rpc.proto
|
||||||
|
|||||||
0
libp2p/crypto/__init__.py
Normal file
0
libp2p/crypto/__init__.py
Normal file
75
libp2p/crypto/keys.py
Normal file
75
libp2p/crypto/keys.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
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
|
||||||
20
libp2p/crypto/pb/crypto.proto
Normal file
20
libp2p/crypto/pb/crypto.proto
Normal file
@ -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;
|
||||||
|
}
|
||||||
162
libp2p/crypto/pb/crypto_pb2.py
Normal file
162
libp2p/crypto/pb/crypto_pb2.py
Normal file
@ -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)
|
||||||
54
libp2p/crypto/rsa.py
Normal file
54
libp2p/crypto/rsa.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
import Crypto.PublicKey.RSA as RSA
|
||||||
|
from Crypto.PublicKey.RSA import RsaKey
|
||||||
|
|
||||||
|
from libp2p.crypto.keys import 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
|
||||||
|
) -> Tuple[PrivateKey, PublicKey]:
|
||||||
|
"""
|
||||||
|
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 private_key, public_key
|
||||||
54
libp2p/crypto/secp256k1.py
Normal file
54
libp2p/crypto/secp256k1.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
import coincurve
|
||||||
|
|
||||||
|
from libp2p.crypto.keys import 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()
|
||||||
|
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) -> Tuple[PrivateKey, PublicKey]:
|
||||||
|
"""
|
||||||
|
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()
|
||||||
|
public_key = private_key.get_public_key()
|
||||||
|
return private_key, public_key
|
||||||
Reference in New Issue
Block a user