File: test_sshsig.py

package info (click to toggle)
python-sshsig 0.2.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 236 kB
  • sloc: python: 901; sh: 53; makefile: 9
file content (176 lines) | stat: -rw-r--r-- 5,916 bytes parent folder | download
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# (c) 2024 E. Castedo Ellerman <castedo@castedo.com>
# Released under the MIT License (https://spdx.org/licenses/MIT)
# fmt: off

from pathlib import Path
from unittest import TestCase

from sshsig import sshsig
from sshsig.allowed_signers import load_for_git_allowed_signers_file

from compat import ssh_keygen


TESTDATA_DIR = Path(__file__).parent.parent / "testdata"
SSHSIG_CASES = list((TESTDATA_DIR / "sshsig").iterdir())


msg_sig_pair_of_commit = (
    """\
tree fd7187a49a7b26eb36d782d34c672245b94b2e30
parent b0162c139e4ea2e4783402de26617c912eef1e19
author Castedo Ellerman <castedo@castedo.com> 1729263976 -0400
committer Castedo Ellerman <castedo@castedo.com> 1729263976 -0400

use vite-plugin-static-copy in doc example of Vite
""",
    """\
-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAghB1C63jrmh3eWRXJVbrTfw9wP/
BIZf/aKPdFxBlMCq0AAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
AAAAQJoJUglNVFSaWhnbCl4WImCRLUEo6ymS9WBVOqpoH4kJVgIAMmCIAq9yDOL4fGYmJ0
GsF7btQ1wFf6sbMzq9nwA=
-----END SSH SIGNATURE-----
""",
)

### The following test case produced by
### echo Hola Mundo > hola.txt
### ssh-keygen -Y sign -f test_sign_key -n git hola.txt
### using test_sign_key in ../testdata

msg_sig_pair_hola_mundo = (
    """\
Hola Mundo
""",
    """\
-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgZz+kZMT7JBP0t1l1HQ0K8CduhZ
XTBP/l3sXkZMqTtAkAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
AAAAQDFSdQINV271MZ5VwFecGD8oJRob5Nb04r06oVVVCflwgbDLcezjmHJQo41/H3/HXj
pQWO8AJXrx7gcPAcCFGQ0=
-----END SSH SIGNATURE-----
""",
)


### The following test case produced by
### cd ../testdata
### echo Hola Mundo > hola.txt
### chmod 600 rsa_key
### ssh-keygen -Y sign -f rsa_key -n git hola.txt

msg_sig_pair_rsa = (
    """\
Hola Mundo
""",
    """\
-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAJXDk57H4TBAsZIkZpI7WS
kyBohbjgBnHNGzOA/pbCwXcYVJQmqgrBSdLv9yWD1JeL51c7ZTAuzZ5IFVe03pg/MaGKdX
5uD7qTfg268lDVrekMUQTvEnDfLRpj0nWT6QMQu2Ux69EyXtMo6dEupFe6gIJgmh/fzJxD
g8tr+YeYk67gxgI6zTNqS27UJGvOSL+aHeGfM4/XvmHnxGD0aP89ab1Aii6aS1e+ReK1Xd
oQfuyaEy9D3T80ggLYwzjpLEStaJ9HYDsDtPPugbeZJPWrdMvt9NDuw+uRCnrgb4//jonw
VxI1fG5pgjy7vD5ZD9SRU7tepebI0tnvaitDZT465e9Bcc5R5ReSU0KGCgCH6NTw+nu8VO
d4IM/ncBym5llF2yqT2Tq26gpIDPqbATpoHrnsYCtyTiWKggWk/2Es+ibJjoUKNedv1bBP
aorffM6nqWAC3eGzXDgvIyKeiLSzNG234d+mkzqLB4hruco8L1FcYW7OW3sqWfI6aoomip
5QAAAANnaXQAAAAAAAAABnNoYTUxMgAAAZQAAAAMcnNhLXNoYTItNTEyAAABgIY1pbSqXK
FJ8/9GaFAuCkcSJ7YwW1MuidrWXqsDH4IE4j10u3LnQhGd8qKDH9dm93sP15IRuRnbJXg3
qVVFrnxS//EO8BmXHDekLo8yp11CqzYfRrboIvzMRufLdZ8Kt7d+p+jvJuDqN9WHDSyifI
D8o7X4tenND+QtELzi2aNrqaAtJYlNBQzxLrUqXSMHdDTDqwkuQaBWCHSmykxi84F9qb6q
K5ogklBOJKekyMFXxgPwu+uBXMkCo18QIlUS+J76H4hmJVsIrHOZZntfLOa1dDU8lRZmJP
wzCztwzaShx4OTASnJ/wKXtqXFvbz+asPnCqngoYO1aWDg5OzvEFG3DMUpTKRthMuYUWAB
TuWmFFLbjPQf1f9r1+/bwquZxapoKNVJfg7IYTfKFI9zKve3z3NLZ52+7eoqYaZsuzjN/a
4r4pfG9OMr60XApWaJkTO0K0RjrCKy9/bsXz5pJTCM5Tm4E3xLQW70GbTVfceDX713lnvx
8ZPva0b19v/BgA==
-----END SSH SIGNATURE-----
""",
)


msg_sig_pair_cases = [
    msg_sig_pair_of_commit,
    msg_sig_pair_hola_mundo,
    msg_sig_pair_rsa,
]


crazy_ascii = "Nobody expects the Spanish ..."
crazy_unicode = "Nobody expects 🥘💃🐂 ..."


def good_check_novalidate(message: str, signature: str, namespace: str = "git") -> bool:
    try:
        ssh_keygen.check_signature(message, signature, namespace)
        return True
    except sshsig.InvalidSignature:
        return False

class SshKeygenCheckNoValidate(TestCase):
    def test_good_msg_sig_pairs(self):
        for case in msg_sig_pair_cases:
            with self.subTest(case=case):
                (msg, sig) = case
                self.assertTrue(good_check_novalidate(msg, sig))

    def test_hola_mundo(self):
        (msg, sig) = msg_sig_pair_hola_mundo
        self.assertTrue(good_check_novalidate(msg, sig))

    def test_reject_mixed_msg_sig_pairs(self):
        (msg1, sig1) = msg_sig_pair_of_commit
        (msg2, sig2) = msg_sig_pair_hola_mundo
        self.assertFalse(good_check_novalidate(msg1, sig2))
        self.assertFalse(good_check_novalidate(msg2, sig1))

    def test_reject_subcmd_check_novalidate(self):
        (msg, sig) = msg_sig_pair_of_commit

        self.assertFalse(good_check_novalidate(msg, crazy_ascii))
        self.assertFalse(good_check_novalidate(msg, crazy_unicode))
        self.assertFalse(good_check_novalidate(crazy_ascii, sig))
        self.assertFalse(good_check_novalidate(crazy_unicode, sig))

        # the signature was signed with namespace "git"
        self.assertFalse(good_check_novalidate(msg, sig, "not-git"))


def good_verify(message: str, signers, signature: str) -> bool:
    try:
        ssh_keygen.verify(message, signature, signers)
        return True
    except sshsig.InvalidSignature:
        return False

class VerifyTests(TestCase):

    def verify(self, case):
        with open(case / "message", "rb") as f:
            msg = f.read()
        with open(case / "message.sig") as f:
            armored = f.read()
        signers = load_for_git_allowed_signers_file(case / "allowed_signers")

        self.assertTrue(good_verify(msg, signers, armored))
        bad = b"Corrupt" + msg
        self.assertFalse(good_verify(bad, signers, armored))

        nobody = load_for_git_allowed_signers_file(
            TESTDATA_DIR / "only_lost_allowed_signer"
        )
        self.assertFalse(good_verify(msg, nobody, armored))

    def test_case_0(self):
        self.verify(SSHSIG_CASES[0])


class ParseSignature(TestCase):

    def test_ascii_armor(self):
        for case in msg_sig_pair_cases:
            armored = case[1]
            with self.subTest(armored=armored):
                buf = sshsig.ssh_dearmor_sshsig(armored)
                sig = sshsig.SshsigSignature(buf)
                buf2 = sshsig.ssh_dearmor_sshsig(sig.to_armored())
                self.assertEqual(buf, buf2)