2017-10-07 02:29:07 +00:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
from nkms.characters import Alice, Ursula, Character
|
2017-10-11 02:18:24 +00:00
|
|
|
from nkms.crypto import api
|
2017-10-11 05:39:25 +00:00
|
|
|
from nkms.crypto.constants import NOT_SIGNED
|
2017-10-13 02:11:07 +00:00
|
|
|
from nkms.crypto.constants import NO_DECRYPTION_PERFORMED
|
2018-02-13 23:45:02 +00:00
|
|
|
from nkms.crypto.powers import CryptoPower, SigningPower, NoSigningPower, \
|
|
|
|
NoEncryptingPower, \
|
2017-10-13 02:11:07 +00:00
|
|
|
EncryptingPower
|
|
|
|
|
2017-11-09 21:48:40 +00:00
|
|
|
"""
|
|
|
|
Chapter 1: SIGNING
|
|
|
|
"""
|
|
|
|
|
2017-10-07 02:29:07 +00:00
|
|
|
|
|
|
|
def test_actor_without_signing_power_cannot_sign():
|
|
|
|
"""
|
|
|
|
We can create a Character with no real CryptoPower to speak of.
|
|
|
|
This Character can't even sign a message.
|
|
|
|
"""
|
|
|
|
cannot_sign = CryptoPower(power_ups=[])
|
|
|
|
non_signer = Character(crypto_power=cannot_sign)
|
|
|
|
|
2017-10-07 02:46:51 +00:00
|
|
|
# The non-signer's seal doesn't work for signing...
|
2017-10-07 02:29:07 +00:00
|
|
|
with pytest.raises(NoSigningPower) as e_info:
|
|
|
|
non_signer.seal("something")
|
|
|
|
|
2017-10-14 22:07:10 +00:00
|
|
|
# ...or as a way to cast the public key to bytes or tuple.
|
2017-10-07 02:29:07 +00:00
|
|
|
with pytest.raises(NoSigningPower) as e_info:
|
2017-10-14 22:07:10 +00:00
|
|
|
bytes(non_signer.seal)
|
2017-10-07 02:46:51 +00:00
|
|
|
with pytest.raises(NoSigningPower) as e_info:
|
2017-10-14 22:07:10 +00:00
|
|
|
tuple(non_signer.seal)
|
2017-10-07 02:29:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_actor_with_signing_power_can_sign():
|
|
|
|
"""
|
|
|
|
However, simply giving that character a PowerUp bestows the power to sign.
|
|
|
|
|
|
|
|
Instead of having a Character verify the signature, we'll use the lower level API.
|
|
|
|
"""
|
|
|
|
message = b"Llamas."
|
|
|
|
|
2018-02-05 19:25:17 +00:00
|
|
|
signer = Character(crypto_power_ups=[SigningPower], is_me=True)
|
2017-10-07 02:29:07 +00:00
|
|
|
seal_of_the_signer = signer.seal
|
|
|
|
|
2018-02-05 19:25:17 +00:00
|
|
|
# We can use the signer's seal to sign a message (since the signer is_me)...
|
2017-10-07 02:29:07 +00:00
|
|
|
signature = seal_of_the_signer(message)
|
|
|
|
|
|
|
|
# ...or to get the signer's public key for verification purposes.
|
2018-02-13 23:45:02 +00:00
|
|
|
# (note: we use the private _der_encoded_bytes here to test directly against the API, instead of Character)
|
|
|
|
verification = api.ecdsa_verify(message, signature._der_encoded_bytes(),
|
|
|
|
seal_of_the_signer.as_umbral_pubkey())
|
2017-10-07 02:29:07 +00:00
|
|
|
|
2017-10-07 02:46:51 +00:00
|
|
|
assert verification is True
|
2017-10-07 02:29:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_anybody_can_verify():
|
|
|
|
"""
|
|
|
|
In the last example, we used the lower-level Crypto API to verify the signature.
|
|
|
|
|
|
|
|
Here, we show that anybody can do it without needing to directly access Crypto.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Alice can sign by default, by dint of her _default_crypto_powerups.
|
|
|
|
alice = Alice()
|
|
|
|
|
|
|
|
# So, our story is fairly simple: an everyman meets Alice.
|
|
|
|
somebody = Character()
|
|
|
|
somebody.learn_about_actor(alice)
|
|
|
|
|
|
|
|
# Alice signs a message.
|
|
|
|
message = b"A message for all my friends who can only verify and not sign."
|
|
|
|
signature = alice.seal(message)
|
|
|
|
|
|
|
|
# Our everyman can verify it.
|
2017-11-22 06:03:31 +00:00
|
|
|
verification, cleartext = somebody.verify_from(alice, message, signature, decrypt=False)
|
2017-10-07 02:46:51 +00:00
|
|
|
assert verification is True
|
2017-10-13 02:11:07 +00:00
|
|
|
assert cleartext is NO_DECRYPTION_PERFORMED
|
2017-10-07 02:29:07 +00:00
|
|
|
|
2017-10-12 20:36:49 +00:00
|
|
|
# If we pass the signature and message backwards, we get TypeError.
|
|
|
|
# with pytest.raises(TypeError):
|
|
|
|
# verification = somebody.verify_from(alice, message, signature)
|
|
|
|
|
2017-10-07 02:29:07 +00:00
|
|
|
|
2017-10-11 05:39:25 +00:00
|
|
|
"""
|
2017-11-09 21:48:40 +00:00
|
|
|
Chapter 2: ENCRYPTION
|
2017-10-11 05:39:25 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
2017-10-07 02:29:07 +00:00
|
|
|
def test_signing_only_power_cannot_encrypt():
|
2017-10-07 02:46:51 +00:00
|
|
|
"""
|
|
|
|
Similar to the above with signing, here we show that a Character without the EncryptingKeypair
|
|
|
|
PowerUp can't encrypt.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Here's somebody who can sign but not encrypt.
|
2017-10-17 21:03:36 +00:00
|
|
|
can_sign_but_not_encrypt = Character(crypto_power_ups=[SigningPower])
|
2017-10-07 02:46:51 +00:00
|
|
|
|
|
|
|
# ..and here's Ursula, for whom our Character above wants to encrypt.
|
2017-10-07 02:29:07 +00:00
|
|
|
ursula = Ursula()
|
|
|
|
|
2017-10-07 02:46:51 +00:00
|
|
|
# They meet.
|
|
|
|
can_sign_but_not_encrypt.learn_about_actor(ursula)
|
|
|
|
|
|
|
|
# The Character has the message ready...
|
2017-11-22 06:03:31 +00:00
|
|
|
cleartext = b"This is Officer Rod Farva. Come in, Ursula! Come in Ursula!"
|
2017-10-07 02:29:07 +00:00
|
|
|
|
2017-10-07 02:46:51 +00:00
|
|
|
# But without the proper PowerUp, no encryption happens.
|
2017-10-07 02:29:07 +00:00
|
|
|
with pytest.raises(NoEncryptingPower) as e_info:
|
2017-10-07 02:46:51 +00:00
|
|
|
can_sign_but_not_encrypt.encrypt_for(ursula, cleartext)
|
2017-10-11 05:39:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_character_with_encrypting_power_can_encrypt():
|
|
|
|
"""
|
|
|
|
Now, a Character *with* EncryptingKeyPair can encrypt.
|
|
|
|
"""
|
2017-10-17 21:03:36 +00:00
|
|
|
can_sign_and_encrypt = Character(crypto_power_ups=[SigningPower, EncryptingPower])
|
2017-10-11 05:39:25 +00:00
|
|
|
ursula = Ursula()
|
|
|
|
can_sign_and_encrypt.learn_about_actor(ursula)
|
|
|
|
|
|
|
|
cleartext = b"This is Officer Rod Farva. Come in, Ursula! Come in Ursula!"
|
|
|
|
|
|
|
|
# TODO: Make encrypt_for actually encrypt.
|
|
|
|
ciphertext, signature = can_sign_and_encrypt.encrypt_for(ursula, cleartext, sign=False)
|
|
|
|
assert signature == NOT_SIGNED
|
|
|
|
|
2017-11-22 04:22:16 +00:00
|
|
|
assert ciphertext is not None
|