Skip to main content

max / makenotwork

Drop openssl from the server: webauthn-rs 0.6, and an explicit rustls provider Three things, all in service of getting openssl-sys out of the tree, which cargo-deny bans on the pure-Rust crypto standing choice. webauthn-rs 0.5 depended on openssl and openssl-sys unconditionally, with no feature gate, so it was the last root. 0.6 replaces that with crypto-glue (RustCrypto). The stored credential format does not move: a Passkey written by 0.5 round-trips through 0.6 byte-identically, and 0.5 still reads what 0.6 writes, so user_passkeys.credential_json needs no migration and a rollback stays open. Verified against the 0.5 test suite's own serialised credential. Upstream has no stable 0.6 yet, so the version is pinned exactly rather than left to float. WebauthnAuthenticator became a trait in 0.6, blanket-implemented on every AuthenticatorBackend, so the passkey workflow tests hold a SoftPasskey directly. The rustls provider is a bug the previous commit introduced and the Stripe webhook tests caught. Moving async-stripe onto rustls put a second rustls consumer in the tree that builds its connector from the process-wide default provider, and rustls refuses to choose when both aws-lc-rs and ring are compiled in. Both are. It panicked lazily, at the first connector build, which put the failure on the money path. StripeClient::new now installs a named provider, so no construction order can get it wrong, and main does it at boot so the choice is visible where it is made. Also drops the direct openssl dependency, which existed only to force `vendored` onto the openssl webauthn-rs pulled in. Nothing pulls it now. fw13 and astra both carry system OpenSSL 3.0.13, and the release build links clean without it. cargo-deny bans and licenses both pass under the sweep policy. 3421 tests pass.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-08 15:26 UTC
Signed with PGP, not checked
Commit: a1d2acb55616e360bdf08995e2d4ee41dc49fd31
Parent: dfaaa9e
8 files changed, +276 insertions, -98 deletions
M server/Cargo.lock +217 -81
@@ -17,6 +17,16 @@
17 17 source = "registry+https://github.com/rust-lang/crates.io-index"
18 18 checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
19 19
20 + [[package]]
21 + name = "aead"
22 + version = "0.5.2"
23 + source = "registry+https://github.com/rust-lang/crates.io-index"
24 + checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
25 + dependencies = [
26 + "crypto-common 0.1.7",
27 + "generic-array",
28 + ]
29 +
20 30 [[package]]
21 31 name = "aead"
22 32 version = "0.6.1"
@@ -27,6 +37,17 @@
27 37 "inout 0.2.2",
28 38 ]
29 39
40 + [[package]]
41 + name = "aes"
42 + version = "0.8.4"
43 + source = "registry+https://github.com/rust-lang/crates.io-index"
44 + checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
45 + dependencies = [
46 + "cfg-if",
47 + "cipher 0.4.4",
48 + "cpufeatures 0.2.17",
49 + ]
50 +
30 51 [[package]]
31 52 name = "aes"
32 53 version = "0.9.1"
@@ -38,6 +59,29 @@
38 59 "cpufeatures 0.3.0",
39 60 ]
40 61
62 + [[package]]
63 + name = "aes-gcm"
64 + version = "0.10.3"
65 + source = "registry+https://github.com/rust-lang/crates.io-index"
66 + checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
67 + dependencies = [
68 + "aead 0.5.2",
69 + "aes 0.8.4",
70 + "cipher 0.4.4",
71 + "ctr",
72 + "ghash",
73 + "subtle",
74 + ]
75 +
76 + [[package]]
77 + name = "aes-kw"
78 + version = "0.2.1"
79 + source = "registry+https://github.com/rust-lang/crates.io-index"
80 + checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c"
81 + dependencies = [
82 + "aes 0.8.4",
83 + ]
84 +
41 85 [[package]]
42 86 name = "ahash"
43 87 version = "0.8.12"
@@ -1298,17 +1342,6 @@
1298 1342 source = "registry+https://github.com/rust-lang/crates.io-index"
1299 1343 checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
1300 1344
1301 - [[package]]
1302 - name = "base64urlsafedata"
1303 - version = "0.5.5"
1304 - source = "registry+https://github.com/rust-lang/crates.io-index"
1305 - checksum = "b08e33815c87d8cadcddb1e74ac307368a3751fbe40c961538afa21a1899f21c"
1306 - dependencies = [
1307 - "base64 0.21.7",
1308 - "pastey",
1309 - "serde",
1310 - ]
1311 -
1312 1345 [[package]]
1313 1346 name = "basic-toml"
1314 1347 version = "0.1.10"
@@ -1478,6 +1511,15 @@
1478 1511 "cfg_aliases",
1479 1512 ]
1480 1513
1514 + [[package]]
1515 + name = "bs58"
1516 + version = "0.5.1"
1517 + source = "registry+https://github.com/rust-lang/crates.io-index"
1518 + checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
1519 + dependencies = [
1520 + "tinyvec",
1521 + ]
1522 +
1481 1523 [[package]]
1482 1524 name = "bstr"
1483 1525 version = "1.12.3"
@@ -1629,7 +1671,7 @@
1629 1671 source = "registry+https://github.com/rust-lang/crates.io-index"
1630 1672 checksum = "9b89e1c441e926b9c82a8d023f6e1b7ae0adcfaa7d621814e4d60789bac751cb"
1631 1673 dependencies = [
1632 - "aead",
1674 + "aead 0.6.1",
1633 1675 "chacha20",
1634 1676 "cipher 0.5.2",
1635 1677 "poly1305",
@@ -2205,6 +2247,7 @@
2205 2247 checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
2206 2248 dependencies = [
2207 2249 "generic-array",
2250 + "rand_core 0.6.4",
2208 2251 "typenum",
2209 2252 ]
2210 2253
@@ -2219,6 +2262,53 @@
2219 2262 "rand_core 0.10.1",
2220 2263 ]
2221 2264
2265 + [[package]]
2266 + name = "crypto-glue"
2267 + version = "0.1.15"
2268 + source = "registry+https://github.com/rust-lang/crates.io-index"
2269 + checksum = "842583c7b5969afdd00c31fd4ffe23f084d62ea3bc07a2132a2a3c4340ab1d09"
2270 + dependencies = [
2271 + "aes 0.8.4",
2272 + "aes-gcm",
2273 + "aes-kw",
2274 + "argon2",
2275 + "base64 0.22.1",
2276 + "cbc",
2277 + "cipher 0.4.4",
2278 + "const-oid 0.9.6",
2279 + "crypto-common 0.1.7",
2280 + "crypto-common 0.2.2",
2281 + "der",
2282 + "digest 0.11.3",
2283 + "ecdsa",
2284 + "elliptic-curve",
2285 + "generic-array",
2286 + "hex",
2287 + "hkdf",
2288 + "hmac 0.12.1",
2289 + "hmac 0.13.0",
2290 + "hybrid-array",
2291 + "kbkdf",
2292 + "p256",
2293 + "p384",
2294 + "p521",
2295 + "pbkdf2 0.12.2",
2296 + "pkcs8",
2297 + "rand 0.8.6",
2298 + "rsa",
2299 + "sec1",
2300 + "serde",
2301 + "sha1 0.10.6",
2302 + "sha2 0.10.9",
2303 + "sha2 0.11.0",
2304 + "spki",
2305 + "subtle",
2306 + "tracing",
2307 + "uuid",
2308 + "x509-cert",
2309 + "zeroize",
2310 + ]
2311 +
2222 2312 [[package]]
2223 2313 name = "cryptographic-message-syntax"
2224 2314 version = "0.27.0"
@@ -2300,6 +2390,15 @@
2300 2390 "memchr",
2301 2391 ]
2302 2392
2393 + [[package]]
2394 + name = "ctr"
2395 + version = "0.9.2"
2396 + source = "registry+https://github.com/rust-lang/crates.io-index"
2397 + checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
2398 + dependencies = [
2399 + "cipher 0.4.4",
2400 + ]
2401 +
2303 2402 [[package]]
2304 2403 name = "ctutils"
2305 2404 version = "0.4.2"
@@ -2674,6 +2773,12 @@
2674 2773 source = "registry+https://github.com/rust-lang/crates.io-index"
2675 2774 checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
2676 2775
2776 + [[package]]
2777 + name = "dyn-clone"
2778 + version = "1.0.20"
2779 + source = "registry+https://github.com/rust-lang/crates.io-index"
2780 + checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
2781 +
2677 2782 [[package]]
2678 2783 name = "ecdsa"
2679 2784 version = "0.16.9"
@@ -2984,21 +3089,6 @@
2984 3089 source = "registry+https://github.com/rust-lang/crates.io-index"
2985 3090 checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
2986 3091
2987 - [[package]]
2988 - name = "foreign-types"
2989 - version = "0.3.2"
2990 - source = "registry+https://github.com/rust-lang/crates.io-index"
2991 - checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
2992 - dependencies = [
2993 - "foreign-types-shared",
2994 - ]
2995 -
2996 - [[package]]
2997 - name = "foreign-types-shared"
2998 - version = "0.1.1"
2999 - source = "registry+https://github.com/rust-lang/crates.io-index"
3000 - checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
3001 -
3002 3092 [[package]]
3003 3093 name = "form_urlencoded"
3004 3094 version = "1.2.2"
@@ -3166,6 +3256,7 @@
3166 3256 source = "registry+https://github.com/rust-lang/crates.io-index"
3167 3257 checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
3168 3258 dependencies = [
3259 + "serde",
3169 3260 "typenum",
3170 3261 "version_check",
3171 3262 "zeroize",
@@ -3221,6 +3312,16 @@
3221 3312 "wasm-bindgen",
3222 3313 ]
3223 3314
3315 + [[package]]
3316 + name = "ghash"
3317 + version = "0.5.1"
3318 + source = "registry+https://github.com/rust-lang/crates.io-index"
3319 + checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
3320 + dependencies = [
3321 + "opaque-debug",
3322 + "polyval",
3323 + ]
3324 +
3224 3325 [[package]]
3225 3326 name = "gimli"
3226 3327 version = "0.32.3"
@@ -3228,7 +3329,7 @@
3228 3329 checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
3229 3330 dependencies = [
3230 3331 "fallible-iterator",
3231 - "indexmap",
3332 + "indexmap 2.14.0",
3232 3333 "stable_deref_trait",
3233 3334 ]
3234 3335
@@ -3240,7 +3341,7 @@
3240 3341 dependencies = [
3241 3342 "fnv",
3242 3343 "hashbrown 0.16.1",
3243 - "indexmap",
3344 + "indexmap 2.14.0",
3244 3345 "stable_deref_trait",
3245 3346 ]
3246 3347
@@ -4102,7 +4203,7 @@
4102 4203 "futures-core",
4103 4204 "futures-sink",
4104 4205 "http 1.4.2",
4105 - "indexmap",
4206 + "indexmap 2.14.0",
4106 4207 "slab",
4107 4208 "tokio",
4108 4209 "tokio-util",
@@ -4135,6 +4236,12 @@
4135 4236 source = "registry+https://github.com/rust-lang/crates.io-index"
4136 4237 checksum = "7040a10f52cba493ddb09926e15d10a9d8a28043708a405931fe4c6f19fac064"
4137 4238
4239 + [[package]]
4240 + name = "hashbrown"
4241 + version = "0.12.3"
4242 + source = "registry+https://github.com/rust-lang/crates.io-index"
4243 + checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
4244 +
4138 4245 [[package]]
4139 4246 name = "hashbrown"
4140 4247 version = "0.14.5"
@@ -4365,7 +4472,9 @@
4365 4472 source = "registry+https://github.com/rust-lang/crates.io-index"
4366 4473 checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
4367 4474 dependencies = [
4475 + "serde",
4368 4476 "typenum",
4477 + "zeroize",
4369 4478 ]
4370 4479
4371 4480 [[package]]
@@ -4633,6 +4742,17 @@
4633 4742 "quote",
4634 4743 ]
4635 4744
4745 + [[package]]
4746 + name = "indexmap"
4747 + version = "1.9.3"
4748 + source = "registry+https://github.com/rust-lang/crates.io-index"
4749 + checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
4750 + dependencies = [
4751 + "autocfg",
4752 + "hashbrown 0.12.3",
4753 + "serde",
4754 + ]
4755 +
4636 4756 [[package]]
4637 4757 name = "indexmap"
4638 4758 version = "2.14.0"
@@ -4884,6 +5004,15 @@
4884 5004 "zeroize",
4885 5005 ]
4886 5006
5007 + [[package]]
5008 + name = "kbkdf"
5009 + version = "0.1.0-rc.1"
5010 + source = "registry+https://github.com/rust-lang/crates.io-index"
5011 + checksum = "90ac93c9768b8d587407881c98b0c3a5d3e3049daa73408ebe5bfb1ab1cb9c84"
5012 + dependencies = [
5013 + "digest 0.11.3",
5014 + ]
5015 +
4887 5016 [[package]]
4888 5017 name = "konst"
4889 5018 version = "0.3.17"
@@ -4989,7 +5118,7 @@
4989 5118 "cssparser-color",
4990 5119 "data-encoding",
4991 5120 "getrandom 0.3.4",
4992 - "indexmap",
5121 + "indexmap 2.14.0",
4993 5122 "itertools 0.10.5",
4994 5123 "lazy_static",
4995 5124 "lightningcss-derive",
@@ -5196,12 +5325,12 @@
5196 5325 "metrics-exporter-prometheus",
5197 5326 "mnw-assumptions",
5198 5327 "object 0.39.1",
5199 - "openssl",
5200 5328 "pom-contract",
5201 5329 "proptest",
5202 5330 "rand 0.10.2",
5203 5331 "regex",
5204 5332 "reqwest 0.13.4",
5333 + "rustls",
5205 5334 "s3-storage",
5206 5335 "semver",
5207 5336 "serde",
@@ -5401,7 +5530,7 @@
5401 5530 dependencies = [
5402 5531 "base64 0.22.1",
5403 5532 "evmap",
5404 - "indexmap",
5533 + "indexmap 2.14.0",
5405 5534 "metrics",
5406 5535 "metrics-util",
5407 5536 "quanta",
@@ -5673,7 +5802,7 @@
5673 5802 "crc32fast",
5674 5803 "flate2",
5675 5804 "hashbrown 0.15.5",
5676 - "indexmap",
5805 + "indexmap 2.14.0",
5677 5806 "memchr",
5678 5807 "ruzstd 0.7.3",
5679 5808 ]
@@ -5686,7 +5815,7 @@
5686 5815 dependencies = [
5687 5816 "crc32fast",
5688 5817 "hashbrown 0.16.1",
5689 - "indexmap",
5818 + "indexmap 2.14.0",
5690 5819 "memchr",
5691 5820 ]
5692 5821
@@ -5732,29 +5861,10 @@
5732 5861 checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
5733 5862
5734 5863 [[package]]
5735 - name = "openssl"
5736 - version = "0.10.81"
5864 + name = "opaque-debug"
5865 + version = "0.3.1"
5737 5866 source = "registry+https://github.com/rust-lang/crates.io-index"
5738 - checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45"
5739 - dependencies = [
5740 - "bitflags 2.13.0",
5741 - "cfg-if",
5742 - "foreign-types",
5743 - "libc",
5744 - "openssl-macros",
5745 - "openssl-sys",
5746 - ]
5747 -
5748 - [[package]]
5749 - name = "openssl-macros"
5750 - version = "0.1.1"
5751 - source = "registry+https://github.com/rust-lang/crates.io-index"
5752 - checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
5753 - dependencies = [
5754 - "proc-macro2",
5755 - "quote",
5756 - "syn 2.0.118",
5757 - ]
5867 + checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
5758 5868
5759 5869 [[package]]
5760 5870 name = "openssl-probe"
@@ -5768,28 +5878,6 @@
5768 5878 source = "registry+https://github.com/rust-lang/crates.io-index"
5769 5879 checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
5770 5880
5771 - [[package]]
5772 - name = "openssl-src"
5773 - version = "300.6.1+3.6.3"
5774 - source = "registry+https://github.com/rust-lang/crates.io-index"
5775 - checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846"
5776 - dependencies = [
5777 - "cc",
5778 - ]
5779 -
5780 - [[package]]
5781 - name = "openssl-sys"
5782 - version = "0.9.117"
5783 - source = "registry+https://github.com/rust-lang/crates.io-index"
5784 - checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695"
5785 - dependencies = [
5786 - "cc",
5787 - "libc",
5788 - "openssl-src",
5789 - "pkg-config",
5790 - "vcpkg",
5791 - ]
5792 -
5793 5881 [[package]]
5794 5882 name = "option-ext"
5795 5883 version = "0.2.0"
@@ -5843,6 +5931,20 @@
5843 5931 "sha2 0.10.9",
5844 5932 ]
5845 5933
5934 + [[package]]
5935 + name = "p521"
5936 + version = "0.13.3"
5937 + source = "registry+https://github.com/rust-lang/crates.io-index"
5938 + checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2"
5939 + dependencies = [
5940 + "base16ct",
5941 + "ecdsa",
5942 + "elliptic-curve",
5943 + "primeorder",
5944 + "rand_core 0.6.4",
5945 + "sha2 0.10.9",
5946 + ]
5947 +
5846 5948 [[package]]
5847 5949 name = "parcel_selectors"
5848 5950 version = "0.28.2"
@@ -5917,6 +6019,16 @@
5917 6019 source = "registry+https://github.com/rust-lang/crates.io-index"
5918 6020 checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
5919 6021
6022 + [[package]]
6023 + name = "pbkdf2"
6024 + version = "0.12.2"
6025 + source = "registry+https://github.com/rust-lang/crates.io-index"
6026 + checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
6027 + dependencies = [
6028 + "digest 0.10.7",
6029 + "hmac 0.12.1",
6030 + ]
6031 +
5920 6032 [[package]]
5921 6033 name = "pbkdf2"
5922 6034 version = "0.13.0"
@@ -6109,6 +6221,21 @@
6109 6221 "spki",
6110 6222 ]
6111 6223
6224 + [[package]]
6225 + name = "pkcs5"
6226 + version = "0.7.1"
6227 + source = "registry+https://github.com/rust-lang/crates.io-index"
6228 + checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6"
6229 + dependencies = [
6230 + "aes 0.8.4",
6231 + "cbc",
6232 + "der",
6233 + "pbkdf2 0.12.2",
6234 + "scrypt",
6235 + "sha2 0.10.9",
6236 + "spki",
6237 + ]
6238 +
6112 6239 [[package]]
6113 6240 name = "pkcs8"
6114 6241 version = "0.10.2"
@@ -6116,6 +6243,8 @@
6116 6243 checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
6117 6244 dependencies = [
6118 6245 "der",
6246 + "pkcs5",
6247 + "rand_core 0.6.4",
6119 6248 "spki",
6120 6249 ]
6121 6250
@@ -6138,7 +6267,7 @@
6138 6267 checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85"
6139 6268 dependencies = [
6140 6269 "base64 0.22.1",
6141 - "indexmap",
6270 + "indexmap 2.14.0",
6142 6271 "quick-xml",
6143 6272 "serde",
6144 6273 "time",
@@ -6164,7 +6293,19 @@
6164 6293 checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c"
6165 6294 dependencies = [
6166 6295 "cpufeatures 0.3.0",
6167 - "universal-hash",
6296 + "universal-hash 0.6.1",
6297 + ]
6298 +
6299 + [[package]]
6300 + name = "polyval"
6301 + version = "0.6.2"
6302 + source = "registry+https://github.com/rust-lang/crates.io-index"
6303 + checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
Lines truncated
@@ -72,14 +72,19 @@
72 72 totp-rs = { version = "5.7", features = ["qr"] }
73 73
74 74 # WebAuthn / Passkeys
75 - webauthn-rs = { version = "0.5", features = ["danger-allow-state-serialisation", "conditional-ui"] }
76 - webauthn-rs-proto = "0.5"
77 -
78 - # OpenSSL (transitive dep from webauthn-rs and async-stripe's native-tls:
79 - # vendored for cross-compilation)
80 - openssl = { version = "0.10", features = ["vendored"] }
75 + # 0.6 is where webauthn-rs drops openssl for crypto-glue (RustCrypto), which is
76 + # what gets openssl-sys out of the tree and off cargo-deny's ban list. Still a
77 + # `-dev` prerelease upstream, so the version is pinned exactly rather than left
78 + # to float. The stored `user_passkeys.credential_json` format is unchanged: a
79 + # 0.5-written Passkey round-trips through 0.6 byte-identically, and 0.5 still
80 + # reads what 0.6 writes, so no migration and a rollback stays open.
81 + webauthn-rs = { version = "=0.6.1-dev", features = ["danger-allow-state-serialisation", "conditional-ui"] }
82 + webauthn-rs-proto = "=0.6.1-dev"
81 83
82 84 # Security
85 + # Direct only so `crypto::install_default_crypto_provider` can name a provider;
86 + # every TLS client here reaches rustls transitively.
87 + rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] }
83 88 rand = "0.10"
84 89 hmac = "0.13.0"
85 90 sha1 = "0.11.0"
@@ -207,7 +212,7 @@
207 212 [dev-dependencies]
208 213 tower = { version = "0.5.3", features = ["util"] }
209 214 http-body-util = "0.1"
210 - webauthn-authenticator-rs = { version = "0.5", features = ["softpasskey"] }
215 + webauthn-authenticator-rs = { version = "=0.6.1-dev", features = ["softpasskey"] }
211 216 tempfile = "3"
212 217 proptest = "1"
213 218 wiremock = "0.6"
@@ -5,6 +5,31 @@
5 5
6 6 use crate::error::{AppError, Result};
7 7
8 + /// Install the process-wide rustls [`CryptoProvider`] before anything opens a
9 + /// TLS connection.
10 + ///
11 + /// rustls 0.23 picks a provider from its own crate features, and refuses to
12 + /// guess when more than one is compiled in. Both are: `aws-lc-rs` arrives with
13 + /// rustls itself, `ring` through apple-codesign's signing stack. So the
14 + /// automatic path panics, and it panics lazily, at the first connector build
15 + /// rather than at startup. That put the failure on the Stripe path, which is
16 + /// the worst place to discover it.
17 + ///
18 + /// Naming a provider here makes the choice explicit and moves the failure to
19 + /// process start. Every other rustls consumer in the tree (reqwest via
20 + /// `rustls-platform-verifier`, the AWS SDK) selects its own provider directly
21 + /// and is unaffected either way.
22 + ///
23 + /// Idempotent: a second call is a no-op, so tests can call it per-case without
24 + /// racing. Not `expect`-ing the result is deliberate. `install_default` returns
25 + /// `Err` only when a provider is already installed, which is exactly the state
26 + /// we want.
27 + ///
28 + /// [`CryptoProvider`]: rustls::crypto::CryptoProvider
29 + pub fn install_default_crypto_provider() {
30 + let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
31 + }
32 +
8 33 /// Version-tagged prefix on an encrypted-at-rest TOTP secret. Its presence is
9 34 /// how [`decrypt_totp_secret`] distinguishes a ciphertext from a legacy
10 35 /// plaintext base32 seed during the dual-read migration window.
@@ -25,6 +25,10 @@
25 25 async fn main() {
26 26 dotenvy::dotenv().ok();
27 27
28 + // Before any TLS client is built; see the function's own docs for why this
29 + // cannot be left to rustls to work out.
30 + makenotwork::crypto::install_default_crypto_provider();
31 +
28 32 // JSON in release, human-readable in dev
29 33 tracing_subscriber::registry()
30 34 .with(
@@ -57,6 +57,12 @@
57 57 /// an internal invariant violation (the secret key comes from validated
58 58 /// config), so it is classified `Internal` and surfaces at boot.
59 59 pub fn new(config: &StripeConfig) -> Result<Self> {
60 + // `build()` constructs the rustls connector, which reads the
61 + // process-wide provider and panics if none is installed. Doing it here
62 + // rather than relying on a caller means no construction order can get
63 + // this wrong; the call is idempotent.
64 + crate::crypto::install_default_crypto_provider();
65 +
60 66 let client = ClientBuilder::new(&config.secret_key)
61 67 .timeout(STRIPE_HTTP_TIMEOUT)
62 68 .build()
@@ -368,6 +368,10 @@
368 368
369 369 /// Builder shared by all constructors. Public so workflow tests can use custom `BuildOptions`.
370 370 pub(crate) async fn build(opts: BuildOptions) -> Self {
371 + // `main` does this for the real binary; tests never run `main`, and
372 + // without it the first Stripe connector build panics inside rustls.
373 + makenotwork::crypto::install_default_crypto_provider();
374 +
371 375 let t0 = std::time::Instant::now();
372 376 let test_db = match opts.existing_db {
373 377 Some(db) => db,
@@ -3,6 +3,9 @@
3 3 use crate::harness::TestHarness;
4 4 use makenotwork::db::UserId;
5 5 use url::Url;
6 + // `WebauthnAuthenticator` is a trait in 0.6, blanket-implemented on every
7 + // `AuthenticatorBackend`, so the backend is held directly and the trait is
8 + // imported only to bring do_registration/do_authentication into scope.
6 9 use webauthn_authenticator_rs::{WebauthnAuthenticator, softpasskey::SoftPasskey};
7 10 use webauthn_rs_proto::{CreationChallengeResponse, RequestChallengeResponse};
8 11
@@ -12,15 +15,15 @@
12 15 /// Returns (authenticator, credential_id_base64url), the credential ID is needed
13 16 /// because SoftPasskey doesn't support discoverable credentials (empty allowCredentials),
14 17 /// so we inject it into the authentication challenge.
15 - async fn register_passkey(h: &mut TestHarness) -> (WebauthnAuthenticator<SoftPasskey>, String) {
18 + async fn register_passkey(h: &mut TestHarness) -> (SoftPasskey, String) {
16 19 register_passkey_with_password(h, "Password1!").await
17 20 }
18 21
19 22 async fn register_passkey_with_password(
20 23 h: &mut TestHarness,
21 24 password: &str,
22 - ) -> (WebauthnAuthenticator<SoftPasskey>, String) {
23 - let mut wa = WebauthnAuthenticator::new(SoftPasskey::new(true));
25 + ) -> (SoftPasskey, String) {
26 + let mut wa = SoftPasskey::new(true);
24 27
25 28 // Start registration (requires password confirmation)
26 29 let resp = h
@@ -73,12 +76,7 @@
73 76 /// - `start_discoverable_authentication` places no restriction on which credential responds
74 77 /// - `identify_discoverable_authentication` uses the userHandle to find the user
75 78 /// - `finish_discoverable_authentication` verifies the cryptographic signature
76 - async fn passkey_login(
77 - h: &mut TestHarness,
78 - wa: &mut WebauthnAuthenticator<SoftPasskey>,
79 - cred_id: &str,
80 - user_id: UserId,
81 - ) {
79 + async fn passkey_login(h: &mut TestHarness, wa: &mut SoftPasskey, cred_id: &str, user_id: UserId) {
82 80 // Load a page to establish a fresh session (needed after logout clears cookies)
83 81 h.client.get("/login").await;
84 82
@@ -116,7 +116,7 @@
116 116 .map_err(|e| AppError::BadRequest(format!("Registration failed: {e}")))?;
117 117
118 118 let credential_json = serde_json::to_value(&passkey).context("serialize passkey")?;
119 - let credential_id = passkey.cred_id().to_vec();
119 + let credential_id = passkey.cred_id().clone();
120 120
121 121 db::passkeys::create_passkey(&db, user.id, "Passkey", &credential_json, &credential_id)
122 122 .await