#! /usr/bin/python

import os, sys
from base64 import b32encode, b32decode
import ed25519
from hashlib import sha256

def help():
    print """\
Usage:

 edsig generate [STEM]
   creates keypair, writes to 'STEM.signing.key' and 'STEM.verifying.key'
   default is to 'signing.key' and 'verifying.key'

 edsig sign (signing.key|keyfile) message.file
   prints signature to stdout
   If message.file is "-", reads from stdin.

 edsig verify (verifying.key|keyfile) message.file (signature|sigfile)
   prints 'good signature!' or raises exception
   If message.file is "-", reads from stdin.

Key-providing arguments can either be the key itself, or point to a file
containing the key.
"""

def b2a(b):
    return b32encode(b).lower().strip("=")
def a2b(a):
    a = a + "="*{0:0, 1:"X", 2:6, 3:"X", 4:4, 5:3, 6:"X", 7:1}[len(a)%8]
    return b32decode(a.upper())

def remove_prefix(prefix, s):
    if not s.startswith(prefix):
        raise ValueError("no prefix found")
    return s[len(prefix):]

def data_from_arg(arg, prefix, keylen, readable):
    if (readable
        and arg.startswith(prefix)
        and len(remove_prefix(prefix, arg))==keylen):
        data_s = arg
    elif os.path.isfile(arg):
        data_s = open(arg,"r").read()
        if readable:
            data_s = data_s.strip()
    else:
        raise ValueError("unable to get data from '%s'" % arg)
    data = remove_prefix(prefix, data_s)
    if readable:
        return a2b(data)
    return data

def message_rep(msg_arg):
    if msg_arg == "-":
        f = sys.stdin
    else:
        f = open(msg_arg, "rb")
    h = sha256()
    while True:
        data = f.read(16*1024)
        if not data:
            break
        h.update(data)
    return h.digest()

if len(sys.argv) < 2:
    help()
elif sys.argv[1] == "generate":
    sk,vk = ed25519.create_keypair()
    if len(sys.argv) > 2:
        sk_outfile = sys.argv[2]+".signing.key"
        vk_outfile = sys.argv[2]+".verifying.key"
    else:
        sk_outfile = "signing.key"
        vk_outfile = "verifying.key"
    sk_s = "sign0-"+sk.to_seed()
    vk_s = "verf0-"+b32encode(vk.to_string()).lower().strip("=")
    open(sk_outfile,"w").write(sk_s)
    open(vk_outfile,"w").write(vk_s+"\n")
    print "wrote private signing key to", sk_outfile
    print "write public verifying key to", vk_outfile
elif sys.argv[1] == "sign":
    sk_arg = sys.argv[2]
    msg_arg = sys.argv[3]
    sk = ed25519.SigningKey(data_from_arg(sk_arg, "sign0-", 52, False))
    sig = sk.sign(message_rep(msg_arg))
    print "sig0-"+b2a(sig)
elif sys.argv[1] == "verify":
    vk_arg = sys.argv[2]
    msg_arg = sys.argv[3]
    sig_arg = sys.argv[4]
    vk = ed25519.VerifyingKey(data_from_arg(vk_arg, "verf0-", 52, True))
    sig = data_from_arg(sig_arg, "sig0-", 103, True)
    vk.verify(sig, message_rep(msg_arg)) # could raise BadSignature
    print "good signature!"
else:
    help()
