Sign Markdown saved to the folder with the configured ENS name and key #2

Merged
temeddix merged 2 commits from signing-hook into main 2026-09-25 17:02:53 +00:00
6 changed files with 766 additions and 12 deletions
Generated
+573 -5
View File
@@ -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"
+1
View File
@@ -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"
+18 -5
View File
@@ -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
+22
View File
@@ -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
View File
@@ -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
View File
@@ -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(())
}
}