diff --git a/nacl/__init__.py b/nacl/__init__.py index 4107460e32739e454d63bfdd56f2269cf8e7bf66..2dcf301c7ebbff9aeb40769984d5ffb1c74e0722 100644 --- a/nacl/__init__.py +++ b/nacl/__init__.py @@ -4,11 +4,10 @@ from __future__ import division from . import __about__ from . import hash # pylint: disable=W0622 from . import signing -from .encoding import encoder from .random import random -__all__ = ["encoder", "hash", "random"] + __about__.__all__ +__all__ = ["hash", "random"] + __about__.__all__ # - Meta Information - diff --git a/nacl/encoding.py b/nacl/encoding.py index 48c96ea187466e0d8c0c80b89d6e81c7062802f6..e885c9b7e62cd6b60c106058d4375fdc408e3737 100644 --- a/nacl/encoding.py +++ b/nacl/encoding.py @@ -1,85 +1,63 @@ import base64 import binascii -from . import six - -class Encoder(object): - - def __init__(self): - self._registry = {} - - def __getitem__(self, name): - if isinstance(name, six.string_types): - return self._registry[name] - return name - - def register(self, name, cls=None): - if cls is None: - def inner(cls): - self._registry[name] = cls() - return cls - return inner - else: - self._registry[name] = cls() - - -# Global encoder -encoder = Encoder() - - -class Encodable(object): - - def encode(self, encoding="raw"): - data = bytes(self) - return encoder[encoding].encode(data) - - -@encoder.register("raw") class RawEncoder(object): - def encode(self, data): + @staticmethod + def encode(data): return data - def decode(self, data): + @staticmethod + def decode(data): return data -@encoder.register("hex") class HexEncoder(object): - def encode(self, data): + @staticmethod + def encode(data): return binascii.hexlify(data) - def decode(self, data): + @staticmethod + def decode(data): return binascii.unhexlify(data) -@encoder.register("base16") class Base16Encoder(object): - def encode(self, data): + @staticmethod + def encode(data): return base64.b16encode(data) - def decode(self, data): + @staticmethod + def decode(data): return base64.b16decode(data) -@encoder.register("base32") class Base32Encoder(object): - def encode(self, data): + @staticmethod + def encode(data): return base64.b32encode(data) - def decode(self, data): + @staticmethod + def decode(data): return base64.b32decode(data) -@encoder.register("base64") class Base64Encoder(object): - def encode(self, data): + @staticmethod + def encode(data): return base64.b64encode(data) - def decode(self, data): + @staticmethod + def decode(data): return base64.b64decode(data) + + +class Encodable(object): + + def encode(self, encoder=RawEncoder): + return encoder.encode(bytes(self)) diff --git a/nacl/hash.py b/nacl/hash.py index b8e80a568c04fcb02a496cd18e30d1cff7a3f49d..a82c3ec1e7b48471b838c92902fff9a16b0a3f9e 100644 --- a/nacl/hash.py +++ b/nacl/hash.py @@ -1,24 +1,23 @@ from __future__ import absolute_import from __future__ import division -from . import nacl -from .encoding import encoder +from . import nacl, encoding from .exceptions import CryptoError -def sha256(message, encoding="hex"): +def sha256(message, encoder=encoding.HexEncoder): digest = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_hash_sha256_BYTES) if not nacl.lib.crypto_hash_sha256(digest, message, len(message)): raise CryptoError("Hashing failed") digest = nacl.ffi.buffer(digest, nacl.lib.crypto_hash_sha256_BYTES)[:] - return encoder[encoding].encode(digest) + return encoder.encode(digest) -def sha512(message, encoding="hex"): +def sha512(message, encoder=encoding.HexEncoder): digest = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_hash_sha512_BYTES) if not nacl.lib.crypto_hash_sha512(digest, message, len(message)): raise CryptoError("Hashing failed") digest = nacl.ffi.buffer(digest, nacl.lib.crypto_hash_sha512_BYTES)[:] - return encoder[encoding].encode(digest) + return encoder.encode(digest) diff --git a/nacl/signing.py b/nacl/signing.py index 1711f6acab9b07b53cfb43074248ac4328350e53..bc196190c6659b07c04c39813f24a1b44ff5ca40 100644 --- a/nacl/signing.py +++ b/nacl/signing.py @@ -3,8 +3,7 @@ from __future__ import division from . import six -from . import nacl -from .encoding import encoder, Encodable +from . import nacl, encoding from .exceptions import CryptoError from .random import random @@ -42,17 +41,18 @@ class SignedMessage(six.binary_type): return self._message -class VerifyKey(Encodable, six.StringFixer, object): +class VerifyKey(encoding.Encodable, six.StringFixer, object): """ The public key counterpart to an Ed25519 SigningKey for producing digital signatures. :param key: [:class:`bytes`] Serialized Ed25519 public key + :param encoding: [:class:`str`] The encoding that the key is encoded with """ - def __init__(self, key, encoding="raw"): + def __init__(self, key, encoder=encoding.RawEncoder): # Decode the key - key = encoder[encoding].decode(key) + key = encoder.decode(key) if len(key) != nacl.lib.crypto_sign_PUBLICKEYBYTES: raise ValueError("The key must be exactly %s bytes long" % @@ -63,7 +63,7 @@ class VerifyKey(Encodable, six.StringFixer, object): def __bytes__(self): return self._key - def verify(self, smessage, signature=None, encoding="raw"): + def verify(self, smessage, signature=None, encoder=encoding.RawEncoder): """ Verifies the signature of a signed message, returning the message if it has not been tampered with else raising @@ -73,6 +73,8 @@ class VerifyKey(Encodable, six.StringFixer, object): signature and message concated together. :param signature: [:class:`bytes`] If an unsigned message is given for smessage then the detached signature must be provded. + :param encoding: [:class:`str`] The encoding that the secret message + and signature is encoded with. :rtype: :class:`bytes` """ if signature is not None: @@ -81,7 +83,7 @@ class VerifyKey(Encodable, six.StringFixer, object): smessage = signature + smessage # Decode the signed message - smessage = encoder[encoding].decode(smessage) + smessage = encoder.decode(smessage) message = nacl.ffi.new("unsigned char[]", len(smessage)) message_len = nacl.ffi.new("unsigned long long *") @@ -92,7 +94,7 @@ class VerifyKey(Encodable, six.StringFixer, object): return nacl.ffi.buffer(message, message_len[0])[:] -class SigningKey(Encodable, six.StringFixer, object): +class SigningKey(encoding.Encodable, six.StringFixer, object): """ Private key for producing digital signatures using the Ed25519 algorithm. @@ -105,14 +107,15 @@ class SigningKey(Encodable, six.StringFixer, object): masquerade as you. :param seed: [:class:`bytes`] Random 32-byte value (i.e. private key) + :param encoding: [:class:`str`] The encoding that the seed is encoded with :ivar: verify_key: [:class:`~nacl.signing.VerifyKey`] The verify (i.e. public) key that corresponds with this signing key. """ - def __init__(self, seed, encoding="raw"): + def __init__(self, seed, encoder=encoding.RawEncoder): # Decode the seed - seed = encoder[encoding].decode(seed) + seed = encoder.decode(seed) # Verify that our seed is the proper size seed_size = nacl.lib.crypto_sign_SECRETKEYBYTES // 2 @@ -144,14 +147,16 @@ class SigningKey(Encodable, six.StringFixer, object): :rtype: :class:`~nacl.signing.SigningKey` """ return cls(random(nacl.lib.crypto_sign_SECRETKEYBYTES // 2), - encoding="raw", + encoder=encoding.RawEncoder, ) - def sign(self, message, encoding="raw"): + def sign(self, message, encoder=encoding.RawEncoder): """ Sign a message using this key. :param message: [:class:`bytes`] The data to be signed. + :param encoding: [:class:`str`] The encoding to encode the signed + message with. :rtype: :class:`~nacl.signing.SignedMessage` """ sm = nacl.ffi.new("unsigned char[]", len(message) + nacl.lib.crypto_sign_BYTES) @@ -162,8 +167,8 @@ class SigningKey(Encodable, six.StringFixer, object): raw_signed = nacl.ffi.buffer(sm, smlen[0])[:] - signature = encoder[encoding].encode(raw_signed[:nacl.lib.crypto_sign_BYTES]) - message = encoder[encoding].encode(raw_signed[nacl.lib.crypto_sign_BYTES:]) - signed = encoder[encoding].encode(raw_signed) + signature = encoder.encode(raw_signed[:nacl.lib.crypto_sign_BYTES]) + message = encoder.encode(raw_signed[nacl.lib.crypto_sign_BYTES:]) + signed = encoder.encode(raw_signed) return SignedMessage._from_parts(signature, message, signed) diff --git a/tests/test_hash.py b/tests/test_hash.py index bc4ecea87ccad73f38f91c28e670453d9000114f..b24b25bd31392705e06035dc842d7f2468ed7392 100644 --- a/tests/test_hash.py +++ b/tests/test_hash.py @@ -1,6 +1,8 @@ -import nacl import pytest +import nacl +import nacl.encoding + @pytest.mark.parametrize(("inp", "expected"), [ ( @@ -27,7 +29,7 @@ def test_sha256_hex(inp, expected): ) ]) def test_sha256_binary(inp, expected): - assert nacl.hash.sha256(inp, encoding="raw") == expected + assert nacl.hash.sha256(inp, encoder=nacl.encoding.RawEncoder) == expected @pytest.mark.parametrize(("inp", "expected"), [ @@ -55,4 +57,4 @@ def test_sha512_hex(inp, expected): ) ]) def test_sha512_binary(inp, expected): - assert nacl.hash.sha512(inp, encoding="raw") == expected + assert nacl.hash.sha512(inp, encoder=nacl.encoding.RawEncoder) == expected diff --git a/tests/test_signing.py b/tests/test_signing.py index 3607c3fd166754c660f6a02f0a755e830c902656..d22e2944320d1eadc84a1d51c8b6d37fcfcaf8f7 100644 --- a/tests/test_signing.py +++ b/tests/test_signing.py @@ -7,6 +7,7 @@ import os import pytest import nacl +import nacl.encoding import nacl.nacl @@ -38,15 +39,15 @@ class TestSigningKey: b"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", ]) def test_initialization_with_seed(self, seed): - nacl.signing.SigningKey(binascii.unhexlify(seed)) + nacl.signing.SigningKey(seed, encoder=nacl.encoding.HexEncoder) @pytest.mark.parametrize(("seed", "message", "signature", "expected"), [(x["seed"], x["message"], x["signature"], x["signed"]) for x in ed25519_known_answers()] ) def test_message_signing(self, seed, message, signature, expected): - signing_key = nacl.signing.SigningKey(seed, encoding="hex") - signed = signing_key.sign(binascii.unhexlify(message), encoding="hex") + signing_key = nacl.signing.SigningKey(seed, encoder=nacl.encoding.HexEncoder) + signed = signing_key.sign(binascii.unhexlify(message), encoder=nacl.encoding.HexEncoder) assert signed == expected assert signed.message == message @@ -59,10 +60,10 @@ class TestVerifyKey: [(x["public_key"], x["signed"], x["message"], x["signature"]) for x in ed25519_known_answers()] ) def test_valid_signed_message(self, public_key, signed, message, signature): - key = nacl.signing.VerifyKey(public_key, encoding="hex") + key = nacl.signing.VerifyKey(public_key, encoder=nacl.encoding.HexEncoder) - assert binascii.hexlify(key.verify(signed, encoding="hex")) == message - assert binascii.hexlify(key.verify(message, signature, encoding="hex")) == message + assert binascii.hexlify(key.verify(signed, encoder=nacl.encoding.HexEncoder)) == message + assert binascii.hexlify(key.verify(message, signature, encoder=nacl.encoding.HexEncoder)) == message def test_invalid_signed_message(self): skey = nacl.signing.SigningKey.generate()