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

Deprecate encoder in signining #523

Closed
wants to merge 11 commits into from
144 changes: 13 additions & 131 deletions docs/signing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@ use it to validate that your messages are actually authentic.
Example
-------

Signing and verifying a message without encoding the key or message

Signer's perspective (:class:`~nacl.signing.SigningKey`)

.. testcode::

from nacl.signing import SigningKey
import nacl.encoding
import nacl.signing

# Generate a new random signing key
signing_key = SigningKey.generate()
signing_key = nacl.signing.SigningKey.generate()

# Sign a message with the signing key
signed = signing_key.sign(b"Attack at Dawn")
Expand All @@ -35,21 +34,21 @@ Signer's perspective (:class:`~nacl.signing.SigningKey`)
verify_key = signing_key.verify_key

# Serialize the verify key to send it to a third party
verify_key_bytes = verify_key.encode()
verify_key_hex = nacl.encoding.HexEncoder.encode(bytes(verify_key))

Verifier's perspective (:class:`~nacl.signing.VerifyKey`)

.. testcode::

from nacl.signing import VerifyKey
import nacl.signing

# Create a VerifyKey object from a hex serialized public key
verify_key = VerifyKey(verify_key_bytes)
decoded_key = nacl.encoding.HexEncoder.decode(verify_key_hex)
verify_key = nacl.signing.VerifyKey(decoded_key)

# Check the validity of a message's signature
# The message and the signature can either be passed together, or
# separately if the signature is decoded to raw bytes.
# These are equivalent:
# The message and the signature can either be passed separately or
# concatenated together. These are equivalent:
verify_key.verify(signed)
verify_key.verify(signed.message, signed.signature)

Expand All @@ -66,122 +65,10 @@ Verifier's perspective (:class:`~nacl.signing.VerifyKey`)
nacl.exceptions.BadSignatureError: Signature was forged or corrupt


Example
-------

Signing and verifying a message encoded with HexEncoder

Signer's perspective (:class:`~nacl.signing.SigningKey`)

.. testcode::

from nacl.encoding import HexEncoder
from nacl.signing import SigningKey

# Generate a new random signing key
signing_key = SigningKey.generate()

# Sign a message with the signing key
signed_hex = signing_key.sign(b"Attack at Dawn", encoder=HexEncoder)

# Obtain the verify key for a given signing key
verify_key = signing_key.verify_key

# Serialize the verify key to send it to a third party
verify_key_hex = verify_key.encode(encoder=HexEncoder)

Verifier's perspective (:class:`~nacl.signing.VerifyKey`)

.. testcode::

from nacl.encoding import HexEncoder
from nacl.signing import VerifyKey

# Create a VerifyKey object from a hex serialized public key
verify_key = VerifyKey(verify_key_hex, encoder=HexEncoder)

# Check the validity of a message's signature
# The message and the signature can either be passed together, or
# separately if the signature is decoded to raw bytes.
# These are equivalent:
verify_key.verify(signed_hex, encoder=HexEncoder)
signature_bytes = HexEncoder.decode(signed_hex.signature)
verify_key.verify(signed_hex.message, signature_bytes,
encoder=HexEncoder)

# Alter the signed message text
forged = signed_hex[:-1] + bytes([int(signed_hex[-1]) ^ 1])
# Will raise nacl.exceptions.BadSignatureError, since the signature check
# is failing
verify_key.verify(forged)

.. testoutput::

Traceback (most recent call last):
...
nacl.exceptions.BadSignatureError: Signature was forged or corrupt


Example
-------

Signing and verifying a message encoded with Base64Encoder

Signer's perspective (:class:`~nacl.signing.SigningKey`)

.. testcode::

from nacl.encoding import Base64Encoder
from nacl.signing import SigningKey

# Generate a new random signing key
signing_key = SigningKey.generate()

# Sign a message with the signing key
signed_b64 = signing_key.sign(b"Attack at Dawn", encoder=Base64Encoder)

# Obtain the verify key for a given signing key
verify_key = signing_key.verify_key

# Serialize the verify key to send it to a third party
verify_key_b64 = verify_key.encode(encoder=Base64Encoder)

Verifier's perspective (:class:`~nacl.signing.VerifyKey`)

.. testcode::

from nacl.encoding import Base64Encoder
from nacl.signing import VerifyKey

# Create a VerifyKey object from a base64 serialized public key
verify_key = VerifyKey(verify_key_b64, encoder=Base64Encoder)

# Check the validity of a message's signature
# The message and the signature can either be passed together, or
# separately if the signature is decoded to raw bytes.
# These are equivalent:
verify_key.verify(signed_b64, encoder=Base64Encoder)
signature_bytes = Base64Encoder.decode(signed_b64.signature)
verify_key.verify(signed_b64.message, signature_bytes,
encoder=Base64Encoder)

# Alter the signed message text
forged = signed_b64[:-1] + bytes([int(signed_b64[-1]) ^ 1])
# Will raise nacl.exceptions.BadSignatureError, since the signature check
# is failing
verify_key.verify(forged)

.. testoutput::

Traceback (most recent call last):
...
nacl.exceptions.BadSignatureError: Signature was forged or corrupt


Reference
---------

.. class:: SigningKey(seed, encoder)
.. class:: SigningKey(seed)

Private key for producing digital signatures using the Ed25519 algorithm.

Expand All @@ -194,7 +81,6 @@ Reference
masquerade as you.

:param bytes seed: Random 32-byte value (i.e. private key).
:param encoder: A class that is able to decode the ``seed``.

.. attribute:: verify_key

Expand All @@ -207,24 +93,22 @@ Reference

:return: An instance of :class:`~nacl.signing.SigningKey`.

.. method:: sign(message, encoder)
.. method:: sign(message)

Sign a message using this key.

:param bytes message: The data to be signed.
:param encoder: A class that is able to decode the signed message.

:return: An instance of :class:`~nacl.signing.SignedMessage`.

.. class:: VerifyKey(key, encoder)
.. class:: VerifyKey(key)

The public key counterpart to an Ed25519 :class:`~nacl.signing.SigningKey`
for producing digital signatures.

:param bytes key: A serialized Ed25519 public key.
:param encoder: A class that is able to decode the ``key``.

.. method:: verify(smessage, signature, encoder)
.. method:: verify(smessage, signature)

Verifies the signature of a signed message.

Expand All @@ -233,8 +117,6 @@ Reference
:param bytes signature: The signature of the message to verify against.
If the value of ``smessage`` is the concated signature and message,
this parameter can be ``None``.
:param encoder: A class that is able to decode the secret message and
signature.

:return bytes: The message if successfully verified.

Expand Down
71 changes: 53 additions & 18 deletions src/nacl/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@

from __future__ import absolute_import, division, print_function

from warnings import warn

import nacl.bindings
from nacl import encoding
from nacl import exceptions as exc
from nacl.public import (PrivateKey as _Curve25519_PrivateKey,
PublicKey as _Curve25519_PublicKey)
from nacl.utils import StringFixer, random
from nacl.utils import PyNaclDeprecated, StringFixer, random


class SignedMessage(bytes):
Expand Down Expand Up @@ -56,10 +58,18 @@ class VerifyKey(encoding.Encodable, StringFixer, object):
signatures.

:param key: [:class:`bytes`] Serialized Ed25519 public key
:param encoder: A class that is able to decode the `key`
:param encoder: (Deprecated) A class that is able to decode the `key`
"""

def __init__(self, key, encoder=encoding.RawEncoder):
def __init__(self, key, encoder=None):
if encoder is None:
encoder = encoding.RawEncoder
else:
warn(("Implicit encoding/decoding of signing keys "
Copy link
Member

Choose a reason for hiding this comment

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

We should just say encoding/decoding of signing keys is deprecated. Remove the words explicit and implicit from this msg.

"is deprecated and will be removed in a future release. "
"Remove explicit 'encoder={}' calling parameter").format(
encoder.__class__),
PyNaclDeprecated)
# Decode the key
key = encoder.decode(key)
if not isinstance(key, bytes):
Expand Down Expand Up @@ -87,7 +97,7 @@ def __eq__(self, other):
def __ne__(self, other):
return not (self == other)

def verify(self, smessage, signature=None, encoder=encoding.RawEncoder):
def verify(self, smessage, signature=None, encoder=None):
"""
Verifies the signature of a signed message, returning the message
if it has not been tampered with else raising
Expand All @@ -97,17 +107,26 @@ def verify(self, smessage, signature=None, encoder=encoding.RawEncoder):
signature and message concated together.
:param signature: [:class:`bytes`] If an unsigned message is given for
smessage then the detached signature must be provided.
:param encoder: A class that is able to decode the secret message and
signature.
:param encoder: (Deprecated) A class that is able to decode the secret
message and signature.
:rtype: :class:`bytes`
"""
if encoder is None:
encoder = encoding.RawEncoder
else:
warn(("Automatic encoding/decoding of signed messages "
Copy link
Member

Choose a reason for hiding this comment

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

This should be the same message (or better yet, this should be a function that everything invokes)

"is deprecated and will be removed in a future release. "
"Remove explicit 'encoder={}' calling parameter").format(
encoder.__class__),
PyNaclDeprecated)

if signature is not None:
# If we were given the message and signature separately, combine
# them.
smessage = signature + encoder.decode(smessage)
Copy link
Member

Choose a reason for hiding this comment

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

Can we quickly pull this revert into its own PR so we can land that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just reopened the Revert PR #505, and rebased it on current master.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For some reason, #505 was kept in the Closed PR list. Just created #545 from the same branch.

else:
# Decode the signed message
smessage = encoder.decode(smessage)
smessage = signature + smessage

# Decode the signed message
smessage = encoder.decode(smessage)

return nacl.bindings.crypto_sign_open(smessage, self._key)

Expand Down Expand Up @@ -135,13 +154,22 @@ class SigningKey(encoding.Encodable, StringFixer, object):
masquerade as you.

:param seed: [:class:`bytes`] Random 32-byte value (i.e. private key)
:param encoder: A class that is able to decode the seed
:param encoder: (Deprecated) A class that is able to decode the seed

:ivar: verify_key: [:class:`~nacl.signing.VerifyKey`] The verify
(i.e. public) key that corresponds with this signing key.
"""

def __init__(self, seed, encoder=encoding.RawEncoder):
def __init__(self, seed, encoder=None):
if encoder is None:
encoder = encoding.RawEncoder
else:
warn(("Implicit encoding/decoding of signing keys "
Copy link
Member

Choose a reason for hiding this comment

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

Same

"is deprecated and will be removed in a future release. "
"Remove explicit 'encoder={}' calling parameter").format(
encoder.__class__),
PyNaclDeprecated)

# Decode the seed
seed = encoder.decode(seed)
if not isinstance(seed, bytes):
Expand Down Expand Up @@ -182,19 +210,26 @@ def generate(cls):

:rtype: :class:`~nacl.signing.SigningKey`
"""
return cls(
random(nacl.bindings.crypto_sign_SEEDBYTES),
encoder=encoding.RawEncoder,
)
return cls(random(nacl.bindings.crypto_sign_SEEDBYTES))

def sign(self, message, encoder=encoding.RawEncoder):
def sign(self, message, encoder=None):
"""
Sign a message using this key.

:param message: [:class:`bytes`] The data to be signed.
:param encoder: A class that is used to encode the signed message.
:param encoder: (Deprecated) A class that is used to encode
the signed message.
:rtype: :class:`~nacl.signing.SignedMessage`
"""
if encoder is None:
encoder = encoding.RawEncoder
else:
warn(("Automatic encoding/decoding of signed messages "
Copy link
Member

Choose a reason for hiding this comment

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

Same

"is deprecated and will be removed in a future release. "
"Remove explicit 'encoder={}' calling parameter").format(
encoder.__class__),
PyNaclDeprecated)

raw_signed = nacl.bindings.crypto_sign(message, self._signing_key)

crypto_sign_BYTES = nacl.bindings.crypto_sign_BYTES
Expand Down
6 changes: 5 additions & 1 deletion src/nacl/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2013 Donald Stufft and individual contributors
# Copyright 2013-2019 Donald Stufft and individual contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,10 @@
import six


class PyNaclDeprecated(UserWarning):
pass


class EncryptedMessage(bytes):
"""
A bytes subclass that holds a messaged that has been encrypted by a
Expand Down
Loading