feat(common): pubky authn and authnverifier

This commit is contained in:
nazeh
2024-07-14 20:15:52 +03:00
parent 3b8f6cf7b8
commit 3a0ad9b028
8 changed files with 1126 additions and 1 deletions

645
Cargo.lock generated
View File

@@ -32,6 +32,18 @@ version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "arrayref"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "async-trait"
version = "0.1.81"
@@ -119,12 +131,52 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base32"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1ce0365f4d5fb6646220bb52fe547afd51796d90f914d4063cb0b032ebee088"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "blake3"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d08263faac5cde2a4d52b513dadb80846023aade56fcd8fc99ba73ba8050e92"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytes"
version = "1.6.1"
@@ -143,6 +195,155 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "constant_time_eq"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "curve25519-dalek"
version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
"rustc_version",
"subtle",
"zeroize",
]
[[package]]
name = "curve25519-dalek-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "der"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
dependencies = [
"const-oid",
"zeroize",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "document-features"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0"
dependencies = [
"litrs",
]
[[package]]
name = "dyn-clone"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]]
name = "ed25519"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
"pkcs8",
"signature",
]
[[package]]
name = "ed25519-dalek"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand_core",
"serde",
"sha2",
"subtle",
"zeroize",
]
[[package]]
name = "fiat-crypto"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "flume"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
dependencies = [
"nanorand",
"spin",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -158,6 +359,21 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.30"
@@ -165,6 +381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@@ -173,6 +390,40 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
@@ -185,10 +436,39 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
@@ -289,6 +569,15 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -301,6 +590,12 @@ version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "litrs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]]
name = "lock_api"
version = "0.4.12"
@@ -317,6 +612,32 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lru"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
[[package]]
name = "mainline"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b751ffb57303217bcae8f490eee6044a5b40eadf6ca05ff476cad37e7b7970d"
dependencies = [
"bytes",
"crc",
"ed25519-dalek",
"flume",
"lru",
"rand",
"serde",
"serde_bencode",
"serde_bytes",
"sha1_smol",
"thiserror",
"tracing",
]
[[package]]
name = "matchers"
version = "0.1.0"
@@ -364,6 +685,15 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "nanorand"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
dependencies = [
"getrandom",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@@ -466,6 +796,48 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkarr"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89f9e12544b00f5561253bbd3cb72a85ff3bc398483dc1bf82bdf095c774136b"
dependencies = [
"bytes",
"document-features",
"dyn-clone",
"ed25519-dalek",
"flume",
"futures",
"js-sys",
"lru",
"mainline",
"rand",
"self_cell",
"simple-dns",
"thiserror",
"tracing",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"z32",
]
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"spki",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.86"
@@ -475,12 +847,26 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "pubky-common"
version = "0.1.0"
dependencies = [
"base32",
"blake3",
"ed25519-dalek",
"once_cell",
"pkarr",
"rand",
"thiserror",
]
[[package]]
name = "pubky_homeserver"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"pkarr",
"tokio",
"tower-http",
"tracing",
@@ -496,6 +882,36 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.5.2"
@@ -555,6 +971,15 @@ version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.17"
@@ -573,6 +998,18 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "self_cell"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a"
[[package]]
name = "semver"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.204"
@@ -582,6 +1019,25 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde_bencode"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a70dfc7b7438b99896e7f8992363ab8e2c4ba26aa5ec675d32d1c3c2c33d413e"
dependencies = [
"serde",
"serde_bytes",
]
[[package]]
name = "serde_bytes"
version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a"
dependencies = [
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.204"
@@ -626,6 +1082,23 @@ dependencies = [
"serde",
]
[[package]]
name = "sha1_smol"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@@ -644,6 +1117,33 @@ dependencies = [
"libc",
]
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"rand_core",
]
[[package]]
name = "simple-dns"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01607fe2e61894468c6dc0b26103abb073fb08b79a3d9e4b6d76a1a341549958"
dependencies = [
"bitflags",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.13.2"
@@ -660,6 +1160,31 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "spki"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.71"
@@ -683,6 +1208,26 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
name = "thiserror"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.8"
@@ -830,6 +1375,12 @@ dependencies = [
"tracing-log",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.12"
@@ -842,12 +1393,94 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "web-sys"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
@@ -1008,3 +1641,15 @@ name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "z32"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edb37266251c28b03d08162174a91c3a092e3bd4f476f8205ee1c507b78b7bdc"
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"

View File

@@ -1,5 +1,5 @@
[workspace]
members = ["homeserver"]
members = [ "common","homeserver"]
# See: https://github.com/rust-lang/rust/issues/90148#issuecomment-949194352
resolver = "2"

15
common/Cargo.toml Normal file
View File

@@ -0,0 +1,15 @@
[package]
name = "pubky-common"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
base32 = "0.5.0"
blake3 = "1.5.1"
ed25519-dalek = "2.1.1"
once_cell = "1.19.0"
pkarr = "2.0.3"
rand = "0.8.5"
thiserror = "1.0.60"

217
common/src/auth.rs Normal file
View File

@@ -0,0 +1,217 @@
use std::sync::{Arc, Mutex};
use ed25519_dalek::ed25519::SignatureBytes;
use crate::{
crypto::{random_hash, Keypair, PublicKey, Signature},
timestamp::Timestamp,
};
// 30 seconds
const TIME_INTERVAL: u64 = 30 * 1_000_000;
#[derive(Debug, PartialEq)]
pub struct AuthnSignature(Box<[u8]>);
impl AuthnSignature {
pub fn new(signer: &Keypair, audience: &PublicKey, token: Option<&[u8]>) -> Self {
let mut bytes = Vec::with_capacity(96);
let time: u64 = Timestamp::now().into();
let time_step = time / TIME_INTERVAL;
let token_hash = token.map_or(random_hash(), |t| crate::crypto::hash(t));
let signature = signer
.sign(&signable(
&time_step.to_be_bytes(),
&signer.public_key(),
audience,
token_hash.as_bytes(),
))
.to_bytes();
bytes.extend_from_slice(&signature);
bytes.extend_from_slice(token_hash.as_bytes());
Self(bytes.into())
}
pub fn for_token(keypair: &Keypair, audience: &PublicKey, token: &[u8]) -> Self {
AuthnSignature::new(keypair, audience, Some(token))
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
#[derive(Debug, Clone)]
pub struct AuthnVerifier {
audience: PublicKey,
inner: Arc<Mutex<Vec<[u8; 40]>>>,
// TODO: Support permisisons
// token_hashes: HashSet<[u8; 32]>,
}
impl AuthnVerifier {
pub fn new(audience: PublicKey) -> Self {
Self {
audience,
inner: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn verify(&self, bytes: &[u8], signer: &PublicKey) -> Result<(), AuthnSignatureError> {
self.gc();
if bytes.len() != 96 {
return Err(AuthnSignatureError::InvalidLength(bytes.len()));
}
let signature_bytes: SignatureBytes = bytes[0..64]
.try_into()
.expect("validate token length on instantiating");
let signature = Signature::from(signature_bytes);
let token_hash: [u8; 32] = bytes[64..].try_into().expect("should not be reachable");
let now = Timestamp::now().into_inner();
let past = now - TIME_INTERVAL;
let future = now + TIME_INTERVAL;
let result = verify_at(now, &self, &signature, signer, &token_hash);
match result {
Ok(_) => return Ok(()),
Err(AuthnSignatureError::AlreadyUsed) => return Err(AuthnSignatureError::AlreadyUsed),
_ => {}
}
let result = verify_at(past, &self, &signature, signer, &token_hash);
match result {
Ok(_) => return Ok(()),
Err(AuthnSignatureError::AlreadyUsed) => return Err(AuthnSignatureError::AlreadyUsed),
_ => {}
}
verify_at(future, &self, &signature, signer, &token_hash)
}
// === Private Methods ===
/// Remove all tokens older than two time intervals in the past.
fn gc(&self) {
let threshold = ((Timestamp::now().into_inner() / TIME_INTERVAL) - 2).to_be_bytes();
let mut inner = self.inner.lock().unwrap();
match inner.binary_search_by(|element| element[0..8].cmp(&threshold)) {
Ok(index) | Err(index) => {
inner.drain(0..index);
}
}
}
}
fn verify_at(
time: u64,
verifier: &AuthnVerifier,
signature: &Signature,
signer: &PublicKey,
token_hash: &[u8; 32],
) -> Result<(), AuthnSignatureError> {
let time_step = time / TIME_INTERVAL;
let time_step_bytes = time_step.to_be_bytes();
let result = signer.verify(
&signable(&time_step_bytes, signer, &verifier.audience, token_hash),
signature,
);
if result.is_ok() {
let mut inner = verifier.inner.lock().unwrap();
let mut candidate = [0_u8; 40];
candidate[..8].copy_from_slice(&time_step_bytes);
candidate[8..].copy_from_slice(token_hash);
match inner.binary_search_by(|element| element.cmp(&candidate)) {
Ok(index) | Err(index) => {
inner.insert(index, candidate);
}
};
return Ok(());
}
Err(AuthnSignatureError::InvalidSignature)
}
fn signable(
time_step_bytes: &[u8; 8],
signer: &PublicKey,
audience: &PublicKey,
token_hash: &[u8; 32],
) -> [u8; 115] {
let mut arr = [0; 115];
arr[..11].copy_from_slice(crate::namespaces::PUBKY_AUTHN);
arr[11..19].copy_from_slice(time_step_bytes);
arr[19..51].copy_from_slice(signer.as_bytes());
arr[51..83].copy_from_slice(audience.as_bytes());
arr[83..].copy_from_slice(token_hash);
arr
}
#[derive(thiserror::Error, Debug)]
pub enum AuthnSignatureError {
#[error("AuthnSignature should be 96 bytes long, got {0} bytes instead")]
InvalidLength(usize),
#[error("Invalid signature")]
InvalidSignature,
#[error("Authn signature already used")]
AlreadyUsed,
}
#[cfg(test)]
mod tests {
use crate::crypto::Keypair;
use super::{AuthnSignature, AuthnVerifier};
#[test]
fn sign_verify() {
let keypair = Keypair::random();
let signer = keypair.public_key();
let audience = Keypair::random().public_key();
let verifier = AuthnVerifier::new(audience.clone());
let authn_signature = AuthnSignature::new(&keypair, &audience, None);
verifier
.verify(authn_signature.as_bytes(), &signer)
.unwrap();
{
// Invalid signable
let mut invalid = authn_signature.as_bytes().to_vec();
invalid[64..].copy_from_slice(&[0; 32]);
assert!(!verifier.verify(&invalid, &signer).is_ok())
}
{
// Invalid signer
let mut invalid = authn_signature.as_bytes().to_vec();
invalid[0..32].copy_from_slice(&[0; 32]);
assert!(!verifier.verify(&invalid, &signer).is_ok())
}
}
}

14
common/src/crypto.rs Normal file
View File

@@ -0,0 +1,14 @@
use rand::prelude::Rng;
pub use pkarr::{Keypair, PublicKey};
pub use ed25519_dalek::Signature;
pub type Hash = blake3::Hash;
pub use blake3::hash;
pub fn random_hash() -> Hash {
let mut rng = rand::thread_rng();
Hash::from_bytes(rng.gen())
}

4
common/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod auth;
pub mod crypto;
pub mod namespaces;
pub mod timestamp;

1
common/src/namespaces.rs Normal file
View File

@@ -0,0 +1 @@
pub const PUBKY_AUTHN: &[u8; 11] = b"PUBKY:AUTHN";

229
common/src/timestamp.rs Normal file
View File

@@ -0,0 +1,229 @@
//! Monotonic unix timestamp in microseconds
use std::fmt::Display;
use std::time::SystemTime;
use std::{
ops::{Add, Sub},
sync::Mutex,
};
use once_cell::sync::Lazy;
use rand::Rng;
/// ~4% chance of none of 10 clocks have matching id.
const CLOCK_MASK: u64 = (1 << 8) - 1;
const TIME_MASK: u64 = !0 >> 8;
pub struct TimestampFactory {
clock_id: u64,
last_time: u64,
}
impl TimestampFactory {
pub fn new() -> Self {
Self {
clock_id: rand::thread_rng().gen::<u64>() & CLOCK_MASK,
last_time: system_time() & TIME_MASK,
}
}
pub fn now(&mut self) -> Timestamp {
// Ensure monotonicity.
self.last_time = (system_time() & TIME_MASK).max(self.last_time + CLOCK_MASK + 1);
// Add clock_id to the end of the timestamp
Timestamp(self.last_time | self.clock_id)
}
}
impl Default for TimestampFactory {
fn default() -> Self {
Self::new()
}
}
static DEFAULT_FACTORY: Lazy<Mutex<TimestampFactory>> =
Lazy::new(|| Mutex::new(TimestampFactory::default()));
/// Monotonic timestamp since [SystemTime::UNIX_EPOCH] in microseconds as u64.
///
/// The purpose of this timestamp is to unique per "user", not globally,
/// it achieves this by:
/// 1. Override the last byte with a random `clock_id`, reducing the probability
/// of two matching timestamps across multiple machines/threads.
/// 2. Gurantee that the remaining 3 bytes are ever increasing (monotonic) within
/// the same thread regardless of the wall clock value
///
/// This timestamp is also serialized as BE bytes to remain sortable.
/// If a `utf-8` encoding is necessary, it is encoded as [base32::Alphabet::Crockford]
/// to act as a sortable Id.
///
/// U64 of microseconds is valid for the next 500 thousand years!
#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
pub struct Timestamp(u64);
impl Timestamp {
pub fn now() -> Self {
DEFAULT_FACTORY.lock().unwrap().now()
}
/// Return big endian bytes
pub fn to_bytes(&self) -> [u8; 8] {
self.0.to_be_bytes()
}
pub fn difference(&self, rhs: &Timestamp) -> u64 {
self.0.abs_diff(rhs.0)
}
pub fn into_inner(&self) -> u64 {
self.0
}
}
impl Display for Timestamp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let bytes: [u8; 8] = self.into();
f.write_str(&base32::encode(base32::Alphabet::Crockford, &bytes))
}
}
impl TryFrom<String> for Timestamp {
type Error = TimestampError;
fn try_from(value: String) -> Result<Self, Self::Error> {
match base32::decode(base32::Alphabet::Crockford, &value) {
Some(vec) => {
let bytes: [u8; 8] = vec
.try_into()
.map_err(|_| TimestampError::InvalidEncoding)?;
Ok(bytes.into())
}
None => Err(TimestampError::InvalidEncoding),
}
}
}
impl TryFrom<&[u8]> for Timestamp {
type Error = TimestampError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let bytes: [u8; 8] = bytes
.try_into()
.map_err(|_| TimestampError::InvalidBytesLength(bytes.len()))?;
Ok(bytes.into())
}
}
impl From<&Timestamp> for [u8; 8] {
fn from(timestamp: &Timestamp) -> Self {
timestamp.0.to_be_bytes()
}
}
impl From<[u8; 8]> for Timestamp {
fn from(bytes: [u8; 8]) -> Self {
Self(u64::from_be_bytes(bytes))
}
}
// === U64 conversion ===
impl From<Timestamp> for u64 {
fn from(value: Timestamp) -> Self {
value.into_inner()
}
}
impl Add<u64> for &Timestamp {
type Output = Timestamp;
fn add(self, rhs: u64) -> Self::Output {
Timestamp(self.0 + rhs)
}
}
impl Sub<u64> for &Timestamp {
type Output = Timestamp;
fn sub(self, rhs: u64) -> Self::Output {
Timestamp(self.0 - rhs)
}
}
#[cfg(not(target_arch = "wasm32"))]
/// Return the number of microseconds since [SystemTime::UNIX_EPOCH]
fn system_time() -> u64 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("time drift")
.as_micros() as u64
}
#[derive(thiserror::Error, Debug)]
pub enum TimestampError {
#[error("Invalid bytes length, Timestamp should be encoded as 8 bytes, got {0}")]
InvalidBytesLength(usize),
#[error("Invalid timestamp encoding")]
InvalidEncoding,
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
#[test]
fn monotonic() {
const COUNT: usize = 100;
let mut set = HashSet::with_capacity(COUNT);
let mut vec = Vec::with_capacity(COUNT);
for _ in 0..COUNT {
let timestamp = Timestamp::now();
set.insert(timestamp.clone());
vec.push(timestamp);
}
let mut ordered = vec.clone();
ordered.sort();
assert_eq!(set.len(), COUNT, "unique");
assert_eq!(ordered, vec, "ordered");
}
#[test]
fn strings() {
const COUNT: usize = 100;
let mut set = HashSet::with_capacity(COUNT);
let mut vec = Vec::with_capacity(COUNT);
for _ in 0..COUNT {
let string = Timestamp::now().to_string();
set.insert(string.clone());
vec.push(string)
}
let mut ordered = vec.clone();
ordered.sort();
assert_eq!(set.len(), COUNT, "unique");
assert_eq!(ordered, vec, "ordered");
}
#[test]
fn to_from_string() {
let timestamp = Timestamp::now();
let string = timestamp.to_string();
let decoded: Timestamp = string.try_into().unwrap();
assert_eq!(decoded, timestamp)
}
}