import _ed25519
BadSignatureError = _ed25519.BadSignatureError

__doc__ = """\
ed25519 -- Ed25519 public-key signatures

To create a new Ed25519 signing key, create a 32-byte unguessable bytestring,
with keybytes=os.urandom(32), and pass it to the SigningKey(keybytes)
constructor. To create the same key in the future, call the constructor with
the same string.

To get a verifying key from a signing key, first get the verifying key bytes
with verfbytes=sk.get_verifying_key_bytes(), then construct the VerifyingKey
instance with VerifyingKey(verfbytes).

To sign a message, use sig=sk.sign(msg), which returns the signature as a
64-byte binary bytestring. To verify a signature, use vk.verify(sig, msg),
which either returns None or raises BadSignatureError.
"""

class SigningKey(object):
    # this is how all keys are created
    def __init__(self, sk_bytes):
        if not isinstance(sk_bytes, type("")):
            raise TypeError("must be bytes, not %s" % type(sk_bytes))
        if len(sk_bytes) != 32:
            raise ValueError("must be exactly 32 bytes")
        vk_bytes, sk_and_vk = _ed25519.publickey(sk_bytes)
        assert sk_and_vk[:32] == sk_bytes
        assert vk_bytes == sk_and_vk[32:]
        self.vk_bytes = vk_bytes
        self.sk_and_vk = sk_and_vk

    def __eq__(self, them):
        if not isinstance(them, object): return False
        return (them.__class__ == self.__class__
                and them.sk_and_vk == self.sk_and_vk)

    def get_verifying_key_bytes(self):
        return self.vk_bytes

    def sign(self, msg):
        sig_and_msg = _ed25519.sign(msg, self.sk_and_vk)
        # the response is R+S+msg
        sig_R = sig_and_msg[0:32]
        sig_S = sig_and_msg[32:64]
        msg_out = sig_and_msg[64:]
        sig_out = sig_R + sig_S
        assert msg_out == msg
        return sig_out

class VerifyingKey(object):
    def __init__(self, vk_bytes):
        if not isinstance(vk_bytes, type("")):
            raise TypeError("must be bytes, not %s" % type(vk_bytes))
        if len(vk_bytes) != 32:
            raise ValueError("must be exactly 32 bytes")
        self.vk_bytes = vk_bytes

    def __eq__(self, them):
        if not isinstance(them, object): return False
        return (them.__class__ == self.__class__
                and them.vk_bytes == self.vk_bytes)

    def verify(self, sig, msg):
        assert isinstance(sig, type("")) # string, really bytes
        assert len(sig) == 64
        sig_R = sig[:32]
        sig_S = sig[32:]
        sig_and_msg = sig_R + sig_S + msg
        # this might raise BadSignatureError
        msg2 = _ed25519.open(sig_and_msg, self.vk_bytes)
        assert msg2 == msg

def selftest():
    from binascii import unhexlify
    message = "crypto libraries should always test themselves at powerup"
    sk_bytes = unhexlify("548b1f9f938519ad3d527d8c47a1e6ec1439fbec61710b245363865c6f234899")
    sk = SigningKey(sk_bytes)
    vk_bytes = unhexlify("787162d9ad1ad571237681560c1ad653fb7df9e09e637e6a8072e4520fd288ca")
    vk = VerifyingKey(vk_bytes)
    assert sk.get_verifying_key_bytes() == vk_bytes
    sig = sk.sign(message)
    assert sig == "13f42bc2d485e76c7cfaad25e1a840ede25b44a73befb0a528d836d7b434cf87e260c09d980388fab4cb564885857ea4dc3fb04107ca74960cc5a4d415fbf50d".decode('hex'), sig
    vk.verify(sig, message)

selftest()
