mirror of https://github.com/nucypher/nucypher.git
Merge branch 'master' of github.com:nucypher/nucypher-kms into header-object
commit
76c4047803
|
@ -1,20 +1,13 @@
|
|||
import importlib
|
||||
from nacl.utils import random # noqa
|
||||
from npre.curves import secp256k1
|
||||
|
||||
# 'Random' parameter g here is derived from Bitcoin's hashMerkleRoot of
|
||||
# its genesis block:
|
||||
# bbs98.ec.serialize(
|
||||
# bbs98.ec.hashEC(
|
||||
# pre.ecgroup,
|
||||
# base64.decodebytes(b'4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b')
|
||||
# bbs98.ec.G)) = b'1:A78WgHh03I38RcZO/FQe9SbmPVzQg+oehzR8QsGXOeqz'
|
||||
default_algorithm = dict(
|
||||
symmetric=dict(
|
||||
cipher='nacl'),
|
||||
pre=dict(
|
||||
cipher='bbs98', # BBS98 is only temporary here, for development
|
||||
curve=714, # secp256k1 in OpenSSL
|
||||
g=b'1:A78WgHh03I38RcZO/FQe9SbmPVzQg+oehzR8QsGXOeqz',
|
||||
curve=secp256k1,
|
||||
m=None, n=None))
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
import msgpack
|
||||
from random import SystemRandom
|
||||
from py_ecc.secp256k1 import N, privtopub, ecdsa_raw_sign, ecdsa_raw_recover
|
||||
from nkms.crypto import default_algorithm, pre_from_algorithm
|
||||
|
||||
|
||||
class EncryptingKeypair(object):
|
||||
def __init__(self, privkey_bytes=None):
|
||||
self.pre = pre_from_algorithm(default_algorithm)
|
||||
|
||||
if not privkey_bytes:
|
||||
self.priv_key = self.pre.gen_priv(dtype='bytes')
|
||||
else:
|
||||
self.priv_key = privkey_bytes
|
||||
self.pub_key = self.pre.priv2pub(self.priv_key)
|
||||
|
||||
def encrypt(self, data, pubkey=None):
|
||||
"""
|
||||
Encrypts the data provided.
|
||||
|
||||
:param bytes data: The data to encrypt
|
||||
:param bytes pubkey: Pubkey to encrypt for
|
||||
|
||||
:rtype: bytes
|
||||
:return: Encrypted ciphertext
|
||||
"""
|
||||
if not pubkey:
|
||||
pubkey = self.pub_key
|
||||
return self.pre.encrypt(pubkey, data)
|
||||
|
||||
def decrypt(self, enc_data):
|
||||
"""
|
||||
Decrypts the data provided
|
||||
|
||||
:param bytes enc_data: Decrypts the data provided
|
||||
|
||||
:rtype: bytes
|
||||
:return: Decrypted plaintext
|
||||
"""
|
||||
return self.pre.decrypt(self.priv_key, enc_data)
|
||||
|
||||
|
||||
class SigningKeypair(object):
|
||||
def __init__(self, privkey_bytes=None):
|
||||
self.secure_rand = SystemRandom()
|
||||
if privkey_bytes:
|
||||
self.priv_key = privkey_bytes
|
||||
else:
|
||||
# Key generation is random([1, N - 1])
|
||||
priv_number = self.secure_rand.randrange(1, N)
|
||||
self.priv_key = priv_number.to_bytes(32, byteorder='big')
|
||||
# Get the public component
|
||||
self.pub_key = privtopub(self.priv_key)
|
||||
|
||||
def _vrs_msgpack_dump(self, v, r, s):
|
||||
v_bytes = v.to_bytes(1, byteorder='big')
|
||||
r_bytes = r.to_bytes(32, byteorder='big')
|
||||
s_bytes = s.to_bytes(32, byteorder='big')
|
||||
return msgpack.dumps((v_bytes, r_bytes, s_bytes))
|
||||
|
||||
def _vrs_msgpack_load(self, msgpack_vrs):
|
||||
sig = msgpack.loads(msgpack_vrs)
|
||||
v = int.from_bytes(sig[0], byteorder='big')
|
||||
r = int.from_bytes(sig[1], byteorder='big')
|
||||
s = int.from_bytes(sig[2], byteorder='big')
|
||||
return (v, r, s)
|
||||
|
||||
def sign(self, msghash):
|
||||
"""
|
||||
Signs a hashed message and returns a msgpack'ed v, r, and s.
|
||||
|
||||
:param bytes msghash: Hash of the message
|
||||
|
||||
:rtype: Bytestring
|
||||
:return: Msgpacked bytestring of v, r, and s (the signature)
|
||||
"""
|
||||
v, r, s = ecdsa_raw_sign(msghash, self.priv_key)
|
||||
return self._vrs_msgpack_dump(v, r, s)
|
||||
|
||||
def verify(self, msghash, signature, pubkey=None):
|
||||
"""
|
||||
Takes a msgpacked signature and verifies the message.
|
||||
|
||||
:param bytes msghash: The hashed message to verify
|
||||
:param bytes signature: The msgpacked signature (v, r, and s)
|
||||
:param bytes pubkey: Pubkey to validate signature for
|
||||
Default is the keypair's pub_key.
|
||||
|
||||
:rtype: Boolean
|
||||
:return: Is the signature valid or not?
|
||||
"""
|
||||
if not pubkey:
|
||||
pubkey = self.pub_key
|
||||
sig = self._vrs_msgpack_load(signature)
|
||||
# Generate the public key from the signature and validate
|
||||
# TODO: Look into fixed processing time functions for comparison
|
||||
verify_sig = ecdsa_raw_recover(msghash, sig)
|
||||
return verify_sig == pubkey
|
|
@ -0,0 +1,70 @@
|
|||
import sha3
|
||||
from nkms.crypto.keypairs import SigningKeypair, EncryptingKeypair
|
||||
|
||||
|
||||
class KeyRing(object):
|
||||
def __init__(self, sig_privkey=None, enc_privkey=None):
|
||||
"""
|
||||
Initializes a KeyRing object. Uses the private keys to initialize
|
||||
their respective objects, if provided. If not, it will generate new
|
||||
keypairs.
|
||||
|
||||
:param bytes sig_privkey: Private key in bytes of ECDSA signing keypair
|
||||
:param bytes enc_privkey: Private key in bytes of encrypting keypair
|
||||
"""
|
||||
self.sig_keypair = SigningKeypair(sig_privkey)
|
||||
self.enc_keypair = EncryptingKeypair(enc_privkey)
|
||||
|
||||
def sign(self, message):
|
||||
"""
|
||||
Signs a message and returns a signature with the keccak hash.
|
||||
|
||||
:param bytes message: Message to sign in bytes
|
||||
|
||||
:rtype: bytestring
|
||||
:return: Signature of message
|
||||
"""
|
||||
msg_digest = sha3.keccak_256(message).digest()
|
||||
return self.sig_keypair.sign(msg_digest)
|
||||
|
||||
def verify(self, message, signature, pubkey=None):
|
||||
"""
|
||||
Verifies a signature.
|
||||
|
||||
:param bytes message: Message to check signature for
|
||||
:param bytes signature: Signature to validate
|
||||
:param bytes pubkey: Pubkey to validate signature with
|
||||
Default is the sig_keypair's pub_key
|
||||
|
||||
:rtype: Boolean
|
||||
:return: Is the message signature valid or not?
|
||||
"""
|
||||
if not pubkey:
|
||||
pubkey = self.sig_keypair.pub_key
|
||||
msg_digest = sha3.keccak_256(message).digest()
|
||||
return self.sig_keypair.verify(msg_digest, signature, pubkey=pubkey)
|
||||
|
||||
def encrypt(self, plaintext, pubkey=None):
|
||||
"""
|
||||
Encrypts the plaintext provided.
|
||||
|
||||
:param bytes plaintext: Plaintext to encrypt w/ EncryptingKeypair
|
||||
:param bytes pubkey: Pubkey to encrypt for
|
||||
|
||||
:rtype: bytes
|
||||
:return: Ciphertext of plaintext
|
||||
"""
|
||||
if not pubkey:
|
||||
pubkey = self.enc_keypair.pub_key
|
||||
return self.enc_keypair.encrypt(plaintext, pubkey=pubkey)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
"""
|
||||
Decrypts the ciphertext provided.
|
||||
|
||||
:param bytes ciphertext: Ciphertext to decrypt w/ EncryptingKeypair
|
||||
|
||||
:rtype: bytes
|
||||
:return: Plaintext of Encrypted ciphertext
|
||||
"""
|
||||
return self.enc_keypair.decrypt(ciphertext)
|
|
@ -1,11 +1,10 @@
|
|||
import base64
|
||||
import msgpack
|
||||
from nkms import crypto
|
||||
from npre.bbs98 import PRE as BasePRE
|
||||
|
||||
|
||||
def convert_priv(sk):
|
||||
return b'0:' + base64.encodebytes(sk).strip()
|
||||
return b'\x00' + sk
|
||||
|
||||
|
||||
class PRE(BasePRE):
|
||||
|
|
4
setup.py
4
setup.py
|
@ -21,9 +21,9 @@ TESTS_REQUIRE = [
|
|||
|
||||
# should add --process-dependency-links to pip
|
||||
LINKS = [
|
||||
'https://github.com/bmuller/kademlia/archive/kms-dependency.tar.gz#egg=kademlia-1.0',
|
||||
'https://github.com/nucypher/kademlia/archive/kms-dependency.tar.gz#egg=kademlia-1.0',
|
||||
'https://github.com/bmuller/rpcudp/archive/python3.5.tar.gz#egg=rpcudp-3.0.0',
|
||||
'https://github.com/nucypher/nucypher-pre-python/archive/master.tar.gz#egg=npre-0.1']
|
||||
'https://github.com/nucypher/nucypher-pre-python/archive/0.3.tar.gz#egg=npre-0.3']
|
||||
|
||||
setup(name='nkms',
|
||||
version='0.1',
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import unittest
|
||||
import sha3
|
||||
import msgpack
|
||||
from nkms.crypto.keypairs import SigningKeypair, EncryptingKeypair
|
||||
|
||||
|
||||
class TestSigningKeypair(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.keypair_a = SigningKeypair()
|
||||
self.keypair_b = SigningKeypair()
|
||||
self.msg = b'this is a test'
|
||||
|
||||
def test_signing(self):
|
||||
msg_digest = sha3.keccak_256(self.msg).digest()
|
||||
signature = self.keypair_a.sign(msg_digest)
|
||||
|
||||
sig = msgpack.loads(signature)
|
||||
self.assertTrue(1, len(sig[0])) # Check v
|
||||
self.assertTrue(32, len(sig[1])) # Check r
|
||||
self.assertTrue(32, len(sig[2])) # Check s
|
||||
|
||||
def test_verification(self):
|
||||
msg_digest = sha3.keccak_256(self.msg).digest()
|
||||
signature = self.keypair_a.sign(msg_digest)
|
||||
|
||||
sig = msgpack.loads(signature)
|
||||
self.assertTrue(1, len(sig[0])) # Check v
|
||||
self.assertTrue(32, len(sig[1])) # Check r
|
||||
self.assertTrue(32, len(sig[2])) # Check s
|
||||
|
||||
verify_sig = self.keypair_b.verify(msg_digest, signature,
|
||||
pubkey=self.keypair_a.pub_key)
|
||||
self.assertTrue(verify_sig)
|
||||
|
||||
|
||||
class TestEncryptingKeypair(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.send_keypair = EncryptingKeypair()
|
||||
self.recv_keypair = EncryptingKeypair()
|
||||
self.msg = b'this is a test'
|
||||
|
||||
def test_encryption(self):
|
||||
ciphertext = self.send_keypair.encrypt(self.msg,
|
||||
pubkey=self.recv_keypair.pub_key)
|
||||
self.assertNotEqual(self.msg, ciphertext)
|
||||
|
||||
def test_decryption(self):
|
||||
ciphertext = self.send_keypair.encrypt(self.msg,
|
||||
pubkey=self.recv_keypair.pub_key)
|
||||
self.assertNotEqual(self.msg, ciphertext)
|
||||
|
||||
plaintext = self.recv_keypair.decrypt(ciphertext)
|
||||
self.assertEqual(self.msg, plaintext)
|
|
@ -0,0 +1,44 @@
|
|||
import unittest
|
||||
import msgpack
|
||||
from nkms.crypto.keyring import KeyRing
|
||||
|
||||
|
||||
class TestKeyRing(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.keyring_a = KeyRing()
|
||||
self.keyring_b = KeyRing()
|
||||
|
||||
self.msg = b'this is a test'
|
||||
|
||||
def test_signing(self):
|
||||
signature = self.keyring_a.sign(self.msg)
|
||||
|
||||
sig = msgpack.loads(signature)
|
||||
self.assertTrue(1, len(sig[0])) # Check v
|
||||
self.assertTrue(32, len(sig[1])) # Check r
|
||||
self.assertTrue(32, len(sig[2])) # Check s
|
||||
|
||||
def test_verification(self):
|
||||
signature = self.keyring_a.sign(self.msg)
|
||||
|
||||
sig = msgpack.loads(signature)
|
||||
self.assertTrue(1, len(sig[0])) # Check v
|
||||
self.assertTrue(32, len(sig[1])) # Check r
|
||||
self.assertTrue(32, len(sig[2])) # Check s
|
||||
|
||||
is_valid = self.keyring_b.verify(self.msg, signature,
|
||||
pubkey=self.keyring_a.sig_keypair.pub_key)
|
||||
self.assertTrue(is_valid)
|
||||
|
||||
def test_encryption(self):
|
||||
ciphertext = self.keyring_a.encrypt(self.msg,
|
||||
pubkey=self.keyring_b.enc_keypair.pub_key)
|
||||
self.assertNotEqual(self.msg, ciphertext)
|
||||
|
||||
def test_decryption(self):
|
||||
ciphertext = self.keyring_a.encrypt(self.msg,
|
||||
pubkey=self.keyring_b.enc_keypair.pub_key)
|
||||
self.assertNotEqual(self.msg, ciphertext)
|
||||
|
||||
plaintext = self.keyring_b.decrypt(ciphertext)
|
||||
self.assertEqual(self.msg, plaintext)
|
|
@ -24,6 +24,11 @@ def test_pre():
|
|||
pk_alice = pre.priv2pub(sk_alice)
|
||||
pk_bob = pre.priv2pub(sk_bob)
|
||||
|
||||
assert len(pk_alice) == 34
|
||||
assert len(pk_bob) == 34
|
||||
assert pk_alice[0] == 1
|
||||
assert pk_bob[0] == 1
|
||||
|
||||
cleartext = b'Hello world'
|
||||
|
||||
cyphertext_for_alice = pre.encrypt(pk_alice, cleartext)
|
||||
|
|
Loading…
Reference in New Issue