Slatepack - Pt 2 - Encryption (#411)

* recreate PR from #400

* first tests with slate encryption

* simplify slatepack model to contain encryption header in payload, and add de/ser tests

* update tests and confirm slatepack encryption working

* remove recipient list, add version check warning
This commit is contained in:
Yeastplume 2020-05-22 11:48:11 +01:00 committed by GitHub
parent 9450d2a3b8
commit 2769436117
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 2064 additions and 97 deletions

542
Cargo.lock generated
View file

@ -2,9 +2,9 @@
# It is not intended for manual editing.
[[package]]
name = "addr2line"
version = "0.12.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456d75cbb82da1ad150c8a9d97285ffcd21c9931dcb11e995903e7d75141b38b"
checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543"
dependencies = [
"gimli",
]
@ -15,6 +15,102 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
[[package]]
name = "aead"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf01b9b56e767bb57b94ebf91a58b338002963785cdd7013e21c0d4679471e4"
dependencies = [
"generic-array 0.12.3",
]
[[package]]
name = "aes"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9"
dependencies = [
"aes-soft",
"aesni",
"block-cipher-trait",
]
[[package]]
name = "aes-ctr"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee"
dependencies = [
"aes-soft",
"aesni",
"ctr",
"stream-cipher",
]
[[package]]
name = "aes-soft"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d"
dependencies = [
"block-cipher-trait",
"byteorder",
"opaque-debug",
]
[[package]]
name = "aesni"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
dependencies = [
"block-cipher-trait",
"opaque-debug",
"stream-cipher",
]
[[package]]
name = "age"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64701e2aa240aa36ad430f8e1b82cd3f226ea0b93185f6fbb9ada0ab12a9a92e"
dependencies = [
"aes",
"aes-ctr",
"age-core",
"base64 0.11.0",
"bcrypt-pbkdf",
"bech32",
"block-cipher-trait",
"block-modes",
"c2-chacha",
"chacha20poly1305",
"cookie-factory",
"curve25519-dalek",
"hkdf",
"hmac 0.7.1",
"nom 5.1.1",
"radix64",
"rand 0.7.3",
"scrypt",
"secrecy",
"sha2 0.8.1",
"subtle 2.2.2",
"x25519-dalek",
"zeroize 1.1.0",
]
[[package]]
name = "age-core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edc5c56a290116d446475265057ff5bc44490f681ee15cb27111ed47d4afe78"
dependencies = [
"base64 0.11.0",
"cookie-factory",
"nom 5.1.1",
]
[[package]]
name = "aho-corasick"
version = "0.7.10"
@ -99,9 +195,9 @@ version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
]
[[package]]
@ -165,6 +261,26 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "bcrypt-pbkdf"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "108e67d628901912875b038f27df25091799ba3b2fb1254a00fde6efb4318313"
dependencies = [
"blowfish",
"byteorder",
"crypto-mac 0.7.0",
"pbkdf2 0.3.0",
"sha2 0.8.1",
"zeroize 1.1.0",
]
[[package]]
name = "bech32"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdcf67bb7ba7797a081cd19009948ab533af7c355d5caf1d08c777582d351e9c"
[[package]]
name = "bindgen"
version = "0.52.0"
@ -181,8 +297,8 @@ dependencies = [
"lazycell",
"log",
"peeking_take_while",
"proc-macro2 1.0.12",
"quote 1.0.5",
"proc-macro2 1.0.13",
"quote 1.0.6",
"regex",
"rustc-hash",
"shlex",
@ -256,6 +372,25 @@ dependencies = [
"generic-array 0.12.3",
]
[[package]]
name = "block-cipher-trait"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
dependencies = [
"generic-array 0.12.3",
]
[[package]]
name = "block-modes"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529"
dependencies = [
"block-cipher-trait",
"block-padding",
]
[[package]]
name = "block-padding"
version = "0.1.5"
@ -265,6 +400,23 @@ dependencies = [
"byte-tools 0.3.1",
]
[[package]]
name = "blowfish"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3"
dependencies = [
"block-cipher-trait",
"byteorder",
"opaque-debug",
]
[[package]]
name = "bs58"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb"
[[package]]
name = "built"
version = "0.3.2"
@ -318,10 +470,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"
[[package]]
name = "cc"
version = "1.0.52"
name = "c2-chacha"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d"
checksum = "217192c943108d8b13bac38a1d51df9ce8a407a3f5a71ab633980665e68fbd9a"
dependencies = [
"byteorder",
"ppv-lite86",
"stream-cipher",
]
[[package]]
name = "cc"
version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
dependencies = [
"jobserver",
]
@ -332,7 +495,7 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d"
dependencies = [
"nom",
"nom 4.2.3",
]
[[package]]
@ -341,6 +504,18 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "chacha20poly1305"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48901293601228db2131606f741db33561f7576b5d19c99cd66222380a7dc863"
dependencies = [
"aead",
"poly1305",
"stream-cipher",
"zeroize 1.1.0",
]
[[package]]
name = "chrono"
version = "0.4.11"
@ -404,6 +579,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "cookie-factory"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f21b581d2f0cb891554812435667bb9610d74feb1a4c6415bf09c28ff0381d"
[[package]]
name = "core-foundation"
version = "0.6.4"
@ -524,6 +705,16 @@ dependencies = [
"generic-array 0.9.0",
]
[[package]]
name = "crypto-mac"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
dependencies = [
"generic-array 0.12.3",
"subtle 1.0.0",
]
[[package]]
name = "csv"
version = "0.15.0"
@ -544,6 +735,16 @@ dependencies = [
"sct",
]
[[package]]
name = "ctr"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736"
dependencies = [
"block-cipher-trait",
"stream-cipher",
]
[[package]]
name = "curve25519-dalek"
version = "2.0.0"
@ -553,7 +754,7 @@ dependencies = [
"byteorder",
"digest 0.8.1",
"rand_core 0.5.1",
"subtle",
"subtle 2.2.2",
"zeroize 1.1.0",
]
@ -744,9 +945,9 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
"synstructure 0.12.3",
]
@ -770,9 +971,9 @@ dependencies = [
[[package]]
name = "fnv"
version = "1.0.6"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
@ -872,9 +1073,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
dependencies = [
"proc-macro-hack",
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
]
[[package]]
@ -975,7 +1176,7 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "grin_api"
version = "4.0.0-alpha.1"
source = "git+https://github.com/mimblewimble/grin#2c621115612013a68de7bd973a42ec88ae5f44fc"
source = "git+https://github.com/mimblewimble/grin#93f5de3d2957f6f30dde8ae8f588efa0754c5ca3"
dependencies = [
"bytes 0.5.4",
"easy-jsonrpc-mw",
@ -1001,14 +1202,14 @@ dependencies = [
"serde_derive",
"serde_json",
"tokio",
"tokio-rustls 0.13.0",
"tokio-rustls 0.13.1",
"url 1.7.2",
]
[[package]]
name = "grin_chain"
version = "4.0.0-alpha.1"
source = "git+https://github.com/mimblewimble/grin#2c621115612013a68de7bd973a42ec88ae5f44fc"
source = "git+https://github.com/mimblewimble/grin#93f5de3d2957f6f30dde8ae8f588efa0754c5ca3"
dependencies = [
"bit-vec",
"bitflags 1.2.1",
@ -1031,7 +1232,7 @@ dependencies = [
[[package]]
name = "grin_core"
version = "4.0.0-alpha.1"
source = "git+https://github.com/mimblewimble/grin#2c621115612013a68de7bd973a42ec88ae5f44fc"
source = "git+https://github.com/mimblewimble/grin#93f5de3d2957f6f30dde8ae8f588efa0754c5ca3"
dependencies = [
"blake2-rfc",
"byteorder",
@ -1057,16 +1258,16 @@ dependencies = [
[[package]]
name = "grin_keychain"
version = "4.0.0-alpha.1"
source = "git+https://github.com/mimblewimble/grin#2c621115612013a68de7bd973a42ec88ae5f44fc"
source = "git+https://github.com/mimblewimble/grin#93f5de3d2957f6f30dde8ae8f588efa0754c5ca3"
dependencies = [
"blake2-rfc",
"byteorder",
"digest 0.7.6",
"grin_util",
"hmac",
"hmac 0.6.3",
"lazy_static",
"log",
"pbkdf2",
"pbkdf2 0.2.3",
"rand 0.6.5",
"ripemd160",
"serde",
@ -1079,7 +1280,7 @@ dependencies = [
[[package]]
name = "grin_p2p"
version = "4.0.0-alpha.1"
source = "git+https://github.com/mimblewimble/grin#2c621115612013a68de7bd973a42ec88ae5f44fc"
source = "git+https://github.com/mimblewimble/grin#93f5de3d2957f6f30dde8ae8f588efa0754c5ca3"
dependencies = [
"bitflags 1.2.1",
"chrono",
@ -1100,7 +1301,7 @@ dependencies = [
[[package]]
name = "grin_pool"
version = "4.0.0-alpha.1"
source = "git+https://github.com/mimblewimble/grin#2c621115612013a68de7bd973a42ec88ae5f44fc"
source = "git+https://github.com/mimblewimble/grin#93f5de3d2957f6f30dde8ae8f588efa0754c5ca3"
dependencies = [
"blake2-rfc",
"chrono",
@ -1134,7 +1335,7 @@ dependencies = [
[[package]]
name = "grin_store"
version = "4.0.0-alpha.1"
source = "git+https://github.com/mimblewimble/grin#2c621115612013a68de7bd973a42ec88ae5f44fc"
source = "git+https://github.com/mimblewimble/grin#93f5de3d2957f6f30dde8ae8f588efa0754c5ca3"
dependencies = [
"byteorder",
"croaring-mw",
@ -1154,7 +1355,7 @@ dependencies = [
[[package]]
name = "grin_util"
version = "4.0.0-alpha.1"
source = "git+https://github.com/mimblewimble/grin#2c621115612013a68de7bd973a42ec88ae5f44fc"
source = "git+https://github.com/mimblewimble/grin#93f5de3d2957f6f30dde8ae8f588efa0754c5ca3"
dependencies = [
"backtrace",
"base64 0.9.3",
@ -1264,6 +1465,7 @@ dependencies = [
"tokio",
"url 1.7.2",
"uuid",
"x25519-dalek",
]
[[package]]
@ -1298,16 +1500,20 @@ dependencies = [
"timer",
"tokio",
"uuid",
"x25519-dalek",
]
[[package]]
name = "grin_wallet_libwallet"
version = "4.0.0-alpha.1"
dependencies = [
"age",
"base64 0.9.3",
"blake2-rfc",
"bs58",
"byteorder",
"chrono",
"curve25519-dalek",
"ed25519-dalek",
"failure",
"failure_derive",
@ -1316,12 +1522,16 @@ dependencies = [
"lazy_static",
"log",
"rand 0.5.6",
"regex",
"secrecy",
"serde",
"serde_derive",
"serde_json",
"sha2 0.8.1",
"strum",
"strum_macros",
"uuid",
"x25519-dalek",
]
[[package]]
@ -1373,23 +1583,43 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.1.12"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4"
checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
dependencies = [
"libc",
]
[[package]]
name = "hkdf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fa08a006102488bd9cd5b8013aabe84955cf5ae22e304c2caf655b633aefae3"
dependencies = [
"digest 0.8.1",
"hmac 0.7.1",
]
[[package]]
name = "hmac"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "733e1b3ac906631ca01ebb577e9bb0f5e37a454032b9036b5eaea4013ed6f99a"
dependencies = [
"crypto-mac",
"crypto-mac 0.6.2",
"digest 0.7.6",
]
[[package]]
name = "hmac"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695"
dependencies = [
"crypto-mac 0.7.0",
"digest 0.8.1",
]
[[package]]
name = "http"
version = "0.1.21"
@ -1474,7 +1704,7 @@ dependencies = [
"rustls 0.16.0",
"rustls-native-certs 0.1.0",
"tokio",
"tokio-rustls 0.12.2",
"tokio-rustls 0.12.3",
"webpki",
]
@ -1492,7 +1722,7 @@ dependencies = [
"rustls 0.17.0",
"rustls-native-certs 0.3.0",
"tokio",
"tokio-rustls 0.13.0",
"tokio-rustls 0.13.1",
"webpki",
]
@ -1641,6 +1871,19 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
[[package]]
name = "lexical-core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7043aa5c05dd34fb73b47acb8c3708eac428de4545ea3682ed2f11293ebd890"
dependencies = [
"arrayvec 0.4.12",
"cfg-if",
"rustc_version 0.2.3",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.70"
@ -1984,7 +2227,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
dependencies = [
"memchr 2.3.3",
"version_check",
"version_check 0.1.5",
]
[[package]]
name = "nom"
version = "5.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6"
dependencies = [
"lexical-core",
"memchr 2.3.3",
"version_check 0.9.1",
]
[[package]]
@ -2238,13 +2492,23 @@ dependencies = [
"base64 0.9.3",
"byteorder",
"constant_time_eq",
"crypto-mac",
"crypto-mac 0.6.2",
"generic-array 0.9.0",
"hmac",
"hmac 0.6.3",
"rand 0.5.6",
"sha2 0.7.1",
]
[[package]]
name = "pbkdf2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9"
dependencies = [
"byteorder",
"crypto-mac 0.7.0",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
@ -2303,22 +2567,22 @@ dependencies = [
[[package]]
name = "pin-project"
version = "0.4.16"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81d480cb4e89522ccda96d0eed9af94180b7a5f93fb28f66e1fd7d68431663d1"
checksum = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "0.4.16"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a82996f11efccb19b685b14b5df818de31c1edcee3daa256ab5775dd98e72feb"
checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
]
[[package]]
@ -2346,10 +2610,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
[[package]]
name = "ppv-lite86"
version = "0.2.6"
name = "poly1305"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
checksum = "b5829f50f48e9ddb79f3f7c3097029d0caee30f8286accb241416df603b080b8"
dependencies = [
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "pretty_assertions"
@ -2398,9 +2671,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.12"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319"
checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639"
dependencies = [
"unicode-xid 0.2.0",
]
@ -2422,11 +2695,21 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e"
checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
dependencies = [
"proc-macro2 1.0.12",
"proc-macro2 1.0.13",
]
[[package]]
name = "radix64"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "999718fa65c3be3a74f3f6dae5a98526ff436ea58a82a574f0de89eecd342bee"
dependencies = [
"arrayref",
"cfg-if",
]
[[package]]
@ -2876,6 +3159,19 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "scrypt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "656c79d0e90d0ab28ac86bf3c3d10bfbbac91450d3f190113b4e76d9fec3cfdd"
dependencies = [
"byte-tools 0.3.1",
"byteorder",
"hmac 0.7.1",
"pbkdf2 0.3.0",
"sha2 0.8.1",
]
[[package]]
name = "sct"
version = "0.6.0"
@ -2886,6 +3182,15 @@ dependencies = [
"untrusted",
]
[[package]]
name = "secrecy"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9182278ed645df3477a9c27bfee0621c621aa16f6972635f7f795dae3d81070f"
dependencies = [
"zeroize 1.1.0",
]
[[package]]
name = "security-framework"
version = "0.3.4"
@ -2976,9 +3281,9 @@ version = "1.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
]
[[package]]
@ -3117,6 +3422,21 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
[[package]]
name = "static_assertions"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
[[package]]
name = "stream-cipher"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c"
dependencies = [
"generic-array 0.12.3",
]
[[package]]
name = "strsim"
version = "0.8.0"
@ -3141,6 +3461,12 @@ dependencies = [
"syn 0.15.44",
]
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]]
name = "subtle"
version = "2.2.2"
@ -3166,12 +3492,12 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.21"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4696caa4048ac7ce2bcd2e484b3cef88c1004e41b8e945a277e2c25dc0b72060"
checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.5",
"proc-macro2 1.0.13",
"quote 1.0.6",
"unicode-xid 0.2.0",
]
@ -3193,9 +3519,9 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
"unicode-xid 0.2.0",
]
@ -3253,7 +3579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e51065bafd2abe106b6036483b69d1741f4a1ec56ce8a2378de341637de689e"
dependencies = [
"fnv",
"nom",
"nom 4.2.3",
"phf",
"phf_codegen",
]
@ -3269,22 +3595,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.17"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "467e5ff447618a916519a4e0d62772ab14f434897f3d63f05d8700ef1e9b22c1"
checksum = "5976891d6950b4f68477850b5b9e5aa64d955961466f9e174363f573e54e8ca7"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.17"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63c1091225b9834089b429bc4a2e01223470e3183e891582909e9d1c4cb55d9"
checksum = "ab81dbd1cd69cd2ce22ecfbdd3bdb73334ba25350649408cc6c085f46d89573d"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
]
[[package]]
@ -3366,16 +3692,16 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
]
[[package]]
name = "tokio-rustls"
version = "0.12.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "141afec0978abae6573065a48882c6bae44c5cc61db9b511ac4abf6a09bfd9cc"
checksum = "3068d891551949b37681724d6b73666787cc63fa8e255c812a41d2513aff9775"
dependencies = [
"futures-core",
"rustls 0.16.0",
@ -3385,9 +3711,9 @@ dependencies = [
[[package]]
name = "tokio-rustls"
version = "0.13.0"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4adb8b3e5f86b707f1b54e7c15b6de52617a823608ccda98a15d3a24222f265a"
checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4"
dependencies = [
"futures-core",
"rustls 0.17.0",
@ -3512,6 +3838,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "universal-hash"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0c900f2f9b4116803415878ff48b63da9edb268668e08cf9292d7503114a01"
dependencies = [
"generic-array 0.12.3",
"subtle 2.2.2",
]
[[package]]
name = "unsafe-any"
version = "0.4.2"
@ -3583,6 +3919,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
[[package]]
name = "void"
version = "1.0.2"
@ -3635,9 +3977,9 @@ dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
"wasm-bindgen-shared",
]
@ -3647,7 +3989,7 @@ version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776"
dependencies = [
"quote 1.0.5",
"quote 1.0.6",
"wasm-bindgen-macro-support",
]
@ -3657,9 +3999,9 @@ version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
dependencies = [
"proc-macro2 1.0.12",
"quote 1.0.5",
"syn 1.0.21",
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -3752,6 +4094,17 @@ dependencies = [
"winapi-build",
]
[[package]]
name = "x25519-dalek"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217"
dependencies = [
"curve25519-dalek",
"rand_core 0.5.1",
"zeroize 1.1.0",
]
[[package]]
name = "yaml-rust"
version = "0.3.5"
@ -3773,7 +4126,7 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86"
dependencies = [
"zeroize_derive",
"zeroize_derive 0.9.3",
]
[[package]]
@ -3781,6 +4134,9 @@ name = "zeroize"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8"
dependencies = [
"zeroize_derive 1.0.0",
]
[[package]]
name = "zeroize_derive"
@ -3794,6 +4150,18 @@ dependencies = [
"synstructure 0.10.2",
]
[[package]]
name = "zeroize_derive"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2"
dependencies = [
"proc-macro2 1.0.13",
"quote 1.0.6",
"syn 1.0.22",
"synstructure 0.12.3",
]
[[package]]
name = "zip"
version = "0.5.5"

View file

@ -16,6 +16,7 @@
use chrono::prelude::*;
use ed25519_dalek::PublicKey as DalekPublicKey;
use ed25519_dalek::SecretKey as DalekSecretKey;
use uuid::Uuid;
use crate::config::{TorConfig, WalletConfig};
@ -1913,6 +1914,16 @@ where
owner::get_public_proof_address(self.wallet_inst.clone(), keychain_mask, derivation_index)
}
// TODO: Doc
/// get public proof a
pub fn get_secret_key(
&self,
keychain_mask: Option<&SecretKey>,
derivation_index: u32,
) -> Result<DalekSecretKey, Error> {
owner::get_secret_key(self.wallet_inst.clone(), keychain_mask, derivation_index)
}
/// Helper function to convert an Onion v3 address to a payment proof address (essentially
/// exctacting and verifying the public key)
///

View file

@ -36,3 +36,6 @@ grin_wallet_api = { path = "../api", version = "4.0.0-alpha.1" }
grin_wallet_impls = { path = "../impls", version = "4.0.0-alpha.1" }
grin_wallet_libwallet = { path = "../libwallet", version = "4.0.0-alpha.1" }
grin_wallet_config = { path = "../config", version = "4.0.0-alpha.1" }
[dev-dependencies]
x25519-dalek = "0.6"

View file

@ -93,7 +93,6 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
api.tx_lock_outputs(m, &slate)?;
slate = api.finalize_tx(m, &slate)?;
println!("Posted Slate: {:?}", slate);
println!("Posted TX: {}", slate);
stored_excess = Some(slate.tx.as_ref().unwrap().body.kernels[0].excess);
api.post_tx(m, &slate, false)?;
Ok(())

View file

@ -0,0 +1,526 @@
// Copyright 2019 The Grin Developers
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Test a wallet file send/recieve
#[macro_use]
extern crate log;
extern crate grin_wallet_controller as wallet;
extern crate grin_wallet_impls as impls;
use grin_wallet_libwallet as libwallet;
use grin_wallet_util::grin_core as core;
use grin_wallet_util::OnionV3Address;
use impls::test_framework::{self, LocalWalletClient};
use impls::{
PathToSlatepack, PathToSlatepackArmored, SlateGetter as _, SlatePutter as _, SlatepackArgs,
};
use std::sync::atomic::Ordering;
use std::thread;
use std::time::Duration;
use grin_wallet_libwallet::{InitTxArgs, IssueInvoiceTxArgs, Slate, Slatepack};
use x25519_dalek::PublicKey as xDalekPublicKey;
use x25519_dalek::StaticSecret;
#[macro_use]
mod common;
use common::{clean_output_dir, create_wallet_proxy, setup};
fn output_slatepack(
slate: &Slate,
file: &str,
armored: bool,
use_bin: bool,
sender: Option<xDalekPublicKey>,
recipients: Vec<xDalekPublicKey>,
) -> Result<(), libwallet::Error> {
if armored {
let file = format!("{}.armored", file);
let args = SlatepackArgs {
pathbuf: file.into(),
sender,
recipients,
dec_key: None,
};
PathToSlatepackArmored::new(args).put_tx(&slate, use_bin)
} else {
let args = SlatepackArgs {
pathbuf: file.into(),
sender,
recipients,
dec_key: None,
};
PathToSlatepack::new(args).put_tx(&slate, use_bin)
}
}
fn slate_from_packed(
file: &str,
armored: bool,
dec_key: Option<&StaticSecret>,
) -> Result<(Slatepack, Slate), libwallet::Error> {
if armored {
let file = format!("{}.armored", file);
let args = SlatepackArgs {
pathbuf: file.into(),
sender: None,
recipients: vec![],
dec_key,
};
let pts = PathToSlatepackArmored::new(args);
Ok((pts.get_slatepack()?, pts.get_tx()?.0))
} else {
let args = SlatepackArgs {
pathbuf: file.into(),
sender: None,
recipients: vec![],
dec_key,
};
let pts = PathToSlatepack::new(args);
Ok((pts.get_slatepack()?, pts.get_tx()?.0))
}
}
/// self send impl
fn slatepack_exchange_test_impl(
test_dir: &'static str,
use_bin: bool,
use_armored: bool,
use_encryption: bool,
) -> Result<(), libwallet::Error> {
// Create a new proxy to simulate server and wallet responses
let mut wallet_proxy = create_wallet_proxy(test_dir);
let chain = wallet_proxy.chain.clone();
let stopper = wallet_proxy.running.clone();
// Create a new wallet test client, and set its queues to communicate with the
// proxy
create_wallet_and_add!(
client1,
wallet1,
mask1_i,
test_dir,
"wallet1",
None,
&mut wallet_proxy,
false
);
let mask1 = (&mask1_i).as_ref();
create_wallet_and_add!(
client2,
wallet2,
mask2_i,
test_dir,
"wallet2",
None,
&mut wallet_proxy,
false
);
let mask2 = (&mask2_i).as_ref();
// Set the wallet proxy listener running
thread::spawn(move || {
if let Err(e) = wallet_proxy.run() {
error!("Wallet Proxy error: {}", e);
}
});
// few values to keep things shorter
let reward = core::consensus::REWARD;
// add some accounts
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
api.create_account_path(m, "mining")?;
api.create_account_path(m, "listener")?;
Ok(())
})?;
// add some accounts
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
api.create_account_path(m, "account1")?;
api.create_account_path(m, "account2")?;
Ok(())
})?;
// Get some mining done
{
wallet_inst!(wallet1, w);
w.set_parent_key_id_by_name("mining")?;
}
let mut bh = 10u64;
let _ =
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
let (recipients_1, dec_key_1, sender_1) = match use_encryption {
true => {
let mut rec_address = xDalekPublicKey::from([0u8; 32]);
let mut sec_key = StaticSecret::from([0u8; 32]);
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
let ed25519_sec_key = api.get_secret_key(m, 0)?;
let mut b = [0u8; 32];
b.copy_from_slice(&ed25519_sec_key.as_ref()[0..32]);
sec_key = StaticSecret::from(b);
rec_address = xDalekPublicKey::from(&sec_key);
Ok(())
})?;
(vec![rec_address], Some(sec_key), Some(rec_address.clone()))
}
false => (vec![], None, None),
};
let (recipients_2, dec_key_2, sender_2) = match use_encryption {
true => {
let mut rec_address = xDalekPublicKey::from([0u8; 32]);
let mut sec_key = StaticSecret::from([0u8; 32]);
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
let ed25519_sec_key = api.get_secret_key(m, 0)?;
let mut b = [0u8; 32];
b.copy_from_slice(&ed25519_sec_key.as_ref()[0..32]);
sec_key = StaticSecret::from(b);
rec_address = xDalekPublicKey::from(&sec_key);
Ok(())
})?;
(vec![rec_address], Some(sec_key), Some(rec_address.clone()))
}
false => (vec![], None, None),
};
let (send_file, receive_file, final_file) = match use_bin {
false => (
format!("{}/standard_S1.slatepack", test_dir),
format!("{}/standard_S2.slatepack", test_dir),
format!("{}/standard_S3.slatepack", test_dir),
),
true => (
format!("{}/standard_S1.slatepackbin", test_dir),
format!("{}/standard_S2.slatepackbin", test_dir),
format!("{}/standard_S3.slatepackbin", test_dir),
),
};
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward);
// send to send
let args = InitTxArgs {
src_acct_name: Some("mining".to_owned()),
amount: reward * 2,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let slate = api.init_send_tx(m, args)?;
// output tx file
output_slatepack(
&slate,
&send_file,
use_armored,
use_bin,
sender_1,
recipients_2.clone(),
)?;
api.tx_lock_outputs(m, &slate)?;
Ok(())
})?;
// Get some mining done
{
wallet_inst!(wallet2, w);
w.set_parent_key_id_by_name("account1")?;
}
let (mut slatepack, mut slate) =
slate_from_packed(&send_file, use_armored, (&dec_key_2).as_ref())?;
// wallet 2 receives file, completes, sends file back
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
slate = api.receive_tx(&slate, None)?;
output_slatepack(
&slate,
&receive_file,
use_armored,
use_bin,
// re-encrypt for sender!
sender_2,
match slatepack.sender {
Some(s) => vec![s.clone()],
None => vec![],
},
)?;
Ok(())
})?;
// wallet 1 finalises and posts
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
let (_, mut slate) = slate_from_packed(&receive_file, use_armored, (&dec_key_1).as_ref())?;
slate = api.finalize_tx(m, &slate)?;
// Output final file for reference
output_slatepack(&slate, &final_file, use_armored, use_bin, None, vec![])?;
api.post_tx(m, &slate, false)?;
bh += 1;
Ok(())
})?;
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
bh += 3;
// Check total in mining account
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
let (wallet1_refreshed, wallet1_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet1_refreshed);
assert_eq!(wallet1_info.last_confirmed_height, bh);
assert_eq!(wallet1_info.total, bh * reward - reward * 2);
Ok(())
})?;
// Check total in 'wallet 2' account
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
let (wallet2_refreshed, wallet2_info) = api.retrieve_summary_info(m, true, 1)?;
assert!(wallet2_refreshed);
assert_eq!(wallet2_info.last_confirmed_height, bh);
assert_eq!(wallet2_info.total, 2 * reward);
Ok(())
})?;
// Now other types of exchange, for reference
// Invoice transaction
let (send_file, receive_file, final_file) = match use_bin {
false => (
format!("{}/invoice_I1.slatepack", test_dir),
format!("{}/invoice_I2.slatepack", test_dir),
format!("{}/invoice_I3.slatepack", test_dir),
),
true => (
format!("{}/invoice_I1.slatepackbin", test_dir),
format!("{}/invoice_I2.slatepackbin", test_dir),
format!("{}/invoice_I3.slatepackbin", test_dir),
),
};
let mut slate = Slate::blank(2, true);
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
let args = IssueInvoiceTxArgs {
amount: 1000000000,
..Default::default()
};
slate = api.issue_invoice_tx(m, args)?;
output_slatepack(
&slate,
&send_file,
use_armored,
use_bin,
sender_2,
recipients_1.clone(),
)?;
Ok(())
})?;
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
let args = InitTxArgs {
src_acct_name: None,
amount: slate.amount,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
..Default::default()
};
let res = slate_from_packed(&send_file, use_armored, (&dec_key_1).as_ref())?;
slatepack = res.0;
slate = res.1;
slate = api.process_invoice_tx(m, &slate, args)?;
api.tx_lock_outputs(m, &slate)?;
output_slatepack(
&slate,
&receive_file,
use_armored,
use_bin,
sender_1,
match slatepack.sender {
Some(s) => vec![s.clone()],
None => vec![],
},
)?;
Ok(())
})?;
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
// Wallet 2 receives the invoice transaction
let res = slate_from_packed(&receive_file, use_armored, (&dec_key_2).as_ref())?;
slate = res.1;
slate = api.finalize_invoice_tx(&slate)?;
output_slatepack(&slate, &final_file, use_armored, use_bin, None, vec![])?;
Ok(())
})?;
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
api.post_tx(m, &slate, false)?;
Ok(())
})?;
// Standard, with payment proof
let _ = test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, 3, false);
let (send_file, receive_file, final_file) = match use_bin {
false => (
format!("{}/standard_pp_S1.slatepack", test_dir),
format!("{}/standard_pp_S2.slatepack", test_dir),
format!("{}/standard_pp_S3.slatepack", test_dir),
),
true => (
format!("{}/standard_pp_S1.slatepackbin", test_dir),
format!("{}/standard_pp_S2.slatepackbin", test_dir),
format!("{}/standard_pp_S3.slatepackbin", test_dir),
),
};
let mut slate = Slate::blank(2, true);
let mut address = None;
wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| {
address = Some(api.get_public_proof_address(m, 0)?);
Ok(())
})?;
let address = OnionV3Address::from_bytes(address.as_ref().unwrap().to_bytes());
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
// send to send
let args = InitTxArgs {
src_acct_name: Some("mining".to_owned()),
amount: reward,
minimum_confirmations: 2,
max_outputs: 500,
num_change_outputs: 1,
selection_strategy_is_use_all: true,
payment_proof_recipient_address: Some(address.clone()),
..Default::default()
};
let slate = api.init_send_tx(m, args)?;
output_slatepack(
&slate,
&send_file,
use_armored,
use_bin,
sender_1,
recipients_2.clone(),
)?;
api.tx_lock_outputs(m, &slate)?;
Ok(())
})?;
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
let res = slate_from_packed(&send_file, use_armored, (&dec_key_2).as_ref())?;
slatepack = res.0;
slate = res.1;
slate = api.receive_tx(&slate, None)?;
output_slatepack(
&slate,
&receive_file,
use_armored,
use_bin,
sender_2,
match slatepack.sender {
Some(s) => vec![s.clone()],
None => vec![],
},
)?;
Ok(())
})?;
// wallet 1 finalises and posts
wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| {
let res = slate_from_packed(&receive_file, use_armored, (&dec_key_1).as_ref())?;
slate = res.1;
slate = api.finalize_tx(m, &slate)?;
// Output final file for reference
output_slatepack(&slate, &final_file, use_armored, use_bin, None, vec![])?;
api.post_tx(m, &slate, false)?;
bh += 1;
Ok(())
})?;
// let logging finish
stopper.store(false, Ordering::Relaxed);
thread::sleep(Duration::from_millis(200));
Ok(())
}
#[test]
fn slatepack_exchange_json() {
let test_dir = "test_output/slatepack_exchange_json";
setup(test_dir);
// Json output
if let Err(e) = slatepack_exchange_test_impl(test_dir, false, false, false) {
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
}
clean_output_dir(test_dir);
}
#[test]
fn slatepack_exchange_bin() {
let test_dir = "test_output/slatepack_exchange_bin";
setup(test_dir);
// Bin output
if let Err(e) = slatepack_exchange_test_impl(test_dir, true, false, false) {
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
}
clean_output_dir(test_dir);
}
#[test]
fn slatepack_exchange_armored() {
let test_dir = "test_output/slatepack_exchange_armored";
setup(test_dir);
// Bin output
if let Err(e) = slatepack_exchange_test_impl(test_dir, true, true, false) {
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
}
clean_output_dir(test_dir);
}
#[test]
fn slatepack_exchange_json_enc() {
let test_dir = "test_output/slatepack_exchange_json_enc";
setup(test_dir);
// Json output
if let Err(e) = slatepack_exchange_test_impl(test_dir, false, false, true) {
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
}
clean_output_dir(test_dir);
}
#[test]
fn slatepack_exchange_bin_enc() {
let test_dir = "test_output/slatepack_exchange_bin_enc";
setup(test_dir);
// Bin output
if let Err(e) = slatepack_exchange_test_impl(test_dir, true, false, true) {
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
}
clean_output_dir(test_dir);
}
#[test]
fn slatepack_exchange_armored_enc() {
let test_dir = "test_output/slatepack_exchange_armored_enc";
setup(test_dir);
// Bin output
if let Err(e) = slatepack_exchange_test_impl(test_dir, true, true, true) {
panic!("Libwallet Error: {} - {}", e, e.backtrace().unwrap());
}
clean_output_dir(test_dir);
}

View file

@ -35,6 +35,7 @@ byteorder = "1"
hyper = "0.13"
hyper-socks2 = "0.4"
ed25519-dalek = "1.0.0-pre.1"
x25519-dalek = "0.6"
data-encoding = "2"
regex = "1.3"
timer = "0.2"

View file

@ -16,13 +16,13 @@
use std::fs::File;
use std::io::{Read, Write};
use crate::client_utils::byte_ser;
use crate::libwallet::slate_versions::v3::SlateV3;
use crate::libwallet::slate_versions::v4::SlateV4;
use crate::libwallet::{
Error, ErrorKind, Slate, SlateState, SlateVersion, VersionedBinSlate, VersionedSlate,
};
use crate::{SlateGetter, SlatePutter};
use grin_wallet_util::byte_ser;
use std::convert::TryFrom;
use std::path::PathBuf;

View file

@ -15,10 +15,12 @@
mod file;
pub mod http;
mod keybase;
mod slatepack;
pub use self::file::PathToSlate;
pub use self::http::{HttpSlateSender, SchemeNotHttp};
pub use self::keybase::{KeybaseAllChannels, KeybaseChannel};
pub use self::slatepack::{PathToSlatepack, PathToSlatepackArmored, SlatepackArgs};
use crate::config::{TorConfig, WalletConfig};
use crate::libwallet::{Error, ErrorKind, Slate};

View file

@ -0,0 +1,169 @@
// Copyright 2020 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::convert::TryFrom;
/// Slatepack Output 'plugin' implementation
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
use x25519_dalek::PublicKey as xDalekPublicKey;
use x25519_dalek::StaticSecret;
use crate::libwallet::{
Error, ErrorKind, Slate, SlateVersion, Slatepack, SlatepackArmor, SlatepackBin,
VersionedBinSlate, VersionedSlate,
};
use crate::{SlateGetter, SlatePutter};
use grin_wallet_util::byte_ser;
#[derive(Clone)]
pub struct SlatepackArgs<'a> {
pub pathbuf: PathBuf,
pub sender: Option<xDalekPublicKey>,
pub recipients: Vec<xDalekPublicKey>,
pub dec_key: Option<&'a StaticSecret>,
}
pub struct PathToSlatepack<'a>(SlatepackArgs<'a>);
impl<'a> PathToSlatepack<'a> {
/// Create with pathbuf and recipients
pub fn new(args: SlatepackArgs<'a>) -> Self {
Self(args)
}
pub fn get_slatepack_file_contents(&self) -> Result<Vec<u8>, Error> {
let mut pub_tx_f = File::open(&self.0.pathbuf)?;
let mut data = Vec::new();
pub_tx_f.read_to_end(&mut data)?;
Ok(data)
}
// return slatepack itself
pub fn deser_slatepack(&self, data: Vec<u8>) -> Result<Slatepack, Error> {
// try as bin first, then as json
let bin_res = byte_ser::from_bytes::<SlatepackBin>(&data);
match bin_res {
Err(e) => debug!("Not a valid binary slatepack: {} - Will try JSON", e),
Ok(s) => return Ok(s.0),
}
// Otherwise try json
let content = String::from_utf8(data).map_err(|_| ErrorKind::SlatepackDeser)?;
let slatepack: Slatepack = serde_json::from_str(&content).map_err(|e| {
error!("Error reading JSON Slatepack: {}", e);
ErrorKind::SlatepackDeser
})?;
slatepack.ver_check_warn();
Ok(slatepack)
}
pub fn get_slatepack(&self) -> Result<Slatepack, Error> {
let data = self.get_slatepack_file_contents()?;
self.deser_slatepack(data)
}
// Create slatepack from slate and args
pub fn create_slatepack(&self, slate: &Slate) -> Result<Slatepack, Error> {
let out_slate = VersionedSlate::into_version(slate.clone(), SlateVersion::V4)?;
let bin_slate =
VersionedBinSlate::try_from(out_slate).map_err(|_| ErrorKind::SlatepackSer)?;
let mut slatepack = Slatepack::default();
slatepack.payload = byte_ser::to_bytes(&bin_slate).map_err(|_| ErrorKind::SlatepackSer)?;
slatepack.sender = self.0.sender;
slatepack.try_encrypt_payload(self.0.recipients.clone())?;
Ok(slatepack)
}
}
impl<'a> SlatePutter for PathToSlatepack<'a> {
fn put_tx(&self, slate: &Slate, as_bin: bool) -> Result<(), Error> {
let slatepack = self.create_slatepack(slate)?;
let mut pub_tx = File::create(&self.0.pathbuf)?;
if as_bin {
pub_tx.write_all(
&byte_ser::to_bytes(&SlatepackBin(slatepack))
.map_err(|_| ErrorKind::SlatepackSer)?,
)?;
} else {
pub_tx.write_all(
serde_json::to_string_pretty(&slatepack)
.map_err(|_| ErrorKind::SlateSer)?
.as_bytes(),
)?;
}
pub_tx.sync_all()?;
Ok(())
}
}
impl<'a> SlateGetter for PathToSlatepack<'a> {
fn get_tx(&self) -> Result<(Slate, bool), Error> {
let data = self.get_slatepack_file_contents()?;
let mut slatepack = self.deser_slatepack(data)?;
slatepack.try_decrypt_payload(self.0.dec_key)?;
let slate = byte_ser::from_bytes::<VersionedBinSlate>(&slatepack.payload)
.map_err(|_| ErrorKind::SlatepackSer)?;
Ok((Slate::upgrade(slate.into())?, true))
}
}
pub struct PathToSlatepackArmored<'a>(SlatepackArgs<'a>);
impl<'a> PathToSlatepackArmored<'a> {
/// Create with pathbuf and recipients
pub fn new(args: SlatepackArgs<'a>) -> Self {
Self(args)
}
/// decode armor
pub fn decode_armored_file(&self) -> Result<Vec<u8>, Error> {
let mut pub_sp_armored = File::open(&self.0.pathbuf)?;
let mut data = Vec::new();
pub_sp_armored.read_to_end(&mut data)?;
SlatepackArmor::decode(&String::from_utf8(data).unwrap())
}
// return slatepack
pub fn get_slatepack(&self) -> Result<Slatepack, Error> {
let data = self.decode_armored_file()?;
let pts = PathToSlatepack::new(self.0.clone());
pts.deser_slatepack(data)
}
}
impl<'a> SlatePutter for PathToSlatepackArmored<'a> {
fn put_tx(&self, slate: &Slate, _as_bin: bool) -> Result<(), Error> {
let pts = PathToSlatepack::new(self.0.clone());
let slatepack = pts.create_slatepack(slate)?;
let armored = SlatepackArmor::encode(&slatepack, 3)?;
let mut pub_tx = File::create(&self.0.pathbuf)?;
pub_tx.write_all(armored.as_bytes())?;
pub_tx.sync_all()?;
Ok(())
}
}
impl<'a> SlateGetter for PathToSlatepackArmored<'a> {
fn get_tx(&self) -> Result<(Slate, bool), Error> {
let mut slatepack = self.get_slatepack()?;
slatepack.try_decrypt_payload(self.0.dec_key)?;
let slate_bin =
byte_ser::from_bytes::<VersionedBinSlate>(&slatepack.payload).map_err(|e| {
error!("Error reading slate from armored slatepack: {}", e);
ErrorKind::SlatepackDeser
})?;
Ok((Slate::upgrade(slate_bin.into())?, true))
}
}

View file

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
pub mod byte_ser;
mod client;
pub mod json_rpc;

View file

@ -44,8 +44,9 @@ pub mod test_framework;
pub mod tor;
pub use crate::adapters::{
create_sender, HttpSlateSender, KeybaseAllChannels, KeybaseChannel, PathToSlate, SlateGetter,
SlatePutter, SlateReceiver, SlateSender,
create_sender, HttpSlateSender, KeybaseAllChannels, KeybaseChannel, PathToSlate,
PathToSlatepack, PathToSlatepackArmored, SlateGetter, SlatePutter, SlateReceiver, SlateSender,
SlatepackArgs,
};
pub use crate::backends::{wallet_db_exists, LMDBBackend};
pub use crate::error::{Error, ErrorKind};

View file

@ -25,8 +25,18 @@ lazy_static = "1"
strum = "0.15"
strum_macros = "0.15"
ed25519-dalek = "1.0.0-pre.1"
x25519-dalek = "0.6"
byteorder = "1"
base64 = "0.9"
regex = "1.3"
sha2 = "0.8"
bs58 = "0.3"
age = "0.4"
curve25519-dalek = "2.0.0"
secrecy = "0.6"
grin_wallet_util = { path = "../util", version = "4.0.0-alpha.1" }
grin_wallet_config = { path = "../config", version = "4.0.0-alpha.1" }
[dev-dependencies]
byteorder = "1.3"

View file

@ -20,7 +20,7 @@ use crate::grin_core::core::hash::Hashed;
use crate::grin_core::core::Transaction;
use crate::grin_util::secp::key::SecretKey;
use crate::grin_util::Mutex;
use crate::util::OnionV3Address;
use crate::util::{OnionV3Address, OnionV3AddressError};
use crate::api_impl::owner_updater::StatusMessage;
use crate::grin_keychain::{Identifier, Keychain};
@ -93,6 +93,36 @@ where
Ok(addr.to_ed25519()?)
}
/// Retrieve the decryption key for the current parent key
/// the given index
/// set active account
pub fn get_secret_key<'a, L, C, K>(
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
keychain_mask: Option<&SecretKey>,
index: u32,
) -> Result<DalekSecretKey, Error>
where
L: WalletLCProvider<'a, C, K>,
C: NodeClient + 'a,
K: Keychain + 'a,
{
wallet_lock!(wallet_inst, w);
let parent_key_id = w.parent_key_id();
let k = w.keychain(keychain_mask)?;
let sec_addr_key = address::address_from_derivation_path(&k, &parent_key_id, index)?;
let d_skey = match DalekSecretKey::from_bytes(&sec_addr_key.0) {
Ok(k) => k,
Err(e) => {
return Err(OnionV3AddressError::InvalidPrivateKey(format!(
"Unable to create secret key: {}",
e
))
.into());
}
};
Ok(d_skey)
}
/// retrieve outputs
pub fn retrieve_outputs<'a, L, C, K>(
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,

View file

@ -202,6 +202,14 @@ pub enum ErrorKind {
#[fail(display = "Can't Deserialize slate")]
SlateDeser,
/// Can't serialize slate pack
#[fail(display = "Can't Serialize slatepack")]
SlatepackSer,
/// Can't deserialize slate
#[fail(display = "Can't Deserialize slatepack")]
SlatepackDeser,
/// Unknown slate version
#[fail(display = "Unknown Slate Version: {}", _0)]
SlateVersion(u16),
@ -270,6 +278,18 @@ pub enum ErrorKind {
#[fail(display = "Unknown Kernel Feature: {}", _0)]
UnknownKernelFeatures(u8),
/// Invalid Slatepack Data
#[fail(display = "Invalid Slatepack Data: {}", _0)]
InvalidSlatepackData(String),
/// Slatepack Encryption
#[fail(display = "Couldn't encrypt Slatepack: {}", _0)]
SlatepackEncryption(String),
/// age error
#[fail(display = "Age error: {}", _0)]
Age(String),
/// Other
#[fail(display = "Generic error: {}", _0)]
GenericError(String),
@ -398,3 +418,11 @@ impl From<util::OnionV3AddressError> for Error {
Error::from(ErrorKind::OnionV3Address(error))
}
}
impl From<age::Error> for Error {
fn from(error: age::Error) -> Error {
Error {
inner: Context::new(ErrorKind::Age(format!("{}", error))),
}
}
}

View file

@ -52,6 +52,7 @@ mod error;
mod internal;
mod slate;
pub mod slate_versions;
mod slatepack;
mod types;
pub use crate::error::{Error, ErrorKind};
@ -60,6 +61,7 @@ pub use crate::slate_versions::{
SlateVersion, VersionedBinSlate, VersionedCoinbase, VersionedSlate, CURRENT_SLATE_VERSION,
GRIN_BLOCK_HEADER_VERSION,
};
pub use crate::slatepack::{Slatepack, SlatepackArmor, SlatepackBin};
pub use api_impl::owner_updater::StatusMessage;
pub use api_impl::types::{
BlockFees, InitTxArgs, InitTxSendArgs, IssueInvoiceTxArgs, NodeHeightResult,

View file

@ -28,13 +28,22 @@ where
serializer.serialize_str(&base64::encode(&bytes))
}
/// Creates a Vec from a base string
pub fn bytes_from_base64<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
String::deserialize(deserializer)
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))
}
/// Creates a BlindingFactor from a base64 string
pub fn blindingfactor_from_base64<'de, D>(deserializer: D) -> Result<BlindingFactor, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let val = String::deserialize(deserializer)
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))?;
Ok(BlindingFactor::from_slice(&val))
@ -280,6 +289,36 @@ pub mod dalek_pubkey_serde {
}
}
/// Serializes an x25519 PublicKey to and from hex
pub mod dalek_xpubkey_serde {
use crate::grin_util::{from_hex, ToHex};
use serde::{Deserialize, Deserializer, Serializer};
use x25519_dalek::PublicKey as xDalekPublicKey;
///
pub fn serialize<S>(key: &xDalekPublicKey, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&key.as_bytes().to_hex())
}
///
pub fn deserialize<'de, D>(deserializer: D) -> Result<xDalekPublicKey, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
String::deserialize(deserializer)
.and_then(|string| from_hex(&string).map_err(|err| Error::custom(err.to_string())))
.and_then(|bytes: Vec<u8>| {
let mut b = [0u8; 32];
b.copy_from_slice(&bytes[0..32]);
Ok(xDalekPublicKey::from(b))
})
}
}
/// Serializes an ed25519 PublicKey to and from base64
pub mod dalek_pubkey_base64 {
use base64;
@ -310,6 +349,47 @@ pub mod dalek_pubkey_base64 {
}
}
/// Serializes an Option<ed25519_dalek::PublicKey> to and from hex
pub mod option_dalek_pubkey_base64 {
use base64;
use ed25519_dalek::PublicKey as DalekPublicKey;
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serializer};
///
pub fn serialize<S>(key: &Option<DalekPublicKey>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match key {
Some(key) => serializer.serialize_str(&base64::encode(&key.to_bytes())),
None => serializer.serialize_none(),
}
}
///
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<DalekPublicKey>, D::Error>
where
D: Deserializer<'de>,
{
Option::<String>::deserialize(deserializer).and_then(|res| match res {
Some(string) => base64::decode(&string)
.map_err(|err| Error::custom(err.to_string()))
.and_then(|bytes: Vec<u8>| {
let mut b = [0u8; 32];
b.copy_from_slice(&bytes[0..32]);
DalekPublicKey::from_bytes(&b)
.map(Some)
.map_err(|err| Error::custom(err.to_string()))
}),
None => {
println!("None fine");
Ok(None)
}
})
}
}
/// Serializes an Option<ed25519_dalek::PublicKey> to and from hex
pub mod option_dalek_pubkey_serde {
use ed25519_dalek::PublicKey as DalekPublicKey;
@ -349,6 +429,43 @@ pub mod option_dalek_pubkey_serde {
}
}
/// Serializes an Option<x25519_dalek::PublicKey> to and from hex
pub mod option_xdalek_pubkey_serde {
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serializer};
use x25519_dalek::PublicKey as xDalekPublicKey;
use crate::grin_util::{from_hex, ToHex};
///
pub fn serialize<S>(key: &Option<xDalekPublicKey>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match key {
Some(key) => serializer.serialize_str(&key.as_bytes().to_hex()),
None => serializer.serialize_none(),
}
}
///
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<xDalekPublicKey>, D::Error>
where
D: Deserializer<'de>,
{
Option::<String>::deserialize(deserializer).and_then(|res| match res {
Some(string) => from_hex(&string)
.map_err(|err| Error::custom(err.to_string()))
.and_then(|bytes: Vec<u8>| {
let mut b = [0u8; 32];
b.copy_from_slice(&bytes[0..32]);
Ok(Some(xDalekPublicKey::from(b)))
}),
None => Ok(None),
})
}
}
/// Serializes an ed25519_dalek::Signature to and from hex
pub mod dalek_sig_serde {
use ed25519_dalek::Signature as DalekSignature;

View file

@ -0,0 +1,187 @@
// Copyright 2020 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// A note on encoding efficiency: 0.75 for Base64, 0.744 for Base62, 0.732 for Base58
// slatepack uses a modified Base58Check encoding to create armored slate payloads:
// 1. Take first four bytes of SHA256(SHA256(slate.as_bytes()))
// 2. Concatenate result of step 1 and slate.as_bytes()
// 3. Base58 encode bytes from step 2
// Finally add armor framing and space/newline formatting as desired
use crate::{Error, ErrorKind};
use grin_wallet_util::byte_ser;
use regex::Regex;
use sha2::{Digest, Sha256};
use std::str;
use super::types::{Slatepack, SlatepackBin};
// Framing and formatting for slate armor
static HEADER: &str = "BEGINSLATEPACK. ";
static FOOTER: &str = ". ENDSLATEPACK.";
const WORD_LENGTH: usize = 15;
lazy_static! {
static ref HEADER_REGEX: Regex =
Regex::new(concat!(r"^[>\n\r\t ]*BEGINSLATEPACK[>\n\r\t ]*$")).unwrap();
static ref FOOTER_REGEX: Regex =
Regex::new(concat!(r"^[>\n\r\t ]*ENDSLATEPACK[>\n\r\t ]*$")).unwrap();
static ref WHITESPACE_LIST: [u8; 5] = [b'>', b'\n', b'\r', b'\t', b' '];
}
/// Wrapper for associated functions
pub struct SlatepackArmor;
impl SlatepackArmor {
/// Decode an armored Slatepack
pub fn decode(data: &str) -> Result<Vec<u8>, Error> {
// Convert the armored slate to bytes for parsing
let armor_bytes: Vec<u8> = data.as_bytes().to_vec();
// Collect the bytes up to the first period, this is the header
let header_bytes = &armor_bytes
.iter()
.take_while(|byte| **byte != b'.')
.cloned()
.collect::<Vec<u8>>();
// Verify the header...
check_header(&header_bytes)?;
// Get the length of the header
let header_len = *&header_bytes.len() + 1;
// Skip the length of the header to read for the payload until the next period
let payload_bytes = &armor_bytes[header_len as usize..]
.iter()
.take_while(|byte| **byte != b'.')
.cloned()
.collect::<Vec<u8>>();
// Get length of the payload to check the footer framing
let payload_len = *&payload_bytes.len();
// Get footer bytes and verify them
let consumed_bytes = header_len + payload_len + 1;
let footer_bytes = &armor_bytes[consumed_bytes as usize..]
.iter()
.take_while(|byte| **byte != b'.')
.cloned()
.collect::<Vec<u8>>();
check_footer(&footer_bytes)?;
// Clean up the payload bytes to be deserialized
let clean_payload = &payload_bytes
.iter()
.filter(|byte| !WHITESPACE_LIST.contains(byte))
.cloned()
.collect::<Vec<u8>>();
// Decode payload from base58
let base_decode = bs58::decode(&clean_payload).into_vec().unwrap();
let error_code = &base_decode[0..4];
let slatepack_bytes = &base_decode[4..];
// Make sure the error check code is valid for the slate data
error_check(&error_code.to_vec(), &slatepack_bytes.to_vec())?;
// Return slate as binary or string
/*let slatepack_bin = byte_ser::from_bytes::<SlatepackBin>(&slate_bytes).map_err(|e| {
error!("Error reading JSON Slatepack: {}", e);
ErrorKind::SlatepackDeser
})?;*/
Ok(slatepack_bytes.to_vec())
}
/// Encode an armored slatepack
pub fn encode(slatepack: &Slatepack, num_cols: usize) -> Result<String, Error> {
let slatepack_bytes = byte_ser::to_bytes(&SlatepackBin(slatepack.clone()))
.map_err(|_| ErrorKind::SlatepackSer)?;
let encoded_slatepack = base58check(&slatepack_bytes)?;
let formatted_slatepack = format_slatepack(&encoded_slatepack, num_cols)?;
Ok(format!("{}{}{}\n", HEADER, formatted_slatepack, FOOTER))
}
}
// Takes an error check code and a slate binary and verifies that the code was generated from slate
fn error_check(error_code: &Vec<u8>, slate_bytes: &Vec<u8>) -> Result<(), Error> {
let new_check = generate_check(&slate_bytes)?;
if error_code.iter().eq(new_check.iter()) {
Ok(())
} else {
Err(ErrorKind::InvalidSlatepackData(
"Bad slate error code- some data was corrupted".to_string(),
)
.into())
}
}
// Checks header framing bytes and returns an error if they are invalid
fn check_header(header: &Vec<u8>) -> Result<(), Error> {
let framing = str::from_utf8(&header).unwrap();
if HEADER_REGEX.is_match(framing) {
Ok(())
} else {
Err(ErrorKind::InvalidSlatepackData("Bad armor header".to_string()).into())
}
}
// Checks footer framing bytes and returns an error if they are invalid
fn check_footer(footer: &Vec<u8>) -> Result<(), Error> {
let framing = str::from_utf8(&footer).unwrap();
if FOOTER_REGEX.is_match(framing) {
Ok(())
} else {
Err(ErrorKind::InvalidSlatepackData("Bad armor footer".to_string()).into())
}
}
// MODIFIED Base58Check encoding for slate bytes
fn base58check(slate: &[u8]) -> Result<String, Error> {
// Serialize the slate json string to a vector of bytes
let mut slate_bytes: Vec<u8> = slate.to_vec();
// Get the four byte checksum for the slate binary
let mut check_bytes: Vec<u8> = generate_check(&slate_bytes)?;
// Make a new buffer and concatenate checksum bytes and slate bytes
let mut slate_buf = Vec::new();
slate_buf.append(&mut check_bytes);
slate_buf.append(&mut slate_bytes);
// Encode the slate buffer containing the slate binary and checksum bytes as Base58
let b58_slate = bs58::encode(slate_buf).into_string();
Ok(b58_slate)
}
// Adds human readable formatting to the slate payload for armoring
fn format_slatepack(slatepack: &str, num_cols: usize) -> Result<String, Error> {
let formatter = slatepack
.chars()
.enumerate()
.flat_map(|(i, c)| {
if i != 0 && i % WORD_LENGTH == 0 {
if num_cols != 0 && i % (WORD_LENGTH * num_cols) == WORD_LENGTH * 2 {
Some('\n')
} else {
Some(' ')
}
} else {
None
}
.into_iter()
.chain(std::iter::once(c))
})
.collect::<String>();
Ok(formatter)
}
// Returns the first four bytes of a double sha256 hash of some bytes
fn generate_check(payload: &Vec<u8>) -> Result<Vec<u8>, Error> {
let mut first_hash = Sha256::new();
first_hash.input(payload);
let mut second_hash = Sha256::new();
second_hash.input(first_hash.result());
let checksum = second_hash.result();
let check_bytes: Vec<u8> = checksum[0..4].to_vec();
Ok(check_bytes)
}

View file

@ -0,0 +1,19 @@
// Copyright 2020 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
mod armor;
mod types;
pub use self::armor::SlatepackArmor;
pub use self::types::{Slatepack, SlatepackBin};

View file

@ -0,0 +1,491 @@
// Copyright 2020 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// Slatepack Types + Serialization implementation
use x25519_dalek::PublicKey as xDalekPublicKey;
use x25519_dalek::StaticSecret;
use crate::dalek_ser;
use crate::grin_core::ser::{self, Readable, Reader, Writeable, Writer};
use crate::Error;
use std::io::{Read, Write};
pub const SLATEPACK_MAJOR_VERSION: u8 = 1;
pub const SLATEPACK_MINOR_VERSION: u8 = 0;
/// Basic Slatepack definition
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Slatepack {
// Required Fields
/// Versioning info
#[serde(with = "slatepack_version")]
pub slatepack: SlatepackVersion,
/// Delivery Mode, 0 = plain_text, 1 = age encrypted
pub mode: u8,
// Optional Fields
/// Optional Sender address
#[serde(default = "default_sender_none")]
#[serde(with = "dalek_ser::option_xdalek_pubkey_serde")]
#[serde(skip_serializing_if = "Option::is_none")]
pub sender: Option<xDalekPublicKey>,
// Payload
/// Binary payload, can be encrypted or plaintext
#[serde(
serialize_with = "dalek_ser::as_base64",
deserialize_with = "dalek_ser::bytes_from_base64"
)]
pub payload: Vec<u8>,
}
fn default_sender_none() -> Option<xDalekPublicKey> {
None
}
impl Default for Slatepack {
fn default() -> Self {
Self {
slatepack: SlatepackVersion {
major: SLATEPACK_MAJOR_VERSION,
minor: SLATEPACK_MINOR_VERSION,
},
mode: 0,
sender: None,
payload: vec![],
}
}
}
impl Slatepack {
/// return length of optional fields
pub fn opt_fields_len(&self) -> Result<usize, ser::Error> {
let mut retval = 0;
if self.sender.is_some() {
retval += 32;
}
Ok(retval)
}
/// age encrypt the payload with the given public key
pub fn try_encrypt_payload(&mut self, recipients: Vec<xDalekPublicKey>) -> Result<(), Error> {
if recipients.is_empty() {
return Ok(());
}
let rec_keys: Result<Vec<_>, _> = recipients
.into_iter()
.map(|pk| {
let key = age::keys::RecipientKey::X25519(pk);
Ok(key)
})
.collect();
let keys = match rec_keys {
Ok(k) => k,
Err(e) => return Err(e),
};
let encryptor = age::Encryptor::with_recipients(keys);
let mut encrypted = vec![];
let mut writer = encryptor.wrap_output(&mut encrypted, age::Format::Binary)?;
writer.write_all(&self.payload)?;
writer.finish()?;
self.payload = encrypted.to_vec();
self.mode = 1;
Ok(())
}
/// As above, decrypt if needed
pub fn try_decrypt_payload(&mut self, dec_key: Option<&StaticSecret>) -> Result<(), Error> {
if self.mode == 0 {
return Ok(());
}
let dec_key = match dec_key {
Some(k) => k,
None => return Ok(()),
};
let key = age::keys::SecretKey::X25519(dec_key.clone());
let decryptor = match age::Decryptor::new(&self.payload[..])? {
age::Decryptor::Recipients(d) => d,
_ => unreachable!(),
};
let mut decrypted = vec![];
let mut reader = decryptor.decrypt(&[key.into()])?;
reader.read_to_end(&mut decrypted)?;
self.payload = decrypted.to_vec();
Ok(())
}
/// version check warning
// TODO: API?
pub fn ver_check_warn(&self) {
if self.slatepack.major > SLATEPACK_MAJOR_VERSION
|| (self.slatepack.major == SLATEPACK_MAJOR_VERSION
&& self.slatepack.minor < SLATEPACK_MINOR_VERSION)
{
warn!("Incoming Slatepack's version is greater than what this wallet recognizes");
warn!("You may need to upgrade if it contains unsupported features");
warn!(
"Incoming: {}.{}, This wallet: {}.{}",
self.slatepack.major,
self.slatepack.minor,
SLATEPACK_MAJOR_VERSION,
SLATEPACK_MINOR_VERSION
);
}
}
}
/// Wrapper for outputting slate as binary
#[derive(Debug, Clone)]
pub struct SlatepackBin(pub Slatepack);
impl serde::Serialize for SlatepackBin {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut vec = vec![];
ser::serialize(&mut vec, ser::ProtocolVersion(4), self)
.map_err(|err| serde::ser::Error::custom(err.to_string()))?;
serializer.serialize_bytes(&vec)
}
}
impl<'de> serde::Deserialize<'de> for SlatepackBin {
fn deserialize<D>(deserializer: D) -> Result<SlatepackBin, D::Error>
where
D: serde::Deserializer<'de>,
{
struct SlatepackBinVisitor;
impl<'de> serde::de::Visitor<'de> for SlatepackBinVisitor {
type Value = SlatepackBin;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a serialised binary Slatepack")
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<SlatepackBin, E>
where
E: serde::de::Error,
{
let mut reader = std::io::Cursor::new(value.to_vec());
let s = ser::deserialize(&mut reader, ser::ProtocolVersion(4))
.map_err(|err| serde::de::Error::custom(err.to_string()))?;
Ok(s)
}
}
deserializer.deserialize_bytes(SlatepackBinVisitor)
}
}
impl Writeable for SlatepackBin {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
let sp = self.0.clone();
// Version (2)
sp.slatepack.write(writer)?;
// Mode (1)
writer.write_u8(sp.mode)?;
// 16 bits of optional content flags (2), most reserved for future use
let mut opt_flags: u16 = 0;
if sp.sender.is_some() {
opt_flags |= 0x01;
}
writer.write_u16(opt_flags)?;
// Bytes to skip from here (Start of optional fields) to get to payload
writer.write_u32(sp.opt_fields_len()? as u32)?;
// write optional fields
if let Some(s) = sp.sender {
writer.write_fixed_bytes(s.as_bytes())?;
};
// Now write payload (length prefixed)
writer.write_bytes(sp.payload.clone())
}
}
impl Readable for SlatepackBin {
fn read<R: Reader>(reader: &mut R) -> Result<SlatepackBin, ser::Error> {
// Version (2)
let slatepack = SlatepackVersion::read(reader)?;
// Mode (1)
let mode = reader.read_u8()?;
if mode > 1 {
return Err(ser::Error::UnexpectedData {
expected: vec![0, 1],
received: vec![mode],
});
}
// optional content flags (2)
let opt_flags = reader.read_u16()?;
// start of header
let mut bytes_to_payload = reader.read_u32()?;
let sender = if opt_flags & 0x01 > 0 {
bytes_to_payload -= 32;
let bytes = reader.read_fixed_bytes(32)?;
let mut b = [0u8; 32];
b.copy_from_slice(&bytes[0..32]);
Some(xDalekPublicKey::from(b))
} else {
None
};
// skip over any unknown future fields until header
while bytes_to_payload > 0 {
let _ = reader.read_u8()?;
bytes_to_payload -= 1;
}
let payload = reader.read_bytes_len_prefix()?;
Ok(SlatepackBin(Slatepack {
slatepack,
mode,
sender,
payload,
}))
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct SlatepackVersion {
/// Major
pub major: u8,
/// Minor
pub minor: u8,
}
impl Writeable for SlatepackVersion {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
writer.write_u8(self.major)?;
writer.write_u8(self.minor)
}
}
impl Readable for SlatepackVersion {
fn read<R: Reader>(reader: &mut R) -> Result<SlatepackVersion, ser::Error> {
let major = reader.read_u8()?;
let minor = reader.read_u8()?;
Ok(SlatepackVersion { major, minor })
}
}
/// Serializes version field JSON
pub mod slatepack_version {
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serializer};
use super::SlatepackVersion;
///
pub fn serialize<S>(v: &SlatepackVersion, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("{}.{}", v.major, v.minor))
}
///
pub fn deserialize<'de, D>(deserializer: D) -> Result<SlatepackVersion, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer).and_then(|s| {
let mut retval = SlatepackVersion { major: 0, minor: 0 };
let v: Vec<&str> = s.split('.').collect();
if v.len() != 2 {
return Err(Error::custom("Cannot parse version"));
}
match u8::from_str_radix(v[0], 10) {
Ok(u) => retval.major = u,
Err(e) => return Err(Error::custom(format!("Cannot parse version: {}", e))),
}
match u8::from_str_radix(v[1], 10) {
Ok(u) => retval.minor = u,
Err(e) => return Err(Error::custom(format!("Cannot parse version: {}", e))),
}
Ok(retval)
})
}
}
/// Header struct definition
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RecipientListEntry {
#[serde(with = "dalek_ser::dalek_xpubkey_serde")]
/// Public Address
pub pub_address: xDalekPublicKey,
}
impl Writeable for RecipientListEntry {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
writer.write_fixed_bytes(self.pub_address.as_bytes())
}
}
impl Readable for RecipientListEntry {
fn read<R: Reader>(reader: &mut R) -> Result<RecipientListEntry, ser::Error> {
let bytes = reader.read_fixed_bytes(32)?;
let mut b = [0u8; 32];
b.copy_from_slice(&bytes[0..32]);
let pub_address = xDalekPublicKey::from(b);
Ok(RecipientListEntry { pub_address })
}
}
#[test]
fn slatepack_bin_basic_ser() -> Result<(), grin_wallet_util::byte_ser::Error> {
use grin_wallet_util::byte_ser;
let slatepack = SlatepackVersion { major: 1, minor: 0 };
let mut payload: Vec<u8> = Vec::with_capacity(243);
for _ in 0..payload.capacity() {
payload.push(rand::random());
}
let sp = Slatepack {
slatepack,
mode: 1,
sender: None,
payload,
};
let ser = byte_ser::to_bytes(&SlatepackBin(sp.clone()))?;
let deser = byte_ser::from_bytes::<SlatepackBin>(&ser)?.0;
assert_eq!(sp.slatepack, deser.slatepack);
assert_eq!(sp.mode, deser.mode);
assert!(sp.sender.is_none());
Ok(())
}
#[test]
fn slatepack_bin_opt_fields_ser() -> Result<(), grin_wallet_util::byte_ser::Error> {
use grin_wallet_util::byte_ser;
use rand::{thread_rng, Rng};
let slatepack = SlatepackVersion { major: 1, minor: 0 };
let mut payload: Vec<u8> = Vec::with_capacity(243);
for _ in 0..payload.capacity() {
payload.push(rand::random());
}
// includes optional fields
let bytes: [u8; 32] = thread_rng().gen();
let sender_secret = StaticSecret::from(bytes);
let sender = Some(xDalekPublicKey::from(&sender_secret));
let sp = Slatepack {
slatepack,
mode: 1,
sender,
payload,
};
let ser = byte_ser::to_bytes(&SlatepackBin(sp.clone()))?;
let deser = byte_ser::from_bytes::<SlatepackBin>(&ser)?.0;
assert_eq!(sp.slatepack, deser.slatepack);
assert_eq!(sp.mode, deser.mode);
assert_eq!(
sp.sender.unwrap().as_bytes(),
deser.sender.unwrap().as_bytes()
);
Ok(())
}
// ensure that a slatepack with unknown data in the optional fields can be read
#[test]
fn slatepack_bin_future() -> Result<(), grin_wallet_util::byte_ser::Error> {
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use grin_wallet_util::byte_ser;
use rand::{thread_rng, Rng};
use std::io::Cursor;
let slatepack = SlatepackVersion { major: 1, minor: 0 };
let payload_size = 1234;
let mut payload: Vec<u8> = Vec::with_capacity(payload_size);
for _ in 0..payload.capacity() {
payload.push(rand::random());
}
let bytes: [u8; 32] = thread_rng().gen();
let sender_secret = StaticSecret::from(bytes);
let sender = Some(xDalekPublicKey::from(&sender_secret));
println!(
"sender key len: {}",
sender.as_ref().unwrap().as_bytes().len()
);
let sp = Slatepack {
slatepack,
mode: 1,
sender,
payload: payload.clone(),
};
let ser = byte_ser::to_bytes(&SlatepackBin(sp.clone()))?;
// Add an amount of meaningless (to us) data
let num_extra_bytes = 248;
let mut new_bytes = vec![];
// Version 2
// mode 1
// opt flags 2
// opt fields len (bytes to payload) 4
// bytes 5-8 are opt fields len
// sender 32
let mut opt_fields_len_bytes = [0u8; 4];
opt_fields_len_bytes.copy_from_slice(&ser[5..9]);
let mut rdr = Cursor::new(opt_fields_len_bytes.to_vec());
let opt_fields_len = rdr.read_u32::<BigEndian>().unwrap();
// check this matches what we expect below
assert_eq!(opt_fields_len, 32);
let end_head_pos = opt_fields_len as usize + 8 + 1;
for i in 0..end_head_pos {
new_bytes.push(ser[i]);
}
for _ in 0..num_extra_bytes {
new_bytes.push(thread_rng().gen());
}
for i in 0..8 {
//push payload length prefix
new_bytes.push(ser[end_head_pos + i]);
}
for i in 0..payload_size {
new_bytes.push(ser[end_head_pos + 8 + i]);
}
assert_eq!(new_bytes.len(), ser.len() + num_extra_bytes as usize);
// and set new opt fields length
let mut wtr = vec![];
wtr.write_u32::<BigEndian>(opt_fields_len + num_extra_bytes as u32)
.unwrap();
for i in 0..wtr.len() {
new_bytes[5 + i] = wtr[i];
}
let deser = byte_ser::from_bytes::<SlatepackBin>(&new_bytes)?.0;
assert_eq!(sp.slatepack, deser.slatepack);
assert_eq!(sp.mode, deser.mode);
assert_eq!(
sp.sender.unwrap().as_bytes(),
deser.sender.unwrap().as_bytes()
);
Ok(())
}

View file

@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Simple serde byte array serializer, assumes target already
// knows how to serialize itself into binary (because that all
// this serializer can do)
//! Simple serde byte array serializer, assumes target already
//! knows how to serialize itself into binary (because that all
//! this serializer can do)
use serde::de::Visitor;
use serde::{de, ser, Deserialize, Serialize};
use std;

View file

@ -27,6 +27,9 @@ mod ov3;
pub use ov3::OnionV3Address;
pub use ov3::OnionV3Error as OnionV3AddressError;
#[allow(missing_docs)]
pub mod byte_ser;
pub use grin_api;
pub use grin_chain;
pub use grin_core;