Skip to content

Commit

Permalink
Merge pull request #127 from warner/checktypes
Browse files Browse the repository at this point in the history
add more type-checking to higher-level API calls. r=dstufft
  • Loading branch information
Brian Warner committed Mar 4, 2015
2 parents 022d9b1 + 3d5fe4c commit e24010c
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 55 deletions.
8 changes: 8 additions & 0 deletions src/nacl/public.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class PublicKey(encoding.Encodable, StringFixer, object):

def __init__(self, public_key, encoder=encoding.RawEncoder):
self._public_key = encoder.decode(public_key)
if not isinstance(self._public_key, bytes):
raise TypeError("PublicKey must be created from 32 bytes")

if len(self._public_key) != self.SIZE:
raise ValueError("The public key must be exactly %s bytes long" %
Expand Down Expand Up @@ -63,6 +65,8 @@ class PrivateKey(encoding.Encodable, StringFixer, object):
def __init__(self, private_key, encoder=encoding.RawEncoder):
# Decode the secret_key
private_key = encoder.decode(private_key)
if not isinstance(private_key, bytes):
raise TypeError("PrivateKey must be created from a 32 byte seed")

# Verify that our seed is the proper size
if len(private_key) != self.SIZE:
Expand Down Expand Up @@ -111,6 +115,10 @@ class Box(encoding.Encodable, StringFixer, object):

def __init__(self, private_key, public_key):
if private_key and public_key:
if ((not isinstance(private_key, PrivateKey) or
not isinstance(public_key, PublicKey))):
raise TypeError("Box must be created from "
"a PrivateKey and a PublicKey")
self._shared_key = nacl.bindings.crypto_box_beforenm(
public_key.encode(encoder=encoding.RawEncoder),
private_key.encode(encoder=encoding.RawEncoder),
Expand Down
2 changes: 2 additions & 0 deletions src/nacl/secret.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class SecretBox(encoding.Encodable, StringFixer, object):

def __init__(self, key, encoder=encoding.RawEncoder):
key = encoder.decode(key)
if not isinstance(key, bytes):
raise TypeError("SecretBox must be created from 32 bytes")

if len(key) != self.KEY_SIZE:
raise ValueError(
Expand Down
4 changes: 4 additions & 0 deletions src/nacl/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class VerifyKey(encoding.Encodable, StringFixer, object):
def __init__(self, key, encoder=encoding.RawEncoder):
# Decode the key
key = encoder.decode(key)
if not isinstance(key, bytes):
raise TypeError("VerifyKey must be created from 32 bytes")

if len(key) != nacl.bindings.crypto_sign_PUBLICKEYBYTES:
raise ValueError(
Expand Down Expand Up @@ -120,6 +122,8 @@ class SigningKey(encoding.Encodable, StringFixer, object):
def __init__(self, seed, encoder=encoding.RawEncoder):
# Decode the seed
seed = encoder.decode(seed)
if not isinstance(seed, bytes):
raise TypeError("SigningKey must be created from a 32 byte seed")

# Verify that our seed is the proper size
if len(seed) != nacl.bindings.crypto_sign_SEEDBYTES:
Expand Down
103 changes: 68 additions & 35 deletions tests/test_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@


VECTORS = [
# skalice, pkalice, skbob, pkbob, nonce, plaintext, ciphertext
# privalice, pubalice, privbob, pubbob, nonce, plaintext, ciphertext
(
b"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
b"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
Expand All @@ -49,57 +49,57 @@ def test_generate_private_key():


def test_box_creation():
pk = PublicKey(
pub = PublicKey(
b"ec2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798",
encoder=HexEncoder,
)
sk = PrivateKey(
priv = PrivateKey(
b"5c2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798",
encoder=HexEncoder,
)
Box(pk, sk)
Box(priv, pub)


def test_box_decode():
pk = PublicKey(
pub = PublicKey(
b"ec2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798",
encoder=HexEncoder,
)
sk = PrivateKey(
priv = PrivateKey(
b"5c2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798",
encoder=HexEncoder,
)
b1 = Box(pk, sk)
b1 = Box(priv, pub)
b2 = Box.decode(b1._shared_key)
assert b1._shared_key == b2._shared_key


def test_box_bytes():
pk = PublicKey(
pub = PublicKey(
b"ec2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798",
encoder=HexEncoder,
)
sk = PrivateKey(
priv = PrivateKey(
b"5c2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798",
encoder=HexEncoder,
)
b = Box(pk, sk)
b = Box(priv, pub)
assert bytes(b) == b._shared_key


@pytest.mark.parametrize(
(
"skalice", "pkalice", "skbob", "pkbob", "nonce", "plaintext",
"privalice", "pubalice", "privbob", "pubbob", "nonce", "plaintext",
"ciphertext",
),
VECTORS,
)
def test_box_encryption(
skalice, pkalice, skbob, pkbob, nonce, plaintext, ciphertext):
pkalice = PublicKey(pkalice, encoder=HexEncoder)
skbob = PrivateKey(skbob, encoder=HexEncoder)
privalice, pubalice, privbob, pubbob, nonce, plaintext, ciphertext):
pubalice = PublicKey(pubalice, encoder=HexEncoder)
privbob = PrivateKey(privbob, encoder=HexEncoder)

box = Box(skbob, pkalice)
box = Box(privbob, pubalice)
encrypted = box.encrypt(
binascii.unhexlify(plaintext),
binascii.unhexlify(nonce),
Expand All @@ -117,17 +117,17 @@ def test_box_encryption(

@pytest.mark.parametrize(
(
"skalice", "pkalice", "skbob", "pkbob", "nonce", "plaintext",
"privalice", "pubalice", "privbob", "pubbob", "nonce", "plaintext",
"ciphertext",
),
VECTORS,
)
def test_box_decryption(
skalice, pkalice, skbob, pkbob, nonce, plaintext, ciphertext):
pkbob = PublicKey(pkbob, encoder=HexEncoder)
skalice = PrivateKey(skalice, encoder=HexEncoder)
privalice, pubalice, privbob, pubbob, nonce, plaintext, ciphertext):
pubbob = PublicKey(pubbob, encoder=HexEncoder)
privalice = PrivateKey(privalice, encoder=HexEncoder)

box = Box(skalice, pkbob)
box = Box(privalice, pubbob)

nonce = binascii.unhexlify(nonce)
decrypted = binascii.hexlify(
Expand All @@ -139,17 +139,17 @@ def test_box_decryption(

@pytest.mark.parametrize(
(
"skalice", "pkalice", "skbob", "pkbob", "nonce", "plaintext",
"privalice", "pubalice", "privbob", "pubbob", "nonce", "plaintext",
"ciphertext",
),
VECTORS,
)
def test_box_decryption_combined(
skalice, pkalice, skbob, pkbob, nonce, plaintext, ciphertext):
pkbob = PublicKey(pkbob, encoder=HexEncoder)
skalice = PrivateKey(skalice, encoder=HexEncoder)
privalice, pubalice, privbob, pubbob, nonce, plaintext, ciphertext):
pubbob = PublicKey(pubbob, encoder=HexEncoder)
privalice = PrivateKey(privalice, encoder=HexEncoder)

box = Box(skalice, pkbob)
box = Box(privalice, pubbob)

combined = binascii.hexlify(
binascii.unhexlify(nonce) + binascii.unhexlify(ciphertext),
Expand All @@ -161,19 +161,19 @@ def test_box_decryption_combined(

@pytest.mark.parametrize(
(
"skalice", "pkalice", "skbob", "pkbob", "nonce", "plaintext",
"privalice", "pubalice", "privbob", "pubbob", "nonce", "plaintext",
"ciphertext",
),
VECTORS,
)
def test_box_failed_decryption(
skalice, pkalice, skbob, pkbob, nonce, plaintext, ciphertext):
pkbob = PublicKey(pkbob, encoder=HexEncoder)
skbob = PrivateKey(skbob, encoder=HexEncoder)
privalice, pubalice, privbob, pubbob, nonce, plaintext, ciphertext):
pubbob = PublicKey(pubbob, encoder=HexEncoder)
privbob = PrivateKey(privbob, encoder=HexEncoder)

# this cannot decrypt the ciphertext!
# the ciphertext must be decrypted by (pkbob, skalice) or (pkalice, skbob)
box = Box(pkbob, skbob)
# this cannot decrypt the ciphertext! the ciphertext must be decrypted by
# (privalice, pubbob) or (privbob, pubalice)
box = Box(privbob, pubbob)

with pytest.raises(CryptoError):
box.decrypt(ciphertext, binascii.unhexlify(nonce), encoder=HexEncoder)
Expand All @@ -185,16 +185,49 @@ def test_box_wrong_length():
with pytest.raises(ValueError):
PrivateKey(b"")

pk = PublicKey(
pub = PublicKey(
b"ec2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798",
encoder=HexEncoder,
)
sk = PrivateKey(
priv = PrivateKey(
b"5c2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798",
encoder=HexEncoder,
)
b = Box(pk, sk)
b = Box(priv, pub)
with pytest.raises(ValueError):
b.encrypt(b"", b"")
with pytest.raises(ValueError):
b.decrypt(b"", b"")


def check_type_error(expected, f, *args):
with pytest.raises(TypeError) as e:
f(*args)
assert expected in str(e)


def test_wrong_types():
priv = PrivateKey.generate()

check_type_error("PrivateKey must be created from a 32 byte seed",
PrivateKey, 12)
check_type_error("PrivateKey must be created from a 32 byte seed",
PrivateKey, priv)
check_type_error("PrivateKey must be created from a 32 byte seed",
PrivateKey, priv.public_key)

check_type_error("PublicKey must be created from 32 bytes",
PublicKey, 13)
check_type_error("PublicKey must be created from 32 bytes",
PublicKey, priv)
check_type_error("PublicKey must be created from 32 bytes",
PublicKey, priv.public_key)

check_type_error("Box must be created from a PrivateKey and a PublicKey",
Box, priv, "not a public key")
check_type_error("Box must be created from a PrivateKey and a PublicKey",
Box, priv.encode(), priv.public_key.encode())
check_type_error("Box must be created from a PrivateKey and a PublicKey",
Box, priv, priv.public_key.encode())
check_type_error("Box must be created from a PrivateKey and a PublicKey",
Box, priv.encode(), priv.public_key)
15 changes: 15 additions & 0 deletions tests/test_secret.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,18 @@ def test_secret_box_wrong_lengths():
box.encrypt(b"", b"")
with pytest.raises(ValueError):
box.decrypt(b"", b"")


def check_type_error(expected, f, *args):
with pytest.raises(TypeError) as e:
f(*args)
assert expected in str(e)


def test_wrong_types():
box = SecretBox(b"11" * 32, encoder=HexEncoder)

check_type_error("SecretBox must be created from 32 bytes",
SecretBox, 12)
check_type_error("SecretBox must be created from 32 bytes",
SecretBox, box)

0 comments on commit e24010c

Please sign in to comment.