123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- # Copyright 2013 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.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- from typing import Optional
- 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
- class SignedMessage(bytes):
- """
- A bytes subclass that holds a messaged that has been signed by a
- :class:`SigningKey`.
- """
- _signature: bytes
- _message: bytes
- @classmethod
- def _from_parts(
- cls, signature: bytes, message: bytes, combined: bytes
- ) -> "SignedMessage":
- obj = cls(combined)
- obj._signature = signature
- obj._message = message
- return obj
- @property
- def signature(self) -> bytes:
- """
- The signature contained within the :class:`SignedMessage`.
- """
- return self._signature
- @property
- def message(self) -> bytes:
- """
- The message contained within the :class:`SignedMessage`.
- """
- return self._message
- class VerifyKey(encoding.Encodable, StringFixer):
- """
- The public key counterpart to an Ed25519 SigningKey for producing digital
- signatures.
- :param key: [:class:`bytes`] Serialized Ed25519 public key
- :param encoder: A class that is able to decode the `key`
- """
- def __init__(
- self, key: bytes, encoder: encoding.Encoder = encoding.RawEncoder
- ):
- # Decode the key
- key = encoder.decode(key)
- if not isinstance(key, bytes):
- raise exc.TypeError("VerifyKey must be created from 32 bytes")
- if len(key) != nacl.bindings.crypto_sign_PUBLICKEYBYTES:
- raise exc.ValueError(
- "The key must be exactly %s bytes long"
- % nacl.bindings.crypto_sign_PUBLICKEYBYTES,
- )
- self._key = key
- def __bytes__(self) -> bytes:
- return self._key
- def __hash__(self) -> int:
- return hash(bytes(self))
- def __eq__(self, other: object) -> bool:
- if not isinstance(other, self.__class__):
- return False
- return nacl.bindings.sodium_memcmp(bytes(self), bytes(other))
- def __ne__(self, other: object) -> bool:
- return not (self == other)
- def verify(
- self,
- smessage: bytes,
- signature: Optional[bytes] = None,
- encoder: encoding.Encoder = encoding.RawEncoder,
- ) -> bytes:
- """
- Verifies the signature of a signed message, returning the message
- if it has not been tampered with else raising
- :class:`~nacl.signing.BadSignatureError`.
- :param smessage: [:class:`bytes`] Either the original messaged or a
- 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.
- :rtype: :class:`bytes`
- """
- if signature is not None:
- # If we were given the message and signature separately, validate
- # signature size and combine them.
- if not isinstance(signature, bytes):
- raise exc.TypeError(
- "Verification signature must be created from %d bytes"
- % nacl.bindings.crypto_sign_BYTES,
- )
- if len(signature) != nacl.bindings.crypto_sign_BYTES:
- raise exc.ValueError(
- "The signature must be exactly %d bytes long"
- % nacl.bindings.crypto_sign_BYTES,
- )
- smessage = signature + encoder.decode(smessage)
- else:
- # Decode the signed message
- smessage = encoder.decode(smessage)
- return nacl.bindings.crypto_sign_open(smessage, self._key)
- def to_curve25519_public_key(self) -> _Curve25519_PublicKey:
- """
- Converts a :class:`~nacl.signing.VerifyKey` to a
- :class:`~nacl.public.PublicKey`
- :rtype: :class:`~nacl.public.PublicKey`
- """
- raw_pk = nacl.bindings.crypto_sign_ed25519_pk_to_curve25519(self._key)
- return _Curve25519_PublicKey(raw_pk)
- class SigningKey(encoding.Encodable, StringFixer):
- """
- Private key for producing digital signatures using the Ed25519 algorithm.
- Signing keys are produced from a 32-byte (256-bit) random seed value. This
- value can be passed into the :class:`~nacl.signing.SigningKey` as a
- :func:`bytes` whose length is 32.
- .. warning:: This **must** be protected and remain secret. Anyone who knows
- the value of your :class:`~nacl.signing.SigningKey` or it's seed can
- 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
- :ivar: verify_key: [:class:`~nacl.signing.VerifyKey`] The verify
- (i.e. public) key that corresponds with this signing key.
- """
- def __init__(
- self,
- seed: bytes,
- encoder: encoding.Encoder = encoding.RawEncoder,
- ):
- # Decode the seed
- seed = encoder.decode(seed)
- if not isinstance(seed, bytes):
- raise exc.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:
- raise exc.ValueError(
- "The seed must be exactly %d bytes long"
- % nacl.bindings.crypto_sign_SEEDBYTES
- )
- public_key, secret_key = nacl.bindings.crypto_sign_seed_keypair(seed)
- self._seed = seed
- self._signing_key = secret_key
- self.verify_key = VerifyKey(public_key)
- def __bytes__(self) -> bytes:
- return self._seed
- def __hash__(self) -> int:
- return hash(bytes(self))
- def __eq__(self, other: object) -> bool:
- if not isinstance(other, self.__class__):
- return False
- return nacl.bindings.sodium_memcmp(bytes(self), bytes(other))
- def __ne__(self, other: object) -> bool:
- return not (self == other)
- @classmethod
- def generate(cls) -> "SigningKey":
- """
- Generates a random :class:`~nacl.signing.SigningKey` object.
- :rtype: :class:`~nacl.signing.SigningKey`
- """
- return cls(
- random(nacl.bindings.crypto_sign_SEEDBYTES),
- encoder=encoding.RawEncoder,
- )
- def sign(
- self,
- message: bytes,
- encoder: encoding.Encoder = encoding.RawEncoder,
- ) -> SignedMessage:
- """
- 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.
- :rtype: :class:`~nacl.signing.SignedMessage`
- """
- raw_signed = nacl.bindings.crypto_sign(message, self._signing_key)
- crypto_sign_BYTES = nacl.bindings.crypto_sign_BYTES
- signature = encoder.encode(raw_signed[:crypto_sign_BYTES])
- message = encoder.encode(raw_signed[crypto_sign_BYTES:])
- signed = encoder.encode(raw_signed)
- return SignedMessage._from_parts(signature, message, signed)
- def to_curve25519_private_key(self) -> _Curve25519_PrivateKey:
- """
- Converts a :class:`~nacl.signing.SigningKey` to a
- :class:`~nacl.public.PrivateKey`
- :rtype: :class:`~nacl.public.PrivateKey`
- """
- sk = self._signing_key
- raw_private = nacl.bindings.crypto_sign_ed25519_sk_to_curve25519(sk)
- return _Curve25519_PrivateKey(raw_private)
|