~marnold128/bsigs

23711e07943e082b553d78f13aa59158292a49da — Matt Arnold 1 year, 10 months ago
Inital import
7 files changed, 163 insertions(+), 0 deletions(-)

A README.md
A Test.sig
A Test.txt
A bclient.salt
A blog.pub
A blog.sec
A bsigs.py
A  => README.md +8 -0
@@ 1,8 @@
# bsigs

A simple signing utility for practice, also use with my new blog engine.
You shouldn't use this for anything serious/production quality. As I am 
new to crypto in general, and have relied heavily on the libsoduim/pynacl
manual for guidence, and this has not been audited

The password for the test private key is 'test' 

A  => Test.sig +1 -0
@@ 1,1 @@
cfWa4pPQiD8XFkS1y3LOsSF8kA1Q0XpPB5v5MklukmZlB9pJ4QTJz/UFuBXS/vga1wekD9EIsKafL0JMVxySBEF0dGFjayBBdCBEYXduIApQaXVzCg==
\ No newline at end of file

A  => Test.txt +2 -0
@@ 1,2 @@
Attack At Dawn 
Pius

A  => bclient.salt +1 -0
@@ 1,1 @@
�&Go��,�,LF�>���
\ No newline at end of file

A  => blog.pub +1 -0
@@ 1,1 @@
nR6FC9GHk+olScO5FPpUYoppgo95SHvd5UJKKFt4Crs=
\ No newline at end of file

A  => blog.sec +0 -0
A  => bsigs.py +150 -0
@@ 1,150 @@
#!/usr/bin/env python3
# A little client for m
import getpass
import click
from nacl import pwhash, secret, utils
from nacl.exceptions import CryptoError
from nacl.signing import SigningKey, VerifyKey
from nacl.encoding import Base64Encoder
from pathlib import Path
import os
import os.path
import sys
ourdir = os.path.join(Path.home(), ".bclient")
kdf = pwhash.argon2i.kdf
salt = None
ops = pwhash.argon2i.OPSLIMIT_SENSITIVE
mem = pwhash.argon2i.MEMLIMIT_SENSITIVE
pubkeyfile = os.path.join(ourdir, "blog.pub")
@click.group()
def cli():
    pass

def load_salt(p, generate=False):
    if os.path.exists(p):
        return open(p, "rb").read()
    if not generate:
        raise EOFError("No Salt file exists")
    slt = utils.random(pwhash.argon2i.SALTBYTES)
    wf = open(p, "wb")
    wf.write(slt)
    wf.close()
    return slt

def load_signkey(inDir, passwd):
    salt = load_salt(os.path.join(inDir, "bclient.salt"))
    cipherText = open(os.path.join(inDir, "blog.sec"), "rb").read()
    Wrap_key = kdf(secret.SecretBox.KEY_SIZE, passwd, salt=salt, opslimit=ops, memlimit=mem)
    sbox = secret.SecretBox(Wrap_key)
    plain = None
    try:
        plain = sbox.decrypt(cipherText)
    except CryptoError:
        raise CryptoError("Wrong password!")
    
    return plain



@cli.command()
@click.option('--outputdir', default=ourdir, help="Select output dir")

def generate(outputdir):
    """Generate a keypair"""
    print(Path.home())
    print(ourdir)
    passwd = getpass.getpass("Enter a password: ")
    repeat = getpass.getpass("repeat it: ")
    if passwd != repeat:
        click.echo("They don't match")
        return
    password = bytes(passwd, "utf-8")
    salt = load_salt(os.path.join(outputdir, "bclient.salt"), generate=True)
    Protected_Key = kdf(secret.SecretBox.KEY_SIZE, password, salt, 
        opslimit=ops, memlimit=mem)
    signkey = SigningKey.generate()
    verify_key = signkey.verify_key
    verify_key_bytes = verify_key.encode(encoder=Base64Encoder)
    
    sbox = secret.SecretBox(Protected_Key)
    nonce = utils.random(secret.SecretBox.NONCE_SIZE)
    coded = signkey.encode()
    enc_key = sbox.encrypt(coded, nonce)
    # all the writing operations sghould be done after crypto ops
    # to ensure it's atomic, we don't want partials hanging about
    # private key first as we can derive the public key from it
    priv_file = open(os.path.join(outputdir, "blog.sec"), "wb")
    priv_file.write(enc_key)
    priv_file.close()
    public_file = open(os.path.join(outputdir, "blog.pub"), "wb")
    public_file.write(verify_key_bytes)
    public_file.close()
    click.echo("keys forged")
    return

@cli.command()
@click.option('--keydir', default=ourdir, help="Select directory keys are in")
@click.option("--signfile", default="STDIN", help="File to sign default stdin")
@click.option("--outfile", default="STDOUT", help="output the signed message to")
def sign_msg(keydir, signfile, outfile):
    passwd = getpass.getpass("Enter your password: ")
    pKey = load_signkey(keydir, bytes(passwd, "utf-8"))
    rKey = SigningKey(pKey)
    click.echo("Vault opened! Ready to sign")
    if signfile == "STDIN":
        msg = sys.stdin.read()
        msg = bytes(msg, "utf-8")

    else:
        msg = open(signfile, "rb").read()
    signed = rKey.sign(msg, encoder=Base64Encoder)
    if outfile == "STDOUT":
        click.clear()
        sys.stdout.write(signed.decode("utf-8"))
    else:
        out = open(outfile, "w")
        out.write(signed.decode("utf-8"))
        
    return




@cli.command()
@click.option('--keyfile', default=pubkeyfile, help="Select directory keys are in")
@click.option("--signfile", default="STDIN", help="File to sign default stdin")

def verify_msg(keyfile, signfile):
    pKey = open(keyfile).read()
    vkBytes = Base64Encoder.decode(pKey)
    vKey = VerifyKey(vkBytes)

    if signfile == "STDIN":
        click.clear()
        click.echo("Ready to verify don't forget the sig")
        buff = sys.stdin.read()
        buffBytes = Base64Encoder.decode(buff)
        
    else:
        buff = open(signfile).read()
        
        buffBytes = Base64Encoder.decode(buff)

    try:
       msg = vKey.verify(buffBytes)
       click.echo(msg)


    except CryptoError as e:
        click.echp(str(e))
        return 1
    click.echo("OK!")
    return 0


if __name__ == '__main__':
    cli()



    
\ No newline at end of file