1909
|
1 import subprocess |
|
2 import tempfile |
|
3 |
|
4 import pytest |
|
5 |
|
6 keytypes = [ |
|
7 "rsa", "rsa-4096", |
|
8 "ed25519", |
|
9 "ecdsa", "ecdsa-256", "ecdsa-384", "ecdsa-521", |
|
10 "dss", |
|
11 ] |
|
12 |
|
13 def parse_keytype(kt): |
|
14 if '-' in kt: |
|
15 return kt.split('-') |
|
16 else: |
|
17 return (kt, None) |
|
18 |
|
19 @pytest.mark.parametrize("keytype", keytypes) |
|
20 @pytest.mark.parametrize("keyformat", [None, "PEM"]) |
|
21 def test_from_openssh(request, tmp_path, keytype, keyformat): |
|
22 """ |
|
23 Convert OpenSSH to Dropbear format, |
|
24 PEM and OpenSSH internal |
|
25 """ |
|
26 opt = request.config.option |
|
27 kt, keybits = parse_keytype(keytype) |
|
28 |
|
29 if kt == 'dss' and keyformat is None: |
|
30 pytest.xfail("dss doesn't support openssh format") |
|
31 |
|
32 if kt == 'ecdsa' and keyformat is None: |
|
33 pytest.skip("ecdsa doesn't support openssh format yet") |
|
34 |
|
35 os_kt = kt |
|
36 if os_kt == 'dss': |
|
37 # OpenSSH calls it 'dsa', Dropbear calls it 'dss' |
|
38 os_kt = 'dsa' |
|
39 |
|
40 os_key = tmp_path / 'oskey1' |
|
41 db_key = tmp_path / 'dbkey1' |
|
42 |
|
43 # Generate an OpenSSH key |
|
44 args = [ |
|
45 opt.ssh_keygen, |
|
46 '-f', os_key, |
|
47 '-t', os_kt, |
|
48 '-N', '', # no password |
|
49 ] |
|
50 if keybits is not None: |
|
51 args += ['-b', keybits] |
|
52 if keyformat: |
|
53 args += ['-m', keyformat] |
|
54 p = subprocess.run(args, check=True) |
|
55 |
|
56 # Convert to dropbear format |
|
57 args = [ |
|
58 opt.dropbearconvert, |
|
59 'openssh', 'dropbear', |
|
60 os_key, db_key, |
|
61 ] |
|
62 p = subprocess.run(args, check=True) |
|
63 |
|
64 # Compare pubkeys |
|
65 args = [ |
|
66 opt.dropbearkey, |
|
67 '-f', db_key, |
|
68 '-y' |
|
69 ] |
|
70 p = subprocess.run(args, check=True, stdout=subprocess.PIPE, text=True) |
|
71 db_pubkey = p.stdout.splitlines()[1].strip() |
|
72 os_pubkey = os_key.with_suffix('.pub').open().read().strip() |
|
73 # we compare the whole key including comment since it currently matches |
|
74 assert db_pubkey == os_pubkey |
|
75 |
|
76 @pytest.mark.parametrize("keytype", keytypes) |
|
77 def test_roundtrip(request, tmp_path, keytype): |
|
78 """ |
|
79 Dropbear's private key format is deterministic so |
|
80 we can compare round trip conversion. (OpenSSH's |
|
81 format has more variable comments and other fields). |
|
82 """ |
|
83 opt = request.config.option |
|
84 kt, keybits = parse_keytype(keytype) |
|
85 |
|
86 os_key = tmp_path / 'oskey1' |
|
87 db_key1 = tmp_path / 'dbkey1' |
|
88 db_key2 = tmp_path / 'dbkey2' |
|
89 |
|
90 # generate a key |
|
91 args = [ |
|
92 opt.dropbearkey, |
|
93 '-t', kt, |
|
94 '-f', db_key1, |
|
95 ] |
|
96 if keybits is not None: |
|
97 args += ['-s', keybits] |
|
98 p = subprocess.run(args, check=True) |
|
99 |
|
100 # convert to openssh |
|
101 args = [ |
|
102 opt.dropbearconvert, |
|
103 'dropbear', 'openssh', |
|
104 db_key1, os_key, |
|
105 ] |
|
106 p = subprocess.run(args, check=True) |
|
107 |
|
108 # Check ssh-keygen can read it |
|
109 args = [ |
|
110 opt.ssh_keygen, |
|
111 '-f', os_key, |
|
112 '-y', |
|
113 ] |
|
114 p = subprocess.run(args, check=True, text=True, stdout=subprocess.PIPE) |
|
115 os_pubkey = p.stdout.strip() |
|
116 |
|
117 # Compare public keys |
|
118 args = [ |
|
119 opt.dropbearkey, |
|
120 '-f', db_key1, |
|
121 '-y', |
|
122 ] |
|
123 p = subprocess.run(args, check=True, text=True, stdout=subprocess.PIPE) |
|
124 db_pubkey = p.stdout.splitlines()[1].strip() |
|
125 # comment may differ |
|
126 db_pubkey = db_pubkey.split(' ')[:2] |
|
127 os_pubkey = os_pubkey.split(' ')[:2] |
|
128 assert db_pubkey == os_pubkey |
|
129 |
|
130 # convert back to dropbear |
|
131 args = [ |
|
132 opt.dropbearconvert, |
|
133 'openssh', 'dropbear', |
|
134 os_key, db_key2, |
|
135 ] |
|
136 p = subprocess.run(args, check=True) |
|
137 # check the round trip is identical |
|
138 assert db_key1.open('rb').read() == db_key2.open('rb').read() |