Newer
Older
from __future__ import division
from . import six
from . import nacl
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
def signature(self):
return self[:nacl.lib.crypto_sign_BYTES]
@property
def message(self):
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):
# 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 not nacl.lib.crypto_sign_seed_keypair(pk, sk, seed):
raise CryptoError("Failed to generate a key pair")
# 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)[:])
@classmethod
def generate(cls):
return cls(random(nacl.lib.crypto_sign_SECRETKEYBYTES // 2))
def sign(self, message):
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")
return SignedMessage(nacl.ffi.buffer(sm, smlen[0])[:])