Add abstraction for a cryptographic key
This commit is contained in:
parent
2c68814bae
commit
61f78c8feb
1
Makefile
1
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
|
||||
|
|
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
|
Loading…
Reference in New Issue
Block a user