diff --git a/Cargo.lock b/Cargo.lock index 3ecf6d76f..69b72a31e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,7 +101,7 @@ checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -135,7 +135,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -147,7 +147,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -260,7 +260,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9cf849ee05b2ee5fba5e36f97ff8ec2533916700fc0758d40d92136a42f3388" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -560,7 +560,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -571,7 +571,7 @@ checksum = "606383658416244b8dc4b36f864ec1f86cb922b95c41a908fd07aeb01cad06fa" dependencies = [ "cipher 0.4.3", "dbl", - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -583,13 +583,13 @@ dependencies = [ "bincode", "bs58", "coins-core", - "digest 0.10.5", + "digest 0.10.7", "getrandom 0.2.8", "hmac 0.12.1", "k256", "lazy_static", "serde", - "sha2 0.10.6", + "sha2 0.10.8", "thiserror", ] @@ -606,7 +606,7 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.8", "thiserror", ] @@ -620,13 +620,13 @@ dependencies = [ "base64 0.12.3", "bech32", "blake2", - "digest 0.10.5", + "digest 0.10.7", "generic-array 0.14.6", "hex", "ripemd", "serde", "serde_derive", - "sha2 0.10.6", + "sha2 0.10.8", "sha3", "thiserror", ] @@ -732,9 +732,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -898,7 +898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" dependencies = [ "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -923,6 +923,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "platforms", + "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 2.0.48", +] + [[package]] name = "dashmap" version = "5.2.0" @@ -943,6 +970,18 @@ dependencies = [ "generic-array 0.14.6", ] +[[package]] +name = "deoxys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00603a49e114bd99d87a4ab8d480f36ecd1451a9d6474f66973d1a829ff77789" +dependencies = [ + "aead", + "aes 0.8.1", + "subtle", + "zeroize", +] + [[package]] name = "der" version = "0.6.0" @@ -961,7 +1000,7 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -1002,9 +1041,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.5" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.0", "crypto-common", @@ -1065,7 +1104,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.0", "ed25519", "rand 0.7.3", "serde", @@ -1088,7 +1127,7 @@ dependencies = [ "base16ct", "crypto-bigint", "der", - "digest 0.10.5", + "digest 0.10.7", "ff", "generic-array 0.14.6", "group", @@ -1165,7 +1204,7 @@ checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ "aes 0.8.1", "ctr", - "digest 0.10.5", + "digest 0.10.7", "hex", "hmac 0.12.1", "pbkdf2 0.11.0", @@ -1173,7 +1212,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.8", "sha3", "thiserror", "uuid 0.8.2", @@ -1297,7 +1336,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "syn", + "syn 1.0.103", "tempfile", "toml", "url", @@ -1314,7 +1353,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn", + "syn 1.0.103", ] [[package]] @@ -1342,7 +1381,7 @@ dependencies = [ "serde", "serde_json", "strum", - "syn", + "syn 1.0.103", "thiserror", "tiny-keccak", "unicode-xid", @@ -1357,7 +1396,22 @@ dependencies = [ "hex", "quote", "serde_json", - "syn", + "syn 1.0.103", +] + +[[package]] +name = "ethers-encryption" +version = "1.0.0" +dependencies = [ + "deoxys", + "hex", + "hmac 0.12.1", + "rand 0.8.5", + "reqwest", + "serde", + "serde_json", + "sha2 0.10.8", + "x25519-dalek", ] [[package]] @@ -1388,6 +1442,7 @@ dependencies = [ "auto_impl 0.5.0", "ethers-contract", "ethers-core", + "ethers-encryption", "ethers-etherscan", "ethers-providers", "ethers-signers", @@ -1418,6 +1473,7 @@ dependencies = [ "base64 0.13.1", "bytes", "ethers-core", + "ethers-encryption", "futures-channel", "futures-core", "futures-timer", @@ -1468,7 +1524,7 @@ dependencies = [ "rusoto_kms", "semver", "serde_json", - "sha2 0.10.6", + "sha2 0.10.8", "spki", "tempfile", "thiserror", @@ -1505,7 +1561,7 @@ dependencies = [ "semver", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.8", "solang-parser", "svm-rs", "svm-rs-builds", @@ -1571,6 +1627,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -1717,7 +1779,7 @@ checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -1929,7 +1991,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -2076,7 +2138,7 @@ checksum = "d5dacb10c5b3bb92d46ba347505a9041e676bb20ad220101326bffb0c93031ee" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2205,7 +2267,7 @@ dependencies = [ "cfg-if 1.0.0", "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.8", "sha3", ] @@ -2255,9 +2317,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libusb1-sys" @@ -2327,7 +2389,7 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -2515,7 +2577,7 @@ dependencies = [ "bytes", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2583,7 +2645,7 @@ checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" dependencies = [ "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.8", ] [[package]] @@ -2594,7 +2656,7 @@ checksum = "70723b6e03216e79df3765a7e4cdf39746c4a2392ba4bdb458111a939494cc1d" dependencies = [ "ecdsa", "elliptic-curve", - "sha2 0.10.6", + "sha2 0.10.8", ] [[package]] @@ -2620,7 +2682,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2705,10 +2767,10 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", "hmac 0.12.1", "password-hash 0.3.2", - "sha2 0.10.6", + "sha2 0.10.8", ] [[package]] @@ -2717,10 +2779,10 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", "hmac 0.12.1", "password-hash 0.4.2", - "sha2 0.10.6", + "sha2 0.10.8", ] [[package]] @@ -2781,7 +2843,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2825,7 +2887,7 @@ checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -2856,6 +2918,12 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +[[package]] +name = "platforms" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" + [[package]] name = "plotters" version = "0.3.1" @@ -2941,7 +3009,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.103", "version_check", ] @@ -2964,9 +3032,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.47" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -2979,9 +3047,9 @@ checksum = "47c327e191621a2158159df97cdbc2e7074bb4e940275e35abf38eb3d2595754" [[package]] name = "quote" -version = "1.0.16" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -3220,7 +3288,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1facec54cb5e0dc08553501fa740091086d0259ad0067e0d4103448e4cb22ed3" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -3241,7 +3309,7 @@ checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -3438,7 +3506,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -3472,7 +3540,7 @@ dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", "salsa20", - "sha2 0.10.6", + "sha2 0.10.8", ] [[package]] @@ -3585,7 +3653,7 @@ checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -3634,7 +3702,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -3645,7 +3713,7 @@ checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -3656,7 +3724,7 @@ checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.7", ] [[package]] @@ -3687,13 +3755,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "digest 0.10.5", + "digest 0.10.7", "sha2-asm", ] @@ -3712,7 +3780,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31f935e31cf406e8c0e96c2815a5516181b7004ae8c5f296293221e9b1e356bd" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", "keccak", ] @@ -3746,7 +3814,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deb766570a2825fa972bceff0d195727876a9cdf2460ab2e52d455dc2de47fd9" dependencies = [ - "digest 0.10.5", + "digest 0.10.7", "rand_core 0.6.3", "signature_derive", ] @@ -3759,7 +3827,7 @@ checksum = "96e6310f022b5c02b3bba689166e833f6b96994a6ce1f138b653d2fd0519920f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -3863,7 +3931,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 1.0.103", ] [[package]] @@ -3927,6 +3995,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -3935,7 +4014,7 @@ checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", "unicode-xid", ] @@ -4021,7 +4100,7 @@ checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -4114,7 +4193,7 @@ checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -4205,7 +4284,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -4479,7 +4558,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.103", "wasm-bindgen-shared", ] @@ -4513,7 +4592,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4771,6 +4850,18 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +dependencies = [ + "curve25519-dalek 4.1.1", + "rand_core 0.6.3", + "serde", + "zeroize", +] + [[package]] name = "xml-rs" version = "0.8.4" @@ -4794,7 +4885,7 @@ dependencies = [ "cbc", "ccm", "cmac", - "digest 0.10.5", + "digest 0.10.7", "ecdsa", "ed25519", "ed25519-dalek", @@ -4808,7 +4899,7 @@ dependencies = [ "rusb", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.8", "signature", "subtle", "thiserror", @@ -4834,7 +4925,7 @@ checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index c02d04f6a..919aa335d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "ethers-etherscan", "ethers-solc", "examples/ethers-wasm", + "ethers-encryption", ] default-members = [ @@ -33,6 +34,7 @@ default-members = [ "ethers-middleware", "ethers-etherscan", "ethers-solc", + "ethers-encryption", ] [package.metadata.docs.rs] diff --git a/ethers-encryption/Cargo.toml b/ethers-encryption/Cargo.toml new file mode 100644 index 000000000..6c5a99101 --- /dev/null +++ b/ethers-encryption/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "ethers-encryption" +version = "1.0.0" +edition = "2021" +rust-version = "1.62" +authors = ["Mike Antonuk "] +license = "MIT OR Apache-2.0" +readme = "README.md" +documentation = "https://docs.rs/ethers" +repository = "https://github.com/SigmaGmbH/ethers-rs" +homepage = "https://docs.rs/ethers" +description = "Complete Ethereum library and wallet implementation in Rust." + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +deoxys = "0.1.0" +rand = "0.8.5" +sha2 = "0.10.7" +hmac = "0.12.1" +x25519-dalek = { version = "2.0.0", features = ["static_secrets"] } +hex = { version = "0.4.3", default-features = false, features = ["std"] } +reqwest = { version = "0.11.13", default-features = false, features = ["json"] } +serde = { version = "1.0.124", default-features = false, features = ["derive"] } +serde_json = { version = "1.0.64", default-features = false, features = ["raw_value"] } diff --git a/ethers-encryption/README.md b/ethers-encryption/README.md new file mode 100644 index 000000000..5ac1e88c8 --- /dev/null +++ b/ethers-encryption/README.md @@ -0,0 +1,3 @@ +#

ethers.rs

+ +**Toolkit used for encryption and decryption in Swisstronik network** diff --git a/ethers-encryption/src/derivation.rs b/ethers-encryption/src/derivation.rs new file mode 100644 index 000000000..d87131888 --- /dev/null +++ b/ethers-encryption/src/derivation.rs @@ -0,0 +1,32 @@ +use deoxys::aead::KeyInit; +use sha2::Sha256; +use hmac::{Hmac, Mac}; + +use crate::KEY_SIZE; + +type HmacSha256 = Hmac; + +/// Converts provided x25519 private key to public key +pub fn x25519_private_to_public(private_key: [u8; KEY_SIZE]) -> [u8; KEY_SIZE] { + let secret = x25519_dalek::StaticSecret::from(private_key); + let public_key = x25519_dalek::PublicKey::from(&secret); + public_key.to_bytes() +} + +/// Performs Diffie-Hellman derivation of encryption key for transaction encryption +/// * public_key – User public key +/// Returns shared secret which can be used for derivation of encryption key +pub fn derive_shared_secret(private_key: [u8; KEY_SIZE], public_key: [u8; KEY_SIZE]) -> x25519_dalek::SharedSecret { + let secret = x25519_dalek::StaticSecret::from(private_key); + secret.diffie_hellman(&x25519_dalek::PublicKey::from(public_key)) +} + +/// Derives encryption key using KDF +pub fn derive_encryption_key(private_key: &[u8], salt: &[u8]) -> [u8; KEY_SIZE] { + let mut kdf = ::new_from_slice(salt).unwrap(); + kdf.update(private_key); + let mut derived_key = [0u8; KEY_SIZE]; + let digest = kdf.finalize(); + derived_key.copy_from_slice(&digest.into_bytes()[..KEY_SIZE]); + derived_key +} diff --git a/ethers-encryption/src/encryption.rs b/ethers-encryption/src/encryption.rs new file mode 100644 index 000000000..eb59562b0 --- /dev/null +++ b/ethers-encryption/src/encryption.rs @@ -0,0 +1,80 @@ +use hmac::{Mac}; +use rand::{rngs::OsRng, RngCore}; +use deoxys::aead::generic_array::GenericArray; +use deoxys::aead::{Aead, KeyInit, Payload}; +use deoxys::DeoxysII256; + +use crate::{KEY_SIZE, TX_KEY_PREFIX, NONCE_SIZE, TAG_SIZE}; + +use crate::derivation::{ + derive_shared_secret, + derive_encryption_key, + x25519_private_to_public, +}; + +pub fn encrypt_ecdh( + private_key: [u8; KEY_SIZE], + node_public_key: [u8; KEY_SIZE], + data: &[u8], +) -> Result, deoxys::Error> { + let shared_secret = derive_shared_secret(private_key, node_public_key); + let salt = TX_KEY_PREFIX.as_bytes(); + let encryption_key = derive_encryption_key(shared_secret.as_bytes(), salt); + + // Append encryption public key + let encrypted_data = deoxys_encrypt(&encryption_key, data)?; + let public_key = x25519_private_to_public(private_key); + let mut result = Vec::::new(); + result.extend_from_slice(&public_key); + result.extend(encrypted_data); + + Ok(result) +} + +pub fn decrypt_ecdh( + private_key: [u8; KEY_SIZE], + node_public_key: [u8; KEY_SIZE], + encrypted_data: &[u8], +) -> Result, deoxys::Error> { + let shared_secret = derive_shared_secret(private_key, node_public_key); + let salt = TX_KEY_PREFIX.as_bytes(); + let encryption_key = derive_encryption_key(shared_secret.as_bytes(), salt); + deoxys_decrypt(&encryption_key, encrypted_data) +} + +pub fn deoxys_encrypt( + private_key: &[u8; KEY_SIZE], + data: &[u8], +) -> Result, deoxys::Error> { + let mut rng = OsRng; + let mut aad = [0u8; TAG_SIZE]; + rng.fill_bytes(&mut aad); + let mut nonce = [0u8; NONCE_SIZE]; + rng.fill_bytes(&mut nonce); + let nonce = GenericArray::from_slice(&nonce); + let payload = Payload { + msg: data, + aad: &aad, + }; + let key = GenericArray::from_slice(private_key); + let encrypted = DeoxysII256::new(key).encrypt(nonce, payload); + match encrypted { + Ok(ciphertext) => { + let encrypted_data = [&nonce, aad.as_slice(), ciphertext.as_slice()].concat(); + Ok(encrypted_data) + } + Err(e) => Err(e) + } +} + +pub fn deoxys_decrypt( + private_key: &[u8; KEY_SIZE], + encrypted_data: &[u8], +) -> Result, deoxys::Error> { + let nonce = &encrypted_data[0..NONCE_SIZE]; + let aad = &encrypted_data[NONCE_SIZE..NONCE_SIZE + TAG_SIZE]; + let ciphertext = &encrypted_data[NONCE_SIZE + TAG_SIZE..]; + let payload = Payload { msg: ciphertext, aad }; + let key = GenericArray::from_slice(private_key); + DeoxysII256::new(key).decrypt(GenericArray::from_slice(nonce), payload) +} diff --git a/ethers-encryption/src/lib.rs b/ethers-encryption/src/lib.rs new file mode 100644 index 000000000..c189dbc24 --- /dev/null +++ b/ethers-encryption/src/lib.rs @@ -0,0 +1,86 @@ +use serde::{Deserialize, Serialize}; +use rand::{rngs::OsRng, RngCore}; +use std::convert::TryInto; + +pub mod derivation; +pub mod encryption; + +/// Salt for derivation of transaction encryption key +pub const TX_KEY_PREFIX: &str = "IOEncryptionKeyV1"; +/// Salt for derivation of user key material +pub const USER_KEY_PREFIX: &str = "UserEncryptionKeyV1"; +/// Size of `tag` for DEOXYS-II encryption +pub const TAG_SIZE: usize = 16; +/// Size of `nonce` for DEOXYS-II encryption +pub const NONCE_SIZE: usize = 15; +/// Default size of private / public key +pub const KEY_SIZE: usize = 32; + +/// Encrypts provided transaction or call data field +/// +/// * node_url - URL of JSON-RPC to obtain node public key +/// * data – raw data to encrypt +/// +/// Returns Some(encrypted_data, used_key) if encryption was successful, returns None in case +/// of error +pub async fn encrypt_data( + node_public_key: [u8; 32], + data: &[u8], +) -> Option<(Vec, [u8; KEY_SIZE])> { + // Generate random encryption key + let mut rng = OsRng; + let mut key_material = [0u8; KEY_SIZE]; + rng.fill_bytes(&mut key_material); + + // Derive encryption key + let encryption_key = derivation::derive_encryption_key( + &key_material, + USER_KEY_PREFIX.as_bytes(), + ); + + // Encrypt data + let encrypted = encryption::encrypt_ecdh( + encryption_key.clone(), + node_public_key, + data, + ); + + match encrypted { + Ok(res) => Some((res.to_vec(), encryption_key)), + Err(err) => { + println!("Cannot encrypt transaction data. Reason: {:?}", err); + None + } + } +} + +/// Decrypts provided ciphertext, received as a node response +/// +/// * node_url – URL of JSON-RPC to obtain node public key +/// * encryption_key – key, used during encryption of raw data +/// * data – ciphertext, received from node +/// +/// Returns Some(decrypted_data) in case of success, otherwise returns None +pub async fn decrypt_data(node_public_key: [u8; 32], encryption_key: [u8; KEY_SIZE], data: &[u8]) -> Option> { + // Decrypt data + let decrypted = encryption::decrypt_ecdh( + encryption_key, + node_public_key, + data, + ); + + match decrypted { + Ok(res) => Some(res.to_vec()), + Err(err) => { + println!("Cannot decrypt node response. Reason: {:?}", err); + None + } + } +} + +pub fn convert_to_fixed_size_array(data: Vec) -> [u8; KEY_SIZE] { + let mut fixed_array = [0u8; KEY_SIZE]; + fixed_array.copy_from_slice(&data[..KEY_SIZE]); + fixed_array +} + diff --git a/ethers-middleware/Cargo.toml b/ethers-middleware/Cargo.toml index b53788bf8..c11944232 100644 --- a/ethers-middleware/Cargo.toml +++ b/ethers-middleware/Cargo.toml @@ -20,6 +20,7 @@ ethers-core = { version = "^1.0.0", path = "../ethers-core", default-features = ethers-etherscan = { version = "^1.0.0", path = "../ethers-etherscan", default-features = false } ethers-providers = { version = "^1.0.0", path = "../ethers-providers", default-features = false } ethers-signers = { version = "^1.0.0", path = "../ethers-signers", default-features = false } +ethers-encryption = { version = "^1.0.0", path = "../ethers-encryption", default-features = false , optional = true} async-trait = { version = "0.1.50", default-features = false } auto_impl = { version = "0.5.0", default-features = false } @@ -56,3 +57,4 @@ tokio = { version = "1.18", default-features = false, features = ["rt", "macros" [features] celo = ["ethers-core/celo", "ethers-providers/celo", "ethers-signers/celo", "ethers-contract/celo"] +swisstronik = ["ethers-providers/swisstronik", "ethers-encryption"] \ No newline at end of file diff --git a/ethers-middleware/src/signer.rs b/ethers-middleware/src/signer.rs index 36af4f031..3feaefd60 100644 --- a/ethers-middleware/src/signer.rs +++ b/ethers-middleware/src/signer.rs @@ -9,6 +9,10 @@ use std::convert::TryFrom; use async_trait::async_trait; use thiserror::Error; +#[cfg(feature = "swisstronik")] +use ethers_providers::SwisstronikMiddleware; + + #[derive(Clone, Debug)] /// Middleware used for locally signing transactions, compatible with any implementer /// of the [`Signer`] trait. @@ -99,6 +103,12 @@ pub enum SignerMiddlewareError { /// Thrown if the signer's chain_id is different than the chain_id of the transaction #[error("specified chain_id is different than the signer's chain_id")] DifferentChainID, + /// Thrown if during encryption we couldn't obtain node public key + #[error("failed to get node public key")] + FailedNodePublicKey, + /// Thrown if during encryption there was an error + #[error("failed to encrypt")] + FailedToEncrypt, } // Helper functions for locally signing transactions @@ -125,6 +135,7 @@ where /// If the transaction does not have a chain id set, it sets it to the signer's chain id. /// Returns an error if the transaction's existing chain id does not match the signer's chain /// id. + #[cfg(not(feature = "swisstronik"))] async fn sign_transaction( &self, mut tx: TypedTransaction, @@ -149,6 +160,43 @@ where Ok(tx.rlp_signed(&signature)) } + #[cfg(feature = "swisstronik")] + async fn sign_transaction( + &self, + mut tx: TypedTransaction, + ) -> Result> { +// compare chain_id and use signer's chain_id if the tranasaction's chain_id is None, + // return an error if they are not consistent + let chain_id = self.signer.chain_id(); + match tx.chain_id() { + Some(id) if id.as_u64() != chain_id => { + return Err(SignerMiddlewareError::DifferentChainID) + } + None => { + tx.set_chain_id(chain_id); + } + _ => {} + } + + if tx.to().is_some() && tx.data().is_some() { + // Encryption transaction data in case of swisstronik network + let node_enc_key = self.provider().get_node_public_key().await.map_err(|_| SignerMiddlewareError::FailedNodePublicKey)?; + // Encrypt call data in case of Swisstronik network + let (encrypted_data, _) = ethers_encryption::encrypt_data(node_enc_key, tx.data().unwrap()) + .await.ok_or(SignerMiddlewareError::FailedToEncrypt)?; + + // Update call data + tx.set_data(encrypted_data.into()); + } + + let signature = + self.signer.sign_transaction(&tx).await.map_err(SignerMiddlewareError::SignerError)?; + + // Return the raw rlp-encoded signed transaction + Ok(tx.rlp_signed(&signature)) + } + + /// Returns the client's address pub fn address(&self) -> Address { self.address diff --git a/ethers-providers/Cargo.toml b/ethers-providers/Cargo.toml index 9e4829234..5d4c1bc2a 100644 --- a/ethers-providers/Cargo.toml +++ b/ethers-providers/Cargo.toml @@ -16,6 +16,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] ethers-core = { version = "^1.0.0", path = "../ethers-core", default-features = false } +ethers-encryption = { version = "^1.0.0", path = "../ethers-encryption", default-features = false , optional = true} async-trait = { version = "0.1.50", default-features = false } hex = { version = "0.4.3", default-features = false, features = ["std"] } @@ -68,6 +69,7 @@ tempfile = "3.3.0" [features] default = ["ws", "rustls"] celo = ["ethers-core/celo"] +swisstronik = [ "ethers-encryption"] ws = ["tokio-tungstenite", "futures-channel"] ipc = ["tokio/io-util", "bytes", "futures-channel"] diff --git a/ethers-providers/src/lib.rs b/ethers-providers/src/lib.rs index 6905b14af..df5fd8a95 100644 --- a/ethers-providers/src/lib.rs +++ b/ethers-providers/src/lib.rs @@ -675,6 +675,17 @@ pub trait CeloMiddleware: Middleware { } } +#[cfg(feature = "swisstronik")] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +pub trait SwisstronikMiddleware: Middleware { + async fn get_node_public_key( + &self + ) -> Result<[u8;32], ProviderError> { + self.provider().get_node_public_key().await.map_err(FromErr::from) + } +} + pub use test_provider::{GOERLI, MAINNET, ROPSTEN}; /// Pre-instantiated Infura HTTP clients which rotate through multiple API keys diff --git a/ethers-providers/src/provider.rs b/ethers-providers/src/provider.rs index ff4eb8b31..9ed32b798 100644 --- a/ethers-providers/src/provider.rs +++ b/ethers-providers/src/provider.rs @@ -1,11 +1,4 @@ -use crate::{ - call_raw::CallBuilder, - ens, erc, maybe, - pubsub::{PubsubClient, SubscriptionStream}, - stream::{FilterWatcher, DEFAULT_LOCAL_POLL_INTERVAL, DEFAULT_POLL_INTERVAL}, - FromErr, Http as HttpProvider, JsonRpcClient, JsonRpcClientWrapper, LogQuery, MockProvider, - PendingTransaction, QuorumProvider, RwClient, SyncingStatus, -}; +use crate::{call_raw::CallBuilder, ens, erc, maybe, pubsub::{PubsubClient, SubscriptionStream}, stream::{FilterWatcher, DEFAULT_LOCAL_POLL_INTERVAL, DEFAULT_POLL_INTERVAL}, FromErr, Http as HttpProvider, JsonRpcClient, JsonRpcClientWrapper, LogQuery, MockProvider, PendingTransaction, QuorumProvider, RwClient, SyncingStatus, EscalationPolicy, EscalatingPending}; #[cfg(all(not(target_arch = "wasm32"), feature = "ws"))] use crate::transports::Authorization; @@ -14,6 +7,11 @@ use crate::transports::{HttpRateLimitRetryPolicy, RetryClient}; #[cfg(feature = "celo")] use crate::CeloMiddleware; +#[cfg(feature = "swisstronik")] +use crate::SwisstronikMiddleware; +#[cfg(feature = "swisstronik")] +use ethers_encryption::convert_to_fixed_size_array; + use crate::Middleware; use async_trait::async_trait; @@ -299,6 +297,20 @@ impl CeloMiddleware for Provider

{ } } + +#[cfg(feature = "swisstronik")] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +impl SwisstronikMiddleware for Provider

{ + async fn get_node_public_key( + &self + ) -> Result<[u8;32], ProviderError> { + let resp: String = self.request("eth_getNodePublicKey", ["latest"]).await?; + let converted = convert_to_fixed_size_array(hex::decode(resp.trim_start_matches("0x"))?); + Ok(converted) + } +} + #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Middleware for Provider

{ @@ -564,6 +576,7 @@ impl Middleware for Provider

{ /// Sends the read-only (constant) transaction to a single Ethereum node and return the result /// (as bytes) of executing it. This is free, since it does not change any state on the /// blockchain. + #[cfg(not(feature = "swisstronik"))] async fn call( &self, tx: &TypedTransaction, @@ -574,10 +587,47 @@ impl Middleware for Provider

{ self.request("eth_call", [tx, block]).await } + #[cfg(feature= "swisstronik")] + async fn call( + &self, + tx: &TypedTransaction, + block: Option, + ) -> Result { + if tx.data().is_some() && tx.to().is_some() { + // Obtain node public key and encrypt tx.data + let node_public_key = self.get_node_public_key().await?; + let (encrypted_data, encryption_key) = ethers_encryption::encrypt_data(node_public_key, tx.data().unwrap()) + .await + .ok_or_else(|| ProviderError::CustomError(String::from("Cannot encrypt transaction data")))?; + + // Update tx.data field + let mut encrypted_tx = tx.clone(); + let encrypted_tx = encrypted_tx.set_data(encrypted_data.into()); + let tx = utils::serialize(encrypted_tx); + let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); + let response: Vec = self.request("eth_call", [tx, block]).await?; + let decrypted_data = ethers_encryption::decrypt_data(node_public_key, encryption_key, &response).await; + match decrypted_data { + Some(data) => { + Ok(data.into()) + }, + None => { + Err(ProviderError::CustomError("Cannot decrypt node response".to_string())) + } + } + } else { + let tx = utils::serialize(tx); + let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); + self.request("eth_call", [tx, block]).await + } + } + + /// Sends a transaction to a single Ethereum node and return the estimated amount of gas /// required (as a U256) to send it This is free, but only an estimate. Providing too little /// gas will result in a transaction being rejected (while still consuming all provided /// gas). + #[cfg(not(feature = "swisstronik"))] async fn estimate_gas( &self, tx: &TypedTransaction, @@ -593,6 +643,36 @@ impl Middleware for Provider

{ }; self.request("eth_estimateGas", params).await } + #[cfg(feature = "swisstronik")] + async fn estimate_gas( + &self, + tx: &TypedTransaction, + block: Option, + ) -> Result { + let tx = if tx.data().is_some() && tx.to().is_some() { + // Obtain node public key and encrypt tx.data + let node_public_key = self.get_node_public_key().await?; + let (encrypted_data, _) = ethers_encryption::encrypt_data(node_public_key, tx.data().unwrap()) + .await + .ok_or_else(|| ProviderError::CustomError(String::from("Cannot encrypt transaction data")))?; + + // Update tx.data field + let mut encrypted_tx = tx.clone(); + let encrypted_tx = encrypted_tx.set_data(encrypted_data.into()); + utils::serialize(encrypted_tx) + } else { + utils::serialize(tx) + }; + // Some nodes (e.g. old Optimism clients) don't support a block ID being passed as a param, + // so refrain from defaulting to BlockNumber::Latest. + let params = if let Some(block_id) = block { + vec![tx, utils::serialize(&block_id)] + } else { + vec![tx] + }; + return self.request("eth_estimateGas", params).await + } + async fn create_access_list( &self, diff --git a/ethers-providers/src/transports/retry.rs b/ethers-providers/src/transports/retry.rs index 11b7386b6..1d42af61b 100644 --- a/ethers-providers/src/transports/retry.rs +++ b/ethers-providers/src/transports/retry.rs @@ -76,7 +76,7 @@ where /// # Example /// /// ``` - /// + /// /// # async fn demo() { /// use ethers_providers::{Http, RetryClient, HttpRateLimitRetryPolicy}; /// use std::time::Duration; diff --git a/ethers-providers/tests/provider.rs b/ethers-providers/tests/provider.rs index 4878964b2..c94f01610 100644 --- a/ethers-providers/tests/provider.rs +++ b/ethers-providers/tests/provider.rs @@ -115,6 +115,21 @@ mod eth_tests { } } + +#[cfg(feature = "swisstronik")] +mod swisstronik_tests { + use super::*; + use ethers_providers::SwisstronikMiddleware; + + #[tokio::test] + async fn get_block() { + let provider = + Provider::::try_from("https://json-rpc.testnet.swisstronik.com").unwrap(); + + provider.get_node_public_key().await.unwrap(); + } +} + #[cfg(feature = "celo")] mod celo_tests { use super::*;