# 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 __future__ import absolute_import, division, print_function from nacl import exceptions as exc from nacl._sodium import ffi, lib from nacl.exceptions import ensure crypto_sign_BYTES = lib.crypto_sign_bytes() # crypto_sign_SEEDBYTES = lib.crypto_sign_seedbytes() crypto_sign_SEEDBYTES = lib.crypto_sign_secretkeybytes() // 2 crypto_sign_PUBLICKEYBYTES = lib.crypto_sign_publickeybytes() crypto_sign_SECRETKEYBYTES = lib.crypto_sign_secretkeybytes() crypto_sign_curve25519_BYTES = lib.crypto_box_secretkeybytes() crypto_sign_ed25519ph_STATEBYTES = lib.crypto_sign_ed25519ph_statebytes() def crypto_sign_keypair(): """ Returns a randomly generated public key and secret key. :rtype: (bytes(public_key), bytes(secret_key)) """ pk = ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES) sk = ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES) rc = lib.crypto_sign_keypair(pk, sk) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) return ( ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:], ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:], ) def crypto_sign_seed_keypair(seed): """ Computes and returns the public key and secret key using the seed ``seed``. :param seed: bytes :rtype: (bytes(public_key), bytes(secret_key)) """ if len(seed) != crypto_sign_SEEDBYTES: raise exc.ValueError("Invalid seed") pk = ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES) sk = ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES) rc = lib.crypto_sign_seed_keypair(pk, sk, seed) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) return ( ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:], ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:], ) def crypto_sign(message, sk): """ Signs the message ``message`` using the secret key ``sk`` and returns the signed message. :param message: bytes :param sk: bytes :rtype: bytes """ signed = ffi.new("unsigned char[]", len(message) + crypto_sign_BYTES) signed_len = ffi.new("unsigned long long *") rc = lib.crypto_sign(signed, signed_len, message, len(message), sk) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) return ffi.buffer(signed, signed_len[0])[:] def crypto_sign_open(signed, pk): """ Verifies the signature of the signed message ``signed`` using the public key ``pk`` and returns the unsigned message. :param signed: bytes :param pk: bytes :rtype: bytes """ message = ffi.new("unsigned char[]", len(signed)) message_len = ffi.new("unsigned long long *") if lib.crypto_sign_open( message, message_len, signed, len(signed), pk) != 0: raise exc.BadSignatureError("Signature was forged or corrupt") return ffi.buffer(message, message_len[0])[:] def crypto_sign_ed25519_pk_to_curve25519(public_key_bytes): """ Converts a public Ed25519 key (encoded as bytes ``public_key_bytes``) to a public Curve25519 key as bytes. Raises a ValueError if ``public_key_bytes`` is not of length ``crypto_sign_PUBLICKEYBYTES`` :param public_key_bytes: bytes :rtype: bytes """ if len(public_key_bytes) != crypto_sign_PUBLICKEYBYTES: raise exc.ValueError("Invalid curve public key") curve_public_key_len = crypto_sign_curve25519_BYTES curve_public_key = ffi.new("unsigned char[]", curve_public_key_len) rc = lib.crypto_sign_ed25519_pk_to_curve25519(curve_public_key, public_key_bytes) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) return ffi.buffer(curve_public_key, curve_public_key_len)[:] def crypto_sign_ed25519_sk_to_curve25519(secret_key_bytes): """ Converts a secret Ed25519 key (encoded as bytes ``secret_key_bytes``) to a secret Curve25519 key as bytes. Raises a ValueError if ``secret_key_bytes``is not of length ``crypto_sign_SECRETKEYBYTES`` :param public_key_bytes: bytes :rtype: bytes """ if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES: raise exc.ValueError("Invalid curve public key") curve_secret_key_len = crypto_sign_curve25519_BYTES curve_secret_key = ffi.new("unsigned char[]", curve_secret_key_len) rc = lib.crypto_sign_ed25519_sk_to_curve25519(curve_secret_key, secret_key_bytes) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) return ffi.buffer(curve_secret_key, curve_secret_key_len)[:] class crypto_sign_ed25519ph_state(object): """ State object wrapping the sha-512 state used in ed25519ph computation """ __slots__ = ['state'] def __init__(self): self.state = ffi.new('unsigned char[]', crypto_sign_ed25519ph_STATEBYTES) rc = lib.crypto_sign_ed25519ph_init(self.state) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) def crypto_sign_ed25519ph_update(edph, pmsg): """ Update the hash state wrapped in edph :param edph: the ed25519ph state being updated :type edph: crypto_sign_ed25519ph_state :param pmsg: the partial message :type pmsg: bytes :rtype: None """ ensure(isinstance(edph, crypto_sign_ed25519ph_state), 'edph parameter must be a ed25519ph_state object', raising=exc.TypeError) ensure(isinstance(pmsg, bytes), 'pmsg parameter must be a bytes object', raising=exc.TypeError) rc = lib.crypto_sign_ed25519ph_update(edph.state, pmsg, len(pmsg)) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) def crypto_sign_ed25519ph_final_create(edph, sk): """ Create a signature for the data hashed in edph using the secret key sk :param edph: the ed25519ph state for the data being signed :type edph: crypto_sign_ed25519ph_state :param sk: the ed25519 secret part of the signing key :type sk: bytes :return: ed25519ph signature :rtype: bytes """ ensure(isinstance(edph, crypto_sign_ed25519ph_state), 'edph parameter must be a ed25519ph_state object', raising=exc.TypeError) ensure(isinstance(sk, bytes), 'secret key parameter must be a bytes object', raising=exc.TypeError) ensure(len(sk) == crypto_sign_SECRETKEYBYTES, ('secret key must be {0} ' 'bytes long').format(crypto_sign_SECRETKEYBYTES), raising=exc.TypeError) signature = ffi.new("unsigned char[]", crypto_sign_BYTES) rc = lib.crypto_sign_ed25519ph_final_create(edph.state, signature, ffi.NULL, sk) ensure(rc == 0, 'Unexpected library error', raising=exc.RuntimeError) return ffi.buffer(signature, crypto_sign_BYTES)[:] def crypto_sign_ed25519ph_final_verify(edph, signature, pk): """ Verify a prehashed signature using the public key pk :param edph: the ed25519ph state for the data being verified :type edph: crypto_sign_ed25519ph_state :param signature: the signature being verified :type signature: bytes :param pk: the ed25519 public part of the signing key :type pk: bytes :return: True if the signature is valid :rtype: boolean :raises exc.BadSignatureError: if the signature is not valid """ ensure(isinstance(edph, crypto_sign_ed25519ph_state), 'edph parameter must be a ed25519ph_state object', raising=exc.TypeError) ensure(isinstance(signature, bytes), 'signature parameter must be a bytes object', raising=exc.TypeError) ensure(len(signature) == crypto_sign_BYTES, ('signature must be {0} ' 'bytes long').format(crypto_sign_BYTES), raising=exc.TypeError) ensure(isinstance(pk, bytes), 'public key parameter must be a bytes object', raising=exc.TypeError) ensure(len(pk) == crypto_sign_PUBLICKEYBYTES, ('public key must be {0} ' 'bytes long').format(crypto_sign_PUBLICKEYBYTES), raising=exc.TypeError) rc = lib.crypto_sign_ed25519ph_final_verify(edph.state, signature, pk) if rc != 0: raise exc.BadSignatureError("Signature was forged or corrupt") return True