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

Refactor the signing support to the new layout

parent 0223438f
No related branches found
No related tags found
No related merge requests found
/* 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.
*/
size_t crypto_sign_bytes();
// size_t crypto_sign_seedbytes();
size_t crypto_sign_publickeybytes();
size_t crypto_sign_secretkeybytes();
int crypto_sign_keypair(unsigned char *pk, unsigned char *sk);
int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk,
const unsigned char *seed);
int crypto_sign(unsigned char *sm, unsigned long long *smlen,
const unsigned char *m, unsigned long long mlen,
const unsigned char *sk);
int crypto_sign_open(unsigned char *m, unsigned long long *mlen,
const unsigned char *sm, unsigned long long smlen,
const unsigned char *pk);
......@@ -28,6 +28,11 @@ from nacl.c.crypto_secretbox import (
crypto_secretbox_ZEROBYTES, crypto_secretbox_BOXZEROBYTES,
crypto_secretbox, crypto_secretbox_open,
)
from nacl.c.crypto_sign import (
crypto_sign_BYTES, crypto_sign_SEEDBYTES, crypto_sign_PUBLICKEYBYTES,
crypto_sign_SECRETKEYBYTES, crypto_sign_keypair, crypto_sign_seed_keypair,
crypto_sign, crypto_sign_open,
)
from nacl.c.randombytes import randombytes
......@@ -56,5 +61,14 @@ __all__ = [
"crypto_secretbox",
"crypto_secretbox_open",
"crypto_sign_BYTES",
"crypto_sign_SEEDBYTES",
"crypto_sign_PUBLICKEYBYTES",
"crypto_sign_SECRETKEYBYTES",
"crypto_sign_keypair",
"crypto_sign_seed_keypair",
"crypto_sign",
"crypto_sign_open",
"randombytes",
]
# 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 _lib as lib
from nacl.exceptions import BadSignatureError, CryptoError
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()
def crypto_sign_keypair():
"""
Returns a randomly generated secret key and public key.
:rtype: (bytes(secret_key), bytes(public_key))
"""
sk = lib.ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
pk = lib.ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
if lib.crypto_sign_keypair(pk, sk) != 0:
raise CryptoError("An error occurred while generating keypairs")
return (
lib.ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
lib.ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
)
def crypto_sign_seed_keypair(seed):
"""
Computes and returns the secret key and public key using the seed ``seed``.
:param seed: bytes
:rtype: (bytes(secret_key), bytes(public_key))
"""
if len(seed) != crypto_sign_SEEDBYTES:
raise ValueError("Invalid seed")
sk = lib.ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
pk = lib.ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
if lib.crypto_sign_seed_keypair(pk, sk, seed) != 0:
raise CryptoError("An error occured while generating keypairs")
return (
lib.ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
lib.ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
)
def crypto_sign(sk, message):
"""
Signs the message ``message`` using the secret key ``sk`` and returns the
signed message.
:param sk: bytes
:param message: bytes
:rtype: bytes
"""
signed = lib.ffi.new("unsigned char[]", len(message) + crypto_sign_BYTES)
signed_len = lib.ffi.new("unsigned long long *")
if lib.crypto_sign(signed, signed_len, message, len(message), sk) != 0:
raise CryptoError("Failed to sign the message")
return lib.ffi.buffer(signed, signed_len[0])[:]
def crypto_sign_open(pk, signed):
"""
Verifies the signature of the signed message ``signed`` using the public
key ``pkg`` and returns the unsigned message.
:param pk: bytes
:param signed: bytes
:rtype: bytes
"""
message = lib.ffi.new("unsigned char[]", len(signed))
message_len = lib.ffi.new("unsigned long long *")
if lib.crypto_sign_open(
message, message_len, signed, len(signed), pk) != 0:
raise BadSignatureError("Signature was forged or corrupt")
return lib.ffi.buffer(message, message_len[0])[:]
......@@ -19,3 +19,9 @@ class CryptoError(Exception):
"""
Base exception for all nacl related errors
"""
class BadSignatureError(CryptoError):
"""
Raised when the signature was forged or otherwise corrupt.
"""
......@@ -16,18 +16,12 @@ from __future__ import division
import six
import nacl.c
from . import encoding
from .c import _lib as nacl
from .exceptions import CryptoError
from .utils import StringFixer, random
class BadSignatureError(CryptoError):
"""
Raised when the signature was forged or otherwise corrupt.
"""
class SignedMessage(six.binary_type):
"""
A bytes subclass that holds a messaged that has been signed by a
......@@ -69,9 +63,9 @@ class VerifyKey(encoding.Encodable, StringFixer, object):
# Decode the key
key = encoder.decode(key)
if len(key) != nacl.lib.crypto_sign_PUBLICKEYBYTES:
if len(key) != nacl.c.crypto_sign_PUBLICKEYBYTES:
raise ValueError("The key must be exactly %s bytes long" %
nacl.lib.crypto_sign_PUBLICKEYBYTES)
nacl.c.crypto_sign_PUBLICKEYBYTES)
self._key = key
......@@ -100,13 +94,7 @@ class VerifyKey(encoding.Encodable, StringFixer, object):
# Decode the signed message
smessage = encoder.decode(smessage)
message = nacl.ffi.new("unsigned char[]", len(smessage))
message_len = nacl.ffi.new("unsigned long long *")
if not nacl.lib.crypto_sign_open(message, message_len, smessage, len(smessage), self._key):
raise BadSignatureError("Signature was forged or corrupt")
return nacl.ffi.buffer(message, message_len[0])[:]
return nacl.c.crypto_sign_open(self._key, smessage)
class SigningKey(encoding.Encodable, StringFixer, object):
......@@ -133,23 +121,15 @@ class SigningKey(encoding.Encodable, StringFixer, object):
seed = encoder.decode(seed)
# Verify that our seed is the proper size
seed_size = nacl.lib.crypto_sign_SECRETKEYBYTES // 2
if len(seed) != seed_size:
raise ValueError(
'The seed must be exactly %d bytes long' % (seed_size,))
pk = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_sign_PUBLICKEYBYTES)
sk = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_sign_SECRETKEYBYTES)
if len(seed) != nacl.c.crypto_sign_SEEDBYTES:
raise ValueError("The seed must be exactly %d bytes long" %
nacl.c.crypto_sign_SEEDBYTES)
if not nacl.lib.crypto_sign_seed_keypair(pk, sk, seed):
raise CryptoError("Failed to generate a key pair")
secret_key, public_key = nacl.c.crypto_sign_seed_keypair(seed)
# Secret values
self._seed = seed
self._signing_key = nacl.ffi.buffer(sk, nacl.lib.crypto_sign_SECRETKEYBYTES)[:]
# Public values
self.verify_key = VerifyKey(nacl.ffi.buffer(pk, nacl.lib.crypto_sign_PUBLICKEYBYTES)[:])
self._signing_key = secret_key
self.verify_key = VerifyKey(public_key)
def __bytes__(self):
return self._seed
......@@ -161,9 +141,10 @@ class SigningKey(encoding.Encodable, StringFixer, object):
:rtype: :class:`~nacl.signing.SigningKey`
"""
return cls(random(nacl.lib.crypto_sign_SECRETKEYBYTES // 2),
encoder=encoding.RawEncoder,
)
return cls(
random(nacl.c.crypto_sign_SEEDBYTES),
encoder=encoding.RawEncoder,
)
def sign(self, message, encoder=encoding.RawEncoder):
"""
......@@ -173,16 +154,10 @@ class SigningKey(encoding.Encodable, StringFixer, object):
:param encoder: A class that is used to encode the signed message.
:rtype: :class:`~nacl.signing.SignedMessage`
"""
sm = nacl.ffi.new("unsigned char[]", len(message) + nacl.lib.crypto_sign_BYTES)
smlen = nacl.ffi.new("unsigned long long *")
if not nacl.lib.crypto_sign(sm, smlen, message, len(message), self._signing_key):
raise CryptoError("Failed to sign the message")
raw_signed = nacl.ffi.buffer(sm, smlen[0])[:]
raw_signed = nacl.c.crypto_sign(self._signing_key, message)
signature = encoder.encode(raw_signed[:nacl.lib.crypto_sign_BYTES])
message = encoder.encode(raw_signed[nacl.lib.crypto_sign_BYTES:])
signature = encoder.encode(raw_signed[:nacl.c.crypto_sign_BYTES])
message = encoder.encode(raw_signed[nacl.c.crypto_sign_BYTES:])
signed = encoder.encode(raw_signed)
return SignedMessage._from_parts(signature, message, signed)
......@@ -21,7 +21,7 @@ import pytest
import nacl.signing
import nacl.encoding
import nacl.nacl
import nacl.exceptions
def ed25519_known_answers():
......@@ -86,9 +86,9 @@ class TestVerifyKey:
# Small sanity check
assert skey.verify_key.verify(smessage)
with pytest.raises(nacl.signing.BadSignatureError):
with pytest.raises(nacl.exceptions.BadSignatureError):
skey.verify_key.verify(message, signature)
with pytest.raises(nacl.signing.BadSignatureError):
with pytest.raises(nacl.exceptions.BadSignatureError):
forged = nacl.signing.SignedMessage(signature + message)
skey.verify_key.verify(forged)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment