From 9fd28e961916e60dc4c7224b1e1e5c0b467a0a26 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Sat, 22 Apr 2023 21:18:22 +1000 Subject: [PATCH 1/6] store user keys in GCP Datastore --- .gitmodules | 3 - Cargo.lock | 325 +++-- Cargo.toml | 1 - mpc-recovery-gcp/.gitignore | 1 - mpc-recovery-gcp/Cargo.toml | 13 - mpc-recovery-gcp/README.md | 3 - mpc-recovery-gcp/build.rs | 33 - mpc-recovery-gcp/googleapis | 1 - mpc-recovery-gcp/src/google/cloud/mod.rs | 1 - .../src/google/cloud/secretmanager/mod.rs | 2 - mpc-recovery-gcp/src/google/iam/mod.rs | 2 - mpc-recovery-gcp/src/google/mod.rs | 4 - mpc-recovery-gcp/src/lib.rs | 2 - mpc-recovery/Cargo.toml | 6 +- mpc-recovery/roots.pem | 1257 ----------------- mpc-recovery/src/gcp/mod.rs | 339 ++++- mpc-recovery/src/key_recovery.rs | 40 +- mpc-recovery/src/leader_node/mod.rs | 23 +- mpc-recovery/src/lib.rs | 1 + mpc-recovery/src/main.rs | 18 +- 20 files changed, 537 insertions(+), 1538 deletions(-) delete mode 100644 .gitmodules delete mode 100644 mpc-recovery-gcp/.gitignore delete mode 100644 mpc-recovery-gcp/Cargo.toml delete mode 100644 mpc-recovery-gcp/README.md delete mode 100644 mpc-recovery-gcp/build.rs delete mode 160000 mpc-recovery-gcp/googleapis delete mode 100644 mpc-recovery-gcp/src/google/cloud/mod.rs delete mode 100644 mpc-recovery-gcp/src/google/cloud/secretmanager/mod.rs delete mode 100644 mpc-recovery-gcp/src/google/iam/mod.rs delete mode 100644 mpc-recovery-gcp/src/google/mod.rs delete mode 100644 mpc-recovery-gcp/src/lib.rs delete mode 100644 mpc-recovery/roots.pem diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index f9fd77f3e..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "mpc-recovery-gcp/googleapis"] -path = mpc-recovery-gcp/googleapis -url = https://github.com/googleapis/googleapis.git diff --git a/Cargo.lock b/Cargo.lock index 33544d5b4..0c1a91421 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -307,7 +307,7 @@ dependencies = [ "matchit 0.5.0", "memchr", "mime", - "percent-encoding", + "percent-encoding 2.2.0", "pin-project-lite", "serde", "serde_json", @@ -338,7 +338,7 @@ dependencies = [ "matchit 0.7.0", "memchr", "mime", - "percent-encoding", + "percent-encoding 2.2.0", "pin-project-lite", "rustversion", "serde", @@ -517,7 +517,7 @@ dependencies = [ "thiserror", "tokio", "tokio-util 0.7.3", - "url", + "url 2.3.1", "winapi", ] @@ -548,7 +548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive 0.10.3", - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] @@ -1021,6 +1021,41 @@ dependencies = [ "syn 2.0.14", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + [[package]] name = "der" version = "0.6.1" @@ -1298,7 +1333,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "percent-encoding", + "percent-encoding 2.2.0", ] [[package]] @@ -1455,6 +1490,67 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +[[package]] +name = "google-apis-common" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "376bc8360e10783fd357de4aad0f6c9e4c0ae4bc3362c5955e40017db0cd4a71" +dependencies = [ + "base64 0.13.1", + "chrono", + "http", + "hyper", + "itertools", + "mime", + "serde", + "serde_json", + "serde_with", + "tokio", + "tower-service", + "url 1.7.2", + "yup-oauth2", +] + +[[package]] +name = "google-datastore1" +version = "5.0.2+20230118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bad526389ac179f0368f60fb411a2e6fe858fabffd1e31495801c44ece7cd33" +dependencies = [ + "anyhow", + "google-apis-common", + "http", + "hyper", + "hyper-rustls", + "itertools", + "mime", + "serde", + "serde_json", + "tokio", + "tower-service", + "url 1.7.2", +] + +[[package]] +name = "google-secretmanager1" +version = "5.0.2+20230114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271d7d2af2f7868a40a153d5d79f738b709e209366680bddbbba11bce7005fe5" +dependencies = [ + "anyhow", + "google-apis-common", + "http", + "hyper", + "hyper-rustls", + "itertools", + "mime", + "serde", + "serde_json", + "tokio", + "tower-service", + "url 1.7.2", +] + [[package]] name = "group" version = "0.6.0" @@ -1499,6 +1595,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "heck" @@ -1638,27 +1737,14 @@ name = "hyper-rustls" version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" -dependencies = [ - "http", - "hyper", - "rustls 0.20.8", - "tokio", - "tokio-rustls 0.23.4", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" dependencies = [ "http", "hyper", "log", - "rustls 0.21.0", + "rustls 0.20.8", "rustls-native-certs", "tokio", - "tokio-rustls 0.24.0", + "tokio-rustls 0.23.4", ] [[package]] @@ -1723,6 +1809,23 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.3.0" @@ -1922,6 +2025,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "matchit" version = "0.5.0" @@ -1979,11 +2088,12 @@ dependencies = [ "clap 4.2.1", "ed25519-dalek", "futures", + "google-datastore1", + "google-secretmanager1", "hex 0.4.3", "hyper", - "hyper-rustls 0.24.0", + "hyper-rustls", "jsonwebtoken", - "mpc-recovery-gcp", "near-crypto 0.16.1", "near-jsonrpc-client 0.5.1", "near-jsonrpc-primitives 0.16.1", @@ -2003,18 +2113,6 @@ dependencies = [ "tower-http 0.4.0", "tracing", "tracing-subscriber", - "yup-oauth2", -] - -[[package]] -name = "mpc-recovery-gcp" -version = "0.1.0" -dependencies = [ - "prost 0.11.9", - "prost-types 0.11.9", - "tonic 0.9.2", - "tonic-build 0.9.1", - "walkdir", ] [[package]] @@ -2624,7 +2722,7 @@ dependencies = [ "serde_path_to_error", "sha2 0.10.6", "thiserror", - "url", + "url 2.3.1", ] [[package]] @@ -2705,7 +2803,7 @@ dependencies = [ "futures-util", "js-sys", "lazy_static", - "percent-encoding", + "percent-encoding 2.2.0", "pin-project", "rand 0.8.5", "thiserror", @@ -2728,7 +2826,7 @@ dependencies = [ "thiserror", "tokio", "tonic 0.6.2", - "tonic-build 0.6.2", + "tonic-build", ] [[package]] @@ -2811,6 +2909,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" + [[package]] name = "percent-encoding" version = "2.2.0" @@ -2918,16 +3022,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "prettyplease" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" -dependencies = [ - "proc-macro2", - "syn 1.0.109", -] - [[package]] name = "primitive-types" version = "0.10.1" @@ -3002,7 +3096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ "bytes", - "prost-derive 0.9.0", + "prost-derive", ] [[package]] @@ -3012,7 +3106,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive 0.11.9", ] [[package]] @@ -3029,34 +3122,12 @@ dependencies = [ "multimap", "petgraph", "prost 0.9.0", - "prost-types 0.9.0", + "prost-types", "regex", "tempfile", "which", ] -[[package]] -name = "prost-build" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" -dependencies = [ - "bytes", - "heck 0.4.1", - "itertools", - "lazy_static", - "log", - "multimap", - "petgraph", - "prettyplease", - "prost 0.11.9", - "prost-types 0.11.9", - "regex", - "syn 1.0.109", - "tempfile", - "which", -] - [[package]] name = "prost-derive" version = "0.9.0" @@ -3070,19 +3141,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "prost-derive" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "prost-types" version = "0.9.0" @@ -3093,15 +3151,6 @@ dependencies = [ "prost 0.9.0", ] -[[package]] -name = "prost-types" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" -dependencies = [ - "prost 0.11.9", -] - [[package]] name = "protobuf" version = "2.28.0" @@ -3282,7 +3331,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls 0.23.2", + "hyper-rustls", "hyper-tls", "ipnet", "js-sys", @@ -3290,7 +3339,7 @@ dependencies = [ "mime", "native-tls", "once_cell", - "percent-encoding", + "percent-encoding 2.2.0", "pin-project-lite", "rustls 0.20.8", "rustls-pemfile", @@ -3301,7 +3350,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls 0.23.4", "tower-service", - "url", + "url 2.3.1", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -3452,15 +3501,6 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schannel" version = "0.1.21" @@ -3621,9 +3661,22 @@ dependencies = [ "indexmap", "serde", "serde_json", + "serde_with_macros", "time 0.3.20", ] +[[package]] +name = "serde_with_macros" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859011bddcc11f289f07f467cc1fe01c7a941daa4d8f6c40d4d1c92eb6d9319c" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "serde_yaml" version = "0.9.21" @@ -4156,10 +4209,10 @@ dependencies = [ "http-body", "hyper", "hyper-timeout", - "percent-encoding", + "percent-encoding 2.2.0", "pin-project", "prost 0.9.0", - "prost-derive 0.9.0", + "prost-derive", "tokio", "tokio-stream", "tokio-util 0.6.10", @@ -4188,7 +4241,7 @@ dependencies = [ "http-body", "hyper", "hyper-timeout", - "percent-encoding", + "percent-encoding 2.2.0", "pin-project", "prost 0.11.9", "rustls-pemfile", @@ -4208,20 +4261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757" dependencies = [ "proc-macro2", - "prost-build 0.9.0", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "tonic-build" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f60a933bbea70c95d633c04c951197ddf084958abaa2ed502a3743bdd8d8dd7" -dependencies = [ - "prettyplease", - "proc-macro2", - "prost-build 0.11.9", + "prost-build", "quote", "syn 1.0.109", ] @@ -4477,6 +4517,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +dependencies = [ + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", +] + [[package]] name = "url" version = "2.3.1" @@ -4484,8 +4535,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna", - "percent-encoding", + "idna 0.3.0", + "percent-encoding 2.2.0", "serde", ] @@ -4528,16 +4579,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.0" @@ -4899,7 +4940,7 @@ dependencies = [ "tokio", "tokio-retry", "tracing", - "url", + "url 2.3.1", ] [[package]] @@ -4913,9 +4954,9 @@ dependencies = [ [[package]] name = "yup-oauth2" -version = "8.2.0" +version = "8.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6135ad28f1957d676384074df0ad1dd920966834bddd973da86119378b4964d7" +checksum = "1da09943413fec574fbc624011874b716e4b446f83d43f8248a5903e7ffc5e52" dependencies = [ "anyhow", "async-trait", @@ -4923,11 +4964,11 @@ dependencies = [ "futures", "http", "hyper", - "hyper-rustls 0.24.0", + "hyper-rustls", "itertools", "log", - "percent-encoding", - "rustls 0.21.0", + "percent-encoding 2.2.0", + "rustls 0.20.8", "rustls-pemfile", "seahash", "serde", @@ -4935,7 +4976,7 @@ dependencies = [ "time 0.3.20", "tokio", "tower-service", - "url", + "url 2.3.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f1adaaf75..d9d8f871e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = [ "mpc-recovery", - "mpc-recovery-gcp", "integration-tests", ] diff --git a/mpc-recovery-gcp/.gitignore b/mpc-recovery-gcp/.gitignore deleted file mode 100644 index 4f62b849d..000000000 --- a/mpc-recovery-gcp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -gen diff --git a/mpc-recovery-gcp/Cargo.toml b/mpc-recovery-gcp/Cargo.toml deleted file mode 100644 index 76d77c5e5..000000000 --- a/mpc-recovery-gcp/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "mpc-recovery-gcp" -version = "0.1.0" -edition = "2021" - -[dependencies] -prost = "0.11.8" -prost-types = "0.11.8" -tonic = "0.9" - -[build-dependencies] -walkdir = "2.3.3" -tonic-build = "0.9" diff --git a/mpc-recovery-gcp/README.md b/mpc-recovery-gcp/README.md deleted file mode 100644 index 7fc219614..000000000 --- a/mpc-recovery-gcp/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# GCP gRPC Bindings - -MPC Recovery runs on GCP and hence needs to interact with its API. Unfortunately, there is no official GCP Rust API/SDK and the only other option is to use raw gRPC services. This module contains these gRPC bindings, but only for services required by MPC Recovery. Proto files are pulled from a git submodule for [googleapis](https://github.com/googleapis/googleapis) (remember to initialize it if you are compiling this project locally). gRPC code is generated using [tonic](https://github.com/hyperium/tonic). diff --git a/mpc-recovery-gcp/build.rs b/mpc-recovery-gcp/build.rs deleted file mode 100644 index 7e1ad122b..000000000 --- a/mpc-recovery-gcp/build.rs +++ /dev/null @@ -1,33 +0,0 @@ -use walkdir::WalkDir; - -fn main() { - std::fs::create_dir_all("gen/protos").unwrap(); - let proto_dirs = vec![ - "googleapis/google/cloud/secretmanager/v1", - "googleapis/google/type", - ]; - - let mut proto_files = Vec::new(); - for proto_dir in proto_dirs { - for entry in WalkDir::new(proto_dir) - .follow_links(true) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| { - e.path() - .extension() - .map(|e| e == "proto") - .unwrap_or_default() - }) - { - proto_files.push(entry.into_path()); - } - } - - tonic_build::configure() - .out_dir("gen/protos") - .build_server(false) - .protoc_arg("--experimental_allow_proto3_optional") - .compile(&proto_files, &["googleapis"]) - .unwrap(); -} diff --git a/mpc-recovery-gcp/googleapis b/mpc-recovery-gcp/googleapis deleted file mode 160000 index 07f4c8f53..000000000 --- a/mpc-recovery-gcp/googleapis +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 07f4c8f531dbd916d340088dd909e64fbd968d91 diff --git a/mpc-recovery-gcp/src/google/cloud/mod.rs b/mpc-recovery-gcp/src/google/cloud/mod.rs deleted file mode 100644 index 54a0161f2..000000000 --- a/mpc-recovery-gcp/src/google/cloud/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod secretmanager; diff --git a/mpc-recovery-gcp/src/google/cloud/secretmanager/mod.rs b/mpc-recovery-gcp/src/google/cloud/secretmanager/mod.rs deleted file mode 100644 index 17fbefac9..000000000 --- a/mpc-recovery-gcp/src/google/cloud/secretmanager/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[path = "../../../../gen/protos/google.cloud.secretmanager.v1.rs"] -pub mod v1; diff --git a/mpc-recovery-gcp/src/google/iam/mod.rs b/mpc-recovery-gcp/src/google/iam/mod.rs deleted file mode 100644 index aa2465023..000000000 --- a/mpc-recovery-gcp/src/google/iam/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[path = "../../../gen/protos/google.iam.v1.rs"] -pub mod v1; diff --git a/mpc-recovery-gcp/src/google/mod.rs b/mpc-recovery-gcp/src/google/mod.rs deleted file mode 100644 index b5faf44d2..000000000 --- a/mpc-recovery-gcp/src/google/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod cloud; -mod iam; -#[path = "../../gen/protos/google.r#type.rs"] -mod r#type; diff --git a/mpc-recovery-gcp/src/lib.rs b/mpc-recovery-gcp/src/lib.rs deleted file mode 100644 index 50cada942..000000000 --- a/mpc-recovery-gcp/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[rustfmt::skip] -pub mod google; diff --git a/mpc-recovery/Cargo.toml b/mpc-recovery/Cargo.toml index dfa8c2a35..2464161ec 100644 --- a/mpc-recovery/Cargo.toml +++ b/mpc-recovery/Cargo.toml @@ -25,11 +25,12 @@ chrono = "0.4.24" clap = { version = "4.2", features = ["derive", "env"] } ed25519-dalek = "1" futures = "0.3" +google-datastore1 = "5" +google-secretmanager1 = "5" hex = "0.4" hyper = { version = "0.14", features = ["full"] } -hyper-rustls = { version = "0.24", features = ["http2"] } +hyper-rustls = { version = "=0.23", features = ["http2"] } jsonwebtoken = "8.3.0" -mpc-recovery-gcp = { path = "../mpc-recovery-gcp" } oauth2 = "4.3.0" rand = "0.7" rand8 = { package = "rand", version = "0.8" } @@ -49,4 +50,3 @@ near-jsonrpc-primitives = "0.16.1" near-primitives = "0.16.1" near-crypto = "0.16.1" tower-http = { version = "0.4.0", features = ["cors"] } -yup-oauth2 = "8" diff --git a/mpc-recovery/roots.pem b/mpc-recovery/roots.pem deleted file mode 100644 index 52ef73bbc..000000000 --- a/mpc-recovery/roots.pem +++ /dev/null @@ -1,1257 +0,0 @@ -# Operating CA: Comodo Group -# Issuer: CN=AAA Certificate Services O=Comodo CA Limited -# Subject: CN=AAA Certificate Services O=Comodo CA Limited -# Label: "Comodo AAA Services root" -# Serial: 1 -# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 -# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 -# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 ------BEGIN CERTIFICATE----- -MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb -MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow -GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj -YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM -GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua -BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe -3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 -YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR -rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm -ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU -oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF -MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v -QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t -b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF -AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q -GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz -Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 -G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi -l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 -smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== ------END CERTIFICATE----- - -# Operating CA: Comodo Group -# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network -# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network -# Label: "AddTrust External Root" -# Serial: 1 -# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f -# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68 -# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2 ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs -IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 -MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h -bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt -H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 -uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX -mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX -a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN -E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 -WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD -VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 -Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU -cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx -IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN -AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH -YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC -Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX -c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a -mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- - -# Operating CA: Comodo Group -# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited -# Subject: CN=COMODO Certification Authority O=COMODO CA Limited -# Label: "COMODO Certification Authority" -# Serial: 104350513648249232941998508985834464573 -# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 -# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b -# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 ------BEGIN CERTIFICATE----- -MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB -gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV -BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw -MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl -YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P -RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 -UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI -2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 -Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp -+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ -DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O -nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW -/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g -PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u -QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY -SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv -IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ -RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 -zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd -BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB -ZQ== ------END CERTIFICATE----- - -# Operating CA: Comodo Group -# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited -# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited -# Label: "COMODO ECC Certification Authority" -# Serial: 41578283867086692638256921589707938090 -# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 -# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 -# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 ------BEGIN CERTIFICATE----- -MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT -IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw -MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy -ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N -T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR -FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J -cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW -BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm -fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv -GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= ------END CERTIFICATE----- - -# Operating CA: Comodo Group -# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited -# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited -# Label: "COMODO RSA Certification Authority" -# Serial: 101909084537582093308941363524873193117 -# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 -# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 -# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 ------BEGIN CERTIFICATE----- -MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB -hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV -BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 -MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT -EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR -Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR -6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X -pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC -9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV -/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf -Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z -+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w -qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah -SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC -u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf -Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq -crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E -FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB -/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl -wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM -4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV -2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna -FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ -CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK -boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke -jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL -S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb -QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl -0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB -NVOFBkpdn627G190 ------END CERTIFICATE----- - -# Operating CA: Comodo Group -# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network -# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network -# Label: "USERTrust ECC Certification Authority" -# Serial: 123013823720199481456569720443997572134 -# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 -# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 -# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a ------BEGIN CERTIFICATE----- -MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL -MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl -eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT -JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx -MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT -Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg -VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo -I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng -o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G -A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB -zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW -RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= ------END CERTIFICATE----- - -# Operating CA: Comodo Group -# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network -# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network -# Label: "USERTrust RSA Certification Authority" -# Serial: 2645093764781058787591871645665788717 -# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 -# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e -# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 ------BEGIN CERTIFICATE----- -MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB -iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl -cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV -BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw -MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU -aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B -3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY -tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ -Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 -VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT -79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 -c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT -Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l -c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee -UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE -Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd -BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G -A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF -Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO -VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 -ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs -8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR -iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze -Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ -XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ -qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB -VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB -L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG -jjxDah2nGN59PRbxYvnKkKj9 ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust -# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust -# Label: "Baltimore CyberTrust Root" -# Serial: 33554617 -# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 -# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 -# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ -RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD -VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX -DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y -ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy -VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr -mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr -IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK -mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu -XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy -dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye -jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 -BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 -DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 -9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx -jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 -Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz -ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS -R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc -# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc -# Label: "Cybertrust Global Root" -# Serial: 4835703278459682877484360 -# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1 -# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6 -# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3 ------BEGIN CERTIFICATE----- -MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG -A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh -bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE -ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS -b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 -7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS -J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y -HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP -t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz -FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY -XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ -MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw -hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js -MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA -A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj -Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx -XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o -omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc -A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW -WL1WMRJOEcgh4LMRkWXbtKaIOM5V ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Assured ID Root CA" -# Serial: 17154717934120587862167794914071425081 -# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 -# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 -# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c ------BEGIN CERTIFICATE----- -MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv -b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl -cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c -JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP -mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ -wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 -VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ -AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB -AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun -pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC -dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf -fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm -NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx -H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe -+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Assured ID Root G2" -# Serial: 15385348160840213938643033620894905419 -# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d -# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f -# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 ------BEGIN CERTIFICATE----- -MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv -b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl -cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA -n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc -biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp -EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA -bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu -YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB -AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW -BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI -QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I -0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni -lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 -B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv -ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo -IhNzbM8m9Yop5w== ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Assured ID Root G3" -# Serial: 15459312981008553731928384953135426796 -# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb -# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 -# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 ------BEGIN CERTIFICATE----- -MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg -RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu -Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf -Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q -RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD -AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY -JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv -6pZjamVFkpUBtA== ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root CA" -# Serial: 10944719598952040374951832963794454346 -# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e -# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 -# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 ------BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root G2" -# Serial: 4293743540046975378534879503202253541 -# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 -# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 -# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f ------BEGIN CERTIFICATE----- -MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH -MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI -2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx -1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ -q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz -tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ -vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP -BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV -5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY -1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 -NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG -Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 -8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe -pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl -MrY= ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root G3" -# Serial: 7089244469030293291760083333884364146 -# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca -# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e -# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 ------BEGIN CERTIFICATE----- -MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe -Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw -EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x -IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF -K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG -fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO -Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd -BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx -AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ -oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 -sycX ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert High Assurance EV Root CA" -# Serial: 3553400076410547919724730734378100087 -# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a -# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 -# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Trusted Root G4" -# Serial: 7451500558977370777930084869016614236 -# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 -# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 -# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 ------BEGIN CERTIFICATE----- -MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg -RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu -Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y -ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If -xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV -ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO -DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ -jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ -CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi -EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM -fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY -uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK -chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t -9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD -ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 -SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd -+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc -fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa -sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N -cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N -0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie -4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI -r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 -/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm -gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ ------END CERTIFICATE----- - -# Operating CA: DigiCert -# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc. -# Subject: CN=GeoTrust Global CA O=GeoTrust Inc. -# Label: "GeoTrust Global CA" -# Serial: 144470 -# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5 -# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12 -# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg -R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 -9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq -fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv -iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU -1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ -bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW -MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA -ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l -uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn -Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS -tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF -PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un -hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV -5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- - -# Operating CA: Entrust Datacard -# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. -# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. -# Label: "Entrust Root Certification Authority" -# Serial: 1164660820 -# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 -# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 -# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c ------BEGIN CERTIFICATE----- -MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 -Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW -KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl -cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw -NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw -NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy -ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV -BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo -Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 -4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 -KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI -rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi -94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB -sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi -gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo -kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE -vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA -A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t -O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua -AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP -9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ -eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m -0vdXcDazv/wor3ElhVsT/h5/WrQ8 ------END CERTIFICATE----- - -# Operating CA: Entrust Datacard -# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only -# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only -# Label: "Entrust Root Certification Authority - EC1" -# Serial: 51543124481930649114116133369 -# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc -# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 -# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 ------BEGIN CERTIFICATE----- -MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG -A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 -d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu -dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq -RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy -MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD -VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 -L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g -Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi -A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt -ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH -Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O -BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC -R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX -hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G ------END CERTIFICATE----- - -# Operating CA: Entrust Datacard -# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only -# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only -# Label: "Entrust Root Certification Authority - G2" -# Serial: 1246989352 -# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 -# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 -# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 ------BEGIN CERTIFICATE----- -MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 -cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs -IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz -dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy -NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu -dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt -dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 -aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T -RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN -cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW -wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 -U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 -jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN -BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ -jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ -Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v -1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R -nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH -VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== ------END CERTIFICATE----- - -# Operating CA: Entrust Datacard -# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited -# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited -# Label: "Entrust.net Premium 2048 Secure Server CA" -# Serial: 946069240 -# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 -# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 -# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML -RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp -bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 -IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 -MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 -LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp -YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG -A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq -K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe -sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX -MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT -XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ -HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH -4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub -j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo -U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf -zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b -u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ -bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er -fF6adulZkMV8gzURZVE= ------END CERTIFICATE----- - -# Operating CA: Entrust Datacard -# Issuer: CN=AffirmTrust Commercial O=AffirmTrust -# Subject: CN=AffirmTrust Commercial O=AffirmTrust -# Label: "AffirmTrust Commercial" -# Serial: 8608355977964138876 -# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 -# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 -# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP -Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr -ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL -MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 -yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr -VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ -nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG -XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj -vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt -Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g -N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC -nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= ------END CERTIFICATE----- - -# Operating CA: Entrust Datacard -# Issuer: CN=AffirmTrust Networking O=AffirmTrust -# Subject: CN=AffirmTrust Networking O=AffirmTrust -# Label: "AffirmTrust Networking" -# Serial: 8957382827206547757 -# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f -# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f -# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y -YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua -kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL -QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp -6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG -yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i -QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO -tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu -QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ -Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u -olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 -x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= ------END CERTIFICATE----- - -# Operating CA: Entrust Datacard -# Issuer: CN=AffirmTrust Premium O=AffirmTrust -# Subject: CN=AffirmTrust Premium O=AffirmTrust -# Label: "AffirmTrust Premium" -# Serial: 7893706540734352110 -# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 -# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 -# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz -dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG -A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U -cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf -qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ -JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ -+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS -s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 -HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 -70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG -V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S -qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S -5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia -C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX -OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE -FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 -KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg -Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B -8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ -MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc -0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ -u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF -u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH -YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 -GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO -RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e -KeC2uAloGRwYQw== ------END CERTIFICATE----- - -# Operating CA: Entrust Datacard -# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust -# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust -# Label: "AffirmTrust Premium ECC" -# Serial: 8401224907861490260 -# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d -# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb -# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 ------BEGIN CERTIFICATE----- -MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC -VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ -cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ -BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt -VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D -0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 -ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G -A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs -aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I -flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== ------END CERTIFICATE----- - -# Operating CA: GlobalSign -# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA -# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA -# Label: "GlobalSign Root CA" -# Serial: 4835703278459707669005204 -# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a -# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c -# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 ------BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw -MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT -aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ -jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp -xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp -1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG -snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ -U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 -9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B -AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz -yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE -38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP -AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad -DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME -HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE----- - -# Operating CA: GlobalSign -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 -# Label: "GlobalSign Root CA - R3" -# Serial: 4835703278459759426209954 -# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 -# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad -# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b ------BEGIN CERTIFICATE----- -MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G -A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp -Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 -MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG -A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 -RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT -gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm -KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd -QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ -XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw -DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o -LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU -RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp -jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK -6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX -mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs -Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH -WD9f ------END CERTIFICATE----- - -# Operating CA: GlobalSign -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 -# Label: "GlobalSign ECC Root CA - R5" -# Serial: 32785792099990507226680698011560947931244 -# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 -# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa -# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 ------BEGIN CERTIFICATE----- -MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk -MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH -bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX -DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD -QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc -8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke -hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI -KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg -515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO -xwy8p2Fp8fc74SrL+SvzZpA3 ------END CERTIFICATE----- - -# Operating CA: GlobalSign -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 -# Label: "GlobalSign Root CA - R6" -# Serial: 1417766617973444989252670301619537 -# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae -# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 -# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg -MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh -bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx -MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET -MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ -KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI -xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k -ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD -aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw -LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw -1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX -k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 -SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h -bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n -WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY -rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce -MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu -bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN -nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt -Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 -55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj -vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf -cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz -oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp -nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs -pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v -JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R -8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 -5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= ------END CERTIFICATE----- - -# Note: "GlobalSign Root CA - R7" not added on purpose. It is P-521. -# Note: "GlobalSign Root CA - R8" is not yet included in Mozilla. - -# Operating CA: GoDaddy -# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. -# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. -# Label: "Go Daddy Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 -# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b -# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT -EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp -ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz -NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH -EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE -AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD -E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH -/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy -DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh -GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR -tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA -AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX -WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu -9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr -gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo -2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO -LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI -4uJEvlz36hz1 ------END CERTIFICATE----- - -# Operating CA: GoDaddy -# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Label: "Starfield Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 -# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e -# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs -ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw -MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 -b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj -aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp -Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg -nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 -HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N -Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN -dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 -HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G -CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU -sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 -4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg -8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K -pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 -mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 ------END CERTIFICATE----- - -# Operating CA: GoDaddy -# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority -# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority -# Label: "Starfield Class 2 CA" -# Serial: 0 -# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 -# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a -# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 ------BEGIN CERTIFICATE----- -MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl -MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp -U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw -NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE -ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp -ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 -DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf -8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN -+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 -X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa -K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA -1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G -A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR -zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 -YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD -bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w -DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 -L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D -eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl -xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp -VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY -WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= ------END CERTIFICATE----- - -# Operating CA: GoDaddy -# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority -# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority -# Label: "Go Daddy Class 2 CA" -# Serial: 0 -# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 -# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 -# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh -MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE -YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 -MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo -ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg -MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN -ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA -PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w -wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi -EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY -avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ -YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE -sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h -/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 -IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD -ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy -OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P -TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ -HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER -dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf -ReYNnyicsbkqWletNw+vHX/bvZ8= ------END CERTIFICATE----- - -# Operating CA: Google Trust Services LLC -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2 -# Label: "GlobalSign Root CA - R2" -# Serial: 4835703278459682885658125 -# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30 -# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe -# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e ------BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G -A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp -Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 -MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG -A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL -v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 -eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq -tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd -C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa -zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB -mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH -V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n -bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG -3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs -J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO -291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS -ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd -AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 -TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== ------END CERTIFICATE----- - -# Operating CA: Google Trust Services LLC -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 -# Label: "GlobalSign ECC Root CA - R4" -# Serial: 14367148294922964480859022125800977897474 -# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e -# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb -# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c ------BEGIN CERTIFICATE----- -MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk -MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH -bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX -DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD -QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ -FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw -DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F -uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX -kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs -ewv4n4Q= ------END CERTIFICATE----- - -# Operating CA: Google Trust Services LLC -# Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R1 -# Subject: C=US, O=Google Trust Services LLC, CN=GTS Root R1 -# Label: "GTS Root R1" -# Serial: 6e:47:a9:c5:4b:47:0c:0d:ec:33:d0:89:b9:1c:f4:e1 -# MD5 Fingerprint: 82:1A:EF:D4:D2:4A:F2:9F:E2:3D:97:06:14:70:72:85 -# SHA1 Fingerprint: E1:C9:50:E6:EF:22:F8:4C:56:45:72:8B:92:20:60:D7:D5:A7:A3:E8 -# SHA256 Fingerprint: 2A:57:54:71:E3:13:40:BC:21:58:1C:BD:2C:F1:3E:15:84:63:20:3E:CE:94:BC:F9:D3:CC:19:6B:F0:9A:54:72 ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH -MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM -QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy -MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl -cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM -f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX -mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7 -zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P -fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc -vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4 -Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp -zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO -Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW -k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+ -DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF -lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW -Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 -d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z -XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR -gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3 -d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv -J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg -DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM -+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy -F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9 -SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws -E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl ------END CERTIFICATE----- - -# Operating CA: Google Trust Services LLC -# Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R2 -# Subject: C=US, O=Google Trust Services LLC, CN=GTS Root R2 -# Label: "GTS Root R2" -# Serial: 6e:47:a9:c6:5a:b3:e7:20:c5:30:9a:3f:68:52:f2:6f -# MD5 Fingerprint: 44:ED:9A:0E:A4:09:3B:00:F2:AE:4C:A3:C6:61:B0:8B -# SHA1 Fingerprint: D2:73:96:2A:2A:5E:39:9F:73:3F:E1:C7:1E:64:3F:03:38:34:FC:4D -# SHA256 Fingerprint: C4:5D:7B:B0:8E:6D:67:E6:2E:42:35:11:0B:56:4E:5F:78:FD:92:EF:05:8C:84:0A:EA:4E:64:55:D7:58:5C:60 ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH -MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM -QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy -MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl -cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv -CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg -GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu -XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd -re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu -PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1 -mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K -8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj -x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR -nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0 -kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok -twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp -8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT -vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT -z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA -pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb -pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB -R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R -RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk -0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC -5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF -izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn -yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC ------END CERTIFICATE----- - -# Operating CA: Google Trust Services LLC -# Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R3 -# Subject: C=US, O=Google Trust Services LLC, CN=GTS Root R3 -# Label: "GTS Root R3" -# Serial: 6e:47:a9:c7:6c:a9:73:24:40:89:0f:03:55:dd:8d:1d -# MD5 Fingerprint: 1A:79:5B:6B:04:52:9C:5D:C7:74:33:1B:25:9A:F9:25 -# SHA1 Fingerprint: 30:D4:24:6F:07:FF:DB:91:89:8A:0B:E9:49:66:11:EB:8C:5E:46:E5 -# SHA256 Fingerprint: 15:D5:B8:77:46:19:EA:7D:54:CE:1C:A6:D0:B0:C4:03:E0:37:A9:17:F1:31:E8:A0:4E:1E:6B:7A:71:BA:BC:E5 ------BEGIN CERTIFICATE----- -MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw -MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout -736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A -DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk -fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA -njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd ------END CERTIFICATE----- - -# Operating CA: Google Trust Services LLC -# Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R4 -# Subject: C=US, O=Google Trust Services LLC, CN=GTS Root R4 -# Label: "GTS Root R4" -# Serial: 6e:47:a9:c8:8b:94:b6:e8:bb:3b:2a:d8:a2:b2:c1:99 -# MD5 Fingerprint: 5D:B6:6A:C4:60:17:24:6A:1A:99:A8:4B:EE:5E:B4:26 -# SHA1 Fingerprint: 2A:1D:60:27:D9:4A:B1:0A:1C:4D:91:5C:CD:33:A0:CB:3E:2D:54:CB -# SHA256 Fingerprint: 71:CC:A5:39:1F:9E:79:4B:04:80:25:30:B3:63:E1:21:DA:8A:30:43:BB:26:66:2F:EA:4D:CA:7F:C9:51:A4:BD ------BEGIN CERTIFICATE----- -MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw -MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu -hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l -xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0 -CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx -sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w== ------END CERTIFICATE----- \ No newline at end of file diff --git a/mpc-recovery/src/gcp/mod.rs b/mpc-recovery/src/gcp/mod.rs index f0d4fcaee..b5edb0bec 100644 --- a/mpc-recovery/src/gcp/mod.rs +++ b/mpc-recovery/src/gcp/mod.rs @@ -1,69 +1,318 @@ -use hyper::client::HttpConnector; -use hyper_rustls::HttpsConnector; -use mpc_recovery_gcp::google::cloud::secretmanager::v1::{ - secret_manager_service_client::SecretManagerServiceClient, AccessSecretVersionRequest, -}; -use tonic::{ - transport::{Certificate, Channel, ClientTlsConfig}, - Request, Status, +use google_datastore1::api::{ + ArrayValue, CommitRequest, Entity, Key, LookupRequest, Mutation, PathElement, Value, }; -use yup_oauth2::{ - authenticator::{ApplicationDefaultCredentialsTypes, Authenticator}, +use google_datastore1::Datastore; +use google_secretmanager1::oauth2::authenticator::ApplicationDefaultCredentialsTypes; +use google_secretmanager1::oauth2::{ ApplicationDefaultCredentialsAuthenticator, ApplicationDefaultCredentialsFlowOpts, }; +use google_secretmanager1::SecretManager; +use hyper::client::HttpConnector; +use hyper_rustls::HttpsConnector; +use serde::de::DeserializeOwned; +use serde::Serialize; +use std::collections::HashMap; + +pub trait DatastoreEntity { + fn kind() -> String; + fn name(&self) -> String; +} + +pub fn to_datastore_value(x: T) -> anyhow::Result { + match serde_json::to_value(x)? { + serde_json::Value::Null => Ok(Value { + null_value: Some(String::from("NULL_VALUE")), + ..Value::default() + }), + serde_json::Value::Bool(x) => Ok(Value { + boolean_value: Some(x), + ..Value::default() + }), + serde_json::Value::Number(x) => { + if x.is_f64() { + Ok(Value { + double_value: Some( + x.as_f64() + .ok_or_else(|| anyhow::anyhow!("non-serializable f64"))?, + ), + ..Value::default() + }) + } else if x.is_i64() { + Ok(Value { + integer_value: Some( + x.as_i64() + .ok_or_else(|| anyhow::anyhow!("non-serializable i64"))?, + ), + ..Value::default() + }) + } else if x.is_u64() { + Ok(Value { + integer_value: Some( + x.as_i64() + .ok_or_else(|| anyhow::anyhow!("non-serializable u64"))?, + ), + ..Value::default() + }) + } else { + anyhow::bail!("unsupported type"); + } + } + serde_json::Value::String(x) => Ok(Value { + string_value: Some(x), + ..Value::default() + }), + serde_json::Value::Array(xs) => { + let mut any_error = None; + let xs = xs + .into_iter() + .filter_map(|x| { + let result = to_datastore_value(x); + match result { + Ok(result) => Some(result), + Err(e) => { + any_error = Some(e); + None + } + } + }) + .collect::>(); + if let Some(e) = any_error { + Err(e) + } else { + Ok(Value { + array_value: Some(ArrayValue { values: Some(xs) }), + ..Value::default() + }) + } + } + serde_json::Value::Object(xs) => { + let mut any_error = None; + let xs = xs + .into_iter() + .filter_map(|(k, v)| { + let v = to_datastore_value(v); + match v { + Ok(v) => Some((k, v)), + Err(e) => { + any_error = Some(e); + None + } + } + }) + .collect::>(); + if let Some(e) = any_error { + Err(e) + } else { + Ok(Value { + entity_value: Some(Entity { + properties: Some(xs), + key: None, + }), + ..Value::default() + }) + } + } + } +} + +fn from_datastore_value(value: Value) -> anyhow::Result { + let serde_value: serde_json::Value; + if let Some(xs) = value.entity_value { + let mut any_error = None; + let xs = xs + .properties + .unwrap_or(HashMap::default()) + .into_iter() + .filter_map(|(k, v)| { + let result = from_datastore_value(v); + match result { + Ok(v) => Some((k, v)), + Err(e) => { + any_error = Some(e); + None + } + } + }) + .collect::>(); + if let Some(e) = any_error { + return Err(e); + } else { + serde_value = serde_json::Value::Object(xs); + } + } else if let Some(xs) = value.timestamp_value { + serde_value = serde_json::Value::String(xs.to_string()); + } else if let Some(_) = value.geo_point_value { + anyhow::bail!("unimplemented"); + } else if let Some(_) = value.blob_value { + anyhow::bail!("unimplemented"); + } else if let Some(xs) = value.double_value { + serde_value = serde_json::Value::Number( + serde_json::Number::from_f64(xs) + .ok_or_else(|| anyhow::anyhow!("invalid f64 values: {xs}"))?, + ); + } else if let Some(_) = value.meaning { + anyhow::bail!("unimplemented"); + } else if let Some(_) = value.exclude_from_indexes { + anyhow::bail!("unimplemented"); + } else if let Some(xs) = value.string_value { + serde_value = serde_json::Value::String(xs); + } else if let Some(_) = value.key_value { + anyhow::bail!("unimplemented"); + } else if let Some(xs) = value.boolean_value { + serde_value = serde_json::Value::Bool(xs); + } else if let Some(xs) = value.array_value { + let mut any_error = None; + let xs = xs + .values + .unwrap_or(Vec::default()) + .into_iter() + .filter_map(|x| { + let result = from_datastore_value(x); + match result { + Ok(result) => Some(result), + Err(e) => { + any_error = Some(e); + None + } + } + }) + .collect::>(); + if let Some(e) = any_error { + return Err(e); + } else { + serde_value = serde_json::Value::Array(xs); + } + } else if let Some(xs) = value.integer_value { + serde_value = serde_json::Value::Number(serde_json::Number::from(xs)); + } else if let Some(_) = value.null_value { + serde_value = serde_json::Value::Null; + } else { + anyhow::bail!("unimplemented"); + } + + Ok(serde_json::from_value(serde_value)?) +} -const DOMAIN_NAME: &str = "secretmanager.googleapis.com"; -const ENDPOINT: &str = "https://secretmanager.googleapis.com"; -const TLS_CERTS: &[u8] = include_bytes!("../../roots.pem"); +fn from_datastore_entity(value: Entity) -> anyhow::Result { + let value = Value { + entity_value: Some(value), + ..Default::default() + }; + from_datastore_value(value) +} +#[derive(Clone)] pub struct GcpService { - authenticator: Authenticator>, + project_id: String, + datastore: Datastore>, + secret_manager: SecretManager>, } impl GcpService { - pub async fn new() -> anyhow::Result { + pub async fn new(project_id: String) -> anyhow::Result { let opts = ApplicationDefaultCredentialsFlowOpts::default(); let authenticator = match ApplicationDefaultCredentialsAuthenticator::builder(opts).await { ApplicationDefaultCredentialsTypes::InstanceMetadata(auth) => auth.build().await?, ApplicationDefaultCredentialsTypes::ServiceAccount(auth) => auth.build().await?, }; + let client = hyper::Client::builder().build( + hyper_rustls::HttpsConnectorBuilder::new() + .with_native_roots() + .https_or_http() + .enable_http1() + .enable_http2() + .build(), + ); + let secret_manager = SecretManager::new(client.clone(), authenticator.clone()); + let datastore = Datastore::new(client, authenticator); - Ok(Self { authenticator }) + Ok(Self { + project_id, + datastore, + secret_manager, + }) } - pub async fn load_secret(&self, name: String) -> anyhow::Result> { - let access_token = self - .authenticator - .token(&["https://www.googleapis.com/auth/cloud-platform"]) + pub async fn load_secret>(&self, name: T) -> anyhow::Result> { + let (_, response) = self + .secret_manager + .projects() + .secrets_versions_access(name.as_ref()) + .doit() .await?; - let token = access_token - .token() - .ok_or_else(|| anyhow::anyhow!("GCP token did not have access_token field in it"))?; + let secret_payload = response + .payload + .ok_or_else(|| anyhow::anyhow!("secret value is missing payload"))?; - let tls_config = ClientTlsConfig::new() - .ca_certificate(Certificate::from_pem(TLS_CERTS)) - .domain_name(DOMAIN_NAME); + Ok(secret_payload + .data + .ok_or_else(|| anyhow::anyhow!("secret value payload is missing data"))?) + } - let channel = Channel::from_static(ENDPOINT) - .tls_config(tls_config)? - .connect() + pub async fn get( + &self, + name_key: K, + ) -> anyhow::Result { + let request = LookupRequest { + keys: Some(vec![Key { + path: Some(vec![PathElement { + kind: Some(T::kind()), + name: Some(name_key.to_string()), + id: None, + }]), + partition_id: None, + }]), + read_options: None, + database_id: Some("".to_string()), + }; + let (_, response) = self + .datastore + .projects() + .lookup(request, &self.project_id) + .doit() + .await?; + let found_entity = response + .found + .and_then(|mut results| results.pop()) + .and_then(|result| result.entity) + .ok_or_else(|| anyhow::anyhow!("not found"))?; + from_datastore_entity(found_entity) + } + + pub async fn insert(&self, value: T) -> anyhow::Result<()> { + let name = value.name(); + let mut entity = to_datastore_value(value)? + .entity_value + .ok_or_else(|| anyhow::anyhow!("failed to convert value to Datastore entity"))?; + entity.key = Some(Key { + partition_id: None, + path: Some(vec![PathElement { + id: None, + kind: Some(T::kind()), + name: Some(name), + }]), + }); + + let request = CommitRequest { + database_id: Some("".to_string()), + mode: Some(String::from("NON_TRANSACTIONAL")), + mutations: Some(vec![Mutation { + insert: Some(entity), + delete: None, + update: None, + base_version: None, + upsert: None, + update_time: None, + }]), + single_use_transaction: None, + transaction: None, + }; + self.datastore + .projects() + .commit(request, &self.project_id) + .doit() .await?; - let mut client = - SecretManagerServiceClient::with_interceptor(channel, move |mut req: Request<()>| { - req.metadata_mut().insert( - "authorization", - format!("Bearer {}", token) - .parse() - .map_err(|_| Status::unauthenticated("failed to parse access token"))?, - ); - Ok(req) - }); - let request = Request::new(AccessSecretVersionRequest { name }); - let response = client.access_secret_version(request).await?; - let secret_payload = response.into_inner().payload.ok_or_else(|| { - anyhow::anyhow!("failed to fetch secret share from GCP Secret Manager") - })?; - Ok(secret_payload.data) + Ok(()) } } diff --git a/mpc-recovery/src/key_recovery.rs b/mpc-recovery/src/key_recovery.rs index 77d639e60..7d23e4f98 100644 --- a/mpc-recovery/src/key_recovery.rs +++ b/mpc-recovery/src/key_recovery.rs @@ -1,14 +1,32 @@ -use crate::primitives::InternalAccountId; -use near_crypto::{PublicKey, SecretKey}; - -pub fn get_user_recovery_pk(_id: InternalAccountId) -> PublicKey { - // TODO: use key derivation or other techniques to generate a key - "ed25519:3BUQYE4ZfQ6A94CqCtAbdLURxo4eHv2L8JjC2KiXXdFn" - .parse() - .unwrap() +use crate::{gcp::DatastoreEntity, primitives::InternalAccountId}; +use near_crypto::{KeyType, PublicKey, SecretKey}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone)] +pub struct UserSecretKey { + pub internal_account_id: InternalAccountId, + pub secret_key: SecretKey, } -pub fn get_user_recovery_sk(_id: InternalAccountId) -> SecretKey { - // TODO: use key derivation or other techniques to generate a key - "ed25519:5pFJN3czPAHFWHZYjD4oTtnJE7PshLMeTkSU7CmWkvLaQWchCLgXGF1wwcJmh2AQChGH85EwcL5VW7tUavcAZDSG".parse().unwrap() +impl DatastoreEntity for UserSecretKey { + fn kind() -> String { + "UserSecretKey".to_string() + } + + fn name(&self) -> String { + self.internal_account_id.clone() + } +} + +impl UserSecretKey { + pub fn random(internal_account_id: InternalAccountId) -> Self { + Self { + internal_account_id, + secret_key: SecretKey::from_random(KeyType::ED25519), + } + } + + pub fn public_key(&self) -> PublicKey { + self.secret_key.public_key() + } } diff --git a/mpc-recovery/src/leader_node/mod.rs b/mpc-recovery/src/leader_node/mod.rs index 5b2d10bc3..58403706a 100644 --- a/mpc-recovery/src/leader_node/mod.rs +++ b/mpc-recovery/src/leader_node/mod.rs @@ -1,4 +1,5 @@ -use crate::key_recovery::{get_user_recovery_pk, get_user_recovery_sk}; +use crate::gcp::GcpService; +use crate::key_recovery::UserSecretKey; use crate::msg::{ AddKeyRequest, AddKeyResponse, LeaderRequest, LeaderResponse, NewAccountRequest, NewAccountResponse, SigShareRequest, SigShareResponse, @@ -26,6 +27,7 @@ use std::net::SocketAddr; use threshold_crypto::{PublicKeySet, SecretKeyShare}; pub struct Config { + pub gcp_service: GcpService, pub id: NodeId, pub pk_set: PublicKeySet, pub sk_share: SecretKeyShare, @@ -41,6 +43,7 @@ pub struct Config { pub async fn run(config: Config) { let Config { + gcp_service, id, pk_set, sk_share, @@ -83,6 +86,7 @@ pub async fn run(config: Config) { sk_share, sign_nodes, client, + gcp_service, near_root_account: near_root_account.parse().unwrap(), account_creator_id, account_creator_sk, @@ -113,6 +117,7 @@ struct LeaderState { sk_share: SecretKeyShare, sign_nodes: Vec, client: NearRpcAndRelayerClient, + gcp_service: GcpService, near_root_account: AccountId, account_creator_id: AccountId, // TODO: temporary solution @@ -153,11 +158,15 @@ async fn process_new_account( ) .await?; + let user_secret_key = UserSecretKey::random(internal_acc_id.clone()); + let public_key = user_secret_key.public_key(); + state.gcp_service.insert(user_secret_key).await?; + let delegate_action = get_create_account_delegate_action( state.account_creator_id.clone(), state.account_creator_sk.public_key(), new_user_account_id.clone(), - get_user_recovery_pk(internal_acc_id.clone()), + public_key, new_user_account_pk.clone(), state.near_root_account.clone(), nonce, @@ -247,15 +256,13 @@ async fn process_add_key( ) -> anyhow::Result<(StatusCode, Json)> { let user_account_id: AccountId = request.near_account_id.parse()?; let new_public_key: PublicKey = request.public_key.parse()?; + let user_secret_key: UserSecretKey = state.gcp_service.get(internal_acc_id.clone()).await?; nar::retry(|| async { // Get nonce and recent block hash let (_hash, block_height, nonce) = match state .client - .access_key( - user_account_id.clone(), - get_user_recovery_pk(internal_acc_id.clone()), - ) + .access_key(user_account_id.clone(), user_secret_key.public_key()) .await { Ok(val) => val, @@ -282,7 +289,7 @@ async fn process_add_key( let max_block_height: u64 = block_height + 100; let delegate_action = get_add_key_delegate_action( user_account_id.clone(), - get_user_recovery_pk(internal_acc_id.clone()), + user_secret_key.public_key(), new_public_key.clone(), nonce, max_block_height, @@ -290,7 +297,7 @@ async fn process_add_key( let signed_delegate_action = get_signed_delegated_action( delegate_action, user_account_id.clone(), - get_user_recovery_sk(internal_acc_id.clone()), + user_secret_key.secret_key.clone(), ); let result = state.client.send_meta_tx(signed_delegate_action).await; diff --git a/mpc-recovery/src/lib.rs b/mpc-recovery/src/lib.rs index bd25ae505..c2ba37220 100644 --- a/mpc-recovery/src/lib.rs +++ b/mpc-recovery/src/lib.rs @@ -1,5 +1,6 @@ use threshold_crypto::{PublicKeySet, SecretKeySet, SecretKeyShare}; +pub mod gcp; mod key_recovery; mod leader_node; pub mod msg; diff --git a/mpc-recovery/src/main.rs b/mpc-recovery/src/main.rs index 76287d69a..41eb0f016 100644 --- a/mpc-recovery/src/main.rs +++ b/mpc-recovery/src/main.rs @@ -1,11 +1,8 @@ use clap::Parser; -use gcp::GcpService; -use mpc_recovery::LeaderConfig; +use mpc_recovery::{gcp::GcpService, LeaderConfig}; use near_primitives::types::AccountId; use threshold_crypto::{serde_impl::SerdeSecret, PublicKeySet, SecretKeyShare}; -mod gcp; - #[derive(Parser, Debug)] enum Cli { Generate { @@ -51,6 +48,9 @@ enum Cli { /// TEMPORARY - Account creator ed25519 secret key #[arg(long, env("MPC_RECOVERY_ACCOUNT_CREATOR_SK"))] account_creator_sk: Option, + /// GCP project ID + #[arg(long, env("MPC_RECOVERY_GCP_PROJECT_ID"))] + gcp_project_id: String, }, StartSign { /// Node ID @@ -65,6 +65,9 @@ enum Cli { /// The web port for this server #[arg(long, env("MPC_RECOVERY_WEB_PORT"))] web_port: u16, + /// GCP project ID + #[arg(long, env("MPC_RECOVERY_GCP_PROJECT_ID"))] + gcp_project_id: String, }, } @@ -129,8 +132,9 @@ async fn main() -> anyhow::Result<()> { near_root_account, account_creator_id, account_creator_sk, + gcp_project_id, } => { - let gcp_service = GcpService::new().await?; + let gcp_service = GcpService::new(gcp_project_id).await?; let sk_share = load_sh_skare(&gcp_service, node_id, sk_share).await?; let account_creator_sk = load_account_creator_sk(&gcp_service, node_id, account_creator_sk).await?; @@ -140,6 +144,7 @@ async fn main() -> anyhow::Result<()> { let account_creator_sk = account_creator_sk.parse()?; mpc_recovery::run_leader_node(LeaderConfig { + gcp_service, id: node_id, pk_set, sk_share, @@ -159,8 +164,9 @@ async fn main() -> anyhow::Result<()> { pk_set, sk_share, web_port, + gcp_project_id, } => { - let gcp_service = GcpService::new().await?; + let gcp_service = GcpService::new(gcp_project_id).await?; let sk_share = load_sh_skare(&gcp_service, node_id, sk_share).await?; let pk_set: PublicKeySet = serde_json::from_str(&pk_set).unwrap(); From f064cf2397e4b03d058bbee2d878f749f4be2c06 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Sun, 23 Apr 2023 13:54:58 +1000 Subject: [PATCH 2/6] simplify value serialization --- mpc-recovery/src/gcp/error.rs | 7 + mpc-recovery/src/gcp/mod.rs | 225 +--------------- mpc-recovery/src/gcp/value.rs | 387 ++++++++++++++++++++++++++++ mpc-recovery/src/key_recovery.rs | 68 ++++- mpc-recovery/src/leader_node/mod.rs | 1 + 5 files changed, 472 insertions(+), 216 deletions(-) create mode 100644 mpc-recovery/src/gcp/error.rs create mode 100644 mpc-recovery/src/gcp/value.rs diff --git a/mpc-recovery/src/gcp/error.rs b/mpc-recovery/src/gcp/error.rs new file mode 100644 index 000000000..de62be199 --- /dev/null +++ b/mpc-recovery/src/gcp/error.rs @@ -0,0 +1,7 @@ +#[derive(Debug, thiserror::Error)] +pub enum ConvertError { + #[error("expected property `{0}` was missing")] + MissingProperty(String), + #[error("expected property type `{expected}`, got `{got}`")] + UnexpectedPropertyType { expected: String, got: String }, +} diff --git a/mpc-recovery/src/gcp/mod.rs b/mpc-recovery/src/gcp/mod.rs index b5edb0bec..ba703fc0b 100644 --- a/mpc-recovery/src/gcp/mod.rs +++ b/mpc-recovery/src/gcp/mod.rs @@ -1,6 +1,8 @@ -use google_datastore1::api::{ - ArrayValue, CommitRequest, Entity, Key, LookupRequest, Mutation, PathElement, Value, -}; +pub mod error; +pub mod value; + +use self::value::{FromValue, IntoValue}; +use google_datastore1::api::{CommitRequest, Entity, Key, LookupRequest, Mutation, PathElement}; use google_datastore1::Datastore; use google_secretmanager1::oauth2::authenticator::ApplicationDefaultCredentialsTypes; use google_secretmanager1::oauth2::{ @@ -9,197 +11,6 @@ use google_secretmanager1::oauth2::{ use google_secretmanager1::SecretManager; use hyper::client::HttpConnector; use hyper_rustls::HttpsConnector; -use serde::de::DeserializeOwned; -use serde::Serialize; -use std::collections::HashMap; - -pub trait DatastoreEntity { - fn kind() -> String; - fn name(&self) -> String; -} - -pub fn to_datastore_value(x: T) -> anyhow::Result { - match serde_json::to_value(x)? { - serde_json::Value::Null => Ok(Value { - null_value: Some(String::from("NULL_VALUE")), - ..Value::default() - }), - serde_json::Value::Bool(x) => Ok(Value { - boolean_value: Some(x), - ..Value::default() - }), - serde_json::Value::Number(x) => { - if x.is_f64() { - Ok(Value { - double_value: Some( - x.as_f64() - .ok_or_else(|| anyhow::anyhow!("non-serializable f64"))?, - ), - ..Value::default() - }) - } else if x.is_i64() { - Ok(Value { - integer_value: Some( - x.as_i64() - .ok_or_else(|| anyhow::anyhow!("non-serializable i64"))?, - ), - ..Value::default() - }) - } else if x.is_u64() { - Ok(Value { - integer_value: Some( - x.as_i64() - .ok_or_else(|| anyhow::anyhow!("non-serializable u64"))?, - ), - ..Value::default() - }) - } else { - anyhow::bail!("unsupported type"); - } - } - serde_json::Value::String(x) => Ok(Value { - string_value: Some(x), - ..Value::default() - }), - serde_json::Value::Array(xs) => { - let mut any_error = None; - let xs = xs - .into_iter() - .filter_map(|x| { - let result = to_datastore_value(x); - match result { - Ok(result) => Some(result), - Err(e) => { - any_error = Some(e); - None - } - } - }) - .collect::>(); - if let Some(e) = any_error { - Err(e) - } else { - Ok(Value { - array_value: Some(ArrayValue { values: Some(xs) }), - ..Value::default() - }) - } - } - serde_json::Value::Object(xs) => { - let mut any_error = None; - let xs = xs - .into_iter() - .filter_map(|(k, v)| { - let v = to_datastore_value(v); - match v { - Ok(v) => Some((k, v)), - Err(e) => { - any_error = Some(e); - None - } - } - }) - .collect::>(); - if let Some(e) = any_error { - Err(e) - } else { - Ok(Value { - entity_value: Some(Entity { - properties: Some(xs), - key: None, - }), - ..Value::default() - }) - } - } - } -} - -fn from_datastore_value(value: Value) -> anyhow::Result { - let serde_value: serde_json::Value; - if let Some(xs) = value.entity_value { - let mut any_error = None; - let xs = xs - .properties - .unwrap_or(HashMap::default()) - .into_iter() - .filter_map(|(k, v)| { - let result = from_datastore_value(v); - match result { - Ok(v) => Some((k, v)), - Err(e) => { - any_error = Some(e); - None - } - } - }) - .collect::>(); - if let Some(e) = any_error { - return Err(e); - } else { - serde_value = serde_json::Value::Object(xs); - } - } else if let Some(xs) = value.timestamp_value { - serde_value = serde_json::Value::String(xs.to_string()); - } else if let Some(_) = value.geo_point_value { - anyhow::bail!("unimplemented"); - } else if let Some(_) = value.blob_value { - anyhow::bail!("unimplemented"); - } else if let Some(xs) = value.double_value { - serde_value = serde_json::Value::Number( - serde_json::Number::from_f64(xs) - .ok_or_else(|| anyhow::anyhow!("invalid f64 values: {xs}"))?, - ); - } else if let Some(_) = value.meaning { - anyhow::bail!("unimplemented"); - } else if let Some(_) = value.exclude_from_indexes { - anyhow::bail!("unimplemented"); - } else if let Some(xs) = value.string_value { - serde_value = serde_json::Value::String(xs); - } else if let Some(_) = value.key_value { - anyhow::bail!("unimplemented"); - } else if let Some(xs) = value.boolean_value { - serde_value = serde_json::Value::Bool(xs); - } else if let Some(xs) = value.array_value { - let mut any_error = None; - let xs = xs - .values - .unwrap_or(Vec::default()) - .into_iter() - .filter_map(|x| { - let result = from_datastore_value(x); - match result { - Ok(result) => Some(result), - Err(e) => { - any_error = Some(e); - None - } - } - }) - .collect::>(); - if let Some(e) = any_error { - return Err(e); - } else { - serde_value = serde_json::Value::Array(xs); - } - } else if let Some(xs) = value.integer_value { - serde_value = serde_json::Value::Number(serde_json::Number::from(xs)); - } else if let Some(_) = value.null_value { - serde_value = serde_json::Value::Null; - } else { - anyhow::bail!("unimplemented"); - } - - Ok(serde_json::from_value(serde_value)?) -} - -fn from_datastore_entity(value: Entity) -> anyhow::Result { - let value = Value { - entity_value: Some(value), - ..Default::default() - }; - from_datastore_value(value) -} #[derive(Clone)] pub struct GcpService { @@ -208,6 +19,10 @@ pub struct GcpService { secret_manager: SecretManager>, } +pub trait KeyKind { + fn kind() -> String; +} + impl GcpService { pub async fn new(project_id: String) -> anyhow::Result { let opts = ApplicationDefaultCredentialsFlowOpts::default(); @@ -249,10 +64,7 @@ impl GcpService { .ok_or_else(|| anyhow::anyhow!("secret value payload is missing data"))?) } - pub async fn get( - &self, - name_key: K, - ) -> anyhow::Result { + pub async fn get(&self, name_key: K) -> anyhow::Result { let request = LookupRequest { keys: Some(vec![Key { path: Some(vec![PathElement { @@ -276,22 +88,11 @@ impl GcpService { .and_then(|mut results| results.pop()) .and_then(|result| result.entity) .ok_or_else(|| anyhow::anyhow!("not found"))?; - from_datastore_entity(found_entity) + Ok(T::from_value(found_entity.into_value())?) } - pub async fn insert(&self, value: T) -> anyhow::Result<()> { - let name = value.name(); - let mut entity = to_datastore_value(value)? - .entity_value - .ok_or_else(|| anyhow::anyhow!("failed to convert value to Datastore entity"))?; - entity.key = Some(Key { - partition_id: None, - path: Some(vec![PathElement { - id: None, - kind: Some(T::kind()), - name: Some(name), - }]), - }); + pub async fn insert(&self, value: T) -> anyhow::Result<()> { + let entity = Entity::from_value(value.into_value())?; let request = CommitRequest { database_id: Some("".to_string()), diff --git a/mpc-recovery/src/gcp/value.rs b/mpc-recovery/src/gcp/value.rs new file mode 100644 index 000000000..c3d9c787b --- /dev/null +++ b/mpc-recovery/src/gcp/value.rs @@ -0,0 +1,387 @@ +use google_datastore1::api::{ArrayValue, Entity, Key, LatLng}; +use std::collections::HashMap; + +use super::error::ConvertError; + +#[derive(Debug, Clone)] +pub enum Value { + BooleanValue(bool), + IntegerValue(i64), + DoubleValue(f64), + KeyValue(Key), + StringValue(String), + BlobValue(Vec), + GeoPointValue(f64, f64), + EntityValue { + key: Key, + properties: HashMap, + }, + ArrayValue(Vec), +} + +impl Value { + pub fn type_name(&self) -> &'static str { + match self { + Value::BooleanValue(_) => "bool", + Value::IntegerValue(_) => "integer", + Value::DoubleValue(_) => "double", + Value::KeyValue(_) => "key", + Value::StringValue(_) => "string", + Value::BlobValue(_) => "blob", + Value::GeoPointValue(_, _) => "geopoint", + Value::EntityValue { .. } => "entity", + Value::ArrayValue(_) => "array", + } + } +} + +pub trait IntoValue { + fn into_value(self) -> Value; +} + +pub trait FromValue: Sized { + fn from_value(value: Value) -> Result; +} + +/* + * IntoValue implementations + */ + +impl IntoValue for Value { + fn into_value(self) -> Value { + self + } +} + +impl IntoValue for String { + fn into_value(self) -> Value { + Value::StringValue(self) + } +} + +impl IntoValue for &str { + fn into_value(self) -> Value { + String::from(self).into_value() + } +} + +impl IntoValue for i8 { + fn into_value(self) -> Value { + Value::IntegerValue(self as i64) + } +} + +impl IntoValue for i16 { + fn into_value(self) -> Value { + Value::IntegerValue(self as i64) + } +} + +impl IntoValue for i32 { + fn into_value(self) -> Value { + Value::IntegerValue(self as i64) + } +} + +impl IntoValue for i64 { + fn into_value(self) -> Value { + Value::IntegerValue(self) + } +} + +impl IntoValue for f32 { + fn into_value(self) -> Value { + Value::DoubleValue(self as f64) + } +} + +impl IntoValue for f64 { + fn into_value(self) -> Value { + Value::DoubleValue(self) + } +} + +impl IntoValue for bool { + fn into_value(self) -> Value { + Value::BooleanValue(self) + } +} + +impl IntoValue for Key { + fn into_value(self) -> Value { + Value::KeyValue(self) + } +} + +#[cfg(feature = "bytes")] +impl IntoValue for Bytes { + fn into_value(self) -> Value { + Value::BlobValue(self.to_vec()) + } +} + +impl IntoValue for Vec +where + T: IntoValue, +{ + fn into_value(self) -> Value { + Value::ArrayValue(self.into_iter().map(IntoValue::into_value).collect()) + } +} + +impl From for Value { + fn from(value: google_datastore1::api::Value) -> Value { + if let Some(val) = value.boolean_value { + Value::BooleanValue(val) + } else if let Some(val) = value.integer_value { + Value::IntegerValue(val) + } else if let Some(val) = value.double_value { + Value::DoubleValue(val) + } else if let Some(_) = value.timestamp_value { + unimplemented!() + } else if let Some(val) = value.key_value { + Value::KeyValue(val) + } else if let Some(val) = value.string_value { + Value::StringValue(val) + } else if let Some(val) = value.blob_value { + Value::BlobValue(val) + } else if let Some(val) = value.geo_point_value { + Value::GeoPointValue( + val.latitude.unwrap_or_default(), + val.longitude.unwrap_or_default(), + ) + } else if let Some(val) = value.entity_value { + Value::EntityValue { + key: val.key.unwrap_or_default(), + properties: val + .properties + .unwrap_or_default() + .into_iter() + .map(|(k, v)| (k, Value::from(v))) + .collect(), + } + } else if let Some(val) = value.array_value { + Value::ArrayValue( + val.values + .unwrap_or_default() + .into_iter() + .map(|e| Value::from(e)) + .collect(), + ) + } else { + unimplemented!() + } + } +} + +impl IntoValue for google_datastore1::api::Value { + fn into_value(self) -> Value { + self.into() + } +} + +impl IntoValue for google_datastore1::api::Entity { + fn into_value(self) -> Value { + Value::EntityValue { + key: self.key.unwrap_or_default(), + properties: self + .properties + .unwrap_or_default() + .into_iter() + .map(|(k, v)| (k, v.into_value())) + .collect(), + } + } +} + +/* + * FromValue implementations + */ + +impl FromValue for Value { + fn from_value(value: Value) -> Result { + Ok(value) + } +} + +impl FromValue for String { + fn from_value(value: Value) -> Result { + match value { + Value::StringValue(value) => Ok(value), + _ => Err(ConvertError::UnexpectedPropertyType { + expected: String::from("string"), + got: String::from(value.type_name()), + }), + } + } +} + +impl FromValue for i64 { + fn from_value(value: Value) -> Result { + match value { + Value::IntegerValue(value) => Ok(value), + _ => Err(ConvertError::UnexpectedPropertyType { + expected: String::from("integer"), + got: String::from(value.type_name()), + }), + } + } +} + +impl FromValue for f64 { + fn from_value(value: Value) -> Result { + match value { + Value::DoubleValue(value) => Ok(value), + _ => Err(ConvertError::UnexpectedPropertyType { + expected: String::from("double"), + got: String::from(value.type_name()), + }), + } + } +} + +impl FromValue for bool { + fn from_value(value: Value) -> Result { + match value { + Value::BooleanValue(value) => Ok(value), + _ => Err(ConvertError::UnexpectedPropertyType { + expected: String::from("bool"), + got: String::from(value.type_name()), + }), + } + } +} + +impl FromValue for Key { + fn from_value(value: Value) -> Result { + match value { + Value::KeyValue(value) => Ok(value), + _ => Err(ConvertError::UnexpectedPropertyType { + expected: String::from("key"), + got: String::from(value.type_name()), + }), + } + } +} + +#[cfg(feature = "bytes")] +impl FromValue for Bytes { + fn from_value(value: Value) -> Result { + match value { + Value::BlobValue(value) => Ok(Bytes::from(value)), + _ => Err(ConvertError::UnexpectedPropertyType { + expected: String::from("blob"), + got: String::from(value.type_name()), + }), + } + } +} + +impl FromValue for Vec +where + T: FromValue, +{ + fn from_value(value: Value) -> Result, ConvertError> { + match value { + Value::ArrayValue(values) => { + let values = values + .into_iter() + .map(FromValue::from_value) + .collect::, ConvertError>>()?; + Ok(values) + } + _ => Err(ConvertError::UnexpectedPropertyType { + expected: String::from("array"), + got: String::from(value.type_name()), + }), + } + } +} + +impl FromValue for Entity { + fn from_value(value: Value) -> Result { + match value { + Value::EntityValue { key, properties } => { + let properties = properties + .into_iter() + .map(|(k, v)| { + let v = FromValue::from_value(v)?; + Ok((k, v)) + }) + .collect::, ConvertError>>()?; + Ok(Entity { + key: Some(key), + properties: Some(properties), + }) + } + _ => Err(ConvertError::UnexpectedPropertyType { + expected: String::from("entity"), + got: String::from(value.type_name()), + }), + } + } +} + +impl FromValue for google_datastore1::api::Value { + fn from_value(value: Value) -> Result { + let result = match value { + Value::BooleanValue(val) => google_datastore1::api::Value { + boolean_value: Some(val), + ..Default::default() + }, + Value::IntegerValue(val) => google_datastore1::api::Value { + integer_value: Some(val), + ..Default::default() + }, + Value::DoubleValue(val) => google_datastore1::api::Value { + double_value: Some(val), + ..Default::default() + }, + Value::KeyValue(val) => google_datastore1::api::Value { + key_value: Some(val), + ..Default::default() + }, + Value::StringValue(val) => google_datastore1::api::Value { + string_value: Some(val), + ..Default::default() + }, + Value::BlobValue(val) => google_datastore1::api::Value { + blob_value: Some(val), + ..Default::default() + }, + Value::GeoPointValue(latitude, longitude) => google_datastore1::api::Value { + geo_point_value: Some(LatLng { + latitude: Some(latitude), + longitude: Some(longitude), + }), + ..Default::default() + }, + Value::EntityValue { key, properties } => { + let properties = properties + .into_iter() + .map(|(k, v)| FromValue::from_value(v).map(|v| (k, v))) + .collect::, ConvertError>>()?; + google_datastore1::api::Value { + entity_value: Some(Entity { + key: Some(key), + properties: Some(properties), + }), + ..Default::default() + } + } + Value::ArrayValue(val) => { + let values = val + .into_iter() + .map(|v| FromValue::from_value(v)) + .collect::, ConvertError>>()?; + google_datastore1::api::Value { + array_value: Some(ArrayValue { + values: Some(values), + }), + ..Default::default() + } + } + }; + Ok(result) + } +} diff --git a/mpc-recovery/src/key_recovery.rs b/mpc-recovery/src/key_recovery.rs index 7d23e4f98..23fd0c802 100644 --- a/mpc-recovery/src/key_recovery.rs +++ b/mpc-recovery/src/key_recovery.rs @@ -1,6 +1,15 @@ -use crate::{gcp::DatastoreEntity, primitives::InternalAccountId}; +use crate::{ + gcp::{ + error::ConvertError, + value::{FromValue, IntoValue, Value}, + KeyKind, + }, + primitives::InternalAccountId, +}; +use google_datastore1::api::{Key, PathElement}; use near_crypto::{KeyType, PublicKey, SecretKey}; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; #[derive(Serialize, Deserialize, Clone)] pub struct UserSecretKey { @@ -8,13 +17,64 @@ pub struct UserSecretKey { pub secret_key: SecretKey, } -impl DatastoreEntity for UserSecretKey { +impl KeyKind for UserSecretKey { fn kind() -> String { "UserSecretKey".to_string() } +} + +impl IntoValue for UserSecretKey { + fn into_value(self) -> Value { + let mut properties = HashMap::new(); + properties.insert( + "internal_account_id".to_string(), + Value::StringValue(self.internal_account_id.clone()), + ); + properties.insert( + "secret_key".to_string(), + Value::StringValue(self.secret_key.to_string()), + ); + Value::EntityValue { + key: Key { + path: Some(vec![PathElement { + kind: Some(UserSecretKey::kind()), + name: Some(self.internal_account_id), + id: None, + }]), + partition_id: None, + }, + properties, + } + } +} + +impl FromValue for UserSecretKey { + fn from_value(value: Value) -> Result { + match value { + Value::EntityValue { mut properties, .. } => { + let (_, internal_account_id) = properties + .remove_entry("internal_account_id") + .ok_or_else(|| { + ConvertError::MissingProperty("internal_account_id".to_string()) + })?; + let internal_account_id = String::from_value(internal_account_id)?; - fn name(&self) -> String { - self.internal_account_id.clone() + let (_, secret_key) = properties + .remove_entry("secret_key") + .ok_or_else(|| ConvertError::MissingProperty("secret_key".to_string()))?; + let secret_key = String::from_value(secret_key)?; + let secret_key = secret_key.parse().unwrap(); + + Ok(UserSecretKey { + internal_account_id, + secret_key, + }) + } + value => Err(ConvertError::UnexpectedPropertyType { + expected: "entity".to_string(), + got: format!("{:?}", value), + }), + } } } diff --git a/mpc-recovery/src/leader_node/mod.rs b/mpc-recovery/src/leader_node/mod.rs index 9a77898db..436109ba2 100644 --- a/mpc-recovery/src/leader_node/mod.rs +++ b/mpc-recovery/src/leader_node/mod.rs @@ -144,6 +144,7 @@ enum NewAccountError { #[error("{0}")] Other(#[from] anyhow::Error), } + async fn process_new_account( state: LeaderState, request: NewAccountRequest, From 1f59aebb8853e6b1424058d227c229b86e6bd14b Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Sun, 23 Apr 2023 16:27:58 +1000 Subject: [PATCH 3/6] mock datastore with emulator in integration tests --- integration-tests/Cargo.toml | 4 + integration-tests/tests/docker/datastore.rs | 108 ++++++++++++++++++++ integration-tests/tests/docker/mod.rs | 88 ++++++++-------- integration-tests/tests/docker/redis.rs | 27 +---- integration-tests/tests/docker/relayer.rs | 27 +---- integration-tests/tests/lib.rs | 7 +- mpc-recovery/src/gcp/mod.rs | 51 ++++++--- mpc-recovery/src/main.rs | 12 ++- 8 files changed, 217 insertions(+), 107 deletions(-) create mode 100644 integration-tests/tests/docker/datastore.rs diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 82bad87e7..455249b03 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -21,3 +21,7 @@ serde_json = "1" threshold_crypto = "0.4.0" tokio = { version = "1.0", features = ["full"] } workspaces = { git = "https://github.com/near/workspaces-rs", branch = "main" } + +[features] +drop-containers = [] +default = ["drop-containers"] diff --git a/integration-tests/tests/docker/datastore.rs b/integration-tests/tests/docker/datastore.rs new file mode 100644 index 000000000..2a76ec7de --- /dev/null +++ b/integration-tests/tests/docker/datastore.rs @@ -0,0 +1,108 @@ +use std::collections::HashMap; + +use crate::drop_container; +use bollard::{ + container::{Config, RemoveContainerOptions}, + image::CreateImageOptions, + service::{HostConfig, PortBinding}, + Docker, +}; +use futures::TryStreamExt; + +pub struct Datastore { + docker: Docker, + container_id: String, + pub address: String, +} + +impl Datastore { + pub async fn start(docker: &Docker, network: &str, project_id: &str) -> anyhow::Result { + super::create_network(docker, network).await?; + let web_port = portpicker::pick_unused_port().expect("no free ports"); + + let mut exposed_ports = HashMap::new(); + let mut port_bindings = HashMap::new(); + let empty = HashMap::<(), ()>::new(); + exposed_ports.insert(format!("{web_port}/tcp"), empty); + port_bindings.insert( + format!("{web_port}/tcp"), + Some(vec![PortBinding { + host_ip: None, + host_port: Some(web_port.to_string()), + }]), + ); + + docker + .create_image( + Some(CreateImageOptions { + from_image: "google/cloud-sdk:latest", + ..Default::default() + }), + None, + None, + ) + .try_collect::>() + .await?; + + let config = Config { + image: Some("google/cloud-sdk:latest".to_string()), + tty: Some(true), + attach_stdout: Some(true), + attach_stderr: Some(true), + exposed_ports: Some(exposed_ports), + cmd: Some(vec![ + "gcloud".to_string(), + "beta".to_string(), + "emulators".to_string(), + "datastore".to_string(), + "start".to_string(), + format!("--project={project_id}"), + "--host-port".to_string(), + format!("0.0.0.0:{web_port}"), + "--no-store-on-disk".to_string(), + ]), + env: Some(vec![ + format!("DATASTORE_EMULATOR_HOST=0.0.0.0:{web_port}"), + format!("DATASTORE_PROJECT_ID={project_id}"), + ]), + host_config: Some(HostConfig { + network_mode: Some(network.to_string()), + port_bindings: Some(port_bindings), + ..Default::default() + }), + ..Default::default() + }; + + let container_id = docker + .create_container::<&str, String>(None, config) + .await? + .id; + + super::continuously_print_docker_output(docker, &container_id).await?; + docker + .start_container::(&container_id, None) + .await?; + + let network_settings = docker + .inspect_container(&container_id, None) + .await? + .network_settings + .unwrap(); + let ip_address = network_settings + .networks + .unwrap() + .get(network) + .cloned() + .unwrap() + .ip_address + .unwrap(); + + Ok(Self { + docker: docker.clone(), + container_id, + address: format!("http://{ip_address}:{web_port}/"), + }) + } +} + +drop_container!(Datastore); diff --git a/integration-tests/tests/docker/mod.rs b/integration-tests/tests/docker/mod.rs index 95cefbe52..cb51fa600 100644 --- a/integration-tests/tests/docker/mod.rs +++ b/integration-tests/tests/docker/mod.rs @@ -1,3 +1,7 @@ +pub mod datastore; +pub mod redis; +pub mod relayer; + use bollard::{ container::{AttachContainerOptions, AttachContainerResults, Config, RemoveContainerOptions}, network::CreateNetworkOptions, @@ -18,11 +22,36 @@ use threshold_crypto::{serde_impl::SerdeSecret, PublicKeySet, SecretKeyShare}; use tokio::io::AsyncWriteExt; use workspaces::AccountId; -pub mod redis; -pub mod relayer; - static NETWORK_MUTEX: Lazy> = Lazy::new(|| Mutex::new(0)); +// Removing container is an asynchronous operation and hence has to be scheduled to execute +// outside of `drop`'s scope. This leads to problems when the drop happens right before the +// execution ends. The invoker needs to be aware of this behavior and give `drop` some time +// to finalize. +#[macro_export] +macro_rules! drop_container { + ( $container:ident ) => { + #[cfg(feature = "drop-containers")] + impl Drop for $container { + fn drop(&mut self) { + let container_id = self.container_id.clone(); + let docker = self.docker.clone(); + tokio::spawn(async move { + docker + .remove_container( + &container_id, + Some(RemoveContainerOptions { + force: true, + ..Default::default() + }), + ) + .await + }); + } + } + }; +} + async fn continuously_print_docker_output(docker: &Docker, id: &str) -> anyhow::Result<()> { let AttachContainerResults { mut output, .. } = docker .attach_container( @@ -158,6 +187,8 @@ impl LeaderNode { sign_nodes: Vec, near_rpc: &str, relayer_url: &str, + datastore_url: &str, + gcp_project_id: &str, near_root_account: &AccountId, account_creator_id: &AccountId, account_creator_sk: &SecretKey, @@ -185,6 +216,10 @@ impl LeaderNode { account_creator_id.to_string(), "--account-creator-sk".to_string(), account_creator_sk.to_string(), + "--gcp-project-id".to_string(), + gcp_project_id.to_string(), + "--gcp-datastore-url".to_string(), + datastore_url.to_string(), ]; for sign_node in sign_nodes { cmd.push("--sign-nodes".to_string()); @@ -249,28 +284,6 @@ impl LeaderNode { } } -// Removing container is an asynchronous operation and hence has to be scheduled to execute -// outside of `drop`'s scope. This leads to problems when the drop happens right before the -// execution ends. The invoker needs to be aware of this behavior and give `drop` some time -// to finalize. -impl Drop for LeaderNode { - fn drop(&mut self) { - let container_id = self.container_id.clone(); - let docker = self.docker.clone(); - tokio::spawn(async move { - docker - .remove_container( - &container_id, - Some(RemoveContainerOptions { - force: true, - ..Default::default() - }), - ) - .await - }); - } -} - pub struct SignNode { docker: Docker, container_id: String, @@ -298,6 +311,8 @@ impl SignNode { serde_json::to_string(&SerdeSecret(sk_share))?, "--web-port".to_string(), web_port.to_string(), + "--gcp-project-id".to_string(), + "pagoda-123".to_string(), ]; let (container_id, ip_address) = @@ -311,24 +326,5 @@ impl SignNode { } } -// Removing container is an asynchronous operation and hence has to be scheduled to execute -// outside of `drop`'s scope. This leads to problems when the drop happens right before the -// execution ends. The invoker needs to be aware of this behavior and give `drop` some time -// to finalize. -impl Drop for SignNode { - fn drop(&mut self) { - let container_id = self.container_id.clone(); - let docker = self.docker.clone(); - tokio::spawn(async move { - docker - .remove_container( - &container_id, - Some(RemoveContainerOptions { - force: true, - ..Default::default() - }), - ) - .await - }); - } -} +drop_container!(LeaderNode); +drop_container!(SignNode); diff --git a/integration-tests/tests/docker/redis.rs b/integration-tests/tests/docker/redis.rs index aa1fb5a6b..b67a8cabf 100644 --- a/integration-tests/tests/docker/redis.rs +++ b/integration-tests/tests/docker/redis.rs @@ -1,3 +1,4 @@ +use crate::drop_container; use bollard::{ container::{Config, RemoveContainerOptions}, image::CreateImageOptions, @@ -13,7 +14,7 @@ pub struct Redis { } impl Redis { - pub async fn start(docker: &Docker, network: &str) -> anyhow::Result { + pub async fn start(docker: &Docker, network: &str) -> anyhow::Result { super::create_network(docker, network).await?; docker .create_image( @@ -64,7 +65,7 @@ impl Redis { .ip_address .unwrap(); - Ok(Redis { + Ok(Self { docker: docker.clone(), container_id, hostname: ip_address, @@ -72,24 +73,4 @@ impl Redis { } } -// Removing container is an asynchronous operation and hence has to be scheduled to execute -// outside of `drop`'s scope. This leads to problems when the drop happens right before the -// execution ends. The invoker needs to be aware of this behavior and give `drop` some time -// to finalize. -impl Drop for Redis { - fn drop(&mut self) { - let container_id = self.container_id.clone(); - let docker = self.docker.clone(); - tokio::spawn(async move { - docker - .remove_container( - &container_id, - Some(RemoveContainerOptions { - force: true, - ..Default::default() - }), - ) - .await - }); - } -} +drop_container!(Redis); diff --git a/integration-tests/tests/docker/relayer.rs b/integration-tests/tests/docker/relayer.rs index 7146ceb17..d166f545c 100644 --- a/integration-tests/tests/docker/relayer.rs +++ b/integration-tests/tests/docker/relayer.rs @@ -1,3 +1,4 @@ +use crate::drop_container; use bollard::{ container::{Config, RemoveContainerOptions}, service::{HostConfig, PortBinding}, @@ -22,7 +23,7 @@ impl Relayer { relayer_account_id: &AccountId, relayer_account_sk: &SecretKey, creator_account_id: &AccountId, - ) -> anyhow::Result { + ) -> anyhow::Result { super::create_network(docker, network).await?; let web_port = portpicker::pick_unused_port().expect("no free ports"); @@ -88,7 +89,7 @@ impl Relayer { .ip_address .unwrap(); - Ok(Relayer { + Ok(Self { docker: docker.clone(), container_id, address: format!("http://{ip_address}:{web_port}"), @@ -96,24 +97,4 @@ impl Relayer { } } -// Removing container is an asynchronous operation and hence has to be scheduled to execute -// outside of `drop`'s scope. This leads to problems when the drop happens right before the -// execution ends. The invoker needs to be aware of this behavior and give `drop` some time -// to finalize. -impl Drop for Relayer { - fn drop(&mut self) { - let container_id = self.container_id.clone(); - let docker = self.docker.clone(); - tokio::spawn(async move { - docker - .remove_container( - &container_id, - Some(RemoveContainerOptions { - force: true, - ..Default::default() - }), - ) - .await - }); - } -} +drop_container!(Relayer); diff --git a/integration-tests/tests/lib.rs b/integration-tests/tests/lib.rs index 0041270b5..5e728e235 100644 --- a/integration-tests/tests/lib.rs +++ b/integration-tests/tests/lib.rs @@ -3,13 +3,14 @@ mod mpc; use crate::docker::{LeaderNode, SignNode}; use bollard::Docker; -use docker::{redis::Redis, relayer::Relayer}; +use docker::{datastore::Datastore, redis::Redis, relayer::Relayer}; use futures::future::BoxFuture; use std::time::Duration; use threshold_crypto::PublicKeySet; use workspaces::{network::Sandbox, AccountId, Worker}; const NETWORK: &str = "mpc_recovery_integration_test_network"; +const GCP_PROJECT_ID: &str = "mpc-recovery-gcp-project"; #[cfg(target_os = "linux")] const HOST_MACHINE_FROM_DOCKER: &str = "172.17.0.1"; #[cfg(target_os = "macos")] @@ -59,6 +60,7 @@ where let (creator_account_id, creator_account_sk) = create_account(&worker).await?; let near_rpc = format!("http://{HOST_MACHINE_FROM_DOCKER}:{}", worker.rpc_port()); + let datastore = Datastore::start(&docker, NETWORK, GCP_PROJECT_ID).await?; let redis = Redis::start(&docker, NETWORK).await?; let relayer = Relayer::start( &docker, @@ -85,6 +87,8 @@ where sign_nodes.iter().map(|n| n.address.clone()).collect(), &near_rpc, &relayer.address, + &datastore.address, + GCP_PROJECT_ID, near_root_account.id(), &creator_account_id, &creator_account_sk, @@ -101,6 +105,7 @@ where }) .await; + drop(datastore); drop(leader_node); drop(sign_nodes); drop(relayer); diff --git a/mpc-recovery/src/gcp/mod.rs b/mpc-recovery/src/gcp/mod.rs index ba703fc0b..37d14058b 100644 --- a/mpc-recovery/src/gcp/mod.rs +++ b/mpc-recovery/src/gcp/mod.rs @@ -3,6 +3,7 @@ pub mod value; use self::value::{FromValue, IntoValue}; use google_datastore1::api::{CommitRequest, Entity, Key, LookupRequest, Mutation, PathElement}; +use google_datastore1::oauth2::AccessTokenAuthenticator; use google_datastore1::Datastore; use google_secretmanager1::oauth2::authenticator::ApplicationDefaultCredentialsTypes; use google_secretmanager1::oauth2::{ @@ -24,12 +25,12 @@ pub trait KeyKind { } impl GcpService { - pub async fn new(project_id: String) -> anyhow::Result { - let opts = ApplicationDefaultCredentialsFlowOpts::default(); - let authenticator = match ApplicationDefaultCredentialsAuthenticator::builder(opts).await { - ApplicationDefaultCredentialsTypes::InstanceMetadata(auth) => auth.build().await?, - ApplicationDefaultCredentialsTypes::ServiceAccount(auth) => auth.build().await?, - }; + pub async fn new( + project_id: String, + gcp_datastore_url: Option, + ) -> anyhow::Result { + let mut datastore; + let secret_manager; let client = hyper::Client::builder().build( hyper_rustls::HttpsConnectorBuilder::new() .with_native_roots() @@ -38,8 +39,26 @@ impl GcpService { .enable_http2() .build(), ); - let secret_manager = SecretManager::new(client.clone(), authenticator.clone()); - let datastore = Datastore::new(client, authenticator); + if let Some(gcp_datastore_url) = gcp_datastore_url { + // Assuming custom GCP URL points to an emulator, so the token does not matter + let authenticator = AccessTokenAuthenticator::builder("TOKEN".to_string()) + .build() + .await?; + secret_manager = SecretManager::new(client.clone(), authenticator.clone()); + datastore = Datastore::new(client, authenticator); + datastore.base_url(gcp_datastore_url.clone()); + datastore.root_url(gcp_datastore_url); + } else { + let opts = ApplicationDefaultCredentialsFlowOpts::default(); + let authenticator = match ApplicationDefaultCredentialsAuthenticator::builder(opts) + .await + { + ApplicationDefaultCredentialsTypes::InstanceMetadata(auth) => auth.build().await?, + ApplicationDefaultCredentialsTypes::ServiceAccount(auth) => auth.build().await?, + }; + secret_manager = SecretManager::new(client.clone(), authenticator.clone()); + datastore = Datastore::new(client, authenticator); + } Ok(Self { project_id, @@ -48,6 +67,7 @@ impl GcpService { }) } + #[tracing::instrument(level = "debug", skip_all, fields(name = name.as_ref()))] pub async fn load_secret>(&self, name: T) -> anyhow::Result> { let (_, response) = self .secret_manager @@ -58,12 +78,15 @@ impl GcpService { let secret_payload = response .payload .ok_or_else(|| anyhow::anyhow!("secret value is missing payload"))?; - - Ok(secret_payload + let data = secret_payload .data - .ok_or_else(|| anyhow::anyhow!("secret value payload is missing data"))?) + .ok_or_else(|| anyhow::anyhow!("secret value payload is missing data"))?; + tracing::debug!("loaded secret successfully"); + + Ok(data) } + #[tracing::instrument(level = "debug", skip_all, fields(key = name_key.to_string()))] pub async fn get(&self, name_key: K) -> anyhow::Result { let request = LookupRequest { keys: Some(vec![Key { @@ -83,6 +106,7 @@ impl GcpService { .lookup(request, &self.project_id) .doit() .await?; + tracing::debug!(?response, "received response"); let found_entity = response .found .and_then(|mut results| results.pop()) @@ -91,6 +115,7 @@ impl GcpService { Ok(T::from_value(found_entity.into_value())?) } + #[tracing::instrument(level = "debug", skip_all)] pub async fn insert(&self, value: T) -> anyhow::Result<()> { let entity = Entity::from_value(value.into_value())?; @@ -108,11 +133,13 @@ impl GcpService { single_use_transaction: None, transaction: None, }; - self.datastore + let (_, response) = self + .datastore .projects() .commit(request, &self.project_id) .doit() .await?; + tracing::debug!(?response, "received response"); Ok(()) } diff --git a/mpc-recovery/src/main.rs b/mpc-recovery/src/main.rs index 41eb0f016..e4777ca96 100644 --- a/mpc-recovery/src/main.rs +++ b/mpc-recovery/src/main.rs @@ -51,6 +51,9 @@ enum Cli { /// GCP project ID #[arg(long, env("MPC_RECOVERY_GCP_PROJECT_ID"))] gcp_project_id: String, + /// GCP datastore URL + #[arg(long, env("MPC_RECOVERY_GCP_DATASTORE_URL"))] + gcp_datastore_url: Option, }, StartSign { /// Node ID @@ -68,6 +71,9 @@ enum Cli { /// GCP project ID #[arg(long, env("MPC_RECOVERY_GCP_PROJECT_ID"))] gcp_project_id: String, + /// GCP datastore URL + #[arg(long, env("MPC_RECOVERY_GCP_DATASTORE_URL"))] + gcp_datastore_url: Option, }, } @@ -133,8 +139,9 @@ async fn main() -> anyhow::Result<()> { account_creator_id, account_creator_sk, gcp_project_id, + gcp_datastore_url, } => { - let gcp_service = GcpService::new(gcp_project_id).await?; + let gcp_service = GcpService::new(gcp_project_id, gcp_datastore_url).await?; let sk_share = load_sh_skare(&gcp_service, node_id, sk_share).await?; let account_creator_sk = load_account_creator_sk(&gcp_service, node_id, account_creator_sk).await?; @@ -165,8 +172,9 @@ async fn main() -> anyhow::Result<()> { sk_share, web_port, gcp_project_id, + gcp_datastore_url, } => { - let gcp_service = GcpService::new(gcp_project_id).await?; + let gcp_service = GcpService::new(gcp_project_id, gcp_datastore_url).await?; let sk_share = load_sh_skare(&gcp_service, node_id, sk_share).await?; let pk_set: PublicKeySet = serde_json::from_str(&pk_set).unwrap(); From affa365854b7df73009ad4d47902b1b6bc1d953a Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Sun, 23 Apr 2023 16:37:42 +1000 Subject: [PATCH 4/6] appease clippy --- mpc-recovery/src/gcp/value.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mpc-recovery/src/gcp/value.rs b/mpc-recovery/src/gcp/value.rs index c3d9c787b..7a952225b 100644 --- a/mpc-recovery/src/gcp/value.rs +++ b/mpc-recovery/src/gcp/value.rs @@ -137,8 +137,6 @@ impl From for Value { Value::IntegerValue(val) } else if let Some(val) = value.double_value { Value::DoubleValue(val) - } else if let Some(_) = value.timestamp_value { - unimplemented!() } else if let Some(val) = value.key_value { Value::KeyValue(val) } else if let Some(val) = value.string_value { @@ -165,7 +163,7 @@ impl From for Value { val.values .unwrap_or_default() .into_iter() - .map(|e| Value::from(e)) + .map(Value::from) .collect(), ) } else { @@ -372,7 +370,7 @@ impl FromValue for google_datastore1::api::Value { Value::ArrayValue(val) => { let values = val .into_iter() - .map(|v| FromValue::from_value(v)) + .map(FromValue::from_value) .collect::, ConvertError>>()?; google_datastore1::api::Value { array_value: Some(ArrayValue { From cb2471346da461b7979966992782b74db94918a7 Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Sun, 23 Apr 2023 19:46:06 +1000 Subject: [PATCH 5/6] rename to UserCredentials --- mpc-recovery/src/key_recovery.rs | 16 ++++++++-------- mpc-recovery/src/leader_node/mod.rs | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/mpc-recovery/src/key_recovery.rs b/mpc-recovery/src/key_recovery.rs index 23fd0c802..03c6a0bb4 100644 --- a/mpc-recovery/src/key_recovery.rs +++ b/mpc-recovery/src/key_recovery.rs @@ -12,18 +12,18 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Serialize, Deserialize, Clone)] -pub struct UserSecretKey { +pub struct UserCredentials { pub internal_account_id: InternalAccountId, pub secret_key: SecretKey, } -impl KeyKind for UserSecretKey { +impl KeyKind for UserCredentials { fn kind() -> String { - "UserSecretKey".to_string() + "UserCredentials".to_string() } } -impl IntoValue for UserSecretKey { +impl IntoValue for UserCredentials { fn into_value(self) -> Value { let mut properties = HashMap::new(); properties.insert( @@ -37,7 +37,7 @@ impl IntoValue for UserSecretKey { Value::EntityValue { key: Key { path: Some(vec![PathElement { - kind: Some(UserSecretKey::kind()), + kind: Some(UserCredentials::kind()), name: Some(self.internal_account_id), id: None, }]), @@ -48,7 +48,7 @@ impl IntoValue for UserSecretKey { } } -impl FromValue for UserSecretKey { +impl FromValue for UserCredentials { fn from_value(value: Value) -> Result { match value { Value::EntityValue { mut properties, .. } => { @@ -65,7 +65,7 @@ impl FromValue for UserSecretKey { let secret_key = String::from_value(secret_key)?; let secret_key = secret_key.parse().unwrap(); - Ok(UserSecretKey { + Ok(Self { internal_account_id, secret_key, }) @@ -78,7 +78,7 @@ impl FromValue for UserSecretKey { } } -impl UserSecretKey { +impl UserCredentials { pub fn random(internal_account_id: InternalAccountId) -> Self { Self { internal_account_id, diff --git a/mpc-recovery/src/leader_node/mod.rs b/mpc-recovery/src/leader_node/mod.rs index 7d2be46bd..87abeefb7 100644 --- a/mpc-recovery/src/leader_node/mod.rs +++ b/mpc-recovery/src/leader_node/mod.rs @@ -1,5 +1,5 @@ use crate::gcp::GcpService; -use crate::key_recovery::UserSecretKey; +use crate::key_recovery::UserCredentials; use crate::msg::{ AddKeyRequest, AddKeyResponse, LeaderRequest, LeaderResponse, NewAccountRequest, NewAccountResponse, SigShareRequest, SigShareResponse, @@ -186,7 +186,7 @@ async fn process_new_account( ) .await?; - let user_secret_key = UserSecretKey::random(internal_acc_id.clone()); + let user_secret_key = UserCredentials::random(internal_acc_id.clone()); let public_key = user_secret_key.public_key(); state.gcp_service.insert(user_secret_key).await?; @@ -351,13 +351,13 @@ async fn process_add_key( .public_key .parse() .map_err(|e| AddKeyError::MalformedPublicKey(request.public_key, e))?; - let user_secret_key: UserSecretKey = state.gcp_service.get(internal_acc_id.clone()).await?; + let user_credentials: UserCredentials = state.gcp_service.get(internal_acc_id.clone()).await?; let user_account_id: AccountId = match &request.near_account_id { Some(near_account_id) => near_account_id .parse() .map_err(|e| AddKeyError::MalformedAccountId(request.near_account_id.unwrap(), e))?, - None => match get_acc_id_from_pk(user_secret_key.public_key(), state.account_lookup_url) { + None => match get_acc_id_from_pk(user_credentials.public_key(), state.account_lookup_url) { Ok(near_account_id) => near_account_id, Err(e) => { tracing::error!(err = ?e); @@ -370,14 +370,14 @@ async fn process_add_key( // Get nonce and recent block hash let (_hash, block_height, nonce) = state .client - .access_key(user_account_id.clone(), user_secret_key.public_key()) + .access_key(user_account_id.clone(), user_credentials.public_key()) .await?; // Create a transaction to create a new account let max_block_height: u64 = block_height + 100; let delegate_action = get_add_key_delegate_action( user_account_id.clone(), - user_secret_key.public_key(), + user_credentials.public_key(), new_public_key.clone(), nonce, max_block_height, @@ -385,7 +385,7 @@ async fn process_add_key( let signed_delegate_action = get_signed_delegated_action( delegate_action, user_account_id.clone(), - user_secret_key.secret_key.clone(), + user_credentials.secret_key.clone(), ); let resp = state.client.send_meta_tx(signed_delegate_action).await; From c7db80589daa7dea68e9d46a8d6b11c0254873ff Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Mon, 24 Apr 2023 18:29:23 +1000 Subject: [PATCH 6/6] integrate signer nodes with Datastore --- integration-tests/tests/docker/mod.rs | 6 +- integration-tests/tests/lib.rs | 15 ++- integration-tests/tests/mpc/positive.rs | 16 ++- mpc-recovery/src/gcp/error.rs | 2 + mpc-recovery/src/gcp/mod.rs | 13 +- mpc-recovery/src/key_recovery.rs | 7 +- mpc-recovery/src/leader_node/mod.rs | 12 +- mpc-recovery/src/main.rs | 2 +- mpc-recovery/src/msg.rs | 1 + mpc-recovery/src/oauth.rs | 10 +- mpc-recovery/src/sign_node/mod.rs | 124 ++++++++++++++---- .../src/sign_node/user_credentials.rs | 43 +++--- mpc-recovery/src/transaction.rs | 9 +- 13 files changed, 189 insertions(+), 71 deletions(-) diff --git a/integration-tests/tests/docker/mod.rs b/integration-tests/tests/docker/mod.rs index 2d063603a..9bf2d6364 100644 --- a/integration-tests/tests/docker/mod.rs +++ b/integration-tests/tests/docker/mod.rs @@ -287,6 +287,8 @@ impl SignNode { node_id: u64, pk_set: &Vec>, sk_share: &ExpandedKeyPair, + datastore_url: &str, + gcp_project_id: &str, ) -> anyhow::Result { create_network(docker, network).await?; let web_port = portpicker::pick_unused_port().expect("no free ports"); @@ -302,7 +304,9 @@ impl SignNode { "--web-port".to_string(), web_port.to_string(), "--gcp-project-id".to_string(), - "pagoda-123".to_string(), + gcp_project_id.to_string(), + "--gcp-datastore-url".to_string(), + datastore_url.to_string(), ]; let (container_id, ip_address) = diff --git a/integration-tests/tests/lib.rs b/integration-tests/tests/lib.rs index 5be5bc454..ff40391a5 100644 --- a/integration-tests/tests/lib.rs +++ b/integration-tests/tests/lib.rs @@ -18,7 +18,7 @@ const HOST_MACHINE_FROM_DOCKER: &str = "docker.for.mac.localhost"; pub struct TestContext<'a> { leader_node: &'a LeaderNode, - pk_set: &'a Vec>, + _pk_set: &'a Vec>, worker: &'a Worker, signer_nodes: &'a Vec, } @@ -76,7 +76,16 @@ where let mut signer_nodes = Vec::new(); for (i, share) in sk_shares.iter().enumerate().take(nodes) { - let addr = SignNode::start(&docker, NETWORK, i as u64, &pk_set, share).await?; + let addr = SignNode::start( + &docker, + NETWORK, + i as u64, + &pk_set, + share, + &datastore.address, + GCP_PROJECT_ID, + ) + .await?; signer_nodes.push(addr); } @@ -105,7 +114,7 @@ where let result = f(TestContext { leader_node: &leader_node, - pk_set: &pk_set, + _pk_set: &pk_set, signer_nodes: &signer_nodes, worker: &worker, }) diff --git a/integration-tests/tests/mpc/positive.rs b/integration-tests/tests/mpc/positive.rs index be4b6bddd..8075e8852 100644 --- a/integration-tests/tests/mpc/positive.rs +++ b/integration-tests/tests/mpc/positive.rs @@ -3,7 +3,8 @@ use ed25519_dalek::Verifier; use hyper::StatusCode; use mpc_recovery::{ msg::{AddKeyRequest, AddKeyResponse, NewAccountRequest, NewAccountResponse}, - transaction::{sign, to_dalek_combined_public_key}, + oauth::get_test_claims, + transaction::{call, sign, to_dalek_combined_public_key}, }; use rand::{distributions::Alphanumeric, Rng}; use std::time::Duration; @@ -26,9 +27,18 @@ async fn test_trio() -> anyhow::Result<()> { .map(|s| s.local_address.clone()) .collect(); - let signature = sign(&client, &signer_urls, payload.clone().into()).await?; + let signature = sign( + &client, + &signer_urls, + "validToken".to_string(), + payload.clone().into(), + ) + .await?; - let combined_pub = to_dalek_combined_public_key(ctx.pk_set).unwrap(); + let account_id = get_test_claims().get_internal_account_id(); + let res = call(&client, &signer_urls, "public_key", account_id).await?; + + let combined_pub = to_dalek_combined_public_key(&res).unwrap(); combined_pub.verify(payload.as_bytes(), &signature)?; Ok(()) diff --git a/mpc-recovery/src/gcp/error.rs b/mpc-recovery/src/gcp/error.rs index de62be199..db661a0bc 100644 --- a/mpc-recovery/src/gcp/error.rs +++ b/mpc-recovery/src/gcp/error.rs @@ -4,4 +4,6 @@ pub enum ConvertError { MissingProperty(String), #[error("expected property type `{expected}`, got `{got}`")] UnexpectedPropertyType { expected: String, got: String }, + #[error("property `{0}` is malfored")] + MalformedProperty(String), } diff --git a/mpc-recovery/src/gcp/mod.rs b/mpc-recovery/src/gcp/mod.rs index 37d14058b..3d0eed37f 100644 --- a/mpc-recovery/src/gcp/mod.rs +++ b/mpc-recovery/src/gcp/mod.rs @@ -87,7 +87,10 @@ impl GcpService { } #[tracing::instrument(level = "debug", skip_all, fields(key = name_key.to_string()))] - pub async fn get(&self, name_key: K) -> anyhow::Result { + pub async fn get( + &self, + name_key: K, + ) -> anyhow::Result> { let request = LookupRequest { keys: Some(vec![Key { path: Some(vec![PathElement { @@ -107,12 +110,14 @@ impl GcpService { .doit() .await?; tracing::debug!(?response, "received response"); - let found_entity = response + match response .found .and_then(|mut results| results.pop()) .and_then(|result| result.entity) - .ok_or_else(|| anyhow::anyhow!("not found"))?; - Ok(T::from_value(found_entity.into_value())?) + { + Some(found_entity) => Ok(Some(T::from_value(found_entity.into_value())?)), + None => Ok(None), + } } #[tracing::instrument(level = "debug", skip_all)] diff --git a/mpc-recovery/src/key_recovery.rs b/mpc-recovery/src/key_recovery.rs index cde148082..f8e2dc7f6 100644 --- a/mpc-recovery/src/key_recovery.rs +++ b/mpc-recovery/src/key_recovery.rs @@ -3,7 +3,7 @@ use crate::{ transaction::{call, to_dalek_public_key}, }; use multi_party_eddsa::protocols::aggsig::KeyAgg; -use near_crypto::{ED25519PublicKey, PublicKey, SecretKey}; +use near_crypto::{ED25519PublicKey, PublicKey}; pub async fn get_user_recovery_pk( client: &reqwest::Client, @@ -15,8 +15,3 @@ pub async fn get_user_recovery_pk( let pk = KeyAgg::key_aggregation_n(&res, 0).apk; to_dalek_public_key(&pk).map(|k| PublicKey::ED25519(ED25519PublicKey(*k.as_bytes()))) } - -pub fn _get_user_recovery_sk(_id: InternalAccountId) -> SecretKey { - // TODO: use key derivation or other techniques to generate a key - "ed25519:5pFJN3czPAHFWHZYjD4oTtnJE7PshLMeTkSU7CmWkvLaQWchCLgXGF1wwcJmh2AQChGH85EwcL5VW7tUavcAZDSG".parse().unwrap() -} diff --git a/mpc-recovery/src/leader_node/mod.rs b/mpc-recovery/src/leader_node/mod.rs index 1f9aed93c..1ef107632 100644 --- a/mpc-recovery/src/leader_node/mod.rs +++ b/mpc-recovery/src/leader_node/mod.rs @@ -1,7 +1,6 @@ use crate::key_recovery::get_user_recovery_pk; use crate::msg::{AddKeyRequest, AddKeyResponse, NewAccountRequest, NewAccountResponse}; -use crate::oauth::{IdTokenClaims, OAuthTokenVerifier, UniversalTokenVerifier}; -use crate::primitives::InternalAccountId; +use crate::oauth::{OAuthTokenVerifier, UniversalTokenVerifier}; use crate::relayer::error::RelayerError; use crate::relayer::msg::RegisterAccountRequest; use crate::relayer::NearRpcAndRelayerClient; @@ -139,7 +138,7 @@ async fn process_new_account( T::verify_token(&request.oidc_token, &state.pagoda_firebase_audience_id) .await .map_err(NewAccountError::OidcVerificationFailed)?; - let internal_acc_id = get_internal_account_id(oidc_token_claims); + let internal_acc_id = oidc_token_claims.get_internal_account_id(); state .client @@ -211,10 +210,6 @@ async fn process_new_account( .await } -fn get_internal_account_id(claims: IdTokenClaims) -> InternalAccountId { - format!("{}:{}", claims.iss, claims.sub) -} - mod response { use crate::msg::AddKeyResponse; use crate::msg::NewAccountResponse; @@ -325,7 +320,7 @@ async fn process_add_key( T::verify_token(&request.oidc_token, &state.pagoda_firebase_audience_id) .await .map_err(AddKeyError::OidcVerificationFailed)?; - let internal_acc_id = get_internal_account_id(oidc_token_claims); + let internal_acc_id = oidc_token_claims.get_internal_account_id(); let user_recovery_pk = get_user_recovery_pk( &state.reqwest_client, &state.sign_nodes, @@ -370,6 +365,7 @@ async fn process_add_key( let signed_delegate_action = get_mpc_signed_delegated_action( &state.reqwest_client, &state.sign_nodes, + request.oidc_token.clone(), delegate_action, ) .await?; diff --git a/mpc-recovery/src/main.rs b/mpc-recovery/src/main.rs index 3c4432848..4346a078e 100644 --- a/mpc-recovery/src/main.rs +++ b/mpc-recovery/src/main.rs @@ -180,7 +180,7 @@ async fn main() -> anyhow::Result<()> { // TODO Import just the private key and derive the rest let sk_share: ExpandedKeyPair = serde_json::from_str(&sk_share).unwrap(); - mpc_recovery::run_sign_node(node_id, pk_set, sk_share, web_port).await; + mpc_recovery::run_sign_node(gcp_service, node_id, pk_set, sk_share, web_port).await; } } diff --git a/mpc-recovery/src/msg.rs b/mpc-recovery/src/msg.rs index 2d8d9b7aa..9eda5953f 100644 --- a/mpc-recovery/src/msg.rs +++ b/mpc-recovery/src/msg.rs @@ -62,6 +62,7 @@ pub enum LeaderResponse { #[derive(Serialize, Deserialize, Debug)] pub struct SigShareRequest { + pub oidc_token: String, pub payload: Vec, } diff --git a/mpc-recovery/src/oauth.rs b/mpc-recovery/src/oauth.rs index 336e9f522..2374d81b6 100644 --- a/mpc-recovery/src/oauth.rs +++ b/mpc-recovery/src/oauth.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; +use crate::primitives::InternalAccountId; + #[async_trait::async_trait] pub trait OAuthTokenVerifier { async fn verify_token(token: &str, audience: &str) -> anyhow::Result; @@ -117,6 +119,12 @@ pub struct IdTokenClaims { pub exp: usize, } +impl IdTokenClaims { + pub fn get_internal_account_id(&self) -> InternalAccountId { + format!("{}:{}", self.iss, self.sub) + } +} + #[derive(Serialize, Deserialize)] struct OpenIdConfig { jwks_uri: String, @@ -138,7 +146,7 @@ fn get_pagoda_firebase_public_key() -> Result { Ok(key) } -fn get_test_claims() -> IdTokenClaims { +pub fn get_test_claims() -> IdTokenClaims { IdTokenClaims { iss: "test_issuer".to_string(), sub: "test_subject".to_string(), diff --git a/mpc-recovery/src/sign_node/mod.rs b/mpc-recovery/src/sign_node/mod.rs index b3f3d35c5..734a94781 100644 --- a/mpc-recovery/src/sign_node/mod.rs +++ b/mpc-recovery/src/sign_node/mod.rs @@ -1,4 +1,6 @@ use self::aggregate_signer::{NodeInfo, Reveal, SignedCommitment, SigningState}; +use self::user_credentials::UserCredentials; +use crate::gcp::GcpService; use crate::msg::SigShareRequest; use crate::oauth::{OAuthTokenVerifier, UniversalTokenVerifier}; use crate::primitives::InternalAccountId; @@ -13,8 +15,9 @@ use tokio::sync::RwLock; pub mod aggregate_signer; pub mod user_credentials; -#[tracing::instrument(level = "debug", skip(node_key, nodes_public_keys))] +#[tracing::instrument(level = "debug", skip(gcp_service, node_key, nodes_public_keys))] pub async fn run( + gcp_service: GcpService, our_index: NodeId, nodes_public_keys: Vec>, node_key: ExpandedKeyPair, @@ -33,6 +36,7 @@ pub async fn run( let signing_state = Arc::new(RwLock::new(SigningState::new())); let state = SignNodeState { + gcp_service, node_key, signing_state, pagoda_firebase_audience_id, @@ -59,41 +63,93 @@ pub async fn run( #[derive(Clone)] struct SignNodeState { + gcp_service: GcpService, pagoda_firebase_audience_id: String, node_key: ExpandedKeyPair, signing_state: Arc>, node_info: NodeInfo, } +#[derive(thiserror::Error, Debug)] +enum CommitError { + #[error("failed to verify oidc token: {0}")] + OidcVerificationFailed(anyhow::Error), + #[error("{0}")] + Other(#[from] anyhow::Error), +} + +async fn get_or_generate_user_creds( + state: &SignNodeState, + internal_account_id: InternalAccountId, +) -> anyhow::Result { + match state + .gcp_service + .get::<_, UserCredentials>(format!( + "{}/{}", + state.node_info.our_index, internal_account_id + )) + .await + { + Ok(Some(user_credentials)) => Ok(user_credentials), + Ok(None) => { + let user_credentials = UserCredentials { + node_id: state.node_info.our_index, + internal_account_id, + key_pair: ExpandedKeyPair::create(), + }; + state.gcp_service.insert(user_credentials.clone()).await?; + Ok(user_credentials) + } + Err(e) => Err(e), + } +} + +async fn process_commit( + state: SignNodeState, + request: SigShareRequest, +) -> Result { + let oidc_token_claims = + T::verify_token(&request.oidc_token, &state.pagoda_firebase_audience_id) + .await + .map_err(CommitError::OidcVerificationFailed)?; + let internal_account_id = oidc_token_claims.get_internal_account_id(); + + let user_credentials = get_or_generate_user_creds(&state, internal_account_id).await?; + + let response = state + .signing_state + .write() + .await + .get_commitment( + &user_credentials.key_pair, + &state.node_key, + // TODO Restrict this payload + request.payload, + ) + .map_err(|e| anyhow::anyhow!(e))?; + Ok(response) +} + #[tracing::instrument(level = "debug", skip_all, fields(id = state.node_info.our_index))] async fn commit( Extension(state): Extension, Json(request): Json, ) -> (StatusCode, Json>) { - // TODO: extract access token from payload - let access_token = "validToken"; - match T::verify_token(access_token, &state.pagoda_firebase_audience_id).await { - Ok(_) => { - tracing::debug!("access token is valid"); - - // TODO use seperate signing and node keys + key derivation - let response = state.signing_state.write().await.get_commitment( - &state.node_key, - &state.node_key, - // TODO Restrict this payload - request.payload, - ); - match &response { - Ok(_) => tracing::debug!("Successful commitment"), - Err(e) => tracing::error!("Commitment payload failed: {}", e), - }; - - (StatusCode::OK, Json(response)) + match process_commit::(state, request).await { + Ok(signed_commitment) => (StatusCode::OK, Json(Ok(signed_commitment))), + Err(ref e @ CommitError::OidcVerificationFailed(ref err_msg)) => { + tracing::error!(err = ?e); + ( + StatusCode::BAD_REQUEST, + Json(Err(format!("failed to verify oidc token: {}", err_msg))), + ) } - Err(_) => { - const ERR: &str = "access token verification failed"; - tracing::debug!(ERR); - (StatusCode::UNAUTHORIZED, Json(Err(ERR.to_string()))) + Err(e) => { + tracing::error!(err = ?e); + ( + StatusCode::BAD_REQUEST, + Json(Err(format!("failed to process new account: {}", e))), + ) } } } @@ -142,10 +198,24 @@ async fn signature_share( } } +#[tracing::instrument(level = "debug", skip_all, fields(id = state.node_info.our_index))] async fn public_key( Extension(state): Extension, - Json(_request): Json, + Json(request): Json, ) -> (StatusCode, Json, String>>) { - // TODO lookup correct public key - (StatusCode::OK, Json(Ok(state.node_key.public_key))) + match get_or_generate_user_creds(&state, request).await { + Ok(user_credentials) => ( + StatusCode::OK, + Json(Ok(user_credentials.public_key().clone())), + ), + Err(err) => { + tracing::error!(?err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(Err( + "failed to fetch/generate a public key for given account".to_string(), + )), + ) + } + } } diff --git a/mpc-recovery/src/sign_node/user_credentials.rs b/mpc-recovery/src/sign_node/user_credentials.rs index 03c6a0bb4..ec3a2c4b2 100644 --- a/mpc-recovery/src/sign_node/user_credentials.rs +++ b/mpc-recovery/src/sign_node/user_credentials.rs @@ -6,15 +6,17 @@ use crate::{ }, primitives::InternalAccountId, }; +use curv::elliptic::curves::{Ed25519, Point}; use google_datastore1::api::{Key, PathElement}; -use near_crypto::{KeyType, PublicKey, SecretKey}; +use multi_party_eddsa::protocols::ExpandedKeyPair; use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Serialize, Deserialize, Clone)] pub struct UserCredentials { + pub node_id: usize, pub internal_account_id: InternalAccountId, - pub secret_key: SecretKey, + pub key_pair: ExpandedKeyPair, } impl KeyKind for UserCredentials { @@ -26,19 +28,23 @@ impl KeyKind for UserCredentials { impl IntoValue for UserCredentials { fn into_value(self) -> Value { let mut properties = HashMap::new(); + properties.insert( + "node_id".to_string(), + Value::IntegerValue(self.node_id as i64), + ); properties.insert( "internal_account_id".to_string(), Value::StringValue(self.internal_account_id.clone()), ); properties.insert( - "secret_key".to_string(), - Value::StringValue(self.secret_key.to_string()), + "key_pair".to_string(), + Value::StringValue(serde_json::to_string(&self.key_pair).unwrap()), ); Value::EntityValue { key: Key { path: Some(vec![PathElement { kind: Some(UserCredentials::kind()), - name: Some(self.internal_account_id), + name: Some(format!("{}/{}", self.node_id, self.internal_account_id)), id: None, }]), partition_id: None, @@ -52,6 +58,10 @@ impl FromValue for UserCredentials { fn from_value(value: Value) -> Result { match value { Value::EntityValue { mut properties, .. } => { + let (_, node_id) = properties + .remove_entry("node_id") + .ok_or_else(|| ConvertError::MissingProperty("node_id".to_string()))?; + let node_id = i64::from_value(node_id)? as usize; let (_, internal_account_id) = properties .remove_entry("internal_account_id") .ok_or_else(|| { @@ -59,15 +69,17 @@ impl FromValue for UserCredentials { })?; let internal_account_id = String::from_value(internal_account_id)?; - let (_, secret_key) = properties - .remove_entry("secret_key") - .ok_or_else(|| ConvertError::MissingProperty("secret_key".to_string()))?; - let secret_key = String::from_value(secret_key)?; - let secret_key = secret_key.parse().unwrap(); + let (_, key_pair) = properties + .remove_entry("key_pair") + .ok_or_else(|| ConvertError::MissingProperty("key_pair".to_string()))?; + let key_pair = String::from_value(key_pair)?; + let key_pair = serde_json::from_str(&key_pair) + .map_err(|_| ConvertError::MalformedProperty("key_pair".to_string()))?; Ok(Self { + node_id, internal_account_id, - secret_key, + key_pair, }) } value => Err(ConvertError::UnexpectedPropertyType { @@ -79,14 +91,15 @@ impl FromValue for UserCredentials { } impl UserCredentials { - pub fn random(internal_account_id: InternalAccountId) -> Self { + pub fn random(node_id: usize, internal_account_id: InternalAccountId) -> Self { Self { + node_id, internal_account_id, - secret_key: SecretKey::from_random(KeyType::ED25519), + key_pair: ExpandedKeyPair::create(), } } - pub fn public_key(&self) -> PublicKey { - self.secret_key.public_key() + pub fn public_key(&self) -> &Point { + &self.key_pair.public_key } } diff --git a/mpc-recovery/src/transaction.rs b/mpc-recovery/src/transaction.rs index a05366b2b..9f7562449 100644 --- a/mpc-recovery/src/transaction.rs +++ b/mpc-recovery/src/transaction.rs @@ -115,6 +115,7 @@ pub fn get_local_signed_delegated_action( pub async fn get_mpc_signed_delegated_action( client: &reqwest::Client, sign_nodes: &[String], + oidc_token: String, delegate_action: DelegateAction, ) -> anyhow::Result { let signable_message = @@ -124,7 +125,7 @@ pub async fn get_mpc_signed_delegated_action( let hash = hash(&bytes); - let signature = sign(client, sign_nodes, hash.as_bytes().to_vec()).await?; + let signature = sign(client, sign_nodes, oidc_token, hash.as_bytes().to_vec()).await?; Ok(SignedDelegateAction { delegate_action, @@ -135,9 +136,13 @@ pub async fn get_mpc_signed_delegated_action( pub async fn sign( client: &reqwest::Client, sign_nodes: &[String], + oidc_token: String, payload: Vec, ) -> anyhow::Result { - let commit_request = SigShareRequest { payload }; + let commit_request = SigShareRequest { + oidc_token, + payload, + }; let commitments: Vec = call(client, sign_nodes, "commit", commit_request).await?;