Sign Markdown saved to the folder with the configured ENS name and key #2
Generated
+573
-5
@@ -2,30 +2,283 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alloy-primitives"
|
||||||
|
version = "1.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d5e9dbd49258ac3ab893a481d46be29b58be7f734dcac46cd80b6b13ee36566c"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"cfg-if",
|
||||||
|
"const-hex",
|
||||||
|
"derive_more",
|
||||||
|
"itoa",
|
||||||
|
"k256",
|
||||||
|
"paste",
|
||||||
|
"ruint",
|
||||||
|
"sha3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.104"
|
version = "1.0.104"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base16ct"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.13.2"
|
version = "2.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
||||||
|
|
||||||
|
[[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 = "block-buffer"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
|
||||||
|
dependencies = [
|
||||||
|
"hybrid-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-hex"
|
||||||
|
version = "1.19.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e59eef12462b0f9b0a3620219be5d639afd79fe39dff0a42c3997061f9298b4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures 0.2.17",
|
||||||
|
"proptest",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-oid"
|
||||||
|
version = "0.9.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-bigint"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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 = "crypto-common"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
|
||||||
|
dependencies = [
|
||||||
|
"hybrid-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "der"
|
||||||
|
version = "0.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||||
|
dependencies = [
|
||||||
|
"const-oid",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustc_version",
|
||||||
|
"syn 2.0.119",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer 0.10.4",
|
||||||
|
"const-oid",
|
||||||
|
"crypto-common 0.1.6",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer 0.12.1",
|
||||||
|
"crypto-common 0.2.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ecdsa"
|
||||||
|
version = "0.16.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"digest 0.10.7",
|
||||||
|
"elliptic-curve",
|
||||||
|
"rfc6979",
|
||||||
|
"signature",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "elliptic-curve"
|
||||||
|
version = "0.13.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"crypto-bigint",
|
||||||
|
"digest 0.10.7",
|
||||||
|
"ff",
|
||||||
|
"generic-array",
|
||||||
|
"group",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"sec1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ff"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "group"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||||
|
dependencies = [
|
||||||
|
"ff",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.17.1"
|
version = "0.17.1"
|
||||||
@@ -41,6 +294,24 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest 0.10.7",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hybrid-array"
|
||||||
|
version = "0.4.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "id-arena"
|
name = "id-arena"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
@@ -65,12 +336,46 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "k256"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"ecdsa",
|
||||||
|
"elliptic-curve",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keccak"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8f198d1db720e4940b5a493201d199d9f24f568f8f746bd13706243a2f71598"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leb128fmt"
|
name = "leb128fmt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.189"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.34"
|
version = "0.4.34"
|
||||||
@@ -85,7 +390,18 @@ checksum = "6e7b3a5bbb2f63aaa223a671ea1bd0e7bc16bd30ce387324cf63995deaddbbd2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 3.0.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "md-ens-signature"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/capsulizers/md-ens-signature?rev=051a5f611eeba8d61c66dbe5074a2e56517897a7#051a5f611eeba8d61c66dbe5074a2e56517897a7"
|
||||||
|
dependencies = [
|
||||||
|
"alloy-primitives",
|
||||||
|
"anyhow",
|
||||||
|
"k256",
|
||||||
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -98,10 +414,36 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|||||||
name = "memona-plugin-signature"
|
name = "memona-plugin-signature"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"md-ens-signature",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"wit-bindgen",
|
"wit-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -109,7 +451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0"
|
checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn",
|
"syn 3.0.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -121,6 +463,20 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proptest"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"num-traits",
|
||||||
|
"rand 0.9.5",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_xorshift",
|
||||||
|
"unarray",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.37.5"
|
version = "0.37.5"
|
||||||
@@ -139,6 +495,108 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.9.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.9.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xorshift"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.9.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfc6979"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||||
|
dependencies = [
|
||||||
|
"hmac",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruint"
|
||||||
|
version = "1.20.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2973657b5127d510e230f5c63d2d106af9c8f79393d8b9f4647323e8196bdde5"
|
||||||
|
dependencies = [
|
||||||
|
"proptest",
|
||||||
|
"rand 0.8.8",
|
||||||
|
"rand 0.9.5",
|
||||||
|
"ruint-macro",
|
||||||
|
"serde_core",
|
||||||
|
"valuable",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruint-macro"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sec1"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||||
|
dependencies = [
|
||||||
|
"base16ct",
|
||||||
|
"der",
|
||||||
|
"generic-array",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.28"
|
version = "1.0.28"
|
||||||
@@ -171,7 +629,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 3.0.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -187,6 +645,54 @@ dependencies = [
|
|||||||
"zmij",
|
"zmij",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures 0.2.17",
|
||||||
|
"digest 0.10.7",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha3"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1"
|
||||||
|
dependencies = [
|
||||||
|
"digest 0.11.3",
|
||||||
|
"keccak",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signature"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||||
|
dependencies = [
|
||||||
|
"digest 0.10.7",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.119"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "3.0.6"
|
version = "3.0.6"
|
||||||
@@ -198,12 +704,48 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.20.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unarray"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-encoder"
|
name = "wasm-encoder"
|
||||||
version = "0.259.0"
|
version = "0.259.0"
|
||||||
@@ -269,7 +811,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"syn",
|
"syn 3.0.6",
|
||||||
"wasm-metadata",
|
"wasm-metadata",
|
||||||
"wit-bindgen-core",
|
"wit-bindgen-core",
|
||||||
"wit-component",
|
"wit-component",
|
||||||
@@ -286,7 +828,7 @@ dependencies = [
|
|||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 3.0.6",
|
||||||
"wit-bindgen-core",
|
"wit-bindgen-core",
|
||||||
"wit-bindgen-rust",
|
"wit-bindgen-rust",
|
||||||
]
|
]
|
||||||
@@ -329,6 +871,32 @@ dependencies = [
|
|||||||
"wasmparser",
|
"wasmparser",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c17e8fafad82b542ff3717217ecdc736231b59e387768c9630123b4ce4d2db44"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "595f56e044df4f46a0c9a626f65c3d99eb8488f7e8a8baa12dd76326d9710bf2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
wit-bindgen = "=0.62.0"
|
wit-bindgen = "=0.62.0"
|
||||||
quick-xml = { version = "0.37", default-features = false }
|
quick-xml = { version = "0.37", default-features = false }
|
||||||
|
md-ens-signature = { git = "https://github.com/capsulizers/md-ens-signature", rev = "051a5f611eeba8d61c66dbe5074a2e56517897a7" }
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
panic = "deny"
|
panic = "deny"
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
# Memona signature plugin
|
# Memona signature plugin
|
||||||
|
|
||||||
Opens a WebDAV server as a folder inside Memona. It is the foundation for
|
Opens a WebDAV server as a folder inside Memona and signs every Markdown file
|
||||||
signing Markdown files with an ENS name as they are saved, writing `signer` and
|
saved to it with an ENS name, writing `signer` and `signature` into the file's
|
||||||
`signature` into each file's frontmatter so anyone can check who wrote it
|
frontmatter so anyone can check who wrote it against the name's current ENSv2
|
||||||
against the name's current ENSv2 records, using the
|
records. Signing comes from the
|
||||||
[md-ens-signature](https://github.com/capsulizers/md-ens-signature) library.
|
[md-ens-signature](https://github.com/capsulizers/md-ens-signature) library.
|
||||||
This version is the WebDAV folder alone; signing is not in it yet.
|
|
||||||
|
|
||||||
The plugin is a WASI component: it speaks no sockets of its own and makes every
|
The plugin is a WASI component: it speaks no sockets of its own and makes every
|
||||||
request through the host's HTTP interface, under the connection's permissions.
|
request through the host's HTTP interface, under the connection's permissions.
|
||||||
@@ -18,6 +17,9 @@ It registers one provider, `signature`, for the `signed-webdav` scheme.
|
|||||||
| Server address | The folder's address, for example `https://dav.example.com/files/` |
|
| Server address | The folder's address, for example `https://dav.example.com/files/` |
|
||||||
| Username | Optional account name for HTTP basic authentication |
|
| Username | Optional account name for HTTP basic authentication |
|
||||||
| Password | Optional password, stored as a secret |
|
| Password | Optional password, stored as a secret |
|
||||||
|
| Signer name | The ENS name that signs, for example `laptop.alice.eth` |
|
||||||
|
| Signing key | The private key of the address the name resolves to, stored as a secret |
|
||||||
|
| Sepolia RPC address | The endpoint used to check signers against ENS, by default `https://ethereum-sepolia-rpc.publicnode.com` |
|
||||||
|
|
||||||
Listing, reading, writing, creating directories, renaming and deleting behave
|
Listing, reading, writing, creating directories, renaming and deleting behave
|
||||||
as in the WebDAV plugin: reads and writes stream in pieces of at most 1 MiB,
|
as in the WebDAV plugin: reads and writes stream in pieces of at most 1 MiB,
|
||||||
@@ -25,6 +27,17 @@ conditional writes use `If-Match` and `If-None-Match`, change polling is not
|
|||||||
supported, a rename never replaces an existing target, and a folder with
|
supported, a rename never replaces an existing target, and a folder with
|
||||||
contents is refused rather than removed whole.
|
contents is refused rather than removed whole.
|
||||||
|
|
||||||
|
## Signing
|
||||||
|
|
||||||
|
Saving a `.md` file holds it whole, up to 8 MiB, and writes it once finished,
|
||||||
|
with `signer` and `signature` set in its frontmatter; every other file streams
|
||||||
|
straight through. The signature is EIP-191 `personal_sign` over the signer name
|
||||||
|
and the body's SHA-256, made with the configured key; other frontmatter and the
|
||||||
|
body stay byte for byte. A key that is not 32 bytes of hex refuses the
|
||||||
|
connection, and a `.md` file that is not UTF-8 refuses the save. Reading passes
|
||||||
|
every file through unchanged. The RPC address is for verifying signers, which a
|
||||||
|
later version adds to reads.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -33,6 +33,28 @@
|
|||||||
"label": "Password",
|
"label": "Password",
|
||||||
"kind": "password",
|
"kind": "password",
|
||||||
"required": false
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ensName",
|
||||||
|
"label": "Signer name",
|
||||||
|
"description": "The ENS name that signs saved Markdown, for example laptop.alice.eth",
|
||||||
|
"kind": "text",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "privateKey",
|
||||||
|
"label": "Signing key",
|
||||||
|
"description": "The private key of the address this name resolves to",
|
||||||
|
"kind": "password",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "rpcUrl",
|
||||||
|
"label": "Sepolia RPC address",
|
||||||
|
"description": "The Ethereum Sepolia endpoint used to check signers against ENS",
|
||||||
|
"kind": "text",
|
||||||
|
"required": false,
|
||||||
|
"default": "https://ethereum-sepolia-rpc.publicnode.com"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-2
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
mod dav;
|
mod dav;
|
||||||
mod sdk;
|
mod sdk;
|
||||||
|
mod sign;
|
||||||
|
|
||||||
use sdk::host;
|
use sdk::host;
|
||||||
use sdk::{
|
use sdk::{
|
||||||
@@ -10,6 +11,7 @@ use sdk::{
|
|||||||
GuestReader, GuestWriter, ListPage, Locator, Reader, Setting, WriteCondition,
|
GuestReader, GuestWriter, ListPage, Locator, Reader, Setting, WriteCondition,
|
||||||
Writer,
|
Writer,
|
||||||
};
|
};
|
||||||
|
use sign::Signer;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
||||||
/// The PROPFIND body. Asking for named properties rather than allprop keeps
|
/// The PROPFIND body. Asking for named properties rather than allprop keeps
|
||||||
@@ -21,12 +23,19 @@ const PROPS: &str = concat!(
|
|||||||
"</d:prop></d:propfind>",
|
"</d:prop></d:propfind>",
|
||||||
);
|
);
|
||||||
const MAX_CHUNK: u32 = 1024 * 1024;
|
const MAX_CHUNK: u32 = 1024 * 1024;
|
||||||
|
/// The largest Markdown file this plugin will hold whole to sign.
|
||||||
|
const MAX_MARKDOWN: usize = 8 * 1024 * 1024;
|
||||||
|
|
||||||
struct Session {
|
struct Session {
|
||||||
root: String,
|
root: String,
|
||||||
authorization: Option<String>,
|
authorization: Option<String>,
|
||||||
|
signer: Signer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The host creates one component instance per connection and calls into it
|
||||||
|
// on a single thread, so this slot holds exactly one connection's session and
|
||||||
|
// is not shared state between connections. A WASI component has no other place
|
||||||
|
// to keep state between exported calls.
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static SESSION: RefCell<Option<Session>> = const { RefCell::new(None) };
|
static SESSION: RefCell<Option<Session>> = const { RefCell::new(None) };
|
||||||
}
|
}
|
||||||
@@ -179,11 +188,27 @@ impl GuestReader for DavReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether a path names a Markdown file, the only kind this plugin signs.
|
||||||
|
fn is_markdown(path: &str) -> bool {
|
||||||
|
path.to_ascii_lowercase().ends_with(".md")
|
||||||
|
}
|
||||||
|
|
||||||
/// Writes one file through a PUT whose body is pushed in pieces.
|
/// Writes one file through a PUT whose body is pushed in pieces.
|
||||||
struct DavWriter {
|
struct DavWriter {
|
||||||
call: host::Exchange,
|
call: host::Exchange,
|
||||||
done: RefCell<bool>,
|
done: RefCell<bool>,
|
||||||
written: Cell<u64>,
|
written: Cell<u64>,
|
||||||
|
/// A Markdown file waits here whole until `finish`, because its signature
|
||||||
|
/// covers the entire body and lands in the frontmatter at the front.
|
||||||
|
markdown: Option<RefCell<Vec<u8>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DavWriter {
|
||||||
|
fn send(&self, data: &[u8]) -> Result<(), ErrorCode> {
|
||||||
|
self.call.push(data).map_err(transport)?;
|
||||||
|
self.written.set(self.written.get() + data.len() as u64);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GuestWriter for DavWriter {
|
impl GuestWriter for DavWriter {
|
||||||
@@ -191,8 +216,14 @@ impl GuestWriter for DavWriter {
|
|||||||
if *self.done.borrow() {
|
if *self.done.borrow() {
|
||||||
return Err(ErrorCode::InvalidInput);
|
return Err(ErrorCode::InvalidInput);
|
||||||
}
|
}
|
||||||
self.call.push(&data).map_err(transport)?;
|
let Some(markdown) = &self.markdown else {
|
||||||
self.written.set(self.written.get() + data.len() as u64);
|
return self.send(&data);
|
||||||
|
};
|
||||||
|
let mut markdown = markdown.borrow_mut();
|
||||||
|
if markdown.len() + data.len() > MAX_MARKDOWN {
|
||||||
|
return Err(ErrorCode::Unsupported);
|
||||||
|
}
|
||||||
|
markdown.extend_from_slice(&data);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
/// One call is enough: the request body is already complete, so the response
|
/// One call is enough: the request body is already complete, so the response
|
||||||
@@ -203,6 +234,12 @@ impl GuestWriter for DavWriter {
|
|||||||
return Err(ErrorCode::InvalidInput);
|
return Err(ErrorCode::InvalidInput);
|
||||||
}
|
}
|
||||||
*self.done.borrow_mut() = true;
|
*self.done.borrow_mut() = true;
|
||||||
|
if let Some(markdown) = &self.markdown {
|
||||||
|
let signed = session(|session| session.signer.sign(&markdown.borrow()))?;
|
||||||
|
for piece in signed.chunks(MAX_CHUNK as usize) {
|
||||||
|
self.send(piece)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
let head = self.call.respond().map_err(transport)?;
|
let head = self.call.respond().map_err(transport)?;
|
||||||
dav::outcome(head.status)?;
|
dav::outcome(head.status)?;
|
||||||
let revision = head
|
let revision = head
|
||||||
@@ -242,9 +279,14 @@ impl Guest for Webdav {
|
|||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
let signer = Signer::new(
|
||||||
|
&lookup("ensName").ok_or(ErrorCode::InvalidInput)?,
|
||||||
|
&lookup("privateKey").ok_or(ErrorCode::InvalidInput)?,
|
||||||
|
)?;
|
||||||
SESSION.set(Some(Session {
|
SESSION.set(Some(Session {
|
||||||
root,
|
root,
|
||||||
authorization,
|
authorization,
|
||||||
|
signer,
|
||||||
}));
|
}));
|
||||||
Ok(Capabilities {
|
Ok(Capabilities {
|
||||||
write: true,
|
write: true,
|
||||||
@@ -358,6 +400,7 @@ impl Guest for Webdav {
|
|||||||
call,
|
call,
|
||||||
done: RefCell::new(false),
|
done: RefCell::new(false),
|
||||||
written: Cell::new(0),
|
written: Cell::new(0),
|
||||||
|
markdown: is_markdown(&path).then(|| RefCell::new(Vec::new())),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
//! Signing a saved Markdown file, the only place this plugin touches its
|
||||||
|
//! bytes. Everything the md-ens-signature library does for the plugin goes
|
||||||
|
//! through the interface here.
|
||||||
|
|
||||||
|
use crate::sdk::ErrorCode;
|
||||||
|
use md_ens_signature::document::{
|
||||||
|
SignatureFields, body_digest, write_signature,
|
||||||
|
};
|
||||||
|
use md_ens_signature::signature::{address_of, sign};
|
||||||
|
|
||||||
|
/// One connection's signing identity: an ENS name and the private key bound
|
||||||
|
/// to it.
|
||||||
|
pub struct Signer {
|
||||||
|
name: String,
|
||||||
|
key: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a 32-byte private key written as 64 hex characters, with or without
|
||||||
|
/// a `0x` prefix.
|
||||||
|
fn parse_key(text: &str) -> Option<[u8; 32]> {
|
||||||
|
let digits = text.trim();
|
||||||
|
let digits = digits.strip_prefix("0x").unwrap_or(digits).as_bytes();
|
||||||
|
if digits.len() != 64 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
|||||||
|
let mut key = [0u8; 32];
|
||||||
|
for (byte, pair) in key.iter_mut().zip(digits.chunks(2)) {
|
||||||
|
let text = std::str::from_utf8(pair).ok()?;
|
||||||
|
*byte = u8::from_str_radix(text, 16).ok()?;
|
||||||
|
}
|
||||||
|
Some(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signer {
|
||||||
|
/// Takes the configured name and key, failing with `InvalidInput` when the
|
||||||
|
/// key cannot sign, so a bad key refuses the connection rather than every
|
||||||
|
/// later save.
|
||||||
|
pub fn new(name: &str, private_key: &str) -> Result<Self, ErrorCode> {
|
||||||
|
let name = name.trim().to_lowercase();
|
||||||
|
let key = parse_key(private_key).ok_or(ErrorCode::InvalidInput)?;
|
||||||
|
if name.is_empty() || address_of(&key).is_err() {
|
||||||
|
return Err(ErrorCode::InvalidInput);
|
||||||
|
}
|
||||||
|
Ok(Self { name, key })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the whole file with its `signer` and `signature` frontmatter set
|
||||||
|
/// for this identity, other frontmatter and the body untouched. A file that
|
||||||
|
/// is not UTF-8 is not Markdown this plugin can sign.
|
||||||
|
pub fn sign(&self, markdown: &[u8]) -> Result<Vec<u8>, ErrorCode> {
|
||||||
|
let text =
|
||||||
|
std::str::from_utf8(markdown).map_err(|_| ErrorCode::InvalidInput)?;
|
||||||
|
let signature = sign(&self.key, &self.name, &body_digest(text))
|
||||||
|
.map_err(|_| ErrorCode::Internal)?;
|
||||||
|
let fields = SignatureFields {
|
||||||
|
signer: self.name.clone(),
|
||||||
|
signature,
|
||||||
|
};
|
||||||
|
Ok(write_signature(text, &fields).into_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{Signer, parse_key};
|
||||||
|
use crate::sdk::ErrorCode;
|
||||||
|
use md_ens_signature::document::{body_digest, read_signature};
|
||||||
|
use md_ens_signature::signature::{address_of, recover};
|
||||||
|
|
||||||
|
const KEY: &str =
|
||||||
|
"0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_signed_file_recovers_to_the_key_and_keeps_its_body()
|
||||||
|
-> Result<(), ErrorCode> {
|
||||||
|
let signer = Signer::new(" Laptop.Alice.eth ", KEY)?;
|
||||||
|
let saved = "---\ntitle: Notes\n---\n# Notes\n\nHello\n";
|
||||||
|
let signed = String::from_utf8(signer.sign(saved.as_bytes())?)
|
||||||
|
.map_err(|_| ErrorCode::Internal)?;
|
||||||
|
assert!(signed.starts_with("---\ntitle: Notes\n"));
|
||||||
|
assert!(signed.ends_with("---\n# Notes\n\nHello\n"));
|
||||||
|
let fields = read_signature(&signed).ok_or(ErrorCode::Internal)?;
|
||||||
|
assert_eq!(fields.signer, "laptop.alice.eth");
|
||||||
|
let key = parse_key(KEY).ok_or(ErrorCode::Internal)?;
|
||||||
|
let recovered =
|
||||||
|
recover(&fields.signature, &fields.signer, &body_digest(&signed))
|
||||||
|
.map_err(|_| ErrorCode::Internal)?;
|
||||||
|
assert_eq!(Some(recovered), address_of(&key).ok());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_key_that_cannot_sign_refuses_the_connection() {
|
||||||
|
assert!(Signer::new("alice.eth", "0x1234").is_err());
|
||||||
|
assert!(Signer::new("alice.eth", &"zz".repeat(32)).is_err());
|
||||||
|
assert!(Signer::new("alice.eth", &"00".repeat(32)).is_err());
|
||||||
|
assert!(Signer::new(" ", KEY).is_err());
|
||||||
|
assert!(Signer::new("alice.eth", &KEY[2..]).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_file_that_is_not_text_is_refused() -> Result<(), ErrorCode> {
|
||||||
|
let signer = Signer::new("alice.eth", KEY)?;
|
||||||
|
assert_eq!(signer.sign(&[0xff, 0xfe]), Err(ErrorCode::InvalidInput));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user
This returns the original bytes, so the configured ENS identity and private key never affect the saved file and no
signerorsignaturefrontmatter is written. Use the signing implementation here and add a test that asserts the signed output.