diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/libsigntools.py | 118 | ||||
| -rwxr-xr-x | tools/sigsum-gensigkey.py | 21 | ||||
| -rwxr-xr-x | tools/sigsum-sign-leaf.py | 61 | ||||
| -rwxr-xr-x | tools/sigsum-verify-leaf.py | 32 | ||||
| -rwxr-xr-x | tools/sshkey2nacl.py | 20 | 
5 files changed, 252 insertions, 0 deletions
| diff --git a/tools/libsigntools.py b/tools/libsigntools.py new file mode 100644 index 0000000..a3f0bcc --- /dev/null +++ b/tools/libsigntools.py @@ -0,0 +1,118 @@ +import sys +import struct +from hashlib import sha256, sha512 + +def checksum_stdin(hashalg='sha256'): +    if hashalg == 'sha256': +        d = sha256() +    elif hashalg == 'sha512': +        d = sha512() +    else: +        return None + +    while True: +        buf = sys.stdin.buffer.read() +        if not buf: +            break +        d.update(buf) + +    return d.digest() + +def ssh_to_sign(namespace, hashalg, checksum): +    if hashalg == 'sha256': +        hashlen = 32 +    elif hashalg == 'sha512': +        hashlen = 64 +    else: +        return None +    s = struct.pack('!6sI{}sII6sI{}s'.format(len(namespace), hashlen), +                    b'SSHSIG', +                    len(namespace), bytes(namespace, 'ascii'), +                    0, +                    6, bytes(hashalg, 'ascii'), +                    hashlen, checksum) +    return s + +# Adapted from https://stackoverflow.com/questions/65684414/how-to-use-ssh-keygen-ed25519-keys-for-encryption-in-python +# Author: LJHW +from base64 import b64decode +from nacl.encoding import RawEncoder +from nacl.signing  import SigningKey, VerifyKey +class C25519: +    # Adapted from https://gist.github.com/R-VdP/b7ac0106a4fd395ee1c37bfe6f552a36 sealing.py +    # Author: Ramses https://github.com/R-VdP +    __key_length = 32 +    __private_key_signature = b'\x00\x00\x00\x40' +    __public_key_signature  = b'\x00\x00\x00\x20' + +    @classmethod +    def __bytes_after(cls, signature, length, bytestr): +        start = bytestr.find(signature) + len(signature) +        return bytestr[start:start+length] + +    @classmethod +    def __extract_signing_key(cls, private_data): +        openssh_bytes = b64decode(private_data) +        private_bytes = cls.__bytes_after( +            cls.__private_key_signature, +            cls.__key_length, +            openssh_bytes +        ) +        signing_key = SigningKey(seed=private_bytes, encoder=RawEncoder) +        return signing_key + +    @classmethod +    def __extract_verify_key(cls, public_data): +        openssh_bytes = b64decode(public_data) +        public_bytes = cls.__bytes_after( +            cls.__public_key_signature, +            cls.__key_length, +            openssh_bytes +        ) +        verify_key = VerifyKey(key=public_bytes, encoder=RawEncoder) +        return verify_key + +    @classmethod +    def __private_data_from_file(cls, file_name): +        with open(file_name, 'r') as file: +            contents = file.read() +        contents = contents.split('\n') +        private_data = '' +        for line in contents: +            if 'PRIVATE KEY' in line: +                continue +            if not line: +                continue +            private_data += line +        return private_data + +    @classmethod +    def __public_data_from_file(cls, file_name): +        with open(file_name, 'r') as file: +            contents = file.read() +        contents = contents.split(' ') +        # assert contents[0] == 'ssh-ed25519' +        public_data = contents[1].strip(' ') +        return public_data + +    @classmethod +    def signingKey(cls, private_ed25519_file): +        private_data = cls.__private_data_from_file(private_ed25519_file) +        signing_key = cls.__extract_signing_key(private_data) +        return signing_key + +    @classmethod +    def verifyKey(cls, public_ed25519_file): +        public_data = cls.__public_data_from_file(public_ed25519_file) +        verify_key = cls.__extract_verify_key(public_data) +        return verify_key + +    @classmethod +    def privateKey(cls, private_ed25519_file): +        signing_key = cls.signingKey(private_ed25519_file) +        return signing_key.to_curve25519_private_key() + +    @classmethod +    def publicKey(cls, public_ed25519_file): +        verify_key = cls.verifyKey(public_ed25519_file) +        return verify_key.to_curve25519_public_key() diff --git a/tools/sigsum-gensigkey.py b/tools/sigsum-gensigkey.py new file mode 100755 index 0000000..3c74108 --- /dev/null +++ b/tools/sigsum-gensigkey.py @@ -0,0 +1,21 @@ +#! /usr/bin/env python3 + +import sys +import os +from stat import * +from nacl.encoding import HexEncoder +from nacl.signing import SigningKey + +def generate_and_store_sigkey(fn): +    signing_key = SigningKey.generate() +    verify_key = signing_key.verify_key +    with open(fn, 'w') as f: +        os.chmod(f.fileno(), S_IRUSR) +        f.write(signing_key.encode(HexEncoder).decode('ascii') + '\n') +    print(verify_key.encode(HexEncoder).decode('ascii')) + +def main(): +    generate_and_store_sigkey(sys.argv[1]) + +if __name__ == '__main__': +    main() diff --git a/tools/sigsum-sign-leaf.py b/tools/sigsum-sign-leaf.py new file mode 100755 index 0000000..19d7f5c --- /dev/null +++ b/tools/sigsum-sign-leaf.py @@ -0,0 +1,61 @@ +#! /usr/bin/env python3 + +# Input: skeyfile shard_hint [checksum] +# Output: tree_leaf signature +# Example: echo foo | ./sigsum-sign-leaf.py nacl.sk 1633039200 +# be70f92465c27bf412008f26fa953d06899c53fa9867f40d9c0a1d657b188c9631699954728c719cf6b3819c1343c6e9e454cd9d519a9bf96dad3cf4cd959c0a + +import struct, sys, binascii +from base64 import b64encode +from nacl.signing import VerifyKey, SigningKey +from nacl.encoding import HexEncoder +from libsigntools import checksum_stdin, ssh_to_sign + +alg = 'sha512' + +def ssh_blob(vk, sig, namespace): +    vkdata = struct.pack('!I11sI32s', +                         11, bytes('ssh-ed25519', 'ascii'), +                         32, vk.encode()) +    assert(len(vkdata) == 51) + +    assert(len(sig) == 64) +    sigdata = struct.pack('!I11sI64s', +                          11, bytes('ssh-ed25519', 'ascii'), +                          64, sig) +    assert(len(sigdata) == 83) + +    s = "-----BEGIN SSH SIGNATURE-----\n" +    b = b64encode(struct.pack('!6sII51sI{}sII6sI83s'.format(len(namespace)), +                              b'SSHSIG', +                              1, +                              51, vkdata, +                              len(namespace), bytes(namespace, 'ascii'), +                              0, +                              6, bytes(alg, 'ascii'), +                              83, sigdata)).decode('ascii') +    while len(b) > 0: +        s += b[:72] + '\n' +        b = b[72:] +    s += "-----END SSH SIGNATURE-----\n" +    return s + +def main(): +    keyfile = sys.argv[1] +    shard_hint = int(sys.argv[2]) +    if len(sys.argv) > 3: +        checksum = bytes.fromhex(sys.argv[3]) +    else: +        checksum = checksum_stdin(hashalg=alg) + +    with open(keyfile, 'r') as f: +        signing_key = SigningKey(f.readline().strip(), encoder=HexEncoder) +    namespace = 'tree_leaf:v0:{}@sigsum.org'.format(shard_hint) +    signature = signing_key.sign(ssh_to_sign(namespace, alg, checksum)).signature + +    print(binascii.hexlify(signature).decode('ascii')) +    if False: +        print(ssh_blob(signing_key.verify_key, signature, namespace)) + +if __name__ == '__main__': +    main() diff --git a/tools/sigsum-verify-leaf.py b/tools/sigsum-verify-leaf.py new file mode 100755 index 0000000..d8a15fa --- /dev/null +++ b/tools/sigsum-verify-leaf.py @@ -0,0 +1,32 @@ +#! /usr/bin/env python3 + +# Input: vkeyfile shard_hint signature [checksum] +# Example: echo foo | ./sigsum-verify-leaf.py nacl.vk 0 $(echo foo | ./sigsum-sign-leaf.py nacl.sk 0) +# OK + +import sys +from nacl.signing import VerifyKey +from nacl.encoding import HexEncoder +from libsigntools import checksum_stdin, ssh_to_sign + +alg = 'sha512' + +def main(): +    keyfile = sys.argv[1] +    shard_hint = int(sys.argv[2]) +    sig = bytes.fromhex(sys.argv[3]) + +    with open(keyfile, 'r') as f: +        vkey = VerifyKey(f.readline().strip(), encoder=HexEncoder) +    if len(sys.argv) > 4: +        checksum = bytes.fromhex(sys.argv[4]) +    else: +        checksum = checksum_stdin(hashalg=alg) + +    namespace = 'tree_leaf:v0:{}@sigsum.org'.format(shard_hint) +    data = ssh_to_sign(namespace, alg, checksum) +    vkey.verify(data, signature=sig) +    print("OK") + +if __name__ == '__main__': +    main() diff --git a/tools/sshkey2nacl.py b/tools/sshkey2nacl.py new file mode 100755 index 0000000..c109a3c --- /dev/null +++ b/tools/sshkey2nacl.py @@ -0,0 +1,20 @@ +#! /usr/bin/env python3 + +import sys +from libsigntools import C25519 +from nacl.encoding import HexEncoder + +def main(): +    input_fn = sys.argv[1] +    output_fn = sys.argv[2] + +    sk = C25519.signingKey(input_fn) +    with open('{}.sk'.format(output_fn), 'w') as f: +        f.write(sk.encode(HexEncoder).decode('ascii')) + +    vk = C25519.verifyKey('{}.pub'.format(input_fn)) +    with open('{}.vk'.format(output_fn), 'w') as f: +        f.write(vk.encode(HexEncoder).decode('ascii')) + +if __name__ == '__main__': +  main() | 
