Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Policy and PolicyGroup. Some methods are faked with random data. #44

Merged
merged 6 commits into from
Sep 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions nkms/crypto/hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import sha3


# TODO: Replace these with actual hash functions.
def signature_hash(hash_input):
return sha3.keccak_256(hash_input).digest()


def content_hash(hash_input):
return sha3.keccak_256(hash_input).digest()
12 changes: 12 additions & 0 deletions nkms/crypto/pre/keygen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# TODO: Make this actually work.
def generate_re_encryption_keys(seckey_enc_alice,
pubkey_enc_bob,
m,
n):
kfrags = [
'sfasdfsd9',
'dfasd09fi',
'sdfksd3f9',
]

return kfrags
Empty file added nkms/policy/__init__.py
Empty file.
1 change: 1 addition & 0 deletions nkms/policy/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UNKNOWN_KFRAG = 550
116 changes: 116 additions & 0 deletions nkms/policy/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from nkms.crypto.hash import content_hash
from nkms.crypto.pre.keygen import generate_re_encryption_keys
from nkms.policy.constants import UNKNOWN_KFRAG


class PolicyManager(object):
pass


class PolicyManagerForAlice(PolicyManager):

def __init__(self, keychain_alice: "KeyChain"):
self.keychain_alice = keychain_alice

def create_policy_group(self,
pubkey_enc_bob: tuple,
uri: bytes,
m: int,
n: int
):
"""
Alice dictates a new group of policies.
"""
re_enc_keys = generate_re_encryption_keys(self.keychain_alice.enc_keypair.priv_key,
pubkey_enc_bob,
m,
n)
policies = []
for kfrag_id, key in enumerate(re_enc_keys):
policy = Policy.from_alice(
key, # Bob won't know this.
self.keychain_alice.sig_keypair.pub_key,
pubkey_enc_bob,
uri, # Ursula won't know this.
kfrag_id,
)
policies.append(policy)

return PolicyGroup(policies)


class PolicyGroup(object):
"""
The terms and conditions by which Alice shares with Bob.
"""

def __init__(self, policies=None):
self.policies = policies or []

def transmit(self, networky_stuff):
for policy in self.policies:
policy_offer = policy.craft_offer(networky_stuff)
result = networky_stuff.transmit_offer(policy.ursula, policy_offer)
if result.was_accepted:
policy.update_treasure_map(result)


class Policy(object):
"""
An individual agreement between Alice and Ursula. Together, all of the Policies by which
Ursula nodes which enter into an agreement regarding the same series of kFrags constitute
a PolicyGroup.

A Policy has a unique ID, which includes a fingerprint of Alice's public key so that
only she can set a policy with that ID. Ursula must verify this; otherwise a collision
attack is possible.
"""
ursula = None
hashed_part = None

def __init__(self, kfrag=UNKNOWN_KFRAG, challenge_size=20):
self.kfrag = kfrag
self.challenge_size = challenge_size
self.treasure_map = []

@staticmethod
def from_alice(kfrag,
pubkey_sig_alice,
pubkey_enc_bob,
uri,
kfrag_id
):
policy = Policy(kfrag)
policy.generate_challenge_pack()
policy.hash(pubkey_sig_alice=pubkey_sig_alice, hash_input=(pubkey_enc_bob, uri, kfrag_id))

return policy

def hash(self, pubkey_sig_alice, hash_input):
hash_input = str(hash_input).encode()
self.hashed_part = content_hash(hash_input)
hash_input_for_id = str(pubkey_sig_alice).encode() + str(self.hashed_part).encode()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of encoding this, you can hash it and, instead of returning the raw digest, you can call hexdigest to get the stringified version of it in hexidecimal.

Copy link
Contributor

@tuxxy tuxxy Sep 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

>>> sha3.keccak_256(b'this is a test').hexdigest()
'9fd09c38c2a5ae0a0bcd617872b735e37909ccc05c956460be7d3d03d881a0dc'

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, I agree that this is a bit of a shitshow. :-)

But I'm not clear on what you're suggesting here. Your sample shows a bytes object being passed to keccak_256(), but how to proceed for an arbitrary object? For example, in this case, hash_input will often be a tuple. And then, on line 84, how to properly do this concatenation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The keccak_256 function will always need a bytes object to hash it, so I recommend taking each element of hash_input, encoding it, then hash it to get a bytes object for that element. Do this for each element, then hash them all together and get a hexdigest version of it to avoid weird nuances in the string when converting them.

For example:

keccak(keccak(pubkey_enc_bob).digest() + keccak(uri).digest() + keccak(kfrag_id).digest).hexdigest()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows two things:

  1. You avoid the string conversion which can add weird elements to the hashed string.
  2. Allows the hashed results to be more replicatable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I would have the hashing functions accept a list of things to hash. Check if the type is of bytes. If it is not, encode it. Then update the hash via update(..).

I would also include a boolean param like hex_format which will cause the hash output to be hexdigest() instead of digest().

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw should the hash_input_for_id be necessarily string (hexdigest) not bytes (digest)?

self.id = content_hash(hash_input_for_id)
return self.id

def craft_offer(self, networky_stuff):
"""
Find an Ursula and craft an offer for her.
"""
self.ursula = networky_stuff.find_ursula(self.id, self.hashed_part)
return self.ursula.encrypt_for((self.kfrag, self.challenge_pack, self.treasure_map))

def generate_challenge_pack(self):
if self.kfrag == UNKNOWN_KFRAG:
raise TypeError(
"Can't generate a challenge pack unless we know the kfrag. Are you Alice?")

# TODO: make this work instead of being random.
import random
self.challenge_pack = [(random.getrandbits(32), random.getrandbits(32)) for x in
range(self.challenge_size)]
return True

def update_treasure_map(self, policy_offer_result):
# TODO: parse the result and add the node information to the treasure map.
self.treasure_map.append(policy_offer_result)
46 changes: 46 additions & 0 deletions tests/test_network_actors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from nkms.crypto.keyring import KeyRing
from nkms.policy.models import PolicyGroup, PolicyManagerForAlice


class MockUrsula(object):
def encrypt_for(self, payload):
# TODO: Make this a testable result
import random
return random.getrandbits(32)



class MockPolicyOfferResponse(object):
was_accepted = True


class MockNetworkyStuff(object):

def transmit_offer(self, ursula, policy_offer):
return MockPolicyOfferResponse()

def find_ursula(self, id, hashed_part):
return MockUrsula()





def test_alice_has_ursulas_public_key_and_uses_it_to_encode_policy_payload():
keychain_alice = KeyRing()
keychain_bob = KeyRing()
keychain_ursula = KeyRing()

# For example, a hashed path.
resource_id = b"as098duasdlkj213098asf"

# Alice runs this to get a policy object.
policy_manager = PolicyManagerForAlice(keychain_alice)
policy_group = policy_manager.create_policy_group(
keychain_bob.enc_keypair.pub_key,
resource_id,
m=20,
n=50
)
networky_stuff = MockNetworkyStuff()
policy_group.transmit(networky_stuff)
File renamed without changes.