Author: Manos Pitsidianakis [manos@pitsidianak.is]
Hash: c470a6129d3e61b0fc03aa549f958f81577beb7c
Timestamp: Thu, 02 Nov 2023 12:11:20 +0000 (10 months ago)

+552 -66 +/-4 browse
web: add in-memory ssh sig verification with ssh-key crate
web: add in-memory ssh sig verification with ssh-key crate

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
1diff --git a/Cargo.lock b/Cargo.lock
2index f0373f1..dfcce4e 100644
3--- a/Cargo.lock
4+++ b/Cargo.lock
5 @@ -429,6 +429,12 @@ dependencies = [
6 ]
7
8 [[package]]
9+ name = "base16ct"
10+ version = "0.2.0"
11+ source = "registry+https://github.com/rust-lang/crates.io-index"
12+ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
13+
14+ [[package]]
15 name = "base64"
16 version = "0.13.1"
17 source = "registry+https://github.com/rust-lang/crates.io-index"
18 @@ -450,6 +456,12 @@ dependencies = [
19 ]
20
21 [[package]]
22+ name = "base64ct"
23+ version = "1.6.0"
24+ source = "registry+https://github.com/rust-lang/crates.io-index"
25+ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
26+
27+ [[package]]
28 name = "bcrypt"
29 version = "0.14.0"
30 source = "registry+https://github.com/rust-lang/crates.io-index"
31 @@ -851,6 +863,12 @@ dependencies = [
32 ]
33
34 [[package]]
35+ name = "const-oid"
36+ version = "0.9.5"
37+ source = "registry+https://github.com/rust-lang/crates.io-index"
38+ checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
39+
40+ [[package]]
41 name = "constant_time_eq"
42 version = "0.1.5"
43 source = "registry+https://github.com/rust-lang/crates.io-index"
44 @@ -872,7 +890,7 @@ dependencies = [
45 "hmac 0.12.1",
46 "percent-encoding",
47 "rand",
48- "sha2 0.10.6",
49+ "sha2 0.10.8",
50 "subtle",
51 "time 0.3.21",
52 "version_check",
53 @@ -913,6 +931,18 @@ dependencies = [
54 ]
55
56 [[package]]
57+ name = "crypto-bigint"
58+ version = "0.5.3"
59+ source = "registry+https://github.com/rust-lang/crates.io-index"
60+ checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124"
61+ dependencies = [
62+ "generic-array",
63+ "rand_core",
64+ "subtle",
65+ "zeroize",
66+ ]
67+
68+ [[package]]
69 name = "crypto-common"
70 version = "0.1.6"
71 source = "registry+https://github.com/rust-lang/crates.io-index"
72 @@ -953,6 +983,33 @@ dependencies = [
73 ]
74
75 [[package]]
76+ name = "curve25519-dalek"
77+ version = "4.1.1"
78+ source = "registry+https://github.com/rust-lang/crates.io-index"
79+ checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
80+ dependencies = [
81+ "cfg-if 1.0.0",
82+ "cpufeatures",
83+ "curve25519-dalek-derive",
84+ "digest 0.10.7",
85+ "fiat-crypto",
86+ "platforms",
87+ "rustc_version",
88+ "subtle",
89+ ]
90+
91+ [[package]]
92+ name = "curve25519-dalek-derive"
93+ version = "0.1.1"
94+ source = "registry+https://github.com/rust-lang/crates.io-index"
95+ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
96+ dependencies = [
97+ "proc-macro2",
98+ "quote",
99+ "syn 2.0.35",
100+ ]
101+
102+ [[package]]
103 name = "cxx"
104 version = "1.0.94"
105 source = "registry+https://github.com/rust-lang/crates.io-index"
106 @@ -1003,6 +1060,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
107 checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
108
109 [[package]]
110+ name = "der"
111+ version = "0.7.8"
112+ source = "registry+https://github.com/rust-lang/crates.io-index"
113+ checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
114+ dependencies = [
115+ "const-oid",
116+ "zeroize",
117+ ]
118+
119+ [[package]]
120 name = "derive_more"
121 version = "0.99.17"
122 source = "registry+https://github.com/rust-lang/crates.io-index"
123 @@ -1038,11 +1105,12 @@ dependencies = [
124
125 [[package]]
126 name = "digest"
127- version = "0.10.6"
128+ version = "0.10.7"
129 source = "registry+https://github.com/rust-lang/crates.io-index"
130- checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
131+ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
132 dependencies = [
133 "block-buffer 0.10.4",
134+ "const-oid",
135 "crypto-common",
136 "subtle",
137 ]
138 @@ -1066,12 +1134,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
139 checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
140
141 [[package]]
142+ name = "ecdsa"
143+ version = "0.16.8"
144+ source = "registry+https://github.com/rust-lang/crates.io-index"
145+ checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4"
146+ dependencies = [
147+ "der",
148+ "digest 0.10.7",
149+ "elliptic-curve",
150+ "rfc6979",
151+ "signature",
152+ "spki",
153+ ]
154+
155+ [[package]]
156+ name = "ed25519"
157+ version = "2.2.3"
158+ source = "registry+https://github.com/rust-lang/crates.io-index"
159+ checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
160+ dependencies = [
161+ "signature",
162+ ]
163+
164+ [[package]]
165+ name = "ed25519-dalek"
166+ version = "2.0.0"
167+ source = "registry+https://github.com/rust-lang/crates.io-index"
168+ checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980"
169+ dependencies = [
170+ "curve25519-dalek",
171+ "ed25519",
172+ "sha2 0.10.8",
173+ ]
174+
175+ [[package]]
176 name = "either"
177 version = "1.8.1"
178 source = "registry+https://github.com/rust-lang/crates.io-index"
179 checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
180
181 [[package]]
182+ name = "elliptic-curve"
183+ version = "0.13.6"
184+ source = "registry+https://github.com/rust-lang/crates.io-index"
185+ checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914"
186+ dependencies = [
187+ "base16ct",
188+ "crypto-bigint",
189+ "digest 0.10.7",
190+ "ff",
191+ "generic-array",
192+ "group",
193+ "pkcs8",
194+ "rand_core",
195+ "sec1",
196+ "subtle",
197+ "zeroize",
198+ ]
199+
200+ [[package]]
201 name = "encoding"
202 version = "0.2.33"
203 source = "registry+https://github.com/rust-lang/crates.io-index"
204 @@ -1213,6 +1334,22 @@ dependencies = [
205 ]
206
207 [[package]]
208+ name = "ff"
209+ version = "0.13.0"
210+ source = "registry+https://github.com/rust-lang/crates.io-index"
211+ checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
212+ dependencies = [
213+ "rand_core",
214+ "subtle",
215+ ]
216+
217+ [[package]]
218+ name = "fiat-crypto"
219+ version = "0.2.2"
220+ source = "registry+https://github.com/rust-lang/crates.io-index"
221+ checksum = "a481586acf778f1b1455424c343f71124b048ffa5f4fc3f8f6ae9dc432dcb3c7"
222+
223+ [[package]]
224 name = "filetime"
225 version = "0.2.21"
226 source = "registry+https://github.com/rust-lang/crates.io-index"
227 @@ -1420,6 +1557,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
228 dependencies = [
229 "typenum",
230 "version_check",
231+ "zeroize",
232 ]
233
234 [[package]]
235 @@ -1455,6 +1593,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
236 checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
237
238 [[package]]
239+ name = "group"
240+ version = "0.13.0"
241+ source = "registry+https://github.com/rust-lang/crates.io-index"
242+ checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
243+ dependencies = [
244+ "ff",
245+ "rand_core",
246+ "subtle",
247+ ]
248+
249+ [[package]]
250 name = "h2"
251 version = "0.3.18"
252 source = "registry+https://github.com/rust-lang/crates.io-index"
253 @@ -1562,7 +1711,7 @@ version = "0.12.1"
254 source = "registry+https://github.com/rust-lang/crates.io-index"
255 checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
256 dependencies = [
257- "digest 0.10.6",
258+ "digest 0.10.7",
259 ]
260
261 [[package]]
262 @@ -1862,6 +2011,9 @@ name = "lazy_static"
263 version = "1.4.0"
264 source = "registry+https://github.com/rust-lang/crates.io-index"
265 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
266+ dependencies = [
267+ "spin",
268+ ]
269
270 [[package]]
271 name = "lazycell"
272 @@ -1898,6 +2050,12 @@ dependencies = [
273 ]
274
275 [[package]]
276+ name = "libm"
277+ version = "0.2.8"
278+ source = "registry+https://github.com/rust-lang/crates.io-index"
279+ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
280+
281+ [[package]]
282 name = "libsqlite3-sys"
283 version = "0.25.2"
284 source = "registry+https://github.com/rust-lang/crates.io-index"
285 @@ -2122,6 +2280,7 @@ dependencies = [
286 "serde",
287 "serde_json",
288 "serde_urlencoded",
289+ "ssh-key",
290 "stderrlog",
291 "tempfile",
292 "tokio",
293 @@ -2366,6 +2525,23 @@ dependencies = [
294 ]
295
296 [[package]]
297+ name = "num-bigint-dig"
298+ version = "0.8.4"
299+ source = "registry+https://github.com/rust-lang/crates.io-index"
300+ checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
301+ dependencies = [
302+ "byteorder",
303+ "lazy_static",
304+ "libm",
305+ "num-integer",
306+ "num-iter",
307+ "num-traits",
308+ "rand",
309+ "smallvec",
310+ "zeroize",
311+ ]
312+
313+ [[package]]
314 name = "num-cmp"
315 version = "0.1.0"
316 source = "registry+https://github.com/rust-lang/crates.io-index"
317 @@ -2420,6 +2596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
318 checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
319 dependencies = [
320 "autocfg",
321+ "libm",
322 ]
323
324 [[package]]
325 @@ -2508,6 +2685,30 @@ dependencies = [
326 ]
327
328 [[package]]
329+ name = "p256"
330+ version = "0.13.2"
331+ source = "registry+https://github.com/rust-lang/crates.io-index"
332+ checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
333+ dependencies = [
334+ "ecdsa",
335+ "elliptic-curve",
336+ "primeorder",
337+ "sha2 0.10.8",
338+ ]
339+
340+ [[package]]
341+ name = "p384"
342+ version = "0.13.0"
343+ source = "registry+https://github.com/rust-lang/crates.io-index"
344+ checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209"
345+ dependencies = [
346+ "ecdsa",
347+ "elliptic-curve",
348+ "primeorder",
349+ "sha2 0.10.8",
350+ ]
351+
352+ [[package]]
353 name = "parking"
354 version = "2.1.0"
355 source = "registry+https://github.com/rust-lang/crates.io-index"
356 @@ -2543,6 +2744,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
357 checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
358
359 [[package]]
360+ name = "pem-rfc7468"
361+ version = "0.7.0"
362+ source = "registry+https://github.com/rust-lang/crates.io-index"
363+ checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
364+ dependencies = [
365+ "base64ct",
366+ ]
367+
368+ [[package]]
369 name = "percent-encoding"
370 version = "2.2.0"
371 source = "registry+https://github.com/rust-lang/crates.io-index"
372 @@ -2589,7 +2799,7 @@ checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411"
373 dependencies = [
374 "once_cell",
375 "pest",
376- "sha2 0.10.6",
377+ "sha2 0.10.8",
378 ]
379
380 [[package]]
381 @@ -2625,12 +2835,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
382 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
383
384 [[package]]
385+ name = "pkcs1"
386+ version = "0.7.5"
387+ source = "registry+https://github.com/rust-lang/crates.io-index"
388+ checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
389+ dependencies = [
390+ "der",
391+ "pkcs8",
392+ "spki",
393+ ]
394+
395+ [[package]]
396+ name = "pkcs8"
397+ version = "0.10.2"
398+ source = "registry+https://github.com/rust-lang/crates.io-index"
399+ checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
400+ dependencies = [
401+ "der",
402+ "spki",
403+ ]
404+
405+ [[package]]
406 name = "pkg-config"
407 version = "0.3.27"
408 source = "registry+https://github.com/rust-lang/crates.io-index"
409 checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
410
411 [[package]]
412+ name = "platforms"
413+ version = "3.2.0"
414+ source = "registry+https://github.com/rust-lang/crates.io-index"
415+ checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0"
416+
417+ [[package]]
418 name = "polling"
419 version = "2.8.0"
420 source = "registry+https://github.com/rust-lang/crates.io-index"
421 @@ -2696,6 +2933,15 @@ dependencies = [
422 ]
423
424 [[package]]
425+ name = "primeorder"
426+ version = "0.13.2"
427+ source = "registry+https://github.com/rust-lang/crates.io-index"
428+ checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3"
429+ dependencies = [
430+ "elliptic-curve",
431+ ]
432+
433+ [[package]]
434 name = "proc-macro-error"
435 version = "1.0.4"
436 source = "registry+https://github.com/rust-lang/crates.io-index"
437 @@ -2846,6 +3092,16 @@ dependencies = [
438 ]
439
440 [[package]]
441+ name = "rfc6979"
442+ version = "0.4.0"
443+ source = "registry+https://github.com/rust-lang/crates.io-index"
444+ checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
445+ dependencies = [
446+ "hmac 0.12.1",
447+ "subtle",
448+ ]
449+
450+ [[package]]
451 name = "ring"
452 version = "0.16.20"
453 source = "registry+https://github.com/rust-lang/crates.io-index"
454 @@ -2878,6 +3134,27 @@ dependencies = [
455 ]
456
457 [[package]]
458+ name = "rsa"
459+ version = "0.9.3"
460+ source = "registry+https://github.com/rust-lang/crates.io-index"
461+ checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d"
462+ dependencies = [
463+ "const-oid",
464+ "digest 0.10.7",
465+ "num-bigint-dig",
466+ "num-integer",
467+ "num-traits",
468+ "pkcs1",
469+ "pkcs8",
470+ "rand_core",
471+ "sha2 0.10.8",
472+ "signature",
473+ "spki",
474+ "subtle",
475+ "zeroize",
476+ ]
477+
478+ [[package]]
479 name = "rusqlite"
480 version = "0.28.0"
481 source = "registry+https://github.com/rust-lang/crates.io-index"
482 @@ -3006,6 +3283,20 @@ dependencies = [
483 ]
484
485 [[package]]
486+ name = "sec1"
487+ version = "0.7.3"
488+ source = "registry+https://github.com/rust-lang/crates.io-index"
489+ checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
490+ dependencies = [
491+ "base16ct",
492+ "der",
493+ "generic-array",
494+ "pkcs8",
495+ "subtle",
496+ "zeroize",
497+ ]
498+
499+ [[package]]
500 name = "secrecy"
501 version = "0.8.0"
502 source = "registry+https://github.com/rust-lang/crates.io-index"
503 @@ -3125,7 +3416,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
504 dependencies = [
505 "cfg-if 1.0.0",
506 "cpufeatures",
507- "digest 0.10.6",
508+ "digest 0.10.7",
509 ]
510
511 [[package]]
512 @@ -3149,13 +3440,13 @@ dependencies = [
513
514 [[package]]
515 name = "sha2"
516- version = "0.10.6"
517+ version = "0.10.8"
518 source = "registry+https://github.com/rust-lang/crates.io-index"
519- checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
520+ checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
521 dependencies = [
522 "cfg-if 1.0.0",
523 "cpufeatures",
524- "digest 0.10.6",
525+ "digest 0.10.7",
526 ]
527
528 [[package]]
529 @@ -3178,6 +3469,16 @@ dependencies = [
530 ]
531
532 [[package]]
533+ name = "signature"
534+ version = "2.1.0"
535+ source = "registry+https://github.com/rust-lang/crates.io-index"
536+ checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
537+ dependencies = [
538+ "digest 0.10.7",
539+ "rand_core",
540+ ]
541+
542+ [[package]]
543 name = "slab"
544 version = "0.4.8"
545 source = "registry+https://github.com/rust-lang/crates.io-index"
546 @@ -3229,6 +3530,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
547 checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
548
549 [[package]]
550+ name = "spki"
551+ version = "0.7.2"
552+ source = "registry+https://github.com/rust-lang/crates.io-index"
553+ checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
554+ dependencies = [
555+ "base64ct",
556+ "der",
557+ ]
558+
559+ [[package]]
560+ name = "ssh-cipher"
561+ version = "0.2.0"
562+ source = "registry+https://github.com/rust-lang/crates.io-index"
563+ checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f"
564+ dependencies = [
565+ "cipher",
566+ "ssh-encoding",
567+ ]
568+
569+ [[package]]
570+ name = "ssh-encoding"
571+ version = "0.2.0"
572+ source = "registry+https://github.com/rust-lang/crates.io-index"
573+ checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15"
574+ dependencies = [
575+ "base64ct",
576+ "pem-rfc7468",
577+ "sha2 0.10.8",
578+ ]
579+
580+ [[package]]
581+ name = "ssh-key"
582+ version = "0.6.2"
583+ source = "registry+https://github.com/rust-lang/crates.io-index"
584+ checksum = "2180b3bc4955efd5661a97658d3cf4c8107e0d132f619195afe9486c13cca313"
585+ dependencies = [
586+ "ed25519-dalek",
587+ "num-bigint-dig",
588+ "p256",
589+ "p384",
590+ "rand_core",
591+ "rsa",
592+ "sec1",
593+ "sha2 0.10.8",
594+ "signature",
595+ "ssh-cipher",
596+ "ssh-encoding",
597+ "subtle",
598+ "zeroize",
599+ ]
600+
601+ [[package]]
602 name = "stderrlog"
603 version = "0.5.4"
604 source = "registry+https://github.com/rust-lang/crates.io-index"
605 diff --git a/web/Cargo.toml b/web/Cargo.toml
606index 39e8d99..8cf5eec 100644
607--- a/web/Cargo.toml
608+++ b/web/Cargo.toml
609 @@ -15,6 +15,10 @@ name = "mpot-web"
610 path = "src/main.rs"
611 doc-scrape-examples = true
612
613+ [features]
614+ default = ["ssh-key"]
615+ ssh-key = ["dep:ssh-key"]
616+
617 [dependencies]
618 axum = { version = "^0.6" }
619 axum-extra = { version = "^0.7", features = ["typed-routing"] }
620 @@ -35,6 +39,7 @@ percent-encoding = { version = "^2.1" }
621 rand = { version = "^0.8", features = ["min_const_gen"] }
622 serde = { version = "^1", features = ["derive", ] }
623 serde_json = "^1"
624+ ssh-key = { version = "0.6.2", optional = true, features = ["crypto"] }
625 stderrlog = "^0.5"
626 tempfile = { version = "^3.5" }
627 tokio = { version = "1", features = ["full"] }
628 diff --git a/web/src/auth.rs b/web/src/auth.rs
629index cc852b2..5da49ae 100644
630--- a/web/src/auth.rs
631+++ b/web/src/auth.rs
632 @@ -255,21 +255,30 @@ pub async fn ssh_signin_POST(
633 token: _prev_token,
634 };
635 #[cfg(not(debug_assertions))]
636- if let Err(err) = ssh_keygen(sig).await {
637- session.add_message(Message {
638- message: format!("Could not verify signature: {err}").into(),
639- level: Level::Error,
640- })?;
641- return Ok(Redirect::to(&format!(
642- "{}{}{}",
643- state.root_url_prefix,
644- LoginPath.to_uri(),
645- next.next.as_ref().map_or(Cow::Borrowed(""), |next| format!(
646- "?next={}",
647- percent_encoding::utf8_percent_encode(next.as_str(), percent_encoding::CONTROLS)
648- )
649- .into())
650- )));
651+ {
652+ #[cfg(not(feature = "ssh-key"))]
653+ let ssh_verify_fn = ssh_verify;
654+ #[cfg(feature = "ssh-key")]
655+ let ssh_verify_fn = ssh_verify_in_memory;
656+ if let Err(err) = ssh_verify_fn(sig).await {
657+ session.add_message(Message {
658+ message: format!("Could not verify signature: {err}").into(),
659+ level: Level::Error,
660+ })?;
661+ return Ok(Redirect::to(&format!(
662+ "{}{}{}",
663+ state.root_url_prefix,
664+ LoginPath.to_uri(),
665+ next.next.as_ref().map_or(Cow::Borrowed(""), |next| format!(
666+ "?next={}",
667+ percent_encoding::utf8_percent_encode(
668+ next.as_str(),
669+ percent_encoding::CONTROLS
670+ )
671+ )
672+ .into())
673+ )));
674+ }
675 }
676
677 let user = User {
678 @@ -311,13 +320,13 @@ pub struct SshSignature {
679 /// Run ssh signature validation with `ssh-keygen` binary.
680 ///
681 /// ```no_run
682- /// use mailpot_web::{ssh_keygen, SshSignature};
683+ /// use mailpot_web::{ssh_verify, SshSignature};
684 ///
685- /// async fn key_gen(
686+ /// async fn verify_signature(
687 /// ssh_public_key: String,
688 /// ssh_signature: String,
689 /// ) -> std::result::Result<(), Box<dyn std::error::Error>> {
690- /// let mut sig = SshSignature {
691+ /// let sig = SshSignature {
692 /// email: "user@example.com".to_string(),
693 /// ssh_public_key,
694 /// ssh_signature,
695 @@ -325,11 +334,11 @@ pub struct SshSignature {
696 /// token: "d074a61990".to_string(),
697 /// };
698 ///
699- /// ssh_keygen(sig.clone()).await?;
700+ /// ssh_verify(sig).await?;
701 /// Ok(())
702 /// }
703 /// ```
704- pub async fn ssh_keygen(sig: SshSignature) -> Result<(), Box<dyn std::error::Error>> {
705+ pub async fn ssh_verify(sig: SshSignature) -> Result<(), Box<dyn std::error::Error>> {
706 let SshSignature {
707 email,
708 ssh_public_key,
709 @@ -435,6 +444,83 @@ pub async fn ssh_keygen(sig: SshSignature) -> Result<(), Box<dyn std::error::Err
710 Ok(())
711 }
712
713+ /// Run ssh signature validation.
714+ ///
715+ /// ```no_run
716+ /// use mailpot_web::{ssh_verify_in_memory, SshSignature};
717+ ///
718+ /// async fn ssh_verify(
719+ /// ssh_public_key: String,
720+ /// ssh_signature: String,
721+ /// ) -> std::result::Result<(), Box<dyn std::error::Error>> {
722+ /// let sig = SshSignature {
723+ /// email: "user@example.com".to_string(),
724+ /// ssh_public_key,
725+ /// ssh_signature,
726+ /// namespace: "doc-test@example.com".into(),
727+ /// token: "d074a61990".to_string(),
728+ /// };
729+ ///
730+ /// ssh_verify_in_memory(sig).await?;
731+ /// Ok(())
732+ /// }
733+ /// ```
734+ #[cfg(feature = "ssh-key")]
735+ pub async fn ssh_verify_in_memory(sig: SshSignature) -> Result<(), Box<dyn std::error::Error>> {
736+ use ssh_key::{PublicKey, SshSig};
737+
738+ let SshSignature {
739+ email: _,
740+ ref ssh_public_key,
741+ ref ssh_signature,
742+ ref namespace,
743+ ref token,
744+ } = sig;
745+
746+ let public_key = ssh_public_key.parse::<PublicKey>().map_err(|err| {
747+ format!("Could not parse user's SSH public key. Is it valid? Reason given: {err}")
748+ })?;
749+ let signature = if ssh_signature.contains("\r\n") {
750+ ssh_signature.trim().replace("\r\n", "\n").parse::<SshSig>()
751+ } else {
752+ ssh_signature.parse::<SshSig>()
753+ }
754+ .map_err(|err| format!("Invalid SSH signature. Reason given: {err}"))?;
755+
756+ if let Err(err) = public_key.verify(namespace, token.as_bytes(), &signature) {
757+ use ssh_key::Error;
758+
759+ #[allow(clippy::wildcard_in_or_patterns)]
760+ return match err {
761+ Error::Io(err_kind) => {
762+ log::error!(
763+ "ssh signature could not be verified because of internal error:\nSignature \
764+ was {sig:#?}\nError was {err_kind}."
765+ );
766+ Err("SSH signature could not be verified because of internal error.".into())
767+ }
768+ Error::Crypto => Err("SSH signature is invalid.".into()),
769+ Error::AlgorithmUnknown
770+ | Error::AlgorithmUnsupported { .. }
771+ | Error::CertificateFieldInvalid(_)
772+ | Error::CertificateValidation
773+ | Error::Decrypted
774+ | Error::Ecdsa(_)
775+ | Error::Encoding(_)
776+ | Error::Encrypted
777+ | Error::FormatEncoding
778+ | Error::Namespace
779+ | Error::PublicKey
780+ | Error::Time
781+ | Error::TrailingData { .. }
782+ | Error::Version { .. }
783+ | _ => Err(format!("SSH signature could not be verified: Reason given: {err}").into()),
784+ };
785+ }
786+
787+ Ok(())
788+ }
789+
790 pub async fn logout_handler(
791 _: LogoutPath,
792 mut auth: AuthContext,
793 @@ -657,10 +743,8 @@ pub mod auth_request {
794 #[cfg(test)]
795 mod tests {
796 use super::*;
797-
798- #[tokio::test]
799- async fn test_ssh_keygen() {
800- const PKEY: &str = concat!("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCzXp8nLJL8GPNw7S+Dqt0m3Dw/",
801+ const PKEY: &str = concat!(
802+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCzXp8nLJL8GPNw7S+Dqt0m3Dw/",
803 "xFOAdwKXcekTFI9cLDEUII2rNPf0uUZTpv57OgU+",
804 "QOEEIvWMjz+5KSWBX8qdP8OtV0QNvynlZkEKZN0cUqGKaNXo5a+PUDyiJ2rHroPe1aMo6mUBL9kLR6J2U1CYD/dLfL8ywXsAGmOL0bsK0GRPVBJAjpUNRjpGU/",
805 "2FFIlU6s6GawdbDXEHDox/UoOVAKIlhKabaTrFBA0ACFLRX2/GCBmHqqt5d4ZZjefYzReLs/beOjafYImoyhHC428wZDcUjvLrpSJbIOE/",
806 @@ -668,48 +752,54 @@ mod tests {
807 "lyjxeWIUYyj7rjlqKJ9tzygek7QNxCtuqH5xsZAZqzQCN8wfrPAlwDykvWityKOw+Bt2DWjimITqyKgsBsOaA+",
808 "eVCllFvooJxoYvAjODASjAUoOdgVzyBDpFnOhLFYiIIyL3F6NROS9i7z086paX7mrzcQzvLr4ckF9qT7DrI88ikISCR9bFR4vPq3aH",
809 "zJdjDDpWxACa5b11NG8KdCJPe/L0kDw82Q00U13CpW9FI9sZjvk+",
810- "lyw8bTFvVsIl6A0ueboFvrNvznAqHrtfWu75fXRh5sKj2TGk8rhm3vyNgrBSr5zAfFVM8LgqBxbAAYw==");
811-
812- const SIG: &str = concat!(
813- "-----BEGIN SSH SIGNATURE-----\n",
814- "U1NIU0lHAAAAAQAAAhcAAAAHc3NoLXJzYQAAAAMBAAEAAAIBALNenycskvwY83DtL4Oq3S\n",
815- "bcPD/EU4B3Apdx6RMUj1wsMRQgjas09/S5RlOm/ns6BT5A4QQi9YyPP7kpJYFfyp0/w61X\n",
816- "RA2/KeVmQQpk3RxSoYpo1ejlr49QPKInaseug97VoyjqZQEv2QtHonZTUJgP90t8vzLBew\n",
817- "AaY4vRuwrQZE9UEkCOlQ1GOkZT/YUUiVTqzoZrB1sNcQcOjH9Sg5UAoiWEpptpOsUEDQAI\n",
818- "UtFfb8YIGYeqq3l3hlmN59jNF4uz9t46Np9giajKEcLjbzBkNxSO8uulIlsg4T+BI8JaVF\n",
819- "tyzGDgkZwo60AtS6sT6iT5q/L0zt4WMaEsZKVMot2yEhVCv/dbrrsztth85PrE/+XKPF5Y\n",
820- "hRjKPuuOWoon23PKB6TtA3EK26ofnGxkBmrNAI3zB+s8CXAPKS9aK3Io7D4G3YNaOKYhOr\n",
821- "IqCwGw5oD55UKWUW+ignGhi8CM4MBKMBSg52BXPIEOkWc6EsViIgjIvcXo1E5L2LvPTzql\n",
822- "pfuavNxDO8uvhyQX2pPsOsjzyKQhIJH1sVHi8+rdofMl2MMOlbEAJrlvXU0bwp0Ik978vS\n",
823- "QPDzZDTRTXcKlb0Uj2xmO+T6XLDxtMW9WwiXoDS55ugW+s2/OcCoeu19a7vl9dGHmwqPZM\n",
824- "aTyuGbe/I2CsFKvnMB8VUzwuCoHFsABjAAAAFGRvYy10ZXN0QGV4YW1wbGUuY29tAAAAAA\n",
825- "AAAAZzaGE1MTIAAAIUAAAADHJzYS1zaGEyLTUxMgAAAgBxaMqIfeapKTrhQzggDssD+76s\n",
826- "jZxv3XxzgsuAjlIdtw+/nyxU6skTnrGoam2shpmQvx0HuqSQ7HyS2USBK7T4LZNoE53zR/\n",
827- "ZmHLGoyQAoexiHSEW9Lk53kyRNPhpXQedTvm8REHPGM3zw6WO6mAXVVxvebvawf81LTbBb\n",
828- "p9ubNRcHgktVeywMO/sD6zWSyShq1gjVv1PdRBOjUgqkwjImL8dFKi1QUeoffCxyk3JhTO\n",
829- "siTy79HZSz/kOvkvL1vQuqaP2R8lE9P1uaD19dGOMTPRod3u+QmpYX47ri5KM3Fmkfxdwq\n",
830- "p8JVmfAA9nme7bmNS1hWgmF2Nbh9qjh1zOZvCimIpuNtz5eEl9K+1DxG6w5tX86wSGvBMO\n",
831- "znx0k1gGfkiAULqgrkdul7mqMPRvPN9J6QlNJ7SLFChRhzlJIJc6tOvCs7qkVD43Zcb+I5\n",
832- "Z+K4NiFf5jf8kVX/pjjeW/ucbrctJIkGsZ58OkHKi1EDRcq7NtCF6SKlcv8g3fMLd9wW6K\n",
833- "aaed0TBDC+s+f6naNIGvWqfWCwDuK5xGyDTTmJGcrsMwWuT9K6uLk8cGdv7t5mOFuWi5jl\n",
834- "E+IKZKVABMuWqSj96ErMIiBjtsAZfNSezpsK49wQztoSPhdwLhD6fHrSAyPCqN2xRkcsIb\n",
835- "6PxWKC/OELf3gyEBRPouxsF7xSZQ==\n",
836- "-----END SSH SIGNATURE-----\n"
837- );
838+ "lyw8bTFvVsIl6A0ueboFvrNvznAqHrtfWu75fXRh5sKj2TGk8rhm3vyNgrBSr5zAfFVM8LgqBxbAAYw=="
839+ );
840+
841+ const ARMOR_SIG: &str = concat!(
842+ "-----BEGIN SSH SIGNATURE-----\n",
843+ "U1NIU0lHAAAAAQAAAhcAAAAHc3NoLXJzYQAAAAMBAAEAAAIBALNenycskvwY83DtL4Oq3S\n",
844+ "bcPD/EU4B3Apdx6RMUj1wsMRQgjas09/S5RlOm/ns6BT5A4QQi9YyPP7kpJYFfyp0/w61X\n",
845+ "RA2/KeVmQQpk3RxSoYpo1ejlr49QPKInaseug97VoyjqZQEv2QtHonZTUJgP90t8vzLBew\n",
846+ "AaY4vRuwrQZE9UEkCOlQ1GOkZT/YUUiVTqzoZrB1sNcQcOjH9Sg5UAoiWEpptpOsUEDQAI\n",
847+ "UtFfb8YIGYeqq3l3hlmN59jNF4uz9t46Np9giajKEcLjbzBkNxSO8uulIlsg4T+BI8JaVF\n",
848+ "tyzGDgkZwo60AtS6sT6iT5q/L0zt4WMaEsZKVMot2yEhVCv/dbrrsztth85PrE/+XKPF5Y\n",
849+ "hRjKPuuOWoon23PKB6TtA3EK26ofnGxkBmrNAI3zB+s8CXAPKS9aK3Io7D4G3YNaOKYhOr\n",
850+ "IqCwGw5oD55UKWUW+ignGhi8CM4MBKMBSg52BXPIEOkWc6EsViIgjIvcXo1E5L2LvPTzql\n",
851+ "pfuavNxDO8uvhyQX2pPsOsjzyKQhIJH1sVHi8+rdofMl2MMOlbEAJrlvXU0bwp0Ik978vS\n",
852+ "QPDzZDTRTXcKlb0Uj2xmO+T6XLDxtMW9WwiXoDS55ugW+s2/OcCoeu19a7vl9dGHmwqPZM\n",
853+ "aTyuGbe/I2CsFKvnMB8VUzwuCoHFsABjAAAAFGRvYy10ZXN0QGV4YW1wbGUuY29tAAAAAA\n",
854+ "AAAAZzaGE1MTIAAAIUAAAADHJzYS1zaGEyLTUxMgAAAgBxaMqIfeapKTrhQzggDssD+76s\n",
855+ "jZxv3XxzgsuAjlIdtw+/nyxU6skTnrGoam2shpmQvx0HuqSQ7HyS2USBK7T4LZNoE53zR/\n",
856+ "ZmHLGoyQAoexiHSEW9Lk53kyRNPhpXQedTvm8REHPGM3zw6WO6mAXVVxvebvawf81LTbBb\n",
857+ "p9ubNRcHgktVeywMO/sD6zWSyShq1gjVv1PdRBOjUgqkwjImL8dFKi1QUeoffCxyk3JhTO\n",
858+ "siTy79HZSz/kOvkvL1vQuqaP2R8lE9P1uaD19dGOMTPRod3u+QmpYX47ri5KM3Fmkfxdwq\n",
859+ "p8JVmfAA9nme7bmNS1hWgmF2Nbh9qjh1zOZvCimIpuNtz5eEl9K+1DxG6w5tX86wSGvBMO\n",
860+ "znx0k1gGfkiAULqgrkdul7mqMPRvPN9J6QlNJ7SLFChRhzlJIJc6tOvCs7qkVD43Zcb+I5\n",
861+ "Z+K4NiFf5jf8kVX/pjjeW/ucbrctJIkGsZ58OkHKi1EDRcq7NtCF6SKlcv8g3fMLd9wW6K\n",
862+ "aaed0TBDC+s+f6naNIGvWqfWCwDuK5xGyDTTmJGcrsMwWuT9K6uLk8cGdv7t5mOFuWi5jl\n",
863+ "E+IKZKVABMuWqSj96ErMIiBjtsAZfNSezpsK49wQztoSPhdwLhD6fHrSAyPCqN2xRkcsIb\n",
864+ "6PxWKC/OELf3gyEBRPouxsF7xSZQ==\n",
865+ "-----END SSH SIGNATURE-----\n"
866+ );
867
868- let mut sig = SshSignature {
869+ fn create_sig() -> SshSignature {
870+ SshSignature {
871 email: "user@example.com".to_string(),
872 ssh_public_key: PKEY.to_string(),
873- ssh_signature: SIG.to_string(),
874+ ssh_signature: ARMOR_SIG.to_string(),
875 namespace: "doc-test@example.com".into(),
876 token: "d074a61990".to_string(),
877- };
878+ }
879+ }
880
881- ssh_keygen(sig.clone()).await.unwrap();
882+ #[tokio::test]
883+ async fn test_ssh_verify() {
884+ let mut sig = create_sig();
885+ ssh_verify(sig.clone()).await.unwrap();
886
887 sig.ssh_signature = sig.ssh_signature.replace('J', "0");
888
889- let err = ssh_keygen(sig).await.unwrap_err();
890+ let err = ssh_verify(sig).await.unwrap_err();
891
892 assert!(
893 err.to_string().starts_with("ssh-keygen exited with"),
894 @@ -717,4 +807,38 @@ mod tests {
895 err
896 );
897 }
898+
899+ #[cfg(feature = "ssh-key")]
900+ #[tokio::test]
901+ async fn test_ssh_verify_in_memory() {
902+ let mut sig = create_sig();
903+ ssh_verify_in_memory(sig.clone()).await.unwrap();
904+
905+ sig.ssh_signature = sig.ssh_signature.replace('J', "0");
906+
907+ let err = ssh_verify_in_memory(sig.clone()).await.unwrap_err();
908+
909+ assert_eq!(
910+ &err.to_string(),
911+ "Invalid SSH signature. Reason given: invalid label: 'ssh-}3a'",
912+ "{}",
913+ err
914+ );
915+
916+ sig.ssh_public_key = sig.ssh_public_key.replace(' ', "0");
917+
918+ let err = ssh_verify_in_memory(sig).await.unwrap_err();
919+ assert_eq!(
920+ &err.to_string(),
921+ "Could not parse user's SSH public key. Is it valid? Reason given: length invalid",
922+ "{}",
923+ err
924+ );
925+
926+ let mut sig = create_sig();
927+ sig.token = sig.token.replace('d', "0");
928+
929+ let err = ssh_verify_in_memory(sig).await.unwrap_err();
930+ assert_eq!(&err.to_string(), "SSH signature is invalid.", "{}", err);
931+ }
932 }
933 diff --git a/web/src/main.rs b/web/src/main.rs
934index 45520a4..125742a 100644
935--- a/web/src/main.rs
936+++ b/web/src/main.rs
937 @@ -246,6 +246,8 @@ mod tests {
938
939 #[tokio::test]
940 async fn test_routes() {
941+ #![cfg_attr(not(debug_assertions), allow(unreachable_code))]
942+
943 init_stderr_logging();
944
945 macro_rules! req {
946 @@ -375,6 +377,8 @@ mod tests {
947 assert_eq!(response.status(), StatusCode::OK);
948 }
949
950+ #[cfg(not(debug_assertions))]
951+ return;
952 // ------------------------------------------------------------
953 // auth.rs...
954