Merge branch 'master' of github.com:nucypher/nucypher-kms into header-object

pull/52/head
tuxxy 2017-09-20 08:32:23 -07:00
commit 76c4047803
9 changed files with 276 additions and 13 deletions

View File

@ -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))

98
nkms/crypto/keypairs.py Normal file
View File

@ -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

70
nkms/crypto/keyring.py Normal file
View File

@ -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)

View File

@ -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):

View File

@ -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',

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -5,6 +5,7 @@ envlist = py35,py36
deps = .[testing]
install_command = pip install --process-dependency-links {opts} {packages}
commands = py.test {posargs}
usedevelop = True
[pytest]
testpaths = tests