Skip to content
Snippets Groups Projects
Commit fc42dee0 authored by Donald Stufft's avatar Donald Stufft
Browse files

Move to passing a full encoder instead of a short string

* This eliminates the global state from the encoding system.
parent 0667f390
No related branches found
No related tags found
No related merge requests found
......@@ -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 -
......
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))
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)
......@@ -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)
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
......@@ -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()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment