diff --git a/Cargo.lock b/Cargo.lock index 81fc9a2..11e9184 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,17 +105,6 @@ version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" -[[package]] -name = "async-trait" -version = "0.1.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - [[package]] name = "base64ct" version = "1.8.3" @@ -146,12 +135,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "bytes" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" - [[package]] name = "cc" version = "1.5.1" @@ -338,17 +321,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "displaydoc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - [[package]] name = "ed25519" version = "2.2.3" @@ -374,18 +346,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -410,15 +370,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - [[package]] name = "fumi" version = "0.1.0" @@ -427,7 +378,6 @@ dependencies = [ "clap", "data-encoding", "fumi-core", - "hickory-resolver", "rand_core", ] @@ -450,45 +400,6 @@ dependencies = [ "x25519-dalek", ] -[[package]] -name = "futures-channel" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" - -[[package]] -name = "futures-io" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" - -[[package]] -name = "futures-task" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" - -[[package]] -name = "futures-util" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -544,51 +455,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hickory-proto" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna", - "ipnet", - "once_cell", - "rand", - "thiserror", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "hickory-resolver" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" -dependencies = [ - "cfg-if", - "futures-util", - "hickory-proto", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand", - "resolv-conf", - "smallvec", - "thiserror", - "tokio", - "tracing", -] - [[package]] name = "hkdf" version = "0.12.4" @@ -607,110 +473,6 @@ dependencies = [ "digest", ] -[[package]] -name = "icu_collections" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" - -[[package]] -name = "icu_properties" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" - -[[package]] -name = "icu_provider" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - [[package]] name = "inout" version = "0.1.4" @@ -720,25 +482,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "ipconfig" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" -dependencies = [ - "socket2", - "widestring", - "windows-registry", - "windows-result", - "windows-sys", -] - -[[package]] -name = "ipnet" -version = "2.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -768,53 +511,12 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "litemap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "memchr" version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" -[[package]] -name = "mio" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" -dependencies = [ - "libc", - "wasi", - "windows-sys", -] - [[package]] name = "once_cell" version = "1.21.4" @@ -833,41 +535,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - [[package]] name = "pkcs8" version = "0.10.2" @@ -907,24 +574,6 @@ dependencies = [ "universal-hash", ] -[[package]] -name = "potential_utf" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro2" version = "1.0.107" @@ -943,27 +592,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -973,21 +601,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "resolv-conf" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" - [[package]] name = "rusqlite" version = "0.32.1" @@ -1011,12 +624,6 @@ dependencies = [ "semver", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "semver" version = "1.0.28" @@ -1092,12 +699,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - [[package]] name = "smallvec" version = "1.16.2" @@ -1120,16 +721,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "socket2" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" -dependencies = [ - "libc", - "windows-sys", -] - [[package]] name = "spki" version = "0.7.3" @@ -1140,12 +731,6 @@ dependencies = [ "der", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - [[package]] name = "strsim" version = "0.11.1" @@ -1180,98 +765,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "tinystr" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" - -[[package]] -name = "tokio" -version = "1.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "windows-sys", -] - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - [[package]] name = "typenum" version = "1.20.1" @@ -1294,24 +787,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "utf8parse" version = "0.2.2" @@ -1336,47 +811,12 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "widestring" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -1386,12 +826,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "writeable" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" - [[package]] name = "x25519-dalek" version = "2.0.1" @@ -1404,29 +838,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.8.59" @@ -1447,27 +858,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", - "synstructure", -] - [[package]] name = "zeroize" version = "1.9.0" @@ -1488,39 +878,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "zerotrie" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - [[package]] name = "zmij" version = "1.0.23" diff --git a/README.md b/README.md index bd169a5..2bbd0f4 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,6 @@ smol+rns://alice@8f2c1d0a7b6e5f4c3d2a1b0e9f8c7d6a short form over smol+rns://alice@8f2c1d0a7b6e5f4c3d2a1b0e9f8c7d6a/mfrggzdfzt... self-certifying over Reticulum ``` -When a TCP address carries no port, the CLI discovers one by DNS SRV before dialing: `_smolmail._tcp.` states the target and port serving that domain's mail (lowest priority wins, weight ignored), the discovered target is only a dial hint, and the domain keeps every identity role. No record, a failing lookup, or an explicit port means the default 1961 — the feature is purely additive, and old deployments never notice it existed. Discovery runs when an address is given; a stored account is dialed at the port it was registered with. - -Server pins are scoped by port: `trust example.org` pins the default port, and `trust example.org:1962` pins any other — one host may serve several domains, each with its own key. The fallback is asymmetric by design: the default port inherits the bare-host pin existing stores already hold, and a non-default port never falls back to it — the first dial at a new port is trust on first use, like any first contact, rather than a mismatch against its sibling's key. - ## Usage One master per store, selected by the global `--key` and `--db` flags; a second identity is a second pair of files. @@ -35,7 +31,7 @@ fumi restore
recover local state from the master a fumi rotate advance the rotation index, sign and push the certificate # servers and contacts -fumi trust [--force] pin a server's Noise static key, scoped by port +fumi trust [--force] pin a server's Noise static key fumi register
[--invite TOKEN] bind this identity to a username fumi resolve
look up a contact's key, walk the chain, pin it fumi import add a contact from a self-certifying address @@ -100,7 +96,7 @@ Every mail column holds a sealed envelope, never plaintext. Timestamps are Unix | Table | Columns | Meaning and invariants | |---|---|---| | `state` | one row, `id = 1`, always present | `username`, `host`, `port`, `scheme` (`tcp` or `rns`) are the account; `rotations` is the sec 2 rotation index the account is at; `after_time` and `after_id` are the sec 6.1 fetch cursor; `sync_ok` (0/1) says whether the local accept-token set may replace the server's — a client restored from the master alone must not erase it (sec 4) | -| `servers` | `host`, `static`, `pinned_at` | The sec 4 pins: a server's Noise static key, keyed by host on the default port and by `host:port` on any other — the same label the trust errors name | +| `servers` | `host`, `static`, `pinned_at` | The sec 4 pins: a server's Noise static key, keyed by host without port | | `contacts` | `address`, `identity`, `verified`, `seen_at` | Every key known for an address; `verified` is 1 only when it came from a self-certifying URI | | `accepted` | `address`, `identity`, `active`, `added_at` | Main-tier admission (sec 5.8). `identity` is frozen at acceptance because the token the correspondent holds is derived from it; `active = 0` is a block and keeps the row | | `tokens` | `address`, `token`, `seen_at` | Accept tokens correspondents issued us, filed under their address, which outlives the keys behind it (sec 5.8) | diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 57abec8..b1d2796 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -20,4 +20,3 @@ clap = { version = "4", features = ["derive"] } anyhow = "1" rand_core = { version = "0.6", features = ["getrandom"] } data-encoding = "2" -hickory-resolver = "0.24" diff --git a/cli/src/main.rs b/cli/src/main.rs index be2db48..e3f0ee8 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -15,8 +15,6 @@ use fumi::client::{self, Pushed, SendDraft, TrustChange}; use fumi::crypto::{b32, fingerprint, unb32, KEY_LEN}; use fumi::store::Store; - -mod srv; use fumi::transport::ID_LEN; #[derive(Parser)] @@ -172,9 +170,8 @@ fn main() { /// reports the distinction; only the CLI knows the command. fn hint(error: Option<&fumi::error::Error>) { match error { - Some(fumi::error::Error::NotPinned { host, port }) => eprintln!( - "obtain the key from the operator through a trusted channel, then:\n fumi trust {} ", - fumi::address::trust_label(host, *port) + Some(fumi::error::Error::NotPinned { host }) => eprintln!( + "obtain the key from the operator through a trusted channel, then:\n fumi trust {host} " ), Some(fumi::error::Error::KeyChanged { address, offered, .. }) => { if let Ok(addr) = Address::parse(address) { @@ -327,39 +324,9 @@ fn whoami(key: &PathBuf, store: &Store) -> Result<()> { Ok(()) } -/// Parses an address and applies SRV discovery when it is TCP and carried no -/// explicit port (agreed with bunshin 2026-09-30): the discovered port scopes -/// the pin and the SRV target is only a dial hint, so the domain keeps every -/// identity role. A miss keeps the default port — one DNS lookup on the -/// failure path and nothing else. Discovery happens when an address is -/// given; the stored account is dialed at the port it was registered with, -/// so an operator moving ports does not move existing accounts. -fn parse_dial_address(raw: &str) -> Result
{ - let mut addr = Address::parse(raw)?; - if addr.scheme == fumi::address::Scheme::Tcp && !port_is_explicit(raw) { - if let Some(endpoint) = srv::discover(&addr.host) { - addr.port = endpoint.port; - addr = addr.with_dial(&endpoint.target); - } - } - Ok(addr) -} - -/// Whether the address text states a port after the host: the same -/// right-hand split `trust` uses, over the host part of either address -/// form, so an invalid suffix is not a port and an explicit port is never -/// overridden by discovery. -fn port_is_explicit(raw: &str) -> bool { - let host_part = raw.rsplit_once('@').map_or(raw, |(_, host)| host); - let host_part = host_part.split('/').next().unwrap_or(host_part); - host_part - .rsplit_once(':') - .is_some_and(|(_, port)| port.parse::().is_ok()) -} - fn restore(key: &PathBuf, store: &Store, address: &str, timeout: u64) -> Result<()> { let master = load_master(key)?; - let addr = parse_dial_address(address)?; + let addr = Address::parse(address)?; let index = client::restore(store, &master, &addr, timeout)?; let identity = fumi::account::Identity::from_seed(identity_seed(&master, index)); println!("restored {} at rotation {index}", addr.short()); @@ -391,30 +358,21 @@ fn trust(store: &Store, host: &str, key_b32: &str, force: bool) -> Result<()> { v.len() )) })?; - // A pin is scoped per server and port (address::trust_label): the port - // splits from the right so a bare host means the default port, and an - // explicit default port normalizes onto the same bare row existing - // stores already hold. - let (host, port) = match host.rsplit_once(':') { - Some((h, p)) => match p.parse::() { - Ok(port) => (h, port), - Err(_) => (host, fumi::address::DEFAULT_PORT), - }, - None => (host, fumi::address::DEFAULT_PORT), - }; - match store.server_pin(host, port)? { + // A pin is per server, and the reference client strips any :port the + // same way its address grammar does: at the first colon. + let host = host.split(':').next().unwrap_or(host); + match store.server_pin(host)? { Some(old) if old != key && !force => { return Err(anyhow!(format!( - "{} is already pinned to {}; use --force to replace", - fumi::address::trust_label(host, port), + "{host} is already pinned to {}; use --force to replace", b32(&old) )) .into()); } _ => {} } - store.pin_server(host, port, &key)?; - println!("pinned {} {}", fumi::address::trust_label(host, port), b32(&key)); + store.pin_server(host, &key)?; + println!("pinned {host} {}", b32(&key)); Ok(()) } @@ -426,7 +384,7 @@ fn register( timeout: u64, ) -> Result<()> { let account = load_account(key, store)?; - let addr = parse_dial_address(address)?; + let addr = Address::parse(address)?; client::register(store, &addr, &account, invite, timeout)?; println!("registered {}", addr.short()); println!("share: {}", addr.uri(&account.me().pk())); @@ -434,7 +392,7 @@ fn register( } fn resolve(store: &Store, address: &str, timeout: u64) -> Result<()> { - let addr = parse_dial_address(address)?; + let addr = Address::parse(address)?; if addr.identity.is_some() { return Err( anyhow!("that address already carries a key; use `import` instead").into(), @@ -591,7 +549,7 @@ fn send(cli: &Cli, store: &Store) -> Result<()> { unreachable!("send dispatches only from the Send command") }; let account = load_account(&cli.key, store)?; - let addr = parse_dial_address(address)?; + let addr = Address::parse(address)?; let text = match (body, file) { (Some(text), _) => text.clone(), (None, Some(path)) if path == "-" => read_stdin()?, diff --git a/cli/src/srv.rs b/cli/src/srv.rs deleted file mode 100644 index 14ca072..0000000 --- a/cli/src/srv.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! SRV discovery for portless TCP addresses (agreed with bunshin -//! 2026-09-30): `_smolmail._tcp.` states which target and port serve a -//! domain's mail. Discovery is routing only — a lying record lands the client -//! on a server with the wrong static key, where the handshake dies exactly as -//! it does today; the pin stays out of band. No record or a failing lookup -//! means the default port, so the feature is purely additive and old -//! deployments never notice it existed. - -/// One endpoint as an SRV record states it. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Endpoint { - /// The host to dial — a routing fact only, never an identity. - pub target: String, - pub port: u16, -} - -/// One SRV record, in the shape `pick` reasons about. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Record { - pub priority: u16, - pub target: String, - pub port: u16, -} - -/// The label the convention reserves (ratified with bunshin): it matches the -/// RNS destination `smolmail.server` and the reference client's name. -pub const SERVICE: &str = "_smolmail._tcp"; - -/// Picks the endpoint: lowest priority wins and weight is ignored — -/// fetching mail needs no client-side load balancing. -pub fn pick(records: &[Record]) -> Option<&Record> { - records.iter().min_by_key(|record| record.priority) -} - -/// Queries `_smolmail._tcp.` through the system resolver. Discovery -/// never fails an operation: any miss or error is simply no endpoint, and -/// the caller keeps the default port. -pub fn discover(host: &str) -> Option { - let name = format!("{SERVICE}.{host}."); - let resolver = hickory_resolver::Resolver::from_system_conf().ok()?; - let srv = resolver.srv_lookup(name).ok()?; - let records: Vec = srv - .iter() - .map(|record| Record { - priority: record.priority(), - target: record.target().to_string(), - port: record.port(), - }) - .collect(); - pick(&records).map(|record| Endpoint { - target: record.target.trim_end_matches('.').to_string(), - port: record.port, - }) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn lowest_priority_wins_and_weight_is_ignored() { - let records = [ - Record { priority: 10, target: "a.example.net".into(), port: 1962 }, - Record { priority: 5, target: "b.example.net".into(), port: 1963 }, - Record { priority: 10, target: "c.example.net".into(), port: 1964 }, - ]; - let picked = pick(&records).expect("records exist"); - assert_eq!((picked.target.as_str(), picked.port), ("b.example.net", 1963)); - } - - #[test] - fn no_records_is_no_endpoint() { - assert!(pick(&[]).is_none()); - } -} diff --git a/core/src/address.rs b/core/src/address.rs index c48ab0e..97aaf04 100644 --- a/core/src/address.rs +++ b/core/src/address.rs @@ -5,22 +5,6 @@ use crate::crypto::{b32, unb32, KEY_LEN}; use crate::error::Error; pub const DEFAULT_PORT: u16 = 1961; - -/// The trust label of a host: the bare host on the default port, `host:port` -/// on any other. One rule for the store's pin keys and the trust errors, so -/// a pin and its error always name the same trust object. The fallback is -/// asymmetric by design: the default port inherits the legacy bare-host pin, -/// and a non-default port never falls back to it — a second domain on one -/// host earns its own trust on first use instead of a mismatch against its -/// sibling's key. -pub fn trust_label(host: &str, port: u16) -> String { - if port == DEFAULT_PORT { - host.to_string() - } else { - format!("{host}:{port}") - } -} - /// A Reticulum destination hash, as printed by every RNS tool: 32 lowercase /// hexadecimal characters (RNS.md sec 13.2). pub const RNS_HASH_HEX: usize = 32; diff --git a/core/src/client.rs b/core/src/client.rs index ce65554..9d58c85 100644 --- a/core/src/client.rs +++ b/core/src/client.rs @@ -79,11 +79,10 @@ pub fn connect( ) -> Result { match addr.scheme { Scheme::Tcp => { - let pinned = store.server_pin(&addr.host, addr.port)?; + let pinned = store.server_pin(&addr.host)?; if pinned.is_none() && require_pin { return Err(Error::NotPinned { host: addr.host.clone(), - port: addr.port, }); } // The dial hint lets a host that resolves DNS itself (an @@ -95,19 +94,6 @@ pub fn connect( pinned, timeout, )?; - // Pin enforcement lives here, one layer above the transport: the - // identity host names the trust object even when a dial hint - // routed the packets elsewhere (sec 4). - if let Some(pinned) = pinned { - if !ct_eq(&pinned, transport.server_static()) { - return Err(Error::PinMismatch { - host: addr.host.clone(), - port: addr.port, - pinned, - presented: *transport.server_static(), - }); - } - } let unpinned_static = if pinned.is_none() { Some(*transport.server_static()) } else { diff --git a/core/src/error.rs b/core/src/error.rs index 5ef7904..4270e37 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -26,14 +26,12 @@ pub enum Error { UnknownStatus(u8, String), /// The host has no pinned server key, so the session cannot be /// authenticated (sec 4). A GUI routes this to pinning; an auth failure - /// is terminal. The port scopes the pin (address::trust_label). - NotPinned { host: String, port: u16 }, + /// is terminal. + NotPinned { host: String }, /// A pinned server presented a different key (sec 4): a hard abort, the - /// pin is the entire trust model. The port scopes the pin - /// (address::trust_label). + /// pin is the entire trust model. PinMismatch { host: String, - port: u16, pinned: [u8; KEY_LEN], presented: [u8; KEY_LEN], }, @@ -91,20 +89,14 @@ impl fmt::Display for Error { Self::UnknownStatus(status, what) => { write!(f, "{what} failed: unknown status {status}") } - Self::NotPinned { host, port } => write!( - f, - "no pinned key for {}", - crate::address::trust_label(host, *port) - ), + Self::NotPinned { host } => write!(f, "no pinned key for {host}"), Self::PinMismatch { host, - port, pinned, presented, } => write!( f, - "{} presented a different key than the one pinned\n pinned: {}\n presented: {}", - crate::address::trust_label(host, *port), + "{host} presented a different key than the one pinned\n pinned: {}\n presented: {}", b32(pinned), b32(presented) ), diff --git a/core/src/export.rs b/core/src/export.rs index 9fd7b59..645acac 100644 --- a/core/src/export.rs +++ b/core/src/export.rs @@ -249,21 +249,11 @@ pub fn import(store: &Store, master: &[u8; KEY_LEN], text: &str) -> Result = unb32(key).ok().and_then(|k| k.try_into().ok()); - // A backup pin key is a bare host (the default port) or host:port, - // scoping exactly as the live store does; the port splits from the - // right, so a host without a valid port suffix stays whole. - let (host, port) = match host.rsplit_once(':') { - Some((h, p)) => match p.parse::() { - Ok(port) => (h.to_string(), port), - Err(_) => (host.clone(), crate::address::DEFAULT_PORT), - }, - None => (host.clone(), crate::address::DEFAULT_PORT), - }; match key { None => summary.malformed += 1, - Some(key) => match store.server_pin(&host, port)? { + Some(key) => match store.server_pin(host)? { None => { - store.pin_server(&host, port, &key)?; + store.pin_server(host, &key)?; summary.pins_added += 1; } Some(existing) if existing != key => summary.pins_conflicted += 1, @@ -347,7 +337,7 @@ mod tests { fn seeded_store() -> (Store, [u8; KEY_LEN]) { let store = Store::open_in_memory().unwrap(); let master = [7u8; KEY_LEN]; - store.pin_server("example.org", crate::address::DEFAULT_PORT, &[1u8; KEY_LEN]).unwrap(); + store.pin_server("example.org", &[1u8; KEY_LEN]).unwrap(); store .save_contact("alice@example.org", &[2u8; KEY_LEN], true) .unwrap(); @@ -376,7 +366,7 @@ mod tests { assert_eq!(summary.contacts_added, 1); assert_eq!(summary.malformed, 0); - assert_eq!(fresh.server_pin("example.org", crate::address::DEFAULT_PORT).unwrap(), Some([1u8; KEY_LEN])); + assert_eq!(fresh.server_pin("example.org").unwrap(), Some([1u8; KEY_LEN])); let (key, verified) = fresh.contact("alice@example.org").unwrap().unwrap(); assert_eq!(key, [2u8; KEY_LEN]); assert!(verified); @@ -405,13 +395,13 @@ mod tests { let file = export(&store, &master).unwrap(); let mine = Store::open_in_memory().unwrap(); - mine.pin_server("example.org", crate::address::DEFAULT_PORT, &[6u8; KEY_LEN]).unwrap(); + mine.pin_server("example.org", &[6u8; KEY_LEN]).unwrap(); mine.save_contact("alice@example.org", &[7u8; KEY_LEN], false) .unwrap(); let summary = import(&mine, &master, &file).unwrap(); assert_eq!(summary.pins_conflicted, 1); assert_eq!(summary.contacts_conflicted, 1); - assert_eq!(mine.server_pin("example.org", crate::address::DEFAULT_PORT).unwrap(), Some([6u8; KEY_LEN])); + assert_eq!(mine.server_pin("example.org").unwrap(), Some([6u8; KEY_LEN])); let (key, _) = mine.contact("alice@example.org").unwrap().unwrap(); assert_eq!(key, [7u8; KEY_LEN]); } diff --git a/core/src/store.rs b/core/src/store.rs index 08ed98b..a13258c 100644 --- a/core/src/store.rs +++ b/core/src/store.rs @@ -276,12 +276,9 @@ impl Store { Ok(()) } - pub fn server_pin(&self, host: &str, port: u16) -> Result, Error> { + pub fn server_pin(&self, host: &str) -> Result, Error> { Ok(self - .one::>>( - "SELECT static FROM servers WHERE host = ?1", - &[&crate::address::trust_label(host, port)], - )? + .one::>>("SELECT static FROM servers WHERE host = ?1", &[&host])? .flatten() .and_then(|k| k.try_into().ok())) } @@ -289,11 +286,9 @@ impl Store { /// Removes a pin: the settings screen's unpin, the inverse of /// `pin_server`. The next session against that host is trust on first /// use again (sec 4, sec 8). - pub fn unpin_server(&self, host: &str, port: u16) -> Result<(), Error> { - self.db().execute( - "DELETE FROM servers WHERE host = ?1", - params![&crate::address::trust_label(host, port)], - )?; + pub fn unpin_server(&self, host: &str) -> Result<(), Error> { + self.db() + .execute("DELETE FROM servers WHERE host = ?1", params![host])?; Ok(()) } @@ -312,20 +307,12 @@ impl Store { .collect()) } - /// Pins a server key, scoped by port (address::trust_label): the default - /// port keeps the bare-host row existing stores already hold, and any - /// other port earns its own row. - pub fn pin_server( - &self, - host: &str, - port: u16, - key: &[u8; KEY_LEN], - ) -> Result<(), Error> { + pub fn pin_server(&self, host: &str, key: &[u8; KEY_LEN]) -> Result<(), Error> { self.db() .execute( "INSERT INTO servers (host, static, pinned_at) VALUES (?1, ?2, ?3) \ ON CONFLICT (host) DO UPDATE SET static = ?2, pinned_at = ?3", - params![&crate::address::trust_label(host, port), key, now()], + params![host, key, now()], ) .map(|_| ())?; Ok(()) @@ -672,7 +659,6 @@ fn row_stored(row: &rusqlite::Row<'_>) -> rusqlite::Result { mod tests { use super::*; use crate::account::{identity_seed, Account}; - use crate::address::DEFAULT_PORT; fn master() -> [u8; 32] { core::array::from_fn(|i| i as u8) @@ -812,11 +798,11 @@ mod tests { #[test] fn pins_and_contacts_round_trip() { let store = temp_store("pins"); - assert!(store.server_pin("example.org", DEFAULT_PORT).unwrap().is_none()); - store.pin_server("example.org", DEFAULT_PORT, &[5u8; 32]).unwrap(); - assert_eq!(store.server_pin("example.org", DEFAULT_PORT).unwrap(), Some([5u8; 32])); - store.pin_server("example.org", DEFAULT_PORT, &[6u8; 32]).unwrap(); - assert_eq!(store.server_pin("example.org", DEFAULT_PORT).unwrap(), Some([6u8; 32])); + assert!(store.server_pin("example.org").unwrap().is_none()); + store.pin_server("example.org", &[5u8; 32]).unwrap(); + assert_eq!(store.server_pin("example.org").unwrap(), Some([5u8; 32])); + store.pin_server("example.org", &[6u8; 32]).unwrap(); + assert_eq!(store.server_pin("example.org").unwrap(), Some([6u8; 32])); let key = Account::new(master(), 1).unwrap().me().pk(); assert!(store.contact("bob@example.org").unwrap().is_none()); @@ -927,48 +913,14 @@ mod tests { assert_eq!(store.accepted_state("a@example.org").unwrap(), Some(false)); } - /// Pins scope by port with an asymmetric fallback: the default port - /// inherits the bare-host row, and any other port never falls back to - /// it — a second server on one host earns its own trust on first use, - /// not a mismatch against its sibling's key. - #[test] - fn pins_scope_by_port_asymmetrically() { - let store = temp_store("pin-scope"); - store - .pin_server("example.org", DEFAULT_PORT, &[1u8; KEY_LEN]) - .unwrap(); - store - .pin_server("example.org", 1962, &[2u8; KEY_LEN]) - .unwrap(); - // The default port reads the legacy bare-host row. - assert_eq!( - store.server_pin("example.org", DEFAULT_PORT).unwrap(), - Some([1u8; KEY_LEN]) - ); - // A non-default port reads only its own scoped row. - assert_eq!( - store.server_pin("example.org", 1962).unwrap(), - Some([2u8; KEY_LEN]) - ); - // The rule: no fallback to the bare row from another port. - assert_eq!(store.server_pin("example.org", 1963).unwrap(), None); - // Unpin is scoped too; the default-port row survives it. - store.unpin_server("example.org", 1962).unwrap(); - assert_eq!(store.server_pin("example.org", 1962).unwrap(), None); - assert_eq!( - store.server_pin("example.org", DEFAULT_PORT).unwrap(), - Some([1u8; KEY_LEN]) - ); - } - #[test] fn unpin_returns_to_trust_on_first_use() { let store = temp_store("unpin"); - store.pin_server("example.org", DEFAULT_PORT, &[1u8; KEY_LEN]).unwrap(); - store.unpin_server("example.org", DEFAULT_PORT).unwrap(); - assert_eq!(store.server_pin("example.org", DEFAULT_PORT).unwrap(), None); + store.pin_server("example.org", &[1u8; KEY_LEN]).unwrap(); + store.unpin_server("example.org").unwrap(); + assert_eq!(store.server_pin("example.org").unwrap(), None); // Unpinning what was never pinned is not an error. - store.unpin_server("never.example.org", DEFAULT_PORT).unwrap(); + store.unpin_server("never.example.org").unwrap(); } #[test] diff --git a/core/src/tcp.rs b/core/src/tcp.rs index 63f17fe..3a88846 100644 --- a/core/src/tcp.rs +++ b/core/src/tcp.rs @@ -7,7 +7,7 @@ use std::time::Duration; use snow::{Builder, TransportState}; -use crate::crypto::KEY_LEN; +use crate::crypto::{ct_eq, KEY_LEN}; use crate::error::Error; use crate::transport::{ Response, Transport, TransportBindValues, MAX_FRAME, NOISE_PARAMS, NOISE_PAYLOAD, PROLOGUE, @@ -40,11 +40,16 @@ impl TcpTransport { let mut refused = None; for attempt in 1..=HANDSHAKE_ATTEMPTS { match handshake(host, port, timeout) { - Ok((stream, noise, _server_static, bind)) => { - // Pin enforcement lives one layer up, in client::connect: - // it knows the identity host, which a dial hint may have - // routed away from, so the mismatch error must name the - // trust object and not wherever the packets went. + Ok((stream, noise, server_static, bind)) => { + if let Some(pinned) = pinned { + if !ct_eq(&pinned, &server_static) { + return Err(Error::PinMismatch { + host: host.to_string(), + pinned, + presented: server_static, + }); + } + } return Ok(TcpTransport { stream, noise,