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

Add VerifyKey which can be used to verify a signed message

* Accepts the binary representation of a public key
* nacl.signing.VerifyKey().verify accepts either a single signed
  message with the signature and message conatted or seperate
  message and signature and will raise an exception if it detects
  tampering, forgery, or corruption.
parent 4617f89d
No related branches found
No related tags found
No related merge requests found
......@@ -21,6 +21,7 @@ ffi.cdef(
int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk, 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);
"""
# Hashing
......@@ -58,6 +59,7 @@ def wrap_nacl_function(func):
lib.crypto_sign_seed_keypair = wrap_nacl_function(lib.crypto_sign_seed_keypair)
lib.crypto_sign = wrap_nacl_function(lib.crypto_sign)
lib.crypto_sign_open = wrap_nacl_function(lib.crypto_sign_open)
lib.crypto_hash = wrap_nacl_function(lib.crypto_hash)
lib.crypto_hash_sha256 = wrap_nacl_function(lib.crypto_hash_sha256)
......
......@@ -7,6 +7,12 @@ from .exceptions import CryptoError
from .random import random
class BadSignatureError(CryptoError):
"""
Raised when the signature was forged or otherwise corrupt.
"""
class SignedMessage(six.binary_type):
@property
......@@ -18,6 +24,30 @@ class SignedMessage(six.binary_type):
return self[nacl.lib.crypto_sign_BYTES:]
class VerifyKey(object):
def __init__(self, key):
if len(key) != nacl.lib.crypto_sign_PUBLICKEYBYTES:
raise ValueError("The key must be exactly %s bytes long" %
nacl.lib.crypto_sign_PUBLICKEYBYTES)
self._key = key
def verify(self, smessage, signature=None):
if signature is not None:
# If we were given the message and signature separately, combine
# them.
smessage = signature + 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])[:]
class SigningKey(object):
def __init__(self, seed):
......@@ -37,6 +67,9 @@ class SigningKey(object):
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)[:])
@classmethod
def generate(cls):
return cls(random(nacl.lib.crypto_sign_SECRETKEYBYTES // 2))
......
......@@ -50,3 +50,33 @@ class TestSigningKey:
assert binascii.hexlify(signed) == expected
assert binascii.hexlify(signed.message) == message
assert binascii.hexlify(signed.signature) == signature
class TestVerifyKey:
@pytest.mark.parametrize(("public_key", "signed", "message", "signature"),
[(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(binascii.unhexlify(public_key))
signedb = binascii.unhexlify(signed)
messageb = binascii.unhexlify(message)
signatureb = binascii.unhexlify(signature)
assert binascii.hexlify(key.verify(signedb)) == message
assert binascii.hexlify(key.verify(messageb, signatureb)) == message
def test_invalid_signed_message(self):
skey = nacl.signing.SigningKey.generate()
smessage = skey.sign("A Test Message!")
signature, message = smessage.signature, "A Forged Test Message!"
# Small sanity check
assert skey.verify_key.verify(smessage)
with pytest.raises(nacl.signing.BadSignatureError):
skey.verify_key.verify(message, signature)
with pytest.raises(nacl.signing.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