From 1d451379b5a98b386dd1e720bd149fa075a90cbc Mon Sep 17 00:00:00 2001 From: Aleksey Proshutisnkiy Date: Thu, 15 Apr 2021 14:00:27 +0300 Subject: [PATCH] Identity rework (#6) --- Cargo.lock | 189 +++++++++++------- Cargo.toml | 11 +- identity/Cargo.toml | 14 +- identity/src/ed25519.rs | 239 +++++++++++++++++++++++ identity/src/error.rs | 69 +++++++ identity/src/key_pair.rs | 340 +++++++++++++++++++-------------- identity/src/lib.rs | 12 +- identity/src/public_key.rs | 144 ++++++++++---- identity/src/rsa.rs | 278 +++++++++++++++++++++++++++ identity/src/secp256k1.rs | 230 ++++++++++++++++++++++ identity/src/secret_key.rs | 35 ---- identity/src/signature.rs | 100 +++++++--- identity/src/test/rsa-2048.pk8 | Bin 0 -> 1218 bytes identity/src/test/rsa-3072.pk8 | Bin 0 -> 1792 bytes identity/src/test/rsa-4096.pk8 | Bin 0 -> 2376 bytes src/certificate.rs | 166 ++++++++-------- src/public_key_hashable.rs | 14 +- src/revoke.rs | 44 +++-- src/trust.rs | 140 +++++++------- src/trust_graph.rs | 94 ++++----- src/trust_graph_storage.rs | 2 +- src/trust_node.rs | 26 +-- 22 files changed, 1589 insertions(+), 558 deletions(-) create mode 100644 identity/src/ed25519.rs create mode 100644 identity/src/error.rs create mode 100644 identity/src/rsa.rs create mode 100644 identity/src/secp256k1.rs delete mode 100644 identity/src/secret_key.rs create mode 100644 identity/src/test/rsa-2048.pk8 create mode 100644 identity/src/test/rsa-3072.pk8 create mode 100644 identity/src/test/rsa-4096.pk8 diff --git a/Cargo.lock b/Cargo.lock index 31fd96f..9f5d6c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,10 +18,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] -name = "anyhow" -version = "1.0.38" +name = "aho-corasick" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] name = "arrayref" @@ -130,9 +139,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" @@ -189,9 +198,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06d4a9551359071d1890820e3571252b91229e0712e7c36b08940e603c5a8fc" +checksum = "e9d6ddad5866bb2170686ed03f6839d31a76e5407d80b1c334a2c24618543ffa" dependencies = [ "darling_core", "darling_macro", @@ -199,9 +208,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b443e5fb0ddd56e0c9bfa47dc060c5306ee500cb731f2b91432dd65589a77684" +checksum = "a9ced1fd13dc386d5a8315899de465708cf34ee2a6d9394654515214e67bb846" dependencies = [ "fnv", "ident_case", @@ -213,9 +222,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0220073ce504f12a70efc4e7cdaea9e9b1b324872e7ad96a208056d7a638b81" +checksum = "0a7a1445d54b2f9792e3b31a3e715feabbace393f38dc4ffd49d94ee9bc487d5" dependencies = [ "darling_core", "quote", @@ -288,6 +297,16 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + [[package]] name = "failure" version = "0.1.8" @@ -325,8 +344,7 @@ checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" [[package]] name = "fluence-fork-libp2p-core" version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "735c96afd9c133fead34204dff9df0b9f0a86e1bb8e6c32faf8b9f60427ce0cd" +source = "git+https://github.com/fluencelabs/rust-libp2p?branch=identity_rework#2d785558cfc9908cbfe54c0750e660c001cb2c3b" dependencies = [ "asn1_der", "bs58 0.4.0", @@ -342,7 +360,7 @@ dependencies = [ "log", "multihash", "parking_lot", - "pin-project 1.0.5", + "pin-project 1.0.6", "prost", "prost-build", "rand 0.7.3", @@ -360,13 +378,12 @@ dependencies = [ [[package]] name = "fluence-fork-multistream-select" version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08cb5798accfd52dc9a104d7928f379e93d8432ac03201f5a16ed6dc2af1520d" +source = "git+https://github.com/fluencelabs/rust-libp2p?branch=identity_rework#2d785558cfc9908cbfe54c0750e660c001cb2c3b" dependencies = [ "bytes", "futures", "log", - "pin-project 1.0.5", + "pin-project 1.0.6", "smallvec", "unsigned-varint 0.7.0", ] @@ -374,8 +391,7 @@ dependencies = [ [[package]] name = "fluence-fork-parity-multiaddr" version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42d11a2d1e7b50e91c947f1a480004ed5f207bb6c8f8a69dbc083588f8419c1" +source = "git+https://github.com/fluencelabs/rust-libp2p?branch=identity_rework#2d785558cfc9908cbfe54c0750e660c001cb2c3b" dependencies = [ "arrayref", "bs58 0.4.0", @@ -393,16 +409,24 @@ dependencies = [ name = "fluence-identity" version = "0.2.7" dependencies = [ + "asn1_der", "bs58 0.3.1", "ed25519", "ed25519-dalek", "fluence-fork-libp2p-core", + "lazy_static", + "libsecp256k1", + "quickcheck", "rand 0.7.3", + "ring", "serde", + "serde_bytes", "serde_json", "serde_with", + "sha2 0.9.3", "signature", "thiserror", + "zeroize", ] [[package]] @@ -631,9 +655,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ "autocfg", "hashbrown", @@ -665,9 +689,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" -version = "0.3.48" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -680,9 +704,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.87" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "265d751d31d6780a3f956bb5b8022feba2d94eeee5a84ba64f4212eedca42213" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "libsecp256k1" @@ -702,9 +726,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" dependencies = [ "scopeguard", ] @@ -769,9 +793,9 @@ dependencies = [ [[package]] name = "multimap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "num_cpus" @@ -850,27 +874,27 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" +checksum = "918192b5c59119d51e0cd221f4d49dde9112824ba717369e903c97d076083d0f" dependencies = [ - "pin-project-internal 0.4.27", + "pin-project-internal 0.4.28", ] [[package]] name = "pin-project" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" +checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6" dependencies = [ - "pin-project-internal 1.0.5", + "pin-project-internal 1.0.6", ] [[package]] name = "pin-project-internal" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" +checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e" dependencies = [ "proc-macro2", "quote", @@ -879,9 +903,9 @@ dependencies = [ [[package]] name = "pin-project-internal" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" +checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5" dependencies = [ "proc-macro2", "quote", @@ -953,9 +977,9 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -1011,6 +1035,18 @@ dependencies = [ "prost", ] +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger", + "log", + "rand 0.7.3", + "rand_core 0.5.1", +] + [[package]] name = "quote" version = "1.0.9" @@ -1130,6 +1166,23 @@ dependencies = [ "syn", ] +[[package]] +name = "regex" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -1167,7 +1220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4da5fcb054c46f5a5dff833b129285a93d3f0179531735e6c866e8cc307d2020" dependencies = [ "futures", - "pin-project 0.4.27", + "pin-project 0.4.28", "static_assertions", ] @@ -1320,9 +1373,9 @@ checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" [[package]] name = "syn" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed22b90a0e734a23a7610f4283ac9e5acfb96cbb30dfefa540d66f866f1c09c5" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" dependencies = [ "proc-macro2", "quote", @@ -1377,9 +1430,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" dependencies = [ "tinyvec_macros", ] @@ -1409,9 +1462,11 @@ dependencies = [ "failure", "fluence-fork-libp2p-core", "fluence-identity", + "libsecp256k1", "log", "rand 0.7.3", "ref-cast", + "ring", "serde", "serde_json", "serde_with", @@ -1421,15 +1476,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" [[package]] name = "unicode-bidi" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" dependencies = [ "matches", ] @@ -1487,9 +1542,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "void" @@ -1511,9 +1566,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1521,9 +1576,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", "lazy_static", @@ -1536,9 +1591,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1546,9 +1601,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", @@ -1559,15 +1614,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.71" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "web-sys" -version = "0.3.48" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b" +checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" dependencies = [ "js-sys", "wasm-bindgen", @@ -1575,12 +1630,12 @@ dependencies = [ [[package]] name = "which" -version = "4.0.2" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c14ef7e1b8b8ecfc75d5eca37949410046e66f15d185c01d70824f1f8111ef" +checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe" dependencies = [ + "either", "libc", - "thiserror", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 37c285a..f2eaf16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,10 @@ license = "Apache-2.0" repository = "https://github.com/fluencelabs/trust-graph" [dependencies] -libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.1" } +libp2p-core = { git = "https://github.com/fluencelabs/rust-libp2p", branch = "identity_rework", package = "fluence-fork-libp2p-core" } serde = { version = "=1.0.118", features = ["derive"] } -fluence-identity = { version = "0.2", path = "identity" } +fluence-identity = {path = "./identity"} serde_json = "1.0.58" bs58 = "0.3.1" failure = "0.1.6" @@ -23,3 +23,10 @@ rand = "0.7.0" signature = "1.3.0" serde_with = "1.6.0" thiserror = "1.0.23" +libsecp256k1 = "0.3.5" +ring = "0.16.20" + +[workspace] +members = [ + "identity" +] diff --git a/identity/Cargo.toml b/identity/Cargo.toml index 044dd10..de011d7 100644 --- a/identity/Cargo.toml +++ b/identity/Cargo.toml @@ -8,13 +8,23 @@ license = "Apache-2.0" repository = "https://github.com/fluencelabs/trust-graph" [dependencies] -libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.1" } serde = { version = "=1.0.118", features = ["derive"] } serde_json = "1.0.58" bs58 = "0.3.1" -ed25519-dalek = { version = "1.0.1", features = ["serde"] } +ed25519-dalek = { version = "1.0.1", features = ["serde", "std"] } rand = "0.7.0" signature = "1.3.0" ed25519 = "1.0.3" serde_with = "1.6.0" thiserror = "1.0.23" +lazy_static = "1.2" +libsecp256k1 = "0.3.5" +ring = { version = "0.16.9", features = ["alloc", "std"], default-features = false } +asn1_der = "0.6.1" +sha2 = "0.9.1" +zeroize = "1" +serde_bytes = "0.11" +libp2p-core = { git = "https://github.com/fluencelabs/rust-libp2p", branch = "identity_rework", package = "fluence-fork-libp2p-core" } + +[dev-dependencies] +quickcheck = "0.9.0" diff --git a/identity/src/ed25519.rs b/identity/src/ed25519.rs new file mode 100644 index 0000000..c8ee2a2 --- /dev/null +++ b/identity/src/ed25519.rs @@ -0,0 +1,239 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Ed25519 keys. +use crate::error::{DecodingError, SigningError}; +use ed25519_dalek::{self as ed25519, Signer as _, Verifier as _}; +use rand::RngCore; +use std::convert::TryFrom; +use zeroize::Zeroize; +use core::fmt; +use serde::{Deserialize, Serialize}; + +/// An Ed25519 keypair. +pub struct Keypair(ed25519::Keypair); + +impl Keypair { + /// Generate a new Ed25519 keypair. + pub fn generate() -> Self { + Keypair::from(SecretKey::generate()) + } + + /// Encode the keypair into a byte array by concatenating the bytes + /// of the secret scalar and the compressed public point, + /// an informal standard for encoding Ed25519 keypairs. + pub fn encode(&self) -> [u8; 64] { + self.0.to_bytes() + } + + /// Decode a keypair from the format produced by `encode`, + /// zeroing the input on success. + pub fn decode(kp: &mut [u8]) -> Result { + ed25519::Keypair::from_bytes(kp) + .map(|k| { + kp.zeroize(); + Keypair(k) + }) + .map_err(DecodingError::Ed25519) + } + + /// Sign a message using the private key of this keypair. + pub fn sign(&self, msg: &[u8]) -> Result, SigningError> { + Ok(self.0.try_sign(msg)?.to_bytes().to_vec()) + } + + /// Get the public key of this keypair. + pub fn public(&self) -> PublicKey { + PublicKey(self.0.public) + } + + /// Get the secret key of this keypair. + pub fn secret(&self) -> SecretKey { + SecretKey::from_bytes(&mut self.0.secret.to_bytes()) + .expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k") + } +} + +impl fmt::Debug for Keypair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Keypair").field("public", &self.0.public).finish() + } +} + +impl Clone for Keypair { + fn clone(&self) -> Self { + let mut sk_bytes = self.0.secret.to_bytes(); + let secret = SecretKey::from_bytes(&mut sk_bytes) + .expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k").0; + let public = ed25519::PublicKey::from_bytes(&self.0.public.to_bytes()) + .expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k"); + Keypair(ed25519::Keypair { secret, public }) + } +} + +/// Build keypair from existing ed25519 keypair +impl From for Keypair { + fn from(kp: ed25519::Keypair) -> Self { + Keypair(kp) + } +} + +/// Demote an Ed25519 keypair to a secret key. +impl From for SecretKey { + fn from(kp: Keypair) -> Self { + SecretKey(kp.0.secret) + } +} + +/// Promote an Ed25519 secret key into a keypair. +impl From for Keypair { + fn from(sk: SecretKey) -> Self { + let secret: ed25519::ExpandedSecretKey = (&sk.0).into(); + let public = ed25519::PublicKey::from(&secret); + Keypair(ed25519::Keypair { secret: sk.0, public }) + } +} + +/// An Ed25519 public key. +#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)] +pub struct PublicKey(ed25519::PublicKey); + +impl PublicKey { + /// Verify the Ed25519 signature on a message using the public key. + pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), SigningError> { + ed25519::Signature::try_from(sig).and_then(|s| self.0.verify(msg, &s)).map_err(SigningError::Ed25519) + } + + /// Encode the public key into a byte array in compressed form, i.e. + /// where one coordinate is represented by a single bit. + pub fn encode(&self) -> [u8; 32] { + self.0.to_bytes() + } + + /// Decode a public key from a byte array as produced by `encode`. + pub fn decode(bytes: &[u8]) -> Result { + ed25519::PublicKey::from_bytes(bytes) + .map_err(DecodingError::Ed25519) + .map(PublicKey) + } +} + +/// An Ed25519 secret key. +pub struct SecretKey(ed25519::SecretKey); + +/// View the bytes of the secret key. +impl AsRef<[u8]> for SecretKey { + fn as_ref(&self) -> &[u8] { + self.0.as_bytes() + } +} + +impl Clone for SecretKey { + fn clone(&self) -> Self { + let mut sk_bytes = self.0.to_bytes(); + Self::from_bytes(&mut sk_bytes) + .expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k") + } +} + +impl fmt::Debug for SecretKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SecretKey") + } +} + +impl SecretKey { + /// Generate a new Ed25519 secret key. + pub fn generate() -> Self { + let mut bytes = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut bytes); + SecretKey(ed25519::SecretKey::from_bytes(&bytes) + .expect("this returns `Err` only if the length is wrong; the length is correct; qed")) + } + + /// Create an Ed25519 secret key from a byte slice, zeroing the input on success. + /// If the bytes do not constitute a valid Ed25519 secret key, an error is + /// returned. + pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result { + let sk_bytes = sk_bytes.as_mut(); + let secret = ed25519::SecretKey::from_bytes(&*sk_bytes) + .map_err(DecodingError::Ed25519)?; + sk_bytes.zeroize(); + Ok(SecretKey(secret)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub struct Signature(pub Vec); + +#[cfg(test)] +mod tests { + use super::*; + use quickcheck::*; + + fn eq_keypairs(kp1: &Keypair, kp2: &Keypair) -> bool { + kp1.public() == kp2.public() + && + kp1.0.secret.as_bytes() == kp2.0.secret.as_bytes() + } + + #[test] + fn ed25519_keypair_encode_decode() { + fn prop() -> bool { + let kp1 = Keypair::generate(); + let mut kp1_enc = kp1.encode(); + let kp2 = Keypair::decode(&mut kp1_enc).unwrap(); + eq_keypairs(&kp1, &kp2) + && + kp1_enc.iter().all(|b| *b == 0) + } + QuickCheck::new().tests(10).quickcheck(prop as fn() -> _); + } + + #[test] + fn ed25519_keypair_from_secret() { + fn prop() -> bool { + let kp1 = Keypair::generate(); + let mut sk = kp1.0.secret.to_bytes(); + let kp2 = Keypair::from(SecretKey::from_bytes(&mut sk).unwrap()); + eq_keypairs(&kp1, &kp2) + && + sk == [0u8; 32] + } + QuickCheck::new().tests(10).quickcheck(prop as fn() -> _); + } + + #[test] + fn ed25519_signature() { + let kp = Keypair::generate(); + let pk = kp.public(); + + let msg = "hello world".as_bytes(); + let sig = kp.sign(msg).unwrap(); + assert!(pk.verify(msg, &sig).is_ok()); + + let mut invalid_sig = sig.clone(); + invalid_sig[3..6].copy_from_slice(&[10, 23, 42]); + assert!(pk.verify(msg, &invalid_sig).is_err()); + + let invalid_msg = "h3ll0 w0rld".as_bytes(); + assert!(pk.verify(invalid_msg, &sig).is_err()); + } +} diff --git a/identity/src/error.rs b/identity/src/error.rs new file mode 100644 index 0000000..fa0b06c --- /dev/null +++ b/identity/src/error.rs @@ -0,0 +1,69 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Errors during identity key operations. +use thiserror::Error as ThisError; + +/// An error during decoding of key material. +#[derive(ThisError, Debug)] +pub enum Error { + #[error("{0} Key format is not supported")] + InvalidKeyFormat(String), +} + +/// An error during decoding of key material. +#[derive(ThisError, Debug)] +pub enum DecodingError { + #[error("Failed to decode with ed25519: {0}")] + Ed25519( + #[from] + #[source] + ed25519_dalek::ed25519::Error + ), + #[error("Failed to decode with RSA")] + Rsa, + #[error("Failed to decode with secp256k1")] + Secp256k1, + #[error("RSA keypair decoding is not supported yet")] + KeypairDecodingIsNotSupported, + #[error("Invalid type prefix")] + InvalidTypeByte, + #[error("Cannot decode public key from base58 :{0}")] + Base58DecodeError(#[source] bs58::decode::Error), +} + +/// An error during signing of a message. +#[derive(ThisError, Debug)] +pub enum SigningError { + #[error("Failed to sign with ed25519: {0}")] + Ed25519( + #[from] + #[source] + ed25519_dalek::ed25519::Error + ), + #[error("Failed to sign with RSA")] + Rsa, + #[error("Failed to sign with secp256k1: {0}")] + Secp256k1( + #[from] + #[source] + secp256k1::Error + ), +} diff --git a/identity/src/key_pair.rs b/identity/src/key_pair.rs index 7de2bb7..9a3df83 100644 --- a/identity/src/key_pair.rs +++ b/identity/src/key_pair.rs @@ -1,168 +1,224 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * 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. - */ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. -use crate::ed25519::Keypair as Libp2pKeyPair; +//! A node's network identity keys. +use crate::ed25519; +#[cfg(not(target_arch = "wasm32"))] +use crate::rsa; +use crate::secp256k1; use crate::public_key::PublicKey; -use crate::secret_key::SecretKey; use crate::signature::Signature; -use ed25519_dalek::SignatureError; -use ed25519_dalek::Signer; +use crate::error::{Error, DecodingError, SigningError}; +use std::str::FromStr; +use std::convert::TryFrom; -use rand::rngs::OsRng; -use std::fmt; +/// Identity keypair of a node. +/// +/// # Example: Generating RSA keys with OpenSSL +/// +/// ```text +/// openssl genrsa -out private.pem 2048 +/// openssl pkcs8 -in private.pem -inform PEM -topk8 -out private.pk8 -outform DER -nocrypt +/// rm private.pem # optional +/// ``` +/// +/// Loading the keys: +/// +/// ```text +/// let mut bytes = std::fs::read("private.pk8").unwrap(); +/// let keypair = Keypair::rsa_from_pkcs8(&mut bytes); +/// ``` +/// -/// An Ed25519 keypair. -#[derive(Debug)] -pub struct KeyPair { - key_pair: ed25519_dalek::Keypair, + +pub enum KeyFormat { + Ed25519, + #[cfg(not(target_arch = "wasm32"))] + Rsa, + Secp256k1, +} + +impl FromStr for KeyFormat { + type Err = Error; + + #[inline] + fn from_str(s: &str) -> Result { + match s { + "ed25519" => Ok(KeyFormat::Ed25519), + "secp256k1" => Ok(KeyFormat::Secp256k1), + #[cfg(not(target_arch = "wasm32"))] + "rsa" => Ok(KeyFormat::Rsa), + _ => Err(Error::InvalidKeyFormat(s.to_string())) + } + } +} + +impl TryFrom for KeyFormat { + type Error = DecodingError; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(KeyFormat::Ed25519), + #[cfg(not(target_arch = "wasm32"))] + 1 => Ok(KeyFormat::Rsa), + 2 => Ok(KeyFormat::Secp256k1), + _ => Err(DecodingError::InvalidTypeByte) + } + } +} + +impl From for u8 { + fn from(kf: KeyFormat) -> Self { + match kf { + KeyFormat::Ed25519 => 0, + #[cfg(not(target_arch = "wasm32"))] + KeyFormat::Rsa => 1, + KeyFormat::Secp256k1 => 2, + } + } +} + +#[derive(Clone)] +pub enum KeyPair { + /// An Ed25519 keypair. + Ed25519(ed25519::Keypair), + #[cfg(not(target_arch = "wasm32"))] + /// An RSA keypair. + Rsa(rsa::Keypair), + /// A Secp256k1 keypair. + Secp256k1(secp256k1::Keypair), } impl KeyPair { + pub fn generate(format: KeyFormat) -> KeyPair { + match format { + KeyFormat::Ed25519 => KeyPair::generate_ed25519(), + KeyFormat::Secp256k1 => KeyPair::generate_secp256k1(), + #[cfg(not(target_arch = "wasm32"))] + KeyFormat::Rsa => todo!("rsa generation is not supported yet!"), + } + } + /// Generate a new Ed25519 keypair. - #[allow(dead_code)] - pub fn generate() -> Self { - let mut csprng = OsRng {}; - let kp = ed25519_dalek::Keypair::generate(&mut csprng); - kp.into() + pub fn generate_ed25519() -> KeyPair { + KeyPair::Ed25519(ed25519::Keypair::generate()) } - pub fn public(&self) -> PublicKey { - PublicKey(self.key_pair.public) + /// Generate a new Secp256k1 keypair. + pub fn generate_secp256k1() -> KeyPair { + KeyPair::Secp256k1(secp256k1::Keypair::generate()) } - pub fn secret(&self) -> SecretKey { - let b = self.key_pair.secret.to_bytes(); - SecretKey::from_bytes(&b).expect("SecretKey::from_bytes(to_bytes(k)) != k") + /// Decode an keypair from a DER-encoded secret key in PKCS#8 PrivateKeyInfo + /// format (i.e. unencrypted) as defined in [RFC5208]. + /// + /// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5 + #[cfg(not(target_arch = "wasm32"))] + pub fn rsa_from_pkcs8(pkcs8_der: &mut [u8]) -> Result { + rsa::Keypair::from_pkcs8(pkcs8_der).map(KeyPair::Rsa) } - pub fn from_bytes(sk_bytes: &[u8]) -> Result { - let kp = ed25519_dalek::Keypair::from_bytes(sk_bytes)?; - Ok(KeyPair { key_pair: kp }) + /// Decode a keypair from a DER-encoded Secp256k1 secret key in an ECPrivateKey + /// structure as defined in [RFC5915]. + /// + /// [RFC5915]: https://tools.ietf.org/html/rfc5915 + pub fn secp256k1_from_der(der: &mut [u8]) -> Result { + secp256k1::SecretKey::from_der(der) + .map(|sk| KeyPair::Secp256k1(secp256k1::Keypair::from(sk))) } - /// Encode the keypair into a byte array by concatenating the bytes - /// of the secret scalar and the compressed public point/ - #[allow(dead_code)] - pub fn encode(&self) -> [u8; 64] { - self.key_pair.to_bytes() - } - - /// Decode a keypair from the format produced by `encode`. - #[allow(dead_code)] - pub fn decode(kp: &[u8]) -> Result { - let kp = ed25519_dalek::Keypair::from_bytes(kp)?; - Ok(Self { key_pair: kp }) + /// Sign a message using the private key of this keypair, producing + /// a signature that can be verified using the corresponding public key. + pub fn sign(&self, msg: &[u8]) -> Result { + use KeyPair::*; + match self { + Ed25519(ref pair) => Ok(Signature::Ed25519(ed25519::Signature(pair.sign(msg)?))), + #[cfg(not(target_arch = "wasm32"))] + Rsa(ref pair) => Ok(Signature::Rsa(rsa::Signature(pair.sign(msg)?))), + Secp256k1(ref pair) => Ok(Signature::Secp256k1(secp256k1::Signature(pair.secret().sign(msg)?))) + } } /// Get the public key of this keypair. - #[allow(dead_code)] - pub fn public_key(&self) -> PublicKey { - PublicKey(self.key_pair.public) - } - - /// Sign a message using the private key of this keypair. - pub fn sign(&self, msg: &[u8]) -> Signature { - Signature(self.key_pair.sign(msg)) - } - - /// Verify the Ed25519 signature on a message using the public key. - pub fn verify(pk: &PublicKey, msg: &[u8], signature: &Signature) -> Result<(), SignatureError> { - // let signature = ed25519_dalek::Signature::from_bytes(signature) - // .map_err(|err| format!("Cannot convert bytes to a signature: {:?}", err))?; - pk.verify_strict(msg, signature) - } -} - -impl From for KeyPair { - fn from(kp: Libp2pKeyPair) -> Self { - // TODO: this is a hack. Convert directly. Maybe use mem::transmute? - let kp = ed25519_dalek::Keypair::from_bytes(&kp.encode()).unwrap(); - Self { key_pair: kp } - } -} - -impl From for KeyPair { - fn from(kp: ed25519_dalek::Keypair) -> Self { - Self { key_pair: kp } - } -} - -impl From for ed25519_dalek::Keypair { - fn from(kp: KeyPair) -> Self { - kp.key_pair - } -} - -impl From for Libp2pKeyPair { - fn from(kp: KeyPair) -> Self { - Libp2pKeyPair::from(kp.key_pair) - } -} - -/// Implement serde::Deserialize for KeyPair -impl<'de> serde::Deserialize<'de> for KeyPair { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use serde::de::{Error, Unexpected, Visitor}; - - struct KeyPairVisitor; - - impl<'de> Visitor<'de> for KeyPairVisitor { - type Value = KeyPair; - - /// Error message stating what was expected - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("byte array or base58 string") - } - - /// Implement deserialization from base58 string - fn visit_str(self, s: &str) -> Result - where - E: Error, - { - bs58::decode(s) - .into_vec() - .map_err(|_| Error::invalid_value(Unexpected::Str(s), &self)) - .and_then(|v| self.visit_bytes(v.as_slice())) - } - - /// Implement deserialization from bytes - fn visit_bytes(self, b: &[u8]) -> Result - where - E: Error, - { - KeyPair::decode(b).map_err(|_| Error::invalid_value(Unexpected::Bytes(b), &self)) - } + pub fn public(&self) -> PublicKey { + use KeyPair::*; + match self { + Ed25519(pair) => PublicKey::Ed25519(pair.public()), + #[cfg(not(target_arch = "wasm32"))] + Rsa(pair) => PublicKey::Rsa(pair.public()), + Secp256k1(pair) => PublicKey::Secp256k1(pair.public().clone()), } - - deserializer.deserialize_str(KeyPairVisitor) } -} -impl Clone for KeyPair { - fn clone(&self) -> KeyPair { - let sk_bytes = self.key_pair.secret.to_bytes(); - let secret = ed25519_dalek::SecretKey::from_bytes(&sk_bytes) - .expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k"); - let public = ed25519_dalek::PublicKey::from_bytes(&self.key_pair.public.to_bytes()) - .expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k"); - KeyPair { - key_pair: ed25519_dalek::Keypair { secret, public }, + /// Verify the signature on a message using the public key. + pub fn verify(pk: &PublicKey, msg: &[u8], signature: &Signature) -> Result<(), SigningError> { + pk.verify(msg, signature) + } + + pub fn to_vec(&self) -> Vec { + use KeyPair::*; + match self { + Ed25519(kp) => kp.encode().to_vec(), + #[cfg(not(target_arch = "wasm32"))] + Rsa(_) => todo!("rsa encoding is not supported yet!"), + Secp256k1(kp) => kp.secret().to_bytes().to_vec(), + } + } + + pub fn from_vec(mut bytes: Vec, format: KeyFormat) -> Result { + use KeyPair::*; + + match format { + KeyFormat::Ed25519 => Ok(Ed25519(ed25519::Keypair::decode(&mut bytes)?)), + KeyFormat::Secp256k1 => Ok(Secp256k1(secp256k1::SecretKey::from_bytes(bytes)?.into())), + #[cfg(not(target_arch = "wasm32"))] + KeyFormat::Rsa => Err(DecodingError::KeypairDecodingIsNotSupported) + } + } +} + +impl From for KeyPair { + fn from(key: libp2p_core::identity::Keypair) -> Self { + use libp2p_core::identity::Keypair::*; + + match key { + Ed25519(kp) => KeyPair::Ed25519(ed25519::Keypair::decode(&mut kp.encode()).unwrap()), + #[cfg(not(target_arch = "wasm32"))] + // safety: these Keypair structures are identical + Rsa(kp) => KeyPair::Rsa(unsafe { std::mem::transmute::(kp) }), + Secp256k1(kp) => KeyPair::Secp256k1(secp256k1::Keypair::from(secp256k1::SecretKey::from_bytes(kp.secret().to_bytes()).unwrap())), + } + } +} + +impl From for libp2p_core::identity::Keypair { + fn from(key: KeyPair) -> Self { + use KeyPair::*; + use libp2p_core::identity::Keypair; + use libp2p_core::identity; + + match key { + Ed25519(kp) => Keypair::Ed25519(identity::ed25519::Keypair::decode(kp.encode().to_vec().as_mut_slice()).unwrap()), + #[cfg(not(target_arch = "wasm32"))] + Rsa(kp) => Keypair::Rsa(unsafe { std::mem::transmute::(kp) }), + Secp256k1(kp) => Keypair::Secp256k1(identity::secp256k1::Keypair::from(identity::secp256k1::SecretKey::from_bytes(kp.secret().to_bytes()).unwrap())), } } } diff --git a/identity/src/lib.rs b/identity/src/lib.rs index 4d63006..db7b509 100644 --- a/identity/src/lib.rs +++ b/identity/src/lib.rs @@ -26,14 +26,16 @@ unreachable_patterns )] +mod secp256k1; +mod ed25519; +#[cfg(not(target_arch = "wasm32"))] +mod rsa; pub mod key_pair; +pub mod error; pub mod public_key; -pub mod secret_key; pub mod signature; -pub use crate::key_pair::KeyPair; +pub use key_pair::KeyPair; +pub use key_pair::KeyFormat; pub use crate::public_key::PublicKey; -pub use crate::secret_key::SecretKey; pub use crate::signature::Signature; - -pub(crate) use libp2p_core::identity::ed25519; diff --git a/identity/src/public_key.rs b/identity/src/public_key.rs index 2f0991b..4264570 100644 --- a/identity/src/public_key.rs +++ b/identity/src/public_key.rs @@ -1,5 +1,5 @@ /* - * Copyright 2020 Fluence Labs Limited + * Copyright 2021 Fluence Labs Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,63 +13,125 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -use crate::public_key::PKError::{FromBase58Error, FromBytesError}; +use crate::ed25519; +#[cfg(not(target_arch = "wasm32"))] +use crate::rsa; +use crate::secp256k1; +use crate::error::{DecodingError, SigningError}; use crate::signature::Signature; -use core::fmt::Debug; -use ed25519_dalek::SignatureError; use serde::{Deserialize, Serialize}; -use thiserror::Error as ThisError; -use libp2p_core::identity; +use crate::key_pair::KeyFormat; +use std::convert::TryFrom; -#[derive(ThisError, Debug)] -pub enum PKError { - #[error("Cannot decode public key from bytes: {0}")] - FromBytesError(#[source] SignatureError), - #[error("Cannot decode public key from base58 format: {0}")] - FromBase58Error(#[source] bs58::decode::Error), -} - -#[derive(Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] -pub struct PublicKey(pub(crate) ed25519_dalek::PublicKey); - -impl Debug for PublicKey { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - write!(f, "{:?}", self.0) - } +/// The public key of a node's identity keypair. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PublicKey { + /// A public Ed25519 key. + Ed25519(ed25519::PublicKey), + #[cfg(not(target_arch = "wasm32"))] + /// A public RSA key. + Rsa(rsa::PublicKey), + /// A public Secp256k1 key. + Secp256k1(secp256k1::PublicKey), } impl PublicKey { - pub fn verify_strict( - &self, - message: &[u8], - signature: &Signature, - ) -> Result<(), SignatureError> { - self.0.verify_strict(message, &signature.0) + /// Verify a signature for a message using this public key, i.e. check + /// that the signature has been produced by the corresponding + /// private key (authenticity), and that the message has not been + /// tampered with (integrity). + pub fn verify(&self, msg: &[u8], sig: &Signature) -> Result<(), SigningError> { + use PublicKey::*; + match self { + Ed25519(pk) => pk.verify(msg, sig.to_vec()), + #[cfg(not(target_arch = "wasm32"))] + Rsa(pk) => pk.verify(msg, sig.to_vec()), + Secp256k1(pk) => pk.verify(msg, sig.to_vec()) + } } - pub fn from_base58(str: &str) -> Result { - let bytes = bs58::decode(str).into_vec().map_err(FromBase58Error)?; - Self::from_bytes(&bytes) + pub fn encode(&self) -> Vec { + use PublicKey::*; + let mut result: Vec = vec![self.get_prefix()]; + + match self { + Ed25519(pk) => result.extend(pk.encode().to_vec()), + #[cfg(not(target_arch = "wasm32"))] + Rsa(pk) => result.extend(pk.to_pkcs1()), + Secp256k1(pk) => result.extend(pk.encode().to_vec()), + }; + + result } - pub fn from_bytes(bytes: &[u8]) -> Result { - let pk = ed25519_dalek::PublicKey::from_bytes(bytes).map_err(FromBytesError)?; - Ok(PublicKey(pk)) + pub fn decode(bytes: &[u8]) -> Result { + match KeyFormat::try_from(bytes[0])? { + KeyFormat::Ed25519 => Ok(PublicKey::Ed25519(ed25519::PublicKey::decode(&bytes[1..])?)), + #[cfg(not(target_arch = "wasm32"))] + KeyFormat::Rsa => Ok(PublicKey::Rsa(rsa::PublicKey::from_pkcs1(bytes[1..].to_owned())?)), + KeyFormat::Secp256k1 => Ok(PublicKey::Secp256k1(secp256k1::PublicKey::decode(&bytes[1..])?)), + } } - pub fn to_bytes(&self) -> [u8; ed25519_dalek::PUBLIC_KEY_LENGTH] { - self.0.to_bytes() + fn get_prefix(&self) -> u8 { + use PublicKey::*; + match self { + Ed25519(_) => KeyFormat::Ed25519.into(), + #[cfg(not(target_arch = "wasm32"))] + Rsa(_) => KeyFormat::Rsa.into(), + Secp256k1(_) => KeyFormat::Secp256k1.into() + } } - pub fn from_libp2p(pk: &identity::ed25519::PublicKey) -> Result { - Self::from_bytes(&pk.encode()) + pub fn from_base58(str: &str) -> Result { + let bytes = bs58::decode(str).into_vec().map_err(DecodingError::Base58DecodeError)?; + Self::decode(&bytes) + } + + pub fn to_vec(&self) -> Vec { + use PublicKey::*; + + match self { + Ed25519(pk) => pk.encode().to_vec(), + #[cfg(not(target_arch = "wasm32"))] + Rsa(pk) => pk.to_pkcs1().to_vec(), + Secp256k1(pk) => pk.encode().to_vec(), + } } } -impl From for ed25519_dalek::PublicKey { - fn from(key: PublicKey) -> Self { - key.0 +impl From for PublicKey { + fn from(key: libp2p_core::identity::PublicKey) -> Self { + use libp2p_core::identity::PublicKey::*; + + match key { + Ed25519(key) => PublicKey::Ed25519(ed25519::PublicKey::decode(&key.encode()[..]).unwrap()), + #[cfg(not(target_arch = "wasm32"))] + Rsa(key) => PublicKey::Rsa(rsa::PublicKey::from_pkcs1(key.encode_pkcs1()).unwrap()), + Secp256k1(key) => PublicKey::Secp256k1(secp256k1::PublicKey::decode(&key.encode()[..]).unwrap()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::KeyPair; + + #[test] + fn public_key_encode_decode_ed25519() { + let kp = KeyPair::generate_ed25519(); + let pk = kp.public(); + let encoded_pk = pk.encode(); + assert_eq!(pk, PublicKey::decode(&encoded_pk).unwrap()); + } + + #[test] + fn public_key_encode_decode_secp256k1() { + let kp = KeyPair::generate_secp256k1(); + let pk = kp.public(); + let encoded_pk = pk.encode(); + assert_eq!(pk, PublicKey::decode(&encoded_pk).unwrap()); } } diff --git a/identity/src/rsa.rs b/identity/src/rsa.rs new file mode 100644 index 0000000..d5c2763 --- /dev/null +++ b/identity/src/rsa.rs @@ -0,0 +1,278 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! RSA keys. +use crate::error::{DecodingError, SigningError}; + +use asn1_der::{Asn1Der, FromDerObject, IntoDerObject, DerObject, DerTag, DerValue, Asn1DerError}; +use lazy_static::lazy_static; +use ring::rand::SystemRandom; +use ring::signature::{self, RsaKeyPair, RSA_PKCS1_SHA256, RSA_PKCS1_2048_8192_SHA256}; +use ring::signature::KeyPair; +use std::{fmt::{self, Write}, sync::Arc}; +use zeroize::Zeroize; +use serde::{Deserialize, Serialize}; + +/// An RSA keypair. +#[derive(Clone)] +pub struct Keypair(Arc); + +impl Keypair { + /// Decode an RSA keypair from a DER-encoded private key in PKCS#8 PrivateKeyInfo + /// format (i.e. unencrypted) as defined in [RFC5208]. + /// + /// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5 + pub fn from_pkcs8(der: &mut [u8]) -> Result { + let kp = RsaKeyPair::from_pkcs8(&der) + .map_err(|_| DecodingError::Rsa)?; + der.zeroize(); + Ok(Keypair(Arc::new(kp))) + } + + /// Get the public key from the keypair. + pub fn public(&self) -> PublicKey { + PublicKey(self.0.public_key().as_ref().to_vec()) + } + + /// Sign a message with this keypair. + pub fn sign(&self, data: &[u8]) -> Result, SigningError> { + let mut signature = vec![0; self.0.public_modulus_len()]; + let rng = SystemRandom::new(); + match self.0.sign(&RSA_PKCS1_SHA256, &rng, &data, &mut signature) { + Ok(()) => Ok(signature), + Err(_) => Err(SigningError::Rsa) + } + } +} + +/// An RSA public key. +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PublicKey(Vec); + +impl PublicKey { + /// Verify an RSA signature on a message using the public key. + pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), SigningError> { + let key = signature::UnparsedPublicKey::new(&RSA_PKCS1_2048_8192_SHA256, &self.0); + key.verify(msg, sig).map_err(|_| SigningError::Rsa) + } + + /// Encode the RSA public key in DER as a PKCS#1 RSAPublicKey structure, + /// as defined in [RFC3447]. + /// + /// [RFC3447]: https://tools.ietf.org/html/rfc3447#appendix-A.1.1 + pub fn to_pkcs1(&self) -> &[u8] { + // This is the encoding currently used in-memory, so it is trivial. + &self.0 + } + + pub fn from_pkcs1(pk: Vec) -> Result { + Ok(PublicKey(pk)) + } + + /// Encode the RSA public key in DER as a X.509 SubjectPublicKeyInfo structure, + /// as defined in [RFC5280]. + /// + /// [RFC5280]: https://tools.ietf.org/html/rfc5280#section-4.1 + pub fn encode_x509(&self) -> Vec { + let spki = Asn1SubjectPublicKeyInfo { + algorithmIdentifier: Asn1RsaEncryption { + algorithm: Asn1OidRsaEncryption(), + parameters: (), + }, + subjectPublicKey: Asn1SubjectPublicKey(self.clone()), + }; + let mut buf = vec![0u8; spki.serialized_len()]; + spki.serialize(buf.iter_mut()).map(|_| buf) + .expect("RSA X.509 public key encoding failed.") + } + + /// Decode an RSA public key from a DER-encoded X.509 SubjectPublicKeyInfo + /// structure. See also `encode_x509`. + pub fn decode_x509(pk: &[u8]) -> Result { + Asn1SubjectPublicKeyInfo::deserialize(pk.iter()) + .map_err(|_| DecodingError::Rsa) + .map(|spki| spki.subjectPublicKey.0) + } +} + +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let bytes = &self.0; + let mut hex = String::with_capacity(bytes.len() * 2); + + for byte in bytes { + write!(hex, "{:02x}", byte).expect("Can't fail on writing to string"); + } + + f.debug_struct("PublicKey") + .field("pkcs1", &hex) + .finish() + } +} + +////////////////////////////////////////////////////////////////////////////// +// DER encoding / decoding of public keys +// +// Primer: http://luca.ntop.org/Teaching/Appunti/asn1.html +// Playground: https://lapo.it/asn1js/ + +lazy_static! { + /// The DER encoding of the object identifier (OID) 'rsaEncryption' for + /// RSA public keys defined for X.509 in [RFC-3279] and used in + /// SubjectPublicKeyInfo structures defined in [RFC-5280]. + /// + /// [RFC-3279]: https://tools.ietf.org/html/rfc3279#section-2.3.1 + /// [RFC-5280]: https://tools.ietf.org/html/rfc5280#section-4.1 + static ref OID_RSA_ENCRYPTION_DER: DerObject = + DerObject { + tag: DerTag::x06, + value: DerValue { + data: vec![ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 ] + } + }; +} + +/// The ASN.1 OID for "rsaEncryption". +#[derive(Clone)] +struct Asn1OidRsaEncryption(); + +impl IntoDerObject for Asn1OidRsaEncryption { + fn into_der_object(self) -> DerObject { + OID_RSA_ENCRYPTION_DER.clone() + } + fn serialized_len(&self) -> usize { + OID_RSA_ENCRYPTION_DER.serialized_len() + } +} + +impl FromDerObject for Asn1OidRsaEncryption { + fn from_der_object(o: DerObject) -> Result { + if o.tag != DerTag::x06 { + return Err(Asn1DerError::InvalidTag); + } + if o.value != OID_RSA_ENCRYPTION_DER.value { + return Err(Asn1DerError::InvalidEncoding); + } + Ok(Asn1OidRsaEncryption()) + } +} + +/// The ASN.1 AlgorithmIdentifier for "rsaEncryption". +#[derive(Asn1Der)] +struct Asn1RsaEncryption { + algorithm: Asn1OidRsaEncryption, + parameters: (), +} + +/// The ASN.1 SubjectPublicKey inside a SubjectPublicKeyInfo, +/// i.e. encoded as a DER BIT STRING. +struct Asn1SubjectPublicKey(PublicKey); + +impl IntoDerObject for Asn1SubjectPublicKey { + fn into_der_object(self) -> DerObject { + let pk_der = (self.0).0; + let mut bit_string = Vec::with_capacity(pk_der.len() + 1); + // The number of bits in pk_der is trivially always a multiple of 8, + // so there are always 0 "unused bits" signaled by the first byte. + bit_string.push(0u8); + bit_string.extend(pk_der); + DerObject::new(DerTag::x03, bit_string.into()) + } + fn serialized_len(&self) -> usize { + DerObject::compute_serialized_len((self.0).0.len() + 1) + } +} + +impl FromDerObject for Asn1SubjectPublicKey { + fn from_der_object(o: DerObject) -> Result { + if o.tag != DerTag::x03 { + return Err(Asn1DerError::InvalidTag); + } + let pk_der: Vec = o.value.data.into_iter().skip(1).collect(); + // We don't parse pk_der further as an ASN.1 RsaPublicKey, since + // we only need the DER encoding for `verify`. + Ok(Asn1SubjectPublicKey(PublicKey(pk_der))) + } +} + +/// ASN.1 SubjectPublicKeyInfo +#[derive(Asn1Der)] +#[allow(non_snake_case)] +struct Asn1SubjectPublicKeyInfo { + algorithmIdentifier: Asn1RsaEncryption, + subjectPublicKey: Asn1SubjectPublicKey, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Signature(pub Vec); + +#[cfg(test)] +mod tests { + use super::*; + use quickcheck::*; + use rand::seq::SliceRandom; + use std::fmt; + + const KEY1: &'static [u8] = include_bytes!("test/rsa-2048.pk8"); + const KEY2: &'static [u8] = include_bytes!("test/rsa-3072.pk8"); + const KEY3: &'static [u8] = include_bytes!("test/rsa-4096.pk8"); + + #[derive(Clone)] + struct SomeKeypair(Keypair); + + impl fmt::Debug for SomeKeypair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SomeKeypair") + } + } + + impl Arbitrary for SomeKeypair { + fn arbitrary(g: &mut G) -> SomeKeypair { + let mut key = [KEY1, KEY2, KEY3].choose(g).unwrap().to_vec(); + SomeKeypair(Keypair::from_pkcs8(&mut key).unwrap()) + } + } + + #[test] + fn rsa_from_pkcs8() { + assert!(Keypair::from_pkcs8(&mut KEY1.to_vec()).is_ok()); + assert!(Keypair::from_pkcs8(&mut KEY2.to_vec()).is_ok()); + assert!(Keypair::from_pkcs8(&mut KEY3.to_vec()).is_ok()); + } + + #[test] + fn rsa_x509_encode_decode() { + fn prop(SomeKeypair(kp): SomeKeypair) -> Result { + let pk = kp.public(); + PublicKey::decode_x509(&pk.encode_x509()) + .map_err(|e| e.to_string()) + .map(|pk2| pk2 == pk) + } + QuickCheck::new().tests(10).quickcheck(prop as fn(_) -> _); + } + + #[test] + fn rsa_sign_verify() { + fn prop(SomeKeypair(kp): SomeKeypair, msg: Vec) -> Result { + kp.sign(&msg).map(|s| kp.public().verify(&msg, &s).is_ok()) + } + QuickCheck::new().tests(10).quickcheck(prop as fn(_, _) -> _); + } +} diff --git a/identity/src/secp256k1.rs b/identity/src/secp256k1.rs new file mode 100644 index 0000000..34c4539 --- /dev/null +++ b/identity/src/secp256k1.rs @@ -0,0 +1,230 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Secp256k1 keys. +use crate::error::{DecodingError, SigningError}; + +use asn1_der::{FromDerObject, DerObject}; +use rand::RngCore; +use sha2::{Digest as ShaDigestTrait, Sha256}; +use secp256k1::Message; +use zeroize::Zeroize; +use core::fmt; +use serde::{Deserialize, Serialize, Serializer, Deserializer}; +use serde::de::Error as SerdeError; +use serde_bytes::{Bytes as SerdeBytes, ByteBuf as SerdeByteBuf}; + +/// A Secp256k1 keypair. +#[derive(Clone)] +pub struct Keypair { + secret: SecretKey, + public: PublicKey, +} + +impl Keypair { + /// Generate a new sec256k1 `Keypair`. + pub fn generate() -> Self { + Keypair::from(SecretKey::generate()) + } + + /// Get the public key of this keypair. + pub fn public(&self) -> &PublicKey { + &self.public + } + + /// Get the secret key of this keypair. + pub fn secret(&self) -> &SecretKey { + &self.secret + } +} + +impl fmt::Debug for Keypair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Keypair").field("public", &self.public).finish() + } +} + +/// Promote a Secp256k1 secret key into a keypair. +impl From for Keypair { + fn from(secret: SecretKey) -> Self { + let public = PublicKey(secp256k1::PublicKey::from_secret_key(&secret.0)); + Keypair { secret, public } + } +} + +/// Demote a Secp256k1 keypair into a secret key. +impl From for SecretKey { + fn from(kp: Keypair) -> Self { + kp.secret + } +} + +/// A Secp256k1 secret key. +#[derive(Clone)] +pub struct SecretKey(secp256k1::SecretKey); + +impl fmt::Debug for SecretKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SecretKey") + } +} + +impl SecretKey { + /// Generate a new Secp256k1 secret key. + pub fn generate() -> Self { + let mut r = rand::thread_rng(); + let mut b = [0; secp256k1::util::SECRET_KEY_SIZE]; + // This is how it is done in `secp256k1::SecretKey::random` which + // we do not use here because it uses `rand::Rng` from rand-0.4. + loop { + r.fill_bytes(&mut b); + if let Ok(k) = secp256k1::SecretKey::parse(&b) { + return SecretKey(k); + } + } + } + + /// Create a secret key from a byte slice, zeroing the slice on success. + /// If the bytes do not constitute a valid Secp256k1 secret key, an + /// error is returned. + pub fn from_bytes(mut sk: impl AsMut<[u8]>) -> Result { + let sk_bytes = sk.as_mut(); + let secret = secp256k1::SecretKey::parse_slice(&*sk_bytes) + .map_err(|_| DecodingError::Secp256k1)?; + sk_bytes.zeroize(); + Ok(SecretKey(secret)) + } + + /// Decode a DER-encoded Secp256k1 secret key in an ECPrivateKey + /// structure as defined in [RFC5915]. + /// + /// [RFC5915]: https://tools.ietf.org/html/rfc5915 + pub fn from_der(mut der: impl AsMut<[u8]>) -> Result { + // TODO: Stricter parsing. + let der_obj = der.as_mut(); + let obj: Vec = FromDerObject::deserialize((&*der_obj).iter()) + .map_err(|_| DecodingError::Secp256k1)?; + der_obj.zeroize(); + let sk_obj = obj.into_iter().nth(1) + .ok_or(DecodingError::Secp256k1)?; + let mut sk_bytes: Vec = FromDerObject::from_der_object(sk_obj) + .map_err(|_| DecodingError::Secp256k1)?; + let sk = SecretKey::from_bytes(&mut sk_bytes)?; + sk_bytes.zeroize(); + Ok(sk) + } + + /// Sign a message with this secret key, producing a DER-encoded + /// ECDSA signature, as defined in [RFC3278]. + /// + /// [RFC3278]: https://tools.ietf.org/html/rfc3278#section-8.2 + pub fn sign(&self, msg: &[u8]) -> Result, SigningError> { + self.sign_hashed(Sha256::digest(msg).as_ref()) + } + + /// Returns the raw bytes of the secret key. + pub fn to_bytes(&self) -> [u8; 32] { + self.0.serialize() + } + + /// Sign a raw message of length 256 bits with this secret key, produces a DER-encoded + /// ECDSA signature. + pub fn sign_hashed(&self, msg: &[u8]) -> Result, SigningError> { + let m = Message::parse_slice(msg) + .map_err(SigningError::Secp256k1)?; + Ok(secp256k1::sign(&m, &self.0).0.serialize_der().as_ref().into()) + } +} + +/// A Secp256k1 public key. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct PublicKey(secp256k1::PublicKey); + +impl PublicKey { + /// Verify the Secp256k1 signature on a message using the public key. + pub fn verify(&self, msg: &[u8], sig: &[u8]) -> Result<(), SigningError> { + self.verify_hashed(Sha256::digest(msg).as_ref(), sig) + } + + /// Verify the Secp256k1 DER-encoded signature on a raw 256-bit message using the public key. + pub fn verify_hashed(&self, msg: &[u8], sig: &[u8]) -> Result<(), SigningError> { + Message::parse_slice(msg) + .and_then(|m| secp256k1::Signature::parse_der(sig).map(|s| secp256k1::verify(&m, &s, &self.0))) + .map_err(SigningError::Secp256k1).map(|_| ()) + } + + /// Encode the public key in compressed form, i.e. with one coordinate + /// represented by a single bit. + pub fn encode(&self) -> [u8; 33] { + self.0.serialize_compressed() + } + + /// Encode the public key in uncompressed form. + pub fn encode_uncompressed(&self) -> [u8; 65] { + self.0.serialize() + } + + /// Decode a public key from a byte slice in the the format produced + /// by `encode`. + pub fn decode(bytes: &[u8]) -> Result { + secp256k1::PublicKey::parse_slice(bytes, Some(secp256k1::PublicKeyFormat::Compressed)) + .map_err(|_| DecodingError::Secp256k1) + .map(PublicKey) + } +} + + +impl Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + SerdeBytes::new(self.encode().to_vec().as_slice()).serialize(serializer) + } +} + +impl<'d> Deserialize<'d> for PublicKey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'d>, + { + let bytes = ::deserialize(deserializer)?; + PublicKey::decode(bytes.as_slice()).map_err(SerdeError::custom) + } +} + + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Signature(pub Vec); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn secp256k1_secret_from_bytes() { + let sk1 = SecretKey::generate(); + let mut sk_bytes = [0; 32]; + sk_bytes.copy_from_slice(&sk1.0.serialize()[..]); + let sk2 = SecretKey::from_bytes(&mut sk_bytes).unwrap(); + assert_eq!(sk1.0.serialize(), sk2.0.serialize()); + assert_eq!(sk_bytes, [0; 32]); + } +} diff --git a/identity/src/secret_key.rs b/identity/src/secret_key.rs deleted file mode 100644 index adc9931..0000000 --- a/identity/src/secret_key.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * 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 ed25519_dalek::SignatureError; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize)] -pub struct SecretKey(ed25519_dalek::SecretKey); - -impl SecretKey { - pub fn from_bytes(bytes: &[u8]) -> Result { - let pk = ed25519_dalek::SecretKey::from_bytes(bytes)?; - - Ok(SecretKey(pk)) - } -} - -impl AsRef<[u8]> for SecretKey { - fn as_ref(&self) -> &[u8] { - self.0.as_bytes() - } -} diff --git a/identity/src/signature.rs b/identity/src/signature.rs index d479f6b..f8f1b09 100644 --- a/identity/src/signature.rs +++ b/identity/src/signature.rs @@ -13,40 +13,88 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +use crate::ed25519; +use crate::secp256k1; +#[cfg(not(target_arch = "wasm32"))] +use crate::rsa; +use crate::error::DecodingError; use serde::{Deserialize, Serialize}; -use signature::Error as SigError; -use signature::Signature as SigSignature; -use thiserror::Error as ThisError; +use crate::key_pair::KeyFormat; +use std::convert::TryFrom; -#[derive(ThisError, Debug)] -pub enum SignatureError { - #[error("{0}")] - Error( - #[from] - #[source] - SigError, - ), +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] +pub enum Signature { + Ed25519(ed25519::Signature), + #[cfg(not(target_arch = "wasm32"))] + Rsa(rsa::Signature), + Secp256k1(secp256k1::Signature), } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Signature(pub ed25519_dalek::Signature); - -pub const SIGNATURE_LENGTH: usize = 64; - impl Signature { - /// Create a new signature from a byte array - pub fn new(bytes: [u8; SIGNATURE_LENGTH]) -> Self { - Signature(ed25519_dalek::Signature::from(bytes)) + fn get_prefix(&self) -> u8 { + use Signature::*; + match self { + Ed25519(_) => KeyFormat::Ed25519.into(), + #[cfg(not(target_arch = "wasm32"))] + Rsa(_) => KeyFormat::Rsa.into(), + Secp256k1(_) => KeyFormat::Secp256k1.into() + } } - /// Return the inner byte array - pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] { - self.0.to_bytes() + /// encode keypair type in first byte and signature as byte array + pub fn encode(&self) -> Vec { + use Signature::*; + + let mut result: Vec = vec![self.get_prefix()]; + + match self { + Ed25519(sig) => result.extend(sig.0.clone()), + #[cfg(not(target_arch = "wasm32"))] + Rsa(sig) => result.extend(sig.0.clone()), + Secp256k1(sig) => result.extend(sig.0.clone()), + } + + result } - pub fn from_bytes(bytes: &[u8]) -> Result { - let sig = ed25519_dalek::Signature::from_bytes(bytes)?; - Ok(Signature(sig)) + /// decode with first byte set as keypair type + pub fn decode(bytes: Vec) -> Result { + match KeyFormat::try_from(bytes[0])? { + KeyFormat::Ed25519 => Ok(Signature::Ed25519(ed25519::Signature(bytes[1..].to_vec()))), + #[cfg(not(target_arch = "wasm32"))] + KeyFormat::Rsa => Ok(Signature::Rsa(rsa::Signature(bytes[1..].to_vec()))), + KeyFormat::Secp256k1 => Ok(Signature::Secp256k1(secp256k1::Signature(bytes[1..].to_vec()))), + + } + } + + pub fn to_vec(&self) -> &[u8] { + use Signature::*; + + match self { + Ed25519(sig) => &sig.0, + #[cfg(not(target_arch = "wasm32"))] + Rsa(sig) => &sig.0, + Secp256k1(sig) => &sig.0, + } + } +} + +#[cfg(test)] +mod tests { + use crate::*; + + #[test] + fn signature_encode_decode() { + let bytes: Vec = (0..10).collect(); + let ed25519_sig = Signature::Ed25519(crate::ed25519::Signature(bytes.clone())); + let secp256k1_sig = Signature::Secp256k1(crate::secp256k1::Signature(bytes.clone())); + #[cfg(not(target_arch = "wasm32"))] + let rsa_sig = Signature::Rsa(crate::rsa::Signature(bytes.clone())); + + assert_eq!(Signature::decode(ed25519_sig.encode()).unwrap(), ed25519_sig); + assert_eq!(Signature::decode(secp256k1_sig.encode()).unwrap(), secp256k1_sig); + #[cfg(not(target_arch = "wasm32"))] + assert_eq!(Signature::decode(rsa_sig.encode()).unwrap(), rsa_sig); } } diff --git a/identity/src/test/rsa-2048.pk8 b/identity/src/test/rsa-2048.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..75b837459fc3530ed231c7e9cf216f0cd8fec65e GIT binary patch literal 1218 zcmV;z1U>sOf&{(-0RS)!1_>&LNQUrs4#*Aqyhl|0)hbn0JO&pJ%_lS z*8}e-UCYWUSg7=))j~6DBL!OoIihsEU)KBrZ0`8 z5c|h!_W3^c^lP!fuQJXvXs-zgCKYpN_Oy{irqwPpMR_RBC@g+Hi)P34S@|D#k+gBl zG|F^doSXOK>E;FDZrZUm(hAcN4nn-|!v|qJRwBtP?a(A39)g`~2xA`nBn6@@(2H`R zwc6xJvlbffdT^o009Dm0RU$n z4^CajqT5wOD!FG6cb!2h;`KM6>vR@DR(>VmOh^599o_cwK203RsP~$jhzwGmTWBdX;&!z^}Z<(<1 z1wOZHT?Nkw&TII3!4+i$?M+E`vwd=o5)psawP{9vD*Q|9ITjS=KOZuB!?jIXDig1K z_>nW`!RkFIPHoZ-_oVTUkNskC;#$<@^Rm$(!VgkW{wYZX7r4thp#O z?Pj(+gwkvk`!B+`%X274Q3CCm-~(;gS;iaWXd)qR6Pse)I;iX#mN(5iz|8`IfdJrU zI`#!$&d&(>Z^Cxbuw{kb8+)^r;GM1u!>!E(w?^{kI14ET1IpZS$nxW{d zgchAY`}z5LvL>85ykp__<3M+{`tt&TfdI}tH?~JCri%=*_1y;7W9L& zWbx74OaUKj*p$+RR;}8&tr-rFW}{-Kz>8u{rc8?<3^ZDdI-tVOVtv?mT#V$WVd%t; zONZ_{+uod*W7yHv&!gXrVtZtkx3QGk2_~P~i>b?Lb_(L9x~ojTy%+H*pgW6;3A+M; zfdIL81qe-y&n*P84_hWmlm{`3%wv1j3L%e@>E=f3s_@T3^E=b5=0^PfsWkPhM> zwk$*e04>*bPc6@ZemMkU~2zLs&PGrZ;ZhiAocs{KgC_@xLHE;SL{)&c~{oD%^T4E{GyR}ONl&Yk1 gqi0utKEx48^+>UhDmy`Ngdrqm7vQl}I68@Y)poi>`~Uy| literal 0 HcmV?d00001 diff --git a/identity/src/test/rsa-3072.pk8 b/identity/src/test/rsa-3072.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..a0bd175f7f6857b6c2d81622afee110622bf9aa7 GIT binary patch literal 1792 zcmV+b2mkmmf(HBo0RS)!1_>&LNQUt<}iW=;sOBx0)hd70LEDhq}gB0 z<5g3ltb*FROnfi{FuKCC9531t-+0ku!B#1J{^YMC!94Uy(B_n%+an%xNx>Ozz}NLu z$*PU}ny2?|LC-7krMPw7szoPP=CfxDY2jsEofs;jn&NbNj+^N}0Xt8YGGCOJG>_f4 zm*Nk5m{~(4oAcd~kGPNTGf)a&u-2C?&_q!LvHwJ9IKL zx-l4Fz5q()N6ms?uL1)B009Dm0f1Jp?5XT%Ho(=kY)?WGAM)A+k|1V4TF_$&$nFPx zT|rMvYT9j4FDr6f!#0l5KHVgUR+CZIuho%`B(%@krm=nCV%&6;998x`ZP?Ms)Lwkz z_|_URm#pQt9KzjSQ-P@)RPs>X1S6O#^}By{#GIL}a6vjK+==4s?g;fl~ z<=1tyi4?*=HMn{fQGW8Tkn>>DuVrh#2cmb>&-xc)*Y`fzSTcYTtG55`lV@kD8-B`2 z#NgVzU4|NF-jL1rfJj=-y;C%>fbZlkt1l{1P+~A6}Tr~QUL;i!2t9WJgy33a+qr|c>sJeUInZ+NA2-SW8D``iXx8mN-r`1zL z*pc;wP?u;K;s8hUrS_5)$#C~_m>DmgYyI~J*(jaEZy&H>Os?;r=?$;7pUVO>n=|Cw z8rqsmM_YX@*_H%@MjkMVB~FzVi#~^!f|92iwVCIwWywYJbpU+tPIBt~?@U1g$MKE5 z&U;|e$^_g&2%Op21opj9%^O{`SCY(AWTE>$aj42JDeeE@J5NV4?hN%Cf~}{8uw>QnDee0Dq-}fG0@U7BQBZW@I;$;}>^D)038Dt{r`$wpee{ zC&K{YAU?(0C)s%^rw}ml^TVa|EfHg^Z2_~U+F^zj`Wf4q-5qU6Ocp*qG3VBbk=MLy z*+8C`fdYZReBM0^p3o2eDQQ6sHV-Q!*}+j^EG?vUJjkkm@PP@+26RdooN(8x5C~8UQ6>?@Skp7f_e-&{Pl(ce0l?HoOiH{hzKsFY^1#2cZ#VtW; zJ_YNf^yg=dO#of-Xasz~CMIZ8#&NbsdLFj10Y1MaoBE)QBW|zhGD%Cb`hfnsb1nRQ z*>e0Q{O4wfDMFC?wJpfKu;2Ox21D0hSzA%thm|A93|dhbTmJtq-&630y8van6&IjH1^uly)28TJTLhu~W&w8hnoc literal 0 HcmV?d00001 diff --git a/identity/src/test/rsa-4096.pk8 b/identity/src/test/rsa-4096.pk8 new file mode 100644 index 0000000000000000000000000000000000000000..252dda818a4b3b49fc91013e5e4312c110453bbe GIT binary patch literal 2376 zcmV-O3Agqzf(b+d0RS)!1_>&LNQUwE--=#Dgprj0)heo0Gx?`GvLs( zDSt2I1nOIQmf)=7jeh z-8w%=YD}`jkQq+q>9${=9shfeYzy?@#H?sL)VeJ%&?5b+quX`q4i;uF#IvlV$*N^h zO;KqK({jSOY+<48Pm-AIAw5%=s*xZ!ly?z>kq}MmcdHe8S+@ri>0GLtRncFghOo#F zHsuX-i`@|5x}(GD`$q%b!`7!)U>_J%9=Lkx5g%ac;eCWW@dSOpT8VnP4|%vheK*#Z=v ztp;mB|XFtQI<+#2(vFYX$>lp?@XGE$*lXOw(Fg1jQ6(zydSy)Eg}|(YI0qEg33rejTz!} zx=kZ=Y&t6% zzPpSbh}-qI>vOi5@YgqRe_?cOU^#zSid!H2ZklRiY zm;UMjZm~hmbnPAzQ8U{itg!K~h^Ze6gUTFKA7elr~pq)7W9T5W2>t;DHO=0YO z84O=#cE#DK%+eW(If~~~Gyb&`B>%<>ILj`O*rI7md;m!Fa2C52%QnP<;5g(}B~CT` zPYw&K$W8=n)IqQ*6cWdTz|_aXN~fOo>&RAL zo)67=J~6kTnJ_gXVZb{*z_w~)_^vl`w*@#X{-xT6 z=NxIWxFVq>;x(-W^0i#ro!ofVaYJc=qXtTV!ke>xYgK`%H!5Rh$C~z)qn@DigPzxZ z!DpRe73)Gv`YtJi6dbGf&l>l!Gz5x)22h; zc86rA#%<&bA}iZVegeZsic;vtKge+*&oUNKV1FB^+`gXZ2nO`^_xyV0WVHo|EC)$LzfJ)uV}brceyO742%yQWDPm#LvTZ8F-$~ndQ^oJ8Rid8 zlHul?)h_2LPoCYz+=nqlu+Z7@KDgGZm0a;2+tr|FJ&SPflYUhFs@@o1tjI%jmee{^ z7L!6K2N z%EPvpuxd>c0vMEWG5w!u=18j z9hj0PPixecWm?y-sUD)9;H(Nf{5$M60AY-fs0V0R-8KdRaJ{1@B2Xu19RV;^frD#{sf7R15nVZpDz3_5eVNnIo+l8 z$CeJ*?8uzSK>;!PHbGXguFAQ&l1KxG+&5X(GrlD@ZG+v)FHQ|0Pl^t0D$O2^t1-6JeA%-$m38uZq_3bCCNW{Wzv#q$eFwK(n!ypLHVxf zxyvg0!E(A6!rum&#$ZcK=c|>;9AAIOfKMylCW?L)#S)Sab0yEZM@BuN!U0sD#7ry1 z`{meSoZgo+M+{y#xt8_AV?1LKuZU(mmR2Gb&JR$;a_}H?e^5h_-LlJu)*e)yn(j> literal 0 HcmV?d00001 diff --git a/src/certificate.rs b/src/certificate.rs index a94697b..e3cc8e0 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -15,11 +15,11 @@ */ use crate::certificate::CertificateError::{ - CertificateLengthError, DecodeError, DecodeTrustError, ExpirationError, IncorrectByteLength, + CertificateLengthError, DecodeError, DecodeTrustError, ExpirationError, IncorrectCertificateFormat, KeyInCertificateError, MalformedRoot, NoTrustedRoot, VerificationError, }; -use crate::trust::{Trust, TrustError, TRUST_LEN}; +use crate::trust::{Trust, TrustError}; use fluence_identity::key_pair::KeyPair; use fluence_identity::public_key::PublicKey; use std::str::FromStr; @@ -32,6 +32,7 @@ const FORMAT: &[u8; 2] = &[0, 0]; /// Serialization format version of a certificate. /// TODO const VERSION: &[u8; 4] = &[0, 0, 0, 0]; +const TRUST_NUMBER_LEN: usize = 1; /// Chain of trusts started from self-signed root trust. #[derive(Debug, Clone, PartialEq, Eq)] @@ -83,7 +84,7 @@ impl Certificate { ) -> Self { let root_expiration = Duration::from_secs(u64::max_value()); - let root_trust = Trust::create(root_kp, root_kp.public_key(), root_expiration, issued_at); + let root_trust = Trust::create(root_kp, root_kp.public(), root_expiration, issued_at); let trust = Trust::create(root_kp, for_pk, expires_at, issued_at); @@ -109,9 +110,9 @@ impl Certificate { } // first, verify given certificate - Certificate::verify(extend_cert, &[extend_cert.chain[0].issued_for], cur_time)?; + Certificate::verify(extend_cert, &[extend_cert.chain[0].issued_for.clone()], cur_time)?; - let issued_by_pk = issued_by.public_key(); + let issued_by_pk = issued_by.public(); // check if `issued_by_pk` is allowed to issue a certificate (i.e., there’s a trust for it in a chain) let mut previous_trust_num: i32 = -1; @@ -172,46 +173,60 @@ impl Certificate { } /// Convert certificate to byte format - /// 2 format + 4 version + (64 signature + 32 public key + 8 expiration) * number of trusts + /// 2 format + 4 version + 1 trusts number + ((1 trust size byte + trust) for each trust) #[allow(dead_code)] pub fn encode(&self) -> Vec { - let mut encoded = - Vec::with_capacity(FORMAT.len() + VERSION.len() + TRUST_LEN * self.chain.len()); + let mut encoded = Vec::new(); encoded.extend_from_slice(FORMAT); encoded.extend_from_slice(VERSION); + encoded.push(self.chain.len() as u8); for t in &self.chain { - encoded.extend(t.encode()); + let trust = t.encode(); + encoded.push(trust.len() as u8); + encoded.extend(trust); } encoded } + fn check_arr_len(arr: &[u8], check_len: usize) -> Result<(), CertificateError> { + if arr.len() < check_len { + Err(CertificateLengthError) + } else { + Ok(()) + } + } + #[allow(dead_code)] pub fn decode(arr: &[u8]) -> Result { - let trusts_offset = arr.len() - 2 - 4; - if trusts_offset % TRUST_LEN != 0 { - return Err(IncorrectByteLength); - } + // TODO do match different formats and versions + Self::check_arr_len(arr, FORMAT.len() + VERSION.len() + TRUST_NUMBER_LEN)?; + let mut offset = 0; + let _format = &arr[offset..offset + FORMAT.len()]; + offset += FORMAT.len(); - let number_of_trusts = trusts_offset / TRUST_LEN; + let _version = &arr[offset..offset + VERSION.len()]; + offset += VERSION.len(); + + let number_of_trusts = arr[offset] as usize; + offset += TRUST_NUMBER_LEN; if number_of_trusts < 2 { return Err(CertificateLengthError); } - - // TODO do match different formats and versions - let _format = &arr[0..1]; - let _version = &arr[2..5]; - let mut chain = Vec::with_capacity(number_of_trusts); - for i in 0..number_of_trusts { - let from = i * TRUST_LEN + 6; - let to = (i + 1) * TRUST_LEN + 6; + for _ in 0..number_of_trusts { + Self::check_arr_len(arr, offset + 1)?; + let trust_len = arr[offset] as usize; + let from = offset + 1; + let to = from + trust_len; + Self::check_arr_len(arr, to)?; let slice = &arr[from..to]; let t = Trust::decode(slice).map_err(DecodeError)?; chain.push(t); + offset += 1 + trust_len; } Ok(Self { chain }) @@ -253,7 +268,7 @@ impl FromStr for Certificate { str_lines[i + 2], str_lines[i + 3], ) - .map_err(|e| DecodeTrustError(i, e))?; + .map_err(|e| DecodeTrustError(i, e))?; trusts.push(trust); } @@ -282,12 +297,12 @@ mod tests { } #[test] - pub fn test_string_encoding_decoding() { + pub fn test_string_encoding_decoding_ed25519() { let (_root_kp, second_kp, cert) = generate_root_cert(); let cur_time = current_time(); - let third_kp = KeyPair::generate(); + let third_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, @@ -297,7 +312,7 @@ mod tests { cur_time, cur_time, ) - .unwrap(); + .unwrap(); let serialized = new_cert.to_string(); let deserialized = Certificate::from_str(&serialized); @@ -309,12 +324,12 @@ mod tests { } #[test] - pub fn test_serialization_deserialization() { + pub fn test_serialization_deserialization_ed25519() { let (_root_kp, second_kp, cert) = generate_root_cert(); let cur_time = current_time(); - let third_kp = KeyPair::generate(); + let third_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, @@ -324,7 +339,7 @@ mod tests { cur_time, cur_time, ) - .unwrap(); + .unwrap(); let serialized = new_cert.encode(); let deserialized = Certificate::decode(serialized.as_slice()); @@ -336,7 +351,7 @@ mod tests { } #[test] - fn test_small_chain() { + fn test_small_chain_ed25519() { let bad_cert = Certificate { chain: Vec::new() }; let check = Certificate::verify(&bad_cert, &[], current_time()); @@ -344,8 +359,8 @@ mod tests { } fn generate_root_cert() -> (KeyPair, KeyPair, Certificate) { - let root_kp = KeyPair::generate(); - let second_kp = KeyPair::generate(); + let root_kp = KeyPair::generate_ed25519(); + let second_kp = KeyPair::generate_ed25519(); let cur_time = current_time(); @@ -354,7 +369,7 @@ mod tests { second_kp.clone(), Certificate::issue_root( &root_kp, - second_kp.public_key(), + second_kp.public(), cur_time.checked_add(one_year()).unwrap(), cur_time, ), @@ -362,9 +377,9 @@ mod tests { } #[test] - fn test_issue_cert() { + fn test_issue_cert_ed25519() { let (root_kp, second_kp, cert) = generate_root_cert(); - let trusted_roots = [root_kp.public_key()]; + let trusted_roots = [root_kp.public()]; // we don't need nanos for serialization, etc let cur_time = Duration::from_secs( @@ -374,7 +389,7 @@ mod tests { .as_secs() as u64, ); - let third_kp = KeyPair::generate(); + let third_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, @@ -386,38 +401,23 @@ mod tests { ); assert_eq!(new_cert.is_ok(), true); let new_cert = new_cert.unwrap(); - - println!( - "root_kp:\n\tprivate: {}\n\tpublic: {}", - bs58::encode(root_kp.clone().secret()).into_string(), - bs58::encode(&root_kp.public().to_bytes().to_vec()).into_string() - ); - println!( - "second_kp:\n\tprivate: {}\n\tpublic: {}", - bs58::encode(second_kp.clone().secret()).into_string(), - bs58::encode(&second_kp.public().to_bytes().to_vec()).into_string() - ); - println!( - "third_kp:\n\tprivate: {}\n\tpublic: {}", - bs58::encode(third_kp.clone().secret()).into_string(), - bs58::encode(&third_kp.public().to_bytes().to_vec()).into_string() - ); + println!("cert is\n{}", new_cert.to_string()); assert_eq!(new_cert.chain.len(), 3); - assert_eq!(new_cert.chain[0].issued_for, root_kp.public_key()); - assert_eq!(new_cert.chain[1].issued_for, second_kp.public_key()); - assert_eq!(new_cert.chain[2].issued_for, third_kp.public_key()); + assert_eq!(new_cert.chain[0].issued_for, root_kp.public()); + assert_eq!(new_cert.chain[1].issued_for, second_kp.public()); + assert_eq!(new_cert.chain[2].issued_for, third_kp.public()); assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_ok()); } #[test] - fn test_cert_expiration() { + fn test_cert_expiration_ed25519() { let (root_kp, second_kp, cert) = generate_root_cert(); - let trusted_roots = [root_kp.public_key()]; + let trusted_roots = [root_kp.public()]; let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let third_kp = KeyPair::generate(); + let third_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, @@ -427,19 +427,19 @@ mod tests { cur_time.checked_sub(one_minute()).unwrap(), cur_time, ) - .unwrap(); + .unwrap(); assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_err()); } #[test] - fn test_issue_in_chain_tail() { + fn test_issue_in_chain_tail_ed25519() { let (root_kp, second_kp, cert) = generate_root_cert(); - let trusted_roots = [root_kp.public_key()]; + let trusted_roots = [root_kp.public()]; let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let third_kp = KeyPair::generate(); - let fourth_kp = KeyPair::generate(); + let third_kp = KeyPair::generate_ed25519(); + let fourth_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, @@ -449,7 +449,7 @@ mod tests { cur_time, cur_time, ) - .unwrap(); + .unwrap(); let new_cert = Certificate::issue( &third_kp, fourth_kp.public(), @@ -463,21 +463,21 @@ mod tests { let new_cert = new_cert.unwrap(); assert_eq!(new_cert.chain.len(), 4); - assert_eq!(new_cert.chain[0].issued_for, root_kp.public_key()); - assert_eq!(new_cert.chain[1].issued_for, second_kp.public_key()); - assert_eq!(new_cert.chain[2].issued_for, third_kp.public_key()); - assert_eq!(new_cert.chain[3].issued_for, fourth_kp.public_key()); + assert_eq!(new_cert.chain[0].issued_for, root_kp.public()); + assert_eq!(new_cert.chain[1].issued_for, second_kp.public()); + assert_eq!(new_cert.chain[2].issued_for, third_kp.public()); + assert_eq!(new_cert.chain[3].issued_for, fourth_kp.public()); assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_ok()); } #[test] - fn test_issue_in_chain_body() { + fn test_issue_in_chain_body_ed25519() { let (root_kp, second_kp, cert) = generate_root_cert(); - let trusted_roots = [root_kp.public_key()]; + let trusted_roots = [root_kp.public()]; let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let third_kp = KeyPair::generate(); - let fourth_kp = KeyPair::generate(); + let third_kp = KeyPair::generate_ed25519(); + let fourth_kp = KeyPair::generate_ed25519(); let new_cert = Certificate::issue( &second_kp, @@ -487,7 +487,7 @@ mod tests { cur_time, cur_time, ) - .unwrap(); + .unwrap(); let new_cert = Certificate::issue( &second_kp, fourth_kp.public(), @@ -501,18 +501,18 @@ mod tests { let new_cert = new_cert.unwrap(); assert_eq!(new_cert.chain.len(), 3); - assert_eq!(new_cert.chain[0].issued_for, root_kp.public_key()); - assert_eq!(new_cert.chain[1].issued_for, second_kp.public_key()); - assert_eq!(new_cert.chain[2].issued_for, fourth_kp.public_key()); + assert_eq!(new_cert.chain[0].issued_for, root_kp.public()); + assert_eq!(new_cert.chain[1].issued_for, second_kp.public()); + assert_eq!(new_cert.chain[2].issued_for, fourth_kp.public()); assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_ok()); } #[test] - fn test_no_cert_in_chain() { + fn test_no_cert_in_chain_ed25519() { let (_root_kp, _second_kp, cert) = generate_root_cert(); let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let bad_kp = KeyPair::generate(); + let bad_kp = KeyPair::generate_ed25519(); let new_cert_bad = Certificate::issue( &bad_kp, bad_kp.public(), @@ -529,7 +529,7 @@ mod tests { let (_root_kp, second_kp, cert) = generate_root_cert(); let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let trusted_roots = [second_kp.public_key()]; + let trusted_roots = [second_kp.public()]; assert!(Certificate::verify(&cert, &trusted_roots, cur_time).is_err()); assert!(Certificate::verify(&cert, &[], cur_time).is_err()); } @@ -538,7 +538,7 @@ mod tests { fn test_forged_cert() { let (root_kp, _second_kp, cert) = generate_root_cert(); let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let trusted_roots = [root_kp.public_key()]; + let trusted_roots = [root_kp.public()]; // forged cert let mut bad_chain = cert.chain; @@ -553,11 +553,11 @@ mod tests { let (root_kp, second_kp, cert) = generate_root_cert(); let cur_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); - let trusted_roots = [root_kp.public_key()]; + let trusted_roots = [root_kp.public()]; assert_eq!(cert.chain.len(), 2); - assert_eq!(cert.chain[0].issued_for, root_kp.public_key()); - assert_eq!(cert.chain[1].issued_for, second_kp.public_key()); + assert_eq!(cert.chain[0].issued_for, root_kp.public()); + assert_eq!(cert.chain[1].issued_for, second_kp.public()); assert!(Certificate::verify(&cert, &trusted_roots, cur_time).is_ok()); } } diff --git a/src/public_key_hashable.rs b/src/public_key_hashable.rs index 07a125d..92abee7 100644 --- a/src/public_key_hashable.rs +++ b/src/public_key_hashable.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use fluence_identity::public_key::{PKError, PublicKey}; +use fluence_identity::public_key::PublicKey; use core::fmt; use ref_cast::RefCast; @@ -33,7 +33,7 @@ pub struct PublicKeyHashable(PublicKey); #[allow(clippy::derive_hash_xor_eq)] impl Hash for PublicKeyHashable { fn hash(&self, state: &mut H) { - state.write(&self.0.to_bytes()); + state.write(&self.0.encode()); state.finish(); } @@ -44,7 +44,7 @@ impl Hash for PublicKeyHashable { // TODO check for overflow let mut bytes: Vec = Vec::with_capacity(data.len() * 32); for d in data { - bytes.extend_from_slice(&d.0.to_bytes()) + bytes.extend_from_slice(&d.0.encode()) } state.write(bytes.as_slice()); state.finish(); @@ -77,12 +77,12 @@ impl AsRef for PublicKey { impl Display for PublicKeyHashable { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", bs58::encode(self.0.to_bytes()).into_string()) + write!(f, "{}", bs58::encode(self.0.encode()).into_string()) } } impl FromStr for PublicKeyHashable { - type Err = PKError; + type Err = fluence_identity::error::DecodingError; fn from_str(s: &str) -> Result { let pk = PublicKey::from_base58(s)?; @@ -95,7 +95,7 @@ impl serde::Serialize for PublicKeyHashable { where S: Serializer, { - serializer.serialize_bytes(&self.0.to_bytes()) + serializer.serialize_bytes(&self.0.encode()) } } @@ -132,7 +132,7 @@ impl<'de> serde::Deserialize<'de> for PublicKeyHashable { where E: Error, { - let pk = PublicKey::from_bytes(b) + let pk = PublicKey::decode(b) .map_err(|err| Error::custom(format!("Invalid bytes {:?}: {}", b, err)))?; Ok(PublicKeyHashable::from(pk)) } diff --git a/src/revoke.rs b/src/revoke.rs index b40c82f..7b00929 100644 --- a/src/revoke.rs +++ b/src/revoke.rs @@ -15,8 +15,6 @@ */ use crate::revoke::RevokeError::IncorrectSignature; -use crate::trust::{EXPIRATION_LEN, PK_LEN}; -use ed25519_dalek::SignatureError; use fluence_identity::key_pair::KeyPair; use fluence_identity::public_key::PublicKey; use fluence_identity::signature::Signature; @@ -27,12 +25,16 @@ use thiserror::Error as ThisError; #[derive(ThisError, Debug)] pub enum RevokeError { #[error("Signature is incorrect: {0}")] - IncorrectSignature(#[source] SignatureError), + IncorrectSignature( + #[from] + #[source] + fluence_identity::error::SigningError + ), } /// "A document" that cancels trust created before. /// TODO delete pk from Revoke (it is already in a trust node) -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Revoke { /// who is revoked pub pk: PublicKey, @@ -64,14 +66,16 @@ impl Revoke { #[allow(dead_code)] pub fn create(revoker: &KeyPair, to_revoke: PublicKey, revoked_at: Duration) -> Self { let msg = Revoke::signature_bytes(&to_revoke, revoked_at); - let signature = revoker.sign(&msg); + let signature = revoker.sign(&msg).unwrap(); - Revoke::new(to_revoke, revoker.public_key(), revoked_at, signature) + Revoke::new(to_revoke, revoker.public(), revoked_at, signature) } fn signature_bytes(pk: &PublicKey, revoked_at: Duration) -> Vec { - let mut msg = Vec::with_capacity(PK_LEN + EXPIRATION_LEN); - msg.extend_from_slice(&pk.to_bytes()); + let mut msg = Vec::new(); + let pk_bytes = &pk.encode(); + msg.push(pk_bytes.len() as u8); + msg.extend(pk_bytes); msg.extend_from_slice(&(revoked_at.as_secs() as u64).to_le_bytes()); msg @@ -83,41 +87,39 @@ impl Revoke { revoke .revoked_by - .verify_strict(msg.as_slice(), &revoke.signature) - .map_err(IncorrectSignature) + .verify(msg.as_slice(), &revoke.signature).map_err(IncorrectSignature) } } #[cfg(test)] mod tests { - use super::*; #[test] - fn test_gen_revoke_and_validate() { - let revoker = KeyPair::generate(); - let to_revoke = KeyPair::generate(); + fn test_gen_revoke_and_validate_ed25519() { + let revoker = KeyPair::generate_ed25519(); + let to_revoke = KeyPair::generate_ed25519(); let duration = Duration::new(100, 0); - let revoke = Revoke::create(&revoker, to_revoke.public_key(), duration); + let revoke = Revoke::create(&revoker, to_revoke.public(), duration); assert_eq!(Revoke::verify(&revoke).is_ok(), true); } #[test] - fn test_validate_corrupted_revoke() { - let revoker = KeyPair::generate(); - let to_revoke = KeyPair::generate(); + fn test_validate_corrupted_revoke_ed25519() { + let revoker = KeyPair::generate_ed25519(); + let to_revoke = KeyPair::generate_ed25519(); let duration = Duration::new(100, 0); - let revoke = Revoke::create(&revoker, to_revoke.public_key(), duration); + let revoke = Revoke::create(&revoker, to_revoke.public(), duration); let duration2 = Duration::new(95, 0); let corrupted_revoke = Revoke::new( - to_revoke.public_key(), - revoker.public_key(), + to_revoke.public(), + revoker.public(), duration2, revoke.signature, ); diff --git a/src/trust.rs b/src/trust.rs index 8d65c5f..f8c9ab7 100644 --- a/src/trust.rs +++ b/src/trust.rs @@ -14,29 +14,23 @@ * limitations under the License. */ -use crate::trust::TrustError::{ - Base58DecodeError, DecodePublicKeyError, IncorrectTrustLength, ParseError, SignatureError, -}; +use crate::trust::TrustError::{Base58DecodeError, DecodePublicKeyError, ParseError, SignatureError, DecodeErrorInvalidSize}; use derivative::Derivative; use fluence_identity::key_pair::KeyPair; -use fluence_identity::public_key::{PKError, PublicKey}; -use fluence_identity::signature::{Signature, SignatureError as SigError}; -use serde::{Deserialize, Serialize}; +use fluence_identity::public_key::PublicKey; +use fluence_identity::signature::Signature; use std::convert::TryInto; use std::num::ParseIntError; use std::time::Duration; use thiserror::Error as ThisError; +use serde::{Deserialize, Serialize}; -pub const SIG_LEN: usize = 64; -pub const PK_LEN: usize = 32; pub const EXPIRATION_LEN: usize = 8; pub const ISSUED_LEN: usize = 8; -pub const METADATA_LEN: usize = PK_LEN + EXPIRATION_LEN + ISSUED_LEN; -pub const TRUST_LEN: usize = SIG_LEN + PK_LEN + EXPIRATION_LEN + ISSUED_LEN; /// One element in chain of trust in a certificate. /// TODO delete pk from Trust (it is already in a trust node) -#[derive(Clone, PartialEq, Derivative, Eq, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Derivative, Eq, Deserialize, Serialize)] #[derivative(Debug)] pub struct Trust { /// For whom this certificate is issued @@ -53,11 +47,11 @@ pub struct Trust { } fn show_pubkey(key: &PublicKey, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - write!(f, "{}", bs58::encode(&key.to_bytes()).into_string()) + write!(f, "{}", bs58::encode(&key.encode()).into_string()) } fn show_sig(sig: &Signature, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - write!(f, "{}", bs58::encode(&sig.to_bytes()).into_string()) + write!(f, "{}", bs58::encode(&sig.encode()).into_string()) } #[derive(ThisError, Debug)] @@ -66,17 +60,17 @@ pub enum TrustError { #[error("Trust is expired at: '{0:?}', current time: '{1:?}'")] Expired(Duration, Duration), - /// Errors occured on signature verification + /// Errors occurred on signature verification #[error("{0}")] SignatureError( #[from] #[source] - ed25519_dalek::SignatureError, + fluence_identity::error::SigningError, ), - /// Errors occured on trust decoding from differrent formats + /// Errors occurred on trust decoding from different formats #[error("Cannot decode the public key: {0} in the trust: {1}")] - DecodePublicKeyError(String, #[source] PKError), + DecodePublicKeyError(String, #[source] fluence_identity::error::DecodingError), #[error("Cannot parse `{0}` field in the trust '{1}': {2}")] ParseError(String, String, #[source] ParseIntError), @@ -84,20 +78,15 @@ pub enum TrustError { #[error("Cannot decode `{0}` from base58 format in the trust '{1}': {2}")] Base58DecodeError(String, String, #[source] bs58::decode::Error), - #[error("Cannot decode a signature from bytes: {0}")] - SignatureFromBytesError(#[from] SigError), - #[error("{0}")] PublicKeyError( #[from] #[source] - PKError, + fluence_identity::error::DecodingError, ), - #[error( - "Trust length should be 104: public key(32) + signature(64) + expiration date(8), was: {0}" - )] - IncorrectTrustLength(usize), + #[error("Cannot decode `{0}` field in the trust: invalid size")] + DecodeErrorInvalidSize(String), } impl Trust { @@ -124,7 +113,7 @@ impl Trust { ) -> Self { let msg = Self::metadata_bytes(&issued_for, expires_at, issued_at); - let signature = issued_by.sign(&msg); + let signature = issued_by.sign(msg.as_slice()).unwrap(); Self { issued_for, @@ -147,22 +136,18 @@ impl Trust { let msg: &[u8] = &Self::metadata_bytes(&trust.issued_for, trust.expires_at, trust.issued_at); - KeyPair::verify(issued_by, msg, &trust.signature).map_err(SignatureError)?; - - Ok(()) + KeyPair::verify(issued_by, msg, &trust.signature).map_err(SignatureError) } - fn metadata_bytes(pk: &PublicKey, expires_at: Duration, issued_at: Duration) -> [u8; 48] { - let pk_encoded = pk.to_bytes(); + fn metadata_bytes(pk: &PublicKey, expires_at: Duration, issued_at: Duration) -> Vec { + let pk_encoded = pk.encode(); let expires_at_encoded: [u8; EXPIRATION_LEN] = (expires_at.as_secs() as u64).to_le_bytes(); let issued_at_encoded: [u8; ISSUED_LEN] = (issued_at.as_secs() as u64).to_le_bytes(); - let mut metadata = [0; METADATA_LEN]; + let mut metadata = Vec::new(); - metadata[..PK_LEN].clone_from_slice(&pk_encoded[..PK_LEN]); - metadata[PK_LEN..PK_LEN + EXPIRATION_LEN] - .clone_from_slice(&expires_at_encoded[0..EXPIRATION_LEN]); - metadata[PK_LEN + EXPIRATION_LEN..METADATA_LEN] - .clone_from_slice(&issued_at_encoded[0..ISSUED_LEN]); + metadata.extend(pk_encoded); + metadata.extend_from_slice(&expires_at_encoded[0..EXPIRATION_LEN]); + metadata.extend_from_slice(&issued_at_encoded[0..ISSUED_LEN]); metadata } @@ -170,32 +155,55 @@ impl Trust { /// Encode the trust into a byte array #[allow(dead_code)] pub fn encode(&self) -> Vec { - let mut vec = Vec::with_capacity(TRUST_LEN); - vec.extend_from_slice(&self.issued_for.to_bytes()); - vec.extend_from_slice(&self.signature.to_bytes()); + let mut vec = Vec::new(); + let mut issued_for = self.issued_for.encode(); + let mut signature = self.signature.encode(); + vec.push(issued_for.len() as u8); + vec.append(&mut issued_for); + vec.push(signature.len() as u8); + vec.append(&mut signature); vec.extend_from_slice(&(self.expires_at.as_secs() as u64).to_le_bytes()); vec.extend_from_slice(&(self.issued_at.as_secs() as u64).to_le_bytes()); vec } + fn check_arr_len(arr: &[u8], field_name: &str, check_len: usize) -> Result<(), TrustError> { + if arr.len() < check_len { + Err(DecodeErrorInvalidSize(field_name.to_string())) + } else { + Ok(()) + } + } + /// Decode a trust from a byte array as produced by `encode`. #[allow(dead_code)] pub fn decode(arr: &[u8]) -> Result { - if arr.len() != TRUST_LEN { - return Err(IncorrectTrustLength(arr.len())); - } + Self::check_arr_len(arr, "public_key_len", 1)?; + let pk_len = arr[0] as usize; + let mut offset = 1; - let pk = PublicKey::from_bytes(&arr[0..PK_LEN])?; + Self::check_arr_len(arr, "public_key", offset + pk_len)?; + let pk = PublicKey::decode(&arr[offset..offset + pk_len])?; + offset += pk_len; - let signature = &arr[PK_LEN..PK_LEN + SIG_LEN]; - let signature = Signature::from_bytes(signature)?; + Self::check_arr_len(arr, "signature_size", offset + 1)?; + let signature_len = arr[offset] as usize; + offset += 1; - let expiration_bytes = &arr[PK_LEN + SIG_LEN..PK_LEN + SIG_LEN + EXPIRATION_LEN]; + Self::check_arr_len(arr, "signature", offset + signature_len)?; + let signature = &arr[offset..offset + signature_len]; + let signature = Signature::decode(signature.to_vec())?; + offset += signature_len; + + Self::check_arr_len(arr, "expiration", offset + EXPIRATION_LEN)?; + let expiration_bytes = &arr[offset..offset + EXPIRATION_LEN]; let expiration_date = u64::from_le_bytes(expiration_bytes.try_into().unwrap()); let expiration_date = Duration::from_secs(expiration_date); + offset += EXPIRATION_LEN; - let issued_bytes = &arr[PK_LEN + SIG_LEN + EXPIRATION_LEN..TRUST_LEN]; + Self::check_arr_len(arr, "issued", offset + ISSUED_LEN)?; + let issued_bytes = &arr[offset..]; let issued_date = u64::from_le_bytes(issued_bytes.try_into().unwrap()); let issued_date = Duration::from_secs(issued_date); @@ -228,12 +236,12 @@ impl Trust { ) -> Result { // PublicKey let issued_for_bytes = Self::bs58_str_to_vec(issued_for, "issued_for")?; - let issued_for = PublicKey::from_bytes(issued_for_bytes.as_slice()) + let issued_for = PublicKey::decode(&issued_for_bytes) .map_err(|e| DecodePublicKeyError(issued_for.to_string(), e))?; // 64 bytes signature let signature = Self::bs58_str_to_vec(signature, "signature")?; - let signature = Signature::from_bytes(&signature)?; + let signature = Signature::decode(signature.to_vec())?; // Duration let expires_at = Self::str_to_duration(expires_at, "expires_at")?; @@ -247,8 +255,8 @@ impl Trust { impl ToString for Trust { fn to_string(&self) -> String { - let issued_for = bs58::encode(self.issued_for.to_bytes()).into_string(); - let signature = bs58::encode(self.signature.to_bytes()).into_string(); + let issued_for = bs58::encode(self.issued_for.encode()).into_string(); + let signature = bs58::encode(self.signature.encode()).into_string(); let expires_at = (self.expires_at.as_secs() as u64).to_string(); let issued_at = (self.issued_at.as_secs() as u64).to_string(); @@ -264,31 +272,31 @@ mod tests { use super::*; #[test] - fn test_gen_revoke_and_validate() { - let truster = KeyPair::generate(); - let trusted = KeyPair::generate(); + fn test_gen_revoke_and_validate_ed25519() { + let truster = KeyPair::generate_ed25519(); + let trusted = KeyPair::generate_ed25519(); let current = Duration::new(100, 0); let duration = Duration::new(1000, 0); let issued_at = Duration::new(10, 0); - let trust = Trust::create(&truster, trusted.public_key(), duration, issued_at); + let trust = Trust::create(&truster, trusted.public(), duration, issued_at); assert_eq!( - Trust::verify(&trust, &truster.public_key(), current).is_ok(), + Trust::verify(&trust, &truster.public(), current).is_ok(), true ); } #[test] - fn test_validate_corrupted_revoke() { - let truster = KeyPair::generate(); - let trusted = KeyPair::generate(); + fn test_validate_corrupted_revoke_ed25519() { + let truster = KeyPair::generate_ed25519(); + let trusted = KeyPair::generate_ed25519(); let current = Duration::new(1000, 0); let issued_at = Duration::new(10, 0); - let trust = Trust::create(&truster, trusted.public_key(), current, issued_at); + let trust = Trust::create(&truster, trusted.public(), current, issued_at); let corrupted_duration = Duration::new(1234, 0); let corrupted_trust = Trust::new( @@ -298,18 +306,18 @@ mod tests { trust.signature, ); - assert!(Trust::verify(&corrupted_trust, &truster.public_key(), current).is_err()); + assert!(Trust::verify(&corrupted_trust, &truster.public(), current).is_err()); } #[test] - fn test_encode_decode() { - let truster = KeyPair::generate(); - let trusted = KeyPair::generate(); + fn test_encode_decode_ed25519() { + let truster = KeyPair::generate_ed25519(); + let trusted = KeyPair::generate_ed25519(); let current = Duration::new(1000, 0); let issued_at = Duration::new(10, 0); - let trust = Trust::create(&truster, trusted.public_key(), current, issued_at); + let trust = Trust::create(&truster, trusted.public(), current, issued_at); let encoded = trust.encode(); let decoded = Trust::decode(encoded.as_slice()).unwrap(); diff --git a/src/trust_graph.rs b/src/trust_graph.rs index 5589141..44eb91f 100644 --- a/src/trust_graph.rs +++ b/src/trust_graph.rs @@ -123,10 +123,10 @@ where // Insert new TrustNode for this root_pk if there wasn't one if self.storage.get(&root_pk)?.is_none() { - let mut trust_node = TrustNode::new(root_trust.issued_for, cur_time); + let mut trust_node = TrustNode::new(root_trust.issued_for.clone(), cur_time); let root_auth = Auth { trust: root_trust.clone(), - issued_by: root_trust.issued_for, + issued_by: root_trust.issued_for.clone(), }; trust_node.update_auth(root_auth); self.storage.insert(root_pk, trust_node)?; @@ -139,7 +139,7 @@ where let auth = Auth { trust: trust.clone(), - issued_by: previous_trust.issued_for, + issued_by: previous_trust.issued_for.clone(), }; self.storage @@ -169,7 +169,7 @@ where // get all possible certificates from the given public key to all roots in the graph let certs = self.get_all_certs(pk, roots.as_slice())?; - Ok(self.certificates_weight(certs)?) + self.certificates_weight(certs) } /// Calculate weight from given certificates @@ -347,8 +347,8 @@ mod tests { } fn generate_root_cert() -> (KeyPair, KeyPair, Certificate) { - let root_kp = KeyPair::generate(); - let second_kp = KeyPair::generate(); + let root_kp = KeyPair::generate_ed25519(); + let second_kp = KeyPair::generate_ed25519(); let cur_time = current_time(); @@ -357,7 +357,7 @@ mod tests { second_kp.clone(), Certificate::issue_root( &root_kp, - second_kp.public_key(), + second_kp.public(), cur_time.checked_add(one_minute()).unwrap(), cur_time, ), @@ -373,20 +373,20 @@ mod tests { ) -> Result<(Vec, Certificate), TrustGraphError> { assert!(len > 2); - let root_kp = KeyPair::generate(); - let second_kp = KeyPair::generate(); + let root_kp = KeyPair::generate_ed25519(); + let second_kp = KeyPair::generate_ed25519(); let mut cert = - Certificate::issue_root(&root_kp, second_kp.public_key(), expires_at, issued_at); + Certificate::issue_root(&root_kp, second_kp.public(), expires_at, issued_at); let mut key_pairs = vec![root_kp, second_kp]; for idx in 2..len { - let kp = keys.get(&idx).unwrap_or(&KeyPair::generate()).clone(); + let kp = keys.get(&idx).unwrap_or(&KeyPair::generate_ed25519()).clone(); let previous_kp = &key_pairs[idx - 1]; cert = Certificate::issue( &previous_kp, - kp.public_key(), + kp.public(), &cert, expires_at, // TODO: why `issued_at = issued_at - 60 seconds`? @@ -434,12 +434,12 @@ mod tests { } #[test] - fn test_add_certs_with_same_trusts_and_different_expirations() { + fn test_add_certs_with_same_trusts_and_different_expirations_ed25519() { let cur_time = current_time(); let far_future = cur_time + Duration::from_secs(10); let far_far_future = cur_time + Duration::from_secs(900); - let key_pair1 = KeyPair::generate(); - let key_pair2 = KeyPair::generate(); + let key_pair1 = KeyPair::generate_ed25519(); + let key_pair2 = KeyPair::generate_ed25519(); // Use key_pair1 and key_pair2 for 5th and 6th trust in the cert chain let mut chain_keys = HashMap::new(); @@ -459,26 +459,26 @@ mod tests { let st = InMemoryStorage::new(); let mut graph = TrustGraph::new(st); - let root1_pk = key_pairs1[0].public_key(); - let root2_pk = key_pairs2[0].public_key(); + let root1_pk = key_pairs1[0].public(); + let root2_pk = key_pairs2[0].public(); graph.add_root_weight(root1_pk.into(), 1).unwrap(); graph.add_root_weight(root2_pk.into(), 0).unwrap(); graph.add(cert1, cur_time).unwrap(); - let node2 = graph.get(key_pair2.public_key()).unwrap().unwrap(); + let node2 = graph.get(key_pair2.public()).unwrap().unwrap(); let auth_by_kp1 = node2 .authorizations() - .find(|a| a.issued_by == key_pair1.public_key()) + .find(|a| a.issued_by == key_pair1.public()) .unwrap(); assert_eq!(auth_by_kp1.trust.expires_at, far_future * 2); graph.add(cert2, cur_time).unwrap(); - let node2 = graph.get(key_pair2.public_key()).unwrap().unwrap(); + let node2 = graph.get(key_pair2.public()).unwrap().unwrap(); let auth_by_kp1 = node2 .authorizations() - .find(|a| a.issued_by == key_pair1.public_key()) + .find(|a| a.issued_by == key_pair1.public()) .unwrap(); assert_eq!(auth_by_kp1.trust.expires_at, far_far_future * 2); @@ -492,21 +492,21 @@ mod tests { let st = InMemoryStorage::new(); let mut graph = TrustGraph::new(st); - let root_pk = key_pairs[0].public_key(); + let root_pk = key_pairs[0].public(); graph.add_root_weight(root_pk.into(), 1).unwrap(); graph.add(cert1, current_time()).unwrap(); - let w1 = graph.weight(key_pairs[0].public_key()).unwrap().unwrap(); + let w1 = graph.weight(key_pairs[0].public()).unwrap().unwrap(); assert_eq!(w1, 1); - let w2 = graph.weight(key_pairs[1].public_key()).unwrap().unwrap(); + let w2 = graph.weight(key_pairs[1].public()).unwrap().unwrap(); assert_eq!(w2, 2); - let w3 = graph.weight(key_pairs[9].public_key()).unwrap().unwrap(); + let w3 = graph.weight(key_pairs[9].public()).unwrap().unwrap(); assert_eq!(w3, 10); - let node = graph.get(key_pairs[9].public_key()).unwrap().unwrap(); + let node = graph.get(key_pairs[9].public()).unwrap().unwrap(); let auths: Vec<&Auth> = node.authorizations().collect(); assert_eq!(auths.len(), 1); @@ -515,9 +515,9 @@ mod tests { #[test] fn test_cycles_in_graph() { - let key_pair1 = KeyPair::generate(); - let key_pair2 = KeyPair::generate(); - let key_pair3 = KeyPair::generate(); + let key_pair1 = KeyPair::generate_ed25519(); + let key_pair2 = KeyPair::generate_ed25519(); + let key_pair3 = KeyPair::generate_ed25519(); let mut chain_keys = HashMap::new(); chain_keys.insert(3, key_pair1.clone()); @@ -535,8 +535,8 @@ mod tests { let st = InMemoryStorage::new(); let mut graph = TrustGraph::new(st); - let root1_pk = key_pairs1[0].public_key(); - let root2_pk = key_pairs2[0].public_key(); + let root1_pk = key_pairs1[0].public(); + let root2_pk = key_pairs2[0].public(); graph.add_root_weight(root1_pk.into(), 1).unwrap(); graph.add_root_weight(root2_pk.into(), 0).unwrap(); @@ -546,15 +546,15 @@ mod tests { graph.add(cert1, current_time()).unwrap(); graph.add(cert2, current_time()).unwrap(); - let revoke1 = Revoke::create(&key_pairs1[3], key_pairs1[4].public_key(), current_time()); + let revoke1 = Revoke::create(&key_pairs1[3], key_pairs1[4].public(), current_time()); graph.revoke(revoke1).unwrap(); - let revoke2 = Revoke::create(&key_pairs2[5], key_pairs2[6].public_key(), current_time()); + let revoke2 = Revoke::create(&key_pairs2[5], key_pairs2[6].public(), current_time()); graph.revoke(revoke2).unwrap(); - let w1 = graph.weight(key_pair1.public_key()).unwrap().unwrap(); + let w1 = graph.weight(key_pair1.public()).unwrap().unwrap(); // all upper trusts are revoked for this public key - let w2 = graph.weight(key_pair2.public_key()).unwrap(); - let w3 = graph.weight(key_pair3.public_key()).unwrap().unwrap(); + let w2 = graph.weight(key_pair2.public()).unwrap(); + let w3 = graph.weight(key_pair3.public()).unwrap().unwrap(); let w_last1 = graph.weight(last_pk1).unwrap().unwrap(); let w_last2 = graph.weight(last_pk2).unwrap().unwrap(); @@ -571,13 +571,13 @@ mod tests { let st = InMemoryStorage::new(); let mut graph = TrustGraph::new(st); - let root1_pk = key_pairs[0].public_key(); + let root1_pk = key_pairs[0].public(); graph.add_root_weight(root1_pk.clone().into(), 1).unwrap(); graph.add(cert.clone(), current_time()).unwrap(); let certs = graph - .get_all_certs(key_pairs.last().unwrap().public_key(), &[root1_pk]) + .get_all_certs(key_pairs.last().unwrap().public(), &[root1_pk]) .unwrap(); assert_eq!(certs.len(), 1); @@ -605,9 +605,9 @@ mod tests { #[test] fn test_find_certs() { - let key_pair1 = KeyPair::generate(); - let key_pair2 = KeyPair::generate(); - let key_pair3 = KeyPair::generate(); + let key_pair1 = KeyPair::generate_ed25519(); + let key_pair2 = KeyPair::generate_ed25519(); + let key_pair3 = KeyPair::generate_ed25519(); let mut chain_keys = HashMap::new(); chain_keys.insert(2, key_pair1.clone()); @@ -632,9 +632,9 @@ mod tests { let st = InMemoryStorage::new(); let mut graph = TrustGraph::new(st); - let root1_pk = key_pairs1[0].public_key(); - let root2_pk = key_pairs2[0].public_key(); - let root3_pk = key_pairs3[0].public_key(); + let root1_pk = key_pairs1[0].public(); + let root2_pk = key_pairs2[0].public(); + let root3_pk = key_pairs3[0].public(); graph.add_root_weight(root1_pk.clone().into(), 1).unwrap(); graph.add_root_weight(root2_pk.clone().into(), 0).unwrap(); graph.add_root_weight(root3_pk.clone().into(), 0).unwrap(); @@ -646,21 +646,21 @@ mod tests { let roots_values = [root1_pk, root2_pk, root3_pk]; let certs1 = graph - .get_all_certs(key_pair1.public_key(), &roots_values) + .get_all_certs(key_pair1.public(), &roots_values) .unwrap(); let lenghts1: Vec = certs1.iter().map(|c| c.chain.len()).collect(); let check_lenghts1: Vec = vec![3, 4, 4, 5, 5]; assert_eq!(lenghts1, check_lenghts1); let certs2 = graph - .get_all_certs(key_pair2.public_key(), &roots_values) + .get_all_certs(key_pair2.public(), &roots_values) .unwrap(); let lenghts2: Vec = certs2.iter().map(|c| c.chain.len()).collect(); let check_lenghts2: Vec = vec![4, 4, 4, 5, 5]; assert_eq!(lenghts2, check_lenghts2); let certs3 = graph - .get_all_certs(key_pair3.public_key(), &roots_values) + .get_all_certs(key_pair3.public(), &roots_values) .unwrap(); let lenghts3: Vec = certs3.iter().map(|c| c.chain.len()).collect(); let check_lenghts3: Vec = vec![3, 3, 5]; diff --git a/src/trust_graph_storage.rs b/src/trust_graph_storage.rs index 09d2fda..18202b6 100644 --- a/src/trust_graph_storage.rs +++ b/src/trust_graph_storage.rs @@ -116,7 +116,7 @@ impl Storage for InMemoryStorage { Ok(()) } None => { - let mut trust_node = TrustNode::new(*issued_for, cur_time); + let mut trust_node = TrustNode::new(issued_for.clone(), cur_time); trust_node.update_auth(auth); self.nodes.insert(pk.clone(), trust_node); Ok(()) diff --git a/src/trust_node.rs b/src/trust_node.rs index a79d491..38ef181 100644 --- a/src/trust_node.rs +++ b/src/trust_node.rs @@ -160,47 +160,47 @@ mod tests { #[test] fn test_auth_and_revoke_trust_node() { - let kp = KeyPair::generate(); + let kp = KeyPair::generate_ed25519(); let now = Duration::new(50, 0); let past = Duration::new(5, 0); let future = Duration::new(500, 0); let mut trust_node = TrustNode { - pk: kp.public_key(), + pk: kp.public(), trust_relations: HashMap::new(), verified_at: now, }; - let truster = KeyPair::generate(); + let truster = KeyPair::generate_ed25519(); - let revoke = Revoke::create(&truster, kp.public_key(), now); + let revoke = Revoke::create(&truster, kp.public(), now); trust_node.update_revoke(revoke); - assert!(trust_node.get_revoke(truster.public_key()).is_some()); + assert!(trust_node.get_revoke(truster.public()).is_some()); - let old_trust = Trust::create(&truster, kp.public_key(), Duration::new(60, 0), past); + let old_trust = Trust::create(&truster, kp.public(), Duration::new(60, 0), past); let old_auth = Auth { trust: old_trust, - issued_by: truster.public_key(), + issued_by: truster.public(), }; trust_node.update_auth(old_auth); - assert!(trust_node.get_revoke(truster.public_key()).is_some()); - assert!(trust_node.get_auth(truster.public_key()).is_none()); + assert!(trust_node.get_revoke(truster.public()).is_some()); + assert!(trust_node.get_auth(truster.public()).is_none()); - let trust = Trust::create(&truster, kp.public_key(), Duration::new(60, 0), future); + let trust = Trust::create(&truster, kp.public(), Duration::new(60, 0), future); let auth = Auth { trust, - issued_by: truster.public_key(), + issued_by: truster.public(), }; trust_node.update_auth(auth); - assert!(trust_node.get_auth(truster.public_key()).is_some()); - assert!(trust_node.get_revoke(truster.public_key()).is_none()); + assert!(trust_node.get_auth(truster.public()).is_some()); + assert!(trust_node.get_revoke(truster.public()).is_none()); } }