mirror of
https://github.com/aljazceru/pubky-core.git
synced 2026-01-30 03:14:25 +01:00
feat(common): pubky authn and authnverifier
This commit is contained in:
645
Cargo.lock
generated
645
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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
15
common/Cargo.toml
Normal 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
217
common/src/auth.rs
Normal 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
14
common/src/crypto.rs
Normal 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
4
common/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod auth;
|
||||
pub mod crypto;
|
||||
pub mod namespaces;
|
||||
pub mod timestamp;
|
||||
1
common/src/namespaces.rs
Normal file
1
common/src/namespaces.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub const PUBKY_AUTHN: &[u8; 11] = b"PUBKY:AUTHN";
|
||||
229
common/src/timestamp.rs
Normal file
229
common/src/timestamp.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user