anvilsign in

collin/anvil · 768b0c63

Bump argon2 to 0.6.0-rc.8, collapsing three duplicate crates

Collin Richards · 2026-08-25 05:46 UTC · 768b0c635e9d6a49b26c1ef6249d774220225a5c · parent d60232e6 · browse files

modifiedCargo.lock+11 −38
⋯ 179 unchanged lines
180180 dependencies = [
181181 "aes-gcm",
182182 "anvil-job",
183- "argon2 0.5.3",
183+ "argon2",
184184 "async-trait",
185185 "base64 0.23.1",
186186 "curve25519-dalek",
⋯ 3 unchanged lines
190190 "jaq-json",
191191 "jaq-std",
192192 "pulldown-cmark",
193+ "rand 0.10.2",
193194 "rusqlite",
194195 "serde",
195196 "serde_json",
⋯ 61 unchanged lines
257258 "anvil-core",
258259 "anvil-git",
259260 "anvil-job",
260- "argon2 0.5.3",
261+ "argon2",
261262 "axum",
262263 "axum-extra",
263264 "base64 0.23.1",
⋯ 1 unchanged line
265266 "lru",
266267 "maud",
267268 "pulldown-cmark",
269+ "rand 0.10.2",
268270 "reqwest",
269271 "ring",
270272 "rsa 0.9.10",
⋯ 49 unchanged lines
320322
321323 [[package]]
322324 name = "argon2"
323-version = "0.5.3"
324-source = "registry+https://github.com/rust-lang/crates.io-index"
325-checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
326-dependencies = [
327- "base64ct",
328- "blake2 0.10.6",
329- "cpufeatures 0.2.17",
330- "password-hash 0.5.0",
331-]
332-
333-[[package]]
334-name = "argon2"
335325 version = "0.6.0-rc.8"
336326 source = "registry+https://github.com/rust-lang/crates.io-index"
337327 checksum = "7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5"
338328 dependencies = [
339329 "base64ct",
340- "blake2 0.11.0-rc.6",
330+ "blake2",
341331 "cpufeatures 0.3.0",
342- "password-hash 0.6.1",
332+ "password-hash",
343333 ]
344334
345335 [[package]]
⋯ 180 unchanged lines
526516
527517 [[package]]
528518 name = "blake2"
529-version = "0.10.6"
530-source = "registry+https://github.com/rust-lang/crates.io-index"
531-checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
532-dependencies = [
533- "digest 0.10.7",
534-]
535-
536-[[package]]
537-name = "blake2"
538519 version = "0.11.0-rc.6"
539520 source = "registry+https://github.com/rust-lang/crates.io-index"
540521 checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690"
⋯ 628 unchanged lines
11691150 "block-buffer 0.10.4",
11701151 "const-oid 0.9.6",
11711152 "crypto-common 0.1.7",
1172- "subtle",
11731153 ]
11741154
11751155 [[package]]
⋯ 2277 unchanged lines
34533433 "redox_syscall",
34543434 "smallvec",
34553435 "windows-link",
3456-]
3457-
3458-[[package]]
3459-name = "password-hash"
3460-version = "0.5.0"
3461-source = "registry+https://github.com/rust-lang/crates.io-index"
3462-checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
3463-dependencies = [
3464- "base64ct",
3465- "rand_core 0.6.4",
3466- "subtle",
34673436 ]
34683437
34693438 [[package]]
⋯ 2 unchanged lines
34723441 source = "registry+https://github.com/rust-lang/crates.io-index"
34733442 checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
34743443 dependencies = [
3444+ "getrandom 0.4.3",
34753445 "phc",
3446+ "rand_core 0.10.1",
34763447 ]
34773448
34783449 [[package]]
⋯ 38 unchanged lines
35173488 dependencies = [
35183489 "base64ct",
35193490 "ctutils",
3491+ "getrandom 0.4.3",
3492+ "rand_core 0.10.1",
35203493 ]
35213494
35223495 [[package]]
⋯ 1276 unchanged lines
47994772 source = "registry+https://github.com/rust-lang/crates.io-index"
48004773 checksum = "f9a32fae177b74a22aa9c5b01bf7e68b33545be32d9e381e248058d2adc15ce3"
48014774 dependencies = [
4802- "argon2 0.6.0-rc.8",
4775+ "argon2",
48034776 "bcrypt-pbkdf",
48044777 "ctutils",
48054778 "ed25519-dalek",
⋯ 1228 unchanged lines
modifiedCargo.toml+1 −1
⋯ 80 unchanged lines
8181
8282 aes-gcm = { version = "0.11.1" }
8383 anyhow = { version = "1" }
84-argon2 = { version = "0.5", features = ["std"] }
84+argon2 = { version = "0.6.0-rc.8", features = ["rand_core"] }
8585 async-trait = { version = "0.1" }
8686 axum = { version = "0.8", features = ["ws"] }
8787 axum-extra = { version = "0.12.6", features = ["cookie"] }
⋯ 38 unchanged lines
modifiedclippy.toml+2 −10
⋯ 6 unchanged lines
77 # list doubles as the "what are we waiting on" record — when a bump makes an
88 # entry unnecessary, delete it rather than leaving it to rot.
99 #
10-# Nothing here is ours to collapse today, with one exception noted below.
10+# Nothing here is ours to collapse today: `argon2` was, and was taken, which
11+# is what removed argon2, blake2 and password-hash from this list.
1112
1213 allowed-duplicate-crates = [
1314 # --- The RustCrypto `digest` 0.10 -> 0.11 seam ------------------------
⋯ 9 unchanged lines
2324 "generic-array",
2425 "sha1",
2526 "sha3",
26-
27- # `argon2` is the one duplicate we could remove ourselves: we pin 0.5 and
28- # ssh-key pulls 0.6.0-rc.8. Deliberately not taken — 0.6 is a pre-release
29- # and argon2 is what verifies account passwords. Moving it would also
30- # collapse `blake2` and `password-hash`, which are here only because
31- # argon2 0.5 holds the old copy.
32- "argon2",
33- "blake2",
34- "password-hash",
3527
3628 # --- Held by direct dependencies of ours ------------------------------
3729 "base64", # axum 0.8 is on 0.22; we are on 0.23.
⋯ 12 unchanged lines
modifiedcrates/anvil-core/Cargo.toml+1 −0
⋯ 16 unchanged lines
1717 toasty.workspace = true
1818 rusqlite.workspace = true
1919 argon2.workspace = true
20+rand.workspace = true
2021 aes-gcm.workspace = true
2122 curve25519-dalek.workspace = true
2223 base64.workspace = true
⋯ 22 unchanged lines
modifiedcrates/anvil-core/src/api_tokens.rs+5 −4
⋯ 47 unchanged lines
4848 name: &str,
4949 scopes: &str,
5050 ) -> Result<(ApiToken, String)> {
51- use argon2::password_hash::rand_core::{
52- OsRng,
53- RngCore,
51+ use rand::{
52+ Rng,
53+ rand_core::UnwrapErr,
54+ rngs::SysRng,
5455 };
5556 let mut raw = [0u8; 32];
56- OsRng.fill_bytes(&mut raw);
57+ UnwrapErr(SysRng).fill_bytes(&mut raw);
5758 let plaintext = format!(
5859 "{PREFIX}{}",
5960 raw.iter().map(|b| format!("{b:02x}")).collect::<String>()
⋯ 85 unchanged lines
modifiedcrates/anvil-core/src/lib.rs+5 −4
⋯ 140 unchanged lines
141141
142142 /// Load the persistent CSRF secret, generating and saving it on first run.
143143 fn load_or_create_csrf_secret(data_dir: &std::path::Path) -> Result<[u8; 32]> {
144- use argon2::password_hash::rand_core::{
145- OsRng,
146- RngCore,
144+ use rand::{
145+ Rng,
146+ rand_core::UnwrapErr,
147+ rngs::SysRng,
147148 };
148149
149150 let path = data_dir.join("csrf_secret");
⋯ 5 unchanged lines
155156 // Malformed (truncated/extended) — regenerate rather than run weak.
156157 }
157158 let mut secret = [0u8; 32];
158- OsRng.fill_bytes(&mut secret);
159+ UnwrapErr(SysRng).fill_bytes(&mut secret);
159160 std::fs::write(&path, secret)?;
160161 #[cfg(unix)]
161162 {
⋯ 5 unchanged lines
modifiedcrates/anvil-core/src/secrets.rs+7 −4
⋯ 363 unchanged lines
364364 // --- primitives ------------------------------------------------------------
365365
366366 fn random_bytes<const N: usize>() -> [u8; N] {
367- use argon2::password_hash::rand_core::{
368- OsRng,
369- RngCore,
367+ // `UnwrapErr(SysRng)` is the OS generator, panicking if it ever fails —
368+ // what `OsRng.fill_bytes` did before rand 0.10 renamed and split the two.
369+ use rand::{
370+ Rng,
371+ rand_core::UnwrapErr,
372+ rngs::SysRng,
370373 };
371374 let mut bytes = [0u8; N];
372- OsRng.fill_bytes(&mut bytes);
375+ UnwrapErr(SysRng).fill_bytes(&mut bytes);
373376 bytes
374377 }
375378
⋯ 715 unchanged lines
modifiedcrates/anvil-core/src/sessions.rs+5 −4
11 //! Web login sessions: opaque random tokens stored server-side and referenced
22 //! by a cookie.
33
4-use argon2::password_hash::rand_core::{
5- OsRng,
6- RngCore,
4+use rand::{
5+ Rng,
6+ rand_core::UnwrapErr,
7+ rngs::SysRng,
78 };
89
910 use crate::{
⋯ 10 unchanged lines
2021 /// Generate a 256-bit random session token, hex-encoded.
2122 fn generate_token() -> String {
2223 let mut bytes = [0u8; 32];
23- let mut rng = OsRng;
24+ let mut rng = UnwrapErr(SysRng);
2425 rng.fill_bytes(&mut bytes);
2526 bytes.iter().map(|b| format!("{b:02x}")).collect()
2627 }
⋯ 55 unchanged lines
modifiedcrates/anvil-core/src/users.rs+24 −5
⋯ 1 unchanged line
22
33 use argon2::{
44 Argon2,
5+ PasswordHash,
56 password_hash::{
6- PasswordHash,
77 PasswordHasher,
88 PasswordVerifier,
9- SaltString,
10- rand_core::OsRng,
119 },
1210 };
1311
⋯ 25 unchanged lines
3937 ];
4038
4139 /// Hash a plaintext password into a PHC-format Argon2 string.
40+///
41+/// argon2 0.6 generates the salt itself rather than taking one, so there is no
42+/// `SaltString` here any more; it still draws from the OS and still lands in
43+/// the PHC string, so stored hashes are the same shape as before.
4244 pub fn hash_password(password: &str) -> Result<String> {
43- let salt = SaltString::generate(&mut OsRng);
4445 Argon2::default()
45- .hash_password(password.as_bytes(), &salt)
46+ .hash_password(password.as_bytes())
4647 .map(|h| h.to_string())
4748 .map_err(|e| Error::Password(e.to_string()))
4849 }
⋯ 267 unchanged lines
316317 mod tests {
317318 use super::*;
318319
320+ /// Every account provisioned before an argon2 bump has its password stored
321+ /// as a PHC string written by the *old* library, and the only thing that
322+ /// keeps those logins working is that the new one still reads them. This
323+ /// hash was produced by argon2 0.5 for the password below; if a future
324+ /// bump breaks it, every existing account is locked out and no other test
325+ /// here would notice, because they all hash and verify within one version.
326+ const PHC_FROM_ARGON2_0_5: &str = "$argon2id$v=19$m=19456,t=2,p=1$\
327+ ERcTTFqHk+Nvke8V0i1j6Q$jQrVdlL3mxHw5/0culSLYUXyHX5++eQGOUI/W3txoR4";
328+
329+ #[test]
330+ fn verifies_a_hash_written_by_an_older_argon2() {
331+ assert!(
332+ verify_password(PHC_FROM_ARGON2_0_5, "correct horse battery staple").unwrap(),
333+ "a password hash written by argon2 0.5 no longer verifies"
334+ );
335+ assert!(!verify_password(PHC_FROM_ARGON2_0_5, "wrong password").unwrap());
336+ }
337+
319338 /// A reset must persist a hash the login path accepts, and retire the old
320339 /// password.
321340 #[tokio::test]
⋯ 28 unchanged lines
modifiedcrates/anvil-web/Cargo.toml+1 −0
⋯ 39 unchanged lines
4040 tempfile = { version = "3" }
4141 serde_json.workspace = true
4242 argon2.workspace = true
43+rand.workspace = true
4344 ssh-key = { workspace = true, features = ["ed25519"] }
4445 tokio = { workspace = true }
4546 tower = { workspace = true, features = ["util"] }
⋯ 8 unchanged lines
modifiedcrates/anvil-web/tests/js_interop.rs+5 −4
⋯ 30 unchanged lines
3131 }
3232
3333 fn getrandom(buf: &mut [u8]) {
34- use argon2::password_hash::rand_core::{
35- OsRng,
36- RngCore,
34+ use rand::{
35+ Rng,
36+ rand_core::UnwrapErr,
37+ rngs::SysRng,
3738 };
38- OsRng.fill_bytes(buf);
39+ UnwrapErr(SysRng).fill_bytes(buf);
3940 }
4041
4142 fn node_available() -> bool {
⋯ 146 unchanged lines