Coverage for tests/__init__.py: 100.000%

1# SPDX-FileCopyrightText: 2024 Marco Ricci <m@the13thletter.info>

2#

3# SPDX-License-Identifier: MIT

4

5 from __future__ import annotations

6

7 import base64

8 from collections.abc import Iterator, Mapping

9 import contextlib

10 import json

11 import os

12 from typing import TYPE_CHECKING

13 from typing_extensions import Any, TypedDict

14

15 import click.testing

16 import derivepassphrase

17 import derivepassphrase.cli

18 import derivepassphrase.types

19 import pytest

20 import ssh_agent_client

21 import ssh_agent_client.types

22

23 __all__ = ()

24

25 if TYPE_CHECKING:

26 class SSHTestKey(TypedDict):

27 private_key: bytes

28 public_key: bytes | str

29 public_key_data: bytes

30 expected_signature: bytes | None

31 derived_passphrase: bytes | str | None

32

33 SUPPORTED_KEYS: Mapping[str, SSHTestKey] = {

34 'ed25519': {

35 'private_key': rb'''-----BEGIN OPENSSH PRIVATE KEY-----

36 b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW

37 QyNTUxOQAAACCBeIFoJtYCSF8P/zJIb+TBMIncHGpFBgnpCQ/7whJpdgAAAKDweO7H8Hju

38 xwAAAAtzc2gtZWQyNTUxOQAAACCBeIFoJtYCSF8P/zJIb+TBMIncHGpFBgnpCQ/7whJpdg

39 AAAEAbM/A869nkWZbe2tp3Dm/L6gitvmpH/aRZt8sBII3ExYF4gWgm1gJIXw//Mkhv5MEw

40 idwcakUGCekJD/vCEml2AAAAG3Rlc3Qga2V5IHdpdGhvdXQgcGFzc3BocmFzZQEC

41-----END OPENSSH PRIVATE KEY-----

42''',

43 'public_key': rb'''ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIF4gWgm1gJIXw//Mkhv5MEwidwcakUGCekJD/vCEml2 test key without passphrase

44''',

45 'public_key_data': bytes.fromhex('''

46 00 00 00 0b 73 73 68 2d 65 64 32 35 35 31 39

47 00 00 00 20

48 81 78 81 68 26 d6 02 48 5f 0f ff 32 48 6f e4 c1

49 30 89 dc 1c 6a 45 06 09 e9 09 0f fb c2 12 69 76

50'''),

51 'expected_signature': bytes.fromhex('''

52 00 00 00 0b 73 73 68 2d 65 64 32 35 35 31 39

53 00 00 00 40

54 f0 98 19 80 6c 1a 97 d5 26 03 6e cc e3 65 8f 86

55 66 07 13 19 13 09 21 33 33 f9 e4 36 53 1d af fd

56 0d 08 1f ec f8 73 9b 8c 5f 55 39 16 7c 53 54 2c

57 1e 52 bb 30 ed 7f 89 e2 2f 69 51 55 d8 9e a6 02

58 '''),

59 'derived_passphrase': rb'8JgZgGwal9UmA27M42WPhmYHExkTCSEzM/nkNlMdr/0NCB/s+HObjF9VORZ8U1QsHlK7MO1/ieIvaVFV2J6mAg==',

60 },

61 # Currently only supported by PuTTY (which is deficient in other

62 # niceties of the SSH agent and the agent's client).

63 'ed448': {

64 'private_key': rb'''-----BEGIN OPENSSH PRIVATE KEY-----

65 b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAAAlz

66 c2gtZWQ0NDgAAAA54vZy009Wu8wExjvEb3hqtLz1GO/+d5vmGUbErWQ4AUO9mYLT

67 zHJHc2m4s+yWzP29Cc3EcxizLG8AAAAA8BdhfCcXYXwnAAAACXNzaC1lZDQ0OAAA

68 ADni9nLTT1a7zATGO8RveGq0vPUY7/53m+YZRsStZDgBQ72ZgtPMckdzabiz7JbM

69/b0JzcRzGLMsbwAAAAByM7GIMRvWJB3YD6SIpAF2uudX4ozZe0X917wPwiBrs373

70 9TM1n94Nib6hrxGNmCk2iBQDe2KALPgA4vZy009Wu8wExjvEb3hqtLz1GO/+d5vm

71 GUbErWQ4AUO9mYLTzHJHc2m4s+yWzP29Cc3EcxizLG8AAAAAG3Rlc3Qga2V5IHdp

72 dGhvdXQgcGFzc3BocmFzZQECAwQFBgcICQ==

73-----END OPENSSH PRIVATE KEY-----

74''',

75 'public_key': rb'''ssh-ed448 AAAACXNzaC1lZDQ0OAAAADni9nLTT1a7zATGO8RveGq0vPUY7/53m+YZRsStZDgBQ72ZgtPMckdzabiz7JbM/b0JzcRzGLMsbwA= test key without passphrase

76''',

77 'public_key_data': bytes.fromhex('''

78 00 00 00 09 73 73 68 2d 65 64 34 34 38

79 00 00 00 39

80 e2 f6 72 d3 4f 56 bb cc 04 c6 3b c4 6f 78 6a b4

81 bc f5 18 ef fe 77 9b e6 19 46 c4 ad 64 38 01 43

82 bd 99 82 d3 cc 72 47 73 69 b8 b3 ec 96 cc fd bd

83 09 cd c4 73 18 b3 2c 6f 00

84 '''),

85

86 'expected_signature': bytes.fromhex('''

87 00 00 00 09 73 73 68 2d 65 64 34 34 38

88 00 00 00 72 06 86

89 f4 64 a4 a6 ba d9 c3 22 c4 93 49 99 fc 11 de 67

90 97 08 f2 d8 b7 3c 2c 13 e7 c5 1c 1e 92 a6 0e d8

91 2f 6d 81 03 82 00 e3 72 e4 32 6d 72 d2 6d 32 84

92 3f cc a9 1e 57 2c 00 9a b3 99 de 45 da ce 2e d1

93 db e5 89 f3 35 be 24 58 90 c6 ca 04 f0 db 88 80

94 db bd 77 7c 80 20 7f 3a 48 61 f6 1f ae a9 5e 53

95 7b e0 9d 93 1e ea dc eb b5 cd 56 4c ea 8f 08 00

96 '''),

97 'derived_passphrase': rb'Bob0ZKSmutnDIsSTSZn8Ed5nlwjy2Lc8LBPnxRwekqYO2C9tgQOCAONy5DJtctJtMoQ/zKkeVywAmrOZ3kXazi7R2+WJ8zW+JFiQxsoE8NuIgNu9d3yAIH86SGH2H66pXlN74J2THurc67XNVkzqjwgA',

98 },

99 'rsa': {

100 'private_key': rb'''-----BEGIN OPENSSH PRIVATE KEY-----

101 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

137-----END OPENSSH PRIVATE KEY-----

138''',

139 'public_key': rb'''ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxoe7pezhxWy4NI0mUwKqg9WCYOAS+IjxN9eYcqpfcmQiojcuy9XsiN/xYJ1O94SrsKS5mEia2xHnYA4RUChTyYNcM2v6cnnBQ/N/VQhpGMN7SVxdbhKUXTWFCwbjBgO6rGyHB6WtoH8vd7TOEPt+NgcXwhsWyoaUUdYTA62V+GF9vEmxMaC4ubgDz+B0QkPnauSoNxmkhcIe0lsLNb1pClZyz88PDnKXCX/d0HuN/HJ+sbPg7dCvOyqFYSyKn3uY6bCXqoIdurxXzH3O7z0P8f5sbmKOrGGKNuNxVRbeVl/D/3uDL0nqsbfUc1qvkfwbJwtMXC4IV6kOZMSk2BAsqh7x48gQ+rhYeEVSi8F3CWs4HJQoqrGt7K9a3mCSlMBHP70u3w6ME7eumoryxlUofewTd17ZEkzdX08l2ZlKzZvwQUrc+xQZ2Uw8z2mfW6Ti4gi0pYGaig7Ke4PwuXpo/C5YAWfeXycsvJZ2uaYRjMdZeJGNAnHLUGLkBscw5aI8= test key without passphrase

140''',

141 'public_key_data': bytes.fromhex('''

142 00 00 00 07 73 73 68 2d 72 73 61

143 00 00 00 03 01 00 01

144 00 00 01 81 00

145 b1 a1 ee e9 7b 38 71 5b 2e 0d 23 49 94 c0 aa a0

146 f5 60 98 38 04 be 22 3c 4d f5 e6 1c aa 97 dc 99

147 08 a8 8d cb b2 f5 7b 22 37 fc 58 27 53 bd e1 2a

148 ec 29 2e 66 12 26 b6 c4 79 d8 03 84 54 0a 14 f2

149 60 d7 0c da fe 9c 9e 70 50 fc df d5 42 1a 46 30

150 de d2 57 17 5b 84 a5 17 4d 61 42 c1 b8 c1 80 ee

151 ab 1b 21 c1 e9 6b 68 1f cb dd ed 33 84 3e df 8d

152 81 c5 f0 86 c5 b2 a1 a5 14 75 84 c0 eb 65 7e 18

153 5f 6f 12 6c 4c 68 2e 2e 6e 00 f3 f8 1d 10 90 f9

154 da b9 2a 0d c6 69 21 70 87 b4 96 c2 cd 6f 5a 42

155 95 9c b3 f3 c3 c3 9c a5 c2 5f f7 74 1e e3 7f 1c

156 9f ac 6c f8 3b 74 2b ce ca a1 58 4b 22 a7 de e6

157 3a 6c 25 ea a0 87 6e af 15 f3 1f 73 bb cf 43 fc

158 7f 9b 1b 98 a3 ab 18 62 8d b8 dc 55 45 b7 95 97

159 f0 ff de e0 cb d2 7a ac 6d f5 1c d6 ab e4 7f 06

160 c9 c2 d3 17 0b 82 15 ea 43 99 31 29 36 04 0b 2a

161 87 bc 78 f2 04 3e ae 16 1e 11 54 a2 f0 5d c2 5a

162 ce 07 25 0a 2a ac 6b 7b 2b d6 b7 98 24 a5 30 11

163 cf ef 4b b7 c3 a3 04 ed eb a6 a2 bc b1 95 4a 1f

164 7b 04 dd d7 b6 44 93 37 57 d3 c9 76 66 52 b3 66

165 fc 10 52 b7 3e c5 06 76 53 0f 33 da 67 d6 e9 38

166 b8 82 2d 29 60 66 a2 83 b2 9e e0 fc 2e 5e 9a 3f

167 0b 96 00 59 f7 97 c9 cb 2f 25 9d ae 69 84 63 31

168 d6 5e 24 63 40 9c 72 d4 18 b9 01 b1 cc 39 68 8f

169'''),

170 'expected_signature': bytes.fromhex('''

171 00 00 00 07 73 73 68 2d 72 73 61

172 00 00 01 80

173 a2 10 7c 2e f6 bb 53 a8 74 2a a1 19 99 ad 81 be

174 79 9c ed d6 9d 09 4e 6e c5 18 48 33 90 77 99 68

175 f7 9e 03 5a cd 4e 18 eb 89 7d 85 a2 ee ae 4a 92

176 f6 6f ce b9 fe 86 7f 2a 6b 31 da 6e 1a fe a2 a5

177 88 b8 44 7f a1 76 73 b3 ec 75 b5 d0 a6 b9 15 97

178 65 09 13 7d 94 21 d1 fb 5d 0f 8b 23 04 77 c2 c3

179 55 22 b1 a0 09 8a f5 38 2a d6 7f 1b 87 29 a0 25

180 d3 25 6f cb 64 61 07 98 dc 14 c5 84 f8 92 24 5e

181 50 11 6b 49 e5 f0 cc 29 cb 29 a9 19 d8 a7 71 1f

182 91 0b 05 b1 01 4b c2 5f 00 a5 b6 21 bf f8 2c 9d

183 67 9b 47 3b 0a 49 6b 79 2d fc 1d ec 0c b0 e5 27

184 22 d5 a9 f8 d3 c3 f9 df 48 68 e9 fb ef 3c dc 26

185 bf cf ea 29 43 01 a6 e3 c5 51 95 f4 66 6d 8a 55

186 e2 47 ec e8 30 45 4c ae 47 e7 c9 a4 21 8b 64 ba

187 b6 88 f6 21 f8 73 b9 cb 11 a1 78 75 92 c6 5a e5

188 64 fe ed 42 d9 95 99 e6 2b 6f 3c 16 3c 28 74 a4

189 72 2f 0d 3f 2c 33 67 aa 35 19 8e e7 b5 11 2f b3

190 f7 6a c5 02 e2 6f a3 42 e3 62 19 99 03 ea a5 20

191 e7 a1 e3 bc c8 06 a3 b5 7c d6 76 5d df 6f 60 46

192 83 2a 08 00 d6 d3 d9 a4 c1 41 8c f8 60 56 45 81

193 da 3b a2 16 1f 9e 4e 75 83 17 da c3 53 c3 3e 19

194 a4 1b bc d2 29 b8 78 61 2b 78 e6 b1 52 b0 d5 ec

195 de 69 2c 48 62 d9 fd d1 9b 6b b0 49 db d3 ff 38

196 e7 10 d9 2d ce 9f 0d 5e 09 7b 37 d2 7b c3 bf ce

197'''),

198 'derived_passphrase': rb'ohB8Lva7U6h0KqEZma2Bvnmc7dadCU5uxRhIM5B3mWj3ngNazU4Y64l9haLurkqS9m/Ouf6GfyprMdpuGv6ipYi4RH+hdnOz7HW10Ka5FZdlCRN9lCHR+10PiyMEd8LDVSKxoAmK9Tgq1n8bhymgJdMlb8tkYQeY3BTFhPiSJF5QEWtJ5fDMKcspqRnYp3EfkQsFsQFLwl8ApbYhv/gsnWebRzsKSWt5Lfwd7Ayw5Sci1an408P530ho6fvvPNwmv8/qKUMBpuPFUZX0Zm2KVeJH7OgwRUyuR+fJpCGLZLq2iPYh+HO5yxGheHWSxlrlZP7tQtmVmeYrbzwWPCh0pHIvDT8sM2eqNRmO57URL7P3asUC4m+jQuNiGZkD6qUg56HjvMgGo7V81nZd329gRoMqCADW09mkwUGM+GBWRYHaO6IWH55OdYMX2sNTwz4ZpBu80im4eGEreOaxUrDV7N5pLEhi2f3Rm2uwSdvT/zjnENktzp8NXgl7N9J7w7/O',

199 },

200}

201

202 UNSUITABLE_KEYS: Mapping[str, SSHTestKey] = {

203 'dsa1024': {

204 'private_key': rb'''-----BEGIN OPENSSH PRIVATE KEY-----

205 b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsQAAAAdzc2gtZH

206 NzAAAAgQC7KAZXqBGNVLBQPrcMYAoNW54BhD8aIhe7BDWYzJcsaMt72VKSkguZ8+XR7nRa

207 0C/ZsBi+uJp0dpxy9ZMTOWX4u5YPMeQcXEdGExZIfimGqSOAsy6fCld2IfJZJZExcCmhe9

208 Ssjsd3YSAPJRluOXFQc95MZoR5hMwlIDD8QzrE7QAAABUA99nOZOgd7aHMVGoXpUEBcn7H

209 ossAAACALr2Ag3hxM3rKdxzVUw8fX0VVPXO+3+Kr8hGe0Kc/7NwVaBVL1GQ8fenBuWynpA

210 UbH0wo3h1wkB/8hX6p+S8cnu5rIBlUuVNwLw/bIYohK98LfqTYK/V+g6KD+8m34wvEiXZm

211 qywY54n2bksch1Nqvj/tNpLzExSx/XS0kSM1aigAAACAbQNRPcVEuGDrEcf+xg5tgAejPX

212 BPXr/Jss+Chk64km3mirMYjAWyWYtVcgT+7hOYxtYRin8LyMLqKRmqa0Q5UrvDfChgLhvs

213 G9YSb/Mpw5qm8PiHSafwhkaz/te3+8hKogqoe7sd+tCF06IpJr5k70ACiNtRGqssNF8Elr

214 l1efYAAAH4swlfVrMJX1YAAAAHc3NoLWRzcwAAAIEAuygGV6gRjVSwUD63DGAKDVueAYQ/

215 GiIXuwQ1mMyXLGjLe9lSkpILmfPl0e50WtAv2bAYvriadHaccvWTEzll+LuWDzHkHFxHRh

216 MWSH4phqkjgLMunwpXdiHyWSWRMXApoXvUrI7Hd2EgDyUZbjlxUHPeTGaEeYTMJSAw/EM6

217 xO0AAAAVAPfZzmToHe2hzFRqF6VBAXJ+x6LLAAAAgC69gIN4cTN6yncc1VMPH19FVT1zvt

218/iq/IRntCnP+zcFWgVS9RkPH3pwblsp6QFGx9MKN4dcJAf/IV+qfkvHJ7uayAZVLlTcC8P

219 2yGKISvfC36k2Cv1foOig/vJt+MLxIl2ZqssGOeJ9m5LHIdTar4/7TaS8xMUsf10tJEjNW

220 ooAAAAgG0DUT3FRLhg6xHH/sYObYAHoz1wT16/ybLPgoZOuJJt5oqzGIwFslmLVXIE/u4T

221 mMbWEYp/C8jC6ikZqmtEOVK7w3woYC4b7BvWEm/zKcOapvD4h0mn8IZGs/7Xt/vISqIKqH

222 u7HfrQhdOiKSa+ZO9AAojbURqrLDRfBJa5dXn2AAAAFQDJHfenj4EJ9WkehpdJatPBlqCW

223 0gAAABt0ZXN0IGtleSB3aXRob3V0IHBhc3NwaHJhc2UBAgMEBQYH

224-----END OPENSSH PRIVATE KEY-----

225''',

226 'public_key': rb'''ssh-dss AAAAB3NzaC1kc3MAAACBALsoBleoEY1UsFA+twxgCg1bngGEPxoiF7sENZjMlyxoy3vZUpKSC5nz5dHudFrQL9mwGL64mnR2nHL1kxM5Zfi7lg8x5BxcR0YTFkh+KYapI4CzLp8KV3Yh8lklkTFwKaF71KyOx3dhIA8lGW45cVBz3kxmhHmEzCUgMPxDOsTtAAAAFQD32c5k6B3tocxUahelQQFyfseiywAAAIAuvYCDeHEzesp3HNVTDx9fRVU9c77f4qvyEZ7Qpz/s3BVoFUvUZDx96cG5bKekBRsfTCjeHXCQH/yFfqn5Lxye7msgGVS5U3AvD9shiiEr3wt+pNgr9X6DooP7ybfjC8SJdmarLBjnifZuSxyHU2q+P+02kvMTFLH9dLSRIzVqKAAAAIBtA1E9xUS4YOsRx/7GDm2AB6M9cE9ev8myz4KGTriSbeaKsxiMBbJZi1VyBP7uE5jG1hGKfwvIwuopGaprRDlSu8N8KGAuG+wb1hJv8ynDmqbw+IdJp/CGRrP+17f7yEqiCqh7ux360IXToikmvmTvQAKI21Eaqyw0XwSWuXV59g== test key without passphrase

227''',

228 'public_key_data': bytes.fromhex('''

229 00 00 00 07 73 73 68 2d 64 73 73

230 00 00 00 81 00

231 bb 28 06 57 a8 11 8d 54 b0 50 3e b7 0c 60 0a 0d

232 5b 9e 01 84 3f 1a 22 17 bb 04 35 98 cc 97 2c 68

233 cb 7b d9 52 92 92 0b 99 f3 e5 d1 ee 74 5a d0 2f

234 d9 b0 18 be b8 9a 74 76 9c 72 f5 93 13 39 65 f8

235 bb 96 0f 31 e4 1c 5c 47 46 13 16 48 7e 29 86 a9

236 23 80 b3 2e 9f 0a 57 76 21 f2 59 25 91 31 70 29

237 a1 7b d4 ac 8e c7 77 61 20 0f 25 19 6e 39 71 50

238 73 de 4c 66 84 79 84 cc 25 20 30 fc 43 3a c4 ed

239 00 00 00 15 00 f7 d9 ce 64

240 e8 1d ed a1 cc 54 6a 17 a5 41 01 72 7e c7 a2 cb

241 00 00 00 80

242 2e bd 80 83 78 71 33 7a ca 77 1c d5 53 0f 1f 5f

243 45 55 3d 73 be df e2 ab f2 11 9e d0 a7 3f ec dc

244 15 68 15 4b d4 64 3c 7d e9 c1 b9 6c a7 a4 05 1b

245 1f 4c 28 de 1d 70 90 1f fc 85 7e a9 f9 2f 1c 9e

246 ee 6b 20 19 54 b9 53 70 2f 0f db 21 8a 21 2b df

247 0b 7e a4 d8 2b f5 7e 83 a2 83 fb c9 b7 e3 0b c4

248 89 76 66 ab 2c 18 e7 89 f6 6e 4b 1c 87 53 6a be

249 3f ed 36 92 f3 13 14 b1 fd 74 b4 91 23 35 6a 28

250 00 00 00 80

251 6d 03 51 3d c5 44 b8 60 eb 11 c7 fe c6 0e 6d 80

252 07 a3 3d 70 4f 5e bf c9 b2 cf 82 86 4e b8 92 6d

253 e6 8a b3 18 8c 05 b2 59 8b 55 72 04 fe ee 13 98

254 c6 d6 11 8a 7f 0b c8 c2 ea 29 19 aa 6b 44 39 52

255 bb c3 7c 28 60 2e 1b ec 1b d6 12 6f f3 29 c3 9a

256 a6 f0 f8 87 49 a7 f0 86 46 b3 fe d7 b7 fb c8 4a

257 a2 0a a8 7b bb 1d fa d0 85 d3 a2 29 26 be 64 ef

258 40 02 88 db 51 1a ab 2c 34 5f 04 96 b9 75 79 f6

259'''),

260 'expected_signature': None,

261 'derived_passphrase': None,

262 },

263 'ecdsa256': {

264 'private_key': rb'''-----BEGIN OPENSSH PRIVATE KEY-----

265 b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS

266 1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTLbU0zDwsk2Dvp+VYIrsNVf5gWwz2S

267 3SZ8TbxiQRkpnGSVqyIoHJOJc+NQItAa7xlJ/8Z6gfz57Z3apUkaMJm6AAAAuKeY+YinmP

268 mIAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMttTTMPCyTYO+n5

269 Vgiuw1V/mBbDPZLdJnxNvGJBGSmcZJWrIigck4lz41Ai0BrvGUn/xnqB/PntndqlSRowmb

270 oAAAAhAKIl/3n0pKVIxpZkXTGtii782Qr4yIcvHdpxjO/QsIqKAAAAG3Rlc3Qga2V5IHdp

271 dGhvdXQgcGFzc3BocmFzZQECAwQ=

272-----END OPENSSH PRIVATE KEY-----

273''',

274 'public_key': rb'''ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMttTTMPCyTYO+n5Vgiuw1V/mBbDPZLdJnxNvGJBGSmcZJWrIigck4lz41Ai0BrvGUn/xnqB/PntndqlSRowmbo= test key without passphrase

275''',

276 'public_key_data': bytes.fromhex('''

277 00 00 00 13 65 63 64 73 61 2d 73 68 61 32 2d 6e

278 69 73 74 70 32 35 36

279 00 00 00 08 6e 69 73 74 70 32 35 36

280 00 00 00 41 04

281 cb 6d 4d 33 0f 0b 24 d8 3b e9 f9 56 08 ae c3 55

282 7f 98 16 c3 3d 92 dd 26 7c 4d bc 62 41 19 29 9c

283 64 95 ab 22 28 1c 93 89 73 e3 50 22 d0 1a ef 19

284 49 ff c6 7a 81 fc f9 ed 9d da a5 49 1a 30 99 ba

285'''),

286 'expected_signature': None,

287 'derived_passphrase': None,

288 },

289 'ecdsa384': {

290 'private_key': rb'''-----BEGIN OPENSSH PRIVATE KEY-----

291 b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS

292 1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQSgkOjkAvq7v5vHuj3KBL4/EAWcn5hZ

293 DyKcbyV0eBMGFq7hKXQlZqIahLVqeMR0QqmkxNJ2rly2VHcXneq3vZ+9fIsWCOdYk5WP3N

294 ZPzv911Xn7wbEkC7QndD5zKlm4pBUAAADomhj+IZoY/iEAAAATZWNkc2Etc2hhMi1uaXN0

295 cDM4NAAAAAhuaXN0cDM4NAAAAGEEoJDo5AL6u7+bx7o9ygS+PxAFnJ+YWQ8inG8ldHgTBh

296 au4Sl0JWaiGoS1anjEdEKppMTSdq5ctlR3F53qt72fvXyLFgjnWJOVj9zWT87/ddV5+8Gx

297 JAu0J3Q+cypZuKQVAAAAMQD5sTy8p+B1cn/DhOmXquui1BcxvASqzzevkBlbQoBa73y04B

298 2OdqVOVRkwZWRROz0AAAAbdGVzdCBrZXkgd2l0aG91dCBwYXNzcGhyYXNlAQIDBA==

299-----END OPENSSH PRIVATE KEY-----

300''',

301 'public_key': rb'''ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBKCQ6OQC+ru/m8e6PcoEvj8QBZyfmFkPIpxvJXR4EwYWruEpdCVmohqEtWp4xHRCqaTE0nauXLZUdxed6re9n718ixYI51iTlY/c1k/O/3XVefvBsSQLtCd0PnMqWbikFQ== test key without passphrase

302''',

303 'public_key_data': bytes.fromhex('''

304 00 00 00 13

305 65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70

306 33 38 34

307 00 00 00 08 6e 69 73 74 70 33 38 34

308 00 00 00 61 04

309 a0 90 e8 e4 02 fa bb bf 9b c7 ba 3d ca 04 be 3f

310 10 05 9c 9f 98 59 0f 22 9c 6f 25 74 78 13 06 16

311 ae e1 29 74 25 66 a2 1a 84 b5 6a 78 c4 74 42 a9

312 a4 c4 d2 76 ae 5c b6 54 77 17 9d ea b7 bd 9f bd

313 7c 8b 16 08 e7 58 93 95 8f dc d6 4f ce ff 75 d5

314 79 fb c1 b1 24 0b b4 27 74 3e 73 2a 59 b8 a4 15

315'''),

316 'expected_signature': None,

317 'derived_passphrase': None,

318 },

319}

320

321 DUMMY_SERVICE = 'service1'

322 DUMMY_PASSPHRASE = b'my secret passphrase\n'

323 DUMMY_KEY1 = SUPPORTED_KEYS['ed25519']['public_key_data']

324 DUMMY_KEY1_B64 = base64.standard_b64encode(DUMMY_KEY1).decode('ASCII')

325 DUMMY_KEY2 = SUPPORTED_KEYS['rsa']['public_key_data']

326 DUMMY_KEY2_B64 = base64.standard_b64encode(DUMMY_KEY2).decode('ASCII')

327 DUMMY_CONFIG_SETTINGS = {"length": 10, "upper": 1, "lower": 1, "repeat": 5,

328 "number": 1, "space": 1, "dash": 1, "symbol": 1}

329 DUMMY_RESULT_PASSPHRASE = b'.2V_QJkd o'

330 DUMMY_RESULT_KEY1 = b'E<b<{ -7iG'

331 DUMMY_PHRASE_FROM_KEY1_RAW = (

332 b'\x00\x00\x00\x0bssh-ed25519'

333 b'\x00\x00\x00@\xf0\x98\x19\x80l\x1a\x97\xd5&\x03n'

334 b'\xcc\xe3e\x8f\x86f\x07\x13\x19\x13\t!33\xf9\xe46S'

335 b'\x1d\xaf\xfd\r\x08\x1f\xec\xf8s\x9b\x8c_U9\x16|ST,'

336 b'\x1eR\xbb0\xed\x7f\x89\xe2/iQU\xd8\x9e\xa6\x02'

337)

338 DUMMY_PHRASE_FROM_KEY1 = b'8JgZgGwal9UmA27M42WPhmYHExkTCSEzM/nkNlMdr/0NCB/s+HObjF9VORZ8U1QsHlK7MO1/ieIvaVFV2J6mAg=='

339

340 skip_if_no_agent = pytest.mark.skipif(

341 not os.environ.get('SSH_AUTH_SOCK'), reason='running SSH agent required')

342

343 def list_keys(

344 self: Any = None,

345) -> list[ssh_agent_client.types.KeyCommentPair]:

346 Pair = ssh_agent_client.types.KeyCommentPair

347 list1 = [Pair(value['public_key_data'], f'{key} test key'.encode('ASCII'))

348 for key, value in SUPPORTED_KEYS.items()]

349 list2 = [Pair(value['public_key_data'], f'{key} test key'.encode('ASCII'))

350 for key, value in UNSUITABLE_KEYS.items()]

351 return list1 + list2

352

353 def list_keys_singleton(

354 self: Any = None,

355) -> list[ssh_agent_client.types.KeyCommentPair]:

356 Pair = ssh_agent_client.types.KeyCommentPair

357 list1 = [Pair(value['public_key_data'], f'{key} test key'.encode('ASCII'))

358 for key, value in SUPPORTED_KEYS.items()]

359 return list1[:1]

360

361 def suitable_ssh_keys(

362 conn: Any

363) -> Iterator[ssh_agent_client.types.KeyCommentPair]:

364 yield from [

365 ssh_agent_client.types.KeyCommentPair(DUMMY_KEY1, b'no comment'),

366 ssh_agent_client.types.KeyCommentPair(DUMMY_KEY2, b'a comment'),

367 ]

368

369 def phrase_from_key(key: bytes) -> bytes:

370 if key == DUMMY_KEY1: # pragma: no branch

371 return DUMMY_PHRASE_FROM_KEY1

372 raise KeyError(key) # pragma: no cover

373

374@contextlib.contextmanager

375 def isolated_config(

376 monkeypatch: Any, runner: click.testing.CliRunner, config: Any,

377):

378 prog_name = derivepassphrase.cli.prog_name

379 env_name = prog_name.replace(' ', '_').upper() + '_PATH'

380 with runner.isolated_filesystem():

381 monkeypatch.setenv('HOME', os.getcwd())

382 monkeypatch.setenv('USERPROFILE', os.getcwd())

383 monkeypatch.delenv(env_name, raising=False)

384 os.makedirs(os.path.dirname(derivepassphrase.cli._config_filename()),

385 exist_ok=True)

386 with open(derivepassphrase.cli._config_filename(), 'wt') as outfile:

387 json.dump(config, outfile)

388 yield

389

390 def auto_prompt(*args: Any, **kwargs: Any) -> str:

391 return DUMMY_PASSPHRASE.decode('UTF-8')