aboutsummaryrefslogtreecommitdiff
path: root/tools/libsigntools.py
blob: a3f0bccca6a73ec18dbcab1026647849549aa5d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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()