From 1c9a654a9e9a452892514c97f24fb3bb560cf2e0 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Fri, 24 May 2024 10:51:19 +0200 Subject: [PATCH 01/43] wip --- memory_storage/src/lib.rs | 31 +- openmls/Cargo.toml | 5 + openmls/benches/large-balanced-group.rs | 462 ++++++++++++++++++++++++ openmls_rust_crypto/src/lib.rs | 21 ++ 4 files changed, 517 insertions(+), 2 deletions(-) create mode 100644 openmls/benches/large-balanced-group.rs diff --git a/memory_storage/src/lib.rs b/memory_storage/src/lib.rs index 4290d5723..4bfb31c3c 100644 --- a/memory_storage/src/lib.rs +++ b/memory_storage/src/lib.rs @@ -1,5 +1,5 @@ use openmls_traits::storage::*; -use serde::{Deserialize, Serialize}; +use serde::{ser::SerializeStruct, Serialize}; use std::{collections::HashMap, sync::RwLock}; /// A storage for the V_TEST version. @@ -9,11 +9,38 @@ mod test_store; #[cfg(feature = "persistence")] pub mod persistence; -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default)] pub struct MemoryStorage { values: RwLock, Vec>>, } +impl serde::ser::Serialize for MemoryStorage { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut store = serializer.serialize_struct("MemoryStorage", 2)?; + let values = self.values.read().unwrap(); + let values_vec: Vec<(Vec, Vec)> = + values.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); + store.serialize_field("values", &values_vec)?; + store.end() + } +} + +impl<'de> serde::de::Deserialize<'de> for MemoryStorage { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let values = Vec::<(Vec, Vec)>::deserialize(deserializer)?; + let values = values.into_iter().collect(); + Ok(Self { + values: RwLock::new(values), + }) + } +} + impl MemoryStorage { /// Internal helper to abstract write operations. #[inline(always)] diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 17a6daa94..17836f0fa 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -74,6 +74,7 @@ pretty_env_logger = "0.5" tempfile = "3" wasm-bindgen = "0.2.90" wasm-bindgen-test = "0.3.40" +clap = { version = "4", features = ["derive"] } # Disable for wasm32 and Win32 [target.'cfg(not(any(target_arch = "wasm32", all(target_arch = "x86", target_os = "windows"))))'.dev-dependencies] @@ -84,3 +85,7 @@ openmls = { path = ".", features = ["test-utils"] } [[bench]] name = "benchmark" harness = false + +[[bench]] +name = "large-balanced-group" +harness = false diff --git a/openmls/benches/large-balanced-group.rs b/openmls/benches/large-balanced-group.rs new file mode 100644 index 000000000..5f9267939 --- /dev/null +++ b/openmls/benches/large-balanced-group.rs @@ -0,0 +1,462 @@ +//! This benchmarks tests the performance of group operations in a large group +//! when the tree is fully populated. +//! +//! In particular do we assume that each member commits after joining the group. +//! +//! ## Measurements +//! +//! | Operation | Time (2 Members) | Time (5 Members) | Time (10 Members) | Time (25 Members) | +//! | ------------ | ---------------- | ---------------- | ----------------- | ----------------- | +//! | Add | | | | | +//! | Remove | | | | | +//! | Update | | | | | + +use std::{ + fs::File, + io::{BufReader, BufWriter}, + time::{Duration, Instant}, +}; + +use clap::{arg, command, Parser}; +use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +use openmls::{ + credentials::{BasicCredential, CredentialWithKey}, + framing::{MlsMessageIn, MlsMessageOut, ProcessedMessageContent}, + group::{MlsGroup, MlsGroupCreateConfig, StagedWelcome, PURE_PLAINTEXT_WIRE_FORMAT_POLICY}, + prelude::LeafNodeIndex, + prelude_test::*, +}; +use openmls_basic_credential::SignatureKeyPair; +use openmls_rust_crypto::OpenMlsRustCrypto; +use openmls_traits::types::Ciphersuite; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +#[allow(dead_code)] +struct Member { + provider: OpenMlsRustCrypto, + credential_with_key: CredentialWithKey, + signer: SignatureKeyPair, +} + +#[inline(always)] +fn process_commit( + group: &mut MlsGroup, + provider: &OpenMlsRustCrypto, + commit: openmls::prelude::MlsMessageOut, +) { + let processed_message = group + .process_message(provider, commit.into_protocol_message().unwrap()) + .unwrap(); + + if let ProcessedMessageContent::StagedCommitMessage(staged_commit) = + processed_message.into_content() + { + group.merge_staged_commit(provider, *staged_commit).unwrap(); + } else { + unreachable!("Expected a StagedCommit."); + } +} + +#[inline(always)] +fn self_update( + group: &mut MlsGroup, + provider: &OpenMlsRustCrypto, + signer: &SignatureKeyPair, +) -> MlsMessageOut { + let (commit, _, _group_info) = group.self_update(provider, signer).unwrap(); + + group.merge_pending_commit(provider).unwrap(); + + commit +} + +/// Remove member 1 +#[inline(always)] +fn remove_member( + group: &mut MlsGroup, + provider: &OpenMlsRustCrypto, + signer: &SignatureKeyPair, +) -> MlsMessageOut { + let (commit, _, _group_info) = group + .remove_members(provider, signer, &[LeafNodeIndex::new(1)]) + .unwrap(); + + group.merge_pending_commit(provider).unwrap(); + + commit +} + +/// Create a new member +fn new_member( + name: &str, +) -> ( + OpenMlsRustCrypto, + SignatureKeyPair, + CredentialWithKey, + openmls::prelude::KeyPackageBundle, +) { + let member_provider = OpenMlsRustCrypto::default(); + let credential = BasicCredential::new(name.into()); + let signer = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); + let credential_with_key = CredentialWithKey { + credential: credential.into(), + signature_key: signer.to_public_vec().into(), + }; + let key_package = KeyPackage::builder() + .build( + CIPHERSUITE, + &member_provider, + &signer, + credential_with_key.clone(), + ) + .expect("An unexpected error occurred."); + (member_provider, signer, credential_with_key, key_package) +} + +#[inline(always)] +fn add_member( + group: &mut MlsGroup, + provider: &OpenMlsRustCrypto, + signer: &SignatureKeyPair, + key_package: KeyPackage, +) -> MlsMessageOut { + let (commit, _welcome, _) = group.add_members(provider, signer, &[key_package]).unwrap(); + + group.merge_pending_commit(provider).unwrap(); + + commit +} + +// const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100, 200, 500]; +const GROUP_SIZES: &[usize] = &[3, 10]; +// const GROUP_SIZES: &[usize] = &[100, 200]; +const CIPHERSUITE: Ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519; + +/// Create a group of `num` clients. +/// All of them committed after joining. +fn setup(num: usize) -> Vec<(MlsGroup, Member)> { + let creator_provider = OpenMlsRustCrypto::default(); + let creator_credential = BasicCredential::new(format!("Creator").into()); + let creator_signer = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); + let creator_credential_with_key = CredentialWithKey { + credential: creator_credential.into(), + signature_key: creator_signer.to_public_vec().into(), + }; + + let mls_group_create_config = MlsGroupCreateConfig::builder() + .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .ciphersuite(CIPHERSUITE) + .build(); + + // Create the group + let creator_group = MlsGroup::new( + &creator_provider, + &creator_signer, + &mls_group_create_config, + creator_credential_with_key.clone(), + ) + .expect("An unexpected error occurred."); + + let mut members: Vec<(MlsGroup, Member)> = vec![( + creator_group, + Member { + provider: creator_provider, + credential_with_key: creator_credential_with_key, + signer: creator_signer, + }, + )]; + + for member_i in 0..num - 1 { + let (member_provider, signer, credential_with_key, key_package) = + new_member(&format!("Member {member_i}")); + + let creator = &mut members[0]; + let creator_group = &mut creator.0; + let creator_provider = &creator.1.provider; + let creator_signer = &creator.1.signer; + let (commit, welcome, _) = creator_group + .add_members( + creator_provider, + creator_signer, + &[key_package.key_package().clone()], + ) + .unwrap(); + + creator_group + .merge_pending_commit(creator_provider) + .expect("error merging pending commit"); + + let welcome: MlsMessageIn = welcome.into(); + let welcome = welcome + .into_welcome() + .expect("expected the message to be a welcome message"); + let mut member_i_group = StagedWelcome::new_from_welcome( + &member_provider, + mls_group_create_config.join_config(), + welcome, + Some(creator_group.export_ratchet_tree().into()), + ) + .unwrap() + .into_group(&member_provider) + .unwrap(); + + // Merge commit on all other members + for (group, member) in members.iter_mut().skip(1) { + process_commit(group, &member.provider, commit.clone()); + } + + // The new member commits and everyone else processes it. + let update_commit = self_update(&mut member_i_group, &member_provider, &signer); + for (group, member) in members.iter_mut() { + process_commit(group, &member.provider, update_commit.clone()); + } + + // Add new member to list + members.push(( + member_i_group, + Member { + provider: member_provider, + credential_with_key, + signer, + }, + )); + } + + members +} + +fn add(c: &mut Criterion) { + let mut group = c.benchmark_group("Add"); + + for group_size in GROUP_SIZES.iter() { + group.bench_with_input( + BenchmarkId::new("Adder", group_size), + group_size, + |b, group_size| { + b.iter_batched( + || { + let groups = setup(*group_size); + + let (_member_provider, _signer, _credential_with_key, key_package) = + new_member(&format!("New Member")); + (groups, key_package.key_package().clone()) + }, + |(mut groups, key_package)| { + add_bench(groups, key_package); + }, + BatchSize::LargeInput, + ) + }, + ); + } +} + +/// Benchmarking the time for member 1 in the list of `groups` to add a new +/// member. +fn add_bench(mut groups: Vec<(MlsGroup, Member)>, key_package: KeyPackage) { + // Let group 1 add a member and merge the commit. + let (updater_group, updater) = &mut groups[1]; + let provider = &updater.provider; + let signer = &updater.signer; + let _ = add_member(updater_group, provider, signer, key_package); +} + +fn remove(c: &mut Criterion) { + let mut group = c.benchmark_group("Remove"); + + for group_size in GROUP_SIZES.iter() { + group.bench_with_input( + BenchmarkId::new("Remover", group_size), + group_size, + |b, group_size| { + b.iter_batched( + || { + let groups = setup(*group_size); + groups + }, + |mut groups| { + // Let group 1 update and merge the commit. + let (updater_group, updater) = &mut groups[0]; + let provider = &updater.provider; + let signer = &updater.signer; + let _ = remove_member(updater_group, provider, signer); + }, + BatchSize::LargeInput, + ) + }, + ); + } +} + +fn update(c: &mut Criterion) { + let mut group = c.benchmark_group("Update"); + + for group_size in GROUP_SIZES.iter() { + group.bench_with_input( + BenchmarkId::new("Updater", group_size), + group_size, + |b, group_size| { + b.iter_batched( + || { + let groups = setup(*group_size); + groups + }, + |mut groups| { + // Let group 1 update and merge the commit. + let (updater_group, updater) = &mut groups[1]; + let provider = &updater.provider; + let signer = &updater.signer; + let _ = self_update(updater_group, provider, signer); + }, + BatchSize::LargeInput, + ) + }, + ); + } + + for group_size in GROUP_SIZES.iter() { + group.bench_with_input( + BenchmarkId::new("Member", group_size), + group_size, + |b, group_size| { + b.iter_batched( + || { + let mut groups = setup(*group_size); + + // Let group 1 update and merge the commit. + let (updater_group, updater) = &mut groups[1]; + let provider = &updater.provider; + let signer = &updater.signer; + let commit = self_update(updater_group, provider, signer); + + (groups, commit) + }, + |(mut groups, commit)| { + // Apply the commit at member 0 + let (member_group, member) = &mut groups[0]; + let provider = &member.provider; + + process_commit(member_group, provider, commit); + }, + BatchSize::LargeInput, + ) + }, + ); + } +} + +fn criterion_benchmark(c: &mut Criterion) { + add(c); + // remove(c); + // update(c); +} + +// Criterion is super slow. So we're doing manual benchmarks here as well +// criterion_group!(benches, criterion_benchmark); +// criterion_main!(benches); + +const ITERATIONS: usize = 10; +const WARMUP_ITERATIONS: usize = 1; + +fn duration(d: Duration) -> f64 { + ((d.as_secs() as f64) + (d.subsec_nanos() as f64 * 1e-9)) * 1000000f64 +} + +fn bench(mut setup: S, mut routine: R) -> f64 +where + S: FnMut() -> I, + R: FnMut(I) -> O, +{ + let mut time = 0f64; + + // Warmup + for _ in 0..WARMUP_ITERATIONS { + let input = setup(); + routine(input); + } + + // Benchmark + for _ in 0..ITERATIONS { + let input = setup(); + + let start = Instant::now(); + core::hint::black_box(routine(input)); + let end = Instant::now(); + + time += duration(end.duration_since(start)); + } + + time +} + +// #[derive(Parser, Debug)] +// #[command(version, about, long_about = None)] +// struct Args { +// /// Write groups out. +// #[arg(short, long, num_args = 0)] +// write: Option, +// } + +fn main() { + // let args = Args::parse(); + + let mut groups = vec![]; + + if false { + println!("Writing groups for benchmarks ..."); + for num in GROUP_SIZES { + println!("Generating group of size {num} ..."); + // Generate and write out groups. + let new_groups = setup(*num); + // let (groups, members): (Vec, Vec) = new_groups.into_iter().unzip(); + // eprintln!("{:?}", serde_json::to_vec(&new_groups)); + groups.push(new_groups); + } + let file = File::create("large-balanced-group.json").unwrap(); + let mut writer = BufWriter::new(file); + serde_json::to_writer(&mut writer, &groups).unwrap(); + + println!("Wrote new test groups to file."); + return; + } + + let file = File::open("large-balanced-group.json").unwrap(); + let mut reader = BufReader::new(file); + let groups: Vec> = serde_json::from_reader(&mut reader).unwrap(); + + // for num in GROUP_SIZES { + // println!("{num} Members"); + + // // Add + // let time = bench( + // || { + // let groups = setup(*num); + // let (_member_provider, _signer, _credential_with_key, key_package) = + // new_member(&format!("New Member")); + // let key_package = key_package.key_package().clone(); + + // (groups, key_package) + // }, + // |(groups, key_package)| add_bench(groups, key_package), + // ); + // println!(" Adder: {}μs", time / (ITERATIONS as f64)); + + // // Update + // let time = bench( + // || setup(*num), + // |groups| { + // bench_update(groups); + // }, + // ); + // println!(" Updater: {}μs", time / (ITERATIONS as f64)); + // } +} + +fn bench_update(mut groups: Vec<(MlsGroup, Member)>) { + // Let group 1 update and merge the commit. + let (updater_group, updater) = &mut groups[1]; + let provider = &updater.provider; + let signer = &updater.signer; + let _ = self_update(updater_group, provider, signer); +} diff --git a/openmls_rust_crypto/src/lib.rs b/openmls_rust_crypto/src/lib.rs index d9e437330..8c90cebf5 100644 --- a/openmls_rust_crypto/src/lib.rs +++ b/openmls_rust_crypto/src/lib.rs @@ -15,6 +15,27 @@ pub struct OpenMlsRustCrypto { key_store: MemoryStorage, } +impl serde::ser::Serialize for OpenMlsRustCrypto { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.key_store.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for OpenMlsRustCrypto { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let key_store = MemoryStorage::deserialize(deserializer)?; + let crypto = RustCrypto::default(); + + Ok(Self { crypto, key_store }) + } +} + impl OpenMlsProvider for OpenMlsRustCrypto { type CryptoProvider = RustCrypto; type RandProvider = RustCrypto; From 40e484c436d71f1ab1b8542514f8439d5da324be Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Fri, 31 May 2024 13:45:31 +0200 Subject: [PATCH 02/43] example as bench --- memory_storage/src/lib.rs | 34 +- memory_storage/src/persistence.rs | 4 +- openmls/Cargo.toml | 7 +- .../large-groups.rs} | 408 ++++++++++-------- .../binary_tree/array_representation/diff.rs | 1 + .../binary_tree/array_representation/tree.rs | 2 +- openmls/src/group/core_group/mod.rs | 3 +- openmls/src/group/core_group/past_secrets.rs | 6 +- openmls/src/group/core_group/proposals.rs | 3 +- openmls/src/group/core_group/staged_commit.rs | 3 + openmls/src/group/mls_group/mod.rs | 3 + openmls/src/group/public_group/diff.rs | 1 + openmls/src/group/public_group/mod.rs | 2 +- .../src/group/public_group/staged_commit.rs | 1 + openmls/src/schedule/message_secrets.rs | 2 +- openmls/src/schedule/mod.rs | 14 +- openmls/src/schedule/psk.rs | 3 +- openmls/src/treesync/diff.rs | 1 + openmls/src/treesync/mod.rs | 2 +- openmls/src/treesync/treesync_node.rs | 4 +- openmls_rust_crypto/Cargo.toml | 3 + openmls_rust_crypto/src/lib.rs | 22 +- openmls_rust_crypto/src/provider.rs | 9 + 23 files changed, 278 insertions(+), 260 deletions(-) rename openmls/{benches/large-balanced-group.rs => examples/large-groups.rs} (56%) diff --git a/memory_storage/src/lib.rs b/memory_storage/src/lib.rs index 4bfb31c3c..4b3f98de0 100644 --- a/memory_storage/src/lib.rs +++ b/memory_storage/src/lib.rs @@ -1,5 +1,5 @@ use openmls_traits::storage::*; -use serde::{ser::SerializeStruct, Serialize}; +use serde::Serialize; use std::{collections::HashMap, sync::RwLock}; /// A storage for the V_TEST version. @@ -11,33 +11,17 @@ pub mod persistence; #[derive(Debug, Default)] pub struct MemoryStorage { - values: RwLock, Vec>>, + pub values: RwLock, Vec>>, } -impl serde::ser::Serialize for MemoryStorage { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut store = serializer.serialize_struct("MemoryStorage", 2)?; +// For testing we want to clone. +#[cfg(feature = "test-utils")] +impl Clone for MemoryStorage { + fn clone(&self) -> Self { let values = self.values.read().unwrap(); - let values_vec: Vec<(Vec, Vec)> = - values.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); - store.serialize_field("values", &values_vec)?; - store.end() - } -} - -impl<'de> serde::de::Deserialize<'de> for MemoryStorage { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let values = Vec::<(Vec, Vec)>::deserialize(deserializer)?; - let values = values.into_iter().collect(); - Ok(Self { - values: RwLock::new(values), - }) + Self { + values: RwLock::new(values.clone()), + } } } diff --git a/memory_storage/src/persistence.rs b/memory_storage/src/persistence.rs index b746aba51..3f9403119 100644 --- a/memory_storage/src/persistence.rs +++ b/memory_storage/src/persistence.rs @@ -23,7 +23,7 @@ impl super::MemoryStorage { get_file_path(&("openmls_cli_".to_owned() + user_name + "_ks.json")) } - fn save_to_file(&self, output_file: &File) -> Result<(), String> { + pub fn save_to_file(&self, output_file: &File) -> Result<(), String> { let writer = BufWriter::new(output_file); let mut ser_ks = SerializableKeyStore::default(); @@ -48,7 +48,7 @@ impl super::MemoryStorage { } } - fn load_from_file(&mut self, input_file: &File) -> Result<(), String> { + pub fn load_from_file(&mut self, input_file: &File) -> Result<(), String> { // Prepare file reader. let reader = BufReader::new(input_file); diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 17836f0fa..87056a929 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -43,7 +43,7 @@ crypto-subtle = [] # Enable subtle crypto APIs that have to be used with care. test-utils = [ "dep:serde_json", "dep:itertools", - "dep:openmls_rust_crypto", + "openmls_rust_crypto/test-utils", "dep:rand", "dep:wasm-bindgen-test", "dep:openmls_basic_credential", @@ -75,6 +75,7 @@ tempfile = "3" wasm-bindgen = "0.2.90" wasm-bindgen-test = "0.3.40" clap = { version = "4", features = ["derive"] } +base64 = "0.22.1" # Disable for wasm32 and Win32 [target.'cfg(not(any(target_arch = "wasm32", all(target_arch = "x86", target_os = "windows"))))'.dev-dependencies] @@ -85,7 +86,3 @@ openmls = { path = ".", features = ["test-utils"] } [[bench]] name = "benchmark" harness = false - -[[bench]] -name = "large-balanced-group" -harness = false diff --git a/openmls/benches/large-balanced-group.rs b/openmls/examples/large-groups.rs similarity index 56% rename from openmls/benches/large-balanced-group.rs rename to openmls/examples/large-groups.rs index 5f9267939..703bb43a4 100644 --- a/openmls/benches/large-balanced-group.rs +++ b/openmls/examples/large-groups.rs @@ -12,13 +12,13 @@ //! | Update | | | | | use std::{ + collections::HashMap, fs::File, io::{BufReader, BufWriter}, time::{Duration, Instant}, }; -use clap::{arg, command, Parser}; -use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +use clap::Parser; use openmls::{ credentials::{BasicCredential, CredentialWithKey}, framing::{MlsMessageIn, MlsMessageOut, ProcessedMessageContent}, @@ -28,10 +28,10 @@ use openmls::{ }; use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; -use openmls_traits::types::Ciphersuite; +use openmls_traits::{types::Ciphersuite, OpenMlsProvider as _}; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone)] #[allow(dead_code)] struct Member { provider: OpenMlsRustCrypto, @@ -39,6 +39,49 @@ struct Member { signer: SignatureKeyPair, } +#[derive(Debug, Default, Serialize, Deserialize)] +struct SerializableStore { + values: HashMap, +} + +impl Member { + fn serialize(&self) -> (Vec, Vec, Vec) { + let storage = self.provider.storage(); + + let mut serializable_storage = SerializableStore::default(); + for (key, value) in &*storage.values.read().unwrap() { + serializable_storage + .values + .insert(base64::encode(key), base64::encode(value)); + } + + ( + serde_json::to_vec(&serializable_storage).unwrap(), + serde_json::to_vec(&self.credential_with_key).unwrap(), + serde_json::to_vec(&self.signer).unwrap(), + ) + } + + fn load(storage: &[u8], ckey: &[u8], signer: &[u8]) -> Self { + let serializable_storage: SerializableStore = serde_json::from_slice(storage).unwrap(); + let credential_with_key: CredentialWithKey = serde_json::from_slice(ckey).unwrap(); + let signer: SignatureKeyPair = serde_json::from_slice(signer).unwrap(); + + let provider = OpenMlsRustCrypto::default(); + let mut ks_map = provider.storage().values.write().unwrap(); + for (key, value) in serializable_storage.values { + ks_map.insert(base64::decode(key).unwrap(), base64::decode(value).unwrap()); + } + drop(ks_map); + + Self { + provider, + credential_with_key, + signer, + } + } +} + #[inline(always)] fn process_commit( group: &mut MlsGroup, @@ -71,6 +114,15 @@ fn self_update( commit } +#[inline(always)] +fn bench_update(mut groups: Vec<(MlsGroup, Member)>) { + // Let group 1 update and merge the commit. + let (updater_group, updater) = &mut groups[1]; + let provider = &updater.provider; + let signer = &updater.signer; + let _ = self_update(updater_group, provider, signer); +} + /// Remove member 1 #[inline(always)] fn remove_member( @@ -128,8 +180,8 @@ fn add_member( commit } -// const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100, 200, 500]; -const GROUP_SIZES: &[usize] = &[3, 10]; +const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100, 200, 500]; +// const GROUP_SIZES: &[usize] = &[2, 3, 4, 5]; // const GROUP_SIZES: &[usize] = &[100, 200]; const CIPHERSUITE: Ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519; @@ -226,32 +278,6 @@ fn setup(num: usize) -> Vec<(MlsGroup, Member)> { members } -fn add(c: &mut Criterion) { - let mut group = c.benchmark_group("Add"); - - for group_size in GROUP_SIZES.iter() { - group.bench_with_input( - BenchmarkId::new("Adder", group_size), - group_size, - |b, group_size| { - b.iter_batched( - || { - let groups = setup(*group_size); - - let (_member_provider, _signer, _credential_with_key, key_package) = - new_member(&format!("New Member")); - (groups, key_package.key_package().clone()) - }, - |(mut groups, key_package)| { - add_bench(groups, key_package); - }, - BatchSize::LargeInput, - ) - }, - ); - } -} - /// Benchmarking the time for member 1 in the list of `groups` to add a new /// member. fn add_bench(mut groups: Vec<(MlsGroup, Member)>, key_package: KeyPackage) { @@ -262,201 +288,205 @@ fn add_bench(mut groups: Vec<(MlsGroup, Member)>, key_package: KeyPackage) { let _ = add_member(updater_group, provider, signer, key_package); } -fn remove(c: &mut Criterion) { - let mut group = c.benchmark_group("Remove"); - - for group_size in GROUP_SIZES.iter() { - group.bench_with_input( - BenchmarkId::new("Remover", group_size), - group_size, - |b, group_size| { - b.iter_batched( - || { - let groups = setup(*group_size); - groups - }, - |mut groups| { - // Let group 1 update and merge the commit. - let (updater_group, updater) = &mut groups[0]; - let provider = &updater.provider; - let signer = &updater.signer; - let _ = remove_member(updater_group, provider, signer); - }, - BatchSize::LargeInput, - ) - }, - ); - } -} - -fn update(c: &mut Criterion) { - let mut group = c.benchmark_group("Update"); - - for group_size in GROUP_SIZES.iter() { - group.bench_with_input( - BenchmarkId::new("Updater", group_size), - group_size, - |b, group_size| { - b.iter_batched( - || { - let groups = setup(*group_size); - groups - }, - |mut groups| { - // Let group 1 update and merge the commit. - let (updater_group, updater) = &mut groups[1]; - let provider = &updater.provider; - let signer = &updater.signer; - let _ = self_update(updater_group, provider, signer); - }, - BatchSize::LargeInput, - ) - }, - ); - } - - for group_size in GROUP_SIZES.iter() { - group.bench_with_input( - BenchmarkId::new("Member", group_size), - group_size, - |b, group_size| { - b.iter_batched( - || { - let mut groups = setup(*group_size); - - // Let group 1 update and merge the commit. - let (updater_group, updater) = &mut groups[1]; - let provider = &updater.provider; - let signer = &updater.signer; - let commit = self_update(updater_group, provider, signer); - - (groups, commit) - }, - |(mut groups, commit)| { - // Apply the commit at member 0 - let (member_group, member) = &mut groups[0]; - let provider = &member.provider; - - process_commit(member_group, provider, commit); - }, - BatchSize::LargeInput, - ) - }, - ); - } -} - -fn criterion_benchmark(c: &mut Criterion) { - add(c); - // remove(c); - // update(c); -} - -// Criterion is super slow. So we're doing manual benchmarks here as well -// criterion_group!(benches, criterion_benchmark); -// criterion_main!(benches); - -const ITERATIONS: usize = 10; -const WARMUP_ITERATIONS: usize = 1; - -fn duration(d: Duration) -> f64 { - ((d.as_secs() as f64) + (d.subsec_nanos() as f64 * 1e-9)) * 1000000f64 -} +const ITERATIONS: usize = 1000; +const WARMUP_ITERATIONS: usize = 5; -fn bench(mut setup: S, mut routine: R) -> f64 +/// A custom benchmarking function. +fn bench(si: SI, mut setup: S, mut routine: R) -> Duration where - S: FnMut() -> I, + SI: Clone, + S: FnMut(SI) -> I, R: FnMut(I) -> O, { - let mut time = 0f64; + let mut time = Duration::ZERO; // Warmup for _ in 0..WARMUP_ITERATIONS { - let input = setup(); + let input = setup(si.clone()); routine(input); } // Benchmark for _ in 0..ITERATIONS { - let input = setup(); + let input = setup(si.clone()); let start = Instant::now(); core::hint::black_box(routine(input)); let end = Instant::now(); - time += duration(end.duration_since(start)); + time += end.duration_since(start); } time } -// #[derive(Parser, Debug)] -// #[command(version, about, long_about = None)] -// struct Args { -// /// Write groups out. -// #[arg(short, long, num_args = 0)] -// write: Option, -// } +#[derive(Parser)] +struct Args { + #[clap(short, long, action)] + write: bool, +} +mod util { + use super::*; + + /// Read benchmark setups from the fiels previously written. + pub fn read() -> Vec> { + let file = File::open("large-balanced-group-groups.json").unwrap(); + let mut reader = BufReader::new(file); + let groups: Vec> = serde_json::from_reader(&mut reader).unwrap(); + + let file = File::open("large-balanced-group-members.json").unwrap(); + let mut reader = BufReader::new(file); + let members: Vec, Vec, Vec)>> = + serde_json::from_reader(&mut reader).unwrap(); + + let members: Vec> = members + .into_iter() + .map(|members| { + members + .into_iter() + .map(|m| Member::load(&m.0, &m.1, &m.2)) + .collect() + }) + .collect(); + + let mut out = vec![]; + for (g, m) in groups.into_iter().zip(members.into_iter()) { + out.push(g.into_iter().zip(m.into_iter()).collect()) + } -fn main() { - // let args = Args::parse(); + out + } - let mut groups = vec![]; + /// Generate benchmark setups and write them out. + pub fn write() { + let mut groups = vec![]; + let mut members = vec![]; - if false { println!("Writing groups for benchmarks ..."); for num in GROUP_SIZES { println!("Generating group of size {num} ..."); // Generate and write out groups. let new_groups = setup(*num); - // let (groups, members): (Vec, Vec) = new_groups.into_iter().unzip(); - // eprintln!("{:?}", serde_json::to_vec(&new_groups)); + let (new_groups, new_members): (Vec, Vec) = + new_groups.into_iter().unzip(); + let new_members: Vec<(Vec, Vec, Vec)> = + new_members.into_iter().map(|m| m.serialize()).collect(); groups.push(new_groups); + members.push(new_members); } - let file = File::create("large-balanced-group.json").unwrap(); + let file = File::create("large-balanced-group-groups.json").unwrap(); let mut writer = BufWriter::new(file); serde_json::to_writer(&mut writer, &groups).unwrap(); + let file = File::create("large-balanced-group-members.json").unwrap(); + let mut writer = BufWriter::new(file); + serde_json::to_writer(&mut writer, &members).unwrap(); println!("Wrote new test groups to file."); - return; } +} +use util::*; + +fn print_time(label: &str, d: Duration) { + let micros = d.as_micros(); + let time = if micros < (1_000 * ITERATIONS as u128) { + format!("{} μs", micros / ITERATIONS as u128) + } else if micros < (1_000_000 * ITERATIONS as u128) { + format!( + "{:.2} ms", + (micros as f64 / (1_000_f64 * ITERATIONS as f64)) + ) + } else { + format!( + "{:.2}s", + (micros as f64 / (1_000_000_f64 * ITERATIONS as f64)) + ) + }; + let space = if label.len() < 6 { + format!("\t\t") + } else { + format!("\t") + }; - let file = File::open("large-balanced-group.json").unwrap(); - let mut reader = BufReader::new(file); - let groups: Vec> = serde_json::from_reader(&mut reader).unwrap(); - - // for num in GROUP_SIZES { - // println!("{num} Members"); - - // // Add - // let time = bench( - // || { - // let groups = setup(*num); - // let (_member_provider, _signer, _credential_with_key, key_package) = - // new_member(&format!("New Member")); - // let key_package = key_package.key_package().clone(); - - // (groups, key_package) - // }, - // |(groups, key_package)| add_bench(groups, key_package), - // ); - // println!(" Adder: {}μs", time / (ITERATIONS as f64)); - - // // Update - // let time = bench( - // || setup(*num), - // |groups| { - // bench_update(groups); - // }, - // ); - // println!(" Updater: {}μs", time / (ITERATIONS as f64)); - // } + println!("{label}:{space}{time}"); } -fn bench_update(mut groups: Vec<(MlsGroup, Member)>) { - // Let group 1 update and merge the commit. - let (updater_group, updater) = &mut groups[1]; - let provider = &updater.provider; - let signer = &updater.signer; - let _ = self_update(updater_group, provider, signer); +fn main() { + let args = Args::parse(); + + if args.write { + // Only generate groups and write them out. + write(); + + return; + } + + let all_groups = read(); + for groups in all_groups { + if groups.len() > 25 { + break; + } + println!("{} Members", groups.len()); + + // Add + let time = bench( + groups.clone(), + |groups| { + let (_member_provider, _signer, _credential_with_key, key_package) = + new_member(&format!("New Member")); + let key_package = key_package.key_package().clone(); + + (groups, key_package) + }, + |(groups, key_package)| add_bench(groups, key_package), + ); + print_time("Adder", time); + + // Update + let time = bench( + groups.clone(), + |groups| groups, + |groups| { + bench_update(groups); + }, + ); + print_time("Updater", time); + + // Remove + let time = bench( + groups.clone(), + |groups| groups, + |mut groups| { + // Let group 1 update and merge the commit. + let (updater_group, updater) = &mut groups[0]; + let provider = &updater.provider; + let signer = &updater.signer; + let _ = remove_member(updater_group, provider, signer); + }, + ); + print_time("Remover", time); + + // Process an update + let time = bench( + groups, + |mut groups| { + // Let group 1 update and merge the commit. + let (updater_group, updater) = &mut groups[1]; + let provider = &updater.provider; + let signer = &updater.signer; + let commit = self_update(updater_group, provider, signer); + + (groups, commit) + }, + |(mut groups, commit)| { + // Apply the commit at member 0 + let (member_group, member) = &mut groups[0]; + let provider = &member.provider; + + process_commit(member_group, provider, commit); + }, + ); + print_time("Process update", time); + + println!(""); + } } diff --git a/openmls/src/binary_tree/array_representation/diff.rs b/openmls/src/binary_tree/array_representation/diff.rs index 36e69d614..554dfa9b2 100644 --- a/openmls/src/binary_tree/array_representation/diff.rs +++ b/openmls/src/binary_tree/array_representation/diff.rs @@ -42,6 +42,7 @@ use super::{ /// was created from. However, the lack of the internal reference means that its /// lifetime is not tied to that of the original tree. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub(crate) struct StagedAbDiff { leaf_diff: BTreeMap, parent_diff: BTreeMap, diff --git a/openmls/src/binary_tree/array_representation/tree.rs b/openmls/src/binary_tree/array_representation/tree.rs index c0cb943d6..79dcf5d59 100644 --- a/openmls/src/binary_tree/array_representation/tree.rs +++ b/openmls/src/binary_tree/array_representation/tree.rs @@ -27,7 +27,7 @@ where Parent(P), } -#[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq))] #[derive(Clone, Debug, Serialize, Deserialize)] /// A representation of a full, left-balanced binary tree that uses a simple /// vector to store nodes. Each tree has to consist of at least one node. diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index a78fff0fc..706d5efb0 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -159,7 +159,8 @@ pub(crate) struct StagedCoreWelcome { } #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub(crate) struct CoreGroup { public_group: PublicGroup, group_epoch_secrets: GroupEpochSecrets, diff --git a/openmls/src/group/core_group/past_secrets.rs b/openmls/src/group/core_group/past_secrets.rs index 88a794eb4..b6847982f 100644 --- a/openmls/src/group/core_group/past_secrets.rs +++ b/openmls/src/group/core_group/past_secrets.rs @@ -6,7 +6,8 @@ use super::*; // Internal helper struct #[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] #[cfg_attr(feature = "crypto-debug", derive(Debug))] struct EpochTree { epoch: u64, @@ -17,7 +18,8 @@ struct EpochTree { /// Can store message secrets for up to `max_epochs`. The trees are added with [`self::add()`] and can be queried /// with [`Self::get_epoch()`]. #[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] #[cfg_attr(feature = "crypto-debug", derive(Debug))] pub(crate) struct MessageSecretsStore { // Maximum size of the `past_epoch_trees` list. diff --git a/openmls/src/group/core_group/proposals.rs b/openmls/src/group/core_group/proposals.rs index f08695864..efbc1164f 100644 --- a/openmls/src/group/core_group/proposals.rs +++ b/openmls/src/group/core_group/proposals.rs @@ -20,7 +20,7 @@ use crate::{ /// A [ProposalStore] can store the standalone proposals that are received from /// the DS in between two commit messages. #[derive(Debug, Default, Serialize, Deserialize, PartialEq)] -#[cfg_attr(test, derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub struct ProposalStore { queued_proposals: Vec, } @@ -204,6 +204,7 @@ impl OrderedProposalRefs { /// accessed efficiently. To enable iteration over the queue in order, the /// `ProposalQueue` also contains a vector of `ProposalRef`s. #[derive(Default, Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub(crate) struct ProposalQueue { /// `proposal_references` holds references to the proposals in the queue and /// determines the order of the queue. diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index 534047dbf..19f967df1 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -424,6 +424,7 @@ impl CoreGroup { } #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub(crate) enum StagedCommitState { PublicState(Box), GroupMember(Box), @@ -431,6 +432,7 @@ pub(crate) enum StagedCommitState { /// Contains the changes from a commit to the group state. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub struct StagedCommit { staged_proposal_queue: ProposalQueue, state: StagedCommitState, @@ -563,6 +565,7 @@ impl StagedCommit { /// This struct is used internally by [StagedCommit] to encapsulate all the modified group state. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub(crate) struct MemberStagedCommitState { group_epoch_secrets: GroupEpochSecrets, message_secrets: MessageSecrets, diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 501dc9c0d..2d0ca8cb1 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -42,6 +42,7 @@ mod test_mls_group; /// Pending Commit state. Differentiates between Commits issued by group members /// and External Commits. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub enum PendingCommitState { /// Commit from a group member Member(StagedCommit), @@ -114,6 +115,7 @@ impl From for StagedCommit { /// external commit process, see [`MlsGroup::join_by_external_commit()`] or /// Section 11.2.1 of the MLS specification. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub enum MlsGroupState { /// There is currently a pending Commit that hasn't been merged yet. PendingCommit(Box), @@ -148,6 +150,7 @@ pub enum MlsGroupState { /// inactive, as well as if it has a pending commit. See [`MlsGroupState`] for /// more information. #[derive(Debug)] +#[cfg_attr(feature = "test-utils", derive(Clone))] pub struct MlsGroup { // The group configuration. See [`MlsGroupJoinConfig`] for more information. mls_group_config: MlsGroupJoinConfig, diff --git a/openmls/src/group/public_group/diff.rs b/openmls/src/group/public_group/diff.rs index 685b7760c..cdc1a9f79 100644 --- a/openmls/src/group/public_group/diff.rs +++ b/openmls/src/group/public_group/diff.rs @@ -238,6 +238,7 @@ impl<'a> PublicGroupDiff<'a> { /// The staged version of a [`PublicGroupDiff`], which means it can no longer be /// modified. Its only use is to merge it into the original [`PublicGroup`]. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub(crate) struct StagedPublicGroupDiff { pub(super) staged_diff: StagedTreeSyncDiff, pub(super) group_context: GroupContext, diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index b4ffff081..1d230291a 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -61,7 +61,7 @@ mod validation; /// This struct holds all public values of an MLS group. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))] pub struct PublicGroup { treesync: TreeSync, proposal_store: ProposalStore, diff --git a/openmls/src/group/public_group/staged_commit.rs b/openmls/src/group/public_group/staged_commit.rs index 342dbcf77..451391e3d 100644 --- a/openmls/src/group/public_group/staged_commit.rs +++ b/openmls/src/group/public_group/staged_commit.rs @@ -13,6 +13,7 @@ use crate::{ }; #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub struct PublicStagedCommitState { pub(super) staged_diff: StagedPublicGroupDiff, pub(super) update_path_leaf_node: Option, diff --git a/openmls/src/schedule/message_secrets.rs b/openmls/src/schedule/message_secrets.rs index 485127d19..792182cc9 100644 --- a/openmls/src/schedule/message_secrets.rs +++ b/openmls/src/schedule/message_secrets.rs @@ -4,7 +4,7 @@ use super::*; /// Combined message secrets that need to be stored for later decryption/verification #[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] #[cfg_attr(feature = "crypto-debug", derive(Debug))] pub(crate) struct MessageSecrets { sender_data_secret: SenderDataSecret, diff --git a/openmls/src/schedule/mod.rs b/openmls/src/schedule/mod.rs index b82a0610b..515061e0f 100644 --- a/openmls/src/schedule/mod.rs +++ b/openmls/src/schedule/mod.rs @@ -190,7 +190,7 @@ impl ResumptionPskSecret { /// A secret that can be used among members to make sure everyone has the same /// group state. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(Eq, PartialEq, Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Eq, PartialEq, Clone))] pub struct EpochAuthenticator { secret: Secret, } @@ -255,7 +255,7 @@ impl CommitSecret { /// The `InitSecret` is used to connect the next epoch to the current one. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))] pub(crate) struct InitSecret { secret: Secret, } @@ -711,7 +711,7 @@ impl EncryptionSecret { /// A secret that we can derive secrets from, that are used outside of OpenMLS. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))] pub(crate) struct ExporterSecret { secret: Secret, } @@ -757,7 +757,7 @@ impl ExporterSecret { /// A secret used when joining a group with an external Commit. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))] pub(crate) struct ExternalSecret { secret: Secret, } @@ -792,7 +792,7 @@ impl ExternalSecret { /// The confirmation key is used to calculate the `ConfirmationTag`. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))] pub(crate) struct ConfirmationKey { secret: Secret, } @@ -864,7 +864,7 @@ impl ConfirmationKey { /// The membership key is used to calculate the `MembershipTag`. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))] pub(crate) struct MembershipKey { secret: Secret, } @@ -1225,7 +1225,7 @@ impl EpochSecrets { } #[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub(crate) struct GroupEpochSecrets { init_secret: InitSecret, exporter_secret: ExporterSecret, diff --git a/openmls/src/schedule/psk.rs b/openmls/src/schedule/psk.rs index a725bb828..cab6ef88d 100644 --- a/openmls/src/schedule/psk.rs +++ b/openmls/src/schedule/psk.rs @@ -490,7 +490,8 @@ pub mod store { /// /// This is where the resumption PSKs are kept in a rollover list. #[derive(Debug, Serialize, Deserialize)] - #[cfg_attr(test, derive(PartialEq, Clone))] + #[cfg_attr(test, derive(PartialEq))] + #[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub(crate) struct ResumptionPskStore { max_number_of_secrets: usize, resumption_psk: Vec<(GroupEpoch, ResumptionPskSecret)>, diff --git a/openmls/src/treesync/diff.rs b/openmls/src/treesync/diff.rs index 71a162441..56be81519 100644 --- a/openmls/src/treesync/diff.rs +++ b/openmls/src/treesync/diff.rs @@ -59,6 +59,7 @@ pub(crate) type UpdatePathResult = ( /// The [`StagedTreeSyncDiff`] can be created from a [`TreeSyncDiff`], examined /// and later merged into a [`TreeSync`] instance. #[derive(Debug, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] pub(crate) struct StagedTreeSyncDiff { diff: StagedMlsBinaryTreeDiff, new_tree_hash: Vec, diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index d1813b59a..35418ead1 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -354,7 +354,7 @@ impl fmt::Display for RatchetTree { /// creating a new instance from an imported set of nodes, as well as when /// merging a diff. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq, Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))] pub(crate) struct TreeSync { tree: MlsBinaryTree, tree_hash: Vec, diff --git a/openmls/src/treesync/treesync_node.rs b/openmls/src/treesync/treesync_node.rs index 5b074fb99..e0e65b5e1 100644 --- a/openmls/src/treesync/treesync_node.rs +++ b/openmls/src/treesync/treesync_node.rs @@ -57,7 +57,7 @@ impl From for TreeNode { } #[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq))] /// This intermediate struct on top of `Option` allows us to cache tree /// hash values. Blank nodes are represented by [`TreeSyncNode`] instances where /// `node = None`. @@ -109,7 +109,7 @@ impl From for Option { } #[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq))] /// This intermediate struct on top of `Option` allows us to cache tree /// hash values. Blank nodes are represented by [`TreeSyncNode`] instances where /// `node = None`. diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index b9594cd36..8a7eadf8f 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -31,3 +31,6 @@ hpke-rs-rust-crypto = { version = "0.2.0" } tls_codec = { workspace = true } thiserror = "1.0" serde = { version = "^1.0", features = ["derive"] } + +[features] +test-utils = [] diff --git a/openmls_rust_crypto/src/lib.rs b/openmls_rust_crypto/src/lib.rs index 8c90cebf5..58f7b5d30 100644 --- a/openmls_rust_crypto/src/lib.rs +++ b/openmls_rust_crypto/src/lib.rs @@ -10,32 +10,12 @@ mod provider; pub use provider::*; #[derive(Default, Debug)] +#[cfg_attr(feature = "test-utils", derive(Clone))] pub struct OpenMlsRustCrypto { crypto: RustCrypto, key_store: MemoryStorage, } -impl serde::ser::Serialize for OpenMlsRustCrypto { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.key_store.serialize(serializer) - } -} - -impl<'de> serde::de::Deserialize<'de> for OpenMlsRustCrypto { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let key_store = MemoryStorage::deserialize(deserializer)?; - let crypto = RustCrypto::default(); - - Ok(Self { crypto, key_store }) - } -} - impl OpenMlsProvider for OpenMlsRustCrypto { type CryptoProvider = RustCrypto; type RandProvider = RustCrypto; diff --git a/openmls_rust_crypto/src/provider.rs b/openmls_rust_crypto/src/provider.rs index 23222c882..bdb2799f8 100644 --- a/openmls_rust_crypto/src/provider.rs +++ b/openmls_rust_crypto/src/provider.rs @@ -31,6 +31,15 @@ pub struct RustCrypto { rng: RwLock, } +// For testing we want to clone. +// But really we just create a new Rng. +#[cfg(feature = "test-utils")] +impl Clone for RustCrypto { + fn clone(&self) -> Self { + Self::default() + } +} + impl Default for RustCrypto { fn default() -> Self { Self { From 90854feda9eb0ef9434f94792f0e28325e23e2e3 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Fri, 31 May 2024 15:04:58 +0200 Subject: [PATCH 03/43] performance numbers --- openmls/examples/large-groups.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index 703bb43a4..4e83c6ab9 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -5,11 +5,12 @@ //! //! ## Measurements //! -//! | Operation | Time (2 Members) | Time (5 Members) | Time (10 Members) | Time (25 Members) | -//! | ------------ | ---------------- | ---------------- | ----------------- | ----------------- | -//! | Add | | | | | -//! | Remove | | | | | -//! | Update | | | | | +//! | | 2 | 3 | 4 | 5 | 10 | 25 | 50 | 100 | 200 | 500 | +//! | -------------- | ------ | ------ | ------ | ------ | ------ | ------- | ------- | -------- | -------- | --------- | +//! | Adder | 613 μs | 935 μs | 680 μs | 711 μs | 901 μs | 1.40 ms | 3.18 ms | 9.97 ms | 39.80 ms | 260.49 ms | +//! | Updater | 308 μs | 510 μs | 496 μs | 595 μs | 748 μs | 1.32 ms | 3.01 ms | 10.47 ms | 39.99 ms | 249.49 ms | +//! | Remover | 193 μs | 305 μs | 320 μs | 474 μs | 698 μs | 1.23 ms | 3.10 ms | 10.07 ms | 38.10 ms | 257.24 ms | +//! | Process update | 303 μs | 433 μs | 429 μs | 529 μs | 698 μs | 1.16 ms | 2.86 ms | 9.61 ms | 35.27 ms | 249.96 ms | use std::{ collections::HashMap, @@ -422,9 +423,6 @@ fn main() { let all_groups = read(); for groups in all_groups { - if groups.len() > 25 { - break; - } println!("{} Members", groups.len()); // Add From 7c4f25856cd59987873d2a01b329221479e36c4c Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Tue, 4 Jun 2024 09:52:22 +0200 Subject: [PATCH 04/43] update large group benchmarks --- openmls/Cargo.toml | 1 + openmls/examples/large-groups.rs | 243 +++++++++++++++---------------- 2 files changed, 122 insertions(+), 122 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 87056a929..286dd0faa 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -76,6 +76,7 @@ wasm-bindgen = "0.2.90" wasm-bindgen-test = "0.3.40" clap = { version = "4", features = ["derive"] } base64 = "0.22.1" +flate2 = "1.0" # Disable for wasm32 and Win32 [target.'cfg(not(any(target_arch = "wasm32", all(target_arch = "x86", target_os = "windows"))))'.dev-dependencies] diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index 4e83c6ab9..5120693a9 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -15,7 +15,6 @@ use std::{ collections::HashMap, fs::File, - io::{BufReader, BufWriter}, time::{Duration, Instant}, }; @@ -115,15 +114,6 @@ fn self_update( commit } -#[inline(always)] -fn bench_update(mut groups: Vec<(MlsGroup, Member)>) { - // Let group 1 update and merge the commit. - let (updater_group, updater) = &mut groups[1]; - let provider = &updater.provider; - let signer = &updater.signer; - let _ = self_update(updater_group, provider, signer); -} - /// Remove member 1 #[inline(always)] fn remove_member( @@ -181,112 +171,109 @@ fn add_member( commit } -const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100, 200, 500]; -// const GROUP_SIZES: &[usize] = &[2, 3, 4, 5]; -// const GROUP_SIZES: &[usize] = &[100, 200]; -const CIPHERSUITE: Ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519; - -/// Create a group of `num` clients. -/// All of them committed after joining. -fn setup(num: usize) -> Vec<(MlsGroup, Member)> { - let creator_provider = OpenMlsRustCrypto::default(); - let creator_credential = BasicCredential::new(format!("Creator").into()); - let creator_signer = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); - let creator_credential_with_key = CredentialWithKey { - credential: creator_credential.into(), - signature_key: creator_signer.to_public_vec().into(), - }; +use generate::CIPHERSUITE; - let mls_group_create_config = MlsGroupCreateConfig::builder() - .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) - .ciphersuite(CIPHERSUITE) - .build(); - - // Create the group - let creator_group = MlsGroup::new( - &creator_provider, - &creator_signer, - &mls_group_create_config, - creator_credential_with_key.clone(), - ) - .expect("An unexpected error occurred."); - - let mut members: Vec<(MlsGroup, Member)> = vec![( - creator_group, - Member { - provider: creator_provider, - credential_with_key: creator_credential_with_key, - signer: creator_signer, - }, - )]; - - for member_i in 0..num - 1 { - let (member_provider, signer, credential_with_key, key_package) = - new_member(&format!("Member {member_i}")); - - let creator = &mut members[0]; - let creator_group = &mut creator.0; - let creator_provider = &creator.1.provider; - let creator_signer = &creator.1.signer; - let (commit, welcome, _) = creator_group - .add_members( - creator_provider, - creator_signer, - &[key_package.key_package().clone()], - ) - .unwrap(); - - creator_group - .merge_pending_commit(creator_provider) - .expect("error merging pending commit"); +mod generate { + use super::*; - let welcome: MlsMessageIn = welcome.into(); - let welcome = welcome - .into_welcome() - .expect("expected the message to be a welcome message"); - let mut member_i_group = StagedWelcome::new_from_welcome( - &member_provider, - mls_group_create_config.join_config(), - welcome, - Some(creator_group.export_ratchet_tree().into()), + pub const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100, 200, 500, 1000]; + // const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100]; + // const GROUP_SIZES: &[usize] = &[100, 200]; + pub const CIPHERSUITE: Ciphersuite = + Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519; + + /// Create a group of `num` clients. + /// All of them committed after joining. + pub fn setup(num: usize) -> Vec<(MlsGroup, Member)> { + let creator_provider = OpenMlsRustCrypto::default(); + let creator_credential = BasicCredential::new(format!("Creator").into()); + let creator_signer = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); + let creator_credential_with_key = CredentialWithKey { + credential: creator_credential.into(), + signature_key: creator_signer.to_public_vec().into(), + }; + + let mls_group_create_config = MlsGroupCreateConfig::builder() + .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .ciphersuite(CIPHERSUITE) + .build(); + + // Create the group + let creator_group = MlsGroup::new( + &creator_provider, + &creator_signer, + &mls_group_create_config, + creator_credential_with_key.clone(), ) - .unwrap() - .into_group(&member_provider) - .unwrap(); - - // Merge commit on all other members - for (group, member) in members.iter_mut().skip(1) { - process_commit(group, &member.provider, commit.clone()); - } - - // The new member commits and everyone else processes it. - let update_commit = self_update(&mut member_i_group, &member_provider, &signer); - for (group, member) in members.iter_mut() { - process_commit(group, &member.provider, update_commit.clone()); - } + .expect("An unexpected error occurred."); - // Add new member to list - members.push(( - member_i_group, + let mut members: Vec<(MlsGroup, Member)> = vec![( + creator_group, Member { - provider: member_provider, - credential_with_key, - signer, + provider: creator_provider, + credential_with_key: creator_credential_with_key, + signer: creator_signer, }, - )); - } + )]; + + for member_i in 0..num - 1 { + let (member_provider, signer, credential_with_key, key_package) = + new_member(&format!("Member {member_i}")); + + let creator = &mut members[0]; + let creator_group = &mut creator.0; + let creator_provider = &creator.1.provider; + let creator_signer = &creator.1.signer; + let (commit, welcome, _) = creator_group + .add_members( + creator_provider, + creator_signer, + &[key_package.key_package().clone()], + ) + .unwrap(); + + creator_group + .merge_pending_commit(creator_provider) + .expect("error merging pending commit"); + + let welcome: MlsMessageIn = welcome.into(); + let welcome = welcome + .into_welcome() + .expect("expected the message to be a welcome message"); + let mut member_i_group = StagedWelcome::new_from_welcome( + &member_provider, + mls_group_create_config.join_config(), + welcome, + Some(creator_group.export_ratchet_tree().into()), + ) + .unwrap() + .into_group(&member_provider) + .unwrap(); - members -} + // Merge commit on all other members + for (group, member) in members.iter_mut().skip(1) { + process_commit(group, &member.provider, commit.clone()); + } + + // The new member commits and everyone else processes it. + let update_commit = self_update(&mut member_i_group, &member_provider, &signer); + for (group, member) in members.iter_mut() { + process_commit(group, &member.provider, update_commit.clone()); + } + + // Add new member to list + members.push(( + member_i_group, + Member { + provider: member_provider, + credential_with_key, + signer, + }, + )); + } -/// Benchmarking the time for member 1 in the list of `groups` to add a new -/// member. -fn add_bench(mut groups: Vec<(MlsGroup, Member)>, key_package: KeyPackage) { - // Let group 1 add a member and merge the commit. - let (updater_group, updater) = &mut groups[1]; - let provider = &updater.provider; - let signer = &updater.signer; - let _ = add_member(updater_group, provider, signer, key_package); + members + } } const ITERATIONS: usize = 1000; @@ -327,16 +314,16 @@ struct Args { write: bool, } mod util { - use super::*; + use super::{generate, *}; /// Read benchmark setups from the fiels previously written. pub fn read() -> Vec> { - let file = File::open("large-balanced-group-groups.json").unwrap(); - let mut reader = BufReader::new(file); + let file = File::open("large-balanced-group-groups.json.gzip").unwrap(); + let mut reader = flate2::read::GzDecoder::new(file); let groups: Vec> = serde_json::from_reader(&mut reader).unwrap(); - let file = File::open("large-balanced-group-members.json").unwrap(); - let mut reader = BufReader::new(file); + let file = File::open("large-balanced-group-members.json.gzip").unwrap(); + let mut reader = flate2::read::GzDecoder::new(file); let members: Vec, Vec, Vec)>> = serde_json::from_reader(&mut reader).unwrap(); @@ -364,10 +351,10 @@ mod util { let mut members = vec![]; println!("Writing groups for benchmarks ..."); - for num in GROUP_SIZES { + for num in generate::GROUP_SIZES { println!("Generating group of size {num} ..."); // Generate and write out groups. - let new_groups = setup(*num); + let new_groups = generate::setup(*num); let (new_groups, new_members): (Vec, Vec) = new_groups.into_iter().unzip(); let new_members: Vec<(Vec, Vec, Vec)> = @@ -375,11 +362,11 @@ mod util { groups.push(new_groups); members.push(new_members); } - let file = File::create("large-balanced-group-groups.json").unwrap(); - let mut writer = BufWriter::new(file); + let file = File::create("large-balanced-group-groups.json.gzip").unwrap(); + let mut writer = flate2::write::GzEncoder::new(file, flate2::Compression::default()); serde_json::to_writer(&mut writer, &groups).unwrap(); - let file = File::create("large-balanced-group-members.json").unwrap(); - let mut writer = BufWriter::new(file); + let file = File::create("large-balanced-group-members.json.gzip").unwrap(); + let mut writer = flate2::write::GzEncoder::new(file, flate2::Compression::default()); serde_json::to_writer(&mut writer, &members).unwrap(); println!("Wrote new test groups to file."); @@ -423,6 +410,9 @@ fn main() { let all_groups = read(); for groups in all_groups { + if groups.len() != 10 { + continue; + } println!("{} Members", groups.len()); // Add @@ -435,7 +425,12 @@ fn main() { (groups, key_package) }, - |(groups, key_package)| add_bench(groups, key_package), + |(mut groups, key_package)| { + let (updater_group, updater) = &mut groups[1]; + let provider = &updater.provider; + let signer = &updater.signer; + let _ = add_member(updater_group, provider, signer, key_package); + }, ); print_time("Adder", time); @@ -443,8 +438,12 @@ fn main() { let time = bench( groups.clone(), |groups| groups, - |groups| { - bench_update(groups); + |mut groups| { + // Let group 1 update and merge the commit. + let (updater_group, updater) = &mut groups[1]; + let provider = &updater.provider; + let signer = &updater.signer; + let _ = self_update(updater_group, provider, signer); }, ); print_time("Updater", time); From 780e29f0b13e53f3ebdd04a9772d257d6305bb1d Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Wed, 5 Jun 2024 12:57:12 +0200 Subject: [PATCH 05/43] better benchmarks --- openmls/examples/large-groups.rs | 110 +++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 21 deletions(-) diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index 5120693a9..bd41a0df2 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -176,15 +176,16 @@ use generate::CIPHERSUITE; mod generate { use super::*; - pub const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100, 200, 500, 1000]; - // const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100]; - // const GROUP_SIZES: &[usize] = &[100, 200]; + pub const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100]; pub const CIPHERSUITE: Ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519; /// Create a group of `num` clients. /// All of them committed after joining. - pub fn setup(num: usize) -> Vec<(MlsGroup, Member)> { + pub fn setup(num: usize, variant: Option) -> Vec<(MlsGroup, Member)> { + // We default to a bare group unless variant wants something else. + let variant = variant.unwrap_or(SetupVariants::Bare); + let creator_provider = OpenMlsRustCrypto::default(); let creator_credential = BasicCredential::new(format!("Creator").into()); let creator_signer = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); @@ -255,10 +256,17 @@ mod generate { process_commit(group, &member.provider, commit.clone()); } - // The new member commits and everyone else processes it. - let update_commit = self_update(&mut member_i_group, &member_provider, &signer); - for (group, member) in members.iter_mut() { - process_commit(group, &member.provider, update_commit.clone()); + // Depending on the variant we do something here. + match variant { + SetupVariants::Bare => (), // Nothing to do in this case. + SetupVariants::CommitAfterJoin => { + // The new member commits and everyone else processes it. + let update_commit = self_update(&mut member_i_group, &member_provider, &signer); + for (group, member) in members.iter_mut() { + process_commit(group, &member.provider, update_commit.clone()); + } + } + SetupVariants::CommitToFullGroup => (), // Commit after everyone was added. } // Add new member to list @@ -272,6 +280,25 @@ mod generate { )); } + // Depending on the variant we do something once everyone was added. + match variant { + SetupVariants::Bare => (), // Nothing to do in this case. + SetupVariants::CommitAfterJoin => (), // Noting to do in this case. + SetupVariants::CommitToFullGroup => { + // Every member commits and everyone else processes it. + for i in 0..members.len() { + let (member_i_group, member_i) = &mut members[i]; + let update_commit = + self_update(member_i_group, &member_i.provider, &member_i.signer); + for (j, (group, member)) in members.iter_mut().enumerate() { + if i != j { + process_commit(group, &member.provider, update_commit.clone()); + } + } + } + } + } + members } } @@ -308,21 +335,42 @@ where time } +#[derive(clap::ValueEnum, Clone, Copy, Debug)] +enum SetupVariants { + Bare, + CommitAfterJoin, + CommitToFullGroup, +} + #[derive(Parser)] struct Args { #[clap(short, long, action)] write: bool, + + #[clap(short, long)] + data: Option, + + #[clap(short, long, value_delimiter = ' ', num_args = 1..)] + groups: Option>, + + #[clap(short, long)] + setup: Option, } mod util { + use std::path::Path; + use super::{generate, *}; + const GROUPS_PATH: &str = "large-balanced-group-groups.json.gzip"; + const MEMBERS_PATH: &str = "large-balanced-group-members.json.gzip"; + /// Read benchmark setups from the fiels previously written. - pub fn read() -> Vec> { - let file = File::open("large-balanced-group-groups.json.gzip").unwrap(); + pub fn read(path: Option) -> Vec> { + let file = File::open(groups_file(&path)).unwrap(); let mut reader = flate2::read::GzDecoder::new(file); let groups: Vec> = serde_json::from_reader(&mut reader).unwrap(); - let file = File::open("large-balanced-group-members.json.gzip").unwrap(); + let file = File::open(members_file(&path)).unwrap(); let mut reader = flate2::read::GzDecoder::new(file); let members: Vec, Vec, Vec)>> = serde_json::from_reader(&mut reader).unwrap(); @@ -345,16 +393,33 @@ mod util { out } + fn groups_file(path: &Option) -> std::path::PathBuf { + let path = path.clone().unwrap_or_default(); + let path = Path::new(&path); + path.join(GROUPS_PATH) + } + + fn members_file(path: &Option) -> std::path::PathBuf { + let path = path.clone().unwrap_or_default(); + let path = Path::new(&path); + path.join(MEMBERS_PATH) + } + /// Generate benchmark setups and write them out. - pub fn write() { + pub fn write( + path: Option, + group_sizes: Option>, + variant: Option, + ) { let mut groups = vec![]; let mut members = vec![]; - println!("Writing groups for benchmarks ..."); - for num in generate::GROUP_SIZES { + let group_sizes = group_sizes.unwrap_or(generate::GROUP_SIZES.to_vec()); + println!("Writing groups for benchmarks {group_sizes:?}..."); + for num in group_sizes { println!("Generating group of size {num} ..."); // Generate and write out groups. - let new_groups = generate::setup(*num); + let new_groups = generate::setup(num, variant); let (new_groups, new_members): (Vec, Vec) = new_groups.into_iter().unzip(); let new_members: Vec<(Vec, Vec, Vec)> = @@ -362,10 +427,10 @@ mod util { groups.push(new_groups); members.push(new_members); } - let file = File::create("large-balanced-group-groups.json.gzip").unwrap(); + let file = File::create(groups_file(&path)).unwrap(); let mut writer = flate2::write::GzEncoder::new(file, flate2::Compression::default()); serde_json::to_writer(&mut writer, &groups).unwrap(); - let file = File::create("large-balanced-group-members.json.gzip").unwrap(); + let file = File::create(members_file(&path)).unwrap(); let mut writer = flate2::write::GzEncoder::new(file, flate2::Compression::default()); serde_json::to_writer(&mut writer, &members).unwrap(); @@ -403,15 +468,18 @@ fn main() { if args.write { // Only generate groups and write them out. - write(); + write(args.data, args.groups, args.setup); return; } - let all_groups = read(); + let all_groups = read(args.data); for groups in all_groups { - if groups.len() != 10 { - continue; + if let Some(group_sizes) = &args.groups { + // Only run the groups of the sizes from the cli + if !group_sizes.contains(&groups.len()) { + continue; + } } println!("{} Members", groups.len()); From 6ae56ff1f7ab1a3690e99d692b2b4f27c93a05b5 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Wed, 5 Jun 2024 14:15:43 +0200 Subject: [PATCH 06/43] don't copy so much --- openmls/examples/large-groups.rs | 102 ++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index bd41a0df2..0ebfbb4f9 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -307,23 +307,27 @@ const ITERATIONS: usize = 1000; const WARMUP_ITERATIONS: usize = 5; /// A custom benchmarking function. -fn bench(si: SI, mut setup: S, mut routine: R) -> Duration +/// +/// DO NOT USE THIS WITH LARGE INPUTS +#[inline(always)] +#[allow(dead_code)] +fn bench(si: &SI, mut setup: S, mut routine: R) -> Duration where SI: Clone, - S: FnMut(SI) -> I, + S: FnMut(&SI) -> I, R: FnMut(I) -> O, { let mut time = Duration::ZERO; // Warmup for _ in 0..WARMUP_ITERATIONS { - let input = setup(si.clone()); + let input = setup(si); routine(input); } // Benchmark for _ in 0..ITERATIONS { - let input = setup(si.clone()); + let input = setup(si); let start = Instant::now(); core::hint::black_box(routine(input)); @@ -335,6 +339,32 @@ where time } +// A benchmarking macro to avoid copying memory and skewing the results. +macro_rules! bench { + ($groups:expr, $setup:expr, $routine:expr) => {{ + let mut time = Duration::ZERO; + + // Warmup + for _ in 0..WARMUP_ITERATIONS { + let input = $setup($groups); + $routine(input); + } + + // Benchmark + for _ in 0..ITERATIONS { + let input = $setup($groups); + + let start = Instant::now(); + core::hint::black_box($routine(input)); + let end = Instant::now(); + + time += end.duration_since(start); + } + + time + }}; +} + #[derive(clap::ValueEnum, Clone, Copy, Debug)] enum SetupVariants { Bare, @@ -474,7 +504,7 @@ fn main() { } let all_groups = read(args.data); - for groups in all_groups { + for groups in all_groups.iter() { if let Some(group_sizes) = &args.groups { // Only run the groups of the sizes from the cli if !group_sizes.contains(&groups.len()) { @@ -484,71 +514,71 @@ fn main() { println!("{} Members", groups.len()); // Add - let time = bench( - groups.clone(), - |groups| { + let time = bench!( + groups, + |groups: &Vec<(MlsGroup, Member)>| { let (_member_provider, _signer, _credential_with_key, key_package) = new_member(&format!("New Member")); let key_package = key_package.key_package().clone(); - (groups, key_package) + (groups[1].clone(), key_package) }, - |(mut groups, key_package)| { - let (updater_group, updater) = &mut groups[1]; + |(group1, key_package): ((MlsGroup, Member), KeyPackage)| { + let (mut updater_group, updater) = group1; let provider = &updater.provider; let signer = &updater.signer; - let _ = add_member(updater_group, provider, signer, key_package); - }, + let _ = add_member(&mut updater_group, provider, signer, key_package); + } ); print_time("Adder", time); // Update - let time = bench( - groups.clone(), - |groups| groups, - |mut groups| { + let time = bench!( + groups, + |groups: &Vec<(MlsGroup, Member)>| groups[1].clone(), + |group1: (MlsGroup, Member)| { // Let group 1 update and merge the commit. - let (updater_group, updater) = &mut groups[1]; + let (mut updater_group, updater) = group1; let provider = &updater.provider; let signer = &updater.signer; - let _ = self_update(updater_group, provider, signer); - }, + let _ = self_update(&mut updater_group, provider, signer); + } ); print_time("Updater", time); // Remove - let time = bench( - groups.clone(), - |groups| groups, - |mut groups| { + let time = bench!( + groups, + |groups: &Vec<(MlsGroup, Member)>| groups[0].clone(), + |group0: (MlsGroup, Member)| { // Let group 1 update and merge the commit. - let (updater_group, updater) = &mut groups[0]; + let (mut updater_group, updater) = group0; let provider = &updater.provider; let signer = &updater.signer; - let _ = remove_member(updater_group, provider, signer); - }, + let _ = remove_member(&mut updater_group, provider, signer); + } ); print_time("Remover", time); // Process an update - let time = bench( + let time = bench!( groups, - |mut groups| { + |groups: &Vec<(MlsGroup, Member)>| { // Let group 1 update and merge the commit. - let (updater_group, updater) = &mut groups[1]; + let (updater_group, updater) = &groups[1]; let provider = &updater.provider; let signer = &updater.signer; - let commit = self_update(updater_group, provider, signer); + let commit = self_update(&mut updater_group.clone(), provider, signer); - (groups, commit) + (groups[0].clone(), commit) }, - |(mut groups, commit)| { + |(group0, commit): ((MlsGroup, Member), MlsMessageOut)| { // Apply the commit at member 0 - let (member_group, member) = &mut groups[0]; + let (mut member_group, member) = group0; let provider = &member.provider; - process_commit(member_group, provider, commit); - }, + process_commit(&mut member_group, provider, commit); + } ); print_time("Process update", time); From ae8d33681316cea46fd4270e12c037e8e88d5838 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Wed, 5 Jun 2024 16:14:36 +0200 Subject: [PATCH 07/43] cleanup --- openmls/examples/large-groups.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index 0ebfbb4f9..9f855a682 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -2,15 +2,6 @@ //! when the tree is fully populated. //! //! In particular do we assume that each member commits after joining the group. -//! -//! ## Measurements -//! -//! | | 2 | 3 | 4 | 5 | 10 | 25 | 50 | 100 | 200 | 500 | -//! | -------------- | ------ | ------ | ------ | ------ | ------ | ------- | ------- | -------- | -------- | --------- | -//! | Adder | 613 μs | 935 μs | 680 μs | 711 μs | 901 μs | 1.40 ms | 3.18 ms | 9.97 ms | 39.80 ms | 260.49 ms | -//! | Updater | 308 μs | 510 μs | 496 μs | 595 μs | 748 μs | 1.32 ms | 3.01 ms | 10.47 ms | 39.99 ms | 249.49 ms | -//! | Remover | 193 μs | 305 μs | 320 μs | 474 μs | 698 μs | 1.23 ms | 3.10 ms | 10.07 ms | 38.10 ms | 257.24 ms | -//! | Process update | 303 μs | 433 μs | 429 μs | 529 μs | 698 μs | 1.16 ms | 2.86 ms | 9.61 ms | 35.27 ms | 249.96 ms | use std::{ collections::HashMap, @@ -18,6 +9,7 @@ use std::{ time::{Duration, Instant}, }; +use base64::prelude::*; use clap::Parser; use openmls::{ credentials::{BasicCredential, CredentialWithKey}, @@ -52,7 +44,7 @@ impl Member { for (key, value) in &*storage.values.read().unwrap() { serializable_storage .values - .insert(base64::encode(key), base64::encode(value)); + .insert(BASE64_STANDARD.encode(key), BASE64_STANDARD.encode(value)); } ( @@ -70,7 +62,10 @@ impl Member { let provider = OpenMlsRustCrypto::default(); let mut ks_map = provider.storage().values.write().unwrap(); for (key, value) in serializable_storage.values { - ks_map.insert(base64::decode(key).unwrap(), base64::decode(value).unwrap()); + ks_map.insert( + BASE64_STANDARD.decode(key).unwrap(), + BASE64_STANDARD.decode(value).unwrap(), + ); } drop(ks_map); From 0af69a5dc0c352639faeb91132a2171c3f714900 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 10 Jun 2024 09:06:59 +0200 Subject: [PATCH 08/43] progress bars and reuse groups --- openmls/Cargo.toml | 1 + openmls/examples/large-groups.rs | 83 +++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 286dd0faa..06a28232b 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -77,6 +77,7 @@ wasm-bindgen-test = "0.3.40" clap = { version = "4", features = ["derive"] } base64 = "0.22.1" flate2 = "1.0" +indicatif = "0.17.8" # Disable for wasm32 and Win32 [target.'cfg(not(any(target_arch = "wasm32", all(target_arch = "x86", target_os = "windows"))))'.dev-dependencies] diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index 9f855a682..b873b87a3 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -169,6 +169,8 @@ fn add_member( use generate::CIPHERSUITE; mod generate { + use indicatif::ProgressBar; + use super::*; pub const GROUP_SIZES: &[usize] = &[2, 3, 4, 5, 10, 25, 50, 100]; @@ -177,42 +179,54 @@ mod generate { /// Create a group of `num` clients. /// All of them committed after joining. - pub fn setup(num: usize, variant: Option) -> Vec<(MlsGroup, Member)> { + pub fn setup( + num: usize, + variant: Option, + members: Option<(Vec, Vec)>, + ) -> Vec<(MlsGroup, Member)> { // We default to a bare group unless variant wants something else. let variant = variant.unwrap_or(SetupVariants::Bare); - let creator_provider = OpenMlsRustCrypto::default(); - let creator_credential = BasicCredential::new(format!("Creator").into()); - let creator_signer = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); - let creator_credential_with_key = CredentialWithKey { - credential: creator_credential.into(), - signature_key: creator_signer.to_public_vec().into(), - }; - let mls_group_create_config = MlsGroupCreateConfig::builder() .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) .ciphersuite(CIPHERSUITE) .build(); - // Create the group - let creator_group = MlsGroup::new( - &creator_provider, - &creator_signer, - &mls_group_create_config, - creator_credential_with_key.clone(), - ) - .expect("An unexpected error occurred."); + // If we have a previous group/member setup, let's use it. + // The creator is always at 0. + let mut members = if let Some(members) = members { + members.0.into_iter().zip(members.1.into_iter()).collect() + } else { + // Create a new setup. + let creator_provider = OpenMlsRustCrypto::default(); + let creator_credential = BasicCredential::new(format!("Creator").into()); + let creator_signer = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); + let creator_credential_with_key = CredentialWithKey { + credential: creator_credential.into(), + signature_key: creator_signer.to_public_vec().into(), + }; + + // Create the group + let creator_group = MlsGroup::new( + &creator_provider, + &creator_signer, + &mls_group_create_config, + creator_credential_with_key.clone(), + ) + .expect("An unexpected error occurred."); - let mut members: Vec<(MlsGroup, Member)> = vec![( - creator_group, - Member { - provider: creator_provider, - credential_with_key: creator_credential_with_key, - signer: creator_signer, - }, - )]; + vec![( + creator_group, + Member { + provider: creator_provider, + credential_with_key: creator_credential_with_key, + signer: creator_signer, + }, + )] + }; - for member_i in 0..num - 1 { + let pb = ProgressBar::new((num - members.len()) as u64); + for member_i in members.len()..num { let (member_provider, signer, credential_with_key, key_package) = new_member(&format!("Member {member_i}")); @@ -273,13 +287,17 @@ mod generate { signer, }, )); + pb.inc(1); } + pb.finish(); // Depending on the variant we do something once everyone was added. match variant { SetupVariants::Bare => (), // Nothing to do in this case. SetupVariants::CommitAfterJoin => (), // Noting to do in this case. SetupVariants::CommitToFullGroup => { + println!("Commit to the full group."); + let pb = ProgressBar::new((num - members.len()) as u64); // Every member commits and everyone else processes it. for i in 0..members.len() { let (member_i_group, member_i) = &mut members[i]; @@ -291,6 +309,7 @@ mod generate { } } } + pb.finish(); } } @@ -384,6 +403,8 @@ struct Args { mod util { use std::path::Path; + use itertools::Itertools; + use super::{generate, *}; const GROUPS_PATH: &str = "large-balanced-group-groups.json.gzip"; @@ -440,18 +461,22 @@ mod util { let mut members = vec![]; let group_sizes = group_sizes.unwrap_or(generate::GROUP_SIZES.to_vec()); - println!("Writing groups for benchmarks {group_sizes:?}..."); - for num in group_sizes { + println!("Generating groups for benchmarks {group_sizes:?}..."); + let mut smaller_groups = None; + for num in group_sizes.into_iter().sorted() { println!("Generating group of size {num} ..."); // Generate and write out groups. - let new_groups = generate::setup(num, variant); + let new_groups = generate::setup(num, variant, smaller_groups); let (new_groups, new_members): (Vec, Vec) = new_groups.into_iter().unzip(); + smaller_groups = Some((new_groups.clone(), new_members.clone())); let new_members: Vec<(Vec, Vec, Vec)> = new_members.into_iter().map(|m| m.serialize()).collect(); groups.push(new_groups); members.push(new_members); } + + println!("Writing out files."); let file = File::create(groups_file(&path)).unwrap(); let mut writer = flate2::write::GzEncoder::new(file, flate2::Compression::default()); serde_json::to_writer(&mut writer, &groups).unwrap(); From 58df3c7639e5ca3c2e52a35a97c0dbeffd7d77bf Mon Sep 17 00:00:00 2001 From: Jan Winkelmann <146678+keks@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:27:34 +0200 Subject: [PATCH 09/43] Make backtrace an optional feature (#1602) Co-authored-by: Jan Winkelmann (keks) --- openmls/Cargo.toml | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 0137d32be..835524003 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -25,52 +25,52 @@ serde_json = { version = "1.0", optional = true } itertools = { version = "0.10", optional = true } openmls_rust_crypto = { version = "0.2.0", path = "../openmls_rust_crypto", optional = true } openmls_basic_credential = { version = "0.2.0", path = "../basic_credential", optional = true, features = [ - "clonable", - "test-utils", + "clonable", + "test-utils", ] } wasm-bindgen-test = { version = "0.3.40", optional = true } getrandom = { version = "0.2.12", optional = true, features = ["js"] } fluvio-wasm-timer = { version = "0.2.5", optional = true } openmls_memory_storage = { path = "../memory_storage", features = [ - "test-utils", + "test-utils", ], optional = true } openmls_test = { path = "../openmls_test", optional = true } openmls_libcrux_crypto = { path = "../libcrux_crypto", optional = true } once_cell = { version = "1.19.0", optional = true } [features] -default = ["backtrace"] crypto-subtle = [] # Enable subtle crypto APIs that have to be used with care. test-utils = [ - "dep:serde_json", - "dep:itertools", - "dep:openmls_rust_crypto", - "dep:rand", - "dep:wasm-bindgen-test", - "dep:openmls_basic_credential", - "dep:openmls_memory_storage", - "dep:openmls_test", - "dep:once_cell", + "dep:serde_json", + "dep:itertools", + "dep:openmls_rust_crypto", + "dep:rand", + "dep:wasm-bindgen-test", + "dep:openmls_basic_credential", + "dep:openmls_memory_storage", + "dep:openmls_test", + "dep:once_cell", + "backtrace", ] +backtrace = ["dep:backtrace"] libcrux-provider = [ - "dep:openmls_libcrux_crypto", - "openmls_test?/libcrux-provider", + "dep:openmls_libcrux_crypto", + "openmls_test?/libcrux-provider", ] crypto-debug = [] # ☣️ Enable logging of sensitive cryptographic information content-debug = [] # ☣️ Enable logging of sensitive message content js = [ - "dep:getrandom", - "dep:fluvio-wasm-timer", + "dep:getrandom", + "dep:fluvio-wasm-timer", ] # enable js randomness source for provider [dev-dependencies] -backtrace = "0.3" criterion = { version = "^0.5", default-features = false } # need to disable default features for wasm hex = { version = "0.4", features = ["serde"] } itertools = "0.10" lazy_static = "1.4" openmls_traits = { version = "0.2.0", path = "../traits", features = [ - "test-utils", + "test-utils", ] } pretty_env_logger = "0.5" tempfile = "3" From 287ad2b0d2a38d2231d7a044055f67312f29e901 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Fri, 5 Jul 2024 09:33:19 +0200 Subject: [PATCH 10/43] add usage infos and ci --- .github/workflows/benches.yml | 9 +++++++-- openmls/examples/large-groups.rs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benches.yml b/.github/workflows/benches.yml index ea41890bf..1f45e70e7 100644 --- a/.github/workflows/benches.yml +++ b/.github/workflows/benches.yml @@ -1,8 +1,8 @@ name: Benchmarks -concurrency: +concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: true on: push: @@ -32,3 +32,8 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Benchmarks run: cargo bench -p openmls --verbose + + - name: Large groups + run: | + cargo run -p openmls --example large-groups --release -- --write -g 2 3 + cargo run -p openmls --example large-groups --release -- -g 3 diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index b873b87a3..3d968a6f7 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -379,24 +379,38 @@ macro_rules! bench { }}; } +/// The different group setups for the benchmarks. #[derive(clap::ValueEnum, Clone, Copy, Debug)] enum SetupVariants { + /// No messages are sent after the setup. Bare, + + /// Every member sends a commit directly after joining the group. CommitAfterJoin, + + /// Every member sends a commit after everyone was added to the group. CommitToFullGroup, } +/// A tool to benchmark openmls (large) groups. +/// +/// The benchmarks need to write a setup first that is then read to run the benchmarks. #[derive(Parser)] struct Args { + /// Write out the setup (groups and states) #[clap(short, long, action)] write: bool, + /// The file to read or write. #[clap(short, long)] data: Option, + /// The group sizes to run or generate. + /// This has to be a list of values, separated by spaces, e.g. 2 3 5 10 #[clap(short, long, value_delimiter = ' ', num_args = 1..)] groups: Option>, + /// The group setup to use. #[clap(short, long)] setup: Option, } From 6adcaf0f8a240d72fdaff9a38734b64dae5b9edc Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Mon, 8 Jul 2024 15:13:11 +0200 Subject: [PATCH 11/43] Move some external commit tests to MlsGroup (#1608) * Move external commit tests to MlsGroup * Change module docs --- .../group/core_group/test_external_init.rs | 233 +--------------- openmls/src/group/tests/mod.rs | 2 + .../src/group/tests/test_external_commit.rs | 257 ++++++++++++++++++ 3 files changed, 262 insertions(+), 230 deletions(-) create mode 100644 openmls/src/group/tests/test_external_commit.rs diff --git a/openmls/src/group/core_group/test_external_init.rs b/openmls/src/group/core_group/test_external_init.rs index 4aa36d837..addf31f2f 100644 --- a/openmls/src/group/core_group/test_external_init.rs +++ b/openmls/src/group/core_group/test_external_init.rs @@ -1,242 +1,15 @@ use crate::{ - framing::{ - test_framing::setup_alice_bob_group, FramedContentBody, FramingParameters, WireFormat, - }, + framing::test_framing::setup_alice_bob_group, group::{ - errors::ExternalCommitError, - public_group::errors::CreationFromExternalError, - test_core_group::{setup_alice_group, setup_client}, - CreateCommitParams, + errors::ExternalCommitError, public_group::errors::CreationFromExternalError, + test_core_group::setup_client, CreateCommitParams, }, - messages::proposals::{ProposalOrRef, ProposalType}, storage::OpenMlsProvider, }; use openmls_traits::prelude::*; use super::{proposals::ProposalStore, CoreGroup}; -#[openmls_test::openmls_test] -fn test_external_init() { - let ( - framing_parameters, - mut group_alice, - alice_signer, - mut group_bob, - bob_signer, - bob_credential_with_key, - ) = setup_alice_bob_group(ciphersuite, provider); - - // Now set up Charlie and try to init externally. - let (charlie_credential, _charlie_kpb, charlie_signer, _charlie_pk) = - setup_client("Charlie", ciphersuite, provider); - - // Have Alice export everything that Charly needs. - let verifiable_group_info = group_alice - .export_group_info(provider.crypto(), &alice_signer, true) - .unwrap() - .into_verifiable_group_info(); - - let proposal_store = ProposalStore::new(); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) - .credential_with_key(charlie_credential) - .build(); - let (mut group_charly, create_commit_result) = CoreGroup::join_by_external_commit( - provider, - &charlie_signer, - params, - None, - verifiable_group_info, - ) - .expect("Error initializing group externally."); - - // Have alice and bob process the commit resulting from external init. - let proposal_store = ProposalStore::default(); - - let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) - .expect("error staging commit"); - group_alice - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - - let staged_commit = group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) - .expect("error staging commit"); - group_bob - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - - // Have charly process their own staged commit - group_charly - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own external commit"); - - assert_eq!( - group_charly.export_secret(provider.crypto(), "", &[], ciphersuite.hash_length()), - group_bob.export_secret(provider.crypto(), "", &[], ciphersuite.hash_length()) - ); - - assert_eq!( - group_charly.public_group().export_ratchet_tree(), - group_bob.public_group().export_ratchet_tree() - ); - - // Check if charly can create valid commits - let proposal_store = ProposalStore::default(); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) - .build(); - let create_commit_result = group_charly - .create_commit(params, provider, &charlie_signer) - .expect("Error creating commit"); - - let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) - .expect("error staging commit"); - group_alice - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - - group_charly - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging commit"); - - // Now we assume that Bob somehow lost his group state and wants to add - // themselves back through an external commit. - - // Have Alice export everything that Bob needs. - let verifiable_group_info = group_alice - .export_group_info(provider.crypto(), &alice_signer, false) - .unwrap() - .into_verifiable_group_info(); - let ratchet_tree = group_alice.public_group().export_ratchet_tree(); - - let proposal_store = ProposalStore::new(); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) - .credential_with_key(bob_credential_with_key) - .build(); - let (mut new_group_bob, create_commit_result) = CoreGroup::join_by_external_commit( - provider, - &bob_signer, - params, - Some(ratchet_tree.into()), - verifiable_group_info, - ) - .expect("Error initializing group externally."); - - // Let's make sure there's a remove in the commit. - let contains_remove = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => { - commit - .proposals - .as_slice() - .iter() - .find(|&proposal| match proposal { - ProposalOrRef::Proposal(proposal) => proposal.is_type(ProposalType::Remove), - _ => false, - }) - } - _ => panic!("Wrong content type."), - } - .is_some(); - assert!(contains_remove); - - // Have alice and charly process the commit resulting from external init. - let proposal_store = ProposalStore::default(); - let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) - .expect("error staging commit"); - group_alice - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - - let staged_commit = group_charly - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) - .expect("error staging commit"); - group_charly - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - - // Have Bob process his own staged commit - new_group_bob - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own external commit"); - - assert_eq!( - group_charly.export_secret(provider.crypto(), "", &[], ciphersuite.hash_length()), - new_group_bob.export_secret(provider.crypto(), "", &[], ciphersuite.hash_length()) - ); - - assert_eq!( - group_charly.public_group().export_ratchet_tree(), - new_group_bob.public_group().export_ratchet_tree() - ); -} - -#[openmls_test::openmls_test] -fn test_external_init_single_member_group() { - let (mut group_alice, _alice_credential_with_key, alice_signer, _alice_pk) = - setup_alice_group(ciphersuite, provider); - - // Framing parameters - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - - // Now set up charly and try to init externally. - let (charly_credential, _charly_kpb, charly_signer, _charly_pk) = - setup_client("Charly", ciphersuite, provider); - - // Have Alice export everything that Charly needs. - let verifiable_group_info = group_alice - .export_group_info(provider.crypto(), &alice_signer, false) - .unwrap() - .into_verifiable_group_info(); - let ratchet_tree = group_alice.public_group().export_ratchet_tree(); - - let proposal_store = ProposalStore::new(); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) - .credential_with_key(charly_credential) - .build(); - let (mut group_charly, create_commit_result) = CoreGroup::join_by_external_commit( - provider, - &charly_signer, - params, - Some(ratchet_tree.into()), - verifiable_group_info, - ) - .expect("Error initializing group externally."); - - // Have alice and bob process the commit resulting from external init. - let proposal_store = ProposalStore::default(); - let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) - .expect("error staging commit"); - group_alice - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - - group_charly - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own external commit"); - - assert_eq!( - group_charly.export_secret(provider.crypto(), "", &[], ciphersuite.hash_length()), - group_alice.export_secret(provider.crypto(), "", &[], ciphersuite.hash_length()) - ); - - assert_eq!( - group_charly.public_group().export_ratchet_tree(), - group_alice.public_group().export_ratchet_tree() - ); -} - #[openmls_test::openmls_test] fn test_external_init_broken_signature() { let ( diff --git a/openmls/src/group/tests/mod.rs b/openmls/src/group/tests/mod.rs index c78c16e77..4a17955d7 100644 --- a/openmls/src/group/tests/mod.rs +++ b/openmls/src/group/tests/mod.rs @@ -13,6 +13,8 @@ mod test_commit_validation; #[cfg(test)] mod test_encoding; #[cfg(test)] +mod test_external_commit; +#[cfg(test)] mod test_external_commit_validation; #[cfg(test)] mod test_framing; diff --git a/openmls/src/group/tests/test_external_commit.rs b/openmls/src/group/tests/test_external_commit.rs new file mode 100644 index 000000000..b1e8ad21f --- /dev/null +++ b/openmls/src/group/tests/test_external_commit.rs @@ -0,0 +1,257 @@ +//! This module contains tests for external commit messages +use tls_codec::{Deserialize, Serialize}; + +use crate::{ + framing::{MlsMessageIn, Sender}, + group::{ + tests::utils::generate_credential_with_key, MlsGroup, MlsGroupCreateConfig, + PURE_PLAINTEXT_WIRE_FORMAT_POLICY, + }, + prelude::ProcessedMessageContent, +}; + +// External Commit in a group of 1 & 2 members and resync +#[openmls_test::openmls_test] +fn test_external_commit() { + // Generate credentials with keys + let alice_credential = + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), provider); + + let bob_credential = + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), provider); + + let charlie_credential = generate_credential_with_key( + "Charlie".into(), + ciphersuite.signature_algorithm(), + provider, + ); + + // Define the MlsGroup configuration + let mls_group_create_config = MlsGroupCreateConfig::builder() + .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .ciphersuite(ciphersuite) + .build(); + + // Alice creates a group + let mut alice_group = MlsGroup::new( + provider, + &alice_credential.signer, + &mls_group_create_config, + alice_credential.credential_with_key.clone(), + ) + .unwrap(); + + // === Single member group external join === + + // Bob wants to commit externally. + + let verifiable_group_info = alice_group + .export_group_info(provider, &alice_credential.signer, false) + .unwrap() + .into_verifiable_group_info() + .unwrap(); + let tree_option = alice_group.export_ratchet_tree(); + + let (mut bob_group, public_message_commit, _) = MlsGroup::join_by_external_commit( + provider, + &bob_credential.signer, + Some(tree_option.into()), + verifiable_group_info, + alice_group.configuration(), + &[], + bob_credential.credential_with_key.clone(), + ) + .unwrap(); + bob_group.merge_pending_commit(provider).unwrap(); + + let public_message_commit = { + let serialized_message = public_message_commit.tls_serialize_detached().unwrap(); + + MlsMessageIn::tls_deserialize(&mut serialized_message.as_slice()) + .unwrap() + .into_plaintext() + .unwrap() + }; + + assert!(matches!( + public_message_commit.sender(), + Sender::NewMemberCommit + )); + + // Alice processes Bob's Commit + + let processed_message = alice_group + .process_message(provider, public_message_commit) + .unwrap(); + + match processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + alice_group + .merge_staged_commit(provider, *staged_commit) + .unwrap(); + } + _ => panic!("Expected Commit message"), + } + + // Compare Alice's and Bob's private & public state + + assert_eq!( + alice_group.export_secret(provider, "label", b"context", 32), + bob_group.export_secret(provider, "label", b"context", 32) + ); + assert_eq!( + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() + ); + + // === 2-member group external join === + + // Charlie wants to commit externally. + + let verifiable_group_info = alice_group + .export_group_info(provider, &alice_credential.signer, false) + .unwrap() + .into_verifiable_group_info() + .unwrap(); + let tree_option = alice_group.export_ratchet_tree(); + + let (mut charlie_group, public_message_commit, _) = MlsGroup::join_by_external_commit( + provider, + &charlie_credential.signer, + Some(tree_option.into()), + verifiable_group_info, + alice_group.configuration(), + &[], + charlie_credential.credential_with_key.clone(), + ) + .unwrap(); + charlie_group.merge_pending_commit(provider).unwrap(); + + // Alice & Bob process Charlie's Commit + + let charlie_commit = MlsMessageIn::from(public_message_commit) + .into_plaintext() + .unwrap(); + + let alice_processed_message = alice_group + .process_message(provider, charlie_commit.clone()) + .unwrap(); + + match alice_processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + alice_group + .merge_staged_commit(provider, *staged_commit) + .unwrap(); + } + _ => panic!("Expected Commit message"), + } + + let bob_processed_message = bob_group.process_message(provider, charlie_commit).unwrap(); + + match bob_processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + bob_group + .merge_staged_commit(provider, *staged_commit) + .unwrap(); + } + _ => panic!("Expected Commit message"), + } + + // Compare Alice's, Bob's and Charlie's private & public state + + assert_eq!( + alice_group.export_secret(provider, "label", b"context", 32), + bob_group.export_secret(provider, "label", b"context", 32) + ); + assert_eq!( + alice_group.export_secret(provider, "label", b"context", 32), + charlie_group.export_secret(provider, "label", b"context", 32) + ); + assert_eq!( + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() + ); + assert_eq!( + alice_group.export_ratchet_tree(), + charlie_group.export_ratchet_tree() + ); + + // === Resync === + + // Alice wants to resync + + let verifiable_group_info = bob_group + .export_group_info(provider, &bob_credential.signer, false) + .unwrap() + .into_verifiable_group_info() + .unwrap(); + let tree_option = bob_group.export_ratchet_tree(); + + let (mut alice_group, public_message_commit, _) = MlsGroup::join_by_external_commit( + provider, + &alice_credential.signer, + Some(tree_option.into()), + verifiable_group_info, + bob_group.configuration(), + &[], + alice_credential.credential_with_key.clone(), + ) + .unwrap(); + alice_group.merge_pending_commit(provider).unwrap(); + + // Bob & Charlie process Alice's Commit + + let alice_commit = MlsMessageIn::from(public_message_commit) + .into_plaintext() + .unwrap(); + + let bob_processed_message = bob_group + .process_message(provider, alice_commit.clone()) + .unwrap(); + + match bob_processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + // Make sure there is a remove proposal for Alice + let remove_proposals = staged_commit.remove_proposals().collect::>(); + assert_eq!(remove_proposals.len(), 1); + let remove_proposal = &remove_proposals[0]; + assert_eq!(remove_proposal.remove_proposal().removed().u32(), 0); + bob_group + .merge_staged_commit(provider, *staged_commit) + .unwrap(); + } + _ => panic!("Expected Commit message"), + } + + let charlie_processed_message = charlie_group + .process_message(provider, alice_commit) + .unwrap(); + + match charlie_processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + charlie_group + .merge_staged_commit(provider, *staged_commit) + .unwrap(); + } + _ => panic!("Expected Commit message"), + } + + // Compare Alice's, Bob's and Charlie's private & public state + + assert_eq!( + alice_group.export_secret(provider, "label", b"context", 32), + bob_group.export_secret(provider, "label", b"context", 32) + ); + assert_eq!( + alice_group.export_secret(provider, "label", b"context", 32), + charlie_group.export_secret(provider, "label", b"context", 32) + ); + assert_eq!( + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() + ); + assert_eq!( + alice_group.export_ratchet_tree(), + charlie_group.export_ratchet_tree() + ); +} From 4d73c988639a648417d62cfa4e634626b8f32c10 Mon Sep 17 00:00:00 2001 From: Jan Winkelmann <146678+keks@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:37:33 +0200 Subject: [PATCH 12/43] Assorted Documentation Fixes for v0.6 (#1603) Co-authored-by: Jan Winkelmann (keks) Co-authored-by: Konrad Kohbrok --- book/src/SUMMARY.md | 1 + book/src/app_validation.md | 62 +++++++++++++++++++++++++++++ book/src/performance.md | 4 +- book/src/traits/traits.md | 29 +++++++------- book/src/user_manual/README.md | 6 ++- book/src/user_manual/persistence.md | 14 +++---- traits/src/storage.rs | 2 + 7 files changed, 93 insertions(+), 25 deletions(-) diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 3763cdf5a..8a03b5b3f 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -8,6 +8,7 @@ - [Group configuration](user_manual/group_config.md) - [Creating groups](user_manual/create_group.md) - [Join a group from a Welcome message](user_manual/join_from_welcome.md) + - [Join a group from an External Commit message](user_manual/join_from_external_commit.md) - [Adding members to a group](user_manual/add_members.md) - [Removing members from a group](user_manual/remove_members.md) - [Updating own key package](user_manual/updates.md) diff --git a/book/src/app_validation.md b/book/src/app_validation.md index bcebc5dc4..35017555b 100644 --- a/book/src/app_validation.md +++ b/book/src/app_validation.md @@ -4,6 +4,64 @@ > > **⚠️** This chapter is work in progress (see [#1504](https://github.com/openmls/openmls/issues/1504)). +## Credential Validation + +### Acceptable Presented Identifiers + +> The application using MLS is responsible for specifying which identifiers +> it finds acceptable for each member in a group. In other words, following +> the model that [[RFC6125]] describes for TLS, the application maintains a list +> of "reference identifiers" for the members of a group, and the credentials +> provide "presented identifiers". A member of a group is authenticated by first +> validating that the member's credential legitimately represents some presented +> identifiers, and then ensuring that the reference identifiers for the member +> are authenticated by those presented identifiers +> +> -- [RFC9420, Section 5.3.1](https://www.rfc-editor.org/rfc/rfc9420.html#section-5.3.1-1) +> +### Validity of Updated Presented Identifiers + +> In cases where a member's credential is being replaced, such as the Update and +> Commit cases above, the AS MUST also verify that the set of presented identifiers +> in the new credential is valid as a successor to the set of presented identifiers +> in the old credential, according to the application's policy. +> +> -- [RFC9420, Section 5.3.1](https://www.rfc-editor.org/rfc/rfc9420.html#section-5.3.1-5) + +### Application ID is Not Authenticed by AS + +> However, applications MUST NOT rely on the data in an application_id extension +> as if it were authenticated by the Authentication Service, and SHOULD gracefully +> handle cases where the identifier presented is not unique. +> +> -- [RFC9420, Section 5.3.3](https://www.rfc-editor.org/rfc/rfc9420.html#section-5.3.3-6) + +## LeafNode Validation + +### Specifying the Maximum Total Acceptable Lifetime + +> Applications MUST define a maximum total lifetime that is acceptable for a +> LeafNode, and reject any LeafNode where the total lifetime is longer than this +> duration. In order to avoid disagreements about whether a LeafNode has a valid +> lifetime, the clients in a group SHOULD maintain time synchronization (e.g., +> using the Network Time Protocol [[RFC5905]]). +> +> -- [RFC9420, Section 7.2](https://www.rfc-editor.org/rfc/rfc9420.html#section-7.2-10) + +## PrivateMessage Validation + +### Structure of AAD is Application-Defined + +> It is up to the application to decide what authenticated_data to provide and +> how much padding to add to a given message (if any). The overall size of the +> AAD and ciphertext MUST fit within the limits established for the group's AEAD +> algorithm in [[CFRG-AEAD-LIMITS]]. +> +> -- [RFC9420, Section 6.3.1](https://www.rfc-editor.org/rfc/rfc9420.html#section-6.3.1-11) + +Therefore, the application must also validate whether the AAD adheres to the +prescribed format. + ## Proposal Validation When processing a commit, the application has to ensure that the application @@ -24,3 +82,7 @@ The RFC requires the following check Since OpenMLS does not know the relevant policies, the application MUST ensure that the credentials are checked according to the policy. + +[RFC6125]: https://www.rfc-editor.org/rfc/rfc6125.html +[RFC5905]: https://www.rfc-editor.org/rfc/rfc5905.html +[CFRG-AEAD-LIMITS]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aead-limits-08 diff --git a/book/src/performance.md b/book/src/performance.md index 76a437eeb..1971dcdc8 100644 --- a/book/src/performance.md +++ b/book/src/performance.md @@ -39,9 +39,9 @@ It is the same scenario as the somewhat stable group but with a very small Y and In addition to the three scenarios above extreme and corner cases are interesting. -### Every second leave is blank +### Every second leaf is blank -Only every second leave in the tree is non-blank. +Only every second leaf in the tree is non-blank. ## Use Case Scenarios diff --git a/book/src/traits/traits.md b/book/src/traits/traits.md index deba322fb..205526048 100644 --- a/book/src/traits/traits.md +++ b/book/src/traits/traits.md @@ -57,18 +57,24 @@ This trait defines all cryptographic functions required by OpenMLS. In particula This trait defines an API for a storage backend that is used for all OpenMLS persistence. -The store provides functions to `store`, `read`, and `delete` values. -Note that it does not allow updating values. -Instead, entries must be deleted and newly stored. +The store provides functions for reading and updating stored values. +Each sort of value has separate methods for accessing or mutating the state. +In order to decouple the provider from the OpenMLS implementation, while still +having legible types at the provider, there are traits that mirror all the types +stored by OpenMLS. The provider methods use values constrained by these traits as +as arguments. ```rust,no_run,noplayground -{{#include ../../../traits/src/storage.rs:16:25}} +{{#include ../../../traits/src/storage.rs:traits}} ``` -The trait is generic over a `VERSION`, which is used to ensure that the values +The traits are generic over a `VERSION`, which is used to ensure that the values that are persisted can be upgraded when OpenMLS changes the stored structs. -Every function takes `Key` and `Value` arguments. +The traits used as arguments to the storage methods are constrained to implement +the `Key` or `Entity` traits as well, depending on whether they are only used for +addressing (in which case they are a `Key`) or whether they represent a stored +value (in which case they are an `Entity`). ```rust,no_run,noplayground {{#include ../../../traits/src/storage.rs:key_trait}} @@ -78,13 +84,6 @@ Every function takes `Key` and `Value` arguments. {{#include ../../../traits/src/storage.rs:entity_trait}} ``` -To ensure that each function takes the correct input, they use trait bounds. -These are the available traits. - -```rust,no_run,noplayground -{{#include ../../../traits/src/storage.rs:traits}} -``` - An implementation of the storage trait should ensure that it can address and efficiently handle values. @@ -140,7 +139,9 @@ Some OpenMLS APIs require only one of the sub-traits, though. ## Implementation Notes It is not necessary to implement all sub-traits if one functionality is missing. -Suppose you want to use a persisting key store. In that case, it is sufficient to do a new implementation of the key store trait and combine it with one of the provided crypto and randomness trait implementations. +Suppose you want to use a persisting storage provider. In that case, it is +sufficient to do a new implementation of the `StorageProvider` trait and +combine it with one of the provided crypto and randomness trait implementations. [rust crypto]: https://crates.io/crates/openmls_rust_crypto [libcrux crypto]: https://crates.io/crates/openmls_libcrux_crypto diff --git a/book/src/user_manual/README.md b/book/src/user_manual/README.md index abcd6a643..ba3e00661 100644 --- a/book/src/user_manual/README.md +++ b/book/src/user_manual/README.md @@ -5,7 +5,10 @@ The user manual describes how to use the different parts of the OpenMLS API. ## Prerequisites Most operations in OpenMLS require a `provider` object that provides all required cryptographic algorithms via the [`OpenMlsCryptoProvider`] trait. -Currently, there are two implementations available through the [openmls_rust_crypto] crate. +Currently, there are two implementations available: + +- one through the [openmls_rust_crypto] crate. +- one through the [openmls_libcrux_crypto] crate. Thus, you can create the `provider` object for the following examples using ... @@ -15,3 +18,4 @@ Thus, you can create the `provider` object for the following examples using ... [`openmlscryptoprovider`]: https://docs.rs/openmls/latest/openmls/prelude/trait.OpenMlsCryptoProvider.html [openmls_rust_crypto]: https://crates.io/crates/openmls_rust_crypto +[openmls_libcrux_crypto]: https://crates.io/crates/openmls_libcrux_crypto diff --git a/book/src/user_manual/persistence.md b/book/src/user_manual/persistence.md index a58fcb30d..56b423754 100644 --- a/book/src/user_manual/persistence.md +++ b/book/src/user_manual/persistence.md @@ -1,13 +1,11 @@ # Persistence of Group Data -The state of a given `MlsGroup` instance can be written or read at any time using the `.save()` or `.load()` functions respectively. The functions take as input a struct implementing either the `Write` (`.save()`) or `Read` (`.load()`) trait. - -Since some group operations might or might not change the `MlsGroup` state depending on the context, the group maintains the `state_changed` flag, which is set to `true` whenever the state is changed by an `MlsGroup` function. The state of the flag can be queried using the `.state_changed()` function. - -## Group Lockout Upon State Loss - -MLS provides strong Post-Compromise Security properties, which means that key material is regularly refreshed and old key material becomes stale very quickly. Consequently, regularly persisting state is important, especially after the client has created a commit or issued an Update proposal, thus introducing new key material into the group. A loss of state in such a situation is only recoverable in specific cases where the commit was rejected by the Delivery Service or if the proposed Update was not committed. A re-join is required in most cases to continue participating in a group after a loss of group state. To avoid a loss of state and the associated re-join, persisting `MlsGroup` state after each state-changing group operation is mandatory. +The state of a given `MlsGroup` instance is continuously written to the configured +`StorageProvider`. Later, the `MlsGroup` can be loaded from the provider using +the `load` constructor, which can be called with the respective storage provider +as well as the `GroupId` of the group to be loaded. For this to work, the group +must have been written to the provider previously. ## Forward-Secrecy Considerations -The `MlsGroup` state that is persisted using the `.save()` function contains private key material. As a consequence, the application needs to delete old group states to achieve Forward-Secrecy w.r.t. that key material. Since, as detailed above, an old group state is stale immediately after most group operations, we recommend deleting old group states as soon as a new one has been written. +OpenMLS uses the `StorageProvider` to store sensitive key material. To achieve forward-secrecy (i.e. to prevent an adversary from decrypting messages sent in the past if a client is compromised), OpenMLS frequently deletes previously used key material through calls to the `StorageProvider`. `StorageProvider` implementations must thus take care to ensure that values deleted through any of the `delete_` functions of the trait are irrevocably deleted and that no copies are kept. diff --git a/traits/src/storage.rs b/traits/src/storage.rs index 6c14781ad..5e6266766 100644 --- a/traits/src/storage.rs +++ b/traits/src/storage.rs @@ -266,11 +266,13 @@ pub trait StorageProvider { group_id: &GroupId, ) -> Result, Self::Error>; + ///ANCHOR: own_leaf_nodes /// Returns the own leaf nodes for the group with given id fn own_leaf_nodes, LeafNode: traits::LeafNode>( &self, group_id: &GroupId, ) -> Result, Self::Error>; + ///ANCHOR_END: own_leaf_nodes /// Returns the AAD for the group with given id /// If the value has not been set, returns an empty vector. From 8d97a323af3fe33813d4be69c32d3e034c05095a Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Tue, 9 Jul 2024 18:10:59 +0200 Subject: [PATCH 13/43] De-duplicate ProposalStore (#1601) * De-duplicate ProposalStore * Remove comment * Remove ProposalStore from CreateCommitParams & empty ProposalSTore in public group after merging a commit --- openmls/src/extensions/test_extensions.rs | 11 +- openmls/src/framing/test_framing.rs | 59 +++---- .../group/core_group/create_commit_params.rs | 27 +--- openmls/src/group/core_group/mod.rs | 12 +- .../core_group/new_from_external_init.rs | 1 - openmls/src/group/core_group/process.rs | 9 +- openmls/src/group/core_group/staged_commit.rs | 16 +- .../src/group/core_group/test_core_group.rs | 100 ++++++------ .../core_group/test_create_commit_params.rs | 3 - .../group/core_group/test_external_init.rs | 4 +- .../src/group/core_group/test_proposals.rs | 46 +++--- openmls/src/group/mls_group/application.rs | 2 +- openmls/src/group/mls_group/builder.rs | 3 +- openmls/src/group/mls_group/creation.rs | 4 - openmls/src/group/mls_group/membership.rs | 8 +- openmls/src/group/mls_group/mod.rs | 28 ++-- openmls/src/group/mls_group/processing.rs | 7 +- openmls/src/group/mls_group/proposal.rs | 9 +- openmls/src/group/mls_group/ser.rs | 3 - openmls/src/group/mls_group/test_mls_group.rs | 4 +- openmls/src/group/mls_group/updates.rs | 5 +- openmls/src/group/public_group/mod.rs | 20 ++- openmls/src/group/public_group/process.rs | 11 +- .../src/group/public_group/staged_commit.rs | 12 +- openmls/src/group/tests/kat_messages.rs | 10 +- .../src/group/tests/test_commit_validation.rs | 7 +- openmls/src/group/tests/test_encoding.rs | 34 ++-- openmls/src/group/tests/test_group.rs | 153 +++++++++--------- openmls/src/group/tests/utils.rs | 10 +- .../kats/kat_message_protection.rs | 10 +- 30 files changed, 296 insertions(+), 332 deletions(-) diff --git a/openmls/src/extensions/test_extensions.rs b/openmls/src/extensions/test_extensions.rs index 492c5bbc6..c76c62c35 100644 --- a/openmls/src/extensions/test_extensions.rs +++ b/openmls/src/extensions/test_extensions.rs @@ -80,18 +80,17 @@ fn ratchet_tree_extension() { ) .expect("Could not create proposal."); - let proposal_store = ProposalStore::from_queued_proposal( + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = alice_group @@ -158,18 +157,18 @@ fn ratchet_tree_extension() { ) .expect("Could not create proposal."); - let proposal_store = ProposalStore::from_queued_proposal( + alice_group.proposal_store_mut().empty(); + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create staged proposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = alice_group diff --git a/openmls/src/framing/test_framing.rs b/openmls/src/framing/test_framing.rs index 124e21568..44876cacf 100644 --- a/openmls/src/framing/test_framing.rs +++ b/openmls/src/framing/test_framing.rs @@ -10,11 +10,7 @@ use crate::{ ciphersuite::signable::{Signable, SignatureError}, extensions::Extensions, framing::*, - group::{ - core_group::proposals::{ProposalStore, QueuedProposal}, - errors::*, - CreateCommitParams, - }, + group::{core_group::proposals::QueuedProposal, errors::*, CreateCommitParams}, key_packages::{test_key_packages::key_package, KeyPackageBundle}, schedule::psk::{store::ResumptionPskStore, PskSecret}, storage::OpenMlsProvider, @@ -437,18 +433,18 @@ fn unknown_sender(ciphersuite: Ciphersuite, provider: ) .expect("Could not create proposal."); - let mut proposal_store = ProposalStore::from_queued_proposal( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, alice_provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = group_alice @@ -481,8 +477,8 @@ fn unknown_sender(ciphersuite: Ciphersuite, provider: ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, alice_provider.crypto(), @@ -493,7 +489,6 @@ fn unknown_sender(ciphersuite: Ciphersuite, provider: let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = group_alice @@ -525,19 +520,23 @@ fn unknown_sender(ciphersuite: Ciphersuite, provider: ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - alice_provider.crypto(), - bob_remove_proposal, - ) - .expect("Could not create staged proposal."), - ); + let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( + ciphersuite, + alice_provider.crypto(), + bob_remove_proposal, + ) + .unwrap(); + + group_alice.proposal_store_mut().empty(); + group_charlie.proposal_store_mut().empty(); + + group_alice + .proposal_store_mut() + .add(queued_proposal.clone()); + group_charlie.proposal_store_mut().add(queued_proposal); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = group_alice @@ -545,12 +544,7 @@ fn unknown_sender(ciphersuite: Ciphersuite, provider: .expect("Error creating Commit"); let staged_commit = group_charlie - .read_keys_and_stage_commit( - &create_commit_result.commit, - &proposal_store, - &[], - alice_provider, - ) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], alice_provider) .expect("Charlie: Could not stage Commit"); group_charlie .merge_commit(charlie_provider, staged_commit) @@ -607,11 +601,8 @@ fn confirmation_tag_presence() { setup_alice_bob_group(ciphersuite, provider); // Alice does an update - let proposal_store = ProposalStore::default(); - let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(true) .build(); let mut create_commit_result = group_alice @@ -621,7 +612,7 @@ fn confirmation_tag_presence() { create_commit_result.commit.unset_confirmation_tag(); let err = group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect_err("No error despite missing confirmation tag."); assert_eq!(err, StageCommitError::ConfirmationTagMissing); @@ -674,18 +665,18 @@ pub(crate) fn setup_alice_bob_group( ) .expect("Could not create proposal."); - let proposal_store = ProposalStore::from_queued_proposal( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); diff --git a/openmls/src/group/core_group/create_commit_params.rs b/openmls/src/group/core_group/create_commit_params.rs index d30a52a8b..1fcadd199 100644 --- a/openmls/src/group/core_group/create_commit_params.rs +++ b/openmls/src/group/core_group/create_commit_params.rs @@ -3,8 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::{ - credentials::CredentialWithKey, framing::FramingParameters, group::ProposalStore, - messages::proposals::Proposal, + credentials::CredentialWithKey, framing::FramingParameters, messages::proposals::Proposal, }; #[cfg(doc)] @@ -19,7 +18,6 @@ pub(crate) enum CommitType { pub(crate) struct CreateCommitParams<'a> { framing_parameters: FramingParameters<'a>, // Mandatory - proposal_store: &'a ProposalStore, // Mandatory inline_proposals: Vec, // Optional force_self_update: bool, // Optional commit_type: CommitType, // Optional (default is `Member`) @@ -28,10 +26,6 @@ pub(crate) struct CreateCommitParams<'a> { pub(crate) struct TempBuilderCCPM0 {} -pub(crate) struct TempBuilderCCPM1<'a> { - framing_parameters: FramingParameters<'a>, -} - pub(crate) struct CreateCommitParamsBuilder<'a> { ccp: CreateCommitParams<'a>, } @@ -39,21 +33,11 @@ pub(crate) struct CreateCommitParamsBuilder<'a> { impl TempBuilderCCPM0 { pub(crate) fn framing_parameters( self, - framing_parameters: FramingParameters<'_>, - ) -> TempBuilderCCPM1<'_> { - TempBuilderCCPM1 { framing_parameters } - } -} - -impl<'a> TempBuilderCCPM1<'a> { - pub(crate) fn proposal_store( - self, - proposal_store: &'a ProposalStore, - ) -> CreateCommitParamsBuilder<'a> { + framing_parameters: FramingParameters, + ) -> CreateCommitParamsBuilder { CreateCommitParamsBuilder { ccp: CreateCommitParams { - framing_parameters: self.framing_parameters, - proposal_store, + framing_parameters, inline_proposals: vec![], force_self_update: true, commit_type: CommitType::Member, @@ -93,9 +77,6 @@ impl<'a> CreateCommitParams<'a> { pub(crate) fn framing_parameters(&self) -> &FramingParameters { &self.framing_parameters } - pub(crate) fn proposal_store(&self) -> &ProposalStore { - self.proposal_store - } pub(crate) fn inline_proposals(&self) -> &[Proposal] { &self.inline_proposals } diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 6f8633a35..b32f520b4 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -622,6 +622,16 @@ impl CoreGroup { &self.public_group } + /// Returns a reference to the proposal store. + pub(crate) fn proposal_store(&self) -> &ProposalStore { + self.public_group.proposal_store() + } + + /// Returns a mutable reference to the proposal store. + pub(crate) fn proposal_store_mut(&mut self) -> &mut ProposalStore { + self.public_group.proposal_store_mut() + } + /// Get the ciphersuite implementation used in this group. pub(crate) fn ciphersuite(&self) -> Ciphersuite { self.public_group.ciphersuite() @@ -851,7 +861,7 @@ impl CoreGroup { ciphersuite, provider.crypto(), sender.clone(), - params.proposal_store(), + self.proposal_store(), params.inline_proposals(), self.own_leaf_index(), ) diff --git a/openmls/src/group/core_group/new_from_external_init.rs b/openmls/src/group/core_group/new_from_external_init.rs index 29a708405..27b130c30 100644 --- a/openmls/src/group/core_group/new_from_external_init.rs +++ b/openmls/src/group/core_group/new_from_external_init.rs @@ -121,7 +121,6 @@ impl CoreGroup { let params = CreateCommitParams::builder() .framing_parameters(*params.framing_parameters()) - .proposal_store(params.proposal_store()) .inline_proposals(inline_proposals) .commit_type(CommitType::External) .credential_with_key(params_credential_with_key) diff --git a/openmls/src/group/core_group/process.rs b/openmls/src/group/core_group/process.rs index e203643af..5b56b1b05 100644 --- a/openmls/src/group/core_group/process.rs +++ b/openmls/src/group/core_group/process.rs @@ -8,7 +8,7 @@ use crate::{ }, }; -use super::{proposals::ProposalStore, *}; +use super::*; impl CoreGroup { /// This processing function does most of the semantic verifications. @@ -43,7 +43,6 @@ impl CoreGroup { &self, provider: &Provider, unverified_message: UnverifiedMessage, - proposal_store: &ProposalStore, old_epoch_keypairs: Vec, leaf_node_keypairs: Vec, ) -> Result> { @@ -80,7 +79,6 @@ impl CoreGroup { FramedContentBody::Commit(_) => { let staged_commit = self.stage_commit( &content, - proposal_store, old_epoch_keypairs, leaf_node_keypairs, provider, @@ -174,7 +172,6 @@ impl CoreGroup { provider: &Provider, message: impl Into, sender_ratchet_configuration: &SenderRatchetConfiguration, - proposal_store: &ProposalStore, own_leaf_nodes: &[LeafNode], ) -> Result> { let message: ProtocolMessage = message.into(); @@ -203,7 +200,6 @@ impl CoreGroup { self.process_unverified_message( provider, unverified_message, - proposal_store, old_epoch_keypairs, leaf_node_keypairs, ) @@ -294,7 +290,6 @@ impl CoreGroup { &mut self, provider: &Provider, staged_commit: StagedCommit, - proposal_store: &mut ProposalStore, ) -> Result<(), MergeCommitError> { // Save the past epoch let past_epoch = self.context().epoch(); @@ -307,7 +302,7 @@ impl CoreGroup { .add(past_epoch, message_secrets, leaves); } // Empty the proposal store - proposal_store.empty(); + self.proposal_store_mut().empty(); Ok(()) } } diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index cc02e78bb..2336ffd40 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -5,7 +5,7 @@ use public_group::diff::{apply_proposals::ApplyProposalsValues, StagedPublicGrou use self::public_group::staged_commit::PublicStagedCommitState; -use super::{super::errors::*, proposals::ProposalStore, *}; +use super::{super::errors::*, *}; use crate::{ ciphersuite::Secret, framing::mls_auth_content::AuthenticatedContent, treesync::node::encryption_keys::EncryptionKeyPair, @@ -124,7 +124,6 @@ impl CoreGroup { pub(crate) fn stage_commit( &self, mls_content: &AuthenticatedContent, - proposal_store: &ProposalStore, old_epoch_keypairs: Vec, leaf_node_keypairs: Vec, provider: &impl OpenMlsProvider, @@ -138,9 +137,9 @@ impl CoreGroup { let ciphersuite = self.ciphersuite(); - let (commit, proposal_queue, sender_index) = - self.public_group - .validate_commit(mls_content, proposal_store, provider.crypto())?; + let (commit, proposal_queue, sender_index) = self + .public_group + .validate_commit(mls_content, provider.crypto())?; // Create the provisional public group state (including the tree and // group context) and apply proposals. @@ -373,7 +372,7 @@ impl CoreGroup { .into()); } - // store the updated group state + // Store the updated group state let storage = provider.storage(); let group_id = self.group_id(); @@ -400,6 +399,9 @@ impl CoreGroup { .map_err(MergeCommitError::StorageError)?; } + // Empty the proposal store + self.proposal_store_mut().empty(); + Ok(Some(message_secrets)) } } @@ -411,7 +413,6 @@ impl CoreGroup { pub(crate) fn read_keys_and_stage_commit( &self, mls_content: &AuthenticatedContent, - proposal_store: &ProposalStore, own_leaf_nodes: &[LeafNode], provider: &impl OpenMlsProvider, ) -> Result { @@ -420,7 +421,6 @@ impl CoreGroup { self.stage_commit( mls_content, - proposal_store, old_epoch_keypairs, leaf_node_keypairs, provider, diff --git a/openmls/src/group/core_group/test_core_group.rs b/openmls/src/group/core_group/test_core_group.rs index 796c29261..b9b059a13 100644 --- a/openmls/src/group/core_group/test_core_group.rs +++ b/openmls/src/group/core_group/test_core_group.rs @@ -178,7 +178,7 @@ fn test_update_path() { framing_parameters, group_alice, _alice_signature_keys, - group_bob, + mut group_bob, bob_signature_keys, _bob_credential_with_key, ) = test_framing::setup_alice_bob_group(ciphersuite, provider); @@ -201,17 +201,19 @@ fn test_update_path() { &bob_signature_keys, ) .expect("Could not create proposal."); - let proposal_store = ProposalStore::from_queued_proposal( + + group_bob.proposal_store_mut().empty(); + group_bob.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), update_proposal_bob, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); + let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = group_bob @@ -263,7 +265,7 @@ fn test_update_path() { ); let staged_commit_res = - group_alice.read_keys_and_stage_commit(&broken_plaintext, &proposal_store, &[], provider); + group_alice.read_keys_and_stage_commit(&broken_plaintext, &[], provider); assert_eq!( staged_commit_res.expect_err("Successful processing of a broken commit."), StageCommitError::UpdatePathError(ApplyUpdatePathError::UnableToDecrypt) @@ -344,26 +346,27 @@ fn test_psks() { ) .expect("Could not create proposal"); - let mut proposal_store = ProposalStore::from_queued_proposal( + alice_group.proposal_store_mut().empty(); + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); - proposal_store.add( + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), psk_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); + log::info!(" >>> Creating commit ..."); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = alice_group @@ -377,7 +380,7 @@ fn test_psks() { .expect("error merging pending commit"); let ratchet_tree = alice_group.public_group().export_ratchet_tree(); - let group_bob = StagedCoreWelcome::new_from_welcome( + let mut group_bob = StagedCoreWelcome::new_from_welcome( create_commit_result .welcome_option .expect("An unexpected error occurred."), @@ -407,17 +410,19 @@ fn test_psks() { &bob_signature_keys, ) .expect("Could not create proposal."); - let proposal_store = ProposalStore::from_queued_proposal( + + group_bob.proposal_store_mut().empty(); + group_bob.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), update_proposal_bob, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); + let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let _create_commit_result = group_bob @@ -455,17 +460,19 @@ fn test_staged_commit_creation( &alice_signature_keys, ) .expect("Could not create proposal."); - let proposal_store = ProposalStore::from_queued_proposal( + + alice_group.proposal_store_mut().empty(); + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); + let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = alice_group @@ -524,11 +531,9 @@ fn test_own_commit_processing( .build(provider, &alice_signature_keys) .expect("Error creating group."); - let proposal_store = ProposalStore::default(); // Alice creates a commit let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(true) .build(); let create_commit_result = alice_group @@ -537,7 +542,7 @@ fn test_own_commit_processing( // Alice attempts to process her own commit let error = alice_group - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect_err("no error while processing own commit"); assert_eq!(error, StageCommitError::OwnCommit); } @@ -607,18 +612,18 @@ fn test_proposal_application_after_self_was_removed( ) .expect("Could not create proposal"); - let bob_add_proposal_store = ProposalStore::from_queued_proposal( + alice_group.proposal_store_mut().empty(); + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&bob_add_proposal_store) .force_self_update(false) .build(); let add_commit_result = alice_group @@ -668,39 +673,42 @@ fn test_proposal_application_after_self_was_removed( ) .expect("Could not create proposal"); - let mut remove_add_proposal_store = ProposalStore::from_queued_proposal( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_remove_proposal, - ) - .expect("Could not create QueuedProposal."), - ); + let queued_bob_remove_proposal = QueuedProposal::from_authenticated_content_by_ref( + ciphersuite, + provider.crypto(), + bob_remove_proposal, + ) + .unwrap(); - remove_add_proposal_store.add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - charlie_add_proposal, - ) - .expect("Could not create QueuedProposal."), - ); + let queued_charlie_add_propsal = QueuedProposal::from_authenticated_content_by_ref( + ciphersuite, + provider.crypto(), + charlie_add_proposal, + ) + .unwrap(); + + *alice_group.proposal_store_mut() = + ProposalStore::from_queued_proposal(queued_bob_remove_proposal.clone()); + *bob_group.proposal_store_mut() = + ProposalStore::from_queued_proposal(queued_bob_remove_proposal); + + alice_group + .proposal_store_mut() + .add(queued_charlie_add_propsal.clone()); + + bob_group + .proposal_store_mut() + .add(queued_charlie_add_propsal); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&remove_add_proposal_store) .build(); let remove_add_commit_result = alice_group .create_commit(params, provider, &alice_signature_keys) .expect("Error creating commit"); let staged_commit = bob_group - .read_keys_and_stage_commit( - &remove_add_commit_result.commit, - &remove_add_proposal_store, - &[], - provider, - ) + .read_keys_and_stage_commit(&remove_add_commit_result.commit, &[], provider) .expect("error staging commit"); bob_group .merge_commit(provider, staged_commit) diff --git a/openmls/src/group/core_group/test_create_commit_params.rs b/openmls/src/group/core_group/test_create_commit_params.rs index 3ec1ba142..5cc5c9767 100644 --- a/openmls/src/group/core_group/test_create_commit_params.rs +++ b/openmls/src/group/core_group/test_create_commit_params.rs @@ -6,19 +6,16 @@ fn build_create_commit_params(provider: &Provider) { let _ = provider; let framing_parameters: FramingParameters = FramingParameters::new(&[1, 2, 3], WireFormat::PrivateMessage); - let proposal_store: &ProposalStore = &ProposalStore::new(); let inline_proposals: Vec = vec![]; let force_self_update: bool = true; let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(proposal_store) .inline_proposals(inline_proposals.clone()) .force_self_update(force_self_update) .build(); assert_eq!(params.framing_parameters(), &framing_parameters); - assert_eq!(params.proposal_store(), proposal_store); assert_eq!(params.inline_proposals(), inline_proposals); assert_eq!(params.force_self_update(), force_self_update); } diff --git a/openmls/src/group/core_group/test_external_init.rs b/openmls/src/group/core_group/test_external_init.rs index addf31f2f..e6da79194 100644 --- a/openmls/src/group/core_group/test_external_init.rs +++ b/openmls/src/group/core_group/test_external_init.rs @@ -8,7 +8,7 @@ use crate::{ }; use openmls_traits::prelude::*; -use super::{proposals::ProposalStore, CoreGroup}; +use super::CoreGroup; #[openmls_test::openmls_test] fn test_external_init_broken_signature() { @@ -34,10 +34,8 @@ fn test_external_init_broken_signature() { verifiable_group_info }; - let proposal_store = ProposalStore::new(); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .build(); let result = CoreGroup::join_by_external_commit( diff --git a/openmls/src/group/core_group/test_proposals.rs b/openmls/src/group/core_group/test_proposals.rs index cc092653e..460888f5d 100644 --- a/openmls/src/group/core_group/test_proposals.rs +++ b/openmls/src/group/core_group/test_proposals.rs @@ -372,18 +372,19 @@ fn test_group_context_extensions( .create_add_proposal(framing_parameters, bob_key_package.clone(), &alice_signer) .expect("Could not create proposal"); - let proposal_store = ProposalStore::from_queued_proposal( + alice_group.proposal_store_mut().empty(); + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); + log::info!(" >>> Creating commit ..."); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = alice_group @@ -455,18 +456,19 @@ fn test_group_context_extension_proposal_fails( .create_add_proposal(framing_parameters, bob_key_package.clone(), &alice_signer) .expect("Could not create proposal"); - let proposal_store = ProposalStore::from_queued_proposal( + alice_group.proposal_store_mut().empty(); + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); + log::info!(" >>> Creating commit ..."); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = alice_group @@ -540,18 +542,19 @@ fn test_group_context_extension_proposal( .create_add_proposal(framing_parameters, bob_key_package.clone(), &alice_signer) .expect("Could not create proposal"); - let proposal_store = ProposalStore::from_queued_proposal( + alice_group.proposal_store_mut().empty(); + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); + log::info!(" >>> Creating commit ..."); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_results = alice_group @@ -593,18 +596,23 @@ fn test_group_context_extension_proposal( ) .expect("Error creating gce proposal."); - let proposal_store = ProposalStore::from_queued_proposal( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - gce_proposal, - ) - .expect("Could not create QueuedProposal."), - ); + let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( + ciphersuite, + provider.crypto(), + gce_proposal, + ) + .unwrap(); + + alice_group.proposal_store_mut().empty(); + bob_group.proposal_store_mut().empty(); + alice_group + .proposal_store_mut() + .add(queued_proposal.clone()); + bob_group.proposal_store_mut().add(queued_proposal); + log::info!(" >>> Creating commit ..."); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = alice_group @@ -614,7 +622,7 @@ fn test_group_context_extension_proposal( log::info!(" >>> Staging & merging commit ..."); let staged_commit = bob_group - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect("error staging commit"); bob_group .merge_commit(provider, staged_commit) diff --git a/openmls/src/group/mls_group/application.rs b/openmls/src/group/mls_group/application.rs index 52601ff4a..14baa41e2 100644 --- a/openmls/src/group/mls_group/application.rs +++ b/openmls/src/group/mls_group/application.rs @@ -24,7 +24,7 @@ impl MlsGroup { MlsGroupStateError::UseAfterEviction, )); } - if !self.proposal_store.is_empty() { + if !self.proposal_store().is_empty() { return Err(CreateMessageError::GroupStateError( MlsGroupStateError::PendingProposal, )); diff --git a/openmls/src/group/mls_group/builder.rs b/openmls/src/group/mls_group/builder.rs index 59fe151b0..5644620de 100644 --- a/openmls/src/group/mls_group/builder.rs +++ b/openmls/src/group/mls_group/builder.rs @@ -7,7 +7,7 @@ use crate::{ group::{ public_group::errors::PublicGroupBuildError, CoreGroup, CoreGroupBuildError, CoreGroupConfig, GroupId, MlsGroupCreateConfig, MlsGroupCreateConfigBuilder, NewGroupError, - ProposalStore, WireFormatPolicy, + WireFormatPolicy, }, key_packages::Lifetime, storage::OpenMlsProvider, @@ -103,7 +103,6 @@ impl MlsGroupBuilder { let mls_group = MlsGroup { mls_group_config: mls_group_create_config.join_config.clone(), group, - proposal_store: ProposalStore::new(), own_leaf_nodes: vec![], aad: vec![], group_state: MlsGroupState::Operational, diff --git a/openmls/src/group/mls_group/creation.rs b/openmls/src/group/mls_group/creation.rs index a61a9eae7..bba28e1d9 100644 --- a/openmls/src/group/mls_group/creation.rs +++ b/openmls/src/group/mls_group/creation.rs @@ -90,10 +90,8 @@ impl MlsGroup { // Prepare the commit parameters let framing_parameters = FramingParameters::new(aad, WireFormat::PublicMessage); - let proposal_store = ProposalStore::new(); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .credential_with_key(credential_with_key) .build(); let (mut group, create_commit_result) = CoreGroup::join_by_external_commit( @@ -108,7 +106,6 @@ impl MlsGroup { let mls_group = MlsGroup { mls_group_config: mls_group_config.clone(), group, - proposal_store: ProposalStore::new(), own_leaf_nodes: vec![], aad: vec![], group_state: MlsGroupState::PendingCommit(Box::new(PendingCommitState::External( @@ -280,7 +277,6 @@ impl StagedWelcome { let mls_group = MlsGroup { mls_group_config: self.mls_group_config, group, - proposal_store: ProposalStore::new(), own_leaf_nodes: vec![], aad: vec![], group_state: MlsGroupState::Operational, diff --git a/openmls/src/group/mls_group/membership.rs b/openmls/src/group/mls_group/membership.rs index 0416b4bfd..39ef4c011 100644 --- a/openmls/src/group/mls_group/membership.rs +++ b/openmls/src/group/mls_group/membership.rs @@ -60,7 +60,6 @@ impl MlsGroup { // TODO #751 let params = CreateCommitParams::builder() .framing_parameters(self.framing_parameters()) - .proposal_store(&self.proposal_store) .inline_proposals(inline_proposals) .build(); let create_commit_result = self.group.create_commit(params, provider, signer)?; @@ -142,7 +141,6 @@ impl MlsGroup { // TODO #751 let params = CreateCommitParams::builder() .framing_parameters(self.framing_parameters()) - .proposal_store(&self.proposal_store) .inline_proposals(inline_proposals) .build(); let create_commit_result = self.group.create_commit(params, provider, signer)?; @@ -190,9 +188,11 @@ impl MlsGroup { .create_remove_proposal(self.framing_parameters(), removed, signer) .map_err(|_| LibraryError::custom("Creating a self removal should not fail"))?; - self.proposal_store + let ciphersuite = self.ciphersuite(); + + self.proposal_store_mut() .add(QueuedProposal::from_authenticated_content_by_ref( - self.ciphersuite(), + ciphersuite, provider.crypto(), remove_proposal.clone(), )?); diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index f353c351e..45130d33c 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -157,9 +157,6 @@ pub struct MlsGroup { // the internal `CoreGroup` used for lower level operations. See `CoreGroup` for more // information. group: CoreGroup, - // A [ProposalStore] that stores incoming proposals from the DS within one epoch. - // The store is emptied after every epoch change. - pub(crate) proposal_store: ProposalStore, // Own [`LeafNode`]s that were created for update proposals and that // are needed in case an update proposal is committed by another group // member. The vector is emptied after every epoch change. @@ -255,7 +252,7 @@ impl MlsGroup { /// Returns an `Iterator` over pending proposals. pub fn pending_proposals(&self) -> impl Iterator { - self.proposal_store.proposals() + self.proposal_store().proposals() } /// Returns a reference to the [`StagedCommit`] of the most recently created @@ -310,9 +307,9 @@ impl MlsGroup { storage: &Storage, ) -> Result<(), Storage::Error> { // If the proposal store is not empty... - if !self.proposal_store.is_empty() { + if !self.proposal_store().is_empty() { // Empty the proposal store - self.proposal_store.empty(); + self.proposal_store_mut().empty(); // Clear proposals in storage storage.clear_proposal_queue::(self.group_id())?; @@ -343,21 +340,14 @@ impl MlsGroup { ) -> Result, Storage::Error> { let group_config = storage.mls_group_join_config(group_id)?; let core_group = CoreGroup::load(storage, group_id)?; - let proposals: Vec<(ProposalRef, QueuedProposal)> = storage.queued_proposals(group_id)?; let own_leaf_nodes = storage.own_leaf_nodes(group_id)?; let aad = storage.aad(group_id)?; let group_state = storage.group_state(group_id)?; - let mut proposal_store = ProposalStore::new(); - - for (_ref, proposal) in proposals { - proposal_store.add(proposal); - } let build = || -> Option { Some(Self { mls_group_config: group_config?, group: core_group?, - proposal_store, own_leaf_nodes, aad, group_state: group_state?, @@ -447,6 +437,16 @@ impl MlsGroup { MlsGroupState::Operational => Ok(()), } } + + /// Returns a reference to the proposal store. + pub(crate) fn proposal_store(&self) -> &ProposalStore { + self.group.proposal_store() + } + + /// Returns a mutable reference to the proposal store. + pub(crate) fn proposal_store_mut(&mut self) -> &mut ProposalStore { + self.group.proposal_store_mut() + } } // Methods used in tests @@ -481,7 +481,7 @@ impl MlsGroup { storage .remove_proposal(self.group_id(), &proposal_ref) .map_err(MlsGroupStateError::StorageError)?; - self.proposal_store + self.proposal_store_mut() .remove(proposal_ref) .ok_or(MlsGroupStateError::PendingProposalNotFound) } diff --git a/openmls/src/group/mls_group/processing.rs b/openmls/src/group/mls_group/processing.rs index ba78d64bc..c932f5253 100644 --- a/openmls/src/group/mls_group/processing.rs +++ b/openmls/src/group/mls_group/processing.rs @@ -56,7 +56,6 @@ impl MlsGroup { provider, message, &sender_ratchet_configuration, - &self.proposal_store, &self.own_leaf_nodes, ) } @@ -69,7 +68,7 @@ impl MlsGroup { ) -> Result<(), Storage::Error> { storage.queue_proposal(self.group_id(), &proposal.proposal_reference(), &proposal)?; // Store the proposal in in the internal ProposalStore - self.proposal_store.add(proposal); + self.proposal_store_mut().add(proposal); Ok(()) } @@ -99,7 +98,6 @@ impl MlsGroup { // TODO #751 let params = CreateCommitParams::builder() .framing_parameters(self.framing_parameters()) - .proposal_store(&self.proposal_store) .build(); let create_commit_result = self.group.create_commit(params, provider, signer)?; @@ -143,8 +141,7 @@ impl MlsGroup { .map_err(MergeCommitError::StorageError)?; // Merge staged commit - self.group - .merge_staged_commit(provider, staged_commit, &mut self.proposal_store)?; + self.group.merge_staged_commit(provider, staged_commit)?; // Extract and store the resumption psk for the current epoch let resumption_psk = self.group.group_epoch_secrets().resumption_psk(); diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index 46cd8ee64..c45c3cd55 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -89,7 +89,7 @@ macro_rules! impl_propose_fun { .storage() .queue_proposal(self.group.group_id(), &proposal_ref, &queued_proposal) .map_err(ProposalError::StorageError)?; - self.proposal_store.add(queued_proposal); + self.proposal_store_mut().add(queued_proposal); let mls_message = self.content_to_mls_message(proposal, provider)?; @@ -256,7 +256,7 @@ impl MlsGroup { .storage() .queue_proposal(self.group_id(), &proposal_ref, &proposal) .map_err(ProposeAddMemberError::StorageError)?; - self.proposal_store.add(proposal); + self.proposal_store_mut().add(proposal); let mls_message = self.content_to_mls_message(add_proposal, provider)?; @@ -291,7 +291,7 @@ impl MlsGroup { .storage() .queue_proposal(self.group_id(), &proposal_ref, &proposal) .map_err(ProposeRemoveMemberError::StorageError)?; - self.proposal_store.add(proposal); + self.proposal_store_mut().add(proposal); let mls_message = self.content_to_mls_message(remove_proposal, provider)?; @@ -380,7 +380,7 @@ impl MlsGroup { .storage() .queue_proposal(self.group_id(), &proposal_ref, &queued_proposal) .map_err(ProposalError::StorageError)?; - self.proposal_store.add(queued_proposal); + self.proposal_store_mut().add(queued_proposal); let mls_message = self.content_to_mls_message(proposal, provider)?; @@ -414,7 +414,6 @@ impl MlsGroup { // Create Commit over all proposals let params = CreateCommitParams::builder() .framing_parameters(self.framing_parameters()) - .proposal_store(&self.proposal_store) .inline_proposals(inline_proposals) .build(); let create_commit_result = self.group.create_commit(params, provider, signer)?; diff --git a/openmls/src/group/mls_group/ser.rs b/openmls/src/group/mls_group/ser.rs index 4449b3a0b..ad6bc5975 100644 --- a/openmls/src/group/mls_group/ser.rs +++ b/openmls/src/group/mls_group/ser.rs @@ -18,7 +18,6 @@ use serde::{ pub struct SerializedMlsGroup { mls_group_config: MlsGroupJoinConfig, group: CoreGroup, - proposal_store: ProposalStore, own_leaf_nodes: Vec, aad: Vec, resumption_psk_store: ResumptionPskStore, @@ -31,7 +30,6 @@ impl Into for SerializedMlsGroup { MlsGroup { mls_group_config: self.mls_group_config, group: self.group, - proposal_store: self.proposal_store, own_leaf_nodes: self.own_leaf_nodes, aad: self.aad, group_state: self.group_state, @@ -47,7 +45,6 @@ impl Serialize for MlsGroup { let mut state = serializer.serialize_struct("SerializedMlsGroup", 6)?; state.serialize_field("mls_group_config", &self.mls_group_config)?; state.serialize_field("group", &self.group)?; - state.serialize_field("proposal_store", &self.proposal_store)?; state.serialize_field("own_leaf_nodes", &self.own_leaf_nodes)?; state.serialize_field("aad", &self.aad)?; state.serialize_field("resumption_psk_store", &self.group.resumption_psk_store)?; diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index 0e9b3bc51..8b2ee40fe 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -1105,12 +1105,12 @@ fn remove_prosposal_by_ref( .propose_add_member(provider, &alice_signer, charlie_key_package) .unwrap(); - assert_eq!(alice_group.proposal_store.proposals().count(), 1); + assert_eq!(alice_group.proposal_store().proposals().count(), 1); // clearing the proposal by reference alice_group .remove_pending_proposal(provider.storage(), reference.clone()) .unwrap(); - assert!(alice_group.proposal_store.is_empty()); + assert!(alice_group.proposal_store().is_empty()); // the proposal should not be stored anymore let err = alice_group diff --git a/openmls/src/group/mls_group/updates.rs b/openmls/src/group/mls_group/updates.rs index 93508f984..d53706aab 100644 --- a/openmls/src/group/mls_group/updates.rs +++ b/openmls/src/group/mls_group/updates.rs @@ -35,7 +35,6 @@ impl MlsGroup { let params = CreateCommitParams::builder() .framing_parameters(self.framing_parameters()) - .proposal_store(&self.proposal_store) .build(); // Create Commit over all proposals. // TODO #751 @@ -144,7 +143,7 @@ impl MlsGroup { .storage() .queue_proposal(self.group_id(), &proposal_ref, &proposal) .map_err(ProposeSelfUpdateError::StorageError)?; - self.proposal_store.add(proposal); + self.proposal_store_mut().add(proposal); let mls_message = self.content_to_mls_message(update_proposal, provider)?; @@ -169,7 +168,7 @@ impl MlsGroup { .storage() .queue_proposal(self.group_id(), &proposal_ref, &proposal) .map_err(ProposeSelfUpdateError::StorageError)?; - self.proposal_store.add(proposal); + self.proposal_store_mut().add(proposal); let mls_message = self.content_to_mls_message(update_proposal, provider)?; diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index 1d230291a..86df39b79 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -26,7 +26,7 @@ use super::{GroupContext, GroupId, Member, ProposalStore, QueuedProposal, Staged use crate::treesync::{node::parent_node::PlainUpdatePathNode, treekem::UpdatePathNode}; use crate::{ binary_tree::{array_representation::TreeSize, LeafNodeIndex}, - ciphersuite::signable::Verifiable, + ciphersuite::{hash_ref::ProposalRef, signable::Verifiable}, error::LibraryError, extensions::RequiredCapabilitiesExtension, framing::InterimTranscriptHashInput, @@ -391,15 +391,21 @@ impl PublicGroup { group_id: &GroupId, ) -> Result, Storage::Error> { let treesync = storage.treesync(group_id)?; + let proposals: Vec<(ProposalRef, QueuedProposal)> = storage.queued_proposals(group_id)?; let group_context = storage.group_context(group_id)?; let interim_transcript_hash: Option = storage.interim_transcript_hash(group_id)?; let confirmation_tag = storage.confirmation_tag(group_id)?; + let mut proposal_store = ProposalStore::new(); + + for (_ref, proposal) in proposals { + proposal_store.add(proposal); + } let build = || -> Option { Some(Self { treesync: treesync?, - proposal_store: ProposalStore::new(), + proposal_store, group_context: group_context?, interim_transcript_hash: interim_transcript_hash?.0, confirmation_tag: confirmation_tag?, @@ -408,6 +414,16 @@ impl PublicGroup { Ok(build()) } + + /// Returns a reference to the [`ProposalStore`]. + pub(crate) fn proposal_store(&self) -> &ProposalStore { + &self.proposal_store + } + + /// Returns a mutable reference to the [`ProposalStore`]. + pub(crate) fn proposal_store_mut(&mut self) -> &mut ProposalStore { + &mut self.proposal_store + } } // Test functions diff --git a/openmls/src/group/public_group/process.rs b/openmls/src/group/public_group/process.rs index fe6daade9..7244d0fc0 100644 --- a/openmls/src/group/public_group/process.rs +++ b/openmls/src/group/public_group/process.rs @@ -9,10 +9,8 @@ use crate::{ ProcessedMessageContent, ProtocolMessage, Sender, SenderContext, UnverifiedMessage, }, group::{ - core_group::proposals::{ProposalStore, QueuedProposal}, - errors::ValidationError, - mls_group::errors::ProcessMessageError, - past_secrets::MessageSecretsStore, + core_group::proposals::QueuedProposal, errors::ValidationError, + mls_group::errors::ProcessMessageError, past_secrets::MessageSecretsStore, }, messages::proposals::Proposal, storage::OpenMlsProvider, @@ -161,7 +159,7 @@ impl PublicGroup { let unverified_message = self .parse_message(decrypted_message, None) .map_err(ProcessMessageError::from)?; - self.process_unverified_message(provider, unverified_message, &self.proposal_store) + self.process_unverified_message(provider, unverified_message) } } @@ -196,7 +194,6 @@ impl PublicGroup { &self, provider: &Provider, unverified_message: UnverifiedMessage, - proposal_store: &ProposalStore, ) -> Result> { let crypto = provider.crypto(); // Checks the following semantic validation: @@ -229,7 +226,7 @@ impl PublicGroup { } } FramedContentBody::Commit(_) => { - let staged_commit = self.stage_commit(&content, proposal_store, crypto)?; + let staged_commit = self.stage_commit(&content, crypto)?; ProcessedMessageContent::StagedCommitMessage(Box::new(staged_commit)) } }; diff --git a/openmls/src/group/public_group/staged_commit.rs b/openmls/src/group/public_group/staged_commit.rs index 451391e3d..1dd7d2f7e 100644 --- a/openmls/src/group/public_group/staged_commit.rs +++ b/openmls/src/group/public_group/staged_commit.rs @@ -2,10 +2,7 @@ use super::{super::errors::*, *}; use crate::{ framing::{mls_auth_content::AuthenticatedContent, mls_content::FramedContentBody, Sender}, group::{ - core_group::{ - proposals::{ProposalQueue, ProposalStore}, - staged_commit::StagedCommitState, - }, + core_group::{proposals::ProposalQueue, staged_commit::StagedCommitState}, StagedCommit, }, messages::{proposals::ProposalOrRef, Commit}, @@ -47,7 +44,6 @@ impl PublicGroup { pub(crate) fn validate_commit<'a>( &self, mls_content: &'a AuthenticatedContent, - proposal_store: &ProposalStore, crypto: &impl OpenMlsCrypto, ) -> Result<(&'a Commit, ProposalQueue, LeafNodeIndex), StageCommitError> { let ciphersuite = self.ciphersuite(); @@ -88,7 +84,7 @@ impl PublicGroup { ciphersuite, crypto, commit.proposals.as_slice().to_vec(), - proposal_store, + self.proposal_store(), sender, ) .map_err(|e| { @@ -211,11 +207,9 @@ impl PublicGroup { pub(crate) fn stage_commit( &self, mls_content: &AuthenticatedContent, - proposal_store: &ProposalStore, crypto: &impl OpenMlsCrypto, ) -> Result { - let (commit, proposal_queue, sender_index) = - self.validate_commit(mls_content, proposal_store, crypto)?; + let (commit, proposal_queue, sender_index) = self.validate_commit(mls_content, crypto)?; let staged_diff = self.stage_diff(mls_content, &proposal_queue, sender_index, crypto)?; let staged_state = PublicStagedCommitState { diff --git a/openmls/src/group/tests/kat_messages.rs b/openmls/src/group/tests/kat_messages.rs index 19aee1a73..22ff3aa8a 100644 --- a/openmls/src/group/tests/kat_messages.rs +++ b/openmls/src/group/tests/kat_messages.rs @@ -251,7 +251,8 @@ pub fn generate_test_vector(ciphersuite: Ciphersuite) -> MessagesTestVector { ) .unwrap(); - let mut proposal_store = ProposalStore::from_queued_proposal( + alice_group.proposal_store_mut().empty(); + alice_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), @@ -261,7 +262,6 @@ pub fn generate_test_vector(ciphersuite: Ciphersuite) -> MessagesTestVector { ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .build(); let create_commit_result = alice_group .create_commit( @@ -271,11 +271,7 @@ pub fn generate_test_vector(ciphersuite: Ciphersuite) -> MessagesTestVector { ) .unwrap(); alice_group - .merge_staged_commit( - &provider, - create_commit_result.staged_commit, - &mut proposal_store, - ) + .merge_staged_commit(&provider, create_commit_result.staged_commit) .unwrap(); let commit = if let FramedContentBody::Commit(commit) = create_commit_result.commit.content() { diff --git a/openmls/src/group/tests/test_commit_validation.rs b/openmls/src/group/tests/test_commit_validation.rs index 186f11666..ae807d1ea 100644 --- a/openmls/src/group/tests/test_commit_validation.rs +++ b/openmls/src/group/tests/test_commit_validation.rs @@ -348,7 +348,6 @@ fn test_valsem201() { let params = CreateCommitParams::builder() .framing_parameters(alice_group.framing_parameters()) - .proposal_store(&alice_group.proposal_store) // has to be turned off otherwise commit path is always present .force_self_update(false) .build(); @@ -835,13 +834,13 @@ fn test_partial_proposal_commit( // Alice creates a commit with only a subset of the epoch's proposals. Bob should still be able to process it. let remaining_proposal = alice_group - .proposal_store + .proposal_store() .proposals() .next() .cloned() .unwrap(); - alice_group.proposal_store.empty(); - alice_group.proposal_store.add(remaining_proposal); + alice_group.proposal_store_mut().empty(); + alice_group.proposal_store_mut().add(remaining_proposal); let (commit, _, _) = alice_group .commit_to_pending_proposals(provider, &alice_credential.signer) .unwrap(); diff --git a/openmls/src/group/tests/test_encoding.rs b/openmls/src/group/tests/test_encoding.rs index c48b89705..aaa5c6d11 100644 --- a/openmls/src/group/tests/test_encoding.rs +++ b/openmls/src/group/tests/test_encoding.rs @@ -294,26 +294,24 @@ fn test_commit_encoding(provider: &impl crate::storage::OpenMlsProvider) { ) .expect("Could not create proposal."); - let mut proposal_store = ProposalStore::from_queued_proposal( - QueuedProposal::from_authenticated_content_by_ref( - group_state.ciphersuite(), - provider.crypto(), - add, - ) - .expect("Could not create QueuedProposal."), + let ciphersuite = group_state.ciphersuite(); + + group_state.proposal_store_mut().empty(); + group_state.proposal_store_mut().add( + QueuedProposal::from_authenticated_content_by_ref(ciphersuite, provider.crypto(), add) + .unwrap(), ); - proposal_store.add( + group_state.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( - group_state.ciphersuite(), + ciphersuite, provider.crypto(), update, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .build(); let create_commit_result = group_state .create_commit( @@ -377,18 +375,16 @@ fn test_welcome_message_encoding(provider: &impl crate::storage::OpenMlsProvider ) .expect("Could not create proposal."); - let proposal_store = ProposalStore::from_queued_proposal( - QueuedProposal::from_authenticated_content_by_ref( - group_state.ciphersuite(), - provider.crypto(), - add, - ) - .expect("Could not create QueuedProposal."), + let ciphersuite = group_state.ciphersuite(); + + group_state.proposal_store_mut().empty(); + group_state.proposal_store_mut().add( + QueuedProposal::from_authenticated_content_by_ref(ciphersuite, provider.crypto(), add) + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .build(); let create_commit_result = group_state .create_commit(params, provider, &credential_with_key_and_signer.signer) diff --git a/openmls/src/group/tests/test_group.rs b/openmls/src/group/tests/test_group.rs index 61984eb7b..2596d734d 100644 --- a/openmls/src/group/tests/test_group.rs +++ b/openmls/src/group/tests/test_group.rs @@ -53,18 +53,18 @@ fn create_commit_optional_path( ) .expect("Could not create proposal."); - let mut proposal_store = ProposalStore::from_queued_proposal( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .build(); let create_commit_result = match group_alice.create_commit( params, /* No PSK fetcher */ @@ -92,19 +92,18 @@ fn create_commit_optional_path( ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = @@ -161,20 +160,19 @@ fn create_commit_optional_path( ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), alice_update_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); // Only UpdateProposal let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = @@ -218,7 +216,7 @@ fn basic_group_setup() { ); // Alice creates a group - let group_alice = CoreGroup::builder( + let mut group_alice = CoreGroup::builder( GroupId::random(provider.rand()), ciphersuite, alice_credential_with_keys.credential_with_key, @@ -235,18 +233,18 @@ fn basic_group_setup() { ) .expect("Could not create proposal."); - let proposal_store = ProposalStore::from_queued_proposal( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .build(); let _commit = match group_alice.create_commit( params, /* PSK fetcher */ @@ -313,18 +311,18 @@ fn group_operations() { ) .expect("Could not create proposal."); - let mut proposal_store = ProposalStore::from_queued_proposal( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = group_alice @@ -427,19 +425,18 @@ fn group_operations() { ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( + group_bob.proposal_store_mut().empty(); + group_bob.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), update_proposal_bob, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = @@ -458,7 +455,7 @@ fn group_operations() { assert!(create_commit_result.welcome_option.is_none()); let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect("Error applying commit (Alice)"); group_alice .merge_commit(provider, staged_commit) @@ -494,19 +491,18 @@ fn group_operations() { ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), update_proposal_alice, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = match group_alice.create_commit( @@ -525,7 +521,7 @@ fn group_operations() { .merge_commit(provider, create_commit_result.staged_commit) .expect("error merging own commits"); let staged_commit = group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect("Error applying commit (Bob)"); group_bob .merge_commit(provider, staged_commit) @@ -557,19 +553,18 @@ fn group_operations() { ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( + group_alice.proposal_store_mut().empty(); + group_alice.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), update_proposal_bob.clone(), ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = @@ -585,22 +580,21 @@ fn group_operations() { .merge_commit(provider, create_commit_result.staged_commit) .expect("error merging own commits"); - proposal_store.add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - update_proposal_bob, - ) - .expect("Could not create StagedProposal."), - ); + let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( + ciphersuite, + provider.crypto(), + update_proposal_bob, + ) + .unwrap(); + + group_alice + .proposal_store_mut() + .add(queued_proposal.clone()); + + group_bob.proposal_store_mut().add(queued_proposal); let staged_commit = group_bob - .read_keys_and_stage_commit( - &create_commit_result.commit, - &proposal_store, - &[bob_new_leaf_node], - provider, - ) + .read_keys_and_stage_commit(&create_commit_result.commit, &[bob_new_leaf_node], provider) .expect("Error applying commit (Bob)"); group_bob .merge_commit(provider, staged_commit) @@ -635,19 +629,23 @@ fn group_operations() { ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - add_charlie_proposal_bob, - ) - .expect("Could not create QueuedProposal."), - ); + let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( + ciphersuite, + provider.crypto(), + add_charlie_proposal_bob, + ) + .unwrap(); + + group_alice.proposal_store_mut().empty(); + group_bob.proposal_store_mut().empty(); + + group_alice + .proposal_store_mut() + .add(queued_proposal.clone()); + group_bob.proposal_store_mut().add(queued_proposal); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = @@ -667,7 +665,7 @@ fn group_operations() { assert!(create_commit_result.welcome_option.is_some()); let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect("Error applying commit (Alice)"); group_alice .merge_commit(provider, staged_commit) @@ -787,19 +785,18 @@ fn group_operations() { ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( + group_charlie.proposal_store_mut().empty(); + group_charlie.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), update_proposal_charlie, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = @@ -816,13 +813,13 @@ fn group_operations() { assert!(commit.has_path()); let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect("Error applying commit (Alice)"); group_alice .merge_commit(provider, staged_commit) .expect("error merging commit"); let staged_commit = group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect("Error applying commit (Bob)"); group_bob .merge_commit(provider, staged_commit) @@ -850,19 +847,25 @@ fn group_operations() { ) .expect("Could not create proposal."); - proposal_store.empty(); - proposal_store.add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - remove_bob_proposal_charlie, - ) - .expect("Could not create QueuedProposal."), - ); + let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( + ciphersuite, + provider.crypto(), + remove_bob_proposal_charlie, + ) + .unwrap(); + + group_alice.proposal_store_mut().empty(); + group_bob.proposal_store_mut().empty(); + group_charlie.proposal_store_mut().empty(); + + group_alice + .proposal_store_mut() + .add(queued_proposal.clone()); + group_bob.proposal_store_mut().add(queued_proposal.clone()); + group_charlie.proposal_store_mut().add(queued_proposal); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); let create_commit_result = match group_charlie.create_commit( @@ -878,13 +881,13 @@ fn group_operations() { assert!(commit.has_path()); let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect("Error applying commit (Alice)"); group_alice .merge_commit(provider, staged_commit) .expect("error merging commit"); assert!(group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &proposal_store, &[], provider) + .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) .expect("Could not stage commit.") .self_removed()); group_charlie diff --git a/openmls/src/group/tests/utils.rs b/openmls/src/group/tests/utils.rs index 3c88fba60..4122d9cac 100644 --- a/openmls/src/group/tests/utils.rs +++ b/openmls/src/group/tests/utils.rs @@ -186,9 +186,8 @@ pub(crate) fn setup( } // Create the commit based on the previously compiled list of // proposals. - let mut proposal_store = ProposalStore::new(); for proposal in proposal_list { - proposal_store.add( + core_group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( group_config.ciphersuite, provider.crypto(), @@ -199,7 +198,6 @@ pub(crate) fn setup( } let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .build(); let create_commit_result = core_group .create_commit(params, provider, &credential_with_key_and_signer.signer) @@ -209,11 +207,7 @@ pub(crate) fn setup( .expect("An unexpected error occurred."); core_group - .merge_staged_commit( - provider, - create_commit_result.staged_commit, - &mut proposal_store, - ) + .merge_staged_commit(provider, create_commit_result.staged_commit) .expect("Error merging commit."); // Distribute the Welcome message to the other members. diff --git a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs index f972186ff..cf5985a8f 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs @@ -268,18 +268,18 @@ pub fn run_test_vector( .create_add_proposal(framing_parameters, bob_key_package.clone(), &signer) .expect("Could not create proposal."); - let proposal_store = ProposalStore::from_queued_proposal( + group.proposal_store_mut().empty(); + group.proposal_store_mut().add( QueuedProposal::from_authenticated_content_by_ref( ciphersuite, provider.crypto(), bob_add_proposal, ) - .expect("Could not create QueuedProposal."), + .unwrap(), ); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .proposal_store(&proposal_store) .force_self_update(false) .build(); @@ -379,7 +379,6 @@ pub fn run_test_vector( ) { // Group stuff we need for openmls let sender_ratchet_config = SenderRatchetConfiguration::new(0, 0); - let proposal_store = ProposalStore::default(); // decrypt private message let processed_message = group @@ -387,7 +386,6 @@ pub fn run_test_vector( provider, proposal_priv.into_protocol_message().unwrap(), &sender_ratchet_config, - &proposal_store, &[], ) .unwrap(); @@ -629,7 +627,6 @@ pub fn run_test_vector( ) { // Group stuff we need for openmls let sender_ratchet_config = SenderRatchetConfiguration::new(0, 0); - let proposal_store = ProposalStore::default(); // check that the proposal in proposal_pub == proposal let processed_message = group @@ -637,7 +634,6 @@ pub fn run_test_vector( provider, application_priv.into_ciphertext().unwrap(), &sender_ratchet_config, - &proposal_store, &[], ) .unwrap(); From c2f5b7bce8f90d3266ff3c89159c1b333df23b08 Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Wed, 10 Jul 2024 08:54:56 +0200 Subject: [PATCH 14/43] Remove clippy warnings & change CI (#1611) * Remove clippy warnings & change CI * Fix interop client * Install protoc for interop client --- .github/workflows/clippy.yml | 5 ++++- cli/src/user.rs | 2 +- interop_client/src/main.rs | 1 + memory_storage/tests/proposals.rs | 16 ++++++---------- openmls/benches/benchmark.rs | 2 +- openmls/examples/large-groups.rs | 17 ++++++++--------- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 9f876c4e1..10e41d1f1 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -16,4 +16,7 @@ jobs: with: components: clippy - uses: Swatinem/rust-cache@v2 - - run: cargo clippy -p openmls --tests -- -D warnings + - run: | + sudo apt-get -y install protoc-gen-go # Needed to build the interop client + echo $(go env GOPATH)/bin >> $GITHUB_PATH + cargo clippy -p openmls --tests --benches --examples -p openmls_basic_credential -p cli -p interop_client -p mls-ds -p ds-lib -p openmls_libcrux_crypto -p openmls_memory_storage -p openmls_rust_crypto -p openmls_test -p openmls-wasm -p openmls_traits -- -D warnings diff --git a/cli/src/user.rs b/cli/src/user.rs index 25c5ec770..3c784302c 100644 --- a/cli/src/user.rs +++ b/cli/src/user.rs @@ -243,7 +243,7 @@ impl User { "Searching for contact {:?}", str::from_utf8(credential.identity()).unwrap() ); - let contact = match self.contacts.get(&credential.identity().to_vec()) { + let contact = match self.contacts.get(credential.identity()) { Some(c) => c.id.clone(), None => panic!("There's a member in the group we don't know."), }; diff --git a/interop_client/src/main.rs b/interop_client/src/main.rs index 4304a7113..cb82d3824 100644 --- a/interop_client/src/main.rs +++ b/interop_client/src/main.rs @@ -6,6 +6,7 @@ use std::{collections::HashMap, fmt::Display, fs::File, io::Write, sync::Mutex}; use clap::Parser; +#[allow(unused_imports)] use clap_derive::*; use mls_client::{ mls_client_server::{MlsClient, MlsClientServer}, diff --git a/memory_storage/tests/proposals.rs b/memory_storage/tests/proposals.rs index f0d888be0..ae973ffc0 100644 --- a/memory_storage/tests/proposals.rs +++ b/memory_storage/tests/proposals.rs @@ -41,31 +41,27 @@ fn read_write_delete() { // Read proposal refs let proposal_refs_read: Vec = storage.queued_proposal_refs(&group_id).unwrap(); assert_eq!( - (0..10).map(|i| ProposalRef(i)).collect::>(), + (0..10).map(ProposalRef).collect::>(), proposal_refs_read ); // Read proposals let proposals_read: Vec<(ProposalRef, Proposal)> = storage.queued_proposals(&group_id).unwrap(); - let proposals_expected: Vec<(ProposalRef, Proposal)> = (0..10) - .map(|i| ProposalRef(i)) - .zip(proposals.clone().into_iter()) - .collect(); + let proposals_expected: Vec<(ProposalRef, Proposal)> = + (0..10).map(ProposalRef).zip(proposals.clone()).collect(); assert_eq!(proposals_expected, proposals_read); // Remove proposal 5 storage.remove_proposal(&group_id, &ProposalRef(5)).unwrap(); let proposal_refs_read: Vec = storage.queued_proposal_refs(&group_id).unwrap(); - let mut expected = (0..10).map(|i| ProposalRef(i)).collect::>(); + let mut expected = (0..10).map(ProposalRef).collect::>(); expected.remove(5); assert_eq!(expected, proposal_refs_read); let proposals_read: Vec<(ProposalRef, Proposal)> = storage.queued_proposals(&group_id).unwrap(); - let mut proposals_expected: Vec<(ProposalRef, Proposal)> = (0..10) - .map(|i| ProposalRef(i)) - .zip(proposals.clone().into_iter()) - .collect(); + let mut proposals_expected: Vec<(ProposalRef, Proposal)> = + (0..10).map(ProposalRef).zip(proposals.clone()).collect(); proposals_expected.remove(5); assert_eq!(proposals_expected, proposals_read); diff --git a/openmls/benches/benchmark.rs b/openmls/benches/benchmark.rs index 1516f846f..97d4198d7 100644 --- a/openmls/benches/benchmark.rs +++ b/openmls/benches/benchmark.rs @@ -255,7 +255,7 @@ fn create_commit(c: &mut Criterion, provider: &impl OpenMlsProvider) { (bob_group, bob_signer) }, |(mut bob_group, bob_signer)| { - let (queued_message, welcome_option, _group_info) = + let (_queued_message, _welcome_option, _group_info) = bob_group.self_update(provider, &bob_signer).unwrap(); bob_group diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index 3d968a6f7..f65f823be 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -195,11 +195,11 @@ mod generate { // If we have a previous group/member setup, let's use it. // The creator is always at 0. let mut members = if let Some(members) = members { - members.0.into_iter().zip(members.1.into_iter()).collect() + members.0.into_iter().zip(members.1).collect() } else { // Create a new setup. let creator_provider = OpenMlsRustCrypto::default(); - let creator_credential = BasicCredential::new(format!("Creator").into()); + let creator_credential = BasicCredential::new("Creator".to_string().into()); let creator_signer = SignatureKeyPair::new(CIPHERSUITE.signature_algorithm()).unwrap(); let creator_credential_with_key = CredentialWithKey { credential: creator_credential.into(), @@ -424,6 +424,8 @@ mod util { const GROUPS_PATH: &str = "large-balanced-group-groups.json.gzip"; const MEMBERS_PATH: &str = "large-balanced-group-members.json.gzip"; + type Members = Vec, Vec, Vec)>>; + /// Read benchmark setups from the fiels previously written. pub fn read(path: Option) -> Vec> { let file = File::open(groups_file(&path)).unwrap(); @@ -432,8 +434,7 @@ mod util { let file = File::open(members_file(&path)).unwrap(); let mut reader = flate2::read::GzDecoder::new(file); - let members: Vec, Vec, Vec)>> = - serde_json::from_reader(&mut reader).unwrap(); + let members: Members = serde_json::from_reader(&mut reader).unwrap(); let members: Vec> = members .into_iter() @@ -519,9 +520,9 @@ fn print_time(label: &str, d: Duration) { ) }; let space = if label.len() < 6 { - format!("\t\t") + "\t\t".to_string() } else { - format!("\t") + "\t".to_string() }; println!("{label}:{space}{time}"); @@ -552,7 +553,7 @@ fn main() { groups, |groups: &Vec<(MlsGroup, Member)>| { let (_member_provider, _signer, _credential_with_key, key_package) = - new_member(&format!("New Member")); + new_member("New Member"); let key_package = key_package.key_package().clone(); (groups[1].clone(), key_package) @@ -615,7 +616,5 @@ fn main() { } ); print_time("Process update", time); - - println!(""); } } From 9c45bdd4eef93d3c9adb437ca9f0b69f9047c380 Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Wed, 10 Jul 2024 09:27:24 +0200 Subject: [PATCH 15/43] LeafNode parameters (#1606) * Harmonize LeafNode update * Expose LeafNode parameters in API * Small fixes * Address review comments * Remove debug * Address more review comments * Address more review comments * Update book * Amend changelog --- CHANGELOG.md | 1 + book/src/user_manual/updates.md | 10 +- interop_client/src/main.rs | 6 +- openmls/benches/benchmark.rs | 5 +- openmls/examples/large-groups.rs | 5 +- .../binary_tree/array_representation/diff.rs | 24 -- openmls/src/credentials/mod.rs | 2 +- .../group/core_group/create_commit_params.rs | 32 +- .../group/core_group/kat_passive_client.rs | 7 +- openmls/src/group/core_group/mod.rs | 9 +- .../core_group/new_from_external_init.rs | 24 +- openmls/src/group/core_group/proposals.rs | 14 - .../src/group/core_group/test_core_group.rs | 35 +- openmls/src/group/errors.rs | 14 +- openmls/src/group/mls_group/creation.rs | 17 +- openmls/src/group/mls_group/errors.rs | 8 +- openmls/src/group/mls_group/proposal.rs | 10 +- openmls/src/group/mls_group/test_mls_group.rs | 23 +- openmls/src/group/mls_group/updates.rs | 87 ++--- .../group/public_group/diff/compute_path.rs | 101 +++--- .../src/group/tests/external_add_proposal.rs | 3 +- .../src/group/tests/test_commit_validation.rs | 37 +- .../src/group/tests/test_external_commit.rs | 6 + .../tests/test_external_commit_validation.rs | 8 + .../group/tests/test_framing_validation.rs | 58 +++- openmls/src/group/tests/test_group.rs | 60 ++-- openmls/src/group/tests/test_past_secrets.rs | 7 +- .../group/tests/test_proposal_validation.rs | 119 +++++-- .../group/tests/test_wire_format_policy.rs | 8 +- openmls/src/key_packages/mod.rs | 1 + openmls/src/prelude.rs | 2 +- .../src/test_utils/frankenstein/leaf_node.rs | 22 +- .../src/test_utils/test_framework/client.rs | 10 +- openmls/src/test_utils/test_framework/mod.rs | 7 +- openmls/src/treesync/diff.rs | 50 ++- openmls/src/treesync/errors.rs | 2 +- openmls/src/treesync/mod.rs | 6 +- openmls/src/treesync/node/encryption_keys.rs | 6 + openmls/src/treesync/node/leaf_node.rs | 328 +++++++++++------- .../tests_and_kats/kats/kat_treekem.rs | 10 +- openmls/src/treesync/treesync_node.rs | 5 - openmls/tests/book_code.rs | 15 +- openmls/tests/test_external_commit.rs | 17 +- openmls/tests/test_interop_scenarios.rs | 5 +- openmls/tests/test_mls_group.rs | 10 +- 45 files changed, 771 insertions(+), 465 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f939c6bd3..e94daa970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1542](https://github.com/openmls/openmls/pull/1542): Add support for custom proposals. ProposalType::Unknown is now called ProposalType::Other. Proposal::Unknown is now called Proposal::Other. - [#1559](https://github.com/openmls/openmls/pull/1559): Remove the `PartialEq` type constraint on the error type of both the `OpenMlsRand` and `OpenMlsKeyStore` traits. Additionally, remove the `Clone` type constraint on the error type of the `OpenMlsRand` trait. - [#1565](https://github.com/openmls/openmls/pull/1565): Removed `OpenMlsKeyStore` and replace it with a new `StorageProvider` trait in the `openmls_traits` crate. +- [#1606](https://github.com/openmls/openmls/pull/1606): Added additional `LeafNodeParameters` argument to `MlsGroup.self_update()` and `MlsGroup.propose_self_update()` to allow for updating the leaf node with custom parameters. `MlsGroup::join_by_external_commit()` now also takes optional parameters to set the capabilities and the extensions of the LeafNode. ### Fixed diff --git a/book/src/user_manual/updates.md b/book/src/user_manual/updates.md index fea47eb65..408179ab1 100644 --- a/book/src/user_manual/updates.md +++ b/book/src/user_manual/updates.md @@ -1,20 +1,20 @@ -# Updating own key package +# Updating own leaf node ## Immediate operation -Members can update their own leaf key package atomically with the `.self_update()` function. -The application can optionally provide a `KeyPackage` manually. If not, a key package will be created on the fly with the same extensions as the current one but with a fresh HPKE init key. +Members can update their own leaf node atomically with the `.self_update()` function. +By default, only the HPKE encryption key is updated. The application can however also provide more parameters like a new credential, capabilities and extensions using the `LeafNodeParameters` struct. ```rust,no_run,noplayground {{#include ../../../openmls/tests/book_code.rs:self_update}} ``` The function returns the tuple `(MlsMessageOut, Option)`. The `MlsMessageOut` contains a Commit message that needs to be fanned out to existing group members. -Even though the member updates its own key package only in this operation, the Commit message could potentially also cover Add Proposals that were previously received in the epoch. Therefore the function can also optionally return a `Welcome` message. The `Welcome` message must be sent to the newly added members. +Even though the member updates its own leaf node only in this operation, the Commit message could potentially also cover Add Proposals that were previously received in the epoch. Therefore the function can also optionally return a `Welcome` message. The `Welcome` message must be sent to the newly added members. ## Proposal -Members can also update their key package as a proposal (without the corresponding Commit message) by using the `.propose_self_update()` function. Just like with the `.self_update()` function, an optional key package can be provided: +Members can also update their leaf node as a proposal (without the corresponding Commit message) by using the `.propose_self_update()` function. Just like with the `.self_update()` function, optional parameters can be set through `LeafNodeParameters`: ```rust,no_run,noplayground {{#include ../../../openmls/tests/book_code.rs:propose_self_update}} diff --git a/interop_client/src/main.rs b/interop_client/src/main.rs index cb82d3824..989bd246a 100644 --- a/interop_client/src/main.rs +++ b/interop_client/src/main.rs @@ -23,7 +23,7 @@ use openmls::{ key_packages::{KeyPackage, KeyPackageBundle}, prelude::{Capabilities, ExtensionType, SenderRatchetConfiguration}, schedule::{psk::ResumptionPskUsage, ExternalPsk, PreSharedKeyId, Psk}, - treesync::RatchetTreeIn, + treesync::{LeafNodeParameters, RatchetTreeIn}, versions::ProtocolVersion, }; use openmls_basic_credential::SignatureKeyPair; @@ -495,6 +495,8 @@ impl MlsClient for MlsClientImpl { ratchet_tree, verifiable_group_info, &mls_group_config, + None, + None, b"", credential_with_key, ) @@ -834,7 +836,7 @@ impl MlsClient for MlsClientImpl { .propose_self_update( &interop_group.crypto_provider, &interop_group.signature_keys, - None, + LeafNodeParameters::default(), ) .map_err(into_status)?; diff --git a/openmls/benches/benchmark.rs b/openmls/benches/benchmark.rs index 97d4198d7..346076d41 100644 --- a/openmls/benches/benchmark.rs +++ b/openmls/benches/benchmark.rs @@ -255,8 +255,9 @@ fn create_commit(c: &mut Criterion, provider: &impl OpenMlsProvider) { (bob_group, bob_signer) }, |(mut bob_group, bob_signer)| { - let (_queued_message, _welcome_option, _group_info) = - bob_group.self_update(provider, &bob_signer).unwrap(); + let (_queued_message, _welcome_option, _group_info) = bob_group + .self_update(provider, &bob_signer, LeafNodeParameters::default()) + .unwrap(); bob_group .merge_pending_commit(provider) diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index f65f823be..2b77a94a9 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -17,6 +17,7 @@ use openmls::{ group::{MlsGroup, MlsGroupCreateConfig, StagedWelcome, PURE_PLAINTEXT_WIRE_FORMAT_POLICY}, prelude::LeafNodeIndex, prelude_test::*, + treesync::LeafNodeParameters, }; use openmls_basic_credential::SignatureKeyPair; use openmls_rust_crypto::OpenMlsRustCrypto; @@ -102,7 +103,9 @@ fn self_update( provider: &OpenMlsRustCrypto, signer: &SignatureKeyPair, ) -> MlsMessageOut { - let (commit, _, _group_info) = group.self_update(provider, signer).unwrap(); + let (commit, _, _group_info) = group + .self_update(provider, signer, LeafNodeParameters::default()) + .unwrap(); group.merge_pending_commit(provider).unwrap(); diff --git a/openmls/src/binary_tree/array_representation/diff.rs b/openmls/src/binary_tree/array_representation/diff.rs index 554dfa9b2..b14bfbfa0 100644 --- a/openmls/src/binary_tree/array_representation/diff.rs +++ b/openmls/src/binary_tree/array_representation/diff.rs @@ -335,30 +335,6 @@ impl<'a, L: Clone + Debug + Default, P: Clone + Debug + Default> AbDiff<'a, L, P self.original_tree.parent_by_index(parent_index) } - /// Returns a mutable reference to the leaf node in the diff at index - /// `leaf_index`. If the diff doesn't have a node at that index, it clones - /// the node to the diff and returns a mutable reference to that node. - pub(crate) fn leaf_mut(&mut self, leaf_index: LeafNodeIndex) -> &mut L { - debug_assert!(leaf_index.u32() < self.leaf_count()); - // We then check if the node is already in the diff. (Not using `if let - // ...` here, because the borrow checker doesn't like that). - if self.leaf_diff.contains_key(&leaf_index) { - return self - .leaf_diff - .get_mut(&leaf_index) - // We just checked that this index exists, so this must be Some. - .unwrap_or(&mut self.default_leaf); - // If not, we take a copy from the original tree and put it in the - // diff before returning a mutable reference to it. - } - let tree_node = self.original_tree.leaf_by_index(leaf_index); - self.replace_leaf(leaf_index, tree_node.clone()); - self.leaf_diff - .get_mut(&leaf_index) - // We just inserted this into the diff, so this should be Some. - .unwrap_or(&mut self.default_leaf) - } - /// Returns a mutable reference to the parent node in the diff at index /// `parent_index`. If the diff doesn't have a node at that index, it clones /// the node to the diff and returns a mutable reference to that node. diff --git a/openmls/src/credentials/mod.rs b/openmls/src/credentials/mod.rs index d00294aff..94a8a6f10 100644 --- a/openmls/src/credentials/mod.rs +++ b/openmls/src/credentials/mod.rs @@ -288,7 +288,7 @@ impl TryFrom for BasicCredential { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] /// A wrapper around a credential with a corresponding public key. pub struct CredentialWithKey { /// The [`Credential`]. diff --git a/openmls/src/group/core_group/create_commit_params.rs b/openmls/src/group/core_group/create_commit_params.rs index 1fcadd199..02d205c76 100644 --- a/openmls/src/group/core_group/create_commit_params.rs +++ b/openmls/src/group/core_group/create_commit_params.rs @@ -8,20 +8,21 @@ use crate::{ #[cfg(doc)] use super::CoreGroup; +use super::LeafNodeParameters; /// Can be used to denote the type of a commit. -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) enum CommitType { - External, + External(CredentialWithKey), Member, } pub(crate) struct CreateCommitParams<'a> { - framing_parameters: FramingParameters<'a>, // Mandatory - inline_proposals: Vec, // Optional - force_self_update: bool, // Optional - commit_type: CommitType, // Optional (default is `Member`) - credential_with_key: Option, // Mandatory for external commits + framing_parameters: FramingParameters<'a>, // Mandatory + inline_proposals: Vec, // Optional + force_self_update: bool, // Optional + commit_type: CommitType, // Optional (default is `Member`) + leaf_node_parameters: LeafNodeParameters, // Optional } pub(crate) struct TempBuilderCCPM0 {} @@ -41,7 +42,7 @@ impl TempBuilderCCPM0 { inline_proposals: vec![], force_self_update: true, commit_type: CommitType::Member, - credential_with_key: None, + leaf_node_parameters: LeafNodeParameters::default(), }, } } @@ -61,8 +62,8 @@ impl<'a> CreateCommitParamsBuilder<'a> { self.ccp.commit_type = commit_type; self } - pub(crate) fn credential_with_key(mut self, credential_with_key: CredentialWithKey) -> Self { - self.ccp.credential_with_key = Some(credential_with_key); + pub(crate) fn leaf_node_parameters(mut self, leaf_node_parameters: LeafNodeParameters) -> Self { + self.ccp.leaf_node_parameters = leaf_node_parameters; self } pub(crate) fn build(self) -> CreateCommitParams<'a> { @@ -80,13 +81,16 @@ impl<'a> CreateCommitParams<'a> { pub(crate) fn inline_proposals(&self) -> &[Proposal] { &self.inline_proposals } + pub(crate) fn set_inline_proposals(&mut self, inline_proposals: Vec) { + self.inline_proposals = inline_proposals; + } pub(crate) fn force_self_update(&self) -> bool { self.force_self_update } - pub(crate) fn commit_type(&self) -> CommitType { - self.commit_type + pub(crate) fn commit_type(&self) -> &CommitType { + &self.commit_type } - pub(crate) fn take_credential_with_key(&mut self) -> Option { - self.credential_with_key.take() + pub(crate) fn leaf_node_parameters(&self) -> &LeafNodeParameters { + &self.leaf_node_parameters } } diff --git a/openmls/src/group/core_group/kat_passive_client.rs b/openmls/src/group/core_group/kat_passive_client.rs index 37566545e..ece189280 100644 --- a/openmls/src/group/core_group/kat_passive_client.rs +++ b/openmls/src/group/core_group/kat_passive_client.rs @@ -1,3 +1,4 @@ +use core_group::LeafNodeParameters; use log::{debug, info, warn}; use openmls_traits::{crypto::OpenMlsCrypto, storage::StorageProvider, OpenMlsProvider}; use serde::{self, Deserialize, Serialize}; @@ -559,7 +560,11 @@ fn update_inline( group: &mut MlsGroup, ) -> TestEpoch { let (mls_message_out_commit, _, _) = group - .self_update(provider, &candidate.signature_keypair) + .self_update( + provider, + &candidate.signature_keypair, + LeafNodeParameters::default(), + ) .unwrap(); group.merge_pending_commit(provider).unwrap(); diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index b32f520b4..589360f46 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -845,14 +845,14 @@ impl CoreGroup { pub(crate) fn create_commit( &self, - mut params: CreateCommitParams, + params: CreateCommitParams, provider: &Provider, signer: &impl Signer, ) -> Result> { let ciphersuite = self.ciphersuite(); let sender = match params.commit_type() { - CommitType::External => Sender::NewMemberCommit, + CommitType::External(_) => Sender::NewMemberCommit, CommitType::Member => Sender::build_member(self.own_leaf_index()), }; @@ -925,7 +925,7 @@ impl CoreGroup { // Apply proposals to tree let apply_proposals_values = diff.apply_proposals(&proposal_queue, self.own_leaf_index())?; - if apply_proposals_values.self_removed && params.commit_type() != CommitType::External { + if apply_proposals_values.self_removed && params.commit_type() == &CommitType::Member { return Err(CreateCommitError::CannotRemoveSelf); } @@ -934,6 +934,7 @@ impl CoreGroup { if apply_proposals_values.path_required || contains_own_updates || params.force_self_update() + || !params.leaf_node_parameters().is_empty() { // Process the path. This includes updating the provisional // group context by updating the epoch and computing the new @@ -943,8 +944,8 @@ impl CoreGroup { self.own_leaf_index(), apply_proposals_values.exclusion_list(), params.commit_type(), + params.leaf_node_parameters(), signer, - params.take_credential_with_key(), apply_proposals_values.extensions.clone() )? } else { diff --git a/openmls/src/group/core_group/new_from_external_init.rs b/openmls/src/group/core_group/new_from_external_init.rs index 27b130c30..ee69ced89 100644 --- a/openmls/src/group/core_group/new_from_external_init.rs +++ b/openmls/src/group/core_group/new_from_external_init.rs @@ -97,17 +97,22 @@ impl CoreGroup { // If there is a group member in the group with the same identity as us, // commit a remove proposal. - let params_credential_with_key = params - .take_credential_with_key() - .ok_or(ExternalCommitError::MissingCredential)?; - if let Some(us) = public_group.members().find(|member| { - member.signature_key == params_credential_with_key.signature_key.as_slice() - }) { + let signature_key = match params.commit_type() { + CommitType::External(credential_with_key) => { + credential_with_key.signature_key.as_slice() + } + _ => return Err(ExternalCommitError::MissingCredential), + }; + if let Some(us) = public_group + .members() + .find(|member| member.signature_key == signature_key) + { let remove_proposal = Proposal::Remove(RemoveProposal { removed: us.index }); inline_proposals.push(remove_proposal); }; let own_leaf_index = public_group.leftmost_free_index(inline_proposals.iter().map(Some))?; + params.set_inline_proposals(inline_proposals); let group = CoreGroup { public_group, @@ -119,13 +124,6 @@ impl CoreGroup { resumption_psk_store: ResumptionPskStore::new(32), }; - let params = CreateCommitParams::builder() - .framing_parameters(*params.framing_parameters()) - .inline_proposals(inline_proposals) - .commit_type(CommitType::External) - .credential_with_key(params_credential_with_key) - .build(); - // Immediately create the commit to add ourselves to the group. let create_commit_result = group.create_commit(params, provider, signer); debug_assert!( diff --git a/openmls/src/group/core_group/proposals.rs b/openmls/src/group/core_group/proposals.rs index efbc1164f..070c094a1 100644 --- a/openmls/src/group/core_group/proposals.rs +++ b/openmls/src/group/core_group/proposals.rs @@ -88,20 +88,6 @@ impl QueuedProposal { ) } - /// Creates a new [QueuedProposal] from an [PublicMessage] - pub(crate) fn from_authenticated_content_by_value( - ciphersuite: Ciphersuite, - crypto: &impl OpenMlsCrypto, - public_message: AuthenticatedContent, - ) -> Result { - Self::from_authenticated_content( - ciphersuite, - crypto, - public_message, - ProposalOrRefType::Proposal, - ) - } - /// Creates a new [QueuedProposal] from an [PublicMessage] pub(crate) fn from_authenticated_content( ciphersuite: Ciphersuite, diff --git a/openmls/src/group/core_group/test_core_group.rs b/openmls/src/group/core_group/test_core_group.rs index b9b059a13..cf3a95c72 100644 --- a/openmls/src/group/core_group/test_core_group.rs +++ b/openmls/src/group/core_group/test_core_group.rs @@ -1,3 +1,4 @@ +use core_group::LeafNodeParameters; use openmls_basic_credential::SignatureKeyPair; use openmls_traits::types::HpkeCiphertext; use tls_codec::Serialize; @@ -12,7 +13,7 @@ use crate::{ messages::{group_info::GroupInfoTBS, *}, schedule::psk::{store::ResumptionPskStore, ExternalPsk, PreSharedKeyId, Psk}, test_utils::*, - treesync::{errors::ApplyUpdatePathError, node::leaf_node::TreeInfoTbs}, + treesync::errors::ApplyUpdatePathError, }; pub(crate) fn setup_alice_group( @@ -184,22 +185,20 @@ fn test_update_path() { ) = test_framing::setup_alice_bob_group(ciphersuite, provider); // === Bob updates and commits === - let bob_old_leaf = group_bob.own_leaf_node().unwrap(); - let bob_update_leaf_node = bob_old_leaf - .updated( + let mut bob_new_leaf_node = group_bob.own_leaf_node().unwrap().clone(); + bob_new_leaf_node + .update( ciphersuite, - TreeInfoTbs::Update(group_bob.own_tree_position()), provider, &bob_signature_keys, + group_bob.group_id().clone(), + group_bob.own_leaf_index(), + LeafNodeParameters::default(), ) .unwrap(); let update_proposal_bob = group_bob - .create_update_proposal( - framing_parameters, - bob_update_leaf_node, - &bob_signature_keys, - ) + .create_update_proposal(framing_parameters, bob_new_leaf_node, &bob_signature_keys) .expect("Could not create proposal."); group_bob.proposal_store_mut().empty(); @@ -393,22 +392,20 @@ fn test_psks() { .expect("Could not create new group from Welcome"); // === Bob updates and commits === - let bob_old_leaf = group_bob.own_leaf_node().unwrap(); - let bob_update_leaf_node = bob_old_leaf - .updated( + let mut bob_new_leaf_node = group_bob.own_leaf_node().unwrap().clone(); + bob_new_leaf_node + .update( ciphersuite, - TreeInfoTbs::Update(group_bob.own_tree_position()), provider, &bob_signature_keys, + group_bob.group_id().clone(), + group_bob.own_leaf_index(), + LeafNodeParameters::default(), ) .unwrap(); let update_proposal_bob = group_bob - .create_update_proposal( - framing_parameters, - bob_update_leaf_node, - &bob_signature_keys, - ) + .create_update_proposal(framing_parameters, bob_new_leaf_node, &bob_signature_keys) .expect("Could not create proposal."); group_bob.proposal_store_mut().empty(); diff --git a/openmls/src/group/errors.rs b/openmls/src/group/errors.rs index 8dc1332e5..58eed0c99 100644 --- a/openmls/src/group/errors.rs +++ b/openmls/src/group/errors.rs @@ -11,11 +11,10 @@ use crate::{ error::LibraryError, extensions::errors::{ExtensionError, InvalidExtensionError}, framing::errors::MessageDecryptionError, - key_packages::errors::KeyPackageVerifyError, - key_packages::errors::{KeyPackageExtensionSupportError, KeyPackageNewError}, + key_packages::errors::{KeyPackageExtensionSupportError, KeyPackageVerifyError}, messages::{group_info::GroupInfoError, GroupSecretsError}, schedule::errors::PskError, - treesync::errors::*, + treesync::{errors::*, node::leaf_node::LeafNodeUpdateError}, }; /// Welcome error @@ -229,9 +228,6 @@ pub enum CreateCommitError { /// Error interacting with storage. #[error("Error interacting with storage.")] KeyStoreError(StorageError), - /// See [`KeyPackageNewError`] for more details. - #[error(transparent)] - KeyPackageGenerationError(#[from] KeyPackageNewError), /// See [`SignatureError`] for more details. #[error(transparent)] SignatureError(#[from] SignatureError), @@ -249,6 +245,12 @@ pub enum CreateCommitError { GroupContextExtensionsProposalValidationError( #[from] GroupContextExtensionsProposalValidationError, ), + /// See [`LeafNodeUpdateError`] for more details. + #[error(transparent)] + LeafNodeUpdateError(#[from] LeafNodeUpdateError), + /// See [`TreeSyncAddLeaf`] for more details. + #[error(transparent)] + TreeSyncAddLeaf(#[from] TreeSyncAddLeaf), } /// Validation error diff --git a/openmls/src/group/mls_group/creation.rs b/openmls/src/group/mls_group/creation.rs index bba28e1d9..d473f3e31 100644 --- a/openmls/src/group/mls_group/creation.rs +++ b/openmls/src/group/mls_group/creation.rs @@ -4,7 +4,7 @@ use super::{builder::MlsGroupBuilder, *}; use crate::{ credentials::CredentialWithKey, group::{ - core_group::create_commit_params::CreateCommitParams, + core_group::create_commit_params::{CommitType, CreateCommitParams}, errors::{ExternalCommitError, WelcomeError}, }, messages::{ @@ -13,7 +13,10 @@ use crate::{ }, schedule::psk::{store::ResumptionPskStore, PreSharedKeyId}, storage::OpenMlsProvider, - treesync::RatchetTreeIn, + treesync::{ + node::leaf_node::{Capabilities, LeafNodeParameters}, + RatchetTreeIn, + }, }; impl MlsGroup { @@ -77,12 +80,15 @@ impl MlsGroup { /// /// Note: If there is a group member in the group with the same identity as /// us, this will create a remove proposal. + #[allow(clippy::too_many_arguments)] pub fn join_by_external_commit( provider: &Provider, signer: &impl Signer, ratchet_tree: Option, verifiable_group_info: VerifiableGroupInfo, mls_group_config: &MlsGroupJoinConfig, + capabilities: Option, + extensions: Option, aad: &[u8], credential_with_key: CredentialWithKey, ) -> Result<(Self, MlsMessageOut, Option), ExternalCommitError> @@ -90,9 +96,14 @@ impl MlsGroup { // Prepare the commit parameters let framing_parameters = FramingParameters::new(aad, WireFormat::PublicMessage); + let leaf_node_parameters = LeafNodeParameters::builder() + .with_capabilities(capabilities.unwrap_or_default()) + .with_extensions(extensions.unwrap_or_default()) + .build(); let params = CreateCommitParams::builder() .framing_parameters(framing_parameters) - .credential_with_key(credential_with_key) + .commit_type(CommitType::External(credential_with_key)) + .leaf_node_parameters(leaf_node_parameters) .build(); let (mut group, create_commit_result) = CoreGroup::join_by_external_commit( provider, diff --git a/openmls/src/group/mls_group/errors.rs b/openmls/src/group/mls_group/errors.rs index abf94bdbe..f5fff760f 100644 --- a/openmls/src/group/mls_group/errors.rs +++ b/openmls/src/group/mls_group/errors.rs @@ -18,7 +18,10 @@ use crate::{ CreateGroupContextExtProposalError, }, schedule::errors::PskError, - treesync::errors::{LeafNodeValidationError, PublicTreeError}, + treesync::{ + errors::{LeafNodeValidationError, PublicTreeError}, + node::leaf_node::LeafNodeUpdateError, + }, }; /// New group error @@ -256,6 +259,9 @@ pub enum ProposeSelfUpdateError { /// See [`PublicTreeError`] for more details. #[error(transparent)] PublicTreeError(#[from] PublicTreeError), + /// See [`LeafNodeUpdateError`] for more details. + #[error(transparent)] + LeafNodeUpdateError(#[from] LeafNodeUpdateError), } /// Commit to pending proposals error diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index c45c3cd55..d023b5b65 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -18,7 +18,7 @@ use crate::{ prelude::LibraryError, schedule::PreSharedKeyId, storage::OpenMlsProvider, - treesync::LeafNode, + treesync::LeafNodeParameters, versions::ProtocolVersion, }; @@ -29,7 +29,7 @@ pub enum Propose { Add(KeyPackage), /// An update proposal requires a new leaf node. - Update(Option), + Update(LeafNodeParameters), /// A remove proposal consists of the leaf index of the leaf to be removed. Remove(u32), @@ -159,12 +159,12 @@ impl MlsGroup { .map_err(|e| e.into()), }, - Propose::Update(leaf_node) => match ref_or_value { + Propose::Update(leaf_node_parameters) => match ref_or_value { ProposalOrRefType::Proposal => self - .propose_self_update_by_value(provider, signer, leaf_node) + .propose_self_update(provider, signer, leaf_node_parameters) .map_err(|e| e.into()), ProposalOrRefType::Reference => self - .propose_self_update(provider, signer, leaf_node) + .propose_self_update(provider, signer, leaf_node_parameters) .map_err(|e| e.into()), }, diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index 8b2ee40fe..4c15cd10c 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -19,6 +19,7 @@ use crate::{ }, }, tree::sender_ratchet::SenderRatchetConfiguration, + treesync::LeafNodeParameters, }; #[openmls_test] @@ -353,7 +354,7 @@ fn test_invalid_plaintext() { .expect("An unexpected error occurred."); let (mls_message, _welcome_option, _group_info) = client - .self_update(Commit, &group_id, None) + .self_update(Commit, &group_id, LeafNodeParameters::default()) .expect("error creating self update"); // Store the context and membership key so that we can re-compute the membership tag later. @@ -479,7 +480,7 @@ fn test_verify_staged_commit_credentials( } let (_msg, welcome_option, _group_info) = alice_group - .self_update(provider, &alice_signer) + .self_update(provider, &alice_signer, LeafNodeParameters::default()) .expect("error creating self-update commit"); // Merging the pending commit should clear the pending commit and we should @@ -521,7 +522,7 @@ fn test_verify_staged_commit_credentials( // === Make a new, empty commit and check that the leaf node credentials match === let (commit_msg, _welcome_option, _group_info) = alice_group - .self_update(provider, &alice_signer) + .self_update(provider, &alice_signer, LeafNodeParameters::default()) .expect("error creating self-update commit"); // empty commits should only produce a single message @@ -659,7 +660,7 @@ fn test_commit_with_update_path_leaf_node( println!("\nCreating commit with add proposal."); let (_msg, welcome_option, _group_info) = alice_group - .self_update(provider, &alice_signer) + .self_update(provider, &alice_signer, LeafNodeParameters::default()) .expect("error creating self-update commit"); println!("Done creating commit."); @@ -704,7 +705,7 @@ fn test_commit_with_update_path_leaf_node( println!("\nCreating self-update commit."); let (commit_msg, _welcome_option, _group_info) = alice_group - .self_update(provider, &alice_signer) + .self_update(provider, &alice_signer, LeafNodeParameters::default()) .expect("error creating self-update commit"); println!("Done creating commit."); @@ -855,7 +856,7 @@ fn test_pending_commit_logic( println!("\nCreating commit with add proposal."); let (_msg, _welcome_option, _group_info) = alice_group - .self_update(provider, &alice_signer) + .self_update(provider, &alice_signer, LeafNodeParameters::default()) .expect("error creating self-update commit"); println!("Done creating commit."); @@ -900,14 +901,14 @@ fn test_pending_commit_logic( CommitToPendingProposalsError::GroupStateError(MlsGroupStateError::PendingCommit) )); let error = alice_group - .self_update(provider, &alice_signer) + .self_update(provider, &alice_signer, LeafNodeParameters::default()) .expect_err("no error committing while a commit is pending"); assert!(matches!( error, SelfUpdateError::GroupStateError(MlsGroupStateError::PendingCommit) )); let error = alice_group - .propose_self_update(provider, &alice_signer, None) + .propose_self_update(provider, &alice_signer, LeafNodeParameters::default()) .expect_err("no error creating a proposal while a commit is pending"); assert!(matches!( error, @@ -922,7 +923,7 @@ fn test_pending_commit_logic( // Creating a new commit should commit the same proposals. let (_msg, welcome_option, _group_info) = alice_group - .self_update(provider, &alice_signer) + .self_update(provider, &alice_signer, LeafNodeParameters::default()) .expect("error creating self-update commit"); // Merging the pending commit should clear the pending commit and we should @@ -962,11 +963,11 @@ fn test_pending_commit_logic( // While a commit is pending, merging Bob's commit should clear the pending commit. let (_msg, _welcome_option, _group_info) = alice_group - .self_update(provider, &alice_signer) + .self_update(provider, &alice_signer, LeafNodeParameters::default()) .expect("error creating self-update commit"); let (msg, _welcome_option, _group_info) = bob_group - .self_update(provider, &bob_signer) + .self_update(provider, &bob_signer, LeafNodeParameters::default()) .expect("error creating self-update commit"); let alice_processed_message = alice_group diff --git a/openmls/src/group/mls_group/updates.rs b/openmls/src/group/mls_group/updates.rs index d53706aab..68693dad5 100644 --- a/openmls/src/group/mls_group/updates.rs +++ b/openmls/src/group/mls_group/updates.rs @@ -1,25 +1,25 @@ use core_group::create_commit_params::CreateCommitParams; use openmls_traits::{signatures::Signer, storage::StorageProvider as _}; -use crate::{messages::group_info::GroupInfo, storage::OpenMlsProvider, treesync::LeafNode}; +use crate::{ + messages::group_info::GroupInfo, storage::OpenMlsProvider, treesync::LeafNodeParameters, +}; use super::*; impl MlsGroup { - /// Updates the own leaf node. + /// Updates the own leaf node. The application can choose to update the + /// credential, the capabilities, and the extensions by buliding the + /// [`LeafNodeParameters`]. /// /// If successful, it returns a tuple of [`MlsMessageOut`] (containing the - /// commit), an optional [`MlsMessageOut`] (containing the [`Welcome`]) and the [GroupInfo]. - /// The [`Welcome`] is [Some] when the queue of pending proposals contained - /// add proposals - /// The [GroupInfo] is [Some] if the group has the `use_ratchet_tree_extension` flag set. + /// commit), an optional [`MlsMessageOut`] (containing the [`Welcome`]) and + /// the [GroupInfo]. The [`Welcome`] is [Some] when the queue of pending + /// proposals contained add proposals The [GroupInfo] is [Some] if the group + /// has the `use_ratchet_tree_extension` flag set. /// /// Returns an error if there is a pending commit. /// - /// TODO #1208 : The caller should be able to optionally provide a - /// [`LeafNode`] here, so that things like extensions can be changed via - /// commit. - /// /// [`Welcome`]: crate::messages::Welcome // FIXME: #1217 #[allow(clippy::type_complexity)] @@ -27,6 +27,7 @@ impl MlsGroup { &mut self, provider: &Provider, signer: &impl Signer, + leaf_node_parameters: LeafNodeParameters, ) -> Result< (MlsMessageOut, Option, Option), SelfUpdateError, @@ -35,6 +36,7 @@ impl MlsGroup { let params = CreateCommitParams::builder() .framing_parameters(self.framing_parameters()) + .leaf_node_parameters(leaf_node_parameters) .build(); // Create Commit over all proposals. // TODO #751 @@ -74,7 +76,7 @@ impl MlsGroup { &mut self, provider: &Provider, signer: &impl Signer, - leaf_node: Option, + leaf_node_parmeters: LeafNodeParameters, ) -> Result> { self.is_operational()?; @@ -88,27 +90,15 @@ impl MlsGroup { .leaf(self.own_leaf_index()) .ok_or_else(|| LibraryError::custom("The tree is broken. Couldn't find own leaf."))? .clone(); - if let Some(leaf) = leaf_node { - own_leaf.update_and_re_sign( - None, - leaf, - self.group_id().clone(), - self.own_leaf_index(), - signer, - )? - } else { - let keypair = own_leaf.rekey( - self.group_id(), - self.own_leaf_index(), - self.ciphersuite(), - provider, - signer, - )?; - // TODO #1207: Move to the top of the function. - keypair - .write(provider.storage()) - .map_err(ProposeSelfUpdateError::StorageError)?; - }; + + own_leaf.update( + self.ciphersuite(), + provider, + signer, + self.group_id().clone(), + self.own_leaf_index(), + leaf_node_parmeters, + )?; let update_proposal = self.group.create_update_proposal( self.framing_parameters(), @@ -125,14 +115,16 @@ impl MlsGroup { Ok(update_proposal) } - /// Creates a proposal to update the own leaf node. + /// Creates a proposal to update the own leaf node. The application can + /// choose to update the credential, the capabilities, and the extensions by + /// buliding the [`LeafNodeParameters`]. pub fn propose_self_update( &mut self, provider: &Provider, signer: &impl Signer, - leaf_node: Option, + leaf_node_parameters: LeafNodeParameters, ) -> Result<(MlsMessageOut, ProposalRef), ProposeSelfUpdateError> { - let update_proposal = self._propose_self_update(provider, signer, leaf_node)?; + let update_proposal = self._propose_self_update(provider, signer, leaf_node_parameters)?; let proposal = QueuedProposal::from_authenticated_content_by_ref( self.ciphersuite(), provider.crypto(), @@ -149,29 +141,4 @@ impl MlsGroup { Ok((mls_message, proposal_ref)) } - - /// Creates a proposal to update the own leaf node. - pub fn propose_self_update_by_value( - &mut self, - provider: &Provider, - signer: &impl Signer, - leaf_node: Option, - ) -> Result<(MlsMessageOut, ProposalRef), ProposeSelfUpdateError> { - let update_proposal = self._propose_self_update(provider, signer, leaf_node)?; - let proposal = QueuedProposal::from_authenticated_content_by_value( - self.ciphersuite(), - provider.crypto(), - update_proposal.clone(), - )?; - let proposal_ref = proposal.proposal_reference(); - provider - .storage() - .queue_proposal(self.group_id(), &proposal_ref, &proposal) - .map_err(ProposeSelfUpdateError::StorageError)?; - self.proposal_store_mut().add(proposal); - - let mls_message = self.content_to_mls_message(update_proposal, provider)?; - - Ok((mls_message, proposal_ref)) - } } diff --git a/openmls/src/group/public_group/diff/compute_path.rs b/openmls/src/group/public_group/diff/compute_path.rs index 6caa719c2..fcb608f4d 100644 --- a/openmls/src/group/public_group/diff/compute_path.rs +++ b/openmls/src/group/public_group/diff/compute_path.rs @@ -9,12 +9,12 @@ use crate::{ error::LibraryError, extensions::Extensions, group::{core_group::create_commit_params::CommitType, errors::CreateCommitError}, - key_packages::{KeyPackage, KeyPackageCreationResult}, schedule::CommitSecret, storage::OpenMlsProvider, treesync::{ node::{ - encryption_keys::EncryptionKeyPair, leaf_node::LeafNode, + encryption_keys::EncryptionKeyPair, + leaf_node::{Capabilities, LeafNodeParameters, UpdateLeafNodeParams}, parent_node::PlainUpdatePathNode, }, treekem::UpdatePath, @@ -40,59 +40,76 @@ impl<'a> PublicGroupDiff<'a> { provider: &Provider, leaf_index: LeafNodeIndex, exclusion_list: HashSet<&LeafNodeIndex>, - commit_type: CommitType, + commit_type: &CommitType, + leaf_node_params: &LeafNodeParameters, signer: &impl Signer, - credential_with_key: Option, - extensions: Option, + gc_extensions: Option, ) -> Result> { let ciphersuite = self.group_context().ciphersuite(); - let group_id = self.group_context().group_id().clone(); - - let mut new_keypairs = if commit_type == CommitType::External { - // If this is an external commit we add a fresh leaf to the diff. - // Generate a KeyPackageBundle to generate a payload from for later - // path generation. - let KeyPackageCreationResult { - key_package, - encryption_keypair, - // The KeyPackage is immediately put into the group. No need for - // the init key. - init_private_key: _, - } = KeyPackage::builder().build_without_storage( - ciphersuite, - provider, - signer, - credential_with_key.ok_or(CreateCommitError::MissingCredential)?, - )?; - - let leaf_node: LeafNode = key_package.into(); - self.diff - .add_leaf(leaf_node) - .map_err(|_| LibraryError::custom("Tree full: cannot add more members"))?; - vec![encryption_keypair] + + let leaf_node_params = if let CommitType::External(credential_with_key) = commit_type { + let capabilities = match leaf_node_params.capabilities() { + Some(c) => c.to_owned(), + None => Capabilities::default(), + }; + + let extensions = match leaf_node_params.extensions() { + Some(e) => e.to_owned(), + None => Extensions::default(), + }; + + UpdateLeafNodeParams { + credential_with_key: credential_with_key.clone(), + capabilities, + extensions, + } } else { - // If we're already in the tree, we rekey our existing leaf. - let own_diff_leaf = self + let leaf = self .diff - .leaf_mut(leaf_index) - .ok_or_else(|| LibraryError::custom("Unable to get own leaf from diff"))?; - let encryption_keypair = - own_diff_leaf.rekey(&group_id, leaf_index, ciphersuite, provider, signer)?; - vec![encryption_keypair] + .leaf(leaf_index) + .ok_or_else(|| LibraryError::custom("Couldn't find own leaf"))?; + + let credential_with_key = match leaf_node_params.credential_with_key() { + Some(cwk) => cwk.to_owned(), + None => CredentialWithKey { + credential: leaf.credential().clone(), + signature_key: leaf.signature_key().clone(), + }, + }; + + let capabilities = match leaf_node_params.capabilities() { + Some(c) => c.to_owned(), + None => leaf.capabilities().clone(), + }; + + let extensions = match leaf_node_params.extensions() { + Some(e) => e.to_owned(), + None => leaf.extensions().clone(), + }; + + UpdateLeafNodeParams { + credential_with_key, + capabilities, + extensions, + } }; // Derive and apply an update path based on the previously // generated new leaf. - let (plain_path, mut new_parent_keypairs, commit_secret) = self - .diff - .apply_own_update_path(provider, signer, ciphersuite, group_id, leaf_index)?; - - new_keypairs.append(&mut new_parent_keypairs); + let (plain_path, new_keypairs, commit_secret) = self.diff.apply_own_update_path( + provider, + signer, + ciphersuite, + commit_type, + self.group_context().group_id().clone(), + leaf_index, + leaf_node_params, + )?; // After we've processed the path, we can update the group context s.t. // the updated group context is used for path secret encryption. Note // that we have not yet updated the confirmed transcript hash. - self.update_group_context(provider.crypto(), extensions)?; + self.update_group_context(provider.crypto(), gc_extensions)?; let serialized_group_context = self .group_context() diff --git a/openmls/src/group/tests/external_add_proposal.rs b/openmls/src/group/tests/external_add_proposal.rs index a284ec20b..4181e20a7 100644 --- a/openmls/src/group/tests/external_add_proposal.rs +++ b/openmls/src/group/tests/external_add_proposal.rs @@ -9,6 +9,7 @@ use crate::{ external_proposals::*, proposals::{AddProposal, Proposal, ProposalType}, }, + treesync::LeafNodeParameters, }; use openmls_traits::{types::Ciphersuite, OpenMlsProvider as _}; @@ -343,7 +344,7 @@ fn new_member_proposal_sender_should_be_reserved_for_join_proposals { + let update_proposal = FrankenUpdateProposal { + leaf_node: franken_leaf_node.clone(), + }; + *update = update_proposal; + } + _ => { + panic!("Unexpected content type"); + } + } + + // Rebuild the PublicMessage with the new content + let group_context = bob_group.export_group_context().clone(); + let membership_key = bob_group + .group() + .message_secrets() + .membership_key() + .as_slice(); + + let new_public_message = FrankenPublicMessage::auth( + provider, + ciphersuite, + &bob_credential_with_key_and_signer.signer, + content, + Some(&group_context.into()), + Some(membership_key), + None, + ); + + // And turn it into a protocol message + let protocol_message = + ProtocolMessage::PublicMessage(PublicMessage::from(new_public_message).into()); + // Have Alice process this proposal. if let ProcessedMessageContent::ProposalMessage(proposal) = alice_group - .process_message( - provider, - update_proposal.try_into_protocol_message().unwrap(), - ) + .process_message(provider, protocol_message) .expect("error processing proposal") .into_content() { @@ -1750,7 +1803,11 @@ fn test_valsem110() { // Create the Commit. let serialized_update = alice_group - .self_update(provider, &alice_credential_with_key_and_signer.signer) + .self_update( + provider, + &alice_credential_with_key_and_signer.signer, + LeafNodeParameters::default(), + ) .expect("Error creating self-update") .tls_serialize_detached() .expect("Could not serialize message."); @@ -1763,7 +1820,7 @@ fn test_valsem110() { let original_plaintext = plaintext.clone(); let update_proposal = Proposal::Update(UpdateProposal { - leaf_node: update_leaf_node, + leaf_node: franken_leaf_node.into(), }); // Artificially add the proposal. @@ -1837,7 +1894,11 @@ fn test_valsem111() { // We now have Alice create a commit. That commit should not contain any // proposals, just a path. let commit = alice_group - .self_update(provider, &alice_credential_with_key_and_signer.signer) + .self_update( + provider, + &alice_credential_with_key_and_signer.signer, + LeafNodeParameters::default(), + ) .expect("Error creating self-update"); // Check that there's no proposal in it. @@ -1918,7 +1979,11 @@ fn test_valsem111() { .unwrap(); let commit = alice_group - .self_update(provider, &alice_credential_with_key_and_signer.signer) + .self_update( + provider, + &alice_credential_with_key_and_signer.signer, + LeafNodeParameters::default(), + ) .expect("Error creating self-update"); let serialized_update = commit @@ -1998,7 +2063,11 @@ fn test_valsem112() { // However, we can test the receiving side by crafting such a proposal // manually. let commit = alice_group - .propose_self_update(provider, &alice_credential_with_key_and_signer.signer, None) + .propose_self_update( + provider, + &alice_credential_with_key_and_signer.signer, + LeafNodeParameters::default(), + ) .expect("Error creating self-update"); // Check that the sender type is indeed `member`. diff --git a/openmls/src/group/tests/test_wire_format_policy.rs b/openmls/src/group/tests/test_wire_format_policy.rs index 2748785d3..59e6a09bb 100644 --- a/openmls/src/group/tests/test_wire_format_policy.rs +++ b/openmls/src/group/tests/test_wire_format_policy.rs @@ -2,7 +2,7 @@ use openmls_traits::{signatures::Signer, types::Ciphersuite}; -use crate::{framing::*, group::*}; +use crate::{framing::*, group::*, treesync::LeafNodeParameters}; use super::utils::{ generate_credential_with_key, generate_key_package, CredentialWithKeyAndSigner, @@ -86,7 +86,11 @@ fn receive_message( .expect("error creating bob's group from staged join"); let (message, _welcome, _group_info) = bob_group - .self_update(provider, &bob_credential_with_key_and_signer.signer) + .self_update( + provider, + &bob_credential_with_key_and_signer.signer, + LeafNodeParameters::default(), + ) .expect("An unexpected error occurred."); message.into() } diff --git a/openmls/src/key_packages/mod.rs b/openmls/src/key_packages/mod.rs index 17b8621de..5de332488 100644 --- a/openmls/src/key_packages/mod.rs +++ b/openmls/src/key_packages/mod.rs @@ -476,6 +476,7 @@ impl KeyPackageBuilder { } } + #[cfg(test)] pub(crate) fn build_without_storage( mut self, ciphersuite: Ciphersuite, diff --git a/openmls/src/prelude.rs b/openmls/src/prelude.rs index 2957e3bcd..9d683a46c 100644 --- a/openmls/src/prelude.rs +++ b/openmls/src/prelude.rs @@ -42,7 +42,7 @@ pub use crate::binary_tree::LeafNodeIndex; // TreeSync pub use crate::treesync::{ errors::{ApplyUpdatePathError, PublicTreeError}, - node::leaf_node::{Capabilities, LeafNode}, + node::leaf_node::{Capabilities, LeafNode, LeafNodeParameters}, node::parent_node::ParentNode, node::Node, RatchetTreeIn, diff --git a/openmls/src/test_utils/frankenstein/leaf_node.rs b/openmls/src/test_utils/frankenstein/leaf_node.rs index 1c2774618..74b4789aa 100644 --- a/openmls/src/test_utils/frankenstein/leaf_node.rs +++ b/openmls/src/test_utils/frankenstein/leaf_node.rs @@ -6,11 +6,12 @@ use tls_codec::*; use super::{extensions::FrankenExtension, key_package::FrankenLifetime, FrankenCredential}; use crate::{ - binary_tree::array_representation::tree, + binary_tree::{array_representation::tree, LeafNodeIndex}, ciphersuite::{ signable::{Signable, SignedStruct}, signature::Signature, }, + group::GroupId, treesync::{ node::leaf_node::{LeafNodeIn, LeafNodeTbs, TreePosition}, LeafNode, @@ -115,6 +116,25 @@ pub struct FrankenTreePosition { pub leaf_index: u32, } +impl From for FrankenTreePosition { + fn from(tp: TreePosition) -> Self { + let (group_id, leaf_index) = tp.into_parts(); + Self { + group_id: group_id.as_slice().to_owned().into(), + leaf_index: leaf_index.u32(), + } + } +} + +impl From for TreePosition { + fn from(ftp: FrankenTreePosition) -> Self { + Self::new( + GroupId::from_slice(ftp.group_id.as_slice()), + LeafNodeIndex::new(ftp.leaf_index), + ) + } +} + #[derive(Debug, Clone, PartialEq, Eq, TlsSize)] pub struct FrankenLeafNodeTbs { pub payload: FrankenLeafNodePayload, diff --git a/openmls/src/test_utils/test_framework/client.rs b/openmls/src/test_utils/test_framework/client.rs index ebdb3bc47..1d707f845 100644 --- a/openmls/src/test_utils/test_framework/client.rs +++ b/openmls/src/test_utils/test_framework/client.rs @@ -24,7 +24,7 @@ use crate::{ storage::OpenMlsProvider, treesync::{ node::{leaf_node::Capabilities, Node}, - LeafNode, RatchetTree, RatchetTreeIn, + LeafNode, LeafNodeParameters, RatchetTree, RatchetTreeIn, }, versions::ProtocolVersion, }; @@ -209,7 +209,7 @@ impl Client { &self, action_type: ActionType, group_id: &GroupId, - leaf_node: Option, + leaf_node_parameters: LeafNodeParameters, ) -> Result< (MlsMessageOut, Option, Option), ClientError, @@ -228,10 +228,12 @@ impl Client { ) .unwrap(); let (msg, welcome_option, group_info) = match action_type { - ActionType::Commit => group.self_update(&self.provider, &signer)?, + ActionType::Commit => { + group.self_update(&self.provider, &signer, LeafNodeParameters::default())? + } ActionType::Proposal => ( group - .propose_self_update(&self.provider, &signer, leaf_node) + .propose_self_update(&self.provider, &signer, leaf_node_parameters) .map(|(out, _)| out)?, None, None, diff --git a/openmls/src/test_utils/test_framework/mod.rs b/openmls/src/test_utils/test_framework/mod.rs index 383283f52..22bb330e8 100644 --- a/openmls/src/test_utils/test_framework/mod.rs +++ b/openmls/src/test_utils/test_framework/mod.rs @@ -23,6 +23,7 @@ use crate::storage::OpenMlsProvider; use crate::test_utils::OpenMlsRustCrypto; +use crate::treesync::LeafNodeParameters; use crate::{ binary_tree::array_representation::LeafNodeIndex, ciphersuite::{hash_ref::KeyPackageRef, *}, @@ -538,7 +539,7 @@ impl MlsGroupTestSetup { action_type: ActionType, group: &mut Group, client_id: &[u8], - leaf_node: Option, + leaf_node_parameters: LeafNodeParameters, authentication_service: &AS, ) -> Result<(), SetupError> { let clients = self.clients.read().expect("An unexpected error occurred."); @@ -548,7 +549,7 @@ impl MlsGroupTestSetup { .read() .expect("An unexpected error occurred."); let (messages, welcome_option, _) = - client.self_update(action_type, &group.group_id, leaf_node)?; + client.self_update(action_type, &group.group_id, leaf_node_parameters)?; self.distribute_to_members( &client.identity, group, @@ -671,7 +672,7 @@ impl MlsGroupTestSetup { action_type, group, &member_id.1, - None, + LeafNodeParameters::default(), authentication_service, )?; } diff --git a/openmls/src/treesync/diff.rs b/openmls/src/treesync/diff.rs index 56be81519..e72b40b73 100644 --- a/openmls/src/treesync/diff.rs +++ b/openmls/src/treesync/diff.rs @@ -24,6 +24,7 @@ use openmls_traits::crypto::OpenMlsCrypto; use openmls_traits::{signatures::Signer, types::Ciphersuite, OpenMlsProvider}; use serde::{Deserialize, Serialize}; +use super::node::leaf_node::UpdateLeafNodeParams; use super::{ errors::*, node::{ @@ -35,6 +36,7 @@ use super::{ treesync_node::{TreeSyncLeafNode, TreeSyncParentNode}, LeafNode, TreeSync, TreeSyncParentHashError, }; +use crate::group::{create_commit_params::CommitType, GroupId}; use crate::{ binary_tree::{ array_representation::{ @@ -44,7 +46,6 @@ use crate::{ }, ciphersuite::Secret, error::LibraryError, - group::GroupId, messages::PathSecret, schedule::CommitSecret, treesync::RatchetTree, @@ -289,28 +290,50 @@ impl<'a> TreeSyncDiff<'a> { /// derivation, as well as the newly derived [`EncryptionKeyPair`]s. /// /// Returns an error if the target leaf is not in the tree. + #[allow(clippy::too_many_arguments)] pub(crate) fn apply_own_update_path( &mut self, provider: &impl OpenMlsProvider, signer: &impl Signer, ciphersuite: Ciphersuite, + commit_type: &CommitType, group_id: GroupId, leaf_index: LeafNodeIndex, - ) -> Result { - debug_assert!( - self.leaf(leaf_index).is_some(), - "Tree diff is missing own leaf" - ); + leaf_node_params: UpdateLeafNodeParams, + ) -> Result { + // For External Commits, we temporarily add a placeholder leaf node to the tree, because it + // might be required to make the tree grow to the right size. If we + // don't do that, calculating the direct path might fail. It's important + // to not do anything with the value of that leaf until it has been + // replaced. + if let CommitType::External(_) = commit_type { + let leaf_node = LeafNode::new_placeholder(); + self.add_leaf(leaf_node)?; + } - let (path, update_path_nodes, keypairs, commit_secret) = + // We calculate the parent hash so that we can use it for a fresh leaf + let (path, update_path_nodes, parent_keypairs, commit_secret) = self.derive_path(provider, ciphersuite, leaf_index)?; - let parent_hash = self.process_update_path(provider.crypto(), ciphersuite, leaf_index, path)?; - self.leaf_mut(leaf_index) - .ok_or_else(|| LibraryError::custom("Didn't find own leaf in diff."))? - .update_parent_hash(&parent_hash, group_id, leaf_index, signer)?; + // We generate the new leaf with all parameters + let (leaf_node, node_keypair) = LeafNode::new_with_parent_hash( + provider, + ciphersuite, + &parent_hash, + leaf_node_params, + group_id, + leaf_index, + signer, + )?; + + // We insert the fresh leaf into the tree. + self.diff.replace_leaf(leaf_index, leaf_node.into()); + + // Prepend parent keypairs with node keypair + let mut keypairs = vec![node_keypair]; + keypairs.extend(parent_keypairs); Ok((update_path_nodes, keypairs, commit_secret)) } @@ -721,11 +744,6 @@ impl<'a> TreeSyncDiff<'a> { self.diff.leaf(index).node().as_ref() } - /// Return a mutable reference to the leaf with the given index. - pub(crate) fn leaf_mut(&mut self, index: LeafNodeIndex) -> Option<&mut LeafNode> { - self.diff.leaf_mut(index).node_mut().as_mut() - } - /// Compute and set the tree hash of all nodes in the tree. pub(crate) fn compute_tree_hashes( &mut self, diff --git a/openmls/src/treesync/errors.rs b/openmls/src/treesync/errors.rs index 53f53a45e..c0ecf3fcb 100644 --- a/openmls/src/treesync/errors.rs +++ b/openmls/src/treesync/errors.rs @@ -128,7 +128,7 @@ pub(crate) enum DerivePathError { /// TreeSync set path error #[derive(Error, Debug, PartialEq, Clone)] -pub(crate) enum TreeSyncAddLeaf { +pub enum TreeSyncAddLeaf { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index 35418ead1..512a76c89 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -83,7 +83,11 @@ pub use node::encryption_keys::test_utils; pub use node::encryption_keys::EncryptionKey; // Public re-exports -pub use node::{leaf_node::LeafNode, parent_node::ParentNode, Node}; +pub use node::{ + leaf_node::{LeafNode, LeafNodeParameters, LeafNodeParametersBuilder, LeafNodeUpdateError}, + parent_node::ParentNode, + Node, +}; // Tests #[cfg(any(feature = "test-utils", test))] diff --git a/openmls/src/treesync/node/encryption_keys.rs b/openmls/src/treesync/node/encryption_keys.rs index 0aab483a4..1055beeea 100644 --- a/openmls/src/treesync/node/encryption_keys.rs +++ b/openmls/src/treesync/node/encryption_keys.rs @@ -65,6 +65,12 @@ impl EncryptionKey { } } +impl From> for EncryptionKey { + fn from(key: Vec) -> Self { + Self { key: key.into() } + } +} + #[derive( Clone, Serialize, Deserialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, )] diff --git a/openmls/src/treesync/node/leaf_node.rs b/openmls/src/treesync/node/leaf_node.rs index c90a7b69a..f32a4c3f7 100644 --- a/openmls/src/treesync/node/leaf_node.rs +++ b/openmls/src/treesync/node/leaf_node.rs @@ -1,14 +1,12 @@ //! This module contains the [`LeafNode`] struct and its implementation. use openmls_traits::{signatures::Signer, types::Ciphersuite}; use serde::{Deserialize, Serialize}; +use thiserror::Error; use tls_codec::{ Serialize as TlsSerializeTrait, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, VLBytes, }; -#[cfg(test)] -use thiserror::Error; - use super::encryption_keys::{EncryptionKey, EncryptionKeyPair}; use crate::{ binary_tree::array_representation::LeafNodeIndex, @@ -16,14 +14,13 @@ use crate::{ signable::{Signable, SignedStruct, Verifiable, VerifiedStruct}, Signature, SignaturePublicKey, }, - credentials::{Credential, CredentialWithKey}, + credentials::{Credential, CredentialType, CredentialWithKey}, error::LibraryError, extensions::{ExtensionType, Extensions}, group::GroupId, key_packages::{KeyPackage, Lifetime}, prelude::KeyPackageBundle, storage::OpenMlsProvider, - treesync::errors::PublicTreeError, }; use crate::treesync::errors::LeafNodeValidationError; @@ -42,6 +39,102 @@ pub(crate) struct NewLeafNodeParams { pub(crate) tree_info_tbs: TreeInfoTbs, } +/// Set of LeafNode parameters that are used when regenerating a LeafNodes +/// during an update operation. +#[derive(Debug, PartialEq, Clone)] +pub(crate) struct UpdateLeafNodeParams { + pub(crate) credential_with_key: CredentialWithKey, + pub(crate) capabilities: Capabilities, + pub(crate) extensions: Extensions, +} + +impl UpdateLeafNodeParams { + #[cfg(test)] + pub(crate) fn derive(leaf_node: &LeafNode) -> Self { + Self { + credential_with_key: CredentialWithKey { + credential: leaf_node.payload.credential.clone(), + signature_key: leaf_node.payload.signature_key.clone(), + }, + capabilities: leaf_node.payload.capabilities.clone(), + extensions: leaf_node.payload.extensions.clone(), + } + } +} + +/// Parameters for a leaf node that can be chosen by the application. +#[derive(Debug, PartialEq, Clone, Default)] +pub struct LeafNodeParameters { + credential_with_key: Option, + capabilities: Option, + extensions: Option, +} + +impl LeafNodeParameters { + /// Create a new [`LeafNodeParametersBuilder`]. + pub fn builder() -> LeafNodeParametersBuilder { + LeafNodeParametersBuilder::default() + } + + /// Returns the credential with key. + pub fn credential_with_key(&self) -> Option<&CredentialWithKey> { + self.credential_with_key.as_ref() + } + + /// Returns the capabilities. + pub fn capabilities(&self) -> Option<&Capabilities> { + self.capabilities.as_ref() + } + + /// Returns the extensions. + pub fn extensions(&self) -> Option<&Extensions> { + self.extensions.as_ref() + } + + pub(crate) fn is_empty(&self) -> bool { + self.credential_with_key.is_none() + && self.capabilities.is_none() + && self.extensions.is_none() + } +} + +/// Builder for [`LeafNodeParameters`]. +#[derive(Debug, Default)] +pub struct LeafNodeParametersBuilder { + credential_with_key: Option, + capabilities: Option, + extensions: Option, +} + +impl LeafNodeParametersBuilder { + /// Set the credential with key. + pub fn with_credential_with_key(mut self, credential_with_key: CredentialWithKey) -> Self { + self.credential_with_key = Some(credential_with_key); + self + } + + /// Set the capabilities. + pub fn with_capabilities(mut self, capabilities: Capabilities) -> Self { + self.capabilities = Some(capabilities); + self + } + + /// Set the extensions. + pub fn with_extensions(mut self, extensions: Extensions) -> Self { + self.extensions = Some(extensions); + self + } + + /// Build the [`LeafNodeParameters`]. + pub fn build(self) -> LeafNodeParameters { + LeafNodeParameters { + credential_with_key: self.credential_with_key, + capabilities: self.capabilities, + extensions: self.extensions, + } + } +} + /// This struct implements the MLS leaf node. /// /// ```c @@ -114,6 +207,27 @@ impl LeafNode { Ok((leaf_node, encryption_key_pair)) } + /// Creates a new placeholder [`LeafNode`] that is used to build external + /// commits. + /// + /// Note: This is not a valid leaf node and it must be rekeyed and signed + /// before it can be used. + pub(crate) fn new_placeholder() -> Self { + let payload = LeafNodePayload { + encryption_key: EncryptionKey::from(Vec::new()), + signature_key: Vec::new().into(), + credential: Credential::new(CredentialType::Basic, Vec::new()), + capabilities: Capabilities::default(), + leaf_node_source: LeafNodeSource::Update, + extensions: Extensions::default(), + }; + + Self { + payload, + signature: Vec::new().into(), + } + } + /// Create a new leaf node with a given HPKE encryption key pair. /// The key pair must be stored in the key store by the caller. fn new_with_key( @@ -132,67 +246,44 @@ impl LeafNode { leaf_node_source, extensions, tree_info_tbs, - )?; + ); leaf_node_tbs .sign(signer) .map_err(|_| LibraryError::custom("Signing failed")) } - /// Update the parent hash of this [`LeafNode`]. - /// - /// This re-signs the leaf node. - pub(in crate::treesync) fn update_parent_hash( - &mut self, + /// New [`LeafNode`] with a parent hash. + #[allow(clippy::too_many_arguments)] + pub(in crate::treesync) fn new_with_parent_hash( + provider: &impl OpenMlsProvider, + ciphersuite: Ciphersuite, parent_hash: &[u8], + leaf_node_params: UpdateLeafNodeParams, group_id: GroupId, leaf_index: LeafNodeIndex, signer: &impl Signer, - ) -> Result<(), LibraryError> { - self.payload.leaf_node_source = LeafNodeSource::Commit(parent_hash.into()); - let tbs = LeafNodeTbs::from( - self.clone(), // TODO: With a better setup we wouldn't have to clone here. + ) -> Result<(Self, EncryptionKeyPair), LibraryError> { + let encryption_key_pair = EncryptionKeyPair::random(provider, ciphersuite)?; + + let leaf_node_tbs = LeafNodeTbs::new( + encryption_key_pair.public_key().clone(), + leaf_node_params.credential_with_key, + leaf_node_params.capabilities, + LeafNodeSource::Commit(parent_hash.into()), + leaf_node_params.extensions, TreeInfoTbs::Commit(TreePosition { group_id, leaf_index, }), ); - let leaf_node = tbs + + // Sign the leaf node + let leaf_node = leaf_node_tbs .sign(signer) .map_err(|_| LibraryError::custom("Signing failed"))?; - self.payload = leaf_node.payload; - self.signature = leaf_node.signature; - Ok(()) - } - - /// Generate a fresh leaf node with a fresh encryption key but otherwise - /// the same properties as the current leaf node. - /// - /// The newly generated encryption key pair is stored in the key store. - /// - /// This function can be used when generating an update. In most other cases - /// a leaf node should be generated as part of a new [`KeyPackage`]. - #[cfg(test)] - pub(crate) fn updated( - &self, - ciphersuite: Ciphersuite, - tree_info_tbs: TreeInfoTbs, - provider: &Provider, - signer: &impl Signer, - ) -> Result> { - Self::generate_update( - ciphersuite, - CredentialWithKey { - credential: self.payload.credential.clone(), - signature_key: self.payload.signature_key.clone(), - }, - self.payload.capabilities.clone(), - self.payload.extensions.clone(), - tree_info_tbs, - provider, - signer, - ) + Ok((leaf_node, encryption_key_pair)) } /// Generate a fresh leaf node. @@ -234,86 +325,59 @@ impl LeafNode { Ok(leaf_node) } - /// Update the `encryption_key` in this leaf node and re-signs it. + /// Update a leaf node. + /// + /// This function generates a new encryption key pair that is stored in the + /// key store and also returned. /// - /// Optionally, a new leaf node can be provided to update more values such as - /// the credential. - pub(crate) fn update_and_re_sign( + /// This function can be used when generating an update. In most other cases + /// a leaf node should be generated as part of a new [`KeyPackage`]. + pub(crate) fn update( &mut self, - new_encryption_key: impl Into>, - leaf_node: impl Into>, + ciphersuite: Ciphersuite, + provider: &Provider, + signer: &impl Signer, group_id: GroupId, leaf_index: LeafNodeIndex, - signer: &impl Signer, - ) -> Result<(), PublicTreeError> { + leaf_node_parmeters: LeafNodeParameters, + ) -> Result> { let tree_info = TreeInfoTbs::Update(TreePosition::new(group_id, leaf_index)); - // TODO: If we could take out the leaf_node without cloning, this would all be nicer. let mut leaf_node_tbs = LeafNodeTbs::from(self.clone(), tree_info); // Update credential - if let Some(leaf_node) = leaf_node.into() { - leaf_node_tbs.payload.credential = leaf_node.credential().clone(); - leaf_node_tbs.payload.encryption_key = leaf_node.encryption_key().clone(); - leaf_node_tbs.payload.leaf_node_source = LeafNodeSource::Update; - } else if let Some(new_encryption_key) = new_encryption_key.into() { - leaf_node_tbs.payload.leaf_node_source = LeafNodeSource::Update; - - // If there's no new leaf, the encryption key must be provided - // explicitly. - leaf_node_tbs.payload.encryption_key = new_encryption_key; - } else { - debug_assert!(false, "update_and_re_sign needs to be called with a new leaf node or a new encryption key. Neither was the case."); - return Err(LibraryError::custom( - "update_and_re_sign needs to be called with a new leaf node or a new encryption key. Neither was the case.").into()); + if let Some(credential_with_key) = leaf_node_parmeters.credential_with_key { + leaf_node_tbs.payload.credential = credential_with_key.credential; + leaf_node_tbs.payload.signature_key = credential_with_key.signature_key; } - // Set the new signed leaf node with the new encryption key - let leaf_node = leaf_node_tbs.sign(signer)?; - self.payload = leaf_node.payload; - self.signature = leaf_node.signature; - - Ok(()) - } + // Update extensions + if let Some(extensions) = leaf_node_parmeters.extensions { + leaf_node_tbs.payload.extensions = extensions; + } - /// Replace the encryption key in this leaf with a random one. - /// - /// This signs the new leaf node as well. - pub(crate) fn rekey( - &mut self, - group_id: &GroupId, - leaf_index: LeafNodeIndex, - ciphersuite: Ciphersuite, - provider: &impl OpenMlsProvider, - signer: &impl Signer, - ) -> Result { - if !self - .payload - .capabilities - .ciphersuites - .contains(&ciphersuite.into()) - { - debug_assert!( - false, - "Ciphersuite or protocol version is not supported by this leaf node.\ - \ncapabilities: {:?}\nciphersuite: {:?}", - self.payload.capabilities, ciphersuite - ); - return Err(LibraryError::custom( - "Ciphersuite or protocol version is not supported by this leaf node.", - ) - .into()); + // Update capabilities + if let Some(capabilities) = leaf_node_parmeters.capabilities { + leaf_node_tbs.payload.capabilities = capabilities; } - let key_pair = EncryptionKeyPair::random(provider, ciphersuite)?; - self.update_and_re_sign( - key_pair.public_key().clone(), - None, - group_id.clone(), - leaf_index, - signer, - )?; + // Create a new encryption key pair + let encryption_key_pair = EncryptionKeyPair::random(provider, ciphersuite)?; + leaf_node_tbs.payload.encryption_key = encryption_key_pair.public_key().clone(); - Ok(key_pair) + // Store the encryption key pair in the key store. + encryption_key_pair + .write(provider.storage()) + .map_err(LeafNodeUpdateError::Storage)?; + + // Set the leaf node source to update + leaf_node_tbs.payload.leaf_node_source = LeafNodeSource::Update; + + // Sign the leaf node + let leaf_node = leaf_node_tbs.sign(signer)?; + self.payload = leaf_node.payload; + self.signature = leaf_node.signature; + + Ok(encryption_key_pair) } /// Returns the `encryption_key`. @@ -355,10 +419,15 @@ impl LeafNode { } /// Return a reference to [`Capabilities`]. - pub(crate) fn capabilities(&self) -> &Capabilities { + pub fn capabilities(&self) -> &Capabilities { &self.payload.capabilities } + /// Return a reference to the leaf node source. + pub fn leaf_node_source(&self) -> &LeafNodeSource { + &self.payload.leaf_node_source + } + /// Return a reference to the leaf node extensions. pub fn extensions(&self) -> &Extensions { &self.payload.extensions @@ -508,7 +577,7 @@ impl LeafNodeTbs { leaf_node_source: LeafNodeSource, extensions: Extensions, tree_info_tbs: TreeInfoTbs, - ) -> Result { + ) -> Self { let payload = LeafNodePayload { encryption_key, signature_key: credential_with_key.signature_key, @@ -517,11 +586,11 @@ impl LeafNodeTbs { leaf_node_source, extensions, }; - let tbs = LeafNodeTbs { + + LeafNodeTbs { payload, tree_info_tbs, - }; - Ok(tbs) + } } } @@ -566,6 +635,11 @@ impl TreePosition { leaf_index, } } + + #[cfg(feature = "test-utils")] + pub(crate) fn into_parts(self) -> (GroupId, LeafNodeIndex) { + (self.group_id, self.leaf_index) + } } const LEAF_NODE_SIGNATURE_LABEL: &str = "LeafNodeTBS"; @@ -856,3 +930,19 @@ pub enum LeafNodeGenerationError { #[error("Error storing leaf private key.")] StorageError(StorageError), } + +/// Leaf Node Update Error +#[derive(Error, Debug, PartialEq, Clone)] +pub enum LeafNodeUpdateError { + /// See [`LibraryError`] for more details. + #[error(transparent)] + LibraryError(#[from] LibraryError), + + /// Error storing leaf private key in storage. + #[error("Error storing leaf private key.")] + Storage(StorageError), + + /// Signature error. + #[error(transparent)] + Signature(#[from] crate::ciphersuite::signable::SignatureError), +} diff --git a/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs b/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs index f33aaf4b4..1f6f6c50c 100644 --- a/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs +++ b/openmls/src/treesync/tests_and_kats/kats/kat_treekem.rs @@ -9,13 +9,13 @@ use tls_codec::{Deserialize as TlsDeserializeTrait, Serialize as TlsSerializeTra use crate::{ binary_tree::{array_representation::ParentNodeIndex, LeafNodeIndex}, extensions::{Extensions, RatchetTreeExtension}, - group::{GroupContext, GroupEpoch, GroupId}, + group::{CommitType, GroupContext, GroupEpoch, GroupId}, messages::PathSecret, prelude_test::Secret, schedule::CommitSecret, test_utils::{hex_to_bytes, OpenMlsRustCrypto}, treesync::{ - node::encryption_keys::EncryptionKeyPair, + node::{encryption_keys::EncryptionKeyPair, leaf_node::UpdateLeafNodeParams}, treekem::{DecryptPathParams, UpdatePath, UpdatePathIn}, TreeSync, }, @@ -243,14 +243,20 @@ pub fn run_test_vector(test: TreeKemTest, provider: &impl OpenMlsProvider) { ) }; + let leaf_index = LeafNodeIndex::new(path_test.sender); + let leaf_node = diff_after_kat.leaf(leaf_index).unwrap(); + let leaf_node_params = UpdateLeafNodeParams::derive(leaf_node); + // TODO(#1279): Update own leaf. let (vec_plain_update_path_nodes, _, commit_secret) = diff_after_kat .apply_own_update_path( provider, &signer, ciphersuite, + &CommitType::Member, group_context.group_id().clone(), LeafNodeIndex::new(path_test.sender), + leaf_node_params, ) .unwrap(); diff --git a/openmls/src/treesync/treesync_node.rs b/openmls/src/treesync/treesync_node.rs index e0e65b5e1..bc3faf280 100644 --- a/openmls/src/treesync/treesync_node.rs +++ b/openmls/src/treesync/treesync_node.rs @@ -76,11 +76,6 @@ impl TreeSyncLeafNode { &self.node } - /// Return a mutable reference to the contained `Option`. - pub(in crate::treesync) fn node_mut(&mut self) -> &mut Option { - &mut self.node - } - /// Compute the tree hash for this node, thus populating the `tree_hash` /// field. pub(in crate::treesync) fn compute_tree_hash( diff --git a/openmls/tests/book_code.rs b/openmls/tests/book_code.rs index 65ede1c72..679d9401e 100644 --- a/openmls/tests/book_code.rs +++ b/openmls/tests/book_code.rs @@ -6,6 +6,7 @@ use openmls::{ use openmls_basic_credential::SignatureKeyPair; use openmls_test::openmls_test; use openmls_traits::{signatures::Signer, types::SignatureScheme}; +use treesync::LeafNodeParameters; #[test] fn create_provider_rust_crypto() { @@ -292,9 +293,11 @@ fn book_operations() { let (mut dave_group, _out, _group_info) = MlsGroup::join_by_external_commit( provider, &dave_signature_keys, - None, + None, // No ratchtet tree extension verifiable_group_info, &mls_group_config, + None, // No special capabilities + None, // No special extensions &[], dave_credential, ) @@ -357,7 +360,7 @@ fn book_operations() { // === Bob updates and commits === // ANCHOR: self_update let (mls_message_out, welcome_option, _group_info) = bob_group - .self_update(provider, &bob_signature_keys) + .self_update(provider, &bob_signature_keys, LeafNodeParameters::default()) .expect("Could not update own key package."); // ANCHOR_END: self_update @@ -407,7 +410,7 @@ fn book_operations() { .propose_self_update( provider, &alice_signature_keys, - None, // We don't provide a leaf node, it will be created on the fly instead + LeafNodeParameters::default(), ) .expect("Could not create update proposal."); // ANCHOR_END: propose_self_update @@ -602,7 +605,11 @@ fn book_operations() { // === Charlie updates and commits === let (queued_message, welcome_option, _group_info) = charlie_group - .self_update(provider, &charlie_signature_keys) + .self_update( + provider, + &charlie_signature_keys, + LeafNodeParameters::default(), + ) .unwrap(); let alice_processed_message = alice_group diff --git a/openmls/tests/test_external_commit.rs b/openmls/tests/test_external_commit.rs index a358c168d..dd85fc5bc 100644 --- a/openmls/tests/test_external_commit.rs +++ b/openmls/tests/test_external_commit.rs @@ -2,6 +2,7 @@ use openmls::{ credentials::test_utils::new_credential, messages::group_info::VerifiableGroupInfo, prelude::{tls_codec::*, *}, + treesync::LeafNodeParameters, }; use openmls_basic_credential::SignatureKeyPair; use openmls_test::openmls_test; @@ -83,6 +84,8 @@ fn test_external_commit() { None, verifiable_group_info, &MlsGroupJoinConfig::default(), + None, + None, b"", bob_credential, ) @@ -100,6 +103,8 @@ fn test_external_commit() { None, verifiable_group_info_broken, &MlsGroupJoinConfig::default(), + None, + None, b"", bob_credential, ) @@ -120,7 +125,9 @@ fn test_group_info() { let (mut alice_group, _, alice_signer) = create_alice_group(ciphersuite, provider, true); // Self update Alice's to get a group info from a commit - let (.., group_info) = alice_group.self_update(provider, &alice_signer).unwrap(); + let (.., group_info) = alice_group + .self_update(provider, &alice_signer, LeafNodeParameters::default()) + .unwrap(); alice_group.merge_pending_commit(provider).unwrap(); // Bob wants to join @@ -138,6 +145,8 @@ fn test_group_info() { None, verifiable_group_info, &MlsGroupJoinConfig::default(), + None, + None, b"", bob_credential, ) @@ -187,6 +196,8 @@ fn test_group_info() { None, verifiable_group_info, &MlsGroupJoinConfig::default(), + None, + None, b"", bob_credential, ) @@ -203,7 +214,9 @@ fn test_not_present_group_info( let (mut alice_group, _, alice_signer) = create_alice_group(ciphersuite, provider, false); // Self update Alice's to get a group info from a commit - let (.., group_info) = alice_group.self_update(provider, &alice_signer).unwrap(); + let (.., group_info) = alice_group + .self_update(provider, &alice_signer, LeafNodeParameters::default()) + .unwrap(); alice_group.merge_pending_commit(provider).unwrap(); assert!(group_info.is_none()); diff --git a/openmls/tests/test_interop_scenarios.rs b/openmls/tests/test_interop_scenarios.rs index baf3e0ab1..09d8a3864 100644 --- a/openmls/tests/test_interop_scenarios.rs +++ b/openmls/tests/test_interop_scenarios.rs @@ -3,6 +3,7 @@ use openmls::{ test_utils::test_framework::{ noop_authentication_service, ActionType, CodecUse, MlsGroupTestSetup, }, + treesync::LeafNodeParameters, }; use openmls_test::openmls_test; @@ -218,7 +219,7 @@ fn update() { ActionType::Commit, group, &alice_id, - None, + LeafNodeParameters::default(), &noop_authentication_service, ) .expect("Error self-updating."); @@ -323,7 +324,7 @@ fn large_group_lifecycle() { ActionType::Commit, group, member_id, - None, + LeafNodeParameters::default(), &noop_authentication_service, ) .expect("Error while updating group.") diff --git a/openmls/tests/test_mls_group.rs b/openmls/tests/test_mls_group.rs index 3f83faff7..24a5fcd74 100644 --- a/openmls/tests/test_mls_group.rs +++ b/openmls/tests/test_mls_group.rs @@ -1,6 +1,7 @@ use openmls::{ prelude::{test_utils::new_credential, *}, storage::OpenMlsProvider, + treesync::LeafNodeParameters, }; use openmls_traits::OpenMlsProvider as _; @@ -175,8 +176,9 @@ fn mls_group_operations() { } // === Bob updates and commits === - let (queued_message, welcome_option, _group_info) = - bob_group.self_update(provider, &bob_signer).unwrap(); + let (queued_message, welcome_option, _group_info) = bob_group + .self_update(provider, &bob_signer, LeafNodeParameters::default()) + .unwrap(); let alice_processed_message = alice_group .process_message( @@ -221,7 +223,7 @@ fn mls_group_operations() { // === Alice updates and commits === let (queued_message, _) = alice_group - .propose_self_update(provider, &alice_signer, None) + .propose_self_update(provider, &alice_signer, LeafNodeParameters::default()) .unwrap(); let bob_processed_message = bob_group @@ -404,7 +406,7 @@ fn mls_group_operations() { // === Charlie updates and commits === let (queued_message, welcome_option, _group_info) = charlie_group - .self_update(provider, &charlie_signer) + .self_update(provider, &charlie_signer, LeafNodeParameters::default()) .unwrap(); let alice_processed_message = alice_group From b947071aca9476aec1817c9a08d3c217af3b98b7 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Fri, 12 Jul 2024 15:02:05 +0200 Subject: [PATCH 16/43] more update proposal tests --- openmls/src/group/mls_group/test_mls_group.rs | 217 ++++++++++++++++++ openmls/src/group/mls_group/updates.rs | 2 +- 2 files changed, 218 insertions(+), 1 deletion(-) diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index 4c15cd10c..7be8be2d6 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -1679,6 +1679,223 @@ fn update_group_context_with_unknown_extension( &mut self, provider: &Provider, From c44386fa4f259694476275b9e952bbe3bd785485 Mon Sep 17 00:00:00 2001 From: Jan Winkelmann <146678+keks@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:02:51 +0200 Subject: [PATCH 17/43] Test all Validation Checks for GroupContextExtensions (#1599) Co-authored-by: Jan Winkelmann (keks) Co-authored-by: raphaelrobert --- openmls/src/group/mls_group/test_mls_group.rs | 222 +--- .../group/tests/group_context_extensions.rs | 1139 +++++++++++++++++ openmls/src/group/tests/mod.rs | 2 + openmls/src/prelude.rs | 2 +- .../src/test_utils/frankenstein/framing.rs | 4 +- .../treesync/node/leaf_node/capabilities.rs | 55 + openmls/tests/test_mls_group.rs | 214 ++-- 7 files changed, 1325 insertions(+), 313 deletions(-) create mode 100644 openmls/src/group/tests/group_context_extensions.rs diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/test_mls_group.rs index 7be8be2d6..4ca182274 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/test_mls_group.rs @@ -10,16 +10,12 @@ use crate::{ group::{errors::*, *}, key_packages::*, messages::proposals::*, - prelude::Capabilities, - test_utils::{ - frankenstein::{self, FrankenMlsMessage}, - test_framework::{ - errors::ClientError, noop_authentication_service, ActionType::Commit, CodecUse, - MlsGroupTestSetup, - }, + test_utils::test_framework::{ + errors::ClientError, noop_authentication_service, ActionType::Commit, CodecUse, + MlsGroupTestSetup, }, tree::sender_ratchet::SenderRatchetConfiguration, - treesync::LeafNodeParameters, + treesync::{node::leaf_node::Capabilities, LeafNodeParameters}, }; #[openmls_test] @@ -1144,216 +1140,6 @@ fn remove_prosposal_by_ref( } } -// Test that the builder pattern accurately configures the new group. -#[openmls_test] -fn group_context_extensions_proposal() { - let alice_provider = &mut Provider::default(); - let bob_provider = &mut Provider::default(); - let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, alice_provider); - let (bob_credential_with_key, _bob_kpb, bob_signer, _bob_pk) = - setup_client("bob", ciphersuite, bob_provider); - - // === Alice creates a group === - let mut alice_group = MlsGroup::builder() - .ciphersuite(ciphersuite) - .with_wire_format_policy(WireFormatPolicy::new( - OutgoingWireFormatPolicy::AlwaysPlaintext, - IncomingWireFormatPolicy::Mixed, - )) - .build(alice_provider, &alice_signer, alice_credential_with_key) - .expect("error creating group using builder"); - - // === Alice adds Bob === - let bob_key_package = KeyPackage::builder() - .build( - ciphersuite, - bob_provider, - &bob_signer, - bob_credential_with_key, - ) - .expect("error building key package"); - - let (_, welcome, _) = alice_group - .add_members( - alice_provider, - &alice_signer, - &[bob_key_package.key_package().clone()], - ) - .unwrap(); - alice_group.merge_pending_commit(alice_provider).unwrap(); - - let welcome: MlsMessageIn = welcome.into(); - let welcome = welcome - .into_welcome() - .expect("expected message to be a welcome"); - - let mut bob_group = StagedWelcome::new_from_welcome( - bob_provider, - alice_group.configuration(), - welcome, - Some(alice_group.export_ratchet_tree().into()), - ) - .expect("Error creating staged join from Welcome") - .into_group(bob_provider) - .expect("Error creating group from staged join"); - - // No required capabilities, so no specifically required extensions. - assert!(alice_group - .group() - .context() - .extensions() - .required_capabilities() - .is_none()); - - let new_extensions = Extensions::single(Extension::RequiredCapabilities( - RequiredCapabilitiesExtension::new(&[ExtensionType::RequiredCapabilities], &[], &[]), - )); - - let new_extensions_2 = Extensions::single(Extension::RequiredCapabilities( - RequiredCapabilitiesExtension::new(&[ExtensionType::RatchetTree], &[], &[]), - )); - - let (proposal, _) = alice_group - .propose_group_context_extensions(alice_provider, new_extensions.clone(), &alice_signer) - .expect("failed to build group context extensions proposal"); - - let proc_msg = bob_group - .process_message(bob_provider, proposal.into_protocol_message().unwrap()) - .unwrap(); - match proc_msg.into_content() { - ProcessedMessageContent::ProposalMessage(proposal) => bob_group - .store_pending_proposal(bob_provider.storage(), *proposal) - .unwrap(), - _ => unreachable!(), - }; - - assert_eq!(alice_group.pending_proposals().count(), 1); - - let (commit, _, _) = alice_group - .commit_to_pending_proposals(alice_provider, &alice_signer) - .expect("failed to commit to pending proposals"); - - // we'll change the commit we feed to bob to include two GCE proposals - let mut franken_commit = FrankenMlsMessage::tls_deserialize( - &mut commit.tls_serialize_detached().unwrap().as_slice(), - ) - .unwrap(); - - // Craft a commit that has two GroupContextExtension proposals. This is forbidden by the RFC. - // Change the commit before alice commits, so alice's state is still in the old epoch and we can - // use her state to forge the macs and signatures - match &mut franken_commit.body { - frankenstein::FrankenMlsMessageBody::PublicMessage(msg) => { - match &mut msg.content.body { - frankenstein::FrankenFramedContentBody::Commit(commit) => { - let second_gces = frankenstein::FrankenProposalOrRef::Proposal( - frankenstein::FrankenProposal::GroupContextExtensions(vec![ - frankenstein::FrankenExtension::LastResort, - ]), - ); - - commit.proposals.push(second_gces); - } - _ => unreachable!(), - } - - let group_context = alice_group.export_group_context().clone(); - - let bob_group_context = bob_group.export_group_context(); - assert_eq!( - bob_group_context.confirmed_transcript_hash(), - group_context.confirmed_transcript_hash() - ); - - let secrets = alice_group.group.message_secrets(); - let membership_key = secrets.membership_key().as_slice(); - - *msg = frankenstein::FrankenPublicMessage::auth( - alice_provider, - group_context.ciphersuite(), - &alice_signer, - msg.content.clone(), - Some(&group_context.into()), - Some(membership_key), - // this is a dummy confirmation_tag: - Some(vec![0u8; 32].into()), - ); - } - _ => unreachable!(), - } - - // alice merges the unmodified commit - alice_group - .merge_pending_commit(alice_provider) - .expect("error merging pending commit"); - - let fake_commit = MlsMessageIn::tls_deserialize( - &mut franken_commit.tls_serialize_detached().unwrap().as_slice(), - ) - .unwrap(); - - let fake_commit_protocol_msg = fake_commit.into_protocol_message().unwrap(); - - let err = { - let validation_skip_handle = crate::skip_validation::checks::confirmation_tag::handle(); - validation_skip_handle.with_disabled(|| { - bob_group.process_message(bob_provider, fake_commit_protocol_msg.clone()) - }) - } - .expect_err("expected an error"); - - assert!(matches!( - err, - ProcessMessageError::InvalidCommit( - StageCommitError::GroupContextExtensionsProposalValidationError( - GroupContextExtensionsProposalValidationError::TooManyGCEProposals - ) - ) - )); - - let required_capabilities = alice_group - .group() - .context() - .extensions() - .required_capabilities() - .expect("couldn't get required_capabilities"); - - // has required_capabilities as required capability - assert!(required_capabilities.extension_types() == [ExtensionType::RequiredCapabilities]); - - // === committing to two group context extensions should fail - - alice_group - .propose_group_context_extensions(alice_provider, new_extensions, &alice_signer) - .expect("failed to build group context extensions proposal"); - - // the proposals need to be different or they will be deduplicated - alice_group - .propose_group_context_extensions(alice_provider, new_extensions_2, &alice_signer) - .expect("failed to build group context extensions proposal"); - - assert_eq!(alice_group.pending_proposals().count(), 2); - - alice_group - .commit_to_pending_proposals(alice_provider, &alice_signer) - .expect_err( - "expected error when committing to multiple group context extensions proposals", - ); - - // === can't update required required_capabilities to extensions that existing group members - // are not capable of - - // contains unsupported extension - let new_extensions = Extensions::single(Extension::RequiredCapabilities( - RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf042)], &[], &[]), - )); - - alice_group - .propose_group_context_extensions(alice_provider, new_extensions, &alice_signer) - .expect_err("expected an error building GCE proposal with bad required_capabilities"); -} - // Test that the builder pattern accurately configures the new group. #[openmls_test] fn builder_pattern() { diff --git a/openmls/src/group/tests/group_context_extensions.rs b/openmls/src/group/tests/group_context_extensions.rs new file mode 100644 index 000000000..5a9615d54 --- /dev/null +++ b/openmls/src/group/tests/group_context_extensions.rs @@ -0,0 +1,1139 @@ +use openmls_basic_credential::SignatureKeyPair; +use openmls_test::openmls_test; +use openmls_traits::types::Ciphersuite; +use openmls_traits::OpenMlsProvider as _; +use tls_codec::{Deserialize as _, Serialize as _}; + +use crate::{ + ciphersuite::hash_ref::ProposalRef, + credentials::CredentialWithKey, + framing::*, + group::{core_group::test_core_group::setup_client, *}, + key_packages::{errors::KeyPackageVerifyError, *}, + messages::group_info::GroupInfo, + test_utils::frankenstein::{self, FrankenMlsMessage}, + treesync::{node::leaf_node::Capabilities, LeafNodeParameters}, +}; + +/// The state of a group member: A PartyState and the corresponding MlsGroup. +struct MemberState { + party: PartyState, + group: MlsGroup, +} + +/// The state of a party that is not part of any groups. +#[allow(dead_code)] +struct PartyState { + provider: Provider, + credential_with_key: CredentialWithKey, + key_package_bundle: KeyPackageBundle, + signer: SignatureKeyPair, + sig_pk: OpenMlsSignaturePublicKey, + name: &'static str, +} + +impl PartyState { + /// Generate the PartyState for a new identity. + fn generate(name: &'static str, ciphersuite: Ciphersuite) -> Self { + let provider = Provider::default(); + let (credential_with_key, key_package_bundle, signer, sig_pk) = + setup_client(name, ciphersuite, &provider); + + PartyState { + provider, + name, + credential_with_key, + key_package_bundle, + signer, + sig_pk, + } + } + + /// Generate a new [`KeyPackage`] for the party. + fn key_package KeyPackageBuilder>( + &self, + ciphersuite: Ciphersuite, + f: F, + ) -> KeyPackageBundle { + f(KeyPackage::builder()) + .build( + ciphersuite, + &self.provider, + &self.signer, + self.credential_with_key.clone(), + ) + .unwrap_or_else(|err| panic!("failed to build key package at {}: {err}", self.name)) + } +} + +struct TestState { + alice: MemberState, + bob: MemberState, +} + +/// Sets up a group with two parties Alice and Bob, where Alice has capabilities for unknown +/// extensions 0xf001 and 0xf002, and Bob has capabilities for extension 0xf001, 0xf002 and +/// 0xf003. +fn setup( + ciphersuite: Ciphersuite, +) -> TestState { + let alice_party = PartyState::generate("alice", ciphersuite); + let bob_party = PartyState::generate("bob", ciphersuite); + + // === Alice creates a group === + let alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(WireFormatPolicy::new( + OutgoingWireFormatPolicy::AlwaysPlaintext, + IncomingWireFormatPolicy::Mixed, + )) + .with_capabilities( + Capabilities::builder() + .extensions(vec![ + ExtensionType::Unknown(0xf001), + ExtensionType::Unknown(0xf002), + ]) + .build(), + ) + .build( + &alice_party.provider, + &alice_party.signer, + alice_party.credential_with_key.clone(), + ) + .expect("error creating group using builder"); + + let mut alice = MemberState { + party: alice_party, + group: alice_group, + }; + + // === Alice adds Bob === + let bob_key_package = bob_party.key_package(ciphersuite, |builder| { + builder.leaf_node_capabilities( + Capabilities::builder() + .extensions(vec![ + ExtensionType::Unknown(0xf001), + ExtensionType::Unknown(0xf002), + ExtensionType::Unknown(0xf003), + ]) + .build(), + ) + }); + + alice.propose_add_member(bob_key_package.key_package()); + let (_, Some(welcome), _) = alice.commit_and_merge_pending() else { + panic!("expected receiving a welcome") + }; + + let welcome: MlsMessageIn = welcome.into(); + let welcome = welcome + .into_welcome() + .expect("expected message to be a welcome"); + + let bob_group = StagedWelcome::new_from_welcome( + &bob_party.provider, + alice.group.configuration(), + welcome, + Some(alice.group.export_ratchet_tree().into()), + ) + .expect("Error creating staged join from Welcome") + .into_group(&bob_party.provider) + .expect("Error creating group from staged join"); + + TestState { + alice, + bob: MemberState { + party: bob_party, + group: bob_group, + }, + } +} + +impl MemberState { + /// Thin wrapper around [`MlsGroup::propose_group_context_extensions`]. + fn propose_group_context_extensions( + &mut self, + extensions: Extensions, + ) -> (MlsMessageOut, ProposalRef) { + self.group + .propose_group_context_extensions(&self.party.provider, extensions, &self.party.signer) + .unwrap_or_else(|err| panic!("couldn't propose GCE at {}: {err}", self.party.name)) + } + + /// Thin wrapper around [`MlsGroup::update_group_context_extensions`]. + fn update_group_context_extensions( + &mut self, + extensions: Extensions, + ) -> (MlsMessageOut, Option, Option) { + self.group + .update_group_context_extensions(&self.party.provider, extensions, &self.party.signer) + .unwrap_or_else(|err| panic!("couldn't propose GCE at {}: {err}", self.party.name)) + } + + /// Thin wrapper around [`MlsGroup::propose_add_member`]. + fn propose_add_member(&mut self, key_package: &KeyPackage) -> (MlsMessageOut, ProposalRef) { + self.group + .propose_add_member(&self.party.provider, &self.party.signer, key_package) + .unwrap_or_else(|err| panic!("failed to propose member at {}: {err}", self.party.name)) + } + + /// Wrapper around [`MlsGroup::process_message`], asserting it's a commit and [`MlsGroup::merge_staged_commit`]. + fn process_and_merge_commit(&mut self, msg: MlsMessageIn) { + let msg = msg.into_protocol_message().unwrap(); + + let processed_msg = self + .group + .process_message(&self.party.provider, msg) + .unwrap_or_else(|err| panic!("error processing message at {}: {err}", self.party.name)); + + match processed_msg.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => self + .group + .merge_staged_commit(&self.party.provider, *staged_commit) + .unwrap_or_else(|err| { + panic!("error merging staged commit at {}: {err}", self.party.name) + }), + + other => { + panic!( + "expected a commit message at {}, got {:?}", + self.party.name, other + ) + } + } + } + + /// Wrapper around [`MlsGroup::process_message`], asserting it's a proposal and [`MlsGroup::store_pending_proposal`]. + fn process_and_store_proposal(&mut self, msg: MlsMessageIn) -> ProposalRef { + let msg = msg.into_protocol_message().unwrap(); + + let processed_msg = self + .group + .process_message(&self.party.provider, msg) + .unwrap_or_else(|err| panic!("error processing message at {}: {err}", self.party.name)); + + match processed_msg.into_content() { + ProcessedMessageContent::ProposalMessage(proposal) => { + let reference = proposal.proposal_reference(); + + self.group + .store_pending_proposal(self.party.provider.storage(), *proposal) + .unwrap_or_else(|err| { + panic!("error storing proposal at {}: {err}", self.party.name) + }); + + reference + } + other => { + panic!( + "expected a proposal message at {}, got {:?}", + self.party.name, other + ) + } + } + } + + /// This wrapper that expects [`MlsGroup::process_message`] to return an error. + fn fail_processing( + &mut self, + msg: MlsMessageIn, + ) -> ProcessMessageError { + let msg = msg.into_protocol_message().unwrap(); + let err_msg = format!( + "expected an error when processing message at {}", + self.party.name + ); + + self.group + .process_message(&self.party.provider, msg) + .expect_err(&err_msg) + } + + /// This wrapper around [`MlsGroup::commit_to_pending_proposals`] + fn commit_to_pending_proposals( + &mut self, + ) -> (MlsMessageOut, Option, Option) { + self.group + .commit_to_pending_proposals(&self.party.provider, &self.party.signer) + .unwrap_or_else(|err| { + panic!( + "{} couldn't commit pending proposal: {err}", + self.party.name + ) + }) + } + + /// This wrapper around [`MlsGroup::merge_pending_commit`] + fn merge_pending_commit(&mut self) { + self.group + .merge_pending_commit(&self.party.provider) + .unwrap_or_else(|err| panic!("{} couldn't merge commit: {err}", self.party.name)); + } + + /// Wrapper around [`MlsGroup::commit_to_pending_proposals`] and [`MlsGroup::merge_pending_commit`]. + fn commit_and_merge_pending( + &mut self, + ) -> (MlsMessageOut, Option, Option) { + let commit_out = self.commit_to_pending_proposals(); + self.merge_pending_commit(); + commit_out + } +} + +/// Test that the happy case of group context extensions works +/// 1. set up group +/// 2. alice sets gce, commits +#[openmls_test] +fn happy_case() { + let TestState { mut alice, mut bob } = setup::(ciphersuite); + + // make extension with type 0xf001 a required capability + let (commit, _, _) = + alice.update_group_context_extensions(Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf001)], &[], &[]), + ))); + + alice.merge_pending_commit(); + bob.process_and_merge_commit(commit.into()); + + // make extensions with type 0xf001 0xf002 a required capability, too; + // this time with a separate proposal + let (proposal, _) = bob.propose_group_context_extensions(Extensions::single( + Extension::RequiredCapabilities(RequiredCapabilitiesExtension::new( + &[ + ExtensionType::Unknown(0xf001), + ExtensionType::Unknown(0xf002), + ], + &[], + &[], + )), + )); + + alice.process_and_store_proposal(proposal.into()); + + let (commit, _, _) = alice.commit_and_merge_pending(); + bob.process_and_merge_commit(commit.into()); +} + +#[openmls_test] +fn self_update_happy_case() { + let TestState { mut alice, mut bob } = setup::(ciphersuite); + + let (update_prop, _) = bob + .group + .propose_self_update( + &bob.party.provider, + &bob.party.signer, + LeafNodeParameters::builder().build(), + ) + .unwrap(); + alice.process_and_store_proposal(update_prop.into()); + let (commit, _, _) = alice.commit_and_merge_pending(); + bob.process_and_merge_commit(commit.into()) +} + +/// This test does the same as self_update_happy_case, but does not use MemberState, so we can +/// can exactly see which calls to OpenMLS are done +#[openmls_test] +fn self_update_happy_case_simple() { + let alice_party = PartyState::::generate("alice", ciphersuite); + let bob_party = PartyState::::generate("bob", ciphersuite); + + // === Alice creates a group === + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(WireFormatPolicy::new( + OutgoingWireFormatPolicy::AlwaysPlaintext, + IncomingWireFormatPolicy::Mixed, + )) + .build( + &alice_party.provider, + &alice_party.signer, + alice_party.credential_with_key.clone(), + ) + .expect("error creating group using builder"); + + // === Alice adds Bob === + let bob_key_package = bob_party.key_package(ciphersuite, |builder| builder); + + alice_group + .propose_add_member( + &alice_party.provider, + &alice_party.signer, + bob_key_package.key_package(), + ) + .unwrap(); + + let (_, Some(welcome), _) = alice_group + .commit_to_pending_proposals(&alice_party.provider, &alice_party.signer) + .unwrap() + else { + panic!("expected receiving a welcome") + }; + + alice_group + .merge_pending_commit(&alice_party.provider) + .unwrap(); + + let welcome: MlsMessageIn = welcome.into(); + let welcome = welcome + .into_welcome() + .expect("expected message to be a welcome"); + + let mut bob_group = StagedWelcome::new_from_welcome( + &bob_party.provider, + alice_group.configuration(), + welcome, + Some(alice_group.export_ratchet_tree().into()), + ) + .expect("Error creating staged join from Welcome") + .into_group(&bob_party.provider) + .expect("Error creating group from staged join"); + + let (update_proposal_msg, _) = bob_group + .propose_self_update( + &bob_party.provider, + &bob_party.signer, + LeafNodeParameters::builder().build(), + ) + .unwrap(); + + let ProcessedMessageContent::ProposalMessage(update_proposal) = alice_group + .process_message( + &alice_party.provider, + update_proposal_msg.clone().into_protocol_message().unwrap(), + ) + .unwrap() + .into_content() + else { + panic!("expected a proposal, got {update_proposal_msg:?}"); + }; + alice_group + .store_pending_proposal(alice_party.provider.storage(), *update_proposal) + .unwrap(); + + let (commit_msg, _, _) = alice_group + .commit_to_pending_proposals(&alice_party.provider, &alice_party.signer) + .unwrap(); + + bob_group + .process_message( + &bob_party.provider, + commit_msg.into_protocol_message().unwrap(), + ) + .unwrap(); + + bob_group.merge_pending_commit(&bob_party.provider).unwrap() +} + +/// This tests makes sure that validation check 103 is performed: +/// +/// Verify that the LeafNode is compatible with the group's parameters. +/// If the GroupContext has a required_capabilities extension, then the +/// required extensions, proposals, and credential types MUST be listed +/// in the LeafNode's capabilities field. +/// +/// So far, we only test whether the check is done for extension types. +#[openmls_test] +fn fail_insufficient_extensiontype_capabilities_add_valno103() { + let TestState { mut alice, mut bob } = setup::(ciphersuite); + + let (gce_req_cap_commit, _, _) = + alice.update_group_context_extensions(Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf002)], &[], &[]), + ))); + + alice.merge_pending_commit(); + bob.process_and_merge_commit(gce_req_cap_commit.clone().into()); + + // extract values we need later + let frankenstein::FrankenMlsMessage { + version, + body: + frankenstein::FrankenMlsMessageBody::PublicMessage(frankenstein::FrankenPublicMessage { + content: + frankenstein::FrankenFramedContent { + group_id, + epoch: gce_commit_epoch, + sender, + authenticated_data, + .. + }, + .. + }), + } = frankenstein::FrankenMlsMessage::from(gce_req_cap_commit) + else { + unreachable!() + }; + + let charlie = PartyState::::generate("charlie", ciphersuite); + let charlie_kpb = charlie.key_package(ciphersuite, |builder| { + builder.leaf_node_capabilities( + Capabilities::builder() + .extensions(vec![ExtensionType::Unknown(0xf001)]) + .build(), + ) + }); + + let commit_content = frankenstein::FrankenFramedContent { + body: frankenstein::FrankenFramedContentBody::Commit(frankenstein::FrankenCommit { + proposals: vec![frankenstein::FrankenProposalOrRef::Proposal( + frankenstein::FrankenProposal::Add(frankenstein::FrankenAddProposal { + key_package: charlie_kpb.key_package.into(), + }), + )], + path: None, + }), + group_id, + epoch: gce_commit_epoch + 1, + sender, + authenticated_data, + }; + + let group_context = alice.group.export_group_context().clone(); + + let bob_group_context = bob.group.export_group_context(); + assert_eq!( + bob_group_context.confirmed_transcript_hash(), + group_context.confirmed_transcript_hash() + ); + + let secrets = alice.group.group().message_secrets(); + let membership_key = secrets.membership_key().as_slice(); + + let franken_commit = frankenstein::FrankenMlsMessage { + version, + body: frankenstein::FrankenMlsMessageBody::PublicMessage( + frankenstein::FrankenPublicMessage::auth( + &alice.party.provider, + ciphersuite, + &alice.party.signer, + commit_content, + Some(&group_context.into()), + Some(membership_key), + // this is a dummy confirmation_tag: + Some(vec![0u8; 32].into()), + ), + ), + }; + + let fake_commit = MlsMessageIn::tls_deserialize( + &mut franken_commit.tls_serialize_detached().unwrap().as_slice(), + ) + .unwrap(); + + // Note: If this starts failing, the order in which validation is checked may have changed and we + // fail on the fact that the confirmation tag is wrong. in that case, either the check has to be + // disabled, or the frankenstein framework needs code to properly compute it. + let err = bob.fail_processing(fake_commit); + assert!( + matches!( + err, + ProcessMessageError::InvalidCommit(StageCommitError::ProposalValidationError( + ProposalValidationError::InsufficientCapabilities + )) + ), + "got wrong error: {err:#?}" + ); +} + +// Test structure: +// - (alice creates group, adds bob, bob accepts) +// - This is part of the setup function +// - alice proposal GCE with required capabilities and commits +// - bob adds the proposal and merges the commit +// - bob proposes a self-update, but we tamper with it by removing +// an extension type from the capabilities. This makes it invalid. +// - we craft a commit by alice, committing the invalid proposal +// - it can't be done by bob, because the sender of a commit +// containing an update proposal can not be the owner of the +// leaf node +// - bob processes the invalid commit, which should give an InsufficientCapabilities error +#[openmls_test] +fn fail_insufficient_extensiontype_capabilities_update_valno103() { + let TestState { mut alice, mut bob } = setup::(ciphersuite); + + // requires that all members need support for extension type 0xf002 + let (gce_req_cap_commit, _, _) = + alice.update_group_context_extensions(Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf002)], &[], &[]), + ))); + + alice.merge_pending_commit(); + bob.process_and_merge_commit(gce_req_cap_commit.clone().into()); + + // let bob propose an update to their leaf node. + // we immediately discard it, because we want to tamper with it. + let (update_prop, _) = bob + .group + .propose_self_update( + &bob.party.provider, + &bob.party.signer, + LeafNodeParameters::builder().build(), + ) + .unwrap(); + bob.group + .clear_pending_proposals(bob.party.provider.storage()) + .unwrap(); + + // extract the FramedContent from the proposal MlsMessage, because that is + // what we'll have to pass into the `FrankenPublicMessage::auth` method later. + let frankenstein::FrankenMlsMessage { + version, + body: + frankenstein::FrankenMlsMessageBody::PublicMessage(frankenstein::FrankenPublicMessage { + content: mut franken_proposal_content, + .. + }), + } = frankenstein::FrankenMlsMessage::from(update_prop.clone()) + else { + unreachable!() + }; + + // we want to change the leaf node in the update proposal, so let's get a mutable borrow on that + let frankenstein::FrankenFramedContent { + body: + frankenstein::FrankenFramedContentBody::Proposal(frankenstein::FrankenProposal::Update( + frankenstein::FrankenUpdateProposal { + leaf_node: bob_franken_leaf_node, + }, + )), + .. + } = &mut franken_proposal_content + else { + unreachable!(); + }; + + // Remove the extension type from the capabilities that is part of required capabilities + // Committing this would be illegal + assert_eq!( + bob_franken_leaf_node.capabilities.extensions.remove(1), + 0xf002 + ); + + // Re-sign the leaf node so the signature checks pass + bob_franken_leaf_node.resign( + Some(frankenstein::FrankenTreePosition { + group_id: bob.group.group_id().as_slice().to_vec().into(), + leaf_index: bob.group.own_leaf_index().u32(), + }), + &bob.party.signer, + ); + + // prepare data needed for proposal + let group_context = bob.group.export_group_context().clone(); + let secrets = bob.group.group().message_secrets(); + let membership_key = secrets.membership_key().as_slice(); + + // build MlsMessage containing the proposal + let franken_proposal = frankenstein::FrankenMlsMessage { + version, + body: frankenstein::FrankenMlsMessageBody::PublicMessage( + frankenstein::FrankenPublicMessage::auth( + &bob.party.provider, + ciphersuite, + &bob.party.signer, + franken_proposal_content.clone(), + Some(&group_context.into()), + Some(membership_key), + // proposals don't have confirmation tags + None, + ), + ), + }; + let fake_proposal = MlsMessageIn::tls_deserialize( + &mut franken_proposal + .tls_serialize_detached() + .unwrap() + .as_slice(), + ) + .unwrap(); + + // alice stores the proposal. + alice.process_and_store_proposal(fake_proposal.clone()); + + // Now we'll craft a commit to the proposal signed by alice. + // For that we need a few values, let's fetch and build them. + let proposal_ref = bob.process_and_store_proposal(fake_proposal); + let alice_sender = frankenstein::FrankenSender::Member(0); + + // This is a commit, claimed to be from alice, that commits to the proposal ref of the invalid proposal + let commit_content = frankenstein::FrankenFramedContent { + sender: alice_sender, + body: frankenstein::FrankenFramedContentBody::Commit(frankenstein::FrankenCommit { + proposals: vec![frankenstein::FrankenProposalOrRef::Reference( + proposal_ref.as_slice().to_vec().into(), + )], + path: None, + }), + + ..franken_proposal_content + }; + + // prepare data needed for making the message authentic + let group_context = alice.group.export_group_context().clone(); + let secrets = alice.group.group().message_secrets(); + let membership_key = secrets.membership_key().as_slice(); + + let franken_commit = frankenstein::FrankenMlsMessage { + version, + body: frankenstein::FrankenMlsMessageBody::PublicMessage( + frankenstein::FrankenPublicMessage::auth( + &alice.party.provider, + ciphersuite, + &alice.party.signer, + commit_content, + Some(&group_context.into()), + Some(membership_key), + Some(vec![0; 32].into()), + ), + ), + }; + let fake_commit = MlsMessageIn::tls_deserialize( + &mut franken_commit.tls_serialize_detached().unwrap().as_slice(), + ) + .unwrap(); + + // when bob processes the commit, it should fail because the leaf node's capabilties do not + // satisfy those required by the group. + let err = bob.fail_processing(fake_commit); + + // Note: If this starts failing, the order in which validation is checked may have changed and we + // fail on the fact that the confirmation tag is wrong. in that case, either the check has to be + // disabled, or the frankenstein framework yet yet needs code to properly commpute it. + assert!( + matches!( + err, + ProcessMessageError::InvalidCommit(StageCommitError::ProposalValidationError( + ProposalValidationError::InsufficientCapabilities + )) + ), + "expected a different error, got: {err} ({err:#?})" + ); +} + +// This test doesn't belong here, but it's nice to have. It would be nice to factor it out, but +// it relies on the testing functions. +// +// I suppose we need to talk about which test framework is the one we need. +// See https://github.com/openmls/openmls/issues/1618. +#[openmls_test] +fn fail_key_package_version_valno201() { + let TestState { mut alice, mut bob } = setup::(ciphersuite); + + let charlie = PartyState::::generate("charlie", ciphersuite); + let charlie_key_package_bundle = charlie.key_package(ciphersuite, |b| b); + let charlie_key_package = charlie_key_package_bundle.key_package(); + + let (original_proposal, _) = alice.propose_add_member(charlie_key_package); + + alice + .group + .clear_pending_proposals(alice.party.provider.storage()) + .unwrap(); + + let Ok(frankenstein::FrankenMlsMessage { + version, + body: + frankenstein::FrankenMlsMessageBody::PublicMessage(frankenstein::FrankenPublicMessage { + content: + frankenstein::FrankenFramedContent { + group_id, + epoch, + sender, + authenticated_data, + body: + frankenstein::FrankenFramedContentBody::Proposal( + frankenstein::FrankenProposal::Add( + frankenstein::FrankenAddProposal { mut key_package }, + ), + ), + }, + .. + }), + }) = frankenstein::FrankenMlsMessage::tls_deserialize( + &mut original_proposal + .tls_serialize_detached() + .unwrap() + .as_slice(), + ) + else { + panic!("proposal message has unexpected format: {original_proposal:#?}") + }; + + key_package.protocol_version = 2; + key_package.resign(&charlie.signer); + + let group_context = alice.group.export_group_context(); + let membership_key = alice.group.group().message_secrets().membership_key(); + + let franken_commit_message = frankenstein::FrankenMlsMessage { + version, + body: frankenstein::FrankenMlsMessageBody::PublicMessage( + frankenstein::FrankenPublicMessage::auth( + &alice.party.provider, + ciphersuite, + &alice.party.signer, + frankenstein::FrankenFramedContent { + group_id, + epoch, + sender, + authenticated_data, + body: frankenstein::FrankenFramedContentBody::Commit( + frankenstein::FrankenCommit { + proposals: vec![frankenstein::FrankenProposalOrRef::Proposal( + frankenstein::FrankenProposal::Add( + frankenstein::FrankenAddProposal { key_package }, + ), + )], + path: None, + }, + ), + }, + Some(&group_context.clone().into()), + Some(membership_key.as_slice()), + // dummy value + Some(vec![0; 32].into()), + ), + ), + }; + + let fake_commit_message = MlsMessageIn::tls_deserialize( + &mut franken_commit_message + .tls_serialize_detached() + .unwrap() + .as_slice(), + ) + .unwrap(); + + let err = { + let validation_skip_handle = crate::skip_validation::checks::confirmation_tag::handle(); + validation_skip_handle.with_disabled(|| bob.fail_processing(fake_commit_message.clone())) + }; + + assert!(matches!( + err, + ProcessMessageError::ValidationError(ValidationError::KeyPackageVerifyError( + KeyPackageVerifyError::InvalidProtocolVersion + )) + )); +} + +// This tests that a commit containing more than one GCE Proposals does not pass validation. +#[openmls_test] +fn fail_2_gce_proposals_1_commit_valno308() { + let TestState { mut alice, mut bob } = setup::(ciphersuite); + + // No required capabilities, so no specifically required extensions. + assert!(alice + .group + .group() + .context() + .extensions() + .required_capabilities() + .is_none()); + + let new_extensions = Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf001)], &[], &[]), + )); + + let (proposal, _) = alice.propose_group_context_extensions(new_extensions.clone()); + bob.process_and_store_proposal(proposal.into()); + + assert_eq!(alice.group.pending_proposals().count(), 1); + + let (commit, _, _) = alice.commit_to_pending_proposals(); + + // we'll change the commit we feed to bob to include two GCE proposals + let mut franken_commit = FrankenMlsMessage::tls_deserialize( + &mut commit.tls_serialize_detached().unwrap().as_slice(), + ) + .unwrap(); + + // Craft a commit that has two GroupContextExtension proposals. This is forbidden by the RFC. + // Change the commit before alice commits, so alice's state is still in the old epoch and we can + // use her state to forge the macs and signatures + match &mut franken_commit.body { + frankenstein::FrankenMlsMessageBody::PublicMessage(msg) => { + match &mut msg.content.body { + frankenstein::FrankenFramedContentBody::Commit(commit) => { + let second_gces = frankenstein::FrankenProposalOrRef::Proposal( + frankenstein::FrankenProposal::GroupContextExtensions(vec![ + // ideally this should be some unknown extension, but it's tricky + // to get the payload set up correctly so we'll just go with this + frankenstein::FrankenExtension::LastResort, + ]), + ); + + commit.proposals.push(second_gces); + } + _ => unreachable!(), + } + + let group_context = alice.group.export_group_context().clone(); + + let bob_group_context = bob.group.export_group_context(); + assert_eq!( + bob_group_context.confirmed_transcript_hash(), + group_context.confirmed_transcript_hash() + ); + + let secrets = alice.group.group().message_secrets(); + let membership_key = secrets.membership_key().as_slice(); + + *msg = frankenstein::FrankenPublicMessage::auth( + &alice.party.provider, + group_context.ciphersuite(), + &alice.party.signer, + msg.content.clone(), + Some(&group_context.into()), + Some(membership_key), + // this is a dummy confirmation_tag: + Some(vec![0u8; 32].into()), + ); + } + _ => unreachable!(), + } + + let fake_commit = MlsMessageIn::tls_deserialize( + &mut franken_commit.tls_serialize_detached().unwrap().as_slice(), + ) + .unwrap(); + + let err = { + let validation_skip_handle = crate::skip_validation::checks::confirmation_tag::handle(); + validation_skip_handle.with_disabled(|| bob.fail_processing(fake_commit.clone())) + }; + + assert!(matches!( + err, + ProcessMessageError::InvalidCommit( + StageCommitError::GroupContextExtensionsProposalValidationError( + GroupContextExtensionsProposalValidationError::TooManyGCEProposals + ) + ) + )); +} + +/// This test makes sure that a commit to a GCE proposal with required_capabilities that are +/// not satisfied by all members' capabilities does not pass validation. +/// +// Test structure: +// - (alice creates group, adds bob, bob accepts) +// - This is part of the setup function +// - bob proposes updating the GC to have required_capabilities with extensions 0xf001 +// - both alice and bob support this extension +// - we modify the proposal and add 0xf003 - this is only supported by bob (see setup function) +// - we craft a commit to the proposal, signed by bob +// - alice processes the commit expecting an error, and the error should be that the GCE is +// invalid +#[openmls_test] +fn fail_unsupported_gces_add_valno1001() { + let TestState { mut alice, mut bob }: TestState = setup(ciphersuite); + + // No required capabilities, so no specifically required extensions. + assert!(alice + .group + .group() + .context() + .extensions() + .required_capabilities() + .is_none()); + + let new_extensions = Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf001)], &[], &[]), + )); + + let (original_proposal, _) = bob.propose_group_context_extensions(new_extensions.clone()); + + assert_eq!(bob.group.pending_proposals().count(), 1); + bob.group + .clear_pending_proposals(bob.party.provider.storage()) + .unwrap(); + + let Ok(frankenstein::FrankenMlsMessage { + version, + body: + frankenstein::FrankenMlsMessageBody::PublicMessage(frankenstein::FrankenPublicMessage { + content: + frankenstein::FrankenFramedContent { + group_id, + epoch, + sender: bob_sender, + authenticated_data, + body: + frankenstein::FrankenFramedContentBody::Proposal( + frankenstein::FrankenProposal::GroupContextExtensions(mut gces), + ), + }, + .. + }), + }) = frankenstein::FrankenMlsMessage::tls_deserialize( + &mut original_proposal + .tls_serialize_detached() + .unwrap() + .as_slice(), + ) + else { + panic!("proposal message has unexpected format: {original_proposal:#?}") + }; + + let Some(frankenstein::FrankenExtension::RequiredCapabilities( + frankenstein::FrankenRequiredCapabilitiesExtension { + extension_types, .. + }, + )) = gces.get_mut(0) + else { + panic!("required capabilities are malformed") + }; + + // this one is supported by bob, but not alice + extension_types.push(0xf003); + + let group_context = bob.group.export_group_context().clone(); + let secrets = bob.group.group().message_secrets(); + let membership_key = secrets.membership_key().as_slice(); + + let franken_commit_message = frankenstein::FrankenMlsMessage { + version, + body: frankenstein::FrankenMlsMessageBody::PublicMessage( + frankenstein::FrankenPublicMessage::auth( + &bob.party.provider, + ciphersuite, + &bob.party.signer, + frankenstein::FrankenFramedContent { + group_id, + epoch, + sender: bob_sender, + authenticated_data, + body: frankenstein::FrankenFramedContentBody::Commit( + frankenstein::FrankenCommit { + proposals: vec![frankenstein::FrankenProposalOrRef::Proposal( + frankenstein::FrankenProposal::GroupContextExtensions(gces), + )], + path: None, + }, + ), + }, + Some(&group_context.into()), + Some(membership_key), + // this is a dummy confirmation_tag: + Some(vec![0u8; 32].into()), + ), + ), + }; + + let fake_commit = MlsMessageIn::tls_deserialize( + &mut franken_commit_message + .tls_serialize_detached() + .unwrap() + .as_slice(), + ) + .unwrap(); + + let err = { + let validation_skip_handle = crate::skip_validation::checks::confirmation_tag::handle(); + validation_skip_handle.with_disabled(|| alice.fail_processing(fake_commit.clone())) + }; + + assert!( + matches!( + err, + ProcessMessageError::InvalidCommit( + StageCommitError::GroupContextExtensionsProposalValidationError( + GroupContextExtensionsProposalValidationError::RequiredExtensionNotSupportedByAllMembers + ) + ) + ), + "expected different error. got {err:?}" + ); +} + +// Test that the builder pattern accurately configures the new group. +#[openmls_test] +fn proposal() { + let TestState { mut alice, mut bob }: TestState = setup(ciphersuite); + + // No required capabilities, so no specifically required extensions. + assert!(alice + .group + .group() + .context() + .extensions() + .required_capabilities() + .is_none()); + + let new_extensions = Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf001)], &[], &[]), + )); + + let (proposal, _) = alice.propose_group_context_extensions(new_extensions.clone()); + bob.process_and_store_proposal(proposal.into()); + + assert_eq!(alice.group.pending_proposals().count(), 1); + + let (commit, _, _) = alice.commit_and_merge_pending(); + bob.process_and_merge_commit(commit.into()); + assert_eq!(alice.group.pending_proposals().count(), 0); + + let required_capabilities = alice + .group + .group() + .context() + .extensions() + .required_capabilities() + .expect("couldn't get required_capabilities"); + + // has required_capabilities as required capability + assert!(required_capabilities.extension_types() == [ExtensionType::Unknown(0xf001)]); + + // === committing to two group context extensions should fail + let new_extensions_2 = Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::RatchetTree], &[], &[]), + )); + + alice + .group + .propose_group_context_extensions( + &alice.party.provider, + new_extensions, + &alice.party.signer, + ) + .expect("failed to build group context extensions proposal"); + + // the proposals need to be different or they will be deduplicated + alice + .group + .propose_group_context_extensions( + &alice.party.provider, + new_extensions_2, + &alice.party.signer, + ) + .expect("failed to build group context extensions proposal"); + + assert_eq!(alice.group.pending_proposals().count(), 2); + + alice + .group + .commit_to_pending_proposals(&alice.party.provider, &alice.party.signer) + .expect_err( + "expected error when committing to multiple group context extensions proposals", + ); + + // === can't update required required_capabilities to extensions that existing group members + // are not capable of + + // contains unsupported extension + let new_extensions = Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf042)], &[], &[]), + )); + + alice + .group + .propose_group_context_extensions( + &alice.party.provider, + new_extensions, + &alice.party.signer, + ) + .expect_err("expected an error building GCE proposal with bad required_capabilities"); +} diff --git a/openmls/src/group/tests/mod.rs b/openmls/src/group/tests/mod.rs index 4a17955d7..607822812 100644 --- a/openmls/src/group/tests/mod.rs +++ b/openmls/src/group/tests/mod.rs @@ -5,6 +5,8 @@ mod external_add_proposal; #[cfg(test)] mod external_remove_proposal; #[cfg(test)] +mod group_context_extensions; +#[cfg(test)] pub mod kat_messages; #[cfg(test)] pub mod kat_transcript_hashes; diff --git a/openmls/src/prelude.rs b/openmls/src/prelude.rs index 9d683a46c..9b3aaba15 100644 --- a/openmls/src/prelude.rs +++ b/openmls/src/prelude.rs @@ -42,7 +42,7 @@ pub use crate::binary_tree::LeafNodeIndex; // TreeSync pub use crate::treesync::{ errors::{ApplyUpdatePathError, PublicTreeError}, - node::leaf_node::{Capabilities, LeafNode, LeafNodeParameters}, + node::leaf_node::{Capabilities, CapabilitiesBuilder, LeafNode, LeafNodeParameters}, node::parent_node::ParentNode, node::Node, RatchetTreeIn, diff --git a/openmls/src/test_utils/frankenstein/framing.rs b/openmls/src/test_utils/frankenstein/framing.rs index 5c4afb1c2..7d867ec16 100644 --- a/openmls/src/test_utils/frankenstein/framing.rs +++ b/openmls/src/test_utils/frankenstein/framing.rs @@ -160,8 +160,8 @@ impl FrankenPublicMessage { let wire_format = 1; // PublicMessage let franken_tbs = FrankenFramedContentTbs { - version: 1, - wire_format: 1, // PublicMessage + version, + wire_format, content: &content, group_context, }; diff --git a/openmls/src/treesync/node/leaf_node/capabilities.rs b/openmls/src/treesync/node/leaf_node/capabilities.rs index 5dfc51082..057e1fb82 100644 --- a/openmls/src/treesync/node/leaf_node/capabilities.rs +++ b/openmls/src/treesync/node/leaf_node/capabilities.rs @@ -93,6 +93,11 @@ impl Capabilities { } } + /// Creates a new [`CapabilitiesBuilder`] for constructing [`Capabilities`] + pub fn builder() -> CapabilitiesBuilder { + CapabilitiesBuilder(Self::default()) + } + // --------------------------------------------------------------------------------------------- /// Get a reference to the list of versions in this extension. @@ -174,6 +179,56 @@ impl Capabilities { } } +/// A helper for building [`Capabilities`] +#[derive(Debug, Clone)] +pub struct CapabilitiesBuilder(Capabilities); + +impl CapabilitiesBuilder { + /// Sets the `versions` field on the [`Capabilities`]. + pub fn versions(self, versions: Vec) -> Self { + Self(Capabilities { versions, ..self.0 }) + } + + /// Sets the `ciphersuites` field on the [`Capabilities`]. + pub fn ciphersuites(self, ciphersuites: Vec) -> Self { + let ciphersuites = ciphersuites.into_iter().map(|cs| cs.into()).collect(); + + Self(Capabilities { + ciphersuites, + ..self.0 + }) + } + + /// Sets the `extensions` field on the [`Capabilities`]. + pub fn extensions(self, extensions: Vec) -> Self { + Self(Capabilities { + extensions, + ..self.0 + }) + } + + /// Sets the `proposals` field on the [`Capabilities`]. + pub fn proposals(self, proposals: Vec) -> Self { + Self(Capabilities { + proposals, + ..self.0 + }) + } + + /// Sets the `credentials` field on the [`Capabilities`]. + pub fn credentials(self, credentials: Vec) -> Self { + Self(Capabilities { + credentials, + ..self.0 + }) + } + + /// Builds the [`Capabilities`]. + pub fn build(self) -> Capabilities { + self.0 + } +} + #[cfg(test)] impl Capabilities { /// Set the versions list. diff --git a/openmls/tests/test_mls_group.rs b/openmls/tests/test_mls_group.rs index 24a5fcd74..c8a4c8aaa 100644 --- a/openmls/tests/test_mls_group.rs +++ b/openmls/tests/test_mls_group.rs @@ -42,21 +42,28 @@ fn mls_group_operations() { for wire_format_policy in WIRE_FORMAT_POLICIES.iter() { let group_id = GroupId::from_slice(b"Test Group"); + let alice_provider = &Provider::default(); + let bob_provider = &Provider::default(); + let charlie_provider = &Provider::default(); + // Generate credentials with keys let (alice_credential, alice_signer) = - new_credential(provider, b"Alice", ciphersuite.signature_algorithm()); + new_credential(alice_provider, b"Alice", ciphersuite.signature_algorithm()); let (bob_credential, bob_signer) = - new_credential(provider, b"Bob", ciphersuite.signature_algorithm()); + new_credential(bob_provider, b"Bob", ciphersuite.signature_algorithm()); - let (charlie_credential, charlie_signer) = - new_credential(provider, b"Charlie", ciphersuite.signature_algorithm()); + let (charlie_credential, charlie_signer) = new_credential( + charlie_provider, + b"Charlie", + ciphersuite.signature_algorithm(), + ); // Generate KeyPackages let bob_key_package = generate_key_package( ciphersuite, Extensions::empty(), - provider, + bob_provider, bob_credential.clone(), &bob_signer, ); @@ -70,7 +77,7 @@ fn mls_group_operations() { // === Alice creates a group === let mut alice_group = MlsGroup::new_with_group_id( - provider, + alice_provider, &alice_signer, &mls_group_create_config, group_id.clone(), @@ -79,10 +86,11 @@ fn mls_group_operations() { .expect("An unexpected error occurred."); // === Alice adds Bob === - let welcome = match alice_group.add_members(provider, &alice_signer, &[bob_key_package]) { - Ok((_, welcome, _)) => welcome, - Err(e) => panic!("Could not add member to group: {e:?}"), - }; + let welcome = + match alice_group.add_members(alice_provider, &alice_signer, &[bob_key_package]) { + Ok((_, welcome, _)) => welcome, + Err(e) => panic!("Could not add member to group: {e:?}"), + }; // Check that we received the correct proposals if let Some(staged_commit) = alice_group.pending_commit() { @@ -104,7 +112,7 @@ fn mls_group_operations() { } alice_group - .merge_pending_commit(provider) + .merge_pending_commit(alice_provider) .expect("error merging pending commit"); // Check that the group now has two members @@ -123,13 +131,13 @@ fn mls_group_operations() { .expect("expected the message to be a welcome message"); let mut bob_group = StagedWelcome::new_from_welcome( - provider, + bob_provider, mls_group_create_config.join_config(), welcome, Some(alice_group.export_ratchet_tree().into()), ) .expect("Error creating StagedWelcome from Welcome") - .into_group(provider) + .into_group(bob_provider) .expect("Error creating group from StagedWelcome"); // Make sure that both groups have the same members @@ -144,12 +152,12 @@ fn mls_group_operations() { // === Alice sends a message to Bob === let message_alice = b"Hi, I'm Alice!"; let queued_message = alice_group - .create_message(provider, &alice_signer, message_alice) + .create_message(alice_provider, &alice_signer, message_alice) .expect("Error creating application message"); let processed_message = bob_group .process_message( - provider, + bob_provider, queued_message .clone() .into_protocol_message() @@ -177,12 +185,12 @@ fn mls_group_operations() { // === Bob updates and commits === let (queued_message, welcome_option, _group_info) = bob_group - .self_update(provider, &bob_signer, LeafNodeParameters::default()) + .self_update(bob_provider, &bob_signer, LeafNodeParameters::default()) .unwrap(); let alice_processed_message = alice_group .process_message( - provider, + alice_provider, queued_message .clone() .into_protocol_message() @@ -196,14 +204,14 @@ fn mls_group_operations() { { // Merge staged Commit alice_group - .merge_staged_commit(provider, *staged_commit) + .merge_staged_commit(alice_provider, *staged_commit) .unwrap(); } else { unreachable!("Expected a StagedCommit."); } bob_group - .merge_pending_commit(provider) + .merge_pending_commit(bob_provider) .expect("error merging pending commit"); // Check we didn't receive a Welcome message @@ -211,8 +219,10 @@ fn mls_group_operations() { // Check that both groups have the same state assert_eq!( - alice_group.export_secret(provider, "", &[], 32).unwrap(), - bob_group.export_secret(provider, "", &[], 32).unwrap() + alice_group + .export_secret(alice_provider, "", &[], 32) + .unwrap(), + bob_group.export_secret(bob_provider, "", &[], 32).unwrap() ); // Make sure that both groups have the same public tree @@ -223,12 +233,12 @@ fn mls_group_operations() { // === Alice updates and commits === let (queued_message, _) = alice_group - .propose_self_update(provider, &alice_signer, LeafNodeParameters::default()) + .propose_self_update(alice_provider, &alice_signer, LeafNodeParameters::default()) .unwrap(); let bob_processed_message = bob_group .process_message( - provider, + bob_provider, queued_message .clone() .into_protocol_message() @@ -248,7 +258,7 @@ fn mls_group_operations() { ); // Store proposal alice_group - .store_pending_proposal(provider.storage(), *staged_proposal.clone()) + .store_pending_proposal(alice_provider.storage(), *staged_proposal.clone()) .unwrap(); } else { unreachable!("Expected a Proposal."); @@ -261,19 +271,19 @@ fn mls_group_operations() { )); bob_group - .store_pending_proposal(provider.storage(), *staged_proposal) + .store_pending_proposal(bob_provider.storage(), *staged_proposal) .unwrap(); } else { unreachable!("Expected a QueuedProposal."); } let (queued_message, _welcome_option, _group_info) = alice_group - .commit_to_pending_proposals(provider, &alice_signer) + .commit_to_pending_proposals(alice_provider, &alice_signer) .unwrap(); let bob_processed_message = bob_group .process_message( - provider, + bob_provider, queued_message .clone() .into_protocol_message() @@ -286,20 +296,22 @@ fn mls_group_operations() { bob_processed_message.into_content() { bob_group - .merge_staged_commit(provider, *staged_commit) + .merge_staged_commit(bob_provider, *staged_commit) .unwrap(); } else { unreachable!("Expected a StagedCommit."); } alice_group - .merge_pending_commit(provider) + .merge_pending_commit(alice_provider) .expect("error merging pending commit"); // Check that both groups have the same state assert_eq!( - alice_group.export_secret(provider, "", &[], 32).unwrap(), - bob_group.export_secret(provider, "", &[], 32).unwrap() + alice_group + .export_secret(alice_provider, "", &[], 32) + .unwrap(), + bob_group.export_secret(bob_provider, "", &[], 32).unwrap() ); // Make sure that both groups have the same public tree @@ -312,18 +324,18 @@ fn mls_group_operations() { let charlie_key_package = generate_key_package( ciphersuite, Extensions::empty(), - provider, + charlie_provider, charlie_credential, &charlie_signer, ); let (queued_message, welcome, _group_info) = bob_group - .add_members(provider, &bob_signer, &[charlie_key_package]) + .add_members(bob_provider, &bob_signer, &[charlie_key_package]) .unwrap(); let alice_processed_message = alice_group .process_message( - provider, + alice_provider, queued_message .clone() .into_protocol_message() @@ -331,7 +343,7 @@ fn mls_group_operations() { ) .expect("Could not process message."); bob_group - .merge_pending_commit(provider) + .merge_pending_commit(bob_provider) .expect("error merging pending commit"); // Merge Commit @@ -339,7 +351,7 @@ fn mls_group_operations() { alice_processed_message.into_content() { alice_group - .merge_staged_commit(provider, *staged_commit) + .merge_staged_commit(alice_provider, *staged_commit) .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -351,13 +363,13 @@ fn mls_group_operations() { .expect("expected the message to be a welcome message"); let mut charlie_group = StagedWelcome::new_from_welcome( - provider, + charlie_provider, mls_group_create_config.join_config(), welcome, Some(bob_group.export_ratchet_tree().into()), ) .expect("Error creating staged join from Welcome") - .into_group(provider) + .into_group(charlie_provider) .expect("Error creating group from staged join"); // Make sure that all groups have the same public tree @@ -382,12 +394,12 @@ fn mls_group_operations() { // === Charlie sends a message to the group === let message_charlie = b"Hi, I'm Charlie!"; let queued_message = charlie_group - .create_message(provider, &charlie_signer, message_charlie) + .create_message(charlie_provider, &charlie_signer, message_charlie) .expect("Error creating application message"); let _alice_processed_message = alice_group .process_message( - provider, + alice_provider, queued_message .clone() .into_protocol_message() @@ -396,7 +408,7 @@ fn mls_group_operations() { .expect("Could not process message."); let _bob_processed_message = bob_group .process_message( - provider, + bob_provider, queued_message .clone() .into_protocol_message() @@ -406,12 +418,16 @@ fn mls_group_operations() { // === Charlie updates and commits === let (queued_message, welcome_option, _group_info) = charlie_group - .self_update(provider, &charlie_signer, LeafNodeParameters::default()) + .self_update( + charlie_provider, + &charlie_signer, + LeafNodeParameters::default(), + ) .unwrap(); let alice_processed_message = alice_group .process_message( - provider, + alice_provider, queued_message .clone() .into_protocol_message() @@ -420,7 +436,7 @@ fn mls_group_operations() { .expect("Could not process message."); let bob_processed_message = bob_group .process_message( - provider, + bob_provider, queued_message .clone() .into_protocol_message() @@ -428,7 +444,7 @@ fn mls_group_operations() { ) .expect("Could not process message."); charlie_group - .merge_pending_commit(provider) + .merge_pending_commit(charlie_provider) .expect("error merging pending commit"); // Merge Commit @@ -436,7 +452,7 @@ fn mls_group_operations() { alice_processed_message.into_content() { alice_group - .merge_staged_commit(provider, *staged_commit) + .merge_staged_commit(alice_provider, *staged_commit) .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -447,7 +463,7 @@ fn mls_group_operations() { bob_processed_message.into_content() { bob_group - .merge_staged_commit(provider, *staged_commit) + .merge_staged_commit(bob_provider, *staged_commit) .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -458,12 +474,18 @@ fn mls_group_operations() { // Check that all groups have the same state assert_eq!( - alice_group.export_secret(provider, "", &[], 32).unwrap(), - bob_group.export_secret(provider, "", &[], 32).unwrap() + alice_group + .export_secret(alice_provider, "", &[], 32) + .unwrap(), + bob_group.export_secret(bob_provider, "", &[], 32).unwrap() ); assert_eq!( - alice_group.export_secret(provider, "", &[], 32).unwrap(), - charlie_group.export_secret(provider, "", &[], 32).unwrap() + alice_group + .export_secret(alice_provider, "", &[], 32) + .unwrap(), + charlie_group + .export_secret(charlie_provider, "", &[], 32) + .unwrap() ); // Make sure that all groups have the same public tree @@ -479,7 +501,11 @@ fn mls_group_operations() { // === Charlie removes Bob === println!(" >>> Charlie is removing bob"); let (queued_message, welcome_option, _group_info) = charlie_group - .remove_members(provider, &charlie_signer, &[bob_group.own_leaf_index()]) + .remove_members( + charlie_provider, + &charlie_signer, + &[bob_group.own_leaf_index()], + ) .expect("Could not remove member from group."); // Check that Bob's group is still active @@ -487,7 +513,7 @@ fn mls_group_operations() { let alice_processed_message = alice_group .process_message( - provider, + alice_provider, queued_message .clone() .into_protocol_message() @@ -496,7 +522,7 @@ fn mls_group_operations() { .expect("Could not process message."); let bob_processed_message = bob_group .process_message( - provider, + bob_provider, queued_message .clone() .into_protocol_message() @@ -504,7 +530,7 @@ fn mls_group_operations() { ) .expect("Could not process message."); charlie_group - .merge_pending_commit(provider) + .merge_pending_commit(charlie_provider) .expect("error merging pending commit"); // Check that we receive the correct proposal for Alice @@ -524,7 +550,7 @@ fn mls_group_operations() { // Merge staged Commit alice_group - .merge_staged_commit(provider, *staged_commit) + .merge_staged_commit(alice_provider, *staged_commit) .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -547,7 +573,7 @@ fn mls_group_operations() { // Merge staged Commit bob_group - .merge_staged_commit(provider, *staged_commit) + .merge_staged_commit(bob_provider, *staged_commit) .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -577,7 +603,7 @@ fn mls_group_operations() { // Check that Bob can no longer send messages assert!(bob_group - .create_message(provider, &bob_signer, b"Should not go through") + .create_message(bob_provider, &bob_signer, b"Should not go through") .is_err()); // === Alice removes Charlie and re-adds Bob === @@ -586,19 +612,23 @@ fn mls_group_operations() { let bob_key_package = generate_key_package( ciphersuite, Extensions::empty(), - provider, + bob_provider, bob_credential.clone(), &bob_signer, ); // Create RemoveProposal and process it let (queued_message, _) = alice_group - .propose_remove_member(provider, &alice_signer, charlie_group.own_leaf_index()) + .propose_remove_member( + alice_provider, + &alice_signer, + charlie_group.own_leaf_index(), + ) .expect("Could not create proposal to remove Charlie"); let charlie_processed_message = charlie_group .process_message( - provider, + charlie_provider, queued_message .clone() .into_protocol_message() @@ -615,7 +645,7 @@ fn mls_group_operations() { assert_eq!(remove_proposal.removed(), members[1].index); // Store proposal charlie_group - .store_pending_proposal(provider.storage(), *staged_proposal.clone()) + .store_pending_proposal(charlie_provider.storage(), *staged_proposal.clone()) .unwrap(); } else { unreachable!("Expected a Proposal."); @@ -632,12 +662,12 @@ fn mls_group_operations() { // Create AddProposal and process it let (queued_message, _) = alice_group - .propose_add_member(provider, &alice_signer, &bob_key_package) + .propose_add_member(alice_provider, &alice_signer, &bob_key_package) .expect("Could not create proposal to add Bob"); let charlie_processed_message = charlie_group .process_message( - provider, + charlie_provider, queued_message .clone() .into_protocol_message() @@ -666,7 +696,7 @@ fn mls_group_operations() { )); // Store proposal charlie_group - .store_pending_proposal(provider.storage(), *staged_proposal) + .store_pending_proposal(charlie_provider.storage(), *staged_proposal) .unwrap(); } else { unreachable!("Expected a QueuedProposal."); @@ -674,12 +704,12 @@ fn mls_group_operations() { // Commit to the proposals and process it let (queued_message, welcome_option, _group_info) = alice_group - .commit_to_pending_proposals(provider, &alice_signer) + .commit_to_pending_proposals(alice_provider, &alice_signer) .expect("Could not flush proposals"); let charlie_processed_message = charlie_group .process_message( - provider, + charlie_provider, queued_message .clone() .into_protocol_message() @@ -689,7 +719,7 @@ fn mls_group_operations() { // Merge Commit alice_group - .merge_pending_commit(provider) + .merge_pending_commit(alice_provider) .expect("error merging pending commit"); // Merge Commit @@ -697,7 +727,7 @@ fn mls_group_operations() { charlie_processed_message.into_content() { charlie_group - .merge_staged_commit(provider, *staged_commit) + .merge_staged_commit(charlie_provider, *staged_commit) .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -720,13 +750,13 @@ fn mls_group_operations() { // Bob creates a new group let mut bob_group = StagedWelcome::new_from_welcome( - provider, + bob_provider, mls_group_create_config.join_config(), welcome, Some(alice_group.export_ratchet_tree().into()), ) .expect("Error creating staged join from Welcome") - .into_group(provider) + .into_group(bob_provider) .expect("Error creating group from staged join"); // Make sure the group contains two members @@ -752,12 +782,12 @@ fn mls_group_operations() { // === Alice sends a message to the group === let message_alice = b"Hi, I'm Alice!"; let queued_message = alice_group - .create_message(provider, &alice_signer, message_alice) + .create_message(alice_provider, &alice_signer, message_alice) .expect("Error creating application message"); let bob_processed_message = bob_group .process_message( - provider, + bob_provider, queued_message .clone() .into_protocol_message() @@ -786,12 +816,12 @@ fn mls_group_operations() { // === Bob leaves the group === let queued_message = bob_group - .leave_group(provider, &bob_signer) + .leave_group(bob_provider, &bob_signer) .expect("Could not leave group"); let alice_processed_message = alice_group .process_message( - provider, + alice_provider, queued_message .clone() .into_protocol_message() @@ -805,7 +835,7 @@ fn mls_group_operations() { { // Store proposal alice_group - .store_pending_proposal(provider.storage(), *staged_proposal) + .store_pending_proposal(alice_provider.storage(), *staged_proposal) .unwrap(); } else { unreachable!("Expected a QueuedProposal."); @@ -813,14 +843,14 @@ fn mls_group_operations() { // Should fail because you cannot remove yourself from a group assert!(matches!( - bob_group.commit_to_pending_proposals(provider, &bob_signer), + bob_group.commit_to_pending_proposals(bob_provider, &bob_signer), Err(CommitToPendingProposalsError::CreateCommitError( CreateCommitError::CannotRemoveSelf )) )); let (queued_message, _welcome_option, _group_info) = alice_group - .commit_to_pending_proposals(provider, &alice_signer) + .commit_to_pending_proposals(alice_provider, &alice_signer) .expect("Could not commit to proposals."); // Check that Bob's group is still active @@ -844,12 +874,12 @@ fn mls_group_operations() { } alice_group - .merge_pending_commit(provider) + .merge_pending_commit(alice_provider) .expect("Could not merge Commit."); let bob_processed_message = bob_group .process_message( - provider, + bob_provider, queued_message .clone() .into_protocol_message() @@ -873,7 +903,7 @@ fn mls_group_operations() { assert!(staged_commit.self_removed()); // Merge staged Commit bob_group - .merge_staged_commit(provider, *staged_commit) + .merge_staged_commit(bob_provider, *staged_commit) .unwrap(); } else { unreachable!("Expected a StagedCommit."); @@ -896,23 +926,23 @@ fn mls_group_operations() { let bob_key_package = generate_key_package( ciphersuite, Extensions::empty(), - provider, + bob_provider, bob_credential, &bob_signer, ); // Add Bob to the group let (_queued_message, welcome, _group_info) = alice_group - .add_members(provider, &alice_signer, &[bob_key_package]) + .add_members(alice_provider, &alice_signer, &[bob_key_package]) .expect("Could not add Bob"); - let _test_group = MlsGroup::load(provider.storage(), &group_id) + let _test_group = MlsGroup::load(alice_provider.storage(), &group_id) .expect("Could not load the group state due to an error.") .expect("Could not load the group state because the group does not exist."); // Merge Commit alice_group - .merge_pending_commit(provider) + .merge_pending_commit(alice_provider) .expect("error merging pending commit"); let welcome: MlsMessageIn = welcome.into(); @@ -921,35 +951,35 @@ fn mls_group_operations() { .expect("expected the message to be a welcome message"); let mut bob_group = StagedWelcome::new_from_welcome( - provider, + bob_provider, mls_group_create_config.join_config(), welcome, Some(alice_group.export_ratchet_tree().into()), ) .expect("Could not create staged join from Welcome") - .into_group(provider) + .into_group(bob_provider) .expect("Could not create group from staged join"); assert_eq!( alice_group - .export_secret(provider, "before load", &[], 32) + .export_secret(alice_provider, "before load", &[], 32) .unwrap(), bob_group - .export_secret(provider, "before load", &[], 32) + .export_secret(bob_provider, "before load", &[], 32) .unwrap() ); - bob_group = MlsGroup::load(provider.storage(), &group_id) + bob_group = MlsGroup::load(bob_provider.storage(), &group_id) .expect("Could not load group from file because of an error") .expect("Could not load group from file because there is no group with given id"); // Make sure the state is still the same assert_eq!( alice_group - .export_secret(provider, "after load", &[], 32) + .export_secret(alice_provider, "after load", &[], 32) .unwrap(), bob_group - .export_secret(provider, "after load", &[], 32) + .export_secret(bob_provider, "after load", &[], 32) .unwrap() ); } From 07fc3b2aeeff397739903969861b51feefa9aeb5 Mon Sep 17 00:00:00 2001 From: Jan Winkelmann <146678+keks@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:47:24 +0200 Subject: [PATCH 18/43] Add a KAT for testing storage stability (#1610) Co-authored-by: Jan Winkelmann (keks) --- memory_storage/src/lib.rs | 59 ++ .../binary_tree/array_representation/diff.rs | 2 +- openmls/src/group/core_group/mod.rs | 8 +- openmls/src/group/core_group/past_secrets.rs | 6 +- openmls/src/group/core_group/proposals.rs | 2 +- openmls/src/group/core_group/staged_commit.rs | 6 +- openmls/src/group/mls_group/mod.rs | 6 +- openmls/src/group/mls_group/processing.rs | 4 + openmls/src/group/mod.rs | 16 +- openmls/src/group/public_group/diff.rs | 2 +- .../src/group/public_group/staged_commit.rs | 2 +- openmls/src/schedule/message_secrets.rs | 2 +- openmls/src/schedule/mod.rs | 20 +- openmls/src/schedule/psk.rs | 10 +- openmls/src/storage.rs | 3 + openmls/src/storage/kat_storage_stability.rs | 726 ++++++++++++++++++ openmls/src/treesync/diff.rs | 2 +- openmls/src/treesync/node/encryption_keys.rs | 4 +- openmls/test_vectors/storage-stability.json | 1 + 19 files changed, 836 insertions(+), 45 deletions(-) create mode 100644 openmls/src/storage/kat_storage_stability.rs create mode 100644 openmls/test_vectors/storage-stability.json diff --git a/memory_storage/src/lib.rs b/memory_storage/src/lib.rs index 40ff199be..4de3e6f03 100644 --- a/memory_storage/src/lib.rs +++ b/memory_storage/src/lib.rs @@ -2,6 +2,9 @@ use openmls_traits::storage::*; use serde::Serialize; use std::{collections::HashMap, sync::RwLock}; +#[cfg(feature = "test-utils")] +use std::io::Write as _; + /// A storage for the V_TEST version. #[cfg(any(test, feature = "test-utils"))] mod test_store; @@ -25,6 +28,62 @@ impl Clone for MemoryStorage { } } +// For testing (KATs in particular) we want to serialize and deserialize the storage +#[cfg(feature = "test-utils")] +impl MemoryStorage { + pub fn serialize(&self, w: &mut Vec) -> std::io::Result { + let values = self.values.read().unwrap(); + + let mut written = 8; + let count = (values.len() as u64).to_be_bytes(); + w.write_all(&count)?; + + for (k, v) in values.iter() { + let rec_len = 8 + 8 + k.len() + v.len(); + let k_len = (k.len() as u64).to_be_bytes(); + let v_len = (v.len() as u64).to_be_bytes(); + + w.write_all(&k_len)?; + w.write_all(&v_len)?; + w.write_all(k)?; + w.write_all(v)?; + + written += rec_len; + } + + Ok(written) + } + + pub fn deserialize(r: &mut R) -> std::io::Result { + let read_u64 = |r: &mut R| { + let mut buf8 = [0u8; 8]; + r.read_exact(&mut buf8).map(|_| u64::from_be_bytes(buf8)) + }; + + let read_bytes = |r: &mut R, len: usize| { + let mut buf = vec![0u8; len]; + r.read_exact(&mut buf).map(|_| buf) + }; + + let mut count = read_u64(r)? as usize; + let mut map = HashMap::new(); + + while count > 0 { + let k_len = read_u64(r)? as usize; + let v_len = read_u64(r)? as usize; + let k = read_bytes(r, k_len)?; + let v = read_bytes(r, v_len)?; + + map.insert(k, v); + count -= 1; + } + + Ok(Self { + values: RwLock::new(map), + }) + } +} + impl MemoryStorage { /// Internal helper to abstract write operations. #[inline(always)] diff --git a/openmls/src/binary_tree/array_representation/diff.rs b/openmls/src/binary_tree/array_representation/diff.rs index b14bfbfa0..4f38fe6e6 100644 --- a/openmls/src/binary_tree/array_representation/diff.rs +++ b/openmls/src/binary_tree/array_representation/diff.rs @@ -42,7 +42,7 @@ use super::{ /// was created from. However, the lack of the internal reference means that its /// lifetime is not tied to that of the original tree. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) struct StagedAbDiff { leaf_diff: BTreeMap, parent_diff: BTreeMap, diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 589360f46..197a4bae9 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -159,8 +159,7 @@ pub(crate) struct StagedCoreWelcome { } #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq))] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) struct CoreGroup { public_group: PublicGroup, group_epoch_secrets: GroupEpochSecrets, @@ -1208,6 +1207,7 @@ impl CoreGroup { } // Test and test-utils functions +#[cfg_attr(all(not(test), feature = "test-utils"), allow(dead_code))] #[cfg(any(feature = "test-utils", test))] impl CoreGroup { pub(crate) fn context_mut(&mut self) -> &mut GroupContext { @@ -1221,6 +1221,10 @@ impl CoreGroup { pub(crate) fn print_ratchet_tree(&self, message: &str) { println!("{}: {}", message, self.public_group().export_ratchet_tree()); } + + pub(crate) fn resumption_psk_store(&self) -> &ResumptionPskStore { + &self.resumption_psk_store + } } /// Configuration for core group. diff --git a/openmls/src/group/core_group/past_secrets.rs b/openmls/src/group/core_group/past_secrets.rs index b6847982f..e3e878717 100644 --- a/openmls/src/group/core_group/past_secrets.rs +++ b/openmls/src/group/core_group/past_secrets.rs @@ -6,8 +6,7 @@ use super::*; // Internal helper struct #[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq))] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] #[cfg_attr(feature = "crypto-debug", derive(Debug))] struct EpochTree { epoch: u64, @@ -18,8 +17,7 @@ struct EpochTree { /// Can store message secrets for up to `max_epochs`. The trees are added with [`self::add()`] and can be queried /// with [`Self::get_epoch()`]. #[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq))] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] #[cfg_attr(feature = "crypto-debug", derive(Debug))] pub(crate) struct MessageSecretsStore { // Maximum size of the `past_epoch_trees` list. diff --git a/openmls/src/group/core_group/proposals.rs b/openmls/src/group/core_group/proposals.rs index 070c094a1..372426891 100644 --- a/openmls/src/group/core_group/proposals.rs +++ b/openmls/src/group/core_group/proposals.rs @@ -190,7 +190,7 @@ impl OrderedProposalRefs { /// accessed efficiently. To enable iteration over the queue in order, the /// `ProposalQueue` also contains a vector of `ProposalRef`s. #[derive(Default, Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) struct ProposalQueue { /// `proposal_references` holds references to the proposals in the queue and /// determines the order of the queue. diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index 2336ffd40..21c8112f6 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -429,7 +429,7 @@ impl CoreGroup { } #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) enum StagedCommitState { PublicState(Box), GroupMember(Box), @@ -437,7 +437,7 @@ pub(crate) enum StagedCommitState { /// Contains the changes from a commit to the group state. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub struct StagedCommit { staged_proposal_queue: ProposalQueue, state: StagedCommitState, @@ -570,7 +570,7 @@ impl StagedCommit { /// This struct is used internally by [StagedCommit] to encapsulate all the modified group state. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) struct MemberStagedCommitState { group_epoch_secrets: GroupEpochSecrets, message_secrets: MessageSecrets, diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 45130d33c..db2360e9d 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -42,7 +42,7 @@ mod test_mls_group; /// Pending Commit state. Differentiates between Commits issued by group members /// and External Commits. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub enum PendingCommitState { /// Commit from a group member Member(StagedCommit), @@ -115,7 +115,7 @@ impl From for StagedCommit { /// external commit process, see [`MlsGroup::join_by_external_commit()`] or /// Section 11.2.1 of the MLS specification. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub enum MlsGroupState { /// There is currently a pending Commit that hasn't been merged yet. PendingCommit(Box), @@ -150,7 +150,7 @@ pub enum MlsGroupState { /// inactive, as well as if it has a pending commit. See [`MlsGroupState`] for /// more information. #[derive(Debug)] -#[cfg_attr(feature = "test-utils", derive(Clone))] +#[cfg_attr(feature = "test-utils", derive(Clone, PartialEq))] pub struct MlsGroup { // The group configuration. See [`MlsGroupJoinConfig`] for more information. mls_group_config: MlsGroupJoinConfig, diff --git a/openmls/src/group/mls_group/processing.rs b/openmls/src/group/mls_group/processing.rs index c932f5253..d58d0a940 100644 --- a/openmls/src/group/mls_group/processing.rs +++ b/openmls/src/group/mls_group/processing.rs @@ -148,6 +148,10 @@ impl MlsGroup { self.group .resumption_psk_store .add(self.group.context().epoch(), resumption_psk.clone()); + provider + .storage() + .write_resumption_psk_store(self.group_id(), &self.group.resumption_psk_store) + .map_err(MergeCommitError::StorageError)?; // Delete own KeyPackageBundles self.own_leaf_nodes.clear(); diff --git a/openmls/src/group/mod.rs b/openmls/src/group/mod.rs index c905da981..e4659c5ea 100644 --- a/openmls/src/group/mod.rs +++ b/openmls/src/group/mod.rs @@ -2,19 +2,19 @@ //! //! This module contains the API to interact with groups. -mod group_context; - use std::fmt::Display; +use serde::{Deserialize, Serialize}; +use tls_codec::*; + +use crate::extensions::*; +use openmls_traits::random::OpenMlsRand; + #[cfg(test)] use crate::ciphersuite::*; -use crate::extensions::*; #[cfg(test)] use crate::utils::*; -use serde::{Deserialize, Serialize}; -use tls_codec::*; - // Crate pub(crate) mod core_group; pub(crate) mod public_group; @@ -32,12 +32,14 @@ pub use mls_group::membership::*; pub use mls_group::*; pub use public_group::*; +// Private +mod group_context; + // Tests #[cfg(test)] pub(crate) use core_group::create_commit_params::*; #[cfg(any(feature = "test-utils", test))] pub(crate) mod tests; -use openmls_traits::random::OpenMlsRand; /// A group ID. The group ID is chosen by the creator of the group and should be globally unique. #[derive( diff --git a/openmls/src/group/public_group/diff.rs b/openmls/src/group/public_group/diff.rs index cdc1a9f79..a6e5e42fa 100644 --- a/openmls/src/group/public_group/diff.rs +++ b/openmls/src/group/public_group/diff.rs @@ -238,7 +238,7 @@ impl<'a> PublicGroupDiff<'a> { /// The staged version of a [`PublicGroupDiff`], which means it can no longer be /// modified. Its only use is to merge it into the original [`PublicGroup`]. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) struct StagedPublicGroupDiff { pub(super) staged_diff: StagedTreeSyncDiff, pub(super) group_context: GroupContext, diff --git a/openmls/src/group/public_group/staged_commit.rs b/openmls/src/group/public_group/staged_commit.rs index 1dd7d2f7e..245d274c2 100644 --- a/openmls/src/group/public_group/staged_commit.rs +++ b/openmls/src/group/public_group/staged_commit.rs @@ -10,7 +10,7 @@ use crate::{ }; #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub struct PublicStagedCommitState { pub(super) staged_diff: StagedPublicGroupDiff, pub(super) update_path_leaf_node: Option, diff --git a/openmls/src/schedule/message_secrets.rs b/openmls/src/schedule/message_secrets.rs index 792182cc9..1742f9193 100644 --- a/openmls/src/schedule/message_secrets.rs +++ b/openmls/src/schedule/message_secrets.rs @@ -117,7 +117,7 @@ impl MessageSecrets { } // In tests we allow comparing secrets. -#[cfg(test)] +#[cfg(any(test, feature = "test-utils"))] impl PartialEq for MessageSecrets { fn eq(&self, other: &Self) -> bool { self.sender_data_secret == other.sender_data_secret diff --git a/openmls/src/schedule/mod.rs b/openmls/src/schedule/mod.rs index 515061e0f..cd2db1259 100644 --- a/openmls/src/schedule/mod.rs +++ b/openmls/src/schedule/mod.rs @@ -163,7 +163,7 @@ pub use psk::{ExternalPsk, PreSharedKeyId, Psk}; /// A group secret that can be used among members to prove that a member was /// part of a group in a given epoch. #[derive(Clone, Debug, Serialize, Deserialize)] -#[cfg_attr(test, derive(Eq, PartialEq))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Eq, PartialEq))] pub struct ResumptionPskSecret { secret: Secret, } @@ -939,10 +939,9 @@ fn ciphertext_sample(ciphersuite: Ciphersuite, ciphertext: &[u8]) -> &[u8] { /// A key that can be used to derive an `AeadKey` and an `AeadNonce`. #[derive(Serialize, Deserialize)] -#[cfg_attr(test, derive(PartialEq))] #[cfg_attr( any(feature = "test-utils", feature = "crypto-debug", test), - derive(Debug, Clone) + derive(Debug, Clone, PartialEq) )] pub(crate) struct SenderDataSecret { secret: Secret, @@ -1225,7 +1224,7 @@ impl EpochSecrets { } #[derive(Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) struct GroupEpochSecrets { init_secret: InitSecret, exporter_secret: ExporterSecret, @@ -1240,24 +1239,13 @@ impl std::fmt::Debug for GroupEpochSecrets { } } -#[cfg(not(test))] +#[cfg(not(any(test, feature = "test-utils")))] impl PartialEq for GroupEpochSecrets { fn eq(&self, _other: &Self) -> bool { false } } -// In tests we allow comparing secrets. -#[cfg(test)] -impl PartialEq for GroupEpochSecrets { - fn eq(&self, other: &Self) -> bool { - self.exporter_secret == other.exporter_secret - && self.epoch_authenticator == other.epoch_authenticator - && self.external_secret == other.external_secret - && self.resumption_psk == other.resumption_psk - } -} - impl GroupEpochSecrets { /// Init secret pub(crate) fn init_secret(&self) -> &InitSecret { diff --git a/openmls/src/schedule/psk.rs b/openmls/src/schedule/psk.rs index cab6ef88d..f9acff865 100644 --- a/openmls/src/schedule/psk.rs +++ b/openmls/src/schedule/psk.rs @@ -490,8 +490,7 @@ pub mod store { /// /// This is where the resumption PSKs are kept in a rollover list. #[derive(Debug, Serialize, Deserialize)] - #[cfg_attr(test, derive(PartialEq))] - #[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] + #[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) struct ResumptionPskStore { max_number_of_secrets: usize, resumption_psk: Vec<(GroupEpoch, ResumptionPskSecret)>, @@ -533,4 +532,11 @@ pub mod store { .map(|(_e, s)| s) } } + + #[cfg(test)] + impl ResumptionPskStore { + pub(crate) fn cursor(&self) -> usize { + self.cursor + } + } } diff --git a/openmls/src/storage.rs b/openmls/src/storage.rs index 585ee4471..bc2ed55ec 100644 --- a/openmls/src/storage.rs +++ b/openmls/src/storage.rs @@ -27,6 +27,9 @@ use crate::{ treesync::{node::encryption_keys::EncryptionKeyPair, EncryptionKey}, }; +#[cfg(test)] +pub mod kat_storage_stability; + /// A convenience trait for the current version of the storage. /// Throughout the code, this one should be used instead of `openmls_traits::storage::StorageProvider`. pub trait StorageProvider: openmls_traits::storage::StorageProvider {} diff --git a/openmls/src/storage/kat_storage_stability.rs b/openmls/src/storage/kat_storage_stability.rs new file mode 100644 index 000000000..29692c105 --- /dev/null +++ b/openmls/src/storage/kat_storage_stability.rs @@ -0,0 +1,726 @@ +//! This modules contains KATs for testing the stability of storage. +//! +//! The KAT generation performs a few group operations (e.g. create, add, set required capabilties) +//! and at each step saves a serialized copy of the provider, along with the group id of the +//! created group. +//! +//! The KAT test reads the serialized providers, loads the [`MlsGroup`] for the given group id, and +//! checks that the group contains the expected information. +//! +//! It contains +//! - a helper function that does the generation of the KAT for a single pair of provider and +//! ciphersuite +//! - a test that runs the KAT generation +//! - a test that runs the KAT generation for all supported providers and ciphersuites and writes +//! the vectors to disk. This test is annotated with #[ignore] and not usually run. +//! - a test that +//! - loads the test data for the given provider and ciphersuite, +//! - deserializes the provider and group id +//! - loads the [`MlsGroup`] +//! - checks that the group matches expectations + +use base64::Engine; +use serde::{Deserialize, Serialize}; + +use std::collections::HashMap; +use std::marker::PhantomData; +use std::{convert::Infallible, io::Write}; + +use openmls_test::openmls_test; +use openmls_traits::OpenMlsProvider as _; + +use crate::{ + prelude::{test_utils::new_credential, *}, + storage::OpenMlsProvider, +}; + +#[derive(Serialize, Deserialize)] +struct KatData { + group_id: GroupId, + storages: Vec, +} + +struct DeterministicRandProvider { + id: String, + ctr: std::sync::atomic::AtomicUsize, + _phantom: PhantomData, +} + +impl DeterministicRandProvider { + fn new(id: &str) -> Self { + Self { + id: id.to_string(), + ctr: std::sync::atomic::AtomicUsize::new(0), + _phantom: PhantomData, + } + } + + fn encode(ctr: usize, id: &str) -> Vec { + ctr.to_be_bytes().into_iter().chain(id.bytes()).collect() + } + + fn block(&self, mut dst: &mut [u8]) -> usize { + let provider = Provider::default(); + let ctr = self.ctr.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + + let block = provider + .crypto() + .hash(HashType::Sha2_256, &Self::encode(ctr, &self.id)) + .unwrap(); + + let write = usize::min(dst.len(), block.len()); + dst.write_all(&block[..write]).unwrap(); + write + } + + fn fill(&self, mut dst: &mut [u8]) { + while !dst.is_empty() { + let written = self.block(dst); + dst = &mut dst[written..]; + } + } +} + +impl openmls_traits::random::OpenMlsRand + for DeterministicRandProvider +{ + type Error = Infallible; + + fn random_array(&self) -> Result<[u8; N], Self::Error> { + let mut arr = [0u8; N]; + self.fill(&mut arr); + Ok(arr) + } + + fn random_vec(&self, len: usize) -> Result, Self::Error> { + let mut arr = vec![0u8; len]; + self.fill(&mut arr); + Ok(arr) + } +} + +struct StorageTestProvider { + rand: DeterministicRandProvider, + storage: openmls_memory_storage::MemoryStorage, + other: Provider, +} + +impl StorageTestProvider { + fn new(id: &str) -> Self { + Self { + rand: DeterministicRandProvider::new(id), + storage: Default::default(), + other: Default::default(), + } + } +} + +impl openmls_traits::OpenMlsProvider + for StorageTestProvider +{ + type CryptoProvider = ::CryptoProvider; + + type RandProvider = DeterministicRandProvider; + + type StorageProvider = openmls_memory_storage::MemoryStorage; + + fn storage(&self) -> &Self::StorageProvider { + &self.storage + } + + fn crypto(&self) -> &Self::CryptoProvider { + self.other.crypto() + } + + fn rand(&self) -> &Self::RandProvider { + &self.rand + } +} + +fn deserialize_provider( + r: &mut R, + name: &str, +) -> StorageTestProvider { + StorageTestProvider:: { + storage: openmls_memory_storage::MemoryStorage::deserialize(r).unwrap(), + rand: DeterministicRandProvider::new(name), + other: Default::default(), + } +} + +fn check_serialized_group_equality( + r: &mut R, + name: &str, + group_id: &GroupId, + group: &MlsGroup, +) { + let provider = deserialize_provider::<_, Provider>(r, name); + let loaded_group = MlsGroup::load(provider.storage(), group_id) + .unwrap() + .unwrap(); + + assert_eq!(group, &loaded_group); +} + +fn helper_generate_kat( + ciphersuite: Ciphersuite, +) -> (GroupId, Vec>) { + let alice_provider = StorageTestProvider::::new("alice"); + let (alice_cwk, alice_signer) = + new_credential(&alice_provider, b"alice", ciphersuite.signature_algorithm()); + + let bob_provider = StorageTestProvider::::new("bob"); + let (bob_cwk, bob_signer) = + new_credential(&bob_provider, b"bob", ciphersuite.signature_algorithm()); + + let charlie_provider = StorageTestProvider::::new("charlie"); + let (charlie_cwk, charlie_signer) = new_credential( + &charlie_provider, + b"charlie", + ciphersuite.signature_algorithm(), + ); + + /////// prepare a group that has some content + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_capabilities(Capabilities::new( + None, + None, + Some(&[ExtensionType::Unknown(0xf042)]), + None, + None, + )) + .build(&alice_provider, &alice_signer, alice_cwk) + .expect("error creating group using builder"); + + let group_id = alice_group.group_id().clone(); + + let mut testdata_new_group = vec![]; + alice_provider + .storage + .serialize(&mut testdata_new_group) + .unwrap(); + + check_serialized_group_equality::<_, Provider>( + &mut testdata_new_group.as_slice(), + "alice", + &group_id, + &alice_group, + ); + + let bob_kpb = KeyPackageBuilder::new() + .leaf_node_capabilities(Capabilities::new( + None, + None, + Some(&[ExtensionType::Unknown(0xf042)]), + None, + None, + )) + .build(ciphersuite, &bob_provider, &bob_signer, bob_cwk.clone()) + .unwrap(); + + alice_group + .add_members( + &alice_provider, + &alice_signer, + &[bob_kpb.key_package().to_owned()], + ) + .unwrap(); + + let mut testdata_pending_add_commit = vec![]; + alice_provider + .storage + .serialize(&mut testdata_pending_add_commit) + .unwrap(); + + check_serialized_group_equality::<_, Provider>( + &mut testdata_pending_add_commit.as_slice(), + "alice", + &group_id, + &alice_group, + ); + + alice_group.merge_pending_commit(&alice_provider).unwrap(); + + let mut testdata_bob_added = vec![]; + alice_provider + .storage + .serialize(&mut testdata_bob_added) + .unwrap(); + + alice_group + .update_group_context_extensions( + &alice_provider, + Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf042)], &[], &[]), + )), + &alice_signer, + ) + .unwrap(); + + let mut testdata_pending_gce_commit = vec![]; + alice_provider + .storage + .serialize(&mut testdata_pending_gce_commit) + .unwrap(); + + check_serialized_group_equality::<_, Provider>( + &mut testdata_pending_gce_commit.as_slice(), + "alice", + &group_id, + &alice_group, + ); + + alice_group.merge_pending_commit(&alice_provider).unwrap(); + + let mut testdata_gce_updated = vec![]; + alice_provider + .storage + .serialize(&mut testdata_gce_updated) + .unwrap(); + + check_serialized_group_equality::<_, Provider>( + &mut testdata_gce_updated.as_slice(), + "alice", + &group_id, + &alice_group, + ); + + //// also serialize with a pending proposal + + let charlie_kpb = KeyPackageBuilder::new() + .leaf_node_capabilities(Capabilities::new( + None, + None, + Some(&[ExtensionType::Unknown(0xf042)]), + None, + None, + )) + .build( + ciphersuite, + &charlie_provider, + &charlie_signer, + charlie_cwk.clone(), + ) + .unwrap(); + + alice_group + .propose_add_member(&alice_provider, &alice_signer, charlie_kpb.key_package()) + .unwrap(); + + let mut testdata_pending_proposal = vec![]; + alice_provider + .storage + .serialize(&mut testdata_pending_proposal) + .unwrap(); + + check_serialized_group_equality::<_, Provider>( + &mut testdata_pending_proposal.as_slice(), + "alice", + &group_id, + &alice_group, + ); + + ( + group_id, + vec![ + testdata_new_group, + testdata_pending_add_commit, + testdata_bob_added, + testdata_pending_gce_commit, + testdata_gce_updated, + testdata_pending_proposal, + ], + ) +} + +#[openmls_test] +fn generate_kats(ciphersuite: Ciphersuite, provider: &Provider) { + helper_generate_kat::(ciphersuite); +} + +#[test] +#[ignore] +#[cfg(not(all( + feature = "libcrux-provider", + not(any( + target_arch = "wasm32", + all(target_arch = "x86", target_os = "windows") + )) +)))] +fn write_kats() { + // setup + let rustcrypto_provider = openmls_rust_crypto::OpenMlsRustCrypto::default(); + + // make a list of all supported ciphersuites + let ciphersuites = rustcrypto_provider.crypto().supported_ciphersuites(); + + // generate the kat data + let kat_data = ciphersuites + .into_iter() + .map(|ciphersuite| { + let (group_id, storages) = + helper_generate_kat::(ciphersuite); + + (ciphersuite, group_id, storages) + }) + .collect(); + + // encode and write to disk + helper_write_kats(kat_data); +} + +#[test] +#[ignore] +#[cfg(all( + feature = "libcrux-provider", + not(any( + target_arch = "wasm32", + all(target_arch = "x86", target_os = "windows") + )) +))] +fn write_kats() { + // setup + let libcrux_provider = openmls_libcrux_crypto::Provider::default(); + let rustcrypto_provider = openmls_rust_crypto::OpenMlsRustCrypto::default(); + + // make a list of all supported ciphersuites + let mut ciphersuites = libcrux_provider.crypto().supported_ciphersuites(); + for ciphersuite in rustcrypto_provider.crypto().supported_ciphersuites() { + if !ciphersuites.contains(&ciphersuite) { + ciphersuites.push(ciphersuite); + } + } + + // generate the kat data + let kat_data = ciphersuites + .into_iter() + .map(|ciphersuite| { + let (group_id, storages) = if libcrux_provider.crypto().supports(ciphersuite).is_ok() { + helper_generate_kat::(ciphersuite) + } else { + helper_generate_kat::(ciphersuite) + }; + + (ciphersuite, group_id, storages) + }) + .collect(); + + // encode and write to disk + helper_write_kats(kat_data); +} + +fn helper_write_kats(kat_data: Vec<(Ciphersuite, GroupId, Vec>)>) { + let base64_engine = base64::engine::GeneralPurpose::new( + &base64::alphabet::URL_SAFE, + base64::engine::GeneralPurposeConfig::new(), + ); + + // test data, keyed by ciphersuite + let mut data = HashMap::new(); + + for (ciphersuite, group_id, storages_bytes) in kat_data { + let storages: Vec = storages_bytes + .iter() + .map(|test| base64_engine.encode(test)) + .collect(); + + data.insert(ciphersuite, KatData { group_id, storages }); + } + // write to file + let mut file = std::fs::File::create("test_vectors/storage-stability-new.json").unwrap(); + serde_json::to_writer(&mut file, &data).unwrap(); +} + +#[openmls_test] +fn test(ciphersuite: Ciphersuite, provider: &Provider) { + // setup + let base64_engine = base64::engine::GeneralPurpose::new( + &base64::alphabet::URL_SAFE, + base64::engine::GeneralPurposeConfig::new(), + ); + + // load data + let mut data: HashMap = { + let file = std::fs::File::open("test_vectors/storage-stability.json").unwrap(); + serde_json::from_reader(file).unwrap() + }; + + let KatData { group_id, storages } = data.remove(&ciphersuite).unwrap(); + + // parse base64-encoded serialized storage + let mut storages = storages + .iter() + .map(|storage| base64_engine.decode(storage).unwrap()); + + //// load group from state right after creation + + let provider_new_group = + deserialize_provider::<_, Provider>(&mut storages.next().unwrap().as_slice(), "alice"); + + let alice_group_new_group = MlsGroup::load(provider_new_group.storage(), &group_id) + .unwrap() + .unwrap(); + + // alice is the sole member + let members = alice_group_new_group.members().collect::>(); + assert_eq!(members.len(), 1); + assert_eq!(members[0].index, LeafNodeIndex::new(0)); + assert_eq!( + members[0].credential, + BasicCredential::new(b"alice".to_vec()).into() + ); + + // there are no pending proposals or commits + assert!(alice_group_new_group.pending_proposals().next().is_none()); + assert!(alice_group_new_group.pending_commit().is_none()); + + // we are in the right epoch + assert_eq!(alice_group_new_group.epoch(), 0.into()); + assert_eq!( + alice_group_new_group + .group() + .resumption_psk_store() + .cursor(), + 1 + ); + + // dropping to prevent accidentally using the wrong provider or group later + drop(alice_group_new_group); + drop(provider_new_group); + + //// load group from state after bob was added, but commit not yet merged + + let provider_pending_add_commit = + deserialize_provider::<_, Provider>(&mut storages.next().unwrap().as_slice(), "alice"); + + let alice_group_pending_add_commit = + MlsGroup::load(provider_pending_add_commit.storage(), &group_id) + .unwrap() + .unwrap(); + + // alice is the sole member + let members = alice_group_pending_add_commit.members().collect::>(); + assert_eq!(members.len(), 1); + assert_eq!(members[0].index, LeafNodeIndex::new(0)); + assert_eq!( + members[0].credential, + BasicCredential::new(b"alice".to_vec()).into() + ); + + // there is one pending add commit + match alice_group_pending_add_commit.pending_commit() { + Some(staged_commit) => { + assert_eq!(staged_commit.queued_proposals().count(), 1); + assert_eq!(staged_commit.add_proposals().count(), 1); + let add_proposal = staged_commit.add_proposals().next().unwrap(); + assert_eq!( + add_proposal + .add_proposal() + .key_package() + .leaf_node() + .credential(), + &BasicCredential::new(b"bob".to_vec()).into() + ); + } + None => panic!("expected a pending commit"), + }; + + // there are no pending proposals + assert_eq!( + alice_group_pending_add_commit.pending_proposals().count(), + 0 + ); + + // we are in the right epoch + assert_eq!(alice_group_pending_add_commit.epoch(), 0.into()); + assert_eq!( + alice_group_pending_add_commit + .group() + .resumption_psk_store() + .cursor(), + 1 + ); + + // dropping to prevent accidentally using the wrong provider or group later + drop(alice_group_pending_add_commit); + drop(provider_pending_add_commit); + + //// load group from state after bob was added + + let provider_bob_added = + deserialize_provider::<_, Provider>(&mut storages.next().unwrap().as_slice(), "alice"); + + let alice_group_bob_added = MlsGroup::load(provider_bob_added.storage(), &group_id) + .unwrap() + .unwrap(); + + // alice and bob are members + let members = alice_group_bob_added.members().collect::>(); + assert_eq!(members.len(), 2); + assert_eq!(members[0].index, LeafNodeIndex::new(0)); + assert_eq!(members[1].index, LeafNodeIndex::new(1)); + assert_eq!( + members[0].credential, + BasicCredential::new(b"alice".to_vec()).into() + ); + assert_eq!( + members[1].credential, + BasicCredential::new(b"bob".to_vec()).into() + ); + + // there are no pending proposals or commits + assert!(alice_group_bob_added.pending_proposals().next().is_none()); + assert!(alice_group_bob_added.pending_commit().is_none()); + + // we are in the right epoch + assert_eq!(alice_group_bob_added.epoch(), 1.into()); + assert_eq!( + alice_group_bob_added + .group() + .resumption_psk_store() + .cursor(), + 2 + ); + + // dropping to prevent accidentally using the wrong provider or group later + drop(alice_group_bob_added); + drop(provider_bob_added); + + //// load group from state after alice updated GCE, but commit is not yet merged + + let provider_pending_gce_commit = + deserialize_provider::<_, Provider>(&mut storages.next().unwrap().as_slice(), "alice"); + + let alice_group_pending_gce_commit = + MlsGroup::load(provider_pending_gce_commit.storage(), &group_id) + .unwrap() + .unwrap(); + + // alice and bob are members + let members = alice_group_pending_gce_commit.members().collect::>(); + assert_eq!(members.len(), 2); + assert_eq!(members[0].index, LeafNodeIndex::new(0)); + assert_eq!(members[1].index, LeafNodeIndex::new(1)); + assert_eq!( + members[0].credential, + BasicCredential::new(b"alice".to_vec()).into() + ); + assert_eq!( + members[1].credential, + BasicCredential::new(b"bob".to_vec()).into() + ); + + // there are no pending proposals + assert!(alice_group_pending_gce_commit + .pending_proposals() + .next() + .is_none()); + + // there is one pending gce commit + match alice_group_pending_gce_commit.pending_commit() { + Some(staged_commit) => { + let proposals: Vec<_> = staged_commit.queued_proposals().collect(); + assert_eq!(proposals.len(), 1); + + let Proposal::GroupContextExtensions(gce_proposal) = &proposals[0].proposal() else { + panic!( + "expected a group context extension proposal, got {:?}", + proposals[0] + ) + }; + + assert_eq!( + gce_proposal.extensions(), + &Extensions::single(Extension::RequiredCapabilities( + RequiredCapabilitiesExtension::new(&[ExtensionType::Unknown(0xf042)], &[], &[]) + )) + ); + } + None => panic!("expected a pending commit"), + }; + + // we are in the right epoch + assert_eq!(alice_group_pending_gce_commit.epoch(), 1.into()); + assert_eq!( + alice_group_pending_gce_commit + .group() + .resumption_psk_store() + .cursor(), + 2 + ); + + // dropping to prevent accidentally using the wrong provider or group later + drop(alice_group_pending_gce_commit); + drop(provider_pending_gce_commit); + + //// load group from state after alice updated GCE + + let provider_gce_updated = + deserialize_provider::<_, Provider>(&mut storages.next().unwrap().as_slice(), "alice"); + + let alice_group_gce_updated = MlsGroup::load(provider_gce_updated.storage(), &group_id) + .unwrap() + .unwrap(); + + // alice and bob are members + let members = alice_group_gce_updated.members().collect::>(); + assert_eq!(members.len(), 2); + assert_eq!(members[0].index, LeafNodeIndex::new(0)); + assert_eq!(members[1].index, LeafNodeIndex::new(1)); + assert_eq!( + members[0].credential, + BasicCredential::new(b"alice".to_vec()).into() + ); + assert_eq!( + members[1].credential, + BasicCredential::new(b"bob".to_vec()).into() + ); + + // there are no pending proposals or commits + assert!(alice_group_gce_updated.pending_proposals().next().is_none()); + assert!(alice_group_gce_updated.pending_commit().is_none()); + + drop(alice_group_gce_updated); + drop(provider_gce_updated); + + //// load group from state after alice creates another proposal + + let provider_pending_proposal = + deserialize_provider::<_, Provider>(&mut storages.next().unwrap().as_slice(), "alice"); + + let alice_group_pending_proposal = + MlsGroup::load(provider_pending_proposal.storage(), &group_id) + .unwrap() + .unwrap(); + + // alice and bob are members + let members = alice_group_pending_proposal.members().collect::>(); + assert_eq!(members.len(), 2); + assert_eq!(members[0].index, LeafNodeIndex::new(0)); + assert_eq!(members[1].index, LeafNodeIndex::new(1)); + assert_eq!( + members[0].credential, + BasicCredential::new(b"alice".to_vec()).into() + ); + assert_eq!( + members[1].credential, + BasicCredential::new(b"bob".to_vec()).into() + ); + + // there is one pending add proposal + let proposals: Vec<_> = alice_group_pending_proposal.pending_proposals().collect(); + assert_eq!(proposals.len(), 1); + match &proposals[0].proposal() { + Proposal::Add(add_proposal) => { + assert_eq!( + add_proposal.key_package().leaf_node().credential(), + &BasicCredential::new(b"charlie".to_vec()).into() + ) + } + other => panic!("expected add proposal, got {:?}", other), + } + + // there is no pending commit + assert!(alice_group_pending_proposal.pending_commit().is_none()); +} diff --git a/openmls/src/treesync/diff.rs b/openmls/src/treesync/diff.rs index e72b40b73..f685136ce 100644 --- a/openmls/src/treesync/diff.rs +++ b/openmls/src/treesync/diff.rs @@ -60,7 +60,7 @@ pub(crate) type UpdatePathResult = ( /// The [`StagedTreeSyncDiff`] can be created from a [`TreeSyncDiff`], examined /// and later merged into a [`TreeSync`] instance. #[derive(Debug, Serialize, Deserialize)] -#[cfg_attr(any(test, feature = "test-utils"), derive(Clone))] +#[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) struct StagedTreeSyncDiff { diff: StagedMlsBinaryTreeDiff, new_tree_hash: Vec, diff --git a/openmls/src/treesync/node/encryption_keys.rs b/openmls/src/treesync/node/encryption_keys.rs index 1055beeea..8a757de47 100644 --- a/openmls/src/treesync/node/encryption_keys.rs +++ b/openmls/src/treesync/node/encryption_keys.rs @@ -74,7 +74,7 @@ impl From> for EncryptionKey { #[derive( Clone, Serialize, Deserialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, )] -#[cfg_attr(test, derive(PartialEq, Eq))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Eq))] pub struct EncryptionPrivateKey { key: HpkePrivateKey, } @@ -146,7 +146,7 @@ impl From for EncryptionKey { #[derive( Debug, Clone, Serialize, Deserialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, )] -#[cfg_attr(test, derive(PartialEq, Eq))] +#[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Eq))] pub(crate) struct EncryptionKeyPair { public_key: EncryptionKey, private_key: EncryptionPrivateKey, diff --git a/openmls/test_vectors/storage-stability.json b/openmls/test_vectors/storage-stability.json new file mode 100644 index 000000000..f5f7e8562 --- /dev/null +++ b/openmls/test_vectors/storage-stability.json @@ -0,0 +1 @@ +{"MLS_128_DHKEMP256_AES128GCM_SHA256_P256":{"group_id":{"value":{"vec":[214,206,173,27,206,87,231,4,172,30,130,210,232,69,61,222]}},"storages":["AAAAAAAAAA0AAAAAAAAAXQAAAAAAAAGaRXBvY2hLZXlQYWlyc3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0wMAABW3sicHVibGljX2tleSI6eyJrZXkiOnsidmVjIjpbNCw2Nyw1LDEyOCwxMDUsMTU0LDEyNyw5MCwyMDgsNTksMjQ3LDE2MiwyMjQsMjM1LDE5MSwxNjAsMjAzLDg3LDEwLDMsMTA2LDUyLDc1LDgyLDQxLDIxMyw1NSw4NywyMTUsMzIsMTM0LDE4MCwxNDQsNSw0MCwxOCwxNDQsMjE5LDg5LDg0LDE4LDE0NCwxOTEsMTc0LDExOSwyMjAsNTksOTEsMTUxLDExMiwxMTgsMTYxLDIyNSw2NywyNDUsMTgzLDEzOCw5MSwyNywyMjcsMTA5LDIyMywyNDAsNzUsMTgxXX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMjcsMjMzLDExNyw3MSwxNTgsNDMsMTE3LDI0LDEyLDEyOCw2MiwxMTEsNjAsMjMyLDE0NywyMTgsMjA3LDg2LDE4NywxNjcsMjQsMTc5LDIxMiwxNTAsODUsMTUwLDIxNCw5MSwxMjEsMTcyLDQ4LDU3XX19fV0AAAAAAAAAXQAAAAAAAACJQ29uZmlybWF0aW9uVGFneyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYWNfdmFsdWUiOnsidmVjIjpbMTkzLDk2LDEwMiwyNDksMTg5LDM4LDgxLDE2MCwyMTksNzYsODEsOTgsMjA3LDE1MywxMDksMTEwLDc0LDkxLDg0LDE4OSwyMzgsMTE3LDg5LDE5NSwyNTQsMTUsMTgyLDE3NCw2MCwxNzUsNjIsNDVdfX0AAAAAAAAAUgAAAAAAAAWMVHJlZXsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsidHJlZSI6eyJsZWFmX25vZGVzIjpbeyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzQsNjcsNSwxMjgsMTA1LDE1NCwxMjcsOTAsMjA4LDU5LDI0NywxNjIsMjI0LDIzNSwxOTEsMTYwLDIwMyw4NywxMCwzLDEwNiw1Miw3NSw4Miw0MSwyMTMsNTUsODcsMjE1LDMyLDEzNCwxODAsMTQ0LDUsNDAsMTgsMTQ0LDIxOSw4OSw4NCwxOCwxNDQsMTkxLDE3NCwxMTksMjIwLDU5LDkxLDE1MSwxMTIsMTE4LDE2MSwyMjUsNjcsMjQ1LDE4MywxMzgsOTEsMjcsMjI3LDEwOSwyMjMsMjQwLDc1LDE4MV19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0LDE1LDEyMSwxMjQsMTc1LDEwMCw4LDI5LDIxNSwyMjMsMzMsMTc3LDIyNyw3NSwxOTAsNDcsMjMsNzMsMzMsMTYsNDYsNzEsMTEsMTM4LDYzLDgyLDE4OCwyMjIsMTAzLDgxLDU3LDUyLDIxNywxMTgsODIsMTAyLDE5OSwxNDAsMTQyLDIzNSwxNDQsMTQ1LDE4OSwxMzYsMzMsMTYxLDI1MywxMjIsMTg5LDgxLDQ4LDE2NSw2NiwxMzIsMTEwLDI0OCw1MSwxOTksMjE1LDE3Nyw4MSwyNDAsMTAyLDE2Myw0Ml19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NDksIm5vdF9hZnRlciI6MTcyNzk1MDE0OX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0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NCw3OCwyMzQsMTgzLDEzNCw0NSwyLDUwLDEwNCwxMCw1MSw5MywxNDcsNTMsMTA4LDIxOSwxNzgsMjExLDE0NiwxODFdfX19LCJzZXJpYWxpemVkX2NvbnRleHQiOlswLDEsMCwyLDE2LDIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyLDAsMCwwLDAsMCwwLDAsMCwzMiwyMDgsMjQ2LDIzOSwyMTQsMTcxLDIwMSwxMTQsMSwxMDUsMjE3LDM3LDIxNCwyMzksMTUsOTAsMTQzLDIwMCw5NiwxNzksMTc3LDE1NiwxODksNzEsMzgsMTQxLDU1LDg5LDE2MywxMjksMjUxLDE5OSw5NSwwLDBdLCJzZWNyZXRfdHJlZSI6eyJvd25faW5kZXgiOjAsImxlYWZfbm9kZXMiOlt7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjEsMTc3LDE3NiwxNTEsMTc1LDEwNSwxOTAsMjE1LDU0LDIwLDE0Nyw2NSw3MywyLDgzLDM5LDIwMSw1Nyw1OCwyMCwxOTIsMTAzLDI3LDY5LDkxLDIwOCwzMCwzOSwxNjIsMTY5LDE2MSw3XX19fV0sInBhcmVudF9ub2RlcyI6W251bGxdLCJoYW5kc2hha2Vfc2VuZGVyX3JhdGNoZXRzIjpbbnVsbF0sImFwcGxpY2F0aW9uX3NlbmRlcl9yYXRjaGV0cyI6W251bGxdLCJzaXplIjoxfX19","AAAAAAAAAA0AAAAAAAAAXQAAAAAAAAGaRXBvY2hLZXlQYWlyc3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0wMAABW3sicHVibGljX2tleSI6eyJrZXkiOnsidmVjIjpbNCw2Nyw1LDEyOCwxMDUsMTU0LDEyNyw5MCwyMDgsNTksMjQ3LDE2MiwyMjQsMjM1LDE5MSwxNjAsMjAzLDg3LDEwLDMsMTA2LDUyLDc1LDgyLDQxLDIxMyw1NSw4NywyMTUsMzIsMTM0LDE4MCwxNDQsNSw0MCwxOCwxNDQsMjE5LDg5LDg0LDE4LDE0NCwxOTEsMTc0LDExOSwyMjAsNTksOTEsMTUxLDExMiwxMTgsMTYxLDIyNSw2NywyNDUsMTgzLDEzOCw5MSwyNywyMjcsMTA5LDIyMywyNDAsNzUsMTgxXX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMjcsMjMzLDExNyw3MSwxNTgsNDMsMTE3LDI0LDEyLDEyOCw2MiwxMTEsNjAsMjMyLDE0NywyMTgsMjA3LDg2LDE4NywxNjcsMjQsMTc5LDIxMiwxNTAsODUsMTUwLDIxNCw5MSwxMjEsMTcyLDQ4LDU3XX19fV0AAAAAAAAAXQAAAAAAAACJQ29uZmlybWF0aW9uVGFneyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYWNfdmFsdWUiOnsidmVjIjpbMTkzLDk2LDEwMiwyNDksMTg5LDM4LDgxLDE2MCwyMTksNzYsODEsOTgsMjA3LDE1MywxMDksMTEwLDc0LDkxLDg0LDE4OSwyMzgsMTE3LDg5LDE5NSwyNTQsMTUsMTgyLDE3NCw2MCwxNzUsNjIsNDVdfX0AAAAAAAAAUgAAAAAAAAWMVHJlZXsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsidHJlZSI6eyJsZWFmX25vZGVzIjpbeyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzQsNjcsNSwxMjgsMTA1LDE1NCwxMjcsOTAsMjA4LDU5LDI0NywxNjIsMjI0LDIzNSwxOTEsMTYwLDIwMyw4NywxMCwzLDEwNiw1Miw3NSw4Miw0MSwyMTMsNTUsODcsMjE1LDMyLDEzNCwxODAsMTQ0LDUsNDAsMTgsMTQ0LDIxOSw4OSw4NCwxOCwxNDQsMTkxLDE3NCwxMTksMjIwLDU5LDkxLDE1MSwxMTIsMTE4LDE2MSwyMjUsNjcsMjQ1LDE4MywxMzgsOTEsMjcsMjI3LDEwOSwyMjMsMjQwLDc1LDE4MV19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0LDE1LDEyMSwxMjQsMTc1LDEwMCw4LDI5LDIxNSwyMjMsMzMsMTc3LDIyNyw3NSwxOTAsNDcsMjMsNzMsMzMsMTYsNDYsNzEsMTEsMTM4LDYzLDgyLDE4OCwyMjIsMTAzLDgxLDU3LDUyLDIxNywxMTgsODIsMTAyLDE5OSwxNDAsMTQyLDIzNSwxNDQsMTQ1LDE4OSwxMzYsMzMsMTYxLDI1MywxMjIsMTg5LDgxLDQ4LDE2NSw2NiwxMzIsMTEwLDI0OCw1MSwxOTksMjE1LDE3Nyw4MSwyNDAsMTAyLDE2Myw0Ml19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NDksIm5vdF9hZnRlciI6MTcyNzk1MDE0OX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0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LDI2LDEwNiwxMjMsMTQ2LDI0NCwxODEsMTY2LDE0MSwxNzcsNjEsMTQ2XX19LCJsZWFmX25vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNCwxMDgsMTU4LDI0NSwyMzgsMTU5LDEyMiwyMywxODgsNzUsMTc0LDIzMSwyMTMsMzksMTgsMTAwLDIxOSwxODYsMTY4LDUxLDMxLDI1MSwyMTgsMzEsMTI5LDIwNyw5MiwyMjgsNTQsNjIsMTEzLDEzMCwyMzUsNTgsNzcsNDEsMjUyLDYwLDEzMCwyNDcsMTk4LDU5LDEyNCw0Myw1MCwxNjksNzEsMTM0LDk4LDExMywyNTMsMTc0LDEzOSw1Niw2OCw5NSwyMjQsOTUsODgsNjcsNzMsMTI0LDE2LDE1NywyMjldfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbNCw2LDIxOCwxODAsMTI2LDY4LDIwMSwyMzIsNDUsMjcsMTAsMSwzOCwxNjYsMTczLDM5LDgzLDI0NSwyNTIsMjQxLDcsMTkwLDE1NiwxNjQsMjA1LDIzMyw3MCwyMzcsMjQ1LDE2MSwxNzgsMTg5LDExMiwxMSw4OSwyMzgsMTE2LDE5NSwyNDQsMTYsMTc4LDE4NCwyMDEsMTUwLDIwMCwyMTUsMTIzLDE2OCwxNDYsNTYsMTU3LDMzLDI4LDEyNCwxMywxNTYsMjgsMjA0LDg4LDMwLDk5LDEzLDU4LDI1MiwxMDBdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5OCwxMTEsOThdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IktleVBhY2thZ2UiOnsibm90X2JlZm9yZSI6MTcyMDY4ODk0OSwibm90X2FmdGVyIjoxNzI3OTUwMTQ5fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbNDgsNjgsMiwzMiw2OCwxNTYsMTUwLDE3NSwyMTQsMzQsMTksODcsMTQ0LDc5LDYwLDE1MCwyMTEsMTM2LDE3MywyMDUsMjUwLDE2LDE5NywxODcsMjA1LDE3NywyMzYsMTI0LDcxLDEwOCwyMDcsMTM3LDg4LDE4MCwxMjUsOTEsMiwzMiwxMiw2MSw5NywyNDksODEsMTg0LDIzNCwxMjcsNjMsMTYyLDU3LDE2MCw5NSw4NSwxNTgsODYsMjI2LDE3LDgsMjQwLDIzMywyMSw4MSwxNzQsMTI3LDEyOSw5NSwxNzAsMTY5LDEzNSw0MSw4OF19fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbNDgsNjgsMiwzMiw4Nyw2Myw0OSwyMSw4NSwxNDYsNjcsMTM1LDEzNSw3NSwxMywxNTMsMTk3LDQ3LDE4NiwxNTgsMTYsMTAyLDc5LDIsMTg0LDU4LDExMywxOTQsODgsMTMsMTMyLDYzLDgsMTIxLDEsMjA4LDIsMzIsMzMsODMsMjQ0LDgyLDE5NywxNjIsMTg1LDg0LDUwLDE0MiwyNTAsMzQsMTY4LDk1LDEzMCwxNzksODUsMTU1LDQyLDExLDAsMTk5LDIwMSwyMjcsMTY2LDE1NSwxODQsNTgsMjQzLDU1LDIzLDE4NV19fX19fSwicHJvcG9zYWxfcmVmZXJlbmNlIjp7InZhbHVlIjp7InZlYyI6WzIwNSw5NSwxLDE4OCw2Miw0NywxMTMsMTg3LDIwMiwxODgsNzIsODUsMTc2LDgzLDg1LDE5NywyMDIsMTQzLDM3LDExNyw2OCwxNjYsMiwxMDEsMTEwLDI2LDI0OSwyNCw2NiwxMjMsODUsMTExXX19LCJzZW5kZXIiOnsiTWVtYmVyIjowfSwicHJvcG9zYWxfb3JfcmVmX3R5cGUiOiJQcm9wb3NhbCJ9XV19LCJzdGF0ZSI6eyJHcm91cE1lbWJlciI6eyJncm91cF9lcG9jaF9zZWNyZXRzIjp7ImluaXRfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNzAsMTkxLDczLDI5LDQ4LDE3NiwyNyw3NiwyMSwxNTQsOTYsNDQsMjM5LDE5MSwxMzcsMjEwLDQ2LDI0NSwxNTMsMTQsMTQzLDUwLDEzOCwxMDUsMTA2LDEzOSwyNDcsMTMxLDE0NywxNTksMTk2LDIyNl19fX0sImV4cG9ydGVyX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTA5LDExMiwxNDcsMzQsOSwyMjUsMTI5LDI0Nyw0NSwzMywxMzQsMTA5LDE3NSwyMzgsMTU1LDczLDE4MCwxMDAsMjA3LDcsMTgzLDEzMiwxMDYsMTI1LDgxLDI0MiwxMjEsMjE4LDE5NCw0MSwyMDMsMTk2XX19fSwiZXBvY2hfYXV0aGVudGljYXRvciI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTc1LDE0NSwxMzEsNTIsMjA1LDEzMCwxNTksMTMzLDI0NSwxOTQsOCw4NCw5NCwxNTAsMywxOTMsMjEyLDQsMTkyLDYsMzksMTEsNDAsNTcsMSwyNDcsMTEwLDQ4LDEwNSw0MSwxMjcsNjFdfX19LCJleHRlcm5hbF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzQzLDEwOSwxMTksMjM2LDE1NCw0NSwyNTAsMTM3LDIxNyw1NCw1NywxNjIsMTEsMTEsMjI4LDIyMywyMzMsOTIsMTU1LDYzLDE4NiwxNzIsMjIsNzAsMTM4LDMwLDcsNzMsMTg0LDIyMSwyMzksMTYzXX19fSwicmVzdW1wdGlvbl9wc2siOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzgzLDc4LDExMiwxODMsMzksMjMsMTc4LDc0LDk0LDUsMjEwLDQwLDI0OCwxMDQsMTc1LDIxMCwxMDMsMzgsMjEyLDUsMTUxLDEzMCwyNCwyMiw3OSwyNywxNzgsNzIsMTE0LDE2MSwxMTYsMjA2XX19fX0sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzU0LDUwLDIxNiwyMDcsMjMsMTI5LDc3LDY2LDQ2LDU0LDIxLDEwNCw1OSwyMTMsMjEwLDIxMSwxNzQsMjExLDI0OSwxMTIsODYsMTIsMTIwLDk3LDE4OSw3Niw3MSwxNyw3LDIxNiwxMjEsMTgxXX19fSwibWVtYmVyc2hpcF9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEyMCwyOCwxMDYsNTEsMTE3LDEyNCwxMjgsMjI3LDE2MCwxMTgsMzQsMjM0LDMsMTA2LDI1MiwxNjcsNjIsMjAxLDEyOSw5LDI0MywzOSwxOTgsMjA4LDIzNiwyMzksNDIsNjAsMjYsMzksMjMsMTIwXX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTM0LDE4NSwxMzgsODMsMTMyLDM0LDc5LDIzNiwxMTYsMTc4LDE0NiwxOTAsMTU1LDE2MiwzMiwxMDksODIsMTkxLDM5LDIyMywyMzIsMjksMjYsNzQsMTQ4LDI1NCwyNiwxODEsMzMsMzMsMTksMTg4XX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsMiwxNiwyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMiwwLDAsMCwwLDAsMCwwLDEsMzIsMTE3LDEyNiwxOTgsMzQsMTkyLDExMiwxMTcsMjMyLDIzMywxNTksNzAsMjQzLDI0OCw0LDE4NCw4LDExLDg0LDUwLDkzLDkxLDQsNzcsMjQ0LDk3LDY4LDAsMSw5MSwxNjAsMTE0LDE4OCwzMiwxOCwyNDgsNzUsNzYsMTEyLDIzNywyMjIsNzksNDcsNyw3OCw1MywxMDQsMTcwLDEyMiwxOTMsMTQ4LDUyLDE1NCwxNDYsMTc4LDQxLDEzNSw4LDM2LDIxMywxNjYsMTI2LDEwNiwyMjAsNzAsMjcsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGwsbnVsbF0sInBhcmVudF9ub2RlcyI6W3sic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6Wzk0LDYyLDkwLDY3LDEwNiw2MywxODAsMjUwLDQ4LDE3Myw3OSwxNzYsMTQ5LDExLDg1LDk1LDIzLDIwNiwyMzYsMTYwLDE4MSwxNiwyMjUsNjUsNzYsMTI2LDQ4LDI0OCw5LDc2LDE3NSwxMTRdfX19LG51bGxdLCJoYW5kc2hha2Vfc2VuZGVyX3JhdGNoZXRzIjpbbnVsbCxudWxsXSwiYXBwbGljYXRpb25fc2VuZGVyX3JhdGNoZXRzIjpbbnVsbCxudWxsXSwic2l6ZSI6M319LCJzdGFnZWRfZGlmZiI6eyJzdGFnZWRfZGlmZiI6eyJkaWZmIjp7ImxlYWZfZGlmZiI6eyIwIjp7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNCwxOTksNTAsMTcsMTMwLDE2MiwyMzAsMTE3LDQ1LDEzNSwyMzcsNiwxMTUsMTQsNDQsMjI4LDE1MSwyMDEsMTYzLDIxNSwyMyw1OCwxMzYsOTUsMjM1LDEzNCwxNjYsNywxNjQsNTIsMTA1LDE0NCwxNjQsMTc3LDg4LDE4MCwxNjIsMjE0LDI0NiwxMSwxODUsOTgsMTM2LDEzOSwyMDQsOTEsMTQ4LDIwLDEyMCwxNTAsMTQxLDI1Miw0NCwxNTQsMjM2LDU0LDk5LDYwLDE1NCw1NiwxOTcsNDUsMTAwLDEzOCwxMDNdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbNCwxNSwxMjEsMTI0LDE3NSwxMDAsOCwyOSwyMTUsMjIzLDMzLDE3NywyMjcsNzUsMTkwLDQ3LDIzLDczLDMzLDE2LDQ2LDcxLDExLDEzOCw2Myw4MiwxODgsMjIyLDEwMyw4MSw1Nyw1MiwyMTcsMTE4LDgyLDEwMiwxOTksMTQwLDE0MiwyMzUsMTQ0LDE0NSwxODksMTM2LDMzLDE2MSwyNTMsMTIyLDE4OSw4MSw0OCwxNjUsNjYsMTMyLDExMCwyNDgsNTEsMTk5LDIxNSwxNzcsODEsMjQwLDEwMiwxNjMsNDJdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5NywxMDgsMTA1LDk5LDEwMV19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiQ29tbWl0Ijp7InZlYyI6WzIxMywxMTgsNDEsMjA2LDIxMSwxMzMsMjgsMTczLDE3MCwyMTIsMTU2LDc3LDEyNSwyNTUsNDcsOTUsMTUwLDE0OSw5MSw2OCw3MCwyNDEsMTUxLDIyOSwxMDIsMTM1LDM2LDc4LDEwMSwyMjYsMjksNzFdfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbNDgsNjksMiwzMiwxMjEsMjMzLDE1NCwxMjYsNjgsMzQsMTA1LDE0Niw5OSwxNjMsMzcsNjQsMTIzLDM1LDQwLDI0MSwxMDIsMjE4LDIxMywxNTEsMjI1LDIxOCwxMjgsMTY3LDE2NywxNzYsMTQ2LDI1MiwyMjAsMjUxLDE1OSwyMDUsMiwzMywwLDIzOSwxMjAsMTEwLDIzMCwyMzQsMTUwLDk0LDI4LDEzOSwyNDEsODEsMTk0LDc2LDE4MCwxNjQsMjAxLDY3LDk2LDEwNSwxNjQsMTk0LDE0LDY5LDk0LDQwLDkyLDY4LDIwMCwxMjksNTcsNzMsNTZdfX19fSwiMSI6eyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzQsMTA4LDE1OCwyNDUsMjM4LDE1OSwxMjIsMjMsMTg4LDc1LDE3NCwyMzEsMjEzLDM5LDE4LDEwMCwyMTksMTg2LDE2OCw1MSwzMSwyNTEsMjE4LDMxLDEyOSwyMDcsOTIsMjI4LDU0LDYyLDExMywxMzAsMjM1LDU4LDc3LDQxLDI1Miw2MCwxMzAsMjQ3LDE5OCw1OSwxMjQsNDMsNTAsMTY5LDcxLDEzNCw5OCwxMTMsMjUzLDE3NCwxMzksNTYsNjgsOTUsMjI0LDk1LDg4LDY3LDczLDEyNCwxNiwxNTcsMjI5XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzQsNiwyMTgsMTgwLDEyNiw2OCwyMDEsMjMyLDQ1LDI3LDEwLDEsMzgsMTY2LDE3MywzOSw4MywyNDUsMjUyLDI0MSw3LDE5MCwxNTYsMTY0LDIwNSwyMzMsNzAsMjM3LDI0NSwxNjEsMTc4LDE4OSwxMTIsMTEsODksMjM4LDExNiwxOTUsMjQ0LDE2LDE3OCwxODQsMjAxLDE1MCwyMDAsMjE1LDEyMywxNjgsMTQ2LDU2LDE1NywzMywyOCwxMjQsMTMsMTU2LDI4LDIwNCw4OCwzMCw5OSwxMyw1OCwyNTIsMTAwXX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NDksIm5vdF9hZnRlciI6MTcyNzk1MDE0OX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzQ4LDY4LDIsMzIsNjgsMTU2LDE1MCwxNzUsMjE0LDM0LDE5LDg3LDE0NCw3OSw2MCwxNTAsMjExLDEzNiwxNzMsMjA1LDI1MCwxNiwxOTcsMTg3LDIwNSwxNzcsMjM2LDEyNCw3MSwxMDgsMjA3LDEzNyw4OCwxODAsMTI1LDkxLDIsMzIsMTIsNjEsOTcsMjQ5LDgxLDE4NCwyMzQsMTI3LDYzLDE2Miw1NywxNjAsOTUsODUsMTU4LDg2LDIyNiwxNyw4LDI0MCwyMzMsMjEsODEsMTc0LDEyNywxMjksOTUsMTcwLDE2OSwxMzUsNDEsODhdfX19fX0sInBhcmVudF9kaWZmIjp7IjAiOnsibm9kZSI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNCw5NywxOTQsMTI5LDEwMSwxNzcsNzgsODYsMzYsMTk4LDE3MSw2MiwxNTUsMTgsMTE1LDE1NCw4Miw3NSwyMDIsNDIsMywxMjEsNzEsOTcsMjcsMjIwLDYwLDE3NywxMjMsMjAxLDE0MCwxLDE3NSw3MCwxODQsMTUyLDYwLDE5OSwxNjEsMjE5LDExMCw3MiwxMSwxMzIsMTkyLDE3NywxMDQsMjE3LDM1LDI0NiwyOCwxMjMsMzQsMjI1LDE2MiwxNzEsMjIsMTI1LDU0LDQyLDQwLDIxMiwyNDMsMjIwLDg4XX19LCJwYXJlbnRfaGFzaCI6eyJ2ZWMiOltdfSwidW5tZXJnZWRfbGVhdmVzIjp7Imxpc3QiOltdfX19fSwic2l6ZSI6M30sIm5ld190cmVlX2hhc2giOlsxMTcsMTI2LDE5OCwzNCwxOTIsMTEyLDExNywyMzIsMjMzLDE1OSw3MCwyNDMsMjQ4LDQsMTg0LDgsMTEsODQsNTAsOTMsOTEsNCw3NywyNDQsOTcsNjgsMCwxLDkxLDE2MCwxMTQsMTg4XX0sImdyb3VwX2NvbnRleHQiOnsicHJvdG9jb2xfdmVyc2lvbiI6Ik1sczEwIiwiY2lwaGVyc3VpdGUiOiJNTFNfMTI4X0RIS0VNUDI1Nl9BRVMxMjhHQ01fU0hBMjU2X1AyNTYiLCJncm91cF9pZCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fSwiZXBvY2giOjEsInRyZWVfaGFzaCI6eyJ2ZWMiOlsxMTcsMTI2LDE5OCwzNCwxOTIsMTEyLDExNywyMzIsMjMzLDE1OSw3MCwyNDMsMjQ4LDQsMTg0LDgsMTEsODQsNTAsOTMsOTEsNCw3NywyNDQsOTcsNjgsMCwxLDkxLDE2MCwxMTQsMTg4XX0sImNvbmZpcm1lZF90cmFuc2NyaXB0X2hhc2giOnsidmVjIjpbMTgsMjQ4LDc1LDc2LDExMiwyMzcsMjIyLDc5LDQ3LDcsNzgsNTMsMTA0LDE3MCwxMjIsMTkzLDE0OCw1MiwxNTQsMTQ2LDE3OCw0MSwxMzUsOCwzNiwyMTMsMTY2LDEyNiwxMDYsMjIwLDcwLDI3XX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJpbnRlcmltX3RyYW5zY3JpcHRfaGFzaCI6Wzc1LDIzMywxMjMsMjcsMTk3LDExOSwxNjYsMjA1LDczLDI1NSw4OSwxNCw1OCwyMTgsMTM2LDgzLDMwLDExOSwzMiwxOTcsMTE2LDIxMCwyNTEsNiwxNzgsMTEzLDIyOCwxOSwxMjAsMTg1LDE5OSw5NF0sImNvbmZpcm1hdGlvbl90YWciOnsibWFjX3ZhbHVlIjp7InZlYyI6WzIxNywyMzEsMTIwLDU3LDczLDEwOCwxNTksMTY4LDIzLDI5LDcwLDQ0LDIxMywyNTIsMTAsMTAwLDIxLDExNiwxNzgsMjEyLDkyLDkyLDI0OCwzMCwyNDUsMjQ4LDExNiwxMDIsMTIyLDIyMywxNSw2MV19fX0sIm5ld19rZXlwYWlycyI6W3sicHVibGljX2tleSI6eyJrZXkiOnsidmVjIjpbNCwxOTksNTAsMTcsMTMwLDE2MiwyMzAsMTE3LDQ1LDEzNSwyMzcsNiwxMTUsMTQsNDQsMjI4LDE1MSwyMDEsMTYzLDIxNSwyMyw1OCwxMzYsOTUsMjM1LDEzNCwxNjYsNywxNjQsNTIsMTA1LDE0NCwxNjQsMTc3LDg4LDE4MCwxNjIsMjE0LDI0NiwxMSwxODUsOTgsMTM2LDEzOSwyMDQsOTEsMTQ4LDIwLDEyMCwxNTAsMTQxLDI1Miw0NCwxNTQsMjM2LDU0LDk5LDYwLDE1NCw1NiwxOTcsNDUsMTAwLDEzOCwxMDNdfX0sInByaXZhdGVfa2V5Ijp7ImtleSI6eyJ2ZWMiOls5NiwxODEsMzUsMTI3LDE4MywxODIsMTk3LDI4LDEyNiwxNTMsNSw3MCw1Nyw4NSwzLDExOSwyMDIsMjE3LDE1MywxNjYsMTU0LDExMywxODEsMTEzLDIxNSwyNDMsMjA3LDE5MSwxMTEsMTY2LDEwNywxNTddfX19LHsicHVibGljX2tleSI6eyJrZXkiOnsidmVjIjpbNCw5NywxOTQsMTI5LDEwMSwxNzcsNzgsODYsMzYsMTk4LDE3MSw2MiwxNTUsMTgsMTE1LDE1NCw4Miw3NSwyMDIsNDIsMywxMjEsNzEsOTcsMjcsMjIwLDYwLDE3NywxMjMsMjAxLDE0MCwxLDE3NSw3MCwxODQsMTUyLDYwLDE5OSwxNjEsMjE5LDExMCw3MiwxMSwxMzIsMTkyLDE3NywxMDQsMjE3LDM1LDI0NiwyOCwxMjMsMzQsMjI1LDE2MiwxNzEsMjIsMTI1LDU0LDQyLDQwLDIxMiwyNDMsMjIwLDg4XX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMTQ3LDUsMjE3LDE0OCwxMyw3Miw3MywxMjksMTk1LDI1NCwyNTMsMTk4LDE3LDIsMjExLDI4LDI1LDc1LDIzNCwyNiwxMTAsMTE3LDE2LDE3NSwxNjAsNDcsNDAsMTA3LDEwMywxNjMsOTMsMTg0XX19fV0sIm5ld19sZWFmX2tleXBhaXJfb3B0aW9uIjpudWxsLCJ1cGRhdGVfcGF0aF9sZWFmX25vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNCwxOTksNTAsMTcsMTMwLDE2MiwyMzAsMTE3LDQ1LDEzNSwyMzcsNiwxMTUsMTQsNDQsMjI4LDE1MSwyMDEsMTYzLDIxNSwyMyw1OCwxMzYsOTUsMjM1LDEzNCwxNjYsNywxNjQsNTIsMTA1LDE0NCwxNjQsMTc3LDg4LDE4MCwxNjIsMjE0LDI0NiwxMSwxODUsOTgsMTM2LDEzOSwyMDQsOTEsMTQ4LDIwLDEyMCwxNTAsMTQxLDI1Miw0NCwxNTQsMjM2LDU0LDk5LDYwLDE1NCw1NiwxOTcsNDUsMTAwLDEzOCwxMDNdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbNCwxNSwxMjEsMTI0LDE3NSwxMDAsOCwyOSwyMTUsMjIzLDMzLDE3NywyMjcsNzUsMTkwLDQ3LDIzLDczLDMzLDE2LDQ2LDcxLDExLDEzOCw2Myw4MiwxODgsMjIyLDEwMyw4MSw1Nyw1MiwyMTcsMTE4LDgyLDEwMiwxOTksMTQwLDE0MiwyMzUsMTQ0LDE0NSwxODksMTM2LDMzLDE2MSwyNTMsMTIyLDE4OSw4MSw0OCwxNjUsNjYsMTMyLDExMCwyNDgsNTEsMTk5LDIxNSwxNzcsODEsMjQwLDEwMiwxNjMsNDJdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5NywxMDgsMTA1LDk5LDEwMV19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiQ29tbWl0Ijp7InZlYyI6WzIxMywxMTgsNDEsMjA2LDIxMSwxMzMsMjgsMTczLDE3MCwyMTIsMTU2LDc3LDEyNSwyNTUsNDcsOTUsMTUwLDE0OSw5MSw2OCw3MCwyNDEsMTUxLDIyOSwxMDIsMTM1LDM2LDc4LDEwMSwyMjYsMjksNzFdfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbNDgsNjksMiwzMiwxMjEsMjMzLDE1NCwxMjYsNjgsMzQsMTA1LDE0Niw5OSwxNjMsMzcsNjQsMTIzLDM1LDQwLDI0MSwxMDIsMjE4LDIxMywxNTEsMjI1LDIxOCwxMjgsMTY3LDE2NywxNzYsMTQ2LDI1MiwyMjAsMjUxLDE1OSwyMDUsMiwzMywwLDIzOSwxMjAsMTEwLDIzMCwyMzQsMTUwLDk0LDI4LDEzOSwyNDEsODEsMTk0LDc2LDE4MCwxNjQsMjAxLDY3LDk2LDEwNSwxNjQsMTk0LDE0LDY5LDk0LDQwLDkyLDY4LDIwMCwxMjksNTcsNzMsNTZdfX19fX19fX0AAAAAAAAAXgAAAAAAAAABT3duTGVhZk5vZGVJbmRleHsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AATAAAAAAAAAAXAAAAAAAAAUMTWVzc2FnZVNlY3JldHN7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7Im1heF9lcG9jaHMiOjAsInBhc3RfZXBvY2hfdHJlZXMiOltdLCJtZXNzYWdlX3NlY3JldHMiOnsic2VuZGVyX2RhdGFfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNiwyNDksMTYsNTMsMjM0LDE0MCw3MSwxNDIsMjMwLDk1LDc0LDI0NywyNDUsMjE3LDE1MSwyMTgsMjIsMTIxLDE0LDIxOCwyNTAsMTA0LDIzMSwxNDEsMTIzLDEwOCwzMywzLDE3MywzMywxMjQsMjM1XX19fSwibWVtYmVyc2hpcF9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE3NCw5OSwyMDYsMjEyLDMyLDQ4LDU2LDIyMSwxMDUsODgsMTk1LDIyNCwxMDUsNTQsMjEwLDIxMCw3NywxMTUsMzUsMTQ1LDE5OSwxNTIsNDgsMTkyLDIyNiwxMzIsMTkzLDExNCwxMTMsNjAsMjQ2LDM1XX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTEsNzgsMTM3LDkxLDE1MywyMCwxODAsMzEsNzksMTI0LDEzNiwyNTUsMTQsNzgsMjM0LDE4MywxMzQsNDUsMiw1MCwxMDQsMTAsNTEsOTMsMTQ3LDUzLDEwOCwyMTksMTc4LDIxMSwxNDYsMTgxXX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsMiwxNiwyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMiwwLDAsMCwwLDAsMCwwLDAsMzIsMjA4LDI0NiwyMzksMjE0LDE3MSwyMDEsMTE0LDEsMTA1LDIxNywzNywyMTQsMjM5LDE1LDkwLDE0MywyMDAsOTYsMTc5LDE3NywxNTYsMTg5LDcxLDM4LDE0MSw1NSw4OSwxNjMsMTI5LDI1MSwxOTksOTUsMCwwXSwic2VjcmV0X3RyZWUiOnsib3duX2luZGV4IjowLCJsZWFmX25vZGVzIjpbbnVsbF0sInBhcmVudF9ub2RlcyI6W251bGxdLCJoYW5kc2hha2Vfc2VuZGVyX3JhdGNoZXRzIjpbeyJFbmNyeXB0aW9uUmF0Y2hldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNzksMTQyLDE2LDE1MSw4Miw1MSwxNzUsMjEwLDM2LDI1MSwyNDEsMTI5LDIzNCwxMTksMTM4LDEzMyw3NCwyMywyMDMsMTU0LDE2MSwxNzIsMTY5LDM4LDkzLDE2LDkwLDg4LDg1LDE5MiwxNDIsMTAxXX19LCJnZW5lcmF0aW9uIjoxfX1dLCJhcHBsaWNhdGlvbl9zZW5kZXJfcmF0Y2hldHMiOlt7IkVuY3J5cHRpb25SYXRjaGV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMyw0MywyMTcsMjQ2LDExNiwxODksMTg4LDU1LDExMSw1OSwxNDMsMjI5LDE4OSw3Miw5NCw4Myw2MywxMzYsODIsODYsMSwxMjEsMjMwLDE2Nyw0MCwyMTEsMjI1LDE4NCwxMzcsMTc5LDY0LDIwMV19fSwiZ2VuZXJhdGlvbiI6MH19XSwic2l6ZSI6MX19fQ==","AAAAAAAAAA0AAAAAAAAAXQAAAAAAAACKQ29uZmlybWF0aW9uVGFneyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYWNfdmFsdWUiOnsidmVjIjpbMjE3LDIzMSwxMjAsNTcsNzMsMTA4LDE1OSwxNjgsMjMsMjksNzAsNDQsMjEzLDI1MiwxMCwxMDAsMjEsMTE2LDE3OCwyMTIsOTIsOTIsMjQ4LDMwLDI0NSwyNDgsMTE2LDEwMiwxMjIsMjIzLDE1LDYxXX19AAAAAAAAAFIAAAAAAAALrFRyZWV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7InRyZWUiOnsibGVhZl9ub2RlcyI6W3sibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDE5OSw1MCwxNywxMzAsMTYyLDIzMCwxMTcsNDUsMTM1LDIzNyw2LDExNSwxNCw0NCwyMjgsMTUxLDIwMSwxNjMsMjE1LDIzLDU4LDEzNiw5NSwyMzUsMTM0LDE2Niw3LDE2NCw1MiwxMDUsMTQ0LDE2NCwxNzcsODgsMTgwLDE2MiwyMTQsMjQ2LDExLDE4NSw5OCwxMzYsMTM5LDIwNCw5MSwxNDgsMjAsMTIwLDE1MCwxNDEsMjUyLDQ0LDE1NCwyMzYsNTQsOTksNjAsMTU0LDU2LDE5Nyw0NSwxMDAsMTM4LDEwM119fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0LDE1LDEyMSwxMjQsMTc1LDEwMCw4LDI5LDIxNSwyMjMsMzMsMTc3LDIyNyw3NSwxOTAsNDcsMjMsNzMsMzMsMTYsNDYsNzEsMTEsMTM4LDYzLDgyLDE4OCwyMjIsMTAzLDgxLDU3LDUyLDIxNywxMTgsODIsMTAyLDE5OSwxNDAsMTQyLDIzNSwxNDQsMTQ1LDE4OSwxMzYsMzMsMTYxLDI1MywxMjIsMTg5LDgxLDQ4LDE2NSw2NiwxMzIsMTEwLDI0OCw1MSwxOTksMjE1LDE3Nyw4MSwyNDAsMTAyLDE2Myw0Ml19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbMjEzLDExOCw0MSwyMDYsMjExLDEzMywyOCwxNzMsMTcwLDIxMiwxNTYsNzcsMTI1LDI1NSw0Nyw5NSwxNTAsMTQ5LDkxLDY4LDcwLDI0MSwxNTEsMjI5LDEwMiwxMzUsMzYsNzgsMTAxLDIyNiwyOSw3MV19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OCw2OSwyLDMyLDEyMSwyMzMsMTU0LDEyNiw2OCwzNCwxMDUsMTQ2LDk5LDE2MywzNyw2NCwxMjMsMzUsNDAsMjQxLDEwMiwyMTgsMjEzLDE1MSwyMjUsMjE4LDEyOCwxNjcsMTY3LDE3NiwxNDYsMjUyLDIyMCwyNTEsMTU5LDIwNSwyLDMzLDAsMjM5LDEyMCwxMTAsMjMwLDIzNCwxNTAsOTQsMjgsMTM5LDI0MSw4MSwxOTQsNzYsMTgwLDE2NCwyMDEsNjcsOTYsMTA1LDE2NCwxOTQsMTQsNjksOTQsNDAsOTIsNjgsMjAwLDEyOSw1Nyw3Myw1Nl19fX19LHsibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDEwOCwxNTgsMjQ1LDIzOCwxNTksMTIyLDIzLDE4OCw3NSwxNzQsMjMxLDIxMywzOSwxOCwxMDAsMjE5LDE4NiwxNjgsNTEsMzEsMjUxLDIxOCwzMSwxMjksMjA3LDkyLDIyOCw1NCw2MiwxMTMsMTMwLDIzNSw1OCw3Nyw0MSwyNTIsNjAsMTMwLDI0NywxOTgsNTksMTI0LDQzLDUwLDE2OSw3MSwxMzQsOTgsMTEzLDI1MywxNzQsMTM5LDU2LDY4LDk1LDIyNCw5NSw4OCw2Nyw3MywxMjQsMTYsMTU3LDIyOV19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0LDYsMjE4LDE4MCwxMjYsNjgsMjAxLDIzMiw0NSwyNywxMCwxLDM4LDE2NiwxNzMsMzksODMsMjQ1LDI1MiwyNDEsNywxOTAsMTU2LDE2NCwyMDUsMjMzLDcwLDIzNywyNDUsMTYxLDE3OCwxODksMTEyLDExLDg5LDIzOCwxMTYsMTk1LDI0NCwxNiwxNzgsMTg0LDIwMSwxNTAsMjAwLDIxNSwxMjMsMTY4LDE0Niw1NiwxNTcsMzMsMjgsMTI0LDEzLDE1NiwyOCwyMDQsODgsMzAsOTksMTMsNTgsMjUyLDEwMF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk4LDExMSw5OF19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTQ5LCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNDl9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OCw2OCwyLDMyLDY4LDE1NiwxNTAsMTc1LDIxNCwzNCwxOSw4NywxNDQsNzksNjAsMTUwLDIxMSwxMzYsMTczLDIwNSwyNTAsMTYsMTk3LDE4NywyMDUsMTc3LDIzNiwxMjQsNzEsMTA4LDIwNywxMzcsODgsMTgwLDEyNSw5MSwyLDMyLDEyLDYx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InNlbmRlcl9kYXRhX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNTQsNTAsMjE2LDIwNywyMywxMjksNzcsNjYsNDYsNTQsMjEsMTA0LDU5LDIxMywyMTAsMjExLDE3NCwyMTEsMjQ5LDExMiw4NiwxMiwxMjAsOTcsMTg5LDc2LDcxLDE3LDcsMjE2LDEyMSwxODFdfX19LCJtZW1iZXJzaGlwX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTIwLDI4LDEwNiw1MSwxMTcsMTI0LDEyOCwyMjcsMTYwLDExOCwzNCwyMzQsMywxMDYsMjUyLDE2Nyw2MiwyMDEsMTI5LDksMjQzLDM5LDE5OCwyMDgsMjM2LDIzOSw0Miw2MCwyNiwzOSwyMywxMjBdfX19LCJjb25maXJtYXRpb25fa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMzQsMTg1LDEzOCw4MywxMzIsMzQsNzksMjM2LDExNiwxNzgsMTQ2LDE5MCwxNTUsMTYyLDMyLDEwOSw4MiwxOTEsMzksMjIzLDIzMiwyOSwyNiw3NCwxNDgsMjU0LDI2LDE4MSwzMywzMywxOSwxODhdfX19LCJzZXJpYWxpemVkX2NvbnRleHQiOlswLDEsMCwyLDE2LDIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyLDAsMCwwLDAsMCwwLDAsMSwzMiwxMTcsMTI2LDE5OCwzNCwxOTIsMTEyLDExNywyMzIsMjMzLDE1OSw3MCwyNDMsMjQ4LDQsMTg0LDgsMTEsODQsNTAsOTMsOTEsNCw3NywyNDQsOTcsNjgsMCwxLDkxLDE2MCwxMTQsMTg4LDMyLDE4LDI0OCw3NSw3NiwxMTIsMjM3LDIyMiw3OSw0Nyw3LDc4LDUzLDEwNCwxNzAsMTIyLDE5MywxNDgsNTIsMTU0LDE0NiwxNzgsNDEsMTM1LDgsMzYsMjEzLDE2NiwxMjYsMTA2LDIyMCw3MCwyNywwXSwic2VjcmV0X3RyZWUiOnsib3duX2luZGV4IjowLCJsZWFmX25vZGVzIjpbbnVsbCxudWxsXSwicGFyZW50X25vZGVzIjpbeyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbOTQsNjIsOTAsNjcsMTA2LDYzLDE4MCwyNTAsNDgsMTczLDc5LDE3NiwxNDksMTEsODUsOTUsMjMsMjA2LDIzNiwxNjAsMTgxLDE2LDIyNSw2NSw3NiwxMjYsNDgsMjQ4LDksNzYsMTc1LDExNF19fX0sbnVsbF0sImhhbmRzaGFrZV9zZW5kZXJfcmF0Y2hldHMiOltudWxsLG51bGxdLCJhcHBsaWNhdGlvbl9zZW5kZXJfcmF0Y2hldHMiOltudWxsLG51bGxdLCJzaXplIjozfX19AAAAAAAAAF0AAAAAAAADOEVwb2NoS2V5UGFpcnN7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19MTAAAVt7InB1YmxpY19rZXkiOnsia2V5Ijp7InZlYyI6WzQsMTk5LDUwLDE3LDEzMCwxNjIsMjMwLDExNyw0NSwxMzUsMjM3LDYsMTE1LDE0LDQ0LDIyOCwxNTEsMjAxLDE2MywyMTUsMjMsNTgsMTM2LDk1LDIzNSwxMzQsMTY2LDcsMTY0LDUyLDEwNSwxNDQsMTY0LDE3Nyw4OCwxODAsMTYyLDIxNCwyNDYsMTEsMTg1LDk4LDEzNiwxMzksMjA0LDkxLDE0OCwyMCwxMjAsMTUwLDE0MSwyNTIsNDQsMTU0LDIzNiw1NCw5OSw2MCwxNTQsNTYsMTk3LDQ1LDEwMCwxMzgsMTAzXX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbOTYsMTgxLDM1LDEyNywxODMsMTgyLDE5NywyOCwxMjYsMTUzLDUsNzAsNTcsODUsMywxMTksMjAyLDIxNywxNTMsMTY2LDE1NCwxMTMsMTgxLDExMywyMTUsMjQzLDIwNywxOTEsMTExLDE2NiwxMDcsMTU3XX19fSx7InB1YmxpY19rZXkiOnsia2V5Ijp7InZlYyI6WzQsOTcsMTk0LDEyOSwxMDEsMTc3LDc4LDg2LDM2LDE5OCwxNzEsNjIsMTU1LDE4LDExNSwxNTQsODIsNzUsMjAyLDQyLDMsMTIxLDcxLDk3LDI3LDIyMCw2MCwxNzcsMTIzLDIwMSwxNDAsMSwxNzUsNzAsMTg0LDE1Miw2MCwxOTksMTYxLDIxOSwxMTAsNzIsMTEsMTMyLDE5MiwxNzcsMTA0LDIxNywzNSwyNDYsMjgsMTIzLDM0LDIyNSwxNjIsMTcxLDIyLDEyNSw1NCw0Miw0MCwyMTIsMjQzLDIyMCw4OF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzE0Nyw1LDIxNywxNDgsMTMsNzIsNzMsMTI5LDE5NSwyNTQsMjUzLDE5OCwxNywyLDIxMSwyOCwyNSw3NSwyMzQsMjYsMTEwLDExNywxNiwxNzUsMTYwLDQ3LDQwLDEwNywxMDMsMTYzLDkzLDE4NF19fX1d","AAAAAAAAAA0AAAAAAAAAXQAAAAAAAACKQ29uZmlybWF0aW9uVGFneyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYWNfdmFsdWUiOnsidmVjIjpbMjE3LDIzMSwxMjAsNTcsNzMsMTA4LDE1OSwxNjgsMjMsMjksNzAsNDQsMjEzLDI1MiwxMCwxMDAsMjEsMTE2LDE3OCwyMTIsOTIsOTIsMjQ4LDMwLDI0NSwyNDgsMTE2LDEwMiwxMjIsMjIzLDE1LDYxXX19AAAAAAAAAFIAAAAAAAALrFRyZWV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7InRyZWUiOnsibGVhZl9ub2RlcyI6W3sibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDE5OSw1MCwxNywxMzAsMTYyLDIzMCwxMTcsNDUsMTM1LDIzNyw2LDExNSwxNCw0NCwyMjgsMTUxLDIwMSwxNjMsMjE1LDIzLDU4LDEzNiw5NSwyMzUsMTM0LDE2Niw3LDE2NCw1MiwxMDUsMTQ0LDE2NCwxNzcsODgsMTgwLDE2MiwyMTQsMjQ2LDExLDE4NSw5OCwxMzYsMTM5LDIwNCw5MSwxNDgsMjAsMTIwLDE1MCwxNDEsMjUyLDQ0LDE1NCwyMzYsNTQsOTksNjAsMTU0LDU2LDE5Nyw0NSwxMDAsMTM4LDEwM119fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0LDE1LDEyMSwxMjQsMTc1LDEwMCw4LDI5LDIxNSwyMjMsMzMsMTc3LDIyNyw3NSwxOTAsNDcsMjMsNzMsMzMsMTYsNDYsNzEsMTEsMTM4LDYzLDgyLDE4OCwyMjIsMTAzLDgxLDU3LDUyLDIxNywxMTgsODIsMTAyLDE5OSwxNDAsMTQyLDIzNSwxNDQsMTQ1LDE4OSwxMzYsMzMsMTYxLDI1MywxMjIsMTg5LDgxLDQ4LDE2NSw2NiwxMzIsMTEwLDI0OCw1MSwxOTksMjE1LDE3Nyw4MSwyNDAsMTAyLDE2Myw0Ml19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbMjEzLDExOCw0MSwyMDYsMjExLDEzMywyOCwxNzMsMTcwLDIxMiwxNTYsNzcsMTI1LDI1NSw0Nyw5NSwxNTAsMTQ5LDkxLDY4LDcwLDI0MSwxNTEsMjI5LDEwMiwxMzUsMzYsNzgsMTAxLDIyNiwyOSw3MV19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OCw2OSwyLDMyLDEyMSwyMzMsMTU0LDEyNiw2OCwzNCwxMDUsMTQ2LDk5LDE2MywzNyw2NCwxMjMsMzUsNDAsMjQxLDEwMiwyMTgsMjEzLDE1MSwyMjUsMjE4LDEyOCwxNjcsMTY3LDE3NiwxNDYsMjUyLDIyMCwyNTEsMTU5LDIwNSwyLDMzLDAsMjM5LDEyMCwxMTAsMjMwLDIzNCwxNTAsOTQsMjgsMTM5LDI0MSw4MSwxOTQsNzYsMTgwLDE2NCwyMDEsNjcsOTYsMTA1LDE2NCwxOTQsMTQsNjksOTQsNDAsOTIsNjgsMjAwLDEyOSw1Nyw3Myw1Nl19fX19LHsibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDEwOCwxNTgsMjQ1LDIzOCwxNTksMTIyLDIzLDE4OCw3NSwxNzQsMjMxLDIxMywzOSwxOCwxMDAsMjE5LDE4NiwxNjgsNTEsMzEsMjUxLDIxOCwzMSwxMjksMjA3LDkyLDIyOCw1NCw2MiwxMTMsMTMwLDIzNSw1OCw3Nyw0MSwyNTIsNjAsMTMwLDI0NywxOTgsNTksMTI0LDQzLDUwLDE2OSw3MSwxMzQsOTgsMTEzLDI1MywxNzQsMTM5LDU2LDY4LDk1LDIyNCw5NSw4OCw2Nyw3MywxMjQsMTYsMTU3LDIyOV19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0LDYsMjE4LDE4MCwxMjYsNjgsMjAxLDIzMiw0NSwyNywxMCwxLDM4LDE2NiwxNzMsMzksODMsMjQ1LDI1MiwyNDEsNywxOTAsMTU2LDE2NCwyMDUsMjMzLDcwLDIzNywyNDUsMTYxLDE3OCwxODksMTEyLDExLDg5LDIzOCwxMTYsMTk1LDI0NCwxNiwxNzgsMTg0LDIwMSwxNTAsMjAwLDIxNSwxMjMsMTY4LDE0Niw1NiwxNTcsMzMsMjgsMTI0LDEzLDE1NiwyOCwyMDQsODgsMzAsOTksMTMsNTgsMjUyLDEwMF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk4LDExMSw5OF19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTQ5LCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNDl9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OCw2OCwyLDMyLDY4LDE1NiwxNTAsMTc1LDIxNCwzNCwxOSw4NywxNDQsNzksNjAsMTUwLDIxMSwxMzYsMTczLDIwNSwyNTAsMTYsMTk3LDE4NywyMDUsMTc3LDIzNiwxMjQsNzEsMTA4LDIwNywxMzcsODgsMTgwLDEyNSw5MSwyLDMyLDEyLDYx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NTEsOTAsMTM2LDIxNCwyMDIsMiwxNSwxNzgsNSwxMzIsOCw1LDU3LDIsNDUsNjcsMTk0LDcwLDgxXX19LHsicHJvcG9zYWwiOnsiR3JvdXBDb250ZXh0RXh0ZW5zaW9ucyI6eyJleHRlbnNpb25zIjp7InVuaXF1ZSI6W3siUmVxdWlyZWRDYXBhYmlsaXRpZXMiOnsiZXh0ZW5zaW9uX3R5cGVzIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbF90eXBlcyI6W10sImNyZWRlbnRpYWxfdHlwZXMiOltdfX1dfX19LCJwcm9wb3NhbF9yZWZlcmVuY2UiOnsidmFsdWUiOnsidmVjIjpbMTQ4LDEwNiwyNSwxMDQsMjA3LDE3Myw1NCw4OCwxNzMsNjAsMTg4LDE4MSwxMiwyNTEsOTAsMTM2LDIxNCwyMDIsMiwxNSwxNzgsNSwxMzIsOCw1LDU3LDIsNDUsNjcsMTk0LDcwLDgxXX19LCJzZW5kZXIiOnsiTWVtYmVyIjowfSwicHJvcG9zYWxfb3JfcmVmX3R5cGUiOiJQcm9wb3NhbCJ9XV19LCJzdGF0ZSI6eyJHcm91cE1lbWJlciI6eyJncm91cF9lcG9jaF9zZWNyZXRzIjp7ImluaXRfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlszOCwxNTQsMjQ1LDE1MiwyMDcsMTQxLDE4MywxODQsNzUsODEsMjMsNDIsMTEyLDM2LDIxMCwxNTUsMjMsMTc3LDExLDE2NiwxMTksMTg5LDE2NywxODEsMjMxLDI0NCwxNjcsNDEsMjQzLDIwMiwyMjAsNzZdfX19LCJleHBvcnRlcl9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzIyNSwxMjIsNjYsNDAsNDYsMTQ2LDIwNiwzNSwxNzAsNjEsMjI4LDIwNiwyMDQsNSwxODgsMTc4LDE3NSwxODAsNDIsOTksOTUsMTcwLDExMCwxNjksMTk4LDU2LDIxMSw4MSw3OSwyNTEsOTAsMTU2XX19fSwiZXBvY2hfYXV0aGVudGljYXRvciI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMjQ0LDIzOCw0OSw3MSwyNywxNjEsMjIzLDQsMjI2LDIwOCwxMzQsMTg2LDI0NCwxODAsMTAyLDExNCw1NSw0OSwxNiwxOTQsMjEsMTA3LDIyNiwyMjQsNTEsMjQ1LDExOSwyNDcsMjM4LDE4NywxNTgsNzZdfX19LCJleHRlcm5hbF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzExNiwxOSwyMzMsMjQyLDI0NSw3OSw2NSwyMjcsMTE1LDcxLDE3LDEwOSwxMTMsMTU0LDI0MiwyMDUsMTExLDUyLDY5LDE5MCwyMzksMTU1LDM0LDExNyw3NCwxMTYsMTAxLDY1LDE2MywxNjUsNjYsNDFdfX19LCJyZXN1bXB0aW9uX3BzayI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTc1LDExMSwyMzUsMTYxLDE0OSw1NSwxNzMsMTMzLDk4LDEwNyw4MSwxMTcsNDMsNTcsMjEsMTgzLDIwOCw2NSwxOTQsMCwxNjQsODMsNDIsMjIwLDQsMjI2LDY2LDEzMywxNDksNjIsMjEsMjA0XX19fX0sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE2MCwyMCwyMzMsMTgxLDI1MCwxOTUsMTkyLDI0LDIxOSwyMTcsMTE1LDIxNywyMjMsMjM1LDgyLDE4MywxNTAsMTQ3LDE3NiwxMTgsODMsMjUyLDc2LDk1LDE5NiwxMDIsMjUzLDIxNSwxOTksNzQsMTc2LDIzMl19fX0sIm1lbWJlcnNoaXBfa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMDEsMTgyLDE3MSwyMywxMTMsMjM5LDYzLDE5LDE0NCwyNDIsOTUsNzYsMzMsMjAwLDc1LDcwLDE1NSwzNSwxNTQsNjEsMTA3LDE5MiwxOSwxMzAsMTQ3LDYzLDI1MiwxNzgsNCwxNzQsMjAwLDEzNF19fX0sImNvbmZpcm1hdGlvbl9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzU0LDY3LDc2LDE4NSw1LDEyNiwxNTIsODIsMTk2LDYwLDE1NSwyMjEsMTc3LDIwLDE2OSwxNjcsMzAsMjA3LDEwNyw0MCw5Nyw4NiwyLDE0NCw5Nyw0LDI0NywyNTMsMjM5LDIxMSwyNDcsMTEwXX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsMiwxNiwyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMiwwLDAsMCwwLDAsMCwwLDIsMzIsMTUwLDIwMSwyMDgsMTgyLDIxNiw0MiwxMTksMTUsMjMsNywxMDAsMTcwLDIxNSwxMTYsODEsMTAxLDI1MCwyMTgsMTExLDQ5LDEyMCwyMTcsNDMsMTcyLDE2MiwyNDcsMjEsMTMsNjAsNTcsMjM0LDE3OSwzMiw4NiwxNTgsMTc4LDk2LDE1NCwxODMsMTEwLDE1Myw5LDcwLDE1NywxOTIsMjQ2LDI3LDQxLDEyNSwyNCwyOCwyOCw1OCwxODksMjQ5LDEwNSwyOCwyNDYsNSwyMjAsMTE5LDEwMSw2OCwxODIsMzMsOCwwLDMsNSwyLDI0MCw2NiwwLDBdLCJzZWNyZXRfdHJlZSI6eyJvd25faW5kZXgiOjAsImxlYWZfbm9kZXMiOltudWxsLG51bGxdLCJwYXJlbnRfbm9kZXMiOlt7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNTksMTcyLDI1MCwyMDYsMTI2LDMxLDQ5LDE3NCw2NSwyMDksMjIxLDcwLDM4LDI0OCw1MCwxMjQsMTQ3LDE1MiwyMjQsODgsMjAwLDczLDIxOSwyMDksMTEyLDE3MiwxMDQsNjIsMTc2LDIxOSw2LDIyMV19fX0sbnVsbF0sImhhbmRzaGFrZV9zZW5kZXJfcmF0Y2hldHMiOltudWxsLG51bGxdLCJhcHBsaWNhdGlvbl9zZW5kZXJfcmF0Y2hldHMiOltudWxsLG51bGxdLCJzaXplIjozfX0sInN0YWdlZF9kaWZmIjp7InN0YWdlZF9kaWZmIjp7ImRpZmYiOnsibGVhZl9kaWZmIjp7IjAiOnsibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDEyOSwxMTYsMTcwLDE5NSwxOCw0NSwyNSwzNCwzNCwxNTAsMTY1LDEyOCwxNjQsMTAxLDIyLDU1LDE2MywyMzAsNCwyMSwxMCwxMzgsMjI0LDIyMSwyNDIsMTMsMjA4LDIxNSw1MywxODAsMjI3LDYsNywxNSwxMDgsMTk1LDIwMywyMjAsODQsMjcsMjEwLDUzLDIwLDE2NSwxMjIsMjEwLDE4NiwwLDM5LDE4LDUzLDIyMSwxMTgsMjExLDY0LDEzMSwxOTYsMTk0LDIyMSw0LDIxOCwxNzAsMzUsMTk3XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzQsMTUsMTIxLDEyNCwxNzUsMTAwLDgsMjksMjE1LDIyMywzMywxNzcsMjI3LDc1LDE5MCw0NywyMyw3MywzMywxNiw0Niw3MSwxMSwxMzgsNjMsODIsMTg4LDIyMiwxMDMsODEsNTcsNTIsMjE3LDExOCw4MiwxMDIsMTk5LDE0MCwxNDIsMjM1LDE0NCwxNDUsMTg5LDEzNiwzMywxNjEsMjUzLDEyMiwxODksODEsNDgsMTY1LDY2LDEzMiwxMTAsMjQ4LDUxLDE5OSwyMTUsMTc3LDgxLDI0MCwxMDIsMTYzLDQyXX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTcsMTA4LDEwNSw5OSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IkNvbW1pdCI6eyJ2ZWMiOls2NSwyMzgsMjQ2LDI0MywxNzMsOSwxMzUsMyw1MywxMzEsMTU1LDE2OSwyNDYsMTk1LDI0OSwyNDEsMjMyLDE3OSwyMTYsODUsMjIxLDIyMCw5LDY5LDgxLDEwOCw2NSwzNywyMjcsMTExLDU3LDcxXX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzQ4LDcwLDIsMzMsMCwxMzYsMTA2LDY0LDI1MSwyMjYsNTgsNjMsMTMsNDcsMzYsMTg3LDEyNCwyNTQsNTUsMTA3LDc0LDExMCwxNjAsMTUzLDE3Nyw2Miw5NywxMjcsMjQ5LDc3LDE5MSwxNDksMTM4LDMxLDQ5LDgyLDE2OCwyLDMzLDAsMTM5LDk1LDE1Miw2NCwxMzksNzEsMTI1LDIyNiwyMzUsMTE2LDIzMSwyNTMsMTAsMzYsMTksNjEsMTE3LDE0MiwxMzYsMywyNDMsMjMzLDc0LDI0OCwxODgsMTI0LDgyLDEyOSwyMTQsMjM4LDEyOCw3Ml19fX19fSwicGFyZW50X2RpZmYiOnsiMCI6eyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDIwOCwzNiwyNTIsMjIwLDE2LDIxMCwyMDEsMTEwLDIzMywzMiwxMzIsMTU4LDI0LDYzLDEyNCwxMzQsMTQ1LDE4Niw3NSw2LDE3OSwyNDIsNDIsMTkyLDQ1LDc0LDE4MywxNjEsNjIsMjQ5LDI0Niw0OSwyMjcsMywyMDIsMTUzLDQ1LDE1MCw1OCwxMDQsMTgyLDI2LDY5LDE5OSw2NCwyMDQsMTY5LDE1NywxMjYsMjUsMTMxLDMwLDcyLDEyLDAsMjYsMTM2LDM4LDE2MCwxNzEsNjIsMjMyLDM3LDIxNV19fSwicGFyZW50X2hhc2giOnsidmVjIjpbXX0sInVubWVyZ2VkX2xlYXZlcyI6eyJsaXN0IjpbXX19fX0sInNpemUiOjN9LCJuZXdfdHJlZV9oYXNoIjpbMTUwLDIwMSwyMDgsMTgyLDIxNiw0MiwxMTksMTUsMjMsNywxMDAsMTcwLDIxNSwxMTYsODEsMTAxLDI1MCwyMTgsMTExLDQ5LDEyMCwyMTcsNDMsMTcyLDE2MiwyNDcsMjEsMTMsNjAsNTcsMjM0LDE3OV19LCJncm91cF9jb250ZXh0Ijp7InByb3RvY29sX3ZlcnNpb24iOiJNbHMxMCIsImNpcGhlcnN1aXRlIjoiTUxTXzEyOF9ESEtFTVAyNTZfQUVTMTI4R0NNX1NIQTI1Nl9QMjU2IiwiZ3JvdXBfaWQiOnsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0sImVwb2NoIjoyLCJ0cmVlX2hhc2giOnsidmVjIjpbMTUwLDIwMSwyMDgsMTgyLDIxNiw0MiwxMTksMTUsMjMsNywxMDAsMTcwLDIxNSwxMTYsODEsMTAxLDI1MCwyMTgsMTExLDQ5LDEyMCwyMTcsNDMsMTcyLDE2MiwyNDcsMjEsMTMsNjAsNTcsMjM0LDE3OV19LCJjb25maXJtZWRfdHJhbnNjcmlwdF9oYXNoIjp7InZlYyI6Wzg2LDE1OCwxNzgsOTYsMTU0LDE4MywxMTAsMTUzLDksNzAsMTU3LDE5MiwyNDYsMjcsNDEsMTI1LDI0LDI4LDI4LDU4LDE4OSwyNDksMTA1LDI4LDI0Niw1LDIyMCwxMTksMTAxLDY4LDE4MiwzM119LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W3siUmVxdWlyZWRDYXBhYmlsaXRpZXMiOnsiZXh0ZW5zaW9uX3R5cGVzIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbF90eXBlcyI6W10sImNyZWRlbnRpYWxfdHlwZXMiOltdfX1dfX0sImludGVyaW1fdHJhbnNjcmlwdF9oYXNoIjpbMTgzLDIwNiw0OSwxODUsMjQ5LDIxNCwxMzMsMTAwLDEwLDUzLDEyMCwxODUsMTcxLDIyNiwyMjEsNzgsMTUsMTY4LDYsNTYsMjE2LDE3MCwyMTQsMjMzLDE0MiwyMTQsMTAsMzQsNDUsMTM1LDIwNCwyMjFdLCJjb25maXJtYXRpb25fdGFnIjp7Im1hY192YWx1ZSI6eyJ2ZWMiOlsxNzUsMTg3LDE2NCwyNywxNDYsNTAsMjE3LDE4OCwyNTUsMTc4LDIwMSwxMzIsMTUsMTM3LDg1LDEzNCwxMTUsMTQ3LDIzMSwxMDksMzYsMjExLDIzNiwyNDksMTE5LDExNywyMjksMTYsMTQyLDIzNywxMDksMjE1XX19fSwibmV3X2tleXBhaXJzIjpbeyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDEyOSwxMTYsMTcwLDE5NSwxOCw0NSwyNSwzNCwzNCwxNTAsMTY1LDEyOCwxNjQsMTAxLDIyLDU1LDE2MywyMzAsNCwyMSwxMCwxMzgsMjI0LDIyMSwyNDIsMTMsMjA4LDIxNSw1MywxODAsMjI3LDYsNywxNSwxMDgsMTk1LDIwMywyMjAsODQsMjcsMjEwLDUzLDIwLDE2NSwxMjIsMjEwLDE4NiwwLDM5LDE4LDUzLDIyMSwxMTgsMjExLDY0LDEzMSwxOTYsMTk0LDIyMSw0LDIxOCwxNzAsMzUsMTk3XX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMjE3LDc1LDM4LDI1NSwyMTgsMTk0LDk4LDg0LDY0LDEsNzksMTkxLDEzMywxODksMTIyLDg4LDYsMjEwLDIyMSwxMyw1LDUzLDY0LDExMiwyOSwxNTMsNTUsNDAsMjA0LDU5LDE4OSw1NV19fX0seyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDIwOCwzNiwyNTIsMjIwLDE2LDIxMCwyMDEsMTEwLDIzMywzMiwxMzIsMTU4LDI0LDYzLDEyNCwxMzQsMTQ1LDE4Niw3NSw2LDE3OSwyNDIsNDIsMTkyLDQ1LDc0LDE4MywxNjEsNjIsMjQ5LDI0Niw0OSwyMjcsMywyMDIsMTUzLDQ1LDE1MCw1OCwxMDQsMTgyLDI2LDY5LDE5OSw2NCwyMDQsMTY5LDE1NywxMjYsMjUsMTMxLDMwLDcyLDEyLDAsMjYsMTM2LDM4LDE2MCwxNzEsNjIsMjMyLDM3LDIxNV19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzI0MCwyMDQsMzIsMjU0LDE2MCw0MCw5LDIxMSw0OSwyMzAsMTE0LDEwMiwyNTUsODAsMjAsMTE5LDQ1LDc2LDIyLDE4OCwxMDgsNjIsODgsMTU0LDI0MywxOSwxMDYsMTg2LDQyLDExNiw1MywyMTldfX19XSwibmV3X2xlYWZfa2V5cGFpcl9vcHRpb24iOm51bGwsInVwZGF0ZV9wYXRoX2xlYWZfbm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDEyOSwxMTYsMTcwLDE5NSwxOCw0NSwyNSwzNCwzNCwxNTAsMTY1LDEyOCwxNjQsMTAxLDIyLDU1LDE2MywyMzAsNCwyMSwxMCwxMzgsMjI0LDIyMSwyNDIsMTMsMjA4LDIxNSw1MywxODAsMjI3LDYsNywxNSwxMDgsMTk1LDIwMywyMjAsODQsMjcsMjEwLDUzLDIwLDE2NSwxMjIsMjEwLDE4NiwwLDM5LDE4LDUzLDIyMSwxMTgsMjExLDY0LDEzMSwxOTYsMTk0LDIyMSw0LDIxOCwxNzAsMzUsMTk3XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzQsMTUsMTIxLDEyNCwxNzUsMTAwLDgsMjksMjE1LDIyMywzMywxNzcsMjI3LDc1LDE5MCw0NywyMyw3MywzMywxNiw0Niw3MSwxMSwxMzgsNjMsODIsMTg4LDIyMiwxMDMsODEsNTcsNTIsMjE3LDExOCw4MiwxMDIsMTk5LDE0MCwxNDIsMjM1LDE0NCwxNDUsMTg5LDEzNiwzMywxNjEsMjUzLDEyMiwxODksODEsNDgsMTY1LDY2LDEzMiwxMTAsMjQ4LDUxLDE5OSwyMTUsMTc3LDgxLDI0MCwxMDIsMTYzLDQyXX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTcsMTA4LDEwNSw5OSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IkNvbW1pdCI6eyJ2ZWMiOls2NSwyMzgsMjQ2LDI0MywxNzMsOSwxMzUsMyw1MywxMzEsMTU1LDE2OSwyNDYsMTk1LDI0OSwyNDEsMjMyLDE3OSwyMTYsODUsMjIxLDIyMCw5LDY5LDgxLDEwOCw2NSwzNywyMjcsMTExLDU3LDcxXX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzQ4LDcwLDIsMzMsMCwxMzYsMTA2LDY0LDI1MSwyMjYsNTgsNjMsMTMsNDcsMzYsMTg3LDEyNCwyNTQsNTUsMTA3LDc0LDExMCwxNjAsMTUzLDE3Nyw2Miw5NywxMjcsMjQ5LDc3LDE5MSwxNDksMTM4LDMxLDQ5LDgyLDE2OCwyLDMzLDAsMTM5LDk1LDE1Miw2NCwxMzksNzEsMTI1LDIyNiwyMzUsMTE2LDIzMSwyNTMsMTAsMzYsMTksNjEsMTE3LDE0MiwxMzYsMywyNDMsMjMzLDc0LDI0OCwxODgsMTI0LDgyLDEyOSwyMTQsMjM4LDEyOCw3Ml19fX19fX19fQAAAAAAAABeAAAAAAAAAAFPd25MZWFmTm9kZUluZGV4eyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABMAAAAAAAAABcAAAAAAAABg9NZXNzYWdlU2VjcmV0c3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X2Vwb2NocyI6MCwicGFzdF9lcG9jaF90cmVlcyI6W10sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzU0LDUwLDIxNiwyMDcsMjMsMTI5LDc3LDY2LDQ2LDU0LDIxLDEwNCw1OSwyMTMsMjEwLDIxMSwxNzQsMjExLDI0OSwxMTIsODYsMTIsMTIwLDk3LDE4OSw3Niw3MSwxNyw3LDIxNiwxMjEsMTgxXX19fSwibWVtYmVyc2hpcF9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEyMCwyOCwxMDYsNTEsMTE3LDEyNCwxMjgsMjI3LDE2MCwxMTgsMzQsMjM0LDMsMTA2LDI1MiwxNjcsNjIsMjAxLDEyOSw5LDI0MywzOSwxOTgsMjA4LDIzNiwyMzksNDIsNjAsMjYsMzksMjMsMTIwXX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTM0LDE4NSwxMzgsODMsMTMyLDM0LDc5LDIzNiwxMTYsMTc4LDE0NiwxOTAsMTU1LDE2MiwzMiwxMDksODIsMTkxLDM5LDIyMywyMzIsMjksMjYsNzQsMTQ4LDI1NCwyNiwxODEsMzMsMzMsMTksMTg4XX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsMiwxNiwyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMiwwLDAsMCwwLDAsMCwwLDEsMzIsMTE3LDEyNiwxOTgsMzQsMTkyLDExMiwxMTcsMjMyLDIzMywxNTksNzAsMjQzLDI0OCw0LDE4NCw4LDExLDg0LDUwLDkzLDkxLDQsNzcsMjQ0LDk3LDY4LDAsMSw5MSwxNjAsMTE0LDE4OCwzMiwxOCwyNDgsNzUsNzYsMTEyLDIzNywyMjIsNzksNDcsNyw3OCw1MywxMDQsMTcwLDEyMiwxOTMsMTQ4LDUyLDE1NCwxNDYsMTc4LDQxLDEzNSw4LDM2LDIxMywxNjYsMTI2LDEwNiwyMjAsNzAsMjcsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGwseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTk0LDE0OCwxNzYsMjE5LDIzMywxODQsNTAsODYsMTU0LDIwNCwyMzAsNTIsMjE5LDcxLDEzNCwxOTIsMCw3OCwxMTcsMTM1LDQ2LDc2LDc2LDQyLDIxNywxOTEsMTY4LDk1LDU1LDEyOCwxMDMsMjMzXX19fV0sInBhcmVudF9ub2RlcyI6W251bGwsbnVsbF0sImhhbmRzaGFrZV9zZW5kZXJfcmF0Y2hldHMiOlt7IkVuY3J5cHRpb25SYXRjaGV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNiwyMTEsMTI0LDE4MywyNCw2LDE0MCwyOCwxODYsMjA5LDIzNywxODUsMTUyLDE2OSwxODUsMjMzLDksMTU0LDExMCwyMjMsMTEsNzYsMjAyLDM5LDQ3LDI2LDE1OCwxNjYsODYsODYsMTI3LDIyMF19fSwiZ2VuZXJhdGlvbiI6MX19LG51bGxdLCJhcHBsaWNhdGlvbl9zZW5kZXJfcmF0Y2hldHMiOlt7IkVuY3J5cHRpb25SYXRjaGV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyNDAsNjIsMywxOTksMTMyLDE1NSwxMzIsMjAsMjIwLDMxLDI0MSwyMzIsMjQ5LDM5LDE0OSw0MSwxMTEsMjM1LDIxOCw3NSwxMSw4OCwyMCwyMDgsODEsMjEzLDIxNiw2MiwyMzksMjIyLDE2MiwyMjVdfX0sImdlbmVyYXRpb24iOjB9fSxudWxsXSwic2l6ZSI6M319fQAAAAAAAABdAAAAAAAAAzhFcG9jaEtleVBhaXJzeyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fTEwAAFbeyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDE5OSw1MCwxNywxMzAsMTYyLDIzMCwxMTcsNDUsMTM1LDIzNyw2LDExNSwxNCw0NCwyMjgsMTUxLDIwMSwxNjMsMjE1LDIzLDU4LDEzNiw5NSwyMzUsMTM0LDE2Niw3LDE2NCw1MiwxMDUsMTQ0LDE2NCwxNzcsODgsMTgwLDE2MiwyMTQsMjQ2LDExLDE4NSw5OCwxMzYsMTM5LDIwNCw5MSwxNDgsMjAsMTIwLDE1MCwxNDEsMjUyLDQ0LDE1NCwyMzYsNTQsOTksNjAsMTU0LDU2LDE5Nyw0NSwxMDAsMTM4LDEwM119fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6Wzk2LDE4MSwzNSwxMjcsMTgzLDE4MiwxOTcsMjgsMTI2LDE1Myw1LDcwLDU3LDg1LDMsMTE5LDIwMiwyMTcsMTUzLDE2NiwxNTQsMTEzLDE4MSwxMTMsMjE1LDI0MywyMDcsMTkxLDExMSwxNjYsMTA3LDE1N119fX0seyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDk3LDE5NCwxMjksMTAxLDE3Nyw3OCw4NiwzNiwxOTgsMTcxLDYyLDE1NSwxOCwxMTUsMTU0LDgyLDc1LDIwMiw0MiwzLDEyMSw3MSw5NywyNywyMjAsNjAsMTc3LDEyMywyMDEsMTQwLDEsMTc1LDcwLDE4NCwxNTIsNjAsMTk5LDE2MSwyMTksMTEwLDcyLDExLDEzMiwxOTIsMTc3LDEwNCwyMTcsMzUsMjQ2LDI4LDEyMywzNCwyMjUsMTYyLDE3MSwyMiwxMjUsNTQsNDIsNDAsMjEyLDI0MywyMjAsODhdfX0sInByaXZhdGVfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNDcsNSwyMTcsMTQ4LDEzLDcyLDczLDEyOSwxOTUsMjU0LDI1MywxOTgsMTcsMiwyMTEsMjgsMjUsNzUsMjM0LDI2LDExMCwxMTcsMTYsMTc1LDE2MCw0Nyw0MCwxMDcsMTAzLDE2Myw5MywxODRdfX19XQ==","AAAAAAAAAA0AAAAAAAAAXQAAAAAAAAMmRXBvY2hLZXlQYWlyc3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0yMAABW3sicHVibGljX2tleSI6eyJrZXkiOnsidmVjIjpbNCwxMjksMTE2LDE3MCwxOTUsMTgsNDUsMjUsMzQsMzQsMTUwLDE2NSwxMjgsMTY0LDEwMSwyMiw1NSwxNjMsMjMwLDQsMjEsMTAsMTM4LDIyNCwyMjEsMjQyLDEzLDIwOCwyMTUsNTMsMTgwLDIyNyw2LDcsMTUsMTA4LDE5NSwyMDMsMjIwLDg0LDI3LDIxMCw1MywyMCwxNjUsMTIyLDIxMCwxODYsMCwzOSwxOCw1MywyMjEsMTE4LDIxMSw2NCwxMzEsMTk2LDE5NCwyMjEsNCwyMTgsMTcwLDM1LDE5N119fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzIxNyw3NSwzOCwyNTUsMjE4LDE5NCw5OCw4NCw2NCwxLDc5LDE5MSwxMzMsMTg5LDEyMiw4OCw2LDIxMCwyMjEsMTMsNSw1Myw2NCwxMTIsMjksMTUzLDU1LDQwLDIwNCw1OSwxODksNTVdfX19LHsicHVibGljX2tleSI6eyJrZXkiOnsidmVjIjpbNCwyMDgsMzYsMjUyLDIyMCwxNiwyMTAsMjAxLDExMCwyMzMsMzIsMTMyLDE1OCwyNCw2MywxMjQsMTM0LDE0NSwxODYsNzUsNiwxNzksMjQyLDQyLDE5Miw0NSw3NCwxODMsMTYxLDYyLDI0OSwyNDYsNDksMjI3LDMsMjAyLDE1Myw0NSwxNTAsNTgsMTA0LDE4MiwyNiw2OSwxOTksNjQsMjA0LDE2OSwxNTcsMTI2LDI1LDEzMSwzMCw3MiwxMiwwLDI2LDEzNiwzOCwxNjAsMTcxLDYyLDIzMiwzNywyMTVdfX0sInByaXZhdGVfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsyNDAsMjA0LDMyLDI1NCwxNjAsNDAsOSwyMTEsNDksMjMwLDExNCwxMDIsMjU1LDgwLDIwLDExOSw0NSw3NiwyMiwxODgsMTA4LDYyLDg4LDE1NCwyNDMsMTksMTA2LDE4Niw0MiwxMTYsNTMsMjE5XX19fV0AAAAAAAAAXQAAAAAAAACRQ29uZmlybWF0aW9uVGFneyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYWNfdmFsdWUiOnsidmVjIjpbMTc1LDE4NywxNjQsMjcsMTQ2LDUwLDIxNywxODgsMjU1LDE3OCwyMDEsMTMyLDE1LDEzNyw4NSwxMzQsMTE1LDE0NywyMzEsMTA5LDM2LDIxMSwyMzYsMjQ5LDExOSwxMTcsMjI5LDE2LDE0MiwyMzcsMTA5LDIxNV19fQAAAAAAAABSAAAAAAAAC6hUcmVleyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJ0cmVlIjp7ImxlYWZfbm9kZXMiOlt7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNCwxMjksMTE2LDE3MCwxOTUsMTgsNDUsMjUsMzQsMzQsMTUwLDE2NSwxMjgsMTY0LDEwMSwyMiw1NSwxNjMsMjMwLDQsMjEsMTAsMTM4LDIyNCwyMjEsMjQyLDEzLDIwOCwyMTUsNTMsMTgwLDIyNyw2LDcsMTUsMTA4LDE5NSwyMDMsMjIwLDg0LDI3LDIxMCw1MywyMCwxNjUsMTIyLDIxMCwxODYsMCwzOSwxOCw1MywyMjEsMTE4LDIxMSw2NCwxMzEsMTk2LDE5NCwyMjEsNCwyMTgsMTcwLDM1LDE5N119fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0LDE1LDEyMSwxMjQsMTc1LDEwMCw4LDI5LDIxNSwyMjMsMzMsMTc3LDIyNyw3NSwxOTAsNDcsMjMsNzMsMzMsMTYsNDYsNzEsMTEsMTM4LDYzLDgyLDE4OCwyMjIsMTAzLDgxLDU3LDUyLDIxNywxMTgsODIsMTAyLDE5OSwxNDAsMTQyLDIzNSwxNDQsMTQ1LDE4OSwxMzYsMzMsMTYxLDI1MywxMjIsMTg5LDgxLDQ4LDE2NSw2NiwxMzIsMTEwLDI0OCw1MSwxOTksMjE1LDE3Nyw4MSwyNDAsMTAyLDE2Myw0Ml19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbNjUsMjM4LDI0NiwyNDMsMTczLDksMTM1LDMsNTMsMTMxLDE1NSwxNjksMjQ2LDE5NSwyNDksMjQxLDIzMiwxNzksMjE2LDg1LDIyMSwyMjAsOSw2OSw4MSwxMDgsNjUsMzcsMjI3LDExMSw1Nyw3MV19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OCw3MCwyLDMzLDAsMTM2LDEwNiw2NCwyNTEsMjI2LDU4LDYzLDEzLDQ3LDM2LDE4NywxMjQsMjU0LDU1LDEwNyw3NCwxMTAsMTYwLDE1MywxNzcsNjIsOTcsMTI3LDI0OSw3NywxOTEsMTQ5LDEzOCwzMSw0OSw4MiwxNjgsMiwzMywwLDEzOSw5NSwxNTIsNjQsMTM5LDcxLDEyNSwyMjYsMjM1LDExNiwyMzEsMjUzLDEwLDM2LDE5LDYxLDExNywxNDIsMTM2LDMsMjQzLDIzMyw3NCwyNDgsMTg4LDEyNCw4MiwxMjksMjE0LDIzOCwxMjgsNzJdfX19fSx7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNCwxMDgsMTU4LDI0NSwyMzgsMTU5LDEyMiwyMywxODgsNzUsMTc0LDIzMSwyMTMsMzksMTgsMTAwLDIxOSwxODYsMTY4LDUxLDMxLDI1MSwyMTgsMzEsMTI5LDIwNyw5MiwyMjgsNTQsNjIsMTEzLDEzMCwyMzUsNTgsNzcsNDEsMjUyLDYwLDEzMCwyNDcsMTk4LDU5LDEyNCw0Myw1MCwxNjksNzEsMTM0LDk4LDExMywyNTMsMTc0LDEzOSw1Niw2OCw5NSwyMjQsOTUsODgsNjcsNzMsMTI0LDE2LDE1NywyMjldfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbNCw2LDIxOCwxODAsMTI2LDY4LDIwMSwyMzIsNDUsMjcsMTAsMSwzOCwxNjYsMTczLDM5LDgzLDI0NSwyNTIsMjQxLDcsMTkwLDE1NiwxNjQsMjA1LDIzMyw3MCwyMzcsMjQ1LDE2MSwxNzgsMTg5LDExMiwxMSw4OSwyMzgsMTE2LDE5NSwyNDQsMTYsMTc4LDE4NCwyMDEsMTUwLDIwMCwyMTUsMTIzLDE2OCwxNDYsNTYsMTU3LDMzLDI4LDEyNCwxMywxNTYsMjgsMjA0LDg4LDMwLDk5LDEzLDU4LDI1MiwxMDBdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5OCwxMTEsOThdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IktleVBhY2thZ2UiOnsibm90X2JlZm9yZSI6MTcyMDY4ODk0OSwibm90X2FmdGVyIjoxNzI3OTUwMTQ5fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbNDgsNjgsMiwzMiw2OCwxNTYsMTUwLDE3NSwyMTQsMzQsMTksODcsMTQ0LDc5LDYwLDE1MCwyMTEsMTM2LDE3MywyMDUsMjUwLDE2LDE5NywxODcsMjA1LDE3NywyMzYsMTI0LDcxLDEwOCwyMDcsMTM3LDg4LDE4MCwxMjUsOTEsMiwzMiwxMiw2MSw5NywyNDksODEsMTg0LDIzNCwxMjcsNjMsMTYyLDU3LDE2MCw5NSw4NSwxNTgsODYsMjI2LDE3LDgsMjQwLDIzMywyMSw4MSwxNzQsMTI3LDEyOSw5NSwxNzAsMTY5LDEzNSw0MSw4OF19fX19XSwicGFyZW50X25vZGVzIjpbeyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDIwOCwzNiwyNTIsMjIwLDE2LDIxMCwyMDEsMTEwLDIzMywzMiwxMzIsMTU4LDI0LDYzLDEyNCwxMzQsMTQ1LDE4Niw3NSw2LDE3OSwyNDIsNDIsMTkyLDQ1LDc0LDE4MywxNjEsNjIsMjQ5LDI0Niw0OSwyMjcsMywyMDIsMTUzLDQ1LDE1MCw1OCwxMDQsMTgyLDI2LDY5LDE5OSw2NCwyMDQsMTY5LDE1NywxMjYsMjUsMTMxLDMwLDcyLDEyLDAsMjYsMTM2LDM4LDE2MCwxNzEsNjIsMjMyLDM3LDIxNV19fSwicGFyZW50X2hhc2giOnsidmVjIjpbXX0sInVubWVyZ2VkX2xlYXZlcyI6eyJsaXN0IjpbXX19fV0sImRlZmF1bHRfbGVhZiI6eyJub2RlIjpudWxsfSwiZGVmYXVsdF9wYXJlbnQiOnsibm9kZSI6bnVsbH19LCJ0cmVlX2hhc2giOlsxNTAsMjAxLDIwOCwxODIsMjE2LDQyLDExOSwxNSwyMyw3LDEwMCwxNzAsMjE1LDExNiw4MSwxMDEsMjUwLDIxOCwxMTEsNDksMTIwLDIxNyw0MywxNzIsMTYyLDI0NywyMSwxMyw2MCw1NywyMzQsMTc5XX0AAAAAAAAAWgAAAAAAAAJbR3JvdXBDb250ZXh0eyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJwcm90b2NvbF92ZXJzaW9uIjoiTWxzMTAiLCJjaXBoZXJzdWl0ZSI6Ik1MU18xMjhfREhLRU1QMjU2X0FFUzEyOEdDTV9TSEEyNTZfUDI1NiIsImdyb3VwX2lkIjp7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19LCJlcG9jaCI6MiwidHJlZV9oYXNoIjp7InZlYyI6WzE1MCwyMDEsMjA4LDE4MiwyMTYsNDIsMTE5LDE1LDIzLDcsMTAwLDE3MCwyMTUsMTE2LDgxLDEwMSwyNTAsMjE4LDExMSw0OSwxMjAsMjE3LDQzLDE3MiwxNjIsMjQ3LDIxLDEzLDYwLDU3LDIzNCwxNzldfSwiY29uZmlybWVkX3RyYW5zY3JpcHRfaGFzaCI6eyJ2ZWMiOls4NiwxNTgsMTc4LDk2LDE1NCwxODMsMTEwLDE1Myw5LDcwLDE1NywxOTIsMjQ2LDI3LDQxLDEyNSwyNCwyOCwyOCw1OCwxODksMjQ5LDEwNSwyOCwyNDYsNSwyMjAsMTE5LDEwMSw2OCwxODIsMzNdfSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOlt7IlJlcXVpcmVkQ2FwYWJpbGl0aWVzIjp7ImV4dGVuc2lvbl90eXBlcyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxfdHlwZXMiOltdLCJjcmVkZW50aWFsX3R5cGVzIjpbXX19XX19AAAAAAAAAGMAAAAAAAAAdkludGVyaW1UcmFuc2NyaXB0SGFzaHsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAVsxODMsMjA2LDQ5LDE4NSwyNDksMjE0LDEzMywxMDAsMTAsNTMsMTIwLDE4NSwxNzEsMjI2LDIyMSw3OCwxNSwxNjgsNiw1NiwyMTYsMTcwLDIxNCwyMzMsMTQyLDIxNCwxMCwzNCw0NSwxMzUsMjA0LDIyMV0AAAAAAAAAWgAAAAAAAAM2RXBvY2hTZWNyZXRzeyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJpbml0X3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMzgsMTU0LDI0NSwxNTIsMjA3LDE0MSwxODMsMTg0LDc1LDgxLDIzLDQyLDExMiwzNiwyMTAsMTU1LDIzLDE3NywxMSwxNjYsMTE5LDE4OSwxNjcsMTgxLDIzMSwyNDQsMTY3LDQxLDI0MywyMDIsMjIwLDc2XX19fSwiZXhwb3J0ZXJfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMjUsMTIyLDY2LDQwLDQ2LDE0NiwyMDYsMzUsMTcwLDYxLDIyOCwyMDYsMjA0LDUsMTg4LDE3OCwxNzUsMTgwLDQyLDk5LDk1LDE3MCwxMTAsMTY5LDE5OCw1NiwyMTEsODEsNzksMjUxLDkwLDE1Nl19fX0sImVwb2NoX2F1dGhlbnRpY2F0b3IiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzI0NCwyMzgsNDksNzEsMjcsMTYxLDIyMyw0LDIyNiwyMDgsMTM0LDE4NiwyNDQsMTgwLDEwMiwxMTQsNTUsNDksMTYsMTk0LDIxLDEwNywyMjYsMjI0LDUxLDI0NSwxMTksMjQ3LDIzOCwxODcsMTU4LDc2XX19fSwiZXh0ZXJuYWxfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMTYsMTksMjMzLDI0MiwyNDUsNzksNjUsMjI3LDExNSw3MSwxNywxMDksMTEzLDE1NCwyNDIsMjA1LDExMSw1Miw2OSwxOTAsMjM5LDE1NSwzNCwxMTcsNzQsMTE2LDEwMSw2NSwxNjMsMTY1LDY2LDQxXX19fSwicmVzdW1wdGlvbl9wc2siOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE3NSwxMTEsMjM1LDE2MSwxNDksNTUsMTczLDEzMyw5OCwxMDcsODEsMTE3LDQzLDU3LDIxLDE4MywyMDgsNjUsMTk0LDAsMTY0LDgzLDQyLDIyMCw0LDIyNiw2NiwxMzMsMTQ5LDYyLDIxLDIwNF19fX19AAAAAAAAAVsAAAAAAAABllNpZ25hdHVyZUtleVBhaXJ7InZhbHVlIjpbNCwxNSwxMjEsMTI0LDE3NSwxMDAsOCwyOSwyMTUsMjIzLDMzLDE3NywyMjcsNzUsMTkwLDQ3LDIzLDczLDMzLDE2LDQ2LDcxLDExLDEzOCw2Myw4MiwxODgsMjIyLDEwMyw4MSw1Nyw1MiwyMTcsMTE4LDgyLDEwMiwxOTksMTQwLDE0MiwyMzUsMTQ0LDE0NSwxODksMTM2LDMzLDE2MSwyNTMsMTIyLDE4OSw4MSw0OCwxNjUsNjYsMTMyLDExMCwyNDgsNTEsMTk5LDIxNSwxNzcsODEsMjQwLDEwMiwxNjMsNDIsODIsMTE3LDExNSwxMTYsNjcsMTE0LDEyMSwxMTIsMTE2LDExMSw4MywxMDUsMTAzLDExMCw5NywxMTYsMTE3LDExNCwxMDEsNzUsMTAxLDEyMSw0LDNdfQABeyJwcml2YXRlIjpbNTIsMTAxLDExMiwxNTEsMTMxLDIzMiwxMTksNSw5NSw3OCw5LDE1LDEzMiwxODIsMTMxLDIyLDIyMSwxNDIsMzEsMzcsMTkyLDE5MSwzNCw0MSwzMiw2LDIxNCwzMSw1MiwyNTAsNCw1NV0sInB1YmxpYyI6WzQsMTUsMTIxLDEyNCwxNzUsMTAwLDgsMjksMjE1LDIyMywzMywxNzcsMjI3LDc1LDE5MCw0NywyMyw3MywzMywxNiw0Niw3MSwxMSwxMzgsNjMsODIsMTg4LDIyMiwxMDMsODEsNTcsNTIsMjE3LDExOCw4MiwxMDIsMTk5LDE0MCwxNDIsMjM1LDE0NCwxNDUsMTg5LDEzNiwzMywxNjEsMjUzLDEyMiwxODksODEsNDgsMTY1LDY2LDEzMiwxMTAsMjQ4LDUxLDE5OSwyMTUsMTc3LDgxLDI0MCwxMDIsMTYzLDQyXSwic2lnbmF0dXJlX3NjaGVtZSI6IkVDRFNBX1NFQ1AyNTZSMV9TSEEyNTYifQAAAAAAAABcAAAAAAAAAAVVc2VSYXRjaGV0VHJlZXsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAWZhbHNlAAAAAAAAAFsAAAAAAAAB6lJlc3VtcHRpb25Qc2t7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7Im1heF9udW1iZXJfb2Zfc2VjcmV0cyI6MzIsInJlc3VtcHRpb25fcHNrIjpbWzAseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbODQsNDUsMjI3LDI4LDY5LDEzLDE0MCw1MCw0NCwxNDksOTQsMiw2NCwzMSwyMTMsNjAsNTUsMTA3LDI0MywxMTQsMTg5LDExLDE5NiwyMTgsMTMsMjQsNTYsMTQ4LDc5LDYsMyw2OV19fX1dLFsxLHsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzgzLDc4LDExMiwxODMsMzksMjMsMTc4LDc0LDk0LDUsMjEwLDQwLDI0OCwxMDQsMTc1LDIxMCwxMDMsMzgsMjEyLDUsMTUxLDEzMCwyNCwyMiw3OSwyNywxNzgsNzIsMTE0LDE2MSwxMTYsMjA2XX19fV0sWzIseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTc1LDExMSwyMzUsMTYxLDE0OSw1NSwxNzMsMTMzLDk4LDEwNyw4MSwxMTcsNDMsNTcsMjEsMTgzLDIwOCw2NSwxOTQsMCwxNjQsODMsNDIsMjIwLDQsMjI2LDY2LDEzMywxNDksNjIsMjEsMjA0XX19fV1dLCJjdXJzb3IiOjN9AAAAAAAAAGAAAAAAAAABFk1sc0dyb3VwSm9pbkNvbmZpZ3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsid2lyZV9mb3JtYXRfcG9saWN5Ijp7Im91dGdvaW5nIjoiQWx3YXlzQ2lwaGVydGV4dCIsImluY29taW5nIjoiQWx3YXlzQ2lwaGVydGV4dCJ9LCJwYWRkaW5nX3NpemUiOjAsIm1heF9wYXN0X2Vwb2NocyI6MCwibnVtYmVyX29mX3Jlc3VtcHRpb25fcHNrcyI6MCwidXNlX3JhdGNoZXRfdHJlZV9leHRlbnNpb24iOmZhbHNlLCJzZW5kZXJfcmF0Y2hldF9jb25maWd1cmF0aW9uIjp7Im91dF9vZl9vcmRlcl90b2xlcmFuY2UiOjUsIm1heGltdW1fZm9yd2FyZF9kaXN0YW5jZSI6MTAwMH19AAAAAAAAAFgAAAAAAAAADUdyb3VwU3RhdGV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAEiT3BlcmF0aW9uYWwiAAAAAAAAAF4AAAAAAAAAAU93bkxlYWZOb2RlSW5kZXh7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAEwAAAAAAAAAFwAAAAAAAAE0E1lc3NhZ2VTZWNyZXRzeyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYXhfZXBvY2hzIjowLCJwYXN0X2Vwb2NoX3RyZWVzIjpbXSwibWVzc2FnZV9zZWNyZXRzIjp7InNlbmRlcl9kYXRhX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTYwLDIwLDIzMywxODEsMjUwLDE5NSwxOTIsMjQsMjE5LDIxNywxMTUsMjE3LDIyMywyMzUsODIsMTgzLDE1MCwxNDcsMTc2LDExOCw4MywyNTIsNzYsOTUsMTk2LDEwMiwyNTMsMjE1LDE5OSw3NCwxNzYsMjMyXX19fSwibWVtYmVyc2hpcF9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEwMSwxODIsMTcxLDIzLDExMywyMzksNjMsMTksMTQ0LDI0Miw5NSw3NiwzMywyMDAsNzUsNzAsMTU1LDM1LDE1NCw2MSwxMDcsMTkyLDE5LDEzMCwxNDcsNjMsMjUyLDE3OCw0LDE3NCwyMDAsMTM0XX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNTQsNjcsNzYsMTg1LDUsMTI2LDE1Miw4MiwxOTYsNjAsMTU1LDIyMSwxNzcsMjAsMTY5LDE2NywzMCwyMDcsMTA3LDQwLDk3LDg2LDIsMTQ0LDk3LDQsMjQ3LDI1MywyMzksMjExLDI0NywxMTBdfX19LCJzZXJpYWxpemVkX2NvbnRleHQiOlswLDEsMCwyLDE2LDIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyLDAsMCwwLDAsMCwwLDAsMiwzMiwxNTAsMjAxLDIwOCwxODIsMjE2LDQyLDExOSwxNSwyMyw3LDEwMCwxNzAsMjE1LDExNiw4MSwxMDEsMjUwLDIxOCwxMTEsNDksMTIwLDIxNyw0MywxNzIsMTYyLDI0NywyMSwxMyw2MCw1NywyMzQsMTc5LDMyLDg2LDE1OCwxNzgsOTYsMTU0LDE4MywxMTAsMTUzLDksNzAsMTU3LDE5MiwyNDYsMjcsNDEsMTI1LDI0LDI4LDI4LDU4LDE4OSwyNDksMTA1LDI4LDI0Niw1LDIyMCwxMTksMTAxLDY4LDE4MiwzMyw4LDAsMyw1LDIsMjQwLDY2LDAsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGwsbnVsbF0sInBhcmVudF9ub2RlcyI6W3sic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE1OSwxNzIsMjUwLDIwNiwxMjYsMzEsNDksMTc0LDY1LDIwOSwyMjEsNzAsMzgsMjQ4LDUwLDEyNCwxNDcsMTUyLDIyNCw4OCwyMDAsNzMsMjE5LDIwOSwxMTIsMTcyLDEwNCw2MiwxNzYsMjE5LDYsMjIxXX19fSxudWxsXSwiaGFuZHNoYWtlX3NlbmRlcl9yYXRjaGV0cyI6W251bGwsbnVsbF0sImFwcGxpY2F0aW9uX3NlbmRlcl9yYXRjaGV0cyI6W251bGwsbnVsbF0sInNpemUiOjN9fX0=","AAAAAAAAAA8AAAAAAAAA5AAAAAAAAAg4UXVldWVkUHJvcG9zYWxbeyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fSx7InZhbHVlIjp7InZlYyI6WzE5NCwyMjEsMjQ5LDYzLDIxMCwyNDksOCwxMSwyNDksMTI5LDMzLDI0NiwxNDgsNDIsMTgwLDIzNCwyMDMsMTAwLDM1LDYzLDEwMCwxMSw0NCwyNDEsMjQsMTExLDY3LDI1NSwyMzUsMjM1LDE4MywwXX19XQABeyJwcm9wb3NhbCI6eyJBZGQiOnsia2V5X3BhY2thZ2UiOnsicGF5bG9hZCI6eyJwcm90b2NvbF92ZXJzaW9uIjoiTWxzMTAiLCJjaXBoZXJzdWl0ZSI6Ik1MU18xMjhfREhLRU1QMjU2X0FFUzEyOEdDTV9TSEEyNTZfUDI1NiIsImluaXRfa2V5Ijp7ImtleSI6eyJ2ZWMiOls0LDg5LDEzMSwxNjIsNTAsMTM2LDk4LDE5Myw2MSw1OCw1MCw2LDIwNiwyMzQsMTYxLDU5LDEyNSwyNDUsMTExLDg5LDE2OCw0OCw1Miw0Nyw1OCwyNTIsOTcsMjI1LDIyOSwzNCwyMzYsODIsMjQ2LDkwLDQ0LDEwNywyNDYsMjQsMjQ0LDEwOSwxMDUsMTI1LDIsMTEzLDE3LDE1MiwxMTQsMTQ4LDE2OCwxMTgsMTA1LDY4LDIwMiwxODksMjM4LDIzMiwxNTgsODEsMzcsMTYyLDY4LDEwMiwyNDksNDUsMjExXX19LCJsZWFmX25vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNCwzOSwxNDMsMjQzLDYzLDM1LDUyLDIxNiw4OSwyMjUsODYsMjA3LDk3LDI0NSwxOCwyMjUsMjQ4LDIzMiwyOCwxMDAsMjQ3LDY0LDYwLDI1MSw2MiwyNDgsMjI2LDIyOCwxNjMsMjAyLDg4LDcwLDcwLDExMCwyNywyNDcsMTQxLDU0LDE0OSwxMTQsNzAsNTQsNjMsMTc2LDIyMSwyMjQsMjQyLDgxLDE5MiwxMDEsODUsMTEsMzcsMTQ1LDYwLDE1MywxODgsMTQyLDg3LDI1LDEwOSwxMjEsMjE5LDE4MywxMDNdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbNCwxOTIsMTAzLDE4LDE2Niw3MCwxMDksMjE2LDcyLDE1NSw0LDE4NywxOTIsMTQ4LDU3LDIzMiwxNTksMTg2LDY2LDYzLDE4Myw2OSwxOSwxNjIsNywxNDgsMTIxLDIzMCwxMzAsMzAsNywyMTAsNzYsMTU3LDIzMCwyNDAsMjI2LDIwMSwxNDksMTI5LDE5MiwxNjQsMjM1LDQ4LDIyOSwxMTMsMjEwLDE4OCwzOSwzNiwxOTIsMTA5LDI1MywxODYsMjAxLDE1OSwxNDcsNzEsNTYsMTczLDI0Niw2LDE4MCwyMDEsMjU0XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTksMTA0LDk3LDExNCwxMDgsMTA1LDEwMV19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTUwLCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNTB9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OCw3MCwyLDMzLDAsMjMzLDE1NywyMzEsNTUsMjM4LDIxOCwxODIsNzAsMTE2LDk1LDIyNywxMzAsMTc5LDIxMCwyMTcsMjI0LDIyMywxOTUsOTgsODcsNjgsMTkxLDE3NiwyMDcsMzcsMTI0LDIyNyw4MywxMTksMTkwLDIyMSw3NSwyLDMzLDAsMjE0LDE3MywxMTYsMzEsNzksMzksNTUsMSwxNTUsMjMyLDcyLDEwNSwxODYsODMsMTI2LDIzNiw1NiwxMzksNTYsOTIsNzUsMTgx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Miw2OSw2MSwyMjJdfX0AAXsidHJlZSI6eyJsZWFmX25vZGVzIjpbeyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzQsMTI5LDExNiwxNzAsMTk1LDE4LDQ1LDI1LDM0LDM0LDE1MCwxNjUsMTI4LDE2NCwxMDEsMjIsNTUsMTYzLDIzMCw0LDIxLDEwLDEzOCwyMjQsMjIxLDI0MiwxMywyMDgsMjE1LDUzLDE4MCwyMjcsNiw3LDE1LDEwOCwxOTUsMjAzLDIyMCw4NCwyNywyMTAsNTMsMjAsMTY1LDEyMiwyMTAsMTg2LDAsMzksMTgsNTMsMjIxLDExOCwyMTEsNjQsMTMxLDE5NiwxOTQsMjIxLDQsMjE4LDE3MCwzNSwxOTddfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbNCwxNSwxMjEsMTI0LDE3NSwxMDAsOCwyOSwyMTUsMjIzLDMzLDE3NywyMjcsNzUsMTkwLDQ3LDIzLDczLDMzLDE2LDQ2LDcxLDExLDEzOCw2Myw4MiwxODgsMjIyLDEwMyw4MSw1Nyw1MiwyMTcsMTE4LDgyLDEwMiwxOTksMTQwLDE0MiwyMzUsMTQ0LDE0NSwxODksMTM2LDMzLDE2MSwyNTMsMTIyLDE4OSw4MSw0OCwxNjUsNjYsMTMyLDExMCwyNDgsNTEsMTk5LDIxNSwxNzcsODEsMjQwLDEwMiwxNjMsNDJdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5NywxMDgsMTA1LDk5LDEwMV19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiQ29tbWl0Ijp7InZlYyI6WzY1LDIzOCwyNDYsMjQzLDE3Myw5LDEzNSwzLDUzLDEzMSwxNTUsMTY5LDI0NiwxOTUsMjQ5LDI0MSwyMzIsMTc5LDIxNiw4NSwyMjEsMjIwLDksNjksODEsMTA4LDY1LDM3LDIyNywxMTEsNTcsNzFdfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbNDgsNzAsMiwzMywwLDEzNiwxMDYsNjQsMjUxLDIyNiw1OCw2MywxMyw0NywzNiwxODcsMTI0LDI1NCw1NSwxMDcsNzQsMTEwLDE2MCwxNTMsMTc3LDYyLDk3LDEyNywyNDksNzcsMTkxLDE0OSwxMzgsMzEsNDksODIsMTY4LDIsMzMsMCwxMzksOTUsMTUyLDY0LDEzOSw3MSwxMjUsMjI2LDIzNSwxMTYsMjMxLDI1MywxMCwzNiwxOSw2MSwxMTcsMTQyLDEzNiwzLDI0MywyMzMsNzQsMjQ4LDE4OCwxMjQsODIsMTI5LDIxNCwyMzgsMTI4LDcyXX19fX0seyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzQsMTA4LDE1OCwyNDUsMjM4LDE1OSwxMjIsMjMsMTg4LDc1LDE3NCwyMzEsMjEzLDM5LDE4LDEwMCwyMTksMTg2LDE2OCw1MSwzMSwyNTEsMjE4LDMxLDEyOSwyMDcsOTIsMjI4LDU0LDYyLDExMywxMzAsMjM1LDU4LDc3LDQxLDI1Miw2MCwxMzAsMjQ3LDE5OCw1OSwxMjQsNDMsNTAsMTY5LDcxLDEzNCw5OCwxMTMsMjUzLDE3NCwxMzksNTYsNjgsOTUsMjI0LDk1LDg4LDY3LDczLDEyNCwxNiwxNTcsMjI5XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzQsNiwyMTgsMTgwLDEyNiw2OCwyMDEsMjMyLDQ1LDI3LDEwLDEsMzgsMTY2LDE3MywzOSw4MywyNDUsMjUyLDI0MSw3LDE5MCwxNTYsMTY0LDIwNSwyMzMsNzAsMjM3LDI0NSwxNjEsMTc4LDE4OSwxMTIsMTEsODksMjM4LDExNiwxOTUsMjQ0LDE2LDE3OCwxODQsMjAxLDE1MCwyMDAsMjE1LDEyMywxNjgsMTQ2LDU2LDE1NywzMywyOCwxMjQsMTMsMTU2LDI4LDIwNCw4OCwzMCw5OSwxMyw1OCwyNTIsMTAwXX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NDksIm5vdF9hZnRlciI6MTcyNzk1MDE0OX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzQ4LDY4LDIsMzIsNjgsMTU2LDE1MCwxNzUsMjE0LDM0LDE5LDg3LDE0NCw3OSw2MCwxNTAsMjExLDEzNiwxNzMsMjA1LDI1MCwxNiwxOTcsMTg3LDIwNSwxNzcsMjM2LDEyNCw3MSwxMDgsMjA3LDEzNyw4OCwxODAsMTI1LDkxLDIsMzIsMTIsNjEsOTcsMjQ5LDgxLDE4NCwyMzQsMTI3LDYzLDE2Miw1NywxNjAsOTUsODUsMTU4LDg2LDIyNiwxNyw4LDI0MCwyMzMsMjEsODEsMTc0LDEyNywxMjksOTUsMTcwLDE2OSwxMzUsNDEsODhdfX19fV0sInBhcmVudF9ub2RlcyI6W3sibm9kZSI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNCwyMDgsMzYsMjUyLDIyMCwxNiwyMTAsMjAxLDExMCwyMzMsMzIsMTMyLDE1OCwyNCw2MywxMjQsMTM0LDE0NSwxODYsNzUsNiwxNzksMjQyLDQyLDE5Miw0NSw3NCwxODMsMTYxLDYyLDI0OSwyNDYsNDksMjI3LDMsMjAyLDE1Myw0NSwxNTAsNTgsMTA0LDE4MiwyNiw2OSwxOTksNjQsMjA0LDE2OSwxNTcsMTI2LDI1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MCwyMDcsMTA3LDQwLDk3LDg2LDIsMTQ0LDk3LDQsMjQ3LDI1MywyMzksMjExLDI0NywxMTBdfX19LCJzZXJpYWxpemVkX2NvbnRleHQiOlswLDEsMCwyLDE2LDIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyLDAsMCwwLDAsMCwwLDAsMiwzMiwxNTAsMjAxLDIwOCwxODIsMjE2LDQyLDExOSwxNSwyMyw3LDEwMCwxNzAsMjE1LDExNiw4MSwxMDEsMjUwLDIxOCwxMTEsNDksMTIwLDIxNyw0MywxNzIsMTYyLDI0NywyMSwxMyw2MCw1NywyMzQsMTc5LDMyLDg2LDE1OCwxNzgsOTYsMTU0LDE4MywxMTAsMTUzLDksNzAsMTU3LDE5MiwyNDYsMjcsNDEsMTI1LDI0LDI4LDI4LDU4LDE4OSwyNDksMTA1LDI4LDI0Niw1LDIyMCwxMTksMTAxLDY4LDE4MiwzMyw4LDAsMyw1LDIsMjQwLDY2LDAsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGwseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTk5LDc3LDIxMiwyNTIsMTcwLDM0LDE0NiwxNTEsMTE1LDEwNCw0MCwzNiwxNCw2MiwxMDIsMTExLDIyNyw2MCw3MSwyMDYsNzUsMjIzLDY4LDE0Niw0MywxNzEsNjMsMTc1LDI0OSwyMzksODYsMTQyXX19fV0sInBhcmVudF9ub2RlcyI6W251bGwsbnVsbF0sImhhbmRzaGFrZV9zZW5kZXJfcmF0Y2hldHMiOlt7IkVuY3J5cHRpb25SYXRjaGV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMywyNTUsMTk3LDE2OSwxMTIsNjksMjI0LDE2MCw4NywzOCwxNDQsODMsNzUsMTE0LDIwNCw0NiwxNDQsMjUyLDUzLDIyMywxMzksMTU0LDU3LDE5OCwyMjIsODIsNzksNTAsMTYzLDEwMSwyNDIsNTNdfX0sImdlbmVyYXRpb24iOjF9fSxudWxsXSwiYXBwbGljYXRpb25fc2VuZGVyX3JhdGNoZXRzIjpbeyJFbmNyeXB0aW9uUmF0Y2hldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMjUyLDExNCwxMDQsODMsMjE2LDE1LDI2LDUwLDExNiwyNDYsOCwxNTMsMTY1LDExNCwxMTEsMTQzLDQ5LDE5NSwxMTMsNjgsMTgyLDExMCw3NiwxNTMsMTc5LDQ3LDEyMSwyMjUsMTg1LDEwMCw3NCwzNF19fSwiZ2VuZXJhdGlvbiI6MH19LG51bGxdLCJzaXplIjozfX19"]},"MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519":{"group_id":{"value":{"vec":[214,206,173,27,206,87,231,4,172,30,130,210,232,69,61,222]}},"storages":["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OTksMTIxLDIwNCw3NiwyMjksODMsMTQ1LDIwNyw1Nyw1NCwyMiwyMjAsMjM3LDI1NCw3MiwyNiwxMDQsMTEyLDc0LDIyMiwyNDcsMjIzLDM4LDg5LDE5MCwxODcsMzhdfX19LCJtZW1iZXJzaGlwX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTIxLDExMSwxMTgsMTI5LDIwNiw3MiwxNCwyMTQsMTI4LDEyNiwxMTgsMjM5LDE0LDE3MywyNTUsMjUzLDEzOCwzOSw5LDEzLDE2NCwxNDUsMTYxLDIzMCwxNTQsMzMsMTcyLDI1MiwyMjcsODksNTgsODBdfX19LCJjb25maXJtYXRpb25fa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOls3NywxMzEsMTQyLDIzMiwxMTUsMCwxMTUsOTAsODAsMjQzLDI1MSwxMiwxMjUsNDcsMTE1LDE0OSwxNjUsMjQzLDY0LDExLDE5OSw5NSwxNTQsMTQsMTYzLDEyNSwxNTcsMjAyLDE2NiwxODQsMTg5LDEwNl19fX0sInNlcmlhbGl6ZWRfY29udGV4dCI6WzAsMSwwLDMsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwwLDMyLDE0NCwyNDQsNjcsNzYsNDMsMTgsMTY5LDEyMSwyNDQsMjA3LDIwOCw4MywyMDAsNDcsMjIyLDE3OCwxOTMsMTU0LDEzNCwyMTgsMTA2LDIzNCwxMDUsMTkwLDEyNCwyMDAsMzksOTEsMTY0LDEzMSwxMjgsMTE2LDAsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W3sic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzI5LDE3NiwzMSwyNDksMTQ1LDIxMSw3MCw1MSw3NCwxMDIsMTE3LDEwNSw2MywxMDgsMTY4LDE2Nyw5OSwxMzUsMTQ5LDksMiwxMjcsODAsOTksMTU1LDE0NywxNjQsMTY5LDEzMywxMTIsMjA0LDY2XX19fV0sInBhcmVudF9ub2RlcyI6W251bGxdLCJoYW5kc2hha2Vfc2VuZGVyX3JhdGNo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LDg5LDIyNCw0OCwxMjYsNzgsMTI0LDE4NiwxNDgsMTU5LDI0NSwxNzUsMTIyLDE1NSwxNiwyMDksMTUzLDIxNiwyMiwxNDEsMTE3LDc1LDk3LDcyLDI0NywzNl19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMTYsMTEwLDE2LDkxLDk5LDQ0LDE3MCwxMjEsMTY0LDEwMiwxMjMsMTIzLDExNCwyMzksNzIsMTk2LDY5LDUsNTgsODcsMjU0LDQ1LDEwNywyMjksMTgyLDQyLDI5LDEzNSw3Niw4MywxOTcsMTk0XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NDksIm5vdF9hZnRlciI6MTcyNzk1MDE0OX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzI4LDE1NCwxOCw1NCwxMjQsMjMxLDI3LDE3MiwxOTIsMTA0LDE4Nyw5OCwxODUsNDQsMSwxNTgsMjA5LDIxMywyNDAsMTc5LDE4NCwyMDQsMTQ0LDEwNywxMjksMjEsMSw4Nyw2OSwxNTcsMTEyLDIwNywyMTQsMjA1LDEzMSwxMzcsNiwzNSwyMTcsMjI5LDE5NSw1NiwxMDksMTYyLDk2LDg5LDE3NiwyOCwxNzYsMjQsMTgsMTc0LDU5LDE0NSwxMzUsMjQ3LDIzMSwxNzAsMzMsMjA5LDQ3LDI1NSw1Myw3XX19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls1LDEzMywyNDUsNDQsMTU0LDE0MiwxNDQsMjQ0LDUsOTIsOTgsMTU3LDI0NSwzLDIwOCwxNzEsMTU0LDQyLDIwOSwxOSwxMDEsMjI4LDI4LDYxLDIyNiwyNDYsMjMxLDE3NCwxODgsMTExLDIyOCw4Nyw3MCwxOTUsMTg2LDE1LDE0MSw2MiwxOTksNDEsMTMsMjMsMjAyLDI0MCw1MCwxNSwxNDgsNTgsMTExLDYwLDIwNSw2MCwyNDEsNDksMzYsMjQzLDYxLDIxNyw1MCwxOTEsMTcxLDI2LDIzOCw1XX19fX19LCJwcm9wb3NhbF9yZWZlcmVuY2UiOnsidmFsdWUiOnsidmVjIjpbMzQsMTY0LDM4LDUyLDI0MSwyMzksMTU0LDg3LDI4LDY2LDc4LDEzOSwxNTksODYsMTg2LDIwNiw0NywyMDQsNDUsMTMyLDE4MiwxMTYsMjksMTIwLDEzOSwxMjYsOTQsMTk4LDEyMSwxLDIzNywxNjZdfX0sInNlbmRlciI6eyJNZW1iZXIiOjB9LCJwcm9wb3NhbF9vcl9yZWZfdHlwZSI6IlByb3Bvc2FsIn1dXX0sInN0YXRlIjp7Ikdyb3VwTWVtYmVyIjp7Imdyb3VwX2Vwb2NoX3NlY3JldHMiOnsiaW5pdF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEyMywxNDUsMjE4LDgxLDE2MCwyMjYsNSwyMzgsMTgzLDIwLDExLDQ5LDIyLDkzLDE5Nyw2NywxNzMsMjI1LDE4OSw2NSw0LDU5LDE5Miw3MywyMjIsMTMwLDIxOSwxODIsMTA5LDE4MywxOTAsMjA1XX19fSwiZXhwb3J0ZXJfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOls0NywyMzIsNDksMTU2LDc3LDE3MywxMzgsMTYyLDI1MywxNzIsMTEzLDI0NiwyMTMsNjEsODIsMjUwLDI3LDksODIsMTM3LDIyNCwxOTUsMjQ1LDIzMywxMDIsNTQsOTIsNzYsOSw1OCwxNDIsMjNdfX19LCJlcG9jaF9hdXRoZW50aWNhdG9yIjp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMzEsNTksMTg0LDY0LDI5LDExNSwxOTksMTYsNDEsMjA4LDUxLDk0LDEwNSwxODAsMTIyLDE3NywyMjEsMTY5LDcyLDE5OSwxMDIsMTMwLDE3NiwyNTIsMzcsMTI0LDIsMTA3LDIzOSwyMCwyNDYsMjhdfX19LCJleHRlcm5hbF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzksMTg0LDE1NiwyNSw3MSw5MSwxMzMsMjEwLDg0LDEyNywxNzMsMjE2LDIwOCwyMTgsNTYsMzcsMjEyLDIyLDIwLDIyMSw1NiwxNiwzNCw3NCwxNzksODcsMjI3LDIzNiw5OCw1NiwxMjYsNTVdfX19LCJyZXN1bXB0aW9uX3BzayI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTUsMjIyLDYsMTU1LDc5LDk1LDE0MCwzMiwxNjcsMTY3LDEzMywyMCwxNTQsNiw0Myw0NSwxNTYsMTk0LDIxLDE1MywxMzIsMTk5LDEyNywxNjMsMTM2LDMsMTgwLDE5NSwwLDkzLDIyLDE3Ml19fX19LCJtZXNzYWdlX3NlY3JldHMiOnsic2VuZGVyX2RhdGFfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMDYsMzYsMTA2LDE2NCwxMDQsMjI3LDE0MywxMzQsMjEwLDEzMCwyMjMsMTQxLDE2MSwxOTMsMTE0LDIzMSwxMjQsMTksMzgsMjQ3LDE4NiwxMjUsMTAxLDE0LDIwMiwxNTYsNTQsMTAsMTMyLDQwLDIxMCwyMzldfX19LCJtZW1iZXJzaGlwX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTI4LDE0Niw4OSwxNDEsOTUsMTQzLDI0OCwxNSw3NCw1MSwxOCwyMjgsODEsMTgxLDIzNyw5OSwyNDQsMTc3LDE3NCwxNzEsMTE0LDk4LDEzNSwzNCwyMzcsMzYsNzEsNDUsMjIwLDgzLDg0LDM3XX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNTQsMjUwLDE3NCwxNjMsMTU0LDIzNiwxOTksMTE5LDE3NCwxMzcsNywyMzMsMjA3LDEzNSwxNDQsMzEsMTM3LDIzNSwyNDMsMTkwLDI0OSwyNiwxMjgsMjI2LDEzMiw4OSwxMjksNzYsMjMwLDE3OCw3Niw4MV19fX0sInNlcmlhbGl6ZWRfY29udGV4dCI6WzAsMSwwLDMsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwxLDMyLDQ3LDIzOCwyNiwyNTEsODQsMTIwLDEzNCw5MywxMSw2LDYyLDIsMTYxLDgsMTUxLDU3LDE0MCwxNTgsMTU2LDEwMCwxODQsMTYxLDg1LDI0NCwyMjEsNzEsMTQ1LDIxOCwxMywxMCwxNTYsOTcsMzIsNCwxMDksMzMsMTg5LDE3OCwxNjAsMTAyLDIxMiwyOSwxODUsMTMzLDEwNCwyMzYsMTE1LDE3LDUxLDE3OSwxNzIsMzcsNDAsMTIwLDE0OCw3NCwyNTMsMjU1LDIyNywxMjcsMjA3LDEzMiw5OSwxMzksMTQ5LDBdLCJzZWNyZXRfdHJlZSI6eyJvd25faW5kZXgiOjAsImxlYWZfbm9kZXMiOltudWxsLG51bGxdLCJwYXJlbnRfbm9kZXMiOlt7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNzYsMzMsMjMsMjQ0LDQ3LDE4MywxODMsODksMjE1LDEwMiw1NSwyMjYsMjA5LDQ1LDY2LDI2LDUzLDEzOSwyMzIsMTIyLDUwLDYxLDE4OCw5MywyNSwxMjAsMTkyLDU1LDE4MCw0MCw3Niw4Nl19fX0sbnVsbF0sImhhbmRzaGFrZV9zZW5kZXJfcmF0Y2hldHMiOltudWxsLG51bGxdLCJhcHBsaWNhdGlvbl9zZW5kZXJfcmF0Y2hldHMiOltudWxsLG51bGxdLCJzaXplIjozfX0sInN0YWdlZF9kaWZmIjp7InN0YWdlZF9kaWZmIjp7ImRpZmYiOnsibGVhZl9kaWZmIjp7IjAiOnsibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNjYsMjM5LDI1LDE5NCwyMTUsMTE3LDk3LDIzOSwwLDIyNCwxOSw1MiwxMjYsMjAyLDEwOSwyNTQsMjMsMTM2LDE1NiwzOSwxNjEsMTMzLDk1LDM4LDM1LDEyMywxMjQsMTM4LDI1LDIzNiw4OCwxMDBdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMTg4LDI5LDI0NywyNDUsODUsMTcxLDE3Niw1NiwxMSw0MSw4OCwxODcsNzEsMjM3LDk4LDEwOCw2LDQwLDE3MiwyMjMsMTI5LDE4OSwxOTIsOTEsMjM2LDI3LDk0LDIyNCwxNzQsMTUyLDExNyw2N119fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbMTM5LDE2OCwyMjcsMTY1LDg0LDM1LDE2NCwxODYsNjcsMjUwLDE4NSw1Myw5Nyw5OCwyMjAsMjI3LDQ1LDc0LDk5LDQxLDM3LDU3LDE3NywxMTgsMjU0LDUxLDM2LDEzMCw2NCwyMiw3Nyw3NF19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjgsMTAxLDE2NCwyNDksMTIxLDcyLDUxLDE2MCw5MSwyMSw5MywxNDgsNiw2MCwxMTAsMjEsNjEsMTc4LDk2LDMwLDIwNywxMTIsMjAxLDIwNiwyNDgsMTg5LDE4OSwxMiw2MywxOCwxMywyMzMsMTAxLDIxNyw4NSw4NCwyMzYsNTEsNDgsNDgsOTIsODIsMTE2LDc2LDE1NywyMzUsMjE4LDE4NCw4OSwzLDY5LDQzLDI0NywxMzcsMTUwLDEyNywxMSw5NiwxNzIsMTMwLDE1Miw5OCw2MiwxXX19fX0sIjEiOnsibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls3MSwxMCw2OSwyMCwyLDkyLDcwLDg5LDIyNCw0OCwxMjYsNzgsMTI0LDE4NiwxNDgsMTU5LDI0NSwxNzUsMTIyLDE1NSwxNiwyMDksMTUzLDIxNiwyMiwxNDEsMTE3LDc1LDk3LDcyLDI0NywzNl19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMTYsMTEwLDE2LDkxLDk5LDQ0LDE3MCwxMjEsMTY0LDEwMiwxMjMsMTIzLDExNCwyMzksNzIsMTk2LDY5LDUsNTgsODcsMjU0LDQ1LDEwNywyMjksMTgyLDQyLDI5LDEzNSw3Niw4MywxOTcsMTk0XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NDksIm5vdF9hZnRlciI6MTcyNzk1MDE0OX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzI4LDE1NCwxOCw1NCwxMjQsMjMxLDI3LDE3MiwxOTIsMTA0LDE4Nyw5OCwxODUsNDQsMSwxNTgsMjA5LDIxMywyNDAsMTc5LDE4NCwyMDQsMTQ0LDEwNywxMjksMjEsMSw4Nyw2OSwxNTcsMTEyLDIwNywyMTQsMjA1LDEzMSwxMzcsNiwzNSwyMTcsMjI5LDE5NSw1NiwxMDksMTYyLDk2LDg5LDE3NiwyOCwxNzYsMjQsMTgsMTc0LDU5LDE0NSwxMzUsMjQ3LDIzMSwxNzAsMzMsMjA5LDQ3LDI1NSw1Myw3XX19fX19LCJwYXJlbnRfZGlmZiI6eyIwIjp7Im5vZGUiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzEzLDE4MSw2MiwxNTksMjcsMjU1LDI0MywyMTEsMjQ3LDUyLDI0NywxMzgsMzcsMjU1LDYwLDMwLDEyNCw1MCw2NCw3OSwyNDEsMjIxLDE1NSwzNSwyMDYsMjI5LDE0MCw3MywxOCwyNDYsMjQ2LDg1XX19LCJwYXJlbnRfaGFzaCI6eyJ2ZWMiOltdfSwidW5tZXJnZWRfbGVhdmVzIjp7Imxpc3QiOltdfX19fSwic2l6ZSI6M30sIm5ld190cmVlX2hhc2giOls0NywyMzgsMjYsMjUxLDg0LDEyMCwxMzQsOTMsMTEsNiw2MiwyLDE2MSw4LDE1MSw1NywxNDAsMTU4LDE1NiwxMDAsMTg0LDE2MSw4NSwyNDQsMjIxLDcxLDE0NSwyMTgsMTMsMTAsMTU2LDk3XX0sImdyb3VwX2NvbnRleHQiOnsicHJvdG9jb2xfdmVyc2lvbiI6Ik1sczEwIiwiY2lwaGVyc3VpdGUiOiJNTFNfMTI4X0RIS0VNWDI1NTE5X0NIQUNIQTIwUE9MWTEzMDVfU0hBMjU2X0VkMjU1MTkiLCJncm91cF9pZCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fSwiZXBvY2giOjEsInRyZWVfaGFzaCI6eyJ2ZWMiOls0NywyMzgsMjYsMjUxLDg0LDEyMCwxMzQsOTMsMTEsNiw2MiwyLDE2MSw4LDE1MSw1NywxNDAsMTU4LDE1NiwxMDAsMTg0LDE2MSw4NSwyNDQsMjIxLDcxLDE0NSwyMTgsMTMsMTAsMTU2LDk3XX0sImNvbmZpcm1lZF90cmFuc2NyaXB0X2hhc2giOnsidmVjIjpbNCwxMDksMzMsMTg5LDE3OCwxNjAsMTAyLDIxMiwyOSwxODUsMTMzLDEwNCwyMzYsMTE1LDE3LDUxLDE3OSwxNzIsMzcsNDAsMTIwLDE0OCw3NCwyNTMsMjU1LDIyNywxMjcsMjA3LDEzMiw5OSwxMzksMTQ5XX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJpbnRlcmltX3RyYW5zY3JpcHRfaGFzaCI6WzE5MSw0NywzNywxNzAsMTkxLDE4NSwxOTgsOTEsMjUzLDIzMywzMSwzNyw5OCw3MSwxMTQsMjA2LDI2LDE4LDU4LDM1LDI0MCw0NCwxODAsNzIsMTcsMTQyLDIwNSwxMjAsMTk0LDE0NSwyMzQsMjQ4XSwiY29uZmlybWF0aW9uX3RhZyI6eyJtYWNfdmFsdWUiOnsidmVjIjpbMTAwLDk5LDIxMyw5MCw1OCwyNTIsMTcsMTE1LDE3MSwyLDEwMSwyNDMsNDQsMjIxLDg2LDE5Niw4Miw1MiwxNjIsMTY3LDExMiw3Myw2LDEzMyw5MCw4MywyNiwxOSwyMjEsMTUsMjA0LDE5MV19fX0sIm5ld19rZXlwYWlycyI6W3sicHVibGljX2tleSI6eyJrZXkiOnsidmVjIjpbMTY2LDIzOSwyNSwxOTQsMjE1LDExNyw5NywyMzksMCwyMjQsMTksNTIsMTI2LDIwMiwxMDksMjU0LDIzLDEzNiwxNTYsMzksMTYxLDEzMyw5NSwzOCwzNSwxMjMsMTI0LDEzOCwyNSwyMzYsODgsMTAwXX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMjQ2LDk4LDEzOCw3LDIsMjIsNDQsMTYxLDE1MSwxMjQsNDksMTMwLDE2OCwxOSwxMzEsNzMsMTQ4LDIzMCwxMTAsNTksMTc4LDk3LDIyLDE2NywyNDIsODUsMjM0LDEwMSw2Myw1OCwyMzksMjQ0XX19fSx7InB1YmxpY19rZXkiOnsia2V5Ijp7InZlYyI6WzEzLDE4MSw2MiwxNTksMjcsMjU1LDI0MywyMTEsMjQ3LDUyLDI0NywxMzgsMzcsMjU1LDYwLDMwLDEyNCw1MCw2NCw3OSwyNDEsMjIxLDE1NSwzNSwyMDYsMjI5LDE0MCw3MywxOCwyNDYsMjQ2LDg1XX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMjcsMjI5LDE0Miw4MywzNCwxNDEsMzAsNjIsMTE1LDEzLDE5MCw3OCw4MSwxNTEsOTgsMTgsMjksNTQsMjE4LDYxLDYwLDIzMCwzMyw3OCwxMzQsMTk5LDExOCw2MiwxOTUsMjQyLDEyNiwyMzNdfX19XSwibmV3X2xlYWZfa2V5cGFpcl9vcHRpb24iOm51bGwsInVwZGF0ZV9wYXRoX2xlYWZfbm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNjYsMjM5LDI1LDE5NCwyMTUsMTE3LDk3LDIzOSwwLDIyNCwxOSw1MiwxMjYsMjAyLDEwOSwyNTQsMjMsMTM2LDE1NiwzOSwxNjEsMTMzLDk1LDM4LDM1LDEyMywxMjQsMTM4LDI1LDIzNiw4OCwxMDBdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMTg4LDI5LDI0NywyNDUsODUsMTcxLDE3Niw1NiwxMSw0MSw4OCwxODcsNzEsMjM3LDk4LDEwOCw2LDQwLDE3MiwyMjMsMTI5LDE4OSwxOTIsOTEsMjM2LDI3LDk0LDIyNCwxNzQsMTUyLDExNyw2N119fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbMTM5LDE2OCwyMjcsMTY1LDg0LDM1LDE2NCwxODYsNjcsMjUwLDE4NSw1Myw5Nyw5OCwyMjAsMjI3LDQ1LDc0LDk5LDQxLDM3LDU3LDE3NywxMTgsMjU0LDUxLDM2LDEzMCw2NCwyMiw3Nyw3NF19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjgsMTAxLDE2NCwyNDksMTIxLDcyLDUxLDE2MCw5MSwyMSw5MywxNDgsNiw2MCwxMTAsMjEsNjEsMTc4LDk2LDMwLDIwNywxMTIsMjAxLDIwNiwyNDgsMTg5LDE4OSwxMiw2MywxOCwxMywyMzMsMTAxLDIxNyw4NSw4NCwyMzYsNTEsNDgsNDgsOTIsODIsMTE2LDc2LDE1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ZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEyMSwxMTEsMTE4LDEyOSwyMDYsNzIsMTQsMjE0LDEyOCwxMjYsMTE4LDIzOSwxNCwxNzMsMjU1LDI1MywxMzgsMzksOSwxMywxNjQsMTQ1LDE2MSwyMzAsMTU0LDMzLDE3MiwyNTIsMjI3LDg5LDU4LDgwXX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNzcsMTMxLDE0MiwyMzIsMTE1LDAsMTE1LDkwLDgwLDI0MywyNTEsMTIsMTI1LDQ3LDExNSwxNDksMTY1LDI0Myw2NCwxMSwxOTksOTUsMTU0LDE0LDE2MywxMjUsMTU3LDIwMiwxNjYsMTg0LDE4OSwxMDZdfX19LCJzZXJpYWxpemVkX2NvbnRleHQiOlswLDEsMCwzLDE2LDIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyLDAsMCwwLDAsMCwwLDAsMCwzMiwxNDQsMjQ0LDY3LDc2LDQzLDE4LDE2OSwxMjEsMjQ0LDIwNywyMDgsODMsMjAwLDQ3LDIyMiwxNzgsMTkzLDE1NCwxMzQsMjE4LDEwNiwyMzQsMTA1LDE5MCwxMjQsMjAwLDM5LDkxLDE2NCwxMzEsMTI4LDExNiwwLDBdLCJzZWNyZXRfdHJlZSI6eyJvd25faW5kZXgiOjAsImxlYWZfbm9kZXMiOltudWxsXSwicGFyZW50X25vZGVzIjpbbnVsbF0sImhhbmRzaGFrZV9zZW5kZXJfcmF0Y2hldHMiOlt7IkVuY3J5cHRpb25SYXRjaGV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlszNywxODEsMTczLDAsMzcsMTU2LDY1LDk1LDE0NSwxNiwxMTQsMTM4LDExNiwyMzEsMzksMTg5LDE0MywzMiw4LDExLDEyLDExNywxNDIsMTYyLDExOCw1MCwxNzQsOTgsMTQyLDEyLDIwNSwxNDNdfX0sImdlbmVyYXRpb24iOjF9fV0sImFwcGxpY2F0aW9uX3NlbmRlcl9yYXRjaGV0cyI6W3siRW5jcnlwdGlvblJhdGNoZXQiOnsic2VjcmV0Ijp7InZh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MTAwXX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzE4OCwyOSwyNDcsMjQ1LDg1LDE3MSwxNzYsNTYsMTEsNDEsODgsMTg3LDcxLDIzNyw5OCwxMDgsNiw0MCwxNzIsMjIzLDEyOSwxODksMTkyLDkxLDIzNiwyNyw5NCwyMjQsMTc0LDE1MiwxMTcsNjddfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5NywxMDgsMTA1LDk5LDEwMV19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiQ29tbWl0Ijp7InZlYyI6WzEzOSwxNjgsMjI3LDE2NSw4NCwzNSwxNjQsMTg2LDY3LDI1MCwxODUsNTMsOTcsOTgsMjIwLDIyNyw0NSw3NCw5OSw0MSwzNyw1NywxNzcsMTE4LDI1NCw1MSwzNiwxMzAsNjQsMjIsNzcsNzRdfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMTY4LDEwMSwxNjQsMjQ5LDEyMSw3Miw1MSwxNjAsOTEsMjEsOTMsMTQ4LDYsNjAsMTEwLDIxLDYxLDE3OCw5NiwzMCwyMDcsMTEyLDIwMSwyMDYsMjQ4LDE4OSwxODksMTIsNjMsMTgsMTMsMjMzLDEwMSwyMTcsODUsODQsMjM2LDUxLDQ4LDQ4LDkyLDgyLDExNiw3NiwxNTcsMjM1LDIxOCwxODQsODksMyw2OSw0MywyNDcsMTM3LDE1MCwxMjcsMTEsOTYsMTcyLDEzMCwxNTIsOTgsNjIsMV19fX19LHsibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls3MSwxMCw2OSwyMCwyLDkyLDcwLDg5LDIyNCw0OCwxMjYsNzgsMTI0LDE4NiwxNDgsMTU5LDI0NSwxNzUsMTIyLDE1NSwxNiwyMDksMTUzLDIxNiwyMiwxNDEsMTE3LDc1LDk3LDcyLDI0NywzNl19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMTYsMTEwLDE2LDkxLDk5LDQ0LDE3MCwxMjEsMTY0LDEwMiwxMjMsMTIzLDExNCwyMzksNzIsMTk2LDY5LDUsNTgsODcsMjU0LDQ1LDEwNywyMjksMTgyLDQyLDI5LDEzNSw3Niw4MywxOTcsMTk0XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NDksIm5vdF9hZnRlciI6MTcyNzk1MDE0OX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzI4LDE1NCwxOCw1NCwxMjQsMjMxLDI3LDE3MiwxOTIsMTA0LDE4Nyw5OCwxODUsNDQsMSwxNTgsMjA5LDIxMywyNDAsMTc5LDE4NCwyMDQsMTQ0LDEwNywxMjksMjEsMSw4Nyw2OSwxNTcsMTEyLDIwNywyMTQsMjA1LDEzMSwxMzcsNiwzNSwyMTcsMjI5LDE5NSw1NiwxMDksMTYyLDk2LDg5LDE3NiwyOCwxNzYsMjQsMTgsMTc0LDU5LDE0NSwxMzUsMjQ3LDIzMSwxNzAsMzMsMjA5LDQ3LDI1NSw1Myw3XX19fX1dLCJwYXJlbnRfbm9kZXMiOlt7Im5vZGUiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzEzLDE4MSw2MiwxNTksMjcsMjU1LDI0MywyMTEsMjQ3LDUyLDI0NywxMzgsMzcsMjU1LDYwLDMwLDEyNCw1MCw2NCw3OSwyNDEsMjIxLDE1NSwzNSwyMDYsMjI5LDE0MCw3MywxOCwyNDYsMjQ2LDg1XX19LCJwYXJlbnRfaGFzaCI6eyJ2ZWMiOltdfSwidW5tZXJnZWRfbGVhdmVzIjp7Imxpc3QiOltdfX19XSwiZGVmYXVsdF9sZWFmIjp7Im5vZGUiOm51bGx9LCJkZWZhdWx0X3BhcmVudCI6eyJub2RlIjpudWxsfX0sInRyZWVfaGFzaCI6WzQ3LDIzOCwyNiwyNTEsODQsMTIwLDEzNCw5MywxMSw2LDYyLDIsMTYxLDgsMTUxLDU3LDE0MCwxNTgsMTU2LDEwMCwxODQsMTYxLDg1LDI0NCwyMjEsNzEsMTQ1LDIxOCwxMywxMCwxNTYsOTddfQAAAAAAAABcAAAAAAAAAAVVc2VSYXRjaGV0VHJlZXsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAWZhbHNlAAAAAAAAAFwAAAAAAAAEvU1lc3NhZ2VTZWNyZXRzeyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYXhfZXBvY2hzIjowLCJwYXN0X2Vwb2NoX3RyZWVzIjpbXSwibWVzc2FnZV9zZWNyZXRzIjp7InNlbmRlcl9kYXRhX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTA2LDM2LDEwNiwxNjQsMTA0LDIyNywxNDMsMTM0LDIxMCwxMzAsMjIzLDE0MSwxNjEsMTkzLDExNCwyMzEsMTI0LDE5LDM4LDI0NywxODYsMTI1LDEwMSwxNCwyMDIsMTU2LDU0LDEwLDEzMiw0MCwyMTAsMjM5XX19fSwibWVtYmVyc2hpcF9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEyOCwxNDYsODksMTQxLDk1LDE0MywyNDgsMTUsNzQsNTEsMTgsMjI4LDgxLDE4MSwyMzcsOTksMjQ0LDE3NywxNzQsMTcxLDExNCw5OCwxMzUsMzQsMjM3LDM2LDcxLDQ1LDIyMCw4Myw4NCwzN119fX0sImNvbmZpcm1hdGlvbl9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzU0LDI1MCwxNzQsMTYzLDE1NCwyMzYsMTk5LDExOSwxNzQsMTM3LDcsMjMzLDIwNywxMzUsMTQ0LDMxLDEzNywyMzUsMjQzLDE5MCwyNDksMjYsMTI4LDIyNiwxMzIsODksMTI5LDc2LDIzMCwxNzgsNzYsODFdfX19LCJzZXJpYWxpemVkX2NvbnRleHQiOlswLDEsMCwzLDE2LDIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyLDAsMCwwLDAsMCwwLDAsMSwzMiw0NywyMzgsMjYsMjUxLDg0LDEyMCwxMzQsOTMsMTEsNiw2MiwyLDE2MSw4LDE1MSw1NywxNDAsMTU4LDE1NiwxMDAsMTg0LDE2MSw4NSwyNDQsMjIxLDcxLDE0NSwyMTgsMTMsMTAsMTU2LDk3LDMyLDQsMTA5LDMzLDE4OSwxNzgsMTYwLDEwMiwyMTIsMjksMTg1LDEzMywxMDQsMjM2LDExNSwxNyw1MSwxNzksMTcyLDM3LDQwLDEyMCwxNDgsNzQsMjUzLDI1NSwyMjcsMTI3LDIwNywxMzIsOTksMTM5LDE0OSwwXSwic2VjcmV0X3RyZWUiOnsib3duX2luZGV4IjowLCJsZWFmX25vZGVzIjpbbnVsbCxudWxsXSwicGFyZW50X25vZGVzIjpbeyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTc2LDMzLDIzLDI0NCw0NywxODMsMTgzLDg5LDIxNSwxMDIsNTUsMjI2LDIwOSw0NSw2NiwyNiw1MywxMzksMjMyLDEyMiw1MCw2MSwxODgsOTMsMjUsMTIwLDE5Miw1NSwxODAsNDAsNzYsODZdfX19LG51bGxdLCJoYW5kc2hha2Vfc2VuZGVyX3JhdGNoZXRzIjpbbnVsbCxudWxsXSwiYXBwbGljYXRpb25fc2VuZGVyX3JhdGNoZXRzIjpbbnVsbCxudWxsXSwic2l6ZSI6M319fQAAAAAA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YyI6WzE0OCwxMDYsMjUsMTA0LDIwNywxNzMsNTQsODgsMTczLDYwLDE4OCwxODEsMTIsMjUxLDkwLDEzNiwyMTQsMjAyLDIsMTUsMTc4LDUsMTMyLDgsNSw1NywyLDQ1LDY3LDE5NCw3MCw4MV19fV0sInF1ZXVlZF9wcm9wb3NhbHMiOltbeyJ2YWx1ZSI6eyJ2ZWMiOlsxNDgsMTA2LDI1LDEwNCwyMDcsMTczLDU0LDg4LDE3Myw2MCwxODgsMTgxLDEyLDI1MSw5MCwxMzYsMjE0LDIwMiwyLDE1LDE3OCw1LDEzMiw4LDUsNTcsMiw0NSw2NywxOTQsNzAsODFdfX0seyJwcm9wb3NhbCI6eyJHcm91cENvbnRleHRFeHRlbnNpb25zIjp7ImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbeyJSZXF1aXJlZENhcGFiaWxpdGllcyI6eyJleHRlbnNpb25fdHlwZXMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FsX3R5cGVzIjpbXSwiY3JlZGVudGlhbF90eXBlcyI6W119fV19fX0sInByb3Bvc2FsX3JlZmVyZW5jZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNDgsMTA2LDI1LDEwNCwyMDcsMTczLDU0LDg4LDE3Myw2MCwxODgsMTgxLDEyLDI1MSw5MCwxMzYsMjE0LDIwMiwyLDE1LDE3OCw1LDEzMiw4LDUsNTcsMiw0NSw2NywxOTQsNzAsODFdfX0sInNlbmRlciI6eyJNZW1iZXIiOjB9LCJwcm9wb3NhbF9vcl9yZWZfdHlwZSI6IlByb3Bvc2FsIn1dXX0sInN0YXRlIjp7Ikdyb3VwTWVtYmVyIjp7Imdyb3VwX2Vwb2NoX3NlY3JldHMiOnsiaW5pdF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzY0LDU0LDE5NCwyMDQsMTY1LDIsMTQwLDExNCwyMTMsNTUsMTc0LDIxOSw1MiwxNzksMywxNzYsMjIwLDYyLDE0OCw1MCwyMjYsNDgsMzEsMTQ0LDI0NCwxMjEsMTM5LDIwNSwxMzcsMTU0LDEyMCwzM119fX0sImV4cG9ydGVyX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTM0LDE2MywzLDI0MSw4MSwxMjksNTIsMTY4LDEzMywyNDIsOTAsMTQwLDEwMSwxNjEsMTgsMywxMzUsMTU5LDUwLDMwLDg3LDI0NSwxOTgsMTk4LDEyOSwyMDIsMTk1LDEzLDksOTIsMjIwLDEzMl19fX0sImVwb2NoX2F1dGhlbnRpY2F0b3IiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzgzLDE5OSwyNiwxMjIsMTYxLDc4LDc4LDI1LDEzMyw2NSw2MiwxMjUsMTksODMsMTE0LDIzNiwxODIsMzksNTIsMjQ1LDQ2LDE3NCwxNDcsMTYxLDQ2LDE4LDIzMywyNDYsNTAsMTEyLDI1MSwxNDldfX19LCJleHRlcm5hbF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE2Miw0OSwxNDksMTQ2LDE3MCwxNzUsODUsMTE5LDIyOCwyMDAsMTI5LDI0NSwyMTYsMTUzLDE2NSw2MiwxNDcsOTQsMjQ2LDIxMSwxMTIsMjAyLDE2NSwxLDkzLDIzMiw5NSwzMiwyMTcsODEsNjcsM119fX0sInJlc3VtcHRpb25fcHNrIjp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMywxMDIsMjU1LDcwLDEwLDIwNCwxODMsMjA2LDQ1LDIxMywxOTMsNTksNjMsMjMzLDgwLDgxLDY1LDE5LDExNCw4Nyw2Myw0NywxNzUsMjA2LDQwLDEyNSwxMiwxNTIsMTc0LDc4LDE0NSw0XX19fX0sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE3OCwyMTEsMTM0LDE4Niw0OSwxNDEsNzYsMTcxLDIzOSwxODQsNTMsMzAsNCwxMjEsMjcsODMsMTQyLDgsMjYsMTAxLDE2Miw2OCwxMDUsMTQzLDI4LDU2LDE0LDg5LDIsNTIsMzEsMTBdfX19LCJtZW1iZXJzaGlwX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMiw0MSwyMTAsMjE1LDEyNiwxNzAsMjE3LDU0LDU3LDEzMywxMTcsMTEwLDU4LDIwOSwxOTMsMTU4LDExOSwxMTgsMTUyLDE4LDEwNiw2LDg0LDYyLDIxNywyMywyMjEsNjEsODcsNzcsMTUxLDIyMF19fX0sImNvbmZpcm1hdGlvbl9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEwNiwyMjAsMTY0LDksMTk3LDgzLDQyLDI1MSw3NywyNTUsNiwxMDEsNCw1OCwyNSw0MCwyLDIyMSwxNTcsNjEsMTQyLDE0MSw1LDQzLDEwMCwyMTQsNzEsOTksMTY5LDEyNiw4MSwxNDZdfX19LCJzZXJpYWxpemVkX2NvbnRleHQiOlswLDEsMCwzLDE2LDIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyLDAsMCwwLDAsMCwwLDAsMiwzMiwxMDcsMjE0LDcyLDE4OSwyMzksMTcsNjksMzAsMjQzLDE5NCw2NCw1NywyMjcsNjEsMTkxLDIzNSwyMzQsMTAyLDEwNSwyMTAsMTQ4LDI0MiwxMjgsMTY5LDExLDI1NCw5OSwzMSwxNzgsMzIsNiwyNDIsMzIsNjgsMTg2LDE3OSwxOTUsMywxNDMsMjI2LDE5NCwxOTQsMjM4LDYyLDI0OSwxNjcsMTE4LDE5OSwxMDksMTEzLDIyMiwxNDUsMjMxLDEsMTA4LDEyNCw1MCwxMTcsNTMsODEsMTIyLDE3NSw5MywyMDYsMjAxLDgsMCwzLDUsMiwyNDAsNjYsMCwwXSwic2VjcmV0X3RyZWUiOnsib3duX2luZGV4IjowLCJsZWFmX25vZGVzIjpbbnVsbCxudWxsXSwicGFyZW50X25vZGVzIjpbeyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbOTksMTExLDI0MCw3OSwyNTMsMjE5LDE2NCwyMiwxNjIsNiwxOTQsNjcsMTkwLDI1LDE5OCwyMjcsMTcwLDIwOSwyNTMsMTEzLDEyOCwxNDMsODQsMTIwLDIyMCwxNTEsMjQ3LDYzLDU0LDEzMSwxMDAsMTkwXX19fSxudWxsXSwiaGFuZHNoYWtlX3NlbmRlcl9yYXRjaGV0cyI6W251bGwsbnVsbF0sImFwcGxpY2F0aW9uX3NlbmRlcl9yYXRjaGV0cyI6W251bGwsbnVsbF0sInNpemUiOjN9fSwic3RhZ2VkX2RpZmYiOnsic3RhZ2VkX2RpZmYiOnsiZGlmZiI6eyJsZWFmX2RpZmYiOnsiMCI6eyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzE1LDI1Miw3MiwxMTAsOTIsNTQsODksMzIsMjQwLDEzMSw2NiwxNTcsOTgsMTcwLDI0NCw2Niw3MiwxOTEsNDksMjAyLDE5NiwxNjEsMTczLDE3LDIyMiwxNDIsNjQsMjMxLDE4NywxNzIsMTY5LDE3XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzE4OCwyOSwyNDcsMjQ1LDg1LDE3MSwxNzYsNTYsMTEsNDEsODgsMTg3LDcxLDIzNyw5OCwxMDgsNiw0MCwxNzIsMjIzLDEyOSwxODksMTkyLDkxLDIzNiwyNyw5NCwyMjQsMTc0LDE1MiwxMTcsNjddfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5NywxMDgsMTA1LDk5LDEwMV19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiQ29tbWl0Ijp7InZlYyI6WzI1MCwyMDIsMTk5LDEzNCwxMDEsMTYxLDIzNCwxNzEsNDYsMTI4LDIzMCw0MSwxNjQsMTAzLDQ2LDE0OCwyMTQsNTMsMjQ0LDQsMTU1LDE4LDY5LDE1Miw3MSwzMSwzNiwxNiwxMzEsMTksOTMsMjAxXX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzY0LDE3OCwyMTYsMTQwLDIwNyw1Miw2MCwyNTAsMzEsMTI2LDIwOCw1OSw3NCwxMzksMTA3LDE5NCw1LDI0Miw2Nyw2MSwxMDQsMjcsNTEsMTUsMTk2LDIyOSwyNDQsODEsNTUsMzksOTYsNzIsOTgsMzIsMTU4LDE5OSw2LDE2MywyMjAsMzgsMTM4LDMzLDEwNCwxNiwxNDUsMzAsNjAsNzYsNzEsMTk4LDk5LDQ4LDIwNCwxMDEsMTIxLDE4LDExNSwyMDEsMTk1LDE1LDU4LDU1LDEzOSwxMV19fX19fSwicGFyZW50X2RpZmYiOnsiMCI6eyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNzEsMTgwLDIyOCwxODgsMTA0LDIwMSwxOTQsMTcxLDM0LDIxNSwyMDcsMjQ3LDI0MCw4NywyMiwxNjIsNTEsMjE2LDEwLDE5NCw0MCw0OSwxNzcsMTk2LDE1NiwyMjMsMTI4LDIwLDIwOCwxMzQsMjE0LDc2XX19LCJwYXJlbnRfaGFzaCI6eyJ2ZWMiOltdfSwidW5tZXJnZWRfbGVhdmVzIjp7Imxpc3QiOltdfX19fSwic2l6ZSI6M30sIm5ld190cmVlX2hhc2giOlsxMDcsMjE0LDcyLDE4OSwyMzksMTcsNjksMzAsMjQzLDE5NCw2NCw1NywyMjcsNjEsMTkxLDIzNSwyMzQsMTAyLDEwNSwyMTAsMTQ4LDI0MiwxMjgsMTY5LDExLDI1NCw5OSwzMSwxNzgsMzIsNiwyNDJdfSwiZ3JvdXBfY29udGV4dCI6eyJwcm90b2NvbF92ZXJzaW9uIjoiTWxzMTAiLCJjaXBoZXJzdWl0ZSI6Ik1MU18xMjhfREhLRU1YMjU1MTlfQ0hBQ0hBMjBQT0xZMTMwNV9TSEEyNTZfRWQyNTUxOSIsImdyb3VwX2lkIjp7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19LCJlcG9jaCI6MiwidHJlZV9oYXNoIjp7InZlYyI6WzEwNywyMTQsNzIsMTg5LDIzOSwxNyw2OSwzMCwyNDMsMTk0LDY0LDU3LDIyNyw2MSwxOTEsMjM1LDIzNCwxMDIsMTA1LDIxMCwxNDgsMjQyLDEyOCwxNjksMTEsMjU0LDk5LDMxLDE3OCwzMiw2LDI0Ml19LCJjb25maXJtZWRfdHJhbnNjcmlwdF9oYXNoIjp7InZlYyI6WzY4LDE4NiwxNzksMTk1LDMsMTQzLDIyNiwxOTQsMTk0LDIzOCw2MiwyNDksMTY3LDExOCwxOTksMTA5LDExMywyMjIsMTQ1LDIzMSwxLDEwOCwxMjQsNTAsMTE3LDUzLDgxLDEyMiwxNzUsOTMsMjA2LDIwMV19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W3siUmVxdWlyZWRDYXBhYmlsaXRpZXMiOnsiZXh0ZW5zaW9uX3R5cGVzIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbF90eXBlcyI6W10sImNyZWRlbnRpYWxfdHlwZXMiOltdfX1dfX0sImludGVyaW1fdHJhbnNjcmlwdF9oYXNoIjpbNjgsMTQyLDIwLDQ3LDIxNywxODUsMTcxLDE3MywxNDksMzIsMTQ4LDExMywxNzcsMTg4LDE1NCwxNjAsNjMsMTAyLDEzNiwyMDcsNjUsNDAsMzYsMjI3LDE3NywyNDYsNzMsMTA5LDExNSw1NywxNywxMDhdLCJjb25maXJtYXRpb25fdGFnIjp7Im1hY192YWx1ZSI6eyJ2ZWMiOlsxOTYsMjAwLDg2LDI1NCwxMDIsMTk1LDgwLDY0LDIwMywyOSwyMjcsMTY5LDIyOSwxOTUsMTgwLDIyMSw3NCw2OCwyNTQsMTU4LDEwNCwxMzMsMTYxLDI0Nyw2MywxMzEsMTgsMjQzLDE4LDk3LDMxLDIyXX19fSwibmV3X2tleXBhaXJzIjpbeyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNSwyNTIsNzIsMTEwLDkyLDU0LDg5LDMyLDI0MCwxMzEsNjYsMTU3LDk4LDE3MCwyNDQsNjYsNzIsMTkxLDQ5LDIwMiwxOTYsMTYxLDE3MywxNywyMjIsMTQyLDY0LDIzMSwxODcsMTcyLDE2OSwxN119fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzE3NCw1OCw4NiwxOTgsMTM2LDIwNiwyMzIsMjUzLDM2LDEyLDMwLDE0NiwxMjksMTA2LDEwOCwxMjEsMjAyLDI1MywyNDIsMTU5LDk3LDE5MywyMTMsMjEsMTcwLDIzNCw2OCwzNSwxNTQsMjI1LDUzLDEwOF19fX0seyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNzEsMTgwLDIyOCwxODgsMTA0LDIwMSwxOTQsMTcxLDM0LDIxNSwyMDcsMjQ3LDI0MCw4NywyMiwxNjIsNTEsMjE2LDEwLDE5NCw0MCw0OSwxNzcsMTk2LDE1NiwyMjMsMTI4LDIwLDIwOCwxMzQsMjE0LDc2XX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMCwyNCwxODksMjQwLDMwLDUsMjU1LDIyMSwxMjcsNTgsMTg0LDEwOCwyMDMsMjMwLDE4LDI4LDUxLDQzLDI1MCwxMjcsMTg1LDEyNSwyNTIsMTA0LDI1MiwzMiwxNjcsMjAsOTEsNzIsMzEsMTgxXX19fV0sIm5ld19sZWFmX2tleXBhaXJfb3B0aW9uIjpudWxsLCJ1cGRhdGVfcGF0aF9sZWFmX25vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTUsMjUyLDcyLDExMCw5Miw1NCw4OSwzMiwyNDAsMTMxLDY2LDE1Nyw5OCwxNzAsMjQ0LDY2LDcyLDE5MSw0OSwyMDIsMTk2LDE2MSwxNzMsMTcsMjIyLDE0Miw2NCwyMzEsMTg3LDE3MiwxNjksMTddfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMTg4LDI5LDI0NywyNDUsODUsMTcxLDE3Niw1NiwxMSw0MSw4OCwxODcsNzEsMjM3LDk4LDEwOCw2LDQwLDE3MiwyMjMsMTI5LDE4OSwxOTIsOTEsMjM2LDI3LDk0LDIyNCwxNzQsMTUyLDExNyw2N119fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVj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dCI6eyJ2ZWMiOlsxMzksMTY4LDIyNywxNjUsODQsMzUsMTY0LDE4Niw2NywyNTAsMTg1LDUzLDk3LDk4LDIyMCwyMjcsNDUsNzQsOTksNDEsMzcsNTcsMTc3LDExOCwyNTQsNTEsMzYsMTMwLDY0LDIyLDc3LDc0XX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzE2OCwxMDEsMTY0LDI0OSwxMjEsNzIsNTEsMTYwLDkxLDIxLDkzLDE0OCw2LDYwLDExMCwyMSw2MSwxNzgsOTYsMzAsMjA3LDExMiwyMDEsMjA2LDI0OCwxODksMTg5LDEyLDYzLDE4LDEzLDIzMywxMDEsMjE3LDg1LDg0LDIzNiw1MSw0OCw0OCw5Miw4MiwxMTYsNzYsMTU3LDIzNSwyMTgsMTg0LDg5LDMsNjksNDMsMjQ3LDEzNywxNTAsMTI3LDExLDk2LDE3MiwxMzAsMTUyLDk4LDYyLDFdfX19fSx7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNzEsMTAsNjksMjAsMiw5Miw3MCw4OSwyMjQsNDgsMTI2LDc4LDEyNCwxODYsMTQ4LDE1OSwyNDUsMTc1LDEyMiwxNTUsMTYsMjA5LDE1MywyMTYsMjIsMTQxLDExNyw3NSw5Nyw3MiwyNDcsMzZdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMTE2LDExMCwxNiw5MSw5OSw0NCwxNzAsMTIxLDE2NCwxMDIsMTIzLDEyMywxMTQsMjM5LDcyLDE5Niw2OSw1LDU4LDg3LDI1NCw0NSwxMDcsMjI5LDE4Miw0MiwyOSwxMzUsNzYsODMsMTk3LDE5NF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk4LDExMSw5OF19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTQ5LCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNDl9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyOCwxNTQsMTgsNTQsMTI0LDIzMSwyNywxNzIsMTkyLDEwNCwxODcsOTgsMTg1LDQ0LDEsMTU4LDIwOSwyMTMsMjQwLDE3OSwxODQsMjA0LDE0NCwxMDcsMTI5LDIxLDEsODcsNjksMTU3LDExMiwyMDcsMjE0LDIwNSwxMzEsMTM3LDYsMzUsMjE3LDIyOSwxOTUsNTYsMTA5LDE2Miw5Niw4OSwxNzYsMjgsMTc2LDI0LDE4LDE3NCw1OSwxNDUsMTM1LDI0NywyMzEsMTcwLDMzLDIwOSw0NywyNTUsNTMsN119fX19XSwicGFyZW50X25vZGVzIjpbeyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxMywxODEsNjIsMTU5LDI3LDI1NSwyNDMsMjExLDI0Nyw1MiwyNDcsMTM4LDM3LDI1NSw2MCwzMCwxMjQsNTAsNjQsNzksMjQxLDIyMSwxNTUsMzUsMjA2LDIyOSwxNDAsNzMsMTgsMjQ2LDI0Niw4NV19fSwicGFyZW50X2hhc2giOnsidmVjIjpbXX0sInVubWVyZ2VkX2xlYXZlcyI6eyJsaXN0IjpbXX19fV0sImRlZmF1bHRfbGVhZiI6eyJub2RlIjpudWxsfSwiZGVmYXVsdF9wYXJlbnQiOnsibm9kZSI6bnVsbH19LCJ0cmVlX2hhc2giOls0NywyMzgsMjYsMjUxLDg0LDEyMCwxMzQsOTMsMTEsNiw2MiwyLDE2MSw4LDE1MSw1NywxNDAsMTU4LDE1NiwxMDAsMTg0LDE2MSw4NSwyNDQsMjIxLDcxLDE0NSwyMTgsMTMsMTAsMTU2LDk3XX0AAAAAAAAAXAAAAAAAAAAFVXNlUmF0Y2hldFRyZWV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAFmYWxzZQAAAAAAAABcAAAAAAAABiVNZXNzYWdlU2VjcmV0c3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X2Vwb2NocyI6MCwicGFzdF9lcG9jaF90cmVlcyI6W10sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEwNiwzNiwxMDYsMTY0LDEwNCwyMjcsMTQzLDEzNCwyMTAsMTMwLDIyMywxNDEsMTYxLDE5MywxMTQsMjMxLDEyNCwxOSwzOCwyNDcsMTg2LDEyNSwxMDEsMTQsMjAyLDE1Niw1NCwxMCwxMzIsNDAsMjEwLDIzOV19fX0sIm1lbWJlcnNoaXBfa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMjgsMTQ2LDg5LDE0MSw5NSwxNDMsMjQ4LDE1LDc0LDUxLDE4LDIyOCw4MSwxODEsMjM3LDk5LDI0NCwxNzcsMTc0LDE3MSwxMTQsOTgsMTM1LDM0LDIzNywzNiw3MSw0NSwyMjAsODMsODQsMzddfX19LCJjb25maXJtYXRpb25fa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOls1NCwyNTAsMTc0LDE2MywxNTQsMjM2LDE5OSwxMTksMTc0LDEzNyw3LDIzMywyMDcsMTM1LDE0NCwzMSwxMzcsMjM1LDI0MywxOTAsMjQ5LDI2LDEyOCwyMjYsMTMyLDg5LDEyOSw3NiwyMzAsMTc4LDc2LDgxXX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsMywxNiwyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMiwwLDAsMCwwLDAsMCwwLDEsMzIsNDcsMjM4LDI2LDI1MSw4NCwxMjAsMTM0LDkzLDExLDYsNjIsMiwxNjEsOCwxNTEsNTcsMTQwLDE1OCwxNTYsMTAwLDE4NCwxNjEsODUsMjQ0LDIyMSw3MSwxNDUsMjE4LDEzLDEwLDE1Niw5NywzMiw0LDEwOSwzMywxODksMTc4LDE2MCwxMDIsMjEyLDI5LDE4NSwxMzMsMTA0LDIzNiwxMTUsMTcsNTEsMTc5LDE3MiwzNyw0MCwxMjAsMTQ4LDc0LDI1MywyNTUsMjI3LDEyNywyMDcsMTMyLDk5LDEzOSwxNDksMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGwseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTcxLDI4LDI3LDEwOCwxNjAsMzksMTQwLDEzNCw0OSw0OCwyNCwyNTEsMTM3LDE1MCwyNDYsMTU3LDI1MywxMTksNTMsMTgzLDYsMTMsMjQ4LDI3LDUzLDE4Nyw0MiwyMjYsMTExLDQ1LDE5LDQxXX19fV0sInBhcmVudF9ub2RlcyI6W251bGwsbnVsbF0sImhhbmRzaGFrZV9zZW5kZXJfcmF0Y2hldHMiOlt7IkVuY3J5cHRpb25SYXRjaGV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMzIsMjI2LDk1LDI1Myw2MiwxNTAsMTcwLDI0MCw1MywxNjIsMTk4LDI1NSwxMzMsNTUsMTM1LDgzLDM0LDE0MSwyMTEsMTY2LDk5LDE5LDEyOSw5Niw4NSw1NSwyMzIsNiwxMzQsNjIsMjE2LDIyXX19LCJnZW5lcmF0aW9uIjoxfX0sbnVsbF0sImFwcGxpY2F0aW9uX3NlbmRlcl9yYXRjaGV0cyI6W3siRW5jcnlwdGlvblJhdGNoZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzU5LDI0MCwyMzIsMTU0LDIxNCwxNjIsMTk1LDEyMywxMTQsMTExLDk5LDE3NywyMjgsNTMsMTMyLDE0NCwxMzEsMTIyLDY0LDI0MiwyMSwyNDAsMjExLDQ4LDE1OSwxMzMsMTk5LDk3LDc0LDEwMSwxMzQsMTUyXX19LCJnZW5lcmF0aW9uIjowfX0sbnVsbF0sInNpemUiOjN9fX0AAAAAAAAAXgAAAAAAAAABT3duTGVhZk5vZGVJbmRleHsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AATAAAAAAAAAAWgAAAAAAAAH9R3JvdXBDb250ZXh0eyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJwcm90b2NvbF92ZXJzaW9uIjoiTWxzMTAiLCJjaXBoZXJzdWl0ZSI6Ik1MU18xMjhfREhLRU1YMjU1MTlfQ0hBQ0hBMjBQT0xZMTMwNV9TSEEyNTZfRWQyNTUxOSIsImdyb3VwX2lkIjp7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19LCJlcG9jaCI6MSwidHJlZV9oYXNoIjp7InZlYyI6WzQ3LDIzOCwyNiwyNTEsODQsMTIwLDEzNCw5MywxMSw2LDYyLDIsMTYxLDgsMTUxLDU3LDE0MCwxNTgsMTU2LDEwMCwxODQsMTYxLDg1LDI0NCwyMjEsNzEsMTQ1LDIxOCwxMywxMCwxNTYsOTddfSwiY29uZmlybWVkX3RyYW5zY3JpcHRfaGFzaCI6eyJ2ZWMiOls0LDEwOSwzMywxODksMTc4LDE2MCwxMDIsMjEyLDI5LDE4NSwxMzMsMTA0LDIzNiwxMTUsMTcsNTEsMTc5LDE3MiwzNyw0MCwxMjAsMTQ4LDc0LDI1MywyNTUsMjI3LDEyNywyMDcsMTMyLDk5LDEzOSwxNDldfSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0AAAAAAAAAYwAAAAAAAABzSW50ZXJpbVRyYW5zY3JpcHRIYXNoeyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABWzE5MSw0NywzNywxNzAsMTkxLDE4NSwxOTgsOTEsMjUzLDIzMywzMSwzNyw5OCw3MSwxMTQsMjA2LDI2LDE4LDU4LDM1LDI0MCw0NCwxODAsNzIsMTcsMTQyLDIwNSwxMjAsMTk0LDE0NSwyMzQsMjQ4XQAAAAAAAABgAAAAAAAAARZNbHNHcm91cEpvaW5Db25maWd7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7IndpcmVfZm9ybWF0X3BvbGljeSI6eyJvdXRnb2luZyI6IkFsd2F5c0NpcGhlcnRleHQiLCJpbmNvbWluZyI6IkFsd2F5c0NpcGhlcnRleHQifSwicGFkZGluZ19zaXplIjowLCJtYXhfcGFzdF9lcG9jaHMiOjAsIm51bWJlcl9vZl9yZXN1bXB0aW9uX3Bza3MiOjAsInVzZV9yYXRjaGV0X3RyZWVfZXh0ZW5zaW9uIjpmYWxzZSwic2VuZGVyX3JhdGNoZXRfY29uZmlndXJhdGlvbiI6eyJvdXRfb2Zfb3JkZXJfdG9sZXJhbmNlIjo1LCJtYXhpbXVtX2ZvcndhcmRfZGlzdGFuY2UiOjEwMDB9fQAAAAAAAABaAAAAAAAAAyZFcG9jaFNlY3JldHN7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7ImluaXRfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMjMsMTQ1LDIxOCw4MSwxNjAsMjI2LDUsMjM4LDE4MywyMCwxMSw0OSwyMiw5MywxOTcsNjcsMTczLDIyNSwxODksNjUsNCw1OSwxOTIsNzMsMjIyLDEzMCwyMTksMTgyLDEwOSwxODMsMTkwLDIwNV19fX0sImV4cG9ydGVyX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNDcsMjMyLDQ5LDE1Niw3NywxNzMsMTM4LDE2MiwyNTMsMTcyLDExMywyNDYsMjEzLDYxLDgyLDI1MCwyNyw5LDgyLDEzNywyMjQsMTk1LDI0NSwyMzMsMTAyLDU0LDkyLDc2LDksNTgsMTQyLDIzXX19fSwiZXBvY2hfYXV0aGVudGljYXRvciI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMjMxLDU5LDE4NCw2NCwyOSwxMTUsMTk5LDE2LDQxLDIwOCw1MSw5NCwxMDUsMTgwLDEyMiwxNzcsMjIxLDE2OSw3MiwxOTksMTAyLDEzMCwxNzYsMjUyLDM3LDEyNCwyLDEwNywyMzksMjAsMjQ2LDI4XX19fSwiZXh0ZXJuYWxfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOls5LDE4NCwxNTYsMjUsNzEsOTEsMTMzLDIxMCw4NCwxMjcsMTczLDIxNiwyMDgsMjE4LDU2LDM3LDIxMiwyMiwyMCwyMjEsNTYsMTYsMzQsNzQsMTc5LDg3LDIyNywyMzYsOTgsNTYsMTI2LDU1XX19fSwicmVzdW1wdGlvbl9wc2siOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE1LDIyMiw2LDE1NSw3OSw5NSwxNDAsMzIsMTY3LDE2NywxMzMsMjAsMTU0LDYsNDMsNDUsMTU2LDE5NCwyMSwxNTMsMTMyLDE5OSwxMjcsMTYzLDEzNiwzLDE4MCwxOTUsMCw5MywyMiwxNzJdfX19fQAAAAAAAADlAAAAAAAAARhTaWduYXR1cmVLZXlQYWlyeyJ2YWx1ZSI6WzE4OCwyOSwyNDcsMjQ1LDg1LDE3MSwxNzYsNTYsMTEsNDEsODgsMTg3LDcxLDIzNyw5OCwxMDgsNiw0MCwxNzIsMjIzLDEyOSwxODksMTkyLDkxLDIzNiwyNyw5NCwyMjQsMTc0LDE1MiwxMTcsNjcsODIsMTE3LDExNSwxMTYsNjcsMTE0LDEyMSwxMTIsMTE2LDExMSw4MywxMDUsMTAzLDExMCw5NywxMTYsMTE3LDExNCwxMDEsNzUsMTAxLDEyMSw4LDddfQABeyJwcml2YXRlIjpbNzAsMjEzLDY5LDIyNSw4NywzOSwxNDEsMTE3LDExMiwxNyw0Miw1NywyMDQsMTcsMTM4LDM1LDc1LDkxLDE1OSw3NSwxNTIsMjIwLDExNywxMCwyMTcsMjAwLDE0MSwxNzAsMTg0LDc0LDIyOCwyMDZdLCJwdWJsaWMiOlsxODgsMjksMjQ3LDI0NSw4NSwxNzEsMTc2LDU2LDExLDQxLDg4LDE4Nyw3MSwyMzcsOTgsMTA4LDYsNDAsMTcyLDIyMywxMjksMTg5LDE5Miw5MSwyMzYsMjcsOTQsMjI0LDE3NCwxNTIsMTE3LDY3XSwic2lnbmF0dXJlX3NjaGVtZSI6IkVEMjU1MTkifQ==","AAAAAAAAAA0AAAAAAAAAWwAAAAAAAAHvUmVzdW1wdGlvblBza3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X251bWJlcl9vZl9zZWNyZXRzIjozMiwicmVzdW1wdGlvbl9wc2siOltbMCx7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjUsNTMsNDIsMjcsNCw2Miw4NSwxMDQsMTY2LDczLDE5OSwyMTUsMTMxLDMsMTQ3LDI1MCw5MiwxNDcsNjAsODUsMTM4LDEwMCwxMzcsMTM4LDc0LDE0MiwxMDUsMjA4LDEyMiw5MiwxMzksNDRdfX19XSxbMSx7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNSwyMjIsNiwxNTUsNzksOTUsMTQwLDMyLDE2NywxNjcsMTMzLDIwLDE1NCw2LDQzLDQ1LDE1NiwxOTQsMjEsMTUzLDEzMiwxOTksMTI3LDE2MywxMzYsMywxODAsMTk1LDAsOTMsMjIsMTcyXX19fV0sWzIseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMjMsMTAyLDI1NSw3MCwxMCwyMDQsMTgzLDIwNiw0NSwyMTMsMTkzLDU5LDYzLDIzMyw4MCw4MSw2NSwxOSwxMTQsODcsNjMsNDcsMTc1LDIwNiw0MCwxMjUsMTIsMTUyLDE3NCw3OCwxNDUsNF19fX1dXSwiY3Vyc29yIjozfQAAAAAAAABdAAAAAAAAAItDb25maXJtYXRpb25UYWd7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7Im1hY192YWx1ZSI6eyJ2ZWMiOlsxOTYsMjAwLDg2LDI1NCwxMDIsMTk1LDgwLDY0LDIwMywyOSwyMjcsMTY5LDIyOSwxOTUsMTgwLDIyMSw3NCw2OCwyNTQsMTU4LDEwNCwxMzMsMTYxLDI0Nyw2MywxMzEsMTgsMjQzLDE4LDk3LDMxLDIyXX19AAAAAAAAAFgAAAAAAAAADUdyb3VwU3RhdGV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAEiT3BlcmF0aW9uYWwiAAAAAAAAAFIAAAAAAAAJN1RyZWV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7InRyZWUiOnsibGVhZl9ub2RlcyI6W3sibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNSwyNTIsNzIsMTEwLDkyLDU0LDg5LDMyLDI0MCwxMzEsNjYsMTU3LDk4LDE3MCwyNDQsNjYsNzIsMTkxLDQ5LDIwMiwxOTYsMTYxLDE3MywxNywyMjIsMTQyLDY0LDIzMSwxODcsMTcyLDE2OSwxN119fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxODgsMjksMjQ3LDI0NSw4NSwxNzEsMTc2LDU2LDExLDQxLDg4LDE4Nyw3MSwyMzcsOTgsMTA4LDYsNDAsMTcyLDIyMywxMjksMTg5LDE5Miw5MSwyMzYsMjcsOTQsMjI0LDE3NCwxNTIsMTE3LDY3XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTcsMTA4LDEwNSw5OSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IkNvbW1pdCI6eyJ2ZWMiOlsyNTAsMjAyLDE5OSwxMzQsMTAxLDE2MSwyMzQsMTcxLDQ2LDEyOCwyMzAsNDEsMTY0LDEwMyw0NiwxNDgsMjE0LDUzLDI0NCw0LDE1NSwxOCw2OSwxNTIsNzEsMzEsMzYsMTYsMTMxLDE5LDkzLDIwMV19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls2NCwxNzgsMjE2LDE0MCwyMDcsNTIsNjAsMjUwLDMxLDEyNiwyMDgsNTksNzQsMTM5LDEwNywxOTQsNSwyNDIsNjcsNjEsMTA0LDI3LDUxLDE1LDE5NiwyMjksMjQ0LDgxLDU1LDM5LDk2LDcyLDk4LDMyLDE1OCwxOTksNiwxNjMsMjIwLDM4LDEzOCwzMywxMDQsMTYsMTQ1LDMwLDYwLDc2LDcxLDE5OCw5OSw0OCwyMDQsMTAxLDEyMSwxOCwxMTUsMjAxLDE5NSwxNSw1OCw1NSwxMzksMTFdfX19fSx7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNzEsMTAsNjksMjAsMiw5Miw3MCw4OSwyMjQsNDgsMTI2LDc4LDEyNCwxODYsMTQ4LDE1OSwyNDUsMTc1LDEyMiwxNTUsMTYsMjA5LDE1MywyMTYsMjIsMTQxLDExNyw3NSw5Nyw3MiwyNDcsMzZdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMTE2LDExMCwxNiw5MSw5OSw0NCwxNzAsMTIxLDE2NCwxMDIsMTIzLDEyMywxMTQsMjM5LDcyLDE5Niw2OSw1LDU4LDg3LDI1NCw0NSwxMDcsMjI5LDE4Miw0MiwyOSwxMzUsNzYsODMsMTk3LDE5NF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk4LDExMSw5OF19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTQ5LCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNDl9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyOCwxNTQsMTgsNTQsMTI0LDIzMSwyNywxNzIsMTkyLDEwNCwxODcsOTgsMTg1LDQ0LDEsMTU4LDIwOSwyMTMsMjQwLDE3OSwxODQsMjA0LDE0NCwxMDcsMTI5LDIxLDEsODcsNjksMTU3LDExMiwyMDcsMjE0LDIwNSwxMzEsMTM3LDYsMzUsMjE3LDIyOSwxOTUsNTYsMTA5LDE2Miw5Niw4OSwxNzYsMjgsMTc2LDI0LDE4LDE3NCw1OSwxNDUsMTM1LDI0NywyMzEsMTcwLDMzLDIwOSw0NywyNTUsNTMsN119fX19XSwicGFyZW50X25vZGVzIjpbeyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNzEsMTgwLDIyOCwxODgsMTA0LDIwMSwxOTQsMTcxLDM0LDIxNSwyMDcsMjQ3LDI0MCw4NywyMiwxNjIsNTEsMjE2LDEwLDE5NCw0MCw0OSwxNzcsMTk2LDE1NiwyMjMsMTI4LDIwLDIwOCwxMzQsMjE0LDc2XX19LCJwYXJlbnRfaGFzaCI6eyJ2ZWMiOltdfSwidW5tZXJnZWRfbGVhdmVzIjp7Imxpc3QiOltdfX19XSwiZGVmYXVsdF9sZWFmIjp7Im5vZGUiOm51bGx9LCJkZWZhdWx0X3BhcmVudCI6eyJub2RlIjpudWxsfX0sInRyZWVfaGFzaCI6WzEwNywyMTQsNzIsMTg5LDIzOSwxNyw2OSwzMCwyNDMsMTk0LDY0LDU3LDIyNyw2MSwxOTEsMjM1LDIzNCwxMDIsMTA1LDIxMCwxNDgsMjQyLDEyOCwxNjksMTEsMjU0LDk5LDMxLDE3OCwzMiw2LDI0Ml19AAAAAAAAAFwAAAAAAAAABVVzZVJhdGNoZXRUcmVleyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABZmFsc2UAAAAAAAAAXAAAAAAAAATDTWVzc2FnZVNlY3JldHN7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7Im1heF9lcG9jaHMiOjAsInBhc3RfZXBvY2hfdHJlZXMiOltdLCJtZXNzYWdlX3NlY3JldHMiOnsic2VuZGVyX2RhdGFfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNzgsMjExLDEzNCwxODYsNDksMTQxLDc2LDE3MSwyMzksMTg0LDUzLDMwLDQsMTIxLDI3LDgzLDE0Miw4LDI2LDEwMSwxNjIsNjgsMTA1LDE0MywyOCw1NiwxNCw4OSwyLDUyLDMxLDEwXX19fSwibWVtYmVyc2hpcF9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzIsNDEsMjEwLDIxNSwxMjYsMTcwLDIxNyw1NCw1NywxMzMsMTE3LDExMCw1OCwyMDksMTkzLDE1OCwxMTksMTE4LDE1MiwxOCwxMDYsNiw4NCw2MiwyMTcsMjMsMjIxLDYxLDg3LDc3LDE1MSwyMjBdfX19LCJjb25maXJtYXRpb25fa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMDYsMjIwLDE2NCw5LDE5Nyw4Myw0MiwyNTEsNzcsMjU1LDYsMTAxLDQsNTgsMjUsNDAsMiwyMjEsMTU3LDYxLDE0MiwxNDEsNSw0MywxMDAsMjE0LDcxLDk5LDE2OSwxMjYsODEsMTQ2XX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsMywxNiwyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMiwwLDAsMCwwLDAsMCwwLDIsMzIsMTA3LDIxNCw3MiwxODksMjM5LDE3LDY5LDMwLDI0MywxOTQsNjQsNTcsMjI3LDYxLDE5MSwyMzUsMjM0LDEwMiwxMDUsMjEwLDE0OCwyNDIsMTI4LDE2OSwxMSwyNTQsOTksMzEsMTc4LDMyLDYsMjQyLDMyLDY4LDE4NiwxNzksMTk1LDMsMTQzLDIyNiwxOTQsMTk0LDIzOCw2MiwyNDksMTY3LDExOCwxOTksMTA5LDExMywyMjIsMTQ1LDIzMSwxLDEwOCwxMjQs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MTE3LDY3LDgyLDExNywxMTUsMTE2LDY3LDExNCwxMjEsMTEyLDExNiwxMTEsODMsMTA1LDEwMywxMTAsOTcsMTE2LDExNywxMTQsMTAxLDc1LDEwMSwxMjEsOCw3XX0AAXsicHJpdmF0ZSI6WzcwLDIxMyw2OSwyMjUsODcsMzksMTQxLDExNywxMTIsMTcsNDIsNTcsMjA0LDE3LDEzOCwzNSw3NSw5MSwxNTksNzUsMTUyLDIyMCwxMTcsMTAsMjE3LDIwMCwxNDEsMTcwLDE4NCw3NCwyMjgsMjA2XSwicHVibGljIjpbMTg4LDI5LDI0NywyNDUsODUsMTcxLDE3Niw1NiwxMSw0MSw4OCwxODcsNzEsMjM3LDk4LDEwOCw2LDQwLDE3MiwyMjMsMTI5LDE4OSwxOTIsOTEsMjM2LDI3LDk0LDIyNCwxNzQsMTUyLDExNyw2N10sInNpZ25hdHVyZV9zY2hlbWUiOiJFRDI1NTE5In0=","AAAAAAAAAA8AAAAAAAAAWwAAAAAAAAHvUmVzdW1wdGlvblBza3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X251bWJlcl9vZl9zZWNyZXRzIjozMiwicmVzdW1wdGlvbl9wc2siOltbMCx7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjUsNTMsNDIsMjcsNCw2Miw4NSwxMDQsMTY2LDczLDE5OSwyMTUsMTMxLDMsMTQ3LDI1MCw5MiwxNDcsNjAsODUsMTM4LDEwMCwxMzcsMTM4LDc0LDE0MiwxMDUsMjA4LDEyMiw5MiwxMzksNDRdfX19XSxbMSx7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNSwyMjIsNiwxNTUsNzksOTUsMTQwLDMyLDE2NywxNjcsMTMzLDIwLDE1NCw2LDQzLDQ1LDE1NiwxOTQsMjEsMTUzLDEzMiwxOTksMTI3LDE2MywxMzYsMywxODAsMTk1LDAsOTMsMjIsMTcyXX19fV0sWzIseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMjMsMTAyLDI1NSw3MCwxMCwyMDQsMTgzLDIwNiw0NSwyMTMsMTkzLDU5LDYzLDIzMyw4MCw4MSw2NSwxOSwxMTQsODcsNjMsNDcsMTc1LDIwNiw0MCwxMjUsMTIsMTUyLDE3NCw3OCwxNDUsNF19fX1dXSwiY3Vyc29yIjozfQAAAAAAAABYAAAAAAAAAA1Hcm91cFN0YXRleyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABIk9wZXJhdGlvbmFsIgAAAAAAAABSAAAAAAAACTdUcmVleyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJ0cmVlIjp7ImxlYWZfbm9kZXMiOlt7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTUsMjUyLDcyLDExMCw5Miw1NCw4OSwzMiwyNDAsMTMxLDY2LDE1Nyw5OCwxNzAsMjQ0LDY2LDcyLDE5MSw0OSwyMDIsMTk2LDE2MSwxNzMsMTcsMjIyLDE0Miw2NCwyMzEsMTg3LDE3MiwxNjksMTddfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMTg4LDI5LDI0NywyNDUsODUsMTcxLDE3Niw1NiwxMSw0MSw4OCwxODcsNzEsMjM3LDk4LDEwOCw2LDQwLDE3MiwyMjMsMTI5LDE4OSwxOTIsOTEsMjM2LDI3LDk0LDIyNCwxNzQsMTUyLDExNyw2N119fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbMjUwLDIwMiwxOTksMTM0LDEwMSwxNjEsMjM0LDE3MSw0NiwxMjgsMjMwLDQxLDE2NCwxMDMsNDYsMTQ4LDIxNCw1MywyNDQsNCwxNTUsMTgsNjksMTUyLDcxLDMxLDM2LDE2LDEzMSwxOSw5MywyMDFdfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbNjQsMTc4LDIxNiwxNDAsMjA3LDUyLDYwLDI1MCwzMSwxMjYsMjA4LDU5LDc0LDEzOSwxMDcsMTk0LDUsMjQyLDY3LDYxLDEwNCwyNyw1MSwxNSwxOTYsMjI5LDI0NCw4MSw1NSwzOSw5Niw3Miw5OCwzMiwxNTgsMTk5LDYsMTYzLDIyMCwzOCwxMzgsMzMsMTA0LDE2LDE0NSwzMCw2MCw3Niw3MSwxOTgsOTksNDgsMjA0LDEwMSwxMjEsMTgsMTE1LDIwMSwxOTUsMTUsNTgsNTUsMTM5LDExXX19fX0seyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzcxLDEwLDY5LDIwLDIsOTIsNzAsODksMjI0LDQ4LDEyNiw3OCwxMjQsMTg2LDE0OCwxNTksMjQ1LDE3NSwxMjIsMTU1LDE2LDIwOSwxNTMsMjE2LDIyLDE0MSwxMTcsNzUsOTcsNzIsMjQ3LDM2XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzExNiwxMTAsMTYsOTEsOTksNDQsMTcwLDEyMSwxNjQsMTAyLDEyMywxMjMsMTE0LDIzOSw3MiwxOTYsNjksNSw1OCw4NywyNTQsNDUsMTA3LDIyOSwxODIsNDIsMjksMTM1LDc2LDgzLDE5NywxOTRdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5OCwxMTEsOThdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IktleVBhY2thZ2UiOnsibm90X2JlZm9yZSI6MTcyMDY4ODk0OSwibm90X2FmdGVyIjoxNzI3OTUwMTQ5fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMjgsMTU0LDE4LDU0LDEyNCwyMzEsMjcsMTcyLDE5MiwxMDQsMTg3LDk4LDE4NSw0NCwxLDE1OCwyMDksMjEzLDI0MCwxNzksMTg0LDIwNCwxNDQsMTA3LDEyOSwyMSwxLDg3LDY5LDE1NywxMTIsMjA3LDIxNCwyMDUsMTMxLDEzNyw2LDM1LDIxNywyMjksMTk1LDU2LDEwOSwxNjIsOTYsODksMTc2LDI4LDE3Niwy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NCw3MSw5OSwxNjksMTI2LDgxLDE0Nl19fX0sInNlcmlhbGl6ZWRfY29udGV4dCI6WzAsMSwwLDMsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwyLDMyLDEwNywyMTQsNzIsMTg5LDIzOSwxNyw2OSwzMCwyNDMsMTk0LDY0LDU3LDIyNyw2MSwxOTEsMjM1LDIzNCwxMDIsMTA1LDIxMCwxNDgsMjQyLDEyOCwxNjksMTEsMjU0LDk5LDMxLDE3OCwzMiw2LDI0MiwzMiw2OCwxODYsMTc5LDE5NSwzLDE0MywyMjYsMTk0LDE5NCwyMzgsNjIsMjQ5LDE2NywxMTgsMTk5LDEwOSwxMTMsMjIyLDE0NSwyMzEsMSwxMDgsMTI0LDUwLDExNyw1Myw4MSwxMjIsMTc1LDkzLDIwNiwyMDEsOCwwLDMsNSwyLDI0MCw2NiwwLDBdLCJzZWNyZXRfdHJlZSI6eyJvd25faW5kZXgiOjAsImxlYWZfbm9kZXMiOltudWxsLHsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzUyLDEwMSwxNzUsMTU3LDIwLDEwMywxNiwyMTgsMTcyLDUzLDI0MCwyNSw3OCw5MSwxODgsMzksNTAsMTI1LDE3MSwyMywzNiwxNTYsMjgsNzIsODQsMTY5LDIxMywxMiwxMDIsMTQ2LDE1MSwyMjVdfX19XSwicGFyZW50X25vZGVzIjpbbnVsbCxudWxsXSwiaGFuZHNoYWtlX3NlbmRlcl9yYXRjaGV0cyI6W3siRW5jcnlwdGlvblJhdGNoZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzY5LDIxMCwxMywxMDcsMTY2LDE3MiwxMTAsMTIwLDI1LDE5Miw3NCw2NSw5NCwxMzUsNjAsMjU0LDI5LDExOSw3LDI0NiwxMSwxNjIsMjA1LDIwOSwyMDgsMjE4LDEzNSwxMDQsMTAwLDQ5LDE5OCwxODldfX0sImdlbmVyYXRpb24iOjF9fSxudWxsXSwiYXBwbGljYXRpb25fc2VuZGVyX3JhdGNoZXRzIjpbeyJFbmNyeXB0aW9uUmF0Y2hldCI6eyJzZWNyZXQiOnsidmFs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LDI4LDIxLDE0NCw5MCwxNDgsMTUzLDEzOSwzOCw3MSwzMCwxNTcsMTcwLDE2MCw3LDE5MiwxMTAsMTc3LDIxMF19fV0AAXsicHJvcG9zYWwiOnsiQWRkIjp7ImtleV9wYWNrYWdlIjp7InBheWxvYWQiOnsicHJvdG9jb2xfdmVyc2lvbiI6Ik1sczEwIiwiY2lwaGVyc3VpdGUiOiJNTFNfMTI4X0RIS0VNWDI1NTE5X0NIQUNIQTIwUE9MWTEzMDVfU0hBMjU2X0VkMjU1MTkiLCJpbml0X2tleSI6eyJrZXkiOnsidmVjIjpbODQsMTMwLDU3LDIyNSwyNiwxNzksMjQ4LDIwNyw1NywxOTYsMTQ5LDE2MSwxMzksMTQsMTQ4LDI0NywyLDE0OSw3NSwyMjYsMTEwLDI1MSwxMzIsMTcwLDE2MiwxODcsMTUwLDY0LDI2LDI0MiwyMjksMzddfX0sImxlYWZfbm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxMCw4MywyMzYsMTU1LDE5MiwyMTYsNDUsMTY2LDE3NCwxMDEsMTAyLDQzLDEyOCw5MSwyNCwxMTgsMTQ2LDU3LDE4OCwxMTEsMjIsMjEsNDIsMTQ0LDYyLDIwNSw1OSwxNTIsMzgsMjA2LDE4NSw3N119fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMDYsMTk2LDExNiw3MCwxOTMsNDksMjM1LDEyMSwxNiw2MiwxNjgsMTM2LDE2LDIwOCwxNjcsMTIsNTgsNTAsMiw1NCwxLDIxNCw3MiwyNTMsMTc5LDIzLDEwNiwxNDMsMTY0LDIwOCw4MiwxMl19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk5LDEwNCw5NywxMTQsMTA4LDEwNSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IktleVBhY2thZ2UiOnsibm90X2JlZm9yZSI6MTcyMDY4ODk0OSwibm90X2FmdGVyIjoxNzI3OTUwMTQ5fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMTM0LDI1NCwxNjgsMTA0LDUwLDk3LDcsMjQyLDIzMSwxOTEsMTE0LDEyMywxMzIsMzAsNjcsNTgsNSw1NywxODEsMTY5LDIzMiw4MSwyOCwxMDIsMTgxLDE4OCw5OCw1MywxMDQsMTgzLDI0MywyMSwyMTMsMjQ0LDEyOCwyNDgsMjI5LDE2OCw5MiwxOTYsMTcwLDE1MSwyMzUsMTgzLDQsMjMyLDIsMjA3LDIwNCwyMjUsMjA1LDE5LDIwNCwxNzYsMzEsMTE4LDIwOCwyMjksMTA3LDE4OCwyMywxNjIsMjI0LDVdfX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzE3OSwyOSw4NSwxODcsMTQxLDE4LDk1LDExLDExMSwyNDIsMTY3LDEyOCwxMDQsMjIyLDgxLDY3LDk3LDIwNiwyNTAsMTcxLDIyMCwxMDksNTMsMTc5LDE3MSwyMzQsNDMsMjEzLDAsMTA1LDQ5LDE1OSw5MSwxODIsMjMxLDc0LDc5LDgzLDE5OCwxMTIsMzAsOTksMjE4LDEzNCwxMDksMjQsMjMwLDI0OCw1MSwxNTgsMTAwLDM3LDEwMyw5LDE1MSwxMzgsMTQ1LDksODcsMTU5LDE2MiwxMzgsMjQ2LDFdfX19fX0sInByb3Bvc2FsX3JlZmVyZW5jZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxLDM3LDk0LDE4MiwzNSw1MywxMjUsMTg3LDE0OCwxNTUsMTQ0LDM1LDE3OSwxODQsMjgsMjEsMTQ0LDkwLDE0OCwxNTMsMTM5LDM4LDcxLDMwLDE1NywxNzAsMTYwLDcsMTkyLDExMCwxNzcsMjEwXX19LCJzZW5kZXIiOnsiTWVtYmVyIjowfSwicHJvcG9zYWxfb3JfcmVmX3R5cGUiOiJSZWZlcmVuY2UifQAAAAAAAADlAAAAAAAAARhTaWduYXR1cmVLZXlQYWlyeyJ2YWx1ZSI6WzE4OCwyOSwyNDcsMjQ1LDg1LDE3MSwxNzYsNTYsMTEsNDEsODgsMTg3LDcxLDIzNyw5OCwxMDgsNiw0MCwxNzIsMjIzLDEyOSwxODksMTkyLDkxLDIzNiwyNyw5NCwyMjQsMTc0LDE1MiwxMTcsNjcsODIsMTE3LDExNSwxMTYsNjcsMTE0LDEyMSwxMTIsMTE2LDExMSw4MywxMDUsMTAzLDExMCw5NywxMTYsMTE3LDExNCwxMDEsNzUsMTAxLDEyMSw4LDddfQABeyJwcml2YXRlIjpbNzAsMjEzLDY5LDIyNSw4NywzOSwxNDEsMTE3LDExMiwxNyw0Miw1NywyMDQsMTcsMTM4LDM1LDc1LDkxLDE1OSw3NSwxNTIsMjIwLDExNywxMCwyMTcsMjAwLDE0MSwxNzAsMTg0LDc0LDIyOCwyMDZdLCJwdWJsaWMiOlsxODgsMjksMjQ3LDI0NSw4NSwxNzEsMTc2LDU2LDExLDQxLDg4LDE4Nyw3MSwyMzcsOTgsMTA4LDYsNDAsMTcyLDIyMywxMjksMTg5LDE5Miw5MSwyMzYsMjcsOTQsMjI0LDE3NCwxNTIsMTE3LDY3XSwic2lnbmF0dXJlX3NjaGVtZSI6IkVEMjU1MTkifQ=="]},"MLS_256_XWING_CHACHA20POLY1305_SHA256_Ed25519":{"group_id":{"value":{"vec":[214,206,173,27,206,87,231,4,172,30,130,210,232,69,61,222]}},"storages":["AAAAAAAAAA0AAAAAAAAAXAAAAAAAAAAFVXNlUmF0Y2hldFRyZWV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAFmYWxzZQAAAAAAAABaAAAAAAAAAYZHcm91cENvbnRleHR7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7InByb3RvY29sX3ZlcnNpb24iOiJNbHMxMCIsImNpcGhlcnN1aXRlIjoiTUxTXzI1Nl9YV0lOR19DSEFDSEEyMFBPTFkxMzA1X1NIQTI1Nl9FZDI1NTE5IiwiZ3JvdXBfaWQiOnsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0sImVwb2NoIjowLCJ0cmVlX2hhc2giOnsidmVjIjpbMTg0LDQxLDEzNCwyMTMsNzIsMjcsMTcxLDEzNCwxNjAsNjQsMTQsODQsNTEsMjM0LDIsMjIxLDEyMyw4MCwxNTUsMTY4LDI0OCwxNTYsMTEzLDIzOSwxLDI1MiwxODcsMTI5LDI4LDU4LDEwMCwyMTFdfSwiY29uZmlybWVkX3RyYW5zY3JpcHRfaGFzaCI6eyJ2ZWMiOltdfSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0AAAAAAAAAXQAAAAAAAACFQ29uZmlybWF0aW9uVGFneyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYWNfdmFsdWUiOnsidmVjIjpbNTAsMTUzLDI1NSwxNDAsOTQsNTksNTgsNDEsMjMzLDEyNCw3MSwxMTQsMTg3LDIxNywyOCw3OCw1LDQzLDE1Nyw5LDEzOCw1MSw1LDIxMiwxMDMsMjA3LDIyNCwzNCwxMTQsNDQsMjI4LDE3NF19fQAAAAAAAABcAAAAAAAABDBNZXNzYWdlU2VjcmV0c3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X2Vwb2NocyI6MCwicGFzdF9lcG9jaF90cmVlcyI6W10sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE2LDMxLDE1LDg5LDIzMyw2OSw3MywxMTcsMjUyLDExNSwxNTAsMTQ5LDEyMiwxMjAsOTYsMTA2LDEzMiwzMiw5NywyLDIwLDI0NywyMTEsMTA0LDIyOCwxOCwyMzMsMTAxLDIxMiwxMzksMTUyLDIzOF19fX0sIm1lbWJlcnNoaXBfa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxODksMTI2LDE3LDExNiwxNzQsMjQ1LDAsNTEsMjA1LDIwMywyNTQsMTQxLDY0LDEyNSwxNjMsMjAzLDE3MCwwLDI0MywzNSwxNDAsMTE5LDE3Nyw1MiwyMTAsNTQsMjQ0LDI0Miw2MSw1LDI0Myw5NV19fX0sImNvbmZpcm1hdGlvbl9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE4NywyNTEsMTk0LDc2LDU0LDE3NSw4MSwxNDYsNjcsMjA2LDEwMSwyMzksMzIsMTU2LDI1LDE2MywxNTAsNjYsMzMsMTc3LDExMSwxNTMsNDMsMTAxLDE2NCwxMTMsMjMsMjQzLDIzNywyMDksMjIwLDQ2XX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsNzcsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwwLDMyLDE4NCw0MSwxMzQsMjEzLDcyLDI3LDE3MSwxMzQsMTYwLDY0LDE0LDg0LDUxLDIzNCwyLDIyMSwxMjMsODAsMTU1LDE2OCwyNDgsMTU2LDExMywyMzksMSwyNTIsMTg3LDEyOSwyOCw1OCwxMDAsMjExLDAsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W3sic2VjcmV0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NSw1NCwyMDEsNTEsMjQ4LDE4NCw4NSwxNDksMTgzLDU3LDIwOSwyNSwxMCwzNyw4NCwxNDYsMjE2LDE1MSwxNDEsNDMsNzgsMjIzLDE2NiwxMjAsNjEsMTk2LDEwOCw2NiwyOCwzLDEwNyw2NiwyNywzMywzMiwxNDAsMjIxLDE2NCwxMzEsMiwxNjAsMTM1LDk3LDE2MSwxMjEsODcsOTcsNjAsMTU4LDEwMywxNDEsOTgsMjI4LDIwLDE1MywxMTUsMTAwLDEwOCw4OCw2NCwzNCwxMDYsMTAyLDg4LDMsMTE1LDgsMTM1LDE5MiwxNDAsMjUyLDM5LDg4LDIyMCwxODcsMywzMywxNTcsMjIwLDEyOSw2Nyw2OSwxNzEsMTA0LDQ4LDIxMCwxNzksNiwxNTUsMTAxLDE0OCwxNzgsNzYsMjUwLDUzLDMyLDE5NywxMCwxNTEsNDksNTQsMTgwLDIzMCwxNjUsNzAsMTQ4LDIzMywxODYsODEsNTMsNjcsMjEyLDUsMTk3LDE3NywxMDYsOTAsMTkwLDIyNywxNTIsNjQsMjAwLDE2MywyMjQsMjYsMTgzLDEzMCw1NywxMTYsNTAsMjEwLDUyLDEyMCwxNzYsMTY0LDIwOCwxNzksMTg2LDUwLDk3LDkxLDI1MCwyMTYsMTg1LDMxLDI0OCwxODcsOTIsMTU1LDIwNSwyMzksMzQsMTg2LDI1NCwyOCwzNiw0MCwwLDI4LDIyMyw0MCwyNCwxMzIsMjE0LDEwNSw2LDM0LDg5LDExMiwyMzEsMTEsOSwxMCwxNTksMTUxLDIxNCw4LDEzMSw0MCwxNDksNTIsMTYyLDI3LDM4LDg1LDE3NSw5MywxNDQsNjAsODcsOTIsNCwyMDksMTk4LDExNiwyNywyNTAsODYsMTc2LDEyMCw5OCw0MywxMDQsODksMTMxLDExLDk4LDExMSw2NSw4OCwxNjYsMTA2LDQ5LDgxLDY3LDUsMTE3LDIzNSwxNTEsMTEyLDI4LDY2LDk0LDEzOCwzNiwxMzQsMTY3LDE4MCw5MCwyNDYsMTQ2LDksMTQ4LDEyNywyNTEsMjI3LDEwNSw2NCwzNCwxNDQsMTEyLDE2Myw2MywxMDUsMTcyLDU3LDIyMywyNDIsNDMsMTI3LDgzLDk1LDE0MiwyMTcsMzcsNTQsOTksNzEsMTQsMjE4LDE4OCwyMTIsMTEyLDk5LDMzLDEyMiwxOTgsMjI5LDEyNCwxMjAsMjMwLDY0LDE4OSwyMDUsNDIsMzgsMjAyLDkyLDU3LDczLDY0LDcxLDgxLDEzOCwxMjQsMjksMTE1LDE3MSwzMCwxNjMsMTUsMjQ2LDEyOSwzMiwxMiwzNCwxNTYsOTEsNjYsODQsMTg4LDE2MCwyMDcsNjAsMjE0LDE0MiwxMDMsMTEsMTYxLDE1LDE0NiwxNzQsMTkxLDExNCw1MCwyMjcsMTcwLDc3LDUyLDEzMywxOTYsNiwxNDgsODgsMzAsMTgyLDE5MywxMjQsNjYsMTE2LDQ4LDEyMywxNDgsMjIyLDQ4LDE3NSwyNDQsMTk4LDgwLDU4LDEyMiw4Nyw1OCwxMjAsMTI5LDE4OCwxNzYsMTMsMCwyNTAsMjA0LDgyLDcwLDE4NywyMDQsMTM2LDEwMCwyMTAsMjE0LDE0Myw2LDEyOCwxMzQsMTI1LDI0MCwyMDUsMTkwLDgzLDE2NiwyNCwxNTAsNiwxNzIsMTIzLDk4LDIzOSwyMzYsMTYwLDIxNSw5NywxNywyMjIsMjA0LDE2OCwxMTIsNjcsOTYsMTI3LDE1MiwxMzIsMTkxLDEzNSwxNjksMjI2LDE3NywxNTgsMjIzLDE4NywxOTQsMjEsMTA1LDE4OCwyMzIsMTMxLDE3NSw2NCwyMDksNDUsNjYsMTQ5LDU2LDIyOSw3LDQzLDYzLDk4LDEzNiwxOTQsMjA0LDEyMywyNiw0LDExMCwyMTQsMTMwLDE4NywyMCw5OSw5Myw1Nyw4MywxNzYsMjE0LDEyMywxODYsMTAwLDE2LDc0LDIyNiwyNDIsMTQ1LDEyOCwxNjMsMTUsMTE5LDIwMiw1NiwxMDEsMTY4LDE0MCw1MCwxNTIsMTI0LDEzNiwyMCwwLDU5LDE2Nyw1OCwxMDIsNzAsMTMwLDI0NSw0Myw5NywxMzQsMTc2LDEwNiwxMjMsMjAzLDE0OSwxODksMjIsMTk2LDIxMSw3NCwxNTIsMTYxLDIyNiwxMjYsMjIwLDIzMiwzMCwyMzUsOTksNjEsMTc3LDIxOSwyOCwyMDgsMTU0LDEwNSw0MywxMDEsMjAzLDI0NiwzNSw0Niw3OSwyNTAsMTYxLDQ2LDM1LDg4LDE2LDQzLDIsMTgwLDM0LDExMiwxNjEsODMsMTExLDkyLDE5OSw1NiwyMzAsOCwxMjIsMTY0LDE2OCwzOSwxMjUsMTc3LDE5MSwxMzQsNCw3NywxNzcsMjMyLDExNCwyOSw1MiwxODAsMjQsMywyMDYsMTIsMTg1LDM4LDE5NSwyMzMsMTgxLDEyNiwyMDgsMTYsMjIwLDEyLDg1LDI0LDU1LDMyLDE5NSwxODQsMTAsNDQsMjEwLDc0LDIwNiw0MCwxODEsOTMsOTIsMywxNjYsMjMsMTE4LDQsODUsMzAsMTcsMjE2LDEwNywyMzksMTMzLDExNiw2MiwxOTIsMTMzLDEzNCw0NCw5OSwyNDEsMTM5LDUzLDY5LDk2LDYxLDI0MCwxMzksOTUsOTIsNywxNDksMTMwLDQ0LDE4MywyMiwxODIsMTc1LDIzMiwxODYsMTgwLDE5OSwyNDAsMzUsMjQyLDEyMCwxMDgsNjEsMTg0LDM2LDM0LDE2MCwxODYsNzMsNSwxNzAsMjM2LDIwOCwxODcsMjM5LDIzLDUyLDAsMTk2LDEwMywxODgsNDksOTAsMjE4LDEwNywzMywxMzUsMTY2LDE5LDE4MiwxNTEsMTksOTIsNzEsNDgsMjQ0LDIwMSwxNDgsMjAwLDEyLDMzLDE0Niw1MiwxODIsMTQsMjQ0LDg0LDIxNiwyMTgsNTAsNjEsMjIwLDYsNSwxMTgsMjQsMTEyLDI0MCwxNTUsMjM5LDI0OCwxMzIsMTYsNjksMTI2LDE4OSwxMDYsMTQzLDE5NiwxNywyMDQsMjA5LDIyOSwxMTksNDUsMTE5LDg1LDY0LDIwOSw3OSwxMjIsODksMTQyLDUzLDE0OCwxNTgsMTM0LDE2NiwxMzIsMTM4LDI0NCwxOTQsMTk5LDE3MSwxMDUsNDcsNDEsOTgsNjksMjQ4LDMzLDQ2LDE3OSwxNzIsMjQ3LDEzNiw0NSwxMDcsMjE2LDE4MiwxOTYsOTcsNzksMjQsMjUwLDE3OCwxMDYsMzQsOTIsMTQ0LDQ0LDI2LDI0MCwyNDksMzcsMjEsMjIwLDExNSwzNSwzOSwyMSwyMywxOTUsMTczLDE1OSwyMDgsMTc3LDEwNCw1MSwxNjEsNzksMzUsODYsMTQ5LDE3OSw5MiwyMiwxNjUsOTMsOTAsMTM3LDEyMywxMzQsMTc5LDYzLDE5MCw3MSwxODAsMjQ1LDI0OSw4LDEzOSwxMzcsMTk2LDIzOSwyMDQsMCwxMTgsNjksMTAsNzksMTUzLDExNiwyMzIsMTAsOTksNzMsMTYsMjAwLDgsMTM2LDE1LDE0OCwyNDIsNSw0OCwwLDEwLDI4LDEyNCw3MCw3Myw3LDE2NCwyMjQsNzIsMjA0LDEzNiwyNiw1MSw2NSwxMSw2OCwxODYsMTE4LDE2MCwxNzQsMTE3LDY3LDcyLDksOCw2NSwxODgsNjcsMTUyLDExNCwxNDQsODgsMiwyNiwxNDksNjcsMTksNzgsMzQsMTQ3LDE0LDk3LDE0OCwyNDEsMjMxLDM5LDIyMSwxNzcsMTMxLDIxMiwxNywzMiw4OCwyMTcsNDYsMjA4LDE4LDExOCwyMzgsMjMzLDEwMywxNzcsMTcyLDkxLDMxLDE2NywzMCwyNTEsMTY1LDE2MywxODcsMTQ0LDE1MSwxNzgsMTE5LDk3LDIyNSwyNDQsODEsMTMxLDE4MiwyMDYsNDYsMjUsMjA0LDEyMiw0OSwyMiwxMzMsMzgsMTYzLDI1MSwxMDMsMTE5LDI0NCw4OCw0NCwxNTUsMTAyLDE5MiwxNzUsMTc3LDEyMCwxMzksMzYsMjksMjQsNTksMjUsMjI1LDEzNCwxNDksMzgsNjgsMTc1LDE3MywyMzEsODksMTc1LDM3LDExMyw4MywyNDQsMTY2LDIwNiwyMDgsOTcsMTQ5LDUyLDgwLDMwLDQ4LDEwMSwxNTIsOTcsMTM3LDE4NywyMTQsMTY0LDkwLDE2Miw2NiwxNzcsMzEsMTM4LDI1MCwxMTgsMjUxLDMyLDEzNiwxOTcsMjMxLDQsNTEsMTc1LDE5NCwxODQsMTk4LDcsMjM3LDk0LDIyNCwyMjIsMjUxLDM0LDIsMjMwLDUsMTIwLDE5Nyw0Myw3NSwyNywxNjUsMTIwLDQsNTAsMiwxNzAsMTkxLDc2LDE1Miw3NSwxMTUsMTAyLDE5Nyw0NCwyMjQsMTcyLDEwNSw4MiwxNDQsODMsMTM2LDEwOSw4LDE0MSwyMjgsMTEsMTM3LDIwNiwxNDcsNDhdfX0sInByaXZhdGVfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsyMzcsMjQ3LDIzLDEzMywzOCwyNiwzMyw4NCw4NCwzMSwxNDgsMjYsNzMsMTU2LDc3LDE4MSwxMiw4OSwxMDcsMjEwLDIwMSwyMDAsNjYsMTYwLDQ5LDE0OCwxMDksMjQ0LDM5LDQyLDMsMTgwLDEzMCw1NSwxOTIsMTI1LDIwNCwxMjQsMTUwLDEwMCwxNjIsMjAsMTM1LDE2MCwxMjEsMTg5LDE5Miw5OCw5OCwxNDgsMTI0LDI0NCwyMjQsMTcyLDE0MiwxMjMsMTQ4LDE2NywyNTIsMzAsMzksMTg3LDY3LDE5MiwxMzgsMTg5LDY2LDc2LDE4Niw1LDIyNywxMDksMTIxLDEzNiwxNTMsNTEsMTQ2LDIwNywxMzIsMTMzLDM4LDE2LDIwLDE2MiwyMzMsMjQyLDE2NCw4OSw1Niw0NiwxNjYsMTk3LDk0LDIwOSw5OCw3NiwxODIsMTc4LDExLDE2NCwxOTcsOTUsMTU4LDU5LDI4LDE4NiwyNDYsMTEsMTYwLDEwMywxODUsMTM3LDE1NSwxLDE5MCwyNDIsMjAyLDM3LDIyOSw3NSwxMzIsMjQsMTIwLDMwLDE4LDc1LDE5NywxMzksMjcsMjEyLDExNywxMjksMjEsNDMsMTYzLDEzNSwxNDAsMTMsMTgzLDE2NywyMDIsMjIwLDIxOSwyMSwxODUsNTEsMTMxLDE5MSwyMCwxODEsMTg0LDIwLDExMSw4NSwyMTksMTA5LDkxLDI0LDMxLDc4LDE2NCw1NSwyMDksMjM2LDE3MSw3Nyw5OCw5MSwxNjIsMjA0LDIwNywxOTQsMTY5LDExOCwyMzEsMjA4LDE3OCw5MywxMDcsMzksMjYsNjUsODAsMTM1LDE3LDE3NiwyNTEsMTI0LDEyLDEyOSw4LDE4NiwxNDIsMTcwLDMsNDMsMjI0LDcwLDc2LDMsMzgsMjMzLDk3LDM5LDgwLDczLDEzMiw0NywyMDMsMTI5LDE2MiwxMzEsMTQxLDE5MCwxNjksMTA5LDUsMTM2LDE1NCw0NywxNjIsMjUsMjksMjI4LDE2NSwxNjIsMjMwLDEyMCwxMTQsMTk3LDEwMywyMjYsMjMwLDExMiwyMzUsMTMyLDE3LDIyOSwyMDksMTA1LDksNzIsMTU1LDkwLDIzNiw3MSwyMTgsMTk5LDEyMCwxNjIsMTc5LDEzNSwxOSw4OSwxNjUsMTkxLDY3LDE1NCwzNiwyNDAsMTM3LDYsMTUzLDE4NywxMTQsNiwxOTUsMTUxLDIzLDQ2LDIwMywzNywxOTMsNzUsMTUwLDEyMSw0MCwxNjYsMTc3LDExLDEsNDQsMjE0LDI0NSw4OCw3OCwxOTMsNjEsMjAxLDEwMCwyMCwyMjMsMjMsMTYxLDIyMSw1MiwzNywxNzYsMTgsMTAwLDEyLDE4NiwxNzYsMjMwLDI0MywwLDEzLDk5LDIyLDMxLDE1MSwxNTQsMTYsMTEzLDgsMTQ4LDI0Myw5OSwxMDMsMTAzLDc2LDEzMCwxMzYsMTY2LDIwMCwyMCwxNCwyMzUsMTcsMTk1LDIyMSwxNDcsNTksNTEsMjQ0LDE3LDE2MywzMyw2LDIyNiw3MiwxMDksMTM2LDE5Nyw0MCwzNSw3MCwxNTIsMjM3LDg1LDE2LDI3LDE3MCw5MSwxNTcsNCw1MCw5NywxMDcsMTE2LDY4LDQ5LDE0MSwxMDYsMTI4LDE5MywxNCwyOCwxMjksMjM5LDcyLDYyLDIwMCwyNDYsMTA2LDM2LDUyLDg3LDYzLDE1MywxNDMsMTg0LDIsMTE4LDE1NSw3NiwxMzIsMTY5LDE5LDExMywyMDksMjMyLDE2MiwyMSwyMzIsNTUsMTM4LDM0LDEwNCw4Niw3Miw3NSw2NSw4MCw1MCwxNzQsMTA0LDE4NSwyMzksMjEzLDU5LDQxLDY1LDUxLDE2NywyNSwxODUsODYsMjMwLDcsMTg3LDI1MSw4OSwyNDksMjMyLDcsMTI0LDk5LDE1NSwxNTUsMTk3LDIwMywxMzMsMTA0LDE3NywzMSw5LDEzNiwxNDQsMTEsOSwxLDE3Miw2OSwyNDEsMTE5LDE3OCwzMCwyNDUsMTE5LDM5LDIxOSwyLDE4NSwxMzAsMTY0LDMxLDI1Miw5MCwyMjUsMjI2LDExMSw3MiwxODAsMTM1LDEwMiwxNjgsMTQ1LDEzMSwxNjQsMTIwLDEsMTcyLDE1MCwxNDAsMTc5LDM2LDIwMCwxNDgsMTI3LDEyNyw1OCw5NiwxOTUsODAsMTMyLDIzNiwxOTMsMTU4LDIzLDQyLDcyLDIyMyw5OSw5NCwyMjYsMjI3LDgwLDM4LDE1MywyNSwxNjEsODcsMTMsMTM4LDEzOSwzOCwyNSwxODYsMzQsMTY3LDI0Nyw4OSwyMDYsNjQsNjAsOTYsMTE3LDEwNiwxOTIsMTUxLDE4OCw5NCwxMSw2NSwxOTUsOTAsMTQ4LDE2NCwyMTYsMjA1LDE3NCwxNTYsMTU5LDM1LDYsNTcsMTAwLDE2NSw4Nyw0OSwzNSw4NCwxOCwwLDc5LDE1MCwyNywxMzMsMjI3LDI0NywxMywxLDIzLDcwLDE5NSwxODYsMTkzLDcwLDY3LDE1OSw4LDI0MywxMzQsMTIyLDg5LDE1MSwxMDcsNzUsNDIsMTc2LDEyMiwyMDMsMTkwLDY4LDE2MCwxNjUsMTk2LDU5LDI1LDM4LDExMywxNSwyMTYsMTc1LDIzMCwxMSwxNjMsMzMsMjQ5LDE5NywxMDksMCw0MSwyOSw3Myw5NiwxNCwxNjQsMzUsNDgsMjA5LDk0LDE5NCwxMDIsMjYsMTU5LDI1Miw1LDI2LDI0Myw2OCwxMDEsMzgsMjA0LDE4MCwxMjgsNjgsNjAsNjcsODUsMzksNiwxNiw4Nyw3MywxNDUsMjA4LDIwNCwzMCwxMzYsMjAxLDIwLDk4LDExNCwxMjAsMTY1LDIzNCwxODksMTM3LDE5NSwzMCwxNzEsMjM2LDEwMyw4MSw4MSwxNzgsNDIsOTAsMTYyLDAsMTY5LDEyNCwxNDQsMjQzLDY4LDM3LDk2LDEwNSwxMTQsOCwxODQsMSwxMzgsMTAxLDE1NSwxNjcsMjA1LDE0OCwyMDgsMTUsMTM3LDIsMTMwLDEyOCwyNDEsMTM4LDI0Miw1NywxNDEsMTk5LDEwMiwxMzYsNjcsMjExLDEwNCwyMDAsNzYsMTM3LDQ3LDEwNywxMTcsMTUwLDE4LDc0LDE1OCwxOTQsNzIsMTI3LDEyMiwxNjQsNTgsMTA1LDI5LDEzNywxODQsNzYsNzksODEsMTMzLDI1MCwxODIsMTgzLDExNCwyNTEsMTA0LDQzLDI0NSwxMTIsNiwyMTAsMTYyLDM5LDkwLDE2OCw2MCwyMTAsNzEsMjI5LDIwMiw2NCwyMTYsMTMxLDE0NSwyMTAsMjAyLDE5OCwyMTEsODEsMTI3LDg4LDE1MiwyMDUsODksMjAyLDEyNywxODksMTM4LDUyLDIwNywxMDUsODksMTc5LDksMTA0LDI0NSw1MSw0LDI0MiwyMzQsMTIyLDExMSwxMiwxMzQsMjM0LDEyLDk2LDM0LDE0OSwxNzUsNzksNzMsMzksMTQxLDIxMywxNDUsNzcsMTMxLDEzMSwxMDQsNzMsMTAyLDEyOCwyMTUsNTQsMzUsMjQ2LDM5LDMxLDI0OSw3OSwyMTYsMjAwLDk0LDE0MCwyMjAsMTE4LDQ2LDM1LDIsMTEwLDIxNCwxMiwxMzksMTgxLDkwLDM4LDE2NywxNjcsMTIyLDI3LDE0Miw0OCwxNzIsMjYsODUsMzMsMTE4LDE2MiwxODQsMTA5LDEyMCwxMjAsNjcsNDQsMTIyLDQ4LDQ2LDE5Miw4Niw4MCw5MSwxMyw1MywyNTIsMTA1LDksMzIsMTEzLDIxMSwzLDEyOCwxMjIsODksMTU1LDE0MCwyMTgsMiwxMDIsNjcsMTUzLDE5MCwyMzIsMjYsMzYsMTMxLDIsOTMsMTM3LDcxLDIxNCw4NCwzMywxMSwyMTEsOTYsMTQ0LDUxLDE4NiwyMDksMjI2LDEyMSwxMjQsMTYwLDg4LDg4LDE4MSw5OCwyNTUsOCwxODEsMjIzLDE4NSwxOTcsMTI0LDI4LDIyLDIyMCw5NiwxMjcsMTc5LDEyNCwxNDksMjMyLDY4LDI5LDIwMSw3MCwxMjAsNDksODUsMTE5LDE3LDE3OCwxMiwxNzYsNDAsNDUsMjE2LDExMiwxLDE2OCwxMzUsOTUsMywxMTYsMjAxLDExMCw1MywxOSwzNSwxMjMsMjMsMTgzLDQzLDE4LDExNiw1MiwxNjEsMTIwLDExNiwxMTEsMTk1LDYsMjA0LDEwNywxMzksMTUwLDI0LDEzMywyMiwxNDgsNTMsMTM1LDE1MCwxMTIsODAsMjQ0LDE3LDEwMiwyMTAsMjQzLDIwNSw1Miw2MCw4LDkwLDEyLDE5MCwyNDgsMTAwLDEwNSwzOSwxODMsMTMxLDQ5LDExNCwxODEsMywxMDMsMTk3LDE4OCwxMTUsMTYxLDM2LDEwLDE0NCw5NCw4MSwxNjcsNzMsNCwxOTcsNDMsMTUyLDUwLDE5NywxOTQsNjksMTk1LDE2NiwyMDEsMjEwLDI1MiwxMDYsNzAsMjQ4LDE2LDEwMCwyMzIsMjAxLDE4Nyw1LDE5Miw1Niw3NSw3OSwxMTEsMzMsNSwxNTIsMTAsMjAwLDE5LDUyLDE2MCwxMTYsMTY1LDE0NiwxMSwyNDMsMTA2LDI0OSwyMzYsNzksMTU3LDg1LDk4LDY3LDE2Niw4NCwyMTQsOTgsMTI3LDUyLDU2LDEwMiwxMTksMjExLDIwMywyMDksMjMsMjQsMTM3LDE5Miw2MiwxMzAsMTMwLDEzMiw4MCwxNTYsMTM2LDIyNywxNjIsMTYwLDQzLDI2LDkyLDE4OSwxNzYsNzEsNDksMTk5LDEyNiwyMDEsNjksMTcsNjcsMiwxMTMsMjA1LDEyMiwxNzUsMjA2LDE4OCwxNTcsMiwxOTgsMTEsNjgsMzQsMTIxLDE5Nyw2Nyw4NiwxNTUsMTM0LDE5Miw2OCwxNTQsNzgsNjEsMjUwLDgsMTE5LDEwNywxNTQsMTY5LDUxLDU4LDMyLDE2Niw2MywxMDQsMTg3LDEwMCwxOTIsMjAsMTQ2LDIzOCwyMSwyMCwxMTMsMTMwLDE1LDYsNTIsMTM5LDU2LDIwOCwxMjQsMTg1LDIxMiwyMDcsMzEsNTgsMTcxLDE0NywyMTQsMTE3LDQzLDE1MywxMjcsMjEsMTUyLDU3LDYzLDcxLDE5MywyMTcsMTU2LDE5NywxNjcsMTcxLDE1NCw5MCwxNjQsMTE1LDgxLDE1NCw4OSwyNywxMzMsMTE5LDg5LDE3LDIsMTczLDEzMSwxODYsMjE5LDc0LDk0LDczLDE4NywyMDMsMzEsMjQ0LDE2OSwxOSwxMDMsMTIsMTQsOTksMTY1LDIyNSwxMzgsMzYsMTk3LDEzNSwxODcsNjgsMTU1LDMxLDExMCwxMDgsOTAsMjIsODEsMTM3LDE5MSwxMDUsMTg5LDksMTcyLDgxLDIwNywyMTAsMzQsNzEsMTE1LDE4NCwxMTEsNDEsMTQzLDUzLDM2LDE5NSwxNiw3MSw4NSwyMzIsMTIwLDc1LDE5NywxNTYsMjA0LDE5LDM4LDE3OCwyLDIwLDE1MiwxMDMsMjcsNjYsMjIyLDU4LDIwMiw3OCw0Myw2OCwxMTgsNzEsOTMsMzMsMTU0LDEyNSwyMjcsMTQ2LDU2LDEzMiwxOTMsMTY3LDg3LDE1NiwyMCwyMDMsNDEsMTI2LDExOSwxMCwxOTUsMjgsNCwyMiwxMzEsOCw1Niw4Niw3LDE4NCwzMyw4NCwzMiwxMjgsMjMsMCwyMTMsMTU0LDExOSwzMiwxNjQsNTcsMjQzLDE4MCwxODMsMjIsMzksMTk3LDExMiwxNDgsOTEsMjE4LDE5OCwxNzUsODQsMTEzLDE5OCwyMDcsMzgsMTIxLDE4LDE2Miw0OSwxNDAsMjI0LDU3LDIzNiwzMiwxMiw5NywyMTcsMzcsNDUsMTc2LDc2LDEwOSw4Miw3LDE2OSwyNDgsMTI3LDI2LDU4LDExNSwxMTcsMjA5LDE2NCwxNzAsMTUwLDE2MCw2NCw5MCw1Nyw0OCwzMiw5Myw1NCwyMjAsMTA4LDE4LDI0Myw1MCw3LDI0MiwxMTAsNTEsMzksMTk5LDI1Myw1MSwxODcsNzgsMTM1LDQxLDc4LDE4NiwyMDcsMjAxLDI0Nyw3MSwzMCw5Nyw0MywxMjQsMjMyLDAsNTgsMTQ1LDEwLDI0MiwxNjksMTg4LDI0Myw4Miw1Myw5MywyOCwxMDQsNjUsNTQsMjAxLDUxLDI0OCwxODQsODUsMTQ5LDE4Myw1NywyMDksMjUsMTAsMzcsODQsMTQ2LDIxNiwxNTEsMTQxLDQzLDc4LDIyMywxNjYsMTIwLDYxLDE5NiwxMDgsNjYsMjgsMywxMDcsNjYsMjcsMzMsMzIsMTQwLDIyMSwxNjQsMTMxLDIsMTYwLDEzNSw5NywxNjEsMTIxLDg3LDk3LDYwLDE1OCwxMDMsMTQxLDk4LDIyOCwyMCwxNTMsMTE1LDEwMCwxMDgsODgsNjQsMzQsMTA2LDEwMiw4OCwzLDExNSw4LDEzNSwxOTIsMTQwLDI1MiwzOSw4OCwyMjAsMTg3LDMsMzMsMTU3LDIyMCwxMjksNjcsNjksMTcxLDEwNCw0OCwyMTAsMTc5LDYsMTU1LDEwMSwxNDgsMTc4LDc2LDI1MCw1MywzMiwxOTcsMTAsMTUxLDQ5LDU0LDE4MCwyMzAsMTY1LDcwLDE0OCwyMzMsMTg2LDgxLDUzLDY3LDIxMiw1LDE5NywxNzcsMTA2LDkwLDE5MCwyMjcsMTUyLDY0LDIwMCwxNjMsMjI0LDI2LDE4MywxMzAsNTcsMTE2LDUwLDIxMCw1MiwxMjAsMTc2LDE2NCwyMDgsMTc5LDE4Niw1MCw5Nyw5MSwyNTAsMjE2LDE4NSwzMSwyNDgsMTg3LDkyLDE1NSwyMDUsMjM5LDM0LDE4NiwyNTQsMjgsMzYsNDAsMCwyOCwyMjMsNDAsMjQsMTMyLDIxNCwxMDUsNiwzNCw4OSwxMTIsMjMxLDExLDksMTAsMTU5LDE1MSwyMTQsOCwxMzEsNDAsMTQ5LDUyLDE2MiwyNywzOCw4NSwxNzUsOTMsMTQ0LDYwLDg3LDkyLDQsMjA5LDE5OCwxMTYsMjcsMjUwLDg2LDE3NiwxMjAsOTgsNDMsMTA0LDg5LDEzMSwxMSw5OCwxMTEsNjUsODgsMTY2LDEwNiw0OSw4MSw2Nyw1LDExNywyMzUsMTUxLDExMiwyOCw2Niw5NCwxMzgsMzYsMTM0LDE2NywxODAsOTAsMjQ2LDE0Niw5LDE0OCwxMjcsMjUxLDIyNywxMDUsNjQsMzQsMTQ0LDExMiwxNjMsNjMsMTA1LDE3Miw1NywyMjMsMjQyLDQzLDEyNyw4Myw5NSwxNDIsMjE3LDM3LDU0LDk5LDcxLDE0LDIxOCwxODgsMjEyLDExMiw5OSwzMywxMjIsMTk4LDIyOSwxMjQsMTIwLDIzMCw2NCwxODksMjA1LDQyLDM4LDIwMiw5Miw1Nyw3Myw2NCw3MSw4MSwxMzgsMTI0LDI5LDExNSwxNzEsMzAsMTYzLDE1LDI0NiwxMjksMzIsMTIsMzQsMTU2LDkxLDY2LDg0LDE4OCwxNjAsMjA3LDYwLDIxNCwxNDIsMTAzLDExLDE2MSwxNSwxNDYsMTc0LDE5MSwxMTQsNTAsMjI3LDE3MCw3Nyw1MiwxMzMsMTk2LDYsMTQ4LDg4LDMwLDE4MiwxOTMsMTI0LDY2LDExNiw0OCwxMjMsMTQ4LDIyMiw0OCwxNzUsMjQ0LDE5OCw4MCw1OCwxMjIsODcsNTgsMTIwLDEyOSwxODgsMTc2LDEzLDAsMjUwLDIwNCw4Miw3MCwxODcsMjA0LDEzNiwxMDAsMjEwLDIxNCwxNDMsNiwxMjgsMTM0LDEyNSwyNDAsMjA1LDE5MCw4MywxNjYsMjQsMTUwLDYsMTcyLDEyMyw5OCwyMzksMjM2LDE2MCwyMTUsOTcsMTcsMjIyLDIwNCwxNjgsMTEyLDY3LDk2LDEyNywxNTIsMTMyLDE5MSwxMzUsMTY5LDIyNiwxNzcsMTU4LDIyMywxODcsMTk0LDIxLDEwNSwxODgsMjMyLDEzMSwxNzUsNjQsMjA5LDQ1LDY2LDE0OSw1NiwyMjksNyw0Myw2Myw5OCwxMzYsMTk0LDIwNCwxMjMsMjYsNCwxMTAsMjE0LDEzMCwxODcsMjAsOTksOTMsNTcsODMsMTc2LDIxNCwxMjMsMTg2LDEwMCwxNiw3NCwyMjYsMjQyLDE0NSwxMjgsMTYzLDE1LDExOSwyMDIsNTYsMTAxLDE2OCwxNDAsNTAsMTUyLDEyNCwxMzYsMjAsMCw1OSwxNjcsNTgsMTAyLDcwLDEzMCwyNDUsNDMsOTcsMTM0LDE3NiwxMDYsMTIzLDIwMywxNDksMTg5LDIyLDE5NiwyMTEsNzQsMTUyLDE2MSwyMjYsMTI2LDIyMCwyMzIsMzAsMjM1LDk5LDYxLDE3NywyMTksMjgsMjA4LDE1NCwxMDUsNDMsMTAxLDIwMywyNDYsMzUsNDYsNzksMjUwLDE2MSw0NiwzNSw4OCwxNiw0MywyLDE4MCwzNCwxMTIsMTYxLDgzLDExMSw5MiwxOTksNTYsMjMwLDgsMTIyLDE2NCwxNjgsMzksMTI1LDE3NywxOTEsMTM0LDQsNzcsMTc3LDIzMiwxMTQsMjksNTIsMTgwLDI0LDMsMjA2LDEyLDE4NSwzOCwxOTUsMjMzLDE4MSwxMjYsMjA4LDE2LDIyMCwxMiw4NSwyNCw1NSwzMiwxOTUsMTg0LDEwLDQ0LDIxMCw3NCwyMDYsNDAsMTgxLDkzLDkyLDMsMTY2LDIzLDExOCw0LDg1LDMwLDE3LDIxNiwxMDcsMjM5LDEzMywxMTYsNjIsMTkyLDEzMywxMzQsNDQsOTksMjQxLDEzOSw1Myw2OSw5Niw2MSwyNDAsMTM5LDk1LDkyLDcsMTQ5LDEzMCw0NCwxODMsMjIsMTgyLDE3NSwyMzIsMTg2LDE4MCwxOTksMjQwLDM1LDI0MiwxMjAsMTA4LDYxLDE4NCwzNiwzNCwxNjAsMTg2LDczLDUsMTcwLDIzNiwyMDgsMTg3LDIzOSwyMyw1MiwwLDE5NiwxMDMsMTg4LDQ5LDkwLDIxOCwxMDcsMzMsMTM1LDE2NiwxOSwxODIsMTUxLDE5LDkyLDcxLDQ4LDI0NCwyMDEsMTQ4LDIwMCwxMiwzMywxNDYsNTIsMTgyLDE0LDI0NCw4NCwyMTYsMjE4LDUwLDYxLDIyMCw2LDUsMTE4LDI0LDExMiwyNDAsMTU1LDIzOSwyNDgsMTMyLDE2LDY5LDEyNiwxODksMTA2LDE0MywxOTYsMTcsMjA0LDIwOSwyMjksMTE5LDQ1LDExOSw4NSw2NCwyMDksNzksMTIyLDg5LDE0Miw1MywxNDgsMTU4LDEzNCwxNjYsMTMyLDEzOCwyNDQsMTk0LDE5OSwxNzEsMTA1LDQ3LDQxLDk4LDY5LDI0OCwzMyw0NiwxNzksMTcyLDI0NywxMzYsNDUsMTA3LDIxNiwxODIsMTk2LDk3LDc5LDI0LDI1MCwxNzgsMTA2LDM0LDkyLDE0NCw0NCwyNiwyNDAsMjQ5LDM3LDIxLDIyMCwxMTUsMzUsMzksMjEsMjMsMTk1LDE3MywxNTksMjA4LDE3NywxMDQsNTEsMTYxLDc5LDM1LDg2LDE0OSwxNzksOTIsMjIsMTY1LDkzLDkwLDEzNywxMjMsMTM0LDE3OSw2MywxOTAsNzEsMTgwLDI0NSwyNDksOCwxMzksMTM3LDE5NiwyMzksMjA0LDAsMTE4LDY5LDEwLDc5LDE1MywxMTYsMjMyLDEwLDk5LDczLDE2LDIwMCw4LDEzNiwxNSwxNDgsMjQyLDUsNDgsMCwxMCwyOCwxMjQsNzAsNzMsNywxNjQsMjI0LDcyLDIwNCwxMzYsMjYsNTEsNjUsMTEsNjgsMTg2LDExOCwxNjAsMTc0LDExNyw2Nyw3Miw5LDgsNjUsMTg4LDY3LDE1MiwxMTQsMTQ0LDg4LDIsMjYsMTQ5LDY3LDE5LDc4LDM0LDE0NywxNCw5NywxNDgsMjQxLDIzMSwzOSwyMjEsMTc3LDEzMSwyMTIsMTcsMzIsODgsMjE3LDQ2LDIwOCwxOCwxMTgsMjM4LDIzMywxMDMsMTc3LDE3Miw5MSwzMSwxNjcsMzAsMjUxLDE2NSwxNjMsMTg3LDE0NCwxNTEsMTc4LDExOSw5NywyMjUsMjQ0LDgxLDEzMSwxODIsMjA2LDQ2LDI1LDIwNCwxMjIsNDksMjIsMTMzLDM4LDE2MywyNTEsMTAzLDExOSwyNDQsODgsNDQsMTU1LDEwMiwxOTIsMTc1LDE3NywxMjAsMTM5LDM2LDI5LDI0LDU5LDI1LDIyNSwxMzQsMTQ5LDM4LDY4LDE3NSwxNzMsMjMxLDg5LDE3NSwzNywxMTMsODMsMjQ0LDE2NiwyMDYsMjA4LDk3LDE0OSw1Miw4MCwzMCw0OCwxMDEsMTUyLDk3LDEzNywxODcsMjE0LDE2NCw5MCwxNjIsNjYsMTc3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NSwxMTcsMjA5LDE2NCwxNzAsMTUwLDE2MCw2NCw5MCw1Nyw0OCwzMiw5Myw1NCwyMjAsMTA4LDE4LDI0Myw1MCw3LDI0MiwxMTAsNTEsMzksMTk5LDI1Myw1MSwxODcsNzgsMTM1LDQxLDc4LDE4NiwyMDcsMjAxLDI0Nyw3MSwzMCw5Nyw0MywxMjQsMjMyLDAsNTgsMTQ1LDEwLDI0MiwxNjksMTg4LDI0Myw4Miw1Myw5MywyOCwxMDQsNjUsNTQsMjAxLDUxLDI0OCwxODQsODUsMTQ5LDE4Myw1NywyMDksMjUsMTAsMzcsODQsMTQ2LDIxNiwxNTEsMTQxLDQzLDc4LDIyMywxNjYsMTIwLDYxLDE5NiwxMDgsNjYsMjgsMywxMDcsNjYsMjcsMzMsMzIsMTQwLDIyMSwxNjQsMTMxLDIsMTYwLDEzNSw5NywxNjEsMTIxLDg3LDk3LDYwLDE1OCwxMDMsMTQxLDk4LDIyOCwyMCwxNTMsMTE1LDEwMCwxMDgsODgsNjQsMzQsMTA2LDEwMiw4OCwzLDExNSw4LDEzNSwxOTIsMTQwLDI1MiwzOSw4OCwyMjAsMTg3LDMsMzMsMTU3LDIyMCwxMjksNjcsNjksMTcxLDEwNCw0OCwyMTAsMTc5LDYsMTU1LDEwMSwxNDgsMTc4LDc2LDI1MCw1MywzMiwxOTcsMTAsMTUxLDQ5LDU0LDE4MCwyMzAsMTY1LDcwLDE0OCwyMzMsMTg2LDgxLDUzLDY3LDIxMiw1LDE5NywxNzcsMTA2LDkwLDE5MCwyMjcsMTUyLDY0LDIwMCwxNjMsMjI0LDI2LDE4MywxMzAsNTcsMTE2LDUwLDIxMCw1MiwxMjAsMTc2LDE2NCwyMDgsMTc5LDE4Niw1MCw5Nyw5MSwyNTAsMjE2LDE4NSwzMSwyNDgsMTg3LDkyLDE1NSwyMDUsMjM5LDM0LDE4NiwyNTQsMjgsMzYsNDAsMCwyOCwyMjMsNDAsMjQsMTMyLDIxNCwxMDUsNiwzNCw4OSwxMTIsMjMxLDExLDksMTAsMTU5LDE1MSwyMTQsOCwxMzEsNDAsMTQ5LDUyLDE2MiwyNywzOCw4NSwxNzUsOTMsMTQ0LDYwLDg3LDkyLDQsMjA5LDE5OCwxMTYsMjcsMjUwLDg2LDE3NiwxMjAsOTgsNDMsMTA0LDg5LDEzMSwxMSw5OCwxMTEsNjUsODgsMTY2LDEwNiw0OSw4MSw2Nyw1LDExNywyMzUsMTUxLDExMiwyOCw2Niw5NCwxMzgsMzYsMTM0LDE2NywxODAsOTAsMjQ2LDE0Niw5LDE0OCwxMjcsMjUxLDIyNywxMDUsNjQsMzQsMTQ0LDExMiwxNjMsNjMsMTA1LDE3Miw1NywyMjMsMjQyLDQzLDEyNyw4Myw5NSwxNDIsMjE3LDM3LDU0LDk5LDcxLDE0LDIxOCwxODgsMjEyLDExMiw5OSwzMywxMjIsMTk4LDIyOSwxMjQsMTIwLDIzMCw2NCwxODksMjA1LDQyLDM4LDIwMiw5Miw1Nyw3Myw2NCw3MSw4MSwxMzgsMTI0LDI5LDExNSwxNzEsMzAsMTYzLDE1LDI0NiwxMjksMzIsMTIsMzQsMTU2LDkxLDY2LDg0LDE4OCwxNjAsMjA3LDYwLDIxNCwxNDIsMTAzLDExLDE2MSwxNSwxNDYsMTc0LDE5MSwxMTQsNTAsMjI3LDE3MCw3Nyw1MiwxMzMsMTk2LDYsMTQ4LDg4LDMwLDE4MiwxOTMsMTI0LDY2LDExNiw0OCwxMjMsMTQ4LDIyMiw0OCwxNzUsMjQ0LDE5OCw4MCw1OCwxMjIsODcsNTgsMTIwLDEyOSwxODgsMTc2LDEzLDAsMjUwLDIwNCw4Miw3MCwxODcsMjA0LDEzNiwxMDAsMjEwLDIxNCwxNDMsNiwxMjgsMTM0LDEyNSwyNDAsMjA1LDE5MCw4MywxNjYsMjQsMTUwLDYsMTcyLDEyMyw5OCwyMzksMjM2LDE2MCwyMTUsOTcsMTcsMjIyLDIwNCwxNjgsMTEyLDY3LDk2LDEyNywxNTIsMTMyLDE5MSwxMzUsMTY5LDIyNiwxNzcsMTU4LDIyMywxODcsMTk0LDIxLDEwNSwxODgsMjMyLDEzMSwxNzUsNjQsMjA5LDQ1LDY2LDE0OSw1NiwyMjksNyw0Myw2Myw5OCwxMzYsMTk0LDIwNCwxMjMsMjYsNCwxMTAsMjE0LDEzMCwxODcsMjAsOTksOTMsNTcsODMsMTc2LDIxNCwxMjMsMTg2LDEwMCwxNiw3NCwyMjYsMjQyLDE0NSwxMjgsMTYzLDE1LDExOSwyMDIsNTYsMTAxLDE2OCwxNDAsNTAsMTUyLDEyNCwxMzYsMjAsMCw1OSwxNjcsNTgsMTAyLDcwLDEzMCwyNDUsNDMsOTcsMTM0LDE3NiwxMDYsMTIzLDIwMywxNDksMTg5LDIyLDE5NiwyMTEsNzQsMTUyLDE2MSwyMjYsMTI2LDIyMCwyMzIsMzAsMjM1LDk5LDYxLDE3NywyMTksMjgsMjA4LDE1NCwxMDUsNDMsMTAxLDIwMywyNDYsMzUsNDYsNzksMjUwLDE2MSw0NiwzNSw4OCwxNiw0MywyLDE4MCwzNCwxMTIsMTYxLDgzLDExMSw5MiwxOTksNTYsMjMwLDgsMTIyLDE2NCwxNjgsMzksMTI1LDE3NywxOTEsMTM0LDQsNzcsMTc3LDIzMiwxMTQsMjksNTIsMTgwLDI0LDMsMjA2LDEyLDE4NSwzOCwxOTUsMjMzLDE4MSwxMjYsMjA4LDE2LDIyMCwxMiw4NSwyNCw1NSwzMiwxOTUsMTg0LDEwLDQ0LDIxMCw3NCwyMDYsNDAsMTgxLDkzLDkyLDMsMTY2LDIzLDExOCw0LDg1LDMwLDE3LDIxNiwxMDcsMjM5LDEzMywxMTYsNjIsMTkyLDEzMywxMzQsNDQsOTksMjQxLDEzOSw1Myw2OSw5Niw2MSwyNDAsMTM5LDk1LDkyLDcsMTQ5LDEzMCw0NCwxODMsMjIsMTgyLDE3NSwyMzIsMTg2LDE4MCwxOTksMjQwLDM1LDI0MiwxMjAsMTA4LDYxLDE4NCwzNiwzNCwxNjAsMTg2LDczLDUsMTcwLDIzNiwyMDgsMTg3LDIzOSwyMyw1MiwwLDE5NiwxMDMsMTg4LDQ5LDkwLDIxOCwxMDcsMzMsMTM1LDE2NiwxOSwxODIsMTUxLDE5LDkyLDcxLDQ4LDI0NCwyMDEsMTQ4LDIwMCwxMiwzMywxNDYsNTIsMTgyLDE0LDI0NCw4NCwyMTYsMjE4LDUwLDYxLDIyMCw2LDUsMTE4LDI0LDExMiwyNDAsMTU1LDIzOSwyNDgsMTMyLDE2LDY5LDEyNiwxODksMTA2LDE0MywxOTYsMTcsMjA0LDIwOSwyMjksMTE5LDQ1LDExOSw4NSw2NCwyMDksNzksMTIyLDg5LDE0Miw1MywxNDgsMTU4LDEzNCwxNjYsMTMyLDEzOCwyNDQsMTk0LDE5OSwxNzEsMTA1LDQ3LDQxLDk4LDY5LDI0OCwzMyw0NiwxNzksMTcyLDI0NywxMzYsNDUsMTA3LDIxNiwxODIsMTk2LDk3LDc5LDI0LDI1MCwxNzgsMTA2LDM0LDkyLDE0NCw0NCwyNiwyNDAsMjQ5LDM3LDIxLDIyMCwxMTUsMzUsMzksMjEsMjMsMTk1LDE3MywxNTksMjA4LDE3NywxMDQsNTEsMTYxLDc5LDM1LDg2LDE0OSwxNzksOTIsMjIsMTY1LDkzLDkwLDEzNywxMjMsMTM0LDE3OSw2MywxOTAsNzEsMTgwLDI0NSwyNDksOCwxMzksMTM3LDE5NiwyMzksMjA0LDAsMTE4LDY5LDEwLDc5LDE1MywxMTYsMjMyLDEwLDk5LDczLDE2LDIwMCw4LDEzNiwxNSwxNDgsMjQyLDUsNDgsMCwxMCwyOCwxMjQsNzAsNzMsNywxNjQsMjI0LDcyLDIwNCwxMzYsMjYsNTEsNjUsMTEsNjgsMTg2LDExOCwxNjAsMTc0LDExNyw2Nyw3Miw5LDgsNjUsMTg4LDY3LDE1MiwxMTQsMTQ0LDg4LDIsMjYsMTQ5LDY3LDE5LDc4LDM0LDE0NywxNCw5NywxNDgsMjQxLDIzMSwzOSwyMjEsMTc3LDEzMSwyMTIsMTcsMzIsODgsMjE3LDQ2LDIwOCwxOCwxMTgsMjM4LDIzMywxMDMsMTc3LDE3Miw5MSwzMSwxNjcsMzAsMjUxLDE2NSwxNjMsMTg3LDE0NCwxNTEsMTc4LDExOSw5NywyMjUsMjQ0LDgxLDEzMSwxODIsMjA2LDQ2LDI1LDIwNCwxMjIsNDksMjIsMTMzLDM4LDE2MywyNTEsMTAzLDExOSwyNDQsODgsNDQsMTU1LDEwMiwxOTIsMTc1LDE3NywxMjAsMTM5LDM2LDI5LDI0LDU5LDI1LDIyNSwxMzQsMTQ5LDM4LDY4LDE3NSwxNzMsMjMxLDg5LDE3NSwzNywxMTMsODMsMjQ0LDE2NiwyMDYsMjA4LDk3LDE0OSw1Miw4MCwzMCw0OCwxMDEsMTUyLDk3LDEzNywxODcsMjE0LDE2NCw5MCwxNjIsNjYsMTc3LDMxLDEzOCwyNTAsMTE4LDI1MSwzMiwxMzYsMTk3LDIzMSw0LDUxLDE3NSwxOTQsMTg0LDE5OCw3LDIzNyw5NCwyMjQsMjIyLDI1MSwzNCwyLDIzMCw1LDEyMCwxOTcsNDMsNzUsMjcsMTY1LDEyMCw0LDUwLDIsMTcwLDE5MSw3NiwxNTIsNzUsMTE1LDEwMiwxOTcsNDQsMjI0LDE3MiwxMDUsODIsMTQ0LDgzLDEzNiwxMDksOCwxNDEsMjI4LDExLDEzNywyMDYsMTQ3LDQ4XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzE2NCwxODAsMTI1LDEyNiw3OSwyMjAsMjIxLDI1MywxMjMsNDIsMjAxLDEyMCwxODAsMTY0LDIwMCwyMDgsMTc3LDk5LDgyLDYwLDU1LDE1NiwxMjYsNzcsMTE3LDExNSwyMDMsMjAzLDE0MiwyNTUsMTY2LDM0XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTcsMTA4LDEwNSw5OSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IktleVBhY2thZ2UiOnsibm90X2JlZm9yZSI6MTcyMDY4ODk1MCwibm90X2FmdGVyIjoxNzI3OTUwMTUwfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMzQsOTUsNyw0Myw3Miw4MCwxNSw0OSw0NSwxMzEsMTIwLDExOSwxNzcsMTI0LDkzLDE1MSwxMjMsMTI4LDQ1LDMzLDIxMywyMzMsMjEyLDEwLDIyMiwxNzgsMTY3LDgyLDU3LDUsMjMxLDIsMTIsMTAxLDEwOSwxNzEsMjM3LDk0LDQsMjA5LDcyLDEyNCwxMTAsMTU2LDIxNywyMzMsMjcsNjksMTk3LDIxMCwxNzksMzEsMTIzLDIzNiwyMDksMTkwLDI1MywxMDAsMjA4LDQ3LDU1LDYsMTksOV19fX19XSwicGFyZW50X25vZGVzIjpbXSwiZGVmYXVsdF9s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dmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X2Vwb2NocyI6MCwicGFzdF9lcG9jaF90cmVlcyI6W10sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE2LDMxLDE1LDg5LDIzMyw2OSw3MywxMTcsMjUyLDExNSwxNTAsMTQ5LDEyMiwxMjAsOTYsMTA2LDEzMiwzMiw5NywyLDIwLDI0NywyMTEsMTA0LDIyOCwxOCwyMzMsMTAxLDIxMiwxMzksMTUyLDIzOF19fX0sIm1lbWJlcnNoaXBfa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxODksMTI2LDE3LDExNiwxNzQsMjQ1LDAsNTEsMjA1LDIwMywyNTQsMTQxLDY0LDEyNSwxNjMsMjAzLDE3MCwwLDI0MywzNSwxNDAsMTE5LDE3Nyw1MiwyMTAsNTQsMjQ0LDI0Miw2MSw1LDI0Myw5NV19fX0sImNvbmZpcm1hdGlvbl9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE4NywyNTEsMTk0LDc2LDU0LDE3NSw4MSwxNDYsNjcsMjA2LDEwMSwyMzksMzIsMTU2LDI1LDE2MywxNTAsNjYsMzMsMTc3LDExMSwxNTMsNDMsMTAxLDE2NCwxMTMsMjMsMjQzLDIzNywyMDksMjIwLDQ2XX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsNzcsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwwLDMyLDE4NCw0MSwxMzQsMjEzLDcyLDI3LDE3MSwxMzQsMTYwLDY0LDE0LDg0LDUxLDIzNCwyLDIyMSwxMjMsODAsMTU1LDE2OCwyNDgsMTU2LDExMywyMzksMSwyNTIsMTg3LDEyOSwyOCw1OCwxMDAsMjExLDAsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGxdLCJw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LDc2LDEwOSw4Miw3LDE2OSwyNDgsMTI3LDI2LDU4LDExNSwxMTcsMjA5LDE2NCwxNzAsMTUwLDE2MCw2NCw5MCw1Nyw0OCwzMiw5Myw1NCwyMjAsMTA4LDE4LDI0Myw1MCw3LDI0MiwxMTAsNTEsMzksMTk5LDI1Myw1MSwxODcsNzgsMTM1LDQxLDc4LDE4NiwyMDcsMjAxLDI0Nyw3MSwzMCw5Nyw0MywxMjQsMjMyLDAsNTgsMTQ1LDEwLDI0MiwxNjksMTg4LDI0Myw4Miw1Myw5MywyOCwxMDQsNjUsNTQsMjAxLDUxLDI0OCwxODQsODUsMTQ5LDE4Myw1NywyMDksMjUsMTAsMzcsODQsMTQ2LDIxNiwxNTEsMTQxLDQzLDc4LDIyMywxNjYsMTIwLDYxLDE5NiwxMDgsNjYsMjgsMywxMDcsNjYsMjcsMzMsMzIsMTQwLDIyMSwxNjQsMTMxLDIsMTYwLDEzNSw5NywxNjEsMTIxLDg3LDk3LDYwLDE1OCwxMDMsMTQxLDk4LDIyOCwyMCwxNTMsMTE1LDEwMCwxMDgsODgsNjQsMzQsMTA2LDEwMiw4OCwzLDExNSw4LDEzNSwxOTIsMTQwLDI1MiwzOSw4OCwyMjAsMTg3LDMsMzMsMTU3LDIyMCwxMjksNjcsNjksMTcxLDEwNCw0OCwyMTAsMTc5LDYsMTU1LDEwMSwxNDgsMTc4LDc2LDI1MCw1MywzMiwxOTcsMTAsMTUxLDQ5LDU0LDE4MCwyMzAsMTY1LDcwLDE0OCwyMzMsMTg2LDgxLDUzLDY3LDIxMiw1LDE5NywxNzcsMTA2LDkwLDE5MCwyMjcsMTUyLDY0LDIwMCwxNjMsMjI0LDI2LDE4MywxMzAsNTcsMTE2LDUwLDIxMCw1MiwxMjAsMTc2LDE2NCwyMDgsMTc5LDE4Niw1MCw5Nyw5MSwyNTAsMjE2LDE4NSwzMSwyNDgsMTg3LDkyLDE1NSwyMDUsMjM5LDM0LDE4NiwyNTQsMjgsMzYsNDAsMCwyOCwyMjMsNDAsMjQsMTMyLDIxNCwxMDUsNiwzNCw4OSwxMTIsMjMxLDExLDksMTAsMTU5LDE1MSwyMTQsOCwxMzEsNDAsMTQ5LDUyLDE2MiwyNywzOCw4NSwxNzUsOTMsMTQ0LDYwLDg3LDkyLDQsMjA5LDE5OCwxMTYsMjcsMjUwLDg2LDE3NiwxMjAsOTgsNDMsMTA0LDg5LDEzMSwxMSw5OCwxMTEsNjUsODgsMTY2LDEwNiw0OSw4MSw2Nyw1LDExNywyMzUsMTUxLDExMiwyOCw2Niw5NCwxMzgsMzYsMTM0LDE2NywxODAsOTAsMjQ2LDE0Niw5LDE0OCwxMjcsMjUxLDIyNywxMDUsNjQsMzQsMTQ0LDExMiwxNjMsNjMsMTA1LDE3Miw1NywyMjMsMjQyLDQzLDEyNyw4Myw5NSwxNDIsMjE3LDM3LDU0LDk5LDcxLDE0LDIxOCwxODgsMjEyLDExMiw5OSwzMywxMjIsMTk4LDIyOSwxMjQsMTIwLDIzMCw2NCwxODksMjA1LDQyLDM4LDIwMiw5Miw1Nyw3Myw2NCw3MSw4MSwxMzgsMTI0LDI5LDExNSwxNzEsMzAsMTYzLDE1LDI0NiwxMjksMzIsMTIsMzQsMTU2LDkxLDY2LDg0LDE4OCwxNjAsMjA3LDYwLDIxNCwxNDIsMTAzLDExLDE2MSwxNSwxNDYsMTc0LDE5MSwxMTQsNTAsMjI3LDE3MCw3Nyw1MiwxMzMsMTk2LDYsMTQ4LDg4LDMwLDE4MiwxOTMsMTI0LDY2LDExNiw0OCwxMjMsMTQ4LDIyMiw0OCwxNzUsMjQ0LDE5OCw4MCw1OCwxMjIsODcsNTgsMTIwLDEyOSwxODgsMTc2LDEzLDAsMjUwLDIwNCw4Miw3MCwxODcsMjA0LDEzNiwxMDAsMjEwLDIxNCwxNDMsNiwxMjgsMTM0LDEyNSwyNDAsMjA1LDE5MCw4MywxNjYsMjQsMTUwLDYsMTcyLDEyMyw5OCwyMzksMjM2LDE2MCwyMTUsOTcsMTcsMjIyLDIwNCwxNjgsMTEyLDY3LDk2LDEyNywxNTIsMTMyLDE5MSwxMzUsMTY5LDIyNiwxNzcsMTU4LDIyMywxODcsMTk0LDIxLDEwNSwxODgsMjMyLDEzMSwxNzUsNjQsMjA5LDQ1LDY2LDE0OSw1NiwyMjksNyw0Myw2Myw5OCwxMzYsMTk0LDIwNCwxMjMsMjYsNCwxMTAsMjE0LDEzMCwxODcsMjAsOTksOTMsNTcsODMsMTc2LDIxNCwxMjMsMTg2LDEwMCwxNiw3NCwyMjYsMjQyLDE0NSwxMjgsMTYzLDE1LDExOSwyMDIsNTYsMTAxLDE2OCwxNDAsNTAsMTUyLDEyNCwxMzYsMjAsMCw1OSwxNjcsNTgsMTAyLDcwLDEzMCwyNDUsNDMsOTcsMTM0LDE3NiwxMDYsMTIzLDIwMywxNDksMTg5LDIyLDE5NiwyMTEsNzQsMTUyLDE2MSwyMjYsMTI2LDIyMCwyMzIsMzAsMjM1LDk5LDYxLDE3NywyMTksMjgsMjA4LDE1NCwxMDUsNDMsMTAxLDIwMywyNDYsMzUsNDYsNzksMjUwLDE2MSw0NiwzNSw4OCwxNiw0MywyLDE4MCwzNCwxMTIsMTYxLDgzLDExMSw5MiwxOTksNTYsMjMwLDgsMTIyLDE2NCwxNjgsMzksMTI1LDE3NywxOTEsMTM0LDQsNzcsMTc3LDIzMiwxMTQsMjksNTIsMTgwLDI0LDMsMjA2LDEyLDE4NSwzOCwxOTUsMjMzLDE4MSwxMjYsMjA4LDE2LDIyMCwxMiw4NSwyNCw1NSwzMiwxOTUsMTg0LDEwLDQ0LDIxMCw3NCwyMDYsNDAsMTgxLDkzLDkyLDMsMTY2LDIzLDExOCw0LDg1LDMwLDE3LDIxNiwxMDcsMjM5LDEzMywxMTYsNjIsMTkyLDEzMywxMzQsNDQsOTksMjQxLDEzOSw1Myw2OSw5Niw2MSwyNDAsMTM5LDk1LDkyLDcsMTQ5LDEzMCw0NCwxODMsMjIsMTgyLDE3NSwyMzIsMTg2LDE4MCwxOTksMjQwLDM1LDI0MiwxMjAsMTA4LDYxLDE4NCwzNiwzNCwxNjAsMTg2LDczLDUsMTcwLDIzNiwyMDgsMTg3LDIzOSwyMyw1MiwwLDE5NiwxMDMsMTg4LDQ5LDkwLDIxOCwxMDcsMzMsMTM1LDE2NiwxOSwxODIsMTUxLDE5LDkyLDcxLDQ4LDI0NCwyMDEsMTQ4LDIwMCwxMiwzMywxNDYsNTIsMTgyLDE0LDI0NCw4NCwyMTYsMjE4LDUwLDYxLDIyMCw2LDUsMTE4LDI0LDExMiwyNDAsMTU1LDIzOSwyNDgsMTMyLDE2LDY5LDEyNiwxODksMTA2LDE0MywxOTYsMTcsMjA0LDIwOSwyMjksMTE5LDQ1LDExOSw4NSw2NCwyMDksNzksMTIyLDg5LDE0Miw1MywxNDgsMTU4LDEzNCwxNjYsMTMyLDEzOCwyNDQsMTk0LDE5OSwxNzEsMTA1LDQ3LDQxLDk4LDY5LDI0OCwzMyw0NiwxNzksMTcyLDI0NywxMzYsNDUsMTA3LDIxNiwxODIsMTk2LDk3LDc5LDI0LDI1MCwxNzgsMTA2LDM0LDkyLDE0NCw0NCwyNiwyNDAsMjQ5LDM3LDIxLDIyMCwxMTUsMzUsMzksMjEsMjMsMTk1LDE3MywxNTksMjA4LDE3NywxMDQsNTEsMTYxLDc5LDM1LDg2LDE0OSwxNzksOTIsMjIsMTY1LDkzLDkwLDEzNywxMjMsMTM0LDE3OSw2MywxOTAsNzEsMTgwLDI0NSwyNDksOCwxMzksMTM3LDE5NiwyMzksMjA0LDAsMTE4LDY5LDEwLDc5LDE1MywxMTYsMjMyLDEwLDk5LDczLDE2LDIwMCw4LDEzNiwxNSwxNDgsMjQyLDUsNDgsMCwxMCwyOCwxMjQsNzAsNzMsNywxNjQsMjI0LDcyLDIwNCwxMzYsMjYsNTEsNjUsMTEsNjgsMTg2LDExOCwxNjAsMTc0LDExNyw2Nyw3Miw5LDgsNjUsMTg4LDY3LDE1MiwxMTQsMTQ0LDg4LDIsMjYsMTQ5LDY3LDE5LDc4LDM0LDE0NywxNCw5NywxNDgsMjQxLDIzMSwzOSwyMjEsMTc3LDEzMSwyMTIsMTcsMzIsODgsMjE3LDQ2LDIwOCwxOCwxMTgsMjM4LDIzMywxMDMsMTc3LDE3Miw5MSwzMSwxNjcsMzAsMjUxLDE2NSwxNjMsMTg3LDE0NCwxNTEsMTc4LDExOSw5NywyMjUsMjQ0LDgxLDEzMSwxODIsMjA2LDQ2LDI1LDIwNCwxMjIsNDksMjIsMTMzLDM4LDE2MywyNTEsMTAzLDExOSwyNDQsODgsNDQsMTU1LDEwMiwxOTIsMTc1LDE3NywxMjAsMTM5LDM2LDI5LDI0LDU5LDI1LDIyNSwxMzQsMTQ5LDM4LDY4LDE3NSwxNzMsMjMxLDg5LDE3NSwzNywxMTMsODMsMjQ0LDE2NiwyMDYsMjA4LDk3LDE0OSw1Miw4MCwzMCw0OCwxMDEsMTUyLDk3LDEzNywxODcsMjE0LDE2NCw5MCwxNjIsNjYsMTc3LDMxLDEzOCwyNTAsMTE4LDI1MSwzMiwxMzYsMTk3LDIzMSw0LDUxLDE3NSwxOTQsMTg0LDE5OCw3LDIzNyw5NCwyMjQsMjIyLDI1MSwzNCwyLDIzMCw1LDEyMCwxOTcsNDMsNzUsMjcsMTY1LDEyMCw0LDUwLDIsMTcwLDE5MSw3NiwxNTIsNzUsMTE1LDEwMiwxOTcsNDQsMjI0LDE3MiwxMDUsODIsMTQ0LDgzLDEzNiwxMDksOCwxNDEsMjI4LDExLDEzNywyMDYsMTQ3LDQ4XX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMjM3LDI0NywyMywxMzMsMzgsMjYsMzMsODQsODQsMzEsMTQ4LDI2LDczLDE1Niw3NywxODEsMTIsODksMTA3LDIxMCwyMDEsMjAwLDY2LDE2MCw0OSwxNDgsMTA5LDI0NCwzOSw0MiwzLDE4MCwxMzAsNTUsMTkyLDEyNSwyMDQsMTI0LDE1MCwxMDAsMTYyLDIwLDEzNSwxNjAsMTIxLDE4OSwxOTIsOTgsOTgsMTQ4LDEyNCwyNDQsMjI0LDE3MiwxNDIsMTIzLDE0OCwxNjcsMjUyLDMwLDM5LDE4Nyw2NywxOTIsMTM4LDE4OSw2Niw3NiwxODYsNSwyMjcsMTA5LDEyMSwxMzYsMTUzLDUxLDE0NiwyMDcsMTMyLDEzMywzOCwxNiwyMCwxNjIsMjMzLDI0MiwxNjQsODksNTYsNDYsMTY2LDE5Nyw5NCwyMDksOTgsNzYsMTgyLDE3OCwxMSwxNjQsMTk3LDk1LDE1OCw1OSwyOCwxODYsMjQ2LDExLDE2MCwxMDMsMTg1LDEzNywxNTUsMSwxOTAsMjQyLDIwMiwzNywyMjksNzUsMTMyLDI0LDEyMCwzMCwxOCw3NSwxOTcsMTM5LDI3LDIxMiwxMTcsMTI5LDIxLDQzLDE2MywxMzUsMTQwLDEzLDE4MywxNjcsMjAyLDIyMCwyMTksMjEsMTg1LDUxLDEzMSwxOTEsMjAsMTgxLDE4NCwyMCwxMTEsODUsMjE5LDEwOSw5MSwyNCwzMSw3OCwxNjQsNTUsMjA5LDIzNiwxNzEsNzcsOTgsOTEsMTYyLDIwNCwyMDcsMTk0LDE2OSwxMTgsMjMxLDIwOCwxNzgsOTMsMTA3LDM5LDI2LDY1LDgwLDEzNSwxNywxNzYsMjUxLDEyNCwxMiwxMjksOCwxODYsMTQyLDE3MCwzLDQzLDIyNCw3MCw3NiwzLDM4LDIzMyw5NywzOSw4MCw3MywxMzIsNDcsMjAzLDEyOSwxNjIsMTMxLDE0MSwxOTAsMTY5LDEwOSw1LDEzNiwxNTQsNDcsMTYyLDI1LDI5LDIyOCwxNjUsMTYyLDIzMCwxMjAsMTE0LDE5NywxMDMsMjI2LDIzMCwxMTIsMjM1LDEzMiwxNywyMjksMjA5LDEwNSw5LDcyLDE1NSw5MCwyMzYsNzEsMjE4LDE5OSwxMjAsMTYyLDE3OSwxMzUsMTksODksMTY1LDE5MSw2NywxNTQsMzYsMjQwLDEzNyw2LDE1MywxODcsMTE0LDYsMTk1LDE1MSwyMyw0NiwyMDMsMzcsMTkzLDc1LDE1MCwxMjEsNDAsMTY2LDE3NywxMSwxLDQ0LDIxNCwyNDUsODgsNzgsMTkzLDYxLDIwMSwxMDAsMjAsMjIzLDIzLDE2MSwyMjEsNTIsMzcsMTc2LDE4LDEwMCwxMiwxODYsMTc2LDIzMCwyNDMsMCwxMyw5OSwyMiwzMSwxNTEsMTU0LDE2LDExMyw4LDE0OCwyNDMsOTksMTAzLDEwMyw3NiwxMzAsMTM2LDE2NiwyMDAsMjAsMTQsMjM1LDE3LDE5NSwyMjEsMTQ3LDU5LDUxLDI0NCwxNywxNjMsMzMsNiwyMjYsNzIsMTA5LDEzNiwxOTcsNDAsMzUsNzAsMTUyLDIzNyw4NSwxNiwyNywxNzAsOTEsMTU3LDQsNTAsOTcsMTA3LDExNiw2OCw0OSwxNDEsMTA2LDEyOCwxOTMsMTQsMjgsMTI5LDIzOSw3Miw2MiwyMDAsMjQ2LDEwNiwzNiw1Miw4Nyw2MywxNTMsMTQzLDE4NCwyLDExOCwxNTUsNzYsMTMyLDE2OSwxOSwxMTMsMjA5LDIzMiwxNjIsMjEsMjMyLDU1LDEzOCwzNCwxMDQsODYsNzIsNzUsNjUsODAsNTAsMTc0LDEwNCwxODUsMjM5LDIxMyw1OSw0MSw2NSw1MSwxNjcsMjUsMTg1LDg2LDIzMCw3LDE4NywyNTEsODksMjQ5LDIzMiw3LDEyNCw5OSwxNTUsMTU1LDE5NywyMDMsMTMzLDEwNCwxNzcsMzEsOSwxMzYsMTQ0LDExLDksMSwxNzIsNjksMjQxLDExOSwxNzgsMzAsMjQ1LDExOSwzOSwyMTksMiwxODUsMTMwLDE2NCwzMSwyNTIsOTAsMjI1LDIyNiwxMTEsNzIsMTgwLDEzNSwxMDIsMTY4LDE0NSwxMzEsMTY0LDEyMCwxLDE3MiwxNTAsMTQwLDE3OSwzNiwyMDAsMTQ4LDEyNywxMjcsNTgsOTYsMTk1LDgwLDEzMiwyMzYsMTkzLDE1OCwyMyw0Miw3MiwyMjMsOTksOTQsMjI2LDIyNyw4MCwzOCwxNTMsMjUsMTYxLDg3LDEzLDEzOCwxMzksMzgsMjUsMTg2LDM0LDE2NywyNDcsODksMjA2LDY0LDYwLDk2LDExNywxMDYsMTkyLDE1MSwxODgsOTQsMTEsNjUsMTk1LDkwLDE0OCwxNjQsMjE2LDIwNSwxNzQsMTU2LDE1OSwzNSw2LDU3LDEwMCwxNjUsODcsNDksMzUsODQsMTgsMCw3OSwxNTAsMjcsMTMzLDIyNywyNDcsMTMsMSwyMyw3MCwxOTUsMTg2LDE5Myw3MCw2NywxNTksOCwyNDMsMTM0LDEyMiw4OSwxNTEsMTA3LDc1LDQyLDE3NiwxMjIsMjAzLDE5MCw2OCwxNjAsMTY1LDE5Niw1OSwyNSwzOCwxMTMsMTUsMjE2LDE3NSwyMzAsMTEsMTYzLDMzLDI0OSwxOTcsMTA5LDAsNDEsMjksNzMsOTYsMTQsMTY0LDM1LDQ4LDIwOSw5NCwxOTQsMTAyLDI2LDE1OSwyNTIsNSwyNiwyNDMsNjgsMTAxLDM4LDIwNCwxODAsMTI4LDY4LDYwLDY3LDg1LDM5LDYsMTYsODcsNzMsMTQ1LDIwOCwyMDQsMzAsMTM2LDIwMSwyMCw5OCwxMTQsMTIwLDE2NSwyMzQsMTg5LDEzNywxOTUsMzAsMTcxLDIzNiwxMDMsODEsODEsMTc4LDQyLDkwLDE2MiwwLDE2OSwxMjQsMTQ0LDI0Myw2OCwzNyw5NiwxMDUsMTE0LDgsMTg0LDEsMTM4LDEwMSwxNTUsMTY3LDIwNSwxNDgsMjA4LDE1LDEzNywyLDEzMCwxMjgsMjQxLDEzOCwyNDIsNTcsMTQxLDE5OSwxMDIsMTM2LDY3LDIxMSwxMDQsMjAwLDc2LDEzNyw0NywxMDcsMTE3LDE1MCwxOCw3NCwxNTgsMTk0LDcyLDEyNywxMjIsMTY0LDU4LDEwNSwyOSwxMzcsMTg0LDc2LDc5LDgxLDEzMywyNTAsMTgyLDE4MywxMTQsMjUxLDEwNCw0MywyNDUsMTEyLDYsMjEwLDE2MiwzOSw5MCwxNjgsNjAsMjEwLDcxLDIyOSwyMDIsNjQsMjE2LDEzMSwxNDUsMjEwLDIwMiwxOTgsMjExLDgxLDEyNyw4OCwxNTIsMjA1LDg5LDIwMiwxMjcsMTg5LDEzOCw1MiwyMDcsMTA1LDg5LDE3OSw5LDEwNCwyNDUsNTEsNCwyNDIsMjM0LDEyMiwxMTEsMTIsMTM0LDIzNCwxMiw5NiwzNCwxNDksMTc1LDc5LDczLDM5LDE0MSwyMTMsMTQ1LDc3LDEzMSwxMzEsMTA0LDczLDEwMiwxMjgsMjE1LDU0LDM1LDI0NiwzOSwzMSwyNDksNzksMjE2LDIwMCw5NCwxNDAsMjIwLDExOCw0NiwzNSwyLDExMCwyMTQsMTIsMTM5LDE4MSw5MCwzOCwxNjcsMTY3LDEyMiwyNywxNDIsNDgsMTcyLDI2LDg1LDMzLDExOCwxNjIsMTg0LDEwOSwxMjAsMTIwLDY3LDQ0LDEyMiw0OCw0NiwxOTIsODYsODAsOTEsMTMsNTMsMjUyLDEwNSw5LDMyLDExMywyMTEsMywxMjgsMTIyLDg5LDE1NSwxNDAsMjE4LDIsMTAyLDY3LDE1MywxOTAsMjMyLDI2LDM2LDEzMSwyLDkzLDEzNyw3MSwyMTQsODQsMzMsMTEsMjExLDk2LDE0NCw1MSwxODYsMjA5LDIyNiwxMjEsMTI0LDE2MCw4OCw4OCwxODEsOTgsMjU1LDgsMTgxLDIyMywxODUsMTk3LDEyNCwyOCwyMiwyMjAsOTYsMTI3LDE3OSwxMjQsMTQ5LDIzMiw2OCwyOSwyMDEsNzAsMTIwLDQ5LDg1LDExOSwxNywxNzgsMTIsMTc2LDQwLDQ1LDIxNiwxMTIsMSwxNjgsMTM1LDk1LDMsMTE2LDIwMSwxMTAsNTMsMTksMzUsMTIzLDIzLDE4Myw0MywxOCwxMTYsNTIsMTYxLDEyMCwxMTYsMTExLDE5NSw2LDIwNCwxMDcsMTM5LDE1MCwyNCwxMzMsMjIsMTQ4LDUzLDEzNSwxNTAsMTEyLDgwLDI0NCwxNywxMDIsMjEwLDI0MywyMDUsNTIsNjAsOCw5MCwxMiwxOTAsMjQ4LDEwMCwxMDUsMzksMTgzLDEzMSw0OSwxMTQsMTgxLDMsMTAzLDE5NywxODgsMTE1LDE2MSwzNiwxMCwxNDQsOTQsODEsMTY3LDczLDQsMTk3LDQzLDE1Miw1MCwxOTcsMTk0LDY5LDE5NSwxNjYsMjAxLDIxMCwyNTIsMTA2LDcwLDI0OCwxNiwxMDAsMjMyLDIwMSwxODcsNSwxOTIsNTYsNzUsNzksMTExLDMzLDUsMTUyLDEwLDIwMCwxOSw1MiwxNjAsMTE2LDE2NSwxNDYsMTEsMjQzLDEwNiwyNDksMjM2LDc5LDE1Nyw4NSw5OCw2NywxNjYsODQsMjE0LDk4LDEyNyw1Miw1NiwxMDIsMTE5LDIxMSwyMDMsMjA5LDIzLDI0LDEzNywxOTIsNjIsMTMwLDEzMCwxMzIsODAsMTU2LDEzNiwyMjcsMTYyLDE2MCw0MywyNiw5MiwxODksMTc2LDcxLDQ5LDE5OSwxMjYsMjAxLDY5LDE3LDY3LDIsMTEzLDIwNSwxMjIsMTc1LDIwNiwxODgsMTU3LDIsMTk4LDExLDY4LDM0LDEyMSwxOTcsNjcsODYsMTU1LDEzNCwxOTIsNjgsMTU0LDc4LDYxLDI1MCw4LDExOSwxMDcsMTU0LDE2OSw1MSw1OCwzMiwxNjYsNjMsMTA0LDE4NywxMDAsMTkyLDIwLDE0NiwyMzgsMjEsMjAsMTEzLDEzMCwxNSw2LDUyLDEzOSw1NiwyMDgsMTI0LDE4NSwyMTIsMjA3LDMxLDU4LDE3MSwxNDcsMjE0LDExNyw0MywxNTMsMTI3LDIxLDE1Miw1Nyw2Myw3MSwxOTMsMjE3LDE1NiwxOTcsMTY3LDE3MSwxNTQsOTAsMTY0LDExNSw4MSwxNTQsODksMjcsMTMzLDExOSw4OSwxNywyLDE3MywxMzEsMTg2LDIxOSw3NCw5NCw3MywxODcsMjAzLDMxLDI0NCwxNjksMTksMTAzLDEyLDE0LDk5LDE2NSwyMjUsMTM4LDM2LDE5NywxMzUsMTg3LDY4LDE1NSwzMSwxMTAsMTA4LDkwLDIyLDgxLDEzNywxOTEsMTA1LDE4OSw5LDE3Miw4MSwyMDcsMjEwLDM0LDcxLDExNSwxODQsMTExLDQxLDE0Myw1MywzNiwxOTUsMTYsNzEsODUsMjMyLDEyMCw3NSwxOTcsMTU2LDIwNCwxOSwzOCwxNzgsMiwyMCwxNTIsMTAzLDI3LDY2LDIyMiw1OCwyMDIsNzgsNDMsNjgsMTE4LDcxLDkzLDMzLDE1NCwxMjUsMjI3LDE0Niw1NiwxMzIsMTkzLDE2Nyw4NywxNTYsMjAsMjAzLDQxLDEyNiwxMTksMTAsMTk1LDI4LDQsMjIsMTMxLDgsNTYsODYsNywxODQsMzMsODQsMzIsMTI4LDIzLDAsMjEzLDE1NCwxMTksMzIsMTY0LDU3LDI0MywxODAsMTgzLDIyLDM5LDE5NywxMTIsMTQ4LDkxLDIxOCwxOTgsMTc1LDg0LDExMywxOTgsMjA3LDM4LDEyMSwxOCwxNjIsNDksMTQwLDIyNCw1NywyMzYsMzIsMTIsOTcsMjE3LDM3LDQ1LDE3Niw3NiwxMDksODIsNywxNjksMjQ4LDEyNywyNiw1OCwxMTUsMTE3LDIwOSwxNjQsMTcwLDE1MCwxNjAsNjQsOTAsNTcsNDgsMzIsOTMsNTQsMjIwLDEwOCwxOCwyNDMsNTAsNywyNDIsMTEwLDUxLDM5LDE5OSwyNTMsNTEsMTg3LDc4LDEzNSw0MSw3OCwxODYsMjA3LDIwMSwyNDcsNzEsMzAsOTcsNDMsMTI0LDIzMiwwLDU4LDE0NSwxMCwyNDIsMTY5LDE4OCwyNDMsODIsNTMsOTMsMjgsMTA0LDY1LDU0LDIwMSw1MSwyNDgsMTg0LDg1LDE0OSwxODMsNTcsMjA5LDI1LDEwLDM3LDg0LDE0NiwyMTYsMTUxLDE0MSw0Myw3OCwyMjMsMTY2LDEyMCw2MSwxOTYsMTA4LDY2LDI4LDMsMTA3LDY2LDI3LDMzLDMyLDE0MCwyMjEsMTY0LDEzMSwyLDE2MCwxMzUsOTcsMTYxLDEyMSw4Nyw5Nyw2MCwxNTgsMTAzLDE0MSw5OCwyMjgsMjAsMTUzLDExNSwxMDAsMTA4LDg4LDY0LDM0LDEwNiwxMDIsODgsMywxMTUsOCwxMzUsMTkyLDE0MCwyNTIsMzksODgsMjIwLDE4NywzLDMzLDE1NywyMjAsMTI5LDY3LDY5LDE3MSwxMDQsNDgsMjEwLDE3OSw2LDE1NSwxMDEsMTQ4LDE3OCw3NiwyNTAsNTMsMzIsMTk3LDEwLDE1MSw0OSw1NCwxODAsMjMwLDE2NSw3MCwxNDgsMjMzLDE4Niw4MSw1Myw2NywyMTIsNSwxOTcsMTc3LDEwNiw5MCwxOTAsMjI3LDE1Miw2NCwyMDAsMTYzLDIyNCwyNiwxODMsMTMwLDU3LDExNiw1MCwyMTAsNTIsMTIwLDE3NiwxNjQsMjA4LDE3OSwxODYsNTAsOTcsOTEsMjUwLDIxNiwxODUsMzEsMjQ4LDE4Nyw5MiwxNTUsMjA1LDIzOSwzNCwxODYsMjU0LDI4LDM2LDQwLDAsMjgsMjIzLDQwLDI0LDEzMiwyMTQsMTA1LDYsMzQsODksMTEyLDIzMSwxMSw5LDEwLDE1OSwxNTEsMjE0LDgsMTMxLDQwLDE0OSw1MiwxNjIsMjcsMzgsODUsMTc1LDkzLDE0NCw2MCw4Nyw5Miw0LDIwOSwxOTgsMTE2LDI3LDI1MCw4NiwxNzYsMTIwLDk4LDQzLDEwNCw4OSwxMzEsMTEsOTgsMTExLDY1LDg4LDE2NiwxMDYsNDksODEsNjcsNSwxMTcsMjM1LDE1MSwxMTIsMjgsNjYsOTQsMTM4LDM2LDEzNCwxNjcsMTgwLDkwLDI0NiwxNDYsOSwxNDgsMTI3LDI1MSwyMjcsMTA1LDY0LDM0LDE0NCwxMTIsMTYzLDYzLDEwNSwxNzIsNTcsMjIzLDI0Miw0MywxMjcsODMsOTUsMTQyLDIxNywzNyw1NCw5OSw3MSwxNCwyMTgsMTg4LDIxMiwxMTIsOTksMzMsMTIyLDE5OCwyMjksMTI0LDEyMCwyMzAsNjQsMTg5LDIwNSw0MiwzOCwyMDIsOTIsNTcsNzMsNjQsNzEsODEsMTM4LDEyNCwyOSwxMTUsMTcxLDMwLDE2MywxNSwyNDYsMTI5LDMyLDEyLDM0LDE1Niw5MSw2Niw4NCwxODgsMTYwLDIwNyw2MCwyMTQsMTQyLDEwMywxMSwxNjEsMTUsMTQ2LDE3NCwxOTEsMTE0LDUwLDIyNywxNzAsNzcsNTIsMTMzLDE5Niw2LDE0OCw4OCwzMCwxODIsMTkzLDEyNCw2NiwxMTYsNDgsMTIzLDE0OCwyMjIsNDgsMTc1LDI0NCwxOTgsODAsNTgsMTIyLDg3LDU4LDEyMCwxMjksMTg4LDE3NiwxMywwLDI1MCwyMDQsODIsNzAsMTg3LDIwNCwxMzYsMTAwLDIxMCwyMTQsMTQzLDYsMTI4LDEzNCwxMjUsMjQwLDIwNSwxOTAsODMsMTY2LDI0LDE1MCw2LDE3MiwxMjMsOTgsMjM5LDIzNiwxNjAsMjE1LDk3LDE3LDIyMiwyMDQsMTY4LDExMiw2Nyw5NiwxMjcsMTUyLDEzMiwxOTEsMTM1LDE2OSwyMjYsMTc3LDE1OCwyMjMsMTg3LDE5NCwyMSwxMDUsMTg4LDIzMiwxMzEsMTc1LDY0LDIwOSw0NSw2NiwxNDksNTYsMjI5LDcsNDMsNjMsOTgsMTM2LDE5NCwyMDQsMTIzLDI2LDQsMTEwLDIxNCwxMzAsMTg3LDIwLDk5LDkzLDU3LDgzLDE3NiwyMTQsMTIzLDE4NiwxMDAsMTYsNzQsMjI2LDI0MiwxNDUsMTI4LDE2MywxNSwxMTksMjAyLDU2LDEwMSwxNjgsMTQwLDUwLDE1MiwxMjQsMTM2LDIwLDAsNTksMTY3LDU4LDEwMiw3MCwxMzAsMjQ1LDQzLDk3LDEzNCwxNzYsMTA2LDEyMywyMDMsMTQ5LDE4OSwyMiwxOTYsMjExLDc0LDE1MiwxNjEsMjI2LDEyNiwyMjAsMjMyLDMwLDIzNSw5OSw2MSwxNzcsMjE5LDI4LDIwOCwxNTQsMTA1LDQzLDEwMSwyMDMsMjQ2LDM1LDQ2LDc5LDI1MCwxNjEsNDYsMzUsODgsMTYsNDMsMiwxODAsMzQsMTEyLDE2MSw4MywxMTEsOTIsMTk5LDU2LDIzMCw4LDEyMiwxNjQsMTY4LDM5LDEyNSwxNzcsMTkxLDEzNCw0LDc3LDE3NywyMzIsMTE0LDI5LDUyLDE4MCwyNCwzLDIwNiwxMiwxODUsMzgsMTk1LDIzMywxODEsMTI2LDIwOCwxNiwyMjAsMTIsODUsMjQsNTUsMzIsMTk1LDE4NCwxMCw0NCwyMTAsNzQsMjA2LDQwLDE4MSw5Myw5MiwzLDE2NiwyMywxMTgsNCw4NSwzMCwxNywyMTYsMTA3LDIzOSwxMzMsMTE2LDYyLDE5MiwxMzMsMTM0LDQ0LDk5LDI0MSwxMzksNTMsNjksOTYsNjEsMjQwLDEzOSw5NSw5Miw3LDE0OSwxMzAsNDQsMTgzLDIyLDE4MiwxNzUsMjMyLDE4NiwxODAsMTk5LDI0MCwzNSwyNDIsMTIwLDEwOCw2MSwxODQsMzYsMzQsMTYwLDE4Niw3Myw1LDE3MCwyMzYsMjA4LDE4NywyMzksMjMsNTIsMCwxOTYsMTAzLDE4OCw0OSw5MCwyMTgsMTA3LDMzLDEzNSwxNjYsMTksMTgyLDE1MSwxOSw5Miw3MSw0OCwyNDQsMjAxLDE0OCwyMDAsMTIsMzMsMTQ2LDUyLDE4MiwxNCwyNDQsODQsMjE2LDIxOCw1MCw2MSwyMjAsNiw1LDExOCwyNCwxMTIsMjQwLDE1NSwyMzksMjQ4LDEzMiwxNiw2OSwxMjYsMTg5LDEwNiwxNDMsMTk2LDE3LDIwNCwyMDksMjI5LDExOSw0NSwxMTksODUsNjQsMjA5LDc5LDEyMiw4OSwxNDIsNTMsMTQ4LDE1OCwxMzQsMTY2LDEzMiwxMzgsMjQ0LDE5NCwxOTksMTcxLDEwNSw0Nyw0MSw5OCw2OSwyNDgsMzMsNDYsMTc5LDE3MiwyNDcsMTM2LDQ1LDEwNywyMTYsMTgyLDE5Niw5Nyw3OSwyNCwyNTAsMTc4LDEwNiwzNCw5MiwxNDQsNDQsMjYsMjQwLDI0OSwzNywyMSwyMjAsMTE1LDM1LDM5LDIxLDIzLDE5NSwxNzMsMTU5LDIwOCwxNzcsMTA0LDUxLDE2MSw3OSwzNSw4NiwxNDksMTc5LDkyLDIyLDE2NSw5Myw5MCwxMzcsMTIzLDEzNCwxNzksNjMsMTkwLDcxLDE4MCwyNDUsMjQ5LDgsMTM5LDEzNywxOTYsMjM5LDIwNCwwLDExOCw2OSwxMCw3OSwxNTMsMTE2LDIzMiwxMCw5OSw3MywxNiwyMDAsOCwxMzYsMTUsMTQ4LDI0Miw1LDQ4LDAsMTAsMjgsMTI0LDcwLDczLDcsMTY0LDIyNCw3MiwyMDQsMTM2LDI2LDUxLDY1LDExLDY4LDE4NiwxMTgsMTYwLDE3NCwxMTcsNjcsNzIsOSw4LDY1LDE4OCw2NywxNTIsMTE0LDE0NCw4OCwyLDI2LDE0OSw2NywxOSw3OCwzNCwxNDcsMTQsOTcsMTQ4LDI0MSwyMzEsMzksMjIxLDE3NywxMzEsMjEyLDE3LDMyLDg4LDIxNyw0NiwyMDgsMTgsMTE4LDIzOCwyMzMsMTAzLDE3NywxNzIsOTEsMzEsMTY3LDMwLDI1MSwxNjUsMTYzLDE4NywxNDQsMTUxLDE3OCwxMTksOTcsMjI1LDI0NCw4MSwxMzEsMTgy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MTczLDI3LDEwNCwzMiw3OSwzNiwyMDUsNjYsODMsMTYzLDE0OCwxMzYsMTczLDI0OSwxNzcsNTgsNiwxMTksMTMyLDE4MCw1OSw0LDEyLDEyMSwxNDMsNDYsNDAsMTQ5LDQzLDEzOSwxNCwxMzYsMTU0LDE0OSwzOCwxMDgsMTg3LDE4NCwxMTIsNDgsMjE2LDEwOCwxMDksNTcsMTE0LDc5LDUwLDcsNiwyMjAsMTA0LDEwNywzOCw4OSwxNDgsNjQsMTE1LDE0MSw1OCw3MCwxNTgsMTYzLDIxMywxNDgsMjQ0LDM2LDIwNCwyMzAsMjAxLDAsNjEsMTAsMzAsMjEyLDE5MywxMzIsODgsODQsMTk1LDExMywyMjgsNDMsMjE2LDEwNywyNSwyMzMsMSw3NiwxMzIsMjA5LDAsMjI5LDE3LDkxLDEzLDExNiwxMTcsMjYsOTksMTUsNzYsMTA1LDEwMywxMDksMjMyLDEzNCw4MiwyMTUsMTg5LDEzLDExMiwzMyw2OSwxNTYsMTAxLDk3LDI0NSwxOTIsMTIzLDU5LDE3MCwyMDUsMTIwLDg4LDE2MywxMTMsNDMsMTkwLDEzMSwxMzksMTQwLDIxMSwxNjgsNzcsMTg2LDEwMSw3NSw1Niw4Myw4NywxNTUsMTMyLDE3Niw5MCw4OSw0NSw5MCwyMSwzNywyNyw1MiwzMyw4OCw1NywxNTQsNTksMTMxLDE0MiwyMTksNzAsMTYyLDEzNSwxNDEsMTU1LDU2LDE5LDE1NSwxNTIsMjMsMjQzLDEwOCwxNTYsMTEyLDE3Nyw2MywxMyw4NCwzMCw2Nyw0MywzMSwxMjMsODIsNTEsMjAzLDEzOSw5MiwyMjEsMjQxLDE0MywyMzQsMjAsMTg5LDI4LDYwLDE5NywxMjQsMTY4LDc2LDYyLDk5LDEzLDgyLDIwOSwxMzAsMiw3Miw0NSwxMTYsOTgsMjgsODEsNTMsNyw0LDU3LDQ0LDIwLDUsNDgsMjQzLDE2MiwzNywxOTksMTkzLDE1NiwyMjIsMTgzLDE3MywxNDcsMjE5LDE3NCwxMDcsOTYsMTk5LDQ1LDE3NywzNSw2LDEwMywxNjMsNTcsMTAwLDEyMSwyNSwxMzksNywxMywxODQsMTY2LDE5OSw1OCwyNSw5LDEzNiwxNTEsMTI4LDAsMTExLDAsOTksNDYsMjQ1LDIyNiwyLDE1LDEzNywxMDksMTY5LDE4MSwxNjIsNjQsMjExLDEwNSwxMDMsMTEzLDE0MywyMjYsODIsOTAsMTM2LDIwMSwxMjUsMTEwLDI0MywxNDMsMTY1LDEsMTA0LDE1MCwxMDgsMzcsMzAsMTAxLDgsMCw5OCwxOTIsMjU1LDE3LDE2OSwyMTksMjQsNTUsNCw0MCw2NSwxNDIsMTM1LDE3NiwxMDQsMTAwLDE3NywxODYsMTkzLDEyNiwyMjQsMTY3LDU1LDIxNywxMTcsMTgxLDc4LDQsMTkyLDUzLDEsNzQsOTgsMjUwLDE1NSw3Miw4NCwxNTksNzgsMTc5LDI2LDMxLDE5NCw0MCwxNjgsNjQsNTgsMTkzLDk3LDY4LDI1MCwxNjEsNDksNDAsMTkyLDIwLDIwOSwxLDE4MiwxNzIsMjMsMTY5LDIxOSwxNTAsNDQsMTIxLDE0NSwxODUsMzMsMTMwLDE1MCwxMTEsMTk0LDE0NCwxNzAsMjQsMTg5LDIzNCw1MCwxMTQsMzcsMTcwLDExMCwyNDYsMTY0LDM3LDIyNCwxMzAsMTYzLDE5MiwxNDUsOTgsMTI4LDIwOCw1MSwyMzIsMTY4LDE0MCw1NCwxOTcsMTI4LDIzOSwyNDksMTE3LDQ4LDgyLDE2MSwxMTMsMTU1LDE5Nyw2NCw4NiwxOTYsODksNywyMywyNTUsNCwxNjUsMTQ5LDEsNTksMTYwLDIxNCw5OCwyMDMsMTk3LDU5LDEwMyw2NiwxOTMsMTMzLDI1LDIwNiwyNDMsMjMyLDEyNSw3MSwyMzUsNzksMTQyLDY2LDU0LDg0LDQ5LDQzLDIwLDE3MiwxMSwyNDMsMjQxLDEzNiw5NCwxMDMsMTQsMTgxLDQwLDMyLDIzNCwyNiw0MSw0MSwyMjAsMTczLDc2LDE0Niw1NiwxMTcsMTA4LDEyOSwxMzgsMTU0LDE2MiwxMjAsMTYwLDE3NiwxNjcsMTU2LDM2LDE5OSw5LDM0LDIyNyw3MiwxOTEsMTE3LDIzMyw1MSwxMDIsMTY0LDE3NiwxNCw0MCw3MCwyMzksMTY4LDE5MywyMjcsMTk4LDE1MSwxNDEsMTIxLDE0MywyMjIsMjQ5LDE2LDE4OSwyNTAsNTYsMjU0LDcsMzIsMjEwLDEzOCwxNjcsNCw3Niw3NSwxMzQsMjE1LDU0LDEzOSwyMTMsNjgsMTIxLDE0NiwxOTYsNDIsMjE2LDExMiw3NiwxMzgsNDEsMjM4LDcsMTg3LDIxOSwzLDE5OSwxOTMsNTgsMTIsMzMsMjUyLDcxLDIwNyw3Miw0OCw3LDczLDE1MCwyOCwxMzgsNDMsNTksMTMwLDEyMywxMDYsMTM4LDY2LDExMSwxMTcsMTc3LDEzMiw5MCw5NCw1MCwwLDExMSw1NCw0LDE1NCw5MywxNzYsMTQ2LDIwOSwyNTIsMTAyLDExNyw2MCwxMzQsMTU4LDE4MCwyMDcsMjA2LDEyNCwzMywxNTgsMTEyLDczLDc1LDE2OSwxNzMsMTM1LDE0NCwxNDAsMTM3LDYwLDM5LDEwOCwxMSwxMTgsNDksMTE2LDc2LDE3MSw4MywxNjIsMTc2LDIzMiwyNiwyNywxNzAsMTI0LDEyMywyMiwxMDgsNzYsMTksMTUxLDE4LDE3MCw4OSw1NywyMzAsMjAzLDIwMCwyNDQsMTcxLDM5LDEwMywxMDAsMjMyLDIzMCw2Myw5MSwxMCw3NiwxMDcsMTc2LDg0LDEzOCwxNzIsNDcsMTI1LDE4NCwxMjYsMSwxMjIsNDYsNjUsMTk3LDM1LDIyOSw1LDExOCwyMjksNDEsNjksNTYsMTE1LDE0NiwxODYsNDIsMTUsMTAsNzIsMTE4LDE5Myw5NywxNDUsMTQ1LDI0MSwxODUsMjEyLDIwNCwzNiw5MiwxMDMsMTM0LDE5OSwxMjAsMzYsMTIyLDE5Miw0Niw0MywyMzEsMTY0LDQ3LDIyLDgyLDcxLDEwMSwxODgsMjA0LDEwMCwyMDcsMTgxLDIzNSwxNSwxNjEsNiwxMjcsMTUxLDI0MiwxODksMTY3LDE2MCwxMzAsMTkwLDIzMCw1Nyw4NSw4LDE0LDIyNCwxLDE4MywxNTcsMjAzLDY5LDIwNiwzMiwxMDQsMTMyLDE2MiwxNzcsMjgsMTY0LDEyLDIyMiwyMzEsOTIsMTQ1LDIzNiwzOSwxMDEsNiwxODksMTUsMTk1LDE1MCw3NSwxMDcsNjQsMTIyLDIwOCwxMiwxOTEsNjUsNzAsODUsMjAwLDE5LDEzNiw5LDk5LDI0NiwyMjUsOTEsMjQ0LDEyNCwxMTksMjM1LDE2MiwzMCwxNzksOTIsOTQsMCwyNDIsMTU2LDIzOSw3NiwxNDUsMzEsMTQ3LDUxLDEzMCwyMTIsMTAyLDE5LDMzLDUwLDk4LDIxMCw0OCwxNTMsOTAsMTI5LDI0NCw3NCwxNDMsMTQ2LDEwNSw5Nyw3MSwxMTQsMjA1LDEwOSwyNDgsNjMsMjQ4LDEzMywxMTAsMTc5LDU3LDQsNzEsNDIsMzcsMjI5LDEwMyw1MiwxMzYsNjksNTYsMTMzLDIzMyw1MCwxNTQsMjE1LDI5LDY2LDE2NCwxMDksMjcsOTEsMTgyLDEzNiwxNDksOTIsMjgsMTA1LDEyOCwyNDUsNzMsMTEsNzAsMTM5LDM5LDE0OSwxOCwyMDAsMTI4LDE0NiwyNiw1Myw4NSwxMjYsMzQsMjAyLDE3OSwxMzgsMTY1LDc0LDMxLDE3OSwyMDEsMTEzLDI3LDU3LDU1LDE1NiwxMTEsNjksNTYsNzQsMywxMzAsMTc3LDIyNSw4LDIwMSwyNDgsMzQsNjEsNCw5NywxMzcsMTMwLDE5NCwxNjcsMTU1LDE4MiwxMiwxODcsNDMsNzcsMTE3LDIxMiwxNjgsMjQ5LDg2LDE0OCw3MywxNjEsMTYyLDIyLDUyLDEyOCwyOSw0OCw0MiwyMTEsMTAwLDEwMCwyMDIsMTkzLDg3LDE3OCwyMzUsNTQsMjI0LDE2LDAsMTAwLDExLDE3NCwxNjEsMjAyLDEyOCwxMTAsMTgwLDE5NCwxOCwxNDksNTQsMTc3LDEwNSwxODgsMTM2LDE2OSw5OCwxOTgsMzgsMTI3LDEzNCwyMTYsNDMsMjM4LDIxNCwxMTgsMTU0LDM1LDYzLDQ4LDE3MCw4NCwyMTgsOTAsNDcsNzcsMTI5LDE5OCwyMzUsNTcsMTU0LDIyNiwxNywxNDUsMTY1LDIxNiw4NywxNzUsMTgzLDE3LDI0MCw0LDc4LDIzNyw0Myw4NSwyMCwyMjYsMTY5LDE0NywxMTQsNDYsMTE0LDExNiw2NiwzNywxNTQsMTE5LDE3MCw1MywxMTQsMjMzLDE2MSwxNTAsMjI5LDE0MCwxNTYsMTMzLDEyMSwxOTQsMTM3LDIsMTQ1LDE4NiwxMTgsMTI5LDI4LDIyNSwxNywxNTcsMjMxLDYwLDIwNiwyMzIsMjgsMjQ4LDM0LDY1LDE5NCwxOTIsMTA5LDEwNSw5NiwxNDYsMiwyNDksMjA0LDE4LDEzOCwxNjIsMjMwLDc0LDEyNiwyMjAsMTAzLDExMywxODgsMzUsMTE4LDE0OSwyNCwxMzAsMzYsMjAsMTcsODAsMjQ3LDY4LDE2NywyMTEsMTMsMTYxLDYsMjEsMTE1LDM1LDE4Niw1MSwyLDg2LDE0LDE2Nyw2MCwyMzQsMTcxLDE4Miw2Miw1OCwxOTgsNzUsMjAzLDEwNCwyNTAsMTI4LDg0LDczLDQ0LDE1NiwyMDMsMTI4LDE3NywyNTQsNzMsNTcsMjI0LDcsMTI5LDI2LDIyOCw3MSwxODEsMTQ4LDEwMywxNzksMTI0LDEyOSwzMiwxMSw3Miw0LDM3LDE2NCwxNzUsMTE5LDk3LDIwMSw5MSwxOTEsMTU5LDIxMSwxNzUsNywyMzQsODgsMjE1LDI0NCwxNDMsMjQ4LDEzNywxMSwxNzUsMTM0LDExNSwxMjEsMTQ2LDE5MSw0MSw3NSwxNDQsMTI0LDEyLDEwNiw3NCwxMjAsMTkxLDU3LDg2LDQ4LDEwNywyMTgsNTgsODEsOTEsMTE1LDQyLDIxOSw3MiwxMjYsODEsMTg5LDIwOCwxNzYsNDQsMjQzLDEzMSw4Miw3MSwxNjAsOTUsMTQ0LDIzNCwxNTIsOSwxNDYsMTIzLDIwMiwxOTYsMTEwLDEzNCwxNjYsMTA5LDEyNiw4OSwxNDMsMjEyLDEzMywyMCwxNzUsMjMwLDI1LDAsNDYsNzUsMTQwLDIyOSwxOTcsMTgwLDE5Miw3NywxMzcsODAsOTksMTMwLDIyOSwyMjYsMjEsMjU0LDE5OCw3MywxNTEsMTMzLDE1NSw1MywyNTUsMTQzLDE5MiwxMDYsMTI0LDE1Miw1NywxMjYsNzYsMTgzLDIwNCw3NywyMDIsODcsMjM2LDEwMyw5Nl19fSwibGVhZl9ub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzcwLDE4MCwxMzgsNzEsMjQyLDEwOCw2MywyNDAsMTg5LDE5OSw0MSwxMTAsODksODksMTk1LDE0NSw4OSw3NCwzMywyMTMsMTQsMTY3LDkxLDEwMywxNjMsNjYsNywyMTAsMjM2LDksNTAsMjQwLDEwMCwxNTMsMTAxLDI4LDg1LDksMTYsMTYzLDE3NywxMjAsNjQsMjQ3LDExLDksMTMzLDk1LDE1OCw4OCw2MiwxNDUsMjUyLDEwOSwxMzQsMjMwLDkyLDIzMiwyMDQsMTAxLDIxNyw2MCwxNjksMTI2LDkyLDIwMywyMzgsODYsMjEsMjMzLDg0LDQ1LDE1NSwxMjksNDIsMTM3LDE1MCw1OCwxMDgsNDEsMTYxLDExMywxNDgsMTc1LDg2LDE1NiwxNiw5OCw3NSwxMzgsMTIyLDcsMzMsMTM1LDkxLDc3LDI0MiwyMywxOTEsMjQzLDQ4LDksMjUsMTAwLDQ0LDI0OCw3MywxMjQsNzEsNzQsMTQ3LDIyMCw0OSw0MywyMjgsNTEsMTg2LDEyMCw5NiwxODksMTc1LDM4LDExNSwxMDgsMTEyLDE2MiwxMDMsNTgsNDcsOTQsMjQ2LDQ4LDI0Niw2NiwxMDksMCw2Nyw2NywyMjksMTQ3LDY0LDEzNywzLDE2MywxMTksMTc5LDYzLDEwNSwyMzEsNDYsMTgwLDk4LDEzMSw4OCw0OCwxNDYsMTA0LDIwOCwxMzQsNiwyMCw1LDcsMjMzLDE0NiwyNDgsMTM1LDExNSwxNjcsODEsNzMsMTE5LDEzNyw4MSw5MCwxNzcsNDMsMTY3LDIyLDQ5LDIxLDI1LDExNiwzMywxMzAsNjIsMTEwLDIxNywxOTUsMTU2LDEwLDE3MiwxMzAsODYsNTgsMjEzLDU4LDEyOCw1Miw2NywzMywxNiwyMTksODMsOCw0LDE4MCwxMTksMTc2LDE5NywxMDcsMyw3OSwyMTYsMjQ0LDEwOCwyNDIsMzksNjQsMTY3LDI0OSw0NiwyMzksMTM2LDE2MSwxODAsMjI4LDE2NywxOTMsNSw0MSwxNTYsMTQ5LDEzOCwyMjAsMTQ3LDE0NywxMDcsMTI4LDY1LDgzLDE4MiwxMzIsMjUxLDIzNSwxNjAsMTk0LDIyOCw3OSwxNTIsMTQ1LDE3MCwxNDYsMTIyLDE2LDIwNiwxNzgsNjEsODQsMjE5LDIxLDksMjUxLDE4OCwyMTcsNjcsMzUsMTQ3LDIzMiwyNyw4NiwxNiwxMTAsMTMzLDE1Myw2MywzMCwxOTMsMjAxLDg4LDIzNSw3MiwyNTQsMTY3LDIxLDkzLDg1LDUsNjIsMjQ3LDc2LDE0LDg3LDEwNCwxNzYsMTY0LDEzLDE1NCwyMTUsMTA0LDEzNiwxMzIsMTQyLDU2LDIzMSw5NiwxNTksMTY4LDI0LDE2NywxMDEsMTQyLDEyNyw5MiwxMywyMzYsNjUsNTUsMTAwLDE3NiwxNDAsMTY5LDY3LDY4LDY4LDcyLDExNCwxOTcsMjExLDExOSw5MiwxMzgsMTk3LDIxMCwxOTksMTMyLDE2Niw1MCw1NCwxNDYsMTE1LDQwLDM4LDE3LDY4LDIzNiw0Myw4NSwxOCw4OCwxNDksMjQyLDgyLDgyLDIwMCw5NywxODAsOTcsNTEsOCwxMTcsMTk4LDExNywyMDEsMTYwLDU1LDExNiwxOTMsMTQwLDE0OCwxNyw0LDYyLDUzLDExNywyMTgsNTQsNzcsMTY3LDIxNCwxMSwyMDEsNDEsMSwxNSwxODYsMTcxLDIyNyw0NCwzNiwxMSwxOTksMjA0LDM2LDI0LDE3OSwyMjAsNTMsOTksMTgwLDEzNCwxNiwxNzgsMjI4LDQyLDE3OCw2OSwxNTEsMjA3LDEyMCw4MSwyMDIsMTE4LDE2MywxMDUsMjA0LDU0LDU5LDE5MywxMDQsMjAwLDE2OCwxNzYsNzMsMjUwLDY3LDI0NSwxNzIsMjksNzMsMTAxLDExMCwzOSwxMDQsMjUsNTksMTMzLDkyLDc1LDEwMSw4NSwxMzksMTE4LDE3NSwzNSw3LDEwNywyMTQsMTIsMTU0LDI0Niw1NiwyMCwxMDksNTMsNDgsNzksNzIsMTEyLDE1Niw4LDkxLDI1MiwzMiw3MSwxNzcsMjQzLDEzOSwxODQsMTk2LDEzNSw3Miw2NywxMjEsNDYsMjQ0LDEzMCwxMjEsMjQ4LDE1NSwyMjUsMiwxMDgsMTY4LDUwLDEwOCwxLDEwMiw1Nyw1OCwxNTAsMTI2LDEyMCwxMDUsNDgsOCwxOTksMTAsMTYwLDE4MSwxMzIsMywxMjMsNTMsMTYyLDE3LDE5MywyMDIsMTg4LDE0OSwyMjUsMjYsODMsMTMyLDExNSwxMDcsMTg2LDExMyw5OSwyOSwyNTIsNTQsMjQ3LDE1MywxNjUsMTQ4LDE0NywzOSwzMCw0OSw2Niw5NywxNjIsMTMwLDQsMTA1LDE2MywyMDAsNzMsMjA2LDc0LDE4Myw2Miw4NiwxNjYsMTg4LDEwMywyMjAsNTgsMTgwLDE2Miw1MCwxMTksMjA5LDExNSwxNjksNjQsMTMwLDEwLDkwLDQ3LDc0LDgsMTYwLDM3LDY0LDExOCwxOTMsMTUwLDEyLDQxLDI1MSwxMSw3NSwxNjQsMTMyLDE5NywyMzYsMTQ2LDIzNiwxMzQsMTMyLDI0MCwzOSwxMDYsMjQ0LDI0NSwyMCwxMzQsMjI1LDgxLDI1LDE0MCw3MiwyNTIsMTM3LDI4LDUyLDEzMSwxNjUsMTcyLDEwOCwyMDMsMjgsMjA4LDc3LDIyMCwxODMsNzYsMjA4LDczLDM4LDIzNiwxODAsMCwxOTksMTE5LDM4LDM5LDQ5LDYwLDQzLDIxNywyOCwxNiwzNiwxODAsMjM1LDExOSwxMjksMTcwLDcxLDE4OSwxNzcsMTM5LDcxLDE1MiwxMjAsMTc2LDEyNiw1OCwxMTQsMTMyLDczLDUwLDE5Niw3LDE3NCw0NSw4NCwxMDMsNzAsOTEsMTgyLDEzNSw2OSw5NSwxODUsMjUwLDEzNSwxNjYsMTYyLDE0NywxOTQsMTk3LDExNCw5MiwyNyw5OCwyMzQsMjMyLDE0MywxMDAsMTQ4LDEzNCwyMDgsMjI3LDMwLDUzLDI0OCwxMTAsMTc1LDI0NCwxODYsMzIsMTI0LDcsMTY4LDI0MCw1MSwyMDMsMjQ5LDIyLDYsMTk0LDcwLDExMSwyMSwxMTgsMywxOTIsNDIsMTc3LDI0MCwxNiwxMTcsMTgxLDI3LDQxLDIwMiwxNSwzOSw2NiwxODEsMjAxLDY2LDg3LDE3OSwxMjksMTAwLDExNCwxNDQsNzcsMTI2LDkyLDIwMiwyMjAsNjQsMTE3LDE0MiwyOCwxMzIsMjE2LDI0OSwxNzcsNTEsMTUwLDEwMiwyMzksMTIzLDMwLDE5LDgwLDIwNCwxNjAsMjMsMTM3LDExNiwxNTYsMTI1LDEyOCwxMzEsOTksMTg4LDYsMjAsMTQ0LDE1MywyNCwyMjcsMjEzLDgwLDI0OSw0MSw2Nyw2NSwxLDQyLDI0NCwxNDYsMTI5LDE4Myw4MywxLDE5NCwxNjQsMTQsMTcxLDE4NCwxMjQsMTI4LDExNyw3MywxMTQsNDksMTc3LDE5Myw4NCwxNTUsMTUyLDAsNTgsNTksMjQ1LDEyMSwxMjcsMjEzLDEzMCwxMDYsMTc5LDcsMjE5LDE2OSwyOCw3MiwxMTcsNSwxOCw5MCwxODQsMjQwLDgyLDE3MCwyNTAsMjQ1LDYzLDQwLDE4Miw2OCw4NCwxNTUsMTc0LDUxLDE3NiwxMjgsMTAwLDExLDE1MSw4NSwyNTAsMTg2LDExNiwyNywyNSwxNTYsMTcxLDUxLDE2NCwxOTcsMjAzLDE4NywxODAsNDYsMTQ3LDIxNSw2NCw3MCwxMTYsMTYsMjE3LDIyOSw5OCw1MSwxNTAsMTQ3LDIxNiwxNzcsMTQ2LDE5MSwxNTMsMTQzLDM2LDIzLDMxLDIxOCwxOTksMTYyLDIwLDIxMiwxMCw3MCwxNjQsNTQsMTYsNjUsMjQsMTE5LDE3Niw0LDIwOCwxNTEsMTg4LDUxLDcxLDE4OSwxMDksMTY2LDE1NiwyMTEsMTU2LDE4NywxOTgsMTM4LDUzLDEzOSwxNTQsNDMsODcsMTUwLDQ5LDE3OSwyNDAsNTEsMTEwLDExOCwxMzIsMjEwLDkwLDEzMCwxNTQsNjQsMTMwLDIxMywyMjcsMTg2LDUxLDEwNywxOTEsOTEsMTU0LDQ3LDEzNCwyMDgsMjA1LDIxMywyMTIsMjA2LDUsMTI4LDE4NywxMDcsMTMzLDEwMSwxOTEsMTE1LDE4LDEzOCwxMDUsMjcsOTgsMjI1LDEzMywyMTgsODEsMzgsMTE5LDIxNyw0OCwxMzYsMjEwLDIwNCwxMjAsMTY3LDcyLDk3LDEzNSwxMDYsMjAzLDM0LDE2LDQ1LDE1NCwxOTAsOTIsNTEsNDYsMjA5LDk2LDE4MCwxNjgsMTAwLDQwLDI1MCwxOTIsMTQ3LDIxOSwxMDUsMTgzLDg3LDQzLDE1OSwxODUsMjQ3LDUzLDIzOCw0NCwxODAsNDYsMTgsMjgsMjE2LDE2NSwxMDEsMTYwLDIzMSw1Myw2MywxMTIsMTAzLDIxNywxODYsMTAyLDEyOSwxOCwxNDEsNzYsMTE5LDczLDIxMiw4MCwxNDcsMTU3LDk4LDEyNSwxMjIsMjAzLDU3LDI1LDE5NywxNSwxODcsMjE5LDMsMTI1LDgyLDEwMyw0MCwwLDQ3LDIyOSwyNSwxMTcsMjQ2LDIwLDE5MiwzLDIwMCwxLDE4MCwxNTAsMjA1LDIwMSw1NCwxMDYsMTgxLDExMyw0Myw4Myw0Myw1NCw3MiwxMDUsODAsMjUsNTksMzcsMTc1LDIxNCwxMjMsMzMsMTUzLDExLDIyMSwxOSwxMzEsMTMyLDI0MCw4MCw5NSwyNTEsNCwyNTQsMjUwLDgwLDEzMywxODcsMTgyLDExMiwyNDUsNzUsMTI0LDEwNywxMjEsNCwyNDAsMTk4LDI0MCwxNjIsNzIsMjI4LDI1MiwxMjAsNzcsMCw3MiwxODEsMTk4LDI5LDEwMiwzNywxODEsNDUsMTk3LDk3LDE1LDIzLDE4NCwxNjgsMTAyLDUxLDY0LDExNSwxMTMsMjM3LDIyNSwxMzgsODksMiwxMjgsMjM5LDE4MSwxNzUsMTAyLDIxMSwxNjAsNTQsMTgyLDM1LDIzMyw1MCw5LDEwNCwxMDAsMTMxLDI2LDg1LDQ4LDE1Nyw2Nyw2OSw3MywyMDMsMTI4LDQsMTcyLDE1OSwyNDMsNzQsMTQyLDE5NiwxNjksMTksNzksMTUyLDIsNiwzMCwxMzIsMTcxLDEwMywyMjEsNywyMTEsMTY5LDc4LDIzNSwyMDYsNjksMjgsMjUyLDEzLDY5LDEyNSw2NCwyNTQsMjQ3LDE3LDE2MCwyMCwyMDIsMTQ2LDAsMjMyLDAsMTIsMTcxLDk2LDc2LDEzMywxMzQsMjQzLDU5LDI0MCwxNTAsMTQ5LDk1LDIwOSwyMTksMTk5LDczLDE4OCwxMDUsMTAzLDEwMSwxODUsMjQ1LDEzLDk1LDIyMiwyMDQsMTYyLDQ1LDEzOSwzMiwxMDUsNV19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMzksMjM3LDI1MSwxNTIsMjMxLDE0MCwyNTIsMzMsMjM3LDE3NCwyNDIsMjQxLDEyNCwxMDMsMTMwLDI0LDc2LDEwNiwxNDUsMjI3LDAsNTEsNjgsMTEyLDE2MCw0Niw0Miw0OSw4MiwyMzMsNzcsMTE3XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NTAsIm5vdF9hZnRlciI6MTcyNzk1MDE1MH19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzIyMiwxNzksMywzMiw5NCwxMjcsNjYsMjQ5LDE4OCwxMDcsNzcsMTg2LDEyNiwxODksMTM1LDUyLDY4LDQwLDE3MiwxMTAsMTM1LDI0NCwyMDgsOTUsMTY3LDIyMywxNzcsMTgyLDE2MCw0NiwxLDQ1LDEwOCwxOTEsNzIsMTE5LDE5NiwyMiwxNTAsMTk0LDUzLDIyLDIwMyw5MSwyMjMsMjA0LDE1MSwxNDQsMTI2LDIwMywyNDksMTIxLDYyLDQ1LDQsMTQ0LDg5LDI3LDE4MCw4NCwyMzYsNjYsNTksMV19fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMTEzLDUwLDEzMSwxNzUsMTU5LDEyNSwzLDIzOSwxMjUsMjA2LDE4LDg4LDU0LDMxLDIzNywxNzYsMjMwLDI0MCw3MywxMjQsNDIsNjksMTg2LDI0OCwyMCwxMTQsMjksNTEsMTEwLDEyMSwxMDksMjQ0LDQ3LDM0LDEzOSw4OCwxNyw1NiwxMjIsMjUwLDIxMSwyNTMsMjUxLDEwOSwyMjgsMTQzLDE5LDkwLDE5NSw2MywxMjIsMjQzLDEwNCwxMjUsNjQsMjUwLDgyLDI5LDEwLDE2MiwxOTksMTYsMTY1LDE0XX19fX19LCJwcm9wb3NhbF9yZWZlcmVuY2UiOnsidmFsdWUiOnsidmVjIjpbMjA1LDExNiwxNjQsMjA2LDI0MCwzNCwyMTQsNjAsMjQzLDE3NiwxMTgsMzEsMjAzLDE1NywxNCwxNTUsMjMxLDI1MywxNjUsMjE0LDcsMTE2LDc5LDMwLDYwLDIxNSw2LDIxMSwyMSwxMDIsOTIsODldfX0sInNlbmRlciI6eyJNZW1iZXIiOjB9LCJwcm9wb3NhbF9vcl9yZWZfdHlwZSI6IlByb3Bvc2FsIn1dXX0sInN0YXRlIjp7Ikdyb3VwTWVtYmVyIjp7Imdyb3VwX2Vwb2NoX3NlY3JldHMiOnsiaW5pdF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzIzOSw4MiwyNDYsMTc2LDI5LDc4LDU3LDE1MiwyMiw5NiwxMjksMywyMjksNDIsMTUyLDE3MiwyMDcsNzEsMjM1LDIwMiw2NCwxNjAsMjM2LDE4OSwyNTAsMTMsMTE0LDEyOCwxNjUsNzIsMTExLDE2Ml19fX0sImV4cG9ydGVyX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNzEsNzksMjEyLDE2MiwxNDgsMTcyLDE3NSw1MiwxMTYsMjE5LDEyMCw5MiwyMDAsMjQxLDEwNyw3OCwxMDMsOTUsMjA4LDM2LDQsMTA3LDE1NiwyNSwyMzIsMjU1LDExMCwyMjAsNTEsMTY1LDQwLDE2NF19fX0sImVwb2NoX2F1dGhlbnRpY2F0b3IiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzY5LDUzLDE3LDc4LDE5NCwxMzAsMjM1LDMxLDE2NywxMTUsMTA4LDExNywyMjUsMjYsMTUsMzEsODIsMTgwLDY5LDEwNCwyOSwxMjEsMTIwLDk5LDE5MCwyMDAsMjQwLDI0LDE3NSw3MSw1OSwxMDVdfX19LCJleHRlcm5hbF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEwOCwxNDQsMTg4LDIyMCwyMjUsMjM3LDIyNCw4NywxMzMsMTc5LDg3LDIyMiwyOCwxNzQsMTQxLDE2OSw0NiwyNDgsMjQsMTQ0LDIwOCwyMTIsMCwxODEsMTk2LDkxLDE0LDExOCwxMzksNDEsMTk5LDddfX19LCJyZXN1bXB0aW9uX3BzayI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTE4LDEzLDg5LDcxLDM0LDE4MSw4NiwyMSwxNDYsMTQ4LDM0LDc1LDE3NCwxNTksMjEwLDE1MCw0MSw1MiwxNTksMTMwLDI1NCwyNDcsMjQ2LDUsMTg5LDE3MywxODEsMTM4LDEzNywxMTgsNDAsODddfX19fSwibWVzc2FnZV9zZWNyZXRzIjp7InNlbmRlcl9kYXRhX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTk1LDE0NiwyMTIsNjksNDQsMTUyLDc0LDEzNSw3OSwxNDQsMTQ0LDIxMCwxNDAsNzIsMjQzLDE0MSw2NSw3OCwxMjMsNzUsMTUxLDE2OCwxNDEsMTU3LDEyMCwyMTYsMjEwLDI0MCwyOSw0MiwyMDYsNjFdfX19LCJtZW1iZXJzaGlwX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTQ2LDE0NiwxMDgsMjA5LDc5LDEyOSw4NywzMywzMywxMzcsMTcwLDIwLDU2LDI0NywxNDMsMTk4LDI0NSwxMDEsODEsMTcxLDY2LDEwOCwyMTgsMjQzLDE1NywxMDQsMjE1LDQ2LDIyNSwyMjYsMTYxLDEzNF19fX0sImNvbmZpcm1hdGlvbl9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6Wzg3LDk2LDExOSw3NSw2MiwyNDcsMTAyLDM1LDQ4LDUxLDI1MSwyMSwxOTgsMTEyLDI1MSwyMTgsMTY1LDIwMywxODIsMTY0LDE5NiwyMCw5Myw4Myw1MywxNjksMTQ1LDY5LDIyOSwxMjYsMzQsNzVdfX19LCJzZXJpYWxpemVkX2NvbnRleHQiOlswLDEsMCw3NywxNiwyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMiwwLDAsMCwwLDAsMCwwLDEsMzIsMTI1LDExNSwyNDQsMTM3LDIzNiwxNTcsMjQsMTM0LDEzMCw3MywxMDEsMjE0LDYwLDg2LDE4OCw5MiwxLDI0Myw3NSw0Niw4NSw2MCwyMjAsMTkwLDEyNywxMjEsNTQsMTU2LDE3LDI1MSwyMzUsMTM1LDMyLDE3Miw2NCwxNTAsNTMsMTcsMTk1LDI1Myw2OSw3LDE2NCwxODcsMTA3LDE2OCwxNzUsMjQxLDkxLDIxMCwyMDUsMTcyLDM1LDEzLDYwLDE4LDI3LDgyLDIxMSwxMTQsMiwyNTEsMTY1LDEzNCwyMjksMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGwsbnVsbF0sInBhcmVudF9ub2RlcyI6W3sic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE3Myw4NywxNDUsMjEwLDIyOSw1Nyw3MCw5OCwxOTcsNzYsMTQyLDIzMywxNTcsNSwxNzMsNjcsMjAzLDEyNSwyOSwxNzksMjQzLDQsNTgsMTg1LDIwMiw5NSwxLDU1LDE0MSwxNTgsNSwxNDddfX19LG51bGxdLCJoYW5kc2hha2Vfc2VuZGVyX3JhdGNoZXRzIjpbbnVsbCxudWxsXSwiYXBwbGljYXRpb25fc2VuZGVyX3JhdGNoZXRzIjpbbnVsbCxudWxsXSwic2l6ZSI6M319LCJzdGFnZWRfZGlmZiI6eyJzdGFnZWRfZGlmZiI6eyJkaWZmIjp7ImxlYWZfZGlmZiI6eyIwIjp7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTM0LDE2OSwxMDQsMjA3LDEzNCwxNDUsMjAsMjcsOTgsMjE0LDY3LDY0LDEwMCw5Miw5MSw3OSwxNjgsMTY4LDI4LDIwOCw3NCwyMzIsMTEsMTc3LDg3LDI1MSwyNSwxLDI1MCwxNzEsMjA5LDE2LDE2MywxNzUsMTQwLDQxLDkyLDE1MiwyMiw4MCw4MCwxMDcsNjgsMjUxLDMsMTU5LDIwOSwxMjUsNDgsMTY1LDEwOCwyMzYsMjAsMTEsNTQsMTAwLDE4OCwyMjgsMTQ0LDE3NiwxODYsMjIwLDE0NywxMDgsMTYsOTMsOTMsMTc2LDQzLDIxMiw2NSwzOCwxNDcsMTYwLDgsNzIsMjcsMTA0LDIyNCwyNDMsMTMzLDE0MiwxMDEsMjA2LDIwNCwxOTcsNjYsODgsMjQsMTQzLDEwNSwxMzcsMTAzLDAsMTk2LDQ5LDI0MSwyMDQsNTMsODcsMjIsMTI3LDE4OCwxMzIsMzgsMTEyLDE2OCw1MCw5Niw0NCw2NywyNDQsMTM5LDExMSwxNjUsNTQsNTMsMTg2LDIxOSw4NCwxMzEsMTEsODIsMTkwLDk2LDE4LDIzMCwxOTMsOTcsMjA5LDgwLDg1LDY0LDczLDU0LDkzLDE1NSwxODgsMTQsNiwzNSwxODIsMTMyLDE3MCwxMTYsMjMwLDQyLDMzLDIzNSw4NSwxMjYsMzksMTA1LDEzOSw5Miw1Nyw2LDE4Niw4OSwxNTcsODMsNDAsMjA3LDE3OSwxNjEsNjAsMTQwLDIwNiwxOTgsMTcxLDExOCwyMzIsMTM0LDg4LDY5LDE0MCw4OSwxMzUsMTE3LDYyLDE1NSwyNTAsOTUsMTUyLDIyNiwzMCwzMCwxMTQsMTkyLDk4LDIxNywxMTYsNzYsMTY4LDAsMjI1LDEsMTU3LDE5MCwxNzIsMTAzLDE0MSw5MiwxNTUsMTgsMTE3LDU4LDEzMCwxODEsMTk2LDI3LDU4LDc5LDk5LDg0LDgsMTg5LDE5NCwxMjgsNSwxMTQsMjEsMTUsOTIsMTg5LDk1LDEzMywxODIsMTQ5LDE4Niw1MiwyNDksMzksNzIsOCwyMSwxMDksMTQsNTMsMzcsMTc4LDE5NiwxMTQsMTc5LDE3MSw2OSwxODksNDEsMTU1LDE0LDgzLDE1NywxNzAsMjQ0LDM5LDE0OCwyNDQsMTcwLDgwLDE4NiwxNTUsMTAwLDI0MywxODksNTAsMTc3LDE0NSwyMjUsODQsMTgsMiw3LDE0OSwyMTgsODQsMTc2LDE5Miw1MCwxMTksNzYsMTAwLDAsNjcsMjgsMjA2LDIyOCwxNDksMTAxLDE3NywyNDcsMTk5LDk4LDE1NCw4LDI0NiwyMjAsNTAsMSwxMTgsMTY3LDYwLDcyLDE1MCwyMjksNTgsMTg5LDQ1LDIyMCwxMTksMjE4LDQwLDI4LDE4NiwxOCwxNzQsMCwyMDksMTQwLDE3MSwxNjMsMTQsOTQsOTIsMTM4LDIxNyw3NSwxNDAsMTcwLDIyNywyMDAsMTI3LDE1NSw0OCwxODEsMTIsMTMzLDIyNSwyNDksMTYwLDQxLDIwNCwyNiwxMDEsMjE5LDAsMTg0LDE1NCwyMDUsMjMzLDE5NiwxNDIsMjMyLDEyMywzMCwyMzcsMjEwLDkzLDY0LDgzLDg2LDE1MCwyMCwxODksMTA3LDU1LDI0LDE2LDg0LDIzLDExNSwxNDUsOCwxMTEsMjcsNCwxODQsNTYsNDQsMjQ2LDIwOCwxMDYsMTE1LDkxLDE1OSwyNDQsMTIzLDE5NiwyNywyMTYsOTcsMTUwLDIwOCwyLDI1Miw2MCwxNzgsMTkwLDM5LDYsMjIsMTQ3LDE2MCwzMywyNDUsMjEsMTYzLDIxNiw1LDE5LDEzNiwxNTYsMTA2LDE1NiwxMjcsMjMxLDEwMiwyMDcsMzUsOTYsMTI5LDExNCwyMDgsODYsMTgwLDExNiwxMTksMTI2LDc0LDEyLDY3LDYsMTMzLDQxLDExOCw3NiwwLDI2LDIwNiw2LDUsMTAzLDE1MSwyMDQsMTM2LDE2NCwzOCw2NiwxNCw5MiwxMjUsMTYzLDUzLDE1MSwxMzAsMjM1LDE4NCwxNTgsMTQ5LDE2MSwyMzgsMTk5LDcxLDI1MSwxODcsMzUsMTg4LDI0LDMxLDE2MywyMzUsMTYzLDYwLDYwLDEzMCw3OSwxNTQsMjUsMjE3LDY0LDE5NSwxNzQsMTM5LDg1LDE5NSwyMDgsMTM5LDczLDYwLDE5NiwxMjksNjQsMjAzLDE2NiwxMjgsMTM5LDIwNCwxOCwxMjcsMTM4LDIyOSw3NywxNjgsMTYxLDE5MywyMDcsMTA1LDAsMTgwLDEwNywxNzIsNjUsMTE3LDYzLDc0LDM2LDE2NSwxNCw1MSwxMjQsMTYsMTA3LDIzLDgyLDE0NSwxMDYsMjQ3LDE0NCw0NCwxOTMsMTIwLDMsMTA0LDE2OCw0MSwxODgsMzQsMjA2LDI1MSwxNzAsMjA1LDIwMywxMTIsMTcxLDE2MSwxMzMsNzYsMTQ4LDEzOCwzNCw4MCw0MSwxNjgsMTI4LDIzLDIwMyw4MSwxNDcsNTAsNTEsMTQ1LDE5OCwxNTIsMjM0LDk2LDcyLDE4NSw1MCwyMzEsMjcsMTQ1LDIwNywyMTEsNDIsNDAsODYsMzEsMjUwLDg2LDg4LDIzNiwxOTcsMTY5LDE3NiwxNjUsMTMwLDIzNCwyNDQsMTg0LDE1NSwxODYsMiwxOTksNTUsNjksNTcsMjAwLDEyOCwxNSwxOSw3OCwyMzEsMjEzLDE3MiwyMCwyMTQsMzQsMTg5LDIxMiwxODMsNCwxMSwxNDIsMTg4LDE2MCw0OSwxNTcsMjcsMjEsMTU0LDE2NCwxNzksMjIwLDE2NiwxNzQsMjA2LDEzMSwxMSw2NCw4MCwyOCwzMSwxMjEsMTEwLDE4MSwxMjIsMTMzLDIyNSwxNDcsMTc5LDk4LDg5LDE3OSw2NiwxNjgsNDIsMjQwLDg3LDIwNSwxODcsMTk1LDExMCwxOTksMjIwLDEzLDE4Myw0NCwxOSwxLDE1MSw1OSwxODAsMTYxLDE5MSwxMDMsMTIzLDEzLDI1NSwxNjgsNzAsODQsMjYsMjA0LDEzMCw4NiwzOCwxMzYsMjMsMTM0LDE1OCwxMDEsMTI4LDI1MywyMDIsMTM5LDE2MiwyMTQsMzAsMTkxLDExMiwxMjAsMjAyLDE1MCwxNzcsMywxNjUsODMsMjQwLDE2NCwxNjYsMjQ2LDE1MiwxNDcsMTk5LDg0LDE1LDIwMiwyMzQsMzAsNzUsMzgsNzMsOTgsNjcsMTY0LDEyMCw1NCw3LDEyMiw1Myw3MSwzNywyNSwxOTcsNzUsMTg3LDEyLDE4OSwxOTcsNDEsMTkwLDMyLDEyMywyMTQsMTM5LDIwNSwxNzIsMjQ3LDEzMywxNTAsNywxOTUsMTI2LDgwLDEyMCwyMDEsOTgsMTQyLDIxNSw4NSwyMDAsMjM0LDc1LDUwLDIxNCw0OSwxMDYsMjE2LDU4LDE5LDY4LDIxMyw3MywzNywxNDgsMTM0LDIwMSwyMzUsMTAyLDE5MSw3NSwxODAsMjE0LDU1LDE4NywxOTksODYsMjksMCw3LDIwMCwxMzMsMTM1LDQyLDE4OSwxODAsMTc4LDE0MiwzNywxMjAsMTI4LDM2LDcwLDIzNCwxMzIsNTksNDYsMjE3LDE0Miw4Myw4Niw2NiwxOTMsMTY4LDE0MiwxOTgsMTAzLDY1LDEzOSwyNDMsMTUxLDI5LDEzOSw2Myw2MywxODgsMTIyLDQyLDM1LDY5LDE1MCw5OSwxMTEsMTQ2LDE4NCwyNSwzOSw5MCwzMywxMjYsMjQ5LDE4OSwxMjcsMTc2LDYxLDExNSwxMDUsOTgsOTEsNTMsMTM1LDExNCwxMjIsMTYwLDgyLDczLDE2NSw1Nyw2OCwxMTAsMjA4LDEyNCwxMjUsMiwxNjksMTE4LDExOSw4NCwzNywxMjgsODYsMTUwLDUxLDEwNSw4NywyNTUsMzMsMjAzLDIwMCwyMzEsMTAzLDEyOSw1Miw2NCwxODgsODIsNTgsNjYsMTYzLDU3LDIxMSwxMjAsMTEwLDE4NiwxOCw4NSwxMzEsMjE4LDE5LDE3LDgsNzgsMjA1LDE3Nyw1NiwyMjMsMTIwLDkyLDQzLDE5Niw3LDEyNywxNTYsNDIsODMsMTA3LDEwLDIyMywxMzAsMTAsMjQwLDIzMywxODksMTUsMTk4LDQwLDI0MCwxMTgsNSw0NSw2NSwyMSw5NSwxODcsMTc5LDEwNiwxMzIsMTEzLDE5MiwxMCwzMCwxMzMsMTY0LDIxLDkzLDkyLDE1OCwxNzAsMTM5LDEyNiwyNDgsMjQ4LDEzMSwyMjcsMTgyLDk0LDE1OSw0LDM3LDU0LDE5OSwxODcsMTQ1LDExNywxMzMsMTcsNTcsNTUsMjEyLDM1LDExMiwxODUsMTMxLDksMjA5LDY0LDc0LDE0NiwyMTcsNTIsMjIsMjI0LDQ5LDE5NSw4NywxNzQsNjAsMjEsODgsNzYsMjA4LDg3LDE5NSwyMjYsNzMsMjAxLDIyOSwxMjgsNjgsMjM2LDg0LDIyNywyNDIsMTY0LDc5LDE5NiwxMzQsMTE4LDY3LDEyOCwyLDEwLDE3LDIyNiwyMCwxMjYsMTM3LDMsMTg2LDEwMSwxNDQsMjAwLDE4OSwxNTMsMTE0LDE2MSwxOTksNSw5MiwxNDgsMTEyLDU0LDIsMTQ0LDYwLDIxOCwwLDIzLDIzMiw2MCwyNDMsMjEyLDE3NSwwLDM3LDI1LDQ3LDIxMiwxMTAsMjE2LDQ4LDQsMTQ3LDIyNCw1NCwxNCw3MCwyMDAsMjMzLDIxMiw1NiwxNCwyMTQsMTM1LDc5LDgxLDE1MCwxMTYsMTMzLDMsNzUsMjMyLDE4OCwyNTMsMjIwLDEzOSwyMDIsMTQ0LDU2LDgyLDUyLDMsMjUsMjI4LDk3LDk4LDI1MiwyMDEsNzYsMTQwLDE5LDE0NCw5OCwxMDIsMjAyLDEzNiwxODcsNTAsMTkyLDEwMyw0NCw4Niw5NywxMjcsMTIwLDEyLDg4LDIzNiwxNjMsODAsMjE0LDE4NCwyMjAsMjE1LDQwLDEzOSwxMTcsNzEsMjE5LDE4MSwxMTcsNTEsMjEyLDEyNCwxMDIsMTEzLDEwMSwzMSwxNDYsNDYsMTkxLDIwMSw5OSw2LDIyOCwxMjIsMjI4LDcxLDIwNyw5MCw5Miw1NCw3MCwyNTAsMTYxLDIyMSwxMzksMTExLDQ2LDg1LDE4MCwxNjUsNyw1NiwxMjUsMjEsMzQsNjcsMTE5LDExMiwxNjUsMTEsMTk3LDEwMCw2NSw2NywyMjEsNTksMjM0LDE4OSwxOTUsMjAzLDE2MiwxNTksMjAzLDE5OSwxNTgsMTgyLDE2NSwxLDE5MiwxNTUsMTE1LDI5LDE3OSwyMzMsMzUsMjE1LDk1LDE5LDIyNCwyLDE5LDgzLDIxNiwxMiw4MSwxOTgsMTgzLDE4MCwwLDU4LDI0OCwzOSwzOSwyNTMsODQsNTAsOTQsMjU0LDIyNSwyMTIsMTUsMTAsODgsMjUzLDIxMCw5LDI0NiwzNywyMjQsMTY1LDE4NSw5LDYsMzMsMF19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjQsMTgwLDEyNSwxMjYsNzksMjIwLDIyMSwyNTMsMTIzLDQyLDIwMSwxMjAsMTgwLDE2NCwyMDAsMjA4LDE3Nyw5OSw4Miw2MCw1NSwxNTYsMTI2LDc3LDExNywxMTUsMjAzLDIwMywxNDIsMjU1LDE2NiwzNF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbMTY1LDI0MCwyMjIsMTQ1LDE5MSwxOTksMjAxLDIwOCw3NiwyMDMsMTYyLDE3OSwxNDIsMjM1LDExNywyMjcsMTM0LDU2LDExNSwzOSw4Miw1MywxNTQsNDksMTc3LDIxOSw4OSwyMDksMTg0LDMsMTM5LDIwOV19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMzIsMzAsMzksODMsNjksMTA3LDE4OSwzNiwxMDEsMTA0LDE3MCwyMjAsMTM0LDI1MSw0MSw0NSwxNjQsOTYsMzksMjQyLDU3LDU5LDE2OCw4Myw4NCwxNjMsMTM5LDE4OSwyMzEsMTI4LDEyNywxOTUsMTkwLDE1NCwxMjIsNTQsMTI2LDE1NywxMzAsNTksNzAsMTQ0LDEwMSwzMiwxNzAsMjUsMjA4LDIwNCwyNDcsMTM0LDE1NSwyMDksOTksMTE1LDU0LDEzNywyMywxMjksMjE5LDE0MywxNTIsMTU2LDEwMCwxM119fX19LCIxIjp7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNzAsMTgwLDEzOCw3MSwyNDIsMTA4LDYzLDI0MCwxODksMTk5LDQxLDExMCw4OSw4OSwxOTUsMTQ1LDg5LDc0LDMzLDIxMywxNCwxNjcsOTEsMTAzLDE2Myw2Niw3LDIxMCwyMzYsOSw1MCwyNDAsMTAwLDE1MywxMDEsMjgsODUsOSwxNiwxNjMsMTc3LDEyMCw2NCwyNDcsMTEsOSwxMzMsOTUsMTU4LDg4LDYyLDE0NSwyNTIsMTA5LDEzNCwyMzAsOTIsMjMyLDIwNCwxMDEsMjE3LDYwLDE2OSwxMjYsOTIsMjAzLDIzOCw4NiwyMSwyMzMsODQsNDUsMTU1LDEyOSw0MiwxMzcsMTUwLDU4LDEwOCw0MSwxNjEsMTEzLDE0OCwxNzUsODYsMTU2LDE2LDk4LDc1LDEzOCwxMjIsNywzMywxMzUsOTEsNzcsMjQyLDIzLDE5MSwyNDMsNDgsOSwyNSwxMDAsNDQsMjQ4LDczLDEyNCw3MSw3NCwxNDcsMjIwLDQ5LDQzLDIyOCw1MSwxODYsMTIwLDk2LDE4OSwxNzUsMzgsMTE1LDEwOCwxMTIsMTYyLDEwMyw1OCw0Nyw5NCwyNDYsNDgsMjQ2LDY2LDEwOSwwLDY3LDY3LDIyOSwxNDcsNjQsMTM3LDMsMTYzLDExOSwxNzksNjMsMTA1LDIzMSw0NiwxODAsOTgsMTMxLDg4LDQ4LDE0NiwxMDQsMjA4LDEzNCw2LDIwLDUsNywyMzMsMTQ2LDI0OCwxMzUsMTE1LDE2Nyw4MSw3MywxMTksMTM3LDgxLDkwLDE3Nyw0MywxNjcsMjIsNDksMjEsMjUsMTE2LDMzLDEzMCw2MiwxMTAsMjE3LDE5NSwxNTYsMTAsMTcyLDEzMCw4Niw1OCwyMTMsNTgsMTI4LDUyLDY3LDMzLDE2LDIxOSw4Myw4LDQsMTgwLDExOSwxNzYsMTk3LDEwNywzLDc5LDIxNiwyNDQsMTA4LDI0MiwzOSw2NCwxNjcsMjQ5LDQ2LDIzOSwxMzYsMTYxLDE4MCwyMjgsMTY3LDE5Myw1LDQxLDE1NiwxNDksMTM4LDIyMCwxNDcsMTQ3LDEwNywxMjgsNjUsODMsMTgyLDEzMiwyNTEsMjM1LDE2MCwxOTQsMjI4LDc5LDE1MiwxNDUsMTcwLDE0NiwxMjIsMTYsMjA2LDE3OCw2MSw4NCwyMTksMjEsOSwyNTEsMTg4LDIxNyw2NywzNSwxNDcsMjMyLDI3LDg2LDE2LDExMCwxMzMsMTUzLDYzLDMwLDE5MywyMDEsODgsMjM1LDcyLDI1NCwxNjcsMjEsOTMsODUsNSw2MiwyNDcsNzYsMTQsODcsMTA0LDE3NiwxNjQsMTMsMTU0LDIxNSwxMDQsMTM2LDEzMiwxNDIsNTYsMjMxLDk2LDE1OSwxNjgsMjQsMTY3LDEwMSwxNDIsMTI3LDkyLDEzLDIzNiw2NSw1NSwxMDAsMTc2LDE0MCwxNjksNjcsNjgsNjgsNzIsMTE0LDE5NywyMTEsMTE5LDkyLDEzOCwxOTcsMjEwLDE5OSwxMzIsMTY2LDUwLDU0LDE0NiwxMTUsNDAsMzgsMTcsNjgsMjM2LDQzLDg1LDE4LDg4LDE0OSwyNDIsODIsODIsMjAwLDk3LDE4MCw5Nyw1MSw4LDExNywxOTgsMTE3LDIwMSwxNjAsNTUsMTE2LDE5MywxNDAsMTQ4LDE3LDQsNjIsNTMsMTE3LDIxOCw1NCw3NywxNjcsMjE0LDExLDIwMSw0MSwxLDE1LDE4NiwxNzEsMjI3LDQ0LDM2LDExLDE5OSwyMDQsMzYsMjQsMTc5LDIyMCw1Myw5OSwxODAsMTM0LDE2LDE3OCwyMjgsNDIsMTc4LDY5LDE1MSwyMDcsMTIwLDgxLDIwMiwxMTgsMTYzLDEwNSwyMDQsNTQsNTksMTkzLDEwNCwyMDAsMTY4LDE3Niw3MywyNTAsNjcsMjQ1LDE3MiwyOSw3MywxMDEsMTEwLDM5LDEwNCwyNSw1OSwxMzMsOTIsNzUsMTAxLDg1LDEzOSwxMTgsMTc1LDM1LDcsMTA3LDIxNCwxMiwxNTQsMjQ2LDU2LDIwLDEwOSw1Myw0OCw3OSw3MiwxMTIsMTU2LDgsOTEsMjUyLDMyLDcxLDE3NywyNDMsMTM5LDE4NCwxOTYsMTM1LDcyLDY3LDEyMSw0NiwyNDQsMTMwLDEyMSwyNDgsMTU1LDIyNSwyLDEwOCwxNjgsNTAsMTA4LDEsMTAyLDU3LDU4LDE1MCwxMjYsMTIwLDEwNSw0OCw4LDE5OSwxMCwxNjAsMTgxLDEzMiwzLDEyMyw1MywxNjIsMTcsMTkzLDIwMiwxODgsMTQ5LDIyNSwyNiw4MywxMzIsMTE1LDEwNywxODYsMTEzLDk5LDI5LDI1Miw1NCwyNDcsMTUzLDE2NSwxNDgsMTQ3LDM5LDMwLDQ5LDY2LDk3LDE2MiwxMzAsNCwxMDUsMTYzLDIwMCw3MywyMDYsNzQsMTgzLDYyLDg2LDE2NiwxODgsMTAzLDIyMCw1OCwxODAsMTYyLDUwLDExOSwyMDksMTE1LDE2OSw2NCwxMzAsMTAsOTAsNDcsNzQsOCwxNjAsMzcsNjQsMTE4LDE5MywxNTAsMTIsNDEsMjUxLDExLDc1LDE2NCwxMzIsMTk3LDIzNiwxNDYsMjM2LDEzNCwxMzIsMjQwLDM5LDEwNiwyNDQsMjQ1LDIwLDEzNCwyMjUsODEsMjUsMTQwLDcyLDI1MiwxMzcsMjgsNTIsMTMxLDE2NSwxNzIsMTA4LDIwMywyOCwyMDgsNzcsMjIwLDE4Myw3NiwyMDgsNzMsMzgsMjM2LDE4MCwwLDE5OSwxMTksMzgsMzksNDksNjAsNDMsMjE3LDI4LDE2LDM2LDE4MCwyMzUsMTE5LDEyOSwxNzAsNzEsMTg5LDE3NywxMzksNzEsMTUyLDEyMCwxNzYsMTI2LDU4LDExNCwxMzIsNzMsNTAsMTk2LDcsMTc0LDQ1LDg0LDEwMyw3MCw5MSwxODIsMTM1LDY5LDk1LDE4NSwyNTAsMTM1LDE2NiwxNjIsMTQ3LDE5NCwxOTcsMTE0LDkyLDI3LDk4LDIzNCwyMzIsMTQzLDEwMCwxNDgsMTM0LDIwOCwyMjcsMzAsNTMsMjQ4LDExMCwxNzUsMjQ0LDE4NiwzMiwxMjQsNywxNjgsMjQwLDUxLDIwMywyNDksMjIsNiwxOTQsNzAsMTExLDIxLDExOCwzLDE5Miw0MiwxNzcsMjQwLDE2LDExNywxODEsMjcsNDEsMjAyLDE1LDM5LDY2LDE4MSwyMDEsNjYsODcsMTc5LDEyOSwxMDAsMTE0LDE0NCw3NywxMjYsOTIsMjAyLDIyMCw2NCwxMTcsMTQyLDI4LDEzMiwyMTYsMjQ5LDE3Nyw1MSwxNTAsMTAyLDIzOSwxMjMsMzAsMTksODAsMjA0LDE2MCwyMywxMzcsMTE2LDE1NiwxMjUsMTI4LDEzMSw5OSwxODgsNiwyMCwxNDQsMTUzLDI0LDIyNywyMTMsODAsMjQ5LDQxLDY3LDY1LDEsNDIsMjQ0LDE0NiwxMjksMTgzLDgzLDEsMTk0LDE2NCwxNCwxNzEsMTg0LDEyNCwxMjgsMTE3LDczLDExNCw0OSwxNzcsMTkzLDg0LDE1NSwxNTIsMCw1OCw1OSwyNDUsMTIxLDEyNywyMTMsMTMwLDEwNiwxNzksNywyMTksMTY5LDI4LDcyLDExNyw1LDE4LDkwLDE4NCwyNDAsODIsMTcwLDI1MCwyNDUsNjMsNDAsMTgyLDY4LDg0LDE1NSwxNzQsNTEsMTc2LDEyOCwxMDAsMTEsMTUxLDg1LDI1MCwxODYsMTE2LDI3LDI1LDE1NiwxNzEsNTEsMTY0LDE5NywyMDMsMTg3LDE4MCw0NiwxNDcsMjE1LDY0LDcwLDExNiwxNiwyMTcsMjI5LDk4LDUxLDE1MCwxNDcsMjE2LDE3NywxNDYsMTkxLDE1MywxNDMsMzYsMjMsMzEsMjE4LDE5OSwxNjIsMjAsMjEyLDEwLDcwLDE2NCw1NCwxNiw2NSwyNCwxMTksMTc2LDQsMjA4LDE1MSwxODgsNTEsNzEsMTg5LDEwOSwxNjYsMTU2LDIxMSwxNTYsMTg3LDE5OCwxMzgsNTMsMTM5LDE1NCw0Myw4NywxNTAsNDksMTc5LDI0MCw1MSwxMTAsMTE4LDEzMiwyMTAsOTAsMTMwLDE1NCw2NCwxMzAsMjEzLDIyNywxODYsNTEsMTA3LDE5MSw5MSwxNTQsNDcsMTM0LDIwOCwyMDUsMjEzLDIxMiwyMDYsNSwxMjgsMTg3LDEwNywxMzMsMTAxLDE5MSwxMTUsMTgsMTM4LDEwNSwyNyw5OCwyMjUsMTMzLDIxOCw4MSwzOCwxMTksMjE3LDQ4LDEzNiwyMTAsMjA0LDEyMCwxNjcsNzIsOTcsMTM1LDEwNiwyMDMsMzQsMTYsNDUsMTU0LDE5MCw5Miw1MSw0NiwyMDksOTYsMTgwLDE2OCwxMDAsNDAsMjUwLDE5MiwxNDcsMjE5LDEwNSwxODMsODcsNDMsMTU5LDE4NSwyNDcsNTMsMjM4LDQ0LDE4MCw0NiwxOCwyOCwyMTYsMTY1LDEwMSwxNjAsMjMxLDUzLDYzLDExMiwxMDMsMjE3LDE4NiwxMDIsMTI5LDE4LDE0MSw3NiwxMTksNzMsMjEyLDgwLDE0NywxNTcsOTgsMTI1LDEyMiwyMDMsNTcsMjUsMTk3LDE1LDE4NywyMTksMywxMjUsODIsMTAzLDQwLDAsNDcsMjI5LDI1LDExNywyNDYsMjAsMTkyLDMsMjAwLDEsMTgwLDE1MCwyMDUsMjAxLDU0LDEwNiwxODEsMTEzLDQzLDgzLDQzLDU0LDcyLDEwNSw4MCwyNSw1OSwzNywxNzUsMjE0LDEyMywzMywxNTMsMTEsMjIxLDE5LDEzMSwxMzIsMjQwLDgwLDk1LDI1MSw0LDI1NCwyNTAsODAsMTMzLDE4NywxODIsMTEyLDI0NSw3NSwxMjQsMTA3LDEyMSw0LDI0MCwxOTgsMjQwLDE2Miw3MiwyMjgsMjUyLDEyMCw3NywwLDcyLDE4MSwxOTgsMjksMTAyLDM3LDE4MSw0NSwxOTcsOTcsMTUsMjMsMTg0LDE2OCwxMDIsNTEsNjQsMTE1LDExMywyMzcsMjI1LDEzOCw4OSwyLDEyOCwyMzksMTgxLDE3NSwxMDIsMjExLDE2MCw1NCwxODIsMzUsMjMzLDUwLDksMTA0LDEwMCwxMzEsMjYsODUsNDgsMTU3LDY3LDY5LDczLDIwMywxMjgsNCwxNzIsMTU5LDI0Myw3NCwxNDIsMTk2LDE2OSwxOSw3OSwxNTIsMiw2LDMwLDEzMiwxNzEsMTAzLDIyMSw3LDIxMSwxNjksNzgsMjM1LDIwNiw2OSwyOCwyNTIsMTMsNjksMTI1LDY0LDI1NCwyNDcsMTcsMTYwLDIwLDIwMiwxNDYsMCwyMzIsMCwxMiwxNzEsOTYsNzYsMTMzLDEzNCwyNDMsNTksMjQwLDE1MCwxNDksOTUsMjA5LDIxOSwxOTksNzMsMTg4LDEwNSwxMDMsMTAxLDE4NSwyNDUsMTMsOTUsMjIyLDIwNCwxNjIsNDUsMTM5LDMyLDEwNSw1XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzIzOSwyMzcsMjUxLDE1MiwyMzEsMTQwLDI1MiwzMywyMzcsMTc0LDI0MiwyNDEsMTI0LDEwMywxMzAsMjQsNzYsMTA2LDE0NSwyMjcsMCw1MSw2OCwxMTIsMTYwLDQ2LDQyLDQ5LDgyLDIzMyw3NywxMTddfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5OCwxMTEsOThdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IktleVBhY2thZ2UiOnsibm90X2JlZm9yZSI6MTcyMDY4ODk1MCwibm90X2FmdGVyIjoxNzI3OTUwMTUwfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMjIyLDE3OSwzLDMyLDk0LDEyNyw2NiwyNDksMTg4LDEwNyw3NywxODYsMTI2LDE4OSwxMzUsNTIsNjgsNDAsMTcyLDExMCwxMzUsMjQ0LDIwOCw5NSwxNjcsMjIzLDE3NywxODIsMTYwLDQ2LDEsNDUsMTA4LDE5MSw3MiwxMTksMTk2LDIyLDE1MCwxOTQsNTMsMjIsMjAzLDkxLDIyMywyMDQsMTUxLDE0NCwxMjYsMjAzLDI0OSwxMjEsNjIsNDUsNCwxNDQsODksMjcsMTgwLDg0LDIzNiw2Niw1OSwxXX19fX19LCJwYXJlbnRfZGlmZiI6eyIwIjp7Im5vZGUiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzExNiwxNjYsNjIsMTI5LDE3NywxNjMsMTI0LDIxNCwxMDgsODAsODYsMTQ1LDI4LDE0NCw2NCwxODUsNCw4OSw2MCwxMTYsMzMsMTksMjI1LDE1Nyw3MCwxMjMsMTMzLDUzLDE4MCw4OSw2NywxNDksNDMsNjYsMTgzLDE0Myw0NywzNCwxODAsODcsMTYsMTEsMTEwLDI1MSw2MSwyMDIsMjMzLDE1Myw4LDEwMiw5MSwxOTgsNDksNTksMTYwLDE5OCw4Niw1OCwyNDAsMTU4LDE1OSw4MSwyMDcsOTQsMjA5LDEyMCwyMjgsODQsNTUsMjM5LDIyLDE5NywxMDMsMjM0LDc3LDE5OCwyNDEsNDcsOSw4MCwxNjIsMTg2LDE1NCwxODIsMjYsMTM5LDg2LDI0MSwyMCwxMDQsMTY5LDIxOSwxMzcsMjA3LDgsMTgsMTU0LDE2LDI1LDExOSwxNDYsMTg2LDE1Myw4OSwyLDksMTA0LDQyLDM2LDE2LDM3LDIxLDE5NSw5NSwzOSwxOTYsMTU0LDE4NSw3NCwxMzIsMjQ2LDg5LDE0NSwxMjAsMjI3LDIxLDYyLDE4MiwxMDIsMjMyLDI1MiwyLDE3OSwxNjksNDIsMTQ1LDEwMywxOTEsMjA1LDIzNCwxMTYsMTkwLDEwOCwxNDIsOTksNTksNDUsMTAxLDEyMSw5LDYzLDE2NywxOTQsODQsMTAyLDQ4LDIzLDIxMCwxMTgsMjI3LDE1MiwxMzUsMTYzLDQzLDQyLDg0LDI0NCwzNywyMDUsMTM1LDE1MSwxMTUsMTUwLDg4LDE4MCwyMzMsMTQ2LDY5LDE5LDIwNyw2Niw0MiwxMjMsMTcyLDI0OCw4OCwxODMsMTY5LDMwLDM1LDUyLDc2LDg1LDE1MywxNTgsOSwxMTUsOTAsMjUxLDk5LDE1LDE2NCwxNjIsMjMsODYsMTUwLDE3Miw4NSwxMDEsMTI5LDI0MSwxMzQsMTk5LDEyNCwyNSwwLDE5MCwxNDYsMzQsNzEsMjQsOTYsMzgsMjEsNjAsMTkxLDE0MCwxNzMsMjUyLDExMyw5NSw2MCw3NSw0OSw1OSwyNDQsMjAzLDI0MSwxMjksNjksMjM1LDIyLDMyLDE1LDIzNiwxOTAsMTIyLDEwMSw2NCwxMTEsNywxNzEsMjI4LDU3LDEyMSwxMTQsMjAzLDEzLDEwMyw5LDM0LDE1LDM2LDI5LDIyMiwyMjcsMTk4LDE1NCwzMywxMTEsMTM3LDE2OCwxMTgsMTA1LDIzMSwxMDEsNjEsMzUsMTgwLDc4LDg5LDQzLDE0MCw5MCwyNiw1MCwyMSwxNDMsMjQxLDIxMiw3NywxMyw0OCwxNzEsMzMsMTYzLDIwMywyMDgsMTgxLDE5NCwxMDgsODIsMTI1LDExNyw4Miw2OSwxMywxNDgsODMsNDUsNjUsNjcsMTExLDE5Nyw4NiwxMDMsMTY2LDMwLDI1MywyNTIsNDksMzUsNTcsMTgzLDc1LDkxLDE0MSwxODYsOTEsMTcwLDE0NywyNTEsMTYzLDQyLDE4MSwxNzQsMjI4LDI0LDIzLDE3NywyMTUsNzYsMjA2LDIxOSw1MiwyNTIsODYsOTIsNjQsMTY4LDIyLDc4LDE4NywxNDQsNzgsODgsMTAxLDIxOCwxNDYsMTA1LDE2Niw2LDEzNCwxNTYsNjYsMTkxLDE4NSwxOTksMTg2LDEyLDUsOTYsMTI1LDY0LDQ0LDE0MiwxMiwxNzEsMTI5LDg0LDc2LDcxLDIxNiw5NiwzMywxNDUsNTIsMjU1LDE0Niw5MSwxMzUsNTUsMTk3LDMzLDI0NCwxNjIsNjgsOTcsNDgsNjcsODksMTMzLDIwMywxODYsMTAwLDgzLDE4NSw3MCwxMTgsNTksMTg5LDI0OCwyMTMsMTQyLDk1LDE5NiwxMTIsMTc5LDE0OCwxODcsNywxMDgsMjgsMjM1LDEzMiwxLDIwMCwyMjgsMTgzLDE2NCwxMzUsMTYsODIsMjgsMTY2LDMwLDIzMSwxMzMsODUsMjUwLDEzOCw4OSwxOTYsMTE4LDE3Miw2NSwxOTAsMjMyLDgsMTMsNTgsODMsNzIsMjM5LDEwMyw0Niw5NiwzNiw1NSw4OSwxODcsMzYsMjUsMjE2LDEwNCwyMDAsMzQsNzUsMjIwLDIzNCwxMTIsMjIzLDAsMTI2LDEwNiwyMTYsMTkyLDE0OSw0Myw4NiwyMCwxNzEsMTE1LDgzLDIwMywxMiw0OSwxODQsODIsMjM4LDIxMywxNDAsNTQsMTUxLDksOTMsMTc3LDgyLDU0LDEyMSwzMiwxMjUsMzIsMTExLDExOSwxODUsMTAxLDEzNCwyNDEsMTQ0LDIyNywyMTYsMjgsMzgsMjI4LDUsMzQsMTE0LDExMSwyOSwyMDksMjA3LDEyNSw0OCwzOSwyMzEsMTI5LDc4LDIyOSw5MSw3NCw1LDI1MiwxNjAsMTY2LDE0OCwyMCw0OCwxNDcsMjAyLDE5OSwyNDcsMTcwLDIzMSwxMjksODMsMjI0LDE2Nyw0OSwyMzQsMTM2LDE2MywzNCwyMjgsMTE5LDE1MCwxNiw1OCwxODYsMTk4LDEzMyw0LDY4LDE1OSwxNzUsODEsMTQwLDI0MywyNCw4MCw3OSwyNDQsMTA4LDgwLDEwMywxNDMsMTYxLDg4LDIwMywyMjUsMTYzLDE2NCwxOTcsNSw4MiwyNywxMzgsNjksMjQwLDE4Niw5MSwyMzMsMTMxLDE1OCwyMDIsNDAsOTAsODEsMTYwLDgyLDE0MywxMzcsMTY0LDEzMSwyMCwxNzMsMTUzLDI1LDk1LDgwLDI2LDE4MSwzOCwxNDgsNTQsMTk2LDEwNCwyMywyMzcsMTM2LDE2MywyMTgsMTcsMjcsNjYsMjI4LDEyNywyMTEsMjUwLDE0OSwxNjQsODUsMTA5LDE2OSwxODYsMjAwLDEsMjUxLDIwMSwxMDMsMTIzLDE4MywxNTUsODcsOTQsMTg3LDIzNSwxNzgsOTgsOTcsMjA2LDI1LDEwNSwxNjEsMjAyLDIzMSwxMjMsNjYsMTM4LDE0LDE3NywxMTQsMTYxLDU5LDE3Nyw0MCwxMywzOSwzMCw4NCw0OCwxODcsMjMzLDEzMSwxODYsMjA0LDE2Nyw5MSwyMjMsMjQzLDE5Myw5MiwyMTMsMTkxLDE0LDE0NywxNywxMzcsNCwzNiw5Niw1Niw0NSwyNywzNSwyMDEsMTkwLDE5MywyNCwxNzcsMTU0LDg2LDE5NywxNTYsMTY4LDE2OCwxMTUsMzEsMTI1LDIyOSwzNSw4MCw4OSw5OSwyMzcsMjUxLDE5MCw4MiwxLDEzMywxNTAsMjE0LDIwNSw4Nyw5LDE5NCwyMzQsMjM0LDg2LDE2OCwxNDQsMTkwLDIwMSw5NywxMTIsMTQxLDc0LDc2LDE0MCw5Niw1LDExNyw2Niw3LDI1NSw0MCwxNTksNDIsNDMsMTE5LDg0LDUsOTcsMSwyMTYsNzEsMTEsMTY3LDE0NSw2NSwyNTEsNTAsMTAwLDUyLDE3MCw0LDE5LDE5LDExOSwxNDAsMTAsMTAsODUsMTcsODQsMzcsMTA5LDI0MCwxOTQsMjMsMTY2LDIzNSwxMzEsNzYsMTg2LDM3LDEwNCwyMTcsMjksMjI3LDE1NSw2MCwxMDksNzUsMTAyLDE3LDEyOSwxNTQsMjI1LDIyLDk5LDE4MiwxNzAsNTYsMjUsMjMxLDE1MSw3OSwxNjEsNTAsMTAsMjI5LDMsNDAsMTk2LDI2LDE3OCwxMzQsMTQ1LDE3LDE1NSwxMTYsMTQxLDIwLDE3MiwxNDEsMTY5LDUsNDAsMjA5LDYyLDM1LDM2LDE0LDc2LDIzNiwxOTQsMjQyLDExNiwxODAsMjM2LDQyLDg0LDkxLDExOCwxMzksMjI1LDE3OSwxODgsMCwxMDUsMTMsOTksMTgzLDUzLDIyMiwyMTMsMTMxLDIzMiwyMjUsOTYsMTkzLDcxLDQsMTA0LDYsMTY1LDE3OSwyMzUsNDksMjA3LDExNSwzOCwxMjMsMTg1LDI5LDY1LDE4MSwxNzYsMTg1LDcwLDE2OSwxODEsMjQxLDIyLDQxLDkwLDY2LDEyMyw4OSwxNjEsMzAsMTQ4LDExNSwxMjQsMTk4LDY0LDY2LDEwMywxMDEsNTIsMTk3LDE5MCwxMzMsMTAwLDEzOSwxNDIsODMsOTcsMjUyLDEsMTMyLDIzMCwyMzQsMTgyLDIyMSwyMSw4OSwxMDUsMTI0LDEwOCwyMjAsMTkzLDE5NiwxNTUsMTU2LDE2MSwxNTEsMjQxLDExNyw2NCwxMjAsMjA2LDM4LDI0MSw5MywxMDYsMjEyLDI1LDE0MCwxNTQsMjcsMTgxLDI0MSwxMDEsMjEyLDM0LDEzOSw1MSw3LDk5LDM0LDE2Niw2MCw1MywxODIsNzgsMTkxLDczLDE1NSwyNTEsNTYsMTcxLDUzLDE5NywzNiw0MiwxOTksMTQ4LDI1MCwxOTYsMTkyLDEzNSwxMzQsMTAyLDI1NCwyOCwxMTcsMTI2LDg2LDUsMTU3LDUzLDE2NSwyMzgsMTM1LDE3OCwxODEsMzksNzYsMTQsMzQsODAsNiwyMjcsMTYyLDEyMCwxMzAsMTAzLDE3LDE4OCwxNzcsNTQsOTgsMTQwLDE5NSwxNzgsNjcsMTYwLDE2NywxNCwxNTIsNDksMTYzLDY3LDI1MCwxNTIsMjA4LDExNiwzMywyNDcsMTgyLDEyMiwxMTYsMTIzLDE2OSwxOTQsMTY0LDExMiwxNTYsNzQsMTQ4LDE0OCw2NywxMjUsMTE5LDM5LDExOSwxMjQsNTUsMTMzLDIxMCw1OSwxMjUsOTEsMTg3LDExNCwxNDUsNzEsMTcyLDE4MSw1MSw5MSwxNjEsNTcsNTUsMTQ4LDgxLDQ5LDE3NCwxMDgsMTUzLDIwNiwyNDIsNzgsMjM5LDE4LDEzMCwzMiwyMTEsMTI5LDIyMiw2NCw4LDEyOCwxOTMsMTI4LDE2LDEwNiw4NSwxNTAsMywyMDIsNzQsMTYzLDIwMiw4LDE5Nyw2MywxMzMsMjA0LDE4NCwxNDgsMTUzLDE4OSw2MiwxNjgsMjYsMTgzLDIxOSwxNTAsMjAwLDIxMSw1NCwyNDgsNzUsMTExLDIzNywyMDIsMTYsMTkzLDIyNSwxOTIsMTgyLDU5LDIwMiwzNiwyMzIsMTgwLDE5NywxMzAsMTIzLDE1OSwxNTQsMTIzLDIxNiwyNDAsMTYxLDIxMiwxNDUsMTEwLDE1MCw4MCwxMTgsMjA4LDkwLDEwNiw4MSw3MiwyMCw2OSw2MCwyOCwxNzcsOTAsMTY0LDE4OSwzOSwwLDgzLDgyLDk4LDEzMCw4MCw0NSwxNzcsNzksMTc3LDIzNSw0MSwxNDQsMjUsMzIsMjIsMTIyLDMxLDE0NSwyMTcsMTYzLDEwMSwyMjAsMSw5OCwxMDgsMTk2LDE3MSwxODYsMTMyLDI5LDI0MCwyMjUsMTEyLDQsNjYsOTYsMTQ4LDE4NCwxNjQsMzYsMjIzLDMwLDExLDY5LDEwNSw3NywxOTYsMTY3LDE2LDE0OSwxODUsMjQ2LDE1MywxMjksMTgwLDEyMCwyNDQsMTU2LDEyLDEzMSw5NywyNDIsMjI3LDc0LDE0NSw3OSw3XX19LCJwYXJlbnRfaGFzaCI6eyJ2ZWMiOltdfSwidW5tZXJnZWRfbGVhdmVzIjp7Imxpc3QiOltdfX19fSwic2l6ZSI6M30sIm5ld190cmVlX2hhc2giOlsxMjUsMTE1LDI0NCwxMzcsMjM2LDE1NywyNCwxMzQsMTMwLDczLDEwMSwyMTQsNjAsODYsMTg4LDkyLDEsMjQzLDc1LDQ2LDg1LDYwLDIyMCwxOTAsMTI3LDEyMSw1NCwxNTYsMTcsMjUxLDIzNSwxMzVdfSwiZ3JvdXBfY29udGV4dCI6eyJwcm90b2NvbF92ZXJzaW9uIjoiTWxzMTAiLCJjaXBoZXJzdWl0ZSI6Ik1MU18yNTZfWFdJTkdfQ0hBQ0hBMjBQT0xZMTMwNV9TSEEyNTZfRWQyNTUxOSIsImdyb3VwX2lkIjp7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19LCJlcG9jaCI6MSwidHJlZV9oYXNoIjp7InZlYyI6WzEyNSwxMTUsMjQ0LDEzNywyMzYsMTU3LDI0LDEzNCwxMzAsNzMsMTAxLDIxNCw2MCw4NiwxODgsOTIsMSwyNDMsNzUsNDYsODUsNjAsMjIwLDE5MCwxMjcsMTIxLDU0LDE1NiwxNywyNTEsMjM1LDEzNV19LCJjb25maXJtZWRfdHJhbnNjcmlwdF9oYXNoIjp7InZlYyI6WzE3Miw2NCwxNTAsNTMsMTcsMTk1LDI1Myw2OSw3LDE2NCwxODcsMTA3LDE2OCwxNzUsMjQxLDkxLDIxMCwyMDUsMTcyLDM1LDEzLDYwLDE4LDI3LDgyLDIxMSwxMTQsMiwyNTEsMTY1LDEzNCwyMjldfSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sImludGVyaW1fdHJhbnNjcmlwdF9oYXNoIjpbMjUsNzAsMTAzLDEwNSwxOTMsMTk4LDE1NCwxOTcsMjM1LDk1LDMyLDE1NiwyMDksMjUxLDQ2LDU5LDE4MSwyNDUsNDgsNjQsMjE5LDcxLDYyLDg2LDEyLDcwLDkyLDQyLDI0NCwzNywxMjcsMjAzXSwiY29uZmlybWF0aW9uX3RhZyI6eyJtYWNfdmFsdWUiOnsidmVjIjpbMjA4LDEsMTgsMjI3LDc0LDE4MiwxMTAsOTEsMTk5LDE2OCwyNDEsMTQ4LDQ5LDExNSwxMSwyNDMsODAsMjEzLDAsMjA3LDEzNyw2NywyMDcsNzEsMjExLDExMSwyNTQsMTczLDc0LDExNCw2OCw4MV19fX0sIm5ld19rZXlwYWlycyI6W3sicHVibGljX2tleSI6eyJrZXkiOnsidmVjIjpbMTM0LDE2OSwxMDQsMjA3LDEzNCwxNDUsMjAsMjcsOTgsMjE0LDY3LDY0LDEwMCw5Miw5MSw3OSwxNjgsMTY4LDI4LDIwOCw3NCwyMzIsMTEsMTc3LDg3LDI1MSwyNSwxLDI1MCwxNzEsMjA5LDE2LDE2MywxNzUsMTQwLDQxLDkyLDE1MiwyMiw4MCw4MCwxMDcsNjgsMjUxLDMsMTU5LDIwOSwxMjUsNDgsMTY1LDEwOCwyMzYsMjAsMTEsNTQsMTAwLDE4OCwyMjgsMTQ0LDE3NiwxODYsMjIwLDE0NywxMDgsMTYsOTMsOTMsMTc2LDQzLDIxMiw2NSwzOCwxNDcsMTYwLDgsNzIsMjcsMTA0LDIyNCwyNDMsMTMzLDE0MiwxMDEsMjA2LDIwNCwxOTcsNjYsODgsMjQsMTQzLDEwNSwxMzcsMTAzLDAsMTk2LDQ5LDI0MSwyMDQsNTMsODcsMjIsMTI3LDE4OCwxMzIsMzgsMTEyLDE2OCw1MCw5Niw0NCw2NywyNDQsMTM5LDExMSwxNjUsNTQsNTMsMTg2LDIxOSw4NCwxMzEsMTEsODIsMTkwLDk2LDE4LDIzMCwxOTMsOTcsMjA5LDgwLDg1LDY0LDczLDU0LDkzLDE1NSwxODgsMTQsNiwzNSwxODIsMTMyLDE3MCwxMTYsMjMwLDQyLDMzLDIzNSw4NSwxMjYsMzksMTA1LDEzOSw5Miw1Nyw2LDE4Niw4OSwxNTcsODMsNDAsMjA3LDE3OSwxNjEsNjAsMTQwLDIwNiwxOTgsMTcxLDExOCwyMzIsMTM0LDg4LDY5LDE0MCw4OSwxMzUsMTE3LDYyLDE1NSwyNTAsOTUsMTUyLDIyNiwzMCwzMCwxMTQsMTkyLDk4LDIxNywxMTYsNzYsMTY4LDAsMjI1LDEsMTU3LDE5MCwxNzIsMTAzLDE0MSw5MiwxNTUsMTgsMTE3LDU4LDEzMCwxODEsMTk2LDI3LDU4LDc5LDk5LDg0LDgsMTg5LDE5NCwxMjgsNSwxMTQsMjEsMTUsOTIsMTg5LDk1LDEzMywxODIsMTQ5LDE4Niw1MiwyNDksMzksNzIsOCwyMSwxMDksMTQsNTMsMzcsMTc4LDE5NiwxMTQsMTc5LDE3MSw2OSwxODksNDEsMTU1LDE0LDgzLDE1NywxNzAsMjQ0LDM5LDE0OCwyNDQsMTcwLDgwLDE4NiwxNTUsMTAwLDI0MywxODksNTAsMTc3LDE0NSwyMjUsODQsMTgsMiw3LDE0OSwyMTgsODQsMTc2LDE5Miw1MCwxMTksNzYsMTAwLDAsNjcsMjgsMjA2LDIyOCwxNDksMTAxLDE3NywyNDcsMTk5LDk4LDE1NCw4LDI0NiwyMjAsNTAsMSwxMTgsMTY3LDYwLDcyLDE1MCwyMjksNTgsMTg5LDQ1LDIyMCwxMTksMjE4LDQwLDI4LDE4NiwxOCwxNzQsMCwyMDksMTQwLDE3MSwxNjMsMTQsOTQsOTIsMTM4LDIxNyw3NSwxNDAsMTcwLDIyNywyMDAsMTI3LDE1NSw0OCwxODEsMTIsMTMzLDIyNSwyNDksMTYwLDQxLDIwNCwyNiwxMDEsMjE5LDAsMTg0LDE1NCwyMDUsMjMzLDE5NiwxNDIsMjMyLDEyMywzMCwyMzcsMjEwLDkzLDY0LDgzLDg2LDE1MCwyMCwxODksMTA3LDU1LDI0LDE2LDg0LDIzLDExNSwxNDUsOCwxMTEsMjcsNCwxODQsNTYsNDQsMjQ2LDIwOCwxMDYsMTE1LDkxLDE1OSwyNDQsMTIzLDE5NiwyNywyMTYsOTcsMTUwLDIwOCwyLDI1Miw2MCwxNzgsMTkwLDM5LDYsMjIsMTQ3LDE2MCwzMywyNDUsMjEsMTYzLDIxNiw1LDE5LDEzNiwxNTYsMTA2LDE1NiwxMjcsMjMxLDEwMiwyMDcsMzUsOTYsMTI5LDExNCwyMDgsODYsMTgwLDExNiwxMTksMTI2LDc0LDEyLDY3LDYsMTMzLDQxLDExOCw3NiwwLDI2LDIwNiw2LDUsMTAzLDE1MSwyMDQsMTM2LDE2NCwzOCw2NiwxNCw5MiwxMjUsMTYzLDUzLDE1MSwxMzAsMjM1LDE4NCwxNTgsMTQ5LDE2MSwyMzgsMTk5LDcxLDI1MSwxODcsMzUsMTg4LDI0LDMxLDE2MywyMzUsMTYzLDYwLDYwLDEzMCw3OSwxNTQsMjUsMjE3LDY0LDE5NSwxNzQsMTM5LDg1LDE5NSwyMDgsMTM5LDczLDYwLDE5NiwxMjksNjQsMjAzLDE2NiwxMjgsMTM5LDIwNCwxOCwxMjcsMTM4LDIyOSw3NywxNjgsMTYxLDE5MywyMDcsMTA1LDAsMTgwLDEwNywxNzIsNjUsMTE3LDYzLDc0LDM2LDE2NSwxNCw1MSwxMjQsMTYsMTA3LDIzLDgyLDE0NSwxMDYsMjQ3LDE0NCw0NCwxOTMsMTIwLDMsMTA0LDE2OCw0MSwxODgsMzQsMjA2LDI1MSwxNzAsMjA1LDIwMywxMTIsMTcxLDE2MSwxMzMsNzYsMTQ4LDEzOCwzNCw4MCw0MSwxNjgsMTI4LDIzLDIwMyw4MSwxNDcsNTAsNTEsMTQ1LDE5OCwxNTIsMjM0LDk2LDcyLDE4NSw1MCwyMzEsMjcsMTQ1LDIwNywyMTEsNDIsNDAsODYsMzEsMjUwLDg2LDg4LDIzNiwxOTcsMTY5LDE3NiwxNjUsMTMwLDIzNCwyNDQsMTg0LDE1NSwxODYsMiwxOTksNTUsNjksNTcsMjAwLDEyOCwxNSwxOSw3OCwyMzEsMjEzLDE3MiwyMCwyMTQsMzQsMTg5LDIxMiwxODMsNCwxMSwxNDIsMTg4LDE2MCw0OSwxNTcsMjcsMjEsMTU0LDE2NCwxNzksMjIwLDE2NiwxNzQsMjA2LDEzMSwxMSw2NCw4MCwyOCwzMSwxMjEsMTEwLDE4MSwxMjIsMTMzLDIyNSwxNDcsMTc5LDk4LDg5LDE3OSw2NiwxNjgsNDIsMjQwLDg3LDIwNSwxODcsMTk1LDExMCwxOTksMjIwLDEzLDE4Myw0NCwxOSwxLDE1MSw1OSwxODAsMTYxLDE5MSwxMDMsMTIzLDEzLDI1NSwxNjgsNzAsODQsMjYsMjA0LDEzMCw4NiwzOCwxMzYsMjMsMTM0LDE1OCwxMDEsMTI4LDI1MywyMDIsMTM5LDE2MiwyMTQsMzAsMTkxLDExMiwxMjAsMjAyLDE1MCwxNzcsMywxNjUsODMsMjQwLDE2NCwxNjYsMjQ2LDE1MiwxNDcsMTk5LDg0LDE1LDIwMiwyMzQsMzAsNzUsMzgsNzMsOTgsNjcsMTY0LDEyMCw1NCw3LDEyMiw1Myw3MSwzNywyNSwxOTcsNzUsMTg3LDEyLDE4OSwxOTcsNDEsMTkwLDMyLDEyMywyMTQsMTM5LDIwNSwxNzIsMjQ3LDEzMywxNTAsNywxOTUsMTI2LDgwLDEyMCwyMDEsOTgsMTQyLDIxNSw4NSwyMDAsMjM0LDc1LDUwLDIxNCw0OSwxMDYsMjE2LDU4LDE5LDY4LDIxMyw3MywzNywxNDgsMTM0LDIwMSwyMzUsMTAyLDE5MSw3NSwxODAsMjE0LDU1LDE4NywxOTksODYsMjksMCw3LDIwMCwxMzMsMTM1LDQyLDE4OSwxODAsMTc4LDE0MiwzNywxMjAsMTI4LDM2LDcwLDIzNCwxMzIsNTksNDYsMjE3LDE0Miw4Myw4Niw2NiwxOTMsMTY4LDE0MiwxOTgsMTAzLDY1LDEzOSwyNDMsMTUxLDI5LDEzOSw2Myw2MywxODgsMTIyLDQyLDM1LDY5LDE1MCw5OSwxMTEsMTQ2LDE4NCwyNSwzOSw5MCwzMywxMjYsMjQ5LDE4OSwxMjcsMTc2LDYxLDExNSwxMDUsOTgsOTEsNTMsMTM1LDExNCwxMjIsMTYwLDgyLDczLDE2NSw1Nyw2OCwxMTAsMjA4LDEyNCwxMjUsMiwxNjksMTE4LDExOSw4NCwzNywxMjgsODYsMTUwLDUxLDEwNSw4NywyNTUsMzMsMjAzLDIwMCwyMzEsMTAzLDEyOSw1Miw2NCwxODgsODIsNTgsNjYsMTYzLDU3LDIxMSwxMjAsMTEwLDE4NiwxOCw4NSwxMzEsMjE4LDE5LDE3LDgsNzgsMjA1LDE3Nyw1NiwyMjMsMTIwLDkyLDQzLDE5Niw3LDEyNywxNTYsNDIsODMsMTA3LDEwLDIyMywxMzAsMTAsMjQwLDIzMywxODksMTUsMTk4LDQwLDI0MCwxMTgsNSw0NSw2NSwyMSw5NSwxODcsMTc5LDEwNiwxMzIsMTEzLDE5MiwxMCwzMCwxMzMsMTY0LDIxLDkzLDkyLDE1OCwxNzAsMTM5LDEyNiwyNDgsMjQ4LDEzMSwyMjcsMTgyLDk0LDE1OSw0LDM3LDU0LDE5OSwxODcsMTQ1LDExNywxMzMsMTcsNTcsNTUsMjEyLDM1LDExMiwxODUsMTMxLDksMjA5LDY0LDc0LDE0NiwyMTcsNTIsMjIsMjI0LDQ5LDE5NSw4NywxNzQsNjAsMjEsODgsNzYsMjA4LDg3LDE5NSwyMjYsNzMsMjAxLDIyOSwxMjgsNjgsMjM2LDg0LDIyNywyNDIsMTY0LDc5LDE5NiwxMzQsMTE4LDY3LDEyOCwyLDEwLDE3LDIyNiwyMCwxMjYsMTM3LDMsMTg2LDEwMSwxNDQsMjAwLDE4OSwxNTMsMTE0LDE2MSwxOTksNSw5MiwxNDgsMTEyLDU0LDIsMTQ0LDYwLDIxOCwwLDIzLDIzMiw2MCwyNDMsMjEyLDE3NSwwLDM3LDI1LDQ3LDIxMiwxMTAsMjE2LDQ4LDQsMTQ3LDIyNCw1NCwxNCw3MCwyMDAsMjMzLDIxMiw1NiwxNCwyMTQsMTM1LDc5LDgxLDE1MCwxMTYsMTMzLDMsNzUsMjMyLDE4OCwyNTMsMjIwLDEzOSwyMDIsMTQ0LDU2LDgyLDUyLDMsMjUsMjI4LDk3LDk4LDI1MiwyMDEsNzYsMTQwLDE5LDE0NCw5OCwxMDIsMjAyLDEzNiwxODcsNTAsMTkyLDEwMyw0NCw4Niw5NywxMjcsMTIwLDEyLDg4LDIzNiwxNjMsODAsMjE0LDE4NCwyMjAsMjE1LDQwLDEzOSwxMTcsNzEsMjE5LDE4MSwxMTcsNTEsMjEyLDEyNCwxMDIsMTEzLDEwMSwzMSwxNDYsNDYsMTkxLDIwMSw5OSw2LDIyOCwxMjIsMjI4LDcxLDIwNyw5MCw5Miw1NCw3MCwyNTAsMTYxLDIyMSwxMzksMTExLDQ2LDg1LDE4MCwxNjUsNyw1NiwxMjUsMjEsMzQsNjcsMTE5LDExMiwxNjUsMTEsMTk3LDEwMCw2NSw2NywyMjEsNTksMjM0LDE4OSwxOTUsMjAzLDE2MiwxNTksMjAzLDE5OSwxNTgsMTgyLDE2NSwxLDE5MiwxNTUsMTE1LDI5LDE3OSwyMzMsMzUsMjE1LDk1LDE5LDIyNCwyLDE5LDgzLDIxNiwxMiw4MSwxOTgsMTgzLDE4MCwwLDU4LDI0OCwzOSwzOSwyNTMsODQsNTAsOTQsMjU0LDIyNSwyMTIsMTUsMTAsODgsMjUzLDIxMCw5LDI0NiwzNywyMjQsMTY1LDE4NSw5LDYsMzMsMF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzEzNCwxNTEsMTcsMjIyLDU3LDE2LDkzLDIzMyw4NywxMjAsMTcxLDE2MywxNTYsMTM5LDEyMywxODIsMjA5LDE1MiwxMDEsMTM4LDE5Myw2NiwxMzgsMTE3LDIyOCwxMzgsNCw2MywxMjIsMSwxNjgsODEsMyw0OSwyMjcsODksMjAzLDI0NSw4MCwyNDMsMTk0LDE4OSwxMTQsNDMsMTIyLDEzLDU4LDE3Niw0MSwxODAsNTUsNDQsMjI1LDE4NiwxMzcsMTAyLDY2LDc4LDMyLDEzMywyMDUsOSwyMiwyMzIsMTUxLDE4MSwxOTQsMTQ0LDI3LDIyMCwxNjcsMTY3LDE2MSwxMzMsMTgyLDc1LDE5OSwxMDQsMyw2OSwxNjAsMjgsNDQsMTU4LDUyLDIzLDMsNTUsMjIsNzMsMjI1LDIyNSwyMDAsNzAsODEsMjUsMjQ4LDE2MiwxMTUsOTksMjE3LDI1LDE4Niw4NywxODYsODcsMjUyLDYzLDEzMSwxNjMsMTg1LDgzLDM3LDE5NSwxNTIsMTI0LDE4MCwxMjQsOTgsNTAsMTU2LDIxOSw2OCw3MywxNzAsNCw0NCwxMjksMTY2LDIyNiwxMDQsMTgwLDI1MCwzNCwxNzAsMjIxLDIxNyw3OSwxOCwyNTIsMTY0LDc0LDI1MiwxNTksMTEzLDM5LDE4MCw0MiwxNywxOTEsMjQ2LDcyLDE5Nyw4MywxOTUsMTQ3LDYzLDIxNSw1NiwyMSw1MCwxMzQsMjI4LDIxNSwxMzUsMjU1LDEzOSw0MSwxNDcsOTAsMjAxLDEzNyw4NiwxMTEsMTEwLDkyLDE1MCwxNTAsMTkyLDE4MSwyNDUsMTMwLDEwNSwxOCw0OSwxNjIsMjUyLDcwLDE1LDI1NSw4MSwxOTQsMTEwLDIzNSw3NCwxOTUsODMsNCwxOTYsMTMyLDE4MywxNDYsMTYsNjgsMTQ2LDk2LDE3MSw5NywwLDI2LDc4LDI4LDE0NSwxMSw1MSwxNjMsMTA0LDcyLDUzLDEzNyw0NCwxNiwyMjEsODgsMjMsMTY5LDY2LDE0Myw2MywyMDMsODEsMjA2LDI0MSwxODcsMjcsMTMxLDE4Myw4Myw0LDEwMCwxODEsNiwxMzUsMjA0LDIyMCw4MywxODcsNTYsODIsNzcsNzMsMTA4LDE2LDE2MywxNTYsOTEsOTgsMTA3LDI4LDMsMTgzLDIxNiw1OSwxNjYsMTgxLDM0LDE1OSwwLDIxMiw0MSwyMDMsMTM1LDEyNiwxOTUsODMsMTg3LDEyMSw4LDExOSwxNDgsMzgsMTM4LDM5LDE3MCwxMTcsMjIwLDE3OSwxMjksNTAsMzUsOTMsMTY5LDY0LDEwMCwxNTYsMjMyLDE3MSwzNiwxNDcsMTM1LDg5LDg5LDE5MiwxNyw5MiwxNDIsMTYwLDE3OCwxMDcsMTQxLDE2OSwxMiwxMzgsMzUsNzEsMTQwLDIyOCwzOCwxMCwyMTUsNzQsMjksMTUwLDI2LDE1NywyMTQsMTkxLDQ4LDIwLDE2OSw0Miw1MiwxOTUsMywxMDgsMTkwLDEwNCwyMTIsMTc0LDIyNSwyNDMsMyw3NCwyMDAsMTY2LDMsMTI5LDEwOSwxNTIsMTk2LDg5LDExMSwxNjcsNjUsMTg4LDkyLDc4LDE5MywyNyw5Nyw4LDE0NywxNCwxNDMsMjQyLDIwNywyMzQsMTEyLDE4MSw4Niw1MywxMjcsOTIsMTk1LDE2MywyMzYsOCwxMDAsMjA1LDE0NiwxMzksMTQzLDk2LDE5MywyMTYsNTgsMTAwLDE2MSw1MiwxMzEsMzYsMTI5LDEzMSwyMzMsNjksMzEsOTcsMTE2LDE3OSwxMTUsMTUxLDgzLDEwMSwxNzIsMjIsMTU1LDIyNyw2NCw5MiwxMTksOTUsNzgsNzUsMTMzLDY5LDk3LDE5MywyOCwyMDMsMTI4LDI0OSwyNDYsMTI4LDIyNCwyMzMsMTM2LDE3OSwxMzQsMTM1LDEyNiwyNiw5NSwyNDMsMTk4LDExLDExMCwxNjQsMjcsMCwyMTUsMjA0LDI1MiwxNTQsMjA3LDEwOCw4MywxMTIsMTIwLDE5NSwzOSwxNDMsNzQsMTM0LDEzNyw0Myw4NSwxNDgsMTUxLDc1LDk0LDI1Miw2OCw3OCwxODQsODUsMjIxLDE3LDEyOSwxOTEsMTY0LDc1LDIxNCwzLDUwLDc3LDg0LDE2NCwyMzMsMjExLDE0OCwyMTcsMjI4LDE0MCwxODQsMjM1LDY1LDEwMSw0LDE1LDEyOSw4MywxNzEsMjQwLDEyMCwxNTIsOTIsOTEsNTIsNTksOTcsOTksMjMsMjE0LDE4NSw3NSwyMjgsMTIsNjQsNjUsNjgsMjYsOTksMTg0LDE3MSwxMTQsMTAsNjAsMjMwLDE4NSwyMzksMTc2LDIwNCwxMDMsMjUwLDEyMyw3NSwxOTUsNjksMjM0LDIyNSwwLDE5OSwyNTEsMTEzLDY2LDU4LDEyMywyNTQsMjE3LDE1OSwxNjQsMTgwLDE2LDg5LDIzLDMwLDExMSwxNzgsMjA1LDEyMCwyMjksMTA3LDExOCwyMjYsODAsMjIsODYsMzEsMTAyLDE2MywxNzgsNzEsMTc2LDUsMTA1LDg5LDE4MSwyMjksNDQsMTgzLDUsMTYwLDEyMSwxMDksNTQsMTc3LDIwMCwyNDYsMTYxLDE3MiwxNDQsMjAzLDIyNiwxOTksMzgsMTExLDI2LDIwNCwxMzEsMjQyLDE5LDEzNSwyNDAsMTg1LDUsMTUzLDE5OCwyMzQsMTEyLDE4NSwyLDE0NiwzMCwyMTIsMjQ5LDgyLDIzOSwyNDEsMTI2LDExNiwyMDgsMTA0LDE2MywyMzAsMywxMzQsMTA0LDE2LDIxNyw3MCwxNjYsMTAzLDUsMTgzLDg4LDEzNiwxMDEsMTkxLDczLDcxLDI1MSwxNjcsNDcsMjE3LDg5LDQzLDExMiwyMTEsMTE1LDE0OCwyNTIsMjgsNzIsMTk5LDEzOSwyMjAsMTcyLDE3NywxMDAsNTUsMTE4LDgsMTQwLDg0LDE0MywyMiwzMyw0Nyw4MSw5OSwzNSwyMDEsMjYsMTk1LDI0MCwxNTAsNTQsMjM0LDIyLDcxLDU0LDIwMCwyMDMsMTYxLDE3NCwyNDYsMjQ5LDQwLDE2OCwyMjYsMTYyLDE4MSwxNTUsMTc0LDIyNiwxMjksODUsMjIwLDg2LDE2OCwxMzUsMTYxLDY4LDY3LDY2LDM4LDE3LDE5LDYsNzYsMTE4LDgwLDI0Nyw5MSw5Nyw0MiwyMTQsMzEsMjA1LDExNCw5NCwxNzQsMjQzLDExLDI1NCwxMzcsMzYsNTksMzQsMTM3LDE4MCw2LDEzNCw1MiwxMTIsMTc1LDE2OSwyMzQsODQsMjYsMjYsMTk0LDIyMSwxOTcsMzMsMjUzLDE5NiwyNiwyOCwxMzEsNDcsMTI0LDIyNyw3NywyNDEsNTcsNjAsMTUwLDE4NiwxNDgsMTg1LDk3LDExMyw3OSwyMzYsMTE1LDIzNyw0LDEwNCwxMTEsMjQsNDksMTg4LDY5LDksOTcsODMsOTEsMjQ0LDEyOCwxODQsMjMyLDIwMywyNSwyMTksMTQ4LDEsMTMyLDIxMCwxNTAsODEsNTMsMjIsMTQzLDE3OSwyMDUsMjE5LDIxMCwxNzIsMTAyLDE5NCwxMDMsMTA2LDE4OCwxOTQsMTM1LDExNiwxNDUsNiw1MCw5LDgwLDIxMCwzMiw4MCw1Nyw1MiwxMzYsMSwxNTIsMTM1LDE4OCw1OCw3MiwxODYsMTYwLDY0LDk3LDE4NiwxOTcsMjA4LDU5LDI1LDk2LDE1OSw3LDE3OSw0Miw2MSwyMTcsODQsMTk5LDE0NywyLDM5LDIzMCw2MywxMzUsMTg3LDE1MSwyMTQsNTAsMTQsMTQ3LDE5MiwyMDQsMTI4LDEwNywxODksMzIsMjI4LDEyNCwxNTgsMTM2LDI4LDEyNSwxNjksMTQ3LDU0LDEwMSwxMTcsMTE0LDExOCwxNTcsNTIsMjA5LDgyLDIyMCw5Nyw3MSwxMiwyMTAsMTE0LDEyMiwyMDMsMTkzLDEyMywxODAsMTE0LDgyLDE1MywxODAsMjIyLDI2LDE2MSwxLDIwLDE4Myw2MCwyMTgsMTU3LDI4LDQ4LDE4NywxOTMsMjQxLDEsMjU0LDEyMSwzNCwxNzUsMTY2LDE1OSwxNDksMTE0LDMzLDc0LDAsNDMsMjM2LDg3LDQ0LDIwMywxNTQsOTAsMjA2LDEzMiw2MSwxMTAsNDEsMzEsMjQ2LDUsOCwyMjEsMTgyLDE0OCwyMTksMTk3LDkyLDE0MSwyMDgsMTE2LDEwMSwxMDgsNzcsMjIzLDE3NiwxNSwxNjEsMjI4LDYyLDUwLDExNCwxMTQsNzIsNzIsNTksMTgwLDE2NCwxMzgsMTM4LDE4Myw4LDkwLDI0LDE3MiwyMjcsMTcxLDI5LDI0NiwzOSwxMzUsNzEsMTMzLDExMywxMDEsMjI0LDU2LDEzNCwyOCwyMDAsMjI2LDI0NCwxMjEsMTgzLDE0OSw5Myw2MCwyMTAsMjA2LDIzMiwxMiwxMjMsMzgsNTcsMTc1LDIzMywxMjEsODQsNTcsMTg3LDQ4LDIyNiwwLDIwNywyOSwyMTksNDcsMTMwLDkyLDYwLDI1NCwxMzIsMTA5LDkwLDEwOCwxNTAsMSw2MCwxMzIsNzYsNTcsNzgsOTEsMTc3LDIwMiwxODAsMTk2LDc4LDM3LDE4MCw0MiwxNTEsMzcsMTU0LDE4MSw5Miw3MiwyNDUsOCwyNCwxNzcsMSwxMjMsMTQyLDEyMCwxMzMsMTc3LDIyOCw3NSw3NSwyMjgsMTM4LDIxNiwyMzMsMTQxLDY1LDIwMiwxODEsMTE3LDU3LDE0MSwyNTAsMjQ0LDEwLDE0OSw1NCwxNzMsMTA0LDM2LDE1NiwzNSwyNTEsMTYsMjExLDE0NCwzMCwxNDEsOTEsMTc2LDMyLDc2LDE2MiwxNTIsMjA4LDEwOCw4NSwyMSwxNTksODksNywxNTMsMjA4LDExMiwxOTUsMjE3LDEwNywxNDksNzMsMTgwLDg0LDEyLDQwLDEzMiwxNTYsODQsMTkwLDE5NCwxNTIsMTQ1LDQwLDEwNCw4LDEsMTEzLDcyLDY5LDI0NywxNTEsMjE3LDI0OSw4Miw1OCwxMTksMTk3LDEzMCwxNjMsMTEwLDk2LDg1LDI3LDEzMiw4Nyw1OSwxMDYsOCwxMDcsMTEyLDU1LDk3LDEwMCwxMTksMTEyLDEzNiwyMTAsNDQsMTk5LDM4LDIwNywyMjAsMTM5LDk2LDcsODYsMTMsNCwxNDQsMTUyLDE4Niw2LDkxLDEzNCwxNjksMTA0LDIwNywxMzQsMTQ1LDIwLDI3LDk4LDIxNCw2Nyw2NCwxMDAsOTIsOTEsNzksMTY4LDE2OCwyOCwyMDgsNzQsMjMyLDExLDE3Nyw4NywyNTEsMjUsMSwyNTAsMTcxLDIwOSwxNiwxNjMsMTc1LDE0MCw0MSw5MiwxNTIsMjIsODAsODAsMTA3LDY4LDI1MSwzLDE1OSwyMDksMTI1LDQ4LDE2NSwxMDgsMjM2LDIwLDExLDU0LDEwMCwxODgsMjI4LDE0NCwxNzYsMTg2LDIyMCwxNDcsMTA4LDE2LDkzLDkzLDE3Niw0MywyMTIsNjUsMzgsMTQ3LDE2MCw4LDcyLDI3LDEwNCwyMjQsMjQzLDEzMywxNDIsMTAxLDIwNiwyMDQsMTk3LDY2LDg4LDI0LDE0MywxMDUsMTM3LDEwMywwLDE5Niw0OSwyNDEsMjA0LDUzLDg3LDIyLDEyNywxODgsMTMyLDM4LDExMiwxNjgsNTAsOTYsNDQsNjcsMjQ0LDEzOSwxMTEsMTY1LDU0LDUzLDE4NiwyMTksODQsMTMxLDExLDgyLDE5MCw5NiwxOCwyMzAsMTkzLDk3LDIwOSw4MCw4NSw2NCw3Myw1NCw5MywxNTUsMTg4LDE0LDYsMzUsMTgyLDEzMiwxNzAsMTE2LDIzMCw0MiwzMywyMzUsODUsMTI2LDM5LDEwNSwxMzksOTIsNTcsNiwxODYsODksMTU3LDgzLDQwLDIwNywxNzksMTYxLDYwLDE0MCwyMDYsMTk4LDE3MSwxMTgsMjMyLDEzNCw4OCw2OSwxNDAsODksMTM1LDExNyw2MiwxNTUsMjUwLDk1LDE1MiwyMjYsMzAsMzAsMTE0LDE5Miw5OCwyMTcsMTE2LDc2LDE2OCwwLDIyNSwxLDE1NywxOTAsMTcyLDEwMywxNDEsOTIsMTU1LDE4LDExNyw1OCwxMzAsMTgxLDE5NiwyNyw1OCw3OSw5OSw4NCw4LDE4OSwxOTQsMTI4LDUsMTE0LDIxLDE1LDkyLDE4OSw5NSwxMzMsMTgyLDE0OSwxODYsNTIsMjQ5LDM5LDcyLDgsMjEsMTA5LDE0LDUzLDM3LDE3OCwxOTYsMTE0LDE3OSwxNzEsNjksMTg5LDQxLDE1NSwxNCw4MywxNTcsMTcwLDI0NCwzOSwxNDgsMjQ0LDE3MCw4MCwxODYsMTU1LDEwMCwyNDMsMTg5LDUwLDE3NywxNDUsMjI1LDg0LDE4LDIsNywxNDksMjE4LDg0LDE3NiwxOTIsNTAsMTE5LDc2LDEwMCwwLDY3LDI4LDIwNiwyMjgsMTQ5LDEwMSwxNzcsMjQ3LDE5OSw5OCwxNTQsOCwyNDYsMjIwLDUwLDEsMTE4LDE2Nyw2MCw3MiwxNTAsMjI5LDU4LDE4OSw0NSwyMjAsMTE5LDIxOCw0MCwyOCwxODYsMTgsMTc0LDAsMjA5LDE0MCwxNzEsMTYzLDE0LDk0LDkyLDEzOCwyMTcsNzUsMTQwLDE3MCwyMjcsMjAwLDEyNywxNTUsNDgsMTgxLDEyLDEzMywyMjUsMjQ5LDE2MCw0MSwyMDQsMjYsMTAxLDIxOSwwLDE4NCwxNTQsMjA1LDIzMywxOTYsMTQyLDIzMiwxMjMsMzAsMjM3LDIxMCw5Myw2NCw4Myw4NiwxNTAsMjAsMTg5LDEwNyw1NSwyNCwxNiw4NCwyMywxMTUsMTQ1LDgsMTExLDI3LDQsMTg0LDU2LDQ0LDI0NiwyMDgsMTA2LDExNSw5MSwxNTksMjQ0LDEyMywxOTYsMjcsMjE2LDk3LDE1MCwyMDgsMiwyNTIsNjAsMTc4LDE5MCwzOSw2LDIyLDE0NywxNjAsMzMsMjQ1LDIxLDE2MywyMTYsNSwxOSwxMzYsMTU2LDEwNiwxNTYsMTI3LDIzMSwxMDIsMjA3LDM1LDk2LDEyOSwxMTQsMjA4LDg2LDE4MCwxMTYsMTE5LDEyNiw3NCwxMiw2Nyw2LDEzMyw0MSwxMTgsNzYsMCwyNiwyMDYsNiw1LDEwMywxNTEsMjA0LDEzNiwxNjQsMzgsNjYsMTQsOTIsMTI1LDE2Myw1MywxNTEsMTMwLDIzNSwxODQsMTU4LDE0OSwxNjEsMjM4LDE5OSw3MSwyNTEsMTg3LDM1LDE4OCwyNCwzMSwxNjMsMjM1LDE2Myw2MCw2MCwxMzAsNzksMTU0LDI1LDIxNyw2NCwxOTUsMTc0LDEzOSw4NSwxOTUsMjA4LDEzOSw3Myw2MCwxOTYsMTI5LDY0LDIwMywxNjYsMTI4LDEzOSwyMDQsMTgsMTI3LDEzOCwyMjksNzcsMTY4LDE2MSwxOTMsMjA3LDEwNSwwLDE4MCwxMDcsMTcyLDY1LDExNyw2Myw3NCwzNiwxNjUsMTQsNTEsMTI0LDE2LDEwNywyMyw4MiwxNDUsMTA2LDI0NywxNDQsNDQsMTkzLDEyMCwzLDEwNCwxNjgsNDEsMTg4LDM0LDIwNiwyNTEsMTcwLDIwNSwyMDMsMTEyLDE3MSwxNjEsMTMzLDc2LDE0OCwxMzgsMzQsODAsNDEsMTY4LDEyOCwyMywyMDMsODEsMTQ3LDUwLDUxLDE0NSwxOTgsMTUyLDIzNCw5Niw3MiwxODUsNTAsMjMxLDI3LDE0NSwyMDcsMjExLDQyLDQwLDg2LDMxLDI1MCw4Niw4OCwyMzYsMTk3LDE2OSwxNzYsMTY1LDEzMCwyMzQsMjQ0LDE4NCwxNTUsMTg2LDIsMTk5LDU1LDY5LDU3LDIwMCwxMjgsMTUsMTksNzgsMjMxLDIxMywxNzIsMjAsMjE0LDM0LDE4OSwyMTIsMTgzLDQsMTEsMTQyLDE4OCwxNjAsNDksMTU3LDI3LDIxLDE1NCwxNjQsMTc5LDIyMCwxNjYsMTc0LDIwNiwxMzEsMTEsNjQsODAsMjgsMzEsMTIxLDExMCwxODEsMTIyLDEzMywyMjUsMTQ3LDE3OSw5OCw4OSwxNzksNjYsMTY4LDQyLDI0MCw4NywyMDUsMTg3LDE5NSwxMTAsMTk5LDIyMCwxMywxODMsNDQsMTksMSwxNTEsNTksMTgwLDE2MSwxOTEsMTAzLDEyMywxMywyNTUsMTY4LDcwLDg0LDI2LDIwNCwxMzAsODYsMzgsMTM2LDIzLDEzNCwxNTgsMTAxLDEyOCwyNTMsMjAyLDEzOSwxNjIsMjE0LDMwLDE5MSwxMTIsMTIwLDIwMiwxNTAsMTc3LDMsMTY1LDgzLDI0MCwxNjQsMTY2LDI0NiwxNTIsMTQ3LDE5OSw4NCwxNSwyMDIsMjM0LDMwLDc1LDM4LDczLDk4LDY3LDE2NCwxMjAsNTQsNywxMjIsNTMsNzEsMzcsMjUsMTk3LDc1LDE4NywxMiwxODksMTk3LDQxLDE5MCwzMiwxMjMsMjE0LDEzOSwyMDUsMTcyLDI0NywxMzMsMTUwLDcsMTk1LDEyNiw4MCwxMjAsMjAxLDk4LDE0MiwyMTUsODUsMjAwLDIzNCw3NSw1MCwyMTQsNDksMTA2LDIxNiw1OCwxOSw2OCwyMTMsNzMsMzcsMTQ4LDEzNCwyMDEsMjM1LDEwMiwxOTEsNzUsMTgwLDIxNCw1NSwxODcsMTk5LDg2LDI5LDAsNywyMDAsMTMzLDEzNSw0MiwxODksMTgwLDE3OCwxNDIsMzcsMTIwLDEyOCwzNiw3MCwyMzQsMTMyLDU5LDQ2LDIxNywxNDIsODMsODYsNjYsMTkzLDE2OCwxNDIsMTk4LDEwMyw2NSwxMzksMjQzLDE1MSwyOSwxMzksNjMsNjMsMTg4LDEyMiw0MiwzNSw2OSwxNTAsOTksMTExLDE0NiwxODQsMjUsMzksOTAsMzMsMTI2LDI0OSwxODksMTI3LDE3Niw2MSwxMTUsMTA1LDk4LDkxLDUzLDEzNSwxMTQsMTIyLDE2MCw4Miw3MywxNjUsNTcsNjgsMTEwLDIwOCwxMjQsMTI1LDIsMTY5LDExOCwxMTksODQsMzcsMTI4LDg2LDE1MCw1MSwxMDUsODcsMjU1LDMzLDIwMywyMDAsMjMxLDEwMywxMjksNTIsNjQsMTg4LDgyLDU4LDY2LDE2Myw1NywyMTEsMTIwLDExMCwxODYsMTgsODUsMTMxLDIxOCwxOSwxNyw4LDc4LDIwNSwxNzcsNTYsMjIzLDEyMCw5Miw0MywxOTYsNywxMjcsMTU2LDQyLDgzLDEwNywxMCwyMjMsMTMwLDEwLDI0MCwyMzMsMTg5LDE1LDE5OCw0MCwyNDAsMTE4LDUsNDUsNjUsMjEsOTUsMTg3LDE3OSwxMDYsMTMyLDExMywxOTIsMTAsMzAsMTMzLDE2NCwyMSw5Myw5MiwxNTgsMTcwLDEzOSwxMjYsMjQ4LDI0OCwxMzEsMjI3LDE4Miw5NCwxNTksNCwzNyw1NCwxOTksMTg3LDE0NSwxMTcsMTMzLDE3LDU3LDU1LDIxMiwzNSwxMTIsMTg1LDEzMSw5LDIwOSw2NCw3NCwxNDYsMjE3LDUyLDIyLDIyNCw0OSwxOTUsODcsMTc0LDYwLDIxLDg4LDc2LDIwOCw4NywxOTUsMjI2LDczLDIwMSwyMjksMTI4LDY4LDIzNiw4NCwyMjcsMjQyLDE2NCw3OSwxOTYsMTM0LDExOCw2NywxMjgsMiwxMCwxNywyMjYsMjAsMTI2LDEzNywzLDE4NiwxMDEsMTQ0LDIwMCwxODksMTUzLDExNCwxNjEsMTk5LDUsOTIsMTQ4LDExMiw1NCwyLDE0NCw2MCwyMTgsMCwyMywyMzIsNjAsMjQzLDIxMiwxNzUsMCwzNywyNSw0NywyMTIsMTEwLDIxNiw0OCw0LDE0NywyMjQsNTQsMTQsNzAsMjAwLDIzMywyMTIsNTYsMTQsMjE0LDEzNSw3OSw4MSwxNTAsMTE2LDEzMywzLDc1LDIzMiwxODgsMjUzLDIyMCwxMzksMjAyLDE0NCw1Niw4Miw1MiwzLDI1LDIyOCw5Nyw5OCwyNTIsMjAxLDc2LDE0MCwxOSwxNDQsOTgsMTAyLDIwMiwxMzYsMTg3LDUwLDE5MiwxMDMsNDQsODYsOTcsMTI3LDEyMCwxMiw4OCwyMzYsMTYzLDgwLDIxNCwxODQsMjIwLDIxNSw0MCwxMzksMTE3LDcxLDIxOSwxODEsMTE3LDUxLDIxMiwxMjQsMTAyLDExMywxMDEsMzEsMTQ2LDQ2LDE5MSwyMDEsOTksNiwyMjgsMTIyLDIyOCw3MSwyMDcsOTAsOTIsNTQsNzAsMjUwLDE2MSwyMjEsMTM5LDExMSw0Niw4NSwxODAsMTY1LDcsNTYsMTI1LDIxLDM0LDY3LDExOSwxMTIsMTY1LDExLDE5NywxMDAsNjUsNjcsMjIxLDU5LDIzNCwxODksMTk1LDIwMywxNjIsMTU5LDIwMywxOTksMTU4LDE4MiwxNjUsMSwxOTIsMTU1LDExNSwyOSwxNzksMjMzLDM1LDIxNSw5NSwxOSwyMjQsMiwxOSw4MywyMTYsMTQzLDE0MiwxMzQsMTUyLDYyLDI1MSwxNTQsMzUsMjAsMTA3LDEyLDE0NywxNTEsNzgsMTUzLDkzLDg3LDE0NSwzOCwyNiwxODUsMTQzLDk0LDY2LDIxMSw1LDIyLDE1NiwzOCwxMjUsMTY4LDUyLDU0LDIwNywxNzEsMTExLDU5LDk1LDIwOCwyMTksMjM1LDksMzAsNTcsMTQyLDE4LDI1NCw2MywxODksMTYwLDc4LDExNiw1MCwyMDQsMjM2LDIwLDE1NywxMzksMTA1LDE0LDI1MCwyMjQsMTk5LDQ5LDgyLDY2LDE2MCwxODEsMzIsMTQxLDEyNiwyMjAsMjEzLDc3LDE4MywyNDYsMjUzLDE5NSwyMTQsMzcsMTk0LDE4NSw5OCwxNDUsMTI0LDYyLDE4LDUyLDQ2LDIyNSwxLDEwLDQ0LDEyMCwxNDMsMjQwLDEyLDgxLDE5OCwxODMsMTgwLDAsNTgsMjQ4LDM5LDM5LDI1Myw4NCw1MCw5NCwyNTQsMjI1LDIxMiwxNSwxMCw4OCwyNTMsMjEwLDksMjQ2LDM3LDIyNCwxNjUsMTg1LDksNiwzMywwXX19fSx7InB1YmxpY19rZXkiOnsia2V5Ijp7InZlYyI6WzExNiwxNjYsNjIsMTI5LDE3NywxNjMsMTI0LDIxNCwxMDgsODAsODYsMTQ1LDI4LDE0NCw2NCwxODUsNCw4OSw2MCwxMTYsMzMsMTksMjI1LDE1Nyw3MCwxMjMsMTMzLDUzLDE4MCw4OSw2NywxNDksNDMsNjYsMTgzLDE0Myw0NywzNCwxODAsODcsMTYsMTEsMTEwLDI1MSw2MSwyMDIsMjMzLDE1Myw4LDEwMiw5MSwxOTgsNDksNTksMTYwLDE5OCw4Niw1OCwyNDAsMTU4LDE1OSw4MSwyMDcsOTQsMjA5LDEyMCwyMjgsODQsNTUsMjM5LDIyLDE5NywxMDMsMjM0LDc3LDE5OCwyNDEsNDcsOSw4MCwxNjIsMTg2LDE1NCwxODIsMjYsMTM5LDg2LDI0MSwyMCwxMDQsMTY5LDIxOSwxMzcsMjA3LDgsMTgsMTU0LDE2LDI1LDExOSwxNDYsMTg2LDE1Myw4OSwyLDksMTA0LDQyLDM2LDE2LDM3LDIxLDE5NSw5NSwzOSwxOTYsMTU0LDE4NSw3NCwxMzIsMjQ2LDg5LDE0NSwxMjAsMjI3LDIxLDYyLDE4MiwxMDIsMjMyLDI1MiwyLDE3OSwxNjksNDIsMTQ1LDEwMywxOTEsMjA1LDIzNCwxMTYsMTkwLDEwOCwxNDIsOTksNTksNDUsMTAxLDEyMSw5LDYzLDE2NywxOTQsODQsMTAyLDQ4LDIzLDIxMCwxMTgsMjI3LDE1MiwxMzUsMTYzLDQzLDQyLDg0LDI0NCwzNywyMDUsMTM1LDE1MSwxMTUsMTUwLDg4LDE4MCwyMzMsMTQ2LDY5LDE5LDIwNyw2Niw0MiwxMjMsMTcyLDI0OCw4OCwxODMsMTY5LDMwLDM1LDUyLDc2LDg1LDE1MywxNTgsOSwxMTUsOTAsMjUxLDk5LDE1LDE2NCwxNjIsMjMsODYsMTUwLDE3Miw4NSwxMDEsMTI5LDI0MSwxMzQsMTk5LDEyNCwyNSwwLDE5MCwxNDYsMzQsNzEsMjQsOTYsMzgsMjEsNjAsMTkxLDE0MCwxNzMsMjUyLDExMyw5NSw2MCw3NSw0OSw1OSwyNDQsMjAzLDI0MSwxMjksNjksMjM1LDIyLDMyLDE1LDIzNiwxOTAsMTIyLDEwMSw2NCwxMTEsNywxNzEsMjI4LDU3LDEyMSwxMTQsMjAzLDEzLDEwMyw5LDM0LDE1LDM2LDI5LDIyMiwyMjcsMTk4LDE1NCwzMywxMTEsMTM3LDE2OCwxMTgsMTA1LDIzMSwxMDEsNjEsMzUsMTgwLDc4LDg5LDQzLDE0MCw5MCwyNiw1MCwyMSwxNDMsMjQxLDIxMiw3NywxMyw0OCwxNzEsMzMsMTYzLDIwMywyMDgsMTgxLDE5NCwxMDgsODIsMTI1LDExNyw4Miw2OSwxMywxNDgsODMsNDUsNjUsNjcsMTExLDE5Nyw4NiwxMDMsMTY2LDMwLDI1MywyNTIsNDksMzUsNTcsMTgzLDc1LDkxLDE0MSwxODYsOTEsMTcwLDE0NywyNTEsMTYzLDQyLDE4MSwxNzQsMjI4LDI0LDIzLDE3NywyMTUsNzYsMjA2LDIxOSw1MiwyNTIsODYsOTIsNjQsMTY4LDIyLDc4LDE4NywxNDQsNzgsODgsMTAxLDIxOCwxNDYsMTA1LDE2Niw2LDEzNCwxNTYsNjYsMTkxLDE4NSwxOTksMTg2LDEyLDUsOTYsMTI1LDY0LDQ0LDE0MiwxMiwxNzEsMTI5LDg0LDc2LDcxLDIxNiw5NiwzMywxNDUsNTIsMjU1LDE0Niw5MSwxMzUsNTUsMTk3LDMzLDI0NCwxNjIsNjgsOTcsNDgsNjcsODksMTMzLDIwMywxODYsMTAwLDgzLDE4NSw3MCwxMTgsNTksMTg5LDI0OCwyMTMsMTQyLDk1LDE5NiwxMTIsMTc5LDE0OCwxODcsNywxMDgsMjgsMjM1LDEzMiwxLDIwMCwyMjgsMTgzLDE2NCwxMzUsMTYsODIsMjgsMTY2LDMwLDIzMSwxMzMsODUsMjUwLDEzOCw4OSwxOTYsMTE4LDE3Miw2NSwxOTAsMjMyLDgsMTMsNTgsODMsNzIsMjM5LDEwMyw0Niw5NiwzNiw1NSw4OSwxODcsMzYsMjUsMjE2LDEwNCwyMDAsMzQsNzUsMjIwLDIzNCwxMTIsMjIzLDAsMTI2LDEwNiwyMTYsMTkyLDE0OSw0Myw4NiwyMCwxNzEsMTE1LDgzLDIwMywxMiw0OSwxODQsODIsMjM4LDIxMywxNDAsNTQsMTUxLDksOTMsMTc3LDgyLDU0LDEyMSwzMiwxMjUsMzIsMTExLDExOSwxODUsMTAxLDEzNCwyNDEsMTQ0LDIyNywyMTYsMjgsMzgsMjI4LDUsMzQsMTE0LDExMSwyOSwyMDksMjA3LDEyNSw0OCwzOSwyMzEsMTI5LDc4LDIyOSw5MSw3NCw1LDI1MiwxNjAsMTY2LDE0OCwyMCw0OCwxNDcsMjAyLDE5OSwyNDcsMTcwLDIzMSwxMjksODMsMjI0LDE2Nyw0OSwyMzQsMTM2LDE2MywzNCwyMjgsMTE5LDE1MCwxNiw1OCwxODYsMTk4LDEzMyw0LDY4LDE1OSwxNzUsODEsMTQwLDI0MywyNCw4MCw3OSwyNDQsMTA4LDgwLDEwMywxNDMsMTYxLDg4LDIwMywyMjUsMTYzLDE2NCwxOTcsNSw4MiwyNywxMzgsNjksMjQwLDE4Niw5MSwyMzMsMTMxLDE1OCwyMDIsNDAsOTAsODEsMTYwLDgyLDE0MywxMzcsMTY0LDEzMSwyMCwxNzMsMTUzLDI1LDk1LDgwLDI2LDE4MSwzOCwxNDgsNTQsMTk2LDEwNCwyMywyMzcsMTM2LDE2MywyMTgsMTcsMjcsNjYsMjI4LDEyNywyMTEsMjUwLDE0OSwxNjQsODUsMTA5LDE2OSwxODYsMjAwLDEsMjUxLDIwMSwxMDMsMTIzLDE4MywxNTUsODcsOTQsMTg3LDIzNSwxNzgsOTgsOTcsMjA2LDI1LDEwNSwxNjEsMjAyLDIzMSwxMjMsNjYsMTM4LDE0LDE3NywxMTQsMTYxLDU5LDE3Nyw0MCwxMywzOSwzMCw4NCw0OCwxODcsMjMzLDEzMSwxODYsMjA0LDE2Nyw5MSwyMjMsMjQzLDE5Myw5MiwyMTMsMTkxLDE0LDE0NywxNywxMzcsNCwzNiw5Niw1Niw0NSwyNywzNSwyMDEsMTkwLDE5MywyNCwxNzcsMTU0LDg2LDE5NywxNTYsMTY4LDE2OCwxMTUsMzEsMTI1LDIyOSwzNSw4MCw4OSw5OSwyMzcsMjUxLDE5MCw4MiwxLDEzMywxNTAsMjE0LDIwNSw4Nyw5LDE5NCwyMzQsMjM0LDg2LDE2OCwxNDQsMTkwLDIwMSw5NywxMTIsMTQxLDc0LDc2LDE0MCw5Niw1LDExNyw2Niw3LDI1NSw0MCwxNTksNDIsNDMsMTE5LDg0LDUsOTcsMSwyMTYsNzEsMTEsMTY3LDE0NSw2NSwyNTEsNTAsMTAwLDUyLDE3MCw0LDE5LDE5LDExOSwxNDAsMTAsMTAsODUsMTcsODQsMzcsMTA5LDI0MCwxOTQsMjMsMTY2LDIzNSwxMzEsNzYsMTg2LDM3LDEwNCwyMTcsMjksMjI3LDE1NSw2MCwxMDksNzUsMTAyLDE3LDEyOSwxNTQsMjI1LDIyLDk5LDE4MiwxNzAsNTYsMjUsMjMxLDE1MSw3OSwxNjEsNTAsMTAsMjI5LDMsNDAsMTk2LDI2LDE3OCwxMzQsMTQ1LDE3LDE1NSwxMTYsMTQxLDIwLDE3MiwxNDEsMTY5LDUsNDAsMjA5LDYyLDM1LDM2LDE0LDc2LDIzNiwxOTQsMjQyLDExNiwxODAsMjM2LDQyLDg0LDkxLDExOCwxMzksMjI1LDE3OSwxODgsMCwxMDUsMTMsOTksMTgzLDUzLDIyMiwyMTMsMTMxLDIzMiwyMjUsOTYsMTkzLDcxLDQsMTA0LDYsMTY1LDE3OSwyMzUsNDksMjA3LDExNSwzOCwxMjMsMTg1LDI5LDY1LDE4MSwxNzYsMTg1LDcwLDE2OSwxODEsMjQxLDIyLDQxLDkwLDY2LDEyMyw4OSwxNjEsMzAsMTQ4LDExNSwxMjQsMTk4LDY0LDY2LDEwMywxMDEsNTIsMTk3LDE5MCwxMzMsMTAwLDEzOSwxNDIsODMsOTcsMjUyLDEsMTMyLDIzMCwyMzQsMTgyLDIyMSwyMSw4OSwxMDUsMTI0LDEwOCwyMjAsMTkzLDE5NiwxNTUsMTU2LDE2MSwxNTEsMjQxLDExNyw2NCwxMjAsMjA2LDM4LDI0MSw5MywxMDYsMjEyLDI1LDE0MCwxNTQsMjcsMTgxLDI0MSwxMDEsMjEyLDM0LDEzOSw1MSw3LDk5LDM0LDE2Niw2MCw1MywxODIsNzgsMTkxLDczLDE1NSwyNTEsNTYsMTcxLDUzLDE5NywzNiw0MiwxOTksMTQ4LDI1MCwxOTYsMTkyLDEzNSwxMzQsMTAyLDI1NCwyOCwxMTcsMTI2LDg2LDUsMTU3LDUzLDE2NSwyMzgsMTM1LDE3OCwxODEsMzksNzYsMTQsMzQsODAsNiwyMjcsMTYyLDEyMCwxMzAsMTAzLDE3LDE4OCwxNzcsNTQsOTgsMTQwLDE5NSwxNzgsNjcsMTYwLDE2NywxNCwxNTIsNDksMTYzLDY3LDI1MCwxNTIsMjA4LDExNiwzMywyNDcsMTgyLDEyMiwxMTYsMTIzLDE2OSwxOTQsMTY0LDExMiwxNTYsNzQsMTQ4LDE0OCw2NywxMjUsMTE5LDM5LDExOSwxMjQsNTUsMTMzLDIxMCw1OSwxMjUsOTEsMTg3LDExNCwxNDUsNzEsMTcyLDE4MSw1MSw5MSwxNjEsNTcsNTUsMTQ4LDgxLDQ5LDE3NCwxMDgsMTUzLDIwNiwyNDIsNzgsMjM5LDE4LDEzMCwzMiwyMTEsMTI5LDIyMiw2NCw4LDEyOCwxOTMsMTI4LDE2LDEwNiw4NSwxNTAsMywyMDIsNzQsMTYzLDIwMiw4LDE5Nyw2MywxMzMsMjA0LDE4NCwxNDgsMTUzLDE4OSw2MiwxNjgsMjYsMTgzLDIxOSwxNTAsMjAwLDIxMSw1NCwyNDgsNzUsMTExLDIzNywyMDIsMTYsMTkzLDIyNSwxOTIsMTgyLDU5LDIwMiwzNiwyMzIsMTgwLDE5NywxMzAsMTIzLDE1OSwxNTQsMTIzLDIxNiwyNDAsMTYxLDIxMiwxNDUsMTEwLDE1MCw4MCwxMTgsMjA4LDkwLDEwNiw4MSw3MiwyMCw2OSw2MCwyOCwxNzcsOTAsMTY0LDE4OSwzOSwwLDgzLDgyLDk4LDEzMCw4MCw0NSwxNzcsNzksMTc3LDIzNSw0MSwxNDQsMjUsMzIsMjIsMTIyLDMxLDE0NSwyMTcsMTYzLDEwMSwyMjAsMSw5OCwxMDgsMTk2LDE3MSwxODYsMTMyLDI5LDI0MCwyMjUsMTEyLDQsNjYsOTYsMTQ4LDE4NCwxNjQsMzYsMjIzLDMwLDExLDY5LDEwNSw3NywxOTYsMTY3LDE2LDE0OSwxODUsMjQ2LDE1MywxMjksMTgwLDEyMCwyNDQsMTU2LDEyLDEzMSw5NywyNDIsMjI3LDc0LDE0NSw3OSw3XX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbNzUsOTgsNDMsNTYsMTY2LDYyLDE0OCw2MCwxNTUsMjA5LDE5NSwxNjksNDEsMjQ5LDE5NiwxOTcsNywxNDksMTU0LDQxLDEyLDEwOSwyMTUsNDIsMTkzLDUyLDEwNCwxNTUsNzIsNDEsMjksNTYsNzksMTcyLDE5NCwxOTAsMTY5LDI0MCw0OSwxODQsMTYxLDI0LDM5LDQ5LDM4LDQ4LDEyMSwxNTEsMzMsNjQsODMsMTE5LDIzNiwzNywxOTUsMTAyLDEwNCwyMTIsMTIwLDE4OSw2MywxNTEsNDIsNzEsMjI2LDE4MCwxMzcsNDEsOCwzOSwxMDEsMTU4LDE0LDE5Nyw2NCw5MCw3MCw2NiwyNSwyMDMsMTY1LDM1LDE1NCw1Niw0Myw4NywxMTIsMTk5LDU4LDE1NSwyNywxNDcsMTEyLDk1LDE3OSwxMTIsNzcsMTcyLDY2LDMyLDEzNiw2LDE1LDEzNSwxNDMsNzUsMTIyLDE5NCwyMDksNDEsMjA3LDksNyw5NiwxMTQsMjMwLDEzOCwxNDAsMjUwLDcyLDE2NCwxNTQsMTM5LDE3NiwxMDQsNDksMTAzLDI3LDI4LDE0MCwyLDEzMywxMjIsMTA1LDEwOCw2MCw1NCwxNjIsMTcwLDE3OSwxOTIsMjMyLDE4NSwxNDYsMzksMjI4LDMwLDIwMiwxNDgsMTQzLDQ5LDI3LDExNywxOTMsMjI5LDEzMiwyNTAsMTA4LDE1MSw3MSwyNDAsMTYwLDM3LDEyNCwxOTQsMjA3LDI0MSwxMTAsMTY4LDIyNyw1LDE5MSwxMDMsODgsMjQ5LDkwLDExMiwyNDcsMTM4LDY2LDQ1LDYsMTEsMTU1LDEyMiw1NSwxNzIsMjE4LDE1MiwzNSw4MiwxMjksMTExLDI0OCwxMTgsOTUsMTk4LDEzNywyMzYsMjcsMzQsMjEzLDc1LDEzOSwyMCwyMjksMTIsOTMsMTUwLDEyNSw3Miw5NiwxMjgsMjAwLDEwNywyNyw0NCwyMjAsMTg1LDQ0LDExNCw4NywxLDEzNiw1MywxMzUsNTIsMTQsOTUsMTY1LDEzNiwyMjYsMTcwLDEwNiwxNzAsMTU1LDEwNCwyMTEsMTksNjUsNTYsMTQ1LDE2NSwyNTMsMTQ4LDk2LDE0LDE4NCwxOTQsMjU1LDcxLDIwLDEzMyw3NSwyMCw3OCwxNTQsMTUwLDE0OCwxMzMsMTA0LDkxLDg4LDEzMCwxMTksNDIsNTEsMjcsMjQxLDEzMCwyNDQsMjMwLDExMCw3MSw1MCwxMjAsMjMwLDU3LDI4LDE1NCwxNjMsMTksNDksMjMyLDE0OSw1MSwxNDksMjUsMTcyLDI0LDE3OSw2NCw3Niw3LDM2LDIyNCwxMzAsMTI1LDE5Myw5NywxNDUsMSwyMSwzMywxODAsMjksMjI0LDE1NCwxNzYsMTE3LDEwMiwxNjAsODUsMjQ3LDExMSwyNTAsMjM0LDIwNSwyMzAsODYsMjA3LDI1MSwzOCw4LDYzLDE2OCwxNjQsMjUyLDEzNCwxMzYsMTgyLDcwLDEyNiw2NywxMTIsMTM0LDE4OSw0MywxODcsMjI2LDE4OCw5Nyw4NCwxNjQsMjA0LDkwLDcwLDY3LDI0NiwxNDAsMTIxLDE5NCw2OCw0Niw4MSwyMzAsMTM2LDE2NywxNzcsNjcsMjM2LDE2MCwxNzAsMTMyLDExNywxNTIsMTQxLDExNCwxODUsMjAwLDEzNCwxNywyMiwyMTUsNjMsMjM3LDEzOCwxMywxNjQsMTE3LDgwLDE1NywxNjIsMTA0LDE5LDIxNSwxNDAsOCw1MCwxOTMsNDksMjQ4LDY5LDE5NSwxNzksMTU2LDE0NywyNywzNiwxNDcsMTk3LDc3LDIwMywyNTEsMTUyLDYxLDIzNSw0MywxOTcsMTAsMTkwLDIyOCw1OCwxNzAsMTY5LDI0Nyw2OCwxMjMsMjI1LDE3MywxNTIsMTcwLDE2MywyNTIsMTMwLDYsMzksMTkzLDE4MywyMDcsMTYsMTY4LDEwMSwwLDUwLDIxMSwyMzYsMjUsMTMxLDE1NCw0Miw1NiwyNDksOTEsMTM0LDUwLDksMTQzLDIwOSwyNSwxMDksMTA0LDE4MywyMDksNjksNDcsMTM0LDk4LDg1LDE0MCwxMzgsMTk5LDIyMSwyMTcsMTAyLDI5LDE1NSw1NSw4OCwxNDUsMSwxMjQsMjExLDE3OSwxMzYsMjE1LDE0NCwxNjAsMjM0LDEyMiw3OSwxODQsMTYxLDEzNSwzNSwxNzMsMTAwLDgsMTU0LDczLDEyNCwxMzIsMTUwLDEwMSw0NiwyMTgsMjA0LDE5NCwxNzksNTYsNjIsMTIwLDE0NiwxNzYsMjM3LDUzLDExOSw5MCw2OCwxOSwyMzcsMjAsMTY2LDIwLDk5LDExMywxODYsOTYsNjQsMjQ4LDIzNSwxNCwyMDUsMTM4LDEwOCwzMSwxMCwxMjUsMjQ0LDM4LDE1NSwxMTAsNzQsNDQsOTksODcsOTYsMTM0LDM2LDEyLDE1MSw3MywxOSwxMzUsMTkyLDIwNCwyMDIsMTM3LDE3NiwzNyw1NywxNzIsMTAzLDIxMyw5NSw2OSwxNDQsMTg4LDM3LDI0LDI0LDQzLDE0NSwxODMsMTcwLDYsMjgsMTQwLDcsMTEyLDE1NiwxMjAsMTIwLDE4MywxMiwxOTEsMjI5LDgzLDMzLDE2OSwxMTYsMzQsMTI2LDEwMiw1NiwxNTcsMTIxLDQ0LDE5LDIwMCwyMSwxMCw1MywxODMsMjYsMjE5LDExNiwyMDUsMTY4LDE2NywyNTUsNywxNzcsMTk3LDQwLDEzLDI1NSwxODcsNTcsMTEsMTg4LDE3NSw1OSwyNDgsODYsMjUwLDEyLDE1LDE2MywyMTcsMTU2LDE4LDE2Myw0OCwxMjcsMTM5LDEwMiwxOTAsMTcwLDE5MiwxLDE4MCwxNjYsMTA2LDE5OCwzMiwxNzcsOTksOTAsMTgyLDI0Miw2MCwxNTIsMTYsMTc2LDE1MCwxODQsMTAxLDc3LDcwLDE3NiwyOCwxNzcsOTAsMTAsMjUwLDYwLDI0MywxOSwxMjMsMTY5LDkwLDQ4LDI1MiwyNDIsMTgwLDE1NywxNyw1NywxMzMsNzAsMTE4LDE2MiwxNjcsOSwxNzMsMTM2LDE3NSwxMTcsMjI0LDIzLDQ2LDQyLDE3MiwxNTIsMTYwLDk2LDE2MCwyMDAsMTUsNTYsMTM0LDUxLDcxLDg0LDkwLDk1LDc1LDI3LDE4LDE0Nyw0OCwxMjMsODgsMTk2LDE4MCw4Nyw1Nyw2NywxODEsOCwyMjcsODAsMTAsMjAwLDE1NiwyMSwyMTksMjUyLDEwOSwxNTAsMTAwLDIzLDQ1LDE4NiwxOTYsMjM3LDIyOCw2MCwyMDIsMTY3LDIzLDc2LDE4Miw5NywyMywxMzYsNTAsODUsMTY5LDExNywyMTUsODMsMjEsMTMwLDE1MSwyMDEsMTY2LDE2NSwxNzYsMTAwLDIxMiw1Myw5MiwxNDUsMTExLDY5LDE2NiwzOCwyNDgsMjMsMjYsMjUwLDIxNiwxODEsMjQ3LDE5MywyMDYsMTE0LDIxNiwxMzIsMTMzLDEwOCw1MCwxNTYsNTksMjksMTg1LDEyLDE2MSw4MSwxMzEsMjAxLDE2NCwxNTEsMTgsMTQ5LDc2LDEyMywxMzIsMjUsOTUsMTQsMTE0LDE1OSwxOTMsMjUwLDEwMCwxMTUsMTcxLDE0MCwxMjYsODksODYsMTU1LDIxOCwxNzIsMTYxLDIxMSw0Myw2NSwxMDEsOSwxNDEsMjQ2LDIwNSw1MiwyNDcsMTUxLDkzLDczLDE0OSwxMzAsMjI4LDIwNSw1MiwxLDI4LDc3LDcyLDIwNywyMjYsMTE0LDM0LDE3NywxODcsOTgsOCwxODcsMTQzLDE0LDE5OCw4Niw0NywxMDIsMTIxLDIwOCwxNDgsNzYsOTcsNzQsNDcsMTM1LDE1MSwxNjEsMTg0LDM4LDEzNiwyMjQsMTA2LDE3NiwxNzIsMTE0LDE0NSwyMzMsMTU2LDk4LDE0MywxODgsMTIwLDE0OSw1Nyw3LDE5NiwxOTMsMzQsMTc4LDkxLDQ1LDgxLDIzMiw0NCwxMTgsMjQ2LDc1LDE3NCwxNzgsOTUsNzksMTIyLDExNCwyNTQsNjAsMTU3LDg5LDI2LDE5MiwxMjAsMTAsNzMsMjA1LDExNywxNTEsMTI2LDE5Nyw5MywxMzMsMjA4LDE3OSw1MiwzNiw3MSwyMTAsMjQ3LDEyNiwxMjksNTIsMzEsOTQsMTcwLDgsMTYxLDIyNSwxMDIsMTcxLDIsMjIsMTM5LDk2LDcsMTA0LDE4Niw0MCwxMTEsMjcsMTAzLDI1Miw0MiwxNzcsOTIsNzEsMTI4LDIyOCwyNTIsMTkzLDE1OCwxMSw2MCwxMzMsNDAsMTAyLDE3NCw0OSwxODksMTU0LDE3OSw5MSw1OCwxNTYsMTEsMjQ0LDE1MywxMDEsNzMsMSwzMiwxNTQsMTg4LDExNSw3NiwxNDQsMTA4LDE3LDIxMyw5MiwyMTYsMjEsOTEsMjM5LDIzMSwxODYsOTksNDgsMTcxLDExMyw5MSw1NSwyMDIsMTMyLDE0MSwxNDUsMTA0LDE0OCwzOSwzOSwxMyw0MywyNDYsMTQxLDE1NSwyMzUsMTIsNCwyMTgsMTg2LDk3LDIsMTgsMjI4LDEzMiwxNTQsMTcsMjIwLDEyMywyMDgsMzgsMjA0LDM4LDg5LDE4MSw1LDIzMyw3MiwyNDgsMTU2LDgyLDIxMywxNzEsMiwxNTIsNTksMTExLDgyLDE2NSw1NiwxOTEsOSw1MCwyNDksMTY2LDk3LDMyLDQ4LDEyOCwyMTUsODgsODMsMTE4LDM3LDEyNiw4NywxNjgsMTY4LDgzLDIzNCwxOTMsMTYyLDIxLDY5LDEwMywxMzMsMTgsOTcsMTAyLDQxLDE5NSwxODcsNzcsNjQsMTYyLDI4LDIxLDgsNTAsOTQsMTAxLDE2MCwxNDMsMTM4LDEsMTA3LDExNywxMTcsMTY5LDIwLDY3LDMwLDIzMCwxMTUsMjMyLDE4OCw0Niw4MywxMDIsMjAyLDIzNywxNjUsNTcsMTE5LDcsMTUsMTkyLDI1MSw3MiwyMTksNDAsNTIsMTY2LDUwLDkzLDM2LDE1MiwyMDEsMjQ2LDgxLDExMyw3NCw3NCwxMzAsNiwxNjQsMTIsMTE4LDIzMywzNywzOCwyNDYsNTgsMzcsMTIxLDEwMiw3OCwwLDIyLDM3LDE5Myw0MSwxMzksMTAwLDE4OSw0NiwxOTksMTg1LDIwNyw1NiwxMzAsMTIxLDgsMCwyMDksNzIsNjEsMTE2LDE2Niw2MiwxMjksMTc3LDE2MywxMjQsMjE0LDEwOCw4MCw4NiwxNDUsMjgsMTQ0LDY0LDE4NSw0LDg5LDYwLDExNiwzMywxOSwyMjUsMTU3LDcwLDEyMywxMzMsNTMsMTgwLDg5LDY3LDE0OSw0Myw2NiwxODMsMTQzLDQ3LDM0LDE4MCw4NywxNiwxMSwxMTAsMjUxLDYxLDIwMiwyMzMsMTUzLDgsMTAyLDkxLDE5OCw0OSw1OSwxNjAsMTk4LDg2LDU4LDI0MCwxNTgsMTU5LDgxLDIwNyw5NCwyMDksMTIwLDIyOCw4NCw1NSwyMzksMjIsMTk3LDEwMywyMzQsNzcsMTk4LDI0MSw0Nyw5LDgwLDE2MiwxODYsMTU0LDE4MiwyNiwxMzksODYsMjQxLDIwLDEwNCwxNjksMjE5LDEzNywyMDcsOCwxOCwxNTQsMTYsMjUsMTE5LDE0NiwxODYsMTUzLDg5LDIsOSwxMDQsNDIsMzYsMTYsMzcsMjEsMTk1LDk1LDM5LDE5NiwxNTQsMTg1LDc0LDEzMiwyNDYsODksMTQ1LDEyMCwyMjcsMjEsNjIsMTgyLDEwMiwyMzIsMjUyLDIsMTc5LDE2OSw0MiwxNDUsMTAzLDE5MSwyMDUsMjM0LDExNiwxOTAsMTA4LDE0Miw5OSw1OSw0NSwxMDEsMTIxLDksNjMsMTY3LDE5NCw4NCwxMDIsNDgsMjMsMjEwLDExOCwyMjcsMTUyLDEzNSwxNjMsNDMsNDIsODQsMjQ0LDM3LDIwNSwxMzUsMTUxLDExNSwxNTAsODgsMTgwLDIzMywxNDYsNjksMTksMjA3LDY2LDQyLDEyMywxNzIsMjQ4LDg4LDE4MywxNjksMzAsMzUsNTIsNzYsODUsMTUzLDE1OCw5LDExNSw5MCwyNTEsOTksMTUsMTY0LDE2MiwyMyw4NiwxNTAsMTcyLDg1LDEwMSwxMjksMjQxLDEzNCwxOTksMTI0LDI1LDAsMTkwLDE0NiwzNCw3MSwyNCw5NiwzOCwyMSw2MCwxOTEsMTQwLDE3MywyNTIsMTEzLDk1LDYwLDc1LDQ5LDU5LDI0NCwyMDMsMjQxLDEyOSw2OSwyMzUsMjIsMzIsMTUsMjM2LDE5MCwxMjIsMTAxLDY0LDExMSw3LDE3MSwyMjgsNTcsMTIxLDExNCwyMDMsMTMsMTAzLDksMzQsMTUsMzYsMjksMjIyLDIyNywxOTgsMTU0LDMzLDExMSwxMzcsMTY4LDExOCwxMDUsMjMxLDEwMSw2MSwzNSwxODAsNzgsODksNDMsMTQwLDkwLDI2LDUwLDIxLDE0MywyNDEsMjEyLDc3LDEzLDQ4LDE3MSwzMywxNjMsMjAzLDIwOCwxODEsMTk0LDEwOCw4MiwxMjUsMTE3LDgyLDY5LDEzLDE0OCw4Myw0NSw2NSw2NywxMTEsMTk3LDg2LDEwMywxNjYsMzAsMjUzLDI1Miw0OSwzNSw1NywxODMsNzUsOTEsMTQxLDE4Niw5MSwxNzAsMTQ3LDI1MSwxNjMsNDIsMTgxLDE3NCwyMjgsMjQsMjMsMTc3LDIxNSw3NiwyMDYsMjE5LDUyLDI1Miw4Niw5Miw2NCwxNjgsMjIsNzgsMTg3LDE0NCw3OCw4OCwxMDEsMjE4LDE0NiwxMDUsMTY2LDYsMTM0LDE1Niw2NiwxOTEsMTg1LDE5OSwxODYsMTIsNSw5NiwxMjUsNjQsNDQsMTQyLDEyLDE3MSwxMjksODQsNzYsNzEsMjE2LDk2LDMzLDE0NSw1MiwyNTUsMTQ2LDkxLDEzNSw1NSwxOTcsMzMsMjQ0LDE2Miw2OCw5Nyw0OCw2Nyw4OSwxMzMsMjAzLDE4NiwxMDAsODMsMTg1LDcwLDExOCw1OSwxODksMjQ4LDIxMywxNDIsOTUsMTk2LDExMiwxNzksMTQ4LDE4Nyw3LDEwOCwyOCwyMzUsMTMyLDEsMjAwLDIyOCwxODMsMTY0LDEzNSwxNiw4MiwyOCwxNjYsMzAsMjMxLDEzMyw4NSwyNTAsMTM4LDg5LDE5NiwxMTgsMTcyLDY1LDE5MCwyMzIsOCwxMyw1OCw4Myw3MiwyMzksMTAzLDQ2LDk2LDM2LDU1LDg5LDE4NywzNiwyNSwyMTYsMTA0LDIwMCwzNCw3NSwyMjAsMjM0LDExMiwyMjMsMCwxMjYsMTA2LDIxNiwxOTIsMTQ5LDQzLDg2LDIwLDE3MSwxMTUsODMsMjAzLDEyLDQ5LDE4NCw4MiwyMzgsMjEzLDE0MCw1NCwxNTEsOSw5MywxNzcsODIsNTQsMTIxLDMyLDEyNSwzMiwxMTEsMTE5LDE4NSwxMDEsMTM0LDI0MSwxNDQsMjI3LDIxNiwyOCwzOCwyMjgsNSwzNCwxMTQsMTExLDI5LDIwOSwyMDcsMTI1LDQ4LDM5LDIzMSwxMjksNzgsMjI5LDkxLDc0LDUsMjUyLDE2MCwxNjYsMTQ4LDIwLDQ4LDE0NywyMDIsMTk5LDI0NywxNzAsMjMxLDEyOSw4MywyMjQsMTY3LDQ5LDIzNCwxMzYsMTYzLDM0LDIyOCwxMTksMTUwLDE2LDU4LDE4NiwxOTgsMTMzLDQsNjgsMTU5LDE3NSw4MSwxNDAsMjQzLDI0LDgwLDc5LDI0NCwxMDgsODAsMTAzLDE0MywxNjEsODgsMjAzLDIyNSwxNjMsMTY0LDE5Nyw1LDgyLDI3LDEzOCw2OSwyNDAsMTg2LDkxLDIzMywxMzEsMTU4LDIwMiw0MCw5MCw4MSwxNjAsODIsMTQzLDEzNywxNjQsMTMxLDIwLDE3MywxNTMsMjUsOTUsODAsMjYsMTgxLDM4LDE0OCw1NCwxOTYsMTA0LDIzLDIzNywxMzYsMTYzLDIxOCwxNywyNyw2NiwyMjgsMTI3LDIxMSwyNTAsMTQ5LDE2NCw4NSwxMDksMTY5LDE4NiwyMDAsMSwyNTEsMjAxLDEwMywxMjMsMTgzLDE1NSw4Nyw5NCwxODcsMjM1LDE3OCw5OCw5NywyMDYsMjUsMTA1LDE2MSwyMDIsMjMxLDEyMyw2NiwxMzgsMTQsMTc3LDExNCwxNjEsNTksMTc3LDQwLDEzLDM5LDMwLDg0LDQ4LDE4NywyMzMsMTMxLDE4NiwyMDQsMTY3LDkxLDIyMywyNDMsMTkzLDkyLDIxMywxOTEsMTQsMTQ3LDE3LDEzNyw0LDM2LDk2LDU2LDQ1LDI3LDM1LDIwMSwxOTAsMTkzLDI0LDE3NywxNTQsODYsMTk3LDE1NiwxNjgsMTY4LDExNSwzMSwxMjUsMjI5LDM1LDgwLDg5LDk5LDIzNywyNTEsMTkwLDgyLDEsMTMzLDE1MCwyMTQsMjA1LDg3LDksMTk0LDIzNCwyMzQsODYsMTY4LDE0NCwxOTAsMjAxLDk3LDExMiwxNDEsNzQsNzYsMTQwLDk2LDUsMTE3LDY2LDcsMjU1LDQwLDE1OSw0Miw0MywxMTksODQsNSw5NywxLDIxNiw3MSwxMSwxNjcsMTQ1LDY1LDI1MSw1MCwxMDAsNTIsMTcwLDQsMTksMTksMTE5LDE0MCwxMCwxMCw4NSwxNyw4NCwzNywxMDksMjQwLDE5NCwyMywxNjYsMjM1LDEzMSw3NiwxODYsMzcsMTA0LDIxNywyOSwyMjcsMTU1LDYwLDEwOSw3NSwxMDIsMTcsMTI5LDE1NCwyMjUsMjIsOTksMTgyLDE3MCw1NiwyNSwyMzEsMTUxLDc5LDE2MSw1MCwxMCwyMjksMyw0MCwxOTYsMjYsMTc4LDEzNCwxNDUsMTcsMTU1LDExNiwxNDEsMjAsMTcyLDE0MSwxNjksNSw0MCwyMDksNjIsMzUsMzYsMTQsNzYsMjM2LDE5NCwyNDIsMTE2LDE4MCwyMzYsNDIsODQsOTEsMTE4LDEzOSwyMjUsMTc5LDE4OCwwLDEwNSwxMyw5OSwxODMsNTMsMjIyLDIxMywxMzEsMjMyLDIyNSw5NiwxOTMsNzEsNCwxMDQsNiwxNjUsMTc5LDIzNSw0OSwyMDcsMTE1LDM4LDEyMywxODUsMjksNjUsMTgxLDE3NiwxODUsNzAsMTY5LDE4MSwyNDEsMjIsNDEsOTAsNjYsMTIzLDg5LDE2MSwzMCwxNDgsMTE1LDEyNCwxOTgsNjQsNjYsMTAzLDEwMSw1MiwxOTcsMTkwLDEzMywxMDAsMTM5LDE0Miw4Myw5NywyNTIsMSwxMzIsMjMwLDIzNCwxODIsMjIxLDIxLDg5LDEwNSwxMjQsMTA4LDIyMCwxOTMsMTk2LDE1NSwxNTYsMTYxLDE1MSwyNDEsMTE3LDY0LDEyMCwyMDYsMzgsMjQxLDkzLDEwNiwyMTIsMjUsMTQwLDE1NCwyNywxODEsMjQxLDEwMSwyMTIsMzQsMTM5LDUxLDcsOTksMzQsMTY2LDYwLDUzLDE4Miw3OCwxOTEsNzMsMTU1LDI1MSw1NiwxNzEsNTMsMTk3LDM2LDQyLDE5OSwxNDgsMjUwLDE5NiwxOTIsMTM1LDEzNCwxMDIsMjU0LDI4LDExNywxMjYsODYsNSwxNTcsNTMsMTY1LDIzOCwxMzUsMTc4LDE4MSwzOSw3NiwxNCwzNCw4MCw2LDIyNywxNjIsMTIwLDEzMCwxMDMsMTcsMTg4LDE3Nyw1NCw5OCwxNDAsMTk1LDE3OCw2NywxNjAsMTY3LDE0LDE1Miw0OSwxNjMsNjcsMjUwLDE1MiwyMDgsMTE2LDMzLDI0NywxODIsMTIyLDExNiwxMjMsMTY5LDE5NCwxNjQsMTEyLDE1Niw3NCwxNDgsMTQ4LDY3LDEyNSwxMTksMzksMTE5LDEyNCw1NSwxMzMsMjEwLDU5LDEyNSw5MSwxODcsMTE0LDE0NSw3MSwxNzIsMTgxLDUxLDkxLDE2MSw1Nyw1NSwxNDgsODEsNDksMTc0LDEwOCwxNTMsMjA2LDI0Miw3OCwyMzksMTgsMTMwLDMyLDIxMSwxMjksMjIyLDY0LDgsMTI4LDE5MywxMjgsMTYsMTA2LDg1LDE1MCwzLDIwMiw3NCwxNjMsMjAyLDgsMTk3LDYzLDEzMywyMDQsMTg0LDE0OCwxNTMsMTg5LDYyLDE2OCwyNiwxODMsMjE5LDE1MCwyMDAsMjExLDU0LDI0OCw3NSwxMTEsMjM3LDIwMiwxNiwxOTMsMjI1LDE5MiwxODIsNTksMjAyLDM2LDIzMiwxODAsMTk3LDEzMCwxMjMsMTU5LDE1NCwxMjMsMjE2LDI0MCwxNjEsMjEyLDE0NSwxMTAsMTUwLDgwLDExOCwyMDgsOTAsMTA2LDgxLDcyLDIwLDY5LDYwLDI4LDE3Nyw5MCwxNjQsMTg5LDM5LDAsODMsODIsOTgsMTMwLDgwLDQ1LDE3Nyw3OSwxNzcsMjM1LDQxLDE0NCwyNSwzMiwyMiwxMjIsMzEsMTQ1LDIxNywxNjMsMTAxLDIyMCwxLDk4LDEwOCwxOTYsMTcxLDE4NiwxMzIsMjksMjQwLDIyNSwxMTIsNCw2NiwxMDIsMTE5LDE0MiwyMjYsMjQzLDQwLDE4NCwyNTEsMTM4LDkzLDIwOSwyMjIsMjE1LDIwOCwxMTAsMjQyLDE2LDI5LDI1Miw1MiwyMjIsOTEsODUsMTQ2LDU2LDY5LDY2LDIxOCw0Niw5MSwzOSwyNCwyNDUsNTcsMjQ4LDIxNCw2LDM4LDc4LDY0LDkwLDEzNyw2Myw3MiwyOSwyMjQsMTIwLDEzOSwzOCwyOSwyMjQsMzUsMTAwLDEwNiw3MCwxMzMsMTMsMTEsMTM3LDI0NCwxNDAsMjM1LDI0LDIyMSwxNDEsMTcsMTU2LDQ2LDEwLDE5MywyMjgsMTY4LDEzMCwxODEsMjMzLDE3LDE0MywxMjksNTUsMTA5LDIwLDM1LDIyNiw0OSwxNjgsMTM3LDE0Nyw5NSwxNzAsMTMwLDI0MCw2MCwxMzksMTAzLDE3NCwxMTYsOTYsMTQ4LDE4NCwxNjQsMzYsMjIzLDMwLDExLDY5LDEwNSw3NywxOTYsMTY3LDE2LDE0OSwxODUsMjQ2LDE1MywxMjksMTgwLDEyMCwyNDQsMTU2LDEyLDEzMSw5NywyNDIsMjI3LDc0LDE0NSw3OSw3XX19fV0sIm5ld19sZWFmX2tleXBhaXJfb3B0aW9uIjpudWxsLCJ1cGRhdGVfcGF0aF9sZWFmX25vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTM0LDE2OSwxMDQsMjA3LDEzNCwxNDUsMjAsMjcsOTgsMjE0LDY3LDY0LDEwMCw5Miw5MSw3OSwxNjgsMTY4LDI4LDIwOCw3NCwyMzIsMTEsMTc3LDg3LDI1MSwyNSwxLDI1MCwxNzEsMjA5LDE2LDE2MywxNzUsMTQwLDQxLDkyLDE1MiwyMiw4MCw4MCwxMDcsNjgsMjUxLDMsMTU5LDIwOSwxMjUsNDgsMTY1LDEwOCwyMzYsMjAsMTEsNTQsMTAwLDE4OCwyMjgsMTQ0LDE3NiwxODYsMjIwLDE0NywxMDgsMTYsOTMsOTMsMTc2LDQzLDIxMiw2NSwzOCwxNDcsMTYwLDgsNzIsMjcsMTA0LDIyNCwyNDMsMTMzLDE0MiwxMDEsMjA2LDIwNCwxOTcsNjYsODgsMjQsMTQzLDEwNSwxMzcsMTAzLDAsMTk2LDQ5LDI0MSwyMDQsNTMsODcsMjIsMTI3LDE4OCwxMzIsMzgsMTEyLDE2OCw1MCw5Niw0NCw2NywyNDQsMTM5LDExMSwxNjUsNTQsNTMsMTg2LDIxOSw4NCwxMzEsMTEsODIsMTkwLDk2LDE4LDIzMCwxOTMsOTcsMjA5LDgwLDg1LDY0LDczLDU0LDkzLDE1NSwxODgsMTQsNiwzNSwxODIsMTMyLDE3MCwxMTYsMjMwLDQyLDMzLDIzNSw4NSwxMjYsMzksMTA1LDEzOSw5Miw1Nyw2LDE4Niw4OSwxNTcsODMsNDAsMjA3LDE3OSwxNjEsNjAsMTQwLDIwNiwxOTgsMTcxLDExOCwyMzIsMTM0LDg4LDY5LDE0MCw4OSwxMzUsMTE3LDYyLDE1NSwyNTAsOTUsMTUyLDIyNiwzMCwzMCwxMTQsMTkyLDk4LDIxNywxMTYsNzYsMTY4LDAsMjI1LDEsMTU3LDE5MCwxNzIsMTAzLDE0MSw5MiwxNTUsMTgsMTE3LDU4LDEzMCwxODEsMTk2LDI3LDU4LDc5LDk5LDg0LDgsMTg5LDE5NCwxMjgsNSwxMTQsMjEsMTUsOTIsMTg5LDk1LDEzMywxODIsMTQ5LDE4Niw1MiwyNDksMzksNzIsOCwyMSwxMDksMTQsNTMsMzcsMTc4LDE5NiwxMTQsMTc5LDE3MSw2OSwxODksNDEsMTU1LDE0LDgzLDE1NywxNzAsMjQ0LDM5LDE0OCwyNDQsMTcwLDgwLDE4NiwxNTUsMTAwLDI0MywxODksNTAsMTc3LDE0NSwyMjUsODQsMTgsMiw3LDE0OSwyMTgsODQsMTc2LDE5Miw1MCwxMTksNzYsMTAwLDAsNjcsMjgsMjA2LDIyOCwxNDksMTAxLDE3NywyNDcsMTk5LDk4LDE1NCw4LDI0NiwyMjAsNTAsMSwxMTgsMTY3LDYwLDcyLDE1MCwyMjksNTgsMTg5LDQ1LDIyMCwxMTksMjE4LDQwLDI4LDE4NiwxOCwxNzQsMCwyMDksMTQwLDE3MSwxNjMsMTQsOTQsOTIsMTM4LDIxNyw3NSwxNDAsMTcwLDIyNywyMDAsMTI3LDE1NSw0OCwxODEsMTIsMTMzLDIyNSwyNDksMTYwLDQxLDIwNCwyNiwxMDEsMjE5LDAsMTg0LDE1NCwyMDUsMjMzLDE5NiwxNDIsMjMyLDEyMywzMCwyMzcsMjEwLDkzLDY0LDgzLDg2LDE1MCwyMCwxODksMTA3LDU1LDI0LDE2LDg0LDIzLDExNSwxNDUsOCwxMTEsMjcsNCwxODQsNTYsNDQsMjQ2LDIwOCwxMDYsMTE1LDkxLDE1OSwyNDQsMTIzLDE5NiwyNywyMTYsOTcsMTUwLDIwOCwyLDI1Miw2MCwxNzgsMTkwLDM5LDYsMjIsMTQ3LDE2MCwzMywyNDUsMjEsMTYzLDIxNiw1LDE5LDEzNiwxNTYsMTA2LDE1NiwxMjcsMjMxLDEwMiwyMDcsMzUsOTYsMTI5LDExNCwyMDgsODYsMTgwLDExNiwxMTksMTI2LDc0LDEyLDY3LDYsMTMzLDQxLDExOCw3NiwwLDI2LDIwNiw2LDUsMTAzLDE1MSwyMDQsMTM2LDE2NCwzOCw2NiwxNCw5MiwxMjUsMTYzLDUzLDE1MSwxMzAsMjM1LDE4NCwxNTgsMTQ5LDE2MSwyMzgsMTk5LDcxLDI1MSwxODcsMzUsMTg4LDI0LDMxLDE2MywyMzUsMTYzLDYwLDYwLDEzMCw3OSwxNTQsMjUsMjE3LDY0LDE5NSwxNzQsMTM5LDg1LDE5NSwyMDgsMTM5LDczLDYwLDE5NiwxMjksNjQsMjAzLDE2NiwxMjgsMTM5LDIwNCwxOCwxMjcsMTM4LDIyOSw3NywxNjgsMTYxLDE5MywyMDcsMTA1LDAsMTgwLDEwNywxNzIsNjUsMTE3LDYzLDc0LDM2LDE2NSwxNCw1MSwxMjQsMTYsMTA3LDIzLDgyLDE0NSwxMDYsMjQ3LDE0NCw0NCwxOTMsMTIwLDMsMTA0LDE2OCw0MSwxODgsMzQsMjA2LDI1MSwxNzAsMjA1LDIwMywxMTIsMTcxLDE2MSwxMzMsNzYsMTQ4LDEzOCwzNCw4MCw0MSwxNjgsMTI4LDIzLDIwMyw4MSwxNDcsNTAsNTEsMTQ1LDE5OCwxNTIsMjM0LDk2LDcyLDE4NSw1MCwyMzEsMjcsMTQ1LDIwNywyMTEsNDIsNDAsODYsMzEsMjUwLDg2LDg4LDIzNiwxOTcsMTY5LDE3NiwxNjUsMTMwLDIzNCwyNDQsMTg0LDE1NSwxODYsMiwxOTksNTUsNjksNTcsMjAwLDEyOCwxNSwxOSw3OCwyMzEsMjEzLDE3MiwyMCwyMTQsMzQsMTg5LDIxMiwxODMsNCwxMSwxNDIsMTg4LDE2MCw0OSwxNTcsMjcsMjEsMTU0LDE2NCwxNzksMjIwLDE2NiwxNzQsMjA2LDEzMSwxMSw2NCw4MCwyOCwzMSwxMjEsMTEwLDE4MSwxMjIsMTMzLDIyNSwxNDcsMTc5LDk4LDg5LDE3OSw2NiwxNjgsNDIsMjQwLDg3LDIwNSwxODcsMTk1LDExMCwxOTksMjIwLDEzLDE4Myw0NCwxOSwxLDE1MSw1OSwxODAsMTYxLDE5MSwxMDMsMTIzLDEzLDI1NSwxNjgsNzAsODQsMjYsMjA0LDEzMCw4NiwzOCwxMzYsMjMsMTM0LDE1OCwxMDEsMTI4LDI1MywyMDIsMTM5LDE2MiwyMTQsMzAsMTkxLDExMiwxMjAsMjAyLDE1MCwxNzcsMywxNjUsODMsMjQwLDE2NCwxNjYsMjQ2LDE1MiwxNDcsMTk5LDg0LDE1LDIwMiwyMzQsMzAsNzUsMzgsNzMsOTgsNjcsMTY0LDEyMCw1NCw3LDEyMiw1Myw3MSwzNywyNSwxOTcsNzUsMTg3LDEyLDE4OSwxOTcsNDEsMTkwLDMyLDEyMywyMTQsMTM5LDIwNSwxNzIsMjQ3LDEzMywxNTAsNywxOTUsMTI2LDgwLDEyMCwyMDEsOTgsMTQyLDIxNSw4NSwyMDAsMjM0LDc1LDUwLDIxNCw0OSwxMDYsMjE2LDU4LDE5LDY4LDIxMyw3MywzNywxNDgsMTM0LDIwMSwyMzUsMTAyLDE5MSw3NSwxODAsMjE0LDU1LDE4NywxOTksODYsMjksMCw3LDIwMCwxMzMsMTM1LDQyLDE4OSwxODAsMTc4LDE0MiwzNywxMjAsMTI4LDM2LDcwLDIzNCwxMzIsNTksNDYsMjE3LDE0Miw4Myw4Niw2NiwxOTMsMTY4LDE0MiwxOTgsMTAzLDY1LDEzOSwyNDMsMTUxLDI5LDEzOSw2Myw2MywxODgsMTIyLDQyLDM1LDY5LDE1MCw5OSwxMTEsMTQ2LDE4NCwyNSwzOSw5MCwzMywxMjYsMjQ5LDE4OSwxMjcsMTc2LDYxLDExNSwxMDUsOTgsOTEsNTMsMTM1LDExNCwxMjIsMTYwLDgyLDczLDE2NSw1Nyw2OCwxMTAsMjA4LDEyNCwxMjUsMiwxNjksMTE4LDExOSw4NCwzNywxMjgsODYsMTUwLDUxLDEwNSw4NywyNTUsMzMsMjAzLDIwMCwyMzEsMTAzLDEyOSw1Miw2NCwxODgsODIsNTgsNjYsMTYzLDU3LDIxMSwxMjAsMTEwLDE4NiwxOCw4NSwxMzEsMjE4LDE5LDE3LDgsNzgsMjA1LDE3Nyw1NiwyMjMsMTIwLDkyLDQzLDE5Niw3LDEyNywxNTYsNDIsODMsMTA3LDEwLDIyMywxMzAsMTAsMjQwLDIzMywxODksMTUsMTk4LDQwLDI0MCwxMTgsNSw0NSw2NSwyMSw5NSwxODcsMTc5LDEwNiwxMzIsMTEzLDE5MiwxMCwzMCwxMzMsMTY0LDIxLDkzLDkyLDE1OCwxNzAsMTM5LDEyNiwyNDgsMjQ4LDEzMSwyMjcsMTgyLDk0LDE1OSw0LDM3LDU0LDE5OSwxODcsMTQ1LDExNywxMzMsMTcsNTcsNTUsMjEyLDM1LDExMiwxODUsMTMxLDksMjA5LDY0LDc0LDE0NiwyMTcsNTIsMjIsMjI0LDQ5LDE5NSw4NywxNzQsNjAsMjEsODgsNzYsMjA4LDg3LDE5NSwyMjYsNzMsMjAxLDIyOSwxMjgsNjgsMjM2LDg0LDIyNywyNDIsMTY0LDc5LDE5NiwxMzQsMTE4LDY3LDEyOCwyLDEwLDE3LDIyNiwyMCwxMjYsMTM3LDMsMTg2LDEwMSwxNDQsMjAwLDE4OSwxNTMsMTE0LDE2MSwxOTksNSw5MiwxNDgsMTEyLDU0LDIsMTQ0LDYwLDIxOCwwLDIzLDIzMiw2MCwyNDMsMjEyLDE3NSwwLDM3LDI1LDQ3LDIxMiwxMTAsMjE2LDQ4LDQsMTQ3LDIyNCw1NCwxNCw3MCwyMDAsMjMzLDIxMiw1NiwxNCwyMTQsMTM1LDc5LDgxLDE1MCwxMTYsMTMzLDMsNzUsMjMyLDE4OCwyNTMsMjIwLDEzOSwyMDIsMTQ0LDU2LDgyLDUyLDMsMjUsMjI4LDk3LDk4LDI1MiwyMDEsNzYsMTQwLDE5LDE0NCw5OCwxMDIsMjAyLDEzNiwxODcsNTAsMTkyLDEwMyw0NCw4Niw5NywxMjcsMTIwLDEyLDg4LDIzNiwxNjMsODAsMjE0LDE4NCwyMjAsMjE1LDQwLDEzOSwxMTcsNzEsMjE5LDE4MSwxMTcsNTEsMjEyLDEyNCwxMDIsMTEzLDEwMSwzMSwxNDYsNDYsMTkxLDIwMSw5OSw2LDIyOCwxMjIsMjI4LDcxLDIwNyw5MCw5Miw1NCw3MCwyNTAsMTYxLDIyMSwxMzksMTExLDQ2LDg1LDE4MCwxNjUsNyw1NiwxMjUsMjEsMzQsNjcsMTE5LDExMiwxNjUsMTEsMTk3LDEwMCw2NSw2NywyMjEsNTksMjM0LDE4OSwxOTUsMjAzLDE2MiwxNTksMjAzLDE5OSwxNTgsMTgyLDE2NSwxLDE5MiwxNTUsMTE1LDI5LDE3OSwyMzMsMzUsMjE1LDk1LDE5LDIyNCwyLDE5LDgzLDIxNiwxMiw4MSwxOTgsMTgzLDE4MCwwLDU4LDI0OCwzOSwzOSwyNTMsODQsNTAsOTQsMjU0LDIyNSwyMTIsMTUsMTAsODgsMjUzLDIxMCw5LDI0NiwzNywyMjQsMTY1LDE4NSw5LDYsMzMsMF19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjQsMTgwLDEyNSwxMjYsNzksMjIwLDIyMSwyNTMsMTIzLDQyLDIwMSwxMjAsMTgwLDE2NCwyMDAsMjA4LDE3Nyw5OSw4Miw2MCw1NSwxNTYsMTI2LDc3LDExNywxMTUsMjAzLDIwMywxNDIsMjU1LDE2NiwzNF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbMTY1LDI0MCwyMjIsMTQ1LDE5MSwxOTksMjAxLDIwOCw3NiwyMDMsMTYyLDE3OSwxNDIsMjM1LDExNywyMjcsMTM0LDU2LDExNSwzOSw4Miw1MywxNTQsNDksMTc3LDIxOSw4OSwyMDksMTg0LDMsMTM5LDIwOV19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMzIsMzAsMzksODMsNjksMTA3LDE4OSwzNiwxMDEsMTA0LDE3MCwyMjAsMTM0LDI1MSw0MSw0NSwxNjQsOTYsMzksMjQyLDU3LDU5LDE2OCw4Myw4NCwxNjMsMTM5LDE4OSwyMzEsMTI4LDEyNywxOTUsMTkwLDE1NCwxMjIsNTQsMTI2LDE1NywxMzAsNTksNzAsMTQ0LDEwMSwzMiwxNzAsMjUsMjA4LDIwNCwyNDcsMTM0LDE1NSwyMDksOTksMTE1LDU0LDEzNywyMywxMjksMjE5LDE0MywxNTIsMTU2LDEwMCwxM119fX19fX19fQAAAAAAAABSAAAAAAAAFM1UcmVleyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJ0cmVlIjp7ImxlYWZfbm9kZXMiOlt7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMjcsMTMzLDExOSw4OSwxNywyLDE3MywxMzEsMTg2LDIxOSw3NCw5NCw3MywxODcsMjAzLDMxLDI0NCwxNjksMTksMTAzLDEyLDE0LDk5LDE2NSwyMjUsMTM4LDM2LDE5NywxMzUsMTg3LDY4LDE1NSwzMSwxMTAsMTA4LDkwLDIyLDgxLDEzNywxOTEsMTA1LDE4OSw5LDE3Miw4MSwyMDcsMjEwLDM0LDcxLDExNSwxODQsMTExLDQxLDE0Myw1MywzNiwxOTUsMTYsNzEsODUsMjMyLDEyMCw3NSwxOTcsMTU2LDIwNCwxOSwzOCwxNzgsMiwyMCwxNTIsMTAzLDI3LDY2LDIyMiw1OCwyMDIsNzgsNDMsNjgsMTE4LDcxLDkzLDMzLDE1NCwxMjUsMjI3LDE0Niw1NiwxMzIsMTkzLDE2Nyw4NywxNTYsMjAsMjAzLDQxLDEyNiwxMTksMTAsMTk1LDI4LDQsMjIsMTMxLDgsNTYsODYsNywxODQsMzMsODQsMzIsMTI4LDIzLDAsMjEzLDE1NCwxMTksMzIsMTY0LDU3LDI0MywxODAsMTgzLDIyLDM5LDE5NywxMTIsMTQ4LDkxLDIxOCwxOTgsMTc1LDg0LDExMywxOTgsMjA3LDM4LDEyMSwxOCwxNjIsNDksMTQwLDIyNCw1NywyMzYsMzIsMTIsOTcsMjE3LDM3LDQ1LDE3Niw3NiwxMDksODIsNywxNjksMjQ4LDEyNywyNiw1OCwxMTUsMTE3LDIwOSwxNjQsMTcwLDE1MCwxNjAsNjQsOTAsNTcsNDgsMzIsOTMsNTQsMjIwLDEwOCwxOCwyNDMsNTAsNywyNDIsMTEwLDUxLDM5LDE5OSwyNTMsNTEsMTg3LDc4LDEzNSw0MSw3OCwxODYsMjA3LDIwMSwyNDcsNzEsMzAsOTcsNDMsMTI0LDIzMiwwLDU4LDE0NSwxMCwyNDIsMTY5LDE4OCwyNDMsODIsNTMsOTMsMjgsMTA0LDY1LDU0LDIwMSw1MSwyNDgsMTg0LDg1LDE0OSwxODMsNTcsMjA5LDI1LDEwLDM3LDg0LDE0NiwyMTYsMTUxLDE0MSw0Myw3OCwyMjMsMTY2LDEyMCw2MSwxOTYsMTA4LDY2LDI4LDMsMTA3LDY2LDI3LDMzLDMyLDE0MCwyMjEsMTY0LDEzMSwyLDE2MCwxMzUsOTcsMTYxLDEyMSw4Nyw5Nyw2MCwxNTgsMTAzLDE0MSw5OCwyMjgsMjAsMTUzLDExNSwxMDAsMTA4LDg4LDY0LDM0LDEwNiwxMDIsODgsMywxMTUsOCwxMzUsMTkyLDE0MCwyNTIsMzksODgsMjIwLDE4NywzLDMzLDE1NywyMjAsMTI5LDY3LDY5LDE3MSwxMDQsNDgsMjEwLDE3OSw2LDE1NSwxMDEsMTQ4LDE3OCw3NiwyNTAsNTMsMzIsMTk3LDEwLDE1MSw0OSw1NCwxODAsMjMwLDE2NSw3MCwxNDgsMjMzLDE4Niw4MSw1Myw2NywyMTIsNSwxOTcsMTc3LDEwNiw5MCwxOTAsMjI3LDE1Miw2NCwyMDAsMTYzLDIyNCwyNiwxODMsMTMwLDU3LDExNiw1MCwyMTAsNTIsMTIwLDE3NiwxNjQsMjA4LDE3OSwxODYsNTAsOTcsOTEsMjUwLDIxNiwxODUsMzEsMjQ4LDE4Nyw5MiwxNTUsMjA1LDIzOSwzNCwxODYsMjU0LDI4LDM2LDQwLDAsMjgsMjIzLDQwLDI0LDEzMiwyMTQsMTA1LDYsMzQsODksMTEyLDIzMSwxMSw5LDEwLDE1OSwxNTEsMjE0LDgsMTMxLDQwLDE0OSw1MiwxNjIsMjcsMzgsODUsMTc1LDkzLDE0NCw2MCw4Nyw5Miw0LDIwOSwxOTgsMTE2LDI3LDI1MCw4NiwxNzYsMTIwLDk4LDQzLDEwNCw4OSwxMzEsMTEsOTgsMTExLDY1LDg4LDE2NiwxMDYsNDksODEsNjcsNSwxMTcsMjM1LDE1MSwxMTIsMjgsNjYsOTQsMTM4LDM2LDEzNCwxNjcsMTgwLDkwLDI0NiwxNDYsOSwxNDgsMTI3LDI1MSwyMjcsMTA1LDY0LDM0LDE0NCwxMTIsMTYzLDYzLDEwNSwxNzIsNTcsMjIzLDI0Miw0MywxMjcsODMsOTUsMTQyLDIxNywzNyw1NCw5OSw3MSwxNCwyMTgsMTg4LDIxMiwxMTIsOTksMzMsMTIyLDE5OCwyMjksMTI0LDEyMCwyMzAsNjQsMTg5LDIwNSw0MiwzOCwyMDIsOTIsNTcsNzMsNjQsNzEsODEsMTM4LDEyNCwyOSwxMTUsMTcxLDMwLDE2MywxNSwyNDYsMTI5LDMyLDEyLDM0LDE1Niw5MSw2Niw4NCwxODgsMTYwLDIwNyw2MCwyMTQsMTQyLDEwMywxMSwxNjEsMTUsMTQ2LDE3NCwxOTEsMTE0LDUwLDIyNywxNzAsNzcsNTIsMTMzLDE5Niw2LDE0OCw4OCwzMCwxODIsMTkzLDEyNCw2NiwxMTYsNDgsMTIzLDE0OCwyMjIsNDgsMTc1LDI0NCwxOTgsODAsNTgsMTIyLDg3LDU4LDEyMCwxMjksMTg4LDE3NiwxMywwLDI1MCwyMDQsODIsNzAsMTg3LDIwNCwxMzYsMTAwLDIxMCwyMTQsMTQzLDYsMTI4LDEzNCwxMjUsMjQwLDIwNSwxOTAsODMsMTY2LDI0LDE1MCw2LDE3MiwxMjMsOTgsMjM5LDIzNiwxNjAsMjE1LDk3LDE3LDIyMiwyMDQsMTY4LDExMiw2Nyw5NiwxMjcsMTUyLDEzMiwxOTEsMTM1LDE2OSwyMjYsMTc3LDE1OCwyMjMsMTg3LDE5NCwyMSwxMDUsMTg4LDIzMiwxMzEsMTc1LDY0LDIwOSw0NSw2NiwxNDksNTYsMjI5LDcsNDMsNjMsOTgsMTM2LDE5NCwyMDQsMTIzLDI2LDQsMTEwLDIxNCwxMzAsMTg3LDIwLDk5LDkzLDU3LDgzLDE3NiwyMTQsMTIzLDE4NiwxMDAsMTYsNzQsMjI2LDI0MiwxNDUsMTI4LDE2MywxNSwxMTksMjAyLDU2LDEwMSwxNjgsMTQwLDUwLDE1MiwxMjQsMTM2LDIwLDAsNTksMTY3LDU4LDEwMiw3MCwxMzAsMjQ1LDQzLDk3LDEzNCwxNzYsMTA2LDEyMywyMDMsMTQ5LDE4OSwyMiwxOTYsMjExLDc0LDE1MiwxNjEsMjI2LDEyNiwyMjAsMjMyLDMwLDIzNSw5OSw2MSwxNzcsMjE5LDI4LDIwOCwxNTQsMTA1LDQzLDEwMSwyMDMsMjQ2LDM1LDQ2LDc5LDI1MCwxNjEsNDYsMzUsODgsMTYsNDMsMiwxODAsMzQsMTEyLDE2MSw4MywxMTEsOTIsMTk5LDU2LDIzMCw4LDEyMiwxNjQsMTY4LDM5LDEyNSwxNzcsMTkxLDEzNCw0LDc3LDE3NywyMzIsMTE0LDI5LDUyLDE4MCwyNCwzLDIwNiwxMiwxODUsMzgsMTk1LDIzMywxODEsMTI2LDIwOCwxNiwyMjAsMTIsODUsMjQsNTUsMzIsMTk1LDE4NCwxMCw0NCwyMTAsNzQsMjA2LDQwLDE4MSw5Myw5MiwzLDE2NiwyMywxMTgsNCw4NSwzMCwxNywyMTYsMTA3LDIzOSwxMzMsMTE2LDYyLDE5MiwxMzMsMTM0LDQ0LDk5LDI0MSwxMzksNTMsNjksOTYsNjEsMjQwLDEzOSw5NSw5Miw3LDE0OSwxMzAsNDQsMTgzLDIyLDE4MiwxNzUsMjMyLDE4NiwxODAsMTk5LDI0MCwzNSwyNDIsMTIwLDEwOCw2MSwxODQsMzYsMzQsMTYwLDE4Niw3Myw1LDE3MCwyMzYsMjA4LDE4NywyMzksMjMsNTIsMCwxOTYsMTAzLDE4OCw0OSw5MCwyMTgsMTA3LDMzLDEzNSwxNjYsMTksMTgyLDE1MSwxOSw5Miw3MSw0OCwyNDQsMjAxLDE0OCwyMDAsMTIsMzMsMTQ2LDUyLDE4MiwxNCwyNDQsODQsMjE2LDIxOCw1MCw2MSwyMjAsNiw1LDExOCwyNCwxMTIsMjQwLDE1NSwyMzksMjQ4LDEzMiwxNiw2OSwxMjYsMTg5LDEwNiwxNDMsMTk2LDE3LDIwNCwyMDksMjI5LDExOSw0NSwxMTksODUsNjQsMjA5LDc5LDEyMiw4OSwxNDIsNTMsMTQ4LDE1OCwxMzQsMTY2LDEzMiwxMzgsMjQ0LDE5NCwxOTksMTcxLDEwNSw0Nyw0MSw5OCw2OSwyNDgsMzMsNDYsMTc5LDE3MiwyNDcsMTM2LDQ1LDEwNywyMTYsMTgyLDE5Niw5Nyw3OSwyNCwyNTAsMTc4LDEwNiwzNCw5MiwxNDQsNDQsMjYsMjQwLDI0OSwzNywyMSwyMjAsMTE1LDM1LDM5LDIxLDIzLDE5NSwxNzMsMTU5LDIwOCwxNzcsMTA0LDUxLDE2MSw3OSwzNSw4NiwxNDksMTc5LDkyLDIyLDE2NSw5Myw5MCwxMzcsMTIzLDEzNCwxNzksNjMsMTkwLDcxLDE4MCwyNDUsMjQ5LDgsMTM5LDEzNywxOTYsMjM5LDIwNCwwLDExOCw2OSwxMCw3OSwxNTMsMTE2LDIzMiwxMCw5OSw3MywxNiwyMDAsOCwxMzYsMTUsMTQ4LDI0Miw1LDQ4LDAsMTAsMjgsMTI0LDcwLDczLDcsMTY0LDIyNCw3MiwyMDQsMTM2LDI2LDUxLDY1LDExLDY4LDE4NiwxMTgsMTYwLDE3NCwxMTcsNjcsNzIsOSw4LDY1LDE4OCw2NywxNTIsMTE0LDE0NCw4OCwyLDI2LDE0OSw2NywxOSw3OCwzNCwxNDcsMTQsOTcsMTQ4LDI0MSwyMzEsMzksMjIxLDE3NywxMzEsMjEyLDE3LDMyLDg4LDIxNyw0NiwyMDgsMTgsMTE4LDIzOCwyMzMsMTAzLDE3NywxNzIsOTEsMzEsMTY3LDMwLDI1MSwxNjUsMTYzLDE4NywxNDQsMTUxLDE3OCwxMTksOTcsMjI1LDI0NCw4MSwxMzEsMTgyLDIwNiw0NiwyNSwyMDQsMTIyLDQ5LDIyLDEzMywzOCwxNjMsMjUxLDEwMywxMTksMjQ0LDg4LDQ0LDE1NSwxMDIsMTkyLDE3NSwxNzcsMTIwLDEzOSwzNiwyOSwyNCw1OSwyNSwyMjUsMTM0LDE0OSwzOCw2OCwxNzUsMTczLDIzMSw4OSwxNzUsMzcsMTEzLDgzLDI0NCwxNjYsMjA2LDIwOCw5NywxNDksNTIsODAsMzAsNDgsMTAxLDE1Miw5NywxMzcsMTg3LDIxNCwxNjQsOTAsMTYyLDY2LDE3NywzMSwxMzgsMjUwLDExOCwyNTEsMzIsMTM2LDE5NywyMzEsNCw1MSwxNzUsMTk0LDE4NCwxOTgsNywyMzcsOTQsMjI0LDIyMiwyNTEsMzQsMiwyMzAsNSwxMjAsMTk3LDQzLDc1LDI3LDE2NSwxMjAsNCw1MCwyLDE3MCwxOTEsNzYsMTUyLDc1LDExNSwxMDIsMTk3LDQ0LDIyNCwxNzIsMTA1LDgyLDE0NCw4MywxMzYsMTA5LDgsMTQxLDIyOCwxMSwxMzcsMjA2LDE0Nyw0OF19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjQsMTgwLDEyNSwxMjYsNzksMjIwLDIyMSwyNTMsMTIzLDQyLDIwMSwxMjAsMTgwLDE2NCwyMDAsMjA4LDE3Nyw5OSw4Miw2MCw1NSwxNTYsMTI2LDc3LDExNywxMTUsMjAzLDIwMywxNDIsMjU1LDE2NiwzNF19fSwiY3JlZGVudGlhbCI6eyJjcmVk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MiwyMDEsMTIwLDE4MCwxNjQsMjAwLDIwOCwxNzcsOTksODIsNjAsNTUsMTU2LDEyNiw3NywxMTcsMTE1LDIwMywyMDMsMTQyLDI1NSwxNjYsMzQsODIsMTE3LDExNSwxMTYsNjcsMTE0LDEyMSwxMTIsMTE2LDExMSw4MywxMDUsMTAzLDExMCw5NywxMTYsMTE3LDExNCwxMDEsNzUsMTAxLDEyMSw4LDddfQABeyJwcml2YXRlIjpbMTgwLDg0LDcyLDI0MiwxMzUsOCwxNTksMTQ3LDk5LDE1LDExLDE3NywxNDYsMTg1LDIyOSwxNiwxNTQsMjAzLDc1LDEzNCw1MiwyMTgsMTc2LDQxLDI1MSwxNywxODcsNjgsMTksMTI0LDMxLDIyMF0sInB1YmxpYyI6WzE2NCwxODAsMTI1LDEyNiw3OSwyMjAsMjIxLDI1MywxMjMsNDIsMjAxLDEyMCwxODAsMTY0LDIwMCwyMDgsMTc3LDk5LDgyLDYwLDU1LDE1NiwxMjYsNzcsMTE3LDExNSwyMDMsMjAzLDE0MiwyNTUsMTY2LDM0XSwic2lnbmF0dXJlX3NjaGVtZSI6IkVEMjU1MTkifQ==","AAAAAAAAAA0AAAAAAAAAXAAAAAAAAAAFVXNlUmF0Y2hldFRyZWV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAFmYWxzZQAAAAAAAABaAAAAAAAAAfdHcm91cENvbnRleHR7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7InByb3RvY29sX3ZlcnNpb24iOiJNbHMxMCIsImNpcGhlcnN1aXRlIjoiTUxTXzI1Nl9YV0lOR19DSEFDSEEyMFBPTFkxMzA1X1NIQTI1Nl9FZDI1NTE5IiwiZ3JvdXBfaWQiOnsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0sImVwb2NoIjoxLCJ0cmVlX2hhc2giOnsidmVjIjpbMTI1LDExNSwyNDQsMTM3LDIzNiwxNTcsMjQsMTM0LDEzMCw3MywxMDEsMjE0LDYwLDg2LDE4OCw5MiwxLDI0Myw3NSw0Niw4NSw2MCwyMjAsMTkwLDEyNywxMjEsNTQsMTU2LDE3LDI1MSwyMzUsMTM1XX0sImNvbmZpcm1lZF90cmFuc2NyaXB0X2hhc2giOnsidmVjIjpbMTcyLDY0LDE1MCw1MywxNywxOTUsMjUzLDY5LDcsMTY0LDE4NywxMDcsMTY4LDE3NSwyNDEsOTEsMjEwLDIwNSwxNzIsMzUsMTMsNjAsMTgsMjcsODIsMjExLDExNCwyLDI1MSwxNjUsMTM0LDIyOV19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fQAAAAAAAABdAAAAAAAAAIhDb25maXJtYXRpb25UYWd7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7Im1hY192YWx1ZSI6eyJ2ZWMiOlsyMDgsMSwxOCwyMjcsNzQsMTgyLDExMCw5MSwxOTksMTY4LDI0MSwx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AVt7InB1YmxpY19rZXkiOnsia2V5Ijp7InZlYyI6WzEzNCwxNjksMTA0LDIwNywxMzQsMTQ1LDIwLDI3LDk4LDIxNCw2Nyw2NCwxMDAsOTIsOTEsNzksMTY4LDE2OCwyOCwyMDgsNzQsMjMyLDExLDE3Nyw4NywyNTEsMjUsMSwyNTAsMTcxLDIwOSwxNiwxNjMsMTc1LDE0MCw0MSw5MiwxNTIsMjIsODAsODAsMTA3LDY4LDI1MSwzLDE1OSwyMDksMTI1LDQ4LDE2NSwxMDgsMjM2LDIwLDExLDU0LDEwMCwxODgsMjI4LDE0NCwxNzYsMTg2LDIyMCwxNDcsMTA4LDE2LDkzLDkzLDE3Niw0MywyMTIsNjUsMzgsMTQ3LDE2MCw4LDcyLDI3LDEwNCwyMjQsMjQzLDEzMywxNDIsMTAxLDIwNiwyMDQsMTk3LDY2LDg4LDI0LDE0MywxMDUsMTM3LDEwMywwLDE5Niw0OSwyNDEsMjA0LDUzLDg3LDIyLDEyNywxODgsMTMyLDM4LDExMiwxNjgsNTAsOTYsNDQsNjcsMjQ0LDEzOSwxMTEsMTY1LDU0LDUzLDE4NiwyMTksODQsMTMxLDExLDgyLDE5MCw5NiwxOCwyMzAsMTkzLDk3LDIwOSw4MCw4NSw2NCw3Myw1NCw5MywxNTUsMTg4LDE0LDYsMzUsMTgyLDEzMiwxNzAsMTE2LDIzMCw0MiwzMywyMzUsODUsMTI2LDM5LDEwNSwxMzksOTIsNTcsNiwxODYsODksMTU3LDgzLDQwLDIwNywxNzksMTYxLDYwLDE0MCwyMDYsMTk4LDE3MSwxMTgsMjMyLDEzNCw4OCw2OSwxNDAsODksMTM1LDExNyw2MiwxNTUsMjUwLDk1LDE1MiwyMjYsMzAsMzAsMTE0LDE5Miw5OCwyMTcsMTE2LDc2LDE2OCwwLDIyNSwxLDE1NywxOTAsMTcyLDEwMywxNDEsOTIsMTU1LDE4LDExNyw1OCwxMzAsMTgxLDE5NiwyNyw1OCw3OSw5OSw4NCw4LDE4OSwxOTQsMTI4LDUsMTE0LDIxLDE1LDkyLDE4OSw5NSwxMzMsMTgyLDE0OSwxODYsNTIsMjQ5LDM5LDcyLDgsMjEsMTA5LDE0LDUzLDM3LDE3OCwxOTYsMTE0LDE3OSwxNzEsNjksMTg5LDQxLDE1NSwxNCw4MywxNTcsMTcwLDI0NCwzOSwxNDgsMjQ0LDE3MCw4MCwxODYsMTU1LDEwMCwyNDMsMTg5LDUwLDE3NywxNDUsMjI1LDg0LDE4LDIsNywxNDksMjE4LDg0LDE3NiwxOTIsNTAsMTE5LDc2LDEwMCwwLDY3LDI4LDIwNiwyMjgsMTQ5LDEwMSwxNzcsMjQ3LDE5OSw5OCwxNTQsOCwyNDYsMjIwLDUwLDEsMTE4LDE2Nyw2MCw3MiwxNTAsMjI5LDU4LDE4OSw0NSwyMjAsMTE5LDIxOCw0MCwyOCwxODYsMTgsMTc0LDAsMjA5LDE0MCwxNzEsMTYzLDE0LDk0LDkyLDEzOCwyMTcsNzUsMTQwLDE3MCwyMjcsMjAwLDEyNywxNTUsNDgsMTgxLDEyLDEzMywyMjUsMjQ5LDE2MCw0MSwyMDQsMjYsMTAxLDIxOSwwLDE4NCwxNTQsMjA1LDIzMywxOTYsMTQyLDIzMiwxMjMsMzAsMjM3LDIxMCw5Myw2NCw4Myw4NiwxNTAsMjAsMTg5LDEwNyw1NSwyNCwxNiw4NCwyMywxMTUsMTQ1LDgsMTExLDI3LDQsMTg0LDU2LDQ0LDI0NiwyMDgsMTA2LDExNSw5MSwxNTksMjQ0LDEyMywxOTYsMjcsMjE2LDk3LDE1MCwyMDgsMiwyNTIsNjAsMTc4LDE5MCwzOSw2LDIyLDE0NywxNjAsMzMsMjQ1LDIxLDE2MywyMTYsNSwxOSwxMzYsMTU2LDEwNiwxNTYsMTI3LDIzMSwxMDIsMjA3LDM1LDk2LDEyOSwxMTQsMjA4LDg2LDE4MCwxMTYsMTE5LDEyNiw3NCwxMiw2Nyw2LDEzMyw0MSwxMTgsNzYsMCwyNiwyMDYsNiw1LDEwMywxNTEsMjA0LDEzNiwxNjQsMzgsNjYsMTQsOTIsMTI1LDE2Myw1MywxNTEsMTMwLDIzNSwxODQsMTU4LDE0OSwxNjEsMjM4LDE5OSw3MSwyNTEsMTg3LDM1LDE4OCwyNCwzMSwxNjMsMjM1LDE2Myw2MCw2MCwxMzAsNzksMTU0LDI1LDIxNyw2NCwxOTUsMTc0LDEzOSw4NSwxOTUsMjA4LDEzOSw3Myw2MCwxOTYsMTI5LDY0LDIwMywxNjYsMTI4LDEzOSwyMDQsMTgsMTI3LDEzOCwyMjksNzcsMTY4LDE2MSwxOTMsMjA3LDEwNSwwLDE4MCwxMDcsMTcyLDY1LDExNyw2Myw3NCwzNiwxNjUsMTQsNTEsMTI0LDE2LDEwNywyMyw4MiwxNDUsMTA2LDI0NywxNDQsNDQsMTkzLDEyMCwzLDEwNCwxNjgsNDEsMTg4LDM0LDIwNiwyNTEsMTcwLDIwNSwyMDMsMTEyLDE3MSwxNjEsMTMzLDc2LDE0OCwxMzgsMzQsODAsNDEsMTY4LDEyOCwyMywyMDMsODEsMTQ3LDUwLDUxLDE0NSwxOTgsMTUyLDIzNCw5Niw3MiwxODUsNTAsMjMxLDI3LDE0NSwyMDcsMjExLDQyLDQwLDg2LDMxLDI1MCw4Niw4OCwyMzYsMTk3LDE2OSwxNzYsMTY1LDEzMCwyMzQsMjQ0LDE4NCwxNTUsMTg2LDIsMTk5LDU1LDY5LDU3LDIwMCwxMjgsMTUsMTksNzgsMjMxLDIxMywxNzIsMjAsMjE0LDM0LDE4OSwyMTIsMTgzLDQsMTEsMTQyLDE4OCwxNjAsNDksMTU3LDI3LDIxLDE1NCwxNjQsMTc5LDIyMCwxNjYsMTc0LDIwNiwxMzEsMTEsNjQsODAsMjgsMzEsMTIxLDExMCwxODEsMTIyLDEzMywyMjUsMTQ3LDE3OSw5OCw4OSwxNzksNjYsMTY4LDQyLDI0MCw4NywyMDUsMTg3LDE5NSwxMTAsMTk5LDIyMCwxMywxODMsNDQsMTksMSwxNTEsNTksMTgwLDE2MSwxOTEsMTAzLDEyMywxMywyNTUsMTY4LDcwLDg0LDI2LDIwNCwxMzAsODYsMzgsMTM2LDIzLDEzNCwxNTgsMTAxLDEyOCwyNTMsMjAyLDEzOSwxNjIsMjE0LDMwLDE5MSwxMTIsMTIwLDIwMiwxNTAsMTc3LDMsMTY1LDgzLDI0MCwxNjQsMTY2LDI0NiwxNTIsMTQ3LDE5OSw4NCwxNSwyMDIsMjM0LDMwLDc1LDM4LDczLDk4LDY3LDE2NCwxMjAsNTQsNywxMjIsNTMsNzEsMzcsMjUsMTk3LDc1LDE4NywxMiwxODksMTk3LDQxLDE5MCwzMiwxMjMsMjE0LDEzOSwyMDUsMTcyLDI0NywxMzMsMTUwLDcsMTk1LDEyNiw4MCwxMjAsMjAxLDk4LDE0MiwyMTUsODUsMjAwLDIzNCw3NSw1MCwyMTQsNDksMTA2LDIxNiw1OCwxOSw2OCwyMTMsNzMsMzcsMTQ4LDEzNCwyMDEsMjM1LDEwMiwxOTEsNzUsMTgwLDIxNCw1NSwxODcsMTk5LDg2LDI5LDAsNywyMDAsMTMzLDEzNSw0MiwxODksMTgwLDE3OCwxNDIsMzcsMTIwLDEyOCwzNiw3MCwyMzQsMTMyLDU5LDQ2LDIxNywxNDIsODMsODYsNjYsMTkzLDE2OCwxNDIsMTk4LDEwMyw2NSwxMzksMjQzLDE1MSwyOSwxMzksNjMsNjMsMTg4LDEyMiw0MiwzNSw2OSwxNTAsOTksMTExLDE0NiwxODQsMjUsMzksOTAsMzMsMTI2LDI0OSwxODksMTI3LDE3Niw2MSwxMTUsMTA1LDk4LDkxLDUzLDEzNSwxMTQsMTIyLDE2MCw4Miw3MywxNjUsNTcsNjgsMTEwLDIwOCwxMjQsMTI1LDIsMTY5LDExOCwxMTksODQsMzcsMTI4LDg2LDE1MCw1MSwxMDUsODcsMjU1LDMzLDIwMywyMDAsMjMxLDEwMywxMjksNTIsNjQsMTg4LDgyLDU4LDY2LDE2Myw1NywyMTEsMTIwLDExMCwxODYsMTgsODUsMTMxLDIxOCwxOSwxNyw4LDc4LDIwNSwxNzcsNTYsMjIzLDEyMCw5Miw0MywxOTYsNywxMjcsMTU2LDQyLDgzLDEwNywxMCwyMjMsMTMwLDEwLDI0MCwyMzMsMTg5LDE1LDE5OCw0MCwyNDAsMTE4LDUsNDUsNjUsMjEsOTUsMTg3LDE3OSwxMDYsMTMyLDExMywxOTIsMTAsMzAsMTMzLDE2NCwyMSw5Myw5MiwxNTgsMTcwLDEzOSwxMjYsMjQ4LDI0OCwxMzEsMjI3LDE4Miw5NCwxNTksNCwzNyw1NCwxOTksMTg3LDE0NSwxMTcsMTMzLDE3LDU3LDU1LDIxMiwzNSwxMTIsMTg1LDEzMSw5LDIwOSw2NCw3NCwxNDYsMjE3LDUyLDIyLDIyNCw0OSwxOTUsODcsMTc0LDYwLDIxLDg4LDc2LDIwOCw4NywxOTUsMjI2LDczLDIwMSwyMjksMTI4LDY4LDIzNiw4NCwyMjcsMjQyLDE2NCw3OSwxOTYsMTM0LDExOCw2NywxMjgsMiwxMCwxNywyMjYsMjAsMTI2LDEzNywzLDE4NiwxMDEsMTQ0LDIwMCwxODksMTUzLDExNCwxNjEsMTk5LDUsOTIsMTQ4LDExMiw1NCwyLDE0NCw2MCwyMTgsMCwyMywyMzIsNjAsMjQzLDIxMiwxNzUsMCwzNywyNSw0NywyMTIsMTEwLDIxNiw0OCw0LDE0NywyMjQsNTQsMTQsNzAsMjAwLDIzMywyMTIsNTYsMTQsMjE0LDEzNSw3OSw4MSwxNTAsMTE2LDEzMywzLDc1LDIzMiwxODgsMjUzLDIyMCwxMzksMjAyLDE0NCw1Niw4Miw1MiwzLDI1LDIyOCw5Nyw5OCwyNTIsMjAxLDc2LDE0MCwxOSwxNDQsOTgsMTAyLDIwMiwxMzYsMTg3LDUwLDE5MiwxMDMsNDQsODYsOTcsMTI3LDEyMCwxMiw4OCwyMzYsMTYzLDgwLDIxNCwxODQsMjIwLDIxNSw0MCwxMzksMTE3LDcxLDIxOSwxODEsMTE3LDUxLDIxMiwxMjQsMTAyLDExMywxMDEsMzEsMTQ2LDQ2LDE5MSwyMDEsOTksNiwyMjgsMTIyLDIyOCw3MSwyMDcsOTAsOTIsNTQsNzAsMjUwLDE2MSwyMjEsMTM5LDExMSw0Niw4NSwxODAsMTY1LDcsNTYsMTI1LDIxLDM0LDY3LDExOSwxMTIsMTY1LDExLDE5NywxMDAsNjUsNjcsMjIxLDU5LDIzNCwxODksMTk1LDIwMywxNjIsMTU5LDIwMywxOTksMTU4LDE4MiwxNjUsMSwxOTIsMTU1LDExNSwyOSwxNzksMjMzLDM1LDIxNSw5NSwxOSwyMjQsMiwxOSw4MywyMTYsMTIsODEsMTk4LDE4MywxODAsMCw1OCwyNDgsMzksMzksMjUzLDg0LDUwLDk0LDI1NCwyMjUsMjEyLDE1LDEwLDg4LDI1MywyMTAsOSwyNDYsMzcsMjI0LDE2NSwxODUsOSw2LDMzLDBdfX0sInByaXZhdGVfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxMzQsMTUxLDE3LDIyMiw1NywxNiw5MywyMzMsODcsMTIwLDE3MSwxNjMsMTU2LDEzOSwxMjMsMTgyLDIwOSwxNTIsMTAxLDEzOCwxOTMsNjYsMTM4LDExNywyMjgsMTM4LDQsNjMsMTIyLDEsMTY4LDgxLDMsNDksMjI3LDg5LDIwMywyNDUsODAsMjQzLDE5NCwxODksMTE0LDQzLDEyMiwxMyw1OCwxNzYsNDEsMTgwLDU1LDQ0LDIyNSwxODYsMTM3LDEwMiw2Niw3OCwzMiwxMzMsMjA1LDksMjIsMjMyLDE1MSwxODEsMTk0LDE0NCwyNywyMjAsMTY3LDE2NywxNjEsMTMzLDE4Miw3NSwxOTksMTA0LDMsNjksMTYwLDI4LDQ0LDE1OCw1MiwyMywzLDU1LDIyLDczLDIyNSwyMjUsMjAwLDcwLDgxLDI1LDI0OCwxNjIsMTE1LDk5LDIxNywyNSwxODYsODcsMTg2LDg3LDI1Miw2MywxMzEsMTYzLDE4NSw4MywzNywxOTUsMTUyLDEyNCwxODAsMTI0LDk4LDUwLDE1NiwyMTksNjgsNzMsMTcwLDQsNDQsMTI5LDE2NiwyMjYsMTA0LDE4MCwyNTAsMzQsMTcwLDIyMSwyMTcsNzksMTgsMjUyLDE2NCw3NCwyNTIsMTU5LDExMywzOSwxODAsNDIsMTcsMTkxLDI0Niw3MiwxOTcsODMsMTk1LDE0Nyw2MywyMTUsNTYsMjEsNTAsMTM0LDIyOCwyMTUsMTM1LDI1NSwxMzksNDEsMTQ3LDkwLDIwMSwxMzcsODYsMTExLDExMCw5MiwxNTAsMTUwLDE5MiwxODEsMjQ1LDEzMCwxMDUsMTgsNDksMTYyLDI1Miw3MCwxNSwyNTUsODEsMTk0LDExMCwyMzUsNzQsMTk1LDgzLDQsMTk2LDEzMiwxODMsMTQ2LDE2LDY4LDE0Niw5NiwxNzEsOTcsMCwyNiw3OCwyOCwxNDUsMTEsNTEsMTYzLDEwNCw3Miw1MywxMzcsNDQsMTYsMjIxLDg4LDIzLDE2OSw2NiwxNDMsNjMsMjAzLDgxLDIwNiwyNDEsMTg3LDI3LDEzMSwxODMsODMsNCwxMDAsMTgxLDYsMTM1LDIwNCwyMjAsODMsMTg3LDU2LDgyLDc3LDczLDEwOCwxNiwxNjMsMTU2LDkxLDk4LDEwNywyOCwzLDE4MywyMTYsNTksMTY2LDE4MSwzNCwxNTksMCwyMTIsNDEsMjAzLDEzNSwxMjYsMTk1LDgzLDE4NywxMjEsOCwxMTksMTQ4LDM4LDEzOCwzOSwxNzAsMTE3LDIyMCwxNzksMTI5LDUwLDM1LDkzLDE2OSw2NCwxMDAsMTU2LDIzMiwxNzEsMzYsMTQ3LDEzNSw4OSw4OSwxOTIsMTcsOTIsMTQyLDE2MCwxNzgsMTA3LDE0MSwxNjksMTIsMTM4LDM1LDcxLDE0MCwyMjgsMzgsMTAsMjE1LDc0LDI5LDE1MCwyNiwxNTcsMjE0LDE5MSw0OCwyMCwxNjksNDIsNTIsMTk1LDMsMTA4LDE5MCwxMDQsMjEyLDE3NCwyMjUsMjQzLDMsNzQsMjAwLDE2NiwzLDEyOSwxMDksMTUyLDE5Niw4OSwxMTEsMTY3LDY1LDE4OCw5Miw3OCwxOTMsMjcsOTcsOCwxNDcsMTQsMTQzLDI0MiwyMDcsMjM0LDExMiwxODEsODYsNTMsMTI3LDkyLDE5NSwxNjMsMjM2LDgsMTAwLDIwNSwxNDYsMTM5LDE0Myw5NiwxOTMsMjE2LDU4LDEwMCwxNjEsNTIsMTMxLDM2LDEyOSwxMzEsMjMzLDY5LDMxLDk3LDExNiwxNzksMTE1LDE1MSw4MywxMDEsMTcyLDIyLDE1NSwyMjcsNjQsOTIsMTE5LDk1LDc4LDc1LDEzMyw2OSw5NywxOTMsMjgsMjAzLDEyOCwyNDksMjQ2LDEyOCwyMjQsMjMzLDEzNiwxNzksMTM0LDEzNSwxMjYsMjYsOTUsMjQzLDE5OCwxMSwxMTAsMTY0LDI3LDAsMjE1LDIwNCwyNTIsMTU0LDIwNywxMDgsODMsMTEyLDEyMCwxOTUsMzksMTQzLDc0LDEzNCwxMzcsNDMsODUsMTQ4LDE1MSw3NSw5NCwyNTIsNjgsNzgsMTg0LDg1LDIyMSwxNywxMjksMTkxLDE2NCw3NSwyMTQsMyw1MCw3Nyw4NCwxNjQsMjMzLDIxMSwxNDgsMjE3LDIyOCwxNDAsMTg0LDIzNSw2NSwxMDEsNCwxNSwxMjksODMsMTcxLDI0MCwxMjAsMTUyLDkyLDkxLDUyLDU5LDk3LDk5LDIzLDIxNCwxODUsNzUsMjI4LDEyLDY0LDY1LDY4LDI2LDk5LDE4NCwxNzEsMTE0LDEwLDYwLDIzMCwxODUsMjM5LDE3NiwyMDQsMTAzLDI1MCwxMjMsNzUsMTk1LDY5LDIzNCwyMjUsMCwxOTksMjUxLDExMyw2Niw1OCwxMjMsMjU0LDIxNywxNTksMTY0LDE4MCwxNiw4OSwyMywzMCwxMTEsMTc4LDIwNSwxMjAsMjI5LDEwNywxMTgsMjI2LDgwLDIyLDg2LDMxLDEwMiwxNjMsMTc4LDcxLDE3Niw1LDEwNSw4OSwxODEsMjI5LDQ0LDE4Myw1LDE2MCwxMjEsMTA5LDU0LDE3NywyMDAsMjQ2LDE2MSwxNzIsMTQ0LDIwMywyMjYsMTk5LDM4LDExMSwyNiwyMDQsMTMxLDI0MiwxOSwxMzUsMjQwLDE4NSw1LDE1MywxOTgsMjM0LDExMiwxODUsMiwxNDYsMzAsMjEyLDI0OSw4MiwyMzksMjQxLDEyNiwxMTYsMjA4LDEwNCwxNjMsMjMwLDMsMTM0LDEwNCwxNiwyMTcsNzAsMTY2LDEwMyw1LDE4Myw4OCwxMzYsMTAxLDE5MSw3Myw3MSwyNTEsMTY3LDQ3LDIxNyw4OSw0MywxMTIsMjExLDExNSwxNDgsMjUyLDI4LDcyLDE5OSwxMzksMjIwLDE3MiwxNzcsMTAwLDU1LDExOCw4LDE0MCw4NCwxNDMsMjIsMzMsNDcsODEsOTksMzUsMjAxLDI2LDE5NSwyNDAsMTUwLDU0LDIzNCwyMiw3MSw1NCwyMDAsMjAzLDE2MSwxNzQsMjQ2LDI0OSw0MCwxNjgsMjI2LDE2MiwxODEsMTU1LDE3NCwyMjYsMTI5LDg1LDIyMCw4NiwxNjgsMTM1LDE2MSw2OCw2Nyw2NiwzOCwxNywxOSw2LDc2LDExOCw4MCwyNDcsOTEsOTcsNDIsMjE0LDMxLDIwNSwxMTQsOTQsMTc0LDI0MywxMSwyNTQsMTM3LDM2LDU5LDM0LDEzNywxODAsNiwxMzQsNTIsMTEyLDE3NSwxNjksMjM0LDg0LDI2LDI2LDE5NCwyMjEsMTk3LDMzLDI1MywxOTYsMjYsMjgsMTMxLDQ3LDEyNCwyMjcsNzcsMjQxLDU3LDYwLDE1MCwxODYsMTQ4LDE4NSw5NywxMTMsNzksMjM2LDExNSwyMzcsNCwxMDQsMTExLDI0LDQ5LDE4OCw2OSw5LDk3LDgzLDkxLDI0NCwxMjgsMTg0LDIzMiwyMDMsMjUsMjE5LDE0OCwxLDEzMiwyMTAsMTUwLDgxLDUzLDIyLDE0MywxNzksMjA1LDIxOSwyMTAsMTcyLDEwMiwxOTQsMTAzLDEwNiwxODgsMTk0LDEzNSwxMTYsMTQ1LDYsNTAsOSw4MCwyMTAsMzIsODAsNTcsNTIsMTM2LDEsMTUyLDEzNSwxODgsNTgsNzIsMTg2LDE2MCw2NCw5NywxODYsMTk3LDIwOCw1OSwyNSw5NiwxNTksNywxNzksNDIsNjEsMjE3LDg0LDE5OSwxNDcsMiwzOSwyMzAsNjMsMTM1LDE4NywxNTEsMjE0LDUwLDE0LDE0NywxOTIsMjA0LDEyOCwxMDcsMTg5LDMyLDIyOCwxMjQsMTU4LDEzNiwyOCwxMjUsMTY5LDE0Nyw1NCwxMDEsMTE3LDExNCwxMTgsMTU3LDUyLDIwOSw4MiwyMjAsOTcsNzEsMTIsMjEwLDExNCwxMjIsMjAzLDE5MywxMjMsMTgwLDExNCw4MiwxNTMsMTgwLDIyMiwyNiwxNjEsMSwyMCwxODMsNjAsMjE4LDE1NywyOCw0OCwxODcsMTkzLDI0MSwxLDI1NCwxMjEsMzQsMTc1LDE2NiwxNTksMTQ5LDExNCwzMyw3NCwwLDQzLDIzNiw4Nyw0NCwyMDMsMTU0LDkwLDIwNiwxMzIsNjEsMTEwLDQxLDMxLDI0Niw1LDgsMjIxLDE4MiwxNDgsMjE5LDE5Nyw5MiwxNDEsMjA4LDExNiwxMDEsMTA4LDc3LDIyMywxNzYsMTUsMTYxLDIyOCw2Miw1MCwxMTQsMTE0LDcyLDcyLDU5LDE4MCwxNjQsMTM4LDEzOCwxODMsOCw5MCwyNCwxNzIsMjI3LDE3MSwyOSwyNDYsMzksMTM1LDcxLDEzMywxMTMsMTAxLDIyNCw1NiwxMzQsMjgsMjAwLDIyNiwyNDQsMTIxLDE4MywxNDksOTMsNjAsMjEwLDIwNiwyMzIsMTIsMTIzLDM4LDU3LDE3NSwyMzMsMTIxLDg0LDU3LDE4Nyw0OCwyMjYsMCwyMDcsMjksMjE5LDQ3LDEzMCw5Miw2MCwyNTQsMTMyLDEwOSw5MCwxMDgsMTUwLDEsNjAsMTMyLDc2LDU3LDc4LDkxLDE3NywyMDIsMTgwLDE5Niw3OCwzNywxODAsNDIsMTUxLDM3LDE1NCwxODEsOTIsNzIsMjQ1LDgsMjQsMTc3LDEsMTIzLDE0MiwxMjAsMTMzLDE3NywyMjgsNzUsNzUsMjI4LDEzOCwyMTYsMjMzLDE0MSw2NSwyMDIsMTgxLDExNyw1NywxNDEsMjUwLDI0NCwxMCwxNDksNTQsMTczLDEwNCwzNiwxNTYsMzUsMjUxLDE2LDIxMSwxNDQsMzAsMTQxLDkxLDE3NiwzMiw3NiwxNjIsMTUyLDIwOCwxMDgsODUsMjEsMTU5LDg5LDcsMTUzLDIwOCwxMTIsMTk1LDIxNywxMDcsMTQ5LDczLDE4MCw4NCwxMiw0MCwxMzIsMTU2LDg0LDE5MCwxOTQsMTUyLDE0NSw0MCwxMDQsOCwxLDExMyw3Miw2OSwyNDcsMTUxLDIxNywyNDksODIsNTgsMTE5LDE5NywxMzAsMTYzLDExMCw5Niw4NSwyNywxMzIsODcsNTksMTA2LDgsMTA3LDExMiw1NSw5NywxMDAsMTE5LDExMiwxMzYsMjEwLDQ0LDE5OSwzOCwyMDcsMjIwLDEzOSw5Niw3LDg2LDEzLDQsMTQ0LDE1MiwxODYsNiw5MSwxMzQsMTY5LDEwNCwyMDcsMTM0LDE0NSwyMCwyNyw5OCwyMTQsNjcsNjQsMTAwLDkyLDkxLDc5LDE2OCwxNjgsMjgsMjA4LDc0LDIzMiwxMSwxNzcsODcsMjUxLDI1LDEsMjUwLDE3MSwyMDksMTYsMTYzLDE3NSwxNDAsNDEsOTIsMTUyLDIyLDgwLDgwLDEwNyw2OCwyNTEsMywxNTksMjA5LDEyNSw0OCwxNjUsMTA4LDIzNiwyMCwxMSw1NCwxMDAsMTg4LDIyOCwxNDQsMTc2LDE4NiwyMjAsMTQ3LDEwOCwxNiw5Myw5MywxNzYsNDMsMjEyLDY1LDM4LDE0NywxNjAsOCw3MiwyNywxMDQsMjI0LDI0MywxMzMsMTQyLDEwMSwyMDYsMjA0LDE5Nyw2Niw4OCwyNCwxNDMsMTA1LDEzNywxMDMsMCwxOTYsNDksMjQxLDIwNCw1Myw4NywyMiwxMjcsMTg4LDEzMiwzOCwxMTIsMTY4LDUwLDk2LDQ0LDY3LDI0NCwxMzksMTExLDE2NSw1NCw1MywxODYsMjE5LDg0LDEzMSwxMSw4MiwxOTAsOTYsMTgsMjMwLDE5Myw5NywyMDksODAsODUsNjQsNzMsNTQsOTMsMTU1LDE4OCwxNCw2LDM1LDE4MiwxMzIsMTcwLDExNiwyMzAsNDIsMzMsMjM1LDg1LDEyNiwzOSwxMDUsMTM5LDkyLDU3LDYsMTg2LDg5LDE1Nyw4Myw0MCwyMDcsMTc5LDE2MSw2MCwxNDAsMjA2LDE5OCwxNzEsMTE4LDIzMiwxMzQsODgsNjksMTQwLDg5LDEzNSwxMTcsNjIsMTU1LDI1MCw5NSwxNTIsMjI2LDMwLDMwLDExNCwxOTIsOTgsMjE3LDExNiw3NiwxNjgsMCwyMjUsMSwxNTcsMTkwLDE3MiwxMDMsMTQxLDkyLDE1NSwxOCwxMTcsNTgsMTMwLDE4MSwxOTYsMjcsNTgsNzksOTksODQsOCwxODksMTk0LDEyOCw1LDExNCwyMSwxNSw5MiwxODksOTUsMTMzLDE4MiwxNDksMTg2LDUyLDI0OSwzOSw3Miw4LDIxLDEwOSwxNCw1MywzNywxNzgsMTk2LDExNCwxNzksMTcxLDY5LDE4OSw0MSwxNTUsMTQsODMsMTU3LDE3MCwyNDQsMzksMTQ4LDI0NCwxNzAsODAsMTg2LDE1NSwxMDAsMjQzLDE4OSw1MCwxNzcsMTQ1LDIyNSw4NCwxOCwyLDcsMTQ5LDIxOCw4NCwxNzYsMTkyLDUwLDExOSw3NiwxMDAsMCw2NywyOCwyMDYsMjI4LDE0OSwxMDEsMTc3LDI0NywxOTksOTgsMTU0LDgsMjQ2LDIyMCw1MCwxLDExOCwxNjcsNjAsNzIsMTUwLDIyOSw1OCwxODksNDUsMjIwLDExOSwyMTgsNDAsMjgsMTg2LDE4LDE3NCwwLDIwOSwxNDAsMTcxLDE2MywxNCw5NCw5MiwxMzgsMjE3LDc1LDE0MCwxNzAsMjI3LDIwMCwxMjcsMTU1LDQ4LDE4MSwxMiwxMzMsMjI1LDI0OSwxNjAsNDEsMjA0LDI2LDEwMSwyMTksMCwxODQsMTU0LDIwNSwyMzMsMTk2LDE0MiwyMzIsMTIzLDMwLDIzNywyMTAsOTMsNjQsODMsODYsMTUwLDIwLDE4OSwxMDcsNTUsMjQsMTYsODQsMjMsMTE1LDE0NSw4LDExMSwyNyw0LDE4NCw1Niw0NCwyNDYsMjA4LDEwNiwxMTUsOTEsMTU5LDI0NCwxMjMsMTk2LDI3LDIxNiw5NywxNTAsMjA4LDIsMjUyLDYwLDE3OCwxOTAsMzksNiwyMiwxNDcsMTYwLDMzLDI0NSwyMSwxNjMsMjE2LDUsMTksMTM2LDE1NiwxMDYsMTU2LDEyNywyMzEsMTAyLDIwNywzNSw5NiwxMjksMTE0LDIwOCw4NiwxODAsMTE2LDExOSwxMjYsNzQsMTIsNjcsNiwxMzMsNDEsMTE4LDc2LDAsMjYsMjA2LDYsNSwxMDMsMTUxLDIwNCwxMzYsMTY0LDM4LDY2LDE0LDkyLDEyNSwxNjMsNTMsMTUxLDEzMCwyMzUsMTg0LDE1OCwxNDksMTYxLDIzOCwxOTksNzEsMjUxLDE4NywzNSwxODgsMjQsMzEsMTYzLDIzNSwxNjMsNjAsNjAsMTMwLDc5LDE1NCwyNSwyMTcsNjQsMTk1LDE3NCwxMzksODUsMTk1LDIwOCwxMzksNzMsNjAsMTk2LDEyOSw2NCwyMDMsMTY2LDEyOCwxMzksMjA0LDE4LDEyNywxMzgsMjI5LDc3LDE2OCwxNjEsMTkzLDIwNywxMDUsMCwxODAsMTA3LDE3Miw2NSwxMTcsNjMsNzQsMzYsMTY1LDE0LDUxLDEyNCwxNiwxMDcsMjMsODIsMTQ1LDEwNiwyNDcsMTQ0LDQ0LDE5MywxMjAsMywxMDQsMTY4LDQxLDE4OCwzNCwyMDYsMjUxLDE3MCwyMDUsMjAzLDExMiwxNzEsMTYxLDEzMyw3NiwxNDgsMTM4LDM0LDgwLDQxLDE2OCwxMjgsMjMsMjAzLDgxLDE0Nyw1MCw1MSwxNDUsMTk4LDE1MiwyMzQsOTYsNzIsMTg1LDUwLDIzMSwyNywxNDUsMjA3LDIxMSw0Miw0MCw4NiwzMSwyNTAsODYsODgsMjM2LDE5NywxNjksMTc2LDE2NSwxMzAsMjM0LDI0NCwxODQsMTU1LDE4NiwyLDE5OSw1NSw2OSw1NywyMDAsMTI4LDE1LDE5LDc4LDIzMSwyMTMsMTcyLDIwLDIxNCwzNCwxODksMjEyLDE4Myw0LDExLDE0MiwxODgsMTYwLDQ5LDE1NywyNywyMSwxNTQsMTY0LDE3OSwyMjAsMTY2LDE3NCwyMDYsMTMxLDExLDY0LDgwLDI4LDMxLDEyMSwxMTAsMTgxLDEyMiwxMzMsMjI1LDE0NywxNzksOTgsODksMTc5LDY2LDE2OCw0MiwyNDAsODcsMjA1LDE4NywxOTUsMTEwLDE5OSwyMjAsMTMsMTgzLDQ0LDE5LDEsMTUxLDU5LDE4MCwxNjEsMTkxLDEwMywxMjMsMTMsMjU1LDE2OCw3MCw4NCwyNiwyMDQsMTMwLDg2LDM4LDEzNiwyMywxMzQsMTU4LDEwMSwxMjgsMjUzLDIwMiwxMzksMTYyLDIxNCwzMCwxOTEsMTEyLDEyMCwyMDIsMTUwLDE3NywzLDE2NSw4MywyNDAsMTY0LDE2NiwyNDYsMTUyLDE0NywxOTksODQsMTUsMjAyLDIzNCwzMCw3NSwzOCw3Myw5OCw2NywxNjQsMTIwLDU0LDcsMTIyLDUzLDcxLDM3LDI1LDE5Nyw3NSwxODcsMTIsMTg5LDE5Nyw0MSwxOTAsMzIsMTIzLDIxNCwxMzksMjA1LDE3MiwyNDcsMTMzLDE1MCw3LDE5NSwxMjYsODAsMTIwLDIwMSw5OCwxNDIsMjE1LDg1LDIwMCwyMzQsNzUsNTAsMjE0LDQ5LDEwNiwyMTYsNTgsMTksNjgsMjEzLDczLDM3LDE0OCwxMzQsMjAxLDIzNSwxMDIsMTkxLDc1LDE4MCwyMTQsNTUsMTg3LDE5OSw4NiwyOSwwLDcsMjAwLDEzMywxMzUsNDIsMTg5LDE4MCwxNzgsMTQyLDM3LDEyMCwxMjgsMzYsNzAsMjM0LDEzMiw1OSw0NiwyMTcsMTQyLDgzLDg2LDY2LDE5MywxNjgsMTQyLDE5OCwxMDMsNjUsMTM5LDI0MywxNTEsMjksMTM5LDYzLDYzLDE4OCwxMjIsNDIsMzUsNjksMTUwLDk5LDExMSwxNDYsMTg0LDI1LDM5LDkwLDMzLDEyNiwyNDksMTg5LDEyNywxNzYsNjEsMTE1LDEwNSw5OCw5MSw1MywxMzUsMTE0LDEyMiwxNjAsODIsNzMsMTY1LDU3LDY4LDExMCwyMDgsMTI0LDEyNSwyLDE2OSwxMTgsMTE5LDg0LDM3LDEyOCw4NiwxNTAsNTEsMTA1LDg3LDI1NSwzMywyMDMsMjAwLDIzMSwxMDMsMTI5LDUyLDY0LDE4OCw4Miw1OCw2NiwxNjMsNTcsMjExLDEyMCwxMTAsMTg2LDE4LDg1LDEzMSwyMTgsMTksMTcsOCw3OCwyMDUsMTc3LDU2LDIyMywxMjAsOTIsNDMsMTk2LDcsMTI3LDE1Niw0Miw4MywxMDcsMTAsMjIzLDEzMCwxMCwyNDAsMjMzLDE4OSwxNSwxOTgsNDAsMjQwLDExOCw1LDQ1LDY1LDIxLDk1LDE4NywxNzksMTA2LDEzMiwxMTMsMTkyLDEwLDMwLDEzMywxNjQsMjEsOTMsOTIsMTU4LDE3MCwxMzksMTI2LDI0OCwyNDgsMTMxLDIyNywxODIsOTQsMTU5LDQsMzcsNTQsMTk5LDE4NywxNDUsMTE3LDEzMywxNyw1Nyw1NSwyMTIsMzUsMTEyLDE4NSwxMzEsOSwyMDksNjQsNzQsMTQ2LDIxNyw1MiwyMiwyMjQsNDksMTk1LDg3LDE3NCw2MCwyMSw4OCw3NiwyMDgsODcsMTk1LDIyNiw3MywyMDEsMjI5LDEyOCw2OCwyMzYsODQsMjI3LDI0MiwxNjQsNzksMTk2LDEzNCwxMTgsNjcsMTI4LDIsMTAsMTcsMjI2LDIwLDEyNiwxMzcsMywxODYsMTAxLDE0NCwyMDAsMTg5LDE1MywxMTQsMTYxLDE5OSw1LDkyLDE0OCwxMTIsNTQsMiwxNDQsNjAsMjE4LDAsMjMsMjMyLDYwLDI0MywyMTIsMTc1LDAsMzcsMjUsNDcsMjEyLDExMCwyMTYsNDgsNCwxNDcsMjI0LDU0LDE0LDcwLDIwMCwyMzMsMjEyLDU2LDE0LDIxNCwxMzUsNzksODEsMTUwLDExNiwxMzMsMyw3NSwyMzIsMTg4LDI1MywyMjAsMTM5LDIwMiwxNDQsNTYsODIsNTIsMywyNSwyMjgsOTcsOTgsMjUyLDIwMSw3NiwxNDAsMTksMTQ0LDk4LDEwMiwyMDIsMTM2LDE4Nyw1MCwxOTIsMTAzLDQ0LDg2LDk3LDEyNywxMjAsMTIsODgsMjM2LDE2Myw4MCwyMTQsMTg0LDIyMCwyMTUsNDAsMTM5LDExNyw3MSwyMTksMTgxLDExNyw1MSwyMTIsMTI0LDEwMiwxMTMsMTAxLDMxLDE0Niw0NiwxOTEsMjAxLDk5LDYsMjI4LDEyMiwyMjgsNzEsMjA3LDkwLDkyLDU0LDcwLDI1MCwxNjEsMjIxLDEzOSwxMTEsNDYsODUsMTgwLDE2NSw3LDU2LDEyNSwyMSwzNCw2NywxMTksMTEyLDE2NSwxMSwxOTcsMTAwLDY1LDY3LDIyMSw1OSwyMzQsMTg5LDE5NSwyMDMsMTYyLDE1OSwyMDMsMTk5LDE1OCwxODIsMTY1LDEsMTkyLDE1NSwxMTUsMjksMTc5LDIzMywzNSwyMTUsOTUsMTksMjI0LDIsMTksODMsMjE2LDE0MywxNDIsMTM0LDE1Miw2MiwyNTEsMTU0LDM1LDIwLDEwNywxMiwxNDcsMTUxLDc4LDE1Myw5Myw4NywxNDUsMzgsMjYsMTg1LDE0Myw5NCw2NiwyMTEsNSwyMiwxNTYsMzgsMTI1LDE2OCw1Miw1NCwyMDcsMTcxLDExMSw1OSw5NSwyMDgsMjE5LDIzNSw5LDMwLDU3LDE0MiwxOCwyNTQsNjMsMTg5LDE2MCw3OCwxMTYsNTAsMjA0LDIzNiwyMCwxNTcsMTM5LDEwNSwxNCwyNTAsMjI0LDE5OSw0OSw4Miw2NiwxNjAsMTgxLDMyLDE0MSwxMjYsMjIwLDIxMyw3NywxODMsMjQ2LDI1MywxOTUsMjE0LDM3LDE5NCwxODUsOTgsMTQ1LDEyNCw2MiwxOCw1Miw0NiwyMjUsMSwxMCw0NCwxMjAsMTQzLDI0MCwxMiw4MSwxOTgsMTgzLDE4MCwwLDU4LDI0OCwzOSwzOSwyNTMsODQsNTAsOTQsMjU0LDIyNSwyMTIsMTUsMTAsODgsMjUzLDIxMCw5LDI0NiwzNywyMjQsMTY1LDE4NSw5LDYsMzMsMF19fX0seyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxMTYsMTY2LDYyLDEyOSwxNzcsMTYzLDEyNCwyMTQsMTA4LDgwLDg2LDE0NSwyOCwxNDQsNjQsMTg1LDQsODksNjAsMTE2LDMzLDE5LDIyNSwxNTcsNzAsMTIzLDEzMyw1MywxODAsODksNjcsMTQ5LDQzLDY2LDE4MywxNDMsNDcsMzQsMTgwLDg3LDE2LDExLDExMCwyNTEsNjEsMjAyLDIzMywxNTMsOCwxMDIsOTEsMTk4LDQ5LDU5LDE2MCwxOTgsODYsNTgsMjQwLDE1OCwxNTksODEsMjA3LDk0LDIwOSwxMjAsMjI4LDg0LDU1LDIzOSwyMiwxOTcsMTAzLDIzNCw3NywxOTgsMjQxLDQ3LDksODAsMTYyLDE4NiwxNTQsMTgyLDI2LDEzOSw4NiwyNDEsMjAsMTA0LDE2OSwyMTksMTM3LDIwNyw4LDE4LDE1NCwxNiwyNSwxMTksMTQ2LDE4NiwxNTMsODksMiw5LDEwNCw0MiwzNiwxNiwzNywyMSwxOTUsOTUsMzksMTk2LDE1NCwxODUsNzQsMTMyLDI0Niw4OSwxNDUsMTIwLDIyNywyMSw2MiwxODIsMTAyLDIzMiwyNTIsMiwxNzksMTY5LDQyLDE0NSwxMDMsMTkxLDIwNSwyMzQsMTE2LDE5MCwxMDgsMTQyLDk5LDU5LDQ1LDEwMSwxMjEsOSw2MywxNjcsMTk0LDg0LDEwMiw0OCwyMywyMTAsMTE4LDIyNywxNTIsMTM1LDE2Myw0Myw0Miw4NCwyNDQsMzcsMjA1LDEzNSwxNTEsMTE1LDE1MCw4OCwxODAsMjMzLDE0Niw2OSwxOSwyMDcsNjYsNDIsMTIzLDE3MiwyNDgsODgsMTgzLDE2OSwzMCwzNSw1Miw3Niw4NSwxNTMsMTU4LDksMTE1LDkwLDI1MSw5OSwxNSwxNjQsMTYyLDIzLDg2LDE1MCwxNzIsODUsMTAxLDEyOSwyNDEsMTM0LDE5OSwxMjQsMjUsMCwxOTAsMTQ2LDM0LDcxLDI0LDk2LDM4LDIxLDYwLDE5MSwxNDAsMTczLDI1MiwxMTMsOTUsNjAsNzUsNDksNTksMjQ0LDIwMywyNDEsMTI5LDY5LDIzNSwyMiwzMiwxNSwyMzYsMTkwLDEyMiwxMDEsNjQsMTExLDcsMTcxLDIyOCw1NywxMjEsMTE0LDIwMywxMywxMDMsOSwzNCwxNSwzNiwyOSwyMjIsMjI3LDE5OCwxNTQsMzMsMTExLDEzNywxNjgsMTE4LDEwNSwyMzEsMTAxLDYxLDM1LDE4MCw3OCw4OSw0MywxNDAsOTAsMjYsNTAsMjEsMTQzLDI0MSwyMTIsNzcsMTMsNDgsMTcxLDMzLDE2MywyMDMsMjA4LDE4MSwxOTQsMTA4LDgyLDEyNSwxMTcsODIsNjksMTMsMTQ4LDgzLDQ1LDY1LDY3LDExMSwxOTcsODYsMTAzLDE2NiwzMCwyNTMsMjUyLDQ5LDM1LDU3LDE4Myw3NSw5MSwxNDEsMTg2LDkxLDE3MCwxNDcsMjUxLDE2Myw0MiwxODEsMTc0LDIyOCwyNCwyMywxNzcsMjE1LDc2LDIwNiwyMTksNTIsMjUyLDg2LDkyLDY0LDE2OCwyMiw3OCwxODcsMTQ0LDc4LDg4LDEwMSwyMTgsMTQ2LDEwNSwxNjYsNiwxMzQsMTU2LDY2LDE5MSwxODUsMTk5LDE4NiwxMiw1LDk2LDEyNSw2NCw0NCwxNDIsMTIsMTcxLDEyOSw4NCw3Niw3MSwyMTYsOTYsMzMsMTQ1LDUyLDI1NSwxNDYsOTEsMTM1LDU1LDE5NywzMywyNDQsMTYyLDY4LDk3LDQ4LDY3LDg5LDEzMywyMDMsMTg2LDEwMCw4MywxODUsNzAsMTE4LDU5LDE4OSwyNDgsMjEzLDE0Miw5NSwxOTYsMTEyLDE3OSwxNDgsMTg3LDcsMTA4LDI4LDIzNSwxMzIsMSwyMDAsMjI4LDE4MywxNjQsMTM1LDE2LDgyLDI4LDE2NiwzMCwyMzEsMTMzLDg1LDI1MCwxMzgsODksMTk2LDExOCwxNzIsNjUsMTkwLDIzMiw4LDEzLDU4LDgzLDcyLDIzOSwxMDMsNDYsOTYsMzYsNTUsODksMTg3LDM2LDI1LDIxNiwxMDQsMjAwLDM0LDc1LDIyMCwyMzQsMTEyLDIyMywwLDEyNiwxMDYsMjE2LDE5MiwxNDksNDMsODYsMjAsMTcxLDExNSw4MywyMDMsMTIsNDksMTg0LDgyLDIzOCwyMTMsMTQwLDU0LDE1MSw5LDkzLDE3Nyw4Miw1NCwxMjEsMzIsMTI1LDMyLDExMSwxMTksMTg1LDEwMSwxMzQsMjQxLDE0NCwyMjcsMjE2LDI4LDM4LDIyOCw1LDM0LDExNCwxMTEsMjksMjA5LDIwNywxMjUsNDgsMzksMjMxLDEyOSw3OCwyMjksOTEsNzQsNSwyNTIsMTYwLDE2NiwxNDgsMjAsNDgsMTQ3LDIwMiwxOTksMjQ3LDE3MCwyMzEsMTI5LDgzLDIyNCwxNjcsNDksMjM0LDEzNiwxNjMsMzQsMjI4LDExOSwxNTAsMTYsNTgsMTg2LDE5OCwxMzMsNCw2OCwxNTksMTc1LDgxLDE0MCwyNDMsMjQsODAsNzksMjQ0LDEwOCw4MCwxMDMsMTQzLDE2MSw4OCwyMDMsMjI1LDE2MywxNjQsMTk3LDUsODIsMjcsMTM4LDY5LDI0MCwxODYsOTEsMjMzLDEzMSwxNTgsMjAyLDQwLDkwLDgxLDE2MCw4MiwxNDMsMTM3LDE2NCwxMzEsMjAsMTczLDE1MywyNSw5NSw4MCwyNiwxODEsMzgsMTQ4LDU0LDE5NiwxMDQsMjMsMjM3LDEzNiwxNjMsMjE4LDE3LDI3LDY2LDIyOCwxMjcsMjExLDI1MCwxNDksMTY0LDg1LDEwOSwxNjksMTg2LDIwMCwxLDI1MSwyMDEsMTAzLDEyMywxODMsMTU1LDg3LDk0LDE4NywyMzUsMTc4LDk4LDk3LDIwNiwyNSwxMDUsMTYxLDIwMiwyMzEsMTIzLDY2LDEzOCwxNCwxNzcsMTE0LDE2MSw1OSwxNzcsNDAsMTMsMzksMzAsODQsNDgsMTg3LDIzMywxMzEsMTg2LDIwNCwxNjcsOTEsMjIzLDI0MywxOTMsOTIsMjEzLDE5MSwxNCwxNDcsMTcsMTM3LDQsMzYsOTYsNTYsNDUsMjcsMzUsMjAxLDE5MCwxOTMsMjQsMTc3LDE1NCw4NiwxOTcsMTU2LDE2OCwxNjgsMTE1LDMxLDEyNSwyMjksMzUsODAsODksOTksMjM3LDI1MSwxOTAsODIsMSwxMzMsMTUwLDIxNCwyMDUsODcsOSwxOTQsMjM0LDIzNCw4NiwxNjgsMTQ0LDE5MCwyMDEsOTcsMTEyLDE0MSw3NCw3NiwxNDAsOTYsNSwxMTcsNjYsNywyNTUsNDAsMTU5LDQyLDQzLDExOSw4NCw1LDk3LDEsMjE2LDcxLDExLDE2NywxNDUsNjUsMjUxLDUwLDEwMCw1MiwxNzAsNCwxOSwxOSwxMTksMTQwLDEwLDEwLDg1LDE3LDg0LDM3LDEwOSwyNDAsMTk0LDIzLDE2NiwyMzUsMTMxLDc2LDE4NiwzNywxMDQsMjE3LDI5LDIyNywxNTUsNjAsMTA5LDc1LDEwMiwxNywxMjksMTU0LDIyNSwyMiw5OSwxODIsMTcwLDU2LDI1LDIzMSwxNTEsNzksMTYxLDUwLDEwLDIyOSwzLDQwLDE5NiwyNiwxNzgsMTM0LDE0NSwxNywxNTUsMTE2LDE0MSwyMCwxNzIsMTQxLDE2OSw1LDQwLDIwOSw2MiwzNSwzNiwxNCw3NiwyMzYsMTk0LDI0MiwxMTYsMTgwLDIzNiw0Miw4NCw5MSwxMTgsMTM5LDIyNSwxNzksMTg4LDAsMTA1LDEzLDk5LDE4Myw1MywyMjIsMjEzLDEzMSwyMzIsMjI1LDk2LDE5Myw3MSw0LDEwNCw2LDE2NSwxNzksMjM1LDQ5LDIwNywxMTUsMzgsMTIzLDE4NSwyOSw2NSwxODEsMTc2LDE4NSw3MCwxNjksMTgxLDI0MSwyMiw0MSw5MCw2NiwxMjMsODksMTYxLDMwLDE0OCwxMTUsMTI0LDE5OCw2NCw2NiwxMDMsMTAxLDUyLDE5NywxOTAsMTMzLDEwMCwxMzksMTQyLDgzLDk3LDI1MiwxLDEzMiwyMzAsMjM0LDE4MiwyMjEsMjEsODksMTA1LDEyNCwxMDgsMjIwLDE5MywxOTYsMTU1LDE1NiwxNjEsMTUxLDI0MSwxMTcsNjQsMTIwLDIwNiwzOCwyNDEsOTMsMTA2LDIxMiwyNSwxNDAsMTU0LDI3LDE4MSwyNDEsMTAxLDIxMiwzNCwxMzksNTEsNyw5OSwzNCwxNjYsNjAsNTMsMTgyLDc4LDE5MSw3MywxNTUsMjUxLDU2LDE3MSw1MywxOTcsMzYsNDIsMTk5LDE0OCwyNTAsMTk2LDE5MiwxMzUsMTM0LDEwMiwyNTQsMjgsMTE3LDEyNiw4Niw1LDE1Nyw1MywxNjUsMjM4LDEzNSwxNzgsMTgxLDM5LDc2LDE0LDM0LDgwLDYsMjI3LDE2MiwxMjAsMTMwLDEwMywxNywxODgsMTc3LDU0LDk4LDE0MCwxOTUsMTc4LDY3LDE2MCwxNjcsMTQsMTUyLDQ5LDE2Myw2NywyNTAsMTUyLDIwOCwxMTYsMzMsMjQ3LDE4MiwxMjIsMTE2LDEyMywxNjksMTk0LDE2NCwxMTIsMTU2LDc0LDE0OCwxNDgsNjcsMTI1LDExOSwzOSwxMTksMTI0LDU1LDEzMywyMTAsNTksMTI1LDkxLDE4NywxMTQsMTQ1LDcxLDE3MiwxODEsNTEsOTEsMTYxLDU3LDU1LDE0OCw4MSw0OSwxNzQsMTA4LDE1MywyMDYsMjQyLDc4LDIzOSwxOCwxMzAsMzIsMjExLDEyOSwyMjIsNjQsOCwxMjgsMTkzLDEyOCwxNiwxMDYsODUsMTUwLDMsMjAyLDc0LDE2MywyMDIsOCwxOTcsNjMsMTMzLDIwNCwxODQsMTQ4LDE1MywxODksNjIsMTY4LDI2LDE4MywyMTksMTUwLDIwMCwyMTEsNTQsMjQ4LDc1LDExMSwyMzcsMjAyLDE2LDE5MywyMjUsMTkyLDE4Miw1OSwyMDIsMzYsMjMyLDE4MCwxOTcsMTMwLDEyMywxNTksMTU0LDEyMywyMTYsMjQwLDE2MSwyMTIsMTQ1LDExMCwxNTAsODAsMTE4LDIwOCw5MCwxMDYsODEsNzIsMjAsNjksNjAsMjgsMTc3LDkwLDE2NCwxODksMzksMCw4Myw4Miw5OCwxMzAsODAsNDUsMTc3LDc5LDE3NywyMzUsNDEsMTQ0LDI1LDMyLDIyLDEyMiwzMSwxNDUsMjE3LDE2MywxMDEsMjIwLDEsOTgsMTA4LDE5NiwxNzEsMTg2LDEzMiwyOSwyNDAsMjI1LDExMiw0LDY2LDk2LDE0OCwxODQsMTY0LDM2LDIyMywzMCwxMSw2OSwxMDUsNzcsMTk2LDE2NywxNiwxNDksMTg1LDI0NiwxNTMsMTI5LDE4MCwxMjAsMjQ0LDE1NiwxMiwxMzEsOTcsMjQyLDIyNyw3NCwxNDUsNzksN119fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6Wzc1LDk4LDQzLDU2LDE2Niw2MiwxNDgsNjAsMTU1LDIwOSwxOTUsMTY5LDQxLDI0OSwxOTYsMTk3LDcsMTQ5LDE1NCw0MSwxMiwxMDksMjE1LDQyLDE5Myw1MiwxMDQsMTU1LDcyLDQxLDI5LDU2LDc5LDE3MiwxOTQsMTkwLDE2OSwyNDAsNDksMTg0LDE2MSwyNCwzOSw0OSwzOCw0OCwxMjEsMTUxLDMzLDY0LDgzLDExOSwyMzYsMzcsMTk1LDEwMiwxMDQsMjEyLDEyMCwxODksNjMsMTUxLDQyLDcxLDIyNiwxODAsMTM3LDQxLDgsMzksMTAxLDE1OCwxNCwxOTcsNjQsOTAsNzAsNjYsMjUsMjAzLDE2NSwzNSwxNTQsNTYsNDMsODcsMTEyLDE5OSw1OCwxNTUsMjcsMTQ3LDExMiw5NSwxNzksMTEyLDc3LDE3Miw2NiwzMiwxMzYsNiwxNSwxMzUsMTQzLDc1LDEyMiwxOTQsMjA5LDQxLDIwNyw5LDcsOTYsMTE0LDIzMCwxMzgsMTQwLDI1MCw3MiwxNjQsMTU0LDEzOSwxNzYsMTA0LDQ5LDEwMywyNywyOCwxNDAsMiwxMzMsMTIyLDEwNSwxMDgsNjAsNTQsMTYyLDE3MCwxNzksMTkyLDIzMiwxODUsMTQ2LDM5LDIyOCwzMCwyMDIsMTQ4LDE0Myw0OSwyNywxMTcsMTkzLDIyOSwxMzIsMjUwLDEwOCwxNTEsNzEsMjQwLDE2MCwzNywxMjQsMTk0LDIwNywyNDEsMTEwLDE2OCwyMjcsNSwxOTEsMTAzLDg4LDI0OSw5MCwxMTIsMjQ3LDEzOCw2Niw0NSw2LDExLDE1NSwxMjIsNTUsMTcyLDIxOCwxNTIsMzUsODIsMTI5LDExMSwyNDgsMTE4LDk1LDE5OCwxMzcsMjM2LDI3LDM0LDIxMyw3NSwxMzksMjAsMjI5LDEyLDkzLDE1MCwxMjUsNzIsOTYsMTI4LDIwMCwxMDcsMjcsNDQsMjIwLDE4NSw0NCwxMTQsODcsMSwxMzYsNTMsMTM1LDUyLDE0LDk1LDE2NSwxMzYsMjI2LDE3MCwxMDYsMTcwLDE1NSwxMDQsMjExLDE5LDY1LDU2LDE0NSwxNjUsMjUzLDE0OCw5NiwxNCwxODQsMTk0LDI1NSw3MSwyMCwxMzMsNzUsMjAsNzgsMTU0LDE1MCwxNDgsMTMzLDEwNCw5MSw4OCwxMzAsMTE5LDQyLDUxLDI3LDI0MSwxMzAsMjQ0LDIzMCwxMTAsNzEsNTAsMTIwLDIzMCw1NywyOCwxNTQsMTYzLDE5LDQ5LDIzMiwxNDksNTEsMTQ5LDI1LDE3MiwyNCwxNzksNjQsNzYsNywzNiwyMjQsMTMwLDEyNSwxOTMsOTcsMTQ1LDEsMjEsMzMsMTgwLDI5LDIyNCwxNTQsMTc2LDExNywxMDIsMTYwLDg1LDI0NywxMTEsMjUwLDIzNCwyMDUsMjMwLDg2LDIwNywyNTEsMzgsOCw2MywxNjgsMTY0LDI1MiwxMzQsMTM2LDE4Miw3MCwxMjYsNjcsMTEyLDEzNCwxODksNDMsMTg3LDIyNiwxODgsOTcsODQsMTY0LDIwNCw5MCw3MCw2NywyNDYsMTQwLDEyMSwxOTQsNjgsNDYsODEsMjMwLDEzNiwxNjcsMTc3LDY3LDIzNiwxNjAsMTcwLDEzMiwxMTcsMTUyLDE0MSwxMTQsMTg1LDIwMCwxMzQsMTcsMjIsMjE1LDYzLDIzNywxMzgsMTMsMTY0LDExNyw4MCwxNTcsMTYyLDEwNCwxOSwyMTUsMTQwLDgsNTAsMTkzLDQ5LDI0OCw2OSwxOTUsMTc5LDE1NiwxNDcsMjcsMzYsMTQ3LDE5Nyw3NywyMDMsMjUxLDE1Miw2MSwyMzUsNDMsMTk3LDEwLDE5MCwyMjgsNTgsMTcwLDE2OSwyNDcsNjgsMTIzLDIyNSwxNzMsMTUyLDE3MCwxNjMsMjUyLDEzMCw2LDM5LDE5MywxODMsMjA3LDE2LDE2OCwxMDEsMCw1MCwyMTEsMjM2LDI1LDEzMSwxNTQsNDIsNTYsMjQ5LDkxLDEzNCw1MCw5LDE0MywyMDksMjUsMTA5LDEwNCwxODMsMjA5LDY5LDQ3LDEzNCw5OCw4NSwxNDAsMTM4LDE5OSwyMjEsMjE3LDEwMiwyOSwxNTUsNTUsODgsMTQ1LDEsMTI0LDIxMSwxNzksMTM2LDIxNSwxNDQsMTYwLDIzNCwxMjIsNzksMTg0LDE2MSwxMzUsMzUsMTczLDEwMCw4LDE1NCw3MywxMjQsMTMyLDE1MCwxMDEsNDYsMjE4LDIwNCwxOTQsMTc5LDU2LDYyLDEyMCwxNDYsMTc2LDIzNyw1MywxMTksOTAsNjgsMTksMjM3LDIwLDE2NiwyMCw5OSwxMTMsMTg2LDk2LDY0LDI0OCwyMzUsMTQsMjA1LDEzOCwxMDgsMzEsMTAsMTI1LDI0NCwzOCwxNTUsMTEwLDc0LDQ0LDk5LDg3LDk2LDEzNCwzNiwxMiwxNTEsNzMsMTksMTM1LDE5MiwyMDQsMjAyLDEzNywxNzYsMzcsNTcsMTcyLDEwMywyMTMsOTUsNjksMTQ0LDE4OCwzNywyNCwyNCw0MywxNDUsMTgzLDE3MCw2LDI4LDE0MCw3LDExMiwxNTYsMTIwLDEyMCwxODMsMTIsMTkxLDIyOSw4MywzMywxNjksMTE2LDM0LDEyNiwxMDIsNTYsMTU3LDEyMSw0NCwxOSwyMDAsMjEsMTAsNTMsMTgzLDI2LDIxOSwxMTYsMjA1LDE2OCwxNjcsMjU1LDcsMTc3LDE5Nyw0MCwxMywyNTUsMTg3LDU3LDExLDE4OCwxNzUsNTksMjQ4LDg2LDI1MCwxMiwxNSwxNjMsMjE3LDE1NiwxOCwxNjMsNDgsMTI3LDEzOSwxMDIsMTkwLDE3MCwxOTIsMSwxODAsMTY2LDEwNiwxOTgsMzIsMTc3LDk5LDkwLDE4MiwyNDIsNjAsMTUyLDE2LDE3NiwxNTAsMTg0LDEwMSw3Nyw3MCwxNzYsMjgsMTc3LDkwLDEwLDI1MCw2MCwyNDMsMTksMTIzLDE2OSw5MCw0OCwyNTIsMjQyLDE4MCwxNTcsMTcsNTcsMTMzLDcwLDExOCwxNjIsMTY3LDksMTczLDEzNiwxNzUsMTE3LDIyNCwyMyw0Niw0MiwxNzIsMTUyLDE2MCw5NiwxNjAsMjAwLDE1LDU2LDEzNCw1MSw3MSw4NCw5MCw5NSw3NSwyNywxOCwxNDcsNDgsMTIzLDg4LDE5NiwxODAsODcsNTcsNjcsMTgxLDgsMjI3LDgwLDEwLDIwMCwxNTYsMjEsMjE5LDI1MiwxMDksMTUwLDEwMCwyMyw0NSwxODYsMTk2LDIzNywyMjgsNjAsMjAyLDE2NywyMyw3NiwxODIsOTcsMjMsMTM2LDUwLDg1LDE2OSwxMTcsMjE1LDgzLDIxLDEzMCwxNTEsMjAxLDE2NiwxNjUsMTc2LDEwMCwyMTIsNTMsOTIsMTQ1LDExMSw2OSwxNjYsMzgsMjQ4LDIzLDI2LDI1MCwyMTYsMTgxLDI0NywxOTMsMjA2LDExNCwyMTYsMTMyLDEzMywxMDgsNTAsMTU2LDU5LDI5LDE4NSwxMiwxNjEsODEsMTMxLDIwMSwxNjQsMTUxLDE4LDE0OSw3NiwxMjMsMTMyLDI1LDk1LDE0LDExNCwxNTksMTkzLDI1MCwxMDAsMTE1LDE3MSwxNDAsMTI2LDg5LDg2LDE1NSwyMTgsMTcyLDE2MSwyMTEsNDMsNjUsMTAxLDksMTQxLDI0NiwyMDUsNTIsMjQ3LDE1MSw5Myw3MywxNDksMTMwLDIyOCwyMDUsNTIsMSwyOCw3Nyw3MiwyMDcsMjI2LDExNCwzNCwxNzcsMTg3LDk4LDgsMTg3LDE0MywxNCwxOTgsODYsNDcsMTAyLDEyMSwyMDgsMTQ4LDc2LDk3LDc0LDQ3LDEzNSwxNTEsMTYxLDE4NCwzOCwxMzYsMjI0LDEwNiwxNzYsMTcyLDExNCwxNDUsMjMzLDE1Niw5OCwxNDMsMTg4LDEyMCwxNDksNTcsNywxOTYsMTkzLDM0LDE3OCw5MSw0NSw4MSwyMzIsNDQsMTE4LDI0Niw3NSwxNzQsMTc4LDk1LDc5LDEyMiwxMTQsMjU0LDYwLDE1Nyw4OSwyNiwxOTIsMTIwLDEwLDczLDIwNSwxMTcsMTUxLDEyNiwxOTcsOTMsMTMzLDIwOCwxNzksNTIsMzYsNzEsMjEwLDI0NywxMjYsMTI5LDUyLDMxLDk0LDE3MCw4LDE2MSwyMjUsMTAyLDE3MSwyLDIyLDEzOSw5Niw3LDEwNCwxODYsNDAsMTExLDI3LDEwMywyNTIsNDIsMTc3LDkyLDcxLDEyOCwyMjgsMjUyLDE5MywxNTgsMTEsNjAsMTMzLDQwLDEwMiwxNzQsNDksMTg5LDE1NCwxNzksOTEsNTgsMTU2LDExLDI0NCwxNTMsMTAxLDczLDEsMzIsMTU0LDE4OCwxMTUsNzYsMTQ0LDEwOCwxNywyMTMsOTIsMjE2LDIxLDkxLDIzOSwyMzEsMTg2LDk5LDQ4LDE3MSwxMTMsOTEsNTUsMjAyLDEzMiwxNDEsMTQ1LDEwNCwxNDgsMzksMzksMTMsNDMsMjQ2LDE0MSwxNTUsMjM1LDEyLDQsMjE4LDE4Niw5NywyLDE4LDIyOCwxMzIsMTU0LDE3LDIyMCwxMjMsMjA4LDM4LDIwNCwzOCw4OSwxODEsNSwyMzMsNzIsMjQ4LDE1Niw4MiwyMTMsMTcxLDIsMTUyLDU5LDExMSw4MiwxNjUsNTYsMTkxLDksNTAsMjQ5LDE2Niw5NywzMiw0OCwxMjgsMjE1LDg4LDgzLDExOCwzNywxMjYsODcsMTY4LDE2OCw4MywyMzQsMTkzLDE2MiwyMSw2OSwxMDMsMTMzLDE4LDk3LDEwMiw0MSwxOTUsMTg3LDc3LDY0LDE2MiwyOCwyMSw4LDUwLDk0LDEwMSwxNjAsMTQzLDEzOCwxLDEwNywxMTcsMTE3LDE2OSwyMCw2NywzMCwyMzAsMTE1LDIzMiwxODgsNDYsODMsMTAyLDIwMiwyMzcsMTY1LDU3LDExOSw3LDE1LDE5MiwyNTEsNzIsMjE5LDQwLDUyLDE2Niw1MCw5MywzNiwxNTIsMjAxLDI0Niw4MSwxMTMsNzQsNzQsMTMwLDYsMTY0LDEyLDExOCwyMzMsMzcsMzgsMjQ2LDU4LDM3LDEyMSwxMDIsNzgsMCwyMiwzNywxOTMsNDEsMTM5LDEwMCwxODksNDYsMTk5LDE4NSwyMDcsNTYsMTMwLDEyMSw4LDAsMjA5LDcyLDYxLDExNiwxNjYsNjIsMTI5LDE3NywxNjMsMTI0LDIxNCwxMDgsODAsODYsMTQ1LDI4LDE0NCw2NCwxODUsNCw4OSw2MCwxMTYsMzMsMTksMjI1LDE1Nyw3MCwxMjMsMTMzLDUzLDE4MCw4OSw2NywxNDksNDMsNjYsMTgzLDE0Myw0NywzNCwxODAsODcsMTYsMTEsMTEwLDI1MSw2MSwyMDIsMjMzLDE1Myw4LDEwMiw5MSwxOTgsNDksNTksMTYwLDE5OCw4Niw1OCwyNDAsMTU4LDE1OSw4MSwyMDcsOTQsMjA5LDEyMCwyMjgsODQsNTUsMjM5LDIyLDE5NywxMDMsMjM0LDc3LDE5OCwyNDEsNDcsOSw4MCwxNjIsMTg2LDE1NCwxODIsMjYsMTM5LDg2LDI0MSwyMCwxMDQsMTY5LDIxOSwxMzcsMjA3LDgsMTgsMTU0LDE2LDI1LDExOSwxNDYsMTg2LDE1Myw4OSwyLDksMTA0LDQyLDM2LDE2LDM3LDIxLDE5NSw5NSwzOSwxOTYsMTU0LDE4NSw3NCwxMzIsMjQ2LDg5LDE0NSwxMjAsMjI3LDIxLDYyLDE4MiwxMDIsMjMyLDI1MiwyLDE3OSwxNjksNDIsMTQ1LDEwMywxOTEsMjA1LDIzNCwxMTYsMTkwLDEwOCwxNDIsOTksNTksNDUsMTAxLDEyMSw5LDYzLDE2NywxOTQsODQsMTAyLDQ4LDIzLDIxMCwxMTgsMjI3LDE1MiwxMzUsMTYzLDQzLDQyLDg0LDI0NCwzNywyMDUsMTM1LDE1MSwxMTUsMTUwLDg4LDE4MCwyMzMsMTQ2LDY5LDE5LDIwNyw2Niw0MiwxMjMsMTcyLDI0OCw4OCwxODMsMTY5LDMwLDM1LDUyLDc2LDg1LDE1MywxNTgsOSwxMTUsOTAsMjUxLDk5LDE1LDE2NCwxNjIsMjMsODYsMTUwLDE3Miw4NSwxMDEsMTI5LDI0MSwxMzQsMTk5LDEyNCwyNSwwLDE5MCwxNDYsMzQsNzEsMjQsOTYsMzgsMjEsNjAsMTkxLDE0MCwxNzMsMjUyLDExMyw5NSw2MCw3NSw0OSw1OSwyNDQsMjAzLDI0MSwxMjksNjksMjM1LDIyLDMyLDE1LDIzNiwxOTAsMTIyLDEwMSw2NCwxMTEsNywxNzEsMjI4LDU3LDEyMSwxMTQsMjAzLDEzLDEwMyw5LDM0LDE1LDM2LDI5LDIyMiwyMjcsMTk4LDE1NCwzMywxMTEsMTM3LDE2OCwxMTgsMTA1LDIzMSwxMDEsNjEsMzUsMTgwLDc4LDg5LDQzLDE0MCw5MCwyNiw1MCwyMSwxNDMsMjQxLDIxMiw3NywxMyw0OCwxNzEsMzMsMTYzLDIwMywyMDgsMTgxLDE5NCwxMDgsODIsMTI1LDExNyw4Miw2OSwxMywxNDgsODMsNDUsNjUsNjcsMTExLDE5Nyw4NiwxMDMsMTY2LDMwLDI1MywyNTIsNDksMzUsNTcsMTgzLDc1LDkxLDE0MSwxODYsOTEsMTcwLDE0NywyNTEsMTYzLDQyLDE4MSwxNzQsMjI4LDI0LDIzLDE3NywyMTUsNzYsMjA2LDIxOSw1MiwyNTIsODYsOTIsNjQsMTY4LDIyLDc4LDE4NywxNDQsNzgsODgsMTAxLDIxOCwxNDYsMTA1LDE2Niw2LDEzNCwxNTYsNjYsMTkxLDE4NSwxOTksMTg2LDEyLDUsOTYsMTI1LDY0LDQ0LDE0MiwxMiwxNzEsMTI5LDg0LDc2LDcxLDIxNiw5NiwzMywxNDUsNTIsMjU1LDE0Niw5MSwxMzUsNTUsMTk3LDMzLDI0NCwxNjIsNjgsOTcsNDgsNjcsODksMTMzLDIwMywxODYsMTAwLDgzLDE4NSw3MCwxMTgsNTksMTg5LDI0OCwyMTMsMTQyLDk1LDE5NiwxMTIsMTc5LDE0OCwxODcsNywxMDgsMjgsMjM1LDEzMiwxLDIwMCwyMjgsMTgzLDE2NCwxMzUsMTYsODIsMjgsMTY2LDMwLDIzMSwxMzMsODUsMjUwLDEzOCw4OSwxOTYsMTE4LDE3Miw2NSwxOTAsMjMyLDgsMTMsNTgsODMsNzIsMjM5LDEwMyw0Niw5NiwzNiw1NSw4OSwxODcsMzYsMjUsMjE2LDEwNCwyMDAsMzQsNzUsMjIwLDIzNCwxMTIsMjIzLDAsMTI2LDEwNiwyMTYsMTkyLDE0OSw0Myw4NiwyMCwxNzEsMTE1LDgzLDIwMywxMiw0OSwxODQsODIsMjM4LDIxMywxNDAsNTQsMTUxLDksOTMsMTc3LDgyLDU0LDEyMSwzMiwxMjUsMzIsMTExLDExOSwxODUsMTAxLDEzNCwyNDEsMTQ0LDIyNywyMTYsMjgsMzgsMjI4LDUsMzQsMTE0LDExMSwyOSwyMDksMjA3LDEyNSw0OCwzOSwyMzEsMTI5LDc4LDIyOSw5MSw3NCw1LDI1MiwxNjAsMTY2LDE0OCwyMCw0OCwxNDcsMjAyLDE5OSwyNDcsMTcwLDIzMSwxMjksODMsMjI0LDE2Nyw0OSwyMzQsMTM2LDE2MywzNCwyMjgsMTE5LDE1MCwxNiw1OCwxODYsMTk4LDEzMyw0LDY4LDE1OSwxNzUsODEsMTQwLDI0MywyNCw4MCw3OSwyNDQsMTA4LDgwLDEwMywxNDMsMTYxLDg4LDIwMywyMjUsMTYzLDE2NCwxOTcsNSw4MiwyNywxMzgsNjksMjQwLDE4Niw5MSwyMzMsMTMxLDE1OCwyMDIsNDAsOTAsODEsMTYwLDgyLDE0MywxMzcsMTY0LDEzMSwyMCwxNzMsMTUzLDI1LDk1LDgwLDI2LDE4MSwzOCwxNDgsNTQsMTk2LDEwNCwyMywyMzcsMTM2LDE2MywyMTgsMTcsMjcsNjYsMjI4LDEyNywyMTEsMjUwLDE0OSwxNjQsODUsMTA5LDE2OSwxODYsMjAwLDEsMjUxLDIwMSwxMDMsMTIzLDE4MywxNTUsODcsOTQsMTg3LDIzNSwxNzgsOTgsOTcsMjA2LDI1LDEwNSwxNjEsMjAyLDIzMSwxMjMsNjYsMTM4LDE0LDE3NywxMTQsMTYxLDU5LDE3Nyw0MCwxMywzOSwzMCw4NCw0OCwxODcsMjMzLDEzMSwxODYsMjA0LDE2Nyw5MSwyMjMsMjQzLDE5Myw5MiwyMTMsMTkxLDE0LDE0NywxNywxMzcsNCwzNiw5Niw1Niw0NSwyNywzNSwyMDEsMTkwLDE5MywyNCwxNzcsMTU0LDg2LDE5NywxNTYsMTY4LDE2OCwxMTUsMzEsMTI1LDIyOSwzNSw4MCw4OSw5OSwyMzcsMjUxLDE5MCw4MiwxLDEzMywxNTAsMjE0LDIwNSw4Nyw5LDE5NCwyMzQsMjM0LDg2LDE2OCwxNDQsMTkwLDIwMSw5NywxMTIsMTQxLDc0LDc2LDE0MCw5Niw1LDExNyw2Niw3LDI1NSw0MCwxNTksNDIsNDMsMTE5LDg0LDUsOTcsMSwyMTYsNzEsMTEsMTY3LDE0NSw2NSwyNTEsNTAsMTAwLDUyLDE3MCw0LDE5LDE5LDExOSwxNDAsMTAsMTAsODUsMTcsODQsMzcsMTA5LDI0MCwxOTQsMjMsMTY2LDIzNSwxMzEsNzYsMTg2LDM3LDEwNCwyMTcsMjksMjI3LDE1NSw2MCwxMDksNzUsMTAyLDE3LDEyOSwxNTQsMjI1LDIyLDk5LDE4MiwxNzAsNTYsMjUsMjMxLDE1MSw3OSwxNjEsNTAsMTAsMjI5LDMsNDAsMTk2LDI2LDE3OCwxMzQsMTQ1LDE3LDE1NSwxMTYsMTQxLDIwLDE3MiwxNDEsMTY5LDUsNDAsMjA5LDYyLDM1LDM2LDE0LDc2LDIzNiwxOTQsMjQyLDExNiwxODAsMjM2LDQyLDg0LDkxLDExOCwxMzksMjI1LDE3OSwxODgsMCwxMDUsMTMsOTksMTgzLDUzLDIyMiwyMTMsMTMxLDIzMiwyMjUsOTYsMTkzLDcxLDQsMTA0LDYsMTY1LDE3OSwyMzUsNDksMjA3LDExNSwzOCwxMjMsMTg1LDI5LDY1LDE4MSwxNzYsMTg1LDcwLDE2OSwxODEsMjQxLDIyLDQxLDkwLDY2LDEyMyw4OSwxNjEsMzAsMTQ4LDExNSwxMjQsMTk4LDY0LDY2LDEwMywxMDEsNTIsMTk3LDE5MCwxMzMsMTAwLDEzOSwxNDIsODMsOTcsMjUyLDEsMTMyLDIzMCwyMzQsMTgyLDIyMSwyMSw4OSwxMDUsMTI0LDEwOCwyMjAsMTkzLDE5NiwxNTUsMTU2LDE2MSwxNTEsMjQxLDExNyw2NCwxMjAsMjA2LDM4LDI0MSw5MywxMDYsMjEyLDI1LDE0MCwxNTQsMjcsMTgxLDI0MSwxMDEsMjEyLDM0LDEzOSw1MSw3LDk5LDM0LDE2Niw2MCw1MywxODIsNzgsMTkxLDczLDE1NSwyNTEsNTYsMTcxLDUzLDE5NywzNiw0MiwxOTksMTQ4LDI1MCwxOTYsMTkyLDEzNSwxMzQsMTAyLDI1NCwyOCwxMTcsMTI2LDg2LDUsMTU3LDUzLDE2NSwyMzgsMTM1LDE3OCwxODEsMzksNzYsMTQsMzQsODAsNiwyMjcsMTYyLDEyMCwxMzAsMTAzLDE3LDE4OCwxNzcsNTQsOTgsMTQwLDE5NSwxNzgsNjcsMTYwLDE2NywxNCwxNTIsNDksMTYzLDY3LDI1MCwxNTIsMjA4LDExNiwzMywyNDcsMTgyLDEyMiwxMTYsMTIzLDE2OSwxOTQsMTY0LDExMiwxNTYsNzQsMTQ4LDE0OCw2NywxMjUsMTE5LDM5LDExOSwxMjQsNTUsMTMzLDIxMCw1OSwxMjUsOTEsMTg3LDExNCwxNDUsNzEsMTcyLDE4MSw1MSw5MSwxNjEsNTcsNTUsMTQ4LDgxLDQ5LDE3NCwxMDgsMTUzLDIwNiwyNDIsNzgsMjM5LDE4LDEzMCwzMiwyMTEsMTI5LDIyMiw2NCw4LDEyOCwxOTMsMTI4LDE2LDEwNiw4NSwxNTAsMywyMDIsNzQsMTYzLDIwMiw4LDE5Nyw2MywxMzMsMjA0LDE4NCwxNDgsMTUzLDE4OSw2MiwxNjgsMjYsMTgzLDIxOSwxNTAsMjAwLDIxMSw1NCwyNDgsNzUsMTExLDIzNywyMDIsMTYsMTkzLDIyNSwxOTIsMTgyLDU5LDIwMiwzNiwyMzIsMTgwLDE5NywxMzAsMTIzLDE1OSwxNTQsMTIzLDIxNiwyNDAsMTYxLDIxMiwxNDUsMTEwLDE1MCw4MCwxMTgsMjA4LDkwLDEwNiw4MSw3MiwyMCw2OSw2MCwyOCwxNzcsOTAsMTY0LDE4OSwzOSwwLDgzLDgyLDk4LDEzMCw4MCw0NSwxNzcsNzksMTc3LDIzNSw0MSwxNDQsMjUsMzIsMjIsMTIyLDMxLDE0NSwyMTcsMTYzLDEwMSwyMjAsMSw5OCwxMDgsMTk2LDE3MSwxODYsMTMyLDI5LDI0MCwyMjUsMTEyLDQsNjYsMTAyLDExOSwxNDIsMjI2LDI0Myw0MCwxODQsMjUxLDEzOCw5MywyMDksMjIyLDIxNSwyMDgsMTEwLDI0MiwxNiwyOSwyNTIsNTIsMjIyLDkxLDg1LDE0Niw1Niw2OSw2NiwyMTgsNDYsOTEsMzksMjQsMjQ1LDU3LDI0OCwyMTQsNiwzOCw3OCw2NCw5MCwxMzcsNjMsNzIsMjksMjI0LDEyMCwxMzksMzgsMjksMjI0LDM1LDEwMCwxMDYsNzAsMTMzLDEzLDExLDEzNywyNDQsMTQwLDIzNSwyNCwyMjEsMTQxLDE3LDE1Niw0NiwxMCwxOTMsMjI4LDE2OCwxMzAsMTgxLDIzMywxNywxNDMsMTI5LDU1LDEwOSwyMCwzNSwyMjYsNDksMTY4LDEzNywxNDcsOTUsMTcwLDEzMCwyNDAsNjAsMTM5LDEwMywxNzQsMTE2LDk2LDE0OCwxODQsMTY0LDM2LDIyMywzMCwxMSw2OSwxMDUsNzcsMTk2LDE2NywxNiwxNDksMTg1LDI0NiwxNTMsMTI5LDE4MCwxMjAsMjQ0LDE1NiwxMiwxMzEsOTcsMjQyLDIyNyw3NCwxNDUsNzksN119fX1dAAAAAAAAAFgAAAAAAAAADUdyb3VwU3RhdGV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAEiT3BlcmF0aW9uYWwiAAAAAAAAAFIAAAAAAAA6f1RyZWV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7InRyZWUiOnsibGVhZl9ub2RlcyI6W3sibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxMzQsMTY5LDEwNCwyMDcsMTM0LDE0NSwyMCwyNyw5OCwyMTQsNjcsNjQsMTAwLDkyLDkxLDc5LDE2OCwxNjgsMjgsMjA4LDc0LDIzMiwxMSwxNzcsODcsMjUxLDI1LDEsMjUwLDE3MSwyMDksMTYsMTYzLDE3NSwxNDAsNDEsOTIsMTUyLDIyLDgwLDgwLDEwNyw2OCwyNTEsMywxNTksMjA5LDEyNSw0OCwxNjUsMTA4LDIzNiwyMCwxMSw1NCwxMDAsMTg4LDIyOCwxNDQsMTc2LDE4NiwyMjAsMTQ3LDEwOCwxNiw5Myw5MywxNzYsNDMsMjEyLDY1LDM4LDE0NywxNjAsOCw3MiwyNywxMDQsMjI0LDI0MywxMzMsMTQyLDEwMSwyMDYsMjA0LDE5Nyw2Niw4OCwyNCwxNDMsMTA1LDEzNywxMDMsMCwxOTYsNDksMjQxLDIwNCw1Myw4NywyMiwxMjcsMTg4LDEzMiwzOCwxMTIsMTY4LDUwLDk2LDQ0LDY3LDI0NCwxMzksMTExLDE2NSw1NCw1MywxODYsMjE5LDg0LDEzMSwxMSw4MiwxOTAsOTYsMTgsMjMwLDE5Myw5NywyMDksODAsODUsNjQsNzMsNTQsOTMsMTU1LDE4OCwxNCw2LDM1LDE4MiwxMzIsMTcwLDExNiwyMzAsNDIsMzMsMjM1LDg1LDEyNiwzOSwxMDUsMTM5LDkyLDU3LDYsMTg2LDg5LDE1Nyw4Myw0MCwyMDcsMTc5LDE2MSw2MCwxNDAsMjA2LDE5OCwxNzEsMTE4LDIzMiwxMzQsODgsNjksMTQwLDg5LDEzNSwxMTcsNjIsMTU1LDI1MCw5NSwxNTIsMjI2LDMwLDMwLDExNCwxOTIsOTgsMjE3LDExNiw3NiwxNjgsMCwyMjUsMSwxNTcsMTkwLDE3MiwxMDMsMTQxLDkyLDE1NSwxOCwxMTcsNTgsMTMwLDE4MSwxOTYsMjcsNTgsNzksOTksODQsOCwxODksMTk0LDEyOCw1LDExNCwyMSwxNSw5MiwxODksOTUsMTMzLDE4MiwxNDksMTg2LDUyLDI0OSwzOSw3Miw4LDIxLDEwOSwxNCw1MywzNywxNzgsMTk2LDExNCwxNzksMTcxLDY5LDE4OSw0MSwxNTUsMTQsODMsMTU3LDE3MCwyNDQsMzksMTQ4LDI0NCwxNzAsODAsMTg2LDE1NSwxMDAsMjQzLDE4OSw1MCwxNzcsMTQ1LDIyNSw4NCwxOCwyLDcsMTQ5LDIxOCw4NCwxNzYsMTkyLDUwLDExOSw3NiwxMDAsMCw2NywyOCwyMDYsMjI4LDE0OSwxMDEsMTc3LDI0NywxOTksOTgsMTU0LDgsMjQ2LDIyMCw1MCwxLDExOCwxNjcsNjAsNzIsMTUwLDIyOSw1OCwxODksNDUsMjIwLDExOSwyMTgsNDAsMjgsMTg2LDE4LDE3NCwwLDIwOSwxNDAsMTcxLDE2MywxNCw5NCw5MiwxMzgsMjE3LDc1LDE0MCwxNzAsMjI3LDIwMCwxMjcsMTU1LDQ4LDE4MSwxMiwxMzMsMjI1LDI0OSwxNjAsNDEsMjA0LDI2LDEwMSwyMTksMCwxODQsMTU0LDIwNSwyMzMsMTk2LDE0MiwyMzIsMTIzLDMwLDIzNywyMTAsOTMsNjQsODMsODYsMTUwLDIwLDE4OSwxMDcsNTUsMjQsMTYsODQsMjMsMTE1LDE0NSw4LDExMSwyNyw0LDE4NCw1Niw0NCwyNDYsMjA4LDEwNiwxMTUsOTEsMTU5LDI0NCwxMjMsMTk2LDI3LDIxNiw5NywxNTAsMjA4LDIsMjUyLDYwLDE3OCwxOTAsMzksNiwyMiwxNDcsMTYwLDMzLDI0NSwyMSwxNjMsMjE2LDUsMTksMTM2LDE1NiwxMDYsMTU2LDEyNywyMzEsMTAyLDIwNywzNSw5NiwxMjksMTE0LDIwOCw4NiwxODAsMTE2LDExOSwxMjYsNzQsMTIsNjcsNiwxMzMsNDEsMTE4LDc2LDAsMjYsMjA2LDYsNSwxMDMsMTUxLDIwNCwxMzYsMTY0LDM4LDY2LDE0LDkyLDEyNSwxNjMsNTMsMTUxLDEzMCwyMzUsMTg0LDE1OCwxNDksMTYxLDIzOCwxOTksNzEsMjUxLDE4NywzNSwxODgsMjQsMzEsMTYzLDIzNSwxNjMsNjAsNjAsMTMwLDc5LDE1NCwyNSwyMTcsNjQsMTk1LDE3NCwxMzksODUsMTk1LDIwOCwxMzksNzMsNjAsMTk2LDEyOSw2NCwyMDMsMTY2LDEyOCwxMzksMjA0LDE4LDEyNywxMzgsMjI5LDc3LDE2OCwxNjEsMTkzLDIwNywxMDUsMCwxODAsMTA3LDE3Miw2NSwxMTcsNjMsNzQsMzYsMTY1LDE0LDUxLDEyNCwxNiwxMDcsMjMsODIsMTQ1LDEwNiwyNDcsMTQ0LDQ0LDE5MywxMjAsMywxMDQsMTY4LDQxLDE4OCwzNCwyMDYsMjUxLDE3MCwyMDUsMjAzLDExMiwxNzEsMTYxLDEzMyw3NiwxNDgsMTM4LDM0LDgwLDQxLDE2OCwxMjgsMjMsMjAzLDgxLDE0Nyw1MCw1MSwxNDUsMTk4LDE1MiwyMzQsOTYsNzIsMTg1LDUwLDIzMSwyNywxNDUsMjA3LDIxMSw0Miw0MCw4NiwzMSwyNTAsODYsODgsMjM2LDE5NywxNjksMTc2LDE2NSwxMzAsMjM0LDI0NCwxODQsMTU1LDE4NiwyLDE5OSw1NSw2OSw1NywyMDAsMTI4LDE1LDE5LDc4LDIzMSwyMTMsMTcyLDIwLDIxNCwzNCwxODksMjEyLDE4Myw0LDExLDE0MiwxODgsMTYwLDQ5LDE1NywyNywyMSwxNTQsMTY0LDE3OSwyMjAsMTY2LDE3NCwyMDYsMTMxLDExLDY0LDgwLDI4LDMxLDEyMSwxMTAsMTgxLDEyMiwxMzMsMjI1LDE0NywxNzksOTgsODksMTc5LDY2LDE2OCw0MiwyNDAsODcsMjA1LDE4NywxOTUsMTEwLDE5OSwyMjAsMTMsMTgzLDQ0LDE5LDEsMTUxLDU5LDE4MCwxNjEsMTkxLDEwMywxMjMsMTMsMjU1LDE2OCw3MCw4NCwyNiwyMDQsMTMwLDg2LDM4LDEzNiwyMywxMzQsMTU4LDEwMSwxMjgsMjUzLDIwMiwxMzksMTYyLDIxNCwzMCwxOTEsMTEyLDEyMCwyMDIsMTUwLDE3NywzLDE2NSw4MywyNDAsMTY0LDE2NiwyNDYsMTUyLDE0NywxOTksODQsMTUsMjAyLDIzNCwzMCw3NSwzOCw3Myw5OCw2NywxNjQsMTIwLDU0LDcsMTIyLDUzLDcxLDM3LDI1LDE5Nyw3NSwxODcsMTIsMTg5LDE5Nyw0MSwxOTAsMzIsMTIzLDIxNCwxMzksMjA1LDE3MiwyNDcsMTMzLDE1MCw3LDE5NSwxMjYsODAsMTIwLDIwMSw5OCwxNDIsMjE1LDg1LDIwMCwyMzQsNzUsNTAsMjE0LDQ5LDEwNiwyMTYsNTgsMTksNjgsMjEzLDczLDM3LDE0OCwxMzQsMjAxLDIzNSwxMDIsMTkxLDc1LDE4MCwyMTQsNTUsMTg3LDE5OSw4NiwyOSwwLDcsMjAwLDEzMywxMzUsNDIsMTg5LDE4MCwxNzgsMTQyLDM3LDEyMCwxMjgsMzYsNzAsMjM0LDEzMiw1OSw0NiwyMTcsMTQyLDgzLDg2LDY2LDE5MywxNjgsMTQyLDE5OCwxMDMsNjUsMTM5LDI0MywxNTEsMjksMTM5LDYzLDYzLDE4OCwxMjIsNDIsMzUsNjksMTUwLDk5LDExMSwxNDYsMTg0LDI1LDM5LDkwLDMzLDEyNiwyNDksMTg5LDEyNywxNzYsNjEsMTE1LDEwNSw5OCw5MSw1MywxMzUsMTE0LDEyMiwxNjAsODIsNzMsMTY1LDU3LDY4LDExMCwyMDgsMTI0LDEyNSwyLDE2OSwxMTgsMTE5LDg0LDM3LDEyOCw4NiwxNTAsNTEsMTA1LDg3LDI1NSwzMywyMDMsMjAwLDIzMSwxMDMsMTI5LDUyLDY0LDE4OCw4Miw1OCw2NiwxNjMsNTcsMjExLDEyMCwxMTAsMTg2LDE4LDg1LDEzMSwyMTgsMTksMTcsOCw3OCwyMDUsMTc3LDU2LDIyMywxMjAsOTIsNDMsMTk2LDcsMTI3LDE1Niw0Miw4MywxMDcsMTAsMjIzLDEzMCwxMCwyNDAsMjMzLDE4OSwxNSwxOTgsNDAsMjQwLDExOCw1LDQ1LDY1LDIxLDk1LDE4NywxNzksMTA2LDEzMiwxMTMsMTkyLDEwLDMwLDEzMywxNjQsMjEsOTMsOTIsMTU4LDE3MCwxMzksMTI2LDI0OCwyNDgsMTMxLDIyNywxODIsOTQsMTU5LDQsMzcsNTQsMTk5LDE4NywxNDUsMTE3LDEzMywxNyw1Nyw1NSwyMTIsMzUsMTEyLDE4NSwxMzEsOSwyMDksNjQsNzQsMTQ2LDIxNyw1MiwyMiwyMjQsNDksMTk1LDg3LDE3NCw2MCwyMSw4OCw3NiwyMDgsODcsMTk1LDIyNiw3MywyMDEsMjI5LDEyOCw2OCwyMzYsODQsMjI3LDI0MiwxNjQsNzksMTk2LDEzNCwxMTgsNjcsMTI4LDIsMTAsMTcsMjI2LDIwLDEyNiwxMzcsMywxODYsMTAxLDE0NCwyMDAsMTg5LDE1MywxMTQsMTYxLDE5OSw1LDkyLDE0OCwxMTIsNTQsMiwxNDQsNjAsMjE4LDAsMjMsMjMyLDYwLDI0MywyMTIsMTc1LDAsMzcsMjUsNDcsMjEyLDExMCwyMTYsNDgsNCwxNDcsMjI0LDU0LDE0LDcwLDIwMCwyMzMsMjEyLDU2LDE0LDIxNCwxMzUsNzksODEsMTUwLDExNiwxMzMsMyw3NSwyMzIsMTg4LDI1MywyMjAsMTM5LDIwMiwxNDQsNTYsODIsNTIsMywyNSwyMjgsOTcsOTgsMjUyLDIwMSw3NiwxNDAsMTksMTQ0LDk4LDEwMiwyMDIsMTM2LDE4Nyw1MCwxOTIsMTAzLDQ0LDg2LDk3LDEyNywxMjAsMTIsODgsMjM2LDE2Myw4MCwyMTQsMTg0LDIyMCwyMTUsNDAsMTM5LDExNyw3MSwyMTksMTgxLDExNyw1MSwyMTIsMTI0LDEwMiwxMTMsMTAxLDMxLDE0Niw0NiwxOTEsMjAxLDk5LDYsMjI4LDEyMiwyMjgsNzEsMjA3LDkwLDkyLDU0LDcwLDI1MCwxNjEsMjIxLDEzOSwxMTEsNDYsODUsMTgwLDE2NSw3LDU2LDEyNSwyMSwzNCw2NywxMTksMTEyLDE2NSwxMSwxOTcsMTAwLDY1LDY3LDIyMSw1OSwyMzQsMTg5LDE5NSwyMDMsMTYyLDE1OSwyMDMsMTk5LDE1OCwxODIsMTY1LDEsMTkyLDE1NSwxMTUsMjksMTc5LDIzMywzNSwyMTUsOTUsMTksMjI0LDIsMTksODMsMjE2LDEyLDgxLDE5OCwxODMsMTgwLDAsNTgsMjQ4LDM5LDM5LDI1Myw4NCw1MCw5NCwyNTQsMjI1LDIxMiwxNSwxMCw4OCwyNTMsMjEwLDksMjQ2LDM3LDIyNCwxNjUsMTg1LDksNiwzMywwXX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzE2NCwxODAsMTI1LDEyNiw3OSwyMjAsMjIxLDI1MywxMjMsNDIsMjAxLDEyMCwxODAsMTY0LDIwMCwyMDgsMTc3LDk5LDgyLDYwLDU1LDE1NiwxMjYsNzcsMTE3LDExNSwyMDMsMjAzLDE0MiwyNTUsMTY2LDM0XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTcsMTA4LDEwNSw5OSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IkNvbW1pdCI6eyJ2ZWMiOlsxNjUsMjQwLDIyMiwxNDUsMTkxLDE5OSwyMDEsMjA4LDc2LDIwMywxNjIsMTc5LDE0MiwyMzUsMTE3LDIyNywxMzQsNTYsMTE1LDM5LDgyLDUzLDE1NCw0OSwxNzcsMjE5LDg5LDIwOSwxODQsMywxMzksMjA5XX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzEzMiwzMCwzOSw4Myw2OSwxMDcsMTg5LDM2LDEwMSwxMDQsMTcwLDIyMCwxMzQsMjUxLDQxLDQ1LDE2NCw5NiwzOSwyNDIsNTcsNTksMTY4LDgzLDg0LDE2MywxMzksMTg5LDIzMSwxMjgsMTI3LDE5NSwxOTAsMTU0LDEyMiw1NCwxMjYsMTU3LDEzMCw1OSw3MCwxNDQsMTAxLDMyLDE3MCwyNSwyMDgsMjA0LDI0NywxMzQsMTU1LDIwOSw5OSwxMTUsNTQsMTM3LDIzLDEyOSwyMTksMTQzLDE1MiwxNTYsMTAwLDEzXX19fX0seyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzcwLDE4MCwxMzgsNzEsMjQyLDEwOCw2MywyNDAsMTg5LDE5OSw0MSwxMTAsODksODksMTk1LDE0NSw4OSw3NCwzMywyMTMsMTQsMTY3LDkxLDEwMywxNjMsNjYsNywyMTAsMjM2LDksNTAsMjQwLDEwMCwxNTMsMTAxLDI4LDg1LDksMTYsMTYzLDE3NywxMjAsNjQsMjQ3LDExLDksMTMzLDk1LDE1OCw4OCw2MiwxNDUsMjUyLDEwOSwxMzQsMjMwLDkyLDIzMiwyMDQsMTAxLDIxNyw2MCwxNjksMTI2LDkyLDIwMywyMzgsODYsMjEsMjMzLDg0LDQ1LDE1NSwxMjksNDIsMTM3LDE1MCw1OCwxMDgsNDEsMTYxLDExMywxNDgsMTc1LDg2LDE1NiwxNiw5OCw3NSwxMzgsMTIyLDcsMzMsMTM1LDkxLDc3LDI0MiwyMywxOTEsMjQzLDQ4LDksMjUsMTAwLDQ0LDI0OCw3MywxMjQsNzEsNzQsMTQ3LDIyMCw0OSw0MywyMjgsNTEsMTg2LDEyMCw5NiwxODksMTc1LDM4LDExNSwxMDgsMTEyLDE2MiwxMDMsNTgsNDcsOTQsMjQ2LDQ4LDI0Niw2NiwxMDksMCw2Nyw2NywyMjksMTQ3LDY0LDEzNywzLDE2MywxMTksMTc5LDYzLDEwNSwyMzEsNDYsMTgwLDk4LDEzMSw4OCw0OCwxNDYsMTA0LDIwOCwxMzQsNiwyMCw1LDcsMjMzLDE0NiwyNDgsMTM1LDExNSwxNjcsODEsNzMsMTE5LDEzNyw4MSw5MCwxNzcsNDMsMTY3LDIyLDQ5LDIxLDI1LDExNiwzMywxMzAsNjIsMTEwLDIxNywxOTUsMTU2LDEwLDE3MiwxMzAsODYsNTgsMjEzLDU4LDEyOCw1Miw2NywzMywxNiwyMTksODMsOCw0LDE4MCwxMTksMTc2LDE5NywxMDcsMyw3OSwyMTYsMjQ0LDEwOCwyNDIsMzksNjQsMTY3LDI0OSw0NiwyMzksMTM2LDE2MSwxODAsMjI4LDE2NywxOTMsNSw0MSwxNTYsMTQ5LDEzOCwyMjAsMTQ3LDE0NywxMDcsMTI4LDY1LDgzLDE4MiwxMzIsMjUxLDIzNSwxNjAsMTk0LDIyOCw3OSwxNTIsMTQ1LDE3MCwxNDYsMTIyLDE2LDIwNiwxNzgsNjEsODQsMjE5LDIxLDksMjUxLDE4OCwyMTcsNjcsMzUsMTQ3LDIzMiwyNyw4NiwxNiwxMTAsMTMzLDE1Myw2MywzMCwxOTMsMjAxLDg4LDIzNSw3MiwyNTQsMTY3LDIxLDkzLDg1LDUsNjIsMjQ3LDc2LDE0LDg3LDEwNCwxNzYsMTY0LDEzLDE1NCwyMTUsMTA0LDEzNiwxMzIsMTQyLDU2LDIzMSw5NiwxNTksMTY4LDI0LDE2NywxMDEsMTQyLDEyNyw5MiwxMywyMzYsNjUsNTUsMTAwLDE3NiwxNDAsMTY5LDY3LDY4LDY4LDcyLDExNCwxOTcsMjExLDExOSw5MiwxMzgsMTk3LDIxMCwxOTksMTMyLDE2Niw1MCw1NCwxNDYsMTE1LDQwLDM4LDE3LDY4LDIzNiw0Myw4NSwxOCw4OCwxNDksMjQyLDgyLDgyLDIwMCw5NywxODAsOTcsNTEsOCwxMTcsMTk4LDExNywyMDEsMTYwLDU1LDExNiwxOTMsMTQwLDE0OCwxNyw0LDYyLDUzLDExNywyMTgsNTQsNzcsMTY3LDIxNCwxMSwyMDEsNDEsMSwxNSwxODYsMTcxLDIyNyw0NCwzNiwxMSwxOTksMjA0LDM2LDI0LDE3OSwyMjAsNTMsOTksMTgwLDEzNCwxNiwxNzgsMjI4LDQyLDE3OCw2OSwxNTEsMjA3LDEyMCw4MSwyMDIsMTE4LDE2MywxMDUsMjA0LDU0LDU5LDE5MywxMDQsMjAwLDE2OCwxNzYsNzMsMjUwLDY3LDI0NSwxNzIsMjksNzMsMTAxLDExMCwzOSwxMDQsMjUsNTksMTMzLDkyLDc1LDEwMSw4NSwxMzksMTE4LDE3NSwzNSw3LDEwNywyMTQsMTIsMTU0LDI0Niw1NiwyMCwxMDksNTMsNDgsNzksNzIsMTEyLDE1Niw4LDkxLDI1MiwzMiw3MSwxNzcsMjQzLDEzOSwxODQsMTk2LDEzNSw3Miw2NywxMjEsNDYsMjQ0LDEzMCwxMjEsMjQ4LDE1NSwyMjUsMiwxMDgsMTY4LDUwLDEwOCwxLDEwMiw1Nyw1OCwxNTAsMTI2LDEyMCwxMDUsNDgsOCwxOTksMTAsMTYwLDE4MSwxMzIsMywxMjMsNTMsMTYyLDE3LDE5MywyMDIsMTg4LDE0OSwyMjUsMjYsODMsMTMyLDExNSwxMDcsMTg2LDExMyw5OSwyOSwyNTIsNTQsMjQ3LDE1MywxNjUsMTQ4LDE0NywzOSwzMCw0OSw2Niw5NywxNjIsMTMwLDQsMTA1LDE2MywyMDAsNzMsMjA2LDc0LDE4Myw2Miw4NiwxNjYsMTg4LDEwMywyMjAsNTgsMTgwLDE2Miw1MCwxMTksMjA5LDExNSwxNjksNjQsMTMwLDEwLDkwLDQ3LDc0LDgsMTYwLDM3LDY0LDExOCwxOTMsMTUwLDEyLDQxLDI1MSwxMSw3NSwxNjQsMTMyLDE5NywyMzYsMTQ2LDIzNiwxMzQsMTMyLDI0MCwzOSwxMDYsMjQ0LDI0NSwyMCwxMzQsMjI1LDgxLDI1LDE0MCw3MiwyNTIsMTM3LDI4LDUyLDEzMSwxNjUsMTcyLDEwOCwyMDMsMjgsMjA4LDc3LDIyMCwxODMsNzYsMjA4LDczLDM4LDIzNiwxODAsMCwxOTksMTE5LDM4LDM5LDQ5LDYwLDQzLDIxNywyOCwxNiwzNiwxODAsMjM1LDExOSwxMjksMTcwLDcxLDE4OSwxNzcsMTM5LDcxLDE1MiwxMjAsMTc2LDEyNiw1OCwxMTQsMTMyLDczLDUwLDE5Niw3LDE3NCw0NSw4NCwxMDMsNzAsOTEsMTgyLDEzNSw2OSw5NSwxODUsMjUwLDEzNSwxNjYsMTYyLDE0NywxOTQsMTk3LDExNCw5MiwyNyw5OCwyMzQsMjMyLDE0MywxMDAsMTQ4LDEzNCwyMDgsMjI3LDMwLDUzLDI0OCwxMTAsMTc1LDI0NCwxODYsMzIsMTI0LDcsMTY4LDI0MCw1MSwyMDMsMjQ5LDIyLDYsMTk0LDcwLDExMSwyMSwxMTgsMywxOTIsNDIsMTc3LDI0MCwxNiwxMTcsMTgxLDI3LDQxLDIwMiwxNSwzOSw2NiwxODEsMjAxLDY2LDg3LDE3OSwxMjksMTAwLDExNCwxNDQsNzcsMTI2LDkyLDIwMiwyMjAsNjQsMTE3LDE0MiwyOCwxMzIsMjE2LDI0OSwxNzcsNTEsMTUwLDEwMiwyMzksMTIzLDMwLDE5LDgwLDIwNCwxNjAsMjMsMTM3LDExNiwxNTYsMTI1LDEyOCwxMzEsOTksMTg4LDYsMjAsMTQ0LDE1MywyNCwyMjcsMjEzLDgwLDI0OSw0MSw2Nyw2NSwxLDQyLDI0NCwxNDYsMTI5LDE4Myw4MywxLDE5NCwxNjQsMTQsMTcxLDE4NCwxMjQsMTI4LDExNyw3MywxMTQsNDksMTc3LDE5Myw4NCwxNTUsMTUyLDAsNTgsNTksMjQ1LDEyMSwxMjcsMjEzLDEzMCwxMDYsMTc5LDcsMjE5LDE2OSwyOCw3MiwxMTcsNSwxOCw5MCwxODQsMjQwLDgyLDE3MCwyNTAsMjQ1LDYzLDQwLDE4Miw2OCw4NCwxNTUsMTc0LDUxLDE3NiwxMjgsMTAwLDExLDE1MSw4NSwyNTAsMTg2LDExNiwyNywyNSwxNTYsMTcxLDUxLDE2NCwxOTcsMjAzLDE4NywxODAsNDYsMTQ3LDIxNSw2NCw3MCwxMTYsMTYsMjE3LDIyOSw5OCw1MSwxNTAsMTQ3LDIxNiwxNzcsMTQ2LDE5MSwxNTMsMTQzLDM2LDIzLDMxLDIxOCwxOTksMTYyLDIwLDIxMiwxMCw3MCwxNjQsNTQsMTYsNjUsMjQsMTE5LDE3Niw0LDIwOCwxNTEsMTg4LDUxLDcxLDE4OSwxMDksMTY2LDE1NiwyMTEsMTU2LDE4NywxOTgsMTM4LDUzLDEzOSwxNTQsNDMsODcsMTUwLDQ5LDE3OSwyNDAsNTEsMTEwLDExOCwxMzIsMjEwLDkwLDEzMCwxNTQsNjQsMTMwLDIxMywyMjcsMTg2LDUxLDEwNywxOTEsOTEsMTU0LDQ3LDEzNCwyMDgsMjA1LDIxMywyMTIsMjA2LDUsMTI4LDE4NywxMDcsMTMzLDEwMSwxOTEsMTE1LDE4LDEzOCwxMDUsMjcsOTgsMjI1LDEzMywyMTgsODEsMzgsMTE5LDIxNyw0OCwxMzYsMjEwLDIwNCwxMjAsMTY3LDcyLDk3LDEzNSwxMDYsMjAzLDM0LDE2LDQ1LDE1NCwxOTAsOTIsNTEsNDYsMjA5LDk2LDE4MCwxNjgsMTAwLDQwLDI1MCwxOTIsMTQ3LDIxOSwxMDUsMTgzLDg3LDQzLDE1OSwxODUsMjQ3LDUzLDIzOCw0NCwxODAsNDYsMTgsMjgsMjE2LDE2NSwxMDEsMTYwLDIzMSw1Myw2MywxMTIsMTAzLDIxNywxODYsMTAyLDEyOSwxOCwxNDEsNzYsMTE5LDczLDIxMiw4MCwxNDcsMTU3LDk4LDEyNSwxMjIsMjAzLDU3LDI1LDE5NywxNSwxODcsMjE5LDMsMTI1LDgyLDEwMyw0MCwwLDQ3LDIyOSwyNSwxMTcsMjQ2LDIwLDE5MiwzLDIwMCwxLDE4MCwxNTAsMjA1LDIwMSw1NCwxMDYsMTgxLDExMyw0Myw4Myw0Myw1NCw3MiwxMDUsODAsMjUsNTksMzcsMTc1LDIxNCwxMjMsMzMsMTUzLDExLDIyMSwxOSwxMzEsMTMyLDI0MCw4MCw5NSwyNTEsNCwyNTQsMjUwLDgwLDEzMywxODcsMTgyLDExMiwyNDUsNzUsMTI0LDEwNywxMjEsNCwyNDAsMTk4LDI0MCwxNjIsNzIsMjI4LDI1MiwxMjAsNzcsMCw3MiwxODEsMTk4LDI5LDEwMiwzNywxODEsNDUsMTk3LDk3LDE1LDIzLDE4NCwxNjgsMTAyLDUxLDY0LDExNSwxMTMsMjM3LDIyNSwxMzgsODksMiwxMjgsMjM5LDE4MSwxNzUsMTAyLDIxMSwxNjAsNTQsMTgyLDM1LDIzMyw1MCw5LDEwNCwxMDAsMTMxLDI2LDg1LDQ4LDE1Nyw2Nyw2OSw3MywyMDMsMTI4LDQsMTcyLDE1OSwyNDMsNzQsMTQyLDE5NiwxNjksMTksNzksMTUyLDIsNiwzMCwxMzIsMTcxLDEwMywyMjEsNywyMTEsMTY5LDc4LDIzNSwyMDYsNjksMjgsMjUyLDEzLDY5LDEyNSw2NCwyNTQsMjQ3LDE3LDE2MCwyMCwyMDIsMTQ2LDAsMjMyLDAsMTIsMTcxLDk2LDc2LDEzMywxMzQsMjQzLDU5LDI0MCwxNTAsMTQ5LDk1LDIwOSwyMTksMTk5LDczLDE4OCwxMDUsMTAzLDEwMSwxODUsMjQ1LDEzLDk1LDIyMiwyMDQsMTYyLDQ1LDEzOSwzMiwxMDUsNV19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMzksMjM3LDI1MSwxNTIsMjMxLDE0MCwyNTIsMzMsMjM3LDE3NCwyNDIsMjQxLDEyNCwxMDMsMTMwLDI0LDc2LDEwNiwxNDUsMjI3LDAsNTEsNjgsMTEyLDE2MCw0Niw0Miw0OSw4MiwyMzMsNzcsMTE3XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NTAsIm5vdF9hZnRlciI6MTcyNzk1MDE1MH19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzIyMiwxNzksMywzMiw5NCwxMjcsNjYsMjQ5LDE4OCwxMDcsNzcsMTg2LDEyNiwxODksMTM1LDUyLDY4LDQwLDE3MiwxMTAsMTM1LDI0NCwyMDgsOTUsMTY3LDIyMywxNzcsMTgyLDE2MCw0NiwxLDQ1LDEwOCwxOTEsNzIsMTE5LDE5NiwyMiwxNTAsMTk0LDUzLDIyLDIwMyw5MSwyMjMsMjA0LDE1MSwxNDQsMTI2LDIwMywyNDksMTIxLDYyLDQ1LDQsMTQ0LDg5LDI3LDE4MCw4NCwyMzYsNjYsNTksMV19fX19XSwicGFyZW50X25vZGVzIjpbeyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxMTYsMTY2LDYyLDEyOSwxNzcsMTYzLDEyNCwyMTQsMTA4LDgwLDg2LDE0NSwyOCwxNDQsNjQsMTg1LDQsODksNjAsMTE2LDMzLDE5LDIyNSwxNTcsNzAsMTIzLDEzMyw1MywxODAsODksNjcsMTQ5LDQzLDY2LDE4MywxNDMsNDcsMzQsMTgwLDg3LDE2LDExLDExMCwyNTEsNjEsMjAyLDIzMywxNTMsOCwxMDIsOTEsMTk4LDQ5LDU5LDE2MCwxOTgsODYsNTgsMjQwLDE1OCwxNTksODEsMjA3LDk0LDIwOSwxMjAsMjI4LDg0LDU1LDIzOSwyMiwxOTcsMTAzLDIzNCw3NywxOTgsMjQxLDQ3LDksODAsMTYyLDE4NiwxNTQsMTgyLDI2LDEzOSw4NiwyNDEsMjAsMTA0LDE2OSwyMTksMTM3LDIwNyw4LDE4LDE1NCwxNiwyNSwxMTksMTQ2LDE4NiwxNTMsODksMiw5LDEwNCw0MiwzNiwxNiwzNywyMSwxOTUsOTUsMzksMTk2LDE1NCwxODUsNzQsMTMyLDI0Niw4OSwxNDUsMTIwLDIyNywyMSw2MiwxODIsMTAyLDIzMiwyNTIsMiwxNzksMTY5LDQyLDE0NSwxMDMsMTkxLDIwNSwyMzQsMTE2LDE5MCwxMDgsMTQyLDk5LDU5LDQ1LDEwMSwxMjEsOSw2MywxNjcsMTk0LDg0LDEwMiw0OCwyMywyMTAsMTE4LDIyNywxNTIsMTM1LDE2Myw0Myw0Miw4NCwyNDQsMzcsMjA1LDEzNSwxNTEsMTE1LDE1MCw4OCwxODAsMjMzLDE0Niw2OSwxOSwyMDcsNjYsNDIsMTIzLDE3MiwyNDgsODgsMTgzLDE2OSwzMCwzNSw1Miw3Niw4NSwxNTMsMTU4LDksMTE1LDkwLDI1MSw5OSwxNSwxNjQsMTYyLDIzLDg2LDE1MCwxNzIsODUsMTAxLDEyOSwyNDEsMTM0LDE5OSwxMjQsMjUsMCwxOTAsMTQ2LDM0LDcxLDI0LDk2LDM4LDIxLDYwLDE5MSwxNDAsMTczLDI1MiwxMTMsOTUsNjAsNzUsNDksNTksMjQ0LDIwMywyNDEsMTI5LDY5LDIzNSwyMiwzMiwxNSwyMzYsMTkwLDEyMiwxMDEsNjQsMTExLDcsMTcxLDIyOCw1NywxMjEsMTE0LDIwMywxMywxMDMsOSwzNCwxNSwzNiwyOSwyMjIsMjI3LDE5OCwxNTQsMzMsMTExLDEzNywxNjgsMTE4LDEwNSwyMzEsMTAxLDYxLDM1LDE4MCw3OCw4OSw0MywxNDAsOTAsMjYsNTAsMjEsMTQzLDI0MSwyMTIsNzcsMTMsNDgsMTcxLDMzLDE2MywyMDMsMjA4LDE4MSwxOTQsMTA4LDgyLDEyNSwxMTcsODIsNjksMTMsMTQ4LDgzLDQ1LDY1LDY3LDExMSwxOTcsODYsMTAzLDE2NiwzMCwyNTMsMjUyLDQ5LDM1LDU3LDE4Myw3NSw5MSwxNDEsMTg2LDkxLDE3MCwxNDcsMjUxLDE2Myw0MiwxODEsMTc0LDIyOCwyNCwyMywxNzcsMjE1LDc2LDIwNiwyMTksNTIsMjUyLDg2LDkyLDY0LDE2OCwyMiw3OCwxODcsMTQ0LDc4LDg4LDEwMSwyMTgsMTQ2LDEwNSwxNjYsNiwxMzQsMTU2LDY2LDE5MSwxODUsMTk5LDE4NiwxMiw1LDk2LDEyNSw2NCw0NCwxNDIsMTIsMTcxLDEyOSw4NCw3Niw3MSwyMTYsOTYsMzMsMTQ1LDUyLDI1NSwxNDYsOTEsMTM1LDU1LDE5NywzMywyNDQsMTYyLDY4LDk3LDQ4LDY3LDg5LDEzMywyMDMsMTg2LDEwMCw4MywxODUsNzAsMTE4LDU5LDE4OSwyNDgsMjEzLDE0Miw5NSwxOTYsMTEyLDE3OSwxNDgsMTg3LDcsMTA4LDI4LDIzNSwxMzIsMSwyMDAsMjI4LDE4MywxNjQsMTM1LDE2LDgyLDI4LDE2NiwzMCwyMzEsMTMzLDg1LDI1MCwxMzgsODksMTk2LDExOCwxNzIsNjUsMTkwLDIzMiw4LDEzLDU4LDgzLDcyLDIzOSwxMDMsNDYsOTYsMzYsNTUsODksMTg3LDM2LDI1LDIxNiwxMDQsMjAwLDM0LDc1LDIyMCwyMzQsMTEyLDIyMywwLDEyNiwxMDYsMjE2LDE5MiwxNDksNDMsODYsMjAsMTcxLDExNSw4MywyMDMsMTIsNDksMTg0LDgyLDIzOCwyMTMsMTQwLDU0LDE1MSw5LDkzLDE3Nyw4Miw1NCwxMjEsMzIsMTI1LDMyLDExMSwxMTksMTg1LDEwMSwxMzQsMjQxLDE0NCwyMjcsMjE2LDI4LDM4LDIyOCw1LDM0LDExNCwxMTEsMjksMjA5LDIwNywxMjUsNDgsMzksMjMxLDEyOSw3OCwyMjksOTEsNzQsNSwyNTIsMTYwLDE2NiwxNDgsMjAsNDgsMTQ3LDIwMiwxOTksMjQ3LDE3MCwyMzEsMTI5LDgzLDIyNCwxNjcsNDksMjM0LDEzNiwxNjMsMzQsMjI4LDExOSwxNTAsMTYsNTgsMTg2LDE5OCwxMzMsNCw2OCwxNTksMTc1LDgxLDE0MCwyNDMsMjQsODAsNzksMjQ0LDEwOCw4MCwxMDMsMTQzLDE2MSw4OCwyMDMsMjI1LDE2MywxNjQsMTk3LDUsODIsMjcsMTM4LDY5LDI0MCwxODYsOTEsMjMzLDEzMSwxNTgsMjAyLDQwLDkwLDgxLDE2MCw4MiwxNDMsMTM3LDE2NCwxMzEsMjAsMTczLDE1MywyNSw5NSw4MCwyNiwxODEsMzgsMTQ4LDU0LDE5NiwxMDQsMjMsMjM3LDEzNiwxNjMsMjE4LDE3LDI3LDY2LDIyOCwxMjcsMjExLDI1MCwxNDksMTY0LDg1LDEwOSwxNjksMTg2LDIwMCwxLDI1MSwyMDEsMTAzLDEyMywxODMsMTU1LDg3LDk0LDE4NywyMzUsMTc4LDk4LDk3LDIwNiwyNSwxMDUsMTYxLDIwMiwyMzEsMTIzLDY2LDEzOCwxNCwxNzcsMTE0LDE2MSw1OSwxNzcsNDAsMTMsMzksMzAsODQsNDgsMTg3LDIzMywxMzEsMTg2LDIwNCwxNjcsOTEsMjIzLDI0MywxOTMsOTIsMjEzLDE5MSwxNCwxNDcsMTcsMTM3LDQsMzYsOTYsNTYsNDUsMjcsMzUsMjAxLDE5MCwxOTMsMjQsMTc3LDE1NCw4NiwxOTcsMTU2LDE2OCwxNjgsMTE1LDMxLDEyNSwyMjksMzUsODAsODksOTksMjM3LDI1MSwxOTAsODIsMSwxMzMsMTUwLDIxNCwyMDUsODcsOSwxOTQsMjM0LDIzNCw4NiwxNjgsMTQ0LDE5MCwyMDEsOTcsMTEyLDE0MSw3NCw3NiwxNDAsOTYsNSwxMTcsNjYsNywyNTUsNDAsMTU5LDQyLDQzLDExOSw4NCw1LDk3LDEsMjE2LDcxLDExLDE2NywxNDUsNjUsMjUxLDUwLDEwMCw1MiwxNzAsNCwxOSwxOSwxMTksMTQwLDEwLDEwLDg1LDE3LDg0LDM3LDEwOSwyNDAsMTk0LDIzLDE2NiwyMzUsMTMxLDc2LDE4NiwzNywxMDQsMjE3LDI5LDIyNywxNTUsNjAsMTA5LDc1LDEwMiwxNywxMjksMTU0LDIyNSwyMiw5OSwxODIsMTcwLDU2LDI1LDIzMSwxNTEsNzksMTYxLDUwLDEwLDIyOSwzLDQwLDE5NiwyNiwxNzgsMTM0LDE0NSwxNywxNTUsMTE2LDE0MSwyMCwxNzIsMTQxLDE2OSw1LDQwLDIwOSw2MiwzNSwzNiwxNCw3NiwyMzYsMTk0LDI0MiwxMTYsMTgwLDIzNiw0Miw4NCw5MSwxMTgsMTM5LDIyNSwxNzksMTg4LDAsMTA1LDEzLDk5LDE4Myw1MywyMjIsMjEzLDEzMSwyMzIsMjI1LDk2LDE5Myw3MSw0LDEwNCw2LDE2NSwxNzksMjM1LDQ5LDIwNywxMTUsMzgsMTIzLDE4NSwyOSw2NSwxODEsMTc2LDE4NSw3MCwxNjksMTgxLDI0MSwyMiw0MSw5MCw2NiwxMjMsODksMTYxLDMwLDE0OCwxMTUsMTI0LDE5OCw2NCw2NiwxMDMsMTAxLDUyLDE5NywxOTAsMTMzLDEwMCwxMzksMTQyLDgzLDk3LDI1MiwxLDEzMiwyMzAsMjM0LDE4MiwyMjEsMjEsODksMTA1LDEyNCwxMDgsMjIwLDE5MywxOTYsMTU1LDE1NiwxNjEsMTUxLDI0MSwxMTcsNjQsMTIwLDIwNiwzOCwyNDEsOTMsMTA2LDIxMiwyNSwxNDAsMTU0LDI3LDE4MSwyNDEsMTAxLDIxMiwzNCwxMzksNTEsNyw5OSwzNCwxNjYsNjAsNTMsMTgyLDc4LDE5MSw3MywxNTUsMjUxLDU2LDE3MSw1MywxOTcsMzYsNDIsMTk5LDE0OCwyNTAsMTk2LDE5MiwxMzUsMTM0LDEwMiwyNTQsMjgsMTE3LDEyNiw4Niw1LDE1Nyw1MywxNjUsMjM4LDEzNSwxNzgsMTgxLDM5LDc2LDE0LDM0LDgwLDYsMjI3LDE2MiwxMjAsMTMwLDEwMywxNywxODgsMTc3LDU0LDk4LDE0MCwxOTUsMTc4LDY3LDE2MCwxNjcsMTQsMTUyLDQ5LDE2Myw2NywyNTAsMTUyLDIwOCwxMTYsMzMsMjQ3LDE4MiwxMjIsMTE2LDEyMywxNjksMTk0LDE2NCwxMTIsMTU2LDc0LDE0OCwxNDgsNjcsMTI1LDExOSwzOSwxMTksMTI0LDU1LDEzMywyMTAsNTksMTI1LDkxLDE4NywxMTQsMTQ1LDcxLDE3MiwxODEsNTEsOTEsMTYxLDU3LDU1LDE0OCw4MSw0OSwxNzQsMTA4LDE1MywyMDYsMjQyLDc4LDIzOSwxOCwxMzAsMzIsMjExLDEyOSwyMjIsNjQsOCwxMjgsMTkzLDEyOCwxNiwxMDYsODUsMTUwLDMsMjAyLDc0LDE2MywyMDIsOCwxOTcsNjMsMTMzLDIwNCwxODQsMTQ4LDE1MywxODksNjIsMTY4LDI2LDE4MywyMTksMTUwLDIwMCwyMTEsNTQsMjQ4LDc1LDExMSwyMzcsMjAyLDE2LDE5MywyMjUsMTkyLDE4Miw1OSwyMDIsMzYsMjMyLDE4MCwxOTcsMTMwLDEyMywxNTksMTU0LDEyMywyMTYsMjQwLDE2MSwyMTIsMTQ1LDExMCwxNTAsODAsMTE4LDIwOCw5MCwxMDYsODEsNzIsMjAsNjksNjAsMjgsMTc3LDkwLDE2NCwxODksMzksMCw4Myw4Miw5OCwxMzAsODAsNDUsMTc3LDc5LDE3NywyMzUsNDEsMTQ0LDI1LDMyLDIyLDEyMiwzMSwxNDUsMjE3LDE2MywxMDEsMjIwLDEsOTgsMTA4LDE5NiwxNzEsMTg2LDEzMiwyOSwyNDAsMjI1LDExMiw0LDY2LDk2LDE0OCwxODQsMTY0LDM2LDIyMywzMCwxMSw2OSwxMDUsNzcsMTk2LDE2NywxNiwxNDksMTg1LDI0NiwxNTMsMTI5LDE4MCwxMjAsMjQ0LDE1NiwxMiwxMzEsOTcsMjQyLDIyNyw3NCwxNDUsNzksN119fSwicGFyZW50X2hhc2giOnsidmVjIjpbXX0sInVubWVyZ2VkX2xlYXZlcyI6eyJsaXN0IjpbXX19fV0sImRlZmF1bHRfbGVhZiI6eyJub2RlIjpudWxsfSwiZGVmYXVsdF9wYXJlbnQiOnsibm9kZSI6bnVsbH19LCJ0cmVlX2hhc2giOlsxMjUsMTE1LDI0NCwxMzcsMjM2LDE1NywyNCwxMzQsMTMwLDczLDEwMSwyMTQsNjAsODYsMTg4LDkyLDEsMjQzLDc1LDQ2LDg1LDYwLDIyMCwxOTAsMTI3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NiwyMzAsNDIsMzMsMjM1LDg1LDEyNiwzOSwxMDUsMTM5LDkyLDU3LDYsMTg2LDg5LDE1Nyw4Myw0MCwyMDcsMTc5LDE2MSw2MCwxNDAsMjA2LDE5OCwxNzEsMTE4LDIzMiwxMzQsODgsNjksMTQwLDg5LDEzNSwxMTcsNjIsMTU1LDI1MCw5NSwxNTIsMjI2LDMwLDMwLDExNCwxOTIsOTgsMjE3LDExNiw3NiwxNjgsMCwyMjUsMSwxNTcsMTkwLDE3MiwxMDMsMTQxLDkyLDE1NSwxOCwxMTcsNTgsMTMwLDE4MSwxOTYsMjcsNTgsNzksOTksODQsOCwxODksMTk0LDEyOCw1LDExNCwyMSwxNSw5MiwxODksOTUsMTMzLDE4MiwxNDksMTg2LDUyLDI0OSwzOSw3Miw4LDIxLDEwOSwxNCw1MywzNywxNzgsMTk2LDExNCwxNzksMTcxLDY5LDE4OSw0MSwxNTUsMTQsODMsMTU3LDE3MCwyNDQsMzksMTQ4LDI0NCwxNzAsODAsMTg2LDE1NSwxMDAsMjQzLDE4OSw1MCwxNzcsMTQ1LDIyNSw4NCwxOCwyLDcsMTQ5LDIxOCw4NCwxNzYsMTkyLDUwLDExOSw3NiwxMDAsMCw2NywyOCwyMDYsMjI4LDE0OSwxMDEsMTc3LDI0NywxOTksOTgsMTU0LDgsMjQ2LDIyMCw1MCwxLDExOCwxNjcsNjAsNzIsMTUwLDIyOSw1OCwxODksNDUsMjIwLDExOSwyMTgsNDAsMjgsMTg2LDE4LDE3NCwwLDIwOSwxNDAsMTcxLDE2MywxNCw5NCw5MiwxMzgsMjE3LDc1LDE0MCwxNzAsMjI3LDIwMCwxMjcsMTU1LDQ4LDE4MSwxMiwxMzMsMjI1LDI0OSwxNjAsNDEsMjA0LDI2LDEwMSwyMTksMCwxODQsMTU0LDIwNSwyMzMsMTk2LDE0MiwyMzIsMTIzLDMwLDIzNywyMTAsOTMsNjQsODMsODYsMTUwLDIwLDE4OSwxMDcsNTUsMjQsMTYsODQsMjMsMTE1LDE0NSw4LDExMSwyNyw0LDE4NCw1Niw0NCwyNDYsMjA4LDEwNiwxMTUsOTEsMTU5LDI0NCwxMjMsMTk2LDI3LDIxNiw5NywxNTAsMjA4LDIsMjUyLDYwLDE3OCwxOTAsMzksNiwyMiwxNDcsMTYwLDMzLDI0NSwyMSwxNjMsMjE2LDUsMTksMTM2LDE1NiwxMDYsMTU2LDEyNywyMzEsMTAyLDIwNywzNSw5NiwxMjksMTE0LDIwOCw4NiwxODAsMTE2LDExOSwxMjYsNzQsMTIsNjcsNiwxMzMsNDEsMTE4LDc2LDAsMjYsMjA2LDYsNSwxMDMsMTUxLDIwNCwxMzYsMTY0LDM4LDY2LDE0LDkyLDEyNSwxNjMsNTMsMTUxLDEzMCwyMzUsMTg0LDE1OCwxNDksMTYxLDIzOCwxOTksNzEsMjUxLDE4NywzNSwxODgsMjQsMzEsMTYzLDIzNSwxNjMsNjAsNjAsMTMwLDc5LDE1NCwyNSwyMTcsNjQsMTk1LDE3NCwxMzksODUsMTk1LDIwOCwxMzksNzMsNjAsMTk2LDEyOSw2NCwyMDMsMTY2LDEyOCwxMzksMjA0LDE4LDEyNywxMzgsMjI5LDc3LDE2OCwxNjEsMTkzLDIwNywxMDUsMCwxODAsMTA3LDE3Miw2NSwxMTcsNjMsNzQsMzYsMTY1LDE0LDUxLDEyNCwxNiwxMDcsMjMsODIsMTQ1LDEwNiwyNDcsMTQ0LDQ0LDE5MywxMjAsMywxMDQsMTY4LDQxLDE4OCwzNCwyMDYsMjUxLDE3MCwyMDUsMjAzLDExMiwxNzEsMTYxLDEzMyw3NiwxNDgsMTM4LDM0LDgwLDQxLDE2OCwxMjgsMjMsMjAzLDgxLDE0Nyw1MCw1MSwxNDUsMTk4LDE1MiwyMzQsOTYsNzIsMTg1LDUwLDIzMSwyNywxNDUsMjA3LDIxMSw0Miw0MCw4NiwzMSwyNTAsODYsODgsMjM2LDE5NywxNjksMTc2LDE2NSwxMzAsMjM0LDI0NCwxODQsMTU1LDE4NiwyLDE5OSw1NSw2OSw1NywyMDAsMTI4LDE1LDE5LDc4LDIzMSwyMTMsMTcyLDIwLDIxNCwzNCwxODksMjEyLDE4Myw0LDExLDE0MiwxODgsMTYwLDQ5LDE1NywyNywyMSwxNTQsMTY0LDE3OSwyMjAsMTY2LDE3NCwyMDYsMTMxLDExLDY0LDgwLDI4LDMxLDEyMSwxMTAsMTgxLDEyMiwxMzMsMjI1LDE0NywxNzksOTgsODksMTc5LDY2LDE2OCw0MiwyNDAsODcsMjA1LDE4NywxOTUsMTEwLDE5OSwyMjAsMTMsMTgzLDQ0LDE5LDEsMTUxLDU5LDE4MCwxNjEsMTkxLDEwMywxMjMsMTMsMjU1LDE2OCw3MCw4NCwyNiwyMDQsMTMwLDg2LDM4LDEzNiwyMywxMzQsMTU4LDEwMSwxMjgsMjUzLDIwMiwxMzksMTYyLDIxNCwzMCwxOTEsMTEyLDEyMCwyMDIsMTUwLDE3NywzLDE2NSw4MywyNDAsMTY0LDE2NiwyNDYsMTUyLDE0NywxOTksODQsMTUsMjAyLDIzNCwzMCw3NSwzOCw3Myw5OCw2NywxNjQsMTIwLDU0LDcsMTIyLDUzLDcxLDM3LDI1LDE5Nyw3NSwxODcsMTIsMTg5LDE5Nyw0MSwxOTAsMzIsMTIzLDIxNCwxMzksMjA1LDE3MiwyNDcsMTMzLDE1MCw3LDE5NSwxMjYsODAsMTIwLDIwMSw5OCwxNDIsMjE1LDg1LDIwMCwyMzQsNzUsNTAsMjE0LDQ5LDEwNiwyMTYsNTgsMTksNjgsMjEzLDczLDM3LDE0OCwxMzQsMjAxLDIzNSwxMDIsMTkxLDc1LDE4MCwyMTQsNTUsMTg3LDE5OSw4NiwyOSwwLDcsMjAwLDEzMywxMzUsNDIsMTg5LDE4MCwxNzgsMTQyLDM3LDEyMCwxMjgsMzYsNzAsMjM0LDEzMiw1OSw0NiwyMTcsMTQyLDgzLDg2LDY2LDE5MywxNjgsMTQyLDE5OCwxMDMsNjUsMTM5LDI0MywxNTEsMjksMTM5LDYzLDYzLDE4OCwxMjIsNDIsMzUsNjksMTUwLDk5LDExMSwxNDYsMTg0LDI1LDM5LDkwLDMzLDEyNiwyNDksMTg5LDEyNywxNzYsNjEsMTE1LDEwNSw5OCw5MSw1MywxMzUsMTE0LDEyMiwxNjAsODIsNzMsMTY1LDU3LDY4LDExMCwyMDgsMTI0LDEyNSwyLDE2OSwxMTgsMTE5LDg0LDM3LDEyOCw4NiwxNTAsNTEsMTA1LDg3LDI1NSwzMywyMDMsMjAwLDIzMSwxMDMsMTI5LDUyLDY0LDE4OCw4Miw1OCw2NiwxNjMsNTcsMjExLDEyMCwxMTAsMTg2LDE4LDg1LDEzMSwyMTgsMTksMTcsOCw3OCwyMDUsMTc3LDU2LDIyMywxMjAsOTIsNDMsMTk2LDcsMTI3LDE1Niw0Miw4MywxMDcsMTAsMjIzLDEzMCwxMCwyNDAsMjMzLDE4OSwxNSwxOTgsNDAsMjQwLDExOCw1LDQ1LDY1LDIxLDk1LDE4NywxNzksMTA2LDEzMiwxMTMsMTkyLDEwLDMwLDEzMywxNjQsMjEsOTMsOTIsMTU4LDE3MCwxMzksMTI2LDI0OCwyNDgsMTMxLDIyNywxODIsOTQsMTU5LDQsMzcsNTQsMTk5LDE4NywxNDUsMTE3LDEzMywxNyw1Nyw1NSwyMTIsMzUsMTEyLDE4NSwxMzEsOSwyMDksNjQsNzQsMTQ2LDIxNyw1MiwyMiwyMjQsNDksMTk1LDg3LDE3NCw2MCwyMSw4OCw3NiwyMDgsODcsMTk1LDIyNiw3MywyMDEsMjI5LDEyOCw2OCwyMzYsODQsMjI3LDI0MiwxNjQsNzksMTk2LDEzNCwxMTgsNjcsMTI4LDIsMTAsMTcsMjI2LDIwLDEyNiwxMzcsMywxODYsMTAxLDE0NCwyMDAsMTg5LDE1MywxMTQsMTYxLDE5OSw1LDkyLDE0OCwxMTIsNTQsMiwxNDQsNjAsMjE4LDAsMjMsMjMyLDYwLDI0MywyMTIsMTc1LDAsMzcsMjUsNDcsMjEyLDExMCwyMTYsNDgsNCwxNDcsMjI0LDU0LDE0LDcwLDIwMCwyMzMsMjEyLDU2LDE0LDIxNCwxMzUsNzksODEsMTUwLDExNiwxMzMsMyw3NSwyMzIsMTg4LDI1MywyMjAsMTM5LDIwMiwxNDQsNTYsODIsNTIsMywyNSwyMjgsOTcsOTgsMjUyLDIwMSw3NiwxNDAsMTksMTQ0LDk4LDEwMiwyMDIsMTM2LDE4Nyw1MCwxOTIsMTAzLDQ0LDg2LDk3LDEyNywxMjAsMTIsODgsMjM2LDE2Myw4MCwyMTQsMTg0LDIyMCwyMTUsNDAsMTM5LDExNyw3MSwyMTksMTgxLDExNyw1MSwyMTIsMTI0LDEwMiwxMTMsMTAxLDMxLDE0Niw0NiwxOTEsMjAxLDk5LDYsMjI4LDEyMiwyMjgsNzEsMjA3LDkwLDkyLDU0LDcwLDI1MCwxNjEsMjIxLDEzOSwxMTEsNDYsODUsMTgwLDE2NSw3LDU2LDEyNSwyMSwzNCw2NywxMTksMTEyLDE2NSwxMSwxOTcsMTAwLDY1LDY3LDIyMSw1OSwyMzQsMTg5LDE5NSwyMDMsMTYyLDE1OSwyMDMsMTk5LDE1OCwxODIsMTY1LDEsMTkyLDE1NSwxMTUsMjksMTc5LDIzMywzNSwyMTUsOTUsMTksMjI0LDIsMTksODMsMjE2LDEyLDgxLDE5OCwxODMsMTgwLDAsNTgsMjQ4LDM5LDM5LDI1Myw4NCw1MCw5NCwyNTQsMjI1LDIxMiwxNSwxMCw4OCwyNTMsMjEwLDksMjQ2LDM3LDIyNCwxNjUsMTg1LDksNiwzMywwXX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMTM0LDE1MSwxNywyMjIsNTcsMTYsOTMsMjMzLDg3LDEyMCwxNzEsMTYzLDE1NiwxMzksMTIzLDE4MiwyMDksMTUyLDEwMSwxMzgsMTkzLDY2LDEzOCwxMTcsMjI4LDEzOCw0LDYzLDEyMiwxLDE2OCw4MSwzLDQ5LDIyNyw4OSwyMDMsMjQ1LDgwLDI0MywxOTQsMTg5LDExNCw0MywxMjIsMTMsNTgsMTc2LDQxLDE4MCw1NSw0NCwyMjUsMTg2LDEzNywxMDIsNjYsNzgsMzIsMTMzLDIwNSw5LDIyLDIzMiwxNTEsMTgxLDE5NCwxNDQsMjcsMjIwLDE2NywxNjcsMTYxLDEzMywxODIsNzUsMTk5LDEwNCwzLDY5LDE2MCwyOCw0NCwxNTgsNTIsMjMsMyw1NSwyMiw3MywyMjUsMjI1LDIwMCw3MCw4MSwyNSwyNDgsMTYyLDExNSw5OSwyMTcsMjUsMTg2LDg3LDE4Niw4NywyNTIsNjMsMTMxLDE2MywxODUsODMsMzcsMTk1LDE1MiwxMjQsMTgwLDEyNCw5OCw1MCwxNTYsMjE5LDY4LDczLDE3MCw0LDQ0LDEyOSwxNjYsMjI2LDEwNCwxODAsMjUwLDM0LDE3MCwyMjEsMjE3LDc5LDE4LDI1MiwxNjQsNzQsMjUyLDE1OSwxMTMsMzksMTgwLDQyLDE3LDE5MSwyNDYsNzIsMTk3LDgzLDE5NSwxNDcsNjMsMjE1LDU2LDIxLDUwLDEzNCwyMjgsMjE1LDEzNSwyNTUsMTM5LDQxLDE0Nyw5MCwyMDEsMTM3LDg2LDExMSwxMTAsOTIsMTUwLDE1MCwxOTIsMTgxLDI0NSwxMzAsMTA1LDE4LDQ5LDE2MiwyNTIsNzAsMTUsMjU1LDgxLDE5NCwxMTAsMjM1LDc0LDE5NSw4Myw0LDE5NiwxMzIsMTgzLDE0NiwxNiw2OCwxNDYsOTYsMTcxLDk3LDAsMjYsNzgsMjgsMTQ1LDExLDUxLDE2MywxMDQsNzIsNTMsMTM3LDQ0LDE2LDIyMSw4OCwyMywxNjksNjYsMTQzLDYzLDIwMyw4MSwyMDYsMjQxLDE4NywyNywxMzEsMTgzLDgzLDQsMTAwLDE4MSw2LDEzNSwyMDQsMjIwLDgzLDE4Nyw1Niw4Miw3Nyw3MywxMDgsMTYsMTYzLDE1Niw5MSw5OCwxMDcsMjgsMywxODMsMjE2LDU5LDE2NiwxODEsMzQsMTU5LDAsMjEyLDQxLDIwMywxMzUsMTI2LDE5NSw4MywxODcsMTIxLDgsMTE5LDE0OCwzOCwxMzgsMzksMTcwLDExNywyMjAsMTc5LDEyOSw1MCwzNSw5MywxNjksNjQsMTAwLDE1NiwyMzIsMTcxLDM2LDE0NywxMzUsODksODksMTkyLDE3LDkyLDE0MiwxNjAsMTc4LDEwNywxNDEsMTY5LDEyLDEzOCwzNSw3MSwxNDAsMjI4LDM4LDEwLDIxNSw3NCwyOSwxNTAsMjYsMTU3LDIxNCwxOTEsNDgsMjAsMTY5LDQyLDUyLDE5NSwzLDEwOCwxOTAsMTA0LDIxMiwxNzQsMjI1LDI0MywzLDc0LDIwMCwxNjYsMywxMjksMTA5LDE1MiwxOTYsODksMTExLDE2Nyw2NSwxODgsOTIsNzgsMTkzLDI3LDk3LDgsMTQ3LDE0LDE0MywyNDIsMjA3LDIzNCwxMTIsMTgxLDg2LDUzLDEyNyw5MiwxOTUsMTYzLDIzNiw4LDEwMCwyMDUsMTQ2LDEzOSwxNDMsOTYsMTkzLDIxNiw1OCwxMDAsMTYxLDUyLDEzMSwzNiwxMjksMTMxLDIzMyw2OSwzMSw5NywxMTYsMTc5LDExNSwxNTEsODMsMTAxLDE3MiwyMiwxNTUsMjI3LDY0LDkyLDExOSw5NSw3OCw3NSwxMzMsNjksOTcsMTkzLDI4LDIwMywxMjgsMjQ5LDI0NiwxMjgsMjI0LDIzMywxMzYsMTc5LDEzNCwxMzUsMTI2LDI2LDk1LDI0MywxOTgsMTEsMTEwLDE2NCwyNywwLDIxNSwyMDQsMjUyLDE1NCwyMDcsMTA4LDgzLDExMiwxMjAsMTk1LDM5LDE0Myw3NCwxMzQsMTM3LDQzLDg1LDE0OCwxNTEsNzUsOTQsMjUyLDY4LDc4LDE4NCw4NSwyMjEsMTcsMTI5LDE5MSwxNjQsNzUsMjE0LDMsNTAsNzcsODQsMTY0LDIzMywyMTEsMTQ4LDIxNywyMjgsMTQwLDE4NCwyMzUsNjUsMTAxLDQsMTUsMTI5LDgzLDE3MSwyNDAsMTIwLDE1Miw5Miw5MSw1Miw1OSw5Nyw5OSwyMywyMTQsMTg1LDc1LDIyOCwxMiw2NCw2NSw2OCwyNiw5OSwxODQsMTcxLDExNCwxMCw2MCwyMzAsMTg1LDIzOSwxNzYsMjA0LDEwMywyNTAsMTIzLDc1LDE5NSw2OSwyMzQsMjI1LDAsMTk5LDI1MSwxMTMsNjYsNTgsMTIzLDI1NCwyMTcsMTU5LDE2NCwxODAsMTYsODksMjMsMzAsMTExLDE3OCwyMDUsMTIwLDIyOSwxMDcsMTE4LDIyNiw4MCwyMiw4NiwzMSwxMDIsMTYzLDE3OCw3MSwxNzYsNSwxMDUsODksMTgxLDIyOSw0NCwxODMsNSwxNjAsMTIxLDEwOSw1NCwxNzcsMjAwLDI0NiwxNjEsMTcyLDE0NCwyMDMsMjI2LDE5OSwzOCwxMTEsMjYsMjA0LDEzMSwyNDIsMTksMTM1LDI0MCwxODUsNSwxNTMsMTk4LDIzNCwxMTIsMTg1LDIsMTQ2LDMwLDIxMiwyNDksODIsMjM5LDI0MSwxMjYsMTE2LDIwOCwxMDQsMTYzLDIzMCwzLDEzNCwxMDQsMTYsMjE3LDcwLDE2NiwxMDMsNSwxODMsODgsMTM2LDEwMSwxOTEsNzMsNzEsMjUxLDE2Nyw0NywyMTcsODksNDMsMTEyLDIxMSwxMTUsMTQ4LDI1MiwyOCw3MiwxOTksMTM5LDIyMCwxNzIsMTc3LDEwMCw1NSwxMTgsOCwxNDAsODQsMTQzLDIyLDMzLDQ3LDgxLDk5LDM1LDIwMSwyNiwxOTUsMjQwLDE1MCw1NCwyMzQsMjIsNzEsNTQsMjAwLDIwMywxNjEsMTc0LDI0NiwyNDksNDAsMTY4LDIyNiwxNjIsMTgxLDE1NSwxNzQsMjI2LDEyOSw4NSwyMjAsODYsMTY4LDEzNSwxNjEsNjgsNjcsNjYsMzgsMTcsMTksNiw3NiwxMTgsODAsMjQ3LDkxLDk3LDQyLDIxNCwzMSwyMDUsMTE0LDk0LDE3NCwyNDMsMTEsMjU0LDEzNywzNiw1OSwzNCwxMzcsMTgwLDYsMTM0LDUyLDExMiwxNzUsMTY5LDIzNCw4NCwyNiwyNiwxOTQsMjIxLDE5NywzMywyNTMsMTk2LDI2LDI4LDEzMSw0NywxMjQsMjI3LDc3LDI0MSw1Nyw2MCwxNTAsMTg2LDE0OCwxODUsOTcsMTEzLDc5LDIzNiwxMTUsMjM3LDQsMTA0LDExMSwyNCw0OSwxODgsNjksOSw5Nyw4Myw5MSwyNDQsMTI4LDE4NCwyMzIsMjAzLDI1LDIxOSwxNDgsMSwxMzIsMjEwLDE1MCw4MSw1MywyMiwxNDMsMTc5LDIwNSwyMTksMjEwLDE3MiwxMDIsMTk0LDEwMywxMDYsMTg4LDE5NCwxMzUsMTE2LDE0NSw2LDUwLDksODAsMjEwLDMyLDgwLDU3LDUyLDEzNiwxLDE1MiwxMzUsMTg4LDU4LDcyLDE4NiwxNjAsNjQsOTcsMTg2LDE5NywyMDgsNTksMjUsOTYsMTU5LDcsMTc5LDQyLDYxLDIxNyw4NCwxOTksMTQ3LDIsMzksMjMwLDYzLDEzNSwxODcsMTUxLDIxNCw1MCwxNCwxNDcsMTkyLDIwNCwxMjgsMTA3LDE4OSwzMiwyMjgsMTI0LDE1OCwxMzYsMjgsMTI1LDE2OSwxNDcsNTQsMTAxLDExNywxMTQsMTE4LDE1Nyw1MiwyMDksODIsMjIwLDk3LDcxLDEyLDIxMCwxMTQsMTIyLDIwMywxOTMsMTIzLDE4MCwxMTQsODIsMTUzLDE4MCwyMjIsMjYsMTYxLDEsMjAsMTgzLDYwLDIxOCwxNTcsMjgsNDgsMTg3LDE5MywyNDEsMSwyNTQsMTIxLDM0LDE3NSwxNjYsMTU5LDE0OSwxMTQsMzMsNzQsMCw0MywyMzYsODcsNDQsMjAzLDE1NCw5MCwyMDYsMTMyLDYxLDExMCw0MSwzMSwyNDYsNSw4LDIyMSwxODIsMTQ4LDIxOSwxOTcsOTIsMTQxLDIwOCwxMTYsMTAxLDEwOCw3NywyMjMsMTc2LDE1LDE2MSwyMjgsNjIsNTAsMTE0LDExNCw3Miw3Miw1OSwxODAsMTY0LDEzOCwxMzgsMTgzLDgsOTAsMjQsMTcyLDIyNywxNzEsMjksMjQ2LDM5LDEzNSw3MSwxMzMsMTEzLDEwMSwyMjQsNTYsMTM0LDI4LDIwMCwyMjYsMjQ0LDEyMSwxODMsMTQ5LDkzLDYwLDIxMCwyMDYsMjMyLDEyLDEyMywzOCw1NywxNzUsMjMzLDEyMSw4NCw1NywxODcsNDgsMjI2LDAsMjA3LDI5LDIxOSw0NywxMzAsOTIsNjAsMjU0LDEzMiwxMDksOTAsMTA4LDE1MCwxLDYwLDEzMiw3Niw1Nyw3OCw5MSwxNzcsMjAyLDE4MCwxOTYsNzgsMzcsMTgwLDQyLDE1MSwzNywxNTQsMTgxLDkyLDcyLDI0NSw4LDI0LDE3NywxLDEyMywxNDIsMTIwLDEzMywxNzcsMjI4LDc1LDc1LDIyOCwxMzgsMjE2LDIzMywxNDEsNjUsMjAyLDE4MSwxMTcsNTcsMTQxLDI1MCwyNDQsMTAsMTQ5LDU0LDE3MywxMDQsMzYsMTU2LDM1LDI1MSwxNiwyMTEsMTQ0LDMwLDE0MSw5MSwxNzYsMzIsNzYsMTYyLDE1MiwyMDgsMTA4LDg1LDIxLDE1OSw4OSw3LDE1MywyMDgsMTEyLDE5NSwyMTcsMTA3LDE0OSw3MywxODAsODQsMTIsNDAsMTMyLDE1Niw4NCwxOTAsMTk0LDE1MiwxNDUsNDAsMTA0LDgsMSwxMTMsNzIsNjksMjQ3LDE1MSwyMTcsMjQ5LDgyLDU4LDExOSwxOTcsMTMwLDE2MywxMTAsOTYsODUsMjcsMTMyLDg3LDU5LDEwNiw4LDEwNywxMTIsNTUsOTcsMTAwLDExOSwxMTIsMTM2LDIxMCw0NCwxOTksMzgsMjA3LDIyMCwxMzksOTYsNyw4NiwxMyw0LDE0NCwxNTIsMTg2LDYsOTEsMTM0LDE2OSwxMDQsMjA3LDEzNCwxNDUsMjAsMjcsOTgsMjE0LDY3LDY0LDEwMCw5Miw5MSw3OSwxNjgsMTY4LDI4LDIwOCw3NCwyMzIsMTEsMTc3LDg3LDI1MSwyNSwxLDI1MCwxNzEsMjA5LDE2LDE2MywxNzUsMTQwLDQxLDkyLDE1MiwyMiw4MCw4MCwxMDcsNjgsMjUxLDMsMTU5LDIwOSwxMjUsNDgsMTY1LDEwOCwyMzYsMjAsMTEsNTQsMTAwLDE4OCwyMjgsMTQ0LDE3NiwxODYsMjIwLDE0NywxMDgsMTYsOTMsOTMsMTc2LDQzLDIxMiw2NSwzOCwxNDcsMTYwLDgsNzIsMjcsMTA0LDIyNCwyNDMsMTMzLDE0MiwxMDEsMjA2LDIwNCwxOTcsNjYsODgsMjQsMTQzLDEwNSwxMzcsMTAzLDAsMTk2LDQ5LDI0MSwyMDQsNTMsODcsMjIsMTI3LDE4OCwxMzIsMzgsMTEyLDE2OCw1MCw5Niw0NCw2NywyNDQsMTM5LDExMSwxNjUsNTQsNTMsMTg2LDIxOSw4NCwxMzEsMTEsODIsMTkwLDk2LDE4LDIzMCwxOTMsOTcsMjA5LDgwLDg1LDY0LDczLDU0LDkzLDE1NSwxODgsMTQsNiwzNSwxODIsMTMyLDE3MCwxMTYsMjMwLDQyLDMzLDIzNSw4NSwxMjYsMzksMTA1LDEzOSw5Miw1Nyw2LDE4Niw4OSwxNTcsODMsNDAsMjA3LDE3OSwxNjEsNjAsMTQwLDIwNiwxOTgsMTcxLDExOCwyMzIsMTM0LDg4LDY5LDE0MCw4OSwxMzUsMTE3LDYyLDE1NSwyNTAsOTUsMTUyLDIyNiwzMCwzMCwxMTQsMTkyLDk4LDIxNywxMTYsNzYsMTY4LDAsMjI1LDEsMTU3LDE5MCwxNzIsMTAzLDE0MSw5MiwxNTUsMTgsMTE3LDU4LDEzMCwxODEsMTk2LDI3LDU4LDc5LDk5LDg0LDgsMTg5LDE5NCwxMjgsNSwxMTQsMjEsMTUsOTIsMTg5LDk1LDEzMywxODIsMTQ5LDE4Niw1MiwyNDksMzksNzIsOCwyMSwxMDksMTQsNTMsMzcsMTc4LDE5NiwxMTQsMTc5LDE3MSw2OSwxODksNDEsMTU1LDE0LDgzLDE1NywxNzAsMjQ0LDM5LDE0OCwyNDQsMTcwLDgwLDE4NiwxNTUsMTAwLDI0MywxODksNTAsMTc3LDE0NSwyMjUsODQsMTgsMiw3LDE0OSwyMTgsODQsMTc2LDE5Miw1MCwxMTksNzYsMTAwLDAsNjcsMjgsMjA2LDIyOCwxNDksMTAxLDE3NywyNDcsMTk5LDk4LDE1NCw4LDI0NiwyMjAsNTAsMSwxMTgsMTY3LDYwLDcyLDE1MCwyMjksNTgsMTg5LDQ1LDIyMCwxMTksMjE4LDQwLDI4LDE4NiwxOCwxNzQsMCwyMDksMTQwLDE3MSwxNjMsMTQsOTQsOTIsMTM4LDIxNyw3NSwxNDAsMTcwLDIyNywyMDAsMTI3LDE1NSw0OCwxODEsMTIsMTMzLDIyNSwyNDksMTYwLDQxLDIwNCwyNiwxMDEsMjE5LDAsMTg0LDE1NCwyMDUsMjMzLDE5NiwxNDIsMjMyLDEyMywzMCwyMzcsMjEwLDkzLDY0LDgzLDg2LDE1MCwyMCwxODksMTA3LDU1LDI0LDE2LDg0LDIzLDExNSwxNDUsOCwxMTEsMjcsNCwxODQsNTYsNDQsMjQ2LDIwOCwxMDYsMTE1LDkxLDE1OSwyNDQsMTIzLDE5NiwyNywyMTYsOTcsMTUwLDIwOCwyLDI1Miw2MCwxNzgsMTkwLDM5LDYsMjIsMTQ3LDE2MCwzMywyNDUsMjEsMTYzLDIxNiw1LDE5LDEzNiwxNTYsMTA2LDE1NiwxMjcsMjMxLDEwMiwyMDcsMzUsOTYsMTI5LDExNCwyMDgsODYsMTgwLDExNiwxMTksMTI2LDc0LDEyLDY3LDYsMTMzLDQxLDExOCw3NiwwLDI2LDIwNiw2LDUsMTAzLDE1MSwyMDQsMTM2LDE2NCwzOCw2NiwxNCw5MiwxMjUsMTYzLDUzLDE1MSwxMzAsMjM1LDE4NCwxNTgsMTQ5LDE2MSwyMzgsMTk5LDcxLDI1MSwxODcsMzUsMTg4LDI0LDMxLDE2MywyMzUsMTYzLDYwLDYwLDEzMCw3OSwxNTQsMjUsMjE3LDY0LDE5NSwxNzQsMTM5LDg1LDE5NSwyMDgsMTM5LDczLDYwLDE5NiwxMjksNjQsMjAzLDE2NiwxMjgsMTM5LDIwNCwxOCwxMjcsMTM4LDIyOSw3NywxNjgsMTYxLDE5MywyMDcsMTA1LDAsMTgwLDEwNywxNzIsNjUsMTE3LDYzLDc0LDM2LDE2NSwxNCw1MSwxMjQsMTYsMTA3LDIzLDgyLDE0NSwxMDYsMjQ3LDE0NCw0NCwxOTMsMTIwLDMsMTA0LDE2OCw0MSwxODgsMzQsMjA2LDI1MSwxNzAsMjA1LDIwMywxMTIsMTcxLDE2MSwxMzMsNzYsMTQ4LDEzOCwzNCw4MCw0MSwxNjgsMTI4LDIzLDIwMyw4MSwxNDcsNTAsNTEsMTQ1LDE5OCwxNTIsMjM0LDk2LDcyLDE4NSw1MCwyMzEsMjcsMTQ1LDIwNywyMTEsNDIsNDAsODYsMzEsMjUwLDg2LDg4LDIzNiwxOTcsMTY5LDE3NiwxNjUsMTMwLDIzNCwyNDQsMTg0LDE1NSwxODYsMiwxOTksNTUsNjksNTcsMjAwLDEyOCwxNSwxOSw3OCwyMzEsMjEzLDE3MiwyMCwyMTQsMzQsMTg5LDIxMiwxODMsNCwxMSwxNDIsMTg4LDE2MCw0OSwxNTcsMjcsMjEsMTU0LDE2NCwxNzksMjIwLDE2NiwxNzQsMjA2LDEzMSwxMSw2NCw4MCwyOCwzMSwxMjEsMTEwLDE4MSwxMjIsMTMzLDIyNSwxNDcsMTc5LDk4LDg5LDE3OSw2NiwxNjgsNDIsMjQwLDg3LDIwNSwxODcsMTk1LDExMCwxOTksMjIwLDEzLDE4Myw0NCwxOSwxLDE1MSw1OSwxODAsMTYxLDE5MSwxMDMsMTIzLDEzLDI1NSwxNjgsNzAsODQsMjYsMjA0LDEzMCw4NiwzOCwxMzYsMjMsMTM0LDE1OCwxMDEsMTI4LDI1MywyMDIsMTM5LDE2MiwyMTQsMzAsMTkxLDExMiwxMjAsMjAyLDE1MCwxNzcsMywxNjUsODMsMjQwLDE2NCwxNjYsMjQ2LDE1MiwxNDcsMTk5LDg0LDE1LDIwMiwyMzQsMzAsNzUsMzgsNzMsOTgsNjcsMTY0LDEyMCw1NCw3LDEyMiw1Myw3MSwzNywyNSwxOTcsNzUsMTg3LDEyLDE4OSwxOTcsNDEsMTkwLDMyLDEyMywyMTQsMTM5LDIwNSwxNzIsMjQ3LDEzMywxNTAsNywxOTUsMTI2LDgwLDEyMCwyMDEsOTgsMTQyLDIxNSw4NSwyMDAsMjM0LDc1LDUwLDIxNCw0OSwxMDYsMjE2LDU4LDE5LDY4LDIxMyw3MywzNywxNDgsMTM0LDIwMSwyMzUsMTAyLDE5MSw3NSwxODAsMjE0LDU1LDE4NywxOTksODYsMjksMCw3LDIwMCwxMzMsMTM1LDQyLDE4OSwxODAsMTc4LDE0MiwzNywxMjAsMTI4LDM2LDcwLDIzNCwxMzIsNTksNDYsMjE3LDE0Miw4Myw4Niw2NiwxOTMsMTY4LDE0MiwxOTgsMTAzLDY1LDEzOSwyNDMsMTUxLDI5LDEzOSw2Myw2MywxODgsMTIyLDQyLDM1LDY5LDE1MCw5OSwxMTEsMTQ2LDE4NCwyNSwzOSw5MCwzMywxMjYsMjQ5LDE4OSwxMjcsMTc2LDYxLDExNSwxMDUsOTgsOTEsNTMsMTM1LDExNCwxMjIsMTYwLDgyLDczLDE2NSw1Nyw2OCwxMTAsMjA4LDEyNCwxMjUsMiwxNjksMTE4LDExOSw4NCwzNywxMjgsODYsMTUwLDUxLDEwNSw4NywyNTUsMzMsMjAzLDIwMCwyMzEsMTAzLDEyOSw1Miw2NCwxODgsODIsNTgsNjYsMTYzLDU3LDIxMSwxMjAsMTEwLDE4NiwxOCw4NSwxMzEsMjE4LDE5LDE3LDgsNzgsMjA1LDE3Nyw1NiwyMjMsMTIwLDkyLDQzLDE5Niw3LDEyNywxNTYsNDIsODMsMTA3LDEwLDIyMywxMzAsMTAsMjQwLDIzMywxODksMTUsMTk4LDQwLDI0MCwxMTgsNSw0NSw2NSwyMSw5NSwxODcsMTc5LDEwNiwxMzIsMTEzLDE5MiwxMCwzMCwxMzMsMTY0LDIxLDkzLDkyLDE1OCwxNzAsMTM5LDEyNiwyNDgsMjQ4LDEzMSwyMjcsMTgyLDk0LDE1OSw0LDM3LDU0LDE5OSwxODcsMTQ1LDExNywxMzMsMTcsNTcsNTUsMjEyLDM1LDExMiwxODUsMTMxLDksMjA5LDY0LDc0LDE0NiwyMTcsNTIsMjIsMjI0LDQ5LDE5NSw4NywxNzQsNjAsMjEsODgsNzYsMjA4LDg3LDE5NSwyMjYsNzMsMjAxLDIyOSwxMjgsNjgsMjM2LDg0LDIyNywyNDIsMTY0LDc5LDE5NiwxMzQsMTE4LDY3LDEyOCwyLDEwLDE3LDIyNiwyMCwxMjYsMTM3LDMsMTg2LDEwMSwxNDQsMjAwLDE4OSwxNTMsMTE0LDE2MSwxOTksNSw5MiwxNDgsMTEyLDU0LDIsMTQ0LDYwLDIxOCwwLDIzLDIzMiw2MCwyNDMsMjEyLDE3NSwwLDM3LDI1LDQ3LDIxMiwxMTAsMjE2LDQ4LDQsMTQ3LDIyNCw1NCwxNCw3MCwyMDAsMjMzLDIxMiw1NiwxNCwyMTQsMTM1LDc5LDgxLDE1MCwxMTYsMTMzLDMsNzUsMjMyLDE4OCwyNTMsMjIwLDEzOSwyMDIsMTQ0LDU2LDgyLDUyLDMsMjUsMjI4LDk3LDk4LDI1MiwyMDEsNzYsMTQwLDE5LDE0NCw5OCwxMDIsMjAyLDEzNiwxODcsNTAsMTkyLDEwMyw0NCw4Niw5NywxMjcsMTIwLDEyLDg4LDIzNiwxNjMsODAsMjE0LDE4NCwyMjAsMjE1LDQwLDEzOSwxMTcsNzEsMjE5LDE4MSwxMTcsNTEsMjEyLDEyNCwxMDIsMTEzLDEwMSwzMSwxNDYsNDYsMTkxLDIwMSw5OSw2LDIyOCwxMjIsMjI4LDcxLDIwNyw5MCw5Miw1NCw3MCwyNTAsMTYxLDIyMSwxMzksMTExLDQ2LDg1LDE4MCwxNjUsNyw1NiwxMjUsMjEsMzQsNjcsMTE5LDExMiwxNjUsMTEsMTk3LDEwMCw2NSw2NywyMjEsNTksMjM0LDE4OSwxOTUsMjAzLDE2MiwxNTksMjAzLDE5OSwxNTgsMTgyLDE2NSwxLDE5MiwxNTUsMTE1LDI5LDE3OSwyMzMsMzUsMjE1LDk1LDE5LDIyNCwyLDE5LDgzLDIxNiwxNDMsMTQyLDEzNCwxNTIsNjIsMjUxLDE1NCwzNSwyMCwxMDcsMTIsMTQ3LDE1MSw3OCwxNTMsOTMsODcsMTQ1LDM4LDI2LDE4NSwxNDMsOTQsNjYsMjExLDUsMjIsMTU2LDM4LDEyNSwxNjgsNTIsNTQsMjA3LDE3MSwxMTEsNTksOTUsMjA4LDIxOSwyMzUsOSwzMCw1NywxNDIsMTgsMjU0LDYzLDE4OSwxNjAsNzgsMTE2LDUwLDIwNCwyMzYsMjAsMTU3LDEzOSwxMDUsMTQsMjUwLDIyNCwxOTksNDksODIsNjYsMTYwLDE4MSwzMiwxNDEsMTI2LDIyMCwyMTMsNzcsMTgzLDI0NiwyNTMsMTk1LDIxNCwzNywxOTQsMTg1LDk4LDE0NSwxMjQsNjIsMTgsNTIsNDYsMjI1LDEsMTAsNDQsMTIwLDE0MywyNDAsMTIsODEsMTk4LDE4MywxODAsMCw1OCwyNDgsMzksMzksMjUzLDg0LDUwLDk0LDI1NCwyMjUsMjEyLDE1LDEwLDg4LDI1MywyMTAsOSwyNDYsMzcsMjI0LDE2NSwxODUsOSw2LDMzLDBdfX19LHsicHVibGljX2tleSI6eyJrZXkiOnsidmVjIjpbMTE2LDE2Niw2MiwxMjksMTc3LDE2MywxMjQsMjE0LDEwOCw4MCw4NiwxNDUsMjgsMTQ0LDY0LDE4NSw0LDg5LDYwLDExNiwzMywxOSwyMjUsMTU3LDcwLDEyMywxMzMsNTMsMTgwLDg5LDY3LDE0OSw0Myw2NiwxODMsMTQzLDQ3LDM0LDE4MCw4NywxNiwxMSwxMTAsMjUxLDYxLDIwMiwyMzMsMTUzLDgsMTAyLDkxLDE5OCw0OSw1OSwxNjAsMTk4LDg2LDU4LDI0MCwxNTgsMTU5LDgxLDIwNyw5NCwyMDksMTIwLDIyOCw4NCw1NSwyMzksMjIsMTk3LDEwMywyMzQsNzcsMTk4LDI0MSw0Nyw5LDgwLDE2MiwxODYsMTU0LDE4MiwyNiwxMzksODYsMjQxLDIwLDEwNCwxNjksMjE5LDEzNywyMDcsOCwxOCwxNTQsMTYsMjUsMTE5LDE0NiwxODYsMTUzLDg5LDIsOSwxMDQsNDIsMzYsMTYsMzcsMjEsMTk1LDk1LDM5LDE5NiwxNTQsMTg1LDc0LDEzMiwyNDYsODksMTQ1LDEyMCwyMjcsMjEsNjIsMTgyLDEwMiwyMzIsMjUyLDIsMTc5LDE2OSw0MiwxNDUsMTAzLDE5MSwyMDUsMjM0LDExNiwxOTAsMTA4LDE0Miw5OSw1OSw0NSwxMDEsMTIxLDksNjMsMTY3LDE5NCw4NCwxMDIsNDgsMjMsMjEwLDExOCwyMjcsMTUyLDEzNSwxNjMsNDMsNDIsODQsMjQ0LDM3LDIwNSwxMzUsMTUxLDExNSwxNTAsODgsMTgwLDIzMywxNDYsNjksMTksMjA3LDY2LDQyLDEyMywxNzIsMjQ4LDg4LDE4MywxNjksMzAsMzUsNTIsNzYsODUsMTUzLDE1OCw5LDExNSw5MCwyNTEsOTksMTUsMTY0LDE2MiwyMyw4NiwxNTAsMTcyLDg1LDEwMSwxMjksMjQxLDEzNCwxOTksMTI0LDI1LDAsMTkwLDE0NiwzNCw3MSwyNCw5NiwzOCwyMSw2MCwxOTEsMTQwLDE3MywyNTIsMTEzLDk1LDYwLDc1LDQ5LDU5LDI0NCwyMDMsMjQxLDEyOSw2OSwyMzUsMjIsMzIsMTUsMjM2LDE5MCwxMjIsMTAxLDY0LDExMSw3LDE3MSwyMjgsNTcsMTIxLDExNCwyMDMsMTMsMTAzLDksMzQsMTUsMzYsMjksMjIyLDIyNywxOTgsMTU0LDMzLDExMSwxMzcsMTY4LDExOCwxMDUsMjMxLDEwMSw2MSwzNSwxODAsNzgsODksNDMsMTQwLDkwLDI2LDUwLDIxLDE0MywyNDEsMjEyLDc3LDEzLDQ4LDE3MSwzMywxNjMsMjAzLDIwOCwxODEsMTk0LDEwOCw4MiwxMjUsMTE3LDgyLDY5LDEzLDE0OCw4Myw0NSw2NSw2NywxMTEsMTk3LDg2LDEwMywxNjYsMzAsMjUzLDI1Miw0OSwzNSw1NywxODMsNzUsOTEsMTQxLDE4Niw5MSwxNzAsMTQ3LDI1MSwxNjMsNDIsMTgxLDE3NCwyMjgsMjQsMjMsMTc3LDIxNSw3NiwyMDYsMjE5LDUyLDI1Miw4Niw5Miw2NCwxNjgsMjIsNzgsMTg3LDE0NCw3OCw4OCwxMDEsMjE4LDE0NiwxMDUsMTY2LDYsMTM0LDE1Niw2NiwxOTEsMTg1LDE5OSwxODYsMTIsNSw5NiwxMjUsNjQsNDQsMTQyLDEyLDE3MSwxMjksODQsNzYsNzEsMjE2LDk2LDMzLDE0NSw1MiwyNTUsMTQ2LDkxLDEzNSw1NSwxOTcsMzMsMjQ0LDE2Miw2OCw5Nyw0OCw2Nyw4OSwxMzMsMjAzLDE4NiwxMDAsODMsMTg1LDcwLDExOCw1OSwxODksMjQ4LDIxMywxNDIsOTUsMTk2LDExMiwxNzksMTQ4LDE4Nyw3LDEwOCwyOCwyMzUsMTMyLDEsMjAwLDIyOCwxODMsMTY0LDEzNSwxNiw4MiwyOCwxNjYsMzAsMjMxLDEzMyw4NSwyNTAsMTM4LDg5LDE5NiwxMTgsMTcyLDY1LDE5MCwyMzIsOCwxMyw1OCw4Myw3MiwyMzksMTAzLDQ2LDk2LDM2LDU1LDg5LDE4NywzNiwyNSwyMTYsMTA0LDIwMCwzNCw3NSwyMjAsMjM0LDExMiwyMjMsMCwxMjYsMTA2LDIxNiwxOTIsMTQ5LDQzLDg2LDIwLDE3MSwxMTUsODMsMjAzLDEyLDQ5LDE4NCw4MiwyMzgsMjEzLDE0MCw1NCwxNTEsOSw5MywxNzcsODIsNTQsMTIxLDMyLDEyNSwzMiwxMTEsMTE5LDE4NSwxMDEsMTM0LDI0MSwxNDQsMjI3LDIxNiwyOCwzOCwyMjgsNSwzNCwxMTQsMTExLDI5LDIwOSwyMDcsMTI1LDQ4LDM5LDIzMSwxMjksNzgsMjI5LDkxLDc0LDUsMjUyLDE2MCwxNjYsMTQ4LDIwLDQ4LDE0NywyMDIsMTk5LDI0NywxNzAsMjMxLDEyOSw4MywyMjQsMTY3LDQ5LDIzNCwxMzYsMTYzLDM0LDIyOCwxMTksMTUwLDE2LDU4LDE4NiwxOTgsMTMzLDQsNjgsMTU5LDE3NSw4MSwxNDAsMjQzLDI0LDgwLDc5LDI0NCwxMDgsODAsMTAzLDE0MywxNjEsODgsMjAzLDIyNSwxNjMsMTY0LDE5Nyw1LDgyLDI3LDEzOCw2OSwyNDAsMTg2LDkxLDIzMywxMzEsMTU4LDIwMiw0MCw5MCw4MSwxNjAsODIsMTQzLDEzNywxNjQsMTMxLDIwLDE3MywxNTMsMjUsOTUsODAsMjYsMTgxLDM4LDE0OCw1NCwxOTYsMTA0LDIzLDIzNywxMzYsMTYzLDIxOCwxNywyNyw2NiwyMjgsMTI3LDIxMSwyNTAsMTQ5LDE2NCw4NSwxMDksMTY5LDE4NiwyMDAsMSwyNTEsMjAxLDEwMywxMjMsMTgzLDE1NSw4Nyw5NCwxODcsMjM1LDE3OCw5OCw5NywyMDYsMjUsMTA1LDE2MSwyMDIsMjMxLDEyMyw2NiwxMzgsMTQsMTc3LDExNCwxNjEsNTksMTc3LDQwLDEzLDM5LDMwLDg0LDQ4LDE4NywyMzMsMTMxLDE4NiwyMDQsMTY3LDkxLDIyMywyNDMsMTkzLDkyLDIxMywxOTEsMTQsMTQ3LDE3LDEzNyw0LDM2LDk2LDU2LDQ1LDI3LDM1LDIwMSwxOTAsMTkzLDI0LDE3NywxNTQsODYsMTk3LDE1NiwxNjgsMTY4LDExNSwzMSwxMjUsMjI5LDM1LDgwLDg5LDk5LDIzNywyNTEsMTkwLDgyLDEsMTMzLDE1MCwyMTQsMjA1LDg3LDksMTk0LDIzNCwyMzQsODYsMTY4LDE0NCwxOTAsMjAxLDk3LDExMiwxNDEsNzQsNzYsMTQwLDk2LDUsMTE3LDY2LDcsMjU1LDQwLDE1OSw0Miw0MywxMTksODQsNSw5NywxLDIxNiw3MSwxMSwxNjcsMTQ1LDY1LDI1MSw1MCwxMDAsNTIsMTcwLDQsMTksMTksMTE5LDE0MCwxMCwxMCw4NSwxNyw4NCwzNywxMDksMjQwLDE5NCwyMywxNjYsMjM1LDEzMSw3NiwxODYsMzcsMTA0LDIxNywyOSwyMjcsMTU1LDYwLDEwOSw3NSwxMDIsMTcsMTI5LDE1NCwyMjUsMjIsOTksMTgyLDE3MCw1NiwyNSwyMzEsMTUxLDc5LDE2MSw1MCwxMCwyMjksMyw0MCwxOTYsMjYsMTc4LDEzNCwxNDUsMTcsMTU1LDExNiwxNDEsMjAsMTcyLDE0MSwxNjksNSw0MCwyMDksNjIsMzUsMzYsMTQsNzYsMjM2LDE5NCwyNDIsMTE2LDE4MCwyMzYsNDIsODQsOTEsMTE4LDEzOSwyMjUsMTc5LDE4OCwwLDEwNSwxMyw5OSwxODMsNTMsMjIyLDIxMywxMzEsMjMyLDIyNSw5NiwxOTMsNzEsNCwxMDQsNiwxNjUsMTc5LDIzNSw0OSwyMDcsMTE1LDM4LDEyMywxODUsMjksNjUsMTgxLDE3NiwxODUsNzAsMTY5LDE4MSwyNDEsMjIsNDEsOTAsNjYsMTIzLDg5LDE2MSwzMCwxNDgsMTE1LDEyNCwxOTgsNjQsNjYsMTAzLDEwMSw1MiwxOTcsMTkwLDEzMywxMDAsMTM5LDE0Miw4Myw5NywyNTIsMSwxMzIsMjMwLDIzNCwxODIsMjIxLDIxLDg5LDEwNSwxMjQsMTA4LDIyMCwxOTMsMTk2LDE1NSwxNTYsMTYxLDE1MSwyNDEsMTE3LDY0LDEyMCwyMDYsMzgsMjQxLDkzLDEwNiwyMTIsMjUsMTQwLDE1NCwyNywxODEsMjQxLDEwMSwyMTIsMzQsMTM5LDUxLDcsOTksMzQsMTY2LDYwLDUzLDE4Miw3OCwxOTEsNzMsMTU1LDI1MSw1NiwxNzEsNTMsMTk3LDM2LDQyLDE5OSwxNDgsMjUwLDE5NiwxOTIsMTM1LDEzNCwxMDIsMjU0LDI4LDExNywxMjYsODYsNSwxNTcsNTMsMTY1LDIzOCwxMzUsMTc4LDE4MSwzOSw3NiwxNCwzNCw4MCw2LDIyNywxNjIsMTIwLDEzMCwxMDMsMTcsMTg4LDE3Nyw1NCw5OCwxNDAsMTk1LDE3OCw2NywxNjAsMTY3LDE0LDE1Miw0OSwxNjMsNjcsMjUwLDE1MiwyMDgsMTE2LDMzLDI0NywxODIsMTIyLDExNiwxMjMsMTY5LDE5NCwxNjQsMTEyLDE1Niw3NCwxNDgsMTQ4LDY3LDEyNSwxMTksMzksMTE5LDEyNCw1NSwxMzMsMjEwLDU5LDEyNSw5MSwxODcsMTE0LDE0NSw3MSwxNzIsMTgxLDUxLDkxLDE2MSw1Nyw1NSwxNDgsODEsNDksMTc0LDEwOCwxNTMsMjA2LDI0Miw3OCwyMzksMTgsMTMwLDMyLDIxMSwxMjksMjIyLDY0LDgsMTI4LDE5MywxMjgsMTYsMTA2LDg1LDE1MCwzLDIwMiw3NCwxNjMsMjAyLDgsMTk3LDYzLDEzMywyMDQsMTg0LDE0OCwxNTMsMTg5LDYyLDE2OCwyNiwxODMsMjE5LDE1MCwyMDAsMjExLDU0LDI0OCw3NSwxMTEsMjM3LDIwMiwxNiwxOTMsMjI1LDE5MiwxODIsNTksMjAyLDM2LDIzMiwxODAsMTk3LDEzMCwxMjMsMTU5LDE1NCwxMjMsMjE2LDI0MCwxNjEsMjEyLDE0NSwxMTAsMTUwLDgwLDExOCwyMDgsOTAsMTA2LDgxLDcyLDIwLDY5LDYwLDI4LDE3Nyw5MCwxNjQsMTg5LDM5LDAsODMsODIsOTgsMTMwLDgwLDQ1LDE3Nyw3OSwxNzcsMjM1LDQxLDE0NCwyNSwzMiwyMiwxMjIsMzEsMTQ1LDIxNywxNjMsMTAxLDIyMCwxLDk4LDEwOCwxOTYsMTcxLDE4NiwxMzIsMjksMjQwLDIyNSwxMTIsNCw2Niw5NiwxNDgsMTg0LDE2NCwzNiwyMjMsMzAsMTEsNjksMTA1LDc3LDE5NiwxNjcsMTYsMTQ5LDE4NSwyNDYsMTUzLDEyOSwxODAsMTIwLDI0NCwxNTYsMTIsMTMxLDk3LDI0MiwyMjcsNzQsMTQ1LDc5LDddfX0sInByaXZhdGVfa2V5Ijp7ImtleSI6eyJ2ZWMiOls3NSw5OCw0Myw1NiwxNjYsNjIsMTQ4LDYwLDE1NSwyMDksMTk1LDE2OSw0MSwyNDksMTk2LDE5Nyw3LDE0OSwxNTQsNDEsMTIsMTA5LDIxNSw0MiwxOTMsNTIsMTA0LDE1NSw3Miw0MSwyOSw1Niw3OSwxNzIsMTk0LDE5MCwxNjksMjQwLDQ5LDE4NCwxNjEsMjQsMzksNDksMzgsNDgsMTIxLDE1MSwzMyw2NCw4MywxMTksMjM2LDM3LDE5NSwxMDIsMTA0LDIxMiwxMjAsMTg5LDYzLDE1MSw0Miw3MSwyMjYsMTgwLDEzNyw0MSw4LDM5LDEwMSwxNTgsMTQsMTk3LDY0LDkwLDcwLDY2LDI1LDIwMywxNjUsMzUsMTU0LDU2LDQzLDg3LDExMiwxOTksNTgsMTU1LDI3LDE0NywxMTIsOTUsMTc5LDExMiw3NywxNzIsNjYsMzIsMTM2LDYsMTUsMTM1LDE0Myw3NSwxMjIsMTk0LDIwOSw0MSwyMDcsOSw3LDk2LDExNCwyMzAsMTM4LDE0MCwyNTAsNzIsMTY0LDE1NCwxMzksMTc2LDEwNCw0OSwxMDMsMjcsMjgsMTQwLDIsMTMzLDEyMiwxMDUsMTA4LDYwLDU0LDE2MiwxNzAsMTc5LDE5MiwyMzIsMTg1LDE0NiwzOSwyMjgsMzAsMjAyLDE0OCwxNDMsNDksMjcsMTE3LDE5MywyMjksMTMyLDI1MCwxMDgsMTUxLDcxLDI0MCwxNjAsMzcsMTI0LDE5NCwyMDcsMjQxLDExMCwxNjgsMjI3LDUsMTkxLDEwMyw4OCwyNDksOTAsMTEyLDI0NywxMzgsNjYsNDUsNiwxMSwxNTUsMTIyLDU1LDE3MiwyMTgsMTUyLDM1LDgyLDEyOSwxMTEsMjQ4LDExOCw5NSwxOTgsMTM3LDIzNiwyNywzNCwyMTMsNzUsMTM5LDIwLDIyOSwxMiw5MywxNTAsMTI1LDcyLDk2LDEyOCwyMDAsMTA3LDI3LDQ0LDIyMCwxODUsNDQsMTE0LDg3LDEsMTM2LDUzLDEzNSw1MiwxNCw5NSwxNjUsMTM2LDIyNiwxNzAsMTA2LDE3MCwxNTUsMTA0LDIxMSwxOSw2NSw1NiwxNDUsMTY1LDI1MywxNDgsOTYsMTQsMTg0LDE5NCwyNTUsNzEsMjAsMTMzLDc1LDIwLDc4LDE1NCwxNTAsMTQ4LDEzMywxMDQsOTEsODgsMTMwLDExOSw0Miw1MSwyNywyNDEsMTMwLDI0NCwyMzAsMTEwLDcxLDUwLDEyMCwyMzAsNTcsMjgsMTU0LDE2MywxOSw0OSwyMzIsMTQ5LDUxLDE0OSwyNSwxNzIsMjQsMTc5LDY0LDc2LDcsMzYsMjI0LDEzMCwxMjUsMTkzLDk3LDE0NSwxLDIxLDMzLDE4MCwyOSwyMjQsMTU0LDE3NiwxMTcsMTAyLDE2MCw4NSwyNDcsMTExLDI1MCwyMzQsMjA1LDIzMCw4NiwyMDcsMjUxLDM4LDgsNjMsMTY4LDE2NCwyNTIsMTM0LDEzNiwxODIsNzAsMTI2LDY3LDExMiwxMzQsMTg5LDQzLDE4NywyMjYsMTg4LDk3LDg0LDE2NCwyMDQsOTAsNzAsNjcsMjQ2LDE0MCwxMjEsMTk0LDY4LDQ2LDgxLDIzMCwxMzYsMTY3LDE3Nyw2NywyMzYsMTYwLDE3MCwxMzIsMTE3LDE1MiwxNDEsMTE0LDE4NSwyMDAsMTM0LDE3LDIyLDIxNSw2MywyMzcsMTM4LDEzLDE2NCwxMTcsODAsMTU3LDE2MiwxMDQsMTksMjE1LDE0MCw4LDUwLDE5Myw0OSwyNDgsNjksMTk1LDE3OSwxNTYsMTQ3LDI3LDM2LDE0NywxOTcsNzcsMjAzLDI1MSwxNTIsNjEsMjM1LDQzLDE5NywxMCwxOTAsMjI4LDU4LDE3MCwxNjksMjQ3LDY4LDEyMywyMjUsMTczLDE1MiwxNzAsMTYzLDI1MiwxMzAsNiwzOSwxOTMsMTgzLDIwNywxNiwxNjgsMTAxLDAsNTAsMjExLDIzNiwyNSwxMzEsMTU0LDQyLDU2LDI0OSw5MSwxMzQsNTAsOSwxNDMsMjA5LDI1LDEwOSwxMDQsMTgzLDIwOSw2OSw0NywxMzQsOTgsODUsMTQwLDEzOCwxOTksMjIxLDIxNywxMDIsMjksMTU1LDU1LDg4LDE0NSwxLDEyNCwyMTEsMTc5LDEzNiwyMTUsMTQ0LDE2MCwyMzQsMTIyLDc5LDE4NCwxNjEsMTM1LDM1LDE3MywxMDAsOCwxNTQsNzMsMTI0LDEzMiwxNTAsMTAxLDQ2LDIxOCwyMDQsMTk0LDE3OSw1Niw2MiwxMjAsMTQ2LDE3NiwyMzcsNTMsMTE5LDkwLDY4LDE5LDIzNywyMCwxNjYsMjAsOTksMTEzLDE4Niw5Niw2NCwyNDgsMjM1LDE0LDIwNSwxMzgsMTA4LDMxLDEwLDEyNSwyNDQsMzgsMTU1LDExMCw3NCw0NCw5OSw4Nyw5NiwxMzQsMzYsMTIsMTUxLDczLDE5LDEzNSwxOTIsMjA0LDIwMiwxMzcsMTc2LDM3LDU3LDE3MiwxMDMsMjEzLDk1LDY5LDE0NCwxODgsMzcsMjQsMjQsNDMsMTQ1LDE4MywxNzAsNiwyOCwxNDAsNywxMTIsMTU2LDEyMCwxMjAsMTgzLDEyLDE5MSwyMjksODMsMzMsMTY5LDExNiwzNCwxMjYsMTAyLDU2LDE1NywxMjEsNDQsMTksMjAwLDIxLDEwLDUzLDE4MywyNiwyMTksMTE2LDIwNSwxNjgsMTY3LDI1NSw3LDE3NywxOTcsNDAsMTMsMjU1LDE4Nyw1NywxMSwxODgsMTc1LDU5LDI0OCw4NiwyNTAsMTIsMTUsMTYzLDIxNywxNTYsMTgsMTYzLDQ4LDEyNywxMzksMTAyLDE5MCwxNzAsMTkyLDEsMTgwLDE2NiwxMDYsMTk4LDMyLDE3Nyw5OSw5MCwxODIsMjQyLDYwLDE1MiwxNiwxNzYsMTUwLDE4NCwxMDEsNzcsNzAsMTc2LDI4LDE3Nyw5MCwxMCwyNTAsNjAsMjQzLDE5LDEyMywxNjksOTAsNDgsMjUyLDI0MiwxODAsMTU3LDE3LDU3LDEzMyw3MCwxMTgsMTYyLDE2Nyw5LDE3MywxMzYsMTc1LDExNywyMjQsMjMsNDYsNDIsMTcyLDE1MiwxNjAsOTYsMTYwLDIwMCwxNSw1NiwxMzQsNTEsNzEsODQsOTAsOTUsNzUsMjcsMTgsMTQ3LDQ4LDEyMyw4OCwxOTYsMTgwLDg3LDU3LDY3LDE4MSw4LDIyNyw4MCwxMCwyMDAsMTU2LDIxLDIxOSwyNTIsMTA5LDE1MCwxMDAsMjMsNDUsMTg2LDE5NiwyMzcsMjI4LDYwLDIwMiwxNjcsMjMsNzYsMTgyLDk3LDIzLDEzNiw1MCw4NSwxNjksMTE3LDIxNSw4MywyMSwxMzAsMTUxLDIwMSwxNjYsMTY1LDE3NiwxMDAsMjEyLDUzLDkyLDE0NSwxMTEsNjksMTY2LDM4LDI0OCwyMywyNiwyNTAsMjE2LDE4MSwyNDcsMTkzLDIwNiwxMTQsMjE2LDEzMiwxMzMsMTA4LDUwLDE1Niw1OSwyOSwxODUsMTIsMTYxLDgxLDEzMSwyMDEsMTY0LDE1MSwxOCwxNDksNzYsMTIzLDEzMiwyNSw5NSwxNCwxMTQsMTU5LDE5MywyNTAsMTAwLDExNSwxNzEsMTQwLDEyNiw4OSw4NiwxNTUsMjE4LDE3MiwxNjEsMjExLDQzLDY1LDEwMSw5LDE0MSwyNDYsMjA1LDUyLDI0NywxNTEsOTMsNzMsMTQ5LDEzMCwyMjgsMjA1LDUyLDEsMjgsNzcsNzIsMjA3LDIyNiwxMTQsMzQsMTc3LDE4Nyw5OCw4LDE4NywxNDMsMTQsMTk4LDg2LDQ3LDEwMiwxMjEsMjA4LDE0OCw3Niw5Nyw3NCw0NywxMzUsMTUxLDE2MSwxODQsMzgsMTM2LDIyNCwxMDYsMTc2LDE3MiwxMTQsMTQ1LDIzMywxNTYsOTgsMTQzLDE4OCwxMjAsMTQ5LDU3LDcsMTk2LDE5MywzNCwxNzgsOTEsNDUsODEsMjMyLDQ0LDExOCwyNDYsNzUsMTc0LDE3OCw5NSw3OSwxMjIsMTE0LDI1NCw2MCwxNTcsODksMjYsMTkyLDEyMCwxMCw3MywyMDUsMTE3LDE1MSwxMjYsMTk3LDkzLDEzMywyMDgsMTc5LDUyLDM2LDcxLDIxMCwyNDcsMTI2LDEyOSw1MiwzMSw5NCwxNzAsOCwxNjEsMjI1LDEwMiwxNzEsMiwyMiwxMzksOTYsNywxMDQsMTg2LDQwLDExMSwyNywxMDMsMjUyLDQyLDE3Nyw5Miw3MSwxMjgsMjI4LDI1MiwxOTMsMTU4LDExLDYwLDEzMyw0MCwxMDIsMTc0LDQ5LDE4OSwxNTQsMTc5LDkxLDU4LDE1NiwxMSwyNDQsMTUzLDEwMSw3MywxLDMyLDE1NCwxODgsMTE1LDc2LDE0NCwxMDgsMTcsMjEzLDkyLDIxNiwyMSw5MSwyMzksMjMxLDE4Niw5OSw0OCwxNzEsMTEzLDkxLDU1LDIwMiwxMzIsMTQxLDE0NSwxMDQsMTQ4LDM5LDM5LDEzLDQzLDI0NiwxNDEsMTU1LDIzNSwxMiw0LDIxOCwxODYsOTcsMiwxOCwyMjgsMTMyLDE1NCwxNywyMjAsMTIzLDIwOCwzOCwyMDQsMzgsODksMTgxLDUsMjMzLDcyLDI0OCwxNTYsODIsMjEzLDE3MSwyLDE1Miw1OSwxMTEsODIsMTY1LDU2LDE5MSw5LDUwLDI0OSwxNjYsOTcsMzIsNDgsMTI4LDIxNSw4OCw4MywxMTgsMzcsMTI2LDg3LDE2OCwxNjgsODMsMjM0LDE5MywxNjIsMjEsNjksMTAzLDEzMywxOCw5NywxMDIsNDEsMTk1LDE4Nyw3Nyw2NCwxNjIsMjgsMjEsOCw1MCw5NCwxMDEsMTYwLDE0MywxMzgsMSwxMDcsMTE3LDExNywxNjksMjAsNjcsMzAsMjMwLDExNSwyMzIsMTg4LDQ2LDgzLDEwMiwyMDIsMjM3LDE2NSw1NywxMTksNywxNSwxOTIsMjUxLDcyLDIxOSw0MCw1MiwxNjYsNTAsOTMsMzYsMTUyLDIwMSwyNDYsODEsMTEzLDc0LDc0LDEzMCw2LDE2NCwxMiwxMTgsMjMzLDM3LDM4LDI0Niw1OCwzNywxMjEsMTAyLDc4LDAsMjIsMzcsMTkzLDQxLDEzOSwxMDAsMTg5LDQ2LDE5OSwxODUsMjA3LDU2LDEzMCwxMjEsOCwwLDIwOSw3Miw2MSwxMTYsMTY2LDYyLDEyOSwxNzcsMTYzLDEyNCwyMTQsMTA4LDgwLDg2LDE0NSwyOCwxNDQsNjQsMTg1LDQsODksNjAsMTE2LDMzLDE5LDIyNSwxNTcsNzAsMTIzLDEzMyw1MywxODAsODksNjcsMTQ5LDQzLDY2LDE4MywxNDMsNDcsMzQsMTgwLDg3LDE2LDExLDExMCwyNTEsNjEsMjAyLDIzMywxNTMsOCwxMDIsOTEsMTk4LDQ5LDU5LDE2MCwxOTgsODYsNTgsMjQwLDE1OCwxNTksODEsMjA3LDk0LDIwOSwxMjAsMjI4LDg0LDU1LDIzOSwyMiwxOTcsMTAzLDIzNCw3NywxOTgsMjQxLDQ3LDksODAsMTYyLDE4NiwxNTQsMTgyLDI2LDEzOSw4NiwyNDEsMjAsMTA0LDE2OSwyMTksMTM3LDIwNyw4LDE4LDE1NCwxNiwyNSwxMTksMTQ2LDE4NiwxNTMsODksMiw5LDEwNCw0MiwzNiwxNiwzNywyMSwxOTUsOTUsMzksMTk2LDE1NCwxODUsNzQsMTMyLDI0Niw4OSwxNDUsMTIwLDIyNywyMSw2MiwxODIsMTAyLDIzMiwyNTIsMiwxNzksMTY5LDQyLDE0NSwxMDMsMTkxLDIwNSwyMzQsMTE2LDE5MCwxMDgsMTQyLDk5LDU5LDQ1LDEwMSwxMjEsOSw2MywxNjcsMTk0LDg0LDEwMiw0OCwyMywyMTAsMTE4LDIyNywxNTIsMTM1LDE2Myw0Myw0Miw4NCwyNDQsMzcsMjA1LDEzNSwxNTEsMTE1LDE1MCw4OCwxODAsMjMzLDE0Niw2OSwxOSwyMDcsNjYsNDIsMTIzLDE3MiwyNDgsODgsMTgzLDE2OSwzMCwzNSw1Miw3Niw4NSwxNTMsMTU4LDksMTE1LDkwLDI1MSw5OSwxNSwxNjQsMTYyLDIzLDg2LDE1MCwxNzIsODUsMTAxLDEyOSwyNDEsMTM0LDE5OSwxMjQsMjUsMCwxOTAsMTQ2LDM0LDcxLDI0LDk2LDM4LDIxLDYwLDE5MSwxNDAsMTczLDI1MiwxMTMsOTUsNjAsNzUsNDksNTksMjQ0LDIwMywyNDEsMTI5LDY5LDIzNSwyMiwzMiwxNSwyMzYsMTkwLDEyMiwxMDEsNjQsMTExLDcsMTcxLDIyOCw1NywxMjEsMTE0LDIwMywxMywxMDMsOSwzNCwxNSwzNiwyOSwyMjIsMjI3LDE5OCwxNTQsMzMsMTExLDEzNywxNjgsMTE4LDEwNSwyMzEsMTAxLDYxLDM1LDE4MCw3OCw4OSw0MywxNDAsOTAsMjYsNTAsMjEsMTQzLDI0MSwyMTIsNzcsMTMsNDgsMTcxLDMzLDE2MywyMDMsMjA4LDE4MSwxOTQsMTA4LDgyLDEyNSwxMTcsODIsNjksMTMsMTQ4LDgzLDQ1LDY1LDY3LDExMSwxOTcsODYsMTAzLDE2NiwzMCwyNTMsMjUyLDQ5LDM1LDU3LDE4Myw3NSw5MSwxNDEsMTg2LDkxLDE3MCwxNDcsMjUxLDE2Myw0MiwxODEsMTc0LDIyOCwyNCwyMywxNzcsMjE1LDc2LDIwNiwyMTksNTIsMjUyLDg2LDkyLDY0LDE2OCwyMiw3OCwxODcsMTQ0LDc4LDg4LDEwMSwyMTgsMTQ2LDEwNSwxNjYsNiwxMzQsMTU2LDY2LDE5MSwxODUsMTk5LDE4NiwxMiw1LDk2LDEyNSw2NCw0NCwxNDIsMTIsMTcxLDEyOSw4NCw3Niw3MSwyMTYsOTYsMzMsMTQ1LDUyLDI1NSwxNDYsOTEsMTM1LDU1LDE5NywzMywyNDQsMTYyLDY4LDk3LDQ4LDY3LDg5LDEzMywyMDMsMTg2LDEwMCw4MywxODUsNzAsMTE4LDU5LDE4OSwyNDgsMjEzLDE0Miw5NSwxOTYsMTEyLDE3OSwxNDgsMTg3LDcsMTA4LDI4LDIzNSwxMzIsMSwyMDAsMjI4LDE4MywxNjQsMTM1LDE2LDgyLDI4LDE2NiwzMCwyMzEsMTMzLDg1LDI1MCwxMzgsODksMTk2LDExOCwxNzIsNjUsMTkwLDIzMiw4LDEzLDU4LDgzLDcyLDIzOSwxMDMsNDYsOTYsMzYsNTUsODksMTg3LDM2LDI1LDIxNiwxMDQsMjAwLDM0LDc1LDIyMCwyMzQsMTEyLDIyMywwLDEyNiwxMDYsMjE2LDE5MiwxNDksNDMsODYsMjAsMTcxLDExNSw4MywyMDMsMTIsNDksMTg0LDgyLDIzOCwyMTMsMTQwLDU0LDE1MSw5LDkzLDE3Nyw4Miw1NCwxMjEsMzIsMTI1LDMyLDExMSwxMTksMTg1LDEwMSwxMzQsMjQxLDE0NCwyMjcsMjE2LDI4LDM4LDIyOCw1LDM0LDExNCwxMTEsMjksMjA5LDIwNywxMjUsNDgsMzksMjMxLDEyOSw3OCwyMjksOTEsNzQsNSwyNTIsMTYwLDE2NiwxNDgsMjAsNDgsMTQ3LDIwMiwxOTksMjQ3LDE3MCwyMzEsMTI5LDgzLDIyNCwxNjcsNDksMjM0LDEzNiwxNjMsMzQsMjI4LDExOSwxNTAsMTYsNTgsMTg2LDE5OCwxMzMsNCw2OCwxNTksMTc1LDgxLDE0MCwyNDMsMjQsODAsNzksMjQ0LDEwOCw4MCwxMDMsMTQzLDE2MSw4OCwyMDMsMjI1LDE2MywxNjQsMTk3LDUsODIsMjcsMTM4LDY5LDI0MCwxODYsOTEsMjMzLDEzMSwxNTgsMjAyLDQwLDkwLDgxLDE2MCw4MiwxNDMsMTM3LDE2NCwxMzEsMjAsMTczLDE1MywyNSw5NSw4MCwyNiwxODEsMzgsMTQ4LDU0LDE5NiwxMDQsMjMsMjM3LDEzNiwxNjMsMjE4LDE3LDI3LDY2LDIyOCwxMjcsMjExLDI1MCwxNDksMTY0LDg1LDEwOSwxNjksMTg2LDIwMCwxLDI1MSwyMDEsMTAzLDEyMywxODMsMTU1LDg3LDk0LDE4NywyMzUsMTc4LDk4LDk3LDIwNiwyNSwxMDUsMTYxLDIwMiwyMzEsMTIzLDY2LDEzOCwxNCwxNzcsMTE0LDE2MSw1OSwxNzcsNDAsMTMsMzksMzAsODQsNDgsMTg3LDIzMywxMzEsMTg2LDIwNCwxNjcsOTEsMjIzLDI0MywxOTMsOTIsMjEzLDE5MSwxNCwxNDcsMTcsMTM3LDQsMzYsOTYsNTYsNDUsMjcsMzUsMjAxLDE5MCwxOTMsMjQsMTc3LDE1NCw4NiwxOTcsMTU2LDE2OCwxNjgsMTE1LDMxLDEyNSwyMjksMzUsODAsODksOTksMjM3LDI1MSwxOTAsODIsMSwxMzMsMTUwLDIxNCwyMDUsODcsOSwxOTQsMjM0LDIzNCw4NiwxNjgsMTQ0LDE5MCwyMDEsOTcsMTEyLDE0MSw3NCw3NiwxNDAsOTYsNSwxMTcsNjYsNywyNTUsNDAsMTU5LDQyLDQzLDExOSw4NCw1LDk3LDEsMjE2LDcxLDExLDE2NywxNDUsNjUsMjUxLDUwLDEwMCw1MiwxNzAsNCwxOSwxOSwxMTksMTQwLDEwLDEwLDg1LDE3LDg0LDM3LDEwOSwyNDAsMTk0LDIzLDE2NiwyMzUsMTMxLDc2LDE4NiwzNywxMDQsMjE3LDI5LDIyNywxNTUsNjAsMTA5LDc1LDEwMiwxNywxMjksMTU0LDIyNSwyMiw5OSwxODIsMTcwLDU2LDI1LDIzMSwxNTEsNzksMTYxLDUwLDEwLDIyOSwzLDQwLDE5NiwyNiwxNzgsMTM0LDE0NSwxNywxNTUsMTE2LDE0MSwyMCwxNzIsMTQxLDE2OSw1LDQwLDIwOSw2MiwzNSwzNiwxNCw3NiwyMzYsMTk0LDI0MiwxMTYsMTgwLDIzNiw0Miw4NCw5MSwxMTgsMTM5LDIyNSwxNzksMTg4LDAsMTA1LDEzLDk5LDE4Myw1MywyMjIsMjEzLDEzMSwyMzIsMjI1LDk2LDE5Myw3MSw0LDEwNCw2LDE2NSwxNzksMjM1LDQ5LDIwNywxMTUsMzgsMTIzLDE4NSwyOSw2NSwxODEsMTc2LDE4NSw3MCwxNjksMTgxLDI0MSwyMiw0MSw5MCw2NiwxMjMsODksMTYxLDMwLDE0OCwxMTUsMTI0LDE5OCw2NCw2NiwxMDMsMTAxLDUyLDE5NywxOTAsMTMzLDEwMCwxMzksMTQyLDgzLDk3LDI1MiwxLDEzMiwyMzAsMjM0LDE4MiwyMjEsMjEsODksMTA1LDEyNCwxMDgsMjIwLDE5MywxOTYsMTU1LDE1NiwxNjEsMTUxLDI0MSwxMTcsNjQsMTIwLDIwNiwzOCwyNDEsOTMsMTA2LDIxMiwyNSwxNDAsMTU0LDI3LDE4MSwyNDEsMTAxLDIxMiwzNCwxMzksNTEsNyw5OSwzNCwxNjYsNjAsNTMsMTgyLDc4LDE5MSw3MywxNTUsMjUxLDU2LDE3MSw1MywxOTcsMzYsNDIsMTk5LDE0OCwyNTAsMTk2LDE5MiwxMzUsMTM0LDEwMiwyNTQsMjgsMTE3LDEyNiw4Niw1LDE1Nyw1MywxNjUsMjM4LDEzNSwxNzgsMTgxLDM5LDc2LDE0LDM0LDgwLDYsMjI3LDE2MiwxMjAsMTMwLDEwMywxNywxODgsMTc3LDU0LDk4LDE0MCwxOTUsMTc4LDY3LDE2MCwxNjcsMTQsMTUyLDQ5LDE2Myw2NywyNTAsMTUyLDIwOCwxMTYsMzMsMjQ3LDE4MiwxMjIsMTE2LDEyMywxNjksMTk0LDE2NCwxMTIsMTU2LDc0LDE0OCwxNDgsNjcsMTI1LDExOSwzOSwxMTksMTI0LDU1LDEzMywyMTAsNTksMTI1LDkxLDE4NywxMTQsMTQ1LDcxLDE3MiwxODEsNTEsOTEsMTYxLDU3LDU1LDE0OCw4MSw0OSwxNzQsMTA4LDE1MywyMDYsMjQyLDc4LDIzOSwxOCwxMzAsMzIsMjExLDEyOSwyMjIsNjQsOCwxMjgsMTkzLDEyOCwxNiwxMDYsODUsMTUwLDMsMjAyLDc0LDE2MywyMDIsOCwxOTcsNjMsMTMzLDIwNCwxODQsMTQ4LDE1MywxODksNjIsMTY4LDI2LDE4MywyMTksMTUwLDIwMCwyMTEsNTQsMjQ4LDc1LDExMSwyMzcsMjAyLDE2LDE5MywyMjUsMTkyLDE4Miw1OSwyMDIsMzYsMjMyLDE4MCwxOTcsMTMwLDEyMywxNTksMTU0LDEyMywyMTYsMjQwLDE2MSwyMTIsMTQ1LDExMCwxNTAsODAsMTE4LDIwOCw5MCwxMDYsODEsNzIsMjAsNjksNjAsMjgsMTc3LDkwLDE2NCwxODksMzksMCw4Myw4Miw5OCwxMzAsODAsNDUsMTc3LDc5LDE3NywyMzUsNDEsMTQ0LDI1LDMyLDIyLDEyMiwzMSwxNDUsMjE3LDE2MywxMDEsMjIwLDEsOTgsMTA4LDE5NiwxNzEsMTg2LDEzMiwyOSwyNDAsMjI1LDExMiw0LDY2LDEwMiwxMTksMTQyLDIyNiwyNDMsNDAsMTg0LDI1MSwxMzgsOTMsMjA5LDIyMiwyMTUsMjA4LDExMCwyNDIsMTYsMjksMjUyLDUyLDIyMiw5MSw4NSwxNDYsNTYsNjksNjYsMjE4LDQ2LDkxLDM5LDI0LDI0NSw1NywyNDgsMjE0LDYsMzgsNzgsNjQsOTAsMTM3LDYzLDcyLDI5LDIyNCwxMjAsMTM5LDM4LDI5LDIyNCwzNSwxMDAsMTA2LDcwLDEzMywxMywxMSwxMzcsMjQ0LDE0MCwyMzUsMjQsMjIxLDE0MSwxNywxNTYsNDYsMTAsMTkzLDIyOCwxNjgsMTMwLDE4MSwyMzMsMTcsMTQzLDEyOSw1NSwxMDksMjAsMzUsMjI2LDQ5LDE2OCwxMzcsMTQ3LDk1LDE3MCwxMzAsMjQwLDYwLDEzOSwxMDMsMTc0LDExNiw5NiwxNDgsMTg0LDE2NCwzNiwyMjMsMzAsMTEsNjksMTA1LDc3LDE5NiwxNjcsMTYsMTQ5LDE4NSwyNDYsMTUzLDEyOSwxODAsMTIwLDI0NCwxNTYsMTIsMTMxLDk3LDI0MiwyMjcsNzQsMTQ1LDc5LDddfX19XQAAAAAAAABYAAAAAAAAr2BHcm91cFN0YXRleyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJQZW5kaW5nQ29tbWl0Ijp7Ik1lbWJlciI6eyJzdGFnZWRfcHJvcG9zYWxfcXVldWUiOnsicHJvcG9zYWxfcmVmZXJlbmNlcyI6W3sidmFsdWUiOnsidmVjIjpbMTQ4LDEwNiwyNSwxMDQsMjA3LDE3Myw1NCw4OCwxNzMsNjAsMTg4LDE4MSwxMiwyNTEsOTAsMTM2LDIxNCwyMDIsMiwxNSwxNzgsNSwxMzIsOCw1LDU3LDIsNDUsNjcsMTk0LDcwLDgxXX19XSwicXVldWVkX3Byb3Bvc2FscyI6W1t7InZhbHVlIjp7InZlYyI6WzE0OCwxMDYsMjUsMTA0LDIwNywxNzMsNTQsODgsMTczLDYwLDE4OCwxODEsMTIsMjUxLDkwLDEzNiwyMTQsMjAyLDIsMTUsMTc4LDUsMTMyLDgsNSw1NywyLDQ1LDY3LDE5NCw3MCw4MV19fSx7InByb3Bvc2FsIjp7Ikdyb3VwQ29udGV4dEV4dGVuc2lvbnMiOnsiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOlt7IlJlcXVpcmVkQ2FwYWJpbGl0aWVzIjp7ImV4dGVuc2lvbl90eXBlcyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxfdHlwZXMiOltdLCJjcmVkZW50aWFsX3R5cGVzIjpbXX19XX19fSwicHJvcG9zYWxfcmVmZXJlbmNlIjp7InZhbHVlIjp7InZlYyI6WzE0OCwxMDYsMjUsMTA0LDIwNywxNzMsNTQsODgsMTczLDYwLDE4OCwxODEsMTIsMjUxLDkwLDEzNiwyMTQsMjAyLDIsMTUsMTc4LDUsMTMyLDgsNSw1NywyLDQ1LDY3LDE5NCw3MCw4MV19fSwic2VuZGVyIjp7Ik1lbWJlciI6MH0sInByb3Bvc2FsX29yX3JlZl90eXBlIjoiUHJvcG9zYWwifV1dfSwic3RhdGUiOnsiR3JvdXBNZW1iZXIiOnsiZ3JvdXBfZXBvY2hfc2VjcmV0cyI6eyJpbml0X3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTg0LDUxLDcxLDk2LDE3NSwyMTEsMTk4LDQsMTg3LDUzLDE0MiwxNzksMTUxLDksMzAsNjksMTQ3LDIwOSwxOTMsMTc5LDE4OCwxMzgsMTYyLDE4NCwxOTEsNjcsMTY1LDE2MiwxOTQsNjEsMjQ2LDE2M119fX0sImV4cG9ydGVyX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTkxLDIxOCw0Myw2NiwxNDQsOTIsMCwyMzAsMjI2LDMzLDE4OSwxNjUsNjksMSwyNTAsMjQ0LDI4LDIwMiwxNzMsMjI4LDkxLDg1LDEwMywyNDksMTI3LDQ1LDEwLDk5LDIyOCw4MywwLDEwNF19fX0sImVwb2NoX2F1dGhlbnRpY2F0b3IiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6Wzk2LDEyNiwyMjgsODQsMjA4LDEwNywxMDEsOTQsMTU5LDg0LDIzNywxNjMsNTksNDQsMTMxLDIxNywyNDcsNTYsMjE4LDEyMSwxMTQsMjU1LDMxLDY3LDcyLDM0LDEsNjcsMTYsMTY2LDE2Miw2NV19fX0sImV4dGVybmFsX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTgsMjE1LDQyLDIxNSwyNDUsNjMsMTM2LDEyMywxMiw0NiwxMjksMjA0LDUwLDE0OCwyMzQsMjIyLDQ0LDM2LDE2OCwxNjIsMjA3LDEwOCwyNSwxMzgsMTA5LDIzNSwxNTQsMiwyNDUsMzAsMTMxLDldfX19LCJyZXN1bXB0aW9uX3BzayI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbOTIsMzAsMTgsMzYsMTc2LDIyMCw0Niw0MywxOTAsNyw0NSw3OCwyNDEsMTAyLDE0MywyNDYsMjcsMTYsMTYwLDIyNCwyNCwxMTQsMjUsNTQsMTI4LDIyNywyMiw1Miw2MywyMDYsMTAyLDExXX19fX0sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzIwMiwxMTcsMjQ3LDEwMCw2OSwyMjEsMjAzLDIxLDIwOCwxNTgsMTExLDI0OSwxNjUsMzgsMjM4LDQ0LDE3MywxNTAsMjExLDE0MiwyMTMsMjksOTYsMjAwLDExMCwyNTEsNDMsMTksMTgyLDI0NiwyNDYsOF19fX0sIm1lbWJlcnNoaXBfa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxOTAsMjQ1LDExMiwxNjAsMzIsMTMsOTgsMTQwLDUzLDEwNiwyMCwyMTYsMjI5LDE3MCw4NywxOTMsNDMsMTM4LDk4LDE0MSwxMzEsMjIxLDE2MSwxMDMsNjEsMjUwLDkwLDYxLDU2LDExMywxMjIsMjM1XX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNDQsMjQzLDUwLDExNCw2MywxNzYsMTg3LDkyLDExMyw0NywxNTIsMjUsMzYsMTc2LDIyOSwxMTQsMTg2LDI1Miw3MiwyMjIsNzIsNzIsOTQsMTQzLDYzLDgzLDAsMTg4LDQxLDI0OSwyMDEsMTQ4XX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsNzcsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwyLDMyLDIxNywxMTcsMTIxLDE4NiwxMTMsMTA4LDE2MCw5MSwyNSwxNjgsMjUzLDU4LDE4NywyMTQsMjQ4LDExMCwxNjcsMjUzLDQ5LDkwLDU4LDEwNCwxMzgsMSwyMCwxOTksMjUxLDIyNywxODksNDksMjAyLDE4MCwzMiwxNywyMzgsMjQ4LDI0LDIzOSwxNzksMTQsMjQxLDE4OSwwLDE1LDE0NCwxMDQsMTcsODYsMTEzLDE4Nyw1Nyw5MSwyMDEsNzQsMjQ4LDk5LDI1NCw3NiwyMzAsMTMsNTAsMjIxLDE0Myw3Nyw1MCw4LDAsMyw1LDIsMjQwLDY2LDAsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGwsbnVsbF0sInBhcmVudF9ub2RlcyI6W3sic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzYxLDE3MCwxNDksMjM4LDIzMCwyMjcsNDMsMTE2LDE5NiwxOTYsMjQ4LDEzOCwzNCwyMTksODMsMTk1LDE3NCwxMjYsOSwzMiwxMiwyMjYsMTMyLDEwNywxNjksMTk1LDI0MywyMiwzOSwxNTQsMTQsMTM4XX19fSxudWxsXSwiaGFuZHNoYWtlX3NlbmRlcl9yYXRjaGV0cyI6W251bGwsbnVsbF0sImFwcGxpY2F0aW9uX3NlbmRlcl9yYXRjaGV0cyI6W251bGwsbnVsbF0sInNpemUiOjN9fSwic3RhZ2VkX2RpZmYiOnsic3RhZ2VkX2RpZmYiOnsiZGlmZiI6eyJsZWFmX2RpZmYiOnsiMCI6eyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzM0LDIxMywxMjYsMTAyLDE4MCwxOTcsNywxMTksNSwxNDgsMjMxLDU3LDE1Niw4OCwxNTQsMjcsMTAxLDEsMTE2LDEzMiw1NCw1OCw4MywxMzAsMjQ3LDI0Nyw3MywxNzgsNCwxNDQsMTI2LDIxNiwxMDUsMjMyLDE0MCwxMywyMzIsMjI2LDE4LDIzMSwxNjMsMjA3LDE1OCwyMzAsMTA2LDgxLDUsNDMsMTE4LDgyLDIsNzAsNDAsNCwyMDYsMTg3LDE1OCwxOTksMTE4LDgwLDU2LDIyNCwzOSwyMTksMTE4LDE1MywzNywyNDUsMjUsNDYsMjI5LDE5MywxMzMsNTIsMjA0LDE1NiwzNSwyMDQsMjM5LDEyNCwxNzMsMjIwLDY1LDE2NCwxOTgsMTY2LDE1LDIyLDEwNCwxOTIsNjksMjM2LDE0OCwxMDYsMTc5LDE3NSwzOSw5NiwxOCwyNyw1NSwxODEsMzcsMTYsNjAsMTksMTc4LDExMiw3MywyMTcsMTgzLDE3OSwxMDgsMTQ5LDIzMCwxNzIsMjA1LDk4LDEyMCwxNDIsMjUzLDE3LDE3MCw2NywxMTcsMTk2LDE4MiwxNzIsMTAxLDIzNiwxNzAsMTIxLDE4Niw1Nyw3NywzMyw2NiwzMiw3MywxMTcsMTcyLDEyMiwxNTAsMTM0LDgwLDIyMCw5MiwyMzksMjI5LDksNzMsMjE5LDE2OCwzMywyNTEsMzMsOTcsMTcxLDIwNCw2NiwxNDAsNjMsMzUsMTUzLDE4OSwyMjksMTA2LDEzOSwxMDEsMTU1LDE4NSwxNjAsMjEwLDM4LDUwLDQxLDE4Myw2OSw1OSwxNjAsNDksMTEyLDE2OCwyMzQsMTM0LDksMjAzLDM2LDE5OSwxNjIsMTA4LDUxLDE3NCwyNDEsMTI3LDczLDE3MiwxODcsNTEsMjYsMTUyLDE3MywxNDksODAsODUsNTgsMTY1LDE4Miw4NywyMDAsMjA3LDEyOSwxMjYsMTM1LDExOSwxNjUsMzAsMjMxLDEzNSwyMjQsMTkzLDE2OCwwLDE2MSwxMzgsNjAsMjQ4LDEwLDE4Niw0OCwzMSw5MywxNzgsNywxOTcsMTk5LDU0LDI0NCwxNTAsMTU3LDAsMTIxLDY4LDI0MCwyNDcsMTEwLDE0OSw2OSwxMTIsMTc3LDEwLDE5OCwyNDksMTIzLDE1MywxMjgsMTkyLDIzLDI2LDU2LDc0LDIyNywyMTcsOTcsMTMyLDE4MywxNSwyMiwxMjAsMTc1LDkzLDQ4LDE4LDExOCwxNzgsMTMsMjMyLDE5MiwxMzEsMTU4LDE3NiwxMTcsMTIwLDE2NSw1MiwyMzksMjI2LDE4NSwzNiwxMTUsMTIxLDE1OSwxNjAsOTIsMTIxLDE0NiwxMDYsMTcxLDE5OSw5MywyMzIsMjMsMzUsMjYsMyw0MCw1Miw1LDIwNiwxODIsMjMzLDc0LDE4NCw5MCw2LDI0NiwxODcsNSwxOTUsMTgwLDE3LDk1LDI0MiwxMDksMTY0LDM3LDE0Niw4MywxOTcsMSwzMiwxNzEsMTI2LDgzLDg5LDEwMyw5LDc2LDksMTc0LDY5LDE2NSw5MywxODMsOSw3MywxNjAsMTExLDEyOCwxMiwxNDksMTk4LDI0OCwxMTcsMCwyOCwyLDE2NywyMzUsMjMsNzMsOTcsMTE2LDExMywxNTAsMTQ1LDE2OSw3LDIwNSwxMTMsMTQ3LDcsMTM4LDE4MCw4LDk3LDEwMCwxLDk1LDE2NCw1MCw1Niw2OCwxNzIsMjQ0LDYsMTMwLDgsMTksMTc4LDExMCwyNDEsMTUyLDEzNiwxMzQsMTk2LDU2LDIwMSwxOTYsMjA4LDUzLDg5LDEzMiwyMjYsNTQsMjI0LDI2LDI3LDIyLDE3OCw5Miw0Niw0Miw2OSwxMDAsNTYsMTE4LDgzLDM4LDE5OCwwLDM3LDg2LDExOCwyMjUsNCw0NywzMywyMywyMDgsMTkyLDEwNCwxMCw3Myw3NywyMTcsMTE3LDE0LDE2NSwyMTMsMTUzLDM4LDgzLDE1MCwxMzcsMjQzLDIwMCwyMzcsMTcsMzQsMTI5LDE3OCw4Nyw1MywxNDksMTUwLDExNywxMzQsMTg2LDc0LDIyNiwxNDIsMTIwLDIxMSwxMDMsNzcsMTQ5LDc1LDMxLDIzMCwxNTUsMTYxLDIyOCw3LDU4LDE2NywxNDYsMTc0LDEzMyw2LDE5OCwyMSw1MCwxNDQsMjUyLDE0MywyNDgsOTgsNCwxNiwxMzksMjA0LDEzLDQ5LDExMiw4MiwxODIsNiw5MiwxNjksNzEsODAsNjQsNDUsMTQxLDM5LDE4MSwyMDQsMTMzLDE2Niw4NSw0MSwxNCwyMDMsMjI5LDUxLDI1NCwyMiwxNTIsNjAsMTk5LDgyLDQyLDE4NiwzNywzMywyNiwxLDIzNiw3MiwxNTUsOTksMTIxLDE1OSwyMjAsMjEsMTY2LDExMiw2Nyw0NCw0MywxNiwzNiwxNTcsMTQwLDQ0LDU3LDE0Niw1MiwxMCwxNjYsMTQzLDE0NSwzMiw4MCwxNTQsOTIsMCw2NCw0OCwxNDAsMjI4LDE5MiwxNzEsMTczLDE3MCwxOTYsNDcsMTA0LDc1LDEwNCw4MCw1MCwxNTcsMTM2LDI5LDEyNywxMzgsNDUsNSwyMTQsMjIsNzEsMTE2LDYxLDIxNywyOCwyNyw5NCwxMjksNiwxMiw3NiwxNDYsOTAsNTksMTI0LDQ2LDE2Niw4NiwyMDgsODMsMTk1LDI0OCwxMTUsNTgsMTU4LDIxNiwxMzgsMjI3LDExNCw3LDI0NCwzOSwyNiwyNywyMzIsMzUsOTYsMjMsOCwyMCwxNTAsODgsMTAyLDExOCwxNzMsMTAzLDE1MSwxMjgsMzIsMTMyLDQ2LDEzOSw2NCwxNjgsMjQwLDE0NCw0Niw0LDIxLDY3LDMsMjI5LDE1NCw2OCwxMTksNzAsMTA4LDI0OCwxNzgsMjA1LDE4NSwxLDM0LDY3LDQyLDE0OSw4MSwxNTMsMTM1LDEzOSw5OSwyMzUsMTgxLDEsMTY5LDI2LDIyLDIwNiw1MCwxNiw1NSwxNiw5LDEsMCwxOTUsMjM0LDE4NSwyMDMsMjQwLDE5NywxMDEsMjQzLDI0NCw5NywxNzgsMjAyLDE4Nyw0MCwzOSwxNDQsMTIyLDcsNjEsMTY4LDEsMTY1LDE3Niw3NCwxMjQsMTY5LDEzOSwxNjEsMTQ3LDIxOCwxODMsMTEzLDIwLDI2LDEyMCwxMCwxMDksMTIyLDgxLDEwMSwxMjMsMTE0LDE0Miw5NiwxNTQsMTc0LDIyOCw3MSw3MSwyNDYsMjMwLDExOCw3NSwxMzQsMjA2LDg5LDIwMSwxMTAsMjM3LDE3OSw0MCwxNjMsMjA5LDE4Nyw5OSwwLDU0LDEwMywxNjEsNzIsMjIwLDEyLDIwNiwxMzUsMTE0LDExNCwxNjMsMzYsMTU4LDEyMSwxMDEsNTQsMTUyLDMyLDE2NywxMTgsMTEsMTk0LDg3LDg3LDIwMiw1MiwyMDgsMjAwLDI1NCwyNDUsMTMsMjAwLDIyLDE0OSwyMDUsMTA1LDEyMSw4MCwxNjcsMzUsMTA4LDEzMiwxNTEsMjA5LDE3MCw2MiwxNzMsMTQ5LDE4Miw3OSwyMywxNiwxLDY5LDEwOSwxNiwyNTIsMTY0LDY5LDI0MiwxOTEsMjgsNSwxMjQsMzAsMTYyLDE2MSw3MCwzMiwxOTksMTA2LDY5LDIwMyw5NywyNTAsODMsMjI0LDIwNCwxOTEsMTkxLDIzNSw5OSwxMjgsNTcsMTUwLDEzNyw1NywxNDYsMTAzLDE1MywxNDMsNzEsNTUsMTAxLDIwNCwxNTUsNzcsNSwyMjYsMjQsNDgsMTM0LDY1LDEzLDIwOSwxODEsNDAsODMsMTA2LDIzNCwxMTksNjksNTAsMTYzLDM4LDcyLDIwMiw4OSwxNjgsOCwyMDMsMjIyLDI0NywxMTcsMTMyLDEyMywxMTUsMTE2LDI0NSwxODksMzUsMTY5LDE0MiwxMjMsNCwxMjgsNDEsMjI3LDExNiwxNDAsMTAyLDExLDI0MiwyMjYsMTgyLDQzLDUyLDIwMSwxMzYsMTc4LDE4NCwxNTcsNCwxMzEsOTUsNTMsNzIsMjEyLDE4MSw5NCwyMzgsMjE2LDY4LDIwMywzNiwxMTUsMzgsMjA4LDE5OCwyMTksMTQ4LDE2Myw3MSwxMzAsMTExLDM3LDEzMiwxOTQsOTYsOTAsMTA4LDE3MywwLDE2NCwxNjAsMjQwLDE2OCw2LDE5NiwzMywxMjIsMTk1LDcsMTI1LDIxMSw5NywxNjgsMjMwLDE3Nyw5OSwxNzIsMTM5LDk5LDEyOSw4MSwyNTQsMTU0LDE0Niw0LDE2MywxMTcsNDAsNywxMjEsMTUwLDEwNywyNCwxNzMsMjA5LDY3LDY0LDE3MiwxODUsNjAsMjYsMTk0LDEyMiwxODUsMTczLDQ4LDIzNiwxMTAsMjUzLDE3OCwxNSwyNTMsMjMyLDUwLDE4OSwxNTEsMTQwLDI0MiwxNzgsMTIwLDIzNyw3NiwxNzksMjE5LDEzOSw2NSwxOTgsNTksNiwxODcsMjMzLDE2Miw0NiwxOTcsOTMsNiwxODAsOSwyMTYsMjUyLDE5NSwyNDAsMjQsMTY5LDE1NCwxOTYsMTYzLDEyMywyMzMsNDksMzUsMTIzLDEwLDIxMSw0LDE5Myw1MywxOTUsNDUsNTcsMTU1LDcyLDkxLDQxLDE0Myw4NCwyNSwyLDEyOCwxMzUsMTg1LDE1Niw1MCwxOTUsMjMzLDg2LDE2MCwxMTYsODUsMTgsNzIsMTYyLDM5LDE5NywyNDAsMTc1LDY1LDE1NSwxMjksNDIsMjEyLDksNjEsMyw1MSw3NSwxNzYsNTUsMTM4LDEzNSwyMDcsMjMsMTI4LDEzMiwxNjYsMjEyLDE1Nyw5MCwxMTYsOTcsMTA0LDI0MywxMCwxNTAsMTQwLDE2MiwxNjYsODUsMTM0LDE1NywzMiwxMjMsMjUxLDEwMywxODMsMjE0LDE3MCwxNCwyNDgsNzIsMTg5LDUyLDE2NCwxODUsNjgsMTQ0LDEwMiw3LDM3LDEwNiw1MiwyMjAsMTU1LDIxMCw4MiwyMDcsNzcsMjQ1LDE5NywyNSw1MiwyNywyMDksMjUxLDIxLDE5LDE5NSw2Miw4OSwxMzgsMzAsODMsNCwxNDQsNzgsMjQzLDg0LDEyOSwyMzIsMSwyMTcsMTQ4LDEwMSwxNjgsMTAyLDg3LDk5LDY2LDE3Miw4LDIsMTU1LDIxNSwxMDgsNTMsMTI5LDgzLDM0LDE5NywxMjgsMzAsMjIwLDk2LDEyMyw4MywzNywzNywxNDgsNjYsMTQ5LDE5MSw1Myw1NiwxNjAsMTEyLDE3NywzOCwyMTksMjI4LDE1LDE0NSwxNDQsMTg3LDYxLDQ0LDEyMSwyNywyMjcsNDIsMzEsNSwxOTgsMTY2LDE2MywyMTYsMTg1LDE1NSwxOTUsMTY4LDEwOCwxNjQsMjMwLDI0LDM2LDU3LDY0LDExNSwxMzIsMTA1LDI0MCwyMSw4MiwyMywzLDgzLDIxMywyMDcsMjMyLDYwXX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzE2NCwxODAsMTI1LDEyNiw3OSwyMjAsMjIxLDI1MywxMjMsNDIsMjAxLDEyMCwxODAsMTY0LDIwMCwyMDgsMTc3LDk5LDgyLDYwLDU1LDE1NiwxMjYsNzcsMTE3LDExNSwyMDMsMjAzLDE0MiwyNTUsMTY2LDM0XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTcsMTA4LDEwNSw5OSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IkNvbW1pdCI6eyJ2ZWMiOls2NywyNDEsMTY2LDIyMiwxMCwxNTMsMjM5LDE1NCwxNDEsNDcsMzEsMTQyLDIyMywxNzgsMjQzLDU3LDE1NywxMjIsMTU2LDEwMiwxOSwxMTYsMjAxLDIxNywyNDcsMjI5LDExOCwxNjYsMTQzLDM2LDEyOCwyMzRdfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMjE4LDEwNSwxNDEsMTgyLDIwMCwyMjQsMTcyLDExMCwxMjYsMTksMTAxLDI2LDI0Nyw0NSwxOTUsMTczLDIxMywxNjIsODQsMjI5LDEwOSw4LDE3MywyNTEsMTQ1LDIxOCwyNDIsMzIsMCwyMTQsMiwxMCwyNDQsMjA2LDMzLDAsNTAsMjQ3LDgxLDIwOSw0MiwwLDEyMywyMDYsMjEyLDM0LDgyLDc0LDU5LDczLDE4NywxMzksMjEsNjUsMTc3LDEyOCwxMTcsMTc1LDExNyw4NCwxNDMsMjMsMTU4LDddfX19fX0sInBhcmVudF9kaWZmIjp7IjAiOnsibm9kZSI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMjM3LDE0NywxNDUsMjUxLDk4LDE1LDksMTIsMTg3LDEwLDE5MiwxNTcsMTE0LDIzLDYwLDEsMjQwLDk1LDIwNSwxODgsNTQsMjQwLDMzLDE2MCwxMjUsMjI0LDE5NiwxMzksMTYwLDg0LDIxNywxNjYsMTA1LDExOSwxMTYsOTYsMjA4LDE2NywxODYsMTY4LDQzLDE5MiwyMTMsMjMwLDE0LDI5LDEwLDUxLDYwLDE0NSwxODgsNDYsMTIxLDE5OCwzMCw1OCwxNjMsNCwyNDcsMTEwLDcyLDE4Miw4Niw4Niw1OCwxNTksMTk3LDUxLDE1NywxMDUsMzQsMTM0LDE4MCwyOCwxNzMsMTk0LDEzMiwxMDYsMjQ3LDEzMSwxOTcsNjgsODMsMTAwLDI0NCwyNDQsNTAsMzgsMjEwLDI5LDY4LDI1MiwzNiw3MCwyNDksNjUsNTEsMjEyLDgyLDE4NSw5MCwxNTIsMTM0LDM5LDEzMSw0OSwxODgsNiwxMDUsOTgsMTgzLDcyLDEwNSw5MiwyMzYsNjQsMTM4LDQwLDE5LDI5LDE1LDYwLDk4LDgxLDExNyw2NSw4NywxNTYsMTAsMTk4LDE4NiwxMzYsMTc5LDI0NCwxNzYsNDUsMjYsNTUsMjMyLDE5NCwxODcsOTUsODIsMTQ1LDUsMTAwLDcxLDIwNiwxMzAsMTk4LDEwNCwyMzQsMTg5LDIsMjI1LDIxLDEyNywzNiwxNCwxMiwxNjcsNSw2NSw1MCwxNTIsNDgsMTQwLDU4LDM2LDIxOCwxMjQsMTk4LDYsMjA2LDE2OSwxMDIsMTA2LDE3Niw1NiwxOTIsMTc3LDE0NSwxNzMsNjMsMzgsMTE1LDE3LDI0OSwxNCwxOTcsMjExLDE4Niw5MiwxMCwxNjgsNTUsNjUsMTA5LDIxNiwyMDEsNTQsMiwxMjIsMTEsMzMsNCwxNzcsNTEsOTAsOTgsNzUsMTEyLDEyNCw1Miw0Miw0MSwxMjcsMzYsMTUyLDczLDgsMTIwLDc5LDE4Myw2OSwxNzgsMjIwLDE1MiwyMTEsNzYsMTY1LDQzLDkxLDYyLDEwNCwxMzIsNzEsNDAsMzgsMTQ5LDI0MywxMjAsMjA3LDIyMiwxMzUsMTc4LDE5NSwxMzIsMTQ0LDQ1LDE2NCwyMywxNDAsODksNDcsMTYwLDEwLDEwOSwxNDAsMjA5LDEwNiwxMzgsMTQ0LDE2OSwxNDMsMTIsMywxOTEsNzQsMTU5LDE5NiwyMjUsMTAxLDIxNywyMTAsMTU5LDE1MywyNDAsMjYsMjUxLDI1MSw1LDEyLDExOSwxMjMsMTA2LDE3MCwwLDE3NSw3MCw5NiwxNDksMjA4LDgsMjIsNjYsMTkxLDIwNiwxMzYsMTAwLDEsODYsNDUsMTU0LDM3LDE4OSwxMTIsNjcsMTU0LDMxLDI1LDk1LDQwLDE2OSwxNjEsMTExLDE2NCwzNSwyMjgsMTM3LDU3LDEyNCwyMzQsODgsOTIsNzQsNTAsMTQ4LDYwLDE2Nyw1MCwyMDgsNTcsODMsMjMxLDEwNiwyMDAsMTEyLDMxLDc5LDExOSw4OCwxNDYsMTIyLDI2LDEwMywyMSw0MCwxNTAsMTM4LDE5MiwxNTcsMzUsMTAyLDEyMiwxMjksMTQ2LDEyNCwyMjcsODAsNjQsMTY1LDU5LDEyOSwxNTUsMywxODksOTAsMjgsMTk5LDE1Miw5MywzLDExMiwxMzksMTksODksMTYwLDEwLDE2MCwxMzYsOCwyMjcsMTg3LDE3NCw1NywxNTIsMjEsODQsOTcsNzUsMTgxLDExLDcxLDIwOSw4MiwxMjQsMzIsNDYsMjgsMjQ2LDQ0LDEyMSw4MCw2MSwxMTcsMTA1LDkwLDIzMSwxMzIsMzUsMTkzLDcxLDE0NiwxNTgsMjcsMTc5LDIzOCw4MywxMDgsNjksODksMTg4LDIyOSwyMjQsMTEwLDIzNywxMTUsOTQsMTU5LDIwMCw1MywxMjgsMTkyLDEyNSwxMzMsMjUyLDE5OCwyNDgsMyw0LDAsMTk5LDEyMCwxNjUsMTgzLDE3NywxMzgsOCwxNjgsOTcsMjI1LDksMTk5LDQwLDEyMSw2MCwyMDQsMTQsMjQ4LDIxMCwyNSwyMzUsODEsMTQsMjE3LDEyOCwwLDE0MSwxOTQsMTI0LDE5OCwyNDksMTgwLDYsNjUsMTE4LDEzMywxNTAsNjYsNzgsOTAsMTU1LDMwLDcxLDE2MiwxNjUsODcsNjgsMTQyLDk4LDE5OCwxNTgsODQsNDEsMTUwLDM1LDEzOSwxMCw5LDE0NCw4LDEzMywxMDIsNzAsNzEsNSwyMDAsNjksMTIzLDE5MSwxNjEsMzgsOTEsMjEyLDMyLDI1MiwyNDcsMTYyLDIxNywxMDUsMTgwLDkwLDE0MCwxNDYsMSwyMTMsMTYwLDQ3LDEzNSwxNTgsMTAyLDExOCwyMywyNTIsMTcwLDc0LDcxLDc1LDUsMjM0LDUsNjUsMjI4LDEwNywzNiw4NywxMCwxMTIsOTcsMzksMTE1LDIxNiwzMiw1OSwxODIsMTM5LDU5LDE3NCwyLDIzLDMwLDEwNyw3NCw1NCwxNjgsMTUwLDkyLDIxMiwxOTEsMTI1LDEwMywxNTUsMTQzLDE0NiwzOCwxODQsOTcsMzIsMTE0LDE5NSw3MCw0OSw5NiwxODEsMTAyLDIyOSwxOCwxNjcsMTc5LDEzNSwxNjMsMjUsOTMsMjcsMTMzLDkwLDE5Myw1MywxNDMsMTEyLDM5LDEyOCwxOTksMjgsMTczLDIwOCw0OCw1NCwxMjgsMTYyLDQ4LDQ5LDM5LDEyNCwyMzQsMjQ2LDE4NSwxMzIsNjQsNjgsOTgsMTgwLDExNSw5NiwxLDE4OCwzNCw4Myw5OSwxNDgsMTQ3LDE0OSwxODAsMTcsMTMwLDE3NSw5LDQ1LDIxLDY4LDE0MSwxMDYsMTk1LDY2LDIwMiwxNjEsNTMsNzksMjA5LDI3LDExMCw2OSw1Myw4NywxNzcsMTc1LDI5LDI0NiwyMDUsMTEyLDIzNSwxMTMsMjM1LDEwMSw0MiwyMTMsMTE5LDQsOSwyMzYsNDIsNjAsMTQ2LDEyOCwxNTcsMTg4LDU2LDIxMywzNiwzMCwyMzEsMzksMTk1LDIyNCw0OCwxNzMsMjExLDQ0LDExNSwyMjUsMTg2LDkzLDM0LDc0LDE5MCwxMzMsOTIsMTYxLDExOCwyNiwxOTcsMTQwLDM3LDU2LDQ5LDM3LDI2LDEwOCwyNDMsMTE0LDI1MSwyMjcsMTIsMyw1Nyw1MSwyMjAsMTQ1LDIyLDIzMiw0MCwyOCwzOSwxMzUsMTY5LDE2NiwxNzYsMjQsMTU1LDE4LDEyNiwyMSwxOTgsMTgzLDI1LDc2LDc0LDE0MywzOCw2NSwyMjUsMTYwLDExLDE3MSwxNTQsOTYsMzgsMTIyLDI5LDcsMTY4LDE3Nyw2MCw1MSwxMTksMjI5LDEwNiw4OSwyMiwxNiwxMjEsMTYxLDEzMCwxMDMsMTAyLDI4LDI4LDIyNSwxMDAsODYsMTUsNTAsNzUsMTk0LDQwLDE0MSwxMzksMTE1LDg2LDQ3LDMsNTcsMTcyLDEzNSwwLDIsMjM1LDIwMSwxNDcsMTcyLDE2NSwxNTIsMTY2LDE0MiwxNCwyNTIsNDcsMjAxLDQyLDE0NywxMzEsMjA0LDUwLDQxLDE5LDE3OSw2LDU2LDEyMCwxMiwyMzAsMTYzLDk5LDEyOCwxNiwxMDEsMTg4LDEzNSwyNTQsMjMyLDEzNywxMTMsNDMsMTkyLDIyNSwxMTIsMjAwLDE2MCwyMCwyLDI1NCwxODMsMjEsMjgsNTUsMTM2LDYwLDIzNSwxNTcsNzQsMzYsMTM5LDIyNiw4NiwxMzAsMjU1LDIyNSwxNjcsMTg4LDQ5LDkxLDE3NCwxNDAsMTI5LDMsODQsMTAwLDExNSw0Myw3NSw0LDEyNCwxMzUsNDUsMTYzLDM4LDQ0LDE1NiwxNDAsMTE3LDE4MCwxNzEsMTgwLDI4LDE5Miw1MCwxMjgsMTI3LDc3LDE5LDE3OCwxMzAsMTM0LDEwNywxODksMTcwLDc0LDIzMywxMDUsMTIzLDI0NCwxMjAsMjA3LDEzMiwxMjMsMTc1LDE3OCwxOCwxODQsMjI0LDM5LDIxLDE4MSwxMDIsMTk1LDIxOSwxNjIsMTEsMzYsMzUsMTE2LDY2LDIxLDE2NSwxNDEsMTAsNDYsMTIzLDE3LDE5Miw3MiwzOCwxODMsMTg5LDE4MSwxNzEsMjUxLDE4Myw5Niw3MSwxMzQsNDYsMzAsMjQ4LDQ0LDYzLDEwOCwxMTUsODAsMjIwLDE0MCwxMjUsMzUsMTMyLDIyNSwyMiw1LDEwOCwyOCwxNzgsNjMsMTk4LDExMSwyMzYsODUsMTc5LDE0OSwxLDE4MywxNDMsMTYyLDEzOSwyMiw4MCw0NiwyMzYsMTM5LDYxLDEwOCwxMzMsMTMsMTc1LDI4LDE3OSwxNjMsMTgxLDczLDE5LDEwMSw0Nyw1NSwyNDUsNCwxMTIsMTAwLDQsMTY3LDIyNCwxMjUsMTUwLDE0OSwyMDEsNSwxMjksMTc5LDIzNCwyMDMsMTM3LDU0LDkxLDEwMSwxMTksMTcxLDk4LDc1LDk5LDY1LDg1LDEyOSwxOTQsOSw5LDEzMCw0LDU1LDEzNSwxNjMsMTUzLDUxLDEzNywyMSwzMiwyMTIsMzksODAsMTE3LDU0LDk1LDc3LDIwMywxMTksMTIxLDE1NCw3LDIxNCwxNDQsMTIyLDczLDEzMSwxNjYsMTI4LDE3NywxODgsMTgzLDEsMTc0LDE1MSwxODUsNjUsNjAsODYsODAsOTksNTksMTQsMjQsNjYsNDAsMjIxLDY4LDQ3LDY1LDY1LDE3NywxMzEsMTkyLDE0NCw0Nyw3LDM3LDMzLDc2LDE2NywyNTQsMTY3LDEyMSwyMjIsMTY2LDE1NCw2Myw5NywxNTksMjQwLDE2OSwxMzYsMjMzLDEyMiwxMTgsNTcsMTEyLDE5OSwxMzEsMTc3LDI4LDUxLDE0MCwxODMsMTExLDI1MiwxNTksOTQsNzYsNTMsMTMwLDQxLDE3MiwxMTQsMTE5LDEzMCwxOTUsMTI0LDU2LDEyMiwyMzAsMTAsMTA4LDExMywxMzUsMTY3LDE1NSw0Nyw3NywxMDcsNjksODQsNzAsMTA5LDI1LDE2LDExNywxNjcsNzIsMTM5LDEzLDEwMywxNTQsMjcsMjEwLDYyLDQzLDAsMTI2LDE4NCwyMDgsMTAwLDQxLDI0MCwxODUsMTYyLDIxMSw1NCw1NiwyMDcsMTE2LDQzLDExNywxOCwyMTAsNDIsMjUwLDIxMSwyNTAsMTEyLDEzNywxMTcsMjQ3LDE1MCwxMTMsNDMsMTczLDIyNCwxODEsNTQsMjUxLDUyLDEzMywxMjIsNzcsMzEsMTM1LDIwNywyMDUsMjM1LDIyNCwxMzQsNTQsMTMwLDIzMywxMDgsMjA3LDE1NSwxNTMsOTcsNjcsNzksOTIsNDAsMjA2LDIxNiwyNDMsODcsMjAsMzEsMjIxLDY1LDgxLDIwNiwxNzksMjUxLDQ3LDM2LDEwLDEzMSwxNzUsNTBdfX0sInBhcmVudF9oYXNoIjp7InZlYyI6W119LCJ1bm1lcmdlZF9sZWF2ZXMiOnsibGlzdCI6W119fX19LCJzaXplIjozfSwibmV3X3RyZWVfaGFzaCI6WzIxNywxMTcsMTIxLDE4NiwxMTMsMTA4LDE2MCw5MSwyNSwxNjgsMjUzLDU4LDE4NywyMTQsMjQ4LDExMCwxNjcsMjUzLDQ5LDkwLDU4LDEwNCwxMzgsMSwyMCwxOTksMjUxLDIyNywxODksNDksMjAyLDE4MF19LCJncm91cF9jb250ZXh0Ijp7InByb3RvY29sX3ZlcnNpb24iOiJNbHMxMCIsImNpcGhlcnN1aXRlIjoiTUxTXzI1Nl9YV0lOR19DSEFDSEEyMFBPTFkxMzA1X1NIQTI1Nl9FZDI1NTE5IiwiZ3JvdXBfaWQiOnsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0sImVwb2NoIjoyLCJ0cmVlX2hhc2giOnsidmVjIjpbMjE3LDExNywxMjEsMTg2LDExMywxMDgsMTYwLDkxLDI1LDE2OCwyNTMsNTgsMTg3LDIxNCwyNDgsMTEwLDE2NywyNTMsNDksOTAsNTgsMTA0LDEzOCwxLDIwLDE5OSwyNTEsMjI3LDE4OSw0OSwyMDIsMTgwXX0sImNvbmZpcm1lZF90cmFuc2NyaXB0X2hhc2giOnsidmVjIjpbMTcsMjM4LDI0OCwyNCwyMzksMTc5LDE0LDI0MSwxODksMCwxNSwxNDQsMTA0LDE3LDg2LDExMywxODcsNTcsOTEsMjAxLDc0LDI0OCw5OSwyNTQsNzYsMjMwLDEzLDUwLDIyMSwxNDMsNzcsNTBdfSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOlt7IlJlcXVpcmVkQ2FwYWJpbGl0aWVzIjp7ImV4dGVuc2lvbl90eXBlcyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxfdHlwZXMiOltdLCJjcmVkZW50aWFsX3R5cGVzIjpbXX19XX19LCJpbnRlcmltX3RyYW5zY3JpcHRfaGFzaCI6WzE1MSwxOTMsMTI1LDM4LDc5LDEzOSw0MiwxODQsODQsMjIsMTgzLDMxLDg4LDU4LDExNSwyMTAsMjQ2LDEzNiw5LDExNiwyMTAsMTkxLDEwNiwyLDE0Nyw4MywzNCwyMDgsMTI1LDQ0LDE2NywxODJdLCJjb25maXJtYXRpb25fdGFnIjp7Im1hY192YWx1ZSI6eyJ2ZWMiOlsxMCwyNTIsMTk4LDE0NiwxMTYsMzMsOTUsNSw4OCwxNTEsMzMsMTU1LDI0NiwyMzAsOTUsMTIxLDU4LDEwMCwxMjAsMTAxLDIyNyw0NSwyMDQsMjQ4LDIxNywyMjQsMTMwLDE2OCwxMyw5NywxMzEsMjA2XX19fSwibmV3X2tleXBhaXJzIjpbeyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlszNCwyMTMsMTI2LDEwMiwxODAsMTk3LDcsMTE5LDUsMTQ4LDIzMSw1NywxNTYsODgsMTU0LDI3LDEwMSwxLDExNiwxMzIsNTQsNTgsODMsMTMwLDI0NywyNDcsNzMsMTc4LDQsMTQ0LDEyNiwyMTYsMTA1LDIzMiwxNDAsMTMsMjMyLDIyNiwxOCwyMzEsMTYzLDIwNywxNTgsMjMwLDEwNiw4MSw1LDQzLDExOCw4MiwyLDcwLDQwLDQsMjA2LDE4NywxNTgsMTk5LDExOCw4MCw1NiwyMjQsMzksMjE5LDExOCwxNTMsMzcsMjQ1LDI1LDQ2LDIyOSwxOTMsMTMzLDUyLDIwNCwxNTYsMzUsMjA0LDIzOSwxMjQsMTczLDIyMCw2NSwxNjQsMTk4LDE2NiwxNSwyMiwxMDQsMTkyLDY5LDIzNiwxNDgsMTA2LDE3OSwxNzUsMzksOTYsMTgsMjcsNTUsMTgxLDM3LDE2LDYwLDE5LDE3OCwxMTIsNzMsMjE3LDE4MywxNzksMTA4LDE0OSwyMzAsMTcyLDIwNSw5OCwxMjAsMTQyLDI1MywxNywxNzAsNjcsMTE3LDE5NiwxODIsMTcyLDEwMSwyMzYsMTcwLDEyMSwxODYsNTcsNzcsMzMsNjYsMzIsNzMsMTE3LDE3MiwxMjIsMTUwLDEzNCw4MCwyMjAsOTIsMjM5LDIyOSw5LDczLDIxOSwxNjgsMzMsMjUxLDMzLDk3LDE3MSwyMDQsNjYsMTQwLDYzLDM1LDE1MywxODksMjI5LDEwNiwxMzksMTAxLDE1NSwxODUsMTYwLDIxMCwzOCw1MCw0MSwxODMsNjksNTksMTYwLDQ5LDExMiwxNjgsMjM0LDEzNCw5LDIwMywzNiwxOTksMTYyLDEwOCw1MSwxNzQsMjQxLDEyNyw3MywxNzIsMTg3LDUxLDI2LDE1MiwxNzMsMTQ5LDgwLDg1LDU4LDE2NSwxODIsODcsMjAwLDIwNywxMjksMTI2LDEzNSwxMTksMTY1LDMwLDIzMSwxMzUsMjI0LDE5MywxNjgsMCwxNjEsMTM4LDYwLDI0OCwxMCwxODYsNDgsMzEsOTMsMTc4LDcsMTk3LDE5OSw1NCwyNDQsMTUwLDE1NywwLDEyMSw2OCwyNDAsMjQ3LDExMCwxNDksNjksMTEyLDE3NywxMCwxOTgsMjQ5LDEyMywxNTMsMTI4LDE5MiwyMywyNiw1Niw3NCwyMjcsMjE3LDk3LDEzMiwxODMsMTUsMjIsMTIwLDE3NSw5Myw0OCwxOCwxMTgsMTc4LDEzLDIzMiwxOTIsMTMxLDE1OCwxNzYsMTE3LDEyMCwxNjUsNTIsMjM5LDIyNiwxODUsMzYsMTE1LDEyMSwxNTksMTYwLDkyLDEyMSwxNDYsMTA2LDE3MSwxOTksOTMsMjMyLDIzLDM1LDI2LDMsNDAsNTIsNSwyMDYsMTgyLDIzMyw3NCwxODQsOTAsNiwyNDYsMTg3LDUsMTk1LDE4MCwxNyw5NSwyNDIsMTA5LDE2NCwzNywxNDYsODMsMTk3LDEsMzIsMTcxLDEyNiw4Myw4OSwxMDMsOSw3Niw5LDE3NCw2OSwxNjUsOTMsMTgzLDksNzMsMTYwLDExMSwxMjgsMTIsMTQ5LDE5OCwyNDgsMTE3LDAsMjgsMiwxNjcsMjM1LDIzLDczLDk3LDExNiwxMTMsMTUwLDE0NSwxNjksNywyMDUsMTEzLDE0Nyw3LDEzOCwxODAsOCw5NywxMDAsMSw5NSwxNjQsNTAsNTYsNjgsMTcyLDI0NCw2LDEzMCw4LDE5LDE3OCwxMTAsMjQxLDE1MiwxMzYsMTM0LDE5Niw1NiwyMDEsMTk2LDIwOCw1Myw4OSwxMzIsMjI2LDU0LDIyNCwyNiwyNywyMiwxNzgsOTIsNDYsNDIsNjksMTAwLDU2LDExOCw4MywzOCwxOTgsMCwzNyw4NiwxMTgsMjI1LDQsNDcsMzMsMjMsMjA4LDE5MiwxMDQsMTAsNzMsNzcsMjE3LDExNywxNCwxNjUsMjEzLDE1MywzOCw4MywxNTAsMTM3LDI0MywyMDAsMjM3LDE3LDM0LDEyOSwxNzgsODcsNTMsMTQ5LDE1MCwxMTcsMTM0LDE4Niw3NCwyMjYsMTQyLDEyMCwyMTEsMTAzLDc3LDE0OSw3NSwzMSwyMzAsMTU1LDE2MSwyMjgsNyw1OCwxNjcsMTQ2LDE3NCwxMzMsNiwxOTgsMjEsNTAsMTQ0LDI1MiwxNDMsMjQ4LDk4LDQsMTYsMTM5LDIwNCwxMyw0OSwxMTIsODIsMTgyLDYsOTIsMTY5LDcxLDgwLDY0LDQ1LDE0MSwzOSwxODEsMjA0LDEzMywxNjYsODUsNDEsMTQsMjAzLDIyOSw1MSwyNTQsMjIsMTUyLDYwLDE5OSw4Miw0MiwxODYsMzcsMzMsMjYsMSwyMzYsNzIsMTU1LDk5LDEyMSwxNTksMjIwLDIxLDE2NiwxMTIsNjcsNDQsNDMsMTYsMzYsMTU3LDE0MCw0NCw1NywxNDYsNTIsMTAsMTY2LDE0MywxNDUsMzIsODAsMTU0LDkyLDAsNjQsNDgsMTQwLDIyOCwxOTIsMTcxLDE3MywxNzAsMTk2LDQ3LDEwNCw3NSwxMDQsODAsNTAsMTU3LDEzNiwyOSwxMjcsMTM4LDQ1LDUsMjE0LDIyLDcxLDExNiw2MSwyMTcsMjgsMjcsOTQsMTI5LDYsMTIsNzYsMTQ2LDkwLDU5LDEyNCw0NiwxNjYsODYsMjA4LDgzLDE5NSwyNDgsMTE1LDU4LDE1OCwyMTYsMTM4LDIyNywxMTQsNywyNDQsMzksMjYsMjcsMjMyLDM1LDk2LDIzLDgsMjAsMTUwLDg4LDEwMiwxMTgsMTczLDEwMywxNTEsMTI4LDMyLDEzMiw0NiwxMzksNjQsMTY4LDI0MCwxNDQsNDYsNCwyMSw2NywzLDIyOSwxNTQsNjgsMTE5LDcwLDEwOCwyNDgsMTc4LDIwNSwxODUsMSwzNCw2Nyw0MiwxNDksODEsMTUzLDEzNSwxMzksOTksMjM1LDE4MSwxLDE2OSwyNiwyMiwyMDYsNTAsMTYsNTUsMTYsOSwxLDAsMTk1LDIzNCwxODUsMjAzLDI0MCwxOTcsMTAxLDI0MywyNDQsOTcsMTc4LDIwMiwxODcsNDAsMzksMTQ0LDEyMiw3LDYxLDE2OCwxLDE2NSwxNzYsNzQsMTI0LDE2OSwxMzksMTYxLDE0NywyMTgsMTgzLDExMywyMCwyNiwxMjAsMTAsMTA5LDEyMiw4MSwxMDEsMTIzLDExNCwxNDIsOTYsMTU0LDE3NCwyMjgsNzEsNzEsMjQ2LDIzMCwxMTgsNzUsMTM0LDIwNiw4OSwyMDEsMTEwLDIzNywxNzksNDAsMTYzLDIwOSwxODcsOTksMCw1NCwxMDMsMTYxLDcyLDIyMCwxMiwyMDYsMTM1LDExNCwxMTQsMTYzLDM2LDE1OCwxMjEsMTAxLDU0LDE1MiwzMiwxNjcsMTE4LDExLDE5NCw4Nyw4NywyMDIsNTIsMjA4LDIwMCwyNTQsMjQ1LDEzLDIwMCwyMiwxNDksMjA1LDEwNSwxMjEsODAsMTY3LDM1LDEwOCwxMzIsMTUxLDIwOSwxNzAsNjIsMTczLDE0OSwxODIsNzksMjMsMTYsMSw2OSwxMDksMTYsMjUyLDE2NCw2OSwyNDIsMTkxLDI4LDUsMTI0LDMwLDE2MiwxNjEsNzAsMzIsMTk5LDEwNiw2OSwyMDMsOTcsMjUwLDgzLDIyNCwyMDQsMTkxLDE5MSwyMzUsOTksMTI4LDU3LDE1MCwxMzcsNTcsMTQ2LDEwMywxNTMsMTQzLDcxLDU1LDEwMSwyMDQsMTU1LDc3LDUsMjI2LDI0LDQ4LDEzNCw2NSwxMywyMDksMTgxLDQwLDgzLDEwNiwyMzQsMTE5LDY5LDUwLDE2MywzOCw3MiwyMDIsODksMTY4LDgsMjAzLDIyMiwyNDcsMTE3LDEzMiwxMjMsMTE1LDExNiwyNDUsMTg5LDM1LDE2OSwxNDIsMTIzLDQsMTI4LDQxLDIyNywxMTYsMTQwLDEwMiwxMSwyNDIsMjI2LDE4Miw0Myw1MiwyMDEsMTM2LDE3OCwxODQsMTU3LDQsMTMxLDk1LDUzLDcyLDIxMiwxODEsOTQsMjM4LDIxNiw2OCwyMDMsMzYsMTE1LDM4LDIwOCwxOTgsMjE5LDE0OCwxNjMsNzEsMTMwLDExMSwzNywxMzIsMTk0LDk2LDkwLDEwOCwxNzMsMCwxNjQsMTYwLDI0MCwxNjgsNiwxOTYsMzMsMTIyLDE5NSw3LDEyNSwyMTEsOTcsMTY4LDIzMCwxNzcsOTksMTcyLDEzOSw5OSwxMjksODEsMjU0LDE1NCwxNDYsNCwxNjMsMTE3LDQwLDcsMTIxLDE1MCwxMDcsMjQsMTczLDIwOSw2Nyw2NCwxNzIsMTg1LDYwLDI2LDE5NCwxMjIsMTg1LDE3Myw0OCwyMzYsMTEwLDI1MywxNzgsMTUsMjUzLDIzMiw1MCwxODksMTUxLDE0MCwyNDIsMTc4LDEyMCwyMzcsNzYsMTc5LDIxOSwxMzksNjUsMTk4LDU5LDYsMTg3LDIzMywxNjIsNDYsMTk3LDkzLDYsMTgwLDksMjE2LDI1MiwxOTUsMjQwLDI0LDE2OSwxNTQsMTk2LDE2MywxMjMsMjMzLDQ5LDM1LDEyMywxMCwyMTEsNCwxOTMsNTMsMTk1LDQ1LDU3LDE1NSw3Miw5MSw0MSwxNDMsODQsMjUsMiwxMjgsMTM1LDE4NSwxNTYsNTAsMTk1LDIzMyw4NiwxNjAsMTE2LDg1LDE4LDcyLDE2MiwzOSwxOTcsMjQwLDE3NSw2NSwxNTUsMTI5LDQyLDIxMiw5LDYxLDMsNTEsNzUsMTc2LDU1LDEzOCwxMzUsMjA3LDIzLDEyOCwxMzIsMTY2LDIxMiwxNTcsOTAsMTE2LDk3LDEwNCwyNDMsMTAsMTUwLDE0MCwxNjIsMTY2LDg1LDEzNCwxNTcsMzIsMTIzLDI1MSwxMDMsMTgzLDIxNCwxNzAsMTQsMjQ4LDcyLDE4OSw1MiwxNjQsMTg1LDY4LDE0NCwxMDIsNywzNywxMDYsNTIsMjIwLDE1NSwyMTAsODIsMjA3LDc3LDI0NSwxOTcsMjUsNTIsMjcsMjA5LDI1MSwyMSwxOSwxOTUsNjIsODksMTM4LDMwLDgzLDQsMTQ0LDc4LDI0Myw4NCwxMjksMjMyLDEsMjE3LDE0OCwxMDEsMTY4LDEwMiw4Nyw5OSw2NiwxNzIsOCwyLDE1NSwyMTUsMTA4LDUzLDEyOSw4MywzNCwxOTcsMTI4LDMwLDIyMCw5NiwxMjMsODMsMzcsMzcsMTQ4LDY2LDE0OSwxOTEsNTMsNTYsMTYwLDExMiwxNzcsMzgsMjE5LDIyOCwxNSwxNDUsMTQ0LDE4Nyw2MSw0NCwxMjEsMjcsMjI3LDQyLDMxLDUsMTk4LDE2NiwxNjMsMjE2LDE4NSwxNTUsMTk1LDE2OCwxMDgsMTY0LDIzMCwyNCwzNiw1Nyw2NCwxMTUsMTMyLDEwNSwyNDAsMjEsODIsMjMsMyw4MywyMTMsMjA3LDIzMiw2MF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzExNSwxLDIsOTUsMjQ1LDU4LDYxLDE5NywyMDIsNjgsMjQ0LDE4OSwxMjgsMTgyLDEyOSwyMSwxODIsMTIsOTIsMjI4LDYyLDIzNCwxNzYsMTg4LDYxLDIxMCwxNDAsMjIxLDE0Niw2NiwxOTUsNzIsMjUsMjE3LDE4NiwxMTYsNTQsMTA4LDEzNCwxNTYsMTIwLDE3NiwyNCwxMTMsOTksMTAwLDk5LDIwNCwyNDUsMTE3LDE2OSw2Myw0NCwxNzQsMTUxLDIzMCw1OCwxNzQsMTA4LDIwMCwxNjcsMTgwLDYzLDIwLDk4LDEwNiwyMzQsMTcyLDEwMywxODYsMjQ2LDE2MCwyMDMsMjQ1LDEzMiwxMjksMTgyLDExNSwxNDMsMTksMTE1LDI0MCw5OSwxOTYsMjMzLDI1MCwxOTQsMjE3LDE2Nyw5NCwyMjgsNDMsOCwyMDUsMjQxLDE1NSwxNTAsNTgsMjAsMTQ2LDE5LDUyLDcxLDExOCw1LDE3MywxMiwxMjYsNjMsMzksMTgyLDI5LDU5LDE2OSwxNTUsMjE1LDIwLDQyLDI0NywxMzAsMTA2LDUyLDE3MSw5MCwyNDAsMTEzLDE2OSwxMzIsMTA2LDI0Myw0MSwxMzIsOTksMTY4LDM0LDEwMSw0OSw0Miw3MiwyMDMsOTAsMjQ0LDM5LDE1MywyMzUsMTEsMjMsMjEyLDQyLDE5NywyMTIsMTE0LDE3MSwxOTEsODIsODEsMjAwLDI0NiwxODQsMTksMjAsMTE5LDIyNiw2NiwxNzAsMjE2LDU3LDE2NSwzMSwxMjksNTAsMjAsODAsMTU2LDEwOSwxMjQsMTk3LDE1NiwxNjAsMTQwLDE2NSwxNjgsMTI0LDAsNzYsNTUsNjQsMjEsNTcsMjA4LDEwMSwxMzIsMjI0LDM2LDEwLDIyNCw0NCwxNjQsMjA2LDIyNywxMTgsMTI2LDEsMzgsMTM3LDE2NSwxMDYsMTQ1LDYsOTIsNTUsMTU0LDE4Nyw3NiwyMjksNiwyMDUsMTg3LDEwNCwyMTksMTEsMTI4LDIwNyw1MywxMDksNzEsMTM1LDk3LDI1MSw0MCw0OCwxNywxMjMsMTQ3LDExMCwxNjksMTI0LDIzNSwzMiwxNTUsMTk2LDEwNSw0NSwxOTcsMTA2LDE1NiwxMiwxMzAsMTMyLDI0NCw4NywxNyw1OSw2OCwxNywyMDQsMTM5LDU2LDY3LDgsMjA1LDcxLDY3LDEsMTA2LDIwMSwxMSwxMTcsODAsNzQsMTU5LDE1NSwxMzMsMTExLDExNywzNCwyNCw4NCw5MCw3NCwxNjUsMTM0LDQsODMsMTUxLDE1MCwxNzksMTAyLDEyMyw5NywxMTUsOTYsMjAyLDE2MCwyMTQsMjcsMjgsMTY3LDEwLDMwLDIzNCwxNTEsMTA5LDExMCw4MiwxODgsMjM3LDE3OCwxMjcsODgsMTMwLDEyOSwxODMsMjMwLDUxLDI1Myw4LDk2LDE4MSwyNTEsOTQsMTkwLDIwLDQ3LDQsMTAzLDExMSwxNjcsMTYsMTE4LDU3LDE2NiwxLDM5LDIyNSw4MSwxMDksOTcsMTg3LDE5MCwxODIsMTY2LDE1NCw3MiwxNDEsMTUsMjMwLDIwNSwyNDMsMTk2LDIwNiw0MiwzMywxOTAsMTM4LDExNCwxMjcsMTgwLDE5LDk0LDg5LDgsMTQ1LDE2NCwyNDMsMjAxLDY3LDIwNCwxNDcsODEsNzEsMjA3LDIyNywxNzEsMTU1LDU4LDExLDIxLDEyMywxOTQsMzgsMTc3LDE3MCwxODMsMjQ3LDEzNiw4LDEyMywxMzksMTQ5LDYsMTYyLDg3LDE5MywxNTAsMTk5LDE4OSwxNzYsMTQyLDI1NCwyNDUsNjEsODEsMTM3LDQyLDE1LDIsMjUsMTE1LDIwMiwxMTYsNjQsMTAyLDMzLDE4MSwxNjgsNTIsMTg4LDE3MSwxMzksNjYsMjA4LDE0NCwxNTIsMTAxLDkyLDEwMiw1NSwxMDQsMjIxLDEwNywxODEsNDAsMTg1LDgwLDE0MCwyNDYsMTY0LDIwNiw5Miw2MSwxNjQsMzUsOTMsMTQxLDE3NywxMDcsMjI0LDEzOSw3LDIxMSwxMzgsMTk3LDI4LDg3LDE0Nyw1MCwxMTYsMTQ1LDIxMCw4MCwxOCwxMDEsMTE0LDE1MywxNDMsMTc4LDEwOCwxNTYsMTUwLDE3MSwxMzAsMTM3LDE5MiwzNCw4NSw1OCwyNDMsNTEsMTk2LDI0NSwxMDMsMjAzLDE2NywyMTcsMTkyLDM3LDM0LDE0NCwxOSwxMSw1NCwyNDQsNTEsMTUzLDIxMiwyNCwxMTYsMTQyLDEwNCw5OSwyMDcsNDIsMTYwLDQ5LDEyMiw1NSwyNTAsNjYsMTA2LDk2LDIxNSw5NSwzMSwxNTEsMzUsODAsMTk0LDEzMCwxOCwxODQsMzgsMTkxLDgsMTgxLDIxMSwxODEsNzIsMTU3LDE2LDE3NCw1LDU4LDEyLDIyNywxOTIsMTg3LDExNCwxMSwxNCw4NSwxMjEsMTg4LDEwLDEwMywxNjAsMTA0LDcxLDEwNywyMTEsNjgsMTEyLDQ4LDExMiwxMzQsMTAwLDg0LDEzMyw5OCw5LDk4LDI1MSw1MSw3OSwyMDEsNTQsMTIxLDI0Myw3NiwwLDI1MiwyNDIsMTc3LDEzLDk2LDEsODgsMTM0LDQwLDI0NywxNiwxMTcsMjIxLDUzLDExMywyNDMsMjQxLDI3LDE4MCwxMzQsMzIsMjM3LDIyLDE2NCwyNTEsMTUwLDQ2LDE1NCwxMjEsMTMxLDg0LDY0LDExMCwxODYsMTU0LDQ2LDIxNiwzNSwxMDgsMTEwLDE5NywxODAsMTQ1LDE0NSwxMSw5NiwyNywxNDAsMTMyLDIxMCw3NywxMzAsMjE3LDE0MywxNCwyMDQsOTQsNjcsMTI0LDEwNyw1OCwxLDMzLDk0LDUzLDE0MSwxNiwyMzMsOTksMTkzLDEzNiw2NCw2MCwxMTksMTYxLDQ2LDExNywxODUsNTMsMTgsMTQ1LDI0MiwxMSw4MywxOTgsMjAzLDEzMyw4OCwyMjgsMTM3LDQ3LDIzMSwxNTcsMTUsMTY4LDE5OCwyLDY0LDE4NCw0NSwyNDUsMTcwLDI5LDIwMiwxMjEsNjMsMjAxLDE3MSwyMTgsNjgsMzksMTA3LDE5OSwyMDQsODEsMjUxLDQ1LDc2LDgwLDIwMiwxODcsNTIsMjEsNzMsMTE1LDE2Nyw0NSw1NSwxMzYsMTM0LDU1LDY0LDYzLDExLDk4LDE1LDE2OSwxODQsMjE0LDY0LDIwNSw2MywyNTIsMTAsMTMsMjI4LDIwNCwyMDksNjYsMTM4LDkxLDE5NCwxMDAsMjU0LDM4LDE0MSw0MywyMzAsODgsMzMsMTIsMTc4LDk2LDE5NSwyMCw4NSwxMDUsMjA0LDgxLDE0NSw1Niw0NSwyMzYsMTgwLDE2OCwxNTUsMTU3LDk0LDIxMiw2OSw5NywxMjksOTcsMjA1LDk2LDgxLDIyMSwxNTEsMjAzLDIxNCw5LDIwMiwxMzMsMTkyLDkxLDQ3LDExNiw2MSw5NSw2NCwyMDEsMjM2LDIyOCwxMzQsMjMwLDIyNiwxOTcsMTE5LDcyLDE0NSwyMjUsNDQsNjAsMzQsMTQ5LDEyNiwxNDQsMTc4LDYxLDEwMSw5MCw1NCw2MiwxMjksOTcsMjI0LDUxLDE5Myw3MSw4OCwxNywwLDQwLDE1NiwyNTMsNDAsNTgsNjcsNTMsMTcsMjAwLDIyOSw0MCwyMzcsMjI4LDE2Miw0NiwyNTEsMTgxLDIxMyw2MCwxNywxOTYsMjQzLDE2MiwyMTIsMTg1LDI0LDIzOSwxNjAsMjAxLDQxLDM1LDk3LDEzOCwyMiwxNTYsOTEsNjAsMywzMywxMTcsMjcsMjE4LDEzOCwxMzgsMjU1LDI1LDEyNSwxNzUsMTkzLDQ1LDEyNyw5LDEyOCwyNTQsMjQ4LDg1LDY2LDE4OCw0MCwxMjcsMjA4LDM3LDE0NywxODgsNDcsNjgsMjE3LDgsNCwyMDQsMTQsMTMzLDEzNCw5Myw4OSwyNTAsOTgsMTA0LDQ4LDQyLDE3NiwxNTAsMTgsMjUyLDE4Myw1NywxNDYsMTY3LDE0NSwxNjAsMTgxLDE5MCwyMSwzOCwyMSwxNDQsMzMsODUsODQsMjQ1LDg2LDgsMjE1LDEyMywzNiw5LDExOSwyMjYsNDgsOTIsMTE3LDMsMTYyLDQ4LDEzNiwxNjEsMTAxLDE5NiwyMiwzOCw1OSw3MCwzMSwyNDYsMTY4LDEwNiw5OSwxNTcsMTU2LDI0OSwxOTIsMTcxLDI1MiwxNjUsMjA0LDE0MCwxNjUsMTk5LDExOSw0OCwxMzIsMTI0LDEzNCwyMzMsMjE5LDE0NCw1MywxNTQsOTMsMTUzLDQsMTgyLDE0NCw3NCw0NywyMjksMjEzLDE5NCwxMDEsNDQsODAsMTkzLDE4OCwxNDcsMTQ0LDExNiwxMDEsMjIxLDE5NCwxNDgsMjQwLDI1MSwxOTksNCwxMTQsNjEsNzksMTIyLDkxLDE3MSwxOTgsMTg3LDEyMCwyMzIsMiwxMDIsMjIwLDEyLDE2Niw1NCwxMSwxOTEsNDksNjMsMjE4LDE3MSwxNTcsMTgsNTEsMTE4LDk0LDU3LDE0MCw4MCwzNywxMzQsNjAsOTksMTEsMTc3LDUxLDEyMSwxMTUsMTg1LDE5Miw4OCw1MywxMTUsMjMzLDE5NSw3Myw3NSwyMTgsMTQyLDIsNDIsNTMsMTM4LDM0LDg4LDE3NiwxMDAsNTIsMTMxLDY5LDE4NSw5MSwxNTYsMTYyLDI0NiwyNywxOTMsMTQsMjMzLDMwLDIwNiwxNjEsNDQsNjgsMTksNzEsMTc1LDE1MiwxNDAsNTcsMTgyLDE3LDYwLDEyMSw4NiwxMDAsMjQ1LDMzLDE3NCwxMTIsMTI0LDE4MSwyNywxNDYsMTM3LDIyOCwxMDcsMTE1LDg3LDE0NywxODUsMTE0LDY3LDk3LDEzNCwxMjIsMTE2LDUyLDUxLDIxNCwxNjUsMjAyLDYyLDAsMTYyLDMxLDIwMywyMSwxNSwxMiw4MSwxNzQsMjI5LDE4OCwxOTcsMTk3LDExMCw1NywyNDEsMTI2LDI0Miw5OCw3MSwxODMsNzIsODQsNDMsMTcsMTA1LDg0LDg1LDQ2LDExMSwyNDQsNCwzNywxNTEsNSwyMDYsMTk3LDE4LDExMyw3MSw2MCwxMjIsMjAwLDE1NiwyMjcsMTMwLDE5OCwyMzYsMTc4LDExNiwzMiw1MSw0MywxMjAsOCwxMSwxMjcsNTksMTA0LDczLDE5NSwxNzgsMzQsMjEzLDEyNiwxMDIsMTgwLDE5Nyw3LDExOSw1LDE0OCwyMzEsNTcsMTU2LDg4LDE1NCwyNywxMDEsMSwxMTYsMTMyLDU0LDU4LDgzLDEzMCwyNDcsMjQ3LDczLDE3OCw0LDE0NCwxMjYsMjE2LDEwNSwyMzIsMTQwLDEzLDIzMiwyMjYsMTgsMjMxLDE2MywyMDcsMTU4LDIzMCwxMDYsODEsNSw0MywxMTgsODIsMiw3MCw0MCw0LDIwNiwxODcsMTU4LDE5OSwxMTgsODAsNTYsMjI0LDM5LDIxOSwxMTgsMTUzLDM3LDI0NSwyNSw0NiwyMjksMTkzLDEzMyw1MiwyMDQsMTU2LDM1LDIwNCwyMzksMTI0LDE3MywyMjAsNjUsMTY0LDE5OCwxNjYsMTUsMjIsMTA0LDE5Miw2OSwyMzYsMTQ4LDEwNiwxNzksMTc1LDM5LDk2LDE4LDI3LDU1LDE4MSwzNywxNiw2MCwxOSwxNzgsMTEyLDczLDIxNywxODMsMTc5LDEwOCwxNDksMjMwLDE3MiwyMDUsOTgsMTIwLDE0MiwyNTMsMTcsMTcwLDY3LDExNywxOTYsMTgyLDE3MiwxMDEsMjM2LDE3MCwxMjEsMTg2LDU3LDc3LDMzLDY2LDMyLDczLDExNywxNzIsMTIyLDE1MCwxMzQsODAsMjIwLDkyLDIzOSwyMjksOSw3MywyMTksMTY4LDMzLDI1MSwzMyw5NywxNzEsMjA0LDY2LDE0MCw2MywzNSwxNTMsMTg5LDIyOSwxMDYsMTM5LDEwMSwxNTUsMTg1LDE2MCwyMTAsMzgsNTAsNDEsMTgzLDY5LDU5LDE2MCw0OSwxMTIsMTY4LDIzNCwxMzQsOSwyMDMsMzYsMTk5LDE2MiwxMDgsNTEsMTc0LDI0MSwxMjcsNzMsMTcyLDE4Nyw1MSwyNiwxNTIsMTczLDE0OSw4MCw4NSw1OCwxNjUsMTgyLDg3LDIwMCwyMDcsMTI5LDEyNiwxMzUsMTE5LDE2NSwzMCwyMzEsMTM1LDIyNCwxOTMsMTY4LDAsMTYxLDEzOCw2MCwyNDgsMTAsMTg2LDQ4LDMxLDkzLDE3OCw3LDE5NywxOTksNTQsMjQ0LDE1MCwxNTcsMCwxMjEsNjgsMjQwLDI0NywxMTAsMTQ5LDY5LDExMiwxNzcsMTAsMTk4LDI0OSwxMjMsMTUzLDEyOCwxOTIsMjMsMjYsNTYsNzQsMjI3LDIxNyw5NywxMzIsMTgzLDE1LDIyLDEyMCwxNzUsOTMsNDgsMTgsMTE4LDE3OCwxMywyMzIsMTkyLDEzMSwxNTgsMTc2LDExNywxMjAsMTY1LDUyLDIzOSwyMjYsMTg1LDM2LDExNSwxMjEsMTU5LDE2MCw5MiwxMjEsMTQ2LDEwNiwxNzEsMTk5LDkzLDIzMiwyMywzNSwyNiwzLDQwLDUyLDUsMjA2LDE4MiwyMzMsNzQsMTg0LDkwLDYsMjQ2LDE4Nyw1LDE5NSwxODAsMTcsOTUsMjQyLDEwOSwxNjQsMzcsMTQ2LDgzLDE5NywxLDMyLDE3MSwxMjYsODMsODksMTAzLDksNzYsOSwxNzQsNjksMTY1LDkzLDE4Myw5LDczLDE2MCwxMTEsMTI4LDEyLDE0OSwxOTgsMjQ4LDExNywwLDI4LDIsMTY3LDIzNSwyMyw3Myw5NywxMTYsMTEzLDE1MCwxNDUsMTY5LDcsMjA1LDExMywxNDcsNywxMzgsMTgwLDgsOTcsMTAwLDEsOTUsMTY0LDUwLDU2LDY4LDE3MiwyNDQsNiwxMzAsOCwxOSwxNzgsMTEwLDI0MSwxNTIsMTM2LDEzNCwxOTYsNTYsMjAxLDE5NiwyMDgsNTMsODksMTMyLDIyNiw1NCwyMjQsMjYsMjcsMjIsMTc4LDkyLDQ2LDQyLDY5LDEwMCw1NiwxMTgsODMsMzgsMTk4LDAsMzcsODYsMTE4LDIyNSw0LDQ3LDMzLDIzLDIwOCwxOTIsMTA0LDEwLDczLDc3LDIxNywxMTcsMTQsMTY1LDIxMywxNTMsMzgsODMsMTUwLDEzNywyNDMsMjAwLDIzNywxNywzNCwxMjksMTc4LDg3LDUzLDE0OSwxNTAsMTE3LDEzNCwxODYsNzQsMjI2LDE0MiwxMjAsMjExLDEwMyw3NywxNDksNzUsMzEsMjMwLDE1NSwxNjEsMjI4LDcsNTgsMTY3LDE0NiwxNzQsMTMzLDYsMTk4LDIxLDUwLDE0NCwyNTIsMTQzLDI0OCw5OCw0LDE2LDEzOSwyMDQsMTMsNDksMTEyLDgyLDE4Miw2LDkyLDE2OSw3MSw4MCw2NCw0NSwxNDEsMzksMTgxLDIwNCwxMzMsMTY2LDg1LDQxLDE0LDIwMywyMjksNTEsMjU0LDIyLDE1Miw2MCwxOTksODIsNDIsMTg2LDM3LDMzLDI2LDEsMjM2LDcyLDE1NSw5OSwxMjEsMTU5LDIyMCwyMSwxNjYsMTEyLDY3LDQ0LDQzLDE2LDM2LDE1NywxNDAsNDQsNTcsMTQ2LDUyLDEwLDE2NiwxNDMsMTQ1LDMyLDgwLDE1NCw5MiwwLDY0LDQ4LDE0MCwyMjgsMTkyLDE3MSwxNzMsMTcwLDE5Niw0NywxMDQsNzUsMTA0LDgwLDUwLDE1NywxMzYsMjksMTI3LDEzOCw0NSw1LDIxNCwyMiw3MSwxMTYsNjEsMjE3LDI4LDI3LDk0LDEyOSw2LDEyLDc2LDE0Niw5MCw1OSwxMjQsNDYsMTY2LDg2LDIwOCw4MywxOTUsMjQ4LDExNSw1OCwxNTgsMjE2LDEzOCwyMjcsMTE0LDcsMjQ0LDM5LDI2LDI3LDIzMiwzNSw5NiwyMyw4LDIwLDE1MCw4OCwxMDIsMTE4LDE3MywxMDMsMTUxLDEyOCwzMiwxMzIsNDYsMTM5LDY0LDE2OCwyNDAsMTQ0LDQ2LDQsMjEsNjcsMywyMjksMTU0LDY4LDExOSw3MCwxMDgsMjQ4LDE3OCwyMDUsMTg1LDEsMzQsNjcsNDIsMTQ5LDgxLDE1MywxMzUsMTM5LDk5LDIzNSwxODEsMSwxNjksMjYsMjIsMjA2LDUwLDE2LDU1LDE2LDksMSwwLDE5NSwyMzQsMTg1LDIwMywyNDAsMTk3LDEwMSwyNDMsMjQ0LDk3LDE3OCwyMDIsMTg3LDQwLDM5LDE0NCwxMjIsNyw2MSwxNjgsMSwxNjUsMTc2LDc0LDEyNCwxNjksMTM5LDE2MSwxNDcsMjE4LDE4MywxMTMsMjAsMjYsMTIwLDEwLDEwOSwxMjIsODEsMTAxLDEyMywxMTQsMTQyLDk2LDE1NCwxNzQsMjI4LDcxLDcxLDI0NiwyMzAsMTE4LDc1LDEzNCwyMDYsODksMjAxLDExMCwyMzcsMTc5LDQwLDE2MywyMDksMTg3LDk5LDAsNTQsMTAzLDE2MSw3MiwyMjAsMTIsMjA2LDEzNSwxMTQsMTE0LDE2MywzNiwxNTgsMTIxLDEwMSw1NCwxNTIsMzIsMTY3LDExOCwxMSwxOTQsODcsODcsMjAyLDUyLDIwOCwyMDAsMjU0LDI0NSwxMywyMDAsMjIsMTQ5LDIwNSwxMDUsMTIxLDgwLDE2NywzNSwxMDgsMTMyLDE1MSwyMDksMTcwLDYyLDE3MywxNDksMTgyLDc5LDIzLDE2LDEsNjksMTA5LDE2LDI1MiwxNjQsNjksMjQyLDE5MSwyOCw1LDEyNCwzMCwxNjIsMTYxLDcwLDMyLDE5OSwxMDYsNjksMjAzLDk3LDI1MCw4MywyMjQsMjA0LDE5MSwxOTEsMjM1LDk5LDEyOCw1NywxNTAsMTM3LDU3LDE0NiwxMDMsMTUzLDE0Myw3MSw1NSwxMDEsMjA0LDE1NSw3Nyw1LDIyNiwyNCw0OCwxMzQsNjUsMTMsMjA5LDE4MSw0MCw4MywxMDYsMjM0LDExOSw2OSw1MCwxNjMsMzgsNzIsMjAyLDg5LDE2OCw4LDIwMywyMjIsMjQ3LDExNywxMzIsMTIzLDExNSwxMTYsMjQ1LDE4OSwzNSwxNjksMTQyLDEyMyw0LDEyOCw0MSwyMjcsMTE2LDE0MCwxMDIsMTEsMjQyLDIyNiwxODIsNDMsNTIsMjAxLDEzNiwxNzgsMTg0LDE1Nyw0LDEzMSw5NSw1Myw3MiwyMTIsMTgxLDk0LDIzOCwyMTYsNjgsMjAzLDM2LDExNSwzOCwyMDgsMTk4LDIxOSwxNDgsMTYzLDcxLDEzMCwxMTEsMzcsMTMyLDE5NCw5Niw5MCwxMDgsMTczLDAsMTY0LDE2MCwyNDAsMTY4LDYsMTk2LDMzLDEyMiwxOTUsNywxMjUsMjExLDk3LDE2OCwyMzAsMTc3LDk5LDE3MiwxMzksOTksMTI5LDgxLDI1NCwxNTQsMTQ2LDQsMTYzLDExNyw0MCw3LDEyMSwxNTAsMTA3LDI0LDE3MywyMDksNjcsNjQsMTcyLDE4NSw2MCwyNiwxOTQsMTIyLDE4NSwxNzMsNDgsMjM2LDExMCwyNTMsMTc4LDE1LDI1MywyMzIsNTAsMTg5LDE1MSwxNDAsMjQyLDE3OCwxMjAsMjM3LDc2LDE3OSwyMTksMTM5LDY1LDE5OCw1OSw2LDE4NywyMzMsMTYyLDQ2LDE5Nyw5Myw2LDE4MCw5LDIxNiwyNTIsMTk1LDI0MCwyNCwxNjksMTU0LDE5NiwxNjMsMTIzLDIzMyw0OSwzNSwxMjMsMTAsMjExLDQsMTkzLDUzLDE5NSw0NSw1NywxNTUsNzIsOTEsNDEsMTQzLDg0LDI1LDIsMTI4LDEzNSwxODUsMTU2LDUwLDE5NSwyMzMsODYsMTYwLDExNiw4NSwxOCw3MiwxNjIsMzksMTk3LDI0MCwxNzUsNjUsMTU1LDEyOSw0MiwyMTIsOSw2MSwzLDUxLDc1LDE3Niw1NSwxMzgsMTM1LDIwNywyMywxMjgsMTMyLDE2NiwyMTIsMTU3LDkwLDExNiw5NywxMDQsMjQzLDEwLDE1MCwxNDAsMTYyLDE2Niw4NSwxMzQsMTU3LDMyLDEyMywyNTEsMTAzLDE4MywyMTQsMTcwLDE0LDI0OCw3MiwxODksNTIsMTY0LDE4NSw2OCwxNDQsMTAyLDcsMzcsMTA2LDUyLDIyMCwxNTUsMjEwLDgyLDIwNyw3NywyNDUsMTk3LDI1LDUyLDI3LDIwOSwyNTEsMjEsMTksMTk1LDYyLDg5LDEzOCwzMCw4Myw0LDE0NCw3OCwyNDMsODQsMTI5LDIzMiwxLDIxNywxNDgsMTAxLDE2OCwxMDIsODcsOTksNjYsMTcyLDgsMiwxNTUsMjE1LDEwOCw1MywxMjksODMsMzQsMTk3LDEyOCwzMCwyMjAsOTYsMTIzLDgzLDM3LDM3LDE0OCw2NiwxNDksMTkxLDUzLDU2LDE2MCwxMTIsMTc3LDM4LDIxOSwyMjgsMTUsMTQ1LDE0NCwxODcsNjEsNDQsMTIxLDI3LDEyMCwyMTIsOTcsNzksMTYxLDE3OCw3Miw4MiwxOTEsNTksMTMwLDExMCwyMzYsMjM0LDE1NCw0OSwxMjAsNTAsMjMsMzIsMTQ4LDExMywxNjksNDYsMjEsMTEwLDExLDEsMTkwLDExLDI1NSwyNCwyMzIsMjUsMTkyLDE2Niw2NywxLDU5LDIyLDEwOCw1MiwyMjEsMjE3LDE5NCwzOSwxMjMsMTYzLDQxLDEwNywyMDgsNjAsMjUwLDY0LDU1LDE5MSwyMzgsMjI4LDEwNyw2Nyw1MCw2NiwxMzcsMTEyLDczLDExNSw4MiwyMTQsOTgsMjUwLDIwNCwxNTAsMjQyLDc5LDIyMSwxODEsMjQ1LDM2LDE1MSwyNSwyMTgsOTQsMjQ1LDE4MiwyNTMsMTcwLDgsMTE2LDEyNiwxODcsNjIsMjU0LDE1NSwzNywxMjcsNjAsMjI3LDQyLDMxLDUsMTk4LDE2NiwxNjMsMjE2LDE4NSwxNTUsMTk1LDE2OCwxMDgsMTY0LDIzMCwyNCwzNiw1Nyw2NCwxMTUsMTMyLDEwNSwyNDAsMjEsODIsMjMsMyw4MywyMTMsMjA3LDIzMiw2MF19fX0seyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsyMzcsMTQ3LDE0NSwyNTEsOTgsMTUsOSwxMiwxODcsMTAsMTkyLDE1NywxMTQsMjMsNjAsMSwyNDAsOTUsMjA1LDE4OCw1NCwyNDAsMzMsMTYwLDEyNSwyMjQsMTk2LDEzOSwxNjAsODQsMjE3LDE2NiwxMDUsMTE5LDExNiw5NiwyMDgsMTY3LDE4NiwxNjgsNDMsMTkyLDIxMywyMzAsMTQsMjksMTAsNTEsNjAsMTQ1LDE4OCw0NiwxMjEsMTk4LDMwLDU4LDE2Myw0LDI0NywxMTAsNzIsMTgyLDg2LDg2LDU4LDE1OSwxOTcsNTEsMTU3LDEwNSwzNCwxMzQsMTgwLDI4LDE3MywxOTQsMTMyLDEwNiwyNDcsMTMxLDE5Nyw2OCw4MywxMDAsMjQ0LDI0NCw1MCwzOCwyMTAsMjksNjgsMjUyLDM2LDcwLDI0OSw2NSw1MSwyMTIsODIsMTg1LDkwLDE1MiwxMzQsMzksMTMxLDQ5LDE4OCw2LDEwNSw5OCwxODMsNzIsMTA1LDkyLDIzNiw2NCwxMzgsNDAsMTksMjksMTUsNjAsOTgsODEsMTE3LDY1LDg3LDE1NiwxMCwxOTgsMTg2LDEzNiwxNzksMjQ0LDE3Niw0NSwyNiw1NSwyMzIsMTk0LDE4Nyw5NSw4MiwxNDUsNSwxMDAsNzEsMjA2LDEzMCwxOTgsMTA0LDIzNCwxODksMiwyMjUsMjEsMTI3LDM2LDE0LDEyLDE2Nyw1LDY1LDUwLDE1Miw0OCwxNDAsNTgsMzYsMjE4LDEyNCwxOTgsNiwyMDYsMTY5LDEwMiwxMDYsMTc2LDU2LDE5MiwxNzcsMTQ1LDE3Myw2MywzOCwxMTUsMTcsMjQ5LDE0LDE5NywyMTEsMTg2LDkyLDEwLDE2OCw1NSw2NSwxMDksMjE2LDIwMSw1NCwyLDEyMiwxMSwzMyw0LDE3Nyw1MSw5MCw5OCw3NSwxMTIsMTI0LDUyLDQyLDQxLDEyNywzNiwxNTIsNzMsOCwxMjAsNzksMTgzLDY5LDE3OCwyMjAsMTUyLDIxMSw3NiwxNjUsNDMsOTEsNjIsMTA0LDEzMiw3MSw0MCwzOCwxNDksMjQzLDEyMCwyMDcsMjIyLDEzNSwxNzgsMTk1LDEzMiwxNDQsNDUsMTY0LDIzLDE0MCw4OSw0NywxNjAsMTAsMTA5LDE0MCwyMDksMTA2LDEzOCwxNDQsMTY5LDE0MywxMiwzLDE5MSw3NCwxNTksMTk2LDIyNSwxMDEsMjE3LDIxMCwxNTksMTUzLDI0MCwyNiwyNTEsMjUxLDUsMTIsMTE5LDEyMywxMDYsMTcwLDAsMTc1LDcwLDk2LDE0OSwyMDgsOCwyMiw2NiwxOTEsMjA2LDEzNiwxMDAsMSw4Niw0NSwxNTQsMzcsMTg5LDExMiw2NywxNTQsMzEsMjUsOTUsNDAsMTY5LDE2MSwxMTEsMTY0LDM1LDIyOCwxMzcsNTcsMTI0LDIzNCw4OCw5Miw3NCw1MCwxNDgsNjAsMTY3LDUwLDIwOCw1Nyw4MywyMzEsMTA2LDIwMCwxMTIsMzEsNzksMTE5LDg4LDE0NiwxMjIsMjYsMTAzLDIxLDQwLDE1MCwxMzgsMTkyLDE1NywzNSwxMDIsMTIyLDEyOSwxNDYsMTI0LDIyNyw4MCw2NCwxNjUsNTksMTI5LDE1NSwzLDE4OSw5MCwyOCwxOTksMTUyLDkzLDMsMTEyLDEzOSwxOSw4OSwxNjAsMTAsMTYwLDEzNiw4LDIyNywxODcsMTc0LDU3LDE1MiwyMSw4NCw5Nyw3NSwxODEsMTEsNzEsMjA5LDgyLDEyNCwzMiw0NiwyOCwyNDYsNDQsMTIxLDgwLDYxLDExNywxMDUsOTAsMjMxLDEzMiwzNSwxOTMsNzEsMTQ2LDE1OCwyNywxNzksMjM4LDgzLDEwOCw2OSw4OSwxODgsMjI5LDIyNCwxMTAsMjM3LDExNSw5NCwxNTksMjAwLDUzLDEyOCwxOTIsMTI1LDEzMywyNTIsMTk4LDI0OCwzLDQsMCwxOTksMTIwLDE2NSwxODMsMTc3LDEzOCw4LDE2OCw5NywyMjUsOSwxOTksNDAsMTIxLDYwLDIwNCwxNCwyNDgsMjEwLDI1LDIzNSw4MSwxNCwyMTcsMTI4LDAsMTQxLDE5NCwxMjQsMTk4LDI0OSwxODAsNiw2NSwxMTgsMTMzLDE1MCw2Niw3OCw5MCwxNTUsMzAsNzEsMTYyLDE2NSw4Nyw2OCwxNDIsOTgsMTk4LDE1OCw4NCw0MSwxNTAsMzUsMTM5LDEwLDksMTQ0LDgsMTMzLDEwMiw3MCw3MSw1LDIwMCw2OSwxMjMsMTkxLDE2MSwzOCw5MSwyMTIsMzIsMjUyLDI0NywxNjIsMjE3LDEwNSwxODAsOTAsMTQwLDE0NiwxLDIxMywxNjAsNDcsMTM1LDE1OCwxMDIsMTE4LDIzLDI1MiwxNzAsNzQsNzEsNzUsNSwyMzQsNSw2NSwyMjgsMTA3LDM2LDg3LDEwLDExMiw5NywzOSwxMTUsMjE2LDMyLDU5LDE4MiwxMzksNTksMTc0LDIsMjMsMzAsMTA3LDc0LDU0LDE2OCwxNTAsOTIsMjEyLDE5MSwxMjUsMTAzLDE1NSwxNDMsMTQ2LDM4LDE4NCw5NywzMiwxMTQsMTk1LDcwLDQ5LDk2LDE4MSwxMDIsMjI5LDE4LDE2NywxNzksMTM1LDE2MywyNSw5MywyNywxMzMsOTAsMTkzLDUzLDE0MywxMTIsMzksMTI4LDE5OSwyOCwxNzMsMjA4LDQ4LDU0LDEyOCwxNjIsNDgsNDksMzksMTI0LDIzNCwyNDYsMTg1LDEzMiw2NCw2OCw5OCwxODAsMTE1LDk2LDEsMTg4LDM0LDgzLDk5LDE0OCwxNDcsMTQ5LDE4MCwxNywxMzAsMTc1LDksNDUsMjEsNjgsMTQxLDEwNiwxOTUsNjYsMjAyLDE2MSw1Myw3OSwyMDksMjcsMTEwLDY5LDUzLDg3LDE3NywxNzUsMjksMjQ2LDIwNSwxMTIsMjM1LDExMywyMzUsMTAxLDQyLDIxMywxMTksNCw5LDIzNiw0Miw2MCwxNDYsMTI4LDE1NywxODgsNTYsMjEzLDM2LDMwLDIzMSwzOSwxOTUsMjI0LDQ4LDE3MywyMTEsNDQsMTE1LDIyNSwxODYsOTMsMzQsNzQsMTkwLDEzMyw5MiwxNjEsMTE4LDI2LDE5NywxNDAsMzcsNTYsNDksMzcsMjYsMTA4LDI0MywxMTQsMjUxLDIyNywxMiwzLDU3LDUxLDIyMCwxNDUsMjIsMjMyLDQwLDI4LDM5LDEzNSwxNjksMTY2LDE3NiwyNCwxNTUsMTgsMTI2LDIxLDE5OCwxODMsMjUsNzYsNzQsMTQzLDM4LDY1LDIyNSwxNjAsMTEsMTcxLDE1NCw5NiwzOCwxMjIsMjksNywxNjgsMTc3LDYwLDUxLDExOSwyMjksMTA2LDg5LDIyLDE2LDEyMSwxNjEsMTMwLDEwMywxMDIsMjgsMjgsMjI1LDEwMCw4NiwxNSw1MCw3NSwxOTQsNDAsMTQxLDEzOSwxMTUsODYsNDcsMyw1NywxNzIsMTM1LDAsMiwyMzUsMjAxLDE0NywxNzIsMTY1LDE1MiwxNjYsMTQyLDE0LDI1Miw0NywyMDEsNDIsMTQ3LDEzMSwyMDQsNTAsNDEsMTksMTc5LDYsNTYsMTIwLDEyLDIzMCwxNjMsOTksMTI4LDE2LDEwMSwxODgsMTM1LDI1NCwyMzIsMTM3LDExMyw0MywxOTIsMjI1LDExMiwyMDAsMTYwLDIwLDIsMjU0LDE4MywyMSwyOCw1NSwxMzYsNjAsMjM1LDE1Nyw3NCwzNiwxMzksMjI2LDg2LDEzMCwyNTUsMjI1LDE2NywxODgsNDksOTEsMTc0LDE0MCwxMjksMyw4NCwxMDAsMTE1LDQzLDc1LDQsMTI0LDEzNSw0NSwxNjMsMzgsNDQsMTU2LDE0MCwxMTcsMTgwLDE3MSwxODAsMjgsMTkyLDUwLDEyOCwxMjcsNzcsMTksMTc4LDEzMCwxMzQsMTA3LDE4OSwxNzAsNzQsMjMzLDEwNSwxMjMsMjQ0LDEyMCwyMDcsMTMyLDEyMywxNzUsMTc4LDE4LDE4NCwyMjQsMzksMjEsMTgxLDEwMiwxOTUsMjE5LDE2MiwxMSwzNiwzNSwxMTYsNjYsMjEsMTY1LDE0MSwxMCw0NiwxMjMsMTcsMTkyLDcyLDM4LDE4MywxODksMTgxLDE3MSwyNTEsMTgzLDk2LDcxLDEzNCw0NiwzMCwyNDgsNDQsNjMsMTA4LDExNSw4MCwyMjAsMTQwLDEyNSwzNSwxMzIsMjI1LDIyLDUsMTA4LDI4LDE3OCw2MywxOTgsMTExLDIzNiw4NSwxNzksMTQ5LDEsMTgzLDE0MywxNjIsMTM5LDIyLDgwLDQ2LDIzNiwxMzksNjEsMTA4LDEzMywxMywxNzUsMjgsMTc5LDE2MywxODEsNzMsMTksMTAxLDQ3LDU1LDI0NSw0LDExMiwxMDAsNCwxNjcsMjI0LDEyNSwxNTAsMTQ5LDIwMSw1LDEyOSwxNzksMjM0LDIwMywxMzcsNTQsOTEsMTAxLDExOSwxNzEsOTgsNzUsOTksNjUsODUsMTI5LDE5NCw5LDksMTMwLDQsNTUsMTM1LDE2MywxNTMsNTEsMTM3LDIxLDMyLDIxMiwzOSw4MCwxMTcsNTQsOTUsNzcsMjAzLDExOSwxMjEsMTU0LDcsMjE0LDE0NCwxMjIsNzMsMTMxLDE2NiwxMjgsMTc3LDE4OCwxODMsMSwxNzQsMTUxLDE4NSw2NSw2MCw4Niw4MCw5OSw1OSwxNCwyNCw2Niw0MCwyMjEsNjgsNDcsNjUsNjUsMTc3LDEzMSwxOTIsMTQ0LDQ3LDcsMzcsMzMsNzYsMTY3LDI1NCwxNjcsMTIxLDIyMiwxNjYsMTU0LDYzLDk3LDE1OSwyNDAsMTY5LDEzNiwyMzMsMTIyLDExOCw1NywxMTIsMTk5LDEzMSwxNzcsMjgsNTEsMTQwLDE4MywxMTEsMjUyLDE1OSw5NCw3Niw1MywxMzAsNDEsMTcyLDExNCwxMTksMTMwLDE5NSwxMjQsNTYsMTIyLDIzMCwxMCwxMDgsMTEzLDEzNSwxNjcsMTU1LDQ3LDc3LDEwNyw2OSw4NCw3MCwxMDksMjUsMTYsMTE3LDE2Nyw3MiwxMzksMTMsMTAzLDE1NCwyNywyMTAsNjIsNDMsMCwxMjYsMTg0LDIwOCwxMDAsNDEsMjQwLDE4NSwxNjIsMjExLDU0LDU2LDIwNywxMTYsNDMsMTE3LDE4LDIxMCw0MiwyNTAsMjExLDI1MCwxMTIsMTM3LDExNywyNDcsMTUwLDExMyw0MywxNzMsMjI0LDE4MSw1NCwyNTEsNTIsMTMzLDEyMiw3NywzMSwxMzUsMjA3LDIwNSwyMzUsMjI0LDEzNCw1NCwxMzAsMjMzLDEwOCwyMDcsMTU1LDE1Myw5Nyw2Nyw3OSw5Miw0MCwyMDYsMjE2LDI0Myw4NywyMCwzMSwyMjEsNjUsODEsMjA2LDE3OSwyNTEsNDcsMzYsMTAsMTMxLDE3NSw1MF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzMsMTg3LDE2MSw2OCwyMDgsMTI5LDIwNSwyMzIsNTEsMjI1LDE4LDQsMTMwLDE2OSwyMDMsMTg4LDExNSwxODgsMjM2LDY3LDE2NywxMjIsMTg0LDE5OCwxMTUsOTYsMTU0LDI1MiwyNDIsMTEwLDE5MywxNTQsMTg1LDI5LDI0NywxMjIsMTQxLDQ0LDE1Myw5MywxNjQsMTkyLDEyMSw1NiwxMTQsMTU5LDEzNSwxNjYsMTIxLDEwNywxLDE5NiwyNyw1NSwyNDMsMjE5LDExMiwyNTQsMTIxLDUwLDQ1LDE5Miw3NSw0MCwzMiwyMDIsMjQ0LDksMTI5LDIyLDU3LDE3Miw0MSwyMzIsODQsOTAsNTIsNzksMTYzLDExNCwzNCwyMjMsOTYsMjksMjQ3LDE0Niw3NSwzNywyMTcsMTA5LDEyOSwxNzksMTksMjQ4LDE5MiwxMjcsMjE4LDQ0LDE3MCw3OCwyMjUsMTUzLDkxLDUwLDU5LDE1MywxODQsNDQsNjcsMTM4LDIwNiwxNzgsMjE1LDYsMTMxLDE1MSwyNCwzOCwxMjIsMTY2LDIzMCw2OSwxMiwxNTIsMTY1LDE3MywxNzIsMTE2LDE0MSw2OCwyMDIsMTg3LDExLDIyNyw3OSw5MywyMTIsNzEsNjYsNDgsNDMsMCwxOTcsOTMsMTYyLDIyLDEwOSwyMTQsOSw1NCwxMTEsNTEsMjYsOTQsODMsMywyMDMsMTk1LDMsMTYxLDExNywxOTAsMjI0LDk5LDIwMywxNzcsMTI0LDExOCw4MCwyLDE0MiwxNTIsMTk5LDE0MSwxMTEsODAsNTQsNDIsMTM3LDE0OCwzNiwxMzYsMTAwLDIyNiwxODQsNjgsNzEsMTIxLDU0LDQ2LDg4LDE0NiwxNywxMzYsMTE3LDI0NSw1OCw3NCwyMzYsMTQwLDM3LDIzMiwxNzksMTg1LDEwMywyMTgsMTk2LDEwMiwxNTEsMjMsMjMsMjE3LDMxLDY1LDEyLDYxLDEyMyw5Miw3NiwxMTYsMjA5LDkzLDIzMyw5OCwzLDUyLDI0Nyw5NiwxNSwyNDcsNDUsMTg2LDE3MSwxMzIsOCwyMTcsMTYsMjE4LDI3LDEyOSwyMjksMTI0LDIwNCw4MCwxNTEsNjMsMTQsMjAzLDM2LDg0LDk4LDE1NCw5MiwxMTIsMzgsMTgyLDI1MiwxNzIsMjA2LDE2NywxOTAsMjM4LDE0NiwxNTQsMTEyLDEwLDQxLDY5LDE3NywyOSwxMjEsMTc5LDE2NCwyMzgsMjI3LDMxLDk4LDIyNSwxNTAsMTE1LDIxMywxNjgsMTg0LDc1LDU0LDIzMSwxNDYsOTksMTM5LDIwOCw1MCwxNjMsODEsNTYsMTk5LDE3Nyw1LDIwMywyMjUsNDYsNTEsMTk4LDE2MiwxODksODQsMTQ1LDMsMjQ0LDE5Miw1MiwxMDUsMzYsMTM2LDIxNCw5OSwyMzksNzEsOTUsOTcsMTMzLDE1NywxNzIsMTk0LDE3OSw1NywxMzUsMTM2LDIzOCw2Niw5MiwxNTgsMTY5LDEzMiwxMTEsMTA1LDE1OCwyNTAsMjIsMTgwLDQ0LDI0LDczLDEwNyw1MSw0MiwyNTAsMTA0LDM0LDE4NCwyMjQsMTQ2LDM1LDEyMSw0NywxNDAsMzYsODIsMjQ0LDIxLDI0LDIzOSwyNTAsMTY2LDI0OSw2OSwxMjAsMTA5LDEwNywxMTgsMjA2LDUyLDEwNSwxNjMsNzMsMjUsMzcsMjAwLDE1MCwxLDE1NSw0OSw1LDIyNSw0Niw4OSwyMzUsMTExLDc5LDE1MSwxNzIsMTk0LDEzMiwzNiwxMCwxLDEyLDM0LDIyNyw2NiwxNjAsMjQ2LDc4LDE4LDEzOCwxNTMsNTEsMjM1LDE3LDk0LDEzNSwxOTMsMTEsMjE0LDUyLDMzLDE5NiwxMTEsMjEwLDI0Miw0MCwyMjcsMjYsODcsMTI1LDE1NCwxMjAsMTE2LDEzMyw4Miw5LDIxOSwxOTYsMTcwLDI3LDEwLDE4OSwyMiwzOSwxMDUsMTcwLDk4LDE1LDEzMSwyMDUsNDEsODksMTQzLDM2LDI0MSwxOTYsMjI1LDE3OSw3NSwxODgsMTQ0LDMxLDU1LDM5LDExNiw1LDIsMTEsMTQ1LDczLDExMiwxODIsMTE5LDIwMiwyMjMsNDQsMTUzLDgzLDI2LDE5OCwxMzgsMjEsNTYsMTU2LDE3OCwxODIsNjgsMjQ0LDE4LDE4NywyMzIsMTQ1LDc1LDIzNiwxMTIsMjQ5LDE1MCw0MCwyMywxOTYsMTg1LDI1NCwxNTEsMTUsMTY0LDI1MSw0OSwyNDMsMTg2LDcwLDIwNiwxMDUsNzEsMjQ5LDE0OSw0LDI0NCwyMzQsMTksNzQsNTQsMTgxLDE3Miw1NywzMSwxODQsMjE5LDEzMywyMTcsMTIwLDU0LDE4Miw3NCwxMjEsMSwxOSw5OCwxOTksMTAsMTIyLDI1NCwzNywzMCwyMCw3LDIsMTE4LDE2OCwxNSwxOTcsODQsMzcsMjE5LDIxNiwxNTQsNDAsMTU2LDUsOTIsMTEyLDE1Miw2MSwxLDE5OCwxNjMsMTkyLDE2NCwxMiw2NCwxNSw1OSwxODQsMTU1LDEwMywwLDkwLDE1MiwyMDAsOTgsOSwyLDYzLDE4MiwyMzEsODUsMTMwLDE2NywxMzQsMTE4LDYwLDE1NywyMjgsMjA5LDE5MywyMDcsMTE2LDE0NywxNCwzNiwxMzksNDEsMjIwLDE1MiwxODQsOSwzNSw4MywxMTYsMTA5LDIzNSwxNzksOTksNjEsNzUsMzQsOTgsMTAxLDMsMzQsNzQsMzIsMTY0LDE5OCw1OCwyNDIsMTk5LDE3MSw3MywyMiw0Myw2NSwyMjUsMTU2LDM1LDMyLDMxLDEyLDIzNiwyNiw2MCwxMTksMTY3LDE1OSwxMDcsNyw5OSwxODUsMTg0LDExMywxMDQsNDYsMjM2LDMsMTM3LDc4LDIxOSw5MCw4NCwyNDEsMTksMjAzLDUzLDEzOCw3NywxNyw2MCwxODIsMzQsNjcsMjEwLDczLDE3NiwyNCw2NSwxNTcsOTQsNDQsMTMzLDE3OCwyNDIsMTQ0LDE0MSwxMzgsMTIwLDE3MiwxODIsMTE2LDYxLDQyLDE1MSwxOTUsNjcsMTk0LDczLDEzMSw2MCwzLDI0Myw4MCw5OSwxNjMsMjA3LDIwMCwxOTMsNzUsMzIsMjI5LDEyMiw4Nyw0MCw3NiwyMjYsMTg4LDUxLDExNyw3MywxNTgsMjA3LDE3MCwxMzEsMTkzLDg4LDIzLDM1LDEyOCwxMTIsMTMwLDIyNywxNzAsNTcsNTAsMTg0LDQ3LDIzMSwxMjksMTYyLDcyLDYzLDIyMiwyMjcsMTA4LDgwLDMsMTY3LDU0LDEwMSwxNDQsMTk0LDIyMCw4MSwyMDgsMzksMTQsMTI3LDExNywxMDUsMTgxLDE0Nyw2LDE4NCwxMzAsMTcxLDIyOSwxMzAsNTAsMTIwLDE2MiwxNjYsMjQzLDg2LDEwMyw3NywzOSwxNjYsMTk2LDYsMTU1LDQ5LDE5OCw0NiwyNTIsMTMyLDI5LDkzLDIxMywxMDUsNjQsNTcsMTk5LDIyOSwzMiw5OSwyNywyMDksNzcsNTIsMTkzLDEzOCwxMzgsMTQ3LDY5LDkyLDE1MywxMTEsMjM5LDE1NSwxNjUsMjM4LDEwLDE2LDk2LDIxMywxMjUsMTQ4LDIzNSwxMTUsNCwxMTcsMTYwLDE2NSwxMzgsMTk4LDk2LDU3LDkzLDIzMywxMzksOTIsNTcsNDMsMTYwLDE2NiwxOSwxNzMsMiw5OSw1LDEwLDEwMSwxMDksOTMsMTcxLDI1LDIyLDY4LDEwNSwyMjMsMjMzLDc3LDE0Niw5NywzMCwxNTMsMTEzLDE3MywyNCwxOTMsMTEwLDI0OCwyNDcsMTk4LDIyMyw0MSwyMDQsMjQyLDI1MiwyMiwxOTUsMjA5LDU3LDE1LDM2LDc5LDM1LDc2LDE1Nyw4OCwxMzEsMTk0LDI1MywxNjksMTQsNywxMCwxMTYsMjM2LDExOCw2NCwxOTIsMjI0LDc4LDEyLDExNiw1MCwxNTAsNjUsMiwxNjgsMjMxLDE0MSw1Miw5Niw4NywyMDcsMTM4LDE5MSw3MCwxOSwxMCwyMTQsMTUzLDE5OCw5NCwyNSwxNzUsMTQsMTM1LDE0NCwxMyw1MiwxMTIsMjM0LDEzMSwxNzMsMTUwLDIxMywxMjEsMTY5LDMzLDQyLDE1MCw2Niw4Nyw4MywxMzksMTY2LDE2NiwzNSwxMzksOTAsMjI4LDExOCwxODQsMTc2LDQ2LDYyLDI0MSwyMDEsNTYsMTUwLDE5NSw1Myw1NiwxMjYsOTYsMTA1LDE4LDI1MCwyNTEsOTEsMTQ2LDIzMSwxODAsNDMsMTg1LDEzNCwyNCw1NSwxMzMsMTE2LDE1MCw2LDIxNywxMDQsOTMsMTk4LDEzNSw2MCwxNywxMjMsMjYsMTgzLDU1LDQyLDIyOSwxOTQsMzksNDUsMTM0LDkyLDI1NSw1MCwxOTMsMjE5LDIwMCwyMiwyMzEsODgsNDYsMjksMTgwLDc0LDkzLDIyNSwxNzgsMjE3LDEzOSw0MCwyMzYsMjM0LDEzLDE2NSwxNTEsMTU2LDY2LDExNCwyNSw2MCwzOSw0NywxMDQsMTY3LDg5LDEsMjI0LDE1MCwxNCwyNywxNjYsNDYsMTgwLDE0OCwyMDcsMTYsNzcsMTIzLDIzMiwyOSwyNTAsMjE2LDMwLDQyLDczLDE2OSwzNiw5Niw3NSw3NSw4NSwxNTQsOTQsMjQsMTQsNiwxNzEsMTQ0LDE4OSwyMzIsMTQ0LDIyMywxNTQsOTIsOTYsMjQ4LDIyLDE1LDE2NiwxMDMsMTk2LDk4LDQ5LDIzOSw2NCwxNzEsMTc4LDIxMCw3OSwyMzUsMjA0LDEwMCwxNyw4OSwxNDgsMTcsMTY2LDEwMCw4NSwxLDE0NiwxNjIsMjQsMTIwLDE2LDIzMCwyMDMsMzcsMTY0LDI2LDY5LDExNCw1MSwxOTgsMTI5LDY5LDE0Nyw4NywxMjMsMTk3LDE1NCw3MSwyMjgsMjE5LDMwLDY0LDMsMTAwLDIxNSwxOTgsMTU3LDE1OSwxLDI1LDY4LDQyLDQ2LDUyLDU4LDQ5LDE3NywyMTAsMjA1LDE0OCw4Nyw2Niw3MywxMDcsODUsMTIxLDE3MCwxOTcsNDIsMzcsMzUsMTU3LDE0NCw5OSwyMDQsODksOTEsMTgzLDE4MywxMTUsMTQ0LDEwOCwxNywyMCwyMTUsNzcsMjM3LDE0NywxNDUsMjUxLDk4LDE1LDksMTIsMTg3LDEwLDE5MiwxNTcsMTE0LDIzLDYwLDEsMjQwLDk1LDIwNSwxODgsNTQsMjQwLDMzLDE2MCwxMjUsMjI0LDE5NiwxMzksMTYwLDg0LDIxNywxNjYsMTA1LDExOSwxMTYsOTYsMjA4LDE2NywxODYsMTY4LDQzLDE5MiwyMTMsMjMwLDE0LDI5LDEwLDUxLDYwLDE0NSwxODgsNDYsMTIxLDE5OCwzMCw1OCwxNjMsNCwyNDcsMTEwLDcyLDE4Miw4Niw4Niw1OCwxNTksMTk3LDUxLDE1NywxMDUsMzQsMTM0LDE4MCwyOCwxNzMsMTk0LDEzMiwxMDYsMjQ3LDEzMSwxOTcsNjgsODMsMTAwLDI0NCwyNDQsNTAsMzgsMjEwLDI5LDY4LDI1MiwzNiw3MCwyNDksNjUsNTEsMjEyLDgyLDE4NSw5MCwxNTIsMTM0LDM5LDEzMSw0OSwxODgsNiwxMDUsOTgsMTgzLDcyLDEwNSw5MiwyMzYsNjQsMTM4LDQwLDE5LDI5LDE1LDYwLDk4LDgxLDExNyw2NSw4NywxNTYsMTAsMTk4LDE4NiwxMzYsMTc5LDI0NCwxNzYsNDUsMjYsNTUsMjMyLDE5NCwxODcsOTUsODIsMTQ1LDUsMTAwLDcxLDIwNiwxMzAsMTk4LDEwNCwyMzQsMTg5LDIsMjI1LDIxLDEyNywzNiwxNCwxMiwxNjcsNSw2NSw1MCwxNTIsNDgsMTQwLDU4LDM2LDIxOCwxMjQsMTk4LDYsMjA2LDE2OSwxMDIsMTA2LDE3Niw1NiwxOTIsMTc3LDE0NSwxNzMsNjMsMzgsMTE1LDE3LDI0OSwxNCwxOTcsMjExLDE4Niw5MiwxMCwxNjgsNTUsNjUsMTA5LDIxNiwyMDEsNTQsMiwxMjIsMTEsMzMsNCwxNzcsNTEsOTAsOTgsNzUsMTEyLDEyNCw1Miw0Miw0MSwxMjcsMzYsMTUyLDczLDgsMTIwLDc5LDE4Myw2OSwxNzgsMjIwLDE1MiwyMTEsNzYsMTY1LDQzLDkxLDYyLDEwNCwxMzIsNzEsNDAsMzgsMTQ5LDI0MywxMjAsMjA3LDIyMiwxMzUsMTc4LDE5NSwxMzIsMTQ0LDQ1LDE2NCwyMywxNDAsODksNDcsMTYwLDEwLDEwOSwxNDAsMjA5LDEwNiwxMzgsMTQ0LDE2OSwxNDMsMTIsMywxOTEsNzQsMTU5LDE5NiwyMjUsMTAxLDIxNywyMTAsMTU5LDE1MywyNDAsMjYsMjUxLDI1MSw1LDEyLDExOSwxMjMsMTA2LDE3MCwwLDE3NSw3MCw5NiwxNDksMjA4LDgsMjIsNjYsMTkxLDIwNiwxMzYsMTAwLDEsODYsNDUsMTU0LDM3LDE4OSwxMTIsNjcsMTU0LDMxLDI1LDk1LDQwLDE2OSwxNjEsMTExLDE2NCwzNSwyMjgsMTM3LDU3LDEyNCwyMzQsODgsOTIsNzQsNTAsMTQ4LDYwLDE2Nyw1MCwyMDgsNTcsODMsMjMxLDEwNiwyMDAsMTEyLDMxLDc5LDExOSw4OCwxNDYsMTIyLDI2LDEwMywyMSw0MCwxNTAsMTM4LDE5MiwxNTcsMzUsMTAyLDEyMiwxMjksMTQ2LDEyNCwyMjcsODAsNjQsMTY1LDU5LDEyOSwxNTUsMywxODksOTAsMjgsMTk5LDE1Miw5MywzLDExMiwxMzksMTksODksMTYwLDEwLDE2MCwxMzYsOCwyMjcsMTg3LDE3NCw1NywxNTIsMjEsODQsOTcsNzUsMTgxLDExLDcxLDIwOSw4MiwxMjQsMzIsNDYsMjgsMjQ2LDQ0LDEyMSw4MCw2MSwxMTcsMTA1LDkwLDIzMSwxMzIsMzUsMTkzLDcxLDE0NiwxNTgsMjcsMTc5LDIzOCw4MywxMDgsNjksODksMTg4LDIyOSwyMjQsMTEwLDIzNywxMTUsOTQsMTU5LDIwMCw1MywxMjgsMTkyLDEyNSwxMzMsMjUyLDE5OCwyNDgsMyw0LDAsMTk5LDEyMCwxNjUsMTgzLDE3NywxMzgsOCwxNjgsOTcsMjI1LDksMTk5LDQwLDEyMSw2MCwyMDQsMTQsMjQ4LDIxMCwyNSwyMzUsODEsMTQsMjE3LDEyOCwwLDE0MSwxOTQsMTI0LDE5OCwyNDksMTgwLDYsNjUsMTE4LDEzMywxNTAsNjYsNzgsOTAsMTU1LDMwLDcxLDE2MiwxNjUsODcsNjgsMTQyLDk4LDE5OCwxNTgsODQsNDEsMTUwLDM1LDEzOSwxMCw5LDE0NCw4LDEzMywxMDIsNzAsNzEsNSwyMDAsNjksMTIzLDE5MSwxNjEsMzgsOTEsMjEyLDMyLDI1MiwyNDcsMTYyLDIxNywxMDUsMTgwLDkwLDE0MCwxNDYsMSwyMTMsMTYwLDQ3LDEzNSwxNTgsMTAyLDExOCwyMywyNTIsMTcwLDc0LDcxLDc1LDUsMjM0LDUsNjUsMjI4LDEwNywzNiw4NywxMCwxMTIsOTcsMzksMTE1LDIxNiwzMiw1OSwxODIsMTM5LDU5LDE3NCwyLDIzLDMwLDEwNyw3NCw1NCwxNjgsMTUwLDkyLDIxMiwxOTEsMTI1LDEwMywxNTUsMTQzLDE0NiwzOCwxODQsOTcsMzIsMTE0LDE5NSw3MCw0OSw5NiwxODEsMTAyLDIyOSwxOCwxNjcsMTc5LDEzNSwxNjMsMjUsOTMsMjcsMTMzLDkwLDE5Myw1MywxNDMsMTEyLDM5LDEyOCwxOTksMjgsMTczLDIwOCw0OCw1NCwxMjgsMTYyLDQ4LDQ5LDM5LDEyNCwyMzQsMjQ2LDE4NSwxMzIsNjQsNjgsOTgsMTgwLDExNSw5NiwxLDE4OCwzNCw4Myw5OSwxNDgsMTQ3LDE0OSwxODAsMTcsMTMwLDE3NSw5LDQ1LDIxLDY4LDE0MSwxMDYsMTk1LDY2LDIwMiwxNjEsNTMsNzksMjA5LDI3LDExMCw2OSw1Myw4NywxNzcsMTc1LDI5LDI0NiwyMDUsMTEyLDIzNSwxMTMsMjM1LDEwMSw0MiwyMTMsMTE5LDQsOSwyMzYsNDIsNjAsMTQ2LDEyOCwxNTcsMTg4LDU2LDIxMywzNiwzMCwyMzEsMzksMTk1LDIyNCw0OCwxNzMsMjExLDQ0LDExNSwyMjUsMTg2LDkzLDM0LDc0LDE5MCwxMzMsOTIsMTYxLDExOCwyNiwxOTcsMTQwLDM3LDU2LDQ5LDM3LDI2LDEwOCwyNDMsMTE0LDI1MSwyMjcsMTIsMyw1Nyw1MSwyMjAsMTQ1LDIyLDIzMiw0MCwyOCwzOSwxMzUsMTY5LDE2NiwxNzYsMjQsMTU1LDE4LDEyNiwyMSwxOTgsMTgzLDI1LDc2LDc0LDE0MywzOCw2NSwyMjUsMTYwLDExLDE3MSwxNTQsOTYsMzgsMTIyLDI5LDcsMTY4LDE3Nyw2MCw1MSwxMTksMjI5LDEwNiw4OSwyMiwxNiwxMjEsMTYxLDEzMCwxMDMsMTAyLDI4LDI4LDIyNSwxMDAsODYsMTUsNTAsNzUsMTk0LDQwLDE0MSwxMzksMTE1LDg2LDQ3LDMsNTcsMTcyLDEzNSwwLDIsMjM1LDIwMSwxNDcsMTcyLDE2NSwxNTIsMTY2LDE0MiwxNCwyNTIsNDcsMjAxLDQyLDE0NywxMzEsMjA0LDUwLDQxLDE5LDE3OSw2LDU2LDEyMCwxMiwyMzAsMTYzLDk5LDEyOCwxNiwxMDEsMTg4LDEzNSwyNTQsMjMyLDEzNywxMTMsNDMsMTkyLDIyNSwxMTIsMjAwLDE2MCwyMCwyLDI1NCwxODMsMjEsMjgsNTUsMTM2LDYwLDIzNSwxNTcsNzQsMzYsMTM5LDIyNiw4NiwxMzAsMjU1LDIyNSwxNjcsMTg4LDQ5LDkxLDE3NCwxNDAsMTI5LDMsODQsMTAwLDExNSw0Myw3NSw0LDEyNCwxMzUsNDUsMTYzLDM4LDQ0LDE1NiwxNDAsMTE3LDE4MCwxNzEsMTgwLDI4LDE5Miw1MCwxMjgsMTI3LDc3LDE5LDE3OCwxMzAsMTM0LDEwNywxODksMTcwLDc0LDIzMywxMDUsMTIzLDI0NCwxMjAsMjA3LDEzMiwxMjMsMTc1LDE3OCwxOCwxODQsMjI0LDM5LDIxLDE4MSwxMDIsMTk1LDIxOSwxNjIsMTEsMzYsMzUsMTE2LDY2LDIxLDE2NSwxNDEsMTAsNDYsMTIzLDE3LDE5Miw3MiwzOCwxODMsMTg5LDE4MSwxNzEsMjUxLDE4Myw5Niw3MSwxMzQsNDYsMzAsMjQ4LDQ0LDYzLDEwOCwxMTUsODAsMjIwLDE0MCwxMjUsMzUsMTMyLDIyNSwyMiw1LDEwOCwyOCwxNzgsNjMsMTk4LDExMSwyMzYsODUsMTc5LDE0OSwxLDE4MywxNDMsMTYyLDEzOSwyMiw4MCw0NiwyMzYsMTM5LDYxLDEwOCwxMzMsMTMsMTc1LDI4LDE3OSwxNjMsMTgxLDczLDE5LDEwMSw0Nyw1NSwyNDUsNCwxMTIsMTAwLDQsMTY3LDIyNCwxMjUsMTUwLDE0OSwyMDEsNSwxMjksMTc5LDIzNCwyMDMsMTM3LDU0LDkxLDEwMSwxMTksMTcxLDk4LDc1LDk5LDY1LDg1LDEyOSwxOTQsOSw5LDEzMCw0LDU1LDEzNSwxNjMsMTUzLDUxLDEzNywyMSwzMiwyMTIsMzksODAsMTE3LDU0LDk1LDc3LDIwMywxMTksMTIxLDE1NCw3LDIxNCwxNDQsMTIyLDczLDEzMSwxNjYsMTI4LDE3NywxODgsMTgzLDEsMTc0LDE1MSwxODUsNjUsNjAsODYsODAsOTksNTksMTQsMjQsNjYsNDAsMjIxLDY4LDQ3LDY1LDY1LDE3NywxMzEsMTkyLDE0NCw0Nyw3LDM3LDMzLDc2LDE2NywyNTQsMTY3LDEyMSwyMjIsMTY2LDE1NCw2Myw5NywxNTksMjQwLDE2OSwxMzYsMjMzLDEyMiwxMTgsNTcsMTEyLDE5OSwxMzEsMTc3LDI4LDUxLDE0MCwxODMsMTExLDI1MiwxNTksOTQsNzYsNTMsMTMwLDQxLDE3MiwxMTQsMTE5LDEzMCwxOTUsMTI0LDU2LDEyMiwyMzAsMTAsMTA4LDExMywxMzUsMTY3LDE1NSw0Nyw3NywxMDcsNjksODQsNzAsMTA5LDI1LDE2LDExNywxNjcsNzIsMTM5LDEzLDEwMywxNTQsMjcsMjEwLDYyLDQzLDAsMTI2LDE4NCwyMDgsMTAwLDQxLDI0MCwxODUsMTYyLDIxMSw1NCw1NiwyMDcsMTE2LDQzLDExNywxOCwyMTAsNDIsMjUwLDIxMSwyNTAsMTEyLDEzNywxMTcsMjQ3LDE1MCwxMTMsNDMsMTczLDIyNCwxODEsNTQsMjUxLDUyLDEzMywxMjIsNzcsMzEsMTM1LDIwNywyMDUsMjM1LDE2NSw1NiwyMjYsMjI2LDY4LDI0OCwxNCw1MSw4OCw3LDIxMyw5NCwxNTEsMiwxNjcsMjAsMTYyLDU4LDczLDM4LDE4LDUzLDE0NCwyMDAsMjYsMTA2LDEyMiwyMDQsMjMwLDEzNSwxMDQsMjAyLDEzLDIwMywxMDIsMTg1LDIwLDI1MywyMTksMTI2LDEzNCwxODIsNjMsMTk2LDUsOCw1OCwyMSwyNDAsNTIsNDgsMjA0LDc0LDExNSwxOTIsMTcwLDI0Myw2MiwyNDQsMTg3LDUyLDMwLDIxMywxNTQsODAsMTg4LDI0MCw1NywyMjIsNiw3OCw5LDQsMTkyLDgwLDE0MCwxODIsMTU1LDIxMCwxMiwxMDEsMTczLDE1MCwxODMsMjQ2LDIwNCwxNjQsMCwxNiwxNCwxNDcsNTQsNCwxMjYsMTQ4LDE2MSwyMjQsMTM0LDU0LDEzMCwyMzMsMTA4LDIwNywxNTUsMTUzLDk3LDY3LDc5LDkyLDQwLDIwNiwyMTYsMjQzLDg3LDIwLDMxLDIyMSw2NSw4MSwyMDYsMTc5LDI1MSw0NywzNiwxMCwxMzEsMTc1LDUwXX19fV0sIm5ld19sZWFmX2tleXBhaXJfb3B0aW9uIjpudWxsLCJ1cGRhdGVfcGF0aF9sZWFmX25vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMzQsMjEzLDEyNiwxMDIsMTgwLDE5Nyw3LDExOSw1LDE0OCwyMzEsNTcsMTU2LDg4LDE1NCwyNywxMDEsMSwxMTYsMTMyLDU0LDU4LDgzLDEzMCwyNDcsMjQ3LDczLDE3OCw0LDE0NCwxMjYsMjE2LDEwNSwyMzIsMTQwLDEzLDIzMiwyMjYsMTgsMjMxLDE2MywyMDcsMTU4LDIzMCwxMDYsODEsNSw0MywxMTgsODIsMiw3MCw0MCw0LDIwNiwxODcsMTU4LDE5OSwxMTgsODAsNTYsMjI0LDM5LDIxOSwxMTgsMTUzLDM3LDI0NSwyNSw0NiwyMjksMTkzLDEzMyw1MiwyMDQsMTU2LDM1LDIwNCwyMzksMTI0LDE3MywyMjAsNjUsMTY0LDE5OCwxNjYsMTUsMjIsMTA0LDE5Miw2OSwyMzYsMTQ4LDEwNiwxNzksMTc1LDM5LDk2LDE4LDI3LDU1LDE4MSwzNywxNiw2MCwxOSwxNzgsMTEyLDczLDIxNywxODMsMTc5LDEwOCwxNDksMjMwLDE3MiwyMDUsOTgsMTIwLDE0MiwyNTMsMTcsMTcwLDY3LDExNywxOTYsMTgyLDE3MiwxMDEsMjM2LDE3MCwxMjEsMTg2LDU3LDc3LDMzLDY2LDMyLDczLDExNywxNzIsMTIyLDE1MCwxMzQsODAsMjIwLDkyLDIzOSwyMjksOSw3MywyMTksMTY4LDMzLDI1MSwzMyw5NywxNzEsMjA0LDY2LDE0MCw2MywzNSwxNTMsMTg5LDIyOSwxMDYsMTM5LDEwMSwxNTUsMTg1LDE2MCwyMTAsMzgsNTAsNDEsMTgzLDY5LDU5LDE2MCw0OSwxMTIsMTY4LDIzNCwxMzQsOSwyMDMsMzYsMTk5LDE2MiwxMDgsNTEsMTc0LDI0MSwxMjcsNzMsMTcyLDE4Nyw1MSwyNiwxNTIsMTczLDE0OSw4MCw4NSw1OCwxNjUsMTgyLDg3LDIwMCwyMDcsMTI5LDEyNiwxMzUsMTE5LDE2NSwzMCwyMzEsMTM1LDIyNCwxOTMsMTY4LDAsMTYxLDEzOCw2MCwyNDgsMTAsMTg2LDQ4LDMxLDkzLDE3OCw3LDE5NywxOTksNTQsMjQ0LDE1MCwxNTcsMCwxMjEsNjgsMjQwLDI0NywxMTAsMTQ5LDY5LDExMiwxNzcsMTAsMTk4LDI0OSwxMjMsMTUzLDEyOCwxOTIsMjMsMjYsNTYsNzQsMjI3LDIxNyw5NywxMzIsMTgzLDE1LDIyLDEyMCwxNzUsOTMsNDgsMTgsMTE4LDE3OCwxMywyMzIsMTkyLDEzMSwxNTgsMTc2LDExNywxMjAsMTY1LDUyLDIzOSwyMjYsMTg1LDM2LDExNSwxMjEsMTU5LDE2MCw5MiwxMjEsMTQ2LDEwNiwxNzEsMTk5LDkzLDIzMiwyMywzNSwyNiwzLDQwLDUyLDUsMjA2LDE4MiwyMzMsNzQsMTg0LDkwLDYsMjQ2LDE4Nyw1LDE5NSwxODAsMTcsOTUsMjQyLDEwOSwxNjQsMzcsMTQ2LDgzLDE5NywxLDMyLDE3MSwxMjYsODMsODksMTAzLDksNzYsOSwxNzQsNjksMTY1LDkzLDE4Myw5LDczLDE2MCwxMTEsMTI4LDEyLDE0OSwxOTgsMjQ4LDExNywwLDI4LDIsMTY3LDIzNSwyMyw3Myw5NywxMTYsMTEzLDE1MCwxNDUsMTY5LDcsMjA1LDExMywxNDcsNywxMzgsMTgwLDgsOTcsMTAwLDEsOTUsMTY0LDUwLDU2LDY4LDE3MiwyNDQsNiwxMzAsOCwxOSwxNzgsMTEwLDI0MSwxNTIsMTM2LDEzNCwxOTYsNTYsMjAxLDE5NiwyMDgsNTMsODksMTMyLDIyNiw1NCwyMjQsMjYsMjcsMjIsMTc4LDkyLDQ2LDQyLDY5LDEwMCw1NiwxMTgsODMsMzgsMTk4LDAsMzcsODYsMTE4LDIyNSw0LDQ3LDMzLDIzLDIwOCwxOTIsMTA0LDEwLDczLDc3LDIxNywxMTcsMTQsMTY1LDIxMywxNTMsMzgsODMsMTUwLDEzNywyNDMsMjAwLDIzNywxNywzNCwxMjksMTc4LDg3LDUzLDE0OSwxNTAsMTE3LDEzNCwxODYsNzQsMjI2LDE0MiwxMjAsMjExLDEwMyw3NywxNDksNzUsMzEsMjMwLDE1NSwxNjEsMjI4LDcsNTgsMTY3LDE0NiwxNzQsMTMzLDYsMTk4LDIxLDUwLDE0NCwyNTIsMTQzLDI0OCw5OCw0LDE2LDEzOSwyMDQsMTMsNDksMTEyLDgyLDE4Miw2LDkyLDE2OSw3MSw4MCw2NCw0NSwxNDEsMzksMTgxLDIwNCwxMzMsMTY2LDg1LDQxLDE0LDIwMywyMjksNTEsMjU0LDIyLDE1Miw2MCwxOTksODIsNDIsMTg2LDM3LDMzLDI2LDEsMjM2LDcyLDE1NSw5OSwxMjEsMTU5LDIyMCwyMSwxNjYsMTEyLDY3LDQ0LDQzLDE2LDM2LDE1NywxNDAsNDQsNTcsMTQ2LDUyLDEwLDE2NiwxNDMsMTQ1LDMyLDgwLDE1NCw5MiwwLDY0LDQ4LDE0MCwyMjgsMTkyLDE3MSwxNzMsMTcwLDE5Niw0NywxMDQsNzUsMTA0LDgwLDUwLDE1NywxMzYsMjksMTI3LDEzOCw0NSw1LDIxNCwyMiw3MSwxMTYsNjEsMjE3LDI4LDI3LDk0LDEyOSw2LDEyLDc2LDE0Niw5MCw1OSwxMjQsNDYsMTY2LDg2LDIwOCw4MywxOTUsMjQ4LDExNSw1OCwxNTgsMjE2LDEzOCwyMjcsMTE0LDcsMjQ0LDM5LDI2LDI3LDIzMiwzNSw5NiwyMyw4LDIwLDE1MCw4OCwxMDIsMTE4LDE3MywxMDMsMTUxLDEyOCwzMiwxMzIsNDYsMTM5LDY0LDE2OCwyNDAsMTQ0LDQ2LDQsMjEsNjcsMywyMjksMTU0LDY4LDExOSw3MCwxMDgsMjQ4LDE3OCwyMDUsMTg1LDEsMzQsNjcsNDIsMTQ5LDgxLDE1MywxMzUsMTM5LDk5LDIzNSwxODEsMSwxNjksMjYsMjIsMjA2LDUwLDE2LDU1LDE2LDksMSwwLDE5NSwyMzQsMTg1LDIwMywyNDAsMTk3LDEwMSwyNDMsMjQ0LDk3LDE3OCwyMDIsMTg3LDQwLDM5LDE0NCwxMjIsNyw2MSwxNjgsMSwxNjUsMTc2LDc0LDEyNCwxNjksMTM5LDE2MSwxNDcsMjE4LDE4MywxMTMsMjAsMjYsMTIwLDEwLDEwOSwxMjIsODEsMTAxLDEyMywxMTQsMTQyLDk2LDE1NCwxNzQsMjI4LDcxLDcxLDI0NiwyMzAsMTE4LDc1LDEzNCwyMDYsODksMjAxLDExMCwyMzcsMTc5LDQwLDE2MywyMDksMTg3LDk5LDAsNTQsMTAzLDE2MSw3MiwyMjAsMTIsMjA2LDEzNSwxMTQsMTE0LDE2MywzNiwxNTgsMTIxLDEwMSw1NCwxNTIsMzIsMTY3LDExOCwxMSwxOTQsODcsODcsMjAyLDUyLDIwOCwyMDAsMjU0LDI0NSwxMywyMDAsMjIsMTQ5LDIwNSwxMDUsMTIxLDgwLDE2NywzNSwxMDgsMTMyLDE1MSwyMDksMTcwLDYyLDE3MywxNDksMTgyLDc5LDIzLDE2LDEsNjksMTA5LDE2LDI1MiwxNjQsNjksMjQyLDE5MSwyOCw1LDEyNCwzMCwxNjIsMTYxLDcwLDMyLDE5OSwxMDYsNjksMjAzLDk3LDI1MCw4MywyMjQsMjA0LDE5MSwxOTEsMjM1LDk5LDEyOCw1NywxNTAsMTM3LDU3LDE0NiwxMDMsMTUzLDE0Myw3MSw1NSwxMDEsMjA0LDE1NSw3Nyw1LDIyNiwyNCw0OCwxMzQsNjUsMTMsMjA5LDE4MSw0MCw4MywxMDYsMjM0LDExOSw2OSw1MCwxNjMsMzgsNzIsMjAyLDg5LDE2OCw4LDIwMywyMjIsMjQ3LDExNywxMzIsMTIzLDExNSwxMTYsMjQ1LDE4OSwzNSwxNjksMTQyLDEyMyw0LDEyOCw0MSwyMjcsMTE2LDE0MCwxMDIsMTEsMjQyLDIyNiwxODIsNDMsNTIsMjAxLDEzNiwxNzgsMTg0LDE1Nyw0LDEzMSw5NSw1Myw3MiwyMTIsMTgxLDk0LDIzOCwyMTYsNjgsMjAzLDM2LDExNSwzOCwyMDgsMTk4LDIxOSwxNDgsMTYzLDcxLDEzMCwxMTEsMzcsMTMyLDE5NCw5Niw5MCwxMDgsMTczLDAsMTY0LDE2MCwyNDAsMTY4LDYsMTk2LDMzLDEyMiwxOTUsNywxMjUsMjExLDk3LDE2OCwyMzAsMTc3LDk5LDE3MiwxMzksOTksMTI5LDgxLDI1NCwxNTQsMTQ2LDQsMTYzLDExNyw0MCw3LDEyMSwxNTAsMTA3LDI0LDE3MywyMDksNjcsNjQsMTcyLDE4NSw2MCwyNiwxOTQsMTIyLDE4NSwxNzMsNDgsMjM2LDExMCwyNTMsMTc4LDE1LDI1MywyMzIsNTAsMTg5LDE1MSwxNDAsMjQyLDE3OCwxMjAsMjM3LDc2LDE3OSwyMTksMTM5LDY1LDE5OCw1OSw2LDE4NywyMzMsMTYyLDQ2LDE5Nyw5Myw2LDE4MCw5LDIxNiwyNTIsMTk1LDI0MCwyNCwxNjksMTU0LDE5NiwxNjMsMTIzLDIzMyw0OSwzNSwxMjMsMTAsMjExLDQsMTkzLDUzLDE5NSw0NSw1NywxNTUsNzIsOTEsNDEsMTQzLDg0LDI1LDIsMTI4LDEzNSwxODUsMTU2LDUwLDE5NSwyMzMsODYsMTYwLDExNiw4NSwxOCw3MiwxNjIsMzksMTk3LDI0MCwxNzUsNjUsMTU1LDEyOSw0MiwyMTIsOSw2MSwzLDUxLDc1LDE3Niw1NSwxMzgsMTM1LDIwNywyMywxMjgsMTMyLDE2NiwyMTIsMTU3LDkwLDExNiw5NywxMDQsMjQzLDEwLDE1MCwxNDAsMTYyLDE2Niw4NSwxMzQsMTU3LDMyLDEyMywyNTEsMTAzLDE4MywyMTQsMTcwLDE0LDI0OCw3MiwxODksNTIsMTY0LDE4NSw2OCwxNDQsMTAyLDcsMzcsMTA2LDUyLDIyMCwxNTUsMjEwLDgyLDIwNyw3NywyNDUsMTk3LDI1LDUyLDI3LDIwOSwyNTEsMjEsMTksMTk1LDYyLDg5LDEzOCwzMCw4Myw0LDE0NCw3OCwyNDMsODQsMTI5LDIzMiwxLDIxNywxNDgsMTAxLDE2OCwxMDIsODcsOTksNjYsMTcyLDgsMiwxNTUsMjE1LDEwOCw1MywxMjksODMsMzQsMTk3LDEyOCwzMCwyMjAsOTYsMTIzLDgzLDM3LDM3LDE0OCw2NiwxNDksMTkxLDUzLDU2LDE2MCwxMTIsMTc3LDM4LDIxOSwyMjgsMTUsMTQ1LDE0NCwxODcsNjEsNDQsMTIxLDI3LDIyNyw0MiwzMSw1LDE5OCwxNjYsMTYzLDIxNiwxODUsMTU1LDE5NSwxNjgsMTA4LDE2NCwyMzAsMjQsMzYsNTcsNjQsMTE1LDEzMiwxMDUsMjQwLDIxLDgyLDIzLDMsODMsMjEzLDIwNywyMzIsNjBdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMTY0LDE4MCwxMjUsMTI2LDc5LDIyMCwyMjEsMjUzLDEyMyw0MiwyMDEsMTIwLDE4MCwxNjQsMjAwLDIwOCwxNzcsOTksODIsNjAsNTUsMTU2LDEyNiw3NywxMTcsMTE1LDIwMywyMDMsMTQyLDI1NSwxNjYsMzRdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5NywxMDgsMTA1LDk5LDEwMV19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiQ29tbWl0Ijp7InZlYyI6WzY3LDI0MSwxNjYsMjIyLDEwLDE1MywyMzksMTU0LDE0MSw0NywzMSwxNDIsMjIzLDE3OCwyNDMsNTcsMTU3LDEyMiwxNTYsMTAyLDE5LDExNiwyMDEsMjE3LDI0NywyMjksMTE4LDE2NiwxNDMsMzYsMTI4LDIzNF19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMTgsMTA1LDE0MSwxODIsMjAwLDIyNCwxNzIsMTEwLDEyNiwxOSwxMDEsMjYsMjQ3LDQ1LDE5NSwxNzMsMjEzLDE2Miw4NCwyMjksMTA5LDgsMTczLDI1MSwxNDUsMjE4LDI0MiwzMiwwLDIxNCwyLDEwLDI0NCwyMDYsMzMsMCw1MCwyNDcsODEsMjA5LDQyLDAsMTIzLDIwNiwyMTIsMzQsODIsNzQsNTksNzMsMTg3LDEzOSwyMSw2NSwxNzcsMTI4LDExNywxNzUsMTE3LDg0LDE0MywyMywxNTgsN119fX19fX19fQAAAAAAAABSAAAAAAAAOn9UcmVleyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJ0cmVlIjp7ImxlYWZfbm9kZXMiOlt7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTM0LDE2OSwxMDQsMjA3LDEzNCwxNDUsMjAsMjcsOTgsMjE0LDY3LDY0LDEwMCw5Miw5MSw3OSwxNjgsMTY4LDI4LDIwOCw3NCwyMzIsMTEsMTc3LDg3LDI1MSwyNSwxLDI1MCwxNzEsMjA5LDE2LDE2MywxNzUsMTQwLDQxLDkyLDE1MiwyMiw4MCw4MCwxMDcsNjgsMjUxLDMsMTU5LDIwOSwxMjUsNDgsMTY1LDEwOCwyMzYsMjAsMTEsNTQsMTAwLDE4OCwyMjgsMTQ0LDE3NiwxODYsMjIwLDE0NywxMDgsMTYsOTMsOTMsMTc2LDQzLDIxMiw2NSwzOCwxNDcsMTYwLDgsNzIsMjcsMTA0LDIyNCwyNDMsMTMzLDE0MiwxMDEsMjA2LDIwNCwxOTcsNjYsODgsMjQsMTQzLDEwNSwxMzcsMTAzLDAsMTk2LDQ5LDI0MSwyMDQsNTMsODcsMjIsMTI3LDE4OCwxMzIsMzgsMTEyLDE2OCw1MCw5Niw0NCw2NywyNDQsMTM5LDExMSwxNjUsNTQsNTMsMTg2LDIxOSw4NCwxMzEsMTEsODIsMTkwLDk2LDE4LDIzMCwxOTMsOTcsMjA5LDgwLDg1LDY0LDczLDU0LDkzLDE1NSwxODgsMTQsNiwzNSwxODIsMTMyLDE3MCwxMTYsMjMwLDQyLDMzLDIzNSw4NSwxMjYsMzksMTA1LDEzOSw5Miw1Nyw2LDE4Niw4OSwxNTcsODMsNDAsMjA3LDE3OSwxNjEsNjAsMTQwLDIwNiwxOTgsMTcxLDExOCwyMzIsMTM0LDg4LDY5LDE0MCw4OSwxMzUsMTE3LDYyLDE1NSwyNTAsOTUsMTUyLDIyNiwzMCwzMCwxMTQsMTkyLDk4LDIxNywxMTYsNzYsMTY4LDAsMjI1LDEsMTU3LDE5MCwxNzIsMTAzLDE0MSw5MiwxNTUsMTgsMTE3LDU4LDEzMCwxODEsMTk2LDI3LDU4LDc5LDk5LDg0LDgsMTg5LDE5NCwxMjgsNSwxMTQsMjEsMTUsOTIsMTg5LDk1LDEzMywxODIsMTQ5LDE4Niw1MiwyNDksMzksNzIsOCwyMSwxMDksMTQsNTMsMzcsMTc4LDE5NiwxMTQsMTc5LDE3MSw2OSwxODksNDEsMTU1LDE0LDgzLDE1NywxNzAsMjQ0LDM5LDE0OCwyNDQsMTcwLDgwLDE4NiwxNTUsMTAwLDI0MywxODksNTAsMTc3LDE0NSwyMjUsODQsMTgsMiw3LDE0OSwyMTgsODQsMTc2LDE5Miw1MCwxMTksNzYsMTAwLDAsNjcsMjgsMjA2LDIyOCwxNDksMTAxLDE3NywyNDcsMTk5LDk4LDE1NCw4LDI0NiwyMjAsNTAsMSwxMTgsMTY3LDYwLDcyLDE1MCwyMjksNTgsMTg5LDQ1LDIyMCwxMTksMjE4LDQwLDI4LDE4NiwxOCwxNzQsMCwyMDksMTQwLDE3MSwxNjMsMTQsOTQsOTIsMTM4LDIxNyw3NSwxNDAsMTcwLDIyNywyMDAsMTI3LDE1NSw0OCwxODEsMTIsMTMzLDIyNSwyNDksMTYwLDQxLDIwNCwyNiwxMDEsMjE5LDAsMTg0LDE1NCwyMDUsMjMzLDE5NiwxNDIsMjMyLDEyMywzMCwyMzcsMjEwLDkzLDY0LDgzLDg2LDE1MCwyMCwxODksMTA3LDU1LDI0LDE2LDg0LDIzLDExNSwxNDUsOCwxMTEsMjcsNCwxODQsNTYsNDQsMjQ2LDIwOCwxMDYsMTE1LDkxLDE1OSwyNDQsMTIzLDE5NiwyNywyMTYsOTcsMTUwLDIwOCwyLDI1Miw2MCwxNzgsMTkwLDM5LDYsMjIsMTQ3LDE2MCwzMywyNDUsMjEsMTYzLDIxNiw1LDE5LDEzNiwxNTYsMTA2LDE1NiwxMjcsMjMxLDEwMiwyMDcsMzUsOTYsMTI5LDExNCwyMDgsODYsMTgwLDExNiwxMTksMTI2LDc0LDEyLDY3LDYsMTMzLDQxLDExOCw3NiwwLDI2LDIwNiw2LDUsMTAzLDE1MSwyMDQsMTM2LDE2NCwzOCw2NiwxNCw5MiwxMjUsMTYzLDUzLDE1MSwxMzAsMjM1LDE4NCwxNTgsMTQ5LDE2MSwyMzgsMTk5LDcxLDI1MSwxODcsMzUsMTg4LDI0LDMxLDE2MywyMzUsMTYzLDYwLDYwLDEzMCw3OSwxNTQsMjUsMjE3LDY0LDE5NSwxNzQsMTM5LDg1LDE5NSwyMDgsMTM5LDczLDYwLDE5NiwxMjksNjQsMjAzLDE2NiwxMjgsMTM5LDIwNCwxOCwxMjcsMTM4LDIyOSw3NywxNjgsMTYxLDE5MywyMDcsMTA1LDAsMTgwLDEwNywxNzIsNjUsMTE3LDYzLDc0LDM2LDE2NSwxNCw1MSwxMjQsMTYsMTA3LDIzLDgyLDE0NSwxMDYsMjQ3LDE0NCw0NCwxOTMsMTIwLDMsMTA0LDE2OCw0MSwxODgsMzQsMjA2LDI1MSwxNzAsMjA1LDIwMywxMTIsMTcxLDE2MSwxMzMsNzYsMTQ4LDEzOCwzNCw4MCw0MSwxNjgsMTI4LDIzLDIwMyw4MSwxNDcsNTAsNTEsMTQ1LDE5OCwxNTIsMjM0LDk2LDcyLDE4NSw1MCwyMzEsMjcsMTQ1LDIwNywyMTEsNDIsNDAsODYsMzEsMjUwLDg2LDg4LDIzNiwxOTcsMTY5LDE3NiwxNjUsMTMwLDIzNCwyNDQsMTg0LDE1NSwxODYsMiwxOTksNTUsNjksNTcsMjAwLDEyOCwxNSwxOSw3OCwyMzEsMjEzLDE3MiwyMCwyMTQsMzQsMTg5LDIxMiwxODMsNCwxMSwxNDIsMTg4LDE2MCw0OSwxNTcsMjcsMjEsMTU0LDE2NCwxNzksMjIwLDE2NiwxNzQsMjA2LDEzMSwxMSw2NCw4MCwyOCwzMSwxMjEsMTEwLDE4MSwxMjIsMTMzLDIyNSwxNDcsMTc5LDk4LDg5LDE3OSw2NiwxNjgsNDIsMjQwLDg3LDIwNSwxODcsMTk1LDExMCwxOTksMjIwLDEzLDE4Myw0NCwxOSwxLDE1MSw1OSwxODAsMTYxLDE5MSwxMDMsMTIzLDEzLDI1NSwxNjgsNzAsODQsMjYsMjA0LDEzMCw4NiwzOCwxMzYsMjMsMTM0LDE1OCwxMDEsMTI4LDI1MywyMDIsMTM5LDE2MiwyMTQsMzAsMTkxLDExMiwxMjAsMjAyLDE1MCwxNzcsMywxNjUsODMsMjQwLDE2NCwxNjYsMjQ2LDE1MiwxNDcsMTk5LDg0LDE1LDIwMiwyMzQsMzAsNzUsMzgsNzMsOTgsNjcsMTY0LDEyMCw1NCw3LDEyMiw1Myw3MSwzNywyNSwxOTcsNzUsMTg3LDEyLDE4OSwxOTcsNDEsMTkwLDMyLDEyMywyMTQsMTM5LDIwNSwxNzIsMjQ3LDEzMywxNTAsNywxOTUsMTI2LDgwLDEyMCwyMDEsOTgsMTQyLDIxNSw4NSwyMDAsMjM0LDc1LDUwLDIxNCw0OSwxMDYsMjE2LDU4LDE5LDY4LDIxMyw3MywzNywxNDgsMTM0LDIwMSwyMzUsMTAyLDE5MSw3NSwxODAsMjE0LDU1LDE4NywxOTksODYsMjksMCw3LDIwMCwxMzMsMTM1LDQyLDE4OSwxODAsMTc4LDE0MiwzNywxMjAsMTI4LDM2LDcwLDIzNCwxMzIsNTksNDYsMjE3LDE0Miw4Myw4Niw2NiwxOTMsMTY4LDE0MiwxOTgsMTAzLDY1LDEzOSwyNDMsMTUxLDI5LDEzOSw2Myw2MywxODgsMTIyLDQyLDM1LDY5LDE1MCw5OSwxMTEsMTQ2LDE4NCwyNSwzOSw5MCwzMywxMjYsMjQ5LDE4OSwxMjcsMTc2LDYxLDExNSwxMDUsOTgsOTEsNTMsMTM1LDExNCwxMjIsMTYwLDgyLDczLDE2NSw1Nyw2OCwxMTAsMjA4LDEyNCwxMjUsMiwxNjksMTE4LDExOSw4NCwzNywxMjgsODYsMTUwLDUxLDEwNSw4NywyNTUsMzMsMjAzLDIwMCwyMzEsMTAzLDEyOSw1Miw2NCwxODgsODIsNTgsNjYsMTYzLDU3LDIxMSwxMjAsMTEwLDE4NiwxOCw4NSwxMzEsMjE4LDE5LDE3LDgsNzgsMjA1LDE3Nyw1NiwyMjMsMTIwLDkyLDQzLDE5Niw3LDEyNywxNTYsNDIsODMsMTA3LDEwLDIyMywxMzAsMTAsMjQwLDIzMywxODksMTUsMTk4LDQwLDI0MCwxMTgsNSw0NSw2NSwyMSw5NSwxODcsMTc5LDEwNiwxMzIsMTEzLDE5MiwxMCwzMCwxMzMsMTY0LDIxLDkzLDkyLDE1OCwxNzAsMTM5LDEyNiwyNDgsMjQ4LDEzMSwyMjcsMTgyLDk0LDE1OSw0LDM3LDU0LDE5OSwxODcsMTQ1LDExNywxMzMsMTcsNTcsNTUsMjEyLDM1LDExMiwxODUsMTMxLDksMjA5LDY0LDc0LDE0NiwyMTcsNTIsMjIsMjI0LDQ5LDE5NSw4NywxNzQsNjAsMjEsODgsNzYsMjA4LDg3LDE5NSwyMjYsNzMsMjAxLDIyOSwxMjgsNjgsMjM2LDg0LDIyNywyNDIsMTY0LDc5LDE5NiwxMzQsMTE4LDY3LDEyOCwyLDEwLDE3LDIyNiwyMCwxMjYsMTM3LDMsMTg2LDEwMSwxNDQsMjAwLDE4OSwxNTMsMTE0LDE2MSwxOTksNSw5MiwxNDgsMTEyLDU0LDIsMTQ0LDYwLDIxOCwwLDIzLDIzMiw2MCwyNDMsMjEyLDE3NSwwLDM3LDI1LDQ3LDIxMiwxMTAsMjE2LDQ4LDQsMTQ3LDIyNCw1NCwxNCw3MCwyMDAsMjMzLDIxMiw1NiwxNCwyMTQsMTM1LDc5LDgxLDE1MCwxMTYsMTMzLDMsNzUsMjMyLDE4OCwyNTMsMjIwLDEzOSwyMDIsMTQ0LDU2LDgyLDUyLDMsMjUsMjI4LDk3LDk4LDI1MiwyMDEsNzYsMTQwLDE5LDE0NCw5OCwxMDIsMjAyLDEzNiwxODcsNTAsMTkyLDEwMyw0NCw4Niw5NywxMjcsMTIwLDEyLDg4LDIzNiwxNjMsODAsMjE0LDE4NCwyMjAsMjE1LDQwLDEzOSwxMTcsNzEsMjE5LDE4MSwxMTcsNTEsMjEyLDEyNCwxMDIsMTEzLDEwMSwzMSwxNDYsNDYsMTkxLDIwMSw5OSw2LDIyOCwxMjIsMjI4LDcxLDIwNyw5MCw5Miw1NCw3MCwyNTAsMTYxLDIyMSwxMzksMTExLDQ2LDg1LDE4MCwxNjUsNyw1NiwxMjUsMjEsMzQsNjcsMTE5LDExMiwxNjUsMTEsMTk3LDEwMCw2NSw2NywyMjEsNTksMjM0LDE4OSwxOTUsMjAzLDE2MiwxNTksMjAzLDE5OSwxNTgsMTgyLDE2NSwxLDE5MiwxNTUsMTE1LDI5LDE3OSwyMzMsMzUsMjE1LDk1LDE5LDIyNCwyLDE5LDgzLDIxNiwxMiw4MSwxOTgsMTgzLDE4MCwwLDU4LDI0OCwzOSwzOSwyNTMsODQsNTAsOTQsMjU0LDIyNSwyMTIsMTUsMTAsODgsMjUzLDIxMCw5LDI0NiwzNywyMjQsMTY1LDE4NSw5LDYsMzMsMF19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjQsMTgwLDEyNSwxMjYsNzksMjIwLDIyMSwyNTMsMTIzLDQyLDIwMSwxMjAsMTgwLDE2NCwyMDAsMjA4LDE3Nyw5OSw4Miw2MCw1NSwxNTYsMTI2LDc3LDExNywxMTUsMjAzLDIwMywxNDIsMjU1LDE2NiwzNF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbMTY1LDI0MCwyMjIsMTQ1LDE5MSwxOTksMjAxLDIwOCw3NiwyMDMsMTYyLDE3OSwxNDIsMjM1LDExNywyMjcsMTM0LDU2LDExNSwzOSw4Miw1MywxNTQsNDksMTc3LDIxOSw4OSwyMDksMTg0LDMsMTM5LDIwOV19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMzIsMzAsMzksODMsNjksMTA3LDE4OSwzNiwxMDEsMTA0LDE3MCwyMjAsMTM0LDI1MSw0MSw0NSwxNjQsOTYsMzksMjQyLDU3LDU5LDE2OCw4Myw4NCwxNjMsMTM5LDE4OSwyMzEsMTI4LDEyNywxOTUsMTkwLDE1NCwxMjIsNTQsMTI2LDE1NywxMzAsNTksNzAsMTQ0LDEwMSwzMiwxNzAsMjUsMjA4LDIwNCwyNDcsMTM0LDE1NSwyMDksOTksMTE1LDU0LDEzNywyMywxMjksMjE5LDE0MywxNTIsMTU2LDEwMCwxM119fX19LHsibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls3MCwxODAsMTM4LDcxLDI0MiwxMDgsNjMsMjQwLDE4OSwxOTksNDEsMTEwLDg5LDg5LDE5NSwxNDUsODksNzQsMzMsMjEzLDE0LDE2Nyw5MSwxMDMsMTYzLDY2LDcsMjEwLDIzNiw5LDUwLDI0MCwxMDAsMTUzLDEwMSwyOCw4NSw5LDE2LDE2MywxNzcsMTIwLDY0LDI0NywxMSw5LDEzMyw5NSwxNTgsODgsNjIsMTQ1LDI1MiwxMDksMTM0LDIzMCw5MiwyMzIsMjA0LDEwMSwyMTcsNjAsMTY5LDEyNiw5MiwyMDMsMjM4LDg2LDIxLDIzMyw4NCw0NSwxNTUsMTI5LDQyLDEzNywxNTAsNTgsMTA4LDQxLDE2MSwxMTMsMTQ4LDE3NSw4NiwxNTYsMTYsOTgsNzUsMTM4LDEyMiw3LDMzLDEzNSw5MSw3NywyNDIsMjMsMTkxLDI0Myw0OCw5LDI1LDEwMCw0NCwyNDgsNzMsMTI0LDcxLDc0LDE0NywyMjAsNDksNDMsMjI4LDUxLDE4NiwxMjAsOTYsMTg5LDE3NSwzOCwxMTUsMTA4LDExMiwxNjIsMTAzLDU4LDQ3LDk0LDI0Niw0OCwyNDYsNjYsMTA5LDAsNjcsNjcsMjI5LDE0Nyw2NCwxMzcsMywxNjMsMTE5LDE3OSw2MywxMDUsMjMxLDQ2LDE4MCw5OCwxMzEsODgsNDgsMTQ2LDEwNCwyMDgsMTM0LDYsMjAsNSw3LDIzMywxNDYsMjQ4LDEzNSwxMTUsMTY3LDgxLDczLDExOSwxMzcsODEsOTAsMTc3LDQzLDE2NywyMiw0OSwyMSwyNSwxMTYsMzMsMTMwLDYyLDExMCwyMTcsMTk1LDE1NiwxMCwxNzIsMTMwLDg2LDU4LDIxMyw1OCwxMjgsNTIsNjcsMzMsMTYsMjE5LDgzLDgsNCwxODAsMTE5LDE3NiwxOTcsMTA3LDMsNzksMjE2LDI0NCwxMDgsMjQyLDM5LDY0LDE2NywyNDksNDYsMjM5LDEzNiwxNjEsMTgwLDIyOCwxNjcsMTkzLDUsNDEsMTU2LDE0OSwxMzgsMjIwLDE0NywxNDcsMTA3LDEyOCw2NSw4MywxODIsMTMyLDI1MSwyMzUsMTYwLDE5NCwyMjgsNzksMTUyLDE0NSwxNzAsMTQ2LDEyMiwxNiwyMDYsMTc4LDYxLDg0LDIxOSwyMSw5LDI1MSwxODgsMjE3LDY3LDM1LDE0NywyMzIsMjcsODYsMTYsMTEwLDEzMywxNTMsNjMsMzAsMTkzLDIwMSw4OCwyMzUsNzIsMjU0LDE2NywyMSw5Myw4NSw1LDYyLDI0Nyw3NiwxNCw4NywxMDQsMTc2LDE2NCwxMywxNTQsMjE1LDEwNCwxMzYsMTMyLDE0Miw1NiwyMzEsOTYsMTU5LDE2OCwyNCwxNjcsMTAxLDE0MiwxMjcsOTIsMTMsMjM2LDY1LDU1LDEwMCwxNzYsMTQwLDE2OSw2Nyw2OCw2OCw3MiwxMTQsMTk3LDIxMSwxMTksOTIsMTM4LDE5NywyMTAsMTk5LDEzMiwxNjYsNTAsNTQsMTQ2LDExNSw0MCwzOCwxNyw2OCwyMzYsNDMsODUsMTgsODgsMTQ5LDI0Miw4Miw4MiwyMDAsOTcsMTgwLDk3LDUxLDgsMTE3LDE5OCwxMTcsMjAxLDE2MCw1NSwxMTYsMTkzLDE0MCwxNDgsMTcsNCw2Miw1MywxMTcsMjE4LDU0LDc3LDE2NywyMTQsMTEsMjAxLDQxLDEsMTUsMTg2LDE3MSwyMjcsNDQsMzYsMTEsMTk5LDIwNCwzNiwyNCwxNzksMjIwLDUzLDk5LDE4MCwxMzQsMTYsMTc4LDIyOCw0MiwxNzgsNjksMTUxLDIwNywxMjAsODEsMjAyLDExOCwxNjMsMTA1LDIwNCw1NCw1OSwxOTMsMTA0LDIwMCwxNjgsMTc2LDczLDI1MCw2NywyNDUsMTcyLDI5LDczLDEwMSwxMTAsMzksMTA0LDI1LDU5LDEzMyw5Miw3NSwxMDEsODUsMTM5LDExOCwxNzUsMzUsNywxMDcsMjE0LDEyLDE1NCwyNDYsNTYsMjAsMTA5LDUzLDQ4LDc5LDcyLDExMiwxNTYsOCw5MSwyNTIsMzIsNzEsMTc3LDI0MywxMzksMTg0LDE5NiwxMzUsNzIsNjcsMTIxLDQ2LDI0NCwxMzAsMTIxLDI0OCwxNTUsMjI1LDIsMTA4LDE2OCw1MCwxMDgsMSwxMDIsNTcsNTgsMTUwLDEyNiwxMjAsMTA1LDQ4LDgsMTk5LDEwLDE2MCwxODEsMTMyLDMsMTIzLDUzLDE2MiwxNywxOTMsMjAyLDE4OCwxNDksMjI1LDI2LDgzLDEzMiwxMTUsMTA3LDE4NiwxMTMsOTksMjksMjUyLDU0LDI0NywxNTMsMTY1LDE0OCwxNDcsMzksMzAsNDksNjYsOTcsMTYyLDEzMCw0LDEwNSwxNjMsMjAwLDczLDIwNiw3NCwxODMsNjIsODYsMTY2LDE4OCwxMDMsMjIwLDU4LDE4MCwxNjIsNTAsMTE5LDIwOSwxMTUsMTY5LDY0LDEzMCwxMCw5MCw0Nyw3NCw4LDE2MCwzNyw2NCwxMTgsMTkzLDE1MCwxMiw0MSwyNTEsMTEsNzUsMTY0LDEzMiwxOTcsMjM2LDE0NiwyMzYsMTM0LDEzMiwyNDAsMzksMTA2LDI0NCwyNDUsMjAsMTM0LDIyNSw4MSwyNSwxNDAsNzIsMjUyLDEzNywyOCw1MiwxMzEsMTY1LDE3MiwxMDgsMjAzLDI4LDIwOCw3NywyMjAsMTgzLDc2LDIwOCw3MywzOCwyMzYsMTgwLDAsMTk5LDExOSwzOCwzOSw0OSw2MCw0MywyMTcsMjgsMTYsMzYsMTgwLDIzNSwxMTksMTI5LDE3MCw3MSwxODksMTc3LDEzOSw3MSwxNTIsMTIwLDE3NiwxMjYsNTgsMTE0LDEzMiw3Myw1MCwxOTYsNywxNzQsNDUsODQsMTAzLDcwLDkxLDE4MiwxMzUsNjksOTUsMTg1LDI1MCwxMzUsMTY2LDE2MiwxNDcsMTk0LDE5NywxMTQsOTIsMjcsOTgsMjM0LDIzMiwxNDMsMTAwLDE0OCwxMzQsMjA4LDIyNywzMCw1MywyNDgsMTEwLDE3NSwyNDQsMTg2LDMyLDEyNCw3LDE2OCwyNDAsNTEsMjAzLDI0OSwyMiw2LDE5NCw3MCwxMTEsMjEsMTE4LDMsMTkyLDQyLDE3NywyNDAsMTYsMTE3LDE4MSwyNyw0MSwyMDIsMTUsMzksNjYsMTgxLDIwMSw2Niw4NywxNzksMTI5LDEwMCwxMTQsMTQ0LDc3LDEyNiw5MiwyMDIsMjIwLDY0LDExNywxNDIsMjgsMTMyLDIxNiwyNDksMTc3LDUxLDE1MCwxMDIsMjM5LDEyMywzMCwxOSw4MCwyMDQsMTYwLDIzLDEzNywxMTYsMTU2LDEyNSwxMjgsMTMxLDk5LDE4OCw2LDIwLDE0NCwxNTMsMjQsMjI3LDIxMyw4MCwyNDksNDEsNjcsNjUsMSw0MiwyNDQsMTQ2LDEyOSwxODMsODMsMSwxOTQsMTY0LDE0LDE3MSwxODQsMTI0LDEyOCwxMTcsNzMsMTE0LDQ5LDE3NywxOTMsODQsMTU1LDE1MiwwLDU4LDU5LDI0NSwxMjEsMTI3LDIxMywxMzAsMTA2LDE3OSw3LDIxOSwxNjksMjgsNzIsMTE3LDUsMTgsOTAsMTg0LDI0MCw4MiwxNzAsMjUwLDI0NSw2Myw0MCwxODIsNjgsODQsMTU1LDE3NCw1MSwxNzYsMTI4LDEwMCwxMSwxNTEsODUsMjUwLDE4NiwxMTYsMjcsMjUsMTU2LDE3MSw1MSwxNjQsMTk3LDIwMywxODcsMTgwLDQ2LDE0NywyMTUsNjQsNzAsMTE2LDE2LDIxNywyMjksOTgsNTEsMTUwLDE0NywyMTYsMTc3LDE0NiwxOTEsMTUzLDE0MywzNiwyMywzMSwyMTgsMTk5LDE2MiwyMCwyMTIsMTAsNzAsMTY0LDU0LDE2LDY1LDI0LDExOSwxNzYsNCwyMDgsMTUxLDE4OCw1MSw3MSwxODksMTA5LDE2NiwxNTYsMjExLDE1NiwxODcsMTk4LDEzOCw1MywxMzksMTU0LDQzLDg3LDE1MCw0OSwxNzksMjQwLDUxLDExMCwxMTgsMTMyLDIxMCw5MCwxMzAsMTU0LDY0LDEzMCwyMTMsMjI3LDE4Niw1MSwxMDcsMTkxLDkxLDE1NCw0NywxMzQsMjA4LDIwNSwyMTMsMjEyLDIwNiw1LDEyOCwxODcsMTA3LDEzMywxMDEsMTkxLDExNSwxOCwxMzgsMTA1LDI3LDk4LDIyNSwxMzMsMjE4LDgxLDM4LDExOSwyMTcsNDgsMTM2LDIxMCwyMDQsMTIwLDE2Nyw3Miw5NywxMzUsMTA2LDIwMywzNCwxNiw0NSwxNTQsMTkwLDkyLDUxLDQ2LDIwOSw5NiwxODAsMTY4LDEwMCw0MCwyNTAsMTkyLDE0NywyMTksMTA1LDE4Myw4Nyw0MywxNTksMTg1LDI0Nyw1MywyMzgsNDQsMTgwLDQ2LDE4LDI4LDIxNiwxNjUsMTAxLDE2MCwyMzEsNTMsNjMsMTEyLDEwMywyMTcsMTg2LDEwMiwxMjksMTgsMTQxLDc2LDExOSw3MywyMTIsODAsMTQ3LDE1Nyw5OCwxMjUsMTIyLDIwMyw1NywyNSwxOTcsMTUsMTg3LDIxOSwzLDEyNSw4MiwxMDMsNDAsMCw0NywyMjksMjUsMTE3LDI0NiwyMCwxOTIsMywyMDAsMSwxODAsMTUwLDIwNSwyMDEsNTQsMTA2LDE4MSwxMTMsNDMsODMsNDMsNTQsNzIsMTA1LDgwLDI1LDU5LDM3LDE3NSwyMTQsMTIzLDMzLDE1MywxMSwyMjEsMTksMTMxLDEzMiwyNDAsODAsOTUsMjUxLDQsMjU0LDI1MCw4MCwxMzMsMTg3LDE4MiwxMTIsMjQ1LDc1LDEyNCwxMDcsMTIxLDQsMjQwLDE5OCwyNDAsMTYyLDcyLDIyOCwyNTIsMTIwLDc3LDAsNzIsMTgxLDE5OCwyOSwxMDIsMzcsMTgxLDQ1LDE5Nyw5NywxNSwyMywxODQsMTY4LDEwMiw1MSw2NCwxMTUsMTEzLDIzNywyMjUsMTM4LDg5LDIsMTI4LDIzOSwxODEsMTc1LDEwMiwyMTEsMTYwLDU0LDE4MiwzNSwyMzMsNTAsOSwxMDQsMTAwLDEzMSwyNiw4NSw0OCwxNTcsNjcsNjksNzMsMjAzLDEyOCw0LDE3MiwxNTksMjQzLDc0LDE0MiwxOTYsMTY5LDE5LDc5LDE1MiwyLDYsMzAsMTMyLDE3MSwxMDMsMjIxLDcsMjExLDE2OSw3OCwyMzUsMjA2LDY5LDI4LDI1MiwxMyw2OSwxMjUsNjQsMjU0LDI0NywxNywxNjAsMjAsMjAyLDE0NiwwLDIzMiwwLDEyLDE3MSw5Niw3NiwxMzMsMTM0LDI0Myw1OSwyNDAsMTUwLDE0OSw5NSwyMDksMjE5LDE5OSw3MywxODgsMTA1LDEwMywxMDEsMTg1LDI0NSwxMyw5NSwyMjIsMjA0LDE2Miw0NSwxMzksMzIsMTA1LDVdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMjM5LDIzNywyNTEsMTUyLDIzMSwxNDAsMjUyLDMzLDIzNywxNzQsMjQyLDI0MSwxMjQsMTAzLDEzMCwyNCw3NiwxMDYsMTQ1LDIyNywwLDUxLDY4LDExMiwxNjAsNDYsNDIsNDksODIsMjMzLDc3LDExN119fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk4LDExMSw5OF19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTUwLCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNTB9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMjIsMTc5LDMsMzIsOTQsMTI3LDY2LDI0OSwxODgsMTA3LDc3LDE4NiwxMjYsMTg5LDEzNSw1Miw2OCw0MCwxNzIsMTEwLDEzNSwyNDQsMjA4LDk1LDE2NywyMjMsMTc3LDE4MiwxNjAsNDYsMSw0NSwxMDgsMTkxLDcyLDExOSwxOTYsMjIsMTUwLDE5NCw1MywyMiwyMDMsOTEsMjIzLDIwNCwxNTEsMTQ0LDEyNiwyMDMsMjQ5LDEyMSw2Miw0NSw0LDE0NCw4OSwyNywxODAsODQsMjM2LDY2LDU5LDFdfX19fV0sInBhcmVudF9ub2RlcyI6W3sibm9kZSI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTE2LDE2Niw2MiwxMjksMTc3LDE2MywxMjQsMjE0LDEwOCw4MCw4NiwxNDUsMjgsMTQ0LDY0LDE4NSw0LDg5LDYwLDExNiwzMywxOSwyMjUsMTU3LDcwLDEyMywxMzMsNTMsMTgwLDg5LDY3LDE0OSw0Myw2NiwxODMsMTQzLDQ3LDM0LDE4MCw4NywxNiwxMSwxMTAsMjUxLDYxLDIwMiwyMzMsMTUzLDgsMTAyLDkxLDE5OCw0OSw1OSwxNjAsMTk4LDg2LDU4LDI0MCwxNTgsMTU5LDgxLDIwNyw5NCwyMDksMTIwLDIyOCw4NCw1NSwyMzksMjIsMTk3LDEwMywyMzQsNzcsMTk4LDI0MSw0Nyw5LDgwLDE2MiwxODYsMTU0LDE4MiwyNiwxMzksODYsMjQxLDIwLDEwNCwxNjksMjE5LDEzNywyMDcsOCwxOCwxNTQsMTYsMjUsMTE5LDE0NiwxODYsMTUzLDg5LDIsOSwxMDQsNDIsMzYsMTYsMzcsMjEsMTk1LDk1LDM5LDE5NiwxNTQsMTg1LDc0LDEzMiwyNDYsODksMTQ1LDEyMCwyMjcsMjEsNjIsMTgyLDEwMiwyMzIsMjUyLDIsMTc5LDE2OSw0MiwxNDUsMTAzLDE5MSwyMDUsMjM0LDExNiwxOTAsMTA4LDE0Miw5OSw1OSw0NSwxMDEsMTIxLDksNjMsMTY3LDE5NCw4NCwxMDIsNDgsMjMsMjEwLDExOCwyMjcsMTUyLDEzNSwxNjMsNDMsNDIsODQsMjQ0LDM3LDIwNSwxMzUsMTUxLDExNSwxNTAsODgsMTgwLDIzMywxNDYsNjksMTksMjA3LDY2LDQyLDEyMywxNzIsMjQ4LDg4LDE4MywxNjksMzAsMzUsNTIsNzYsODUsMTUzLDE1OCw5LDExNSw5MCwyNTEsOTksMTUsMTY0LDE2MiwyMyw4NiwxNTAsMTcyLDg1LDEwMSwxMjksMjQxLDEzNCwxOTksMTI0LDI1LDAsMTkwLDE0NiwzNCw3MSwyNCw5NiwzOCwyMSw2MCwxOTEsMTQwLDE3MywyNTIsMTEzLDk1LDYwLDc1LDQ5LDU5LDI0NCwyMDMsMjQxLDEyOSw2OSwyMzUsMjIsMzIsMTUsMjM2LDE5MCwxMjIsMTAxLDY0LDExMSw3LDE3MSwyMjgsNTcsMTIxLDExNCwyMDMsMTMsMTAzLDksMzQsMTUsMzYsMjksMjIyLDIyNywxOTgsMTU0LDMzLDExMSwxMzcsMTY4LDExOCwxMDUsMjMxLDEwMSw2MSwzNSwxODAsNzgsODksNDMsMTQwLDkwLDI2LDUwLDIxLDE0MywyNDEsMjEyLDc3LDEzLDQ4LDE3MSwzMywxNjMsMjAzLDIwOCwxODEsMTk0LDEwOCw4MiwxMjUsMTE3LDgyLDY5LDEzLDE0OCw4Myw0NSw2NSw2NywxMTEsMTk3LDg2LDEwMywxNjYsMzAsMjUzLDI1Miw0OSwzNSw1NywxODMsNzUsOTEsMTQxLDE4Niw5MSwxNzAsMTQ3LDI1MSwxNjMsNDIsMTgxLDE3NCwyMjgsMjQsMjMsMTc3LDIxNSw3NiwyMDYsMjE5LDUyLDI1Miw4Niw5Miw2NCwxNjgsMjIsNzgsMTg3LDE0NCw3OCw4OCwxMDEsMjE4LDE0NiwxMDUsMTY2LDYsMTM0LDE1Niw2NiwxOTEsMTg1LDE5OSwxODYsMTIsNSw5NiwxMjUsNjQsNDQsMTQyLDEyLDE3MSwxMjksODQsNzYsNzEsMjE2LDk2LDMzLDE0NSw1MiwyNTUsMTQ2LDkxLDEzNSw1NSwxOTcsMzMsMjQ0LDE2Miw2OCw5Nyw0OCw2Nyw4OSwxMzMsMjAzLDE4NiwxMDAsODMsMTg1LDcwLDExOCw1OSwxODksMjQ4LDIxMywxNDIsOTUsMTk2LDExMiwxNzksMTQ4LDE4Nyw3LDEwOCwyOCwyMzUsMTMyLDEsMjAwLDIyOCwxODMsMTY0LDEzNSwxNiw4MiwyOCwxNjYsMzAsMjMxLDEzMyw4NSwyNTAsMTM4LDg5LDE5NiwxMTgsMTcyLDY1LDE5MCwyMzIsOCwxMyw1OCw4Myw3MiwyMzksMTAzLDQ2LDk2LDM2LDU1LDg5LDE4NywzNiwyNSwyMTYsMTA0LDIwMCwzNCw3NSwyMjAsMjM0LDExMiwyMjMsMCwxMjYsMTA2LDIxNiwxOTIsMTQ5LDQzLDg2LDIwLDE3MSwxMTUsODMsMjAzLDEyLDQ5LDE4NCw4MiwyMzgsMjEzLDE0MCw1NCwxNTEsOSw5MywxNzcsODIsNTQsMTIxLDMyLDEyNSwzMiwxMTEsMTE5LDE4NSwxMDEsMTM0LDI0MSwxNDQsMjI3LDIxNiwyOCwzOCwyMjgsNSwzNCwxMTQsMTExLDI5LDIwOSwyMDcsMTI1LDQ4LDM5LDIzMSwxMjksNzgsMjI5LDkxLDc0LDUsMjUyLDE2MCwxNjYsMTQ4LDIwLDQ4LDE0NywyMDIsMTk5LDI0NywxNzAsMjMxLDEyOSw4MywyMjQsMTY3LDQ5LDIzNCwxMzYsMTYzLDM0LDIyOCwxMTksMTUwLDE2LDU4LDE4NiwxOTgsMTMzLDQsNjgsMTU5LDE3NSw4MSwxNDAsMjQzLDI0LDgwLDc5LDI0NCwxMDgsODAsMTAzLDE0MywxNjEsODgsMjAzLDIyNSwxNjMsMTY0LDE5Nyw1LDgyLDI3LDEzOCw2OSwyNDAsMTg2LDkxLDIzMywxMzEsMTU4LDIwMiw0MCw5MCw4MSwxNjAsODIsMTQzLDEzNywxNjQsMTMxLDIwLDE3MywxNTMsMjUsOTUsODAsMjYsMTgxLDM4LDE0OCw1NCwxOTYsMTA0LDIzLDIzNywxMzYsMTYzLDIxOCwxNywyNyw2NiwyMjgsMTI3LDIxMSwyNTAsMTQ5LDE2NCw4NSwxMDksMTY5LDE4NiwyMDAsMSwyNTEsMjAxLDEwMywxMjMsMTgzLDE1NSw4Nyw5NCwxODcsMjM1LDE3OCw5OCw5NywyMDYsMjUsMTA1LDE2MSwyMDIsMjMxLDEyMyw2NiwxMzgsMTQsMTc3LDExNCwxNjEsNTksMTc3LDQwLDEzLDM5LDMwLDg0LDQ4LDE4NywyMzMsMTMxLDE4NiwyMDQsMTY3LDkxLDIyMywyNDMsMTkzLDkyLDIxMywxOTEsMTQsMTQ3LDE3LDEzNyw0LDM2LDk2LDU2LDQ1LDI3LDM1LDIwMSwxOTAsMTkzLDI0LDE3NywxNTQsODYsMTk3LDE1NiwxNjgsMTY4LDExNSwzMSwxMjUsMjI5LDM1LDgwLDg5LDk5LDIzNywyNTEsMTkwLDgyLDEsMTMzLDE1MCwyMTQsMjA1LDg3LDksMTk0LDIzNCwyMzQsODYsMTY4LDE0NCwxOTAsMjAxLDk3LDExMiwxNDEsNzQsNzYsMTQwLDk2LDUsMTE3LDY2LDcsMjU1LDQwLDE1OSw0Miw0MywxMTksODQsNSw5NywxLDIxNiw3MSwxMSwxNjcsMTQ1LDY1LDI1MSw1MCwxMDAsNTIsMTcwLDQsMTksMTksMTE5LDE0MCwxMCwxMCw4NSwxNyw4NCwzNywxMDksMjQwLDE5NCwyMywxNjYsMjM1LDEzMSw3NiwxODYsMzcsMTA0LDIxNywyOSwyMjcsMTU1LDYwLDEwOSw3NSwxMDIsMTcsMTI5LDE1NCwyMjUsMjIsOTksMTgyLDE3MCw1NiwyNSwyMzEsMTUxLDc5LDE2MSw1MCwxMCwyMjksMyw0MCwxOTYsMjYsMTc4LDEzNCwxNDUsMTcsMTU1LDExNiwxNDEsMjAsMTcyLDE0MSwxNjksNSw0MCwyMDksNjIsMzUsMzYsMTQsNzYsMjM2LDE5NCwyNDIsMTE2LDE4MCwyMzYsNDIsODQsOTEsMTE4LDEzOSwyMjUsMTc5LDE4OCwwLDEwNSwxMyw5OSwxODMsNTMsMjIyLDIxMywxMzEsMjMyLDIyNSw5NiwxOTMsNzEsNCwxMDQsNiwxNjUsMTc5LDIzNSw0OSwyMDcsMTE1LDM4LDEyMywxODUsMjksNjUsMTgxLDE3NiwxODUsNzAsMTY5LDE4MSwyNDEsMjIsNDEsOTAsNjYsMTIzLDg5LDE2MSwzMCwxNDgsMTE1LDEyNCwxOTgsNjQsNjYsMTAzLDEwMSw1MiwxOTcsMTkwLDEzMywxMDAsMTM5LDE0Miw4Myw5NywyNTIsMSwxMzIsMjMwLDIzNCwxODIsMjIxLDIxLDg5LDEwNSwxMjQsMTA4LDIyMCwxOTMsMTk2LDE1NSwxNTYsMTYxLDE1MSwyNDEsMTE3LDY0LDEyMCwyMDYsMzgsMjQxLDkzLDEwNiwyMTIsMjUsMTQwLDE1NCwyNywxODEsMjQxLDEwMSwyMTIsMzQsMTM5LDUxLDcsOTksMzQsMTY2LDYwLDUzLDE4Miw3OCwxOTEsNzMsMTU1LDI1MSw1NiwxNzEsNTMsMTk3LDM2LDQyLDE5OSwxNDgsMjUwLDE5NiwxOTIsMTM1LDEzNCwxMDIsMjU0LDI4LDExNywxMjYsODYsNSwxNTcsNTMsMTY1LDIzOCwxMzUsMTc4LDE4MSwzOSw3NiwxNCwzNCw4MCw2LDIyNywxNjIsMTIwLDEzMCwxMDMsMTcsMTg4LDE3Nyw1NCw5OCwxNDAsMTk1LDE3OCw2NywxNjAsMTY3LDE0LDE1Miw0OSwxNjMsNjcsMjUwLDE1MiwyMDgsMTE2LDMzLDI0NywxODIsMTIyLDExNiwxMjMsMTY5LDE5NCwxNjQsMTEyLDE1Niw3NCwxNDgsMTQ4LDY3LDEyNSwxMTksMzksMTE5LDEyNCw1NSwxMzMsMjEwLDU5LDEyNSw5MSwxODcsMTE0LDE0NSw3MSwxNzIsMTgxLDUxLDkxLDE2MSw1Nyw1NSwxNDgsODEsNDksMTc0LDEwOCwxNTMsMjA2LDI0Miw3OCwyMzksMTgsMTMwLDMyLDIxMSwxMjksMjIyLDY0LDgsMTI4LDE5MywxMjgsMTYsMTA2LDg1LDE1MCwzLDIwMiw3NCwxNjMsMjAyLDgsMTk3LDYzLDEzMywyMDQsMTg0LDE0OCwxNTMsMTg5LDYyLDE2OCwyNiwxODMsMjE5LDE1MCwyMDAsMjExLDU0LDI0OCw3NSwxMTEsMjM3LDIwMiwxNiwxOTMsMjI1LDE5MiwxODIsNTksMjAyLDM2LDIzMiwxODAsMTk3LDEzMCwxMjMsMTU5LDE1NCwxMjMsMjE2LDI0MCwxNjEsMjEyLDE0NSwxMTAsMTUwLDgwLDExOCwyMDgsOTAsMTA2LDgxLDcyLDIwLDY5LDYwLDI4LDE3Nyw5MCwxNjQsMTg5LDM5LDAsODMsODIsOTgsMTMwLDgwLDQ1LDE3Nyw3OSwxNzcsMjM1LDQxLDE0NCwyNSwzMiwyMiwxMjIsMzEsMTQ1LDIxNywxNjMsMTAxLDIyMCwxLDk4LDEwOCwxOTYsMTcxLDE4NiwxMzIsMjksMjQwLDIyNSwxMTIsNCw2Niw5NiwxNDgsMTg0LDE2NCwzNiwyMjMsMzAsMTEsNjksMTA1LDc3LDE5NiwxNjcsMTYsMTQ5LDE4NSwyNDYsMTUzLDEyOSwxODAsMTIwLDI0NCwxNTYsMTIsMTMxLDk3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NjcsMTE3LDE5NiwxODIsMTcyLDEwMSwyMzYsMTcwLDEyMSwxODYsNTcsNzcsMzMsNjYsMzIsNzMsMTE3LDE3MiwxMjIsMTUwLDEzNCw4MCwyMjAsOTIsMjM5LDIyOSw5LDczLDIxOSwxNjgsMzMsMjUxLDMzLDk3LDE3MSwyMDQsNjYsMTQwLDYzLDM1LDE1MywxODksMjI5LDEwNiwxMzksMTAxLDE1NSwxODUsMTYwLDIxMCwzOCw1MCw0MSwxODMsNjksNTksMTYwLDQ5LDExMiwxNjgsMjM0LDEzNCw5LDIwMywzNiwxOTksMTYyLDEwOCw1MSwxNzQsMjQxLDEyNyw3MywxNzIsMTg3LDUxLDI2LDE1MiwxNzMsMTQ5LDgwLDg1LDU4LDE2NSwxODIsODcsMjAwLDIwNywxMjksMTI2LDEzNSwxMTksMTY1LDMwLDIzMSwxMzUsMjI0LDE5MywxNjgsMCwxNjEsMTM4LDYwLDI0OCwxMCwxODYsNDgsMzEsOTMsMTc4LDcsMTk3LDE5OSw1NCwyNDQsMTUwLDE1NywwLDEyMSw2OCwyNDAsMjQ3LDExMCwxNDksNjksMTEyLDE3NywxMCwxOTgsMjQ5LDEyMywxNTMsMTI4LDE5MiwyMywyNiw1Niw3NCwyMjcsMjE3LDk3LDEzMiwxODMsMTUsMjIsMTIwLDE3NSw5Myw0OCwxOCwxMTgsMTc4LDEzLDIzMiwxOTIsMTMxLDE1OCwxNzYsMTE3LDEyMCwxNjUsNTIsMjM5LDIyNiwxODUsMzYsMTE1LDEyMSwxNTksMTYwLDkyLDEyMSwxNDYsMTA2LDE3MSwxOTksOTMsMjMyLDIzLDM1LDI2LDMsNDAsNTIsNSwyMDYsMTgyLDIzMyw3NCwxODQsOTAsNiwyNDYsMTg3LDUsMTk1LDE4MCwxNyw5NSwyNDIsMTA5LDE2NCwzNywxNDYsODMsMTk3LDEsMzIsMTcxLDEyNiw4Myw4OSwxMDMsOSw3Niw5LDE3NCw2OSwxNjUsOTMsMTgzLDksNzMsMTYwLDExMSwxMjgsMTIsMTQ5LDE5OCwyNDgsMTE3LDAsMjgsMiwxNjcsMjM1LDIzLDczLDk3LDExNiwxMTMsMTUwLDE0NSwxNjksNywyMDUsMTEzLDE0Nyw3LDEzOCwxODAsOCw5NywxMDAsMSw5NSwxNjQsNTAsNTYsNjgsMTcyLDI0NCw2LDEzMCw4LDE5LDE3OCwxMTAsMjQxLDE1MiwxMzYsMTM0LDE5Niw1NiwyMDEsMTk2LDIwOCw1Myw4OSwxMzIsMjI2LDU0LDIyNCwyNiwyNywyMiwxNzgsOTIsNDYsNDIsNjksMTAwLDU2LDExOCw4MywzOCwxOTgsMCwzNyw4NiwxMTgsMjI1LDQsNDcsMzMsMjMsMjA4LDE5MiwxMDQsMTAsNzMsNzcsMjE3LDExNywxNCwxNjUsMjEzLDE1MywzOCw4MywxNTAsMTM3LDI0MywyMDAsMjM3LDE3LDM0LDEyOSwxNzgsODcsNTMsMTQ5LDE1MCwxMTcsMTM0LDE4Niw3NCwyMjYsMTQyLDEyMCwyMTEsMTAzLDc3LDE0OSw3NSwzMSwyMzAsMTU1LDE2MSwyMjgsNyw1OCwxNjcsMTQ2LDE3NCwxMzMsNiwxOTgsMjEsNTAsMTQ0LDI1MiwxNDMsMjQ4LDk4LDQsMTYsMTM5LDIwNCwxMyw0OSwxMTIsODIsMTgyLDYsOTIsMTY5LDcxLDgwLDY0LDQ1LDE0MSwzOSwxODEsMjA0LDEzMywxNjYsODUsNDEsMTQsMjAzLDIyOSw1MSwyNTQsMjIsMTUyLDYwLDE5OSw4Miw0MiwxODYsMzcsMzMsMjYsMSwyMzYsNzIsMTU1LDk5LDEyMSwxNTksMjIwLDIxLDE2NiwxMTIsNjcsNDQsNDMsMTYsMzYsMTU3LDE0MCw0NCw1NywxNDYsNTIsMTAsMTY2LDE0MywxNDUsMzIsODAsMTU0LDkyLDAsNjQsNDgsMTQwLDIyOCwxOTIsMTcxLDE3MywxNzAsMTk2LDQ3LDEwNCw3NSwxMDQsODAsNTAsMTU3LDEzNiwyOSwxMjcsMTM4LDQ1LDUsMjE0LDIyLDcxLDExNiw2MSwyMTcsMjgsMjcsOTQsMTI5LDYsMTIsNzYsMTQ2LDkwLDU5LDEyNCw0NiwxNjYsODYsMjA4LDgzLDE5NSwyNDgsMTE1LDU4LDE1OCwyMTYsMTM4LDIyNywxMTQsNywyNDQsMzksMjYsMjcsMjMyLDM1LDk2LDIzLDgsMjAsMTUwLDg4LDEwMiwxMTgsMTczLDEwMywxNTEsMTI4LDMyLDEzMiw0NiwxMzksNjQsMTY4LDI0MCwxNDQsNDYsNCwyMSw2NywzLDIyOSwxNTQsNjgsMTE5LDcwLDEwOCwyNDgsMTc4LDIwNSwxODUsMSwzNCw2Nyw0MiwxNDksODEsMTUzLDEzNSwxMzksOTksMjM1LDE4MSwxLDE2OSwyNiwyMiwyMDYsNTAsMTYsNTUsMTYsOSwxLDAsMTk1LDIzNCwxODUsMjAzLDI0MCwxOTcsMTAxLDI0MywyNDQsOTcsMTc4LDIwMiwxODcsNDAsMzksMTQ0LDEyMiw3LDYxLDE2OCwxLDE2NSwxNzYsNzQsMTI0LDE2OSwxMzksMTYxLDE0NywyMTgsMTgzLDExMywyMCwyNiwxMjAsMTAsMTA5LDEyMiw4MSwxMDEsMTIzLDExNCwxNDIsOTYsMTU0LDE3NCwyMjgsNzEsNzEsMjQ2LDIzMCwxMTgsNzUsMTM0LDIwNiw4OSwyMDEsMTEwLDIzNywxNzksNDAsMTYzLDIwOSwxODcsOTksMCw1NCwxMDMsMTYxLDcyLDIyMCwxMiwyMDYsMTM1LDExNCwxMTQsMTYzLDM2LDE1OCwxMjEsMTAxLDU0LDE1MiwzMiwxNjcsMTE4LDExLDE5NCw4Nyw4NywyMDIsNTIsMjA4LDIwMCwyNTQsMjQ1LDEzLDIwMCwyMiwxNDksMjA1LDEwNSwxMjEsODAsMTY3LDM1LDEwOCwxMzIsMTUxLDIwOSwxNzAsNjIsMTczLDE0OSwxODIsNzksMjMsMTYsMSw2OSwxMDksMTYsMjUyLDE2NCw2OSwyNDIsMTkxLDI4LDUsMTI0LDMwLDE2MiwxNjEsNzAsMzIsMTk5LDEwNiw2OSwyMDMsOTcsMjUwLDgzLDIyNCwyMDQsMTkxLDE5MSwyMzUsOTksMTI4LDU3LDE1MCwxMzcsNTcsMTQ2LDEwMywxNTMsMTQzLDcxLDU1LDEwMSwyMDQsMTU1LDc3LDUsMjI2LDI0LDQ4LDEzNCw2NSwxMywyMDksMTgxLDQwLDgzLDEwNiwyMzQsMTE5LDY5LDUwLDE2MywzOCw3MiwyMDIsODksMTY4LDgsMjAzLDIyMiwyNDcsMTE3LDEzMiwxMjMsMTE1LDExNiwyNDUsMTg5LDM1LDE2OSwxNDIsMTIzLDQsMTI4LDQxLDIyNywxMTYsMTQwLDEwMiwxMSwyNDIsMjI2LDE4Miw0Myw1MiwyMDEsMTM2LDE3OCwxODQsMTU3LDQsMTMxLDk1LDUzLDcyLDIxMiwxODEsOTQsMjM4LDIxNiw2OCwyMDMsMzYsMTE1LDM4LDIwOCwxOTgsMjE5LDE0OCwxNjMsNzEsMTMwLDExMSwzNywxMzIsMTk0LDk2LDkwLDEwOCwxNzMsMCwxNjQsMTYwLDI0MCwxNjgsNiwxOTYsMzMsMTIyLDE5NSw3LDEyNSwyMTEsOTcsMTY4LDIzMCwxNzcsOTksMTcyLDEzOSw5OSwxMjksODEsMjU0LDE1NCwxNDYsNCwxNjMsMTE3LDQwLDcsMTIxLDE1MCwxMDcsMjQsMTczLDIwOSw2Nyw2NCwxNzIsMTg1LDYwLDI2LDE5NCwxMjIsMTg1LDE3Myw0OCwyMzYsMTEwLDI1MywxNzgsMTUsMjUzLDIzMiw1MCwxODksMTUxLDE0MCwyNDIsMTc4LDEyMCwyMzcsNzYsMTc5LDIxOSwxMzksNjUsMTk4LDU5LDYsMTg3LDIzMywxNjIsNDYsMTk3LDkzLDYsMTgwLDksMjE2LDI1MiwxOTUsMjQwLDI0LDE2OSwxNTQsMTk2LDE2MywxMjMsMjMzLDQ5LDM1LDEyMywxMCwyMTEsNCwxOTMsNTMsMTk1LDQ1LDU3LDE1NSw3Miw5MSw0MSwxNDMsODQsMjUsMiwxMjgsMTM1LDE4NSwxNTYsNTAsMTk1LDIzMyw4NiwxNjAsMTE2LDg1LDE4LDcyLDE2MiwzOSwxOTcsMjQwLDE3NSw2NSwxNTUsMTI5LDQyLDIxMiw5LDYxLDMsNTEsNzUsMTc2LDU1LDEzOCwxMzUsMjA3LDIzLDEyOCwxMzIsMTY2LDIxMiwxNTcsOTAsMTE2LDk3LDEwNCwyNDMsMTAsMTUwLDE0MCwxNjIsMTY2LDg1LDEzNCwxNTcsMzIsMTIzLDI1MSwxMDMsMTgzLDIxNCwxNzAsMTQsMjQ4LDcyLDE4OSw1MiwxNjQsMTg1LDY4LDE0NCwxMDIsNywzNywxMDYsNTIsMjIwLDE1NSwyMTAsODIsMjA3LDc3LDI0NSwxOTcsMjUsNTIsMjcsMjA5LDI1MSwyMSwxOSwxOTUsNjIsODksMTM4LDMwLDgzLDQsMTQ0LDc4LDI0Myw4NCwxMjksMjMyLDEsMjE3LDE0OCwxMDEsMTY4LDEwMiw4Nyw5OSw2NiwxNzIsOCwyLDE1NSwyMTUsMTA4LDUzLDEyOSw4MywzNCwxOTcsMTI4LDMwLDIyMCw5NiwxMjMsODMsMzcsMzcsMTQ4LDY2LDE0OSwxOTEsNTMsNTYsMTYwLDExMiwxNzcsMzgsMjE5LDIyOCwxNSwxNDUsMTQ0LDE4Nyw2MSw0NCwxMjEsMjcsMjI3LDQyLDMxLDUsMTk4LDE2NiwxNjMsMjE2LDE4NSwxNTUsMTk1LDE2OCwxMDgsMTY0LDIzMCwyNCwzNiw1Nyw2NCwxMTUsMTMyLDEwNSwyNDAsMjEsODIsMjMsMyw4MywyMTMsMjA3LDIzMiw2MF19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjQsMTgwLDEyNSwxMjYsNzksMjIwLDIyMSwyNTMsMTIzLDQyLDIwMSwxMjAsMTgwLDE2NCwyMDAsMjA4LDE3Nyw5OSw4Miw2MCw1NSwxNTYsMTI2LDc3LDExNywxMTUsMjAzLDIwMywxNDIsMjU1LDE2NiwzNF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbNjcsMjQxLDE2NiwyMjIsMTAsMTUzLDIzOSwxNTQsMTQxLDQ3LDMxLDE0MiwyMjMsMTc4LDI0Myw1NywxNTcsMTIyLDE1NiwxMDIsMTksMTE2LDIwMSwyMTcsMjQ3LDIyOSwxMTgsMTY2LDE0MywzNiwxMjgsMjM0XX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzIxOCwxMDUsMTQxLDE4MiwyMDAsMjI0LDE3MiwxMTAsMTI2LDE5LDEwMSwyNiwyNDcsNDUsMTk1LDE3MywyMTMsMTYyLDg0LDIyOSwxMDksOCwxNzMsMjUxLDE0NSwyMTgsMjQyLDMyLDAsMjE0LDIsMTAsMjQ0LDIwNiwzMywwLDUwLDI0Nyw4MSwyMDksNDIsMCwxMjMsMjA2LDIxMiwzNCw4Miw3NCw1OSw3MywxODcsMTM5LDIxLDY1LDE3NywxMjgsMTE3LDE3NSwxMTcsODQsMTQzLDIzLDE1OCw3XX19fX0seyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzcwLDE4MCwxMzgsNzEsMjQyLDEwOCw2MywyNDAsMTg5LDE5OSw0MSwxMTAsODksODksMTk1LDE0NSw4OSw3NCwzMywyMTMsMTQsMTY3LDkxLDEwMywxNjMsNjYsNywyMTAsMjM2LDksNTAsMjQwLDEwMCwxNTMsMTAxLDI4LDg1LDksMTYsMTYzLDE3NywxMjAsNjQsMjQ3LDExLDksMTMzLDk1LDE1OCw4OCw2MiwxNDUsMjUyLDEwOSwxMzQsMjMwLDkyLDIzMiwyMDQsMTAxLDIxNyw2MCwxNjksMTI2LDkyLDIwMywyMzgsODYsMjEsMjMzLDg0LDQ1LDE1NSwxMjksNDIsMTM3LDE1MCw1OCwxMDgsNDEsMTYxLDExMywxNDgsMTc1LDg2LDE1NiwxNiw5OCw3NSwxMzgsMTIyLDcsMzMsMTM1LDkxLDc3LDI0MiwyMywxOTEsMjQzLDQ4LDksMjUsMTAwLDQ0LDI0OCw3MywxMjQsNzEsNzQsMTQ3LDIyMCw0OSw0MywyMjgsNTEsMTg2LDEyMCw5NiwxODksMTc1LDM4LDExNSwxMDgsMTEyLDE2MiwxMDMsNTgsNDcsOTQsMjQ2LDQ4LDI0Niw2NiwxMDksMCw2Nyw2NywyMjksMTQ3LDY0LDEzNywzLDE2MywxMTksMTc5LDYzLDEwNSwyMzEsNDYsMTgwLDk4LDEzMSw4OCw0OCwxNDYsMTA0LDIwOCwxMzQsNiwyMCw1LDcsMjMzLDE0NiwyNDgsMTM1LDExNSwxNjcsODEsNzMsMTE5LDEzNyw4MSw5MCwxNzcsNDMsMTY3LDIyLDQ5LDIxLDI1LDExNiwzMywxMzAsNjIsMTEwLDIxNywxOTUsMTU2LDEwLDE3MiwxMzAsODYsNTgsMjEzLDU4LDEyOCw1Miw2NywzMywxNiwyMTksODMsOCw0LDE4MCwxMTksMTc2LDE5NywxMDcsMyw3OSwyMTYsMjQ0LDEwOCwyNDIsMzksNjQsMTY3LDI0OSw0NiwyMzksMTM2LDE2MSwxODAsMjI4LDE2NywxOTMsNSw0MSwxNTYsMTQ5LDEzOCwyMjAsMTQ3LDE0NywxMDcsMTI4LDY1LDgzLDE4MiwxMzIsMjUxLDIzNSwxNjAsMTk0LDIyOCw3OSwxNTIsMTQ1LDE3MCwxNDYsMTIyLDE2LDIwNiwxNzgsNjEsODQsMjE5LDIxLDksMjUxLDE4OCwyMTcsNjcsMzUsMTQ3LDIzMiwyNyw4NiwxNiwxMTAsMTMzLDE1Myw2MywzMCwxOTMsMjAxLDg4LDIzNSw3MiwyNTQsMTY3LDIxLDkzLDg1LDUsNjIsMjQ3LDc2LDE0LDg3LDEwNCwxNzYsMTY0LDEzLDE1NCwyMTUsMTA0LDEzNiwxMzIsMTQyLDU2LDIzMSw5NiwxNTksMTY4LDI0LDE2NywxMDEsMTQyLDEyNyw5MiwxMywyMzYsNjUsNTUsMTAwLDE3NiwxNDAsMTY5LDY3LDY4LDY4LDcyLDExNCwxOTcsMjExLDExOSw5MiwxMzgsMTk3LDIxMCwxOTksMTMyLDE2Niw1MCw1NCwxNDYsMTE1LDQwLDM4LDE3LDY4LDIzNiw0Myw4NSwxOCw4OCwxNDksMjQyLDgyLDgyLDIwMCw5NywxODAsOTcsNTEsOCwxMTcsMTk4LDExNywyMDEsMTYwLDU1LDExNiwxOTMsMTQwLDE0OCwxNyw0LDYyLDUzLDExNywyMTgsNTQsNzcsMTY3LDIxNCwxMSwyMDEsNDEsMSwxNSwxODYsMTcxLDIyNyw0NCwzNiwxMSwxOTksMjA0LDM2LDI0LDE3OSwyMjAsNTMsOTksMTgwLDEzNCwxNiwxNzgsMjI4LDQyLDE3OCw2OSwxNTEsMjA3LDEyMCw4MSwyMDIsMTE4LDE2MywxMDUsMjA0LDU0LDU5LDE5MywxMDQsMjAwLDE2OCwxNzYsNzMsMjUwLDY3LDI0NSwxNzIsMjksNzMsMTAxLDExMCwzOSwxMDQsMjUsNTksMTMzLDkyLDc1LDEwMSw4NSwxMzksMTE4LDE3NSwzNSw3LDEwNywyMTQsMTIsMTU0LDI0Niw1NiwyMCwxMDksNTMsNDgsNzksNzIsMTEyLDE1Niw4LDkxLDI1MiwzMiw3MSwxNzcsMjQzLDEzOSwxODQsMTk2LDEzNSw3Miw2NywxMjEsNDYsMjQ0LDEzMCwxMjEsMjQ4LDE1NSwyMjUsMiwxMDgsMTY4LDUwLDEwOCwxLDEwMiw1Nyw1OCwxNTAsMTI2LDEyMCwxMDUsNDgsOCwxOTksMTAsMTYwLDE4MSwxMzIsMywxMjMsNTMsMTYyLDE3LDE5MywyMDIsMTg4LDE0OSwyMjUsMjYsODMsMTMyLDExNSwxMDcsMTg2LDExMyw5OSwyOSwyNTIsNTQsMjQ3LDE1MywxNjUsMTQ4LDE0NywzOSwzMCw0OSw2Niw5NywxNjIsMTMwLDQsMTA1LDE2MywyMDAsNzMsMjA2LDc0LDE4Myw2Miw4NiwxNjYsMTg4LDEwMywyMjAsNTgsMTgwLDE2Miw1MCwxMTksMjA5LDExNSwxNjksNjQsMTMwLDEwLDkwLDQ3LDc0LDgsMTYwLDM3LDY0LDExOCwxOTMsMTUwLDEyLDQxLDI1MSwxMSw3NSwxNjQsMTMyLDE5NywyMzYsMTQ2LDIzNiwxMzQsMTMyLDI0MCwzOSwxMDYsMjQ0LDI0NSwyMCwxMzQsMjI1LDgxLDI1LDE0MCw3MiwyNTIsMTM3LDI4LDUyLDEzMSwxNjUsMTcyLDEwOCwyMDMsMjgsMjA4LDc3LDIyMCwxODMsNzYsMjA4LDczLDM4LDIzNiwxODAsMCwxOTksMTE5LDM4LDM5LDQ5LDYwLDQzLDIxNywyOCwxNiwzNiwxODAsMjM1LDExOSwxMjksMTcwLDcxLDE4OSwxNzcsMTM5LDcxLDE1MiwxMjAsMTc2LDEyNiw1OCwxMTQsMTMyLDczLDUwLDE5Niw3LDE3NCw0NSw4NCwxMDMsNzAsOTEsMTgyLDEzNSw2OSw5NSwxODUsMjUwLDEzNSwxNjYsMTYyLDE0NywxOTQsMTk3LDExNCw5MiwyNyw5OCwyMzQsMjMyLDE0MywxMDAsMTQ4LDEzNCwyMDgsMjI3LDMwLDUzLDI0OCwxMTAsMTc1LDI0NCwxODYsMzIsMTI0LDcsMTY4LDI0MCw1MSwyMDMsMjQ5LDIyLDYsMTk0LDcwLDExMSwyMSwxMTgsMywxOTIsNDIsMTc3LDI0MCwxNiwxMTcsMTgxLDI3LDQxLDIwMiwxNSwzOSw2NiwxODEsMjAxLDY2LDg3LDE3OSwxMjksMTAwLDExNCwxNDQsNzcsMTI2LDkyLDIwMiwyMjAsNjQsMTE3LDE0MiwyOCwxMzIsMjE2LDI0OSwxNzcsNTEsMTUwLDEwMiwyMzksMTIzLDMwLDE5LDgwLDIwNCwxNjAsMjMsMTM3LDExNiwxNTYsMTI1LDEyOCwxMzEsOTksMTg4LDYsMjAsMTQ0LDE1MywyNCwyMjcsMjEzLDgwLDI0OSw0MSw2Nyw2NSwxLDQyLDI0NCwxNDYsMTI5LDE4Myw4MywxLDE5NCwxNjQsMTQsMTcxLDE4NCwxMjQsMTI4LDExNyw3MywxMTQsNDksMTc3LDE5Myw4NCwxNTUsMTUyLDAsNTgsNTksMjQ1LDEyMSwxMjcsMjEzLDEzMCwxMDYsMTc5LDcsMjE5LDE2OSwyOCw3MiwxMTcsNSwxOCw5MCwxODQsMjQwLDgyLDE3MCwyNTAsMjQ1LDYzLDQwLDE4Miw2OCw4NCwxNTUsMTc0LDUxLDE3NiwxMjgsMTAwLDExLDE1MSw4NSwyNTAsMTg2LDExNiwyNywyNSwxNTYsMTcxLDUxLDE2NCwxOTcsMjAzLDE4NywxODAsNDYsMTQ3LDIxNSw2NCw3MCwxMTYsMTYsMjE3LDIyOSw5OCw1MSwxNTAsMTQ3LDIxNiwxNzcsMTQ2LDE5MSwxNTMsMTQzLDM2LDIzLDMxLDIxOCwxOTksMTYyLDIwLDIxMiwxMCw3MCwxNjQsNTQsMTYsNjUsMjQsMTE5LDE3Niw0LDIwOCwxNTEsMTg4LDUxLDcxLDE4OSwxMDksMTY2LDE1NiwyMTEsMTU2LDE4NywxOTgsMTM4LDUzLDEzOSwxNTQsNDMsODcsMTUwLDQ5LDE3OSwyNDAsNTEsMTEwLDExOCwxMzIsMjEwLDkwLDEzMCwxNTQsNjQsMTMwLDIxMywyMjcsMTg2LDUxLDEwNywxOTEsOTEsMTU0LDQ3LDEzNCwyMDgsMjA1LDIxMywyMTIsMjA2LDUsMTI4LDE4NywxMDcsMTMzLDEwMSwxOTEsMTE1LDE4LDEzOCwxMDUsMjcsOTgsMjI1LDEzMywyMTgsODEsMzgsMTE5LDIxNyw0OCwxMzYsMjEwLDIwNCwxMjAsMTY3LDcyLDk3LDEzNSwxMDYsMjAzLDM0LDE2LDQ1LDE1NCwxOTAsOTIsNTEsNDYsMjA5LDk2LDE4MCwxNjgsMTAwLDQwLDI1MCwxOTIsMTQ3LDIxOSwxMDUsMTgzLDg3LDQzLDE1OSwxODUsMjQ3LDUzLDIzOCw0NCwxODAsNDYsMTgsMjgsMjE2LDE2NSwxMDEsMTYwLDIzMSw1Myw2MywxMTIsMTAzLDIxNywxODYsMTAyLDEyOSwxOCwxNDEsNzYsMTE5LDczLDIxMiw4MCwxNDcsMTU3LDk4LDEyNSwxMjIsMjAzLDU3LDI1LDE5NywxNSwxODcsMjE5LDMsMTI1LDgyLDEwMyw0MCwwLDQ3LDIyOSwyNSwxMTcsMjQ2LDIwLDE5MiwzLDIwMCwxLDE4MCwxNTAsMjA1LDIwMSw1NCwxMDYsMTgxLDExMyw0Myw4Myw0Myw1NCw3MiwxMDUsODAsMjUsNTksMzcsMTc1LDIxNCwxMjMsMzMsMTUzLDExLDIyMSwxOSwxMzEsMTMyLDI0MCw4MCw5NSwyNTEsNCwyNTQsMjUwLDgwLDEzMywxODcsMTgyLDExMiwyNDUsNzUsMTI0LDEwNywxMjEsNCwyNDAsMTk4LDI0MCwxNjIsNzIsMjI4LDI1MiwxMjAsNzcsMCw3MiwxODEsMTk4LDI5LDEwMiwzNywxODEsNDUsMTk3LDk3LDE1LDIzLDE4NCwxNjgsMTAyLDUxLDY0LDExNSwxMTMsMjM3LDIyNSwxMzgsODksMiwxMjgsMjM5LDE4MSwxNzUsMTAyLDIxMSwxNjAsNTQsMTgyLDM1LDIzMyw1MCw5LDEwNCwxMDAsMTMxLDI2LDg1LDQ4LDE1Nyw2Nyw2OSw3MywyMDMsMTI4LDQsMTcyLDE1OSwyNDMsNzQsMTQyLDE5NiwxNjksMTksNzksMTUyLDIsNiwzMCwxMzIsMTcxLDEwMywyMjEsNywyMTEsMTY5LDc4LDIzNSwyMDYsNjksMjgsMjUyLDEzLDY5LDEyNSw2NCwyNTQsMjQ3LDE3LDE2MCwyMCwyMDIsMTQ2LDAsMjMyLDAsMTIsMTcxLDk2LDc2LDEzMywxMzQsMjQzLDU5LDI0MCwxNTAsMTQ5LDk1LDIwOSwyMTksMTk5LDczLDE4OCwxMDUsMTAzLDEwMSwxODUsMjQ1LDEzLDk1LDIyMiwyMDQsMTYyLDQ1LDEzOSwzMiwxMDUsNV19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMzksMjM3LDI1MSwxNTIsMjMxLDE0MCwyNTIsMzMsMjM3LDE3NCwyNDIsMjQxLDEyNCwxMDMsMTMwLDI0LDc2LDEwNiwxNDUsMjI3LDAsNTEsNjgsMTEyLDE2MCw0Niw0Miw0OSw4MiwyMzMsNzcsMTE3XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NTAsIm5vdF9hZnRlciI6MTcyNzk1MDE1MH19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzIyMiwxNzksMywzMiw5NCwxMjcsNjYsMjQ5LDE4OCwxMDcsNzcsMTg2LDEyNiwxODksMTM1LDUyLDY4LDQwLDE3MiwxMTAsMTM1LDI0NCwyMDgsOTUsMTY3LDIyMywxNzcsMTgyLDE2MCw0NiwxLDQ1LDEwOCwxOTEsNzIsMTE5LDE5NiwyMiwxNTAsMTk0LDUzLDIyLDIwMyw5MSwyMjMsMjA0LDE1MSwxNDQsMTI2LDIwMywyNDksMTIxLDYyLDQ1LDQsMTQ0LDg5LDI3LDE4MCw4NCwyMzYsNjYsNTksMV19fX19XSwicGFyZW50X25vZGVzIjpbeyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsyMzcsMTQ3LDE0NSwyNTEsOTgsMTUsOSwxMiwxODcsMTAsMTkyLDE1NywxMTQsMjMsNjAsMSwyNDAsOTUsMjA1LDE4OCw1NCwyNDAsMzMsMTYwLDEyNSwyMjQsMTk2LDEzOSwxNjAsODQsMjE3LDE2NiwxMDUsMTE5LDExNiw5NiwyMDgsMTY3LDE4NiwxNjgsNDMsMTkyLDIxMywyMzAsMTQsMjksMTAsNTEsNjAsMTQ1LDE4OCw0NiwxMjEsMTk4LDMwLDU4LDE2Myw0LDI0NywxMTAsNzIsMTgyLDg2LDg2LDU4LDE1OSwxOTcsNTEsMTU3LDEwNSwzNCwxMzQsMTgwLDI4LDE3MywxOTQsMTMyLDEwNiwyNDcsMTMxLDE5Nyw2OCw4MywxMDAsMjQ0LDI0NCw1MCwzOCwyMTAsMjksNjgsMjUyLDM2LDcwLDI0OSw2NSw1MSwyMTIsODIsMTg1LDkwLDE1MiwxMzQsMzksMTMxLDQ5LDE4OCw2LDEwNSw5OCwxODMsNzIsMTA1LDkyLDIzNiw2NCwxMzgsNDAsMTksMjksMTUsNjAsOTgsODEsMTE3LDY1LDg3LDE1NiwxMCwxOTgsMTg2LDEzNiwxNzksMjQ0LDE3Niw0NSwyNiw1NSwyMzIsMTk0LDE4Nyw5NSw4MiwxNDUsNSwxMDAsNzEsMjA2LDEzMCwxOTgsMTA0LDIzNCwxODksMiwyMjUsMjEsMTI3LDM2LDE0LDEyLDE2Nyw1LDY1LDUwLDE1Miw0OCwxNDAsNTgsMzYsMjE4LDEyNCwxOTgsNiwyMDYsMTY5LDEwMiwxMDYsMTc2LDU2LDE5MiwxNzcsMTQ1LDE3Myw2MywzOCwxMTUsMTcsMjQ5LDE0LDE5NywyMTEsMTg2LDkyLDEwLDE2OCw1NSw2NSwxMDksMjE2LDIwMSw1NCwyLDEyMiwxMSwzMyw0LDE3Nyw1MSw5MCw5OCw3NSwxMTIsMTI0LDUyLDQyLDQxLDEyNywzNiwxNTIsNzMsOCwxMjAsNzksMTgzLDY5LDE3OCwyMjAsMTUyLDIxMSw3NiwxNjUsNDMsOTEsNjIsMTA0LDEzMiw3MSw0MCwzOCwxNDksMjQzLDEyMCwyMDcsMjIyLDEzNSwxNzgsMTk1LDEzMiwxNDQsNDUsMTY0LDIzLDE0MCw4OSw0NywxNjAsMTAsMTA5LDE0MCwyMDksMTA2LDEzOCwxNDQsMTY5LDE0MywxMiwzLDE5MSw3NCwxNTksMTk2LDIyNSwxMDEsMjE3LDIxMCwxNTksMTUzLDI0MCwyNiwyNTEsMjUxLDUsMTIsMTE5LDEyMywxMDYsMTcwLDAsMTc1LDcwLDk2LDE0OSwyMDgsOCwyMiw2NiwxOTEsMjA2LDEzNiwxMDAsMSw4Niw0NSwxNTQsMzcsMTg5LDExMiw2NywxNTQsMzEsMjUsOTUsNDAsMTY5LDE2MSwxMTEsMTY0LDM1LDIyOCwxMzcsNTcsMTI0LDIzNCw4OCw5Miw3NCw1MCwxNDgsNjAsMTY3LDUwLDIwOCw1Nyw4MywyMzEsMTA2LDIwMCwxMTIsMzEsNzksMTE5LDg4LDE0NiwxMjIsMjYsMTAzLDIxLDQwLDE1MCwxMzgsMTkyLDE1NywzNSwxMDIsMTIyLDEyOSwxNDYsMTI0LDIyNyw4MCw2NCwxNjUsNTksMTI5LDE1NSwzLDE4OSw5MCwyOCwxOTksMTUyLDkzLDMsMTEyLDEzOSwxOSw4OSwxNjAsMTAsMTYwLDEzNiw4LDIyNywxODcsMTc0LDU3LDE1MiwyMSw4NCw5Nyw3NSwxODEsMTEsNzEsMjA5LDgyLDEyNCwzMiw0NiwyOCwyNDYsNDQsMTIxLDgwLDYxLDExNywxMDUsOTAsMjMxLDEzMiwzNSwxOTMsNzEsMTQ2LDE1OCwyNywxNzksMjM4LDgzLDEwOCw2OSw4OSwxODgsMjI5LDIyNCwxMTAsMjM3LDExNSw5NCwxNTksMjAwLDUzLDEyOCwxOTIsMTI1LDEzMywyNTIsMTk4LDI0OCwzLDQsMCwxOTksMTIwLDE2NSwxODMsMTc3LDEzOCw4LDE2OCw5NywyMjUsOSwxOTksNDAsMTIxLDYwLDIwNCwxNCwyNDgsMjEwLDI1LDIzNSw4MSwxNCwyMTcsMTI4LDAsMTQxLDE5NCwxMjQsMTk4LDI0OSwxODAsNiw2NSwxMTgsMTMzLDE1MCw2Niw3OCw5MCwxNTUsMzAsNzEsMTYyLDE2NSw4Nyw2OCwxNDIsOTgsMTk4LDE1OCw4NCw0MSwxNTAsMzUsMTM5LDEwLDksMTQ0LDgsMTMzLDEwMiw3MCw3MSw1LDIwMCw2OSwxMjMsMTkxLDE2MSwzOCw5MSwyMTIsMzIsMjUyLDI0NywxNjIsMjE3LDEwNSwxODAsOTAsMTQwLDE0NiwxLDIxMywxNjAsNDcsMTM1LDE1OCwxMDIsMTE4LDIzLDI1MiwxNzAsNzQsNzEsNzUsNSwyMzQsNSw2NSwyMjgsMTA3LDM2LDg3LDEwLDExMiw5NywzOSwxMTUsMjE2LDMyLDU5LDE4MiwxMzksNTksMTc0LDIsMjMsMzAsMTA3LDc0LDU0LDE2OCwxNTAsOTIsMjEyLDE5MSwxMjUsMTAzLDE1NSwxNDMsMTQ2LDM4LDE4NCw5NywzMiwxMTQsMTk1LDcwLDQ5LDk2LDE4MSwxMDIsMjI5LDE4LDE2NywxNzksMTM1LDE2MywyNSw5MywyNywxMzMsOTAsMTkzLDUzLDE0MywxMTIsMzksMTI4LDE5OSwyOCwxNzMsMjA4LDQ4LDU0LDEyOCwxNjIsNDgsNDksMzksMTI0LDIzNCwyNDYsMTg1LDEzMiw2NCw2OCw5OCwxODAsMTE1LDk2LDEsMTg4LDM0LDgzLDk5LDE0OCwxNDcsMTQ5LDE4MCwxNywxMzAsMTc1LDksNDUsMjEsNjgsMTQxLDEwNiwxOTUsNjYsMjAyLDE2MSw1Myw3OSwyMDksMjcsMTEwLDY5LDUzLDg3LDE3NywxNzUsMjksMjQ2LDIwNSwxMTIsMjM1LDExMywyMzUsMTAxLDQyLDIxMywxMTksNCw5LDIzNiw0Miw2MCwxNDYsMTI4LDE1NywxODgsNTYsMjEzLDM2LDMwLDIzMSwzOSwxOTUsMjI0LDQ4LDE3MywyMTEsNDQsMTE1LDIyNSwxODYsOTMsMzQsNzQsMTkwLDEzMyw5MiwxNjEsMTE4LDI2LDE5NywxNDAsMzcsNTYsNDksMzcsMjYsMTA4LDI0MywxMTQsMjUxLDIyNywxMiwzLDU3LDUxLDIyMCwxNDUsMjIsMjMyLDQwLDI4LDM5LDEzNSwxNjksMTY2LDE3NiwyNCwxNTUsMTgsMTI2LDIxLDE5OCwxODMsMjUsNzYsNzQsMTQzLDM4LDY1LDIyNSwxNjAsMTEsMTcxLDE1NCw5NiwzOCwxMjIsMjksNywxNjgsMTc3LDYwLDUxLDExOSwyMjksMTA2LDg5LDIyLDE2LDEyMSwxNjEsMTMwLDEwMywxMDIsMjgsMjgsMjI1LDEwMCw4NiwxNSw1MCw3NSwxOTQsNDAsMTQxLDEzOSwxMTUsODYsNDcsMyw1NywxNzIsMTM1LDAsMiwyMzUsMjAxLDE0NywxNzIsMTY1LDE1MiwxNjYsMTQyLDE0LDI1Miw0NywyMDEsNDIsMTQ3LDEzMSwyMDQsNTAsNDEsMTksMTc5LDYsNTYsMTIwLDEyLDIzMCwxNjMsOTksMTI4LDE2LDEwMSwxODgsMTM1LDI1NCwyMzIsMTM3LDExMyw0MywxOTIsMjI1LDExMiwyMDAsMTYwLDIwLDIsMjU0LDE4MywyMSwyOCw1NSwxMzYsNjAsMjM1LDE1Nyw3NCwzNiwxMzksMjI2LDg2LDEzMCwyNTUsMjI1LDE2NywxODgsNDksOTEsMTc0LDE0MCwxMjksMyw4NCwxMDAsMTE1LDQzLDc1LDQsMTI0LDEzNSw0NSwxNjMsMzgsNDQsMTU2LDE0MCwxMTcsMTgwLDE3MSwxODAsMjgsMTkyLDUwLDEyOCwxMjcsNzcsMTksMTc4LDEzMCwxMzQsMTA3LDE4OSwxNzAsNzQsMjMzLDEwNSwxMjMsMjQ0LDEyMCwyMDcsMTMyLDEyMywxNzUsMTc4LDE4LDE4NCwyMjQsMzksMjEsMTgxLDEwMiwxOTUsMjE5LDE2MiwxMSwzNiwzNSwxMTYsNjYsMjEsMTY1LDE0MSwxMCw0NiwxMjMsMTcsMTkyLDcyLDM4LDE4MywxODksMTgxLDE3MSwyNTEsMTgzLDk2LDcxLDEzNCw0NiwzMCwyNDgsNDQsNjMsMTA4LDExNSw4MCwyMjAsMTQwLDEyNSwzNSwxMzIsMjI1LDIyLDUsMTA4LDI4LDE3OCw2MywxOTgsMTExLDIzNiw4NSwxNzksMTQ5LDEsMTgzLDE0MywxNjIsMTM5LDIyLDgwLDQ2LDIzNiwxMzksNjEsMTA4LDEzMywxMywxNzUsMjgsMTc5LDE2MywxODEsNzMsMTksMTAxLDQ3LDU1LDI0NSw0LDExMiwxMDAsNCwxNjcsMjI0LDEyNSwxNTAsMTQ5LDIwMSw1LDEyOSwxNzksMjM0LDIwMywxMzcsNTQsOTEsMTAxLDExOSwxNzEsOTgsNzUsOTksNjUsODUsMTI5LDE5NCw5LDksMTMwLDQsNTUsMTM1LDE2MywxNTMsNTEsMTM3LDIxLDMyLDIxMiwzOSw4MCwxMTcsNTQsOTUsNzcsMjAzLDExOSwxMjEsMTU0LDcsMjE0LDE0NCwxMjIsNzMsMTMxLDE2NiwxMjgsMTc3LDE4OCwxODMsMSwxNzQsMTUxLDE4NSw2NSw2MCw4Niw4MCw5OSw1OSwxNCwyNCw2Niw0MCwyMjEsNjgsNDcsNjUsNjUsMTc3LDEzMSwxOTIsMTQ0LDQ3LDcsMzcsMzMsNzYsMTY3LDI1NCwxNjcsMTIxLDIyMiwxNjYsMTU0LDYzLDk3LDE1OSwyNDAsMTY5LDEzNiwyMzMsMTIyLDExOCw1NywxMTIsMTk5LDEzMSwxNzcsMjgsNTEsMTQwLDE4MywxMTEsMjUyLDE1OSw5NCw3Niw1MywxMzAsNDEsMTcyLDExNCwxMTksMTMwLDE5NSwxMjQsNTYsMTIyLDIzMCwxMCwxMDgsMTEzLDEzNSwxNjcsMTU1LDQ3LDc3LDEwNyw2OSw4NCw3MCwxMDksMjUsMTYsMTE3LDE2Nyw3MiwxMzksMTMsMTAzLDE1NCwyNywyMTAsNjIsNDMsMCwxMjYsMTg0LDIwOCwxMDAsNDEsMjQwLDE4NSwxNjIsMjExLDU0LDU2LDIwNywxMTYsNDMsMTE3LDE4LDIxMCw0MiwyNTAsMjExLDI1MCwxMTIsMTM3LDExNywyNDcsMTUwLDExMyw0MywxNzMsMjI0LDE4MSw1NCwyNTEsNTIsMTMzLDEyMiw3NywzMSwxMzUsMjA3LDIwNSwyMzUsMjI0LDEzNCw1NCwxMzAsMjMzLDEwOCwyMDcsMTU1LDE1Myw5Nyw2Nyw3OSw5Miw0MCwyMDYsMjE2LDI0Myw4NywyMCwzMSwyMjEsNjUsODEsMjA2LDE3OSwyNTEsNDcsMzYsMTAsMTMxLDE3NSw1MF19fSwicGFyZW50X2hhc2giOnsidmVjIjpbXX0sInVubWVyZ2VkX2xlYXZlcyI6eyJsaXN0IjpbXX19fV0sImRlZmF1bHRfbGVhZiI6eyJub2RlIjpudWxsfSwiZGVmYXVsdF9wYXJlbnQiOnsibm9kZSI6bnVsbH19LCJ0cmVlX2hhc2giOlsyMTcsMTE3LDEyMSwxODYsMTEzLDEwOCwxNjAsOTEsMjUsMTY4LDI1Myw1OCwxODcsMjE0LDI0OCwxMTAsMTY3LDI1Myw0OSw5MCw1OCwxMDQsMTM4LDEsMjAsMTk5LDI1MSwyMjcsMTg5LDQ5LDIwMiwxODBdfQAAAAAAAABdAAAAAAAAZhJFcG9jaEtleVBhaXJzeyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fTIwAAFbeyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlszNCwyMTMsMTI2LDEwMiwxODAsMTk3LDcsMTE5LDUsMTQ4LDIzMSw1NywxNTYsODgsMTU0LDI3LDEwMSwxLDExNiwxMzIsNTQsNTgsODMsMTMwLDI0NywyNDcsNzMsMTc4LDQsMTQ0LDEyNiwyMTYsMTA1LDIzMiwxNDAsMTMsMjMyLDIyNiwxOCwyMzEsMTYzLDIwNywxNTgsMjMwLDEwNiw4MSw1LDQzLDExOCw4MiwyLDcwLDQwLDQsMjA2LDE4NywxNTgsMTk5LDExOCw4MCw1NiwyMjQsMzksMjE5LDExOCwxNTMsMzcsMjQ1LDI1LDQ2LDIyOSwxOTMsMTMzLDUyLDIwNCwxNTYsMzUsMjA0LDIzOSwxMjQsMTczLDIyMCw2NSwxNjQsMTk4LDE2NiwxNSwyMiwxMDQsMTkyLDY5LDIzNiwxNDgsMTA2LDE3OSwxNzUsMzksOTYsMTgsMjcsNTUsMTgxLDM3LDE2LDYwLDE5LDE3OCwxMTIsNzMsMjE3LDE4MywxNzksMTA4LDE0OSwyMzAsMTcyLDIwNSw5OCwxMjAsMTQyLDI1MywxNywxNzAsNjcsMTE3LDE5NiwxODIsMTcyLDEwMSwyMzYsMTcwLDEyMSwxODYsNTcsNzcsMzMsNjYsMzIsNzMsMTE3LDE3MiwxMjIsMTUwLDEzNCw4MCwyMjAsOTIsMjM5LDIyOSw5LDczLDIxOSwxNjgsMzMsMjUxLDMzLDk3LDE3MSwyMDQsNjYsMTQwLDYzLDM1LDE1MywxODksMjI5LDEwNiwxMzksMTAxLDE1NSwxODUsMTYwLDIxMCwzOCw1MCw0MSwxODMsNjksNTksMTYwLDQ5LDExMiwxNjgsMjM0LDEzNCw5LDIwMywzNiwxOTksMTYyLDEwOCw1MSwxNzQsMjQxLDEyNyw3MywxNzIsMTg3LDUxLDI2LDE1MiwxNzMsMTQ5LDgwLDg1LDU4LDE2NSwxODIsODcsMjAwLDIwNywxMjksMTI2LDEzNSwxMTksMTY1LDMwLDIzMSwxMzUsMjI0LDE5MywxNjgsMCwxNjEsMTM4LDYwLDI0OCwxMCwxODYsNDgsMzEsOTMsMTc4LDcsMTk3LDE5OSw1NCwyNDQsMTUwLDE1NywwLDEyMSw2OCwyNDAsMjQ3LDExMCwxNDksNjksMTEyLDE3NywxMCwxOTgsMjQ5LDEyMywxNTMsMTI4LDE5MiwyMywyNiw1Niw3NCwyMjcsMjE3LDk3LDEzMiwxODMsMTUsMjIsMTIwLDE3NSw5Myw0OCwxOCwxMTgsMTc4LDEzLDIzMiwxOTIsMTMxLDE1OCwxNzYsMTE3LDEyMCwxNjUsNTIsMjM5LDIyNiwxODUsMzYsMTE1LDEyMSwxNTksMTYwLDkyLDEyMSwxNDYsMTA2LDE3MSwxOTksOTMsMjMyLDIzLDM1LDI2LDMsNDAsNTIsNSwyMDYsMTgyLDIzMyw3NCwxODQsOTAsNiwyNDYsMTg3LDUsMTk1LDE4MCwxNyw5NSwyNDIsMTA5LDE2NCwzNywxNDYsODMsMTk3LDEsMzIsMTcxLDEyNiw4Myw4OSwxMDMsOSw3Niw5LDE3NCw2OSwxNjUsOTMsMTgzLDksNzMsMTYwLDExMSwxMjgsMTIsMTQ5LDE5OCwyNDgsMTE3LDAsMjgsMiwxNjcsMjM1LDIzLDczLDk3LDExNiwxMTMsMTUwLDE0NSwxNjksNywyMDUsMTEzLDE0Nyw3LDEzOCwxODAsOCw5NywxMDAsMSw5NSwxNjQsNTAsNTYsNjgsMTcyLDI0NCw2LDEzMCw4LDE5LDE3OCwxMTAsMjQxLDE1MiwxMzYsMTM0LDE5Niw1NiwyMDEsMTk2LDIwOCw1Myw4OSwxMzIsMjI2LDU0LDIyNCwyNiwyNywyMiwxNzgsOTIsNDYsNDIsNjksMTAwLDU2LDExOCw4MywzOCwxOTgsMCwzNyw4NiwxMTgsMjI1LDQsNDcsMzMsMjMsMjA4LDE5MiwxMDQsMTAsNzMsNzcsMjE3LDExNywxNCwxNjUsMjEzLDE1MywzOCw4MywxNTAsMTM3LDI0MywyMDAsMjM3LDE3LDM0LDEyOSwxNzgsODcsNTMsMTQ5LDE1MCwxMTcsMTM0LDE4Niw3NCwyMjYsMTQyLDEyMCwyMTEsMTAzLDc3LDE0OSw3NSwzMSwyMzAsMTU1LDE2MSwyMjgsNyw1OCwxNjcsMTQ2LDE3NCwxMzMsNiwxOTgsMjEsNTAsMTQ0LDI1MiwxNDMsMjQ4LDk4LDQsMTYsMTM5LDIwNCwxMyw0OSwxMTIsODIsMTgyLDYsOTIsMTY5LDcxLDgwLDY0LDQ1LDE0MSwzOSwxODEsMjA0LDEzMywxNjYsODUsNDEsMTQsMjAzLDIyOSw1MSwyNTQsMjIsMTUyLDYwLDE5OSw4Miw0MiwxODYsMzcsMzMsMjYsMSwyMzYsNzIsMTU1LDk5LDEyMSwxNTksMjIwLDIxLDE2NiwxMTIsNjcsNDQsNDMsMTYsMzYsMTU3LDE0MCw0NCw1NywxNDYsNTIsMTAsMTY2LDE0MywxNDUsMzIsODAsMTU0LDkyLDAsNjQsNDgsMTQwLDIyOCwxOTIsMTcxLDE3MywxNzAsMTk2LDQ3LDEwNCw3NSwxMDQsODAsNTAsMTU3LDEzNiwyOSwxMjcsMTM4LDQ1LDUsMjE0LDIyLDcxLDExNiw2MSwyMTcsMjgsMjcsOTQsMTI5LDYsMTIsNzYsMTQ2LDkwLDU5LDEyNCw0NiwxNjYsODYsMjA4LDgzLDE5NSwyNDgsMTE1LDU4LDE1OCwyMTYsMTM4LDIyNywxMTQsNywyNDQsMzksMjYsMjcsMjMyLDM1LDk2LDIzLDgsMjAsMTUwLDg4LDEwMiwxMTgsMTczLDEwMywxNTEsMTI4LDMyLDEzMiw0NiwxMzksNjQsMTY4LDI0MCwxNDQsNDYsNCwyMSw2NywzLDIyOSwxNTQsNjgsMTE5LDcwLDEwOCwyNDgsMTc4LDIwNSwxODUsMSwzNCw2Nyw0MiwxNDksODEsMTUzLDEzNSwxMzksOTksMjM1LDE4MSwxLDE2OSwyNiwyMiwyMDYsNTAsMTYsNTUsMTYsOSwxLDAsMTk1LDIzNCwxODUsMjAzLDI0MCwxOTcsMTAxLDI0MywyNDQsOTcsMTc4LDIwMiwxODcsNDAsMzksMTQ0LDEyMiw3LDYxLDE2OCwxLDE2NSwxNzYsNzQsMTI0LDE2OSwxMzksMTYxLDE0NywyMTgsMTgzLDExMywyMCwyNiwxMjAsMTAsMTA5LDEyMiw4MSwxMDEsMTIzLDExNCwxNDIsOTYsMTU0LDE3NCwyMjgsNzEsNzEsMjQ2LDIzMCwxMTgsNzUsMTM0LDIwNiw4OSwyMDEsMTEwLDIzNywxNzksNDAsMTYzLDIwOSwxODcsOTksMCw1NCwxMDMsMTYxLDcyLDIyMCwxMiwyMDYsMTM1LDExNCwxMTQsMTYzLDM2LDE1OCwxMjEsMTAxLDU0LDE1MiwzMiwxNjcsMTE4LDExLDE5NCw4Nyw4NywyMDIsNTIsMjA4LDIwMCwyNTQsMjQ1LDEzLDIwMCwyMiwxNDksMjA1LDEwNSwxMjEsODAsMTY3LDM1LDEwOCwxMzIsMTUxLDIwOSwxNzAsNjIsMTczLDE0OSwxODIsNzksMjMsMTYsMSw2OSwxMDksMTYsMjUyLDE2NCw2OSwyNDIsMTkxLDI4LDUsMTI0LDMwLDE2MiwxNjEsNzAsMzIsMTk5LDEwNiw2OSwyMDMsOTcsMjUwLDgzLDIyNCwyMDQsMTkxLDE5MSwyMzUsOTksMTI4LDU3LDE1MCwxMzcsNTcsMTQ2LDEwMywxNTMsMTQzLDcxLDU1LDEwMSwyMDQsMTU1LDc3LDUsMjI2LDI0LDQ4LDEzNCw2NSwxMywyMDksMTgxLDQwLDgzLDEwNiwyMzQsMTE5LDY5LDUwLDE2MywzOCw3MiwyMDIsODksMTY4LDgsMjAzLDIyMiwyNDcsMTE3LDEzMiwxMjMsMTE1LDExNiwyNDUsMTg5LDM1LDE2OSwxNDIsMTIzLDQsMTI4LDQxLDIyNywxMTYsMTQwLDEwMiwxMSwyNDIsMjI2LDE4Miw0Myw1MiwyMDEsMTM2LDE3OCwxODQsMTU3LDQsMTMxLDk1LDUzLDcyLDIxMiwxODEsOTQsMjM4LDIxNiw2OCwyMDMsMzYsMTE1LDM4LDIwOCwxOTgsMjE5LDE0OCwxNjMsNzEsMTMwLDExMSwzNywxMzIsMTk0LDk2LDkwLDEwOCwxNzMsMCwxNjQsMTYwLDI0MCwxNjgsNiwxOTYsMzMsMTIyLDE5NSw3LDEyNSwyMTEsOTcsMTY4LDIzMCwxNzcsOTksMTcyLDEzOSw5OSwxMjksODEsMjU0LDE1NCwxNDYsNCwxNjMsMTE3LDQwLDcsMTIxLDE1MCwxMDcsMjQsMTczLDIwOSw2Nyw2NCwxNzIsMTg1LDYwLDI2LDE5NCwxMjIsMTg1LDE3Myw0OCwyMzYsMTEwLDI1MywxNzgsMTUsMjUzLDIzMiw1MCwxODksMTUxLDE0MCwyNDIsMTc4LDEyMCwyMzcsNzYsMTc5LDIxOSwxMzksNjUsMTk4LDU5LDYsMTg3LDIzMywxNjIsNDYsMTk3LDkzLDYsMTgwLDksMjE2LDI1MiwxOTUsMjQwLDI0LDE2OSwxNTQsMTk2LDE2MywxMjMsMjMzLDQ5LDM1LDEyMywxMCwyMTEsNCwxOTMsNTMsMTk1LDQ1LDU3LDE1NSw3Miw5MSw0MSwxNDMsODQsMjUsMiwxMjgsMTM1LDE4NSwxNTYsNTAsMTk1LDIzMyw4NiwxNjAsMTE2LDg1LDE4LDcyLDE2MiwzOSwxOTcsMjQwLDE3NSw2NSwxNTUsMTI5LDQyLDIxMiw5LDYxLDMsNTEsNzUsMTc2LDU1LDEzOCwxMzUsMjA3LDIzLDEyOCwxMzIsMTY2LDIxMiwxNTcsOTAsMTE2LDk3LDEwNCwyNDMsMTAsMTUwLDE0MCwxNjIsMTY2LDg1LDEzNCwxNTcsMzIsMTIzLDI1MSwxMDMsMTgzLDIxNCwxNzAsMTQsMjQ4LDcyLDE4OSw1MiwxNjQsMTg1LDY4LDE0NCwxMDIsNywzNywxMDYsNTIsMjIwLDE1NSwyMTAsODIsMjA3LDc3LDI0NSwxOTcsMjUsNTIsMjcsMjA5LDI1MSwyMSwxOSwxOTUsNjIsODksMTM4LDMwLDgzLDQsMTQ0LDc4LDI0Myw4NCwxMjksMjMyLDEsMjE3LDE0OCwxMDEsMTY4LDEwMiw4Nyw5OSw2NiwxNzIsOCwyLDE1NSwyMTUsMTA4LDUzLDEyOSw4MywzNCwxOTcsMTI4LDMwLDIyMCw5NiwxMjMsODMsMzcsMzcsMTQ4LDY2LDE0OSwxOTEsNTMsNTYsMTYwLDExMiwxNzcsMzgsMjE5LDIyOCwxNSwxNDUsMTQ0LDE4Nyw2MSw0NCwxMjEsMjcsMjI3LDQyLDMxLDUsMTk4LDE2NiwxNjMsMjE2LDE4NSwxNTUsMTk1LDE2OCwxMDgsMTY0LDIzMCwyNCwzNiw1Nyw2NCwxMTUsMTMyLDEwNSwyNDAsMjEsODIsMjMsMyw4MywyMTMsMjA3LDIzMiw2MF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzExNSwxLDIsOTUsMjQ1LDU4LDYxLDE5NywyMDIsNjgsMjQ0LDE4OSwxMjgsMTgyLDEyOSwyMSwxODIsMTIsOTIsMjI4LDYyLDIzNCwxNzYsMTg4LDYxLDIxMCwxNDAsMjIxLDE0Niw2NiwxOTUsNzIsMjUsMjE3LDE4NiwxMTYsNTQsMTA4LDEzNCwxNTYsMTIwLDE3NiwyNCwxMTMsOTksMTAwLDk5LDIwNCwyNDUsMTE3LDE2OSw2Myw0NCwxNzQsMTUxLDIzMCw1OCwxNzQsMTA4LDIwMCwxNjcsMTgwLDYzLDIwLDk4LDEwNiwyMzQsMTcyLDEwMywxODYsMjQ2LDE2MCwyMDMsMjQ1LDEzMiwxMjksMTgyLDExNSwxNDMsMTksMTE1LDI0MCw5OSwxOTYsMjMzLDI1MCwxOTQsMjE3LDE2Nyw5NCwyMjgsNDMsOCwyMDUsMjQxLDE1NSwxNTAsNTgsMjAsMTQ2LDE5LDUyLDcxLDExOCw1LDE3MywxMiwxMjYsNjMsMzksMTgyLDI5LDU5LDE2OSwxNTUsMjE1LDIwLDQyLDI0NywxMzAsMTA2LDUyLDE3MSw5MCwyNDAsMTEzLDE2OSwxMzIsMTA2LDI0Myw0MSwxMzIsOTksMTY4LDM0LDEwMSw0OSw0Miw3MiwyMDMsOTAsMjQ0LDM5LDE1MywyMzUsMTEsMjMsMjEyLDQyLDE5NywyMTIsMTE0LDE3MSwxOTEsODIsODEsMjAwLDI0NiwxODQsMTksMjAsMTE5LDIyNiw2NiwxNzAsMjE2LDU3LDE2NSwzMSwxMjksNTAsMjAsODAsMTU2LDEwOSwxMjQsMTk3LDE1NiwxNjAsMTQwLDE2NSwxNjgsMTI0LDAsNzYsNTUsNjQsMjEsNTcsMjA4LDEwMSwxMzIsMjI0LDM2LDEwLDIyNCw0NCwxNjQsMjA2LDIyNywxMTgsMTI2LDEsMzgsMTM3LDE2NSwxMDYsMTQ1LDYsOTIsNTUsMTU0LDE4Nyw3NiwyMjksNiwyMDUsMTg3LDEwNCwyMTksMTEsMTI4LDIwNyw1MywxMDksNzEsMTM1LDk3LDI1MSw0MCw0OCwxNywxMjMsMTQ3LDExMCwxNjksMTI0LDIzNSwzMiwxNTUsMTk2LDEwNSw0NSwxOTcsMTA2LDE1NiwxMiwxMzAsMTMyLDI0NCw4NywxNyw1OSw2OCwxNywyMDQsMTM5LDU2LDY3LDgsMjA1LDcxLDY3LDEsMTA2LDIwMSwxMSwxMTcsODAsNzQsMTU5LDE1NSwxMzMsMTExLDExNywzNCwyNCw4NCw5MCw3NCwxNjUsMTM0LDQsODMsMTUxLDE1MCwxNzksMTAyLDEyMyw5NywxMTUsOTYsMjAyLDE2MCwyMTQsMjcsMjgsMTY3LDEwLDMwLDIzNCwxNTEsMTA5LDExMCw4MiwxODgsMjM3LDE3OCwxMjcsODgsMTMwLDEyOSwxODMsMjMwLDUxLDI1Myw4LDk2LDE4MSwyNTEsOTQsMTkwLDIwLDQ3LDQsMTAzLDExMSwxNjcsMTYsMTE4LDU3LDE2NiwxLDM5LDIyNSw4MSwxMDksOTcsMTg3LDE5MCwxODIsMTY2LDE1NCw3MiwxNDEsMTUsMjMwLDIwNSwyNDMsMTk2LDIwNiw0MiwzMywxOTAsMTM4LDExNCwxMjcsMTgwLDE5LDk0LDg5LDgsMTQ1LDE2NCwyNDMsMjAxLDY3LDIwNCwxNDcsODEsNzEsMjA3LDIyNywxNzEsMTU1LDU4LDExLDIxLDEyMywxOTQsMzgsMTc3LDE3MCwxODMsMjQ3LDEzNiw4LDEyMywxMzksMTQ5LDYsMTYyLDg3LDE5MywxNTAsMTk5LDE4OSwxNzYsMTQyLDI1NCwyNDUsNjEsODEsMTM3LDQyLDE1LDIsMjUsMTE1LDIwMiwxMTYsNjQsMTAyLDMzLDE4MSwxNjgsNTIsMTg4LDE3MSwxMzksNjYsMjA4LDE0NCwxNTIsMTAxLDkyLDEwMiw1NSwxMDQsMjIxLDEwNywxODEsNDAsMTg1LDgwLDE0MCwyNDYsMTY0LDIwNiw5Miw2MSwxNjQsMzUsOTMsMTQxLDE3NywxMDcsMjI0LDEzOSw3LDIxMSwxMzgsMTk3LDI4LDg3LDE0Nyw1MCwxMTYsMTQ1LDIxMCw4MCwxOCwxMDEsMTE0LDE1MywxNDMsMTc4LDEwOCwxNTYsMTUwLDE3MSwxMzAsMTM3LDE5MiwzNCw4NSw1OCwyNDMsNTEsMTk2LDI0NSwxMDMsMjAzLDE2NywyMTcsMTkyLDM3LDM0LDE0NCwxOSwxMSw1NCwyNDQsNTEsMTUzLDIxMiwyNCwxMTYsMTQyLDEwNCw5OSwyMDcsNDIsMTYwLDQ5LDEyMiw1NSwyNTAsNjYsMTA2LDk2LDIxNSw5NSwzMSwxNTEsMzUsODAsMTk0LDEzMCwxOCwxODQsMzgsMTkxLDgsMTgxLDIxMSwxODEsNzIsMTU3LDE2LDE3NCw1LDU4LDEyLDIyNywxOTIsMTg3LDExNCwxMSwxNCw4NSwxMjEsMTg4LDEwLDEwMywxNjAsMTA0LDcxLDEwNywyMTEsNjgsMTEyLDQ4LDExMiwxMzQsMTAwLDg0LDEzMyw5OCw5LDk4LDI1MSw1MSw3OSwyMDEsNTQsMTIxLDI0Myw3NiwwLDI1MiwyNDIsMTc3LDEzLDk2LDEsODgsMTM0LDQwLDI0NywxNiwxMTcsMjIxLDUzLDExMywyNDMsMjQxLDI3LDE4MCwxMzQsMzIsMjM3LDIyLDE2NCwyNTEsMTUwLDQ2LDE1NCwxMjEsMTMxLDg0LDY0LDExMCwxODYsMTU0LDQ2LDIxNiwzNSwxMDgsMTEwLDE5NywxODAsMTQ1LDE0NSwxMSw5NiwyNywxNDAsMTMyLDIxMCw3NywxMzAsMjE3LDE0MywxNCwyMDQsOTQsNjcsMTI0LDEwNyw1OCwxLDMzLDk0LDUzLDE0MSwxNiwyMzMsOTksMTkzLDEzNiw2NCw2MCwxMTksMTYxLDQ2LDExNywxODUsNTMsMTgsMTQ1LDI0MiwxMSw4MywxOTgsMjAzLDEzMyw4OCwyMjgsMTM3LDQ3LDIzMSwxNTcsMTUsMTY4LDE5OCwyLDY0LDE4NCw0NSwyNDUsMTcwLDI5LDIwMiwxMjEsNjMsMjAxLDE3MSwyMTgsNjgsMzksMTA3LDE5OSwyMDQsODEsMjUxLDQ1LDc2LDgwLDIwMiwxODcsNTIsMjEsNzMsMTE1LDE2Nyw0NSw1NSwxMzYsMTM0LDU1LDY0LDYzLDExLDk4LDE1LDE2OSwxODQsMjE0LDY0LDIwNSw2MywyNTIsMTAsMTMsMjI4LDIwNCwyMDksNjYsMTM4LDkxLDE5NCwxMDAsMjU0LDM4LDE0MSw0MywyMzAsODgsMzMsMTIsMTc4LDk2LDE5NSwyMCw4NSwxMDUsMjA0LDgxLDE0NSw1Niw0NSwyMzYsMTgwLDE2OCwxNTUsMTU3LDk0LDIxMiw2OSw5NywxMjksOTcsMjA1LDk2LDgxLDIyMSwxNTEsMjAzLDIxNCw5LDIwMiwxMzMsMTkyLDkxLDQ3LDExNiw2MSw5NSw2NCwyMDEsMjM2LDIyOCwxMzQsMjMwLDIyNiwxOTcsMTE5LDcyLDE0NSwyMjUsNDQsNjAsMzQsMTQ5LDEyNiwxNDQsMTc4LDYxLDEwMSw5MCw1NCw2MiwxMjksOTcsMjI0LDUxLDE5Myw3MSw4OCwxNywwLDQwLDE1NiwyNTMsNDAsNTgsNjcsNTMsMTcsMjAwLDIyOSw0MCwyMzcsMjI4LDE2Miw0NiwyNTEsMTgxLDIxMyw2MCwxNywxOTYsMjQzLDE2MiwyMTIsMTg1LDI0LDIzOSwxNjAsMjAxLDQxLDM1LDk3LDEzOCwyMiwxNTYsOTEsNjAsMywzMywxMTcsMjcsMjE4LDEzOCwxMzgsMjU1LDI1LDEyNSwxNzUsMTkzLDQ1LDEyNyw5LDEyOCwyNTQsMjQ4LDg1LDY2LDE4OCw0MCwxMjcsMjA4LDM3LDE0NywxODgsNDcsNjgsMjE3LDgsNCwyMDQsMTQsMTMzLDEzNCw5Myw4OSwyNTAsOTgsMTA0LDQ4LDQyLDE3NiwxNTAsMTgsMjUyLDE4Myw1NywxNDYsMTY3LDE0NSwxNjAsMTgxLDE5MCwyMSwzOCwyMSwxNDQsMzMsODUsODQsMjQ1LDg2LDgsMjE1LDEyMywzNiw5LDExOSwyMjYsNDgsOTIsMTE3LDMsMTYyLDQ4LDEzNiwxNjEsMTAxLDE5NiwyMiwzOCw1OSw3MCwzMSwyNDYsMTY4LDEwNiw5OSwxNTcsMTU2LDI0OSwxOTIsMTcxLDI1MiwxNjUsMjA0LDE0MCwxNjUsMTk5LDExOSw0OCwxMzIsMTI0LDEzNCwyMzMsMjE5LDE0NCw1MywxNTQsOTMsMTUzLDQsMTgyLDE0NCw3NCw0NywyMjksMjEzLDE5NCwxMDEsNDQsODAsMTkzLDE4OCwxNDcsMTQ0LDExNiwxMDEsMjIxLDE5NCwxNDgsMjQwLDI1MSwxOTksNCwxMTQsNjEsNzksMTIyLDkxLDE3MSwxOTgsMTg3LDEyMCwyMzIsMiwxMDIsMjIwLDEyLDE2Niw1NCwxMSwxOTEsNDksNjMsMjE4LDE3MSwxNTcsMTgsNTEsMTE4LDk0LDU3LDE0MCw4MCwzNywxMzQsNjAsOTksMTEsMTc3LDUxLDEyMSwxMTUsMTg1LDE5Miw4OCw1MywxMTUsMjMzLDE5NSw3Myw3NSwyMTgsMTQyLDIsNDIsNTMsMTM4LDM0LDg4LDE3NiwxMDAsNTIsMTMxLDY5LDE4NSw5MSwxNTYsMTYyLDI0NiwyNywxOTMsMTQsMjMzLDMwLDIwNiwxNjEsNDQsNjgsMTksNzEsMTc1LDE1MiwxNDAsNTcsMTgyLDE3LDYwLDEyMSw4NiwxMDAsMjQ1LDMzLDE3NCwxMTIsMTI0LDE4MSwyNywxNDYsMTM3LDIyOCwxMDcsMTE1LDg3LDE0NywxODUsMTE0LDY3LDk3LDEzNCwxMjIsMTE2LDUyLDUxLDIxNCwxNjUsMjAyLDYyLDAsMTYyLDMxLDIwMywyMSwxNSwxMiw4MSwxNzQsMjI5LDE4OCwxOTcsMTk3LDExMCw1NywyNDEsMTI2LDI0Miw5OCw3MSwxODMsNzIsODQsNDMsMTcsMTA1LDg0LDg1LDQ2LDExMSwyNDQsNCwzNywxNTEsNSwyMDYsMTk3LDE4LDExMyw3MSw2MCwxMjIsMjAwLDE1NiwyMjcsMTMwLDE5OCwyMzYsMTc4LDExNiwzMiw1MSw0MywxMjAsOCwxMSwxMjcsNTksMTA0LDczLDE5NSwxNzgsMzQsMjEzLDEyNiwxMDIsMTgwLDE5Nyw3LDExOSw1LDE0OCwyMzEsNTcsMTU2LDg4LDE1NCwyNywxMDEsMSwxMTYsMTMyLDU0LDU4LDgzLDEzMCwyNDcsMjQ3LDczLDE3OCw0LDE0NCwxMjYsMjE2LDEwNSwyMzIsMTQwLDEzLDIzMiwyMjYsMTgsMjMxLDE2MywyMDcsMTU4LDIzMCwxMDYsODEsNSw0MywxMTgsODIsMiw3MCw0MCw0LDIwNiwxODcsMTU4LDE5OSwxMTgsODAsNTYsMjI0LDM5LDIxOSwxMTgsMTUzLDM3LDI0NSwyNSw0NiwyMjksMTkzLDEzMyw1MiwyMDQsMTU2LDM1LDIwNCwyMzksMTI0LDE3MywyMjAsNjUsMTY0LDE5OCwxNjYsMTUsMjIsMTA0LDE5Miw2OSwyMzYsMTQ4LDEwNiwxNzksMTc1LDM5LDk2LDE4LDI3LDU1LDE4MSwzNywxNiw2MCwxOSwxNzgsMTEyLDczLDIxNywxODMsMTc5LDEwOCwxNDksMjMwLDE3MiwyMDUsOTgsMTIwLDE0MiwyNTMsMTcsMTcwLDY3LDExNywxOTYsMTgyLDE3MiwxMDEsMjM2LDE3MCwxMjEsMTg2LDU3LDc3LDMzLDY2LDMyLDczLDExNywxNzIsMTIyLDE1MCwxMzQsODAsMjIwLDkyLDIzOSwyMjksOSw3MywyMTksMTY4LDMzLDI1MSwzMyw5NywxNzEsMjA0LDY2LDE0MCw2MywzNSwxNTMsMTg5LDIyOSwxMDYsMTM5LDEwMSwxNTUsMTg1LDE2MCwyMTAsMzgsNTAsNDEsMTgzLDY5LDU5LDE2MCw0OSwxMTIsMTY4LDIzNCwxMzQsOSwyMDMsMzYsMTk5LDE2MiwxMDgsNTEsMTc0LDI0MSwxMjcsNzMsMTcyLDE4Nyw1MSwyNiwxNTIsMTczLDE0OSw4MCw4NSw1OCwxNjUsMTgyLDg3LDIwMCwyMDcsMTI5LDEyNiwxMzUsMTE5LDE2NSwzMCwyMzEsMTM1LDIyNCwxOTMsMTY4LDAsMTYxLDEzOCw2MCwyNDgsMTAsMTg2LDQ4LDMxLDkzLDE3OCw3LDE5NywxOTksNTQsMjQ0LDE1MCwxNTcsMCwxMjEsNjgsMjQwLDI0NywxMTAsMTQ5LDY5LDExMiwxNzcsMTAsMTk4LDI0OSwxMjMsMTUzLDEyOCwxOTIsMjMsMjYsNTYsNzQsMjI3LDIxNyw5NywxMzIsMTgzLDE1LDIyLDEyMCwxNzUsOTMsNDgsMTgsMTE4LDE3OCwxMywyMzIsMTkyLDEzMSwxNTgsMTc2LDExNywxMjAsMTY1LDUyLDIzOSwyMjYsMTg1LDM2LDExNSwxMjEsMTU5LDE2MCw5MiwxMjEsMTQ2LDEwNiwxNzEsMTk5LDkzLDIzMiwyMywzNSwyNiwzLDQwLDUyLDUsMjA2LDE4MiwyMzMsNzQsMTg0LDkwLDYsMjQ2LDE4Nyw1LDE5NSwxODAsMTcsOTUsMjQyLDEwOSwxNjQsMzcsMTQ2LDgzLDE5NywxLDMyLDE3MSwxMjYsODMsODksMTAzLDksNzYsOSwxNzQsNjksMTY1LDkzLDE4Myw5LDczLDE2MCwxMTEsMTI4LDEyLDE0OSwxOTgsMjQ4LDExNywwLDI4LDIsMTY3LDIzNSwyMyw3Myw5NywxMTYsMTEzLDE1MCwxNDUsMTY5LDcsMjA1LDExMywxNDcsNywxMzgsMTgwLDgsOTcsMTAwLDEsOTUsMTY0LDUwLDU2LDY4LDE3MiwyNDQsNiwxMzAsOCwxOSwxNzgsMTEwLDI0MSwxNTIsMTM2LDEzNCwxOTYsNTYsMjAxLDE5NiwyMDgsNTMsODksMTMyLDIyNiw1NCwyMjQsMjYsMjcsMjIsMTc4LDkyLDQ2LDQyLDY5LDEwMCw1NiwxMTgsODMsMzgsMTk4LDAsMzcsODYsMTE4LDIyNSw0LDQ3LDMzLDIzLDIwOCwxOTIsMTA0LDEwLDczLDc3LDIxNywxMTcsMTQsMTY1LDIxMywxNTMsMzgsODMsMTUwLDEzNywyNDMsMjAwLDIzNywxNywzNCwxMjksMTc4LDg3LDUzLDE0OSwxNTAsMTE3LDEzNCwxODYsNzQsMjI2LDE0MiwxMjAsMjExLDEwMyw3NywxNDksNzUsMzEsMjMwLDE1NSwxNjEsMjI4LDcsNTgsMTY3LDE0NiwxNzQsMTMzLDYsMTk4LDIxLDUwLDE0NCwyNTIsMTQzLDI0OCw5OCw0LDE2LDEzOSwyMDQsMTMsNDksMTEyLDgyLDE4Miw2LDkyLDE2OSw3MSw4MCw2NCw0NSwxNDEsMzksMTgxLDIwNCwxMzMsMTY2LDg1LDQxLDE0LDIwMywyMjksNTEsMjU0LDIyLDE1Miw2MCwxOTksODIsNDIsMTg2LDM3LDMzLDI2LDEsMjM2LDcyLDE1NSw5OSwxMjEsMTU5LDIyMCwyMSwxNjYsMTEyLDY3LDQ0LDQzLDE2LDM2LDE1NywxNDAsNDQsNTcsMTQ2LDUyLDEwLDE2NiwxNDMsMTQ1LDMyLDgwLDE1NCw5MiwwLDY0LDQ4LDE0MCwyMjgsMTkyLDE3MSwxNzMsMTcwLDE5Niw0NywxMDQsNzUsMTA0LDgwLDUwLDE1NywxMzYsMjksMTI3LDEzOCw0NSw1LDIxNCwyMiw3MSwxMTYsNjEsMjE3LDI4LDI3LDk0LDEyOSw2LDEyLDc2LDE0Niw5MCw1OSwxMjQsNDYsMTY2LDg2LDIwOCw4MywxOTUsMjQ4LDExNSw1OCwxNTgsMjE2LDEzOCwyMjcsMTE0LDcsMjQ0LDM5LDI2LDI3LDIzMiwzNSw5NiwyMyw4LDIwLDE1MCw4OCwxMDIsMTE4LDE3MywxMDMsMTUxLDEyOCwzMiwxMzIsNDYsMTM5LDY0LDE2OCwyNDAsMTQ0LDQ2LDQsMjEsNjcsMywyMjksMTU0LDY4LDExOSw3MCwxMDgsMjQ4LDE3OCwyMDUsMTg1LDEsMzQsNjcsNDIsMTQ5LDgxLDE1MywxMzUsMTM5LDk5LDIzNSwxODEsMSwxNjksMjYsMjIsMjA2LDUwLDE2LDU1LDE2LDksMSwwLDE5NSwyMzQsMTg1LDIwMywyNDAsMTk3LDEwMSwyNDMsMjQ0LDk3LDE3OCwyMDIsMTg3LDQwLDM5LDE0NCwxMjIsNyw2MSwxNjgsMSwxNjUsMTc2LDc0LDEyNCwxNjksMTM5LDE2MSwxNDcsMjE4LDE4MywxMTMsMjAsMjYsMTIwLDEwLDEwOSwxMjIsODEsMTAxLDEyMywxMTQsMTQyLDk2LDE1NCwxNzQsMjI4LDcxLDcxLDI0NiwyMzAsMTE4LDc1LDEzNCwyMDYsODksMjAxLDExMCwyMzcsMTc5LDQwLDE2MywyMDksMTg3LDk5LDAsNTQsMTAzLDE2MSw3MiwyMjAsMTIsMjA2LDEzNSwxMTQsMTE0LDE2MywzNiwxNTgsMTIxLDEwMSw1NCwxNTIsMzIsMTY3LDExOCwxMSwxOTQsODcsODcsMjAyLDUyLDIwOCwyMDAsMjU0LDI0NSwxMywyMDAsMjIsMTQ5LDIwNSwxMDUsMTIxLDgwLDE2NywzNSwxMDgsMTMyLDE1MSwyMDksMTcwLDYyLDE3MywxNDksMTgyLDc5LDIzLDE2LDEsNjksMTA5LDE2LDI1MiwxNjQsNjksMjQyLDE5MSwyOCw1LDEyNCwzMCwxNjIsMTYxLDcwLDMyLDE5OSwxMDYsNjksMjAzLDk3LDI1MCw4MywyMjQsMjA0LDE5MSwxOTEsMjM1LDk5LDEyOCw1NywxNTAsMTM3LDU3LDE0NiwxMDMsMTUzLDE0Myw3MSw1NSwxMDEsMjA0LDE1NSw3Nyw1LDIyNiwyNCw0OCwxMzQsNjUsMTMsMjA5LDE4MSw0MCw4MywxMDYsMjM0LDExOSw2OSw1MCwxNjMsMzgsNzIsMjAyLDg5LDE2OCw4LDIwMywyMjIsMjQ3LDExNywxMzIsMTIzLDExNSwxMTYsMjQ1LDE4OSwzNSwxNjksMTQyLDEyMyw0LDEyOCw0MSwyMjcsMTE2LDE0MCwxMDIsMTEsMjQyLDIyNiwxODIsNDMsNTIsMjAxLDEzNiwxNzgsMTg0LDE1Nyw0LDEzMSw5NSw1Myw3MiwyMTIsMTgxLDk0LDIzOCwyMTYsNjgsMjAzLDM2LDExNSwzOCwyMDgsMTk4LDIxOSwxNDgsMTYzLDcxLDEzMCwxMTEsMzcsMTMyLDE5NCw5Niw5MCwxMDgsMTczLDAsMTY0LDE2MCwyNDAsMTY4LDYsMTk2LDMzLDEyMiwxOTUsNywxMjUsMjExLDk3LDE2OCwyMzAsMTc3LDk5LDE3MiwxMzksOTksMTI5LDgxLDI1NCwxNTQsMTQ2LDQsMTYzLDExNyw0MCw3LDEyMSwxNTAsMTA3LDI0LDE3MywyMDksNjcsNjQsMTcyLDE4NSw2MCwyNiwxOTQsMTIyLDE4NSwxNzMsNDgsMjM2LDExMCwyNTMsMTc4LDE1LDI1MywyMzIsNTAsMTg5LDE1MSwxNDAsMjQyLDE3OCwxMjAsMjM3LDc2LDE3OSwyMTksMTM5LDY1LDE5OCw1OSw2LDE4NywyMzMsMTYyLDQ2LDE5Nyw5Myw2LDE4MCw5LDIxNiwyNTIsMTk1LDI0MCwyNCwxNjksMTU0LDE5NiwxNjMsMTIzLDIzMyw0OSwzNSwxMjMsMTAsMjExLDQsMTkzLDUzLDE5NSw0NSw1NywxNTUsNzIsOTEsNDEsMTQzLDg0LDI1LDIsMTI4LDEzNSwxODUsMTU2LDUwLDE5NSwyMzMsODYsMTYwLDExNiw4NSwxOCw3MiwxNjIsMzksMTk3LDI0MCwxNzUsNjUsMTU1LDEyOSw0MiwyMTIsOSw2MSwzLDUxLDc1LDE3Niw1NSwxMzgsMTM1LDIwNywyMywxMjgsMTMyLDE2NiwyMTIsMTU3LDkwLDExNiw5NywxMDQsMjQzLDEwLDE1MCwxNDAsMTYyLDE2Niw4NSwxMzQsMTU3LDMyLDEyMywyNTEsMTAzLDE4MywyMTQsMTcwLDE0LDI0OCw3MiwxODksNTIsMTY0LDE4NSw2OCwxNDQsMTAyLDcsMzcsMTA2LDUyLDIyMCwxNTUsMjEwLDgyLDIwNyw3NywyNDUsMTk3LDI1LDUyLDI3LDIwOSwyNTEsMjEsMTksMTk1LDYyLDg5LDEzOCwzMCw4Myw0LDE0NCw3OCwyNDMsODQsMTI5LDIzMiwxLDIxNywxNDgsMTAxLDE2OCwxMDIsODcsOTksNjYsMTcyLDgsMiwxNTUsMjE1LDEwOCw1MywxMjksODMsMzQsMTk3LDEyOCwzMCwyMjAsOTYsMTIzLDgzLDM3LDM3LDE0OCw2NiwxNDksMTkxLDUzLDU2LDE2MCwxMTIsMTc3LDM4LDIxOSwyMjgsMTUsMTQ1LDE0NCwxODcsNjEsNDQsMTIxLDI3LDEyMCwyMTIsOTcsNzksMTYxLDE3OCw3Miw4MiwxOTEsNTksMTMwLDExMCwyMzYsMjM0LDE1NCw0OSwxMjAsNTAsMjMsMzIsMTQ4LDExMywxNjksNDYsMjEsMTEwLDExLDEsMTkwLDExLDI1NSwyNCwyMzIsMjUsMTkyLDE2Niw2NywxLDU5LDIyLDEwOCw1MiwyMjEsMjE3LDE5NCwzOSwxMjMsMTYzLDQxLDEwNywyMDgsNjAsMjUwLDY0LDU1LDE5MSwyMzgsMjI4LDEwNyw2Nyw1MCw2NiwxMzcsMTEyLDczLDExNSw4MiwyMTQsOTgsMjUwLDIwNCwxNTAsMjQyLDc5LDIyMSwxODEsMjQ1LDM2LDE1MSwyNSwyMTgsOTQsMjQ1LDE4MiwyNTMsMTcwLDgsMTE2LDEyNiwxODcsNjIsMjU0LDE1NSwzNywxMjcsNjAsMjI3LDQyLDMxLDUsMTk4LDE2NiwxNjMsMjE2LDE4NSwxNTUsMTk1LDE2OCwxMDgsMTY0LDIzMCwyNCwzNiw1Nyw2NCwxMTUsMTMyLDEwNSwyNDAsMjEsODIsMjMsMyw4MywyMTMsMjA3LDIzMiw2MF19fX0seyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsyMzcsMTQ3LDE0NSwyNTEsOTgsMTUsOSwxMiwxODcsMTAsMTkyLDE1NywxMTQsMjMsNjAsMSwyNDAsOTUsMjA1LDE4OCw1NCwyNDAsMzMsMTYwLDEyNSwyMjQsMTk2LDEzOSwxNjAsODQsMjE3LDE2NiwxMDUsMTE5LDExNiw5NiwyMDgsMTY3LDE4NiwxNjgsNDMsMTkyLDIxMywyMzAsMTQsMjksMTAsNTEsNjAsMTQ1LDE4OCw0NiwxMjEsMTk4LDMwLDU4LDE2Myw0LDI0NywxMTAsNzIsMTgyLDg2LDg2LDU4LDE1OSwxOTcsNTEsMTU3LDEwNSwzNCwxMzQsMTgwLDI4LDE3MywxOTQsMTMyLDEwNiwyNDcsMTMxLDE5Nyw2OCw4MywxMDAsMjQ0LDI0NCw1MCwzOCwyMTAsMjksNjgsMjUyLDM2LDcwLDI0OSw2NSw1MSwyMTIsODIsMTg1LDkwLDE1MiwxMzQsMzksMTMxLDQ5LDE4OCw2LDEwNSw5OCwxODMsNzIsMTA1LDkyLDIzNiw2NCwxMzgsNDAsMTksMjksMTUsNjAsOTgsODEsMTE3LDY1LDg3LDE1NiwxMCwxOTgsMTg2LDEzNiwxNzksMjQ0LDE3Niw0NSwyNiw1NSwyMzIsMTk0LDE4Nyw5NSw4MiwxNDUsNSwxMDAsNzEsMjA2LDEzMCwxOTgsMTA0LDIzNCwxODksMiwyMjUsMjEsMTI3LDM2LDE0LDEyLDE2Nyw1LDY1LDUwLDE1Miw0OCwxNDAsNTgsMzYsMjE4LDEyNCwxOTgsNiwyMDYsMTY5LDEwMiwxMDYsMTc2LDU2LDE5MiwxNzcsMTQ1LDE3Myw2MywzOCwxMTUsMTcsMjQ5LDE0LDE5NywyMTEsMTg2LDkyLDEwLDE2OCw1NSw2NSwxMDksMjE2LDIwMSw1NCwyLDEyMiwxMSwzMyw0LDE3Nyw1MSw5MCw5OCw3NSwxMTIsMTI0LDUyLDQyLDQxLDEyNywzNiwxNTIsNzMsOCwxMjAsNzksMTgzLDY5LDE3OCwyMjAsMTUyLDIxMSw3NiwxNjUsNDMsOTEsNjIsMTA0LDEzMiw3MSw0MCwzOCwxNDksMjQzLDEyMCwyMDcsMjIyLDEzNSwxNzgsMTk1LDEzMiwxNDQsNDUsMTY0LDIzLDE0MCw4OSw0NywxNjAsMTAsMTA5LDE0MCwyMDksMTA2LDEzOCwxNDQsMTY5LDE0MywxMiwzLDE5MSw3NCwxNTksMTk2LDIyNSwxMDEsMjE3LDIxMCwxNTksMTUzLDI0MCwyNiwyNTEsMjUxLDUsMTIsMTE5LDEyMywxMDYsMTcwLDAsMTc1LDcwLDk2LDE0OSwyMDgsOCwyMiw2NiwxOTEsMjA2LDEzNiwxMDAsMSw4Niw0NSwxNTQsMzcsMTg5LDExMiw2NywxNTQsMzEsMjUsOTUsNDAsMTY5LDE2MSwxMTEsMTY0LDM1LDIyOCwxMzcsNTcsMTI0LDIzNCw4OCw5Miw3NCw1MCwxNDgsNjAsMTY3LDUwLDIwOCw1Nyw4MywyMzEsMTA2LDIwMCwxMTIsMzEsNzksMTE5LDg4LDE0NiwxMjIsMjYsMTAzLDIxLDQwLDE1MCwxMzgsMTkyLDE1NywzNSwxMDIsMTIyLDEyOSwxNDYsMTI0LDIyNyw4MCw2NCwxNjUsNTksMTI5LDE1NSwzLDE4OSw5MCwyOCwxOTksMTUyLDkzLDMsMTEyLDEzOSwxOSw4OSwxNjAsMTAsMTYwLDEzNiw4LDIyNywxODcsMTc0LDU3LDE1MiwyMSw4NCw5Nyw3NSwxODEsMTEsNzEsMjA5LDgyLDEyNCwzMiw0NiwyOCwyNDYsNDQsMTIxLDgwLDYxLDExNywxMDUsOTAsMjMxLDEzMiwzNSwxOTMsNzEsMTQ2LDE1OCwyNywxNzksMjM4LDgzLDEwOCw2OSw4OSwxODgsMjI5LDIyNCwxMTAsMjM3LDExNSw5NCwxNTksMjAwLDUzLDEyOCwxOTIsMTI1LDEzMywyNTIsMTk4LDI0OCwzLDQsMCwxOTksMTIwLDE2NSwxODMsMTc3LDEzOCw4LDE2OCw5NywyMjUsOSwxOTksNDAsMTIxLDYwLDIwNCwxNCwyNDgsMjEwLDI1LDIzNSw4MSwxNCwyMTcsMTI4LDAsMTQxLDE5NCwxMjQsMTk4LDI0OSwxODAsNiw2NSwxMTgsMTMzLDE1MCw2Niw3OCw5MCwxNTUsMzAsNzEsMTYyLDE2NSw4Nyw2OCwxNDIsOTgsMTk4LDE1OCw4NCw0MSwxNTAsMzUsMTM5LDEwLDksMTQ0LDgsMTMzLDEwMiw3MCw3MSw1LDIwMCw2OSwxMjMsMTkxLDE2MSwzOCw5MSwyMTIsMzIsMjUyLDI0NywxNjIsMjE3LDEwNSwxODAsOTAsMTQwLDE0NiwxLDIxMywxNjAsNDcsMTM1LDE1OCwxMDIsMTE4LDIzLDI1MiwxNzAsNzQsNzEsNzUsNSwyMzQsNSw2NSwyMjgsMTA3LDM2LDg3LDEwLDExMiw5NywzOSwxMTUsMjE2LDMyLDU5LDE4MiwxMzksNTksMTc0LDIsMjMsMzAsMTA3LDc0LDU0LDE2OCwxNTAsOTIsMjEyLDE5MSwxMjUsMTAzLDE1NSwxNDMsMTQ2LDM4LDE4NCw5NywzMiwxMTQsMTk1LDcwLDQ5LDk2LDE4MSwxMDIsMjI5LDE4LDE2NywxNzksMTM1LDE2MywyNSw5MywyNywxMzMsOTAsMTkzLDUzLDE0MywxMTIsMzksMTI4LDE5OSwyOCwxNzMsMjA4LDQ4LDU0LDEyOCwxNjIsNDgsNDksMzksMTI0LDIzNCwyNDYsMTg1LDEzMiw2NCw2OCw5OCwxODAsMTE1LDk2LDEsMTg4LDM0LDgzLDk5LDE0OCwxNDcsMTQ5LDE4MCwxNywxMzAsMTc1LDksNDUsMjEsNjgsMTQxLDEwNiwxOTUsNjYsMjAyLDE2MSw1Myw3OSwyMDksMjcsMTEwLDY5LDUzLDg3LDE3NywxNzUsMjksMjQ2LDIwNSwxMTIsMjM1LDExMywyMzUsMTAxLDQyLDIxMywxMTksNCw5LDIzNiw0Miw2MCwxNDYsMTI4LDE1NywxODgsNTYsMjEzLDM2LDMwLDIzMSwzOSwxOTUsMjI0LDQ4LDE3MywyMTEsNDQsMTE1LDIyNSwxODYsOTMsMzQsNzQsMTkwLDEzMyw5MiwxNjEsMTE4LDI2LDE5NywxNDAsMzcsNTYsNDksMzcsMjYsMTA4LDI0MywxMTQsMjUxLDIyNywxMiwzLDU3LDUxLDIyMCwxNDUsMjIsMjMyLDQwLDI4LDM5LDEzNSwxNjksMTY2LDE3NiwyNCwxNTUsMTgsMTI2LDIxLDE5OCwxODMsMjUsNzYsNzQsMTQzLDM4LDY1LDIyNSwxNjAsMTEsMTcxLDE1NCw5NiwzOCwxMjIsMjksNywxNjgsMTc3LDYwLDUxLDExOSwyMjksMTA2LDg5LDIyLDE2LDEyMSwxNjEsMTMwLDEwMywxMDIsMjgsMjgsMjI1LDEwMCw4NiwxNSw1MCw3NSwxOTQsNDAsMTQxLDEzOSwxMTUsODYsNDcsMyw1NywxNzIsMTM1LDAsMiwyMzUsMjAxLDE0NywxNzIsMTY1LDE1MiwxNjYsMTQyLDE0LDI1Miw0NywyMDEsNDIsMTQ3LDEzMSwyMDQsNTAsNDEsMTksMTc5LDYsNTYsMTIwLDEyLDIzMCwxNjMsOTksMTI4LDE2LDEwMSwxODgsMTM1LDI1NCwyMzIsMTM3LDExMyw0MywxOTIsMjI1LDExMiwyMDAsMTYwLDIwLDIsMjU0LDE4MywyMSwyOCw1NSwxMzYsNjAsMjM1LDE1Nyw3NCwzNiwxMzksMjI2LDg2LDEzMCwyNTUsMjI1LDE2NywxODgsNDksOTEsMTc0LDE0MCwxMjksMyw4NCwxMDAsMTE1LDQzLDc1LDQsMTI0LDEzNSw0NSwxNjMsMzgsNDQsMTU2LDE0MCwxMTcsMTgwLDE3MSwxODAsMjgsMTkyLDUwLDEyOCwxMjcsNzcsMTksMTc4LDEzMCwxMzQsMTA3LDE4OSwxNzAsNzQsMjMzLDEwNSwxMjMsMjQ0LDEyMCwyMDcsMTMyLDEyMywxNzUsMTc4LDE4LDE4NCwyMjQsMzksMjEsMTgxLDEwMiwxOTUsMjE5LDE2MiwxMSwzNiwzNSwxMTYsNjYsMjEsMTY1LDE0MSwxMCw0NiwxMjMsMTcsMTkyLDcyLDM4LDE4MywxODksMTgxLDE3MSwyNTEsMTgzLDk2LDcxLDEzNCw0NiwzMCwyNDgsNDQsNjMsMTA4LDExNSw4MCwyMjAsMTQwLDEyNSwzNSwxMzIsMjI1LDIyLDUsMTA4LDI4LDE3OCw2MywxOTgsMTExLDIzNiw4NSwxNzksMTQ5LDEsMTgzLDE0MywxNjIsMTM5LDIyLDgwLDQ2LDIzNiwxMzksNjEsMTA4LDEzMywxMywxNzUsMjgsMTc5LDE2MywxODEsNzMsMTksMTAxLDQ3LDU1LDI0NSw0LDExMiwxMDAsNCwxNjcsMjI0LDEyNSwxNTAsMTQ5LDIwMSw1LDEyOSwxNzksMjM0LDIwMywxMzcsNTQsOTEsMTAxLDExOSwxNzEsOTgsNzUsOTksNjUsODUsMTI5LDE5NCw5LDksMTMwLDQsNTUsMTM1LDE2MywxNTMsNTEsMTM3LDIxLDMyLDIxMiwzOSw4MCwxMTcsNTQsOTUsNzcsMjAzLDExOSwxMjEsMTU0LDcsMjE0LDE0NCwxMjIsNzMsMTMxLDE2NiwxMjgsMTc3LDE4OCwxODMsMSwxNzQsMTUxLDE4NSw2NSw2MCw4Niw4MCw5OSw1OSwxNCwyNCw2Niw0MCwyMjEsNjgsNDcsNjUsNjUsMTc3LDEzMSwxOTIsMTQ0LDQ3LDcsMzcsMzMsNzYsMTY3LDI1NCwxNjcsMTIxLDIyMiwxNjYsMTU0LDYzLDk3LDE1OSwyNDAsMTY5LDEzNiwyMzMsMTIyLDExOCw1NywxMTIsMTk5LDEzMSwxNzcsMjgsNTEsMTQwLDE4MywxMTEsMjUyLDE1OSw5NCw3Niw1MywxMzAsNDEsMTcyLDExNCwxMTksMTMwLDE5NSwxMjQsNTYsMTIyLDIzMCwxMCwxMDgsMTEzLDEzNSwxNjcsMTU1LDQ3LDc3LDEwNyw2OSw4NCw3MCwxMDksMjUsMTYsMTE3LDE2Nyw3MiwxMzksMTMsMTAzLDE1NCwyNywyMTAsNjIsNDMsMCwxMjYsMTg0LDIwOCwxMDAsNDEsMjQwLDE4NSwxNjIsMjExLDU0LDU2LDIwNywxMTYsNDMsMTE3LDE4LDIxMCw0MiwyNTAsMjExLDI1MCwxMTIsMTM3LDExNywyNDcsMTUwLDExMyw0MywxNzMsMjI0LDE4MSw1NCwyNTEsNTIsMTMzLDEyMiw3NywzMSwxMzUsMjA3LDIwNSwyMzUsMjI0LDEzNCw1NCwxMzAsMjMzLDEwOCwyMDcsMTU1LDE1Myw5Nyw2Nyw3OSw5Miw0MCwyMDYsMjE2LDI0Myw4NywyMCwzMSwyMjEsNjUsODEsMjA2LDE3OSwyNTEsNDcsMzYsMTAsMTMxLDE3NSw1MF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzMsMTg3LDE2MSw2OCwyMDgsMTI5LDIwNSwyMzIsNTEsMjI1LDE4LDQsMTMwLDE2OSwyMDMsMTg4LDExNSwxODgsMjM2LDY3LDE2NywxMjIsMTg0LDE5OCwxMTUsOTYsMTU0LDI1MiwyNDIsMTEwLDE5MywxNTQsMTg1LDI5LDI0NywxMjIsMTQxLDQ0LDE1Myw5MywxNjQsMTkyLDEyMSw1NiwxMTQsMTU5LDEzNSwxNjYsMTIxLDEwNywxLDE5NiwyNyw1NSwyNDMsMjE5LDExMiwyNTQsMTIxLDUwLDQ1LDE5Miw3NSw0MCwzMiwyMDIsMjQ0LDksMTI5LDIyLDU3LDE3Miw0MSwyMzIsODQsOTAsNTIsNzksMTYzLDExNCwzNCwyMjMsOTYsMjksMjQ3LDE0Niw3NSwzNywyMTcsMTA5LDEyOSwxNzksMTksMjQ4LDE5MiwxMjcsMjE4LDQ0LDE3MCw3OCwyMjUsMTUzLDkxLDUwLDU5LDE1MywxODQsNDQsNjcsMTM4LDIwNiwxNzgsMjE1LDYsMTMxLDE1MSwyNCwzOCwxMjIsMTY2LDIzMCw2OSwxMiwxNTIsMTY1LDE3MywxNzIsMTE2LDE0MSw2OCwyMDIsMTg3LDExLDIyNyw3OSw5MywyMTIsNzEsNjYsNDgsNDMsMCwxOTcsOTMsMTYyLDIyLDEwOSwyMTQsOSw1NCwxMTEsNTEsMjYsOTQsODMsMywyMDMsMTk1LDMsMTYxLDExNywxOTAsMjI0LDk5LDIwMywxNzcsMTI0LDExOCw4MCwyLDE0MiwxNTIsMTk5LDE0MSwxMTEsODAsNTQsNDIsMTM3LDE0OCwzNiwxMzYsMTAwLDIyNiwxODQsNjgsNzEsMTIxLDU0LDQ2LDg4LDE0NiwxNywxMzYsMTE3LDI0NSw1OCw3NCwyMzYsMTQwLDM3LDIzMiwxNzksMTg1LDEwMywyMTgsMTk2LDEwMiwxNTEsMjMsMjMsMjE3LDMxLDY1LDEyLDYxLDEyMyw5Miw3NiwxMTYsMjA5LDkzLDIzMyw5OCwzLDUyLDI0Nyw5NiwxNSwyNDcsNDUsMTg2LDE3MSwxMzIsOCwyMTcsMTYsMjE4LDI3LDEyOSwyMjksMTI0LDIwNCw4MCwxNTEsNjMsMTQsMjAzLDM2LDg0LDk4LDE1NCw5MiwxMTIsMzgsMTgyLDI1MiwxNzIsMjA2LDE2NywxOTAsMjM4LDE0NiwxNTQsMTEyLDEwLDQxLDY5LDE3NywyOSwxMjEsMTc5LDE2NCwyMzgsMjI3LDMxLDk4LDIyNSwxNTAsMTE1LDIxMywxNjgsMTg0LDc1LDU0LDIzMSwxNDYsOTksMTM5LDIwOCw1MCwxNjMsODEsNTYsMTk5LDE3Nyw1LDIwMywyMjUsNDYsNTEsMTk4LDE2MiwxODksODQsMTQ1LDMsMjQ0LDE5Miw1MiwxMDUsMzYsMTM2LDIxNCw5OSwyMzksNzEsOTUsOTcsMTMzLDE1NywxNzIsMTk0LDE3OSw1NywxMzUsMTM2LDIzOCw2Niw5MiwxNTgsMTY5LDEzMiwxMTEsMTA1LDE1OCwyNTAsMjIsMTgwLDQ0LDI0LDczLDEwNyw1MSw0MiwyNTAsMTA0LDM0LDE4NCwyMjQsMTQ2LDM1LDEyMSw0NywxNDAsMzYsODIsMjQ0LDIxLDI0LDIzOSwyNTAsMTY2LDI0OSw2OSwxMjAsMTA5LDEwNywxMTgsMjA2LDUyLDEwNSwxNjMsNzMsMjUsMzcsMjAwLDE1MCwxLDE1NSw0OSw1LDIyNSw0Niw4OSwyMzUsMTExLDc5LDE1MSwxNzIsMTk0LDEzMiwzNiwxMCwxLDEyLDM0LDIyNyw2NiwxNjAsMjQ2LDc4LDE4LDEzOCwxNTMsNTEsMjM1LDE3LDk0LDEzNSwxOTMsMTEsMjE0LDUyLDMzLDE5NiwxMTEsMjEwLDI0Miw0MCwyMjcsMjYsODcsMTI1LDE1NCwxMjAsMTE2LDEzMyw4Miw5LDIxOSwxOTYsMTcwLDI3LDEwLDE4OSwyMiwzOSwxMDUsMTcwLDk4LDE1LDEzMSwyMDUsNDEsODksMTQzLDM2LDI0MSwxOTYsMjI1LDE3OSw3NSwxODgsMTQ0LDMxLDU1LDM5LDExNiw1LDIsMTEsMTQ1LDczLDExMiwxODIsMTE5LDIwMiwyMjMsNDQsMTUzLDgzLDI2LDE5OCwxMzgsMjEsNTYsMTU2LDE3OCwxODIsNjgsMjQ0LDE4LDE4NywyMzIsMTQ1LDc1LDIzNiwxMTIsMjQ5LDE1MCw0MCwyMywxOTYsMTg1LDI1NCwxNTEsMTUsMTY0LDI1MSw0OSwyNDMsMTg2LDcwLDIwNiwxMDUsNzEsMjQ5LDE0OSw0LDI0NCwyMzQsMTksNzQsNTQsMTgxLDE3Miw1NywzMSwxODQsMjE5LDEzMywyMTcsMTIwLDU0LDE4Miw3NCwxMjEsMSwxOSw5OCwxOTksMTAsMTIyLDI1NCwzNywzMCwyMCw3LDIsMTE4LDE2OCwxNSwxOTcsODQsMzcsMjE5LDIxNiwxNTQsNDAsMTU2LDUsOTIsMTEyLDE1Miw2MSwxLDE5OCwxNjMsMTkyLDE2NCwxMiw2NCwxNSw1OSwxODQsMTU1LDEwMywwLDkwLDE1MiwyMDAsOTgsOSwyLDYzLDE4MiwyMzEsODUsMTMwLDE2NywxMzQsMTE4LDYwLDE1NywyMjgsMjA5LDE5MywyMDcsMTE2LDE0NywxNCwzNiwxMzksNDEsMjIwLDE1MiwxODQsOSwzNSw4MywxMTYsMTA5LDIzNSwxNzksOTksNjEsNzUsMzQsOTgsMTAxLDMsMzQsNzQsMzIsMTY0LDE5OCw1OCwyNDIsMTk5LDE3MSw3MywyMiw0Myw2NSwyMjUsMTU2LDM1LDMyLDMxLDEyLDIzNiwyNiw2MCwxMTksMTY3LDE1OSwxMDcsNyw5OSwxODUsMTg0LDExMywxMDQsNDYsMjM2LDMsMTM3LDc4LDIxOSw5MCw4NCwyNDEsMTksMjAzLDUzLDEzOCw3NywxNyw2MCwxODIsMzQsNjcsMjEwLDczLDE3NiwyNCw2NSwxNTcsOTQsNDQsMTMzLDE3OCwyNDIsMTQ0LDE0MSwxMzgsMTIwLDE3MiwxODIsMTE2LDYxLDQyLDE1MSwxOTUsNjcsMTk0LDczLDEzMSw2MCwzLDI0Myw4MCw5OSwxNjMsMjA3LDIwMCwxOTMsNzUsMzIsMjI5LDEyMiw4Nyw0MCw3NiwyMjYsMTg4LDUxLDExNyw3MywxNTgsMjA3LDE3MCwxMzEsMTkzLDg4LDIzLDM1LDEyOCwxMTIsMTMwLDIyNywxNzAsNTcsNTAsMTg0LDQ3LDIzMSwxMjksMTYyLDcyLDYzLDIyMiwyMjcsMTA4LDgwLDMsMTY3LDU0LDEwMSwxNDQsMTk0LDIyMCw4MSwyMDgsMzksMTQsMTI3LDExNywxMDUsMTgxLDE0Nyw2LDE4NCwxMzAsMTcxLDIyOSwxMzAsNTAsMTIwLDE2MiwxNjYsMjQzLDg2LDEwMyw3NywzOSwxNjYsMTk2LDYsMTU1LDQ5LDE5OCw0NiwyNTIsMTMyLDI5LDkzLDIxMywxMDUsNjQsNTcsMTk5LDIyOSwzMiw5OSwyNywyMDksNzcsNTIsMTkzLDEzOCwxMzgsMTQ3LDY5LDkyLDE1MywxMTEsMjM5LDE1NSwxNjUsMjM4LDEwLDE2LDk2LDIxMywxMjUsMTQ4LDIzNSwxMTUsNCwxMTcsMTYwLDE2NSwxMzgsMTk4LDk2LDU3LDkzLDIzMywxMzksOTIsNTcsNDMsMTYwLDE2NiwxOSwxNzMsMiw5OSw1LDEwLDEwMSwxMDksOTMsMTcxLDI1LDIyLDY4LDEwNSwyMjMsMjMzLDc3LDE0Niw5NywzMCwxNTMsMTEzLDE3MywyNCwxOTMsMTEwLDI0OCwyNDcsMTk4LDIyMyw0MSwyMDQsMjQyLDI1MiwyMiwxOTUsMjA5LDU3LDE1LDM2LDc5LDM1LDc2LDE1Nyw4OCwxMzEsMTk0LDI1MywxNjksMTQsNywxMCwxMTYsMjM2LDExOCw2NCwxOTIsMjI0LDc4LDEyLDExNiw1MCwxNTAsNjUsMiwxNjgsMjMxLDE0MSw1Miw5Niw4NywyMDcsMTM4LDE5MSw3MCwxOSwxMCwyMTQsMTUzLDE5OCw5NCwyNSwxNzUsMTQsMTM1LDE0NCwxMyw1MiwxMTIsMjM0LDEzMSwxNzMsMTUwLDIxMywxMjEsMTY5LDMzLDQyLDE1MCw2Niw4Nyw4MywxMzksMTY2LDE2NiwzNSwxMzksOTAsMjI4LDExOCwxODQsMTc2LDQ2LDYyLDI0MSwyMDEsNTYsMTUwLDE5NSw1Myw1NiwxMjYsOTYsMTA1LDE4LDI1MCwyNTEsOTEsMTQ2LDIzMSwxODAsNDMsMTg1LDEzNCwyNCw1NSwxMzMsMTE2LDE1MCw2LDIxNywxMDQsOTMsMTk4LDEzNSw2MCwxNywxMjMsMjYsMTgzLDU1LDQyLDIyOSwxOTQsMzksNDUsMTM0LDkyLDI1NSw1MCwxOTMsMjE5LDIwMCwyMiwyMzEsODgsNDYsMjksMTgwLDc0LDkzLDIyNSwxNzgsMjE3LDEzOSw0MCwyMzYsMjM0LDEzLDE2NSwxNTEsMTU2LDY2LDExNCwyNSw2MCwzOSw0NywxMDQsMTY3LDg5LDEsMjI0LDE1MCwxNCwyNywxNjYsNDYsMTgwLDE0OCwyMDcsMTYsNzcsMTIzLDIzMiwyOSwyNTAsMjE2LDMwLDQyLDczLDE2OSwzNiw5Niw3NSw3NSw4NSwxNTQsOTQsMjQsMTQsNiwxNzEsMTQ0LDE4OSwyMzIsMTQ0LDIyMywxNTQsOTIsOTYsMjQ4LDIyLDE1LDE2NiwxMDMsMTk2LDk4LDQ5LDIzOSw2NCwxNzEsMTc4LDIxMCw3OSwyMzUsMjA0LDEwMCwxNyw4OSwxNDgsMTcsMTY2LDEwMCw4NSwxLDE0NiwxNjIsMjQsMTIwLDE2LDIzMCwyMDMsMzcsMTY0LDI2LDY5LDExNCw1MSwxOTgsMTI5LDY5LDE0Nyw4NywxMjMsMTk3LDE1NCw3MSwyMjgsMjE5LDMwLDY0LDMsMTAwLDIxNSwxOTgsMTU3LDE1OSwxLDI1LDY4LDQyLDQ2LDUyLDU4LDQ5LDE3NywyMTAsMjA1LDE0OCw4Nyw2Niw3MywxMDcsODUsMTIxLDE3MCwxOTcsNDIsMzcsMzUsMTU3LDE0NCw5OSwyMDQsODksOTEsMTgzLDE4MywxMTUsMTQ0LDEwOCwxNywyMCwyMTUsNzcsMjM3LDE0NywxNDUsMjUxLDk4LDE1LDksMTIsMTg3LDEwLDE5MiwxNTcsMTE0LDIzLDYwLDEsMjQwLDk1LDIwNSwxODgsNTQsMjQwLDMzLDE2MCwxMjUsMjI0LDE5NiwxMzksMTYwLDg0LDIxNywxNjYsMTA1LDExOSwxMTYsOTYsMjA4LDE2NywxODYsMTY4LDQzLDE5MiwyMTMsMjMwLDE0LDI5LDEwLDUxLDYwLDE0NSwxODgsNDYsMTIxLDE5OCwzMCw1OCwxNjMsNCwyNDcsMTEwLDcyLDE4Miw4Niw4Niw1OCwxNTksMTk3LDUxLDE1NywxMDUsMzQsMTM0LDE4MCwyOCwxNzMsMTk0LDEzMiwxMDYsMjQ3LDEzMSwxOTcsNjgsODMsMTAwLDI0NCwyNDQsNTAsMzgsMjEwLDI5LDY4LDI1MiwzNiw3MCwyNDksNjUsNTEsMjEyLDgyLDE4NSw5MCwxNTIsMTM0LDM5LDEzMSw0OSwxODgsNiwxMDUsOTgsMTgzLDcyLDEwNSw5MiwyMzYsNjQsMTM4LDQwLDE5LDI5LDE1LDYwLDk4LDgxLDExNyw2NSw4NywxNTYsMTAsMTk4LDE4NiwxMzYsMTc5LDI0NCwxNzYsNDUsMjYsNTUsMjMyLDE5NCwxODcsOTUsODIsMTQ1LDUsMTAwLDcxLDIwNiwxMzAsMTk4LDEwNCwyMzQsMTg5LDIsMjI1LDIxLDEyNywzNiwxNCwxMiwxNjcsNSw2NSw1MCwxNTIsNDgsMTQwLDU4LDM2LDIxOCwxMjQsMTk4LDYsMjA2LDE2OSwxMDIsMTA2LDE3Niw1NiwxOTIsMTc3LDE0NSwxNzMsNjMsMzgsMTE1LDE3LDI0OSwxNCwxOTcsMjExLDE4Niw5MiwxMCwxNjgsNTUsNjUsMTA5LDIxNiwyMDEsNTQsMiwxMjIsMTEsMzMsNCwxNzcsNTEsOTAsOTgsNzUsMTEyLDEyNCw1Miw0Miw0MSwxMjcsMzYsMTUyLDczLDgsMTIwLDc5LDE4Myw2OSwxNzgsMjIwLDE1MiwyMTEsNzYsMTY1LDQzLDkxLDYyLDEwNCwxMzIsNzEsNDAsMzgsMTQ5LDI0MywxMjAsMjA3LDIyMiwxMzUsMTc4LDE5NSwxMzIsMTQ0LDQ1LDE2NCwyMywxNDAsODksNDcsMTYwLDEwLDEwOSwxNDAsMjA5LDEwNiwxMzgsMTQ0LDE2OSwxNDMsMTIsMywxOTEsNzQsMTU5LDE5NiwyMjUsMTAxLDIxNywyMTAsMTU5LDE1MywyNDAsMjYsMjUxLDI1MSw1LDEyLDExOSwxMjMsMTA2LDE3MCwwLDE3NSw3MCw5NiwxNDksMjA4LDgsMjIsNjYsMTkxLDIwNiwxMzYsMTAwLDEsODYsNDUsMTU0LDM3LDE4OSwxMTIsNjcsMTU0LDMxLDI1LDk1LDQwLDE2OSwxNjEsMTExLDE2NCwzNSwyMjgsMTM3LDU3LDEyNCwyMzQsODgsOTIsNzQsNTAsMTQ4LDYwLDE2Nyw1MCwyMDgsNTcsODMsMjMxLDEwNiwyMDAsMTEyLDMxLDc5LDExOSw4OCwxNDYsMTIyLDI2LDEwMywyMSw0MCwxNTAsMTM4LDE5MiwxNTcsMzUsMTAyLDEyMiwxMjksMTQ2LDEyNCwyMjcsODAsNjQsMTY1LDU5LDEyOSwxNTUsMywxODksOTAsMjgsMTk5LDE1Miw5MywzLDExMiwxMzksMTksODksMTYwLDEwLDE2MCwxMzYsOCwyMjcsMTg3LDE3NCw1NywxNTIsMjEsODQsOTcsNzUsMTgxLDExLDcxLDIwOSw4MiwxMjQsMzIsNDYsMjgsMjQ2LDQ0LDEyMSw4MCw2MSwxMTcsMTA1LDkwLDIzMSwxMzIsMzUsMTkzLDcxLDE0NiwxNTgsMjcsMTc5LDIzOCw4MywxMDgsNjksODksMTg4LDIyOSwyMjQsMTEwLDIzNywxMTUsOTQsMTU5LDIwMCw1MywxMjgsMTkyLDEyNSwxMzMsMjUyLDE5OCwyNDgsMyw0LDAsMTk5LDEyMCwxNjUsMTgzLDE3NywxMzgsOCwxNjgsOTcsMjI1LDksMTk5LDQwLDEyMSw2MCwyMDQsMTQsMjQ4LDIxMCwyNSwyMzUsODEsMTQsMjE3LDEyOCwwLDE0MSwxOTQsMTI0LDE5OCwyNDksMTgwLDYsNjUsMTE4LDEzMywxNTAsNjYsNzgsOTAsMTU1LDMwLDcxLDE2MiwxNjUsODcsNjgsMTQyLDk4LDE5OCwxNTgsODQsNDEsMTUwLDM1LDEzOSwxMCw5LDE0NCw4LDEzMywxMDIsNzAsNzEsNSwyMDAsNjksMTIzLDE5MSwxNjEsMzgsOTEsMjEyLDMyLDI1MiwyNDcsMTYyLDIxNywxMDUsMTgwLDkwLDE0MCwxNDYsMSwyMTMsMTYwLDQ3LDEzNSwxNTgsMTAyLDExOCwyMywyNTIsMTcwLDc0LDcxLDc1LDUsMjM0LDUsNjUsMjI4LDEwNywzNiw4NywxMCwxMTIsOTcsMzksMTE1LDIxNiwzMiw1OSwxODIsMTM5LDU5LDE3NCwyLDIzLDMwLDEwNyw3NCw1NCwxNjgsMTUwLDkyLDIxMiwxOTEsMTI1LDEwMywxNTUsMTQzLDE0NiwzOCwxODQsOTcsMzIsMTE0LDE5NSw3MCw0OSw5NiwxODEsMTAyLDIyOSwxOCwxNjcsMTc5LDEzNSwxNjMsMjUsOTMsMjcsMTMzLDkwLDE5Myw1MywxNDMsMTEyLDM5LDEyOCwxOTksMjgsMTczLDIwOCw0OCw1NCwxMjgsMTYyLDQ4LDQ5LDM5LDEyNCwyMzQsMjQ2LDE4NSwxMzIsNjQsNjgsOTgsMTgwLDExNSw5NiwxLDE4OCwzNCw4Myw5OSwxNDgsMTQ3LDE0OSwxODAsMTcsMTMwLDE3NSw5LDQ1LDIxLDY4LDE0MSwxMDYsMTk1LDY2LDIwMiwxNjEsNTMsNzksMjA5LDI3LDExMCw2OSw1Myw4NywxNzcsMTc1LDI5LDI0NiwyMDUsMTEyLDIzNSwxMTMsMjM1LDEwMSw0MiwyMTMsMTE5LDQsOSwyMzYsNDIsNjAsMTQ2LDEyOCwxNTcsMTg4LDU2LDIxMywzNiwzMCwyMzEsMzksMTk1LDIyNCw0OCwxNzMsMjExLDQ0LDExNSwyMjUsMTg2LDkzLDM0LDc0LDE5MCwxMzMsOTIsMTYxLDExOCwyNiwxOTcsMTQwLDM3LDU2LDQ5LDM3LDI2LDEwOCwyNDMsMTE0LDI1MSwyMjcsMTIsMyw1Nyw1MSwyMjAsMTQ1LDIyLDIzMiw0MCwyOCwzOSwxMzUsMTY5LDE2NiwxNzYsMjQsMTU1LDE4LDEyNiwyMSwxOTgsMTgzLDI1LDc2LDc0LDE0MywzOCw2NSwyMjUsMTYwLDExLDE3MSwxNTQsOTYsMzgsMTIyLDI5LDcsMTY4LDE3Nyw2MCw1MSwxMTksMjI5LDEwNiw4OSwyMiwxNiwxMjEsMTYxLDEzMCwxMDMsMTAyLDI4LDI4LDIyNSwxMDAsODYsMTUsNTAsNzUsMTk0LDQwLDE0MSwxMzksMTE1LDg2LDQ3LDMsNTcsMTcyLDEzNSwwLDIsMjM1LDIwMSwxNDcsMTcyLDE2NSwxNTIsMTY2LDE0MiwxNCwyNTIsNDcsMjAxLDQyLDE0NywxMzEsMjA0LDUwLDQxLDE5LDE3OSw2LDU2LDEyMCwxMiwyMzAsMTYzLDk5LDEyOCwxNiwxMDEsMTg4LDEzNSwyNTQsMjMyLDEzNywxMTMsNDMsMTkyLDIyNSwxMTIsMjAwLDE2MCwyMCwyLDI1NCwxODMsMjEsMjgsNTUsMTM2LDYwLDIzNSwxNTcsNzQsMzYsMTM5LDIyNiw4NiwxMzAsMjU1LDIyNSwxNjcsMTg4LDQ5LDkxLDE3NCwxNDAsMTI5LDMsODQsMTAwLDExNSw0Myw3NSw0LDEyNCwxMzUsNDUsMTYzLDM4LDQ0LDE1NiwxNDAsMTE3LDE4MCwxNzEsMTgwLDI4LDE5Miw1MCwxMjgsMTI3LDc3LDE5LDE3OCwxMzAsMTM0LDEwNywxODksMTcwLDc0LDIzMywxMDUsMTIzLDI0NCwxMjAsMjA3LDEzMiwxMjMsMTc1LDE3OCwxOCwxODQsMjI0LDM5LDIxLDE4MSwxMDIsMTk1LDIxOSwxNjIsMTEsMzYsMzUsMTE2LDY2LDIxLDE2NSwxNDEsMTAsNDYsMTIzLDE3LDE5Miw3MiwzOCwxODMsMTg5LDE4MSwxNzEsMjUxLDE4Myw5Niw3MSwxMzQsNDYsMzAsMjQ4LDQ0LDYzLDEwOCwxMTUsODAsMjIwLDE0MCwxMjUsMzUsMTMyLDIyNSwyMiw1LDEwOCwyOCwxNzgsNjMsMTk4LDExMSwyMzYsODUsMTc5LDE0OSwxLDE4MywxNDMsMTYyLDEzOSwyMiw4MCw0NiwyMzYsMTM5LDYxLDEwOCwxMzMsMTMsMTc1LDI4LDE3OSwxNjMsMTgxLDczLDE5LDEwMSw0Nyw1NSwyNDUsNCwxMTIsMTAwLDQsMTY3LDIyNCwxMjUsMTUwLDE0OSwyMDEsNSwxMjksMTc5LDIzNCwyMDMsMTM3LDU0LDkxLDEwMSwxMTksMTcxLDk4LDc1LDk5LDY1LDg1LDEyOSwxOTQsOSw5LDEzMCw0LDU1LDEzNSwxNjMsMTUzLDUxLDEzNywyMSwzMiwyMTIsMzksODAsMTE3LDU0LDk1LDc3LDIwMywxMTksMTIxLDE1NCw3LDIxNCwxNDQsMTIyLDczLDEzMSwxNjYsMTI4LDE3NywxODgsMTgzLDEsMTc0LDE1MSwxODUsNjUsNjAsODYsODAsOTksNTksMTQsMjQsNjYsNDAsMjIxLDY4LDQ3LDY1LDY1LDE3NywxMzEsMTkyLDE0NCw0Nyw3LDM3LDMzLDc2LDE2NywyNTQsMTY3LDEyMSwyMjIsMTY2LDE1NCw2Myw5NywxNTksMjQwLDE2OSwxMzYsMjMzLDEyMiwxMTgsNTcsMTEyLDE5OSwxMzEsMTc3LDI4LDUxLDE0MCwxODMsMTExLDI1MiwxNTksOTQsNzYsNTMsMTMwLDQxLDE3MiwxMTQsMTE5LDEzMCwxOTUsMTI0LDU2LDEyMiwyMzAsMTAsMTA4LDExMywxMzUsMTY3LDE1NSw0Nyw3NywxMDcsNjksODQsNzAsMTA5LDI1LDE2LDExNywxNjcsNzIsMTM5LDEzLDEwMywxNTQsMjcsMjEwLDYyLDQzLDAsMTI2LDE4NCwyMDgsMTAwLDQxLDI0MCwxODUsMTYyLDIxMSw1NCw1NiwyMDcsMTE2LDQzLDExNywxOCwyMTAsNDIsMjUwLDIxMSwyNTAsMTEyLDEzNywxMTcsMjQ3LDE1MCwxMTMsNDMsMTczLDIyNCwxODEsNTQsMjUxLDUyLDEzMywxMjIsNzcsMzEsMTM1LDIwNywyMDUsMjM1LDE2NSw1NiwyMjYsMjI2LDY4LDI0OCwxNCw1MSw4OCw3LDIxMyw5NCwxNTEsMiwxNjcsMjAsMTYyLDU4LDczLDM4LDE4LDUzLDE0NCwyMDAsMjYsMTA2LDEyMiwyMDQsMjMwLDEzNSwxMDQsMjAyLDEzLDIwMywxMDIsMTg1LDIwLDI1MywyMTksMTI2LDEzNCwxODIsNjMsMTk2LDUsOCw1OCwyMSwyNDAsNTIsNDgsMjA0LDc0LDExNSwxOTIsMTcwLDI0Myw2MiwyNDQsMTg3LDUyLDMwLDIxMywxNTQsODAsMTg4LDI0MCw1NywyMjIsNiw3OCw5LDQsMTkyLDgwLDE0MCwxODIsMTU1LDIxMCwxMiwxMDEsMTczLDE1MCwx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NCwyMTMsMTI2LDEwMiwxODAsMTk3LDcsMTE5LDUsMTQ4LDIzMSw1NywxNTYsODgsMTU0LDI3LDEwMSwxLDExNiwxMzIsNTQsNTgsODMsMTMwLDI0NywyNDcsNzMsMTc4LDQsMTQ0LDEyNiwyMTYsMTA1LDIzMiwxNDAsMTMsMjMyLDIyNiwxOCwyMzEsMTYzLDIwNywxNTgsMjMwLDEwNiw4MSw1LDQzLDExOCw4MiwyLDcwLDQwLDQsMjA2LDE4NywxNTgsMTk5LDExOCw4MCw1NiwyMjQsMzksMjE5LDExOCwxNTMsMzcsMjQ1LDI1LDQ2LDIyOSwxOTMsMTMzLDUyLDIwNCwxNTYsMzUsMjA0LDIzOSwxMjQsMTczLDIyMCw2NSwxNjQsMTk4LDE2NiwxNSwyMiwxMDQsMTkyLDY5LDIzNiwxNDgsMTA2LDE3OSwxNzUsMzksOTYsMTgsMjcsNTUsMTgxLDM3LDE2LDYwLDE5LDE3OCwxMTIsNzMsMjE3LDE4MywxNzksMTA4LDE0OSwyMzAsMTcyLDIwNSw5OCwxMjAsMTQyLDI1MywxNywxNzAsNjcsMTE3LDE5NiwxODIsMTcyLDEwMSwyMzYsMTcwLDEyMSwxODYsNTcsNzcsMzMsNjYsMzIsNzMsMTE3LDE3MiwxMjIsMTUwLDEzNCw4MCwyMjAsOTIsMjM5LDIyOSw5LDczLDIxOSwxNjgsMzMsMjUxLDMzLDk3LDE3MSwyMDQsNjYsMTQwLDYzLDM1LDE1MywxODksMjI5LDEwNiwxMzksMTAxLDE1NSwxODUsMTYwLDIxMCwzOCw1MCw0MSwxODMsNjksNTksMTYwLDQ5LDExMiwxNjgsMjM0LDEzNCw5LDIwMywzNiwxOTksMTYyLDEwOCw1MSwxNzQsMjQxLDEyNyw3MywxNzIsMTg3LDUxLDI2LDE1MiwxNzMsMTQ5LDgwLDg1LDU4LDE2NSwxODIsODcsMjAwLDIwNywxMjksMTI2LDEzNSwxMTksMTY1LDMwLDIzMSwxMzUsMjI0LDE5MywxNjgsMCwxNjEsMTM4LDYwLDI0OCwxMCwxODYsNDgsMzEsOTMsMTc4LDcsMTk3LDE5OSw1NCwyNDQsMTUwLDE1NywwLDEyMSw2OCwyNDAsMjQ3LDExMCwxNDksNjksMTEyLDE3NywxMCwxOTgsMjQ5LDEyMywxNTMsMTI4LDE5MiwyMywyNiw1Niw3NCwyMjcsMjE3LDk3LDEzMiwxODMsMTUsMjIsMTIwLDE3NSw5Myw0OCwxOCwxMTgsMTc4LDEzLDIzMiwxOTIsMTMxLDE1OCwxNzYsMTE3LDEyMCwxNjUsNTIsMjM5LDIyNiwxODUsMzYsMTE1LDEyMSwxNTksMTYwLDkyLDEyMSwxNDYsMTA2LDE3MSwxOTksOTMsMjMyLDIzLDM1LDI2LDMsNDAsNTIsNSwyMDYsMTgyLDIzMyw3NCwxODQsOTAsNiwyNDYsMTg3LDUsMTk1LDE4MCwxNyw5NSwyNDIsMTA5LDE2NCwzNywxNDYsODMsMTk3LDEsMzIsMTcxLDEyNiw4Myw4OSwxMDMsOSw3Niw5LDE3NCw2OSwxNjUsOTMsMTgzLDksNzMsMTYwLDExMSwxMjgsMTIsMTQ5LDE5OCwyNDgsMTE3LDAsMjgsMiwxNjcsMjM1LDIzLDczLDk3LDExNiwxMTMsMTUwLDE0NSwxNjksNywyMDUsMTEzLDE0Nyw3LDEzOCwxODAsOCw5NywxMDAsMSw5NSwxNjQsNTAsNTYsNjgsMTcyLDI0NCw2LDEzMCw4LDE5LDE3OCwxMTAsMjQxLDE1MiwxMzYsMTM0LDE5Niw1NiwyMDEsMTk2LDIwOCw1Myw4OSwxMzIsMjI2LDU0LDIyNCwyNiwyNywyMiwxNzgsOTIsNDYsNDIsNjksMTAwLDU2LDExOCw4MywzOCwxOTgsMCwzNyw4NiwxMTgsMjI1LDQsNDcsMzMsMjMsMjA4LDE5MiwxMDQsMTAsNzMsNzcsMjE3LDExNywxNCwxNjUsMjEzLDE1MywzOCw4MywxNTAsMTM3LDI0MywyMDAsMjM3LDE3LDM0LDEyOSwxNzgsODcsNTMsMTQ5LDE1MCwxMTcsMTM0LDE4Niw3NCwyMjYsMTQyLDEyMCwyMTEsMTAzLDc3LDE0OSw3NSwzMSwyMzAsMTU1LDE2MSwyMjgsNyw1OCwxNjcsMTQ2LDE3NCwxMzMsNiwxOTgsMjEsNTAsMTQ0LDI1MiwxNDMsMjQ4LDk4LDQsMTYsMTM5LDIwNCwxMyw0OSwxMTIsODIsMTgyLDYsOTIsMTY5LDcxLDgwLDY0LDQ1LDE0MSwzOSwxODEsMjA0LDEzMywxNjYsODUsNDEsMTQsMjAzLDIyOSw1MSwyNTQsMjIsMTUyLDYwLDE5OSw4Miw0MiwxODYsMzcsMzMsMjYsMSwyMzYsNzIsMTU1LDk5LDEyMSwxNTksMjIwLDIxLDE2NiwxMTIsNjcsNDQsNDMsMTYsMzYsMTU3LDE0MCw0NCw1NywxNDYsNTIsMTAsMTY2LDE0MywxNDUsMzIsODAsMTU0LDkyLDAsNjQsNDgsMTQwLDIyOCwxOTIsMTcxLDE3MywxNzAsMTk2LDQ3LDEwNCw3NSwxMDQsODAsNTAsMTU3LDEzNiwyOSwxMjcsMTM4LDQ1LDUsMjE0LDIyLDcxLDExNiw2MSwyMTcsMjgsMjcsOTQsMTI5LDYsMTIsNzYsMTQ2LDkwLDU5LDEyNCw0NiwxNjYsODYsMjA4LDgzLDE5NSwyNDgsMTE1LDU4LDE1OCwyMTYsMTM4LDIyNywxMTQsNywyNDQsMzksMjYsMjcsMjMyLDM1LDk2LDIzLDgsMjAsMTUwLDg4LDEwMiwxMTgsMTczLDEwMywxNTEsMTI4LDMyLDEzMiw0NiwxMzksNjQsMTY4LDI0MCwxNDQsNDYsNCwyMSw2NywzLDIyOSwxNTQsNjgsMTE5LDcwLDEwOCwyNDgsMTc4LDIwNSwxODUsMSwzNCw2Nyw0MiwxNDksODEsMTUzLDEzNSwxMzksOTksMjM1LDE4MSwxLDE2OSwyNiwyMiwyMDYsNTAsMTYsNTUsMTYsOSwxLDAsMTk1LDIzNCwxODUsMjAzLDI0MCwxOTcsMTAxLDI0MywyNDQsOTcsMTc4LDIwMiwxODcsNDAsMzksMTQ0LDEyMiw3LDYxLDE2OCwxLDE2NSwxNzYsNzQsMTI0LDE2OSwxMzksMTYxLDE0NywyMTgsMTgzLDExMywyMCwyNiwxMjAsMTAsMTA5LDEyMiw4MSwxMDEsMTIzLDExNCwxNDIsOTYsMTU0LDE3NCwyMjgsNzEsNzEsMjQ2LDIzMCwxMTgsNzUsMTM0LDIwNiw4OSwyMDEsMTEwLDIzNywxNzksNDAsMTYzLDIwOSwxODcsOTksMCw1NCwxMDMsMTYxLDcyLDIyMCwxMiwyMDYsMTM1LDExNCwxMTQsMTYzLDM2LDE1OCwxMjEsMTAxLDU0LDE1MiwzMiwxNjcsMTE4LDExLDE5NCw4Nyw4NywyMDIsNTIsMjA4LDIwMCwyNTQsMjQ1LDEzLDIwMCwyMiwxNDksMjA1LDEwNSwxMjEsODAsMTY3LDM1LDEwOCwxMzIsMTUxLDIwOSwxNzAsNjIsMTczLDE0OSwxODIsNzksMjMsMTYsMSw2OSwxMDksMTYsMjUyLDE2NCw2OSwyNDIsMTkxLDI4LDUsMTI0LDMwLDE2MiwxNjEsNzAsMzIsMTk5LDEwNiw2OSwyMDMsOTcsMjUwLDgzLDIyNCwyMDQsMTkxLDE5MSwyMzUsOTksMTI4LDU3LDE1MCwxMzcsNTcsMTQ2LDEwMywxNTMsMTQzLDcxLDU1LDEwMSwyMDQsMTU1LDc3LDUsMjI2LDI0LDQ4LDEzNCw2NSwxMywyMDksMTgxLDQwLDgzLDEwNiwyMzQsMTE5LDY5LDUwLDE2MywzOCw3MiwyMDIsODksMTY4LDgsMjAzLDIyMiwyNDcsMTE3LDEzMiwxMjMsMTE1LDExNiwyNDUsMTg5LDM1LDE2OSwxNDIsMTIzLDQsMTI4LDQxLDIyNywxMTYsMTQwLDEwMiwxMSwyNDIsMjI2LDE4Miw0Myw1MiwyMDEsMTM2LDE3OCwxODQsMTU3LDQsMTMxLDk1LDUzLDcyLDIxMiwxODEsOTQsMjM4LDIxNiw2OCwyMDMsMzYsMTE1LDM4LDIwOCwxOTgsMjE5LDE0OCwxNjMsNzEsMTMwLDExMSwzNywxMzIsMTk0LDk2LDkwLDEwOCwxNzMsMCwxNjQsMTYwLDI0MCwxNjgsNiwxOTYsMzMsMTIyLDE5NSw3LDEyNSwyMTEsOTcsMTY4LDIzMCwxNzcsOTksMTcyLDEzOSw5OSwxMjksODEsMjU0LDE1NCwxNDYsNCwxNjMsMTE3LDQwLDcsMTIxLDE1MCwxMDcsMjQsMTczLDIwOSw2Nyw2NCwxNzIsMTg1LDYwLDI2LDE5NCwxMjIsMTg1LDE3Myw0OCwyMzYsMTEwLDI1MywxNzgsMTUsMjUzLDIzMiw1MCwxODksMTUxLDE0MCwyNDIsMTc4LDEyMCwyMzcsNzYsMTc5LDIxOSwxMzksNjUsMTk4LDU5LDYsMTg3LDIzMywxNjIsNDYsMTk3LDkzLDYsMTgwLDksMjE2LDI1MiwxOTUsMjQwLDI0LDE2OSwxNTQsMTk2LDE2MywxMjMsMjMzLDQ5LDM1LDEyMywxMCwyMTEsNCwxOTMsNTMsMTk1LDQ1LDU3LDE1NSw3Miw5MSw0MSwxNDMsODQsMjUsMiwxMjgsMTM1LDE4NSwxNTYsNTAsMTk1LDIzMyw4NiwxNjAsMTE2LDg1LDE4LDcyLDE2MiwzOSwxOTcsMjQwLDE3NSw2NSwxNTUsMTI5LDQyLDIxMiw5LDYxLDMsNTEsNzUsMTc2LDU1LDEzOCwxMzUsMjA3LDIzLDEyOCwxMzIsMTY2LDIxMiwxNTcsOTAsMTE2LDk3LDEwNCwyNDMsMTAsMTUwLDE0MCwxNjIsMTY2LDg1LDEzNCwxNTcsMzIsMTIzLDI1MSwxMDMsMTgzLDIxNCwxNzAsMTQsMjQ4LDcyLDE4OSw1MiwxNjQsMTg1LDY4LDE0NCwxMDIsNywzNywxMDYsNTIsMjIwLDE1NSwyMTAsODIsMjA3LDc3LDI0NSwxOTcsMjUsNTIsMjcsMjA5LDI1MSwyMSwxOSwxOTUsNjIsODksMTM4LDMwLDgzLDQsMTQ0LDc4LDI0Myw4NCwxMjksMjMyLDEsMjE3LDE0OCwxMDEsMTY4LDEwMiw4Nyw5OSw2NiwxNzIsOCwyLDE1NSwyMTUsMTA4LDUzLDEyOSw4MywzNCwxOTcsMTI4LDMwLDIyMCw5NiwxMjMsODMsMzcsMzcsMTQ4LDY2LDE0OSwxOTEsNTMsNTYsMTYwLDExMiwxNzcsMzgsMjE5LDIyOCwxNSwxNDUsMTQ0LDE4Nyw2MSw0NCwxMjEsMjcsMjI3LDQyLDMxLDUsMTk4LDE2NiwxNjMsMjE2LDE4NSwxNTUsMTk1LDE2OCwxMDgsMTY0LDIzMCwyNCwzNiw1Nyw2NCwxMTUsMTMyLDEwNSwyNDAsMjEsODIsMjMsMyw4MywyMTMsMjA3LDIzMiw2MF19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjQsMTgwLDEyNSwxMjYsNzksMjIwLDIyMSwyNTMsMTIzLDQyLDIwMSwxMjAsMTgwLDE2NCwyMDAsMjA4LDE3Nyw5OSw4Miw2MCw1NSwxNTYsMTI2LDc3LDExNywxMTUsMjAzLDIwMywxNDIsMjU1LDE2NiwzNF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbNjcsMjQxLDE2NiwyMjIsMTAsMTUzLDIzOSwxNTQsMTQxLDQ3LDMxLDE0MiwyMjMsMTc4LDI0Myw1NywxNTcsMTIyLDE1NiwxMDIsMTksMTE2LDIwMSwyMTcsMjQ3LDIyOSwxMTgsMTY2LDE0MywzNiwxMjgsMjM0XX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzIxOCwxMDUsMTQxLDE4MiwyMDAsMjI0LDE3MiwxMTAsMTI2LDE5LDEwMSwyNiwyNDcsNDUsMTk1LDE3MywyMTMsMTYyLDg0LDIyOSwxMDksOCwxNzMsMjUxLDE0NSwyMTgsMjQyLDMyLDAsMjE0LDIsMTAsMjQ0LDIwNiwzMywwLDUwLDI0Nyw4MSwyMDksNDIsMCwxMjMsMjA2LDIxMiwzNCw4Miw3NCw1OSw3MywxODcsMTM5LDIxLDY1LDE3NywxMjgsMTE3LDE3NSwxMTcsODQsMTQzLDIzLDE1OCw3XX19fX0seyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzcwLDE4MCwxMzgsNzEsMjQyLDEwOCw2MywyNDAsMTg5LDE5OSw0MSwxMTAsODksODksMTk1LDE0NSw4OSw3NCwzMywyMTMsMTQsMTY3LDkxLDEwMywxNjMsNjYsNywyMTAsMjM2LDksNTAsMjQwLDEwMCwxNTMsMTAxLDI4LDg1LDksMTYsMTYzLDE3NywxMjAsNjQsMjQ3LDExLDksMTMzLDk1LDE1OCw4OCw2MiwxNDUsMjUyLDEwOSwxMzQsMjMwLDkyLDIzMiwyMDQsMTAxLDIxNyw2MCwxNjksMTI2LDkyLDIwMywyMzgsODYsMjEsMjMzLDg0LDQ1LDE1NSwxMjksNDIsMTM3LDE1MCw1OCwxMDgsNDEsMTYxLDExMywxNDgsMTc1LDg2LDE1NiwxNiw5OCw3NSwxMzgsMTIyLDcsMzMsMTM1LDkxLDc3LDI0MiwyMywxOTEsMjQzLDQ4LDksMjUsMTAwLDQ0LDI0OCw3MywxMjQsNzEsNzQsMTQ3LDIyMCw0OSw0MywyMjgsNTEsMTg2LDEyMCw5NiwxODksMTc1LDM4LDExNSwxMDgsMTEyLDE2MiwxMDMsNTgsNDcsOTQsMjQ2LDQ4LDI0Niw2NiwxMDksMCw2Nyw2NywyMjksMTQ3LDY0LDEzNywzLDE2MywxMTksMTc5LDYzLDEwNSwyMzEsNDYsMTgwLDk4LDEzMSw4OCw0OCwxNDYsMTA0LDIwOCwxMzQsNiwyMCw1LDcsMjMzLDE0NiwyNDgsMTM1LDExNSwxNjcsODEsNzMsMTE5LDEzNyw4MSw5MCwxNzcsNDMsMTY3LDIyLDQ5LDIxLDI1LDExNiwzMywxMzAsNjIsMTEwLDIxNywxOTUsMTU2LDEwLDE3MiwxMzAsODYsNTgsMjEzLDU4LDEyOCw1Miw2NywzMywxNiwyMTksODMsOCw0LDE4MCwxMTksMTc2LDE5NywxMDcsMyw3OSwyMTYsMjQ0LDEwOCwyNDIsMzksNjQsMTY3LDI0OSw0NiwyMzksMTM2LDE2MSwxODAsMjI4LDE2NywxOTMsNSw0MSwxNTYsMTQ5LDEzOCwyMjAsMTQ3LDE0NywxMDcsMTI4LDY1LDgzLDE4MiwxMzIsMjUxLDIzNSwxNjAsMTk0LDIyOCw3OSwxNTIsMTQ1LDE3MCwxNDYsMTIyLDE2LDIwNiwxNzgsNjEsODQsMjE5LDIxLDksMjUxLDE4OCwyMTcsNjcsMzUsMTQ3LDIzMiwyNyw4NiwxNiwxMTAsMTMzLDE1Myw2MywzMCwxOTMsMjAxLDg4LDIzNSw3MiwyNTQsMTY3LDIxLDkzLDg1LDUsNjIsMjQ3LDc2LDE0LDg3LDEwNCwxNzYsMTY0LDEzLDE1NCwyMTUsMTA0LDEzNiwxMzIsMTQyLDU2LDIzMSw5NiwxNTksMTY4LDI0LDE2NywxMDEsMTQyLDEyNyw5MiwxMywyMzYsNjUsNTUsMTAwLDE3NiwxNDAsMTY5LDY3LDY4LDY4LDcyLDExNCwxOTcsMjExLDExOSw5MiwxMzgsMTk3LDIxMCwxOTksMTMyLDE2Niw1MCw1NCwxNDYsMTE1LDQwLDM4LDE3LDY4LDIzNiw0Myw4NSwxOCw4OCwxNDksMjQyLDgyLDgyLDIwMCw5NywxODAsOTcsNTEsOCwxMTcsMTk4LDExNywyMDEsMTYwLDU1LDExNiwxOTMsMTQwLDE0OCwxNyw0LDYyLDUzLDExNywyMTgsNTQsNzcsMTY3LDIxNCwxMSwyMDEsNDEsMSwxNSwxODYsMTcxLDIyNyw0NCwzNiwxMSwxOTksMjA0LDM2LDI0LDE3OSwyMjAsNTMsOTksMTgwLDEzNCwxNiwxNzgsMjI4LDQyLDE3OCw2OSwxNTEsMjA3LDEyMCw4MSwyMDIsMTE4LDE2MywxMDUsMjA0LDU0LDU5LDE5MywxMDQsMjAwLDE2OCwxNzYsNzMsMjUwLDY3LDI0NSwxNzIsMjksNzMsMTAxLDExMCwzOSwxMDQsMjUsNTksMTMzLDkyLDc1LDEwMSw4NSwxMzksMTE4LDE3NSwzNSw3LDEwNywyMTQsMTIsMTU0LDI0Niw1NiwyMCwxMDksNTMsNDgsNzksNzIsMTEyLDE1Niw4LDkxLDI1MiwzMiw3MSwxNzcsMjQzLDEzOSwxODQsMTk2LDEzNSw3Miw2NywxMjEsNDYsMjQ0LDEzMCwxMjEsMjQ4LDE1NSwyMjUsMiwxMDgsMTY4LDUwLDEwOCwxLDEwMiw1Nyw1OCwxNTAsMTI2LDEyMCwxMDUsNDgsOCwxOTksMTAsMTYwLDE4MSwxMzIsMywxMjMsNTMsMTYyLDE3LDE5MywyMDIsMTg4LDE0OSwyMjUsMjYsODMsMTMyLDExNSwxMDcsMTg2LDExMyw5OSwyOSwyNTIsNTQsMjQ3LDE1MywxNjUsMTQ4LDE0NywzOSwzMCw0OSw2Niw5NywxNjIsMTMwLDQsMTA1LDE2MywyMDAsNzMsMjA2LDc0LDE4Myw2Miw4NiwxNjYsMTg4LDEwMywyMjAsNTgsMTgwLDE2Miw1MCwxMTksMjA5LDExNSwxNjksNjQsMTMwLDEwLDkwLDQ3LDc0LDgsMTYwLDM3LDY0LDExOCwxOTMsMTUwLDEyLDQxLDI1MSwxMSw3NSwxNjQsMTMyLDE5NywyMzYsMTQ2LDIzNiwxMzQsMTMyLDI0MCwzOSwxMDYsMjQ0LDI0NSwyMCwxMzQsMjI1LDgxLDI1LDE0MCw3MiwyNTIsMTM3LDI4LDUyLDEzMSwxNjUsMTcyLDEwOCwyMDMsMjgsMjA4LDc3LDIyMCwxODMsNzYsMjA4LDczLDM4LDIzNiwxODAsMCwxOTksMTE5LDM4LDM5LDQ5LDYwLDQzLDIxNywyOCwxNiwzNiwxODAsMjM1LDExOSwxMjksMTcwLDcxLDE4OSwxNzcsMTM5LDcxLDE1MiwxMjAsMTc2LDEyNiw1OCwxMTQsMTMyLDczLDUwLDE5Niw3LDE3NCw0NSw4NCwxMDMsNzAsOTEsMTgyLDEzNSw2OSw5NSwxODUsMjUwLDEzNSwxNjYsMTYyLDE0NywxOTQsMTk3LDExNCw5MiwyNyw5OCwyMzQsMjMyLDE0MywxMDAsMTQ4LDEzNCwyMDgsMjI3LDMwLDUzLDI0OCwxMTAsMTc1LDI0NCwxODYsMzIsMTI0LDcsMTY4LDI0MCw1MSwyMDMsMjQ5LDIyLDYsMTk0LDcwLDExMSwyMSwxMTgsMywxOTIsNDIsMTc3LDI0MCwxNiwxMTcsMTgxLDI3LDQxLDIwMiwxNSwzOSw2NiwxODEsMjAxLDY2LDg3LDE3OSwxMjksMTAwLDExNCwxNDQsNzcsMTI2LDkyLDIwMiwyMjAsNjQsMTE3LDE0MiwyOCwxMzIsMjE2LDI0OSwxNzcsNTEsMTUwLDEwMiwyMzksMTIzLDMwLDE5LDgwLDIwNCwxNjAsMjMsMTM3LDExNiwxNTYsMTI1LDEyOCwxMzEsOTksMTg4LDYsMjAsMTQ0LDE1MywyNCwyMjcsMjEzLDgwLDI0OSw0MSw2Nyw2NSwxLDQyLDI0NCwxNDYsMTI5LDE4Myw4MywxLDE5NCwxNjQsMTQsMTcxLDE4NCwxMjQsMTI4LDExNyw3MywxMTQsNDksMTc3LDE5Myw4NCwxNTUsMTUyLDAsNTgsNTksMjQ1LDEyMSwxMjcsMjEzLDEzMCwxMDYsMTc5LDcsMjE5LDE2OSwyOCw3MiwxMTcsNSwxOCw5MCwxODQsMjQwLDgyLDE3MCwyNTAsMjQ1LDYzLDQwLDE4Miw2OCw4NCwxNTUsMTc0LDUxLDE3NiwxMjgsMTAwLDExLDE1MSw4NSwyNTAsMTg2LDExNiwyNywyNSwxNTYsMTcxLDUxLDE2NCwxOTcsMjAzLDE4NywxODAsNDYsMTQ3LDIxNSw2NCw3MCwxMTYsMTYsMjE3LDIyOSw5OCw1MSwxNTAsMTQ3LDIxNiwxNzcsMTQ2LDE5MSwxNTMsMTQzLDM2LDIzLDMxLDIxOCwxOTksMTYyLDIwLDIxMiwxMCw3MCwxNjQsNTQsMTYsNjUsMjQsMTE5LDE3Niw0LDIwOCwxNTEsMTg4LDUxLDcxLDE4OSwxMDksMTY2LDE1NiwyMTEsMTU2LDE4NywxOTgsMTM4LDUzLDEzOSwxNTQsNDMsODcsMTUwLDQ5LDE3OSwyNDAsNTEsMTEwLDExOCwxMzIsMjEwLDkwLDEzMCwxNTQsNjQsMTMwLDIxMywyMjcsMTg2LDUxLDEwNywxOTEsOTEsMTU0LDQ3LDEzNCwyMDgsMjA1LDIxMywyMTIsMjA2LDUsMTI4LDE4NywxMDcsMTMzLDEwMSwxOTEsMTE1LDE4LDEzOCwxMDUsMjcsOTgsMjI1LDEzMywyMTgsODEsMzgsMTE5LDIxNyw0OCwxMzYsMjEwLDIwNCwxMjAsMTY3LDcyLDk3LDEzNSwxMDYsMjAzLDM0LDE2LDQ1LDE1NCwxOTAsOTIsNTEsNDYsMjA5LDk2LDE4MCwxNjgsMTAwLDQwLDI1MCwxOTIsMTQ3LDIxOSwxMDUsMTgzLDg3LDQzLDE1OSwxODUsMjQ3LDUzLDIzOCw0NCwxODAsNDYsMTgsMjgsMjE2LDE2NSwxMDEsMTYwLDIzMSw1Myw2MywxMTIsMTAzLDIxNywxODYsMTAyLDEyOSwxOCwxNDEsNzYsMTE5LDczLDIxMiw4MCwxNDcsMTU3LDk4LDEyNSwxMjIsMjAzLDU3LDI1LDE5NywxNSwxODcsMjE5LDMsMTI1LDgyLDEwMyw0MCwwLDQ3LDIyOSwyNSwxMTcsMjQ2LDIwLDE5MiwzLDIwMCwxLDE4MCwxNTAsMjA1LDIwMSw1NCwxMDYsMTgxLDExMyw0Myw4Myw0Myw1NCw3MiwxMDUsODAsMjUsNTksMzcsMTc1LDIxNCwxMjMsMzMsMTUzLDExLDIyMSwxOSwxMzEsMTMyLDI0MCw4MCw5NSwyNTEsNCwyNTQsMjUwLDgwLDEzMywxODcsMTgyLDExMiwyNDUsNzUsMTI0LDEwNywxMjEsNCwyNDAsMTk4LDI0MCwxNjIsNzIsMjI4LDI1MiwxMjAsNzcsMCw3MiwxODEsMTk4LDI5LDEwMiwzNywxODEsNDUsMTk3LDk3LDE1LDIzLDE4NCwxNjgsMTAyLDUxLDY0LDExNSwxMTMsMjM3LDIyNSwxMzgsODksMiwxMjgsMjM5LDE4MSwxNzUsMTAyLDIxMSwxNjAsNTQsMTgyLDM1LDIzMyw1MCw5LDEwNCwxMDAsMTMxLDI2LDg1LDQ4LDE1Nyw2Nyw2OSw3MywyMDMsMTI4LDQsMTcyLDE1OSwyNDMsNzQsMTQyLDE5NiwxNjksMTksNzksMTUyLDIsNiwzMCwxMzIsMTcxLDEwMywyMjEsNywyMTEsMTY5LDc4LDIzNSwyMDYsNjksMjgsMjUyLDEzLDY5LDEyNSw2NCwyNTQsMjQ3LDE3LDE2MCwyMCwyMDIsMTQ2LDAsMjMyLDAsMTIsMTcxLDk2LDc2LDEzMywxMzQsMjQzLDU5LDI0MCwxNTAsMTQ5LDk1LDIwOSwyMTksMTk5LDczLDE4OCwxMDUsMTAzLDEwMSwxODUsMjQ1LDEzLDk1LDIyMiwyMDQsMTYyLDQ1LDEzOSwzMiwxMDUsNV19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMzksMjM3LDI1MSwxNTIsMjMxLDE0MCwyNTIsMzMsMjM3LDE3NCwyNDIsMjQxLDEyNCwxMDMsMTMwLDI0LDc2LDEwNiwxNDUsMjI3LDAsNTEsNjgsMTEyLDE2MCw0Niw0Miw0OSw4MiwyMzMsNzcsMTE3XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NTAsIm5vdF9hZnRlciI6MTcyNzk1MDE1MH19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzIyMiwxNzksMywzMiw5NCwxMjcsNjYsMjQ5LDE4OCwxMDcsNzcsMTg2LDEyNiwxODksMTM1LDUyLDY4LDQwLDE3MiwxMTAsMTM1LDI0NCwyMDgsOTUsMTY3LDIyMywxNzcsMTgyLDE2MCw0NiwxLDQ1LDEwOCwxOTEsNzIsMTE5LDE5NiwyMiwxNTAsMTk0LDUzLDIyLDIwMyw5MSwyMjMsMjA0LDE1MSwxNDQsMTI2LDIwMywyNDksMTIxLDYyLDQ1LDQsMTQ0LDg5LDI3LDE4MCw4NCwyMzYsNjYsNTksMV19fX19XSwicGFyZW50X25vZGVzIjpbeyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsyMzcsMTQ3LDE0NSwyNTEsOTgsMTUsOSwxMiwxODcsMTAsMTkyLDE1NywxMTQsMjMsNjAsMSwyNDAsOTUsMjA1LDE4OCw1NCwyNDAsMzMsMTYwLDEyNSwyMjQsMTk2LDEzOSwxNjAsODQsMjE3LDE2NiwxMDUsMTE5LDExNiw5NiwyMDgsMTY3LDE4NiwxNjgsNDMsMTkyLDIxMywyMzAsMTQsMjksMTAsNTEsNjAsMTQ1LDE4OCw0NiwxMjEsMTk4LDMwLDU4LDE2Myw0LDI0NywxMTAsNzIsMTgyLDg2LDg2LDU4LDE1OSwxOTcsNTEsMTU3LDEwNSwzNCwxMzQsMTgwLDI4LDE3MywxOTQsMTMyLDEwNiwyNDcsMTMxLDE5Nyw2OCw4MywxMDAsMjQ0LDI0NCw1MCwzOCwyMTAsMjksNjgsMjUyLDM2LDcwLDI0OSw2NSw1MSwyMTIsODIsMTg1LDkwLDE1MiwxMzQsMzksMTMxLDQ5LDE4OCw2LDEwNSw5OCwxODMsNzIsMTA1LDkyLDIzNiw2NCwxMzgsNDAsMTksMjksMTUsNjAsOTgsODEsMTE3LDY1LDg3LDE1NiwxMCwxOTgsMTg2LDEzNiwxNzksMjQ0LDE3Niw0NSwyNiw1NSwyMzIsMTk0LDE4Nyw5NSw4MiwxNDUsNSwxMDAsNzEsMjA2LDEzMCwxOTgsMTA0LDIzNCwxODksMiwyMjUsMjEsMTI3LDM2LDE0LDEyLDE2Nyw1LDY1LDUwLDE1Miw0OCwxNDAsNTgsMzYsMjE4LDEyNCwxOTgsNiwyMDYsMTY5LDEwMiwxMDYsMTc2LDU2LDE5MiwxNzcsMTQ1LDE3Myw2MywzOCwxMTUsMTcsMjQ5LDE0LDE5NywyMTEsMTg2LDkyLDEwLDE2OCw1NSw2NSwxMDksMjE2LDIwMSw1NCwyLDEyMiwxMSwzMyw0LDE3Nyw1MSw5MCw5OCw3NSwxMTIsMTI0LDUyLDQyLDQxLDEyNywzNiwxNTIsNzMsOCwxMjAsNzksMTgzLDY5LDE3OCwyMjAsMTUyLDIxMSw3NiwxNjUsNDMsOTEsNjIsMTA0LDEzMiw3MSw0MCwzOCwxNDksMjQzLDEyMCwyMDcsMjIyLDEzNSwxNzgsMTk1LDEzMiwxNDQsNDUsMTY0LDIzLDE0MCw4OSw0NywxNjAsMTAsMTA5LDE0MCwyMDksMTA2LDEzOCwxNDQsMTY5LDE0MywxMiwzLDE5MSw3NCwxNTksMTk2LDIyNSwxMDEsMjE3LDIxMCwxNTksMTUzLDI0MCwyNiwyNTEsMjUxLDUsMTIsMTE5LDEyMywxMDYsMTcwLDAsMTc1LDcwLDk2LDE0OSwyMDgsOCwyMiw2NiwxOTEsMjA2LDEzNiwxMDAsMSw4Niw0NSwxNTQsMzcsMTg5LDExMiw2NywxNTQsMzEsMjUsOTUsNDAsMTY5LDE2MSwxMTEsMTY0LDM1LDIyOCwxMzcsNTcsMTI0LDIzNCw4OCw5Miw3NCw1MCwxNDgsNjAsMTY3LDUwLDIwOCw1Nyw4MywyMzEsMTA2LDIwMCwxMTIsMzEsNzksMTE5LDg4LDE0NiwxMjIsMjYsMTAzLDIxLDQwLDE1MCwxMzgsMTkyLDE1NywzNSwxMDIsMTIyLDEyOSwxNDYsMTI0LDIyNyw4MCw2NCwxNjUsNTksMTI5LDE1NSwzLDE4OSw5MCwyOCwxOTksMTUyLDkzLDMsMTEyLDEzOSwxOSw4OSwxNjAsMTAsMTYwLDEzNiw4LDIyNywxODcsMTc0LDU3LDE1MiwyMSw4NCw5Nyw3NSwxODEsMTEsNzEsMjA5LDgyLDEyNCwzMiw0NiwyOCwyNDYsNDQsMTIxLDgwLDYxLDExNywxMDUsOTAsMjMxLDEzMiwzNSwxOTMsNzEsMTQ2LDE1OCwyNywxNzksMjM4LDgzLDEwOCw2OSw4OSwxODgsMjI5LDIyNCwxMTAsMjM3LDExNSw5NCwxNTksMjAwLDUzLDEyOCwxOTIsMTI1LDEzMywyNTIsMTk4LDI0OCwzLDQsMCwxOTksMTIwLDE2NSwxODMsMTc3LDEzOCw4LDE2OCw5NywyMjUsOSwxOTksNDAsMTIxLDYwLDIwNCwxNCwyNDgsMjEwLDI1LDIzNSw4MSwxNCwyMTcsMTI4LDAsMTQxLDE5NCwxMjQsMTk4LDI0OSwxODAsNiw2NSwxMTgsMTMzLDE1MCw2Niw3OCw5MCwxNTUsMzAsNzEsMTYyLDE2NSw4Nyw2OCwxNDIsOTgsMTk4LDE1OCw4NCw0MSwxNTAsMzUsMTM5LDEwLDksMTQ0LDgsMTMzLDEwMiw3MCw3MSw1LDIwMCw2OSwxMjMsMTkxLDE2MSwzOCw5MSwyMTIsMzIsMjUyLDI0NywxNjIsMjE3LDEwNSwxODAsOTAsMTQwLDE0NiwxLDIxMywxNjAsNDcsMTM1LDE1OCwxMDIsMTE4LDIzLDI1MiwxNzAsNzQsNzEsNzUsNSwyMzQsNSw2NSwyMjgsMTA3LDM2LDg3LDEwLDExMiw5NywzOSwxMTUsMjE2LDMyLDU5LDE4MiwxMzksNTksMTc0LDIsMjMsMzAsMTA3LDc0LDU0LDE2OCwxNTAsOTIsMjEyLDE5MSwxMjUsMTAzLDE1NSwxNDMsMTQ2LDM4LDE4NCw5NywzMiwxMTQsMTk1LDcwLDQ5LDk2LDE4MSwxMDIsMjI5LDE4LDE2NywxNzksMTM1LDE2MywyNSw5MywyNywxMzMsOTAsMTkzLDUzLDE0MywxMTIsMzksMTI4LDE5OSwyOCwxNzMsMjA4LDQ4LDU0LDEyOCwxNjIsNDgsNDksMzksMTI0LDIzNCwyNDYsMTg1LDEzMiw2NCw2OCw5OCwxODAsMTE1LDk2LDEsMTg4LDM0LDgzLDk5LDE0OCwxNDcsMTQ5LDE4MCwxNywxMzAsMTc1LDksNDUsMjEsNjgsMTQxLDEwNiwxOTUsNjYsMjAyLDE2MSw1Myw3OSwyMDksMjcsMTEwLDY5LDUzLDg3LDE3NywxNzUsMjksMjQ2LDIwNSwxMTIsMjM1LDExMywyMzUsMTAxLDQyLDIxMywxMTksNCw5LDIzNiw0Miw2MCwxNDYsMTI4LDE1NywxODgsNTYsMjEzLDM2LDMwLDIzMSwzOSwxOTUsMjI0LDQ4LDE3MywyMTEsNDQsMTE1LDIyNSwxODYsOTMsMzQsNzQsMTkwLDEzMyw5MiwxNjEsMTE4LDI2LDE5NywxNDAsMzcsNTYsNDksMzcsMjYsMTA4LDI0MywxMTQsMjUxLDIyNywxMiwzLDU3LDUxLDIyMCwxNDUsMjIsMjMyLDQwLDI4LDM5LDEzNSwxNjksMTY2LDE3NiwyNCwxNTUsMTgsMTI2LDIxLDE5OCwxODMsMjUsNzYsNzQsMTQzLDM4LDY1LDIyNSwxNjAsMTEsMTcxLDE1NCw5NiwzOCwxMjIsMjksNywxNjgsMTc3LDYwLDUxLDExOSwyMjksMTA2LDg5LDIyLDE2LDEyMSwxNjEsMTMwLDEwMywxMDIsMjgsMjgsMjI1LDEwMCw4NiwxNSw1MCw3NSwxOTQsNDAsMTQxLDEzOSwxMTUsODYsNDcsMyw1NywxNzIsMTM1LDAsMiwyMzUsMjAxLDE0NywxNzIsMTY1LDE1MiwxNjYsMTQyLDE0LDI1Miw0NywyMDEsNDIsMTQ3LDEzMSwyMDQsNTAsNDEsMTksMTc5LDYsNTYsMTIwLDEyLDIzMCwxNjMsOTksMTI4LDE2LDEwMSwxODgsMTM1LDI1NCwyMzIsMTM3LDExMyw0MywxOTIsMjI1LDExMiwyMDAsMTYwLDIwLDIsMjU0LDE4MywyMSwyOCw1NSwxMzYsNjAsMjM1LDE1Nyw3NCwzNiwxMzksMjI2LDg2LDEzMCwyNTUsMjI1LDE2NywxODgsNDksOTEsMTc0LDE0MCwxMjksMyw4NCwxMDAsMTE1LDQzLDc1LDQsMTI0LDEzNSw0NSwxNjMsMzgsNDQsMTU2LDE0MCwxMTcsMTgwLDE3MSwxODAsMjgsMTkyLDUwLDEyOCwxMjcsNzcsMTksMTc4LDEzMCwxMzQsMTA3LDE4OSwxNzAsNzQsMjMzLDEwNSwxMjMsMjQ0LDEyMCwyMDcsMTMyLDEyMywxNzUsMTc4LDE4LDE4NCwyMjQsMzksMjEsMTgxLDEwMiwxOTUsMjE5LDE2MiwxMSwzNiwzNSwxMTYsNjYsMjEsMTY1LDE0MSwxMCw0NiwxMjMsMTcsMTkyLDcyLDM4LDE4MywxODksMTgxLDE3MSwyNTEsMTgzLDk2LDcxLDEzNCw0NiwzMCwyNDgsNDQsNjMsMTA4LDExNSw4MCwyMjAsMTQwLDEyNSwzNSwxMzIsMjI1LDIyLDUsMTA4LDI4LDE3OCw2MywxOTgsMTExLDIzNiw4NSwxNzksMTQ5LDEsMTgzLDE0MywxNjIsMTM5LDIyLDgwLDQ2LDIzNiwxMzksNjEsMTA4LDEzMywxMywxNzUsMjgsMTc5LDE2MywxODEsNzMsMTksMTAxLDQ3LDU1LDI0NSw0LDExMiwxMDAsNCwxNjcsMjI0LDEyNSwxNTAsMTQ5LDIwMSw1LDEyOSwxNzksMjM0LDIwMywxMzcsNTQsOTEsMTAxLDExOSwxNzEsOTgsNzUsOTksNjUsODUsMTI5LDE5NCw5LDksMTMwLDQsNTUsMTM1LDE2MywxNTMsNTEsMTM3LDIxLDMyLDIxMiwzOSw4MCwxMTcsNTQsOTUsNzcsMjAzLDExOSwxMjEsMTU0LDcsMjE0LDE0NCwxMjIsNzMsMTMxLDE2NiwxMjgsMTc3LDE4OCwxODMsMSwxNzQsMTUxLDE4NSw2NSw2MCw4Niw4MCw5OSw1OSwxNCwyNCw2Niw0MCwyMjEsNjgsNDcsNjUsNjUsMTc3LDEzMSwxOTIsMTQ0LDQ3LDcsMzcsMzMsNzYsMTY3LDI1NCwxNjcsMTIxLDIyMiwxNjYsMTU0LDYzLDk3LDE1OSwyNDAsMTY5LDEzNiwyMzMsMTIyLDExOCw1NywxMTIsMTk5LDEzMSwxNzcsMjgsNTEsMTQwLDE4MywxMTEsMjUyLDE1OSw5NCw3Niw1MywxMzAsNDEsMTcyLDExNCwxMTksMTMwLDE5NSwxMjQsNTYsMTIyLDIzMCwxMCwxMDgsMTEzLDEzNSwxNjcsMTU1LDQ3LDc3LDEwNyw2OSw4NCw3MCwxMDksMjUsMTYsMTE3LDE2Nyw3MiwxMzksMTMsMTAzLDE1NCwyNywyMTAsNjIsNDMsMCwxMjYsMTg0LDIwOCwxMDAsNDEsMjQwLDE4NSwxNjIsMjExLDU0LDU2LDIwNywxMTYsNDMsMTE3LDE4LDIxMCw0MiwyNTAsMjExLDI1MCwxMTIsMTM3LDExNywyNDcsMTUwLDExMyw0MywxNzMsMjI0LDE4MSw1NCwyNTEsNTIsMTMzLDEyMiw3NywzMSwxMzUsMjA3LDIwNSwyMzUsMjI0LDEzNCw1NCwxMzAsMjMzLDEwOCwyMDcsMTU1LDE1Myw5Nyw2Nyw3OSw5Miw0MCwyMDYsMjE2LDI0Myw4NywyMCwzMSwyMjEsNjUsODEsMjA2LDE3OSwyNTEsNDcsMzYsMTAsMTMxLDE3NSw1MF19fSwicGFyZW50X2hhc2giOnsidmVjIjpbXX0sInVubWVyZ2VkX2xlYXZlcyI6eyJsaXN0IjpbXX19fV0sImRlZmF1bHRfbGVhZiI6eyJub2RlIjpudWxsfSwiZGVmYXVsdF9wYXJlbnQiOnsibm9kZSI6bnVsbH19LCJ0cmVlX2hhc2giOlsyMTcsMTE3LDEyMSwxODYsMTEzLDEwOCwxNjAsOTEsMjUsMTY4LDI1Myw1OCwxODcsMjE0LDI0OCwxMTAsMTY3LDI1Myw0OSw5MCw1OCwxMDQsMTM4LDEsMjAsMTk5LDI1MSwyMjcsMTg5LDQ5LDIwMiwxODBdfQAAAAAAAABdAAAAAAAAZhJFcG9jaEtleVBhaXJzeyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fTIwAAFbeyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlszNCwyMTMsMTI2LDEwMiwxODAsMTk3LDcsMTE5LDUsMTQ4LDIzMSw1NywxNTYsODgsMTU0LDI3LDEwMSwxLDExNiwxMzIsNTQsNTgsODMsMTMwLDI0NywyNDcsNzMsMTc4LDQsMTQ0LDEyNiwyMTYsMTA1LDIzMiwxNDAsMTMsMjMyLDIyNiwxOCwyMzEsMTYzLDIwNywxNTgsMjMwLDEwNiw4MSw1LDQzLDExOCw4MiwyLDcwLDQwLDQsMjA2LDE4NywxNTgsMTk5LDExOCw4MCw1NiwyMjQsMzksMjE5LDExOCwxNTMsMzcsMjQ1LDI1LDQ2LDIyOSwxOTMsMTMzLDUyLDIwNCwxNTYsMzUsMjA0LDIzOSwxMjQsMTczLDIyMCw2NSwxNjQsMTk4LDE2NiwxNSwyMiwxMDQsMTkyLDY5LDIzNiwxNDgsMTA2LDE3OSwxNzUsMzksOTYsMTgsMjcsNTUsMTgxLDM3LDE2LDYwLDE5LDE3OCwxMTIsNzMsMjE3LDE4MywxNzksMTA4LDE0OSwyMzAsMTcyLDIwNSw5OCwxMjAsMTQyLDI1MywxNywxNzAsNjcsMTE3LDE5NiwxODIsMTcyLDEwMSwyMzYsMTcwLDEyMSwxODYsNTcsNzcsMzMsNjYsMzIsNzMsMTE3LDE3MiwxMjIsMTUwLDEzNCw4MCwyMjAsOTIsMjM5LDIyOSw5LDczLDIxOSwxNjgsMzMsMjUxLDMzLDk3LDE3MSwyMDQsNjYsMTQwLDYzLDM1LDE1MywxODksMjI5LDEwNiwxMzksMTAxLDE1NSwxODUsMTYwLDIxMCwzOCw1MCw0MSwxODMsNjksNTksMTYwLDQ5LDExMiwxNjgsMjM0LDEzNCw5LDIwMywzNiwxOTksMTYyLDEwOCw1MSwxNzQsMjQxLDEyNyw3MywxNzIsMTg3LDUxLDI2LDE1MiwxNzMsMTQ5LDgwLDg1LDU4LDE2NSwxODIsODcsMjAwLDIwNywxMjksMTI2LDEzNSwxMTksMTY1LDMwLDIzMSwxMzUsMjI0LDE5MywxNjgsMCwxNjEsMTM4LDYwLDI0OCwxMCwxODYsNDgsMzEsOTMsMTc4LDcsMTk3LDE5OSw1NCwyNDQsMTUwLDE1NywwLDEyMSw2OCwyNDAsMjQ3LDExMCwxNDksNjksMTEyLDE3NywxMCwxOTgsMjQ5LDEyMywxNTMsMTI4LDE5MiwyMywyNiw1Niw3NCwyMjcsMjE3LDk3LDEzMiwxODMsMTUsMjIsMTIwLDE3NSw5Myw0OCwxOCwxMTgsMTc4LDEzLDIzMiwxOTIsMTMxLDE1OCwxNzYsMTE3LDEyMCwxNjUsNTIsMjM5LDIyNiwxODUsMzYsMTE1LDEyMSwxNTksMTYwLDkyLDEyMSwxNDYsMTA2LDE3MSwxOTksOTMsMjMyLDIzLDM1LDI2LDMsNDAsNTIsNSwyMDYsMTgyLDIzMyw3NCwxODQsOTAsNiwyNDYsMTg3LDUsMTk1LDE4MCwxNyw5NSwyNDIsMTA5LDE2NCwzNywxNDYsODMsMTk3LDEsMzIsMTcxLDEyNiw4Myw4OSwxMDMsOSw3Niw5LDE3NCw2OSwxNjUsOTMsMTgzLDksNzMsMTYwLDExMSwxMjgsMTIsMTQ5LDE5OCwyNDgsMTE3LDAsMjgsMiwxNjcsMjM1LDIzLDczLDk3LDExNiwxMTMsMTUwLDE0NSwxNjksNywyMDUsMTEzLDE0Nyw3LDEzOCwxODAsOCw5NywxMDAsMSw5NSwxNjQsNTAsNTYsNjgsMTcyLDI0NCw2LDEzMCw4LDE5LDE3OCwxMTAsMjQxLDE1MiwxMzYsMTM0LDE5Niw1NiwyMDEsMTk2LDIwOCw1Myw4OSwxMzIsMjI2LDU0LDIyNCwyNiwyNywyMiwxNzgsOTIsNDYsNDIsNjksMTAwLDU2LDExOCw4MywzOCwxOTgsMCwzNyw4NiwxMTgsMjI1LDQsNDcsMzMsMjMsMjA4LDE5MiwxMDQsMTAsNzMsNzcsMjE3LDExNywxNCwxNjUsMjEzLDE1MywzOCw4MywxNTAsMTM3LDI0MywyMDAsMjM3LDE3LDM0LDEyOSwxNzgsODcsNTMsMTQ5LDE1MCwxMTcsMTM0LDE4Niw3NCwyMjYsMTQyLDEyMCwyMTEsMTAzLDc3LDE0OSw3NSwzMSwyMzAsMTU1LDE2MSwyMjgsNyw1OCwxNjcsMTQ2LDE3NCwxMzMsNiwxOTgsMjEsNTAsMTQ0LDI1MiwxNDMsMjQ4LDk4LDQsMTYsMTM5LDIwNCwxMyw0OSwxMTIsODIsMTgyLDYsOTIsMTY5LDcxLDgwLDY0LDQ1LDE0MSwzOSwxODEsMjA0LDEzMywxNjYsODUsNDEsMTQsMjAzLDIyOSw1MSwyNTQsMjIsMTUyLDYwLDE5OSw4Miw0MiwxODYsMzcsMzMsMjYsMSwyMzYsNzIsMTU1LDk5LDEyMSwxNTksMjIwLDIxLDE2NiwxMTIsNjcsNDQsNDMsMTYsMzYsMTU3LDE0MCw0NCw1NywxNDYsNTIsMTAsMTY2LDE0MywxNDUsMzIsODAsMTU0LDkyLDAsNjQsNDgsMTQwLDIyOCwxOTIsMTcxLDE3MywxNzAsMTk2LDQ3LDEwNCw3NSwxMDQsODAsNTAsMTU3LDEzNiwyOSwxMjcsMTM4LDQ1LDUsMjE0LDIyLDcxLDExNiw2MSwyMTcsMjgsMjcsOTQsMTI5LDYsMTIsNzYsMTQ2LDkwLDU5LDEyNCw0NiwxNjYsODYsMjA4LDgzLDE5NSwyNDgsMTE1LDU4LDE1OCwyMTYsMTM4LDIyNywxMTQsNywyNDQsMzksMjYsMjcsMjMyLDM1LDk2LDIzLDgsMjAsMTUwLDg4LDEwMiwxMTgsMTczLDEwMywxNTEsMTI4LDMyLDEzMiw0NiwxMzksNjQsMTY4LDI0MCwxNDQsNDYsNCwyMSw2NywzLDIyOSwxNTQsNjgsMTE5LDcwLDEwOCwyNDgsMTc4LDIwNSwxODUsMSwzNCw2Nyw0MiwxNDksODEsMTUzLDEzNSwxMzksOTksMjM1LDE4MSwxLDE2OSwyNiwyMiwyMDYsNTAsMTYsNTUsMTYsOSwxLDAsMTk1LDIzNCwxODUsMjAzLDI0MCwxOTcsMTAxLDI0MywyNDQsOTcsMTc4LDIwMiwxODcsNDAsMzksMTQ0LDEyMiw3LDYxLDE2OCwxLDE2NSwxNzYsNzQsMTI0LDE2OSwxMzksMTYxLDE0NywyMTgsMTgzLDExMywyMCwyNiwxMjAsMTAsMTA5LDEyMiw4MSwxMDEsMTIzLDExNCwxNDIsOTYsMTU0LDE3NCwyMjgsNzEsNzEsMjQ2LDIzMCwxMTgsNzUsMTM0LDIwNiw4OSwyMDEsMTEwLDIzNywxNzksNDAsMTYzLDIwOSwxODcsOTksMCw1NCwxMDMsMTYxLDcyLDIyMCwxMiwyMDYsMTM1LDExNCwxMTQsMTYzLDM2LDE1OCwxMjEsMTAxLDU0LDE1MiwzMiwxNjcsMTE4LDExLDE5NCw4Nyw4NywyMDIsNTIsMjA4LDIwMCwyNTQsMjQ1LDEzLDIwMCwyMiwxNDksMjA1LDEwNSwxMjEsODAsMTY3LDM1LDEwOCwxMzIsMTUxLDIwOSwxNzAsNjIsMTczLDE0OSwxODIsNzksMjMsMTYsMSw2OSwxMDksMTYsMjUyLDE2NCw2OSwyNDIsMTkxLDI4LDUsMTI0LDMwLDE2MiwxNjEsNzAsMzIsMTk5LDEwNiw2OSwyMDMsOTcsMjUwLDgzLDIyNCwyMDQsMTkxLDE5MSwyMzUsOTksMTI4LDU3LDE1MCwxMzcsNTcsMTQ2LDEwMywxNTMsMTQzLDcxLDU1LDEwMSwyMDQsMTU1LDc3LDUsMjI2LDI0LDQ4LDEzNCw2NSwxMywyMDksMTgxLDQwLDgzLDEwNiwyMzQsMTE5LDY5LDUwLDE2MywzOCw3MiwyMDIsODksMTY4LDgsMjAzLDIyMiwyNDcsMTE3LDEzMiwxMjMsMTE1LDExNiwyNDUsMTg5LDM1LDE2OSwxNDIsMTIzLDQsMTI4LDQxLDIyNywxMTYsMTQwLDEwMiwxMSwyNDIsMjI2LDE4Miw0Myw1MiwyMDEsMTM2LDE3OCwxODQsMTU3LDQsMTMxLDk1LDUzLDcyLDIxMiwxODEsOTQsMjM4LDIxNiw2OCwyMDMsMzYsMTE1LDM4LDIwOCwxOTgsMjE5LDE0OCwxNjMsNzEsMTMwLDExMSwzNywxMzIsMTk0LDk2LDkwLDEwOCwxNzMsMCwxNjQsMTYwLDI0MCwxNjgsNiwxOTYsMzMsMTIyLDE5NSw3LDEyNSwyMTEsOTcsMTY4LDIzMCwxNzcsOTksMTcyLDEzOSw5OSwxMjksODEsMjU0LDE1NCwxNDYsNCwxNjMsMTE3LDQwLDcsMTIxLDE1MCwxMDcsMjQsMTczLDIwOSw2Nyw2NCwxNzIsMTg1LDYwLDI2LDE5NCwxMjIsMTg1LDE3Myw0OCwyMzYsMTEwLDI1MywxNzgsMTUsMjUzLDIzMiw1MCwxODksMTUxLDE0MCwyNDIsMTc4LDEyMCwyMzcsNzYsMTc5LDIxOSwxMzksNjUsMTk4LDU5LDYsMTg3LDIzMywxNjIsNDYsMTk3LDkzLDYsMTgwLDksMjE2LDI1MiwxOTUsMjQwLDI0LDE2OSwxNTQsMTk2LDE2MywxMjMsMjMzLDQ5LDM1LDEyMywxMCwyMTEsNCwxOTMsNTMsMTk1LDQ1LDU3LDE1NSw3Miw5MSw0MSwxNDMsODQsMjUsMiwxMjgsMTM1LDE4NSwxNTYsNTAsMTk1LDIzMyw4NiwxNjAsMTE2LDg1LDE4LDcyLDE2MiwzOSwxOTcsMjQwLDE3NSw2NSwxNTUsMTI5LDQyLDIxMiw5LDYxLDMsNTEsNzUsMTc2LDU1LDEzOCwxMzUsMjA3LDIzLDEyOCwxMzIsMTY2LDIxMiwxNTcsOTAsMTE2LDk3LDEwNCwyNDMsMTAsMTUwLDE0MCwxNjIsMTY2LDg1LDEzNCwxNTcsMzIsMTIzLDI1MSwxMDMsMTgzLDIxNCwxNzAsMTQsMjQ4LDcyLDE4OSw1MiwxNjQsMTg1LDY4LDE0NCwxMDIsNywzNywxMDYsNTIsMjIwLDE1NSwyMTAsODIsMjA3LDc3LDI0NSwxOTcsMjUsNTIsMjcsMjA5LDI1MSwyMSwxOSwxOTUsNjIsODksMTM4LDMwLDgzLDQsMTQ0LDc4LDI0Myw4NCwxMjksMjMyLDEsMjE3LDE0OCwxMDEsMTY4LDEwMiw4Nyw5OSw2NiwxNzIsOCwyLDE1NSwyMTUsMTA4LDUzLDEyOSw4MywzNCwxOTcsMTI4LDMwLDIyMCw5NiwxMjMsODMsMzcsMzcsMTQ4LDY2LDE0OSwxOTEsNTMsNTYsMTYwLDExMiwxNzcsMzgsMjE5LDIyOCwxNSwxNDUsMTQ0LDE4Nyw2MSw0NCwxMjEsMjcsMjI3LDQyLDMxLDUsMTk4LDE2NiwxNjMsMjE2LDE4NSwxNTUsMTk1LDE2OCwxMDgsMTY0LDIzMCwyNCwzNiw1Nyw2NCwxMTUsMTMyLDEwNSwyNDAsMjEsODIsMjMsMyw4MywyMTMsMjA3LDIzMiw2MF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzExNSwxLDIsOTUsMjQ1LDU4LDYxLDE5NywyMDIsNjgsMjQ0LDE4OSwxMjgsMTgyLDEyOSwyMSwxODIsMTIsOTIsMjI4LDYyLDIzNCwxNzYsMTg4LDYxLDIxMCwxNDAsMjIxLDE0Niw2NiwxOTUsNzIsMjUsMjE3LDE4NiwxMTYsNTQsMTA4LDEzNCwxNTYsMTIwLDE3NiwyNCwxMTMsOTksMTAwLDk5LDIwNCwyNDUsMTE3LDE2OSw2Myw0NCwxNzQsMTUxLDIzMCw1OCwxNzQsMTA4LDIwMCwxNjcsMTgwLDYzLDIwLDk4LDEwNiwyMzQsMTcyLDEwMywxODYsMjQ2LDE2MCwyMDMsMjQ1LDEzMiwxMjksMTgyLDExNSwxNDMsMTksMTE1LDI0MCw5OSwxOTYsMjMzLDI1MCwxOTQsMjE3LDE2Nyw5NCwyMjgsNDMsOCwyMDUsMjQxLDE1NSwxNTAsNTgsMjAsMTQ2LDE5LDUyLDcxLDExOCw1LDE3MywxMiwxMjYsNjMsMzksMTgyLDI5LDU5LDE2OSwxNTUsMjE1LDIwLDQyLDI0NywxMzAsMTA2LDUyLDE3MSw5MCwyNDAsMTEzLDE2OSwxMzIsMTA2LDI0Myw0MSwxMzIsOTksMTY4LDM0LDEwMSw0OSw0Miw3MiwyMDMsOTAsMjQ0LDM5LDE1MywyMzUsMTEsMjMsMjEyLDQyLDE5NywyMTIsMTE0LDE3MSwxOTEsODIsODEsMjAwLDI0NiwxODQsMTksMjAsMTE5LDIyNiw2NiwxNzAsMjE2LDU3LDE2NSwzMSwxMjksNTAsMjAsODAsMTU2LDEwOSwxMjQsMTk3LDE1NiwxNjAsMTQwLDE2NSwxNjgsMTI0LDAsNzYsNTUsNjQsMjEsNTcsMjA4LDEwMSwxMzIsMjI0LDM2LDEwLDIyNCw0NCwxNjQsMjA2LDIyNywxMTgsMTI2LDEsMzgsMTM3LDE2NSwxMDYsMTQ1LDYsOTIsNTUsMTU0LDE4Nyw3NiwyMjksNiwyMDUsMTg3LDEwNCwyMTksMTEsMTI4LDIwNyw1MywxMDksNzEsMTM1LDk3LDI1MSw0MCw0OCwxNywxMjMsMTQ3LDExMCwxNjksMTI0LDIzNSwzMiwxNTUsMTk2LDEwNSw0NSwxOTcsMTA2LDE1NiwxMiwxMzAsMTMyLDI0NCw4NywxNyw1OSw2OCwxNywyMDQsMTM5LDU2LDY3LDgsMjA1LDcxLDY3LDEsMTA2LDIwMSwxMSwxMTcsODAsNzQsMTU5LDE1NSwxMzMsMTExLDExNywzNCwyNCw4NCw5MCw3NCwxNjUsMTM0LDQsODMsMTUxLDE1MCwxNzksMTAyLDEyMyw5NywxMTUsOTYsMjAyLDE2MCwyMTQsMjcsMjgsMTY3LDEwLDMwLDIzNCwxNTEsMTA5LDExMCw4MiwxODgsMjM3LDE3OCwxMjcsODgsMTMwLDEyOSwxODMsMjMwLDUxLDI1Myw4LDk2LDE4MSwyNTEsOTQsMTkwLDIwLDQ3LDQsMTAzLDExMSwxNjcsMTYsMTE4LDU3LDE2NiwxLDM5LDIyNSw4MSwxMDksOTcsMTg3LDE5MCwxODIsMTY2LDE1NCw3MiwxNDEsMTUsMjMwLDIwNSwyNDMsMTk2LDIwNiw0MiwzMywxOTAsMTM4LDExNCwxMjcsMTgwLDE5LDk0LDg5LDgsMTQ1LDE2NCwyNDMsMjAxLDY3LDIwNCwxNDcsODEsNzEsMjA3LDIyNywxNzEsMTU1LDU4LDExLDIxLDEyMywxOTQsMzgsMTc3LDE3MCwxODMsMjQ3LDEzNiw4LDEyMywxMzksMTQ5LDYsMTYyLDg3LDE5MywxNTAsMTk5LDE4OSwxNzYsMTQyLDI1NCwyNDUsNjEsODEsMTM3LDQyLDE1LDIsMjUsMTE1LDIwMiwxMTYsNjQsMTAyLDMzLDE4MSwxNjgsNTIsMTg4LDE3MSwxMzksNjYsMjA4LDE0NCwxNTIsMTAxLDkyLDEwMiw1NSwxMDQsMjIxLDEwNywxODEsNDAsMTg1LDgwLDE0MCwyNDYsMTY0LDIwNiw5Miw2MSwxNjQsMzUsOTMsMTQxLDE3NywxMDcsMjI0LDEzOSw3LDIxMSwxMzgsMTk3LDI4LDg3LDE0Nyw1MCwxMTYsMTQ1LDIxMCw4MCwxOCwxMDEsMTE0LDE1MywxNDMsMTc4LDEwOCwxNTYsMTUwLDE3MSwxMzAsMTM3LDE5MiwzNCw4NSw1OCwyNDMsNTEsMTk2LDI0NSwxMDMsMjAzLDE2NywyMTcsMTkyLDM3LDM0LDE0NCwxOSwxMSw1NCwyNDQsNTEsMTUzLDIxMiwyNCwxMTYsMTQyLDEwNCw5OSwyMDcsNDIsMTYwLDQ5LDEyMiw1NSwyNTAsNjYsMTA2LDk2LDIxNSw5NSwzMSwxNTEsMzUsODAsMTk0LDEzMCwxOCwxODQsMzgsMTkxLDgsMTgxLDIxMSwxODEsNzIsMTU3LDE2LDE3NCw1LDU4LDEyLDIyNywxOTIsMTg3LDExNCwxMSwxNCw4NSwxMjEsMTg4LDEwLDEwMywxNjAsMTA0LDcxLDEwNywyMTEsNjgsMTEyLDQ4LDExMiwxMzQsMTAwLDg0LDEzMyw5OCw5LDk4LDI1MSw1MSw3OSwyMDEsNTQsMTIxLDI0Myw3NiwwLDI1MiwyNDIsMTc3LDEzLDk2LDEsODgsMTM0LDQwLDI0NywxNiwxMTcsMjIxLDUzLDExMywyNDMsMjQxLDI3LDE4MCwxMzQsMzIsMjM3LDIyLDE2NCwyNTEsMTUwLDQ2LDE1NCwxMjEsMTMxLDg0LDY0LDExMCwxODYsMTU0LDQ2LDIxNiwzNSwxMDgsMTEwLDE5NywxODAsMTQ1LDE0NSwxMSw5NiwyNywxNDAsMTMyLDIxMCw3NywxMzAsMjE3LDE0MywxNCwyMDQsOTQsNjcsMTI0LDEwNyw1OCwxLDMzLDk0LDUzLDE0MSwxNiwyMzMsOTksMTkzLDEzNiw2NCw2MCwxMTksMTYxLDQ2LDExNywxODUsNTMsMTgsMTQ1LDI0MiwxMSw4MywxOTgsMjAzLDEzMyw4OCwyMjgsMTM3LDQ3LDIzMSwxNTcsMTUsMTY4LDE5OCwyLDY0LDE4NCw0NSwyNDUsMTcwLDI5LDIwMiwxMjEsNjMsMjAxLDE3MSwyMTgsNjgsMzksMTA3LDE5OSwyMDQsODEsMjUxLDQ1LDc2LDgwLDIwMiwxODcsNTIsMjEsNzMsMTE1LDE2Nyw0NSw1NSwxMzYsMTM0LDU1LDY0LDYzLDExLDk4LDE1LDE2OSwxODQsMjE0LDY0LDIwNSw2MywyNTIsMTAsMTMsMjI4LDIwNCwyMDksNjYsMTM4LDkxLDE5NCwxMDAsMjU0LDM4LDE0MSw0MywyMzAsODgsMzMsMTIsMTc4LDk2LDE5NSwyMCw4NSwxMDUsMjA0LDgxLDE0NSw1Niw0NSwyMzYsMTgwLDE2OCwxNTUsMTU3LDk0LDIxMiw2OSw5NywxMjksOTcsMjA1LDk2LDgxLDIyMSwxNTEsMjAzLDIxNCw5LDIwMiwxMzMsMTkyLDkxLDQ3LDExNiw2MSw5NSw2NCwyMDEsMjM2LDIyOCwxMzQsMjMwLDIyNiwxOTcsMTE5LDcyLDE0NSwyMjUsNDQsNjAsMzQsMTQ5LDEyNiwxNDQsMTc4LDYxLDEwMSw5MCw1NCw2MiwxMjksOTcsMjI0LDUxLDE5Myw3MSw4OCwxNywwLDQwLDE1NiwyNTMsNDAsNTgsNjcsNTMsMTcsMjAwLDIyOSw0MCwyMzcsMjI4LDE2Miw0NiwyNTEsMTgxLDIxMyw2MCwxNywxOTYsMjQzLDE2MiwyMTIsMTg1LDI0LDIzOSwxNjAsMjAxLDQxLDM1LDk3LDEzOCwyMiwxNTYsOTEsNjAsMywzMywxMTcsMjcsMjE4LDEzOCwxMzgsMjU1LDI1LDEyNSwxNzUsMTkzLDQ1LDEyNyw5LDEyOCwyNTQsMjQ4LDg1LDY2LDE4OCw0MCwxMjcsMjA4LDM3LDE0NywxODgsNDcsNjgsMjE3LDgsNCwyMDQsMTQsMTMzLDEzNCw5Myw4OSwyNTAsOTgsMTA0LDQ4LDQyLDE3NiwxNTAsMTgsMjUyLDE4Myw1NywxNDYsMTY3LDE0NSwxNjAsMTgxLDE5MCwyMSwzOCwyMSwxNDQsMzMsODUsODQsMjQ1LDg2LDgsMjE1LDEyMywzNiw5LDExOSwyMjYsNDgsOTIsMTE3LDMsMTYyLDQ4LDEzNiwxNjEsMTAxLDE5NiwyMiwzOCw1OSw3MCwzMSwyNDYsMTY4LDEwNiw5OSwxNTcsMTU2LDI0OSwxOTIsMTcxLDI1MiwxNjUsMjA0LDE0MCwxNjUsMTk5LDExOSw0OCwxMzIsMTI0LDEzNCwyMzMsMjE5LDE0NCw1MywxNTQsOTMsMTUzLDQsMTgyLDE0NCw3NCw0NywyMjksMjEzLDE5NCwxMDEsNDQsODAsMTkzLDE4OCwxNDcsMTQ0LDExNiwxMDEsMjIxLDE5NCwxNDgsMjQwLDI1MSwxOTksNCwxMTQsNjEsNzksMTIyLDkxLDE3MSwxOTgsMTg3LDEyMCwyMzIsMiwxMDIsMjIwLDEyLDE2Niw1NCwxMSwxOTEsNDksNjMsMjE4LDE3MSwxNTcsMTgsNTEsMTE4LDk0LDU3LDE0MCw4MCwzNywxMzQsNjAsOTksMTEsMTc3LDUxLDEyMSwxMTUsMTg1LDE5Miw4OCw1MywxMTUsMjMzLDE5NSw3Myw3NSwyMTgsMTQyLDIsNDIsNTMsMTM4LDM0LDg4LDE3NiwxMDAsNTIsMTMxLDY5LDE4NSw5MSwxNTYsMTYyLDI0NiwyNywxOTMsMTQsMjMzLDMwLDIwNiwxNjEsNDQsNjgsMTksNzEsMTc1LDE1MiwxNDAsNTcsMTgyLDE3LDYwLDEyMSw4NiwxMDAsMjQ1LDMzLDE3NCwxMTIsMTI0LDE4MSwyNywxNDYsMTM3LDIyOCwxMDcsMTE1LDg3LDE0NywxODUsMTE0LDY3LDk3LDEzNCwxMjIsMTE2LDUyLDUxLDIxNCwxNjUsMjAyLDYyLDAsMTYyLDMxLDIwMywyMSwxNSwxMiw4MSwxNzQsMjI5LDE4OCwxOTcsMTk3LDExMCw1NywyNDEsMTI2LDI0Miw5OCw3MSwxODMsNzIsODQsNDMsMTcsMTA1LDg0LDg1LDQ2LDExMSwyNDQsNCwzNywxNTEsNSwyMDYsMTk3LDE4LDExMyw3MSw2MCwxMjIsMjAwLDE1NiwyMjcsMTMwLDE5OCwyMzYsMTc4LDExNiwzMiw1MSw0MywxMjAsOCwxMSwxMjcsNTksMTA0LDczLDE5NSwxNzgsMzQsMjEzLDEyNiwxMDIsMTgwLDE5Nyw3LDExOSw1LDE0OCwyMzEsNTcsMTU2LDg4LDE1NCwyNywxMDEsMSwxMTYsMTMyLDU0LDU4LDgzLDEzMCwyNDcsMjQ3LDczLDE3OCw0LDE0NCwxMjYsMjE2LDEwNSwyMzIsMTQwLDEzLDIzMiwyMjYsMTgsMjMxLDE2MywyMDcsMTU4LDIzMCwxMDYsODEsNSw0MywxMTgsODIsMiw3MCw0MCw0LDIwNiwxODcsMTU4LDE5OSwxMTgsODAsNTYsMjI0LDM5LDIxOSwxMTgsMTUzLDM3LDI0NSwyNSw0NiwyMjksMTkzLDEzMyw1MiwyMDQsMTU2LDM1LDIwNCwyMzksMTI0LDE3MywyMjAsNjUsMTY0LDE5OCwxNjYsMTUsMjIsMTA0LDE5Miw2OSwyMzYsMTQ4LDEwNiwxNzksMTc1LDM5LDk2LDE4LDI3LDU1LDE4MSwzNywxNiw2MCwxOSwxNzgsMTEyLDczLDIxNywxODMsMTc5LDEwOCwxNDksMjMwLDE3MiwyMDUsOTgsMTIwLDE0MiwyNTMsMTcsMTcwLDY3LDExNywxOTYsMTgyLDE3MiwxMDEsMjM2LDE3MCwxMjEsMTg2LDU3LDc3LDMzLDY2LDMyLDczLDExNywxNzIsMTIyLDE1MCwxMzQsODAsMjIwLDkyLDIzOSwyMjksOSw3MywyMTksMTY4LDMzLDI1MSwzMyw5NywxNzEsMjA0LDY2LDE0MCw2MywzNSwxNTMsMTg5LDIyOSwxMDYsMTM5LDEwMSwxNTUsMTg1LDE2MCwyMTAsMzgsNTAsNDEsMTgzLDY5LDU5LDE2MCw0OSwxMTIsMTY4LDIzNCwxMzQsOSwyMDMsMzYsMTk5LDE2MiwxMDgsNTEsMTc0LDI0MSwxMjcsNzMsMTcyLDE4Nyw1MSwyNiwxNTIsMTczLDE0OSw4MCw4NSw1OCwxNjUsMTgyLDg3LDIwMCwyMDcsMTI5LDEyNiwxMzUsMTE5LDE2NSwzMCwyMzEsMTM1LDIyNCwxOTMsMTY4LDAsMTYxLDEzOCw2MCwyNDgsMTAsMTg2LDQ4LDMxLDkzLDE3OCw3LDE5NywxOTksNTQsMjQ0LDE1MCwxNTcsMCwxMjEsNjgsMjQwLDI0NywxMTAsMTQ5LDY5LDExMiwxNzcsMTAsMTk4LDI0OSwxMjMsMTUzLDEyOCwxOTIsMjMsMjYsNTYsNzQsMjI3LDIxNyw5NywxMzIsMTgzLDE1LDIyLDEyMCwxNzUsOTMsNDgsMTgsMTE4LDE3OCwxMywyMzIsMTkyLDEzMSwxNTgsMTc2LDExNywxMjAsMTY1LDUyLDIzOSwyMjYsMTg1LDM2LDExNSwxMjEsMTU5LDE2MCw5MiwxMjEsMTQ2LDEwNiwxNzEsMTk5LDkzLDIzMiwyMywzNSwyNiwzLDQwLDUyLDUsMjA2LDE4MiwyMzMsNzQsMTg0LDkwLDYsMjQ2LDE4Nyw1LDE5NSwxODAsMTcsOTUsMjQyLDEwOSwxNjQsMzcsMTQ2LDgzLDE5NywxLDMyLDE3MSwxMjYsODMsODksMTAzLDksNzYsOSwxNzQsNjksMTY1LDkzLDE4Myw5LDczLDE2MCwxMTEsMTI4LDEyLDE0OSwxOTgsMjQ4LDExNywwLDI4LDIsMTY3LDIzNSwyMyw3Myw5NywxMTYsMTEzLDE1MCwxNDUsMTY5LDcsMjA1LDExMywxNDcsNywxMzgsMTgwLDgsOTcsMTAwLDEsOTUsMTY0LDUwLDU2LDY4LDE3MiwyNDQsNiwxMzAsOCwxOSwxNzgsMTEwLDI0MSwxNTIsMTM2LDEzNCwxOTYsNTYsMjAxLDE5NiwyMDgsNTMsODksMTMyLDIyNiw1NCwyMjQsMjYsMjcsMjIsMTc4LDkyLDQ2LDQyLDY5LDEwMCw1NiwxMTgsODMsMzgsMTk4LDAsMzcsODYsMTE4LDIyNSw0LDQ3LDMzLDIzLDIwOCwxOTIsMTA0LDEwLDczLDc3LDIxNywxMTcsMTQsMTY1LDIxMywxNTMsMzgsODMsMTUwLDEzNywyNDMsMjAwLDIzNywxNywzNCwxMjksMTc4LDg3LDUzLDE0OSwxNTAsMTE3LDEzNCwxODYsNzQsMjI2LDE0MiwxMjAsMjExLDEwMyw3NywxNDksNzUsMzEsMjMwLDE1NSwxNjEsMjI4LDcsNTgsMTY3LDE0NiwxNzQsMTMzLDYsMTk4LDIxLDUwLDE0NCwyNTIsMTQzLDI0OCw5OCw0LDE2LDEzOSwyMDQsMTMsNDksMTEyLDgyLDE4Miw2LDkyLDE2OSw3MSw4MCw2NCw0NSwxNDEsMzksMTgxLDIwNCwxMzMsMTY2LDg1LDQxLDE0LDIwMywyMjksNTEsMjU0LDIyLDE1Miw2MCwxOTksODIsNDIsMTg2LDM3LDMzLDI2LDEsMjM2LDcyLDE1NSw5OSwxMjEsMTU5LDIyMCwyMSwxNjYsMTEyLDY3LDQ0LDQzLDE2LDM2LDE1NywxNDAsNDQsNTcsMTQ2LDUyLDEwLDE2NiwxNDMsMTQ1LDMyLDgwLDE1NCw5MiwwLDY0LDQ4LDE0MCwyMjgsMTkyLDE3MSwxNzMsMTcwLDE5Niw0NywxMDQsNzUsMTA0LDgwLDUwLDE1NywxMzYsMjksMTI3LDEzOCw0NSw1LDIxNCwyMiw3MSwxMTYsNjEsMjE3LDI4LDI3LDk0LDEyOSw2LDEyLDc2LDE0Niw5MCw1OSwxMjQsNDYsMTY2LDg2LDIwOCw4MywxOTUsMjQ4LDExNSw1OCwxNTgsMjE2LDEzOCwyMjcsMTE0LDcsMjQ0LDM5LDI2LDI3LDIzMiwzNSw5NiwyMyw4LDIwLDE1MCw4OCwxMDIsMTE4LDE3MywxMDMsMTUxLDEyOCwzMiwxMzIsNDYsMTM5LDY0LDE2OCwyNDAsMTQ0LDQ2LDQsMjEsNjcsMywyMjksMTU0LDY4LDExOSw3MCwxMDgsMjQ4LDE3OCwyMDUsMTg1LDEsMzQsNjcsNDIsMTQ5LDgxLDE1MywxMzUsMTM5LDk5LDIzNSwxODEsMSwxNjksMjYsMjIsMjA2LDUwLDE2LDU1LDE2LDksMSwwLDE5NSwyMzQsMTg1LDIwMywyNDAsMTk3LDEwMSwyNDMsMjQ0LDk3LDE3OCwyMDIsMTg3LDQwLDM5LDE0NCwxMjIsNyw2MSwxNjgsMSwxNjUsMTc2LDc0LDEyNCwxNjksMTM5LDE2MSwxNDcsMjE4LDE4MywxMTMsMjAsMjYsMTIwLDEwLDEwOSwxMjIsODEsMTAxLDEyMywxMTQsMTQyLDk2LDE1NCwxNzQsMjI4LDcxLDcxLDI0NiwyMzAsMTE4LDc1LDEzNCwyMDYsODksMjAxLDExMCwyMzcsMTc5LDQwLDE2MywyMDksMTg3LDk5LDAsNTQsMTAzLDE2MSw3MiwyMjAsMTIsMjA2LDEzNSwxMTQsMTE0LDE2MywzNiwxNTgsMTIxLDEwMSw1NCwxNTIsMzIsMTY3LDExOCwxMSwxOTQsODcsODcsMjAyLDUyLDIwOCwyMDAsMjU0LDI0NSwxMywyMDAsMjIsMTQ5LDIwNSwxMDUsMTIxLDgwLDE2NywzNSwxMDgsMTMyLDE1MSwyMDksMTcwLDYyLDE3MywxNDksMTgyLDc5LDIzLDE2LDEsNjksMTA5LDE2LDI1MiwxNjQsNjksMjQyLDE5MSwyOCw1LDEyNCwzMCwxNjIsMTYxLDcwLDMyLDE5OSwxMDYsNjksMjAzLDk3LDI1MCw4MywyMjQsMjA0LDE5MSwxOTEsMjM1LDk5LDEyOCw1NywxNTAsMTM3LDU3LDE0NiwxMDMsMTUzLDE0Myw3MSw1NSwxMDEsMjA0LDE1NSw3Nyw1LDIyNiwyNCw0OCwxMzQsNjUsMTMsMjA5LDE4MSw0MCw4MywxMDYsMjM0LDExOSw2OSw1MCwxNjMsMzgsNzIsMjAyLDg5LDE2OCw4LDIwMywyMjIsMjQ3LDExNywxMzIsMTIzLDExNSwxMTYsMjQ1LDE4OSwzNSwxNjksMTQyLDEyMyw0LDEyOCw0MSwyMjcsMTE2LDE0MCwxMDIsMTEsMjQyLDIyNiwxODIsNDMsNTIsMjAxLDEzNiwxNzgsMTg0LDE1Nyw0LDEzMSw5NSw1Myw3MiwyMTIsMTgxLDk0LDIzOCwyMTYsNjgsMjAzLDM2LDExNSwzOCwyMDgsMTk4LDIxOSwxNDgsMTYzLDcxLDEzMCwxMTEsMzcsMTMyLDE5NCw5Niw5MCwxMDgsMTczLDAsMTY0LDE2MCwyNDAsMTY4LDYsMTk2LDMzLDEyMiwxOTUsNywxMjUsMjExLDk3LDE2OCwyMzAsMTc3LDk5LDE3MiwxMzksOTksMTI5LDgxLDI1NCwxNTQsMTQ2LDQsMTYzLDExNyw0MCw3LDEyMSwxNTAsMTA3LDI0LDE3MywyMDksNjcsNjQsMTcyLDE4NSw2MCwyNiwxOTQsMTIyLDE4NSwxNzMsNDgsMjM2LDExMCwyNTMsMTc4LDE1LDI1MywyMzIsNTAsMTg5LDE1MSwxNDAsMjQyLDE3OCwxMjAsMjM3LDc2LDE3OSwyMTksMTM5LDY1LDE5OCw1OSw2LDE4NywyMzMsMTYyLDQ2LDE5Nyw5Myw2LDE4MCw5LDIxNiwyNTIsMTk1LDI0MCwyNCwxNjksMTU0LDE5NiwxNjMsMTIzLDIzMyw0OSwzNSwxMjMsMTAsMjExLDQsMTkzLDUzLDE5NSw0NSw1NywxNTUsNzIsOTEsNDEsMTQzLDg0LDI1LDIsMTI4LDEzNSwxODUsMTU2LDUwLDE5NSwyMzMsODYsMTYwLDExNiw4NSwxOCw3MiwxNjIsMzksMTk3LDI0MCwxNzUsNjUsMTU1LDEyOSw0MiwyMTIsOSw2MSwzLDUxLDc1LDE3Niw1NSwxMzgsMTM1LDIwNywyMywxMjgsMTMyLDE2NiwyMTIsMTU3LDkwLDExNiw5NywxMDQsMjQzLDEwLDE1MCwxNDAsMTYyLDE2Niw4NSwxMzQsMTU3LDMyLDEyMywyNTEsMTAzLDE4MywyMTQsMTcwLDE0LDI0OCw3MiwxODksNTIsMTY0LDE4NSw2OCwxNDQsMTAyLDcsMzcsMTA2LDUyLDIyMCwxNTUsMjEwLDgyLDIwNyw3NywyNDUsMTk3LDI1LDUyLDI3LDIwOSwyNTEsMjEsMTksMTk1LDYyLDg5LDEzOCwzMCw4Myw0LDE0NCw3OCwyNDMsODQsMTI5LDIzMiwxLDIxNywxNDgsMTAxLDE2OCwxMDIsODcsOTksNjYsMTcyLDgsMiwxNTUsMjE1LDEwOCw1MywxMjksODMsMzQsMTk3LDEyOCwzMCwyMjAsOTYsMTIzLDgzLDM3LDM3LDE0OCw2NiwxNDksMTkxLDUzLDU2LDE2MCwxMTIsMTc3LDM4LDIxOSwyMjgsMTUsMTQ1LDE0NCwxODcsNjEsNDQsMTIxLDI3LDEyMCwyMTIsOTcsNzksMTYxLDE3OCw3Miw4MiwxOTEsNTksMTMwLDExMCwyMzYsMjM0LDE1NCw0OSwxMjAsNTAsMjMsMzIsMTQ4LDExMywxNjksNDYsMjEsMTEwLDExLDEsMTkwLDExLDI1NSwyNCwyMzIsMjUsMTkyLDE2Niw2NywxLDU5LDIyLDEwOCw1MiwyMjEsMjE3LDE5NCwzOSwxMjMsMTYzLDQxLDEwNywyMDgsNjAsMjUwLDY0LDU1LDE5MSwyMzgsMjI4LDEwNyw2Nyw1MCw2NiwxMzcsMTEyLDczLDExNSw4MiwyMTQsOTgsMjUwLDIwNCwxNTAsMjQyLDc5LDIyMSwxODEsMjQ1LDM2LDE1MSwyNSwyMTgsOTQsMjQ1LDE4MiwyNTMsMTcwLDgsMTE2LDEyNiwxODcsNjIsMjU0LDE1NSwzNywxMjcsNjAsMjI3LDQyLDMxLDUsMTk4LDE2NiwxNjMsMjE2LDE4NSwxNTUsMTk1LDE2OCwxMDgsMTY0LDIzMCwyNCwzNiw1Nyw2NCwxMTUsMTMyLDEwNSwyNDAsMjEsODIsMjMsMyw4MywyMTMsMjA3LDIzMiw2MF19fX0seyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsyMzcsMTQ3LDE0NSwyNTEsOTgsMTUsOSwxMiwxODcsMTAsMTkyLDE1NywxMTQsMjMsNjAsMSwyNDAsOTUsMjA1LDE4OCw1NCwyNDAsMzMsMTYwLDEyNSwyMjQsMTk2LDEzOSwxNjAsODQsMjE3LDE2NiwxMDUsMTE5LDExNiw5NiwyMDgsMTY3LDE4NiwxNjgsNDMsMTkyLDIxMywyMzAsMTQsMjksMTAsNTEsNjAsMTQ1LDE4OCw0NiwxMjEsMTk4LDMwLDU4LDE2Myw0LDI0NywxMTAsNzIsMTgyLDg2LDg2LDU4LDE1OSwxOTcsNTEsMTU3LDEwNSwzNCwxMzQsMTgwLDI4LDE3MywxOTQsMTMyLDEwNiwyNDcsMTMxLDE5Nyw2OCw4MywxMDAsMjQ0LDI0NCw1MCwzOCwyMTAsMjksNjgsMjUyLDM2LDcwLDI0OSw2NSw1MSwyMTIsODIsMTg1LDkwLDE1MiwxMzQsMzksMTMxLDQ5LDE4OCw2LDEwNSw5OCwxODMsNzIsMTA1LDkyLDIzNiw2NCwxMzgsNDAsMTksMjksMTUsNjAsOTgsODEsMTE3LDY1LDg3LDE1NiwxMCwxOTgsMTg2LDEzNiwxNzksMjQ0LDE3Niw0NSwyNiw1NSwyMzIsMTk0LDE4Nyw5NSw4MiwxNDUsNSwxMDAsNzEsMjA2LDEzMCwxOTgsMTA0LDIzNCwxODksMiwyMjUsMjEsMTI3LDM2LDE0LDEyLDE2Nyw1LDY1LDUwLDE1Miw0OCwxNDAsNTgsMzYsMjE4LDEyNCwxOTgsNiwyMDYsMTY5LDEwMiwxMDYsMTc2LDU2LDE5MiwxNzcsMTQ1LDE3Myw2MywzOCwxMTUsMTcsMjQ5LDE0LDE5NywyMTEsMTg2LDkyLDEwLDE2OCw1NSw2NSwxMDksMjE2LDIwMSw1NCwyLDEyMiwxMSwzMyw0LDE3Nyw1MSw5MCw5OCw3NSwxMTIsMTI0LDUyLDQyLDQxLDEyNywzNiwxNTIsNzMsOCwxMjAsNzksMTgzLDY5LDE3OCwyMjAsMTUyLDIxMSw3NiwxNjUsNDMsOTEsNjIsMTA0LDEzMiw3MSw0MCwzOCwxNDksMjQzLDEyMCwyMDcsMjIyLDEzNSwxNzgsMTk1LDEzMiwxNDQsNDUsMTY0LDIzLDE0MCw4OSw0NywxNjAsMTAsMTA5LDE0MCwyMDksMTA2LDEzOCwxNDQsMTY5LDE0MywxMiwzLDE5MSw3NCwxNTksMTk2LDIyNSwxMDEsMjE3LDIxMCwxNTksMTUzLDI0MCwyNiwyNTEsMjUxLDUsMTIsMTE5LDEyMywxMDYsMTcwLDAsMTc1LDcwLDk2LDE0OSwyMDgsOCwyMiw2NiwxOTEsMjA2LDEzNiwxMDAsMSw4Niw0NSwxNTQsMzcsMTg5LDExMiw2NywxNTQsMzEsMjUsOTUsNDAsMTY5LDE2MSwxMTEsMTY0LDM1LDIyOCwxMzcsNTcsMTI0LDIzNCw4OCw5Miw3NCw1MCwxNDgsNjAsMTY3LDUwLDIwOCw1Nyw4MywyMzEsMTA2LDIwMCwxMTIsMzEsNzksMTE5LDg4LDE0NiwxMjIsMjYsMTAzLDIxLDQwLDE1MCwxMzgsMTkyLDE1NywzNSwxMDIsMTIyLDEyOSwxNDYsMTI0LDIyNyw4MCw2NCwxNjUsNTksMTI5LDE1NSwzLDE4OSw5MCwyOCwxOTksMTUyLDkzLDMsMTEyLDEzOSwxOSw4OSwxNjAsMTAsMTYwLDEzNiw4LDIyNywxODcsMTc0LDU3LDE1MiwyMSw4NCw5Nyw3NSwxODEsMTEsNzEsMjA5LDgyLDEyNCwzMiw0NiwyOCwyNDYsNDQsMTIxLDgwLDYxLDExNywxMDUsOTAsMjMxLDEzMiwzNSwxOTMsNzEsMTQ2LDE1OCwyNywxNzksMjM4LDgzLDEwOCw2OSw4OSwxODgsMjI5LDIyNCwxMTAsMjM3LDExNSw5NCwxNTksMjAwLDUzLDEyOCwxOTIsMTI1LDEzMywyNTIsMTk4LDI0OCwzLDQsMCwxOTksMTIwLDE2NSwxODMsMTc3LDEzOCw4LDE2OCw5NywyMjUsOSwxOTksNDAsMTIxLDYwLDIwNCwxNCwyNDgsMjEwLDI1LDIzNSw4MSwxNCwyMTcsMTI4LDAsMTQxLDE5NCwxMjQsMTk4LDI0OSwxODAsNiw2NSwxMTgsMTMzLDE1MCw2Niw3OCw5MCwxNTUsMzAsNzEsMTYyLDE2NSw4Nyw2OCwxNDIsOTgsMTk4LDE1OCw4NCw0MSwxNTAsMzUsMTM5LDEwLDksMTQ0LDgsMTMzLDEwMiw3MCw3MSw1LDIwMCw2OSwxMjMsMTkxLDE2MSwzOCw5MSwyMTIsMzIsMjUyLDI0NywxNjIsMjE3LDEwNSwxODAsOTAsMTQwLDE0NiwxLDIxMywxNjAsNDcsMTM1LDE1OCwxMDIsMTE4LDIzLDI1MiwxNzAsNzQsNzEsNzUsNSwyMzQsNSw2NSwyMjgsMTA3LDM2LDg3LDEwLDExMiw5NywzOSwxMTUsMjE2LDMyLDU5LDE4MiwxMzksNTksMTc0LDIsMjMsMzAsMTA3LDc0LDU0LDE2OCwxNTAsOTIsMjEyLDE5MSwxMjUsMTAzLDE1NSwxNDMsMTQ2LDM4LDE4NCw5NywzMiwxMTQsMTk1LDcwLDQ5LDk2LDE4MSwxMDIsMjI5LDE4LDE2NywxNzksMTM1LDE2MywyNSw5MywyNywxMzMsOTAsMTkzLDUzLDE0MywxMTIsMzksMTI4LDE5OSwyOCwxNzMsMjA4LDQ4LDU0LDEyOCwxNjIsNDgsNDksMzksMTI0LDIzNCwyNDYsMTg1LDEzMiw2NCw2OCw5OCwxODAsMTE1LDk2LDEsMTg4LDM0LDgzLDk5LDE0OCwxNDcsMTQ5LDE4MCwxNywxMzAsMTc1LDksNDUsMjEsNjgsMTQxLDEwNiwxOTUsNjYsMjAyLDE2MSw1Myw3OSwyMDksMjcsMTEwLDY5LDUzLDg3LDE3NywxNzUsMjksMjQ2LDIwNSwxMTIsMjM1LDExMywyMzUsMTAxLDQyLDIxMywxMTksNCw5LDIzNiw0Miw2MCwxNDYsMTI4LDE1NywxODgsNTYsMjEzLDM2LDMwLDIzMSwzOSwxOTUsMjI0LDQ4LDE3MywyMTEsNDQsMTE1LDIyNSwxODYsOTMsMzQsNzQsMTkwLDEzMyw5MiwxNjEsMTE4LDI2LDE5NywxNDAsMzcsNTYsNDksMzcsMjYsMTA4LDI0MywxMTQsMjUxLDIyNywxMiwzLDU3LDUxLDIyMCwxNDUsMjIsMjMyLDQwLDI4LDM5LDEzNSwxNjksMTY2LDE3NiwyNCwxNTUsMTgsMTI2LDIxLDE5OCwxODMsMjUsNzYsNzQsMTQzLDM4LDY1LDIyNSwxNjAsMTEsMTcxLDE1NCw5NiwzOCwxMjIsMjksNywxNjgsMTc3LDYwLDUxLDExOSwyMjksMTA2LDg5LDIyLDE2LDEyMSwxNjEsMTMwLDEwMywxMDIsMjgsMjgsMjI1LDEwMCw4NiwxNSw1MCw3NSwxOTQsNDAsMTQxLDEzOSwxMTUsODYsNDcsMyw1NywxNzIsMTM1LDAsMiwyMzUsMjAxLDE0NywxNzIsMTY1LDE1MiwxNjYsMTQyLDE0LDI1Miw0NywyMDEsNDIsMTQ3LDEzMSwyMDQsNTAsNDEsMTksMTc5LDYsNTYsMTIwLDEyLDIzMCwxNjMsOTksMTI4LDE2LDEwMSwxODgsMTM1LDI1NCwyMzIsMTM3LDExMyw0MywxOTIsMjI1LDExMiwyMDAsMTYwLDIwLDIsMjU0LDE4MywyMSwyOCw1NSwxMzYsNjAsMjM1LDE1Nyw3NCwzNiwxMzksMjI2LDg2LDEzMCwyNTUsMjI1LDE2NywxODgsNDksOTEsMTc0LDE0MCwxMjksMyw4NCwxMDAsMTE1LDQzLDc1LDQsMTI0LDEzNSw0NSwxNjMsMzgsNDQsMTU2LDE0MCwxMTcsMTgwLDE3MSwxODAsMjgsMTkyLDUwLDEyOCwxMjcsNzcsMTksMTc4LDEzMCwxMzQsMTA3LDE4OSwxNzAsNzQsMjMzLDEwNSwxMjMsMjQ0LDEyMCwyMDcsMTMyLDEyMywxNzUsMTc4LDE4LDE4NCwyMjQsMzksMjEsMTgxLDEwMiwxOTUsMjE5LDE2MiwxMSwzNiwzNSwxMTYsNjYsMjEsMTY1LDE0MSwxMCw0NiwxMjMsMTcsMTkyLDcyLDM4LDE4MywxODksMTgxLDE3MSwyNTEsMTgzLDk2LDcxLDEzNCw0NiwzMCwyNDgsNDQsNjMsMTA4LDExNSw4MCwyMjAsMTQwLDEyNSwzNSwxMzIsMjI1LDIyLDUsMTA4LDI4LDE3OCw2MywxOTgsMTExLDIzNiw4NSwxNzksMTQ5LDEsMTgzLDE0MywxNjIsMTM5LDIyLDgwLDQ2LDIzNiwxMzksNjEsMTA4LDEzMywxMywxNzUsMjgsMTc5LDE2MywxODEsNzMsMTksMTAxLDQ3LDU1LDI0NSw0LDExMiwxMDAsNCwxNjcsMjI0LDEyNSwxNTAsMTQ5LDIwMSw1LDEyOSwxNzksMjM0LDIwMywxMzcsNTQsOTEsMTAxLDExOSwxNzEsOTgsNzUsOTksNjUsODUsMTI5LDE5NCw5LDksMTMwLDQsNTUsMTM1LDE2MywxNTMsNTEsMTM3LDIxLDMyLDIxMiwzOSw4MCwxMTcsNTQsOTUsNzcsMjAzLDExOSwxMjEsMTU0LDcsMjE0LDE0NCwxMjIsNzMsMTMxLDE2NiwxMjgsMTc3LDE4OCwxODMsMSwxNzQsMTUxLDE4NSw2NSw2MCw4Niw4MCw5OSw1OSwxNCwyNCw2Niw0MCwyMjEsNjgsNDcsNjUsNjUsMTc3LDEzMSwxOTIsMTQ0LDQ3LDcsMzcsMzMsNzYsMTY3LDI1NCwxNjcsMTIxLDIyMiwxNjYsMTU0LDYzLDk3LDE1OSwyNDAsMTY5LDEzNiwyMzMsMTIyLDExOCw1NywxMTIsMTk5LDEzMSwxNzcsMjgsNTEsMTQwLDE4MywxMTEsMjUyLDE1OSw5NCw3Niw1MywxMzAsNDEsMTcyLDExNCwxMTksMTMwLDE5NSwxMjQsNTYsMTIyLDIzMCwxMCwxMDgsMTEzLDEzNSwxNjcsMTU1LDQ3LDc3LDEwNyw2OSw4NCw3MCwxMDksMjUsMTYsMTE3LDE2Nyw3MiwxMzksMTMsMTAzLDE1NCwyNywyMTAsNjIsNDMsMCwxMjYsMTg0LDIwOCwxMDAsNDEsMjQwLDE4NSwxNjIsMjExLDU0LDU2LDIwNywxMTYsNDMsMTE3LDE4LDIxMCw0MiwyNTAsMjExLDI1MCwxMTIsMTM3LDExNywyNDcsMTUwLDExMyw0MywxNzMsMjI0LDE4MSw1NCwyNTEsNTIsMTMzLDEyMiw3NywzMSwxMzUsMjA3LDIwNSwyMzUsMjI0LDEzNCw1NCwxMzAsMjMzLDEwOCwyMDcsMTU1LDE1Myw5Nyw2Nyw3OSw5Miw0MCwyMDYsMjE2LDI0Myw4NywyMCwzMSwyMjEsNjUsODEsMjA2LDE3OSwyNTEsNDcsMzYsMTAsMTMxLDE3NSw1MF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzMsMTg3LDE2MSw2OCwyMDgsMTI5LDIwNSwyMzIsNTEsMjI1LDE4LDQsMTMwLDE2OSwyMDMsMTg4LDExNSwxODgsMjM2LDY3LDE2NywxMjIsMTg0LDE5OCwxMTUsOTYsMTU0LDI1MiwyNDIsMTEwLDE5MywxNTQsMTg1LDI5LDI0NywxMjIsMTQxLDQ0LDE1Myw5MywxNjQsMTkyLDEyMSw1NiwxMTQsMTU5LDEzNSwxNjYsMTIxLDEwNywxLDE5NiwyNyw1NSwyNDMsMjE5LDExMiwyNTQsMTIxLDUwLDQ1LDE5Miw3NSw0MCwzMiwyMDIsMjQ0LDksMTI5LDIyLDU3LDE3Miw0MSwyMzIsODQsOTAsNTIsNzksMTYzLDExNCwzNCwyMjMsOTYsMjksMjQ3LDE0Niw3NSwzNywyMTcsMTA5LDEyOSwxNzksMTksMjQ4LDE5MiwxMjcsMjE4LDQ0LDE3MCw3OCwyMjUsMTUzLDkxLDUwLDU5LDE1MywxODQsNDQsNjcsMTM4LDIwNiwxNzgsMjE1LDYsMTMxLDE1MSwyNCwzOCwxMjIsMTY2LDIzMCw2OSwxMiwxNTIsMTY1LDE3MywxNzIsMTE2LDE0MSw2OCwyMDIsMTg3LDExLDIyNyw3OSw5MywyMTIsNzEsNjYsNDgsNDMsMCwxOTcsOTMsMTYyLDIyLDEwOSwyMTQsOSw1NCwxMTEsNTEsMjYsOTQsODMsMywyMDMsMTk1LDMsMTYxLDExNywxOTAsMjI0LDk5LDIwMywxNzcsMTI0LDExOCw4MCwyLDE0MiwxNTIsMTk5LDE0MSwxMTEsODAsNTQsNDIsMTM3LDE0OCwzNiwxMzYsMTAwLDIyNiwxODQsNjgsNzEsMTIxLDU0LDQ2LDg4LDE0NiwxNywxMzYsMTE3LDI0NSw1OCw3NCwyMzYsMTQwLDM3LDIzMiwxNzksMTg1LDEwMywyMTgsMTk2LDEwMiwxNTEsMjMsMjMsMjE3LDMxLDY1LDEyLDYxLDEyMyw5Miw3NiwxMTYsMjA5LDkzLDIzMyw5OCwzLDUyLDI0Nyw5NiwxNSwyNDcsNDUsMTg2LDE3MSwxMzIsOCwyMTcsMTYsMjE4LDI3LDEyOSwyMjksMTI0LDIwNCw4MCwxNTEsNjMsMTQsMjAzLDM2LDg0LDk4LDE1NCw5MiwxMTIsMzgsMTgyLDI1MiwxNzIsMjA2LDE2NywxOTAsMjM4LDE0NiwxNTQsMTEyLDEwLDQxLDY5LDE3NywyOSwxMjEsMTc5LDE2NCwyMzgsMjI3LDMxLDk4LDIyNSwxNTAsMTE1LDIxMywxNjgsMTg0LDc1LDU0LDIzMSwxNDYsOTksMTM5LDIwOCw1MCwxNjMsODEsNTYsMTk5LDE3Nyw1LDIwMywyMjUsNDYsNTEsMTk4LDE2MiwxODksODQsMTQ1LDMsMjQ0LDE5Miw1MiwxMDUsMzYsMTM2LDIxNCw5OSwyMzksNzEsOTUsOTcsMTMzLDE1NywxNzIsMTk0LDE3OSw1NywxMzUsMTM2LDIzOCw2Niw5MiwxNTgsMTY5LDEzMiwxMTEsMTA1LDE1OCwyNTAsMjIsMTgwLDQ0LDI0LDczLDEwNyw1MSw0MiwyNTAsMTA0LDM0LDE4NCwyMjQsMTQ2LDM1LDEyMSw0NywxNDAsMzYsODIsMjQ0LDIxLDI0LDIzOSwyNTAsMTY2LDI0OSw2OSwxMjAsMTA5LDEwNywxMTgsMjA2LDUyLDEwNSwxNjMsNzMsMjUsMzcsMjAwLDE1MCwxLDE1NSw0OSw1LDIyNSw0Niw4OSwyMzUsMTExLDc5LDE1MSwxNzIsMTk0LDEzMiwzNiwxMCwxLDEyLDM0LDIyNyw2NiwxNjAsMjQ2LDc4LDE4LDEzOCwxNTMsNTEsMjM1LDE3LDk0LDEzNSwxOTMsMTEsMjE0LDUyLDMzLDE5NiwxMTEsMjEwLDI0Miw0MCwyMjcsMjYsODcsMTI1LDE1NCwxMjAsMTE2LDEzMyw4Miw5LDIxOSwxOTYsMTcwLDI3LDEwLDE4OSwyMiwzOSwxMDUsMTcwLDk4LDE1LDEzMSwyMDUsNDEsODksMTQzLDM2LDI0MSwxOTYsMjI1LDE3OSw3NSwxODgsMTQ0LDMxLDU1LDM5LDExNiw1LDIsMTEsMTQ1LDczLDExMiwxODIsMTE5LDIwMiwyMjMsNDQsMTUzLDgzLDI2LDE5OCwxMzgsMjEsNTYsMTU2LDE3OCwxODIsNjgsMjQ0LDE4LDE4NywyMzIsMTQ1LDc1LDIzNiwxMTIsMjQ5LDE1MCw0MCwyMywxOTYsMTg1LDI1NCwxNTEsMTUsMTY0LDI1MSw0OSwyNDMsMTg2LDcwLDIwNiwxMDUsNzEsMjQ5LDE0OSw0LDI0NCwyMzQsMTksNzQsNTQsMTgxLDE3Miw1NywzMSwxODQsMjE5LDEzMywyMTcsMTIwLDU0LDE4Miw3NCwxMjEsMSwxOSw5OCwxOTksMTAsMTIyLDI1NCwzNywzMCwyMCw3LDIsMTE4LDE2OCwxNSwxOTcsODQsMzcsMjE5LDIxNiwxNTQsNDAsMTU2LDUsOTIsMTEyLDE1Miw2MSwxLDE5OCwxNjMsMTkyLDE2NCwxMiw2NCwxNSw1OSwxODQsMTU1LDEwMywwLDkwLDE1MiwyMDAsOTgsOSwyLDYzLDE4MiwyMzEsODUsMTMwLDE2NywxMzQsMTE4LDYwLDE1NywyMjgsMjA5LDE5MywyMDcsMTE2LDE0NywxNCwzNiwxMzksNDEsMjIwLDE1MiwxODQsOSwzNSw4MywxMTYsMTA5LDIzNSwxNzksOTksNjEsNzUsMzQsOTgsMTAxLDMsMzQsNzQsMzIsMTY0LDE5OCw1OCwyNDIsMTk5LDE3MSw3MywyMiw0Myw2NSwyMjUsMTU2LDM1LDMyLDMxLDEyLDIzNiwyNiw2MCwxMTksMTY3LDE1OSwxMDcsNyw5OSwxODUsMTg0LDExMywxMDQsNDYsMjM2LDMsMTM3LDc4LDIxOSw5MCw4NCwyNDEsMTksMjAzLDUzLDEzOCw3NywxNyw2MCwxODIsMzQsNjcsMjEwLDczLDE3NiwyNCw2NSwxNTcsOTQsNDQsMTMzLDE3OCwyNDIsMTQ0LDE0MSwxMzgsMTIwLDE3MiwxODIsMTE2LDYxLDQyLDE1MSwxOTUsNjcsMTk0LDczLDEzMSw2MCwzLDI0Myw4MCw5OSwxNjMsMjA3LDIwMCwxOTMsNzUsMzIsMjI5LDEyMiw4Nyw0MCw3NiwyMjYsMTg4LDUxLDExNyw3MywxNTgsMjA3LDE3MCwxMzEsMTkzLDg4LDIzLDM1LDEyOCwxMTIsMTMwLDIyNywxNzAsNTcsNTAsMTg0LDQ3LDIzMSwxMjksMTYyLDcyLDYzLDIyMiwyMjcsMTA4LDgwLDMsMTY3LDU0LDEwMSwxNDQsMTk0LDIyMCw4MSwyMDgsMzksMTQsMTI3LDExNywxMDUsMTgxLDE0Nyw2LDE4NCwxMzAsMTcxLDIyOSwxMzAsNTAsMTIwLDE2MiwxNjYsMjQzLDg2LDEwMyw3NywzOSwxNjYsMTk2LDYsMTU1LDQ5LDE5OCw0NiwyNTIsMTMyLDI5LDkzLDIxMywxMDUsNjQsNTcsMTk5LDIyOSwzMiw5OSwyNywyMDksNzcsNTIsMTkzLDEzOCwxMzgsMTQ3LDY5LDkyLDE1MywxMTEsMjM5LDE1NSwxNjUsMjM4LDEwLDE2LDk2LDIxMywxMjUsMTQ4LDIzNSwxMTUsNCwxMTcsMTYwLDE2NSwxMzgsMTk4LDk2LDU3LDkzLDIzMywxMzksOTIsNTcsNDMsMTYwLDE2NiwxOSwxNzMsMiw5OSw1LDEwLDEwMSwxMDksOTMsMTcxLDI1LDIyLDY4LDEwNSwyMjMsMjMzLDc3LDE0Niw5NywzMCwxNTMsMTEzLDE3MywyNCwxOTMsMTEwLDI0OCwyNDcsMTk4LDIyMyw0MSwyMDQsMjQyLDI1MiwyMiwxOTUsMjA5LDU3LDE1LDM2LDc5LDM1LDc2LDE1Nyw4OCwxMzEsMTk0LDI1MywxNjksMTQsNywxMCwxMTYsMjM2LDExOCw2NCwxOTIsMjI0LDc4LDEyLDExNiw1MCwxNTAsNjUsMiwxNjgsMjMxLDE0MSw1Miw5Niw4NywyMDcsMTM4LDE5MSw3MCwxOSwxMCwyMTQsMTUzLDE5OCw5NCwyNSwxNzUsMTQsMTM1LDE0NCwxMyw1MiwxMTIsMjM0LDEzMSwxNzMsMTUwLDIxMywxMjEsMTY5LDMzLDQyLDE1MCw2Niw4Nyw4MywxMzksMTY2LDE2NiwzNSwxMzksOTAsMjI4LDExOCwxODQsMTc2LDQ2LDYyLDI0MSwyMDEsNTYsMTUwLDE5NSw1Myw1NiwxMjYsOTYsMTA1LDE4LDI1MCwyNTEsOTEsMTQ2LDIzMSwxODAsNDMsMTg1LDEzNCwyNCw1NSwxMzMsMTE2LDE1MCw2LDIxNywxMDQsOTMsMTk4LDEzNSw2MCwxNywxMjMsMjYsMTgzLDU1LDQyLDIyOSwxOTQsMzksNDUsMTM0LDkyLDI1NSw1MCwxOTMsMjE5LDIwMCwyMiwyMzEsODgsNDYsMjksMTgwLDc0LDkzLDIyNSwxNzgsMjE3LDEzOSw0MCwyMzYsMjM0LDEzLDE2NSwxNTEsMTU2LDY2LDExNCwyNSw2MCwzOSw0NywxMDQsMTY3LDg5LDEsMjI0LDE1MCwxNCwyNywxNjYsNDYsMTgwLDE0OCwyMDcsMTYsNzcsMTIzLDIzMiwyOSwyNTAsMjE2LDMwLDQyLDczLDE2OSwzNiw5Niw3NSw3NSw4NSwxNTQsOTQsMjQsMTQsNiwxNzEsMTQ0LDE4OSwyMzIsMTQ0LDIyMywxNTQsOTIsOTYsMjQ4LDIyLDE1LDE2NiwxMDMsMTk2LDk4LDQ5LDIzOSw2NCwxNzEsMTc4LDIxMCw3OSwyMzUsMjA0LDEwMCwxNyw4OSwxNDgsMTcsMTY2LDEwMCw4NSwxLDE0NiwxNjIsMjQsMTIwLDE2LDIzMCwyMDMsMzcsMTY0LDI2LDY5LDExNCw1MSwxOTgsMTI5LDY5LDE0Nyw4NywxMjMsMTk3LDE1NCw3MSwyMjgsMjE5LDMwLDY0LDMsMTAwLDIxNSwxOTgsMTU3LDE1OSwxLDI1LDY4LDQyLDQ2LDUyLDU4LDQ5LDE3NywyMTAsMjA1LDE0OCw4Nyw2Niw3MywxMDcsODUsMTIxLDE3MCwxOTcsNDIsMzcsMzUsMTU3LDE0NCw5OSwyMDQsODksOTEsMTgzLDE4MywxMTUsMTQ0LDEwOCwxNywyMCwyMTUsNzcsMjM3LDE0NywxNDUsMjUxLDk4LDE1LDksMTIsMTg3LDEwLDE5MiwxNTcsMTE0LDIzLDYwLDEsMjQwLDk1LDIwNSwxODgsNTQsMjQwLDMzLDE2MCwxMjUsMjI0LDE5NiwxMzksMTYwLDg0LDIxNywxNjYsMTA1LDExOSwxMTYsOTYsMjA4LDE2NywxODYsMTY4LDQzLDE5MiwyMTMsMjMwLDE0LDI5LDEwLDUxLDYwLDE0NSwxODgsNDYsMTIxLDE5OCwzMCw1OCwxNjMsNCwyNDcsMTEwLDcyLDE4Miw4Niw4Niw1OCwxNTksMTk3LDUxLDE1NywxMDUsMzQsMTM0LDE4MCwyOCwxNzMsMTk0LDEzMiwxMDYsMjQ3LDEzMSwxOTcsNjgsODMsMTAwLDI0NCwyNDQsNTAsMzgsMjEwLDI5LDY4LDI1MiwzNiw3MCwyNDksNjUsNTEsMjEyLDgyLDE4NSw5MCwxNTIsMTM0LDM5LDEzMSw0OSwxODgsNiwxMDUsOTgsMTgzLDcyLDEwNSw5MiwyMzYsNjQsMTM4LDQwLDE5LDI5LDE1LDYwLDk4LDgxLDExNyw2NSw4NywxNTYsMTAsMTk4LDE4NiwxMzYsMTc5LDI0NCwxNzYsNDUsMjYsNTUsMjMyLDE5NCwxODcsOTUsODIsMTQ1LDUsMTAwLDcxLDIwNiwxMzAsMTk4LDEwNCwyMzQsMTg5LDIsMjI1LDIxLDEyNywzNiwxNCwxMiwxNjcsNSw2NSw1MCwxNTIsNDgsMTQwLDU4LDM2LDIxOCwxMjQsMTk4LDYsMjA2LDE2OSwxMDIsMTA2LDE3Niw1NiwxOTIsMTc3LDE0NSwxNzMsNjMsMzgsMTE1LDE3LDI0OSwxNCwxOTcsMjExLDE4Niw5MiwxMCwxNjgsNTUsNjUsMTA5LDIxNiwyMDEsNTQsMiwxMjIsMTEsMzMsNCwxNzcsNTEsOTAsOTgsNzUsMTEyLDEyNCw1Miw0Miw0MSwxMjcsMzYsMTUyLDczLDgsMTIwLDc5LDE4Myw2OSwxNzgsMjIwLDE1MiwyMTEsNzYsMTY1LDQzLDkxLDYyLDEwNCwxMzIsNzEsNDAsMzgsMTQ5LDI0MywxMjAsMjA3LDIyMiwxMzUsMTc4LDE5NSwxMzIsMTQ0LDQ1LDE2NCwyMywxNDAsODksNDcsMTYwLDEwLDEwOSwxNDAsMjA5LDEwNiwxMzgsMTQ0LDE2OSwxNDMsMTIsMywxOTEsNzQsMTU5LDE5NiwyMjUsMTAxLDIxNywyMTAsMTU5LDE1MywyNDAsMjYsMjUxLDI1MSw1LDEyLDExOSwxMjMsMTA2LDE3MCwwLDE3NSw3MCw5NiwxNDksMjA4LDgsMjIsNjYsMTkxLDIwNiwxMzYsMTAwLDEsODYsNDUsMTU0LDM3LDE4OSwxMTIsNjcsMTU0LDMxLDI1LDk1LDQwLDE2OSwxNjEsMTExLDE2NCwzNSwyMjgsMTM3LDU3LDEyNCwyMzQsODgsOTIsNzQsNTAsMTQ4LDYwLDE2Nyw1MCwyMDgsNTcsODMsMjMxLDEwNiwyMDAsMTEyLDMxLDc5LDExOSw4OCwxNDYsMTIyLDI2LDEwMywyMSw0MCwxNTAsMTM4LDE5MiwxNTcsMzUsMTAyLDEyMiwxMjksMTQ2LDEyNCwyMjcsODAsNjQsMTY1LDU5LDEyOSwxNTUsMywxODksOTAsMjgsMTk5LDE1Miw5MywzLDExMiwxMzksMTksODksMTYwLDEwLDE2MCwxMzYsOCwyMjcsMTg3LDE3NCw1NywxNTIsMjEsODQsOTcsNzUsMTgxLDExLDcxLDIwOSw4MiwxMjQsMzIsNDYsMjgsMjQ2LDQ0LDEyMSw4MCw2MSwxMTcsMTA1LDkwLDIzMSwxMzIsMzUsMTkzLDcxLDE0NiwxNTgsMjcsMTc5LDIzOCw4MywxMDgsNjksODksMTg4LDIyOSwyMjQsMTEwLDIzNywxMTUsOTQsMTU5LDIwMCw1MywxMjgsMTkyLDEyNSwxMzMsMjUyLDE5OCwyNDgsMyw0LDAsMTk5LDEyMCwxNjUsMTgzLDE3NywxMzgsOCwxNjgsOTcsMjI1LDksMTk5LDQwLDEyMSw2MCwyMDQsMTQsMjQ4LDIxMCwyNSwyMzUsODEsMTQsMjE3LDEyOCwwLDE0MSwxOTQsMTI0LDE5OCwyNDksMTgwLDYsNjUsMTE4LDEzMywxNTAsNjYsNzgsOTAsMTU1LDMwLDcxLDE2MiwxNjUsODcsNjgsMTQyLDk4LDE5OCwxNTgsODQsNDEsMTUwLDM1LDEzOSwxMCw5LDE0NCw4LDEzMywxMDIsNzAsNzEsNSwyMDAsNjksMTIzLDE5MSwxNjEsMzgsOTEsMjEyLDMyLDI1MiwyNDcsMTYyLDIxNywxMDUsMTgwLDkwLDE0MCwxNDYsMSwyMTMsMTYwLDQ3LDEzNSwxNTgsMTAyLDExOCwyMywyNTIsMTcwLDc0LDcxLDc1LDUsMjM0LDUsNjUsMjI4LDEwNywzNiw4NywxMCwxMTIsOTcsMzksMTE1LDIxNiwzMiw1OSwxODIsMTM5LDU5LDE3NCwyLDIzLDMwLDEwNyw3NCw1NCwxNjgsMTUwLDkyLDIxMiwxOTEsMTI1LDEwMywxNTUsMTQzLDE0NiwzOCwxODQsOTcsMzIsMTE0LDE5NSw3MCw0OSw5NiwxODEsMTAyLDIyOSwxOCwxNjcsMTc5LDEzNSwxNjMsMjUsOTMsMjcsMTMzLDkwLDE5Myw1MywxNDMsMTEyLDM5LDEyOCwxOTksMjgsMTczLDIwOCw0OCw1NCwxMjgsMTYyLDQ4LDQ5LDM5LDEyNCwyMzQsMjQ2LDE4NSwxMzIsNjQsNjgsOTgsMTgwLDExNSw5NiwxLDE4OCwzNCw4Myw5OSwxNDgsMTQ3LDE0OSwxODAsMTcsMTMwLDE3NSw5LDQ1LDIxLDY4LDE0MSwxMDYsMTk1LDY2LDIwMiwxNjEsNTMsNzksMjA5LDI3LDExMCw2OSw1Myw4NywxNzcsMTc1LDI5LDI0NiwyMDUsMTEyLDIzNSwxMTMsMjM1LDEwMSw0MiwyMTMsMTE5LDQsOSwyMzYsNDIsNjAsMTQ2LDEyOCwxNTcsMTg4LDU2LDIxMywzNiwzMCwyMzEsMzksMTk1LDIyNCw0OCwxNzMsMjExLDQ0LDExNSwyMjUsMTg2LDkzLDM0LDc0LDE5MCwxMzMsOTIsMTYxLDExOCwyNiwxOTcsMTQwLDM3LDU2LDQ5LDM3LDI2LDEwOCwyNDMsMTE0LDI1MSwyMjcsMTIsMyw1Nyw1MSwyMjAsMTQ1LDIyLDIzMiw0MCwyOCwzOSwxMzUsMTY5LDE2NiwxNzYsMjQsMTU1LDE4LDEyNiwyMSwxOTgsMTgzLDI1LDc2LDc0LDE0MywzOCw2NSwyMjUsMTYwLDExLDE3MSwxNTQsOTYsMzgsMTIyLDI5LDcsMTY4LDE3Nyw2MCw1MSwxMTksMjI5LDEwNiw4OSwyMiwxNiwxMjEsMTYxLDEzMCwxMDMsMTAyLDI4LDI4LDIyNSwxMDAsODYsMTUsNTAsNzUsMTk0LDQwLDE0MSwxMzksMTE1LDg2LDQ3LDMsNTcsMTcyLDEzNSwwLDIsMjM1LDIwMSwxNDcsMTcyLDE2NSwxNTIsMTY2LDE0MiwxNCwyNTIsNDcsMjAxLDQyLDE0NywxMzEsMjA0LDUwLDQxLDE5LDE3OSw2LDU2LDEyMCwxMiwyMzAsMTYzLDk5LDEyOCwxNiwxMDEsMTg4LDEzNSwyNTQsMjMyLDEzNywxMTMsNDMsMTkyLDIyNSwxMTIsMjAwLDE2MCwyMCwyLDI1NCwxODMsMjEsMjgsNTUsMTM2LDYwLDIzNSwxNTcsNzQsMzYsMTM5LDIyNiw4NiwxMzAsMjU1LDIyNSwxNjcsMTg4LDQ5LDkxLDE3NCwxNDAsMTI5LDMsODQsMTAwLDExNSw0Myw3NSw0LDEyNCwxMzUsNDUsMTYzLDM4LDQ0LDE1NiwxNDAsMTE3LDE4MCwxNzEsMTgwLDI4LDE5Miw1MCwxMjgsMTI3LDc3LDE5LDE3OCwxMzAsMTM0LDEwNywxODksMTcwLDc0LDIzMywxMDUsMTIzLDI0NCwxMjAsMjA3LDEzMiwxMjMsMTc1LDE3OCwxOCwxODQsMjI0LDM5LDIxLDE4MSwxMDIsMTk1LDIxOSwxNjIsMTEsMzYsMzUsMTE2LDY2LDIxLDE2NSwxNDEsMTAsNDYsMTIzLDE3LDE5Miw3MiwzOCwxODMsMTg5LDE4MSwxNzEsMjUxLDE4Myw5Niw3MSwxMzQsNDYsMzAsMjQ4LDQ0LDYzLDEwOCwxMTUsODAsMjIwLDE0MCwxMjUsMzUsMTMyLDIyNSwyMiw1LDEwOCwyOCwxNzgsNjMsMTk4LDExMSwyMzYsODUsMTc5LDE0OSwxLDE4MywxNDMsMTYyLDEzOSwyMiw4MCw0NiwyMzYsMTM5LDYxLDEwOCwxMzMsMTMsMTc1LDI4LDE3OSwxNjMsMTgxLDczLDE5LDEwMSw0Nyw1NSwyNDUsNCwxMTIsMTAwLDQsMTY3LDIyNCwxMjUsMTUwLDE0OSwyMDEsNSwxMjksMTc5LDIzNCwyMDMsMTM3LDU0LDkxLDEwMSwxMTksMTcxLDk4LDc1LDk5LDY1LDg1LDEyOSwxOTQsOSw5LDEzMCw0LDU1LDEzNSwxNjMsMTUzLDUxLDEzNywyMSwzMiwyMTIsMzksODAsMTE3LDU0LDk1LDc3LDIwMywxMTksMTIxLDE1NCw3LDIxNCwxNDQsMTIyLDczLDEzMSwxNjYsMTI4LDE3NywxODgsMTgzLDEsMTc0LDE1MSwxODUsNjUsNjAsODYsODAsOTksNTksMTQsMjQsNjYsNDAsMjIxLDY4LDQ3LDY1LDY1LDE3NywxMzEsMTkyLDE0NCw0Nyw3LDM3LDMzLDc2LDE2NywyNTQsMTY3LDEyMSwyMjIsMTY2LDE1NCw2Myw5NywxNTksMjQwLDE2OSwxMzYsMjMzLDEyMiwxMTgsNTcsMTEyLDE5OSwxMzEsMTc3LDI4LDUxLDE0MCwxODMsMTExLDI1MiwxNTksOTQsNzYsNTMsMTMwLDQxLDE3MiwxMTQsMTE5LDEzMCwxOTUsMTI0LDU2LDEyMiwyMzAsMTAsMTA4LDExMywxMzUsMTY3LDE1NSw0Nyw3NywxMDcsNjksODQsNzAsMTA5LDI1LDE2LDExNywxNjcsNzIsMTM5LDEzLDEwMywxNTQsMjcsMjEwLDYyLDQzLDAsMTI2LDE4NCwy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MzYsMjQwLDEzMywxMTAsMjUzLDQ0XX19XQABeyJwcm9wb3NhbCI6eyJBZGQiOnsia2V5X3BhY2thZ2UiOnsicGF5bG9hZCI6eyJwcm90b2NvbF92ZXJzaW9uIjoiTWxzMTAiLCJjaXBoZXJzdWl0ZSI6Ik1MU18yNTZfWFdJTkdfQ0hBQ0hBMjBQT0xZMTMwNV9TSEEyNTZfRWQyNTUxOSIsImluaXRfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxOCwxMDQsMTIsOTYsMTEsNzgsODUsMjQ5LDIwNiwyMjQsNjcsMTg3LDQxLDg0LDQ3LDE5MiwyMTYsMTQ1LDEzOCwxMzAsMjA2LDI0Myw1NywyNywxMzMsMTM1LDIwNSwxNiwyNTEsNzgsNDAsMjI0LDUzLDcyLDksMTU1LDIyOSw5MSw1NiwyMTksMTQ2LDE5MSwyMzYsMjI2LDMyLDI1Miw1NywxOTMsMTc2LDE2OSwxODYsMjQ1LDg4LDE0Miw1MiwxNTYsMjUsOCwxOTMsMTE0LDEyNyw4MCw2MCwzLDcwLDc3LDkxLDkxLDE0MSwxMDcsNTEsMTY3LDE5NCwxNDcsNTUsMTg3LDE3MiwxMDYsMTYyLDk5LDU1LDE5Nyw4MCwzMCwyMDMsNzQsMTk2LDUwLDgyLDE5OCw5NCwyMTUsMTU3LDI1NCwyMCw3LDE1Miw5MCwxMjMsMCwxMTcsMjAwLDE4OSw1LDEwOCw4OSw1Miw1LDE1NSw1NSwxMzIsMjI0LDgwLDE0OSwyMjUsODQsNDQsMjMyLDExNywxNzAsMjMwLDg3LDUwLDEyOCwxNTEsNzUsMTA2LDI1MiwxMTEsMTk0LDE0NiwxMjIsODMsMTM5LDg0LDMzLDMzLDg4LDE1NiwxMTksMTAsNDMsMTUzLDIwMyw3MiwxMDUsNDAsMTYsMTcsMTcsMTAyLDE1NSwzNywyMTksMTg0LDYzLDIxNiwxODAsMTAwLDIzNSw5OCwxMTMsMjM3LDU4LDQyLDMyLDE2NCwzOSw4NywyMjAsMTQ0LDI1MSwxNjAsNjQsMTE3LDEwNCwzMCwxODgsNTYsMTQ0LDg0LDEyMCwxNzgsMTQ4LDgwLDE0Nyw0Nyw1Niw4NiwyNDgsNjUsODAsMTMwLDE2OCw4Myw2NCwxMDcsMTUyLDIwMSwxODUsOTMsMjQ2LDQzLDEzMSw5OCwxNzgsMTI4LDE0NiwyMDksOTIsMjU1LDc2LDE2MiwyNDcsMTQ5LDY4LDE3LDE5MiwxMzQsMjYsMTE1LDk4LDE0MiwxMjgsMTE2LDE2MCwzNCwxMiw5OCwxNTEsNTMsMjM1LDE0NSw3MywyMiw1MCw3NSwxNDAsMjAsMjAsMjA2LDEzMCwyMDAsMjAsMTEsNjMsMTI3LDkxLDYzLDEwMiwxOTgsNjcsNDAsNTgsNDUsNjksOTIsMTU3LDE2LDEzMCwxOTAsMjIsMTIxLDQ4LDc5LDE4Miw1NSwzLDE0NCwxODgsMjI5LDQzLDg0LDE3MSwxOTYsMTg0LDEsMTE5LDE1NywyMjYsMTM4LDI4LDE3OCw4LDMzLDIwNSwyNTIsNjMsMTc1LDE3OSwzLDEzNCwxOTYsMTgyLDIzMCwxNDgsMTcwLDExNCwxNTUsMTU2LDE0Myw3LDExMywyMywyMTYsMTgsMTIyLDI0NSwxMTMsMjE2LDE4MSwxMDUsMTk4LDEzOSwxMjksNTYsMjM1LDg2LDIxNCwxNzIsMTg0LDQ5LDIzMiwxMiwyMzcsMTEzLDIwNyw3NCwyNCw5MiwxMDAsMTYsMjAsMjMzLDIzNiwzLDEwNSwxMTgsMTg4LDE4OSw1MiwxODUsMjAsMjQ4LDE0NywyMTgsMTk2LDIyLDE5LDIxNCwxMDcsMjgsMTU2LDExMSw4MiwyMzMsMTAyLDE5MSw1NiwxMjksMjAwLDY2LDgwLDEzNywxNzYsMTQ4LDEwMCwyMTksMjYsMjA4LDEzMSwxNTQsMjksNzYsMTUyLDMyLDU2LDE1MiwyMDIsNDIsMTY4LDE3NywzNCwxMjQsMiw0NCw1LDE2MiwxODQsMzQsNjksMTksMTc4LDQwLDIxMSwxOTIsMTgwLDMzLDcwLDM3LDgsODQsMTQ1LDEwMSwyMCwzNCwyMjAsMTI0LDMyLDczLDk0LDE0MCwxMzIsMTU2LDU4LDIxMiwxMzEsMTY1LDEzNCwxMzEsMTY5LDE4NCwxODIsOTQsMTU1LDM3LDE2MywyNDAsMzAsMjQ4LDEwNSw3NCw4MCw5MSwxNjIsMjQzLDE4MCw1LDE4NSwxODAsMjcsMTI4LDE3OSw0NCwxMDgsOTcsOTgsMTEyLDE1Niw5NSwzNCwyNywxNDcsMTIsNTgsNSwxMjEsMTk4LDUxLDIwNywxMzksMTA1LDExNCwxODIsNzMsMTYzLDE4MSw3MSwyMjAsMjUxLDE3NSwxOTEsMywxNjcsMjI0LDY2LDE3NCwyMjEsMjI0LDEzNCwyLDQzLDIwNSwzNCw0OCwxMiw0NiwxMjMsMTIsMTUzLDAsMTEsMTksMTI4LDk5LDc0LDE0NywyMDMsMTA5LDEwMywxMDcsMzAsMTUwLDE0OSwxODgsMjEzLDEzMiwzNywxMjksNDEsMTUwLDIxMyw1OCwxNDEsNjUsMTIwLDQwLDU3LDg4LDEzNCwyMjcsNDIsMjA4LDIyOCwxODQsMTE1LDE5NSwxNTAsMjUsMjE5LDEzMCw1LDExMyw2NCwxNTEsMTM5LDU0LDIxNCwxMCwxMzgsMjA1LDEzMSwxMzAsMjksMTYzLDExOSwxOCwxOTQsMTA4LDMwLDEwNiwxMzcsMTAzLDksODQsMjI2LDUsMTc3LDE0MSwxNywxOTAsNywxNjIsNzQsMTk2LDE0NSwxNCwzMCwxNDcsMTgyLDcsMjMwLDE5MCwxMDUsMjQ4LDQxLDI0NywxNTMsMzcsMTI4LDE3MSwxNDIsMTcwLDEwMiwxMzksMSwxNjQsMTcwLDE1NiwxNTEsMjA0LDExNiwyMDAsNzYsNTksMTE0LDE3NCwxOTksMTcsNzUsMTU0LDIxLDYzLDMyLDE5OCwxNzQsMjMsMTQ2LDU4LDI0NCwyNSwxOTAsMjI1LDEyMywxMjMsMjI5LDE3OCw4NiwyNDUsNzAsMTI5LDI1NCwxMjAsNjksNzIsMjUwLDksMzEsNzQsODAsMjEsNSw5MSwyMzQsNzYsMTA0LDIyNiwxOTIsNzYsMjAsOCwxNjIsMTAsOTEsMTcwLDg3LDExNywxNiwyLDIwLDU3LDU0LDIxMiw4NCw3MywxNDAsMTE5LDIzOSw0NCwxNjcsNDcsMjUwLDYyLDE2Myw4OSw5NywxMzIsNDEsOTksNjcsMjI2LDEyLDIxNyw4LDc4LDYsODgsMTMsNjEsNDEsMTI3LDE2MiwxODgsMTkxLDQzLDE4MywxMSwyNywyMTEsMjcsMTksNSwxODIsMTk3LDIzMCwxNjQsNDUsNTcsMjA3LDIwMCwxMDIsNTcsMjI0LDExLDIwNSwxNTAsMjE3LDEyNSw2NiwxNjEsMTM4LDE1OCw0MSw0OSwxNTYsNDQsNiwxNTgsMjA5LDE1NCwxMDYsNTQsMTc5LDEwOCwxMjQsOTQsMjI4LDEyMCwxNjIsMjQ3LDI0NCwxMDEsMjA4LDE5MywxMzcsMTI1LDcwLDUsMjMzLDIyMCwyMDcsMjEsMTI0LDE3NCw0MSwxNTYsMTE3LDcwLDE4MywxMzUsMTkwLDE2Nyw3OCwyNywxOTIsMTg5LDI0MywyMTEsMjAzLDE0MywzLDM0LDE3LDEyOSwxODIsNjUsMTg3LDg5LDE4MCwxNzksMTAsODcsMTIsMTY3LDIxMiwxMzIsMzQsOTYsMTI0LDU5LDI1MywyMiwxMTQsMTc1LDE2OCwxMjgsNCwxODgsMTE5LDEzNSwxLDEyNiwxNTYsMjAyLDE4MiwxMDcsMjQ2LDQyLDE5MCwxNTMsNzYsMTU2LDEyMSwxNzEsODIsMzgsMTgwLDYxLDY0LDYzLDcyLDEwMyw2OSwxNiw0Myw2OCw5NCwxNTAsMTAsMTksNTUsMTcsMTUwLDg5LDc4LDAsMywxMTgsMTA1LDE3MSwxNzUsMTkwLDI1MCw4OSw1Myw5Miw2MywxMjYsOTgsMTE3LDUzLDIwMCwxMzQsMzYsMTk4LDEwNiwxODgsMTgzLDEyMiwyMjcsMTM5LDg5LDIxNSw2NywxMjMsNCwyMjcsMTQ4LDI3LDE5Nyw0NiwxODksODcsMTQ0LDE0NiwxNzksODEsMzEsMjE3LDgwLDE2LDE3OCwxMDUsMTcsMTA2LDE1OCwxMDEsMTU2LDIwNywxMjksMTE3LDc1LDE5NSw5NiwxNTgsMTIzLDEzMiwxNzcsMTEzLDIzMCw0NSwyMTEsMzMsMzYsMTcwLDI4LDE2NiwxOTUsMjEwLDQwLDE4OSw2LDE0NywyMDAsODYsMzAsMjE2LDc1LDE4OCwyMSwyNTAsMTU1LDEyMCw5Niw2MywyMDcsNjUsOTIsMTQsMTcwLDI5LDEyOSw5MSwxOTYsMiwxMTgsNDgsOTQsMTI4LDQ5LDEzMCwyMDgsODgsMjI5LDE4NCw3OCwyMTYsMjM1LDIwMCwxMiwxNjksNjYsMTQyLDM3LDM3LDE1OCwxOTcsMTQxLDIwNyw3LDM5LDIxLDE0NywxNTYsMjM0LDEzMiwxNDIsMSwxODAsMTAxLDk0LDIzMCwxOTIsNjMsMzUsMTQ1LDE3LDQ0LDEwNSw0MywyMywyNSw3MiwxOTIsOCwyMTQsOTAsMTYsMTk0LDEzNywwLDE5MiwxMiw5Niw5MSw4MSwxMTEsMTQ5LDE4Miw3OCwxMjcsMTQ1LDEzMCwxMDIsNTEsMzEsODQsMzksMTQyLDE1NywxNjYsMTYsMTksNjgsMTc0LDkwLDk4LDg2LDExMSwxMzQsMTM4LDEwLDI0MywxMTAsMjM0LDEyMiwyMDUsMjIwLDcxLDU1LDEyNSwxNTUsMTgwLDIzNSwxNTMsMTM2LDEwNiw3NiwxNjUsMjQ4LDkyLDkxLDE4OCwxMjIsMTUwLDEzMSwyMjgsMTA5LDExNCwxMDMsMTcwLDcxLDI0NywxNzcsMTAyLDE4NiwxMjQsNDEsMTUxLDcsMzYsMjcsOTYsMjUyLDQ4LDY5LDIyMywyNTIsMTEzLDExNiwxNzIsOTAsMjMwLDAsMiwxOTAsMTgxLDAsMTIyLDE2NywxNjgsMTc1LDMsMTQ0LDE2MCwyNSw4NiwxNTYsMTcyLDYxLDE5LDIyOSwyMDAsMjE0LDE2NiwxNjEsNTksMzcsNTQsMTkxLDE2Nyw5MSwyNiwzMyw3NiwxNzYsMTE3LDY0LDE3OSwxOTgsMTAzLDEyMCwxMiw4LDEzLDE2MSwyNyw0MCwyNDMsMTc1LDAsMTA4LDcwLDE5OSwxOTgsMTkwLDIyLDI0OCwxMjYsMTc1LDE3OSwxMjYsMTY5LDEzMyw2MiwxNzAsNTMsNiwxOTYsMjAyLDcyLDc2LDE0NSwyMCwxOTUsODAsNzgsNzUsMjUxLDE1OCw4NCwxMDgsMTI4LDEzOSw3MCwxNiw1NSwxNjUsMTY1LDUzLDE2MywxOTQsMjUsMjA2LDE2NywxNTMsMTI1LDE5MywxMDIsMjAsOTEsNjQsMTcsMTY5LDI1NSw5OCwxODIsMTk5LDIwOSw4NSwyNDgsMjM3LDkwLDEwMiwxMzAsMjIyLDEyMiw3MSwxNzAsMTI2LDE5NywyMzgsNTksMjE1LDE3MCwyNTUsMjE4LDMxLDEyMywxMjUsMTU0LDEzMSwyMywxLDUsMjE0LDE4NSwxMjgsMjQzLDE1NiwyMCwxNTUsMTYwLDM5LDUwLDI0MywxMDQsMTU3LDExNiw2LDMwLDg0LDIzLDg4LDEyMywxMDhdfX0sImxlYWZfbm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxOCwxMSwxNjUsMTY1LDEyOSwxNjMsOTksOTcsMTMwLDEzNCwyNTAsMTE3LDE4Myw1OCwxMzUsMTI2LDQ0LDIwMSw0LDIyLDExNywxMTUsMTE4LDE4Myw2LDczLDE3Miw0MywxMTYsMTE3LDE2LDIwMSwyMDMsMzksNjksMjgsMTEyLDE4LDgyLDcyLDQzLDE1MSwxMzMsMjQzLDUzLDIwNywwLDE5MywyMDksODksMTg2LDY2LDIxNiwxNDYsMjA1LDkwLDE0OCw3OCwxNTEsMTM3LDc2LDE0OSw5LDIyOSw4LDE2NCwxNjksMTkzLDIsMTkyLDk3LDE5MywyMTYsMTg2LDEwOCwxNjksMTg3LDExMiwyMTcsMjEwLDExNCw0NiwyMzMsMjAsMjIxLDkxLDE1LDI0Myw5LDEyOSwyMTEsMjIwLDE3OCw3NCwxNDgsMTAwLDc2LDI0NSw0MywxMTQsMTI0LDE2OSwxMzIsMTE3LDM1LDE4MywyNDgsMzMsMTY1LDIsMTIsMjM1LDEyMCw5MywxMDUsMzcsNjMsMTYwLDIzNCw4Nyw4LDU3LDE1OSw3Miw3Miw2OCwyMTYsNSwxMjQsMTM3LDEzMSwxNTAsMTgyLDEzMyw2NSwxLDEwNiw1NiwxNjIsMTM5LDg2LDIwNSwxNiw5MywxMjcsNSwxMzUsMjE5LDE2NSwxNywxNjIsMjcsMjIsMTgsMzMsMTgsMjIzLDI0MSwxMzgsMjAyLDE0MCw0OCwxMDIsODIsMTE4LDE3MCwyMTAsMTcyLDE5MiwxNjQsOTYsMjQ2LDU1LDgxLDE1MSw2Niw2OSw3LDg2LDM3LDE1NywzNCw5MSw3OCwyOCwxNjAsMTA2LDIsMTg1LDE5MSwyNDIsMTk1LDE2MSwxMDYsODIsMjMyLDQyLDExNSwxOTAsMTAzLDg5LDIwMCw1Niw0MiwzNiwxMzIsMTgsMTg2LDI0MSwxNjQsOCwyMTMsODQsNTYsMjExLDEwNywxNzUsMTAzLDUyLDEyMSw1NywxMzcsNjQsMTM1LDQ4LDIyMywxODQsOTYsODcsMjE5LDMwLDE4MSwxNTIsMTg3LDE5MiwxODgsMTQ3LDI0OSw5MiwxMDcsNzEsMjMzLDgzLDEzMiwyMTcsNTUsMjQwLDI0MywxNDIsNDIsMTM4LDE0MiwxMzYsMjQ0LDE5NCwyMTAsMTM1LDUwLDMwLDE3MiwxNjIsMjUyLDE3NiwxMDksOTQsODYsMTU2LDMwLDExOCwyMiwyNTQsMjQxLDExNywxNjUsMTE1LDY4LDE1NywyMTMsMTQzLDI0NywxOTcsNDUsMTQsMTQ0LDg4LDIxMSwxNTYsMjA3LDE1NSwxMDIsMTI4LDI3LDIwOSwxMSwxNjgsNDAsMSwzOCwyMzYsNjIsNCw0MiwxNCwxOTcsMTE0LDY3LDIzOCw3NiwxODQsODksMTYzLDE5LDI0Nyw4OCw2MiwxNTMsMTY1LDEwMCwxNzcsNDgsOTYsNzUsODUsNTMsNzUsMjAxLDE3LDcyLDIxOCw4NCwxNTMsMTU2LDk5LDE0NSw3MywxMjAsMjYsNjksMTY3LDIwNSwxNjcsMTQwLDc3LDEyOSwxOTgsMTk3LDAsMTYxLDE1NCwyMjksMTIyLDQ3LDg1LDE3MiwyNTMsNjksNzcsOTAsNTYsNiwxNCwxMjAsMTIzLDE5NywyNiw0MiwxMTUsMTE4LDY3LDYsMjQ2LDEwNCwyMzAsOTIsNDEsMTQ2LDEwMywxNzcsNDEsMTcyLDE2NiwxMTYsMjQ1LDEwNywxMzgsMTIxLDAsMTExLDEyMiw4MiwyNDEsODEsMTAwLDE4MSwyNDQsNzYsMTUwLDE2MSwyMDEsNSw2OCw1NSwxMjksMTYxLDExMCwxOTEsMjM0LDEzMSwyMzAsMjI3LDE4MSwxNDcsMTM4LDk0LDQsMjgsMTAsMTIxLDMyLDMsMjIsMjQxLDM4LDE2MSwxMjIsMTMyLDczLDExMiw1MiwxODQsMTcsMSwxNTUsMiw0Myw4NCwxOTcsMTgxLDExOCwxNDAsMTc2LDIwMiwxMjgsMTMyLDY0LDE0NywxMjEsMTI5LDMsMzQsOTYsMTE1LDg5LDI0NCwyNDcsMTQ5LDI1LDk2LDIwLDEyOCwyMTAsNiwxMiwyMjksMTIzLDQ5LDIxMCwxMSwyNDYsMTE2LDIwMSwxNzIsMTIxLDEwNywxMzEsNCwxNTQsMTMwLDE3LDE2NywyMjMsMTc3LDE2OCw3OSwxMCwxMTAsMTYxLDIyNCw1LDIzNywyMjQsMTYsMjEyLDM4LDE3NCwxOTIsOSw4OSwxNjIsNTgsNTgsMjU0LDEzOSwyOSwzNCwxNjIsOTYsMTQ2LDE4Niw2LDIxNiwxNTIsNDgsMTA5LDEwNSw2MSwyOSwxMjIsMTIyLDQ1LDE5MywxMDAsMTEsODgsMTc3LDI1MSwyMzEsMTA2LDI2LDk3LDQzLDI0OSwyNDEsNzAsNzIsNzEsMTQyLDQ1LDc2LDI0LDc0LDY5LDkyLDI1Myw1MSw0MywxOTUsMTg4LDIwLDI5LDE5OSwxOTIsMSwxODYsMSwxNjIsMTE2LDE3NSwyMTcsMjMxLDY5LDE4OSwxNzIsMiwyNTIsMTQwLDU1LDYxLDc1LDE2MSw2NSwyMCwxMjIsNzEsMTYyLDY0LDE3MywxOTMsMTY1LDE0LDM1LDEyMSwxMjcsMTMxLDYyLDkyLDExMiwxMzEsMTQyLDEzMCw2NSw1NiwyNTIsMjIsOTAsMjQyLDIwNiw1Myw1MSw1MSwxNzksMzgsMTkwLDEyMCwxMTQsMTYxLDI0OSwwLDE0MiwyMDQsODAsNjUsMTMzLDIzNiwyLDI0NSwyMzAsNzksNzMsOTYsMjAzLDUxLDM3LDIwMSwxOCwxODQsNjMsMjcsOTcsNDMsMTQ4LDg3LDM1LDE3LDI3LDE1MCw1OCwyNDgsMTcwLDIyMywyMjksMTg5LDE3OCwxMzEsODksMTczLDI1MiwxODQsMjMwLDIwMSw4OCw4MywyNDIsODcsMTY1LDEyOCw5NywyOCwxODgsMTU1LDEzNCwxMjEsODAsNywyMzEsNDMsNTUsMjcsMTM3LDczLDQxLDI2LDE1LDEyMCw4OCwxODQsMjA0LDcyLDI0MiwxNzEsNDAsMjI3LDE5LDIzLDIxMCwyMTAsMTkzLDEwMSw4MSwzMSwxNiwxMjEsNDUsMTAyLDE3LDE5LDE3LDEwNiwxMTIsMTk1LDE3OCw0MywxMDQsNiwxMDMsMjksMjI1LDExMiwyMTcsMTk5LDgyLDcxLDAsMTM1LDI1MCwxMzYsMTc3LDEwLDUxLDE3LDEwNSwxODQsMTUwLDE0NywyMTEsMjA1LDY4LDUsMTQwLDI0Nyw0LDM0LDE4OSwxOSwyMiw5NywxNDksMTYsMTMxLDIwLDI4LDI1NCwxOTksNzEsNjMsMTE1LDEwMiwyMDYsODksMzQsOTcsMTQ0LDEzMywxODEsMTQ4LDE4LDYxLDIzMyw2NiwyNDUsMTI5LDE0OSw1NywxMTYsMTgwLDAsNjQsMTM0LDE1NywxMjEsMTUzLDExNSwxMTgsMTM1LDE5NSw0OSwxNTIsMTAxLDY3LDEyNSw4LDEzMSwxMTIsMTY0LDEwNCwxMTEsMjE2LDc0LDE1OSwyNDMsOTgsMTEsMTUwLDEzNSwyMywxNTYsOTAsNDEsODYsMjQxLDIzLDE1OSwyNDAsMjAyLDIyMywxLDk3LDIzNCw0MCw0MiwxNjYsODUsOTksMjIsNTksNzEsODcsMTIyLDM1LDEwMSwxMTksMTI2LDEwOCwyMDksNDIsMjU0LDQyLDEwNywxMTAsMjIsMTIyLDE0Miw5OSwxMTAsMTI3LDE5NywyNywyMzMsMTcyLDEyNSw0NSwxNjksMTUzLDE0LDEzMywxNjAsMjA5LDEwNCw1MiwzMywxMzAsNTUsMjQ3LDEzNywxNzMsNDUsMTA2LDQ0LDQsMjAxLDEzMiwyOSwxOTksOTgsNTIsMTA3LDE4NywxNjMsNDEsMTM1LDI0MywxMzEsMTE3LDI0LDY3LDIwNywxNzAsMTYyLDE0MiwyNCw2NSwxLDE4NCw4MywxNzksMjYsMTAzLDEwNiwwLDQsMywzNSwzNiw1NiwyMTQsMTk3LDIwLDE0NCwxOCw2OCwxMzIsMjMwLDUzLDUyLDU1LDkwLDQsMTg2LDEzOSw4Niw1MSwxOTYsMTgzLDE2NCw5NiwxMDgsMTE3LDU5LDIyOCwyMTEsOTgsMTQ1LDIzNSwxMjEsMTgzLDY4LDg5LDEyNCwxNjAsMTc3LDE5MywyMzMsMTM0LDMyLDE0NCwxMjIsMTIwLDI0MiwxNjgsMjE2LDIxLDg3LDI4LDM5LDUsMjUxLDEyNCwxODAsMTUwLDQ5LDQ4LDcxLDIzMiwxODEsMyw2LDEyLDEwMiwyMjksMjA0LDE2OSwyMzUsNjMsMjUsMTE0LDgxLDM4LDI0MCwxNjksODgsODcsODQsMTM5LDE5OSwyMiwxODcsMTM3LDk0LDE1MiwxOTUsMTg3LDIxMiwxODIsMTI5LDk3LDIxMiwxNTgsMzEsNywxNjgsMjUxLDIwMSwxNTMsMTEwLDIxNSw0NywxMTgsMjQ1LDQwLDIzLDEwNCwxMTQsMTU0LDEzOSwwLDIyOCwzNyw3MSw5MCwxODgsNzEsMzEsMTg1LDE1NywxMzIsMTg4LDE0NSwyNDQsMTEzLDE3MywyNDksMTUzLDg5LDEzOSw4MywxMywxNywxODIsMTMyLDIwMCwyOCwxODgsNzUsMTk1LDE5MCwyOCwxMjEsMTk4LDc4LDM0LDIxLDIxLDUxLDE4MywzMiw5OCwxOTAsMTk4LDEzMSwxNzEsMjMsMjMsMTk2LDE1MSwyNDAsMTk3LDE0Myw0MSwxMiw4MSwzNCwyMywxNDQsMjYsODUsMTUsMTI0LDE2Miw3Myw5NiwxNDUsMjM4LDgsNjMsMTksMTAxLDQwLDIwOSwzNCwxMDAsMjE3LDY2LDU1LDQzLDIxMywyNiwxOTAsMTU2LDY4LDY3LDE3Nyw1LDExNSwxMzAsMTcwLDk4LDk3LDEyMywxNzYsMjgsMTQ1LDE2Myw2NSwxNyw0OSw1MSwxOTgsNTksMTU2LDExMCw1NSwyNSw5MCw4MiwxNDgsMTc4LDE3Nyw3LDEwMSwxMzIsMjM1LDgyLDE1LDI1MSwzMCwxNTQsMTg4LDE2NywxODUsMTk0LDE3NCw4OCwyNDUsMTA2LDUyLDM4LDE5NywyNTAsMjAxLDEyNywxMjIsODcsMTE1LDE2MCw3MCwyOSwxNjEsMTEyLDc4LDUzLDI0MCwyNCwyNDIsMTIsMjUwLDE3OSw5MSwxMzcsNjksMTk4LDgwLDU0LDI0NSwyNSwzLDc3LDEyMiwxMTUsMTc0LDEzLDE4LDE0MCwxMjQsNzYsMTc1LDIwMCw0MywyLDU2LDI0MSwxNjgsMTgsMjksODAsMTk3LDIwMSw1NiwyMTcsNTksNTYsNTIsNDIsMTA4LDU2LDE4NCw2NywxOTgsMTE1LDI3LDIyNSwyMDQsNywyMTQsMTEyLDE5MSwyMjYsMjI1LDEyMiwxOTgsNjIsMTY2LDE1NiwxNTIsMjQwLDMzLDM1XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzE0OCw3OSw5OCwyMDIsMjUxLDEzNywxMDEsMTQ1LDEzOCwyNDIsMjQ4LDE1MywyNiwxNDYsMTg2LDIzLDI0NywzMCwyMjIsMTE4LDIxMywyNTUsMjExLDY5LDI0MSwxNjAsMjU1LDIxNywxMTcsMjgsMjQ4LDIyOF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk5LDEwNCw5NywxMTQsMTA4LDEwNSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IktleVBhY2thZ2UiOnsibm90X2JlZm9yZSI6MTcyMDY4ODk1MCwibm90X2FmdGVyIjoxNzI3OTUwMTUwfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMyw0NCwyNDQsMyw4OSw1NSw2MiwyNSwxNzUsMTg2LDQ4LDE3OSwxMzEsNDIsMTc5LDEyNCw3MSwyMTQsMTI4LDE3MSwxMTgsNDQsMTk4LDE3OSwxOTYsMzMsMTcxLDE5Myw2Nyw5Miw4NSwxMjQsMTY3LDE5Nyw0MiwyMDgsMjU1LDE0Nyw4NCwxNjMsNTcsMjI4LDE2OSwyNTAsODYsOTcsNzgsODgsMTgyLDEzLDIyMCwxMDAsMjQzLDE3MSwyMzUsMTYyLDE5OSwzMSwxNjEsMjA0LDEyNyw1MCwxNDYsNF19fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMTAyLDExOSwyNTAsNzEsMTI2LDIxLDIwMSw5NSwxNTUsOTQsMjQsMjAyLDg0LDM1LDIyNywxNDksMjksOSwyMzMsMjgsMTM1LDY3LDY0LDE1MiwxOTAsMTEsODMsMjEsMjU0LDE2NywyMjcsMTkzLDE3OSwyNTUsMTc3LDIxMSwxODcsNDcsMjUwLDMsMjMyLDE1NCwxMzksNjUsNjQsOTUsOCwyNTMsNzYsMTA0LDE1NSwxMzYsMTQ2LDEyNSw5Myw1MywxMiwyNCwyMDgsOTMsMjA0LDIxNCw4MiwxXX19fX19LCJwcm9wb3NhbF9yZWZlcmVuY2UiOnsidmFsdWUiOnsidmVjIjpbMTksMTUsMTA3LDIzMSwyMDEsOTgsMjQ0LDMyLDE3MywxNzksNzMsMTU1LDE0LDQsMzEsMzgsMjM5LDExNiwxNjQsMTg0LDEyNSwxMDIsMTI0LDEzMyw5NCw0NSwzNiwyNDAsMTMzLDExMCwyNTMsNDRdfX0sInNlbmRlciI6eyJNZW1iZXIiOjB9LCJwcm9wb3NhbF9vcl9yZWZfdHlwZSI6IlJlZmVyZW5jZSJ9AAAAAAAAAFoAAAAAAAADJkVwb2NoU2VjcmV0c3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsiaW5pdF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE4NCw1MSw3MSw5NiwxNzUsMjExLDE5OCw0LDE4Nyw1MywxNDIsMTc5LDE1MSw5LDMwLDY5LDE0NywyMDksMTkzLDE3OSwxODgsMTM4LDE2MiwxODQsMTkxLDY3LDE2NSwxNjIsMTk0LDYxLDI0NiwxNjNdfX19LCJleHBvcnRlcl9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE5MSwyMTgsNDMsNjYsMTQ0LDkyLDAsMjMwLDIyNiwzMywxODksMTY1LDY5LDEsMjUwLDI0NCwyOCwyMDIsMTczLDIyOCw5MSw4NSwxMDMsMjQ5LDEyNyw0NSwxMCw5OSwyMjgsODMsMCwxMDRdfX19LCJlcG9jaF9hdXRoZW50aWNhdG9yIjp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOls5NiwxMjYsMjI4LDg0LDIwOCwxMDcsMTAxLDk0LDE1OSw4NCwyMzcsMTYzLDU5LDQ0LDEzMSwyMTcsMjQ3LDU2LDIxOCwxMjEsMTE0LDI1NSwzMSw2Nyw3MiwzNCwxLDY3LDE2LDE2NiwxNjIsNjVdfX19LCJleHRlcm5hbF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE4LDIxNSw0MiwyMTUsMjQ1LDYzLDEzNiwxMjMsMTIsNDYsMTI5LDIwNCw1MCwxNDgsMjM0LDIyMiw0NCwzNiwxNjgsMTYyLDIwNywxMDgsMjUsMTM4LDEwOSwyMzUsMTU0LDIsMjQ1LDMwLDEzMSw5XX19fSwicmVzdW1wdGlvbl9wc2siOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzkyLDMwLDE4LDM2LDE3NiwyMjAsNDYsNDMsMTkwLDcsNDUsNzgsMjQxLDEwMiwxNDMsMjQ2LDI3LDE2LDE2MCwyMjQsMjQsMTE0LDI1LDU0LDEyOCwyMjcsMjIsNTIsNjMsMjA2LDEwMiwxMV19fX19"]},"MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519":{"group_id":{"value":{"vec":[214,206,173,27,206,87,231,4,172,30,130,210,232,69,61,222]}},"storages":["AAAAAAAAAA0AAAAAAAAAXQAAAAAAAACBQ29uZmlybWF0aW9uVGFneyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYWNfdmFsdWUiOnsidmVjIjpbMTk1LDUzLDIzLDc2LDQ0LDE3MSwyMSw1LDE3MCwxMzAsNCwxODIsMjI3LDM4LDIxNSwxMTgsNTUsMjQ4LDY4LDEwMywyMiw5MCwwLDEyOCwxODAsNDgsMTkwLDM1LDE2LDUsMjE1LDY5XX19AAAAAAAAAFoAAAAAAAABhkdyb3VwQ29udGV4dHsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsicHJvdG9jb2xfdmVyc2lvbiI6Ik1sczEwIiwiY2lwaGVyc3VpdGUiOiJNTFNfMTI4X0RIS0VNWDI1NTE5X0FFUzEyOEdDTV9TSEEyNTZfRWQyNTUxOSIsImdyb3VwX2lkIjp7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19LCJlcG9jaCI6MCwidHJlZV9oYXNoIjp7InZlYyI6WzE2NSw3MSwxMCwxMDAsOTMsMTg0LDI0NiwyMzksMTc2LDIzNiwzMCwxMjEsMTY2LDU1LDE2MCwxNyw4NywxNjcsMTAsMjI5LDM5LDE2MywxNzEsMjIyLDIyNCwxNzEsOTQsNDEsMTUxLDkxLDExNyw1NF19LCJjb25maXJtZWRfdHJhbnNjcmlwdF9oYXNoIjp7InZlYyI6W119LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fQAAAAAAAABcAAAAAAAABCtNZXNzYWdlU2VjcmV0c3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X2Vwb2NocyI6MCwicGFzdF9lcG9jaF90cmVlcyI6W10sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzIzMyw1MSwxNTMsMTQwLDI5LDQ1LDI1MiwyNTIsOTQsMTk3LDQ0LDEwMSwxNzAsMTc5LDI5LDI3LDE1MCw3OCwxNjEsMzEsMTksMjUsMTQ5LDEwOSwyMDMsMjE1LDUzLDI0LDE5MCw1LDE3MiwxNzddfX19LCJtZW1iZXJzaGlwX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbODAsMTU3LDE0MywzMSwyMDQsMjI5LDI0Myw0OCw2MCw4MiwxODEsMjcsMjI5LDEwNSwxODgsNjYsNTEsOTEsODAsMTUwLDg1LDEzOCwzMSwyNDMsNTksMTI0LDEwMSwxMTcsMjQ5LDE3MywxMjgsNjRdfX19LCJjb25maXJtYXRpb25fa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyLDEyNiwzMywxMDIsMTgzLDMsNDksNDMsMjEwLDE0OCwyNTMsMjAyLDE5OSw4MSw0MCw4MCwzLDExNSw4LDE0OSwyNCwyMTgsMTk1LDc5LDQzLDExNywxMTEsODMsMTg2LDEsMTc0LDI1NV19fX0sInNlcmlhbGl6ZWRfY29udGV4dCI6WzAsMSwwLDEsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwwLDMyLDE2NSw3MSwxMCwxMDAsOTMsMTg0LDI0NiwyMzksMTc2LDIzNiwzMCwxMjEsMTY2LDU1LDE2MCwxNyw4NywxNjcsMTAsMjI5LDM5LDE2MywxNzEsMjIyLDIyNCwxNzEsOTQsNDEsMTUxLDkxLDExNyw1NCwwLDBdLCJzZWNyZXRfdHJlZSI6eyJvd25faW5kZXgiOjAsImxlYWZfbm9kZXMiOlt7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxOTQsODcsMjA3LDE4LDE0MCwxMjksMTYxLDEwLDkyLDIxOSwxMDgsMTY4LDIzNSwyMDEsODEsMTE1LDQxLDk2LDIxMywxNCwyMzQsMTM1LDE3Niw0OSwxMTgsMjI4LDU1LDM4LDU3LDExMCwx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cmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzIzMyw1MSwxNTMsMTQwLDI5LDQ1LDI1MiwyNTIsOTQsMTk3LDQ0LDEwMSwxNzAsMTc5LDI5LDI3LDE1MCw3OCwxNjEsMzEsMTksMjUsMTQ5LDEwOSwyMDMsMjE1LDUzLDI0LDE5MCw1LDE3MiwxNzddfX19LCJtZW1iZXJzaGlwX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbODAsMTU3LDE0MywzMSwyMDQsMjI5LDI0Myw0OCw2MCw4MiwxODEsMjcsMjI5LDEwNSwxODgsNjYsNTEsOTEsODAsMTUwLDg1LDEzOCwzMSwyNDMsNTksMTI0LDEwMSwxMTcsMjQ5LDE3MywxMjgsNjRdfX19LCJjb25maXJtYXRpb25fa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyLDEyNiwzMywxMDIsMTgzLDMsNDksNDMsMjEwLDE0OCwyNTMsMjAyLDE5OSw4MSw0MCw4MCwzLDExNSw4LDE0OSwyNCwyMTgsMTk1LDc5LDQzLDExNywxMTEsODMsMTg2LDEsMTc0LDI1NV19fX0sInNlcmlhbGl6ZWRfY29udGV4dCI6WzAsMSwwLDEsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwwLDMyLDE2NSw3MSwxMCwxMDAsOTMsMTg0LDI0NiwyMzksMTc2LDIzNiwzMCwxMjEsMTY2LDU1LDE2MCwxNyw4NywxNjcsMTAsMjI5LDM5LDE2MywxNzEsMjIyLDIyNCwxNzEsOTQsNDEsMTUxLDkxLDExNyw1NCwwLDBdLCJzZWNyZXRfdHJlZSI6eyJvd25faW5kZXgiOjAsImxlYWZfbm9kZXMiOltudWxsXSwicGFyZW50X25vZGVzIjpbbnVsbF0sImhhbmRzaGFrZV9zZW5kZXJfcmF0Y2hldHMiOlt7IkVuY3J5cHRpb25SYXRjaGV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjEsOTAsMTI2LDI1NCwxMTcsMzEsMTAsMTMsODQs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MCwxNzQsMTE4LDIwNCwyNTEsMjAyLDE3NiwyMDMsMTQyLDk1LDEyMywzOSwzNSwxMDEsMTA0LDE3Miw5OSw1NiwxMDYsMTMwLDE1NSwwLDI0XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6Wzg0LDE2OSw2NSwxNDksNzUsMjI2LDE5MSwzMywyNTQsMTQxLDE3LDIxMSwxODEsNDgsOTEsMTU5LDEwMCw0NSw3OCwxMTIsNzksMTQ4LDEzLDE1LDIyMiwxOTYsMjExLDY5LDE1NiwzNiwxOTEsMTBdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5NywxMDgsMTA1LDk5LDEwMV19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTQ5LCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNDl9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMTIsMjQ5LDIyMCwxNjYsMTg1LDU2LDE1MCw5NywxNDAsMjE0LDIxMiwyMjAsMTczLDE4OSwxNDcsMTI1LDE0NywyMTAsNTcsNjYsNDgsMTQ0LDE1MCwyMDUsMjgsMzYsMTMyLDM5LDE0MiwxNDYsMTMxLDkzLDEzNCwxODgsODcsMTQ4LDAsMjIsMTM0LDM4LDYsMTMzLDUwLDIzNyw3MywxNzUsMjM2LDIxNCwxNjIsMywxMTUsMTIxLDU5LDI0MSwxNiwxNzEsOTksMTA1LDg0LDE2OSwyNDgsMjUsMTk2LDhdfX19fV0sInBhcmVudF9ub2RlcyI6W10sImRlZmF1bHRfbGVhZiI6eyJub2RlIjpudWxsfSwiZGVmYXVs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eyJ2YWx1ZSI6eyJ2ZWMiOls5MiwyMTgsMTg4LDE1OCw0OCw4Myw3OSw4MywxNjYsMTM2LDEzOCwxODMsMzIsMTE4LDQ5LDEzMywxODYsMTY0LDg1LDg5LDE5MiwxMDQsNjEsNjksMTU0LDExMywyMTQsMjI1LDIwNCwxNjEsMTg5LDQ4XX19LHsicHJvcG9zYWwiOnsiQWRkIjp7ImtleV9wYWNrYWdlIjp7InBheWxvYWQiOnsicHJvdG9jb2xfdmVyc2lvbiI6Ik1sczEwIiwiY2lwaGVyc3VpdGUiOiJNTFNfMTI4X0RIS0VNWDI1NTE5X0FFUzEyOEdDTV9TSEEyNTZfRWQyNTUxOSIsImluaXRfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxODAsMjI0LDc1LDg3LDE0LDM1LDEzNCw2MCwzNiw5MSwzMCwyNDUsMTQsMjA3LDk1LDE2MSwxMzYsMjAwLDEwNywxNDEsMjU0LDk2LDE5Miw1NCwxODUsMTU5LDI0MSw3NywxMjIsMTA0LDI0Niw3NF19fSwibGVhZl9ub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzcxLDEwLDY5LDIwLDIsOTIsNzAsODksMjI0LDQ4LDEyNiw3OCwxMjQsMTg2LDE0OCwxNTksMjQ1LDE3NSwxMjIsMTU1LDE2LDIwOSwxNTMsMjE2LDIyLDE0MSwxMTcsNzUsOTcsNzIsMjQ3LDM2XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzI1NCwxODIsOTUsMTg3LDI5LDEwNiwyNDEsMjgsMTk0LDIwMyw1NiwzNywyMDcsNjAsNiw4LDExOCwxNzcsMzksMTU0LDIyMSwxNiwxNywyNTIsNjAsMjE5LDEyMiwyMjIsMTYzLDIzMywxMzIsMjVdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5OCwxMTEsOThdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IktleVBhY2thZ2UiOnsibm90X2JlZm9yZSI6MTcyMDY4ODk0OSwibm90X2FmdGVyIjoxNzI3OTUwMTQ5fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbNDksMTAxLDE5MSw2MCwxNjcsMjQ1LDIyMiwyMDIsMjA4LDMwLDIxMiwxMzksNjUsMTAsMjksMjM4LDE5Niw3NCw4Nyw1MCw3LDE2MywyMTgsMTA2LDYzLDMsODcsMjAzLDEzLDIzLDEwLDIyLDEzLDgxLDE0MCwxNzgsOTEsMTE0LDQ0LDIyNSw3NSw3LDIyOSw4MSwxMzcsNjMsMTI0LDE1NSwxNjAsMjQsMTgyLDQyLDkzLDUyLDI0LDE2NywxOTksMjAyLDE3OSwxNjAsMTU4LDYyLDE2LDEzXX19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNjMsMTEwLDE3Miw1NiwyMTgsMjI2LDU4LDU5LDE5NSwyMDksMTI3LDI0NCw1OSwyMDQsMTg2LDgwLDE3Miw4LDE4MSwyMjksMTcsMTAwLDIyOCw5MCwyMjUsNjIsMjQxLDI0OCwxMjEsMTMxLDEwNSwxNDcsMjI3LDE5OCwxNzgsODgsMjMzLDExOCw5MCw5NywyNDQsMTQsMTk2LDI5LDEwNiwxODgsMjYsMTI1LDEyMiwxMzksMjMwLDc0LDk3LDIwNSw1Miw0OCwyNCwyMTUsOTgsMTc3LDE1LDg0LDcwLDldfX19fX0sInByb3Bvc2FsX3JlZmVyZW5jZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls5MiwyMTgsMTg4LDE1OCw0OCw4Myw3OSw4MywxNjYsMTM2LDEzOCwxODMsMzIsMTE4LDQ5LDEzMywxODYsMTY0LDg1LDg5LDE5MiwxMDQsNjEsNjksMTU0LDExMywyMTQsMjI1LDIwNCwxNjEsMTg5LDQ4XX19LCJzZW5kZXIiOnsiTWVtYmVyIjowfSwicHJvcG9zYWxfb3JfcmVmX3R5cGUiOiJQcm9wb3NhbCJ9XV19LCJzdGF0ZSI6eyJHcm91cE1lbWJlciI6eyJncm91cF9lcG9jaF9zZWNyZXRzIjp7ImluaXRfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMiw4NSwxMzksOTQsMTM5LDE5MSwxMCw1MCwyMDMsMjE2LDU1LDc4LDEzLDI1MSw3NSw5MywxOTMsMTY3LDQxLDk1LDE3NywzMSwyNDEsMTE3LDE3MCwyMDUsMTIyLDEwNSwxMzIsMTcyLDEwNSwxMTddfX19LCJleHBvcnRlcl9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6Wzc5LDIzMCwxMzUsMTEyLDEzOCwyNDQsMjU1LDI1MSwyMjIsMTA3LDUyLDEwMSwxMDcsMzgsMTIyLDM2LDE4Niw4OCwyMTAsMTk2LDE0OSwyMTEsMTU0LDk0LDEwOSwwLDE0OSwxNywxNDksMTQ2LDY0LDEwMV19fX0sImVwb2NoX2F1dGhlbnRpY2F0b3IiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzIyNiw1MSw0OSwxNzQsMjM5LDc3LDQwLDExNSwyNTUsMTc1LDM4LDkwLDIwMiwxOTcsODYsMTQxLDE2NiwyMjMsMjQ3LDIyOSwxODksMjI4LDExMywyMzMsODAsMTM4LDg4LDIwLDY2LDE3Niw5MCw1Ml19fX0sImV4dGVybmFsX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTIwLDIzNywxMTksMTIyLDY3LDExMCw4OSwyNDYsMjA1LDE0OCwxOTYsMjQxLDE0NiwxMzksNDksMiwyNDgsMTk1LDUxLDY2LDk2LDEwNiwxMzksMzAsMjQyLDE5OCw5OSwyMTcsODIsMjQ5LDE2Nyw3Ml19fX0sInJlc3VtcHRpb25fcHNrIjp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMSw3NSwyMzksMjQsMjM2LDE2OSwyNTAsMTQ0LDg3LDEzMyw4MywxMjAsOTEsMjA0LDE4NCw4NiwxNDQsMTEwLDIzMiwyMDEsMTU2LDQsMjExLDE1NiwxNzcsMzksMzUsMjMwLDIxOSwxNywxMDcsNTBdfX19fSwibWVzc2FnZV9zZWNyZXRzIjp7InNlbmRlcl9kYXRhX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMzYsMTk1LDcyLDc3LDE2NCwyMCwyNSwyMTgsMjQ4LDExLDEwNiwyMTMsMjI5LDYsNjksNzYsMTA2LDg2LDEwNiwxNywyNTIsMjM1LDgyLDIxMCw1NiwxMTgsMzYsODAsMCwxMzYsMTU4LDQzXX19fSwibWVtYmVyc2hpcF9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzExLDEwNSw5MCwxMiwyMTAsODgsNDksODEsMTY4LDEyMywxNDgsMTE5LDk4LDQwLDE4Nyw4NSwyMDIsMjAxLDE3NCw0MywxMjgsMzYsMTI1LDE3MywxMjQsMywzOCwyMDYsOTcsMTk5LDExMywxMTldfX19LCJjb25maXJtYXRpb25fa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOls0MCwxNTcsOTUsMTU1LDE2OCwxNTYsMTk5LDQ3LDIwMCwxMTMsODQsMjM1LDE0MCwyNSw3NSw1Myw2NiwxNzEsMjM1LDE2NSwxODIsMjEwLDEwOCwxNjQsMjE1LDEwNCwxMzEsNjAsMTIsNSwxMTgsMTIzXX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsMSwxNiwyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMiwwLDAsMCwwLDAsMCwwLDEsMzIsMiwxNjMsMTExLDIzNywxNDYsMjI4LDEyNCwyMDUsMTUyLDQyLDc2LDQ4LDkyLDk5LDQxLDE0NSwyMjQsMzksOSwxNDUsMTgzLDE2NCwyMjEsMTA4LDE2LDIsNTcsMjUwLDYsMTM0LDIyMywyNDksMzIsMjEsMTM1LDE5NCwxMjIsMjQ4LDExMCwxMDMsMTE3LDExNSw1OSwyMSwyMDQsNjEsMTA3LDE5NiwxMzMsMzEsMjQxLDE2LDEzNiwxMjMsMTUyLDEzNSwxNTQsMTc1LDE2NywyMDUsOTcsMTMyLDE4NywzLDMsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGwsbnVsbF0sInBhcmVudF9ub2RlcyI6W3sic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE4OSwyNDIsNTYsODUsODksMTg3LDExMSwyMTUsMTYzLDE5NSwyMTIsMjYsMjAxLDExOCwyNDcsNjEsNTIsNjgsMjI3LDEwNSw1NSwxNTEsODEsMjQzLDY3LDIzNiwxNjksMjEyLDExNiwxNTksMjEsMTc2XX19fSxudWxsXSwiaGFuZHNoYWtlX3NlbmRlcl9yYXRjaGV0cyI6W251bGwsbnVsbF0sImFwcGxpY2F0aW9uX3NlbmRlcl9yYXRjaGV0cyI6W251bGwsbnVsbF0sInNpemUiOjN9fSwic3RhZ2VkX2RpZmYiOnsic3RhZ2VkX2RpZmYiOnsiZGlmZiI6eyJsZWFmX2RpZmYiOnsiMCI6eyJub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzE2NiwyMzksMjUsMTk0LDIxNSwxMTcsOTcsMjM5LDAsMjI0LDE5LDUyLDEyNiwyMDIsMTA5LDI1NCwyMywxMzYsMTU2LDM5LDE2MSwxMzMsOTUsMzgsMzUsMTIzLDEyNCwxMzgsMjUsMjM2LDg4LDEwMF19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls4NCwxNjksNjUsMTQ5LDc1LDIyNiwxOTEsMzMsMjU0LDE0MSwxNywyMTEsMTgxLDQ4LDkxLDE1OSwxMDAsNDUsNzgsMTEyLDc5LDE0OCwxMywxNSwyMjIsMTk2LDIxMSw2OSwxNTYsMzYsMTkxLDEwXX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTcsMTA4LDEwNSw5OSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IkNvbW1pdCI6eyJ2ZWMiOls3LDE4Nyw2OCwxNyw0MiwyMTMsMTkzLDMwLDE0OCwxMzUsNjEsNDQsMjQ3LDEzNiwxODYsMTIwLDIwMywxNjQsMjI2LDExMyw0OSwxNzYsMTUxLDEyOSwxNzAsMTkwLDE5MSw2OCwxNDYsMjI0LDE0OCwxOTJdfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbOTksMjQzLDIzMiw3NSwxMzgsNjUsMTQ3LDI0OSwyMTcsNzgsMjA3LDEzMSwxMiwxNjQsMTc0LDg4LDE0LDIwLDEyMSw5OSw2MCwyMDksMjM4LDkyLDI4LDIyLDE0NiwxNjQsOTUsMTAxLDU0LDEzNCwxMyw3MCwxNCwxNTIsMjQyLDkzLDE0NywxLDE2MiwxNDgsMTU0LDE0MCw0NSwxNDksMTI1LDE4NSwxMjksMjA0LDg4LDIwMiwyNSw1NywzMiwxMjAsMTQxLDE0OSwyMDksMTAsMTU4LDE5MywyMTQsOF19fX19LCIxIjp7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNzEsMTAsNjksMjAsMiw5Miw3MCw4OSwyMjQsNDgsMTI2LDc4LDEyNCwxODYsMTQ4LDE1OSwyNDUsMTc1LDEyMiwxNTUsMTYsMjA5LDE1MywyMTYsMjIsMTQxLDExNyw3NSw5Nyw3MiwyNDcsMzZdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMjU0LDE4Miw5NSwxODcsMjksMTA2LDI0MSwyOCwxOTQsMjAzLDU2LDM3LDIwNyw2MCw2LDgsMTE4LDE3NywzOSwxNTQsMjIxLDE2LDE3LDI1Miw2MCwyMTksMTIyLDIyMiwxNjMsMjMzLDEzMiwyNV19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk4LDExMSw5OF19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTQ5LCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNDl9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OSwxMDEsMTkxLDYwLDE2NywyNDUsMjIyLDIwMiwyMDgsMzAsMjEyLDEzOSw2NSwxMCwyOSwyMzgsMTk2LDc0LDg3LDUwLDcsMTYzLDIxOCwxMDYsNjMsMyw4NywyMDMsMTMsMjMsMTAsMjIsMTMsODEsMTQwLDE3OCw5MSwxMTQsNDQsMjI1LDc1LDcsMjI5LDgxLDEzNyw2MywxMjQsMTU1LDE2MCwyNCwxODIsNDIsOTMsNTIsMjQsMTY3LDE5OSwyMDIsMTc5LDE2MCwxNTgsNjIsMTYsMTNdfX19fX0sInBhcmVudF9kaWZmIjp7IjAiOnsibm9kZSI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTMsMTgxLDYyLDE1OSwyNywyNTUsMjQzLDIxMSwyNDcsNTIsMjQ3LDEzOCwzNywyNTUsNjAsMzAsMTI0LDUwLDY0LDc5LDI0MSwyMjEsMTU1LDM1LDIwNiwyMjksMTQwLDczLDE4LDI0NiwyNDYsODVdfX0sInBhcmVudF9oYXNoIjp7InZlYyI6W119LCJ1bm1lcmdlZF9sZWF2ZXMiOnsibGlzdCI6W119fX19LCJzaXplIjozfSwibmV3X3RyZWVfaGFzaCI6WzIsMTYzLDExMSwyMzcsMTQ2LDIyOCwxMjQsMjA1LDE1Miw0Miw3Niw0OCw5Miw5OSw0MSwxNDUsMjI0LDM5LDksMTQ1LDE4MywxNjQsMjIxLDEwOCwxNiwyLDU3LDI1MCw2LDEzNCwyMjMsMjQ5XX0sImdyb3VwX2NvbnRleHQiOnsicHJvdG9jb2xfdmVyc2lvbiI6Ik1sczEwIiwiY2lwaGVyc3VpdGUiOiJNTFNfMTI4X0RIS0VNWDI1NTE5X0FFUzEyOEdDTV9TSEEyNTZfRWQyNTUxOSIsImdyb3VwX2lkIjp7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19LCJlcG9jaCI6MSwidHJlZV9oYXNoIjp7InZlYyI6WzIsMTYzLDExMSwyMzcsMTQ2LDIyOCwxMjQsMjA1LDE1Miw0Miw3Niw0OCw5Miw5OSw0MSwxNDUsMjI0LDM5LDksMTQ1LDE4MywxNjQsMjIxLDEwOCwxNiwyLDU3LDI1MCw2LDEzNCwyMjMsMjQ5XX0sImNvbmZpcm1lZF90cmFuc2NyaXB0X2hhc2giOnsidmVjIjpbMjEsMTM1LDE5NCwxMjIsMjQ4LDExMCwxMDMsMTE3LDExNSw1OSwyMSwyMDQsNjEsMTA3LDE5NiwxMzMsMzEsMjQxLDE2LDEzNiwxMjMsMTUyLDEzNSwxNTQsMTc1LDE2NywyMDUsOTcsMTMyLDE4NywzLDNdfSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sImludGVyaW1fdHJhbnNjcmlwdF9oYXNoIjpbOTcsMiwyOCwzNiwxMDksNSw0MSw0MywxMywxNDgsMjMxLDEyLDE4NywyMjksMjUwLDIxOCw0MiwxMDMsOTMsMTQ0LDExNCwxOTksMTIxLDI1MCwxMTAsMTM2LDIxMyw4NCwyNTAsMTI3LDQyLDY3XSwiY29uZmlybWF0aW9uX3RhZyI6eyJtYWNfdmFsdWUiOnsidmVjIjpbNDMsMjQwLDE4OCwyMzUsNzksNjIsNiwxNzksMTk1LDE0MCwzMSwxNDgsMjEsMjA1LDIzNSw3NSwxOTEsMjA2LDE1NywxODIsMTEwLDMyLDExMywxNTksMTYsNzEsMTU2LDEyNSwxNjgsMjAxLDEyNiwyMjRdfX19LCJuZXdfa2V5cGFpcnMiOlt7InB1YmxpY19rZXkiOnsia2V5Ijp7InZlYyI6WzE2NiwyMzksMjUsMTk0LDIxNSwxMTcsOTcsMjM5LDAsMjI0LDE5LDUyLDEyNiwyMDIsMTA5LDI1NCwyMywxMzYsMTU2LDM5LDE2MSwxMzMsOTUsMzgsMzUsMTIzLDEyNCwxMzgsMjUsMjM2LDg4LDEwMF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzI0Niw5OCwxMzgsNywyLDIyLDQ0LDE2MSwxNTEsMTI0LDQ5LDEzMCwxNjgsMTksMTMxLDczLDE0OCwyMzAsMTEwLDU5LDE3OCw5NywyMiwxNjcsMjQyLDg1LDIzNCwxMDEsNjMsNTgsMjM5LDI0NF19fX0seyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxMywxODEsNjIsMTU5LDI3LDI1NSwyNDMsMjExLDI0Nyw1MiwyNDcsMTM4LDM3LDI1NSw2MCwzMCwxMjQsNTAsNjQsNzksMjQxLDIyMSwxNTUsMzUsMjA2LDIyOSwxNDAsNzMsMTgsMjQ2LDI0Niw4NV19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzI3LDIyOSwxNDIsODMsMzQsMTQxLDMwLDYyLDExNSwxMywxOTAsNzgsODEsMTUxLDk4LDE4LDI5LDU0LDIxOCw2MSw2MCwyMzAsMzMsNzgsMTM0LDE5OSwxMTgsNjIsMTk1LDI0MiwxMjYsMjMzXX19fV0sIm5ld19sZWFmX2tleXBhaXJfb3B0aW9uIjpudWxsLCJ1cGRhdGVfcGF0aF9sZWFmX25vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTY2LDIzOSwyNSwxOTQsMjE1LDExNyw5NywyMzksMCwyMjQsMTksNTIsMTI2LDIwMiwxMDksMjU0LDIzLDEzNiwxNTYsMzksMTYxLDEzMyw5NSwzOCwzNSwxMjMsMTI0LDEzOCwyNSwyMzYsODgsMTAwXX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6Wzg0LDE2OSw2NSwxNDksNzUsMjI2LDE5MSwzMywyNTQsMTQxLDE3LDIxMSwxODEsNDgsOTEsMTU5LDEwMCw0NSw3OCwxMTIsNzksMTQ4LDEzLDE1LDIyMiwxOTYsMjExLDY5LDE1NiwzNiwxOTEsMTBdfX0sImNyZWRlbnRpYWwiOnsiY3JlZGVudGlhbF90eXBlIjoiQmFzaWMiLCJzZXJpYWxpemVkX2NyZWRlbnRpYWxfY29udGVudCI6eyJ2ZWMiOls5NywxMDgsMTA1LDk5LDEwMV19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiQ29tbWl0Ijp7InZlYyI6WzcsMTg3LDY4LDE3LDQyLDIxMywxOTMsMzAsMTQ4LDEzNSw2MSw0NCwyNDcsMTM2LDE4NiwxMjAsMjAzLDE2NCwyMjYsMTEzLDQ5LDE3NiwxNTEsMTI5LDE3MCwxOTAsMTkxLDY4LDE0NiwyMjQsMTQ4LDE5Ml19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls5OSwyNDMsMjMyLDc1LDEzOCw2NSwxNDcsMjQ5LDIxNyw3OCwyMDcsMTMxLDEyLDE2NCwxNzQsODgsMTQsMjAsMTIxLDk5LDYwLDIwOSwyMzgsOTIsMjgsMjIsMTQ2LDE2NCw5NSwxMDEsNTQsMTM0LDEzLDcwLDE0LDE1MiwyNDIsOTMsMTQ3LDEsMTYyLDE0OCwxNTQsMTQwLDQ1LDE0OSwxMjUsMTg1LDEyOSwyMDQsODgsMjAyLDI1LDU3LDMyLDEyMCwxNDEsMTQ5LDIwOSwxMCwxNTgsMTkzLDIxNCw4XX19fX19fX19AAAAAAAAAF4AAAAAAAAAAU93bkxlYWZOb2RlSW5kZXh7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAEwAAAAAAAAAFsAAAAAAAAA0lJlc3VtcHRpb25Qc2t7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7Im1heF9udW1iZXJfb2Zfc2VjcmV0cyI6MzIsInJlc3VtcHRpb25fcHNrIjpbWzAseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMjIyLDE5LDEwMCw5MywyNTAsNjEsMTM0LDQ4LDE2MywxNzUsMTg1LDExMCw3NiwzMCwyMDcsMjQwLDEyNywyMzAsMTE2LDE4MywxMzEsMjQyLDksMTc4LDEwNSwyLDE3NSwxNzIsODgsMTMxLDIzMywyMzNdfX19XV0sImN1cnNvciI6MX0=","AAAAAAAAAA0AAAAAAAAAXQAAAAAAAACMQ29uZmlybWF0aW9uVGFneyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYWNfdmFsdWUiOnsidmVjIjpbNDMsMjQwLDE4OCwyMzUsNzksNjIsNiwxNzksMTk1LDE0MCwzMSwxNDgsMjEsMjA1LDIzNSw3NSwxOTEsMjA2LDE1NywxODIsMTEwLDMyLDExMywxNTksMTYsNzEsMTU2LDEyNSwxNjgsMjAxLDEyNiwyMjRdfX0AAAAAAAAAWgAAAAAAAAH2R3JvdXBDb250ZXh0eyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJwcm90b2NvbF92ZXJzaW9uIjoiTWxzMTAiLCJjaXBoZXJzdWl0ZSI6Ik1MU18xMjhfREhLRU1YMjU1MTlfQUVTMTI4R0NNX1NIQTI1Nl9FZDI1NTE5IiwiZ3JvdXBfaWQiOnsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0sImVwb2NoIjoxLCJ0cmVlX2hhc2giOnsidmVjIjpbMiwxNjMsMTExLDIzNywxNDYsMjI4LDEyNCwyMDUsMTUyLDQyLDc2LDQ4LDkyLDk5LDQxLDE0NSwyMjQsMzksOSwxNDUsMTgzLDE2NCwyMjEsMTA4LDE2LDIsNTcsMjUwLDYsMTM0LDIyMywyNDldfSwiY29uZmlybWVkX3RyYW5zY3JpcHRfaGFzaCI6eyJ2ZWMiOlsyMSwxMzUsMTk0LDEyMiwyNDgsMTEwLDEwMywxMTcsMTE1LDU5LDIxLDIwNCw2MSwxMDcsMTk2LDEzMywzMSwyNDEsMTYsMTM2LDEyMywxNTIsMTM1LDE1NCwxNzUsMTY3LDIwNSw5NywxMzIsMTg3LDMsM119LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fQAAAAAAAABcAAAAAAAABLVNZXNzYWdlU2VjcmV0c3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X2Vwb2NocyI6MCwicGFzdF9lcG9jaF90cmVlcyI6W10sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzM2LDE5NSw3Miw3NywxNjQsMjAsMjUsMjE4LDI0OCwxMSwxMDYsMjEzLDIyOSw2LDY5LDc2LDEwNiw4NiwxMDYsMTcsMjUyLDIzNSw4MiwyMTAsNTYsMTE4LDM2LDgwLDAsMTM2LDE1OCw0M119fX0sIm1lbWJlcnNoaXBfa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMSwxMDUsOTAsMTIsMjEwLDg4LDQ5LDgxLDE2OCwxMjMsMTQ4LDExOSw5OCw0MCwxODcsODUsMjAyLDIwMSwxNzQsNDMsMTI4LDM2LDEyNSwxNzMsMTI0LDMsMzgsMjA2LDk3LDE5OSwxMTMsMTE5XX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNDAsMTU3LDk1LDE1NSwxNjgsMTU2LDE5OSw0NywyMDAsMTEzLDg0LDIzNSwxNDAsMjUsNzUsNTMsNjYsMTcxLDIzNSwxNjUsMTgyLDIxMCwxMDgsMTY0LDIxNSwxMDQsMTMxLDYwLDEyLDUsMTE4LDEyM119fX0sInNlcmlhbGl6ZWRfY29udGV4dCI6WzAsMSwwLDEsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwxLDMyLDIsMTYzLDExMSwyMzcsMTQ2LDIyOCwxMjQsMjA1LDE1Miw0Miw3Niw0OCw5Miw5OSw0MSwxNDUsMjI0LDM5LDksMTQ1LDE4MywxNjQsMjIxLDEwOCwxNiwyLDU3LDI1MCw2LDEzNCwyMjMsMjQ5LDMyLDIxLDEzNSwxOTQsMTIyLDI0OCwxMTAsMTAzLDExNywxMTUsNTksMjEsMjA0LDYxLDEwNywxOTYsMTMz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eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IkNvbW1pdCI6eyJ2ZWMiOls3LDE4Nyw2OCwxNyw0MiwyMTMsMTkzLDMwLDE0OCwxMzUsNjEsNDQsMjQ3LDEzNiwxODYsMTIwLDIwMywxNjQsMjI2LDExMyw0OSwxNzYsMTUxLDEyOSwxNzAsMTkwLDE5MSw2OCwxNDYsMjI0LDE0OCwxOTJdfX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbOTksMjQzLDIzMiw3NSwxMzgsNjUsMTQ3LDI0OSwyMTcsNzgsMjA3LDEzMSwxMiwxNjQsMTc0LDg4LDE0LDIwLDEyMSw5OSw2MCwyMDksMjM4LDkyLDI4LDIyLDE0NiwxNjQsOTUsMTAxLDU0LDEzNCwxMyw3MCwxNCwxNTIsMjQyLDkzLDE0NywxLDE2MiwxNDgsMTU0LDE0MCw0NSwxNDksMTI1LDE4NSwxMjksMjA0LDg4LDIwMiwyNSw1NywzMiwxMjAsMTQxLDE0OSwyMDksMTAsMTU4LDE5MywyMTQsOF19fX19LHsibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOls3MSwxMCw2OSwyMCwyLDkyLDcwLDg5LDIyNCw0OCwxMjYsNzgsMTI0LDE4NiwxNDgsMTU5LDI0NSwxNzUsMTIyLDE1NSwxNiwyMDksMTUzLDIxNiwyMiwxNDEsMTE3LDc1LDk3LDcyLDI0NywzNl19fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyNTQsMTgyLDk1LDE4NywyOSwxMDYsMjQxLDI4LDE5NCwyMDMsNTYsMzcsMjA3LDYwLDYsOCwxMTgsMTc3LDM5LDE1NCwyMjEsMTYsMTcsMjUyLDYwLDIxOSwxMjIsMjIyLDE2MywyMzMsMTMyLDI1XX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTgsMTExLDk4XX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJLZXlQYWNrYWdlIjp7Im5vdF9iZWZvcmUiOjE3MjA2ODg5NDksIm5vdF9hZnRlciI6MTcyNzk1MDE0OX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzQ5LDEwMSwxOTEsNjAsMTY3LDI0NSwyMjIsMjAyLDIwOCwzMCwyMTIsMTM5LDY1LDEwLDI5LDIzOCwxOTYsNzQsODcsNTAsNywxNjMsMjE4LDEwNiw2MywzLDg3LDIwMywxMywyMywxMCwyMiwxMyw4MSwxNDAsMTc4LDkxLDExNCw0NCwyMjUsNzUsNywyMjksODEsMTM3LDYzLDEyNCwxNTUsMTYwLDI0LDE4Miw0Miw5Myw1MiwyNCwxNjcsMTk5LDIwMiwxNzksMTYwLDE1OCw2MiwxNiwxM119fX19XSwicGFyZW50X25vZGVzIjpbeyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxMywxODEsNjIsMTU5LDI3LDI1NSwyNDMsMjExLDI0Nyw1MiwyNDcsMTM4LDM3LDI1NSw2MCwzMCwxMjQsNTAsNjQsNzksMjQxLDIyMSwxNTUsMzUsMjA2LDIyOSwxNDAsNzMsMTgsMjQ2LDI0Niw4NV19fSwicGFyZW50X2hhc2giOnsidmVjIjpbXX0sInVubWVyZ2VkX2xlYXZlcyI6eyJsaXN0IjpbXX19fV0sImRlZmF1bHRfbGVhZiI6eyJub2RlIjpudWxsfSwiZGVmYXVsdF9wYXJlbnQiOnsibm9kZSI6bnVsbH19LCJ0cmVlX2hhc2giOlsyLDE2MywxMTEsMjM3LDE0NiwyMjgsMTI0LDIwNSwxNTIsNDIsNzYsNDgsOTIsOTksNDEsMTQ1LDIyNCwzOSw5LDE0NSwxODMsMTY0LDIyMSwxMDgsMTYsMiw1NywyNTAsNiwxMzQsMjIzLDI0OV19AAAAAAAAAFoAAAAAAAADOEVwb2NoU2VjcmV0c3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsiaW5pdF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzIyLDg1LDEzOSw5NCwxMzksMTkxLDEwLDUwLDIwMywyMTYsNTUsNzgsMTMsMjUxLDc1LDkzLDE5MywxNjcsNDEsOTUsMTc3LDMxLDI0MSwxMTcsMTcwLDIwNSwxMjIsMTA1LDEzMiwxNzIsMTA1LDExN119fX0sImV4cG9ydGVyX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNzksMjMwLDEzNSwxMTIsMTM4LDI0NCwyNTUsMjUxLDIyMiwxMDcsNTIsMTAxLDEwNywzOCwxMjIsMzYsMTg2LDg4LDIxMCwxOTYsMTQ5LDIxMSwxNTQsOTQsMTA5LDAsMTQ5LDE3LDE0OSwxNDYsNjQsMTAxXX19fSwiZXBvY2hfYXV0aGVudGljYXRvciI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMjI2LDUxLDQ5LDE3NCwyMzksNzcsNDAsMTE1LDI1NSwxNzUsMzgsOTAsMjAyLDE5Nyw4NiwxNDEsMTY2LDIyMywyNDcsMjI5LDE4OSwyMjgsMTEzLDIzMyw4MCwxMzgsODgsMjAsNjYsMTc2LDkwLDUyXX19fSwiZXh0ZXJuYWxfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMjAsMjM3LDExOSwxMjIsNjcsMTEwLDg5LDI0NiwyMDUsMTQ4LDE5NiwyNDEsMTQ2LDEzOSw0OSwyLDI0OCwxOTUsNTEsNjYsOTYsMTA2LDEzOSwzMCwyNDIsMTk4LDk5LDIxNyw4MiwyNDksMTY3LDcyXX19fSwicmVzdW1wdGlvbl9wc2siOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzExLDc1LDIzOSwyNCwyMzYsMTY5LDI1MCwxNDQsODcsMTMzLDgzLDEyMCw5MSwyMDQsMTg0LDg2LDE0NCwxMTAsMjMyLDIwMSwxNTYsNCwyMTEsMTU2LDE3NywzOSwzNSwyMzAsMjE5LDE3LDEwNyw1MF19fX19AAAAAAAAAGAAAAAAAAABFk1sc0dyb3VwSm9pbkNvbmZpZ3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsid2lyZV9mb3JtYXRfcG9saWN5Ijp7Im91dGdvaW5nIjoiQWx3YXlzQ2lwaGVydGV4dCIsImluY29taW5nIjoiQWx3YXlzQ2lwaGVydGV4dCJ9LCJwYWRkaW5nX3NpemUiOjAsIm1heF9wYXN0X2Vwb2NocyI6MCwibnVtYmVyX29mX3Jlc3VtcHRpb25fcHNrcyI6MCwidXNlX3JhdGNoZXRfdHJlZV9leHRlbnNpb24iOmZhbHNlLCJzZW5kZXJfcmF0Y2hldF9jb25maWd1cmF0aW9uIjp7Im91dF9vZl9vcmRlcl90b2xlcmFuY2UiOjUsIm1heGltdW1fZm9yd2FyZF9kaXN0YW5jZSI6MTAwMH19AAAAAAAAAFgAAAAAAAAADUdyb3VwU3RhdGV7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAEiT3BlcmF0aW9uYWwiAAAAAAAAAF4AAAAAAAAAAU93bkxlYWZOb2RlSW5kZXh7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAEwAAAAAAAAAF0AAAAAAAACR0Vwb2NoS2V5UGFpcnN7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19MTAAAVt7InB1YmxpY19rZXkiOnsia2V5Ijp7InZlYyI6WzE2NiwyMzksMjUsMTk0LDIxNSwxMTcsOTcsMjM5LDAsMjI0LDE5LDUyLDEyNiwyMDIsMTA5LDI1NCwyMywxMzYsMTU2LDM5LDE2MSwxMzMsOTUsMzgsMzUsMTIzLDEyNCwxMzgsMjUsMjM2LDg4LDEwMF19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzI0Niw5OCwxMzgsNywyLDIyLDQ0LDE2MSwxNTEsMTI0LDQ5LDEzMCwxNjgsMTksMTMxLDczLDE0OCwyMzAsMTEwLDU5LDE3OCw5NywyMiwxNjcsMjQyLDg1LDIzNCwxMDEsNjMsNTgsMjM5LDI0NF19fX0seyJwdWJsaWNfa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxMywxODEsNjIsMTU5LDI3LDI1NSwyNDMsMjExLDI0Nyw1MiwyNDcsMTM4LDM3LDI1NSw2MCwzMCwxMjQsNTAsNjQsNzksMjQxLDIyMSwxNTUsMzUsMjA2LDIyOSwxNDAsNzMsMTgsMjQ2LDI0Niw4NV19fSwicHJpdmF0ZV9rZXkiOnsia2V5Ijp7InZlYyI6WzI3LDIyOSwxNDIsODMsMzQsMTQxLDMwLDYyLDExNSwxMywxOTAsNzgsODEsMTUxLDk4LDE4LDI5LDU0LDIxOCw2MSw2MCwyMzAsMzMsNzgsMTM0LDE5OSwxMTgsNjIsMTk1LDI0MiwxMjYsMjMzXX19fV0AAAAAAAAAWwAAAAAAAAFoUmVzdW1wdGlvblBza3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X251bWJlcl9vZl9zZWNyZXRzIjozMiwicmVzdW1wdGlvbl9wc2siOltbMCx7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsyMjIsMTksMTAwLDkzLDI1MCw2MSwxMzQsNDgsMTYzLDE3NSwxODUsMTEwLDc2LDMwLDIwNywyNDAsMTI3LDIzMCwxMTYsMTgzLDEzMSwyNDIsOSwxNzgsMTA1LDIsMTc1LDE3Miw4OCwxMzEsMjMzLDIzM119fX1dLFsxLHsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzExLDc1LDIzOSwyNCwyMzYsMTY5LDI1MCwxNDQsODcsMTMzLDgzLDEyMCw5MSwyMDQsMTg0LDg2LDE0NCwxMTAsMjMyLDIwMSwxNTYsNCwyMTEsMTU2LDE3NywzOSwzNSwyMzAsMjE5LDE3LDEwNyw1MF19fX1dXSwiY3Vyc29yIjoyfQ==","AAAAAAAAAA0AAAAAAAAAXQAAAAAAAACMQ29uZmlybWF0aW9uVGFneyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJtYWNfdmFsdWUiOnsidmVjIjpbNDMsMjQwLDE4OCwyMzUsNzksNjIsNiwxNzksMTk1LDE0MCwzMSwxNDgsMjEsMjA1LDIzNSw3NSwxOTEsMjA2LDE1NywxODIsMTEwLDMyLDExMywxNTksMTYsNzEsMTU2LDEyNSwxNjgsMjAxLDEyNiwyMjRdfX0AAAAAAAAAWgAAAAAAAAH2R3JvdXBDb250ZXh0eyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJwcm90b2NvbF92ZXJzaW9uIjoiTWxzMTAiLCJjaXBoZXJzdWl0ZSI6Ik1MU18xMjhfREhLRU1YMjU1MTlfQUVTMTI4R0NNX1NIQTI1Nl9FZDI1NTE5IiwiZ3JvdXBfaWQiOnsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0sImVwb2NoIjoxLCJ0cmVlX2hhc2giOnsidmVjIjpbMiwxNjMsMTExLDIzNywxNDYsMjI4LDEyNCwyMDUsMTUyLDQyLDc2LDQ4LDkyLDk5LDQxLDE0NSwyMjQsMzksOSwxNDUsMTgzLDE2NCwyMjEsMTA4LDE2LDIsNTcsMjUwLDYsMTM0LDIyMywyNDldfSwiY29uZmlybWVkX3RyYW5zY3JpcHRfaGFzaCI6eyJ2ZWMiOlsyMSwxMzUsMTk0LDEyMiwyNDgsMTEwLDEwMywxMTcsMTE1LDU5LDIxLDIwNCw2MSwxMDcsMTk2LDEzMywzMSwyNDEsMTYsMTM2LDEyMywxNTIsMTM1LDE1NCwxNzUsMTY3LDIwNSw5NywxMzIsMTg3LDMsM119LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fQAAAAAAAABcAAAAAAAABhtNZXNzYWdlU2VjcmV0c3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X2Vwb2NocyI6MCwicGFzdF9lcG9jaF90cmVlcyI6W10sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzM2LDE5NSw3Miw3NywxNjQsMjAsMjUsMjE4LDI0OCwxMSwxMDYsMjEzLDIyOSw2LDY5LDc2LDEwNiw4NiwxMDYsMTcsMjUyLDIzNSw4MiwyMTAsNTYsMTE4LDM2LDgwLDAsMTM2LDE1OCw0M119fX0sIm1lbWJlcnNoaXBfa2V5Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMSwxMDUsOTAsMTIsMjEwLDg4LDQ5LDgxLDE2OCwxMjMsMTQ4LDExOSw5OCw0MCwxODcsODUsMjAyLDIwMSwxNzQsNDMsMTI4LDM2LDEyNSwxNzMsMTI0LDMsMzgsMjA2LDk3LDE5OSwxMTMsMTE5XX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNDAsMTU3LDk1LDE1NSwxNjgsMTU2LDE5OSw0NywyMDAsMTEzLDg0LDIzNSwxNDAsMjUsNzUsNTMsNjYsMTcxLDIzNSwxNjUsMTgyLDIxMCwxMDgsMTY0LDIxNSwxMDQsMTMxLDYwLDEyLDUsMTE4LDEyM119fX0sInNlcmlhbGl6ZWRfY29udGV4dCI6WzAsMSwwLDEsMTYsMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjIsMCwwLDAsMCwwLDAsMCwxLDMyLDIsMTYzLDExMSwyMzcsMTQ2LDIyOCwxMjQsMjA1LDE1Miw0Miw3Niw0OCw5Miw5OSw0MSwxNDUsMjI0LDM5LDksMTQ1LDE4MywxNjQsMjIxLDEwOCwxNiwyLDU3LDI1MCw2LDEzNCwyMjMsMjQ5LDMyLDIxLDEzNSwxOTQsMTIyLDI0OCwxMTAsMTAzLDExNywxMTUsNTksMjEsMjA0LDYxLDEwNywxOTYsMTMz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LDIzOSwwLDIyNCwxOSw1MiwxMjYsMjAyLDEwOSwyNTQsMjMsMTM2LDE1NiwzOSwxNjEsMTMzLDk1LDM4LDM1LDEyMywxMjQsMTM4LDI1LDIzNiw4OCwxMDBdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbODQsMTY5LDY1LDE0OSw3NSwyMjYsMTkxLDMzLDI1NCwxNDEsMTcsMjExLDE4MSw0OCw5MSwxNTksMTAwLDQ1LDc4LDExMiw3OSwxNDgsMTMsMTUsMjIyLDE5NiwyMTEsNjksMTU2LDM2LDE5MSwxMF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbNywxODcsNjgsMTcsNDIsMjEzLDE5MywzMCwxNDgsMTM1LDYxLDQ0LDI0NywxMzYsMTg2LDEyMCwyMDMsMTY0LDIyNiwxMTMsNDksMTc2LDE1MSwxMjksMTcwLDE5MCwxOTEsNjgsMTQ2LDIyNCwxNDgsMTkyXX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6Wzk5LDI0MywyMzIsNzUsMTM4LDY1LDE0NywyNDksMjE3LDc4LDIwNywxMzEsMTIsMTY0LDE3NCw4OCwxNCwyMCwxMjEsOTksNjAsMjA5LDIzOCw5MiwyOCwyMiwxNDYsMTY0LDk1LDEwMSw1NCwxMzQsMTMsNzAsMTQsMTUyLDI0Miw5MywxNDcsMSwxNjIsMTQ4LDE1NCwxNDAsNDUsMTQ5LDEyNSwxODUsMTI5LDIwNCw4OCwyMDIsMjUsNTcsMzIsMTIwLDE0MSwxNDksMjA5LDEwLDE1OCwxOTMsMjE0LDhdfX19fSx7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNzEsMTAsNjksMjAsMiw5Miw3MCw4OSwyMjQsNDgsMTI2LDc4LDEyNCwxODYsMTQ4LDE1OSwyNDUsMTc1LDEyMiwxNTUsMTYsMjA5LDE1MywyMTYsMjIsMTQxLDExNyw3NSw5Nyw3MiwyNDcsMzZdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMjU0LDE4Miw5NSwxODcsMjksMTA2LDI0MSwyOCwxOTQsMjAzLDU2LDM3LDIwNyw2MCw2LDgsMTE4LDE3NywzOSwxNTQsMjIxLDE2LDE3LDI1Miw2MCwyMTksMTIyLDIyMiwxNjMsMjMzLDEzMiwyNV19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk4LDExMSw5OF19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTQ5LCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNDl9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OSwxMDEsMTkxLDYwLDE2NywyNDUsMjIyLDIwMiwyMDgsMzAsMjEyLDEzOSw2NSwxMCwyOSwyMzgsMTk2LDc0LDg3LDUwLDcsMTYzLDIxOCwxMDYsNjMsMyw4NywyMDMsMTMsMjMsMTAsMjIsMTMsODEsMTQwLDE3OCw5MSwxMTQsNDQsMjI1LDc1LDcsMjI5LDgxLDEzNyw2MywxMjQsMTU1LDE2MCwyNCwxODIsNDIsOTMsNTIsMjQsMTY3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eyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fQABeyJQZW5kaW5nQ29tbWl0Ijp7Ik1lbWJlciI6eyJzdGFnZWRfcHJvcG9zYWxfcXVldWUiOnsicHJvcG9zYWxfcmVmZXJlbmNlcyI6W3sidmFsdWUiOnsidmVjIjpbMTQ4LDEwNiwyNSwxMDQsMjA3LDE3Myw1NCw4OCwxNzMsNjAsMTg4LDE4MSwxMiwyNTEsOTAsMTM2LDIxNCwyMDIsMiwxNSwxNzgsNSwxMzIsOCw1LDU3LDIsNDUsNjcsMTk0LDcwLDgxXX19XSwicXVldWVkX3Byb3Bvc2FscyI6W1t7InZhbHVlIjp7InZlYyI6WzE0OCwxMDYsMjUsMTA0LDIwNywxNzMsNTQsODgsMTczLDYwLDE4OCwxODEsMTIsMjUxLDkwLDEzNiwyMTQsMjAyLDIsMTUsMTc4LDUsMTMyLDgsNSw1NywyLDQ1LDY3LDE5NCw3MCw4MV19fSx7InByb3Bvc2FsIjp7Ikdyb3VwQ29udGV4dEV4dGVuc2lvbnMiOnsiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOlt7IlJlcXVpcmVkQ2FwYWJpbGl0aWVzIjp7ImV4dGVuc2lvbl90eXBlcyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxfdHlwZXMiOltdLCJjcmVkZW50aWFsX3R5cGVzIjpbXX19XX19fSwicHJvcG9zYWxfcmVmZXJlbmNlIjp7InZhbHVlIjp7InZlYyI6WzE0OCwxMDYsMjUsMTA0LDIwNywxNzMsNTQsODgsMTczLDYwLDE4OCwxODEsMTIsMjUxLDkwLDEzNiwyMTQsMjAyLDIsMTUsMTc4LDUsMTMyLDgsNSw1NywyLDQ1LDY3LDE5NCw3MCw4MV19fSwic2VuZGVyIjp7Ik1lbWJlciI6MH0sInByb3Bvc2FsX29yX3JlZl90eXBlIjoiUHJvcG9zYWwifV1dfSwic3RhdGUiOnsiR3JvdXBNZW1iZXIiOnsiZ3JvdXBfZXBvY2hfc2VjcmV0cyI6eyJpbml0X3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTU5LDI0NiwyNTAsMjI1LDIzMCwxMiwxNDAsMzksMjAxLDEyNCwxODQsNzUsMjI2LDE1MCwxNDQsODUsMTc4LDIzOCwyMiwxNzEsMTc1LDExNSwxMzAsNjAsMTAsNDAsNTcsMTgsMjM4LDEyOSw4NywxOF19fX0sImV4cG9ydGVyX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTU0LDIzNSwyOSwyMTgsMjQ4LDEyNiw4NSwyOCwxNjksNDAsODEsMTgwLDIwMSwxNTAsMiwzNiw1MywyNSwxOTEsNTMsMTM5LDcwLDEyOSwxMTUsMjA2LDE3NSwyMSwxNzIsMTI4LDExOSwxMzEsOTNdfX19LCJlcG9jaF9hdXRoZW50aWNhdG9yIjp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxMjcsMTI1LDIyMyw4Myw5NCwxMTUsMjExLDE3OSwwLDIyNiwxMDksMTE0LDkzLDE3NiwxNjIsMTAzLDI4LDE1NSw3Niw3NywxNjUsMTcsNzAsMjM1LDIzOSwxNjIsMTcwLDYzLDIzNSw0MCwxMzMsNjFdfX19LCJleHRlcm5hbF9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6Wzc0LDg1LDQ2LDYxLDEsMTY2LDE4MSwyMTAsMTgyLDksMTk4LDUzLDIwLDIxMCwyMjYsMTcsMjEwLDc3LDEwOCwyMjUsMTk5LDEzOSwzNywyMTMsMjIyLDIyNiwyMjAsMTIyLDE3LDEzMywyMjYsMzBdfX19LCJyZXN1bXB0aW9uX3BzayI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTk5LDE5MCw4Nyw4OCw2LDIzNiwxNTMsMTcwLDEzMiwyNTUsMTU5LDE0MSw0MiwxNjYsMTE4LDEyMSwxODMsMTY1LDQyLDE3OSw0MywxMzgsMTkyLDE5OCwyOSwxMTUsMTM5LDIwMywyMzksNywxNjYsOTBdfX19fSwibWVzc2FnZV9zZWNyZXRzIjp7InNlbmRlcl9kYXRhX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTEyLDE0MiwyMjQsMTI0LDE4NSw5NiwzOCw4OCwwLDEyNiwyMzcsODksNDQsMTEwLDE5OCw0OCwxMzMsMTU1LDE4Miw4OSwxNCw5OCwyMDEsMTIyLDUsNzYsOTUsMjEsMTcyLDQ1LDE2NiwxNzRdfX19LCJtZW1iZXJzaGlwX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMiwxMywyMDMsMzEsODcsMjAwLDE3MiwyMzAsNzEsNyw1Niw1LDE0Niw5OSwxOTUsMCwxMjgsNjgsMjUwLDU4LDkwLDM1LDE4MCwxNjYsODUsOTEsMTE1LDE2OCwxODAsMTMzLDg1LDUzXX19fSwiY29uZmlybWF0aW9uX2tleSI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTc1LDE2MCwxMDksNzAsMjAsMjMyLDIzNCwyNTIsMTA3LDY1LDUxLDIzOCwxNyw5OSwxNjcsMjQyLDEyMywxNzUsMjAsMTc1LDE1OCwxNzQsNiwxMjQsMjQ2LDE0OSwxNTAsMzEsMTA4LDgwLDQ5LDhdfX19LCJzZXJpYWxpemVkX2NvbnRleHQiOlswLDEsMCwxLDE2LDIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyLDAsMCwwLDAsMCwwLDAsMiwzMiw3Nyw0NywyMjUsMjAxLDE2NCw2MiwxMTAsMjUzLDEyLDMsMTk1LDEzMiwxNjYsMTgsNjIsMjIxLDg2LDEwMCw2LDExMiwxNjEsMTczLDg1LDI0OCwyMjQsODksMTI5LDIxMyw0NiwxMzksNTYsNDgsMzIsMTA0LDI1MCwyOCwyLDE3MSw5MCwyMTksMTM1LDE1OCwxNjMsMjQyLDI0NywyMjUsMjMsMTkyLDE5NSw4NSwyMDIsMTQ4LDczLDI1MiwxNDAsNywyNDgsNjYsMTgyLDIyNCw3Miw2NywxNjcsMTA2LDIxOSw4LDAsMyw1LDIsMjQwLDY2LDAsMF0sInNlY3JldF90cmVlIjp7Im93bl9pbmRleCI6MCwibGVhZl9ub2RlcyI6W251bGwsbnVsbF0sInBhcmVudF9ub2RlcyI6W3sic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzM3LDM5LDE3MCw3LDE0MCwyMzgsMjA0LDgxLDI0MywzOSwyMTgsNTEsMTA5LDUsMTMwLDM0LDUxLDI0OCwyNDcsMTksMTY3LDI1NCwxMzksMTkxLDUxLDMsMjcsMjIxLDEyLDEzNSwyNiw1OF19fX0sbnVsbF0sImhhbmRzaGFrZV9zZW5kZXJfcmF0Y2hldHMiOltudWxsLG51bGxdLCJhcHBsaWNhdGlvbl9zZW5kZXJfcmF0Y2hldHMiOltudWxsLG51bGxdLCJzaXplIjozfX0sInN0YWdlZF9kaWZmIjp7InN0YWdlZF9kaWZmIjp7ImRpZmYiOnsibGVhZl9kaWZmIjp7IjAiOnsibm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNSwyNTIsNzIsMTEwLDkyLDU0LDg5LDMyLDI0MCwxMzEsNjYsMTU3LDk4LDE3MCwyNDQsNjYsNzIsMTkxLDQ5LDIwMiwxOTYsMTYxLDE3MywxNywyMjIsMTQyLDY0LDIzMSwxODcsMTcyLDE2OSwxN119fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls4NCwxNjksNjUsMTQ5LDc1LDIyNiwxOTEsMzMsMjU0LDE0MSwxNywyMTEsMTgxLDQ4LDkxLDE1OSwxMDAsNDUsNzgsMTEyLDc5LDE0OCwxMywxNSwyMjIsMTk2LDIxMSw2OSwxNTYsMzYsMTkxLDEwXX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTcsMTA4LDEwNSw5OSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IkNvbW1pdCI6eyJ2ZWMiOls3Niw3LDIxOCwyNDEsOTUsMTk2LDE0NSwxNzQsMzQsMTkwLDU3LDIzLDI1NSw0MywyMTgsMTk3LDIwMywxMzIsMTMwLDEzMCwyMzgsMjA3LDQ0LDEwNiwxOTcsNzgsNzEsNTgsMTY3LDE4MCwxMzksMTkyXX19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W119fSwic2lnbmF0dXJlIjp7InZhbHVlIjp7InZlYyI6WzE3NiwxMDMsMjE2LDk4LDgsODIsMTI1LDE5NiwyNDgsMTM3LDg3LDIxNiwyNTAsMTk2LDE5MSw1LDE1LDIxOSwxNjEsMTAsMzQsMjI2LDkzLDE4MCwxNTYsMTQyLDk2LDU4LDE5OSwxODEsMTM0LDE2NiwxNTYsMTM4LDE4NywxNywxNjQsMTk4LDM2LDgzLDExOCwxMTMsMTE3LDEwMSwyNTMsNjQsNzEsNzgsMjI5LDEzOSw4Niw4MiwxNCwxNTYsMTU0LDUzLDIxOSw4OSw1Miw0LDE5LDk1LDksN119fX19fSwicGFyZW50X2RpZmYiOnsiMCI6eyJub2RlIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNzEsMTgwLDIyOCwxODgsMTA0LDIwMSwxOTQsMTcxLDM0LDIxNSwyMDcsMjQ3LDI0MCw4NywyMiwxNjIsNTEsMjE2LDEwLDE5NCw0MCw0OSwxNzcsMTk2LDE1NiwyMjMsMTI4LDIwLDIwOCwxMzQsMjE0LDc2XX19LCJwYXJlbnRfaGFzaCI6eyJ2ZWMiOltdfSwidW5tZXJnZWRfbGVhdmVzIjp7Imxpc3QiOltdfX19fSwic2l6ZSI6M30sIm5ld190cmVlX2hhc2giOls3Nyw0NywyMjUsMjAxLDE2NCw2MiwxMTAsMjUzLDEyLDMsMTk1LDEzMiwxNjYsMTgsNjIsMjIxLDg2LDEwMCw2LDExMiwxNjEsMTczLDg1LDI0OCwyMjQsODksMTI5LDIxMyw0NiwxMzksNTYsNDhdfSwiZ3JvdXBfY29udGV4dCI6eyJwcm90b2NvbF92ZXJzaW9uIjoiTWxzMTAiLCJjaXBoZXJzdWl0ZSI6Ik1MU18xMjhfREhLRU1YMjU1MTlfQUVTMTI4R0NNX1NIQTI1Nl9FZDI1NTE5IiwiZ3JvdXBfaWQiOnsidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0sImVwb2NoIjoyLCJ0cmVlX2hhc2giOnsidmVjIjpbNzcsNDcsMjI1LDIwMSwxNjQsNjIsMTEwLDI1MywxMiwzLDE5NSwxMzIsMTY2LDE4LDYyLDIyMSw4NiwxMDAsNiwxMTIsMTYxLDE3Myw4NSwyNDgsMjI0LDg5LDEyOSwyMTMsNDYsMTM5LDU2LDQ4XX0sImNvbmZpcm1lZF90cmFuc2NyaXB0X2hhc2giOnsidmVjIjpbMTA0LDI1MCwyOCwyLDE3MSw5MCwyMTksMTM1LDE1OCwxNjMsMjQyLDI0NywyMjUsMjMsMTkyLDE5NSw4NSwyMDIsMTQ4LDczLDI1MiwxNDAsNywyNDgsNjYsMTgyLDIyNCw3Miw2NywxNjcsMTA2LDIxOV19LCJleHRlbnNpb25zIjp7InVuaXF1ZSI6W3siUmVxdWlyZWRDYXBhYmlsaXRpZXMiOnsiZXh0ZW5zaW9uX3R5cGVzIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbF90eXBlcyI6W10sImNyZWRlbnRpYWxfdHlwZXMiOltdfX1dfX0sImludGVyaW1fdHJhbnNjcmlwdF9oYXNoIjpbMTI2LDIzMSw5Miw1MywyMiwxMTMsNjUsMjksMTk2LDEzOSwxMjUsMjgsODgsMTU3LDgxLDExLDc4LDQ1LDQsMTI5LDE3NiwxOTYsMTEyLDI1MywxMjksMTI1LDExMSwyMzQsOSwxNzMsMTEzLDI0M10sImNvbmZpcm1hdGlvbl90YWciOnsibWFjX3ZhbHVlIjp7InZlYyI6WzE2NCwyMjcsNTksMTI4LDEsMTU0LDIzMiwxMTQsODMsMTAsMTY5LDIxNSwyNDMsMjE2LDI0MiwxNzUsODEsMzYsMTc4LDE3MSwxMzYsMTI5LDI5LDE3LDU0LDIwLDU5LDk3LDE1LDM5LDIxNywxNDRdfX19LCJuZXdfa2V5cGFpcnMiOlt7InB1YmxpY19rZXkiOnsia2V5Ijp7InZlYyI6WzE1LDI1Miw3MiwxMTAsOTIsNTQsODksMzIsMjQwLDEzMSw2NiwxNTcsOTgsMTcwLDI0NCw2Niw3MiwxOTEsNDksMjAyLDE5NiwxNjEsMTczLDE3LDIyMiwxNDIsNjQsMjMxLDE4NywxNzIsMTY5LDE3XX19LCJwcml2YXRlX2tleSI6eyJrZXkiOnsidmVjIjpbMTc0LDU4LDg2LDE5OCwxMzYsMjA2LDIzMiwyNTMsMzYsMTIsMzAsMTQ2LDEyOSwxMDYsMTA4LDEyMSwyMDIsMjUzLDI0MiwxNTksOTcsMTkzLDIxMywyMSwxNzAsMjM0LDY4LDM1LDE1NCwyMjUsNTMsMTA4XX19fSx7InB1YmxpY19rZXkiOnsia2V5Ijp7InZlYyI6WzE3MSwxODAsMjI4LDE4OCwxMDQsMjAxLDE5NCwxNzEsMzQsMjE1LDIwNywyNDcsMjQwLDg3LDIyLDE2Miw1MSwyMTYsMTAsMTk0LDQwLDQ5LDE3NywxOTYsMTU2LDIyMywxMjgsMjAsMjA4LDEzNCwyMTQsNzZdfX0sInByaXZhdGVfa2V5Ijp7ImtleSI6eyJ2ZWMiOlswLDI0LDE4OSwyNDAsMzAsNSwyNTUsMjIxLDEyNyw1OCwxODQsMTA4LDIwMywyMzAsMTgsMjgsNTEsNDMsMjUwLDEyNywxODUsMTI1LDI1MiwxMDQsMjUyLDMyLDE2NywyMCw5MSw3MiwzMSwxODFdfX19XSwibmV3X2xlYWZfa2V5cGFpcl9vcHRpb24iOm51bGwsInVwZGF0ZV9wYXRoX2xlYWZfbm9kZSI6eyJwYXlsb2FkIjp7ImVuY3J5cHRpb25fa2V5Ijp7ImtleSI6eyJ2ZWMiOlsxNSwyNTIsNzIsMTEwLDkyLDU0LDg5LDMyLDI0MCwxMzEsNjYsMTU3LDk4LDE3MCwyNDQsNjYsNzIsMTkxLDQ5LDIwMiwxOTYsMTYxLDE3MywxNywyMjIsMTQyLDY0LDIzMSwxODcsMTcyLDE2OSwxN119fSwic2lnbmF0dXJlX2tleSI6eyJ2YWx1ZSI6eyJ2ZWMiOls4NCwxNjksNjUsMTQ5LDc1LDIyNiwxOTEsMzMsMjU0LDE0MSwxNywyMTEsMTgxLDQ4LDkxLDE1OSwxMDAsNDUsNzgsMTEyLDc5LDE0OCwxMywxNSwyMjIsMTk2LDIxMSw2OSwxNTYsMzYsMTkxLDEwXX19LCJjcmVkZW50aWFsIjp7ImNyZWRlbnRpYWxfdHlwZSI6IkJhc2ljIiwic2VyaWFsaXplZF9jcmVkZW50aWFsX2NvbnRlbnQiOnsidmVjIjpbOTcsMTA4LDEwNSw5OSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJz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bCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbNzYsNywyMTgsMjQxLDk1LDE5NiwxNDUsMTc0LDM0LDE5MCw1NywyMywyNTUsNDMsMjE4LDE5NywyMDMsMTMyLDEzMCwxMzAsMjM4LDIwNyw0NCwxMDYsMTk3LDc4LDcxLDU4LDE2NywxODAsMTM5LDE5Ml19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNzYsMTAzLDIxNiw5OCw4LDgyLDEyNSwxOTYsMjQ4LDEzNyw4NywyMTYsMjUwLDE5NiwxOTEsNSwxNSwyMTksMTYxLDEwLDM0LDIyNiw5MywxODAsMTU2LDE0Miw5Niw1OCwxOTksMTgxLDEzNCwxNjYsMTU2LDEzOCwxODcsMTcsMTY0LDE5OCwzNiw4MywxMTgsMTEzLDExNywxMDEsMjUzLDY0LDcxLDc4LDIyOSwxMzksODYsODIsMTQsMTU2LDE1NCw1MywyMTksODksNTIsNCwxOSw5NSw5LDddfX19fSx7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNzEsMTAsNjksMjAsMiw5Miw3MCw4OSwyMjQsNDgsMTI2LDc4LDEyNCwxODYsMTQ4LDE1OSwyNDUsMTc1LDEyMiwxNTUsMTYsMjA5LDE1MywyMTYsMjIsMTQxLDExNyw3NSw5Nyw3MiwyNDcsMzZdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMjU0LDE4Miw5NSwxODcsMjksMTA2LDI0MSwyOCwxOTQsMjAzLDU2LDM3LDIwNyw2MCw2LDgsMTE4LDE3NywzOSwxNTQsMjIxLDE2LDE3LDI1Miw2MCwyMTksMTIyLDIyMiwxNjMsMjMzLDEzMiwyNV19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk4LDExMSw5OF19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTQ5LCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNDl9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OSwxMDEsMTkxLDYwLDE2NywyNDUsMjIyLDIwMiwyMDgsMzAsMjEyLDEzOSw2NSwxMCwyOSwyMzgsMTk2LDc0LDg3LDUwLDcsMTYzLDIxOCwxMDYsNjMsMyw4NywyMDMsMTMsMjMsMTAsMjIsMTMsODEsMTQwLDE3OCw5MSwxMTQsNDQsMjI1LDc1LDcsMjI5LDgxLDEzNyw2MywxMjQsMTU1LDE2MCwyNCwxODIsNDIsOTMsNTIsMjQsMTY3LDE5OSwyMDIsMTc5LDE2MCwxNTgsNjIsMTYsMTNdfX19fV0sInBhcmVudF9ub2RlcyI6W3sibm9kZSI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTcxLDE4MCwyMjgsMTg4LDEwNCwyMDEsMTk0LDE3MSwzNCwyMTUsMjA3LDI0NywyNDAsODcsMjIsMTYyLDUxLDIxNiwxMCwxOTQsNDAsNDksMTc3LDE5NiwxNTYsMjIzLDEyOCwyMCwyMDgsMTM0LDIxNCw3Nl19fSwicGFyZW50X2hhc2giOnsidmVjIjpbXX0sInVubWVyZ2VkX2xlYXZlcyI6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NiwxODMsMTMxLDI0Miw5LDE3OCwxMDUsMiwxNzUsMTcyLDg4LDEzMSwyMzMsMjMzXX19fV0sWzEseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTEsNzUsMjM5LDI0LDIzNiwxNjksMjUwLDE0NCw4NywxMzMsODMsMTIwLDkxLDIwNCwxODQsODYsMTQ0LDExMCwyMzIsMjAxLDE1Niw0LDIxMSwxNTYsMTc3LDM5LDM1LDIzMCwyMTksMTcsMTA3LDUwXX19fV0sWzIseyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbMTk5LDE5MCw4Nyw4OCw2LDIzNiwxNTMsMTcwLDEzMiwyNTUsMTU5LDE0MSw0MiwxNjYsMTE4LDEyMSwxODMsMTY1LDQyLDE3OSw0MywxMzgsMTkyLDE5OCwyOSwxMTUsMTM5LDIwMywyMzksNywxNjYsOTBdfX19XV0sImN1cnNvciI6M30=","AAAAAAAAAA8AAAAAAAAA5QAAAAAAAAayUXVldWVkUHJvcG9zYWxbeyJ2YWx1ZSI6eyJ2ZWMiOlsyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMl19fSx7InZhbHVlIjp7InZlYyI6Wzk1LDE0MywyNSw5NCwxODgsMTU4LDE4LDE0MiwxNDcsMjI1LDEwMCwyMDQsMTUxLDgzLDEsMTksMTM5LDYyLDg3LDIwNCwxNjMsMTA1LDI0NSwxODYsMTU1LDkxLDE1LDExNyw5NCwyMTUsMTAwLDIyNl19fV0AAXsicHJvcG9zYWwiOnsiQWRkIjp7ImtleV9wYWNrYWdlIjp7InBheWxvYWQiOnsicHJvdG9jb2xfdmVyc2lvbiI6Ik1sczEwIiwiY2lwaGVyc3VpdGUiOiJNTFNfMTI4X0RIS0VNWDI1NTE5X0FFUzEyOEdDTV9TSEEyNTZfRWQyNTUxOSIsImluaXRfa2V5Ijp7ImtleSI6eyJ2ZWMiOls4NCwxMzAsNTcsMjI1LDI2LDE3OSwyNDgsMjA3LDU3LDE5NiwxNDksMTYxLDEzOSwxNCwxNDgsMjQ3LDIsMTQ5LDc1LDIyNiwxMTAsMjUxLDEzMiwxNzAsMTYyLDE4NywxNTAsNjQsMjYsMjQyLDIyOSwzN119fSwibGVhZl9ub2RlIjp7InBheWxvYWQiOnsiZW5jcnlwdGlvbl9rZXkiOnsia2V5Ijp7InZlYyI6WzEwLDgzLDIzNiwxNTUsMTkyLDIxNiw0NSwxNjYsMTc0LDEwMSwxMDIsNDMsMTI4LDkxLDI0LDExOCwxNDYsNTcsMTg4LDExMSwyMiwyMSw0MiwxNDQsNjIsMjA1LDU5LDE1MiwzOCwyMDYsMTg1LDc3XX19LCJzaWduYXR1cmVfa2V5Ijp7InZhbHVlIjp7InZlYyI6WzQsMTIsMTczLDE3MSwxMjcsMTgsMjA0LDQzLDE1LDk5LDIyNiw2OCw4NCwxMzksNjUsMTQyLDI2LDQxLDg3LDE3MCwxNjYsNTQsMjQ3LDEyMiw0OSwxOTEsMTIxLDUyLDIsMjI3LDU5LDIzMl19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk5LDEwNCw5NywxMTQsMTA4LDEwNSwxMDFdfX0sImNhcGFiaWxpdGllcyI6eyJ2ZXJzaW9ucyI6WyJNbHMxMCJdLCJjaXBoZXJzdWl0ZXMiOlsxLDIsMyw3N10sImV4dGVuc2lvbnMiOlt7IlVua25vd24iOjYxNTA2fV0sInByb3Bvc2FscyI6W10sImNyZWRlbnRpYWxzIjpbIkJhc2ljIl19LCJsZWFmX25vZGVfc291cmNlIjp7IktleVBhY2thZ2UiOnsibm90X2JlZm9yZSI6MTcyMDY4ODk0OSwibm90X2FmdGVyIjoxNzI3OTUwMTQ5fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbMjA4LDcyLDIyLDE0MCwxOTgsMTg1LDM5LDEyMywxMDYsNDAsMTc4LDE5NCw4OSwxMzAsMjI3LDEzMCw2MiwxNCw4Nyw1MywxNSwxOTcsMTQsMTY4LDI0MywyNDEsNjIsMTM1LDIxMiwyMjEsMTIzLDUyLDU5LDU3LDI5LDIsMjMwLDIyNiwxNTQsMTU3LDY5LDE4NywxODQsMTg2LDE5NiwxNjUsMzYsODMsMTg0LDU1LDEzNiwxNTYsMTY2LDYxLDgyLDI1NCwyMTMsMTI3LDEzNywyMTEsMTA2LDM5LDI0NSwxMV19fX0sImV4dGVuc2lvbnMiOnsidW5pcXVlIjpbXX19LCJzaWduYXR1cmUiOnsidmFsdWUiOnsidmVjIjpbNTYsMTU2LDEwMiwxMTEsMTA1LDEyNCwyMjEsMjAyLDM5LDEwNywzLDExNSwyMjUsNjksMjM1LDEzMCwyNTIsMjE0LDIwMCwyNSwzMCwyNDksMTE2LDEyMywzNCwyMjQsNyw4MSwyNDUsNDAsMTMzLDI0Niw5NywzNywxLDI1MSw5MiwxNywxMTMsNzMsNzMsNDUsMzAsMjEsMjA0LDI0NSwxMSwyMDksODEsMzEsOTQsMjAzLDEyOCwxODIsMjM1LDc2LDQ3LDAsMTY4LDEzLDE3NCwyMjYsOTYsMl19fX19fSwicHJvcG9zYWxfcmVmZXJlbmNlIjp7InZhbHVlIjp7InZlYyI6Wzk1LDE0Mywy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AAAAAAAABipNZXNzYWdlU2VjcmV0c3sidmFsdWUiOnsidmVjIjpbMjE0LDIwNiwxNzMsMjcsMjA2LDg3LDIzMSw0LDE3MiwzMCwxMzAsMjEwLDIzMiw2OSw2MSwyMjJdfX0AAXsibWF4X2Vwb2NocyI6MCwicGFzdF9lcG9jaF90cmVlcyI6W10sIm1lc3NhZ2Vfc2VjcmV0cyI6eyJzZW5kZXJfZGF0YV9zZWNyZXQiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzExMiwxNDIsMjI0LDEyNCwxODUsOTYsMzgsODgsMCwxMjYsMjM3LDg5LDQ0LDExMCwxOTgsNDgsMTMzLDE1NSwxODIsODksMTQsOTgsMjAxLDEyMiw1LDc2LDk1LDIxLDE3Miw0NSwxNjYsMTc0XX19fSwibWVtYmVyc2hpcF9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzIsMTMsMjAzLDMxLDg3LDIwMCwxNzIsMjMwLDcxLDcsNTYsNSwxNDYsOTksMTk1LDAsMTI4LDY4LDI1MCw1OCw5MCwzNSwxODAsMTY2LDg1LDkxLDExNSwxNjgsMTgwLDEzMyw4NSw1M119fX0sImNvbmZpcm1hdGlvbl9rZXkiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzE3NSwxNjAsMTA5LDcwLDIwLDIzMiwyMzQsMjUyLDEwNyw2NSw1MSwyMzgsMTcsOTksMTY3LDI0MiwxMjMsMTc1LDIwLDE3NSwxNTgsMTc0LDYsMTI0LDI0NiwxNDksMTUwLDMxLDEwOCw4MCw0OSw4XX19fSwic2VyaWFsaXplZF9jb250ZXh0IjpbMCwxLDAsMSwxNiwyMTQsMjA2LDE3MywyNywyMDYsODcsMjMxLDQsMTcyLDMwLDEzMCwyMTAsMjMyLDY5LDYxLDIyMiwwLDAsMCwwLDAsMCwwLDIsMzIsNzcsNDcsMjI1LDIwMSwxNjQsNjIsMTEwLDI1MywxMiwzLDE5NSwxMzIsMTY2LDE4LDYyLDIyMSw4NiwxMDAsNiwxMTIsMTYxLDE3Myw4NSwyNDgsMjI0LDg5LDEyOSwyMTMsNDYsMTM5LDU2LDQ4LDMyLDEwNCwyNTAsMjgsMiwxNzEsOTAsMjE5LDEzNSwxNTgsMTYzLDI0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eSI6eyJrZXkiOnsidmVjIjpbMTUsMjUyLDcyLDExMCw5Miw1NCw4OSwzMiwyNDAsMTMxLDY2LDE1Nyw5OCwxNzAsMjQ0LDY2LDcyLDE5MSw0OSwyMDIsMTk2LDE2MSwxNzMsMTcsMjIyLDE0Miw2NCwyMzEsMTg3LDE3MiwxNjksMTddfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbODQsMTY5LDY1LDE0OSw3NSwyMjYsMTkxLDMzLDI1NCwxNDEsMTcsMjExLDE4MSw0OCw5MSwxNTksMTAwLDQ1LDc4LDExMiw3OSwxNDgsMTMsMTUsMjIyLDE5NiwyMTEsNjksMTU2LDM2LDE5MSwxMF19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk3LDEwOCwxMDUsOTksMTAxXX19LCJjYXBhYmlsaXRpZXMiOnsidmVyc2lvbnMiOlsiTWxzMTAiXSwiY2lwaGVyc3VpdGVzIjpbMSwyLDMsNzddLCJleHRlbnNpb25zIjpbeyJVbmtub3duIjo2MTUwNn1dLCJwcm9wb3NhbHMiOltdLCJjcmVkZW50aWFscyI6WyJCYXNpYyJdfSwibGVhZl9ub2RlX3NvdXJjZSI6eyJDb21taXQiOnsidmVjIjpbNzYsNywyMTgsMjQxLDk1LDE5NiwxNDUsMTc0LDM0LDE5MCw1NywyMywyNTUsNDMsMjE4LDE5NywyMDMsMTMyLDEzMCwxMzAsMjM4LDIwNyw0NCwxMDYsMTk3LDc4LDcxLDU4LDE2NywxODAsMTM5LDE5Ml19fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNzYsMTAzLDIxNiw5OCw4LDgyLDEyNSwxOTYsMjQ4LDEzNyw4NywyMTYsMjUwLDE5NiwxOTEsNSwxNSwyMTksMTYxLDEwLDM0LDIyNiw5MywxODAsMTU2LDE0Miw5Niw1OCwxOTksMTgxLDEzNCwxNjYsMTU2LDEzOCwxODcsMTcsMTY0LDE5OCwzNiw4MywxMTgsMTEzLDExNywxMDEsMjUzLDY0LDcxLDc4LDIyOSwxMzksODYsODIsMTQsMTU2LDE1NCw1MywyMTksODksNTIsNCwxOSw5NSw5LDddfX19fSx7Im5vZGUiOnsicGF5bG9hZCI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbNzEsMTAsNjksMjAsMiw5Miw3MCw4OSwyMjQsNDgsMTI2LDc4LDEyNCwxODYsMTQ4LDE1OSwyNDUsMTc1LDEyMiwxNTUsMTYsMjA5LDE1MywyMTYsMjIsMTQxLDExNyw3NSw5Nyw3MiwyNDcsMzZdfX0sInNpZ25hdHVyZV9rZXkiOnsidmFsdWUiOnsidmVjIjpbMjU0LDE4Miw5NSwxODcsMjksMTA2LDI0MSwyOCwxOTQsMjAzLDU2LDM3LDIwNyw2MCw2LDgsMTE4LDE3NywzOSwxNTQsMjIxLDE2LDE3LDI1Miw2MCwyMTksMTIyLDIyMiwxNjMsMjMzLDEzMiwyNV19fSwiY3JlZGVudGlhbCI6eyJjcmVkZW50aWFsX3R5cGUiOiJCYXNpYyIsInNlcmlhbGl6ZWRfY3JlZGVudGlhbF9jb250ZW50Ijp7InZlYyI6Wzk4LDExMSw5OF19fSwiY2FwYWJpbGl0aWVzIjp7InZlcnNpb25zIjpbIk1sczEwIl0sImNpcGhlcnN1aXRlcyI6WzEsMiwzLDc3XSwiZXh0ZW5zaW9ucyI6W3siVW5rbm93biI6NjE1MDZ9XSwicHJvcG9zYWxzIjpbXSwiY3JlZGVudGlhbHMiOlsiQmFzaWMiXX0sImxlYWZfbm9kZV9zb3VyY2UiOnsiS2V5UGFja2FnZSI6eyJub3RfYmVmb3JlIjoxNzIwNjg4OTQ5LCJub3RfYWZ0ZXIiOjE3Mjc5NTAxNDl9fSwiZXh0ZW5zaW9ucyI6eyJ1bmlxdWUiOltdfX0sInNpZ25hdHVyZSI6eyJ2YWx1ZSI6eyJ2ZWMiOls0OSwxMDEsMTkxLDYwLDE2NywyNDUsMjIyLDIwMiwyMDgsMzAsMjEyLDEzOSw2NSwxMCwyOSwyMzgsMTk2LDc0LDg3LDUwLDcsMTYzLDIxOCwxMDYsNjMsMyw4NywyMDMsMTMsMjMsMTAsMjIsMTMsODEsMTQwLDE3OCw5MSwxMTQsNDQsMjI1LDc1LDcsMjI5LDgxLDEzNyw2MywxMjQsMTU1LDE2MCwyNCwxODIsNDIsOTMsNTIsMjQsMTY3LDE5OSwyMDIsMTc5LDE2MCwxNTgsNjIsMTYsMTNdfX19fV0sInBhcmVudF9ub2RlcyI6W3sibm9kZSI6eyJlbmNyeXB0aW9uX2tleSI6eyJrZXkiOnsidmVjIjpbMTcxLDE4MCwyMjgsMTg4LDEwNCwyMDEsMTk0LDE3MSwzNCwyMTUsMjA3LDI0NywyNDAsODcsMjIsMTYyLDUxLDIxNiwxMCwxOTQsNDAsNDksMTc3LDE5NiwxNTYsMjIzLDEyOCwyMCwyMDgsMTM0LDIxNCw3Nl19fSwicGFyZW50X2hhc2giOnsidmVjIjpbXX0sInVubWVyZ2VkX2xlYXZlcyI6eyJsaXN0IjpbXX19fV0sImRlZmF1bHRfbGVhZiI6eyJub2RlIjpudWxsfSwiZGVmYXVsdF9wYXJlbnQiOnsibm9kZSI6bnVsbH19LCJ0cmVlX2hhc2giOls3Nyw0NywyMjUsMjAxLDE2NCw2MiwxMTAsMjUzLDEyLDMsMTk1LDEzMiwxNjYsMTgsNjIsMjIxLDg2LDEwMCw2LDExMiwxNjEsMTczLDg1LDI0OCwyMjQsODksMTI5LDIxMyw0NiwxMzksNTYsNDhdfQAAAAAAAABaAAAAAAAAAzRFcG9jaFNlY3JldHN7InZhbHVlIjp7InZlYyI6WzIxNCwyMDYsMTczLDI3LDIwNiw4NywyMzEsNCwxNzIsMzAsMTMwLDIxMCwyMzIsNjksNjEsMjIyXX19AAF7ImluaXRfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNTksMjQ2LDI1MCwyMjUsMjMwLDEyLDE0MCwzOSwyMDEsMTI0LDE4NCw3NSwyMjYsMTUwLDE0NCw4NSwxNzgsMjM4LDIyLDE3MSwxNzUsMTE1LDEzMCw2MCwxMCw0MCw1NywxOCwyMzgsMTI5LDg3LDE4XX19fSwiZXhwb3J0ZXJfc2VjcmV0Ijp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxNTQsMjM1LDI5LDIxOCwyNDgsMTI2LDg1LDI4LDE2OSw0MCw4MSwxODAsMjAxLDE1MCwyLDM2LDUzLDI1LDE5MSw1MywxMzksNzAsMTI5LDExNSwyMDYsMTc1LDIxLDE3MiwxMjgsMTE5LDEzMSw5M119fX0sImVwb2NoX2F1dGhlbnRpY2F0b3IiOnsic2VjcmV0Ijp7InZhbHVlIjp7InZlYyI6WzEyNywxMjUsMjIzLDgzLDk0LDExNSwyMTEsMTc5LDAsMjI2LDEwOSwxMTQsOTMsMTc2LDE2MiwxMDMsMjgsMTU1LDc2LDc3LDE2NSwxNyw3MCwyMzUsMjM5LDE2MiwxNzAsNjMsMjM1LDQwLDEzMyw2MV19fX0sImV4dGVybmFsX3NlY3JldCI6eyJzZWNyZXQiOnsidmFsdWUiOnsidmVjIjpbNzQsODUsNDYsNjEsMSwxNjYsMTgxLDIxMCwxODIsOSwxOTgsNTMsMjAsMjEwLDIyNiwxNywyMTAsNzcsMTA4LDIyNSwxOTksMTM5LDM3LDIxMywyMjIsMjI2LDIyMCwxMjIsMTcsMTMzLDIyNiwzMF19fX0sInJlc3VtcHRpb25fcHNrIjp7InNlY3JldCI6eyJ2YWx1ZSI6eyJ2ZWMiOlsxOTksMTkwLDg3LDg4LDYsMjM2LDE1MywxNzAsMTMyLDI1NSwxNTksMTQxLDQyLDE2NiwxMTgsMTIxLDE4MywxNjUsNDIsMTc5LDQzLDEzOCwxOTIsMTk4LDI5LDExNSwxMzksMjAzLDIzOSw3LDE2Niw5MF19fX19"]}} \ No newline at end of file From a1bc0cad22b258f4b24d37eeb0c01ef1f85f64e6 Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Wed, 17 Jul 2024 18:00:08 +0200 Subject: [PATCH 19/43] Change how the AAD is set (#1615) * Change ho the AAD is set * Add tests * Add changelog entry + docs * Address review comments --- CHANGELOG.md | 1 + book/src/SUMMARY.md | 1 + book/src/user_manual/aad.md | 20 +++ cli/src/user.rs | 14 +- interop_client/src/main.rs | 8 +- memory_storage/src/lib.rs | 16 -- memory_storage/src/test_store.rs | 15 -- openmls/src/framing/validation.rs | 4 +- openmls/src/group/mls_group/application.rs | 1 + openmls/src/group/mls_group/membership.rs | 3 + openmls/src/group/mls_group/mod.rs | 48 +++--- openmls/src/group/mls_group/processing.rs | 1 + openmls/src/group/mls_group/proposal.rs | 25 ++- openmls/src/group/mls_group/ser.rs | 4 +- openmls/src/group/mls_group/updates.rs | 2 + openmls/src/group/tests/mod.rs | 2 + openmls/src/group/tests/test_aad.rs | 185 +++++++++++++++++++++ openmls/tests/book_code.rs | 30 ++++ traits/src/storage.rs | 13 -- 19 files changed, 294 insertions(+), 99 deletions(-) create mode 100644 book/src/user_manual/aad.md create mode 100644 openmls/src/group/tests/test_aad.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e94daa970..99ff2e508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1559](https://github.com/openmls/openmls/pull/1559): Remove the `PartialEq` type constraint on the error type of both the `OpenMlsRand` and `OpenMlsKeyStore` traits. Additionally, remove the `Clone` type constraint on the error type of the `OpenMlsRand` trait. - [#1565](https://github.com/openmls/openmls/pull/1565): Removed `OpenMlsKeyStore` and replace it with a new `StorageProvider` trait in the `openmls_traits` crate. - [#1606](https://github.com/openmls/openmls/pull/1606): Added additional `LeafNodeParameters` argument to `MlsGroup.self_update()` and `MlsGroup.propose_self_update()` to allow for updating the leaf node with custom parameters. `MlsGroup::join_by_external_commit()` now also takes optional parameters to set the capabilities and the extensions of the LeafNode. +- [#1615](https://github.com/openmls/openmls/pull/1615): Changes the AAD handlling. The AAD is no longer persisted and needs to be set before every API call that generates an `MlsMessageOut`. The functions `ProccessedMessage` to accees the AAD has been renamed to `aad()`. ### Fixed diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 8a03b5b3f..9b8001efc 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -12,6 +12,7 @@ - [Adding members to a group](user_manual/add_members.md) - [Removing members from a group](user_manual/remove_members.md) - [Updating own key package](user_manual/updates.md) + - [Using Additional Authenticated Data (AAD)](user_manual/aad.md) - [Leaving a group](user_manual/leaving.md) - [Custom proposals](user_manual/custom_proposals.md) - [Creating application messages](user_manual/application_messages.md) diff --git a/book/src/user_manual/aad.md b/book/src/user_manual/aad.md new file mode 100644 index 000000000..dda8b3c50 --- /dev/null +++ b/book/src/user_manual/aad.md @@ -0,0 +1,20 @@ +# Using Additional Authenticated Data (AAD) + +The Additional Authenticated Data (AAD) is a byte sequence that can be included in both private and public messages. By design, it is always authenticated (signed) but never encrypted. Its purpose is to contain data that can be inspected but not changed while a message is in transit. + +## Setting the AAD + +Members can set the AAD by calling the `.set_aad()` function. The AAD will remain set until the next API call that successfully generates an `MlsMessageOut`. Until then, the AAD can be inspected with the `.aad()` function. + +```rust,no_run,noplayground +{{#include ../../../openmls/tests/book_code.rs:set_aad}} +``` + +## Inspecting the AAD + +Members can inspect the AAD of an incoming message once the message has been processed. The AAD can be accessed with the `.aad()` function of a `ProcessedMessage`. + +```rust,no_run,noplayground +{{#include ../../../openmls/tests/book_code.rs:inspect_aad}} +``` + diff --git a/cli/src/user.rs b/cli/src/user.rs index 3c784302c..9440996df 100644 --- a/cli/src/user.rs +++ b/cli/src/user.rs @@ -548,8 +548,6 @@ impl User { pub fn create_group(&mut self, name: String) { log::debug!("{} creates group {}", self.username(), name); let group_id = name.as_bytes(); - let mut group_aad = group_id.to_vec(); - group_aad.extend(b" AAD"); // NOTE: Since the DS currently doesn't distribute copies of the group's ratchet // tree, we need to include the ratchet_tree_extension. @@ -557,7 +555,7 @@ impl User { .use_ratchet_tree_extension(true) .build(); - let mut mls_group = MlsGroup::new_with_group_id( + let mls_group = MlsGroup::new_with_group_id( &self.provider, &self.identity.borrow().signer, &group_config, @@ -565,9 +563,6 @@ impl User { self.identity.borrow().credential_with_key.clone(), ) .expect("Failed to create MlsGroup"); - mls_group - .set_aad(self.provider.storage(), group_aad.as_slice()) - .expect("Failed to write the AAD for the new group to storage"); let group = Group { group_name: name.clone(), @@ -708,7 +703,7 @@ impl User { let group_config = MlsGroupJoinConfig::builder() .use_ratchet_tree_extension(true) .build(); - let mut mls_group = + let mls_group = StagedWelcome::new_from_welcome(&self.provider, &group_config, welcome, None) .expect("Failed to create staged join") .into_group(&self.provider) @@ -717,11 +712,6 @@ impl User { let group_id = mls_group.group_id().to_vec(); // XXX: Use Welcome's encrypted_group_info field to store group_name. let group_name = String::from_utf8(group_id.clone()).unwrap(); - let group_aad = group_name.clone() + " AAD"; - - mls_group - .set_aad(self.provider.storage(), group_aad.as_bytes()) - .expect("Failed to update the AAD in the storage"); let group = Group { group_name: group_name.clone(), diff --git a/interop_client/src/main.rs b/interop_client/src/main.rs index 989bd246a..235d3a95a 100644 --- a/interop_client/src/main.rs +++ b/interop_client/src/main.rs @@ -607,11 +607,7 @@ impl MlsClient for MlsClientImpl { interop_group .group - .set_aad( - interop_group.crypto_provider.storage(), - &request.authenticated_data, - ) - .map_err(|err| tonic::Status::internal(format!("error setting aad: {err}")))?; + .set_aad(request.authenticated_data.clone()); let ciphertext = interop_group .group @@ -660,7 +656,7 @@ impl MlsClient for MlsClientImpl { debug!("Processed."); trace!(?processed_message); - let authenticated_data = processed_message.authenticated_data().to_vec(); + let authenticated_data = processed_message.aad().to_vec(); let plaintext = match processed_message.into_content() { ProcessedMessageContent::ApplicationMessage(application_message) => { application_message.into_bytes() diff --git a/memory_storage/src/lib.rs b/memory_storage/src/lib.rs index 4de3e6f03..c0754f828 100644 --- a/memory_storage/src/lib.rs +++ b/memory_storage/src/lib.rs @@ -949,22 +949,6 @@ impl StorageProvider for MemoryStorage { }) } - fn write_aad>( - &self, - group_id: &GroupId, - aad: &[u8], - ) -> Result<(), Self::Error> { - let key = serde_json::to_vec(group_id)?; - self.write::(AAD_LABEL, &key, aad.to_vec()) - } - - fn delete_aad>( - &self, - group_id: &GroupId, - ) -> Result<(), Self::Error> { - self.delete::(AAD_LABEL, &serde_json::to_vec(group_id).unwrap()) - } - fn delete_own_leaf_nodes>( &self, group_id: &GroupId, diff --git a/memory_storage/src/test_store.rs b/memory_storage/src/test_store.rs index c17d76fee..d521dd328 100644 --- a/memory_storage/src/test_store.rs +++ b/memory_storage/src/test_store.rs @@ -494,14 +494,6 @@ impl StorageProvider for MemoryStorage { todo!() } - fn write_aad>( - &self, - _group_id: &GroupId, - _aad: &[u8], - ) -> Result<(), Self::Error> { - todo!() - } - fn queued_proposals< GroupId: traits::GroupId, ProposalRef: traits::ProposalRef, @@ -524,13 +516,6 @@ impl StorageProvider for MemoryStorage { todo!() } - fn delete_aad>( - &self, - _group_id: &GroupId, - ) -> Result<(), Self::Error> { - todo!() - } - fn delete_own_leaf_nodes>( &self, _group_id: &GroupId, diff --git a/openmls/src/framing/validation.rs b/openmls/src/framing/validation.rs index 6e27b814f..0b124c736 100644 --- a/openmls/src/framing/validation.rs +++ b/openmls/src/framing/validation.rs @@ -343,8 +343,8 @@ impl ProcessedMessage { &self.sender } - /// Returns the authenticated data of the message. - pub fn authenticated_data(&self) -> &[u8] { + /// Returns the additional authenticated data (AAD) of the message. + pub fn aad(&self) -> &[u8] { &self.authenticated_data } diff --git a/openmls/src/group/mls_group/application.rs b/openmls/src/group/mls_group/application.rs index 14baa41e2..583a81325 100644 --- a/openmls/src/group/mls_group/application.rs +++ b/openmls/src/group/mls_group/application.rs @@ -42,6 +42,7 @@ impl MlsGroup { // We know the application message is wellformed and we have the key material of the current epoch .map_err(|_| LibraryError::custom("Malformed plaintext"))?; + self.reset_aad(); Ok(MlsMessageOut::from_private_message( ciphertext, self.group.version(), diff --git a/openmls/src/group/mls_group/membership.rs b/openmls/src/group/mls_group/membership.rs index 39ef4c011..c90d4a2e1 100644 --- a/openmls/src/group/mls_group/membership.rs +++ b/openmls/src/group/mls_group/membership.rs @@ -86,6 +86,7 @@ impl MlsGroup { .write_group_state(self.group_id(), &self.group_state) .map_err(AddMembersError::StorageError)?; + self.reset_aad(); Ok(( mls_messages, MlsMessageOut::from_welcome(welcome, self.group.version()), @@ -160,6 +161,7 @@ impl MlsGroup { .write_group_state(self.group_id(), &self.group_state) .map_err(RemoveMembersError::StorageError)?; + self.reset_aad(); Ok(( mls_message, create_commit_result @@ -197,6 +199,7 @@ impl MlsGroup { remove_proposal.clone(), )?); + self.reset_aad(); Ok(self.content_to_mls_message(remove_proposal, provider)?) } diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index db2360e9d..56ca9e302 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -161,8 +161,9 @@ pub struct MlsGroup { // are needed in case an update proposal is committed by another group // member. The vector is emptied after every epoch change. own_leaf_nodes: Vec, - // The AAD that is used for all outgoing handshake messages. The AAD can be set through - // `set_aad()`. + // Additional authenticated data (AAD) for the next outgoing message. This + // is ephemeral and will be reset by every API call that successfully + // returns an [`MlsMessageOut`]. aad: Vec, // A variable that indicates the state of the group. See [`MlsGroupState`] // for more information. @@ -187,19 +188,17 @@ impl MlsGroup { storage.write_mls_join_config(self.group_id(), mls_group_config) } - /// Returns the AAD used in the framing. - pub fn aad(&self) -> &[u8] { - &self.aad + /// Sets the additional authenticated data (AAD) for the next outgoing + /// message. This is ephemeral and will be reset by every API call that + /// successfully returns an [`MlsMessageOut`]. + pub fn set_aad(&mut self, aad: Vec) { + self.aad = aad; } - /// Sets the AAD used in the framing. - pub fn set_aad( - &mut self, - storage: &Storage, - aad: &[u8], - ) -> Result<(), Storage::Error> { - self.aad = aad.to_vec(); - storage.write_aad(self.group_id(), aad) + /// Returns the additional authenticated data (AAD) for the next outgoing + /// message. + pub fn aad(&self) -> &[u8] { + &self.aad } // === Advanced functions === @@ -341,7 +340,7 @@ impl MlsGroup { let group_config = storage.mls_group_join_config(group_id)?; let core_group = CoreGroup::load(storage, group_id)?; let own_leaf_nodes = storage.own_leaf_nodes(group_id)?; - let aad = storage.aad(group_id)?; + let aad = Vec::new(); let group_state = storage.group_state(group_id)?; let build = || -> Option { @@ -366,7 +365,6 @@ impl MlsGroup { storage.delete_group_config(self.group_id())?; storage.clear_proposal_queue::(self.group_id())?; storage.delete_own_leaf_nodes(self.group_id())?; - storage.delete_aad(self.group_id())?; storage.delete_group_state(self.group_id())?; Ok(()) @@ -447,6 +445,12 @@ impl MlsGroup { pub(crate) fn proposal_store_mut(&mut self) -> &mut ProposalStore { self.group.proposal_store_mut() } + + /// Resets the AAD. + #[inline] + pub(crate) fn reset_aad(&mut self) { + self.aad.clear(); + } } // Methods used in tests @@ -471,20 +475,6 @@ impl MlsGroup { pub(crate) fn group(&self) -> &CoreGroup { &self.group } - - /// Removes a specific proposal from the store. - pub fn remove_pending_proposal( - &mut self, - storage: &Storage, - proposal_ref: ProposalRef, - ) -> Result<(), MlsGroupStateError> { - storage - .remove_proposal(self.group_id(), &proposal_ref) - .map_err(MlsGroupStateError::StorageError)?; - self.proposal_store_mut() - .remove(proposal_ref) - .ok_or(MlsGroupStateError::PendingProposalNotFound) - } } /// A [`StagedWelcome`] can be inspected and then turned into a [`MlsGroup`]. diff --git a/openmls/src/group/mls_group/processing.rs b/openmls/src/group/mls_group/processing.rs index d58d0a940..ee9016108 100644 --- a/openmls/src/group/mls_group/processing.rs +++ b/openmls/src/group/mls_group/processing.rs @@ -115,6 +115,7 @@ impl MlsGroup { .write_group_state(self.group_id(), &self.group_state) .map_err(CommitToPendingProposalsError::StorageError)?; + self.reset_aad(); Ok(( mls_message, create_commit_result diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index d023b5b65..dee801740 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -1,10 +1,10 @@ -use openmls_traits::{signatures::Signer, storage::StorageProvider, types::Ciphersuite}; +use openmls_traits::{signatures::Signer, storage::StorageProvider as _, types::Ciphersuite}; use super::{ core_group::create_commit_params::CreateCommitParams, errors::{ProposalError, ProposeAddMemberError, ProposeRemoveMemberError}, CreateGroupContextExtProposalError, CustomProposal, GroupContextExtensionProposal, MlsGroup, - MlsGroupState, PendingCommitState, Proposal, + MlsGroupState, MlsGroupStateError, PendingCommitState, Proposal, }; use crate::{ binary_tree::LeafNodeIndex, @@ -17,7 +17,7 @@ use crate::{ messages::{group_info::GroupInfo, proposals::ProposalOrRefType}, prelude::LibraryError, schedule::PreSharedKeyId, - storage::OpenMlsProvider, + storage::{OpenMlsProvider, StorageProvider}, treesync::LeafNodeParameters, versions::ProtocolVersion, }; @@ -93,6 +93,7 @@ macro_rules! impl_propose_fun { let mls_message = self.content_to_mls_message(proposal, provider)?; + self.reset_aad(); Ok((mls_message, proposal_ref)) } }; @@ -260,6 +261,7 @@ impl MlsGroup { let mls_message = self.content_to_mls_message(add_proposal, provider)?; + self.reset_aad(); Ok((mls_message, proposal_ref)) } @@ -295,6 +297,7 @@ impl MlsGroup { let mls_message = self.content_to_mls_message(remove_proposal, provider)?; + self.reset_aad(); Ok((mls_message, proposal_ref)) } @@ -384,6 +387,7 @@ impl MlsGroup { let mls_message = self.content_to_mls_message(proposal, provider)?; + self.reset_aad(); Ok((mls_message, proposal_ref)) } @@ -431,6 +435,7 @@ impl MlsGroup { .write_group_state(self.group_id(), &self.group_state) .map_err(CreateGroupContextExtProposalError::StorageError)?; + self.reset_aad(); Ok(( mls_messages, create_commit_result @@ -439,4 +444,18 @@ impl MlsGroup { create_commit_result.group_info, )) } + + /// Removes a specific proposal from the store. + pub fn remove_pending_proposal( + &mut self, + storage: &Storage, + proposal_ref: ProposalRef, + ) -> Result<(), MlsGroupStateError> { + storage + .remove_proposal(self.group_id(), &proposal_ref) + .map_err(MlsGroupStateError::StorageError)?; + self.proposal_store_mut() + .remove(proposal_ref) + .ok_or(MlsGroupStateError::PendingProposalNotFound) + } } diff --git a/openmls/src/group/mls_group/ser.rs b/openmls/src/group/mls_group/ser.rs index ad6bc5975..cd7d85ff0 100644 --- a/openmls/src/group/mls_group/ser.rs +++ b/openmls/src/group/mls_group/ser.rs @@ -19,7 +19,6 @@ pub struct SerializedMlsGroup { mls_group_config: MlsGroupJoinConfig, group: CoreGroup, own_leaf_nodes: Vec, - aad: Vec, resumption_psk_store: ResumptionPskStore, group_state: MlsGroupState, } @@ -31,7 +30,7 @@ impl Into for SerializedMlsGroup { mls_group_config: self.mls_group_config, group: self.group, own_leaf_nodes: self.own_leaf_nodes, - aad: self.aad, + aad: Vec::new(), group_state: self.group_state, } } @@ -46,7 +45,6 @@ impl Serialize for MlsGroup { state.serialize_field("mls_group_config", &self.mls_group_config)?; state.serialize_field("group", &self.group)?; state.serialize_field("own_leaf_nodes", &self.own_leaf_nodes)?; - state.serialize_field("aad", &self.aad)?; state.serialize_field("resumption_psk_store", &self.group.resumption_psk_store)?; state.serialize_field("group_state", &self.group_state)?; state.end() diff --git a/openmls/src/group/mls_group/updates.rs b/openmls/src/group/mls_group/updates.rs index 87d087f04..10bc74e08 100644 --- a/openmls/src/group/mls_group/updates.rs +++ b/openmls/src/group/mls_group/updates.rs @@ -60,6 +60,7 @@ impl MlsGroup { .store(provider.storage()) .map_err(SelfUpdateError::StorageError)?; + self.reset_aad(); Ok(( mls_message, create_commit_result @@ -139,6 +140,7 @@ impl MlsGroup { let mls_message = self.content_to_mls_message(update_proposal, provider)?; + self.reset_aad(); Ok((mls_message, proposal_ref)) } } diff --git a/openmls/src/group/tests/mod.rs b/openmls/src/group/tests/mod.rs index 607822812..33c148d2d 100644 --- a/openmls/src/group/tests/mod.rs +++ b/openmls/src/group/tests/mod.rs @@ -11,6 +11,8 @@ pub mod kat_messages; #[cfg(test)] pub mod kat_transcript_hashes; #[cfg(test)] +pub(crate) mod test_aad; +#[cfg(test)] mod test_commit_validation; #[cfg(test)] mod test_encoding; diff --git a/openmls/src/group/tests/test_aad.rs b/openmls/src/group/tests/test_aad.rs new file mode 100644 index 000000000..29bdb42c3 --- /dev/null +++ b/openmls/src/group/tests/test_aad.rs @@ -0,0 +1,185 @@ +// Import necessary modules and dependencies +use super::utils::{generate_credential_with_key, generate_key_package}; +use crate::{binary_tree::LeafNodeIndex, framing::*, group::*}; + +// Tests the different variants of the RemoveOperation enum. +#[openmls_test::openmls_test] +fn test_add_member_with_aad( + ciphersuite: Ciphersuite, + provider: &impl crate::storage::OpenMlsProvider, +) { + // Test over both wire format policies + for wire_format_policy in [ + PURE_PLAINTEXT_WIRE_FORMAT_POLICY, + PURE_CIPHERTEXT_WIRE_FORMAT_POLICY, + ] { + let group_id = GroupId::from_slice(b"Test Group"); + + // Generate credentials with keys + let alice_credential_with_key_and_signer = generate_credential_with_key( + "Alice".into(), + ciphersuite.signature_algorithm(), + provider, + ); + + let bob_credential_with_key_and_signer = + generate_credential_with_key("Bob".into(), ciphersuite.signature_algorithm(), provider); + + let charlie_credential_with_key_and_signer = generate_credential_with_key( + "Charlie".into(), + ciphersuite.signature_algorithm(), + provider, + ); + + // Generate KeyPackages + let bob_key_package = generate_key_package( + ciphersuite, + Extensions::empty(), + provider, + bob_credential_with_key_and_signer.clone(), + ); + let charlie_key_package = generate_key_package( + ciphersuite, + Extensions::empty(), + provider, + charlie_credential_with_key_and_signer, + ); + + // Define the MlsGroup configuration + let mls_group_create_config = MlsGroupCreateConfig::builder() + .ciphersuite(ciphersuite) + .wire_format_policy(wire_format_policy) + .build(); + + // === Alice creates a group === + + let mut alice_group = MlsGroup::new_with_group_id( + provider, + &alice_credential_with_key_and_signer.signer, + &mls_group_create_config, + group_id, + alice_credential_with_key_and_signer + .credential_with_key + .clone(), + ) + .expect("An unexpected error occurred."); + + let aad = b"Test AAD".to_vec(); + + alice_group.set_aad(aad.clone()); + + // Test the AAD was set correctly + assert_eq!(alice_group.aad(), &aad); + + // === Alice adds Bob === + + let (_message, welcome, _group_info) = alice_group + .add_members( + provider, + &alice_credential_with_key_and_signer.signer, + &[bob_key_package.key_package().clone()], + ) + .expect("An unexpected error occurred."); + alice_group + .merge_pending_commit(provider) + .expect("error merging pending commit"); + + let welcome: MlsMessageIn = welcome.into(); + let welcome = welcome + .into_welcome() + .expect("expected message to be a welcome"); + + let mut bob_group = StagedWelcome::new_from_welcome( + provider, + mls_group_create_config.join_config(), + welcome.clone(), + Some(alice_group.export_ratchet_tree().into()), + ) + .expect("Error creating staged join from Welcome") + .into_group(provider) + .expect("Error creating group from staged join"); + + // === Alice sends a message to Bob === + + let message = b"Hello, World!".to_vec(); + alice_group.set_aad(aad.clone()); + let alice_message: MlsMessageIn = alice_group + .create_message( + provider, + &alice_credential_with_key_and_signer.signer, + &message, + ) + .expect("Error creating message") + .into(); + + // Test the AAD was reset + assert_eq!(alice_group.aad().len(), 0); + + let bob_message = bob_group + .process_message( + provider, + alice_message.clone().into_protocol_message().unwrap(), + ) + .expect("Error handling message"); + + // Test the AAD was set correctly + assert_eq!(bob_message.aad(), &aad); + + // === Alice adds Charlie === + + alice_group.set_aad(aad.clone()); + let (commit, _welcome, _group_info) = alice_group + .add_members( + provider, + &alice_credential_with_key_and_signer.signer, + &[charlie_key_package.key_package().clone()], + ) + .expect("An unexpected error occurred."); + alice_group + .merge_pending_commit(provider) + .expect("error merging pending commit"); + + // Test the AAD was reset + assert_eq!(alice_group.aad().len(), 0); + + let bob_processed_message = bob_group + .process_message(provider, commit.clone().into_protocol_message().unwrap()) + .expect("Error handling message"); + + match bob_processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(bob_staged_commit) => { + bob_group + .merge_staged_commit(provider, *bob_staged_commit) + .unwrap(); + } + _ => panic!("Expected a StagedCommitMessage"), + } + + // Test the AAD was set correctly + assert_eq!(bob_message.aad(), &aad); + + // === Alice removes Charlie === + + alice_group.set_aad(aad.clone()); + let (commit, _welcome, _group_info) = alice_group + .remove_members( + provider, + &alice_credential_with_key_and_signer.signer, + &[LeafNodeIndex::new(2)], + ) + .expect("An unexpected error occurred."); + alice_group + .merge_pending_commit(provider) + .expect("error merging pending commit"); + + // Test the AAD was reset + assert_eq!(alice_group.aad().len(), 0); + + let bob_processed_message = bob_group + .process_message(provider, commit.clone().into_protocol_message().unwrap()) + .expect("Error handling message"); + + // Test the AAD was set correctly + assert_eq!(bob_processed_message.aad(), &aad); + } +} diff --git a/openmls/tests/book_code.rs b/openmls/tests/book_code.rs index 679d9401e..573d58768 100644 --- a/openmls/tests/book_code.rs +++ b/openmls/tests/book_code.rs @@ -357,6 +357,36 @@ fn book_operations() { unreachable!("Expected an ApplicationMessage."); } + // ANCHOR: set_aad + alice_group.set_aad(b"Additional Authenticated Data".to_vec()); + + assert_eq!(alice_group.aad(), b"Additional Authenticated Data"); + // ANCHOR_END: set_aad + + let message_alice = b"Hi, I'm Alice!"; + let mls_message_out = alice_group + .create_message(provider, &alice_signature_keys, message_alice) + .expect("Error creating application message."); + + let bytes = mls_message_out + .to_bytes() + .expect("Could not serialize message."); + + let mls_message = + MlsMessageIn::tls_deserialize_exact(bytes).expect("Could not deserialize message."); + + let protocol_message: ProtocolMessage = mls_message + .try_into_protocol_message() + .expect("Expected a PublicMessage or a PrivateMessage"); + + // ANCHOR: inspect_aad + let processed_message = bob_group + .process_message(provider, protocol_message) + .expect("Could not process message."); + + assert_eq!(processed_message.aad(), b"Additional Authenticated Data"); + // ANCHOR_END: inspect_aad + // === Bob updates and commits === // ANCHOR: self_update let (mls_message_out, welcome_option, _group_info) = bob_group diff --git a/traits/src/storage.rs b/traits/src/storage.rs index 5e6266766..d63e4b22b 100644 --- a/traits/src/storage.rs +++ b/traits/src/storage.rs @@ -49,13 +49,6 @@ pub trait StorageProvider { config: &MlsGroupJoinConfig, ) -> Result<(), Self::Error>; - /// Writes the AAD for the group with given id to storage - fn write_aad>( - &self, - group_id: &GroupId, - aad: &[u8], - ) -> Result<(), Self::Error>; - /// Adds an own leaf node for the group with given id to storage fn append_own_leaf_node< GroupId: traits::GroupId, @@ -455,12 +448,6 @@ pub trait StorageProvider { proposal_ref: &ProposalRef, ) -> Result<(), Self::Error>; - /// Deletes the AAD for the given id from storage - fn delete_aad>( - &self, - group_id: &GroupId, - ) -> Result<(), Self::Error>; - /// Deletes own leaf nodes for the given id from storage fn delete_own_leaf_nodes>( &self, From d775391baac533615b752d36b1f0035b75e538f3 Mon Sep 17 00:00:00 2001 From: Konrad Kohbrok Date: Fri, 19 Jul 2024 08:47:23 +0200 Subject: [PATCH 20/43] Re-organize and re-name test and kat modules (#1620) --- openmls/src/binary_tree/mod.rs | 2 +- .../{test_binary_tree.rs => tests.rs} | 0 openmls/src/ciphersuite/mod.rs | 2 +- .../{tests.rs => tests_and_kats.rs} | 2 +- .../kat_crypto_basics.rs | 0 .../tests.rs} | 0 openmls/src/extensions/mod.rs | 2 +- .../{test_extensions.rs => tests.rs} | 23 ++-- openmls/src/framing/mod.rs | 2 +- .../src/framing/{test_framing.rs => tests.rs} | 2 +- openmls/src/group/core_group/mod.rs | 16 --- .../src/group/core_group/new_from_welcome.rs | 24 +--- openmls/src/group/mls_group/mod.rs | 2 +- .../mls_group/tests_and_kats/kats/mod.rs | 2 + .../tests_and_kats/kats/passive_client.rs} | 7 +- .../tests_and_kats/kats/welcome.rs} | 21 ++-- .../src/group/mls_group/tests_and_kats/mod.rs | 3 + .../tests_and_kats/tests/core_group.rs} | 103 +----------------- .../tests/create_commit_params.rs} | 5 +- .../tests_and_kats/tests/external_init.rs} | 24 ++-- .../tests/mls_group.rs} | 2 +- .../mls_group/tests_and_kats/tests/mod.rs | 8 ++ .../tests_and_kats/tests/past_secrets.rs} | 0 .../tests_and_kats/tests/proposals.rs} | 5 +- .../group/mls_group/tests_and_kats/utils.rs | 100 +++++++++++++++++ openmls/src/group/mod.rs | 2 +- openmls/src/group/public_group/tests.rs | 4 +- openmls/src/group/tests/mod.rs | 40 ------- .../kats/messages.rs} | 4 +- openmls/src/group/tests_and_kats/kats/mod.rs | 2 + .../kats/transcript_hashes.rs} | 2 +- openmls/src/group/tests_and_kats/mod.rs | 8 ++ .../tests/aad.rs} | 10 +- .../tests/commit_validation.rs} | 2 +- .../tests/encoding.rs} | 2 +- .../tests/external_add_proposal.rs | 2 +- .../tests/external_commit.rs} | 2 +- .../tests/external_commit_validation.rs} | 4 +- .../tests/external_remove_proposal.rs | 2 +- .../tests/framing.rs} | 2 +- .../tests/framing_validation.rs} | 2 +- .../tests/group.rs} | 18 +-- .../tests/group_context_extensions.rs | 3 +- openmls/src/group/tests_and_kats/tests/mod.rs | 17 +++ .../tests/past_secrets.rs} | 2 +- .../tests/proposal_validation.rs} | 2 +- .../tests/remove_operation.rs} | 2 +- .../tests/wire_format_policy.rs} | 2 +- .../tree_printing.rs | 0 .../group/{tests => tests_and_kats}/utils.rs | 0 openmls/src/key_packages/mod.rs | 2 +- .../{test_key_packages.rs => tests.rs} | 0 openmls/src/messages/mod.rs | 10 ++ .../tests/{test_codec.rs => codec.rs} | 0 ...ort_group_info.rs => export_group_info.rs} | 2 +- openmls/src/messages/tests/mod.rs | 8 +- .../tests/{test_proposals.rs => proposals.rs} | 0 .../tests/{test_welcome.rs => welcome.rs} | 12 +- openmls/src/prelude_test.rs | 2 +- openmls/src/schedule/mod.rs | 8 +- .../kats/key_schedule.rs} | 12 +- .../src/schedule/tests_and_kats/kats/mod.rs | 4 + .../kats/psk_secret.rs} | 7 +- openmls/src/schedule/tests_and_kats/mod.rs | 4 + .../tests.rs} | 3 +- openmls/src/storage.rs | 4 +- openmls/src/test_utils/mod.rs | 2 +- openmls/src/treesync/mod.rs | 5 +- openmls/src/treesync/tests_and_kats/tests.rs | 2 +- ...n_key_index.rs => decryption_key_index.rs} | 0 ..._external_commit.rs => external_commit.rs} | 0 ...erop_scenarios.rs => interop_scenarios.rs} | 0 .../{test_managed_api.rs => managed_api.rs} | 0 .../tests/{test_mls_group.rs => mls_group.rs} | 0 74 files changed, 287 insertions(+), 293 deletions(-) rename openmls/src/binary_tree/{test_binary_tree.rs => tests.rs} (100%) rename openmls/src/ciphersuite/{tests.rs => tests_and_kats.rs} (83%) rename openmls/src/ciphersuite/{tests => tests_and_kats}/kat_crypto_basics.rs (100%) rename openmls/src/ciphersuite/{tests/test_ciphersuite.rs => tests_and_kats/tests.rs} (100%) rename openmls/src/extensions/{test_extensions.rs => tests.rs} (96%) rename openmls/src/framing/{test_framing.rs => tests.rs} (99%) create mode 100644 openmls/src/group/mls_group/tests_and_kats/kats/mod.rs rename openmls/src/group/{core_group/kat_passive_client.rs => mls_group/tests_and_kats/kats/passive_client.rs} (98%) rename openmls/src/group/{core_group/kat_welcome.rs => mls_group/tests_and_kats/kats/welcome.rs} (95%) create mode 100644 openmls/src/group/mls_group/tests_and_kats/mod.rs rename openmls/src/group/{core_group/test_core_group.rs => mls_group/tests_and_kats/tests/core_group.rs} (88%) rename openmls/src/group/{core_group/test_create_commit_params.rs => mls_group/tests_and_kats/tests/create_commit_params.rs} (88%) rename openmls/src/group/{core_group/test_external_init.rs => mls_group/tests_and_kats/tests/external_init.rs} (69%) rename openmls/src/group/mls_group/{test_mls_group.rs => tests_and_kats/tests/mls_group.rs} (99%) create mode 100644 openmls/src/group/mls_group/tests_and_kats/tests/mod.rs rename openmls/src/group/{core_group/test_past_secrets.rs => mls_group/tests_and_kats/tests/past_secrets.rs} (100%) rename openmls/src/group/{core_group/test_proposals.rs => mls_group/tests_and_kats/tests/proposals.rs} (99%) create mode 100644 openmls/src/group/mls_group/tests_and_kats/utils.rs delete mode 100644 openmls/src/group/tests/mod.rs rename openmls/src/group/{tests/kat_messages.rs => tests_and_kats/kats/messages.rs} (99%) create mode 100644 openmls/src/group/tests_and_kats/kats/mod.rs rename openmls/src/group/{tests/kat_transcript_hashes.rs => tests_and_kats/kats/transcript_hashes.rs} (99%) create mode 100644 openmls/src/group/tests_and_kats/mod.rs rename openmls/src/group/{tests/test_aad.rs => tests_and_kats/tests/aad.rs} (97%) rename openmls/src/group/{tests/test_commit_validation.rs => tests_and_kats/tests/commit_validation.rs} (99%) rename openmls/src/group/{tests/test_encoding.rs => tests_and_kats/tests/encoding.rs} (99%) rename openmls/src/group/{ => tests_and_kats}/tests/external_add_proposal.rs (99%) rename openmls/src/group/{tests/test_external_commit.rs => tests_and_kats/tests/external_commit.rs} (98%) rename openmls/src/group/{tests/test_external_commit_validation.rs => tests_and_kats/tests/external_commit_validation.rs} (99%) rename openmls/src/group/{ => tests_and_kats}/tests/external_remove_proposal.rs (99%) rename openmls/src/group/{tests/test_framing.rs => tests_and_kats/tests/framing.rs} (99%) rename openmls/src/group/{tests/test_framing_validation.rs => tests_and_kats/tests/framing_validation.rs} (99%) rename openmls/src/group/{tests/test_group.rs => tests_and_kats/tests/group.rs} (98%) rename openmls/src/group/{ => tests_and_kats}/tests/group_context_extensions.rs (99%) create mode 100644 openmls/src/group/tests_and_kats/tests/mod.rs rename openmls/src/group/{tests/test_past_secrets.rs => tests_and_kats/tests/past_secrets.rs} (98%) rename openmls/src/group/{tests/test_proposal_validation.rs => tests_and_kats/tests/proposal_validation.rs} (99%) rename openmls/src/group/{tests/test_remove_operation.rs => tests_and_kats/tests/remove_operation.rs} (99%) rename openmls/src/group/{tests/test_wire_format_policy.rs => tests_and_kats/tests/wire_format_policy.rs} (99%) rename openmls/src/group/{tests => tests_and_kats}/tree_printing.rs (100%) rename openmls/src/group/{tests => tests_and_kats}/utils.rs (100%) rename openmls/src/key_packages/{test_key_packages.rs => tests.rs} (100%) rename openmls/src/messages/tests/{test_codec.rs => codec.rs} (100%) rename openmls/src/messages/tests/{test_export_group_info.rs => export_group_info.rs} (93%) rename openmls/src/messages/tests/{test_proposals.rs => proposals.rs} (100%) rename openmls/src/messages/tests/{test_welcome.rs => welcome.rs} (96%) rename openmls/src/schedule/{kat_key_schedule.rs => tests_and_kats/kats/key_schedule.rs} (98%) create mode 100644 openmls/src/schedule/tests_and_kats/kats/mod.rs rename openmls/src/schedule/{kat_psk_secret.rs => tests_and_kats/kats/psk_secret.rs} (93%) create mode 100644 openmls/src/schedule/tests_and_kats/mod.rs rename openmls/src/schedule/{unit_tests.rs => tests_and_kats/tests.rs} (94%) rename openmls/tests/{test_decryption_key_index.rs => decryption_key_index.rs} (100%) rename openmls/tests/{test_external_commit.rs => external_commit.rs} (100%) rename openmls/tests/{test_interop_scenarios.rs => interop_scenarios.rs} (100%) rename openmls/tests/{test_managed_api.rs => managed_api.rs} (100%) rename openmls/tests/{test_mls_group.rs => mls_group.rs} (100%) diff --git a/openmls/src/binary_tree/mod.rs b/openmls/src/binary_tree/mod.rs index 2d2139614..4ae734213 100644 --- a/openmls/src/binary_tree/mod.rs +++ b/openmls/src/binary_tree/mod.rs @@ -14,7 +14,7 @@ pub(crate) mod array_representation; // Tests #[cfg(test)] -mod test_binary_tree; +mod tests; // Crate types diff --git a/openmls/src/binary_tree/test_binary_tree.rs b/openmls/src/binary_tree/tests.rs similarity index 100% rename from openmls/src/binary_tree/test_binary_tree.rs rename to openmls/src/binary_tree/tests.rs diff --git a/openmls/src/ciphersuite/mod.rs b/openmls/src/ciphersuite/mod.rs index 4707bf67e..112272214 100644 --- a/openmls/src/ciphersuite/mod.rs +++ b/openmls/src/ciphersuite/mod.rs @@ -36,7 +36,7 @@ pub(crate) use signature::*; pub(crate) use serde::{Deserialize, Serialize}; #[cfg(test)] -mod tests; +mod tests_and_kats; const LABEL_PREFIX: &str = "MLS 1.0 "; diff --git a/openmls/src/ciphersuite/tests.rs b/openmls/src/ciphersuite/tests_and_kats.rs similarity index 83% rename from openmls/src/ciphersuite/tests.rs rename to openmls/src/ciphersuite/tests_and_kats.rs index a1c700565..e60de6d02 100644 --- a/openmls/src/ciphersuite/tests.rs +++ b/openmls/src/ciphersuite/tests_and_kats.rs @@ -1,6 +1,6 @@ //! Unit tests for the ciphersuites. -mod test_ciphersuite; +mod tests; // Test vector for basic crypto functionality mod kat_crypto_basics; diff --git a/openmls/src/ciphersuite/tests/kat_crypto_basics.rs b/openmls/src/ciphersuite/tests_and_kats/kat_crypto_basics.rs similarity index 100% rename from openmls/src/ciphersuite/tests/kat_crypto_basics.rs rename to openmls/src/ciphersuite/tests_and_kats/kat_crypto_basics.rs diff --git a/openmls/src/ciphersuite/tests/test_ciphersuite.rs b/openmls/src/ciphersuite/tests_and_kats/tests.rs similarity index 100% rename from openmls/src/ciphersuite/tests/test_ciphersuite.rs rename to openmls/src/ciphersuite/tests_and_kats/tests.rs diff --git a/openmls/src/extensions/mod.rs b/openmls/src/extensions/mod.rs index 20f46beec..c892cac2f 100644 --- a/openmls/src/extensions/mod.rs +++ b/openmls/src/extensions/mod.rs @@ -54,7 +54,7 @@ use tls_codec::{ }; #[cfg(test)] -mod test_extensions; +mod tests; /// MLS Extension Types /// diff --git a/openmls/src/extensions/test_extensions.rs b/openmls/src/extensions/tests.rs similarity index 96% rename from openmls/src/extensions/test_extensions.rs rename to openmls/src/extensions/tests.rs index c76c62c35..6e723f3fb 100644 --- a/openmls/src/extensions/test_extensions.rs +++ b/openmls/src/extensions/tests.rs @@ -8,7 +8,7 @@ use super::*; use crate::{ credentials::*, framing::*, - group::{errors::*, *}, + group::{errors::*, tests_and_kats::utils::generate_credential_with_key, *}, key_packages::*, messages::proposals::ProposalType, prelude::{Capabilities, RatchetTreeIn}, @@ -245,11 +245,8 @@ fn with_group_context_extensions() { let test_extension = Extension::Unknown(0xf023, UnknownExtension(vec![0xca, 0xfe])); let extensions = Extensions::single(test_extension.clone()); - let alice_credential_with_key_and_signer = tests::utils::generate_credential_with_key( - "Alice".into(), - ciphersuite.signature_algorithm(), - provider, - ); + let alice_credential_with_key_and_signer = + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), provider); let mls_group_create_config = MlsGroupCreateConfig::builder() .with_group_context_extensions(extensions) @@ -282,11 +279,8 @@ fn wrong_extension_with_group_context_extensions() { // - external pub // - ratchet tree - let alice_credential_with_key_and_signer = tests::utils::generate_credential_with_key( - "Alice".into(), - ciphersuite.signature_algorithm(), - provider, - ); + let alice_credential_with_key_and_signer = + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), provider); // create an extension that we can check for later let test_extension = Extension::ApplicationId(ApplicationIdExtension::new(&[0xca, 0xfe])); @@ -397,11 +391,8 @@ fn last_resort_extension() { // If we join a group using a last resort KP, it shouldn't be deleted from the // provider. - let alice_credential_with_key_and_signer = tests::utils::generate_credential_with_key( - "Alice".into(), - ciphersuite.signature_algorithm(), - provider, - ); + let alice_credential_with_key_and_signer = + generate_credential_with_key("Alice".into(), ciphersuite.signature_algorithm(), provider); let mls_group_create_config = MlsGroupCreateConfig::builder() .ciphersuite(ciphersuite) diff --git a/openmls/src/framing/mod.rs b/openmls/src/framing/mod.rs index 41fa1feb0..33c860997 100644 --- a/openmls/src/framing/mod.rs +++ b/openmls/src/framing/mod.rs @@ -94,7 +94,7 @@ pub use validation::*; // Tests #[cfg(test)] -pub(crate) mod test_framing; +pub(crate) mod tests; /// Wire format of MLS messages. /// diff --git a/openmls/src/framing/test_framing.rs b/openmls/src/framing/tests.rs similarity index 99% rename from openmls/src/framing/test_framing.rs rename to openmls/src/framing/tests.rs index 44876cacf..31dc0c8d2 100644 --- a/openmls/src/framing/test_framing.rs +++ b/openmls/src/framing/tests.rs @@ -11,7 +11,7 @@ use crate::{ extensions::Extensions, framing::*, group::{core_group::proposals::QueuedProposal, errors::*, CreateCommitParams}, - key_packages::{test_key_packages::key_package, KeyPackageBundle}, + key_packages::{tests::key_package, KeyPackageBundle}, schedule::psk::{store::ResumptionPskStore, PskSecret}, storage::OpenMlsProvider, test_utils::frankenstein::*, diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 197a4bae9..a97b82e86 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -16,22 +16,6 @@ pub(crate) mod process; pub(crate) mod proposals; pub(crate) mod staged_commit; -// Tests -#[cfg(test)] -pub(crate) mod kat_passive_client; -#[cfg(test)] -pub(crate) mod kat_welcome; -#[cfg(test)] -pub(crate) mod test_core_group; -#[cfg(test)] -mod test_create_commit_params; -#[cfg(test)] -mod test_external_init; -#[cfg(test)] -mod test_past_secrets; -#[cfg(test)] -mod test_proposals; - use log::{debug, trace}; use openmls_traits::{ crypto::OpenMlsCrypto, signatures::Signer, storage::StorageProvider as _, types::Ciphersuite, diff --git a/openmls/src/group/core_group/new_from_welcome.rs b/openmls/src/group/core_group/new_from_welcome.rs index 3a50c1758..ed47fe4a4 100644 --- a/openmls/src/group/core_group/new_from_welcome.rs +++ b/openmls/src/group/core_group/new_from_welcome.rs @@ -1,7 +1,6 @@ use log::debug; use crate::{ - ciphersuite::hash_ref::HashReference, group::{core_group::*, errors::WelcomeError}, schedule::psk::store::ResumptionPskStore, storage::OpenMlsProvider, @@ -244,14 +243,11 @@ pub(in crate::group) fn process_welcome( WelcomeError, > { let ciphersuite = welcome.ciphersuite(); - let egs = if let Some(egs) = CoreGroup::find_key_package_from_welcome_secrets( + let Some(egs) = welcome.find_encrypted_group_secret( key_package_bundle .key_package() .hash_ref(provider.crypto())?, - welcome.secrets(), - ) { - egs - } else { + ) else { return Err(WelcomeError::JoinerSecretNotFound); }; if ciphersuite != key_package_bundle.key_package().ciphersuite() { @@ -310,19 +306,3 @@ pub(in crate::group) fn process_welcome( verifiable_group_info, )) } - -impl CoreGroup { - // Helper functions - - pub(crate) fn find_key_package_from_welcome_secrets( - hash_ref: HashReference, - welcome_secrets: &[EncryptedGroupSecrets], - ) -> Option { - for egs in welcome_secrets { - if hash_ref == egs.new_member() { - return Some(egs.clone()); - } - } - None - } -} diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 56ca9e302..6a67a72e8 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -37,7 +37,7 @@ pub(crate) mod ser; // Tests #[cfg(test)] -mod test_mls_group; +pub(crate) mod tests_and_kats; /// Pending Commit state. Differentiates between Commits issued by group members /// and External Commits. diff --git a/openmls/src/group/mls_group/tests_and_kats/kats/mod.rs b/openmls/src/group/mls_group/tests_and_kats/kats/mod.rs new file mode 100644 index 000000000..813d47fe3 --- /dev/null +++ b/openmls/src/group/mls_group/tests_and_kats/kats/mod.rs @@ -0,0 +1,2 @@ +mod passive_client; +mod welcome; diff --git a/openmls/src/group/core_group/kat_passive_client.rs b/openmls/src/group/mls_group/tests_and_kats/kats/passive_client.rs similarity index 98% rename from openmls/src/group/core_group/kat_passive_client.rs rename to openmls/src/group/mls_group/tests_and_kats/kats/passive_client.rs index ece189280..1184739ee 100644 --- a/openmls/src/group/core_group/kat_passive_client.rs +++ b/openmls/src/group/mls_group/tests_and_kats/kats/passive_client.rs @@ -1,4 +1,3 @@ -use core_group::LeafNodeParameters; use log::{debug, info, warn}; use openmls_traits::{crypto::OpenMlsCrypto, storage::StorageProvider, OpenMlsProvider}; use serde::{self, Deserialize, Serialize}; @@ -6,8 +5,12 @@ use tls_codec::{Deserialize as TlsDeserialize, Serialize as TlsSerialize}; use crate::{ framing::{MlsMessageBodyIn, MlsMessageIn, MlsMessageOut, ProcessedMessageContent}, - group::*, + group::{ + HpkePrivateKey, IncomingWireFormatPolicy, Member, MlsGroup, MlsGroupCreateConfig, + MlsGroupJoinConfig, OutgoingWireFormatPolicy, StagedWelcome, WireFormatPolicy, + }, key_packages::*, + prelude::LeafNodeParameters, schedule::psk::PreSharedKeyId, test_utils::*, treesync::{ diff --git a/openmls/src/group/core_group/kat_welcome.rs b/openmls/src/group/mls_group/tests_and_kats/kats/welcome.rs similarity index 95% rename from openmls/src/group/core_group/kat_welcome.rs rename to openmls/src/group/mls_group/tests_and_kats/kats/welcome.rs index 4ed16f194..5691bbf1c 100644 --- a/openmls/src/group/core_group/kat_welcome.rs +++ b/openmls/src/group/mls_group/tests_and_kats/kats/welcome.rs @@ -19,8 +19,7 @@ //! from the key schedule epoch and the `confirmed_transcript_hash` from the //! decrypted GroupContext -use crate::test_utils::OpenMlsRustCrypto; -use kat_welcome::core_group::node::encryption_keys::EncryptionPrivateKey; +use crate::{test_utils::OpenMlsRustCrypto, treesync::node::encryption_keys::EncryptionPrivateKey}; use openmls_traits::{crypto::OpenMlsCrypto, storage::StorageProvider, OpenMlsProvider}; use serde::{self, Deserialize, Serialize}; use tls_codec::{Deserialize as TlsDeserialize, Serialize as TlsSerialize}; @@ -29,7 +28,7 @@ use crate::{ binary_tree::{array_representation::TreeSize, LeafNodeIndex}, ciphersuite::signable::Verifiable, framing::{MlsMessageBodyIn, MlsMessageIn}, - group::*, + group::{GroupContext, HpkePrivateKey, OpenMlsSignaturePublicKey, SignaturePublicKey}, key_packages::*, messages::*, prelude::group_info::{GroupInfo, VerifiableGroupInfo}, @@ -177,14 +176,14 @@ pub fn run_test_vector(test_vector: WelcomeTestVector) -> Result<(), &'static st // Verification: // * Decrypt the Welcome message: // * Identify the entry in `welcome.secrets` corresponding to `key_package` - let encrypted_group_secrets = CoreGroup::find_key_package_from_welcome_secrets( - key_package_bundle - .key_package() - .hash_ref(provider.crypto()) - .unwrap(), - welcome.secrets(), - ) - .unwrap(); + let encrypted_group_secrets = welcome + .find_encrypted_group_secret( + key_package_bundle + .key_package() + .hash_ref(provider.crypto()) + .unwrap(), + ) + .unwrap(); println!("{encrypted_group_secrets:?}"); // // // * Decrypt the encrypted group secrets using `init_priv` diff --git a/openmls/src/group/mls_group/tests_and_kats/mod.rs b/openmls/src/group/mls_group/tests_and_kats/mod.rs new file mode 100644 index 000000000..acbbd9ff6 --- /dev/null +++ b/openmls/src/group/mls_group/tests_and_kats/mod.rs @@ -0,0 +1,3 @@ +mod kats; +mod tests; +pub(crate) mod utils; diff --git a/openmls/src/group/core_group/test_core_group.rs b/openmls/src/group/mls_group/tests_and_kats/tests/core_group.rs similarity index 88% rename from openmls/src/group/core_group/test_core_group.rs rename to openmls/src/group/mls_group/tests_and_kats/tests/core_group.rs index cf3a95c72..aa9a5757c 100644 --- a/openmls/src/group/core_group/test_core_group.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/core_group.rs @@ -1,60 +1,20 @@ -use core_group::LeafNodeParameters; -use openmls_basic_credential::SignatureKeyPair; -use openmls_traits::types::HpkeCiphertext; +use mls_group::tests_and_kats::utils::{flip_last_byte, setup_alice_bob, setup_client}; use tls_codec::Serialize; use crate::{ binary_tree::*, ciphersuite::{signable::Signable, AeadNonce}, credentials::*, - framing::*, + framing::{tests::setup_alice_bob_group, *}, group::{errors::*, *}, key_packages::*, messages::{group_info::GroupInfoTBS, *}, + prelude::LeafNodeParameters, schedule::psk::{store::ResumptionPskStore, ExternalPsk, PreSharedKeyId, Psk}, test_utils::*, treesync::errors::ApplyUpdatePathError, }; -pub(crate) fn setup_alice_group( - ciphersuite: Ciphersuite, - provider: &impl crate::storage::OpenMlsProvider, -) -> ( - CoreGroup, - CredentialWithKey, - SignatureKeyPair, - OpenMlsSignaturePublicKey, -) { - // Create credentials and keys - let (alice_credential_with_key, alice_signature_keys) = - test_utils::new_credential(provider, b"Alice", ciphersuite.signature_algorithm()); - let pk = OpenMlsSignaturePublicKey::new( - alice_signature_keys.to_public_vec().into(), - ciphersuite.signature_algorithm(), - ) - .unwrap(); - - // Alice creates a group - let group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_key.clone(), - ) - .build(provider, &alice_signature_keys) - .expect("Error creating group."); - (group, alice_credential_with_key, alice_signature_keys, pk) -} - -/// This function flips the last byte of the ciphertext. -pub fn flip_last_byte(ctxt: &mut HpkeCiphertext) { - let mut last_bits = ctxt - .ciphertext - .pop() - .expect("An unexpected error occurred."); - last_bits ^= 0xff; - ctxt.ciphertext.push(last_bits); -} - #[openmls_test::openmls_test] fn test_failed_groupinfo_decryption( ciphersuite: Ciphersuite, @@ -182,7 +142,7 @@ fn test_update_path() { mut group_bob, bob_signature_keys, _bob_credential_with_key, - ) = test_framing::setup_alice_bob_group(ciphersuite, provider); + ) = setup_alice_bob_group(ciphersuite, provider); // === Bob updates and commits === let mut bob_new_leaf_node = group_bob.own_leaf_node().unwrap().clone(); @@ -271,33 +231,6 @@ fn test_update_path() { ); } -fn setup_alice_bob( - ciphersuite: Ciphersuite, - provider: &impl crate::storage::OpenMlsProvider, -) -> ( - CredentialWithKey, - SignatureKeyPair, - KeyPackageBundle, - SignatureKeyPair, -) { - // Create credentials and keys - let (alice_credential_with_key, alice_signer) = - test_utils::new_credential(provider, b"Alice", ciphersuite.signature_algorithm()); - let (bob_credential_with_key, bob_signer) = - test_utils::new_credential(provider, b"Bob", ciphersuite.signature_algorithm()); - - // Generate Bob's KeyPackage - let bob_key_package_bundle = - KeyPackageBundle::generate(provider, &bob_signer, ciphersuite, bob_credential_with_key); - - ( - alice_credential_with_key, - alice_signer, - bob_key_package_bundle, - bob_signer, - ) -} - // Test several scenarios when PSKs are used in a group #[openmls_test::openmls_test] fn test_psks() { @@ -544,34 +477,6 @@ fn test_own_commit_processing( assert_eq!(error, StageCommitError::OwnCommit); } -pub(crate) fn setup_client( - id: &str, - ciphersuite: Ciphersuite, - provider: &impl crate::storage::OpenMlsProvider, -) -> ( - CredentialWithKey, - KeyPackageBundle, - SignatureKeyPair, - OpenMlsSignaturePublicKey, -) { - let (credential_with_key, signature_keys) = - test_utils::new_credential(provider, id.as_bytes(), ciphersuite.signature_algorithm()); - let pk = OpenMlsSignaturePublicKey::new( - signature_keys.to_public_vec().into(), - ciphersuite.signature_algorithm(), - ) - .unwrap(); - - // Generate the KeyPackage - let key_package_bundle = KeyPackageBundle::generate( - provider, - &signature_keys, - ciphersuite, - credential_with_key.clone(), - ); - (credential_with_key, key_package_bundle, signature_keys, pk) -} - #[openmls_test::openmls_test] fn test_proposal_application_after_self_was_removed( ciphersuite: Ciphersuite, diff --git a/openmls/src/group/core_group/test_create_commit_params.rs b/openmls/src/group/mls_group/tests_and_kats/tests/create_commit_params.rs similarity index 88% rename from openmls/src/group/core_group/test_create_commit_params.rs rename to openmls/src/group/mls_group/tests_and_kats/tests/create_commit_params.rs index 5cc5c9767..876b8b94c 100644 --- a/openmls/src/group/core_group/test_create_commit_params.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/create_commit_params.rs @@ -1,4 +1,7 @@ -use super::*; +use crate::group::{ + mls_group::{FramingParameters, Proposal, WireFormat}, + CreateCommitParams, +}; // Tests that the builder for CreateCommitParams works as expected #[openmls_test::openmls_test] diff --git a/openmls/src/group/core_group/test_external_init.rs b/openmls/src/group/mls_group/tests_and_kats/tests/external_init.rs similarity index 69% rename from openmls/src/group/core_group/test_external_init.rs rename to openmls/src/group/mls_group/tests_and_kats/tests/external_init.rs index e6da79194..171ec90f0 100644 --- a/openmls/src/group/core_group/test_external_init.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/external_init.rs @@ -1,19 +1,17 @@ use crate::{ - framing::test_framing::setup_alice_bob_group, + framing::tests::setup_alice_bob_group, group::{ - errors::ExternalCommitError, public_group::errors::CreationFromExternalError, - test_core_group::setup_client, CreateCommitParams, + errors::ExternalCommitError, mls_group::tests_and_kats::utils::setup_client, + public_group::errors::CreationFromExternalError, MlsGroup, MlsGroupJoinConfig, }, storage::OpenMlsProvider, }; use openmls_traits::prelude::*; -use super::CoreGroup; - #[openmls_test::openmls_test] fn test_external_init_broken_signature() { let ( - framing_parameters, + _framing_parameters, group_alice, alice_signer, _group_bob, @@ -22,7 +20,7 @@ fn test_external_init_broken_signature() { ) = setup_alice_bob_group(ciphersuite, provider); // Now set up charly and try to init externally. - let (_charlie_credential, _charlie_kpb, charlie_signer, _charlie_pk) = + let (charlie_credential, _charlie_kpb, charlie_signer, _charlie_pk) = setup_client("Charlie", ciphersuite, provider); let verifiable_group_info = { @@ -34,16 +32,16 @@ fn test_external_init_broken_signature() { verifiable_group_info }; - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .build(); - - let result = CoreGroup::join_by_external_commit( + let result = MlsGroup::join_by_external_commit( provider, &charlie_signer, - params, None, verifiable_group_info, + &MlsGroupJoinConfig::default(), + None, + None, + &[], + charlie_credential, ) .expect_err("Signature was corrupted. This should have failed."); assert!(matches!( diff --git a/openmls/src/group/mls_group/test_mls_group.rs b/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs similarity index 99% rename from openmls/src/group/mls_group/test_mls_group.rs rename to openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs index 4ca182274..a18a3d01c 100644 --- a/openmls/src/group/mls_group/test_mls_group.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs @@ -1,4 +1,4 @@ -use core_group::test_core_group::setup_client; +use mls_group::tests_and_kats::utils::setup_client; use openmls_test::openmls_test; use openmls_traits::OpenMlsProvider as _; use tls_codec::{Deserialize, Serialize}; diff --git a/openmls/src/group/mls_group/tests_and_kats/tests/mod.rs b/openmls/src/group/mls_group/tests_and_kats/tests/mod.rs new file mode 100644 index 000000000..6281969e6 --- /dev/null +++ b/openmls/src/group/mls_group/tests_and_kats/tests/mod.rs @@ -0,0 +1,8 @@ +//! Test and Known Answer Test (KAT) modules for the MLS group. + +mod core_group; +mod create_commit_params; +mod external_init; +mod mls_group; +mod past_secrets; +mod proposals; diff --git a/openmls/src/group/core_group/test_past_secrets.rs b/openmls/src/group/mls_group/tests_and_kats/tests/past_secrets.rs similarity index 100% rename from openmls/src/group/core_group/test_past_secrets.rs rename to openmls/src/group/mls_group/tests_and_kats/tests/past_secrets.rs diff --git a/openmls/src/group/core_group/test_proposals.rs b/openmls/src/group/mls_group/tests_and_kats/tests/proposals.rs similarity index 99% rename from openmls/src/group/core_group/test_proposals.rs rename to openmls/src/group/mls_group/tests_and_kats/tests/proposals.rs index 460888f5d..dddcba815 100644 --- a/openmls/src/group/core_group/test_proposals.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/proposals.rs @@ -1,4 +1,3 @@ -use super::CoreGroup; use crate::{ binary_tree::LeafNodeIndex, ciphersuite::hash_ref::ProposalRef, @@ -9,9 +8,9 @@ use crate::{ }, group::{ errors::*, + mls_group::tests_and_kats::utils::setup_client, proposals::{ProposalQueue, ProposalStore, QueuedProposal}, - test_core_group::setup_client, - CreateCommitParams, GroupContext, GroupId, StagedCoreWelcome, + CoreGroup, CreateCommitParams, GroupContext, GroupId, StagedCoreWelcome, }, key_packages::{KeyPackageBundle, KeyPackageIn}, messages::proposals::{AddProposal, Proposal, ProposalOrRef, ProposalType}, diff --git a/openmls/src/group/mls_group/tests_and_kats/utils.rs b/openmls/src/group/mls_group/tests_and_kats/utils.rs new file mode 100644 index 000000000..83c169417 --- /dev/null +++ b/openmls/src/group/mls_group/tests_and_kats/utils.rs @@ -0,0 +1,100 @@ +//! Test utilities for (MLS group) tests. + +use openmls_basic_credential::SignatureKeyPair; +use openmls_traits::types::HpkeCiphertext; + +use crate::{credentials::*, group::*, key_packages::*, test_utils::*}; + +pub(crate) fn setup_alice_group( + ciphersuite: Ciphersuite, + provider: &impl crate::storage::OpenMlsProvider, +) -> ( + CoreGroup, + CredentialWithKey, + SignatureKeyPair, + OpenMlsSignaturePublicKey, +) { + // Create credentials and keys + let (alice_credential_with_key, alice_signature_keys) = + test_utils::new_credential(provider, b"Alice", ciphersuite.signature_algorithm()); + let pk = OpenMlsSignaturePublicKey::new( + alice_signature_keys.to_public_vec().into(), + ciphersuite.signature_algorithm(), + ) + .unwrap(); + + // Alice creates a group + let group = CoreGroup::builder( + GroupId::random(provider.rand()), + ciphersuite, + alice_credential_with_key.clone(), + ) + .build(provider, &alice_signature_keys) + .expect("Error creating group."); + (group, alice_credential_with_key, alice_signature_keys, pk) +} + +/// This function flips the last byte of the ciphertext. +pub fn flip_last_byte(ctxt: &mut HpkeCiphertext) { + let mut last_bits = ctxt + .ciphertext + .pop() + .expect("An unexpected error occurred."); + last_bits ^= 0xff; + ctxt.ciphertext.push(last_bits); +} + +pub(crate) fn setup_alice_bob( + ciphersuite: Ciphersuite, + provider: &impl crate::storage::OpenMlsProvider, +) -> ( + CredentialWithKey, + SignatureKeyPair, + KeyPackageBundle, + SignatureKeyPair, +) { + // Create credentials and keys + let (alice_credential_with_key, alice_signer) = + test_utils::new_credential(provider, b"Alice", ciphersuite.signature_algorithm()); + let (bob_credential_with_key, bob_signer) = + test_utils::new_credential(provider, b"Bob", ciphersuite.signature_algorithm()); + + // Generate Bob's KeyPackage + let bob_key_package_bundle = + KeyPackageBundle::generate(provider, &bob_signer, ciphersuite, bob_credential_with_key); + + ( + alice_credential_with_key, + alice_signer, + bob_key_package_bundle, + bob_signer, + ) +} + +pub(crate) fn setup_client( + id: &str, + ciphersuite: Ciphersuite, + provider: &impl crate::storage::OpenMlsProvider, +) -> ( + CredentialWithKey, + KeyPackageBundle, + SignatureKeyPair, + OpenMlsSignaturePublicKey, +) { + let (credential_with_key, signature_keys) = + test_utils::new_credential(provider, id.as_bytes(), ciphersuite.signature_algorithm()); + let pk = OpenMlsSignaturePublicKey::new( + signature_keys.to_public_vec().into(), + ciphersuite.signature_algorithm(), + ) + .unwrap(); + + // Generate the KeyPackage + let key_package_bundle = KeyPackageBundle::generate( + provider, + &signature_keys, + ciphersuite, + credential_with_key.clone(), + ); + (credential_with_key, key_package_bundle, signature_keys, pk) +} diff --git a/openmls/src/group/mod.rs b/openmls/src/group/mod.rs index e4659c5ea..ac1ec6127 100644 --- a/openmls/src/group/mod.rs +++ b/openmls/src/group/mod.rs @@ -39,7 +39,7 @@ mod group_context; #[cfg(test)] pub(crate) use core_group::create_commit_params::*; #[cfg(any(feature = "test-utils", test))] -pub(crate) mod tests; +pub(crate) mod tests_and_kats; /// A group ID. The group ID is chosen by the creator of the group and should be globally unique. #[derive( diff --git a/openmls/src/group/public_group/tests.rs b/openmls/src/group/public_group/tests.rs index 6f39076b3..e1dfc8ae4 100644 --- a/openmls/src/group/public_group/tests.rs +++ b/openmls/src/group/public_group/tests.rs @@ -7,8 +7,8 @@ use crate::{ ProcessedMessageContent, ProtocolMessage, Sender, }, group::{ - test_core_group::setup_client, GroupId, MlsGroup, MlsGroupCreateConfig, ProposalStore, - StagedCommit, PURE_PLAINTEXT_WIRE_FORMAT_POLICY, + mls_group::tests_and_kats::utils::setup_client, GroupId, MlsGroup, MlsGroupCreateConfig, + ProposalStore, StagedCommit, PURE_PLAINTEXT_WIRE_FORMAT_POLICY, }, messages::proposals::Proposal, }; diff --git a/openmls/src/group/tests/mod.rs b/openmls/src/group/tests/mod.rs deleted file mode 100644 index 33c148d2d..000000000 --- a/openmls/src/group/tests/mod.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Unit tests for the core group - -#[cfg(test)] -mod external_add_proposal; -#[cfg(test)] -mod external_remove_proposal; -#[cfg(test)] -mod group_context_extensions; -#[cfg(test)] -pub mod kat_messages; -#[cfg(test)] -pub mod kat_transcript_hashes; -#[cfg(test)] -pub(crate) mod test_aad; -#[cfg(test)] -mod test_commit_validation; -#[cfg(test)] -mod test_encoding; -#[cfg(test)] -mod test_external_commit; -#[cfg(test)] -mod test_external_commit_validation; -#[cfg(test)] -mod test_framing; -#[cfg(test)] -mod test_framing_validation; -#[cfg(test)] -mod test_group; -#[cfg(test)] -mod test_past_secrets; -#[cfg(test)] -mod test_proposal_validation; -#[cfg(test)] -mod test_remove_operation; -#[cfg(test)] -mod test_wire_format_policy; -#[cfg(test)] -pub(crate) mod utils; - -pub(crate) mod tree_printing; diff --git a/openmls/src/group/tests/kat_messages.rs b/openmls/src/group/tests_and_kats/kats/messages.rs similarity index 99% rename from openmls/src/group/tests/kat_messages.rs rename to openmls/src/group/tests_and_kats/kats/messages.rs index 22ff3aa8a..d64f6efd0 100644 --- a/openmls/src/group/tests/kat_messages.rs +++ b/openmls/src/group/tests_and_kats/kats/messages.rs @@ -15,7 +15,7 @@ use crate::{ ciphersuite::Mac, framing::*, group::{ - tests::utils::{generate_credential_with_key, generate_key_package, randombytes}, + tests_and_kats::utils::{generate_credential_with_key, generate_key_package, randombytes}, *, }, key_packages::*, @@ -640,7 +640,7 @@ pub fn run_test_vector(tv: MessagesTestVector) -> Result<(), EncodingMismatch> { #[test] fn read_test_vectors_messages() { - let tests: Vec = read_json!("../../../test_vectors/messages.json"); + let tests: Vec = read_json!("../../../../test_vectors/messages.json"); for test_vector in tests { match run_test_vector(test_vector) { diff --git a/openmls/src/group/tests_and_kats/kats/mod.rs b/openmls/src/group/tests_and_kats/kats/mod.rs new file mode 100644 index 000000000..c6ed6b901 --- /dev/null +++ b/openmls/src/group/tests_and_kats/kats/mod.rs @@ -0,0 +1,2 @@ +mod messages; +mod transcript_hashes; diff --git a/openmls/src/group/tests/kat_transcript_hashes.rs b/openmls/src/group/tests_and_kats/kats/transcript_hashes.rs similarity index 99% rename from openmls/src/group/tests/kat_transcript_hashes.rs rename to openmls/src/group/tests_and_kats/kats/transcript_hashes.rs index 5d7ec46c9..383b95150 100644 --- a/openmls/src/group/tests/kat_transcript_hashes.rs +++ b/openmls/src/group/tests_and_kats/kats/transcript_hashes.rs @@ -13,7 +13,7 @@ use crate::{ binary_tree::array_representation::LeafNodeIndex, framing::{mls_auth_content::AuthenticatedContent, *}, group::{ - tests::utils::{generate_credential_with_key, randombytes}, + tests_and_kats::utils::{generate_credential_with_key, randombytes}, *, }, messages::*, diff --git a/openmls/src/group/tests_and_kats/mod.rs b/openmls/src/group/tests_and_kats/mod.rs new file mode 100644 index 000000000..62632607f --- /dev/null +++ b/openmls/src/group/tests_and_kats/mod.rs @@ -0,0 +1,8 @@ +#[cfg(test)] +mod kats; +#[cfg(test)] +mod tests; +#[cfg(any(feature = "test-utils", test))] +pub(crate) mod tree_printing; +#[cfg(test)] +pub(crate) mod utils; diff --git a/openmls/src/group/tests/test_aad.rs b/openmls/src/group/tests_and_kats/tests/aad.rs similarity index 97% rename from openmls/src/group/tests/test_aad.rs rename to openmls/src/group/tests_and_kats/tests/aad.rs index 29bdb42c3..67af269b1 100644 --- a/openmls/src/group/tests/test_aad.rs +++ b/openmls/src/group/tests_and_kats/tests/aad.rs @@ -1,6 +1,12 @@ // Import necessary modules and dependencies -use super::utils::{generate_credential_with_key, generate_key_package}; -use crate::{binary_tree::LeafNodeIndex, framing::*, group::*}; +use crate::{ + binary_tree::LeafNodeIndex, + framing::*, + group::{ + tests_and_kats::utils::{generate_credential_with_key, generate_key_package}, + *, + }, +}; // Tests the different variants of the RemoveOperation enum. #[openmls_test::openmls_test] diff --git a/openmls/src/group/tests/test_commit_validation.rs b/openmls/src/group/tests_and_kats/tests/commit_validation.rs similarity index 99% rename from openmls/src/group/tests/test_commit_validation.rs rename to openmls/src/group/tests_and_kats/tests/commit_validation.rs index c611fb9a9..acfd5fdb6 100644 --- a/openmls/src/group/tests/test_commit_validation.rs +++ b/openmls/src/group/tests_and_kats/tests/commit_validation.rs @@ -4,7 +4,7 @@ use openmls_traits::{prelude::*, signatures::Signer, types::Ciphersuite}; use tls_codec::{Deserialize, Serialize}; -use super::utils::{ +use crate::group::tests_and_kats::utils::{ generate_credential_with_key, generate_key_package, resign_message, CredentialWithKeyAndSigner, }; use crate::{ diff --git a/openmls/src/group/tests/test_encoding.rs b/openmls/src/group/tests_and_kats/tests/encoding.rs similarity index 99% rename from openmls/src/group/tests/test_encoding.rs rename to openmls/src/group/tests_and_kats/tests/encoding.rs index aaa5c6d11..452c2e7e5 100644 --- a/openmls/src/group/tests/test_encoding.rs +++ b/openmls/src/group/tests_and_kats/tests/encoding.rs @@ -1,7 +1,7 @@ use openmls_traits::crypto::OpenMlsCrypto; use tls_codec::{Deserialize, Serialize}; -use super::utils::*; +use crate::group::tests_and_kats::utils::*; use crate::{ binary_tree::LeafNodeIndex, framing::*, group::*, key_packages::*, messages::*, schedule::psk::store::ResumptionPskStore, test_utils::*, diff --git a/openmls/src/group/tests/external_add_proposal.rs b/openmls/src/group/tests_and_kats/tests/external_add_proposal.rs similarity index 99% rename from openmls/src/group/tests/external_add_proposal.rs rename to openmls/src/group/tests_and_kats/tests/external_add_proposal.rs index 4181e20a7..40df36c7c 100644 --- a/openmls/src/group/tests/external_add_proposal.rs +++ b/openmls/src/group/tests_and_kats/tests/external_add_proposal.rs @@ -14,7 +14,7 @@ use crate::{ use openmls_traits::{types::Ciphersuite, OpenMlsProvider as _}; -use super::utils::*; +use crate::group::tests_and_kats::utils::*; struct ProposalValidationTestSetup { alice_group: (MlsGroup, SignatureKeyPair), diff --git a/openmls/src/group/tests/test_external_commit.rs b/openmls/src/group/tests_and_kats/tests/external_commit.rs similarity index 98% rename from openmls/src/group/tests/test_external_commit.rs rename to openmls/src/group/tests_and_kats/tests/external_commit.rs index fcc7c251b..0b04c366a 100644 --- a/openmls/src/group/tests/test_external_commit.rs +++ b/openmls/src/group/tests_and_kats/tests/external_commit.rs @@ -4,7 +4,7 @@ use tls_codec::{Deserialize, Serialize}; use crate::{ framing::{MlsMessageIn, Sender}, group::{ - tests::utils::generate_credential_with_key, MlsGroup, MlsGroupCreateConfig, + tests_and_kats::utils::generate_credential_with_key, MlsGroup, MlsGroupCreateConfig, PURE_PLAINTEXT_WIRE_FORMAT_POLICY, }, prelude::ProcessedMessageContent, diff --git a/openmls/src/group/tests/test_external_commit_validation.rs b/openmls/src/group/tests_and_kats/tests/external_commit_validation.rs similarity index 99% rename from openmls/src/group/tests/test_external_commit_validation.rs rename to openmls/src/group/tests_and_kats/tests/external_commit_validation.rs index 9ae598b97..627381f26 100644 --- a/openmls/src/group/tests/test_external_commit_validation.rs +++ b/openmls/src/group/tests_and_kats/tests/external_commit_validation.rs @@ -16,7 +16,7 @@ use crate::{ errors::{ ExternalCommitValidationError, ProcessMessageError, StageCommitError, ValidationError, }, - tests::utils::{ + tests_and_kats::utils::{ generate_credential_with_key, generate_key_package, resign_external_commit, }, Extensions, MlsGroup, OpenMlsSignaturePublicKey, PURE_CIPHERTEXT_WIRE_FORMAT_POLICY, @@ -612,7 +612,7 @@ mod utils { use crate::{ framing::{MlsMessageIn, PublicMessage, Sender}, group::{ - tests::utils::{generate_credential_with_key, CredentialWithKeyAndSigner}, + tests_and_kats::utils::{generate_credential_with_key, CredentialWithKeyAndSigner}, MlsGroup, MlsGroupCreateConfig, WireFormatPolicy, }, }; diff --git a/openmls/src/group/tests/external_remove_proposal.rs b/openmls/src/group/tests_and_kats/tests/external_remove_proposal.rs similarity index 99% rename from openmls/src/group/tests/external_remove_proposal.rs rename to openmls/src/group/tests_and_kats/tests/external_remove_proposal.rs index c6393d3d4..ead7cdfa0 100644 --- a/openmls/src/group/tests/external_remove_proposal.rs +++ b/openmls/src/group/tests_and_kats/tests/external_remove_proposal.rs @@ -4,7 +4,7 @@ use crate::{credentials::BasicCredential, framing::*, group::*, messages::extern use openmls_traits::{types::Ciphersuite, OpenMlsProvider as _}; -use super::utils::*; +use crate::group::tests_and_kats::utils::*; // Creates a standalone group fn new_test_group( diff --git a/openmls/src/group/tests/test_framing.rs b/openmls/src/group/tests_and_kats/tests/framing.rs similarity index 99% rename from openmls/src/group/tests/test_framing.rs rename to openmls/src/group/tests_and_kats/tests/framing.rs index 9dd1e8b43..17052e804 100644 --- a/openmls/src/group/tests/test_framing.rs +++ b/openmls/src/group/tests_and_kats/tests/framing.rs @@ -5,7 +5,7 @@ use openmls_traits::random::OpenMlsRand; use tls_codec::Serialize; -use super::utils::*; +use crate::group::tests_and_kats::utils::*; use crate::{ binary_tree::{array_representation::TreeSize, *}, ciphersuite::signable::Signable, diff --git a/openmls/src/group/tests/test_framing_validation.rs b/openmls/src/group/tests_and_kats/tests/framing_validation.rs similarity index 99% rename from openmls/src/group/tests/test_framing_validation.rs rename to openmls/src/group/tests_and_kats/tests/framing_validation.rs index dd2f87740..4ddf658ed 100644 --- a/openmls/src/group/tests/test_framing_validation.rs +++ b/openmls/src/group/tests_and_kats/tests/framing_validation.rs @@ -8,7 +8,7 @@ use crate::{ binary_tree::LeafNodeIndex, framing::*, group::*, key_packages::*, treesync::LeafNodeParameters, }; -use super::utils::{ +use crate::group::tests_and_kats::utils::{ generate_credential_with_key, generate_key_package, CredentialWithKeyAndSigner, }; diff --git a/openmls/src/group/tests/test_group.rs b/openmls/src/group/tests_and_kats/tests/group.rs similarity index 98% rename from openmls/src/group/tests/test_group.rs rename to openmls/src/group/tests_and_kats/tests/group.rs index f2e4e1496..f20d468fa 100644 --- a/openmls/src/group/tests/test_group.rs +++ b/openmls/src/group/tests_and_kats/tests/group.rs @@ -1,12 +1,16 @@ -use framing::mls_content_in::FramedContentBodyIn; -use tests::utils::{generate_credential_with_key, generate_key_package}; -use treesync::LeafNodeParameters; - use crate::{ - ciphersuite::signable::Verifiable, framing::*, group::*, key_packages::*, - schedule::psk::store::ResumptionPskStore, test_utils::*, - tree::sender_ratchet::SenderRatchetConfiguration, *, + ciphersuite::signable::Verifiable, + framing::*, + group::{tests_and_kats::utils::generate_key_package, *}, + key_packages::*, + schedule::psk::store::ResumptionPskStore, + test_utils::*, + tree::sender_ratchet::SenderRatchetConfiguration, + *, }; +use framing::mls_content_in::FramedContentBodyIn; +use group::tests_and_kats::utils::generate_credential_with_key; +use treesync::LeafNodeParameters; #[openmls_test::openmls_test] fn create_commit_optional_path( diff --git a/openmls/src/group/tests/group_context_extensions.rs b/openmls/src/group/tests_and_kats/tests/group_context_extensions.rs similarity index 99% rename from openmls/src/group/tests/group_context_extensions.rs rename to openmls/src/group/tests_and_kats/tests/group_context_extensions.rs index 5a9615d54..6d9fef48a 100644 --- a/openmls/src/group/tests/group_context_extensions.rs +++ b/openmls/src/group/tests_and_kats/tests/group_context_extensions.rs @@ -1,3 +1,4 @@ +use mls_group::tests_and_kats::utils::setup_client; use openmls_basic_credential::SignatureKeyPair; use openmls_test::openmls_test; use openmls_traits::types::Ciphersuite; @@ -8,7 +9,7 @@ use crate::{ ciphersuite::hash_ref::ProposalRef, credentials::CredentialWithKey, framing::*, - group::{core_group::test_core_group::setup_client, *}, + group::*, key_packages::{errors::KeyPackageVerifyError, *}, messages::group_info::GroupInfo, test_utils::frankenstein::{self, FrankenMlsMessage}, diff --git a/openmls/src/group/tests_and_kats/tests/mod.rs b/openmls/src/group/tests_and_kats/tests/mod.rs new file mode 100644 index 000000000..3bcc322e9 --- /dev/null +++ b/openmls/src/group/tests_and_kats/tests/mod.rs @@ -0,0 +1,17 @@ +//! Unit tests for the core group + +mod aad; +mod commit_validation; +mod encoding; +mod external_add_proposal; +mod external_commit; +mod external_commit_validation; +mod external_remove_proposal; +mod framing; +mod framing_validation; +mod group; +mod group_context_extensions; +mod past_secrets; +mod proposal_validation; +mod remove_operation; +mod wire_format_policy; diff --git a/openmls/src/group/tests/test_past_secrets.rs b/openmls/src/group/tests_and_kats/tests/past_secrets.rs similarity index 98% rename from openmls/src/group/tests/test_past_secrets.rs rename to openmls/src/group/tests_and_kats/tests/past_secrets.rs index 9ca990155..b5158e673 100644 --- a/openmls/src/group/tests/test_past_secrets.rs +++ b/openmls/src/group/tests_and_kats/tests/past_secrets.rs @@ -1,6 +1,6 @@ //! This module contains tests regarding the use of [`MessageSecretsStore`] in [`MlsGroup`] -use super::utils::{generate_credential_with_key, generate_key_package}; +use crate::group::tests_and_kats::utils::{generate_credential_with_key, generate_key_package}; use crate::{ framing::{MessageDecryptionError, MlsMessageIn, ProcessedMessageContent}, group::*, diff --git a/openmls/src/group/tests/test_proposal_validation.rs b/openmls/src/group/tests_and_kats/tests/proposal_validation.rs similarity index 99% rename from openmls/src/group/tests/test_proposal_validation.rs rename to openmls/src/group/tests_and_kats/tests/proposal_validation.rs index 99968fb69..21363dc3b 100644 --- a/openmls/src/group/tests/test_proposal_validation.rs +++ b/openmls/src/group/tests_and_kats/tests/proposal_validation.rs @@ -8,7 +8,7 @@ use openmls_traits::{ }; use tls_codec::{Deserialize, Serialize}; -use super::utils::{ +use crate::group::tests_and_kats::utils::{ generate_credential_with_key, generate_key_package, resign_message, CredentialWithKeyAndSigner, }; use crate::{ diff --git a/openmls/src/group/tests/test_remove_operation.rs b/openmls/src/group/tests_and_kats/tests/remove_operation.rs similarity index 99% rename from openmls/src/group/tests/test_remove_operation.rs rename to openmls/src/group/tests_and_kats/tests/remove_operation.rs index f287a5e13..d5daa0540 100644 --- a/openmls/src/group/tests/test_remove_operation.rs +++ b/openmls/src/group/tests_and_kats/tests/remove_operation.rs @@ -1,6 +1,6 @@ //! This module tests the classification of remove operations with RemoveOperation -use super::utils::{generate_credential_with_key, generate_key_package}; +use crate::group::tests_and_kats::utils::{generate_credential_with_key, generate_key_package}; use crate::{framing::*, group::*}; use openmls_traits::prelude::*; diff --git a/openmls/src/group/tests/test_wire_format_policy.rs b/openmls/src/group/tests_and_kats/tests/wire_format_policy.rs similarity index 99% rename from openmls/src/group/tests/test_wire_format_policy.rs rename to openmls/src/group/tests_and_kats/tests/wire_format_policy.rs index 59e6a09bb..805462256 100644 --- a/openmls/src/group/tests/test_wire_format_policy.rs +++ b/openmls/src/group/tests_and_kats/tests/wire_format_policy.rs @@ -4,7 +4,7 @@ use openmls_traits::{signatures::Signer, types::Ciphersuite}; use crate::{framing::*, group::*, treesync::LeafNodeParameters}; -use super::utils::{ +use crate::group::tests_and_kats::utils::{ generate_credential_with_key, generate_key_package, CredentialWithKeyAndSigner, }; diff --git a/openmls/src/group/tests/tree_printing.rs b/openmls/src/group/tests_and_kats/tree_printing.rs similarity index 100% rename from openmls/src/group/tests/tree_printing.rs rename to openmls/src/group/tests_and_kats/tree_printing.rs diff --git a/openmls/src/group/tests/utils.rs b/openmls/src/group/tests_and_kats/utils.rs similarity index 100% rename from openmls/src/group/tests/utils.rs rename to openmls/src/group/tests_and_kats/utils.rs diff --git a/openmls/src/key_packages/mod.rs b/openmls/src/key_packages/mod.rs index 5de332488..eeae2d9f6 100644 --- a/openmls/src/key_packages/mod.rs +++ b/openmls/src/key_packages/mod.rs @@ -128,7 +128,7 @@ mod lifetime; // Tests #[cfg(test)] -pub(crate) mod test_key_packages; +pub(crate) mod tests; // Public types pub use key_package_in::KeyPackageIn; diff --git a/openmls/src/key_packages/test_key_packages.rs b/openmls/src/key_packages/tests.rs similarity index 100% rename from openmls/src/key_packages/test_key_packages.rs rename to openmls/src/key_packages/tests.rs diff --git a/openmls/src/messages/mod.rs b/openmls/src/messages/mod.rs index 395b566a8..be861a120 100644 --- a/openmls/src/messages/mod.rs +++ b/openmls/src/messages/mod.rs @@ -3,6 +3,7 @@ //! This module contains the types and implementations for Commit & Welcome messages, //! as well as Proposals & the group info used for External Commits. +use hash_ref::HashReference; use openmls_traits::{ crypto::OpenMlsCrypto, types::{Ciphersuite, HpkeCiphertext, HpkeKeyPair}, @@ -81,6 +82,15 @@ impl Welcome { } } + pub(crate) fn find_encrypted_group_secret( + &self, + hash_ref: HashReference, + ) -> Option<&EncryptedGroupSecrets> { + self.secrets() + .iter() + .find(|egs| hash_ref == egs.new_member()) + } + /// Returns a reference to the ciphersuite in this Welcome message. pub(crate) fn ciphersuite(&self) -> Ciphersuite { self.cipher_suite diff --git a/openmls/src/messages/tests/test_codec.rs b/openmls/src/messages/tests/codec.rs similarity index 100% rename from openmls/src/messages/tests/test_codec.rs rename to openmls/src/messages/tests/codec.rs diff --git a/openmls/src/messages/tests/test_export_group_info.rs b/openmls/src/messages/tests/export_group_info.rs similarity index 93% rename from openmls/src/messages/tests/test_export_group_info.rs rename to openmls/src/messages/tests/export_group_info.rs index 54dd4f1c1..0452d6bb8 100644 --- a/openmls/src/messages/tests/test_export_group_info.rs +++ b/openmls/src/messages/tests/export_group_info.rs @@ -2,7 +2,7 @@ use tls_codec::{Deserialize, Serialize}; use crate::{ ciphersuite::signable::Verifiable, - group::test_core_group::setup_alice_group, + group::mls_group::tests_and_kats::utils::setup_alice_group, messages::group_info::{GroupInfo, VerifiableGroupInfo}, test_utils::*, }; diff --git a/openmls/src/messages/tests/mod.rs b/openmls/src/messages/tests/mod.rs index 74f35e27d..053b9e2cd 100644 --- a/openmls/src/messages/tests/mod.rs +++ b/openmls/src/messages/tests/mod.rs @@ -1,6 +1,6 @@ //! Unit tests for messages -mod test_codec; -mod test_export_group_info; -mod test_proposals; -mod test_welcome; +mod codec; +mod export_group_info; +mod proposals; +mod welcome; diff --git a/openmls/src/messages/tests/test_proposals.rs b/openmls/src/messages/tests/proposals.rs similarity index 100% rename from openmls/src/messages/tests/test_proposals.rs rename to openmls/src/messages/tests/proposals.rs diff --git a/openmls/src/messages/tests/test_welcome.rs b/openmls/src/messages/tests/welcome.rs similarity index 96% rename from openmls/src/messages/tests/test_welcome.rs rename to openmls/src/messages/tests/welcome.rs index 888f94fd8..3b331c500 100644 --- a/openmls/src/messages/tests/test_welcome.rs +++ b/openmls/src/messages/tests/welcome.rs @@ -9,8 +9,8 @@ use crate::{ }, extensions::Extensions, group::{ - errors::WelcomeError, GroupContext, GroupId, MlsGroup, MlsGroupCreateConfig, - ProcessedWelcome, StagedWelcome, + errors::WelcomeError, mls_group::tests_and_kats::utils::setup_client, GroupContext, + GroupId, MlsGroup, MlsGroupCreateConfig, ProcessedWelcome, StagedWelcome, }, messages::{ group_info::{GroupInfoTBS, VerifiableGroupInfo}, @@ -47,9 +47,9 @@ fn test_welcome_context_mismatch( .build(); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_signature_key) = - crate::group::test_core_group::setup_client("Alice", ciphersuite, provider); + setup_client("Alice", ciphersuite, provider); let (_bob_credential, bob_kpb, _bob_signer, _bob_signature_key) = - crate::group::test_core_group::setup_client("Bob", ciphersuite, provider); + setup_client("Bob", ciphersuite, provider); let bob_kp = bob_kpb.key_package(); let bob_private_key = bob_kpb.init_private_key(); @@ -308,9 +308,9 @@ fn test_welcome_processing() { .build(); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_signature_key) = - crate::group::test_core_group::setup_client("Alice", ciphersuite, provider); + setup_client("Alice", ciphersuite, provider); let (_bob_credential, bob_kpb, _bob_signer, _bob_signature_key) = - crate::group::test_core_group::setup_client("Bob", ciphersuite, provider); + setup_client("Bob", ciphersuite, provider); let bob_kp = bob_kpb.key_package(); diff --git a/openmls/src/prelude_test.rs b/openmls/src/prelude_test.rs index f926676a0..9de9f3153 100644 --- a/openmls/src/prelude_test.rs +++ b/openmls/src/prelude_test.rs @@ -6,7 +6,7 @@ pub use crate::ciphersuite::{signable::Verifiable, *}; // KATs pub use crate::binary_tree::array_representation::kat_treemath; pub use crate::key_packages::KeyPackage; -pub use crate::schedule::kat_key_schedule::{self, KeyScheduleTestVector}; +pub use crate::schedule::tests_and_kats::kats::key_schedule::{self, KeyScheduleTestVector}; // TODO: #624 - re-enable test vectors. // pub use crate::group::tests::{ // kat_messages::{self, MessagesTestVector}, diff --git a/openmls/src/schedule/mod.rs b/openmls/src/schedule/mod.rs index cd2db1259..ea8aec63b 100644 --- a/openmls/src/schedule/mod.rs +++ b/openmls/src/schedule/mod.rs @@ -149,13 +149,9 @@ use message_secrets::MessageSecrets; use openmls_traits::random::OpenMlsRand; use psk::PskSecret; -// Tests +// Tests and kats #[cfg(any(feature = "test-utils", test))] -pub mod kat_key_schedule; -#[cfg(test)] -pub mod kat_psk_secret; -#[cfg(test)] -mod unit_tests; +pub mod tests_and_kats; // Public types pub use psk::{ExternalPsk, PreSharedKeyId, Psk}; diff --git a/openmls/src/schedule/kat_key_schedule.rs b/openmls/src/schedule/tests_and_kats/kats/key_schedule.rs similarity index 98% rename from openmls/src/schedule/kat_key_schedule.rs rename to openmls/src/schedule/tests_and_kats/kats/key_schedule.rs index 5e8e7e9df..a46765b1b 100644 --- a/openmls/src/schedule/kat_key_schedule.rs +++ b/openmls/src/schedule/tests_and_kats/kats/key_schedule.rs @@ -10,10 +10,15 @@ use openmls_traits::{random::OpenMlsRand, types::HpkeKeyPair, OpenMlsProvider}; use serde::{self, Deserialize, Serialize}; use tls_codec::Serialize as TlsSerializeTrait; -use super::{errors::KsTestVectorError, CommitSecret}; #[cfg(test)] use crate::test_utils::write; -use crate::{ciphersuite::*, extensions::Extensions, group::*, schedule::*, test_utils::*}; +use crate::{ + ciphersuite::*, + extensions::Extensions, + group::*, + schedule::{errors::KsTestVectorError, CommitSecret, *}, + test_utils::*, +}; #[derive(Serialize, Deserialize, Debug, Clone, Default)] struct Exporter { @@ -258,7 +263,8 @@ fn write_test_vectors() { fn read_test_vectors_key_schedule() { let _ = pretty_env_logger::try_init(); - let tests: Vec = read_json!("../../test_vectors/key-schedule.json"); + let tests: Vec = + read_json!("../../../../test_vectors/key-schedule.json"); for test_vector in tests { match run_test_vector(test_vector, provider) { diff --git a/openmls/src/schedule/tests_and_kats/kats/mod.rs b/openmls/src/schedule/tests_and_kats/kats/mod.rs new file mode 100644 index 000000000..98c704da1 --- /dev/null +++ b/openmls/src/schedule/tests_and_kats/kats/mod.rs @@ -0,0 +1,4 @@ +#[cfg(any(feature = "test-utils", test))] +pub mod key_schedule; +#[cfg(test)] +pub mod psk_secret; diff --git a/openmls/src/schedule/kat_psk_secret.rs b/openmls/src/schedule/tests_and_kats/kats/psk_secret.rs similarity index 93% rename from openmls/src/schedule/kat_psk_secret.rs rename to openmls/src/schedule/tests_and_kats/kats/psk_secret.rs index f13d6c592..56f64b65e 100644 --- a/openmls/src/schedule/kat_psk_secret.rs +++ b/openmls/src/schedule/tests_and_kats/kats/psk_secret.rs @@ -35,9 +35,10 @@ use openmls_traits::crypto::OpenMlsCrypto; use serde::Deserialize; -use super::psk::{ExternalPsk, PreSharedKeyId, Psk, PskSecret}; use crate::{ - schedule::psk::{load_psks, store::ResumptionPskStore}, + schedule::psk::{ + load_psks, store::ResumptionPskStore, ExternalPsk, PreSharedKeyId, Psk, PskSecret, + }, test_utils::*, }; @@ -106,7 +107,7 @@ fn read_test_vectors_ps() { let _ = pretty_env_logger::try_init(); log::debug!("Reading test vectors ..."); - let tests: Vec = read_json!("../../test_vectors/psk_secret.json"); + let tests: Vec = read_json!("../../../../test_vectors/psk_secret.json"); for test_vector in tests { match run_test_vector(test_vector, provider) { diff --git a/openmls/src/schedule/tests_and_kats/mod.rs b/openmls/src/schedule/tests_and_kats/mod.rs new file mode 100644 index 000000000..ee33dc040 --- /dev/null +++ b/openmls/src/schedule/tests_and_kats/mod.rs @@ -0,0 +1,4 @@ +#[cfg(any(feature = "test-utils", test))] +pub mod kats; +#[cfg(test)] +mod tests; diff --git a/openmls/src/schedule/unit_tests.rs b/openmls/src/schedule/tests_and_kats/tests.rs similarity index 94% rename from openmls/src/schedule/unit_tests.rs rename to openmls/src/schedule/tests_and_kats/tests.rs index 64305a899..2a89320e0 100644 --- a/openmls/src/schedule/unit_tests.rs +++ b/openmls/src/schedule/tests_and_kats/tests.rs @@ -2,10 +2,9 @@ use openmls_traits::{random::OpenMlsRand, OpenMlsProvider}; -use super::PskSecret; use crate::{ ciphersuite::Secret, - schedule::psk::{store::ResumptionPskStore, *}, + schedule::psk::{store::ResumptionPskStore, PskSecret, *}, }; #[openmls_test::openmls_test] diff --git a/openmls/src/storage.rs b/openmls/src/storage.rs index bc2ed55ec..80a6a9744 100644 --- a/openmls/src/storage.rs +++ b/openmls/src/storage.rs @@ -126,7 +126,9 @@ impl traits::PskBundle for PskBundle {} #[cfg(test)] mod test { - use crate::{group::test_core_group::setup_client, prelude::KeyPackageBuilder}; + use crate::{ + group::mls_group::tests_and_kats::utils::setup_client, prelude::KeyPackageBuilder, + }; use super::*; diff --git a/openmls/src/test_utils/mod.rs b/openmls/src/test_utils/mod.rs index 85fa9dd75..21efccaf0 100644 --- a/openmls/src/test_utils/mod.rs +++ b/openmls/src/test_utils/mod.rs @@ -17,7 +17,7 @@ pub use openmls_traits::{ use serde::{self, de::DeserializeOwned, Serialize}; #[cfg(test)] -use crate::group::tests::utils::CredentialWithKeyAndSigner; +use crate::group::tests_and_kats::utils::CredentialWithKeyAndSigner; pub use crate::utils::*; use crate::{ ciphersuite::{HpkePrivateKey, OpenMlsSignaturePublicKey}, diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index 512a76c89..484df545c 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -46,7 +46,7 @@ use self::{ use crate::binary_tree::array_representation::ParentNodeIndex; #[cfg(any(feature = "test-utils", test))] use crate::{ - binary_tree::array_representation::level, group::tests::tree_printing::root, + binary_tree::array_representation::level, group::tests_and_kats::tree_printing::root, test_utils::bytes_to_hex, }; use crate::{ @@ -731,8 +731,7 @@ mod test { ciphersuite: Ciphersuite, provider: &impl OpenMlsProvider, ) { - let (key_package, _, _) = - crate::key_packages::test_key_packages::key_package(ciphersuite, provider); + let (key_package, _, _) = crate::key_packages::tests::key_package(ciphersuite, provider); let node_in = NodeIn::from(Node::LeafNode(LeafNode::from(key_package))); let tests = [ (vec![], false), diff --git a/openmls/src/treesync/tests_and_kats/tests.rs b/openmls/src/treesync/tests_and_kats/tests.rs index 210a2986a..6cf703665 100644 --- a/openmls/src/treesync/tests_and_kats/tests.rs +++ b/openmls/src/treesync/tests_and_kats/tests.rs @@ -1,6 +1,6 @@ use crate::{ group::{ - tests::utils::{generate_credential_with_key, CredentialWithKeyAndSigner}, + tests_and_kats::utils::{generate_credential_with_key, CredentialWithKeyAndSigner}, MlsGroup, MlsGroupCreateConfig, }, key_packages::KeyPackage, diff --git a/openmls/tests/test_decryption_key_index.rs b/openmls/tests/decryption_key_index.rs similarity index 100% rename from openmls/tests/test_decryption_key_index.rs rename to openmls/tests/decryption_key_index.rs diff --git a/openmls/tests/test_external_commit.rs b/openmls/tests/external_commit.rs similarity index 100% rename from openmls/tests/test_external_commit.rs rename to openmls/tests/external_commit.rs diff --git a/openmls/tests/test_interop_scenarios.rs b/openmls/tests/interop_scenarios.rs similarity index 100% rename from openmls/tests/test_interop_scenarios.rs rename to openmls/tests/interop_scenarios.rs diff --git a/openmls/tests/test_managed_api.rs b/openmls/tests/managed_api.rs similarity index 100% rename from openmls/tests/test_managed_api.rs rename to openmls/tests/managed_api.rs diff --git a/openmls/tests/test_mls_group.rs b/openmls/tests/mls_group.rs similarity index 100% rename from openmls/tests/test_mls_group.rs rename to openmls/tests/mls_group.rs From 6aae27f68cc32b1ea9b5a8a2e1ff00ecea260220 Mon Sep 17 00:00:00 2001 From: Konrad Kohbrok Date: Mon, 22 Jul 2024 17:32:04 +0200 Subject: [PATCH 21/43] Migrate tests to `MlsGroup` and fix a small bug (#1619) --- openmls/src/extensions/tests.rs | 144 +-- openmls/src/framing/mls_auth_content.rs | 4 - openmls/src/framing/tests.rs | 386 ++------ openmls/src/group/core_group/mod.rs | 11 - openmls/src/group/core_group/staged_commit.rs | 47 +- openmls/src/group/mls_group/mod.rs | 9 + .../tests_and_kats/tests/core_group.rs | 732 ++++++++------- .../tests_and_kats/tests/external_init.rs | 23 +- .../tests_and_kats/tests/proposals.rs | 359 +++----- .../group/mls_group/tests_and_kats/utils.rs | 78 +- .../src/group/tests_and_kats/kats/messages.rs | 180 ++-- .../src/group/tests_and_kats/tests/group.rs | 868 ++++++------------ .../src/messages/tests/export_group_info.rs | 10 +- openmls/src/test_utils/frankenstein/commit.rs | 4 +- openmls/src/treesync/diff.rs | 6 +- 15 files changed, 1084 insertions(+), 1777 deletions(-) diff --git a/openmls/src/extensions/tests.rs b/openmls/src/extensions/tests.rs index 6e723f3fb..ab878a778 100644 --- a/openmls/src/extensions/tests.rs +++ b/openmls/src/extensions/tests.rs @@ -13,7 +13,6 @@ use crate::{ messages::proposals::ProposalType, prelude::{Capabilities, RatchetTreeIn}, prelude_test::HpkePublicKey, - schedule::psk::store::ResumptionPskStore, versions::ProtocolVersion, }; use openmls_traits::prelude::*; @@ -39,8 +38,6 @@ fn application_id() { #[openmls_test::openmls_test] fn ratchet_tree_extension() { // Basic group setup. - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); // Create credentials and keys let (alice_credential_with_key, alice_signature_keys) = @@ -57,62 +54,37 @@ fn ratchet_tree_extension() { ); let bob_key_package = bob_key_package_bundle.key_package(); - let config = CoreGroupConfig { - add_ratchet_tree_extension: true, - }; - // === Alice creates a group with the ratchet tree extension === - let mut alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_key.clone(), - ) - .with_config(config) - .build(provider, &alice_signature_keys) - .expect("Error creating group."); - - // === Alice adds Bob === - let bob_add_proposal = alice_group - .create_add_proposal( - framing_parameters, - bob_key_package.clone(), + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .use_ratchet_tree_extension(true) + .build( + provider, &alice_signature_keys, + alice_credential_with_key.clone(), ) - .expect("Could not create proposal."); + .expect("Error creating group."); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + // === Alice adds Bob === + let (_commit, welcome, _group_info_option) = alice_group + .add_members(provider, &alice_signature_keys, &[bob_key_package.clone()]) + .expect("An unexpected error occurred."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) + alice_group.merge_pending_commit(provider).unwrap(); + + let config = MlsGroupJoinConfig::builder() + .use_ratchet_tree_extension(true) .build(); - let create_commit_result = alice_group - .create_commit(params, provider, &alice_signature_keys) - .expect("Error creating commit"); - - alice_group - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging commit"); - - let bob_group = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - None, - bob_key_package_bundle, + + let bob_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &config, + welcome.into_welcome().unwrap(), + Some(alice_group.export_ratchet_tree().into()), ) - .expect("Could not stage group join with ratchet tree extension") - .into_core_group(provider) - .expect("Could not join group with ratchet tree extension"); + .expect("Error staging welcome") + .into_group(provider) + .expect("Error creating group from welcome"); // Make sure the group state is the same assert_eq!( @@ -121,8 +93,8 @@ fn ratchet_tree_extension() { ); // Make sure both groups have set the flag correctly - assert!(alice_group.use_ratchet_tree_extension()); - assert!(bob_group.use_ratchet_tree_extension()); + assert!(alice_group.configuration().use_ratchet_tree_extension); + assert!(bob_group.configuration().use_ratchet_tree_extension); // === Alice creates a group without the ratchet tree extension === @@ -135,61 +107,25 @@ fn ratchet_tree_extension() { ); let bob_key_package = bob_key_package_bundle.key_package(); - let config = CoreGroupConfig { - add_ratchet_tree_extension: false, - }; - - let mut alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_key, - ) - .with_config(config) - .build(provider, &alice_signature_keys) - .expect("Error creating group."); + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .use_ratchet_tree_extension(false) + .build(provider, &alice_signature_keys, alice_credential_with_key) + .expect("Error creating group."); // === Alice adds Bob === - let bob_add_proposal = alice_group - .create_add_proposal( - framing_parameters, - bob_key_package.clone(), - &alice_signature_keys, - ) - .expect("Could not create proposal."); - - alice_group.proposal_store_mut().empty(); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + let (_commit, welcome, _group_info_option) = alice_group + .add_members(provider, &alice_signature_keys, &[bob_key_package.clone()]) + .expect("An unexpected error occurred."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) + let config = MlsGroupJoinConfig::builder() + .use_ratchet_tree_extension(false) .build(); - let create_commit_result = alice_group - .create_commit(params, provider, &alice_signature_keys) - .expect("Error creating commit"); - - alice_group - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging commit"); - - let error = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - None, - bob_key_package_bundle, - provider, - ResumptionPskStore::new(1024), - ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .err(); + + let error = + StagedWelcome::new_from_welcome(provider, &config, welcome.into_welcome().unwrap(), None) + .and_then(|staged_join| staged_join.into_group(provider)) + .err(); // We expect an error because the ratchet tree is missing assert!(matches!( diff --git a/openmls/src/framing/mls_auth_content.rs b/openmls/src/framing/mls_auth_content.rs index 3d7eae0b3..92607e339 100644 --- a/openmls/src/framing/mls_auth_content.rs +++ b/openmls/src/framing/mls_auth_content.rs @@ -290,10 +290,6 @@ impl AuthenticatedContent { pub fn test_signature(&self) -> &Signature { &self.auth.signature } - - pub(super) fn unset_confirmation_tag(&mut self) { - self.auth.confirmation_tag = None; - } } impl SignedStruct for AuthenticatedContent { diff --git a/openmls/src/framing/tests.rs b/openmls/src/framing/tests.rs index 31dc0c8d2..d04a5b658 100644 --- a/openmls/src/framing/tests.rs +++ b/openmls/src/framing/tests.rs @@ -2,6 +2,7 @@ use openmls_basic_credential::SignatureKeyPair; use openmls_traits::prelude::*; use openmls_traits::types::Ciphersuite; +use mls_group::tests_and_kats::utils::{setup_alice_bob_group, setup_client}; use signable::Verifiable; use tls_codec::{Deserialize, Serialize}; @@ -10,9 +11,10 @@ use crate::{ ciphersuite::signable::{Signable, SignatureError}, extensions::Extensions, framing::*, - group::{core_group::proposals::QueuedProposal, errors::*, CreateCommitParams}, - key_packages::{tests::key_package, KeyPackageBundle}, - schedule::psk::{store::ResumptionPskStore, PskSecret}, + group::errors::*, + key_packages::tests::key_package, + prelude::LeafNodeParameters, + schedule::psk::PskSecret, storage::OpenMlsProvider, test_utils::frankenstein::*, tree::{secret_tree::SecretTree, sender_ratchet::SenderRatchetConfiguration}, @@ -379,183 +381,65 @@ fn membership_tag() { fn unknown_sender(ciphersuite: Ciphersuite, provider: &Provider) { let _ = pretty_env_logger::try_init(); - let alice_provider = provider; - let bob_provider = provider; - let charlie_provider = provider; - - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - let configuration = &SenderRatchetConfiguration::default(); - // Define credentials with keys - let (alice_credential, alice_signature_keys) = - test_utils::new_credential(alice_provider, b"Alice", ciphersuite.signature_algorithm()); - let (bob_credential, bob_signature_keys) = - test_utils::new_credential(bob_provider, b"Bob", ciphersuite.signature_algorithm()); - let (charlie_credential, charlie_signature_keys) = test_utils::new_credential( - charlie_provider, - b"Charlie", - ciphersuite.signature_algorithm(), - ); - - // Generate KeyPackages - let bob_key_package_bundle = KeyPackageBundle::generate( - bob_provider, - &bob_signature_keys, - ciphersuite, - bob_credential, - ); - let bob_key_package = bob_key_package_bundle.key_package(); - - let charlie_key_package_bundle = KeyPackageBundle::generate( - charlie_provider, - &charlie_signature_keys, - ciphersuite, - charlie_credential, - ); - let charlie_key_package = charlie_key_package_bundle.key_package(); - - // Alice creates a group - let mut group_alice = CoreGroup::builder( - GroupId::random(alice_provider.rand()), - ciphersuite, - alice_credential, - ) - .build(alice_provider, &alice_signature_keys) - .expect("Error creating group."); - - // Alice adds Bob - let bob_add_proposal = group_alice - .create_add_proposal( - framing_parameters, - bob_key_package.clone(), - &alice_signature_keys, - ) - .expect("Could not create proposal."); - - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - alice_provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + let ( + _charlie_credential, + charlie_key_package_bundle, + _charlie_signature_keys, + _charlie_public_signature_key, + ) = setup_client("Charlie", ciphersuite, provider); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = group_alice - .create_commit(params, alice_provider, &alice_signature_keys) - .expect("Error creating Commit"); - - group_alice - .merge_commit(alice_provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); - - let _group_bob = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(group_alice.public_group().export_ratchet_tree().into()), - bob_key_package_bundle, - bob_provider, - ResumptionPskStore::new(1024), - ) - .and_then(|staged_join| staged_join.into_core_group(bob_provider)) - .expect("Bob: Error creating group from Welcome"); + let (mut alice_group, alice_signature_keys, _bob_group, _bob_signature_keys, _bob_credential) = + setup_alice_bob_group(ciphersuite, provider); // Alice adds Charlie - - let charlie_add_proposal = group_alice - .create_add_proposal( - framing_parameters, - charlie_key_package.clone(), + let (_commit, welcome, _group_info_option) = alice_group + .add_members( + provider, &alice_signature_keys, + &[charlie_key_package_bundle.key_package().clone()], ) - .expect("Could not create proposal."); + .expect("Could not add members."); - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - alice_provider.crypto(), - charlie_add_proposal, - ) - .expect("Could not create staged proposal."), - ); + alice_group + .merge_pending_commit(provider) + .expect("Could not merge commit."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) + let config = MlsGroupJoinConfig::builder() + .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) .build(); - let create_commit_result = group_alice - .create_commit(params, alice_provider, &alice_signature_keys) - .expect("Error creating Commit"); - - group_alice - .merge_commit(alice_provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); - - let mut group_charlie = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(group_alice.public_group().export_ratchet_tree().into()), - charlie_key_package_bundle, - charlie_provider, - ResumptionPskStore::new(1024), - ) - .and_then(|staged_join| staged_join.into_core_group(charlie_provider)) - .expect("Charlie: Error creating group from Welcome"); - - // Alice removes Bob - let bob_remove_proposal = group_alice - .create_remove_proposal( - framing_parameters, - LeafNodeIndex::new(1), - &alice_signature_keys, - ) - .expect("Could not create proposal."); - let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - alice_provider.crypto(), - bob_remove_proposal, + let mut charlie_group = StagedWelcome::new_from_welcome( + provider, + &config, + welcome.into_welcome().unwrap(), + Some(alice_group.export_ratchet_tree().into()), ) - .unwrap(); - - group_alice.proposal_store_mut().empty(); - group_charlie.proposal_store_mut().empty(); + .expect("Could not create group from Welcome") + .into_group(provider) + .expect("Could not create group from Welcome"); - group_alice - .proposal_store_mut() - .add(queued_proposal.clone()); - group_charlie.proposal_store_mut().add(queued_proposal); + // Alice removes Bob + let (commit, _welcome_option, _group_info_option) = alice_group + .remove_members(provider, &alice_signature_keys, &[LeafNodeIndex::new(1)]) + .expect("Could not remove members."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = group_alice - .create_commit(params, alice_provider, &alice_signature_keys) - .expect("Error creating Commit"); + alice_group + .merge_pending_commit(provider) + .expect("Could not merge commit."); - let staged_commit = group_charlie - .read_keys_and_stage_commit(&create_commit_result.commit, &[], alice_provider) - .expect("Charlie: Could not stage Commit"); - group_charlie - .merge_commit(charlie_provider, staged_commit) - .expect("error merging commit"); + let processed_message = charlie_group + .process_message(provider, commit.into_protocol_message().unwrap()) + .expect("Could not process message."); - group_alice - .merge_commit(alice_provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); + let staged_commit = match processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => *staged_commit, + _ => panic!("Wrong message type."), + }; - group_alice.print_ratchet_tree("Alice tree"); - group_charlie.print_ratchet_tree("Charlie tree"); + charlie_group + .merge_staged_commit(provider, staged_commit) + .expect("Could not merge commit."); // Alice sends a message with a sender that is outside of the group // Expected result: SenderError::UnknownSender @@ -563,161 +447,91 @@ fn unknown_sender(ciphersuite: Ciphersuite, provider: LeafNodeIndex::new(0), &[], &[1, 2, 3], - group_alice.context(), + alice_group.export_group_context(), &alice_signature_keys, ) - .expect("Could not create new PublicMessage."); + .expect("Could not create new ApplicationMessage."); let enc_message = PrivateMessage::encrypt_with_different_header( &bogus_sender_message, ciphersuite, - alice_provider, + provider, MlsMessageHeader { - group_id: group_alice.group_id().clone(), - epoch: group_alice.context().epoch(), + group_id: alice_group.group_id().clone(), + epoch: alice_group.epoch(), sender: LeafNodeIndex::new(987543210u32), }, - group_alice.message_secrets_test_mut(), + alice_group.message_secrets_test_mut(), 0, ) .expect("Encryption error"); - let received_message = group_charlie.decrypt_message( - charlie_provider.crypto(), + let received_message = charlie_group.process_message( + provider, ProtocolMessage::from(PrivateMessageIn::from(enc_message)), - configuration, ); + assert_eq!( received_message.unwrap_err(), - ValidationError::UnableToDecrypt(MessageDecryptionError::SecretTreeError( - SecretTreeError::IndexOutOfBounds + ProcessMessageError::ValidationError(ValidationError::UnableToDecrypt( + MessageDecryptionError::SecretTreeError(SecretTreeError::IndexOutOfBounds) )) ); } #[openmls_test::openmls_test] fn confirmation_tag_presence() { - let (framing_parameters, group_alice, alice_signature_keys, group_bob, _, _) = - setup_alice_bob_group(ciphersuite, provider); + let ( + mut alice_group, + alice_signature_keys, + mut bob_group, + _bob_signature_keys, + _bob_credential, + ) = setup_alice_bob_group(ciphersuite, provider); // Alice does an update - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(true) - .build(); - let mut create_commit_result = group_alice - .create_commit(params, provider, &alice_signature_keys) - .expect("Error creating Commit"); - - create_commit_result.commit.unset_confirmation_tag(); - - let err = group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) - .expect_err("No error despite missing confirmation tag."); - - assert_eq!(err, StageCommitError::ConfirmationTagMissing); -} - -pub(crate) fn setup_alice_bob_group( - ciphersuite: Ciphersuite, - provider: &Provider, -) -> ( - FramingParameters, - CoreGroup, - SignatureKeyPair, - CoreGroup, - SignatureKeyPair, - CredentialWithKey, -) { - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - - // Create credentials and keys - let (alice_credential, alice_signature_keys) = - test_utils::new_credential(provider, b"Alice", ciphersuite.signature_algorithm()); - let (bob_credential, bob_signature_keys) = - test_utils::new_credential(provider, b"Bob", ciphersuite.signature_algorithm()); - - // Generate KeyPackages - let bob_key_package_bundle = KeyPackageBundle::generate( - provider, - &bob_signature_keys, - ciphersuite, - bob_credential.clone(), - ); - let bob_key_package = bob_key_package_bundle.key_package(); - - // Alice creates a group - let mut group_alice = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential, - ) - .build(provider, &alice_signature_keys) - .expect("Error creating group."); - - // Alice adds Bob - let bob_add_proposal = group_alice - .create_add_proposal( - framing_parameters, - bob_key_package.clone(), + let (commit, _welcome_option, _group_info_option) = alice_group + .self_update( + provider, &alice_signature_keys, + LeafNodeParameters::default(), ) - .expect("Could not create proposal."); + .expect("Could not update group."); - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + let commit = match commit.body { + MlsMessageBodyOut::PublicMessage(pm) => pm, + _ => panic!("Wrong message type."), + }; - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); + let mut franken_pm = FrankenPublicMessage::from(commit); - let create_commit_result = group_alice - .create_commit(params, provider, &alice_signature_keys) - .expect("Error creating Commit"); + franken_pm.auth.confirmation_tag = None; - let commit = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => commit, - _ => panic!("Wrong content type"), + let serialized_pm = franken_pm + .tls_serialize_detached() + .expect("Could not serialize message."); + + let pm = match PublicMessageIn::tls_deserialize(&mut serialized_pm.as_slice()) { + Ok(pm) => pm, + Err(err) => { + assert!(matches!(err, tls_codec::Error::InvalidVectorLength)); + return; + } }; - assert!(!commit.has_path()); - // Check that the function returned a Welcome message - assert!(create_commit_result.welcome_option.is_some()); - - group_alice - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); - - // We have to create Bob's group so he can process the commit with the - // broken confirmation tag, because Alice can't process her own commit. - let group_bob = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("commit didn't return a welcome as expected"), - Some(group_alice.public_group().export_ratchet_tree().into()), - bob_key_package_bundle, - provider, - ResumptionPskStore::new(1024), - ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .expect("error creating group from welcome"); - ( - framing_parameters, - group_alice, - alice_signature_keys, - group_bob, - bob_signature_keys, - bob_credential, - ) + // Just in case the decoding succeeds, we need to make sure that the + // missing confirmation tag is detected when processing the message. + + let protocol_message: ProtocolMessage = pm.into(); + + let err = bob_group + .process_message(provider, protocol_message) + .expect_err("Could not process message."); + + assert_eq!( + err, + ProcessMessageError::InvalidCommit(StageCommitError::ConfirmationTagMissing) + ); } /// Test divergent protocol versions in KeyPackages diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index a97b82e86..5ac4ac9aa 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -193,13 +193,6 @@ impl CoreGroupBuilder { self } - /// Set the [`Vec`] of the [`CoreGroup`]. - #[cfg(test)] - pub(crate) fn with_psk(mut self, psk_ids: Vec) -> Self { - self.psk_ids = psk_ids; - self - } - /// Set the [`Capabilities`] of the group's creator. pub(crate) fn with_capabilities(mut self, capabilities: Capabilities) -> Self { self.public_group_builder = self.public_group_builder.with_capabilities(capabilities); @@ -1169,10 +1162,6 @@ impl CoreGroup { // Test functions #[cfg(test)] impl CoreGroup { - pub(crate) fn use_ratchet_tree_extension(&self) -> bool { - self.use_ratchet_tree_extension - } - pub(crate) fn set_own_leaf_index(&mut self, own_leaf_index: LeafNodeIndex) { self.own_leaf_index = own_leaf_index; } diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index 21c8112f6..a429c93f9 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -148,19 +148,6 @@ impl CoreGroup { let apply_proposals_values = diff.apply_proposals(&proposal_queue, self.own_leaf_index())?; - // Check if we were removed from the group - if apply_proposals_values.self_removed { - let staged_diff = diff.into_staged_diff(provider.crypto(), ciphersuite)?; - let staged_state = PublicStagedCommitState::new( - staged_diff, - commit.path.as_ref().map(|path| path.leaf_node().clone()), - ); - return Ok(StagedCommit::new( - proposal_queue, - StagedCommitState::PublicState(Box::new(staged_state)), - )); - } - // Determine if Commit has a path let (commit_secret, new_keypairs, new_leaf_keypair_option, update_path_leaf_node) = if let Some(path) = commit.path.clone() { @@ -179,6 +166,20 @@ impl CoreGroup { apply_proposals_values.extensions.clone(), )?; + // Check if we were removed from the group + if apply_proposals_values.self_removed { + // If so, we return here, because we can't decrypt the path + let staged_diff = diff.into_staged_diff(provider.crypto(), ciphersuite)?; + let staged_state = PublicStagedCommitState::new( + staged_diff, + commit.path.as_ref().map(|path| path.leaf_node().clone()), + ); + return Ok(StagedCommit::new( + proposal_queue, + StagedCommitState::PublicState(Box::new(staged_state)), + )); + } + let decryption_keypairs: Vec<&EncryptionKeyPair> = old_epoch_keypairs .iter() .chain(leaf_node_keypairs.iter()) @@ -406,26 +407,6 @@ impl CoreGroup { } } } - - #[cfg(test)] - /// Helper function that reads the decryption keys from the key store - /// (unwrapping the result) and stages the given commit. - pub(crate) fn read_keys_and_stage_commit( - &self, - mls_content: &AuthenticatedContent, - own_leaf_nodes: &[LeafNode], - provider: &impl OpenMlsProvider, - ) -> Result { - let (old_epoch_keypairs, leaf_node_keypairs) = - self.read_decryption_keypairs(provider, own_leaf_nodes)?; - - self.stage_commit( - mls_content, - old_epoch_keypairs, - leaf_node_keypairs, - provider, - ) - } } #[derive(Debug, Serialize, Deserialize)] diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 6a67a72e8..cdc600803 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -1,6 +1,10 @@ //! MLS Group //! //! This module contains [`MlsGroup`] and its submodules. +//! + +#[cfg(test)] +use crate::schedule::message_secrets::MessageSecrets; use super::proposals::{ProposalStore, QueuedProposal}; use crate::{ @@ -465,6 +469,11 @@ impl MlsGroup { self.group.public_group().group_context().tree_hash() } + #[cfg(test)] + pub(crate) fn message_secrets_test_mut(&mut self) -> &mut MessageSecrets { + self.group.message_secrets_test_mut() + } + #[cfg(any(feature = "test-utils", test))] pub fn print_ratchet_tree(&self, message: &str) { self.group.print_ratchet_tree(message) diff --git a/openmls/src/group/mls_group/tests_and_kats/tests/core_group.rs b/openmls/src/group/mls_group/tests_and_kats/tests/core_group.rs index aa9a5757c..405453615 100644 --- a/openmls/src/group/mls_group/tests_and_kats/tests/core_group.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/core_group.rs @@ -1,22 +1,27 @@ -use mls_group::tests_and_kats::utils::{flip_last_byte, setup_alice_bob, setup_client}; +use core::panic; + +use frankenstein::{FrankenFramedContentBody, FrankenPublicMessage}; +use mls_group::tests_and_kats::utils::{ + flip_last_byte, setup_alice_bob, setup_alice_bob_group, setup_client, +}; use tls_codec::Serialize; use crate::{ binary_tree::*, ciphersuite::{signable::Signable, AeadNonce}, credentials::*, - framing::{tests::setup_alice_bob_group, *}, + framing::*, group::{errors::*, *}, key_packages::*, messages::{group_info::GroupInfoTBS, *}, prelude::LeafNodeParameters, - schedule::psk::{store::ResumptionPskStore, ExternalPsk, PreSharedKeyId, Psk}, + schedule::psk::{ExternalPsk, PreSharedKeyId, Psk}, test_utils::*, treesync::errors::ApplyUpdatePathError, }; #[openmls_test::openmls_test] -fn test_failed_groupinfo_decryption( +fn failed_groupinfo_decryption( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) { @@ -114,15 +119,14 @@ fn test_failed_groupinfo_decryption( // Now build the welcome message. let broken_welcome = Welcome::new(ciphersuite, broken_secrets, encrypted_group_info); - let error = StagedCoreWelcome::new_from_welcome( + let error = StagedWelcome::new_from_welcome( + provider, + &MlsGroupJoinConfig::default(), broken_welcome, None, - key_package_bundle, - provider, - ResumptionPskStore::new(1024), ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .expect_err("Creation of core group from a broken Welcome was successful."); + .and_then(|staged_join| staged_join.into_group(provider)) + .expect_err("Creation of mls group from a broken Welcome was successful."); assert!(matches!( error, @@ -133,11 +137,10 @@ fn test_failed_groupinfo_decryption( /// Test what happens if the KEM ciphertext for the receiver in the UpdatePath /// is broken. #[openmls_test::openmls_test] -fn test_update_path() { +fn update_path() { // === Alice creates a group with her and Bob === let ( - framing_parameters, - group_alice, + mut group_alice, _alice_signature_keys, mut group_bob, bob_signature_keys, @@ -157,87 +160,69 @@ fn test_update_path() { ) .unwrap(); - let update_proposal_bob = group_bob - .create_update_proposal(framing_parameters, bob_new_leaf_node, &bob_signature_keys) + let (update_bob, _welcome_option, _group_info_option) = group_bob + .self_update(provider, &bob_signature_keys, LeafNodeParameters::default()) .expect("Could not create proposal."); - group_bob.proposal_store_mut().empty(); - group_bob.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - update_proposal_bob, - ) - .unwrap(), - ); - - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = group_bob - .create_commit(params, provider, &bob_signature_keys) - .expect("An unexpected error occurred."); - // Now we break Alice's HPKE ciphertext in Bob's commit by breaking // apart the commit, manipulating the ciphertexts and the piecing it // back together. - let commit = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => commit, - _ => panic!("Bob created a commit, which does not contain an actual commit."), + let pm = match update_bob.body { + mls_group::MlsMessageBodyOut::PublicMessage(pm) => pm, + _ => panic!("Wrong message type"), }; - let commit = commit.clone(); - - let path = commit.path.expect("An unexpected error occurred."); + let franken_pm = FrankenPublicMessage::from(pm.clone()); + let mut content = franken_pm.content.clone(); + let FrankenFramedContentBody::Commit(ref mut commit) = content.body else { + panic!("Unexpected content type"); + }; + let Some(ref mut path) = commit.path else { + panic!("No path in commit."); + }; - let mut broken_path = path; - // For simplicity, let's just break all the ciphertexts. - broken_path.flip_eps_bytes(); + for node in &mut path.nodes { + for eps in &mut node.encrypted_path_secrets { + let mut eps_ctxt_vec = Vec::::from(eps.ciphertext.clone()); + eps_ctxt_vec[0] ^= 0xff; + eps.ciphertext = eps_ctxt_vec.into(); + } + } - // Now let's create a new commit from out broken update path. - let broken_commit = Commit { - proposals: commit.proposals, - path: Some(broken_path), - }; + // Rebuild the PublicMessage with the new content + let group_context = group_bob.export_group_context().clone(); + let membership_key = group_bob + .group() + .message_secrets() + .membership_key() + .as_slice(); - let mut broken_plaintext = AuthenticatedContent::commit( - framing_parameters, - create_commit_result.commit.sender().clone(), - broken_commit, - group_bob.context(), + let broken_message = FrankenPublicMessage::auth( + provider, + ciphersuite, &bob_signature_keys, - ) - .expect("Could not create plaintext."); - - broken_plaintext.set_confirmation_tag( - create_commit_result - .commit - .confirmation_tag() - .cloned() - .expect("An unexpected error occurred."), + content, + Some(&group_context.into()), + Some(membership_key), + Some(pm.confirmation_tag().unwrap().0.mac_value.clone()), ); - println!( - "Confirmation tag: {:?}", - broken_plaintext.confirmation_tag() - ); + let protocol_message = + ProtocolMessage::PublicMessage(PublicMessage::from(broken_message).into()); - let staged_commit_res = - group_alice.read_keys_and_stage_commit(&broken_plaintext, &[], provider); + let result = group_alice.process_message(provider, protocol_message); assert_eq!( - staged_commit_res.expect_err("Successful processing of a broken commit."), - StageCommitError::UpdatePathError(ApplyUpdatePathError::UnableToDecrypt) + result.expect_err("Successful processing of a broken commit."), + ProcessMessageError::InvalidCommit(StageCommitError::UpdatePathError( + ApplyUpdatePathError::UnableToDecrypt + )) ); } // Test several scenarios when PSKs are used in a group #[openmls_test::openmls_test] -fn test_psks() { +fn psks() { // Basic group setup. - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - let ( alice_credential_with_key, alice_signature_keys, @@ -254,231 +239,144 @@ fn test_psks() { PreSharedKeyId::new(ciphersuite, provider.rand(), Psk::External(external_psk)) .expect("An unexpected error occured."); preshared_key_id.store(provider, secret.as_slice()).unwrap(); - let mut alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_key, - ) - .with_psk(vec![preshared_key_id.clone()]) - .build(provider, &alice_signature_keys) - .expect("Error creating group."); + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(provider, &alice_signature_keys, alice_credential_with_key) + .expect("Error creating group."); // === Alice creates a PSK proposal === log::info!(" >>> Creating psk proposal ..."); - let psk_proposal = alice_group - .create_presharedkey_proposal(framing_parameters, preshared_key_id, &alice_signature_keys) + let (_psk_proposal, _proposal_ref) = alice_group + .propose_external_psk(provider, &alice_signature_keys, preshared_key_id) .expect("Could not create PSK proposal"); - // === Alice adds Bob === - let bob_add_proposal = alice_group - .create_add_proposal( - framing_parameters, - bob_key_package_bundle.key_package().clone(), + // === Alice adds Bob (and commits to PSK proposal) === + let (_commit, welcome, _group_info_option) = alice_group + .add_members( + provider, &alice_signature_keys, + &[bob_key_package_bundle.key_package().clone()], ) - .expect("Could not create proposal"); + .expect("Could not create commit"); - alice_group.proposal_store_mut().empty(); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - psk_proposal, - ) - .unwrap(), - ); + log::info!(" >>> Merging commit ..."); - log::info!(" >>> Creating commit ..."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = alice_group - .create_commit(params, provider, &alice_signature_keys) - .expect("Error creating commit"); + alice_group + .merge_pending_commit(provider) + .expect("Could not merge commit"); - log::info!(" >>> Staging & merging commit ..."); + let ratchet_tree = alice_group.export_ratchet_tree(); - alice_group - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); - let ratchet_tree = alice_group.public_group().export_ratchet_tree(); - - let mut group_bob = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(ratchet_tree.into()), - bob_key_package_bundle, + let mut bob_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &MlsGroupJoinConfig::default(), + welcome.into_welcome().unwrap(), + Some(ratchet_tree.into()), ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .expect("Could not create new group from Welcome"); + .expect("Could not stage welcome") + .into_group(provider) + .expect("Could not create group from welcome"); // === Bob updates and commits === - let mut bob_new_leaf_node = group_bob.own_leaf_node().unwrap().clone(); - bob_new_leaf_node - .update( - ciphersuite, - provider, - &bob_signature_keys, - group_bob.group_id().clone(), - group_bob.own_leaf_index(), - LeafNodeParameters::default(), - ) - .unwrap(); - - let update_proposal_bob = group_bob - .create_update_proposal(framing_parameters, bob_new_leaf_node, &bob_signature_keys) - .expect("Could not create proposal."); - - group_bob.proposal_store_mut().empty(); - group_bob.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - update_proposal_bob, - ) - .unwrap(), - ); - - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let _create_commit_result = group_bob - .create_commit(params, provider, &bob_signature_keys) + let (_commit, _welcome_option, _group_info_option) = bob_group + .self_update(provider, &bob_signature_keys, LeafNodeParameters::default()) .expect("An unexpected error occurred."); } // Test several scenarios when PSKs are used in a group #[openmls_test::openmls_test] -fn test_staged_commit_creation( +fn staged_commit_creation( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) { // Basic group setup. - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - let (alice_credential_with_key, alice_signature_keys, bob_key_package_bundle, _) = setup_alice_bob(ciphersuite, provider); // === Alice creates a group === - let mut alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_key, - ) - .build(provider, &alice_signature_keys) - .expect("Error creating group."); + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(provider, &alice_signature_keys, alice_credential_with_key) + .expect("Error creating group."); // === Alice adds Bob === - let bob_add_proposal = alice_group - .create_add_proposal( - framing_parameters, - bob_key_package_bundle.key_package().clone(), + let (_commit, welcome, _group_info_option) = alice_group + .add_members( + provider, &alice_signature_keys, + &[bob_key_package_bundle.key_package().clone()], ) - .expect("Could not create proposal."); + .expect("Could not create commit"); - alice_group.proposal_store_mut().empty(); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + alice_group + .merge_pending_commit(provider) + .expect("Could not merge commit"); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = alice_group - .create_commit(params, provider, &alice_signature_keys) - .expect("Error creating commit"); + let ratchet_tree = alice_group.export_ratchet_tree(); - // === Alice merges her own commit === - alice_group - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error processing own staged commit"); - - // === Bob joins the group using Alice's tree === - let group_bob = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(alice_group.public_group().export_ratchet_tree().into()), - bob_key_package_bundle, + let bob_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &MlsGroupJoinConfig::default(), + welcome.into_welcome().unwrap(), + Some(ratchet_tree.into()), ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .expect("An unexpected error occurred."); + .expect("Could not stage welcome") + .into_group(provider) + .expect("Could not create group from welcome"); // Let's make sure we end up in the same group state. assert_eq!( - group_bob.export_secret(provider.crypto(), "", b"test", ciphersuite.hash_length()), - alice_group.export_secret(provider.crypto(), "", b"test", ciphersuite.hash_length()) + bob_group.epoch_authenticator(), + alice_group.epoch_authenticator() ); assert_eq!( - group_bob.public_group().export_ratchet_tree(), - alice_group.public_group().export_ratchet_tree() + bob_group.export_ratchet_tree(), + alice_group.export_ratchet_tree() ) } // Test processing of own commits #[openmls_test::openmls_test] -fn test_own_commit_processing( +fn own_commit_processing( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) { // Basic group setup. - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - - // Create credentials and keys let (alice_credential_with_key, alice_signature_keys) = test_utils::new_credential(provider, b"Alice", ciphersuite.signature_algorithm()); // === Alice creates a group === - let alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_key, - ) - .build(provider, &alice_signature_keys) - .expect("Error creating group."); + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(provider, &alice_signature_keys, alice_credential_with_key) + .expect("Error creating group."); // Alice creates a commit - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(true) - .build(); - let create_commit_result = alice_group - .create_commit(params, provider, &alice_signature_keys) - .expect("error creating commit"); + let (commit_out, _welcome_option, _group_info_option) = alice_group + .self_update( + provider, + &alice_signature_keys, + LeafNodeParameters::default(), + ) + .expect("Could not create commit"); + + let commit_in = MlsMessageIn::from(commit_out); // Alice attempts to process her own commit let error = alice_group - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) + .process_message(provider, commit_in.into_protocol_message().unwrap()) .expect_err("no error while processing own commit"); - assert_eq!(error, StageCommitError::OwnCommit); + assert_eq!( + error, + ProcessMessageError::InvalidCommit(StageCommitError::OwnCommit) + ); } #[openmls_test::openmls_test] -fn test_proposal_application_after_self_was_removed( +fn proposal_application_after_self_was_removed( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) { @@ -489,70 +387,48 @@ fn test_proposal_application_after_self_was_removed( // everyone's membership list is as expected. // Basic group setup. - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - let (alice_credential_with_key, _, alice_signature_keys, _pk) = setup_client("Alice", ciphersuite, provider); let (_, bob_kpb, _, _) = setup_client("Bob", ciphersuite, provider); let (_, charlie_kpb, _, _) = setup_client("Charlie", ciphersuite, provider); - let mut alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_key, - ) - .build(provider, &alice_signature_keys) - .expect("Error creating CoreGroup."); - - // Adding Bob - let bob_add_proposal = alice_group - .create_add_proposal( - framing_parameters, - bob_kpb.key_package().clone(), - &alice_signature_keys, - ) - .expect("Could not create proposal"); + let join_group_config = MlsGroupJoinConfig::builder() + .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(); - alice_group.proposal_store_mut().empty(); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(provider, &alice_signature_keys, alice_credential_with_key) + .expect("Error creating group."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let add_commit_result = alice_group - .create_commit(params, provider, &alice_signature_keys) - .expect("Error creating commit"); + let (_commit, welcome, _group_info_option) = alice_group + .add_members( + provider, + &alice_signature_keys, + &[bob_kpb.key_package().clone()], + ) + .expect("Could not create commit"); alice_group - .merge_commit(provider, add_commit_result.staged_commit) - .expect("error merging pending commit"); + .merge_pending_commit(provider) + .expect("Could not merge commit"); - let ratchet_tree = alice_group.public_group().export_ratchet_tree(); + let ratchet_tree = alice_group.export_ratchet_tree(); - let mut bob_group = StagedCoreWelcome::new_from_welcome( - add_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(ratchet_tree.into()), - bob_kpb, + let mut bob_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &join_group_config, + welcome.into_welcome().unwrap(), + Some(ratchet_tree.into()), ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .expect("Error joining group."); + .expect("Could not stage welcome") + .into_group(provider) + .expect("Could not create group from welcome"); // Alice adds Charlie and removes Bob in the same commit. + // She first creates a proposal to remove Bob let bob_index = alice_group - .public_group() .members() .find( |Member { @@ -563,86 +439,274 @@ fn test_proposal_application_after_self_was_removed( ) .expect("Couldn't find Bob in tree.") .index; - let bob_remove_proposal = alice_group - .create_remove_proposal(framing_parameters, bob_index, &alice_signature_keys) + + assert_eq!(bob_index.u32(), 1); + + let (bob_remove_proposal, _bob_remove_proposal_ref) = alice_group + .propose_remove_member(provider, &alice_signature_keys, bob_index) .expect("Could not create proposal"); - let charlie_add_proposal = alice_group - .create_add_proposal( - framing_parameters, - charlie_kpb.key_package().clone(), + // Bob processes the proposal + let processed_message = bob_group + .process_message( + provider, + bob_remove_proposal.into_protocol_message().unwrap(), + ) + .unwrap(); + + let staged_proposal = match processed_message.into_content() { + ProcessedMessageContent::ProposalMessage(proposal) => *proposal, + _ => panic!("Wrong message type"), + }; + + bob_group + .store_pending_proposal(provider.storage(), staged_proposal) + .expect("Error storing proposal"); + + // Alice then commit to the proposal and at the same time adds Charlie + let (commit, welcome, _group_info_option) = alice_group + .add_members( + provider, &alice_signature_keys, + &[charlie_kpb.key_package().clone()], ) - .expect("Could not create proposal"); + .expect("Could not create commit"); - let queued_bob_remove_proposal = QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_remove_proposal, - ) - .unwrap(); + // Alice merges her own commit + alice_group + .merge_pending_commit(provider) + .expect("Could not merge commit"); - let queued_charlie_add_propsal = QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - charlie_add_proposal, + // Bob processes the commit + println!("Bob processes the commit"); + let processed_message = bob_group + .process_message(provider, commit.into_protocol_message().unwrap()) + .unwrap(); + + let staged_commit = match processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(commit) => *commit, + _ => panic!("Wrong message type"), + }; + + bob_group + .merge_staged_commit(provider, staged_commit) + .expect("Error merging commit."); + + // Charlie processes the welcome + println!("Charlie processes the commit"); + let ratchet_tree = alice_group.export_ratchet_tree(); + + let charlie_group = StagedWelcome::new_from_welcome( + provider, + &join_group_config, + welcome.into_welcome().unwrap(), + Some(ratchet_tree.into()), ) - .unwrap(); + .expect("Error staging welcome.") + .into_group(provider) + .expect("Error creating group from welcome."); + + // We can now check that Bob correctly processed his commit and applied the changes + // to his tree after he was removed by comparing membership lists. In + // particular, Bob's list should show that he was removed and Charlie was + // added. + let alice_members = alice_group.members(); + + let bob_members = bob_group.members(); + + let charlie_members = charlie_group.members(); + + for (alice_member, (bob_member, charlie_member)) in + alice_members.zip(bob_members.zip(charlie_members)) + { + // Note that we can't compare encryption keys for Bob because they + // didn't get updated. + assert_eq!(alice_member.index, bob_member.index); + + let alice_id = alice_member.credential.serialized_content(); + let bob_id = bob_member.credential.serialized_content(); + let charlie_id = charlie_member.credential.serialized_content(); + assert_eq!(alice_id, bob_id); + assert_eq!(alice_member.signature_key, bob_member.signature_key); + assert_eq!(charlie_member.index, bob_member.index); + assert_eq!(charlie_id, bob_id); + assert_eq!(charlie_member.signature_key, bob_member.signature_key); + assert_eq!(charlie_member.encryption_key, alice_member.encryption_key); + } + + let mut bob_members = bob_group.members(); - *alice_group.proposal_store_mut() = - ProposalStore::from_queued_proposal(queued_bob_remove_proposal.clone()); - *bob_group.proposal_store_mut() = - ProposalStore::from_queued_proposal(queued_bob_remove_proposal); + let member = bob_members.next().unwrap(); + let bob_next_id = member.credential.serialized_content(); + assert_eq!(bob_next_id, b"Alice"); + let member = bob_members.next().unwrap(); + let bob_next_id = member.credential.serialized_content(); + assert_eq!(bob_next_id, b"Charlie"); +} + +#[openmls_test::openmls_test] +fn proposal_application_after_self_was_removed_ref( + ciphersuite: Ciphersuite, + provider: &impl crate::storage::OpenMlsProvider, +) { + // We're going to test if proposals are still applied, even after a client + // notices that it was removed from a group. We do so by having Alice + // create a group, add Bob and then create a commit where Bob is removed and + // Charlie is added in a single commit (by Alice). We then check if + // everyone's membership list is as expected. + + // Basic group setup. + let (alice_credential_with_key, _, alice_signature_keys, _pk) = + setup_client("Alice", ciphersuite, provider); + let (_, bob_kpb, _, _) = setup_client("Bob", ciphersuite, provider); + let (_, charlie_kpb, _, _) = setup_client("Charlie", ciphersuite, provider); + + let join_group_config = MlsGroupJoinConfig::builder() + .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(); + + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(provider, &alice_signature_keys, alice_credential_with_key) + .expect("Error creating group."); + + let (_commit, welcome, _group_info_option) = alice_group + .add_members( + provider, + &alice_signature_keys, + &[bob_kpb.key_package().clone()], + ) + .expect("Could not create commit"); alice_group - .proposal_store_mut() - .add(queued_charlie_add_propsal.clone()); + .merge_pending_commit(provider) + .expect("Could not merge commit"); + + let ratchet_tree = alice_group.export_ratchet_tree(); + + let mut bob_group = StagedWelcome::new_from_welcome( + provider, + &join_group_config, + welcome.into_welcome().unwrap(), + Some(ratchet_tree.into()), + ) + .expect("Could not stage welcome") + .into_group(provider) + .expect("Could not create group from welcome"); + + // Alice adds Charlie and removes Bob in the same commit. + // She first creates a proposal to remove Bob + let bob_index = alice_group + .members() + .find( + |Member { + index: _, + credential, + .. + }| { credential.serialized_content() == b"Bob" }, + ) + .expect("Couldn't find Bob in tree.") + .index; + + assert_eq!(bob_index.u32(), 1); + + let (bob_remove_proposal, _bob_remove_proposal_ref) = alice_group + .propose_remove_member(provider, &alice_signature_keys, bob_index) + .expect("Could not create proposal"); + + let (charlie_add_proposal, _charlie_add_proposal_ref) = alice_group + .propose_add_member(provider, &alice_signature_keys, charlie_kpb.key_package()) + .expect("Could not create proposal"); + + // Bob processes the proposals + let processed_message = bob_group + .process_message( + provider, + bob_remove_proposal.into_protocol_message().unwrap(), + ) + .unwrap(); + + let staged_proposal = match processed_message.into_content() { + ProcessedMessageContent::ProposalMessage(proposal) => *proposal, + _ => panic!("Wrong message type"), + }; bob_group - .proposal_store_mut() - .add(queued_charlie_add_propsal); + .store_pending_proposal(provider.storage(), staged_proposal) + .expect("Error storing proposal"); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .build(); - let remove_add_commit_result = alice_group - .create_commit(params, provider, &alice_signature_keys) - .expect("Error creating commit"); + let processed_message = bob_group + .process_message( + provider, + charlie_add_proposal.into_protocol_message().unwrap(), + ) + .unwrap(); + + let staged_proposal = match processed_message.into_content() { + ProcessedMessageContent::ProposalMessage(proposal) => *proposal, + _ => panic!("Wrong message type"), + }; - let staged_commit = bob_group - .read_keys_and_stage_commit(&remove_add_commit_result.commit, &[], provider) - .expect("error staging commit"); bob_group - .merge_commit(provider, staged_commit) - .expect("Error merging commit."); + .store_pending_proposal(provider.storage(), staged_proposal) + .expect("Error storing proposal"); + + // Alice then commits to the proposal and at the same time adds Charlie + alice_group.print_ratchet_tree("Alice's tree before commit\n"); + let alice_rt_before = alice_group.export_ratchet_tree(); + let (commit, welcome, _group_info_option) = alice_group + .commit_to_pending_proposals(provider, &alice_signature_keys) + .expect("Could not create commit"); + // Alice merges her own commit alice_group - .merge_commit(provider, remove_add_commit_result.staged_commit) + .merge_pending_commit(provider) + .expect("Could not merge commit"); + alice_group.print_ratchet_tree("Alice's tree after commit\n"); + + // Bob processes the commit + println!("Bob processes the commit"); + bob_group.print_ratchet_tree("Bob's tree before processing the commit\n"); + let bob_rt_before = bob_group.export_ratchet_tree(); + assert_eq!(alice_rt_before, bob_rt_before); + let processed_message = bob_group + .process_message(provider, commit.into_protocol_message().unwrap()) + .unwrap(); + println!("Bob finished processesing the commit"); + + let staged_commit = match processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(commit) => *commit, + _ => panic!("Wrong message type"), + }; + + bob_group + .merge_staged_commit(provider, staged_commit) .expect("Error merging commit."); - let ratchet_tree = alice_group.public_group().export_ratchet_tree(); + // Charlie processes the welcome + println!("Charlie processes the commit"); + let ratchet_tree = alice_group.export_ratchet_tree(); - let charlie_group = StagedCoreWelcome::new_from_welcome( - remove_add_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(ratchet_tree.into()), - charlie_kpb, + let charlie_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &join_group_config, + welcome.unwrap().into_welcome().unwrap(), + Some(ratchet_tree.into()), ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .expect("Error joining group."); + .expect("Error staging welcome.") + .into_group(provider) + .expect("Error creating group from welcome."); // We can now check that Bob correctly processed his and applied the changes // to his tree after he was removed by comparing membership lists. In // particular, Bob's list should show that he was removed and Charlie was // added. - let alice_members = alice_group.public_group().members(); + let alice_members = alice_group.members(); - let bob_members = bob_group.public_group().members(); + let bob_members = bob_group.members(); - let charlie_members = charlie_group.public_group().members(); + let charlie_members = charlie_group.members(); for (alice_member, (bob_member, charlie_member)) in alice_members.zip(bob_members.zip(charlie_members)) @@ -662,7 +726,7 @@ fn test_proposal_application_after_self_was_removed( assert_eq!(charlie_member.encryption_key, alice_member.encryption_key); } - let mut bob_members = bob_group.public_group().members(); + let mut bob_members = bob_group.members(); let member = bob_members.next().unwrap(); let bob_next_id = member.credential.serialized_content(); diff --git a/openmls/src/group/mls_group/tests_and_kats/tests/external_init.rs b/openmls/src/group/mls_group/tests_and_kats/tests/external_init.rs index 171ec90f0..5b634f57b 100644 --- a/openmls/src/group/mls_group/tests_and_kats/tests/external_init.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/external_init.rs @@ -1,23 +1,17 @@ use crate::{ - framing::tests::setup_alice_bob_group, group::{ - errors::ExternalCommitError, mls_group::tests_and_kats::utils::setup_client, - public_group::errors::CreationFromExternalError, MlsGroup, MlsGroupJoinConfig, + errors::ExternalCommitError, + mls_group::tests_and_kats::utils::{setup_alice_bob_group, setup_client}, + public_group::errors::CreationFromExternalError, + MlsGroup, MlsGroupJoinConfig, }, storage::OpenMlsProvider, }; -use openmls_traits::prelude::*; #[openmls_test::openmls_test] fn test_external_init_broken_signature() { - let ( - _framing_parameters, - group_alice, - alice_signer, - _group_bob, - _bob_signer, - _bob_credential_with_key, - ) = setup_alice_bob_group(ciphersuite, provider); + let (group_alice, alice_signer, _group_bob, _bob_signer, _bob_credential_with_key) = + setup_alice_bob_group(ciphersuite, provider); // Now set up charly and try to init externally. let (charlie_credential, _charlie_kpb, charlie_signer, _charlie_pk) = @@ -25,9 +19,10 @@ fn test_external_init_broken_signature() { let verifiable_group_info = { let mut verifiable_group_info = group_alice - .export_group_info(provider.crypto(), &alice_signer, true) + .export_group_info(provider, &alice_signer, true) .unwrap() - .into_verifiable_group_info(); + .into_verifiable_group_info() + .unwrap(); verifiable_group_info.break_signature(); verifiable_group_info }; diff --git a/openmls/src/group/mls_group/tests_and_kats/tests/proposals.rs b/openmls/src/group/mls_group/tests_and_kats/tests/proposals.rs index dddcba815..6b28abad5 100644 --- a/openmls/src/group/mls_group/tests_and_kats/tests/proposals.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/proposals.rs @@ -8,13 +8,15 @@ use crate::{ }, group::{ errors::*, - mls_group::tests_and_kats::utils::setup_client, + mls_group::{ + tests_and_kats::utils::{setup_alice_bob_group, setup_client}, + ProcessedMessageContent, + }, proposals::{ProposalQueue, ProposalStore, QueuedProposal}, - CoreGroup, CreateCommitParams, GroupContext, GroupId, StagedCoreWelcome, + GroupContext, GroupId, MlsGroup, MlsGroupJoinConfig, StagedWelcome, }, key_packages::{KeyPackageBundle, KeyPackageIn}, messages::proposals::{AddProposal, Proposal, ProposalOrRef, ProposalType}, - schedule::psk::store::ResumptionPskStore, test_utils::*, versions::ProtocolVersion, }; @@ -278,14 +280,11 @@ fn proposal_queue_order() { } #[openmls_test::openmls_test] -fn test_required_extension_key_package_mismatch( +fn required_extension_key_package_mismatch( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) { // Basic group setup. - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - let (alice_credential, _, alice_signer, _alice_pk) = setup_client("Alice", ciphersuite, provider); let (_bob_credential_with_key, bob_key_package_bundle, _, _) = @@ -300,42 +299,32 @@ fn test_required_extension_key_package_mismatch( let required_capabilities = RequiredCapabilitiesExtension::new(extensions, proposals, credentials); - let alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential, - ) - .with_group_context_extensions(Extensions::single(Extension::RequiredCapabilities( - required_capabilities, - ))) - .expect("error adding group context extensions") - .build(provider, &alice_signer) - .expect("Error creating CoreGroup."); - - let e = alice_group - .create_add_proposal( - framing_parameters, - bob_key_package.clone(), - &alice_signer, - ) + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_group_context_extensions(Extensions::single(Extension::RequiredCapabilities( + required_capabilities, + ))) + .unwrap() + .build(provider, &alice_signer, alice_credential) + .expect("Error creating CoreGroup."); + + let e = alice_group.propose_add_member(provider, &alice_signer, bob_key_package) .expect_err("Proposal was created even though the key package didn't support the required extensions."); + assert_eq!( e, - CreateAddProposalError::LeafNodeValidation( + ProposeAddMemberError::LeafNodeValidation( crate::treesync::errors::LeafNodeValidationError::UnsupportedExtensions ) ); } #[openmls_test::openmls_test] -fn test_group_context_extensions( +fn group_context_extensions( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) { // Basic group setup. - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - let (alice_credential, _, alice_signer, _alice_pk) = setup_client("Alice", ciphersuite, provider); let (_bob_credential_with_key, bob_key_package_bundle, _, _) = @@ -355,75 +344,43 @@ fn test_group_context_extensions( let required_capabilities = RequiredCapabilitiesExtension::new(extensions, proposals, credentials); - let mut alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential, - ) - .with_group_context_extensions(Extensions::single(Extension::RequiredCapabilities( - required_capabilities, - ))) - .unwrap() - .build(provider, &alice_signer) - .expect("Error creating CoreGroup."); - - let bob_add_proposal = alice_group - .create_add_proposal(framing_parameters, bob_key_package.clone(), &alice_signer) - .expect("Could not create proposal"); - - alice_group.proposal_store_mut().empty(); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_group_context_extensions(Extensions::single(Extension::RequiredCapabilities( + required_capabilities, + ))) + .unwrap() + .build(provider, &alice_signer, alice_credential) + .expect("Error creating MlsGroup."); - log::info!(" >>> Creating commit ..."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = alice_group - .create_commit(params, provider, &alice_signer) - .expect("Error creating commit"); + let (_commit, welcome, _group_info_option) = alice_group + .add_members(provider, &alice_signer, &[bob_key_package.clone()]) + .expect("Error adding members."); - log::info!(" >>> Staging & merging commit ..."); + alice_group.merge_pending_commit(provider).unwrap(); - alice_group - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own staged commit"); - let ratchet_tree = alice_group.public_group().export_ratchet_tree(); + let ratchet_tree = alice_group.export_ratchet_tree(); // Make sure that Bob can join the group with the required extension in place // and Bob's key package supporting them. - let _bob_group = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(ratchet_tree.into()), - bob_key_package_bundle, + let _bob_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &MlsGroupJoinConfig::default(), + welcome.into_welcome().unwrap(), + Some(ratchet_tree.into()), ) - .and_then(|staged_join| staged_join.into_core_group(provider)) .expect("Error joining group."); } #[openmls_test::openmls_test] -fn test_group_context_extension_proposal_fails( +fn group_context_extension_proposal_fails( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) { // Basic group setup. - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - let (alice_credential, _, alice_signer, _alice_pk) = setup_client("Alice", ciphersuite, provider); - let (_bob_credential_with_key, bob_key_package_bundle, _, _) = + let (_bob_credential_with_key, bob_key_package_bundle, _bob_signer, _) = setup_client("Bob", ciphersuite, provider); let bob_key_package = bob_key_package_bundle.key_package(); @@ -438,147 +395,62 @@ fn test_group_context_extension_proposal_fails( let credentials = &[CredentialType::Basic]; let required_capabilities = RequiredCapabilitiesExtension::new(&[], proposals, credentials); - let mut alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential, - ) - .with_group_context_extensions(Extensions::single(Extension::RequiredCapabilities( - required_capabilities, - ))) - .unwrap() - .build(provider, &alice_signer) - .expect("Error creating CoreGroup."); + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_group_context_extensions(Extensions::single(Extension::RequiredCapabilities( + required_capabilities, + ))) + .unwrap() + .build(provider, &alice_signer, alice_credential) + .expect("Error creating CoreGroup."); // Adding Bob - let bob_add_proposal = alice_group - .create_add_proposal(framing_parameters, bob_key_package.clone(), &alice_signer) - .expect("Could not create proposal"); + let (_commit, welcome, _group_info_option) = alice_group + .add_members(provider, &alice_signer, &[bob_key_package.clone()]) + .expect("Error adding members."); - alice_group.proposal_store_mut().empty(); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + alice_group.merge_pending_commit(provider).unwrap(); - log::info!(" >>> Creating commit ..."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = alice_group - .create_commit(params, provider, &alice_signer) - .expect("Error creating commit"); - - log::info!(" >>> Staging & merging commit ..."); - - alice_group - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); - let ratchet_tree = alice_group.public_group().export_ratchet_tree(); - - let _bob_group = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(ratchet_tree.into()), - bob_key_package_bundle, + let ratchet_tree = alice_group.export_ratchet_tree(); + + let _bob_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &MlsGroupJoinConfig::default(), + welcome.into_welcome().unwrap(), + Some(ratchet_tree.into()), ) - .and_then(|staged_join| staged_join.into_core_group(provider)) + .and_then(|staged_join| staged_join.into_group(provider)) .expect("Error joining group."); // TODO: openmls/openmls#1130 re-enable // // Now Bob wants the ApplicationId extension to be required. // // This should fail because Alice doesn't support it. - // let e = bob_group - // .create_group_context_ext_proposal( - // framing_parameters, - // &alice_credential_bundle, - // &[required_application_id], - // provider, - // ) - // .expect_err("Bob was able to create a gce proposal for an extension not supported by all other parties."); - // assert_eq!( - // e, - // CreateGroupContextExtProposalError::TreeSyncError( - // crate::treesync::errors::TreeSyncError::UnsupportedExtension - // ) - // ); + //let unsupported_extensions = Extensions::single(Extension::Unknown( + // 0xff00, + // UnknownExtension(vec![0, 1, 2, 3]), + //)); + //let e = bob_group + // .propose_group_context_extensions(provider, unsupported_extensions, &bob_signer) + // .expect_err("Bob was able to propose an extension not supported by all other parties."); + // + //assert_eq!( + // e, + // ProposalError::CreateGroupContextExtProposalError( + // CreateGroupContextExtProposalError::KeyPackageExtensionSupport( + // KeyPackageExtensionSupportError::UnsupportedExtension + // ) + // ) + //); } #[openmls_test::openmls_test] -fn test_group_context_extension_proposal( +fn group_context_extension_proposal( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) { // Basic group setup. - let group_aad = b"Alice's test group"; - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - - let (alice_credential, _, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, provider); - let (_bob_credential_with_key, bob_key_package_bundle, _, _) = - setup_client("Bob", ciphersuite, provider); - - let bob_key_package = bob_key_package_bundle.key_package(); - - let mut alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential, - ) - .build(provider, &alice_signer) - .expect("Error creating CoreGroup."); - - // Adding Bob - let bob_add_proposal = alice_group - .create_add_proposal(framing_parameters, bob_key_package.clone(), &alice_signer) - .expect("Could not create proposal"); - - alice_group.proposal_store_mut().empty(); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); - - log::info!(" >>> Creating commit ..."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_results = alice_group - .create_commit(params, provider, &alice_signer) - .expect("Error creating commit"); - - log::info!(" >>> Staging & merging commit ..."); - - alice_group - .merge_commit(provider, create_commit_results.staged_commit) - .expect("error merging pending commit"); - - let ratchet_tree = alice_group.public_group().export_ratchet_tree(); - - let mut bob_group = StagedCoreWelcome::new_from_welcome( - create_commit_results - .welcome_option - .expect("An unexpected error occurred."), - Some(ratchet_tree.into()), - bob_key_package_bundle, - provider, - ResumptionPskStore::new(1024), - ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .expect("Error joining group."); + let (mut alice_group, alice_signer, mut bob_group, bob_signer, _bob_credential) = + setup_alice_bob_group(ciphersuite, provider); // Alice adds a required capability. let required_application_id = @@ -587,56 +459,47 @@ fn test_group_context_extension_proposal( &[], &[CredentialType::Basic], )); - let gce_proposal = alice_group - .create_group_context_ext_proposal::( - framing_parameters, + let (gce_proposal, _) = alice_group + .propose_group_context_extensions( + provider, Extensions::single(required_application_id), &alice_signer, ) - .expect("Error creating gce proposal."); + .expect("Error proposing gce."); + + let processed_message = bob_group + .process_message(provider, gce_proposal.into_protocol_message().unwrap()) + .expect("Error processing gce proposal."); + + match processed_message.into_content() { + ProcessedMessageContent::ProposalMessage(queued_proposal) => { + bob_group + .store_pending_proposal(provider.storage(), *queued_proposal) + .unwrap(); + } + _ => panic!("Expected a StagedCommitMessage."), + }; - let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - gce_proposal, - ) - .unwrap(); + // Bob commits the proposal. + let (commit, _, _) = bob_group + .commit_to_pending_proposals(provider, &bob_signer) + .unwrap(); + + bob_group.merge_pending_commit(provider).unwrap(); - alice_group.proposal_store_mut().empty(); - bob_group.proposal_store_mut().empty(); - alice_group - .proposal_store_mut() - .add(queued_proposal.clone()); - bob_group.proposal_store_mut().add(queued_proposal); - - log::info!(" >>> Creating commit ..."); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = alice_group - .create_commit(params, provider, &alice_signer) - .expect("Error creating commit"); - - log::info!(" >>> Staging & merging commit ..."); - - let staged_commit = bob_group - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) - .expect("error staging commit"); - bob_group - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - - alice_group - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); + let processed_message = alice_group + .process_message(provider, commit.into_protocol_message().unwrap()) + .expect("Error processing commit."); + + match processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(commit) => { + alice_group.merge_staged_commit(provider, *commit).unwrap(); + } + _ => panic!("Expected a StagedCommitMessage."), + }; assert_eq!( - alice_group - .export_secret(provider.crypto(), "label", b"gce test", 32) - .expect("Error exporting secret."), - bob_group - .export_secret(provider.crypto(), "label", b"gce test", 32) - .expect("Error exporting secret.") + alice_group.epoch_authenticator(), + bob_group.epoch_authenticator() ) } diff --git a/openmls/src/group/mls_group/tests_and_kats/utils.rs b/openmls/src/group/mls_group/tests_and_kats/utils.rs index 83c169417..cf824f2be 100644 --- a/openmls/src/group/mls_group/tests_and_kats/utils.rs +++ b/openmls/src/group/mls_group/tests_and_kats/utils.rs @@ -9,7 +9,7 @@ pub(crate) fn setup_alice_group( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) -> ( - CoreGroup, + MlsGroup, CredentialWithKey, SignatureKeyPair, OpenMlsSignaturePublicKey, @@ -24,13 +24,14 @@ pub(crate) fn setup_alice_group( .unwrap(); // Alice creates a group - let group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_key.clone(), - ) - .build(provider, &alice_signature_keys) - .expect("Error creating group."); + let group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .build( + provider, + &alice_signature_keys, + alice_credential_with_key.clone(), + ) + .expect("Error creating group."); (group, alice_credential_with_key, alice_signature_keys, pk) } @@ -98,3 +99,64 @@ pub(crate) fn setup_client( ); (credential_with_key, key_package_bundle, signature_keys, pk) } + +pub(crate) fn setup_alice_bob_group( + ciphersuite: Ciphersuite, + provider: &Provider, +) -> ( + MlsGroup, + SignatureKeyPair, + MlsGroup, + SignatureKeyPair, + CredentialWithKey, +) { + // Create credentials and keys + let (alice_credential, alice_signature_keys) = + test_utils::new_credential(provider, b"Alice", ciphersuite.signature_algorithm()); + let (bob_credential, bob_signature_keys) = + test_utils::new_credential(provider, b"Bob", ciphersuite.signature_algorithm()); + + // Generate KeyPackages + let bob_key_package_bundle = KeyPackageBundle::generate( + provider, + &bob_signature_keys, + ciphersuite, + bob_credential.clone(), + ); + let bob_key_package = bob_key_package_bundle.key_package(); + + // Alice creates a group + let mut group_alice = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(provider, &alice_signature_keys, alice_credential.clone()) + .expect("Error creating group."); + + // Alice adds Bob + let (_commit, welcome, _group_info_option) = group_alice + .add_members(provider, &alice_signature_keys, &[bob_key_package.clone()]) + .expect("Could not create proposal."); + + group_alice + .merge_pending_commit(provider) + .expect("error merging pending commit"); + + let group_bob = StagedWelcome::new_from_welcome( + provider, + &MlsGroupJoinConfig::builder() + .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(), + welcome.into_welcome().unwrap(), + Some(group_alice.export_ratchet_tree().into()), + ) + .and_then(|staged_join| staged_join.into_group(provider)) + .expect("error creating group from welcome"); + + ( + group_alice, + alice_signature_keys, + group_bob, + bob_signature_keys, + bob_credential, + ) +} diff --git a/openmls/src/group/tests_and_kats/kats/messages.rs b/openmls/src/group/tests_and_kats/kats/messages.rs index d64f6efd0..fcf207437 100644 --- a/openmls/src/group/tests_and_kats/kats/messages.rs +++ b/openmls/src/group/tests_and_kats/kats/messages.rs @@ -4,15 +4,14 @@ //! See //! for more description on the test vectors. +use frankenstein::{FrankenFramedContentBody, FrankenPublicMessage}; use openmls_traits::{random::OpenMlsRand, types::SignatureScheme, OpenMlsProvider}; -use rand::{rngs::OsRng, RngCore}; use serde::{self, Deserialize, Serialize}; use thiserror::Error; use tls_codec::{Deserialize as TlsDeserialize, Serialize as TlsSerialize}; use crate::{ binary_tree::array_representation::LeafNodeIndex, - ciphersuite::Mac, framing::*, group::{ tests_and_kats::utils::{generate_credential_with_key, generate_key_package, randombytes}, @@ -25,11 +24,10 @@ use crate::{ *, }, prelude::{CredentialType, LeafNode}, - schedule::psk::{store::ResumptionPskStore, *}, + schedule::psk::*, test_utils::*, - tree::sender_ratchet::*, treesync::node::{ - leaf_node::{Capabilities, TreeInfoTbs}, + leaf_node::{Capabilities, TreeInfoTbs, TreePosition}, NodeIn, }, versions::ProtocolVersion, @@ -135,22 +133,24 @@ pub fn generate_test_vector(ciphersuite: Ciphersuite) -> MessagesTestVector { ); // Let's create a group - let mut alice_group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_key_and_signer - .credential_with_key - .clone(), - ) - .with_max_past_epoch_secrets(2) - .build(&provider, &alice_credential_with_key_and_signer.signer) - .unwrap(); + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .max_past_epochs(2) + .with_wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build( + &provider, + &alice_credential_with_key_and_signer.signer, + alice_credential_with_key_and_signer + .credential_with_key + .clone(), + ) + .unwrap(); - let alice_ratchet_tree = alice_group.public_group().export_ratchet_tree(); + let alice_ratchet_tree = alice_group.export_ratchet_tree(); let alice_group_info = alice_group .export_group_info( - provider.rand(), + &provider, &alice_credential_with_key_and_signer.signer, true, ) @@ -176,7 +176,10 @@ pub fn generate_test_vector(ciphersuite: Ciphersuite) -> MessagesTestVector { .clone(), capabilities, Extensions::default(), - TreeInfoTbs::Update(alice_group.own_tree_position()), + TreeInfoTbs::Update(TreePosition::new( + alice_group.group_id().clone(), + alice_group.own_leaf_index(), + )), &provider, &alice_credential_with_key_and_signer.signer.clone(), ) @@ -241,113 +244,51 @@ pub fn generate_test_vector(ciphersuite: Ciphersuite) -> MessagesTestVector { let group_context_extensions_proposal = GroupContextExtensionProposal::new(Extensions::default()); - let framing_parameters = FramingParameters::new(b"aad", WireFormat::PrivateMessage); - - let add_proposal_content = alice_group - .create_add_proposal( - framing_parameters, - bob_key_package_bundle.key_package.clone(), - &alice_credential_with_key_and_signer.signer.clone(), - ) - .unwrap(); - - alice_group.proposal_store_mut().empty(); - alice_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - add_proposal_content.clone(), - ) - .unwrap(), - ); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .build(); - let create_commit_result = alice_group - .create_commit( - params, + let (proposal_pt, _) = alice_group + .propose_add_member( &provider, &alice_credential_with_key_and_signer.signer, + bob_key_package_bundle.key_package(), ) .unwrap(); - alice_group - .merge_staged_commit(&provider, create_commit_result.staged_commit) + + let (commit_pt, welcome, _) = alice_group + .commit_to_pending_proposals(&provider, &alice_credential_with_key_and_signer.signer) .unwrap(); - let commit = if let FramedContentBody::Commit(commit) = create_commit_result.commit.content() { - commit.clone() - } else { - panic!("Wrong content of MLS plaintext"); + let welcome = welcome.unwrap(); + + alice_group.merge_pending_commit(&provider).unwrap(); + + let commit_pm = match commit_pt.clone().body { + MlsMessageBodyOut::PublicMessage(pm) => pm, + _ => panic!("Wrong message type."), }; - let alice_welcome = create_commit_result.welcome_option.unwrap(); + let franken_commit_pm = FrankenPublicMessage::from(commit_pm.clone()); - let mut receiver_group = StagedCoreWelcome::new_from_welcome( - alice_welcome.clone(), - Some(alice_group.public_group().export_ratchet_tree().into()), - bob_key_package_bundle, - &provider, - ResumptionPskStore::new(1024), - ) - .and_then(|staged_join| staged_join.into_core_group(&provider)) - .expect("Error creating receiver group."); - - // Clone the secret tree to bypass FS restrictions - let private_message_application = alice_group - .create_application_message( - b"aad", - b"msg", - random_u8() as usize, + let FrankenFramedContentBody::Commit(commit) = franken_commit_pm.content.body else { + panic!("Wrong content of MLS plaintext"); + }; + + let application_ctxt = alice_group + .create_message( &provider, &alice_credential_with_key_and_signer.signer, + b"test", ) .unwrap(); - // Replace the secret tree - let verifiable_public_message_application: VerifiableAuthenticatedContentIn = receiver_group - .decrypt_message( - provider.crypto(), - ProtocolMessage::from(PrivateMessageIn::from(private_message_application)), - &SenderRatchetConfiguration::default(), - ) - .unwrap() - .verifiable_content() - .to_owned(); - let mls_content_application: AuthenticatedContent = - AuthenticatedContentIn::from(verifiable_public_message_application).into(); - - let encryption_target = match random_u32() % 3 { - 0 => create_commit_result.commit.clone(), - 1 => add_proposal_content.clone(), - 2 => mls_content_application.clone(), - _ => panic!("Modulo 3 of u32 shouldn't give us anything larger than 2"), - }; - let mut mac_value = vec![0u8; alice_group.ciphersuite().hash_length()]; - OsRng.fill_bytes(&mut mac_value); - let random_membership_tag = MembershipTag(Mac { - mac_value: mac_value.into(), - }); - - let mut application_pt: PublicMessage = mls_content_application.into(); - application_pt.set_membership_tag_test(random_membership_tag.clone()); - - let mut proposal_pt: PublicMessage = add_proposal_content.into(); - proposal_pt.set_membership_tag_test(random_membership_tag.clone()); - - let mut commit_pt: PublicMessage = create_commit_result.commit.into(); - commit_pt.set_membership_tag_test(random_membership_tag); - - let private_message = alice_group - .encrypt(encryption_target, random_u8() as usize, &provider) - .unwrap(); + // Craft a fake public application message from the valid commit. + let mut application_pt = FrankenPublicMessage::from(commit_pm); + application_pt.content.body = FrankenFramedContentBody::Application(randombytes(32).into()); + application_pt.auth.confirmation_tag = None; + let application_pt = PublicMessage::from(application_pt); + let application_message = MlsMessageOut::from(application_pt); MessagesTestVector { - mls_welcome: MlsMessageOut::from_welcome(alice_welcome, ProtocolVersion::Mls10) - .tls_serialize_detached() - .unwrap(), - mls_group_info: MlsMessageOut::from(alice_group_info) - .tls_serialize_detached() - .unwrap(), + mls_welcome: welcome.tls_serialize_detached().unwrap(), + mls_group_info: alice_group_info.tls_serialize_detached().unwrap(), mls_key_package: MlsMessageOut::from(alice_key_package) .tls_serialize_detached() .unwrap(), @@ -367,21 +308,10 @@ pub fn generate_test_vector(ciphersuite: Ciphersuite) -> MessagesTestVector { commit: commit.tls_serialize_detached().unwrap(), - public_message_application: MlsMessageOut::from(application_pt) - .tls_serialize_detached() - .unwrap(), - public_message_proposal: MlsMessageOut::from(proposal_pt) - .tls_serialize_detached() - .unwrap(), - public_message_commit: MlsMessageOut::from(commit_pt) - .tls_serialize_detached() - .unwrap(), - private_message: MlsMessageOut::from_private_message( - private_message, - ProtocolVersion::Mls10, - ) - .tls_serialize_detached() - .unwrap(), + public_message_application: application_message.tls_serialize_detached().unwrap(), + public_message_proposal: proposal_pt.tls_serialize_detached().unwrap(), + public_message_commit: commit_pt.tls_serialize_detached().unwrap(), + private_message: application_ctxt.tls_serialize_detached().unwrap(), } } @@ -569,8 +499,6 @@ pub fn run_test_vector(tv: MessagesTestVector) -> Result<(), EncodingMismatch> { // MlsPlaintextApplication let tv_public_message_application = tv.public_message_application; - // // Fake the wire format so we can deserialize - //tv_public_message_application[0] = WireFormat::PublicMessage as u8; let my_public_message_application = MlsMessageIn::tls_deserialize_exact(&tv_public_message_application) .unwrap() diff --git a/openmls/src/group/tests_and_kats/tests/group.rs b/openmls/src/group/tests_and_kats/tests/group.rs index f20d468fa..774711512 100644 --- a/openmls/src/group/tests_and_kats/tests/group.rs +++ b/openmls/src/group/tests_and_kats/tests/group.rs @@ -1,15 +1,12 @@ use crate::{ - ciphersuite::signable::Verifiable, framing::*, group::{tests_and_kats::utils::generate_key_package, *}, - key_packages::*, schedule::psk::store::ResumptionPskStore, test_utils::*, - tree::sender_ratchet::SenderRatchetConfiguration, *, }; -use framing::mls_content_in::FramedContentBodyIn; use group::tests_and_kats::utils::generate_credential_with_key; +use mls_group::tests_and_kats::utils::{setup_alice_bob_group, setup_client}; use treesync::LeafNodeParameters; #[openmls_test::openmls_test] @@ -200,66 +197,13 @@ fn create_commit_optional_path( #[openmls_test::openmls_test] fn basic_group_setup() { - let group_aad = b"Alice's test group"; - // Framing parameters - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - - // Define credentials with keys - let alice_credential_with_keys = generate_credential_with_key( - b"Alice".to_vec(), - ciphersuite.signature_algorithm(), - provider, - ); - let bob_credential_with_keys = - generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), provider); - - // Generate KeyPackages - let bob_key_package = generate_key_package( - ciphersuite, - Extensions::empty(), - provider, - bob_credential_with_keys, - ); - - // Alice creates a group - let mut group_alice = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_keys.credential_with_key, - ) - .build(provider, &alice_credential_with_keys.signer) - .expect("Error creating CoreGroup."); - - // Alice adds Bob - let bob_add_proposal = group_alice - .create_add_proposal( - framing_parameters, - bob_key_package.key_package().clone(), - &alice_credential_with_keys.signer, - ) - .expect("Could not create proposal."); - - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + let (mut alice_group, alice_signer, _, _, _) = setup_alice_bob_group(ciphersuite, provider); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .build(); - let _commit = match group_alice.create_commit( - params, /* PSK fetcher */ - provider, - &alice_credential_with_keys.signer, - ) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + let _result = + match alice_group.self_update(provider, &alice_signer, LeafNodeParameters::default()) { + Ok(c) => c, + Err(e) => panic!("Error creating commit: {e:?}"), + }; } /// This test simulates various group operations like Add, Update, Remove in a @@ -276,656 +220,376 @@ fn basic_group_setup() { /// - Charlie removes Bob #[openmls_test::openmls_test] fn group_operations() { - let group_aad = b"Alice's test group"; - // Framing parameters - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - let sender_ratchet_configuration = SenderRatchetConfiguration::default(); - - // Define credentials with keys - let alice_credential_with_keys = generate_credential_with_key( - b"Alice".to_vec(), - ciphersuite.signature_algorithm(), - provider, - ); - let bob_credential_with_keys = - generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), provider); - - // Generate KeyPackages - let bob_key_package_bundle = KeyPackageBundle::generate( - provider, - &bob_credential_with_keys.signer, - ciphersuite, - bob_credential_with_keys.credential_with_key.clone(), - ); - let bob_key_package = bob_key_package_bundle.key_package(); - - // === Alice creates a group === - let mut group_alice = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_keys.credential_with_key.clone(), - ) - .build(provider, &alice_credential_with_keys.signer) - .expect("Error creating CoreGroup."); - - // === Alice adds Bob === - let bob_add_proposal = group_alice - .create_add_proposal( - framing_parameters, - bob_key_package.clone(), - &alice_credential_with_keys.signer, - ) - .expect("Could not create proposal."); - - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); - - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = group_alice - .create_commit(params, provider, &alice_credential_with_keys.signer) - .expect("Error creating commit"); - let commit = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => commit, - _ => panic!("Wrong content type"), - }; - assert!(!commit.has_path()); - // Check that the function returned a Welcome message - assert!(create_commit_result.welcome_option.is_some()); - - group_alice - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own commits"); - let ratchet_tree = group_alice.public_group().export_ratchet_tree(); - - let mut group_bob = match StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(ratchet_tree.into()), - bob_key_package_bundle, - provider, - ResumptionPskStore::new(1024), - ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - { - Ok(group) => group, - Err(e) => panic!("Error creating group from Welcome: {e:?}"), - }; + // Create group with alice and bob + let (mut alice_group, alice_signer, mut bob_group, bob_signer, _) = + setup_alice_bob_group(ciphersuite, provider); // Make sure that both groups have the same public tree assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_bob.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() ); // Make sure that both groups have the same group context - if group_alice.context() != group_bob.context() { + if alice_group.export_group_context() != bob_group.export_group_context() { panic!("Different group contexts"); } // === Alice sends a message to Bob === let message_alice = [1, 2, 3]; - let mls_ciphertext_alice: PrivateMessageIn = group_alice - .create_application_message( - &[], - &message_alice, - 0, - provider, - &alice_credential_with_keys.signer, - ) - .expect("An unexpected error occurred.") - .into(); - - let verifiable_plaintext = group_bob - .decrypt_message( - provider.crypto(), - mls_ciphertext_alice.into(), - &sender_ratchet_configuration, - ) - .expect("An unexpected error occurred.") - .verifiable_content() - .to_owned(); - - let mls_plaintext_bob: AuthenticatedContentIn = verifiable_plaintext - .verify( - provider.crypto(), - &OpenMlsSignaturePublicKey::new( - alice_credential_with_keys.signer.to_public_vec().into(), - ciphersuite.signature_algorithm(), - ) - .unwrap(), - ) + let mls_cipertext_alice = alice_group + .create_message(provider, &alice_signer, &message_alice) .expect("An unexpected error occurred."); - assert!(matches!( - mls_plaintext_bob.content(), - FramedContentBodyIn::Application(message) if message.as_slice() == &message_alice[..])); - - // === Bob updates and commits === - let mut bob_new_leaf_node = group_bob.own_leaf_node().unwrap().clone(); - bob_new_leaf_node - .update( - ciphersuite, + let processed_message = bob_group + .process_message( provider, - &bob_credential_with_keys.signer, - group_bob.group_id().clone(), - group_bob.own_leaf_index(), - LeafNodeParameters::default(), + mls_cipertext_alice.into_protocol_message().unwrap(), ) .unwrap(); - let update_proposal_bob = group_bob - .create_update_proposal( - framing_parameters, - bob_new_leaf_node, - &alice_credential_with_keys.signer, - ) - .expect("Could not create proposal."); - - group_bob.proposal_store_mut().empty(); - group_bob.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - update_proposal_bob, - ) - .unwrap(), - ); + match processed_message.content() { + ProcessedMessageContent::ApplicationMessage(message) => { + assert_eq!(message, &ApplicationMessage::new(message_alice.to_vec())); + } + _ => panic!("Wrong content type"), + } - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = - match group_bob.create_commit(params, provider, &bob_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + // === Bob updates and commits === + let (commit_message, welcome_option, _) = bob_group + .self_update(provider, &bob_signer, LeafNodeParameters::default()) + .expect("Error updating group"); // Check that there is a path - let commit = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => commit, - _ => panic!("Wrong content type"), + let commit = match &commit_message.body { + MlsMessageBodyOut::PublicMessage(pm) => match pm.content() { + FramedContentBody::Commit(commit) => commit, + _ => panic!("Wrong content type"), + }, + _ => panic!("Wrong message type"), }; assert!(commit.has_path()); // Check there is no Welcome message - assert!(create_commit_result.welcome_option.is_none()); + assert!(welcome_option.is_none()); - let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) - .expect("Error applying commit (Alice)"); - group_alice - .merge_commit(provider, staged_commit) + bob_group + .merge_pending_commit(provider) .expect("error merging commit"); - group_bob - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own commits"); + let processed_message = alice_group + .process_message(provider, commit_message.into_protocol_message().unwrap()) + .unwrap(); + match processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + alice_group + .merge_staged_commit(provider, *staged_commit) + .expect("error merging commit"); + } + _ => panic!("Wrong content type"), + } // Make sure that both groups have the same public tree assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_bob.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() ); // === Alice updates and commits === - let mut alice_new_leaf_node = group_alice.own_leaf_node().unwrap().clone(); - alice_new_leaf_node - .update( - ciphersuite, - provider, - &alice_credential_with_keys.signer, - group_alice.group_id().clone(), - group_alice.own_leaf_index(), - LeafNodeParameters::default(), - ) - .unwrap(); - - let update_proposal_alice = group_alice - .create_update_proposal( - framing_parameters, - alice_new_leaf_node, - &alice_credential_with_keys.signer, - ) - .expect("Could not create proposal."); - - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - update_proposal_alice, - ) - .unwrap(), - ); - - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = match group_alice.create_commit( - params, /* PSK fetcher */ - provider, - &alice_credential_with_keys.signer, - ) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), + let (commit_message, _, _) = alice_group + .self_update(provider, &alice_signer, LeafNodeParameters::default()) + .expect("Error updating group"); + + let commit = match &commit_message.body { + MlsMessageBodyOut::PublicMessage(pm) => match pm.content() { + FramedContentBody::Commit(commit) => commit, + _ => panic!("Wrong content type"), + }, + _ => panic!("Wrong message type"), }; // Check that there is a path assert!(commit.has_path()); - group_alice - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own commits"); - let staged_commit = group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) - .expect("Error applying commit (Bob)"); - group_bob - .merge_commit(provider, staged_commit) + alice_group + .merge_pending_commit(provider) .expect("error merging commit"); + let processed_message = bob_group + .process_message(provider, commit_message.into_protocol_message().unwrap()) + .unwrap(); + + match processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + bob_group + .merge_staged_commit(provider, *staged_commit) + .expect("error merging commit"); + } + _ => panic!("Wrong content type"), + } + // Make sure that both groups have the same public tree assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_bob.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() ); // === Bob updates and Alice commits === - let mut bob_new_leaf_node = group_bob.own_leaf_node().unwrap().clone(); - bob_new_leaf_node - .update( - ciphersuite, - provider, - &bob_credential_with_keys.signer, - group_bob.group_id().clone(), - group_bob.own_leaf_index(), - LeafNodeParameters::default(), - ) - .unwrap(); + let (bob_update_proposal, _) = bob_group + .propose_self_update(provider, &bob_signer, LeafNodeParameters::default()) + .expect("Error proposing update"); - let update_proposal_bob = group_bob - .create_update_proposal( - framing_parameters, - bob_new_leaf_node.clone(), - &bob_credential_with_keys.signer, + match alice_group + .process_message( + provider, + bob_update_proposal.into_protocol_message().unwrap(), ) - .expect("Could not create proposal."); + .unwrap() + .into_content() + { + ProcessedMessageContent::ProposalMessage(proposal) => { + alice_group + .store_pending_proposal(provider.storage(), *proposal) + .unwrap(); + } + _ => panic!("Wrong content type"), + } - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - update_proposal_bob.clone(), - ) - .unwrap(), - ); + let (commit_message, _, _) = alice_group + .commit_to_pending_proposals(provider, &alice_signer) + .unwrap(); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = - match group_alice.create_commit(params, provider, &alice_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + let commit = match &commit_message.body { + MlsMessageBodyOut::PublicMessage(pm) => match pm.content() { + FramedContentBody::Commit(commit) => commit, + _ => panic!("Wrong content type"), + }, + _ => panic!("Wrong message type"), + }; // Check that there is a path assert!(commit.has_path()); - group_alice - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own commits"); - - let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - update_proposal_bob, - ) - .unwrap(); - - group_alice - .proposal_store_mut() - .add(queued_proposal.clone()); - - group_bob.proposal_store_mut().add(queued_proposal); - - let staged_commit = group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &[bob_new_leaf_node], provider) - .expect("Error applying commit (Bob)"); - group_bob - .merge_commit(provider, staged_commit) - .expect("error merging commit"); + alice_group.merge_pending_commit(provider).unwrap(); + + match bob_group.process_message(provider, commit_message.into_protocol_message().unwrap()) { + Ok(processed_message) => match processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + bob_group + .merge_staged_commit(provider, *staged_commit) + .expect("error merging commit"); + } + _ => panic!("Wrong content type"), + }, + Err(e) => panic!("Error processing message: {e:?}"), + } // Make sure that both groups have the same public tree assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_bob.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() ); // === Bob adds Charlie === - let charlie_credential_with_keys = generate_credential_with_key( - b"Charlie".to_vec(), - ciphersuite.signature_algorithm(), - provider, - ); - - let charlie_key_package_bundle = KeyPackageBundle::generate( - provider, - &charlie_credential_with_keys.signer, - ciphersuite, - charlie_credential_with_keys.credential_with_key.clone(), - ); - let charlie_key_package = charlie_key_package_bundle.key_package().clone(); - - let add_charlie_proposal_bob = group_bob - .create_add_proposal( - framing_parameters, - charlie_key_package, - &bob_credential_with_keys.signer, - ) - .expect("Could not create proposal."); - - let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - add_charlie_proposal_bob, - ) - .unwrap(); - - group_alice.proposal_store_mut().empty(); - group_bob.proposal_store_mut().empty(); - - group_alice - .proposal_store_mut() - .add(queued_proposal.clone()); - group_bob.proposal_store_mut().add(queued_proposal); + let (_charlie_credential_with_key, charlie_kpb, charlie_signer, _charlie_sig_pk) = + setup_client("Charlie", ciphersuite, provider); + + let (commit_message, welcome, _) = bob_group + .add_members(provider, &bob_signer, &[charlie_kpb.key_package().clone()]) + .expect("Could not create add commit."); + + bob_group.merge_pending_commit(provider).unwrap(); + + match alice_group.process_message(provider, commit_message.into_protocol_message().unwrap()) { + Ok(processed_message) => match processed_message.into_content() { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + alice_group + .merge_staged_commit(provider, *staged_commit) + .expect("error merging commit"); + } + _ => panic!("Wrong content type"), + }, + Err(e) => panic!("Error processing message: {e:?}"), + } - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) + let config = MlsGroupJoinConfig::builder() + .wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) .build(); - let create_commit_result = - match group_bob.create_commit(params, provider, &bob_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; - - // Check there is no path since there are only Add Proposals and no forced - // self-update - let commit = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => commit, - _ => panic!("Wrong content type"), - }; - assert!(!commit.has_path()); - // Make sure this is a Welcome message for Charlie - assert!(create_commit_result.welcome_option.is_some()); - - let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) - .expect("Error applying commit (Alice)"); - group_alice - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - group_bob - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own commits"); - let ratchet_tree = group_alice.public_group().export_ratchet_tree(); - let mut group_charlie = match StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(ratchet_tree.into()), - charlie_key_package_bundle, + let ratchet_tree = alice_group.export_ratchet_tree(); + let mut charlie_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &config, + welcome.into_welcome().unwrap(), + Some(ratchet_tree.into()), ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - { - Ok(group) => group, - Err(e) => panic!("Error creating group from Welcome: {e:?}"), - }; + .unwrap() + .into_group(provider) + .unwrap(); // Make sure that all groups have the same public tree assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_bob.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() ); assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_charlie.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + charlie_group.export_ratchet_tree() ); // === Charlie sends a message to the group === let message_charlie = [1, 2, 3]; - let mls_ciphertext_charlie: PrivateMessageIn = group_charlie - .create_application_message( - &[], - &message_charlie, - 0, - provider, - &charlie_credential_with_keys.signer, - ) - .expect("An unexpected error occurred.") - .into(); - - // Alice decrypts and verifies - let verifiable_plaintext = group_alice - .decrypt_message( - provider.crypto(), - mls_ciphertext_charlie.clone().into(), - &sender_ratchet_configuration, - ) - .expect("An unexpected error occurred.") - .verifiable_content() - .to_owned(); - - let mls_plaintext_alice: AuthenticatedContentIn = verifiable_plaintext - .verify( - provider.crypto(), - &OpenMlsSignaturePublicKey::new( - charlie_credential_with_keys.signer.to_public_vec().into(), - ciphersuite.signature_algorithm(), - ) - .unwrap(), - ) + let mls_ciphertext_charlie = charlie_group + .create_message(provider, &charlie_signer, &message_charlie) .expect("An unexpected error occurred."); - assert!(matches!( - mls_plaintext_alice.content(), - FramedContentBodyIn::Application(message) if message.as_slice() == &message_charlie[..])); - - // Bob decrypts and verifies - let verifiable_plaintext = group_bob - .decrypt_message( - provider.crypto(), - mls_ciphertext_charlie.into(), - &sender_ratchet_configuration, - ) - .expect("An unexpected error occurred.") - .verifiable_content() - .to_owned(); - - let mls_plaintext_bob: AuthenticatedContentIn = verifiable_plaintext - .verify( - provider.crypto(), - &OpenMlsSignaturePublicKey::new( - charlie_credential_with_keys.signer.to_public_vec().into(), - ciphersuite.signature_algorithm(), - ) - .unwrap(), + let processed_message = alice_group + .process_message( + provider, + mls_ciphertext_charlie + .clone() + .into_protocol_message() + .unwrap(), ) - .expect("An unexpected error occurred."); + .unwrap(); assert!(matches!( - mls_plaintext_bob.content(), - FramedContentBodyIn::Application(message) if message.as_slice() == &message_charlie[..])); + processed_message.content(), + ProcessedMessageContent::ApplicationMessage(message) if message == &ApplicationMessage::new(message_charlie.to_vec()))); - // === Charlie updates and commits === - let mut charlie_new_leaf_node = group_bob.own_leaf_node().unwrap().clone(); - charlie_new_leaf_node - .update( - ciphersuite, + let processed_message = bob_group + .process_message( provider, - &charlie_credential_with_keys.signer, - group_charlie.group_id().clone(), - group_charlie.own_leaf_index(), - LeafNodeParameters::default(), + mls_ciphertext_charlie.into_protocol_message().unwrap(), ) .unwrap(); - let update_proposal_charlie = group_charlie - .create_update_proposal( - framing_parameters, - charlie_new_leaf_node, - &charlie_credential_with_keys.signer, - ) - .expect("Could not create proposal."); + assert!(matches!( + processed_message.content(), + ProcessedMessageContent::ApplicationMessage(message) if message == &ApplicationMessage::new(message_charlie.to_vec()))); - group_charlie.proposal_store_mut().empty(); - group_charlie.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - update_proposal_charlie, - ) - .unwrap(), - ); + // === Charlie updates and commits === + let (commit_message, _, _) = charlie_group + .self_update(provider, &charlie_signer, LeafNodeParameters::default()) + .expect("Error updating group"); + + let commit = match &commit_message.body { + MlsMessageBodyOut::PublicMessage(pm) => match pm.content() { + FramedContentBody::Commit(commit) => commit, + _ => panic!("Wrong content type"), + }, + _ => panic!("Wrong message type"), + }; - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = - match group_charlie.create_commit(params, provider, &charlie_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; + assert!(commit.has_path()); - // Check that there is a new KeyPackageBundle - let commit = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => commit, + charlie_group + .merge_pending_commit(provider) + .expect("error merging commit"); + + match alice_group + .process_message( + provider, + commit_message.clone().into_protocol_message().unwrap(), + ) + .unwrap() + .into_content() + { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + alice_group + .merge_staged_commit(provider, *staged_commit) + .expect("error merging commit"); + } _ => panic!("Wrong content type"), }; - assert!(commit.has_path()); - let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) - .expect("Error applying commit (Alice)"); - group_alice - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - let staged_commit = group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) - .expect("Error applying commit (Bob)"); - group_bob - .merge_commit(provider, staged_commit) - .expect("error merging commit"); - group_charlie - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own commits"); + match bob_group + .process_message(provider, commit_message.into_protocol_message().unwrap()) + .unwrap() + .into_content() + { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + bob_group + .merge_staged_commit(provider, *staged_commit) + .expect("error merging commit"); + } + _ => panic!("Wrong content type"), + }; // Make sure that all groups have the same public tree assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_bob.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() ); assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_charlie.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + charlie_group.export_ratchet_tree() ); // === Charlie removes Bob === - let remove_bob_proposal_charlie = group_charlie - .create_remove_proposal( - framing_parameters, - group_bob.own_leaf_index(), - &charlie_credential_with_keys.signer, - ) - .expect("Could not create proposal."); - - let queued_proposal = QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - remove_bob_proposal_charlie, - ) - .unwrap(); - - group_alice.proposal_store_mut().empty(); - group_bob.proposal_store_mut().empty(); - group_charlie.proposal_store_mut().empty(); - - group_alice - .proposal_store_mut() - .add(queued_proposal.clone()); - group_bob.proposal_store_mut().add(queued_proposal.clone()); - group_charlie.proposal_store_mut().add(queued_proposal); - - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = match group_charlie.create_commit( - params, /* PSK fetcher */ - provider, - &charlie_credential_with_keys.signer, - ) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), + let (commit_message, _, _) = charlie_group + .remove_members(provider, &charlie_signer, &[bob_group.own_leaf_index()]) + .expect("Could not create remove commit."); + + let commit = match &commit_message.body { + MlsMessageBodyOut::PublicMessage(pm) => match pm.content() { + FramedContentBody::Commit(commit) => commit, + _ => panic!("Wrong content type"), + }, + _ => panic!("Wrong message type"), }; - // Check that there is a new KeyPackageBundle assert!(commit.has_path()); - let staged_commit = group_alice - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) - .expect("Error applying commit (Alice)"); - group_alice - .merge_commit(provider, staged_commit) + charlie_group + .merge_pending_commit(provider) .expect("error merging commit"); - assert!(group_bob - .read_keys_and_stage_commit(&create_commit_result.commit, &[], provider) - .expect("Could not stage commit.") - .self_removed()); - group_charlie - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own commits"); - assert_ne!( - group_alice.public_group().export_ratchet_tree(), - group_bob.public_group().export_ratchet_tree() + match alice_group + .process_message( + provider, + commit_message.clone().into_protocol_message().unwrap(), + ) + .unwrap() + .into_content() + { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + alice_group + .merge_staged_commit(provider, *staged_commit) + .expect("error merging commit"); + } + _ => panic!("Wrong content type"), + }; + + match bob_group + .process_message(provider, commit_message.into_protocol_message().unwrap()) + .unwrap() + .into_content() + { + ProcessedMessageContent::StagedCommitMessage(staged_commit) => { + bob_group + .merge_staged_commit(provider, *staged_commit) + .expect("error merging commit"); + } + _ => panic!("Wrong content type"), + }; + + assert_eq!( + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() ); assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_charlie.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + charlie_group.export_ratchet_tree() ); // Make sure all groups export the same key - let alice_exporter = group_alice - .export_secret(provider.crypto(), "export test", &[], 32) - .expect("An unexpected error occurred."); - let charlie_exporter = group_charlie - .export_secret(provider.crypto(), "export test", &[], 32) - .expect("An unexpected error occurred."); + let alice_exporter = alice_group.epoch_authenticator(); + let charlie_exporter = charlie_group.epoch_authenticator(); assert_eq!(alice_exporter, charlie_exporter); // Now alice tries to derive an exporter with too large of a key length. let exporter_length: usize = u16::MAX.into(); let exporter_length = exporter_length + 1; - let alice_exporter = - group_alice.export_secret(provider.crypto(), "export test", &[], exporter_length); + let alice_exporter = alice_group.export_secret(provider, "export test", &[], exporter_length); assert!(alice_exporter.is_err()) } diff --git a/openmls/src/messages/tests/export_group_info.rs b/openmls/src/messages/tests/export_group_info.rs index 0452d6bb8..e1b33314c 100644 --- a/openmls/src/messages/tests/export_group_info.rs +++ b/openmls/src/messages/tests/export_group_info.rs @@ -4,6 +4,7 @@ use crate::{ ciphersuite::signable::Verifiable, group::mls_group::tests_and_kats::utils::setup_alice_group, messages::group_info::{GroupInfo, VerifiableGroupInfo}, + prelude::MlsMessageBodyOut, test_utils::*, }; @@ -13,10 +14,15 @@ fn export_group_info() { // Alice creates a group let (group_alice, _, signer, pk) = setup_alice_group(ciphersuite, provider); - let group_info: GroupInfo = group_alice - .export_group_info(provider.crypto(), &signer, true) + let group_info_message = group_alice + .export_group_info(provider, &signer, true) .unwrap(); + let group_info = match group_info_message.body() { + MlsMessageBodyOut::GroupInfo(group_info) => group_info, + _ => panic!("Wrong message type"), + }; + let verifiable_group_info = { let serialized = group_info.tls_serialize_detached().unwrap(); VerifiableGroupInfo::tls_deserialize(&mut serialized.as_slice()).unwrap() diff --git a/openmls/src/test_utils/frankenstein/commit.rs b/openmls/src/test_utils/frankenstein/commit.rs index c635a0dfd..b2537a961 100644 --- a/openmls/src/test_utils/frankenstein/commit.rs +++ b/openmls/src/test_utils/frankenstein/commit.rs @@ -32,8 +32,8 @@ pub struct FrankenUpdatePathIn { Debug, Clone, PartialEq, Eq, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize, )] pub struct FrankenUpdatePathNode { - pub(super) public_key: VLBytes, - pub(super) encrypted_path_secrets: Vec, + pub public_key: VLBytes, + pub encrypted_path_secrets: Vec, } #[derive( diff --git a/openmls/src/treesync/diff.rs b/openmls/src/treesync/diff.rs index f685136ce..b5fd68c2c 100644 --- a/openmls/src/treesync/diff.rs +++ b/openmls/src/treesync/diff.rs @@ -690,7 +690,7 @@ impl<'a> TreeSyncDiff<'a> { /// This turns the diff into a staged diff. In the process, the diff /// computes and sets the new tree hash. pub(crate) fn into_staged_diff( - mut self, + self, crypto: &impl OpenMlsCrypto, ciphersuite: Ciphersuite, ) -> Result { @@ -705,7 +705,7 @@ impl<'a> TreeSyncDiff<'a> { /// Helper function to compute and set the tree hash of the given node and /// all nodes below it in the tree. The leaf nodes in `exclusion_list` are /// not included in the tree hash. - pub(super) fn compute_tree_hash( + pub(crate) fn compute_tree_hash( &self, crypto: &impl OpenMlsCrypto, ciphersuite: Ciphersuite, @@ -746,7 +746,7 @@ impl<'a> TreeSyncDiff<'a> { /// Compute and set the tree hash of all nodes in the tree. pub(crate) fn compute_tree_hashes( - &mut self, + &self, crypto: &impl OpenMlsCrypto, ciphersuite: Ciphersuite, ) -> Result, LibraryError> { From ea5ac1383e976c479cc9c6e05fa9e89851652c33 Mon Sep 17 00:00:00 2001 From: Jan Winkelmann <146678+keks@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:11:44 +0200 Subject: [PATCH 22/43] Bump versions and remove git deps for release (#1625) --- CHANGELOG.md | 2 +- basic_credential/Cargo.toml | 4 ++-- interop_client/Cargo.toml | 2 +- libcrux_crypto/Cargo.toml | 4 ++-- libcrux_crypto/src/lib.rs | 6 +++--- memory_storage/Cargo.toml | 4 ++-- openmls/Cargo.toml | 10 +++++----- openmls_rust_crypto/Cargo.toml | 6 +++--- openmls_test/Cargo.toml | 2 +- traits/Cargo.toml | 2 +- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99ff2e508..d8da050c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## 0.6.0-pre.1 (2024-07-22) ### Added diff --git a/basic_credential/Cargo.toml b/basic_credential/Cargo.toml index c008f3c1c..806a01952 100644 --- a/basic_credential/Cargo.toml +++ b/basic_credential/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls_basic_credential" -version = "0.2.0" +version = "0.3.0" authors = ["OpenMLS Authors"] edition = "2021" description = "A Basic Credential implementation for OpenMLS" @@ -10,7 +10,7 @@ repository = "https://github.com/openmls/openmls/tree/main/basic_credential" readme = "README.md" [dependencies] -openmls_traits = { version = "0.2.0", path = "../traits" } +openmls_traits = { version = "0.3.0", path = "../traits" } tls_codec = { workspace = true } serde = "1.0" diff --git a/interop_client/Cargo.toml b/interop_client/Cargo.toml index f36544155..74844d3a6 100644 --- a/interop_client/Cargo.toml +++ b/interop_client/Cargo.toml @@ -24,6 +24,6 @@ clap_derive = "4.1" serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" tls_codec = { workspace = true } -openmls_basic_credential = { version = "0.2.0", path = "../basic_credential" } +openmls_basic_credential = { path = "../basic_credential" } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/libcrux_crypto/Cargo.toml b/libcrux_crypto/Cargo.toml index 4fd77b810..f5dc72a3e 100644 --- a/libcrux_crypto/Cargo.toml +++ b/libcrux_crypto/Cargo.toml @@ -11,8 +11,8 @@ readme = "../README.md" [dependencies] getrandom = "0.2.12" -libcrux = { git = "https://github.com/cryspen/libcrux", features = ["rand"] } +libcrux = { version = "=0.0.2-alpha.3", features = ["rand"] } openmls_traits = { path = "../traits" } -openmls_rust_crypto = { path = "../openmls_rust_crypto" } +openmls_memory_storage = { path = "../memory_storage" } rand = "0.8.5" tls_codec.workspace = true diff --git a/libcrux_crypto/src/lib.rs b/libcrux_crypto/src/lib.rs index c61a43758..5deeb0e93 100644 --- a/libcrux_crypto/src/lib.rs +++ b/libcrux_crypto/src/lib.rs @@ -12,16 +12,16 @@ pub use rand::RandProvider; pub struct Provider { crypto: crypto::CryptoProvider, rand: rand::RandProvider, - key_store: openmls_rust_crypto::MemoryStorage, + storage: openmls_memory_storage::MemoryStorage, } impl OpenMlsProvider for Provider { type CryptoProvider = CryptoProvider; type RandProvider = RandProvider; - type StorageProvider = openmls_rust_crypto::MemoryStorage; + type StorageProvider = openmls_memory_storage::MemoryStorage; fn storage(&self) -> &Self::StorageProvider { - &self.key_store + &self.storage } fn crypto(&self) -> &Self::CryptoProvider { diff --git a/memory_storage/Cargo.toml b/memory_storage/Cargo.toml index d6387d9d7..a3eba5fbb 100644 --- a/memory_storage/Cargo.toml +++ b/memory_storage/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "openmls_memory_storage" authors = ["OpenMLS Authors"] -version = "0.2.0" +version = "0.3.0" edition = "2021" description = "A very basic storage for OpenMLS implementing openmls_traits." license = "MIT" @@ -10,7 +10,7 @@ repository = "https://github.com/openmls/openmls/tree/main/memory_storage" readme = "README.md" [dependencies] -openmls_traits = { version = "0.2.0", path = "../traits" } +openmls_traits = { version = "0.3.0", path = "../traits" } thiserror = "1.0" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 4f37489cb..24400d40b 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls" -version = "0.5.0" +version = "0.6.0-pre.1" authors = ["OpenMLS Authors"] edition = "2021" description = "A Rust implementation of the Messaging Layer Security (MLS) protocol, as defined in RFC 9420." @@ -11,7 +11,7 @@ readme = "../README.md" keywords = ["MLS", "IETF", "RFC9420", "Encryption", "E2EE"] [dependencies] -openmls_traits = { version = "0.2.0", path = "../traits" } +openmls_traits = { version = "0.3.0", path = "../traits" } serde = { version = "^1.0", features = ["derive"] } log = { version = "0.4", features = ["std"] } tls_codec = { workspace = true } @@ -23,8 +23,8 @@ rand = { version = "0.8", optional = true } serde_json = { version = "1.0", optional = true } # Crypto providers required for KAT and testing - "test-utils" feature itertools = { version = "0.10", optional = true } -openmls_rust_crypto = { version = "0.2.0", path = "../openmls_rust_crypto", optional = true } -openmls_basic_credential = { version = "0.2.0", path = "../basic_credential", optional = true, features = [ +openmls_rust_crypto = { version = "0.3.0", path = "../openmls_rust_crypto", optional = true } +openmls_basic_credential = { version = "0.3.0", path = "../basic_credential", optional = true, features = [ "clonable", "test-utils", ] } @@ -69,7 +69,7 @@ criterion = { version = "^0.5", default-features = false } # need to disable def hex = { version = "0.4", features = ["serde"] } itertools = "0.10" lazy_static = "1.4" -openmls_traits = { version = "0.2.0", path = "../traits", features = [ +openmls_traits = { version = "0.3.0", path = "../traits", features = [ "test-utils", ] } pretty_env_logger = "0.5" diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index 8a7eadf8f..4e299cf12 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "openmls_rust_crypto" authors = ["OpenMLS Authors"] -version = "0.2.0" +version = "0.3.0" edition = "2021" description = "A crypto backend for OpenMLS implementing openmls_traits using RustCrypto primitives." license = "MIT" @@ -10,8 +10,8 @@ repository = "https://github.com/openmls/openmls/tree/main/openmls_rust_crypto" readme = "README.md" [dependencies] -openmls_traits = { version = "0.2.0", path = "../traits" } -openmls_memory_storage = { version = "0.2.0", path = "../memory_storage" } +openmls_traits = { version = "0.3.0", path = "../traits" } +openmls_memory_storage = { version = "0.3.0", path = "../memory_storage" } hpke = { version = "0.2.0", package = "hpke-rs", default-features = false, features = [ "hazmat", "serialization", diff --git a/openmls_test/Cargo.toml b/openmls_test/Cargo.toml index be54b59fc..eb300d968 100644 --- a/openmls_test/Cargo.toml +++ b/openmls_test/Cargo.toml @@ -18,6 +18,6 @@ ansi_term = "0.12.1" quote = "1.0" rstest = { version = "0.17" } rstest_reuse = { version = "0.5" } -openmls_rust_crypto = { version = "=0.2.0", path = "../openmls_rust_crypto" } +openmls_rust_crypto = { version = "=0.3.0", path = "../openmls_rust_crypto" } openmls_libcrux_crypto = { version = "=0.1.0", path = "../libcrux_crypto", optional = true } openmls_traits = { path = "../traits" } diff --git a/traits/Cargo.toml b/traits/Cargo.toml index 440a3d64a..7176e9fb5 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls_traits" -version = "0.2.0" +version = "0.3.0" authors = ["OpenMLS Authors"] edition = "2021" description = "Traits used by OpenMLS" From e5ad6cad8b4d11a12e75748650a44509eb44d619 Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 23 Jul 2024 13:54:54 +0200 Subject: [PATCH 23/43] make all the versions pre-release --- basic_credential/Cargo.toml | 4 ++-- libcrux_crypto/Cargo.toml | 6 +++--- memory_storage/Cargo.toml | 4 ++-- openmls/Cargo.toml | 12 ++++++------ openmls_rust_crypto/Cargo.toml | 6 +++--- openmls_test/Cargo.toml | 6 +++--- traits/Cargo.toml | 2 +- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/basic_credential/Cargo.toml b/basic_credential/Cargo.toml index 806a01952..3f6c224cc 100644 --- a/basic_credential/Cargo.toml +++ b/basic_credential/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls_basic_credential" -version = "0.3.0" +version = "0.3.0-pre.1" authors = ["OpenMLS Authors"] edition = "2021" description = "A Basic Credential implementation for OpenMLS" @@ -10,7 +10,7 @@ repository = "https://github.com/openmls/openmls/tree/main/basic_credential" readme = "README.md" [dependencies] -openmls_traits = { version = "0.3.0", path = "../traits" } +openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } tls_codec = { workspace = true } serde = "1.0" diff --git a/libcrux_crypto/Cargo.toml b/libcrux_crypto/Cargo.toml index f5dc72a3e..3ed0ef628 100644 --- a/libcrux_crypto/Cargo.toml +++ b/libcrux_crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls_libcrux_crypto" -version = "0.1.0" +version = "0.1.0-pre.1" edition = "2021" authors = ["OpenMLS Authors"] description = "A crypto backend for OpenMLS based on libcrux implementing openmls_traits." @@ -12,7 +12,7 @@ readme = "../README.md" [dependencies] getrandom = "0.2.12" libcrux = { version = "=0.0.2-alpha.3", features = ["rand"] } -openmls_traits = { path = "../traits" } -openmls_memory_storage = { path = "../memory_storage" } +openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } +openmls_memory_storage = { version = "0.3.0-pre.1", path = "../memory_storage" } rand = "0.8.5" tls_codec.workspace = true diff --git a/memory_storage/Cargo.toml b/memory_storage/Cargo.toml index a3eba5fbb..981d18df8 100644 --- a/memory_storage/Cargo.toml +++ b/memory_storage/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "openmls_memory_storage" authors = ["OpenMLS Authors"] -version = "0.3.0" +version = "0.3.0-pre.1" edition = "2021" description = "A very basic storage for OpenMLS implementing openmls_traits." license = "MIT" @@ -10,7 +10,7 @@ repository = "https://github.com/openmls/openmls/tree/main/memory_storage" readme = "README.md" [dependencies] -openmls_traits = { version = "0.3.0", path = "../traits" } +openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } thiserror = "1.0" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 24400d40b..5202e8446 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -11,7 +11,7 @@ readme = "../README.md" keywords = ["MLS", "IETF", "RFC9420", "Encryption", "E2EE"] [dependencies] -openmls_traits = { version = "0.3.0", path = "../traits" } +openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } serde = { version = "^1.0", features = ["derive"] } log = { version = "0.4", features = ["std"] } tls_codec = { workspace = true } @@ -23,19 +23,19 @@ rand = { version = "0.8", optional = true } serde_json = { version = "1.0", optional = true } # Crypto providers required for KAT and testing - "test-utils" feature itertools = { version = "0.10", optional = true } -openmls_rust_crypto = { version = "0.3.0", path = "../openmls_rust_crypto", optional = true } -openmls_basic_credential = { version = "0.3.0", path = "../basic_credential", optional = true, features = [ +openmls_rust_crypto = { version = "0.3.0-pre.1", path = "../openmls_rust_crypto", optional = true } +openmls_basic_credential = { version = "0.3.0-pre.1", path = "../basic_credential", optional = true, features = [ "clonable", "test-utils", ] } wasm-bindgen-test = { version = "0.3.40", optional = true } getrandom = { version = "0.2.12", optional = true, features = ["js"] } fluvio-wasm-timer = { version = "0.2.5", optional = true } -openmls_memory_storage = { path = "../memory_storage", features = [ +openmls_memory_storage = { version = "0.3.0-pre.1", path = "../memory_storage", features = [ "test-utils", ], optional = true } -openmls_test = { path = "../openmls_test", optional = true } -openmls_libcrux_crypto = { path = "../libcrux_crypto", optional = true } +openmls_test = { version = "0.2.0-pre.1", path = "../openmls_test", optional = true } +openmls_libcrux_crypto = { version = "0.1.0-pre.1", path = "../libcrux_crypto", optional = true } once_cell = { version = "1.19.0", optional = true } [features] diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index 4e299cf12..e771f5f46 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "openmls_rust_crypto" authors = ["OpenMLS Authors"] -version = "0.3.0" +version = "0.3.0-pre.1" edition = "2021" description = "A crypto backend for OpenMLS implementing openmls_traits using RustCrypto primitives." license = "MIT" @@ -10,8 +10,8 @@ repository = "https://github.com/openmls/openmls/tree/main/openmls_rust_crypto" readme = "README.md" [dependencies] -openmls_traits = { version = "0.3.0", path = "../traits" } -openmls_memory_storage = { version = "0.3.0", path = "../memory_storage" } +openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } +openmls_memory_storage = { version = "0.3.0-pre.1", path = "../memory_storage" } hpke = { version = "0.2.0", package = "hpke-rs", default-features = false, features = [ "hazmat", "serialization", diff --git a/openmls_test/Cargo.toml b/openmls_test/Cargo.toml index eb300d968..8c0a4fa46 100644 --- a/openmls_test/Cargo.toml +++ b/openmls_test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls_test" -version = "0.1.0" +version = "0.2.0-pre.1" edition = "2021" authors = ["Franziskus Kiefer "] @@ -18,6 +18,6 @@ ansi_term = "0.12.1" quote = "1.0" rstest = { version = "0.17" } rstest_reuse = { version = "0.5" } -openmls_rust_crypto = { version = "=0.3.0", path = "../openmls_rust_crypto" } -openmls_libcrux_crypto = { version = "=0.1.0", path = "../libcrux_crypto", optional = true } +openmls_rust_crypto = { path = "../openmls_rust_crypto" } +openmls_libcrux_crypto = { path = "../libcrux_crypto", optional = true } openmls_traits = { path = "../traits" } diff --git a/traits/Cargo.toml b/traits/Cargo.toml index 7176e9fb5..44f91806f 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls_traits" -version = "0.3.0" +version = "0.3.0-pre.1" authors = ["OpenMLS Authors"] edition = "2021" description = "Traits used by OpenMLS" From b21beea5d60d78e172a9fa9eccddab2c352b14ac Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 23 Jul 2024 14:01:40 +0200 Subject: [PATCH 24/43] missed one --- openmls/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 5202e8446..406564557 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -69,7 +69,7 @@ criterion = { version = "^0.5", default-features = false } # need to disable def hex = { version = "0.4", features = ["serde"] } itertools = "0.10" lazy_static = "1.4" -openmls_traits = { version = "0.3.0", path = "../traits", features = [ +openmls_traits = { version = "0.3.0-pre.1", path = "../traits", features = [ "test-utils", ] } pretty_env_logger = "0.5" From 000cffb81bb70fa4a2e16a9099d9bb13e12e4abe Mon Sep 17 00:00:00 2001 From: "Jan Winkelmann (keks)" Date: Tue, 23 Jul 2024 14:24:05 +0200 Subject: [PATCH 25/43] make openmls_test 0.1.0-pre.1 --- openmls/Cargo.toml | 2 +- openmls_test/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 406564557..ad6d3f888 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -34,7 +34,7 @@ fluvio-wasm-timer = { version = "0.2.5", optional = true } openmls_memory_storage = { version = "0.3.0-pre.1", path = "../memory_storage", features = [ "test-utils", ], optional = true } -openmls_test = { version = "0.2.0-pre.1", path = "../openmls_test", optional = true } +openmls_test = { version = "0.1.0-pre.1", path = "../openmls_test", optional = true } openmls_libcrux_crypto = { version = "0.1.0-pre.1", path = "../libcrux_crypto", optional = true } once_cell = { version = "1.19.0", optional = true } diff --git a/openmls_test/Cargo.toml b/openmls_test/Cargo.toml index 8c0a4fa46..42b995bda 100644 --- a/openmls_test/Cargo.toml +++ b/openmls_test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls_test" -version = "0.2.0-pre.1" +version = "0.1.0-pre.1" edition = "2021" authors = ["Franziskus Kiefer "] From 1f0b14abdc0ab515dc9e5b8e4211523cd8bdf01d Mon Sep 17 00:00:00 2001 From: Jan Winkelmann <146678+keks@users.noreply.github.com> Date: Tue, 23 Jul 2024 17:35:53 +0200 Subject: [PATCH 26/43] Downgrade tls_codec to 0.4.1 and bump a few prerelease versions (#1627) --- Cargo.toml | 4 ++-- basic_credential/Cargo.toml | 2 +- libcrux_crypto/Cargo.toml | 6 +++--- memory_storage/Cargo.toml | 4 ++-- openmls/Cargo.toml | 24 ++++++++++++------------ openmls_rust_crypto/Cargo.toml | 4 ++-- traits/Cargo.toml | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 04e824d33..d516edc41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ resolver = "2" # Central dependency management for some crates [workspace.dependencies] -tls_codec = { version = "0.4.2-pre.1", features = [ +tls_codec = { version = "0.4.1", features = [ "derive", "serde", "mls", -], git = "https://github.com/rustcrypto/formats" } +]} diff --git a/basic_credential/Cargo.toml b/basic_credential/Cargo.toml index 3f6c224cc..84016983c 100644 --- a/basic_credential/Cargo.toml +++ b/basic_credential/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/openmls/openmls/tree/main/basic_credential" readme = "README.md" [dependencies] -openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } +openmls_traits = { version = "0.3.0-pre.2", path = "../traits" } tls_codec = { workspace = true } serde = "1.0" diff --git a/libcrux_crypto/Cargo.toml b/libcrux_crypto/Cargo.toml index 3ed0ef628..a2cb16811 100644 --- a/libcrux_crypto/Cargo.toml +++ b/libcrux_crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls_libcrux_crypto" -version = "0.1.0-pre.1" +version = "0.1.0-pre.2" edition = "2021" authors = ["OpenMLS Authors"] description = "A crypto backend for OpenMLS based on libcrux implementing openmls_traits." @@ -12,7 +12,7 @@ readme = "../README.md" [dependencies] getrandom = "0.2.12" libcrux = { version = "=0.0.2-alpha.3", features = ["rand"] } -openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } -openmls_memory_storage = { version = "0.3.0-pre.1", path = "../memory_storage" } +openmls_traits = { version = "0.3.0-pre.2", path = "../traits" } +openmls_memory_storage = { version = "0.3.0-pre.2", path = "../memory_storage" } rand = "0.8.5" tls_codec.workspace = true diff --git a/memory_storage/Cargo.toml b/memory_storage/Cargo.toml index 981d18df8..4b4c1dc69 100644 --- a/memory_storage/Cargo.toml +++ b/memory_storage/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "openmls_memory_storage" authors = ["OpenMLS Authors"] -version = "0.3.0-pre.1" +version = "0.3.0-pre.2" edition = "2021" description = "A very basic storage for OpenMLS implementing openmls_traits." license = "MIT" @@ -10,7 +10,7 @@ repository = "https://github.com/openmls/openmls/tree/main/memory_storage" readme = "README.md" [dependencies] -openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } +openmls_traits = { version = "0.3.0-pre.2", path = "../traits" } thiserror = "1.0" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index ad6d3f888..726dc14be 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -11,7 +11,17 @@ readme = "../README.md" keywords = ["MLS", "IETF", "RFC9420", "Encryption", "E2EE"] [dependencies] -openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } +openmls_traits = { version = "0.3.0-pre.2", path = "../traits" } +openmls_rust_crypto = { version = "0.3.0-pre.1", path = "../openmls_rust_crypto", optional = true } +openmls_basic_credential = { version = "0.3.0-pre.1", path = "../basic_credential", optional = true, features = [ + "clonable", + "test-utils", +] } +openmls_memory_storage = { version = "0.3.0-pre.2", path = "../memory_storage", features = [ + "test-utils", +], optional = true } +openmls_test = { version = "0.1.0-pre.1", path = "../openmls_test", optional = true } +openmls_libcrux_crypto = { version = "0.1.0-pre.2", path = "../libcrux_crypto", optional = true } serde = { version = "^1.0", features = ["derive"] } log = { version = "0.4", features = ["std"] } tls_codec = { workspace = true } @@ -23,19 +33,9 @@ rand = { version = "0.8", optional = true } serde_json = { version = "1.0", optional = true } # Crypto providers required for KAT and testing - "test-utils" feature itertools = { version = "0.10", optional = true } -openmls_rust_crypto = { version = "0.3.0-pre.1", path = "../openmls_rust_crypto", optional = true } -openmls_basic_credential = { version = "0.3.0-pre.1", path = "../basic_credential", optional = true, features = [ - "clonable", - "test-utils", -] } wasm-bindgen-test = { version = "0.3.40", optional = true } getrandom = { version = "0.2.12", optional = true, features = ["js"] } fluvio-wasm-timer = { version = "0.2.5", optional = true } -openmls_memory_storage = { version = "0.3.0-pre.1", path = "../memory_storage", features = [ - "test-utils", -], optional = true } -openmls_test = { version = "0.1.0-pre.1", path = "../openmls_test", optional = true } -openmls_libcrux_crypto = { version = "0.1.0-pre.1", path = "../libcrux_crypto", optional = true } once_cell = { version = "1.19.0", optional = true } [features] @@ -69,7 +69,7 @@ criterion = { version = "^0.5", default-features = false } # need to disable def hex = { version = "0.4", features = ["serde"] } itertools = "0.10" lazy_static = "1.4" -openmls_traits = { version = "0.3.0-pre.1", path = "../traits", features = [ +openmls_traits = { version = "0.3.0-pre.2", path = "../traits", features = [ "test-utils", ] } pretty_env_logger = "0.5" diff --git a/openmls_rust_crypto/Cargo.toml b/openmls_rust_crypto/Cargo.toml index e771f5f46..d25544542 100644 --- a/openmls_rust_crypto/Cargo.toml +++ b/openmls_rust_crypto/Cargo.toml @@ -10,8 +10,8 @@ repository = "https://github.com/openmls/openmls/tree/main/openmls_rust_crypto" readme = "README.md" [dependencies] -openmls_traits = { version = "0.3.0-pre.1", path = "../traits" } -openmls_memory_storage = { version = "0.3.0-pre.1", path = "../memory_storage" } +openmls_traits = { version = "0.3.0-pre.2", path = "../traits" } +openmls_memory_storage = { version = "0.3.0-pre.2", path = "../memory_storage" } hpke = { version = "0.2.0", package = "hpke-rs", default-features = false, features = [ "hazmat", "serialization", diff --git a/traits/Cargo.toml b/traits/Cargo.toml index 44f91806f..dad103802 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls_traits" -version = "0.3.0-pre.1" +version = "0.3.0-pre.2" authors = ["OpenMLS Authors"] edition = "2021" description = "Traits used by OpenMLS" From fb42f4b784d537e6a94e4bd009dccafc957d4c6a Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Wed, 24 Jul 2024 06:56:11 +0200 Subject: [PATCH 27/43] Add metadata to openmls_test crate --- openmls_test/Cargo.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openmls_test/Cargo.toml b/openmls_test/Cargo.toml index 42b995bda..13b963bc5 100644 --- a/openmls_test/Cargo.toml +++ b/openmls_test/Cargo.toml @@ -1,8 +1,13 @@ [package] name = "openmls_test" version = "0.1.0-pre.1" +authors = ["OpenMLS Authors"] edition = "2021" -authors = ["Franziskus Kiefer "] +description = "Test utility used by OpenMLS" +license = "MIT" +documentation = "https://docs.rs/openmls_test" +repository = "https://github.com/openmls/openmls/tree/main/openmls_test" +readme = "README.md" [lib] proc-macro = true From 5cf547f0233c8f4c7bf78734a45622a624a86518 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Wed, 24 Jul 2024 08:32:22 +0200 Subject: [PATCH 28/43] Update openmls_test/Cargo.toml Co-authored-by: Jan Winkelmann <146678+keks@users.noreply.github.com> --- openmls_test/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmls_test/Cargo.toml b/openmls_test/Cargo.toml index 13b963bc5..90c87021c 100644 --- a/openmls_test/Cargo.toml +++ b/openmls_test/Cargo.toml @@ -7,7 +7,7 @@ description = "Test utility used by OpenMLS" license = "MIT" documentation = "https://docs.rs/openmls_test" repository = "https://github.com/openmls/openmls/tree/main/openmls_test" -readme = "README.md" +readme = "Readme.md" [lib] proc-macro = true From d4e88e23b7e71ffb0f81bf979342d0c9993278c4 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Wed, 24 Jul 2024 14:11:04 +0200 Subject: [PATCH 29/43] set versions for openmls_test --- openmls_test/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openmls_test/Cargo.toml b/openmls_test/Cargo.toml index 90c87021c..02b9cbe1c 100644 --- a/openmls_test/Cargo.toml +++ b/openmls_test/Cargo.toml @@ -23,6 +23,6 @@ ansi_term = "0.12.1" quote = "1.0" rstest = { version = "0.17" } rstest_reuse = { version = "0.5" } -openmls_rust_crypto = { path = "../openmls_rust_crypto" } -openmls_libcrux_crypto = { path = "../libcrux_crypto", optional = true } -openmls_traits = { path = "../traits" } +openmls_rust_crypto = { version = "0.3.0-pre.1", path = "../openmls_rust_crypto" } +openmls_libcrux_crypto = { version = "0.1.0-pre.2", path = "../libcrux_crypto", optional = true } +openmls_traits = { version = "0.3.0-pre.2", path = "../traits" } From 15eb787fdc4b4fb6f51564f06b594c000544935e Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Thu, 25 Jul 2024 15:12:04 +0200 Subject: [PATCH 30/43] openmls v0.6.0-pre.2 This version drops the test_vectors folders and reduces the compressed package to 373KiB --- openmls/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmls/Cargo.toml b/openmls/Cargo.toml index 726dc14be..c96b8cf55 100644 --- a/openmls/Cargo.toml +++ b/openmls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openmls" -version = "0.6.0-pre.1" +version = "0.6.0-pre.2" authors = ["OpenMLS Authors"] edition = "2021" description = "A Rust implementation of the Messaging Layer Security (MLS) protocol, as defined in RFC 9420." @@ -9,6 +9,7 @@ documentation = "https://openmls.github.io/openmls/" repository = "https://github.com/openmls/openmls/" readme = "../README.md" keywords = ["MLS", "IETF", "RFC9420", "Encryption", "E2EE"] +exclude = ["/test_vectors"] [dependencies] openmls_traits = { version = "0.3.0-pre.2", path = "../traits" } From 2c223568a4b3bc20ba3b19fe1497b4c1f4149c00 Mon Sep 17 00:00:00 2001 From: Konrad Kohbrok Date: Thu, 25 Jul 2024 19:14:44 +0200 Subject: [PATCH 31/43] Fix minor problems, mostly in docs (#1634) --- interop_client/src/main.rs | 2 +- openmls/src/group/core_group/staged_commit.rs | 2 +- openmls/src/group/mls_group/mod.rs | 64 +++++++++---------- .../src/group/public_group/staged_commit.rs | 1 + openmls/src/group/public_group/validation.rs | 1 + openmls/src/lib.rs | 6 +- openmls/src/messages/external_proposals.rs | 2 +- openmls/src/tree/sender_ratchet.rs | 16 ++--- 8 files changed, 46 insertions(+), 48 deletions(-) diff --git a/interop_client/src/main.rs b/interop_client/src/main.rs index 235d3a95a..9ef69f8db 100644 --- a/interop_client/src/main.rs +++ b/interop_client/src/main.rs @@ -978,7 +978,7 @@ impl MlsClient for MlsClientImpl { let (proposal, _proposal_ref) = match proposal_type.as_ref() { "add" => { let key_package = - MlsMessageIn::tls_deserialize_exact(&mut proposal.key_package.clone()) + MlsMessageIn::tls_deserialize_exact(proposal.key_package.clone()) .map_err(|_| Status::invalid_argument("Invalid key package"))?; let key_package = key_package .into_keypackage() diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index a429c93f9..30039bd20 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -120,7 +120,7 @@ impl CoreGroup { /// - ValSem241 /// - ValSem242 /// - ValSem244 Returns an error if the given commit was sent by the owner - /// of this group. + /// of this group. pub(crate) fn stage_commit( &self, mls_content: &AuthenticatedContent, diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index cdc600803..21fca975e 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -78,46 +78,46 @@ impl From for StagedCommit { /// states and their transitions are as follows: /// /// * [`MlsGroupState::Operational`]: This is the main state of the group, which -/// allows access to all of its functionality, (except merging pending commits, -/// see the [`MlsGroupState::PendingCommit`] for more information) and it's the -/// state the group starts in (except when created via -/// [`MlsGroup::join_by_external_commit()`], see the functions documentation for -/// more information). From this `Operational`, the group state can either -/// transition to [`MlsGroupState::Inactive`], when it processes a commit that -/// removes this client from the group, or to [`MlsGroupState::PendingCommit`], -/// when this client creates a commit. +/// allows access to all of its functionality, (except merging pending commits, +/// see the [`MlsGroupState::PendingCommit`] for more information) and it's the +/// state the group starts in (except when created via +/// [`MlsGroup::join_by_external_commit()`], see the functions documentation for +/// more information). From this `Operational`, the group state can either +/// transition to [`MlsGroupState::Inactive`], when it processes a commit that +/// removes this client from the group, or to [`MlsGroupState::PendingCommit`], +/// when this client creates a commit. /// /// * [`MlsGroupState::Inactive`]: A group can enter this state from any other -/// state when it processes a commit that removes this client from the group. -/// This is a terminal state that the group can not exit from. If the clients -/// wants to re-join the group, it can either be added by a group member or it -/// can join via external commit. +/// state when it processes a commit that removes this client from the group. +/// This is a terminal state that the group can not exit from. If the clients +/// wants to re-join the group, it can either be added by a group member or it +/// can join via external commit. /// /// * [`MlsGroupState::PendingCommit`]: This state is split into two possible -/// sub-states, one for each Commit type: -/// [`PendingCommitState::Member`] and [`PendingCommitState::External`]: +/// sub-states, one for each Commit type: +/// [`PendingCommitState::Member`] and [`PendingCommitState::External`]: /// /// * If the client creates a commit for this group, the `PendingCommit` state -/// is entered with [`PendingCommitState::Member`] and with the [`StagedCommit`] as -/// additional state variable. In this state, it can perform the same -/// operations as in the [`MlsGroupState::Operational`], except that it cannot -/// create proposals or commits. However, it can merge or clear the stored -/// [`StagedCommit`], where both actions result in a transition to the -/// [`MlsGroupState::Operational`]. Additionally, if a commit from another -/// group member is processed, the own pending commit is also cleared and -/// either the `Inactive` state is entered (if this client was removed from -/// the group as part of the processed commit), or the `Operational` state is -/// entered. +/// is entered with [`PendingCommitState::Member`] and with the [`StagedCommit`] as +/// additional state variable. In this state, it can perform the same +/// operations as in the [`MlsGroupState::Operational`], except that it cannot +/// create proposals or commits. However, it can merge or clear the stored +/// [`StagedCommit`], where both actions result in a transition to the +/// [`MlsGroupState::Operational`]. Additionally, if a commit from another +/// group member is processed, the own pending commit is also cleared and +/// either the `Inactive` state is entered (if this client was removed from +/// the group as part of the processed commit), or the `Operational` state is +/// entered. /// /// * A group can enter the [`PendingCommitState::External`] sub-state only as -/// the initial state when the group is created via -/// [`MlsGroup::join_by_external_commit()`]. In contrast to the -/// [`PendingCommitState::Member`] `PendingCommit` state, the only possible -/// functionality that can be used is the [`MlsGroup::merge_pending_commit()`] -/// function, which merges the pending external commit and transitions the -/// state to [`MlsGroupState::PendingCommit`]. For more information on the -/// external commit process, see [`MlsGroup::join_by_external_commit()`] or -/// Section 11.2.1 of the MLS specification. +/// the initial state when the group is created via +/// [`MlsGroup::join_by_external_commit()`]. In contrast to the +/// [`PendingCommitState::Member`] `PendingCommit` state, the only possible +/// functionality that can be used is the [`MlsGroup::merge_pending_commit()`] +/// function, which merges the pending external commit and transitions the +/// state to [`MlsGroupState::PendingCommit`]. For more information on the +/// external commit process, see [`MlsGroup::join_by_external_commit()`] or +/// Section 11.2.1 of the MLS specification. #[derive(Debug, Serialize, Deserialize)] #[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub enum MlsGroupState { diff --git a/openmls/src/group/public_group/staged_commit.rs b/openmls/src/group/public_group/staged_commit.rs index 245d274c2..4595f7933 100644 --- a/openmls/src/group/public_group/staged_commit.rs +++ b/openmls/src/group/public_group/staged_commit.rs @@ -202,6 +202,7 @@ impl PublicGroup { /// - ValSem241 /// - ValSem242 /// - ValSem244 + /// /// Returns an error if the given commit was sent by the owner of this /// group. pub(crate) fn stage_commit( diff --git a/openmls/src/group/public_group/validation.rs b/openmls/src/group/public_group/validation.rs index b7969e0f4..abfc52597 100644 --- a/openmls/src/group/public_group/validation.rs +++ b/openmls/src/group/public_group/validation.rs @@ -431,6 +431,7 @@ impl PublicGroup { /// Validate Update proposals. This function implements the following checks: /// - ValSem111: Update Proposal: The sender of a full Commit must not include own update proposals /// - ValSem112: Update Proposal: The sender of a standalone update proposal must be of type member + /// /// TODO: #133 This validation must be updated according to Sec. 13.2 pub(crate) fn validate_update_proposals( &self, diff --git a/openmls/src/lib.rs b/openmls/src/lib.rs index 3f46f0455..1a5930560 100644 --- a/openmls/src/lib.rs +++ b/openmls/src/lib.rs @@ -142,11 +142,7 @@ #![cfg_attr(not(feature = "test-utils"), deny(missing_docs))] #![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::private_intra_doc_links)] -#![cfg(any( - target_pointer_width = "32", - target_pointer_width = "64", - target_pointer_width = "128" -))] +#![cfg(any(target_pointer_width = "32", target_pointer_width = "64",))] #[cfg(all(target_arch = "wasm32", not(feature = "js")))] compile_error!("In order for OpenMLS to build for WebAssembly, JavaScript APIs must be available (for access to secure randomness and the current time). This can be signalled by setting the `js` feature on OpenMLS."); diff --git a/openmls/src/messages/external_proposals.rs b/openmls/src/messages/external_proposals.rs index e88b1fbea..99838c799 100644 --- a/openmls/src/messages/external_proposals.rs +++ b/openmls/src/messages/external_proposals.rs @@ -69,7 +69,7 @@ impl ExternalProposal { /// * `epoch` - group's epoch /// * `signer` - of the sender to sign the message /// * `sender` - index of the sender of the proposal (in the [crate::extensions::ExternalSendersExtension] array - /// from the Group Context) + /// from the Group Context) pub fn new_remove( removed: LeafNodeIndex, group_id: GroupId, diff --git a/openmls/src/tree/sender_ratchet.rs b/openmls/src/tree/sender_ratchet.rs index 240a87465..380a71ae2 100644 --- a/openmls/src/tree/sender_ratchet.rs +++ b/openmls/src/tree/sender_ratchet.rs @@ -21,14 +21,14 @@ pub(crate) type Generation = u32; /// /// **Parameters** /// -/// - out_of_order_tolerance: -/// This parameter defines a window for which decryption secrets are kept. -/// This is useful in case the DS cannot guarantee that all application messages have total order within an epoch. -/// Use this carefully, since keeping decryption secrets affects forward secrecy within an epoch. -/// The default value is 5. -/// - maximum_forward_distance: -/// This parameter defines how many incoming messages can be skipped. This is useful if the DS -/// drops application messages. The default value is 1000. +/// - out_of_order_tolerance: +/// This parameter defines a window for which decryption secrets are kept. +/// This is useful in case the DS cannot guarantee that all application messages have total order within an epoch. +/// Use this carefully, since keeping decryption secrets affects forward secrecy within an epoch. +/// The default value is 5. +/// - maximum_forward_distance: +/// This parameter defines how many incoming messages can be skipped. This is useful if the DS +/// drops application messages. The default value is 1000. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SenderRatchetConfiguration { out_of_order_tolerance: Generation, From b66deda5c4fd2d06e479eda3faabd1f4105f5cf4 Mon Sep 17 00:00:00 2001 From: Konrad Kohbrok Date: Thu, 25 Jul 2024 19:41:14 +0200 Subject: [PATCH 32/43] Remove `aad` getter from storage provider trait (#1633) --- memory_storage/src/lib.rs | 14 -------------- memory_storage/src/test_store.rs | 7 ------- traits/src/storage.rs | 7 ------- 3 files changed, 28 deletions(-) diff --git a/memory_storage/src/lib.rs b/memory_storage/src/lib.rs index c0754f828..217a606f3 100644 --- a/memory_storage/src/lib.rs +++ b/memory_storage/src/lib.rs @@ -275,7 +275,6 @@ const USE_RATCHET_TREE_LABEL: &[u8] = b"UseRatchetTree"; // related to MlsGroup const JOIN_CONFIG_LABEL: &[u8] = b"MlsGroupJoinConfig"; const OWN_LEAF_NODES_LABEL: &[u8] = b"OwnLeafNodes"; -const AAD_LABEL: &[u8] = b"AAD"; const GROUP_STATE_LABEL: &[u8] = b"GroupState"; const QUEUED_PROPOSAL_LABEL: &[u8] = b"QueuedProposal"; const PROPOSAL_QUEUE_REFS_LABEL: &[u8] = b"ProposalQueueRefs"; @@ -936,19 +935,6 @@ impl StorageProvider for MemoryStorage { self.append::(OWN_LEAF_NODES_LABEL, &key, value) } - fn aad>( - &self, - group_id: &GroupId, - ) -> Result, Self::Error> { - let key = serde_json::to_vec(group_id)?; - self.read::>(AAD_LABEL, &key) - .map(|v| { - // When we didn't find the value, we return an empty vector as - // required by the trait. - v.unwrap_or_default() - }) - } - fn delete_own_leaf_nodes>( &self, group_id: &GroupId, diff --git a/memory_storage/src/test_store.rs b/memory_storage/src/test_store.rs index d521dd328..2e379fb71 100644 --- a/memory_storage/src/test_store.rs +++ b/memory_storage/src/test_store.rs @@ -487,13 +487,6 @@ impl StorageProvider for MemoryStorage { todo!() } - fn aad>( - &self, - _group_id: &GroupId, - ) -> Result, Self::Error> { - todo!() - } - fn queued_proposals< GroupId: traits::GroupId, ProposalRef: traits::ProposalRef, diff --git a/traits/src/storage.rs b/traits/src/storage.rs index d63e4b22b..dcb382da4 100644 --- a/traits/src/storage.rs +++ b/traits/src/storage.rs @@ -267,13 +267,6 @@ pub trait StorageProvider { ) -> Result, Self::Error>; ///ANCHOR_END: own_leaf_nodes - /// Returns the AAD for the group with given id - /// If the value has not been set, returns an empty vector. - fn aad>( - &self, - group_id: &GroupId, - ) -> Result, Self::Error>; - /// Returns references of all queued proposals for the group with group id `group_id`, or an empty vector of none are stored. fn queued_proposal_refs< GroupId: traits::GroupId, From ad07ef5f819fd87e89f9a0b10c0e97404d2fb58f Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 29 Jul 2024 07:19:44 +0200 Subject: [PATCH 33/43] update traits.md in the book for v0.6 --- book/src/traits/traits.md | 10 +++------- traits/src/random.rs | 2 ++ traits/src/traits.rs | 2 ++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/book/src/traits/traits.md b/book/src/traits/traits.md index 205526048..1f1d6b275 100644 --- a/book/src/traits/traits.md +++ b/book/src/traits/traits.md @@ -35,7 +35,7 @@ It simply needs to implement two functions to generate cryptographically secure randomness and store it in an array or vector. ```rust,no_run,noplayground -{{#include ../../../traits/src/random.rs:8:16}} +{{#include ../../../traits/src/random.rs:openmls_rand}} ``` ### OpenMlsCrypto @@ -48,10 +48,6 @@ This trait defines all cryptographic functions required by OpenMLS. In particula - Signatures - HPKE -```rust,no_run,noplayground -{{#include ../../../traits/src/crypto.rs:10}} -``` - ### StorageProvider This trait defines an API for a storage backend that is used for all OpenMLS @@ -126,14 +122,14 @@ fn write_key_package< This allows the application to iterate over the hash references and delete outdated key packages. -### OpenMlsCryptoProvider +### OpenMlsProvider Additionally, there's a wrapper trait defined that is expected to be passed into the public OpenMLS API. Some OpenMLS APIs require only one of the sub-traits, though. ```rust,no_run,noplayground -{{#include ../../../traits/src/traits.rs:15:28}} +{{#include ../../../traits/src/traits.rs:openmls_provider}} ``` ## Implementation Notes diff --git a/traits/src/random.rs b/traits/src/random.rs index 424961a49..c6da9851f 100644 --- a/traits/src/random.rs +++ b/traits/src/random.rs @@ -5,6 +5,7 @@ use std::fmt::Debug; +// ANCHOR: openmls_rand pub trait OpenMlsRand { type Error: std::error::Error + Debug; @@ -14,3 +15,4 @@ pub trait OpenMlsRand { /// Fill a vector of length `len` with bytes. fn random_vec(&self, len: usize) -> Result, Self::Error>; } +// ANCHOR_END: openmls_rand diff --git a/traits/src/traits.rs b/traits/src/traits.rs index 96514da36..a1022d468 100644 --- a/traits/src/traits.rs +++ b/traits/src/traits.rs @@ -23,6 +23,7 @@ pub mod prelude { /// /// An implementation of this trait must be passed in to the public OpenMLS API /// to perform randomness generation, cryptographic operations, and key storage. +// ANCHOR: openmls_provider pub trait OpenMlsProvider { type CryptoProvider: crypto::OpenMlsCrypto; type RandProvider: random::OpenMlsRand; @@ -37,3 +38,4 @@ pub trait OpenMlsProvider { /// Get the randomness provider. fn rand(&self) -> &Self::RandProvider; } +// ANCHOR_END: openmls_provider From 7dd498303cdf2955892044f7b08c7e120e11a32d Mon Sep 17 00:00:00 2001 From: Konrad Kohbrok Date: Tue, 30 Jul 2024 14:35:44 +0200 Subject: [PATCH 34/43] Add `MlsGroup` endpoint to create Add commit without path (#1629) --- CHANGELOG.md | 1 + book/src/user_manual/add_members.md | 6 + .../group/core_group/create_commit_params.rs | 1 - openmls/src/group/mls_group/membership.rs | 52 ++++- .../src/group/tests_and_kats/tests/group.rs | 204 +++++------------- 5 files changed, 109 insertions(+), 155 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8da050c1..05a7d457c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- [#1629](https://github.com/openmls/openmls/pull/1629): Add `add_members_without_update` function to `MlsGroup` to allow the creation of add-only commits - [#1506](https://github.com/openmls/openmls/pull/1506): Add `StagedWelcome` and `StagedCoreWelcome` to make joining a group staged in order to inspect the `Welcome` message. This was followed up with PR [#1533](https://github.com/openmls/openmls/pull/1533) to adjust the API. - [#1516](https://github.com/openmls/openmls/pull/1516): Add `MlsGroup::clear_pending_proposals` to the public API; this allows users to clear a group's internal `ProposalStore` - [#1565](https://github.com/openmls/openmls/pull/1565): Add new `StorageProvider` trait to the `openmls_traits` crate. diff --git a/book/src/user_manual/add_members.md b/book/src/user_manual/add_members.md index eabb33862..a817a8c26 100644 --- a/book/src/user_manual/add_members.md +++ b/book/src/user_manual/add_members.md @@ -10,6 +10,12 @@ Members can be added to the group atomically with the `.add_members()` function. The function returns the tuple `(MlsMessageOut, Welcome)`. The `MlsMessageOut` contains a Commit message that needs to be fanned out to existing group members. The `Welcome` message must be sent to the newly added members. +### Adding members without update + +The `.add_members_without_update()` function functions the same as the `.add_members()` function, except that it will only include an update to the sender's key material if the sender's proposal store includes a proposal that requires a path. For a list of proposals and an indication whether they require a `path` (i.e. a key material update) see [Section 17.4 of RFC 9420](https://www.rfc-editor.org/rfc/rfc9420.html#section-17.4). + +Not sending an update means that the sender will not achieve post-compromise security with this particular commit. However, not sending an update saves on performance both in terms of computation and bandwidth. Using `.add_members_without_update()` can thus be a useful option if the ciphersuite of the group features large public keys and/or expensive encryption operations. + ## Proposal Members can also be added as a proposal (without the corresponding Commit message) by using the `.propose_add_member()` function: diff --git a/openmls/src/group/core_group/create_commit_params.rs b/openmls/src/group/core_group/create_commit_params.rs index 02d205c76..c5fbf4646 100644 --- a/openmls/src/group/core_group/create_commit_params.rs +++ b/openmls/src/group/core_group/create_commit_params.rs @@ -53,7 +53,6 @@ impl<'a> CreateCommitParamsBuilder<'a> { self.ccp.inline_proposals = inline_proposals; self } - #[cfg(test)] pub(crate) fn force_self_update(mut self, force_self_update: bool) -> Self { self.ccp.force_self_update = force_self_update; self diff --git a/openmls/src/group/mls_group/membership.rs b/openmls/src/group/mls_group/membership.rs index c90d4a2e1..6f0d41f59 100644 --- a/openmls/src/group/mls_group/membership.rs +++ b/openmls/src/group/mls_group/membership.rs @@ -20,7 +20,9 @@ impl MlsGroup { /// New members are added by providing a `KeyPackage` for each member. /// /// This operation results in a Commit with a `path`, i.e. it includes an - /// update of the committer's leaf [KeyPackage]. + /// update of the committer's leaf [KeyPackage]. To add members without + /// forcing an update of the committer's leaf [KeyPackage], use + /// [`Self::add_members_without_update()`]. /// /// If successful, it returns a triple of [`MlsMessageOut`]s, where the first /// contains the commit, the second one the [`Welcome`] and the third an optional [GroupInfo] that @@ -39,6 +41,53 @@ impl MlsGroup { ) -> Result< (MlsMessageOut, MlsMessageOut, Option), AddMembersError, + > { + self.add_members_internal(provider, signer, key_packages, true) + } + + /// Adds members to the group. + /// + /// New members are added by providing a `KeyPackage` for each member. + /// + /// This operation results in a Commit that does not necessarily include a + /// `path`, i.e. an update of the committer's leaf [KeyPackage]. In + /// particular, it will only include a path if the group's proposal store + /// includes one or more proposals that require a path (see [Section 17.4 of + /// RFC 9420](https://www.rfc-editor.org/rfc/rfc9420.html#section-17.4) for + /// a list of proposals and whether they require a path). + /// + /// If successful, it returns a triple of [`MlsMessageOut`]s, where the + /// first contains the commit, the second one the [`Welcome`] and the third + /// an optional [GroupInfo] that will be [Some] if the group has the + /// `use_ratchet_tree_extension` flag set. + /// + /// Returns an error if there is a pending commit. + /// + /// [`Welcome`]: crate::messages::Welcome + // FIXME: #1217 + #[allow(clippy::type_complexity)] + pub fn add_members_without_update( + &mut self, + provider: &Provider, + signer: &impl Signer, + key_packages: &[KeyPackage], + ) -> Result< + (MlsMessageOut, MlsMessageOut, Option), + AddMembersError, + > { + self.add_members_internal(provider, signer, key_packages, false) + } + + #[allow(clippy::type_complexity)] + fn add_members_internal( + &mut self, + provider: &Provider, + signer: &impl Signer, + key_packages: &[KeyPackage], + force_self_update: bool, + ) -> Result< + (MlsMessageOut, MlsMessageOut, Option), + AddMembersError, > { self.is_operational()?; @@ -61,6 +110,7 @@ impl MlsGroup { let params = CreateCommitParams::builder() .framing_parameters(self.framing_parameters()) .inline_proposals(inline_proposals) + .force_self_update(force_self_update) .build(); let create_commit_result = self.group.create_commit(params, provider, signer)?; diff --git a/openmls/src/group/tests_and_kats/tests/group.rs b/openmls/src/group/tests_and_kats/tests/group.rs index 774711512..3bfc5f40d 100644 --- a/openmls/src/group/tests_and_kats/tests/group.rs +++ b/openmls/src/group/tests_and_kats/tests/group.rs @@ -1,12 +1,5 @@ -use crate::{ - framing::*, - group::{tests_and_kats::utils::generate_key_package, *}, - schedule::psk::store::ResumptionPskStore, - test_utils::*, - *, -}; -use group::tests_and_kats::utils::generate_credential_with_key; -use mls_group::tests_and_kats::utils::{setup_alice_bob_group, setup_client}; +use crate::{framing::*, group::*, test_utils::*, *}; +use mls_group::tests_and_kats::utils::{setup_alice_bob, setup_alice_bob_group, setup_client}; use treesync::LeafNodeParameters; #[openmls_test::openmls_test] @@ -14,185 +7,90 @@ fn create_commit_optional_path( ciphersuite: Ciphersuite, provider: &impl crate::storage::OpenMlsProvider, ) { - let group_aad = b"Alice's test group"; - // Framing parameters - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); - // Define identities - let alice_credential_with_keys = generate_credential_with_key( - b"Alice".to_vec(), - ciphersuite.signature_algorithm(), - provider, - ); - let bob_credential_with_keys = - generate_credential_with_key(b"Bob".to_vec(), ciphersuite.signature_algorithm(), provider); - - // Generate Bob's KeyPackage - let bob_key_package = generate_key_package( - ciphersuite, - Extensions::empty(), - provider, - bob_credential_with_keys, - ); + let (alice_credential_with_key, alice_signer, bob_kpb, _bob_signer) = + setup_alice_bob(ciphersuite, provider); // Alice creates a group - let mut group_alice = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - alice_credential_with_keys.credential_with_key, - ) - .build(provider, &alice_credential_with_keys.signer) - .expect("Error creating CoreGroup."); + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build(provider, &alice_signer, alice_credential_with_key) + .unwrap(); // Alice proposes to add Bob with forced self-update // Even though there are only Add Proposals, this should generated a path field // on the Commit - let bob_add_proposal = group_alice - .create_add_proposal( - framing_parameters, - bob_key_package.key_package().clone(), - &alice_credential_with_keys.signer, - ) - .expect("Could not create proposal."); - - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + let (commit_message, _welcome, _) = alice_group + .add_members(provider, &alice_signer, &[bob_kpb.key_package().clone()]) + .unwrap(); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .build(); - let create_commit_result = match group_alice.create_commit( - params, /* No PSK fetcher */ - provider, - &alice_credential_with_keys.signer, - ) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; - let commit = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => commit, + let commit = match commit_message.body() { + MlsMessageBodyOut::PublicMessage(pm) => match pm.content() { + FramedContentBody::Commit(commit) => commit, + _ => panic!(), + }, _ => panic!(), }; + assert!(commit.has_path()); + alice_group + .clear_pending_commit(provider.storage()) + .unwrap(); + // Alice adds Bob without forced self-update - // Since there are only Add Proposals, this does not generate a path field on - // the Commit Creating a second proposal to add the same member should - // not fail, only committing that proposal should fail - let bob_add_proposal = group_alice - .create_add_proposal( - framing_parameters, - bob_key_package.key_package().clone(), - &alice_credential_with_keys.signer, - ) - .expect("Could not create proposal."); - - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); + let (commit_message, welcome, _) = alice_group + .add_members_without_update(provider, &alice_signer, &[bob_kpb.key_package().clone()]) + .unwrap(); - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = - match group_alice.create_commit(params, provider, &alice_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; - let commit = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => commit, + let commit = match commit_message.body() { + MlsMessageBodyOut::PublicMessage(pm) => match pm.content() { + FramedContentBody::Commit(commit) => commit, + _ => panic!(), + }, _ => panic!(), }; + assert!(!commit.has_path()); // Alice applies the Commit without the forced self-update - group_alice - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); - let ratchet_tree = group_alice.public_group().export_ratchet_tree(); + alice_group.merge_pending_commit(provider).unwrap(); + let ratchet_tree = alice_group.export_ratchet_tree(); // Bob creates group from Welcome - let group_bob = StagedCoreWelcome::new_from_welcome( - create_commit_result - .welcome_option - .expect("An unexpected error occurred."), - Some(ratchet_tree.into()), - bob_key_package, + let bob_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &MlsGroupJoinConfig::default(), + welcome.into_welcome().unwrap(), + Some(ratchet_tree.into()), ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .unwrap_or_else(|e| panic!("Error creating group from Welcome: {e:?}")); + .unwrap() + .into_group(provider) + .unwrap(); assert_eq!( - group_alice.public_group().export_ratchet_tree(), - group_bob.public_group().export_ratchet_tree() + alice_group.export_ratchet_tree(), + bob_group.export_ratchet_tree() ); // Alice updates - let mut alice_new_leaf_node = group_alice.own_leaf_node().unwrap().clone(); - alice_new_leaf_node - .update( - ciphersuite, - provider, - &alice_credential_with_keys.signer, - group_alice.group_id().clone(), - group_alice.own_leaf_index(), - LeafNodeParameters::default(), - ) + let (commit_message, _, _) = alice_group + .self_update(provider, &alice_signer, LeafNodeParameters::default()) .unwrap(); - let alice_update_proposal = group_alice - .create_update_proposal( - framing_parameters, - alice_new_leaf_node, - &alice_credential_with_keys.signer, - ) - .expect("Could not create proposal."); - - group_alice.proposal_store_mut().empty(); - group_alice.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - alice_update_proposal, - ) - .unwrap(), - ); - // Only UpdateProposal - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - let create_commit_result = - match group_alice.create_commit(params, provider, &alice_credential_with_keys.signer) { - Ok(c) => c, - Err(e) => panic!("Error creating commit: {e:?}"), - }; - let commit = match create_commit_result.commit.content() { - FramedContentBody::Commit(commit) => commit, + let commit = match commit_message.body() { + MlsMessageBodyOut::PublicMessage(pm) => match pm.content() { + FramedContentBody::Commit(commit) => commit, + _ => panic!(), + }, _ => panic!(), }; + assert!(commit.has_path()); // Apply UpdateProposal - group_alice - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); + alice_group.merge_pending_commit(provider).unwrap(); } #[openmls_test::openmls_test] From 96c38a806f9c706d2cf67566c9c846eee3ac4430 Mon Sep 17 00:00:00 2001 From: Konrad Kohbrok Date: Tue, 30 Jul 2024 15:00:39 +0200 Subject: [PATCH 35/43] Migrate remaining `CoreGroup` tests to `MlsGroup` (#1631) --- openmls/src/group/mls_group/mod.rs | 49 ++- .../group/tests_and_kats/tests/encoding.rs | 242 ++++----------- .../src/group/tests_and_kats/tests/framing.rs | 23 +- openmls/src/group/tests_and_kats/utils.rs | 144 +++------ .../tests_and_kats/kats/kat_encryption.rs | 66 +++-- .../kats/kat_message_protection.rs | 279 ++++-------------- 6 files changed, 258 insertions(+), 545 deletions(-) diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 21fca975e..6b371d84c 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -3,9 +3,15 @@ //! This module contains [`MlsGroup`] and its submodules. //! -#[cfg(test)] +#[cfg(any(feature = "test-utils", test))] use crate::schedule::message_secrets::MessageSecrets; +#[cfg(test)] +use openmls_traits::crypto::OpenMlsCrypto; + +#[cfg(test)] +use crate::prelude::SenderRatchetConfiguration; + use super::proposals::{ProposalStore, QueuedProposal}; use crate::{ binary_tree::array_representation::LeafNodeIndex, @@ -469,7 +475,7 @@ impl MlsGroup { self.group.public_group().group_context().tree_hash() } - #[cfg(test)] + #[cfg(any(feature = "test-utils", test))] pub(crate) fn message_secrets_test_mut(&mut self) -> &mut MessageSecrets { self.group.message_secrets_test_mut() } @@ -479,6 +485,45 @@ impl MlsGroup { self.group.print_ratchet_tree(message) } + #[cfg(any(feature = "test-utils", test))] + pub(crate) fn context_mut(&mut self) -> &mut GroupContext { + self.group.context_mut() + } + + // Encrypt an AuthenticatedContent into a PrivateMessage. Only needed for + // the message protection KAT. + #[cfg(test)] + pub(crate) fn encrypt( + &mut self, + public_message: AuthenticatedContent, + padding_size: usize, + provider: &Provider, + ) -> Result> { + self.group.encrypt(public_message, padding_size, provider) + } + + #[cfg(test)] + // Decrypt a ProtocolMessage. Only needed for the message protection KAT. + pub(crate) fn decrypt_message( + &mut self, + crypto: &impl OpenMlsCrypto, + message: ProtocolMessage, + sender_ratchet_configuration: &SenderRatchetConfiguration, + ) -> Result { + self.group + .decrypt_message(crypto, message, sender_ratchet_configuration) + } + + #[cfg(test)] + pub(crate) fn set_group_context(&mut self, group_context: GroupContext) { + self.group.set_group_context(group_context) + } + + #[cfg(test)] + pub(crate) fn set_own_leaf_index(&mut self, own_leaf_index: LeafNodeIndex) { + self.group.set_own_leaf_index(own_leaf_index) + } + /// Returns the underlying [CoreGroup]. #[cfg(test)] pub(crate) fn group(&self) -> &CoreGroup { diff --git a/openmls/src/group/tests_and_kats/tests/encoding.rs b/openmls/src/group/tests_and_kats/tests/encoding.rs index 452c2e7e5..f5b014b60 100644 --- a/openmls/src/group/tests_and_kats/tests/encoding.rs +++ b/openmls/src/group/tests_and_kats/tests/encoding.rs @@ -1,10 +1,13 @@ use openmls_traits::crypto::OpenMlsCrypto; use tls_codec::{Deserialize, Serialize}; -use crate::group::tests_and_kats::utils::*; use crate::{ - binary_tree::LeafNodeIndex, framing::*, group::*, key_packages::*, messages::*, - schedule::psk::store::ResumptionPskStore, test_utils::*, + binary_tree::LeafNodeIndex, + framing::*, + group::{tests_and_kats::utils::*, *}, + key_packages::*, + messages::*, + treesync::LeafNodeParameters, }; /// Creates a simple test setup for various encoding tests. @@ -69,15 +72,14 @@ fn test_application_message_encoding(provider: &impl crate::storage::OpenMlsProv // Test encoding/decoding of Application messages. let message = randombytes(random_usize() % 1000); let aad = randombytes(random_usize() % 1000); + group_state.set_aad(aad); let encrypted_message = group_state - .create_application_message( - &aad, - &message, - 0, - provider, - &credential_with_key_and_signer.signer, - ) - .expect("An unexpected error occurred."); + .create_message(provider, &credential_with_key_and_signer.signer, &message) + .unwrap(); + let encrypted_message = match encrypted_message.body { + MlsMessageBodyOut::PrivateMessage(pm) => pm, + _ => panic!("Expected a PrivateMessage"), + }; let encrypted_message_bytes = encrypted_message .tls_serialize_detached() .expect("An unexpected error occurred."); @@ -100,8 +102,6 @@ fn test_update_proposal_encoding(provider: &impl crate::storage::OpenMlsProvider .get("alice") .expect("An unexpected error occurred.") .borrow(); - // Framing parameters - let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); for group_state in alice.group_states.borrow_mut().values_mut() { let credential_with_key_and_signer = alice @@ -109,29 +109,17 @@ fn test_update_proposal_encoding(provider: &impl crate::storage::OpenMlsProvider .get(&group_state.ciphersuite()) .expect("An unexpected error occurred."); - let key_package_bundle = KeyPackageBundle::generate( - provider, - &credential_with_key_and_signer.signer, - group_state.ciphersuite(), - credential_with_key_and_signer.credential_with_key.clone(), - ); - - let mut update: PublicMessage = group_state - .create_update_proposal( - framing_parameters, - key_package_bundle.key_package().leaf_node().clone(), + let (update, _) = group_state + .propose_self_update( + provider, &credential_with_key_and_signer.signer, + LeafNodeParameters::default(), ) - .expect("Could not create proposal.") - .into(); - update - .set_membership_tag( - provider.crypto(), - group_state.ciphersuite(), - group_state.message_secrets().membership_key(), - group_state.message_secrets().serialized_context(), - ) - .expect("error setting membership tag"); + .unwrap(); + let update = match update.body { + MlsMessageBodyOut::PublicMessage(pm) => pm, + _ => panic!("Expected a PublicMessage"), + }; let update_encoded = update .tls_serialize_detached() .expect("Could not encode proposal."); @@ -154,8 +142,6 @@ fn test_add_proposal_encoding(provider: &impl crate::storage::OpenMlsProvider) { .get("alice") .expect("An unexpected error occurred.") .borrow(); - // Framing parameters - let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); for group_state in alice.group_states.borrow_mut().values_mut() { let credential_with_key_and_signer = alice @@ -171,21 +157,17 @@ fn test_add_proposal_encoding(provider: &impl crate::storage::OpenMlsProvider) { ); // Adds - let mut add: PublicMessage = group_state - .create_add_proposal( - framing_parameters, - key_package_bundle.key_package().clone(), + let (add, _) = group_state + .propose_add_member( + provider, &credential_with_key_and_signer.signer, + key_package_bundle.key_package(), ) - .expect("Could not create proposal.") - .into(); - add.set_membership_tag( - provider.crypto(), - group_state.ciphersuite(), - group_state.message_secrets().membership_key(), - group_state.message_secrets().serialized_context(), - ) - .expect("error setting membership tag"); + .unwrap(); + let add = match add.body { + MlsMessageBodyOut::PublicMessage(pm) => pm, + _ => panic!("Expected a PublicMessage"), + }; let add_encoded = add .tls_serialize_detached() .expect("Could not encode proposal."); @@ -205,8 +187,6 @@ fn test_remove_proposal_encoding(provider: &impl crate::storage::OpenMlsProvider .get("alice") .expect("An unexpected error occurred.") .borrow(); - // Framing parameters - let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); for group_state in alice.group_states.borrow_mut().values_mut() { let credential_with_key_and_signer = alice @@ -214,22 +194,18 @@ fn test_remove_proposal_encoding(provider: &impl crate::storage::OpenMlsProvider .get(&group_state.ciphersuite()) .expect("An unexpected error occurred."); - let mut remove: PublicMessage = group_state - .create_remove_proposal( - framing_parameters, - LeafNodeIndex::new(1), + let (remove, _) = group_state + .propose_remove_member( + provider, &credential_with_key_and_signer.signer, + LeafNodeIndex::new(1), ) - .expect("Could not create proposal.") - .into(); - remove - .set_membership_tag( - provider.crypto(), - group_state.ciphersuite(), - group_state.message_secrets().membership_key(), - group_state.message_secrets().serialized_context(), - ) - .expect("error setting membership tag"); + .unwrap(); + let remove = match remove.body { + MlsMessageBodyOut::PublicMessage(pm) => pm, + _ => panic!("Expected a PublicMessage"), + }; + let remove_encoded = remove .tls_serialize_detached() .expect("Could not encode proposal."); @@ -249,8 +225,6 @@ fn test_commit_encoding(provider: &impl crate::storage::OpenMlsProvider) { .get("alice") .expect("An unexpected error occurred.") .borrow(); - // Framing parameters - let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); for group_state in alice.group_states.borrow_mut().values_mut() { let alice_credential_with_key_and_signer = alice @@ -258,27 +232,7 @@ fn test_commit_encoding(provider: &impl crate::storage::OpenMlsProvider) { .get(&group_state.ciphersuite()) .expect("An unexpected error occurred."); - let alice_key_package_bundle = KeyPackageBundle::generate( - provider, - &alice_credential_with_key_and_signer.signer, - group_state.ciphersuite(), - alice_credential_with_key_and_signer - .credential_with_key - .clone(), - ); - - // Create a few proposals to put into the commit - - // Alice updates her own leaf - let update = group_state - .create_update_proposal( - framing_parameters, - alice_key_package_bundle.key_package().leaf_node().clone(), - &alice_credential_with_key_and_signer.signer, - ) - .expect("Could not create proposal."); - - // Alice adds Charlie to the group + // Alice updates her own leaf and adds Charlie to the group let charlie_key_package = test_setup ._key_store .borrow_mut() @@ -286,49 +240,18 @@ fn test_commit_encoding(provider: &impl crate::storage::OpenMlsProvider) { .expect("An unexpected error occurred.") .pop() .expect("An unexpected error occurred."); - let add = group_state - .create_add_proposal( - framing_parameters, - charlie_key_package.clone(), - &alice_credential_with_key_and_signer.signer, - ) - .expect("Could not create proposal."); - - let ciphersuite = group_state.ciphersuite(); - - group_state.proposal_store_mut().empty(); - group_state.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref(ciphersuite, provider.crypto(), add) - .unwrap(), - ); - group_state.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - update, - ) - .unwrap(), - ); - - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .build(); - let create_commit_result = group_state - .create_commit( - params, + let (commit, _, _) = group_state + .add_members( provider, &alice_credential_with_key_and_signer.signer, + &[charlie_key_package.clone()], ) - .expect("An unexpected error occurred."); - let mut commit: PublicMessage = create_commit_result.commit.into(); - commit - .set_membership_tag( - provider.crypto(), - group_state.ciphersuite(), - group_state.message_secrets().membership_key(), - group_state.message_secrets().serialized_context(), - ) - .expect("error setting membership tag"); + .expect("Could not create commit."); + + let commit = match commit.body { + MlsMessageBodyOut::PublicMessage(pm) => pm, + _ => panic!("Expected a PublicMessage"), + }; let commit_encoded = commit .tls_serialize_detached() .expect("An unexpected error occurred."); @@ -348,8 +271,6 @@ fn test_welcome_message_encoding(provider: &impl crate::storage::OpenMlsProvider .get("alice") .expect("An unexpected error occurred.") .borrow(); - // Framing parameters - let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); for group_state in alice.group_states.borrow_mut().values_mut() { let credential_with_key_and_signer = alice @@ -367,39 +288,17 @@ fn test_welcome_message_encoding(provider: &impl crate::storage::OpenMlsProvider .expect("An unexpected error occurred.") .pop() .expect("An unexpected error occurred."); - let add = group_state - .create_add_proposal( - framing_parameters, - charlie_key_package.clone(), + let (_commit, welcome, _) = group_state + .add_members( + provider, &credential_with_key_and_signer.signer, + &[charlie_key_package.clone()], ) - .expect("Could not create proposal."); - - let ciphersuite = group_state.ciphersuite(); - - group_state.proposal_store_mut().empty(); - group_state.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref(ciphersuite, provider.crypto(), add) - .unwrap(), - ); - - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .build(); - let create_commit_result = group_state - .create_commit(params, provider, &credential_with_key_and_signer.signer) - .expect("An unexpected error occurred."); - // Alice applies the commit - group_state - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging own commits"); + .expect("Could not create commit."); + group_state.merge_pending_commit(provider).unwrap(); + let welcome = welcome.into_welcome().unwrap(); // Welcome messages - - let welcome = create_commit_result - .welcome_option - .expect("An unexpected error occurred."); - let welcome_encoded = welcome .tls_serialize_detached() .expect("An unexpected error occurred."); @@ -410,25 +309,14 @@ fn test_welcome_message_encoding(provider: &impl crate::storage::OpenMlsProvider assert_eq!(welcome, welcome_decoded); - let charlie = test_clients - .get("charlie") - .expect("An unexpected error occurred.") - .borrow(); - - let charlie_key_package_bundle = charlie - .find_key_package_bundle(&charlie_key_package, provider.crypto()) - .expect("An unexpected error occurred."); - // This makes Charlie decode the internals of the Welcome message, for // example the RatchetTreeExtension. - assert!(StagedCoreWelcome::new_from_welcome( - welcome, - Some(group_state.public_group().export_ratchet_tree().into()), - charlie_key_package_bundle, - provider, - ResumptionPskStore::new(1024), - ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - .is_ok()); + let config = MlsGroupJoinConfig::default(); + let ratchet_tree = Some(group_state.export_ratchet_tree().into()); + let charlie_group = + StagedWelcome::new_from_welcome(provider, &config, welcome, ratchet_tree) + .unwrap() + .into_group(provider); + assert!(charlie_group.is_ok()); } } diff --git a/openmls/src/group/tests_and_kats/tests/framing.rs b/openmls/src/group/tests_and_kats/tests/framing.rs index 17052e804..8d133260f 100644 --- a/openmls/src/group/tests_and_kats/tests/framing.rs +++ b/openmls/src/group/tests_and_kats/tests/framing.rs @@ -63,18 +63,23 @@ fn padding(provider: &impl crate::storage::OpenMlsProvider) { .credentials .get(&group_state.ciphersuite()) .expect("An unexpected error occurred."); + // Set the padding size + let mut new_config = group_state.configuration().clone(); + new_config.padding_size = padding_size; + group_state + .set_configuration(provider.storage(), &new_config) + .unwrap(); for _ in 0..10 { let message = randombytes(random_usize() % 1000); let aad = randombytes(random_usize() % 1000); - let private_message = group_state - .create_application_message( - &aad, - &message, - padding_size, - provider, - &credential.signer, - ) - .expect("An unexpected error occurred."); + group_state.set_aad(aad); + let application_message = group_state + .create_message(provider, &credential.signer, &message) + .unwrap(); + let private_message = match application_message.body() { + MlsMessageBodyOut::PrivateMessage(pm) => pm, + _ => panic!("Unexpected match."), + }; let ciphertext = private_message.ciphertext(); let length = ciphertext.len(); let overflow = if padding_size > 0 { diff --git a/openmls/src/group/tests_and_kats/utils.rs b/openmls/src/group/tests_and_kats/utils.rs index 4122d9cac..9907b7fd5 100644 --- a/openmls/src/group/tests_and_kats/utils.rs +++ b/openmls/src/group/tests_and_kats/utils.rs @@ -7,14 +7,13 @@ use std::{cell::RefCell, collections::HashMap}; use openmls_basic_credential::SignatureKeyPair; -use openmls_traits::crypto::OpenMlsCrypto; use openmls_traits::{signatures::Signer, types::SignatureScheme}; use rand::{rngs::OsRng, RngCore}; use tls_codec::Serialize; use crate::{ ciphersuite::signable::Signable, credentials::*, framing::*, group::*, key_packages::*, - messages::ConfirmationTag, schedule::psk::store::ResumptionPskStore, test_utils::*, *, + messages::ConfirmationTag, test_utils::*, *, }; use self::storage::OpenMlsProvider; @@ -45,22 +44,7 @@ pub(crate) struct TestSetupConfig { /// A client in a test setup. pub(crate) struct TestClient { pub(crate) credentials: HashMap, - pub(crate) key_package_bundles: RefCell>, - pub(crate) group_states: RefCell>, -} - -impl TestClient { - pub(crate) fn find_key_package_bundle( - &self, - key_package: &KeyPackage, - crypto: &impl OpenMlsCrypto, - ) -> Option { - let mut key_package_bundles = self.key_package_bundles.borrow_mut(); - key_package_bundles - .iter() - .position(|x| x.key_package().hash_ref(crypto) == key_package.hash_ref(crypto)) - .map(|index| key_package_bundles.remove(index)) - } + pub(crate) group_states: RefCell>, } /// The state of a test setup, including the state of the clients and the @@ -117,7 +101,6 @@ pub(crate) fn setup( // Create the client. let test_client = TestClient { credentials, - key_package_bundles: RefCell::new(key_package_bundles), group_states: RefCell::new(HashMap::new()), }; test_clients.insert(client.name, RefCell::new(test_client)); @@ -139,30 +122,30 @@ pub(crate) fn setup( .get(&group_config.ciphersuite) .expect("An unexpected error occurred."); // Initialize the group state for the initial member. - let core_group = CoreGroup::builder( - GroupId::from_slice(&group_id.to_be_bytes()), - group_config.ciphersuite, - credential_with_key_and_signer.credential_with_key.clone(), - ) - .with_config(group_config.config) - .build(provider, &credential_with_key_and_signer.signer) - .expect("Error creating new CoreGroup"); - let mut proposal_list = Vec::new(); - let group_aad = b""; - // Framing parameters - let framing_parameters = FramingParameters::new(group_aad, WireFormat::PublicMessage); + let mls_group = MlsGroup::builder() + .with_group_id(GroupId::from_slice(&group_id.to_be_bytes())) + .ciphersuite(group_config.ciphersuite) + .use_ratchet_tree_extension(group_config.config.add_ratchet_tree_extension) + .with_wire_format_policy(PURE_PLAINTEXT_WIRE_FORMAT_POLICY) + .build( + provider, + &credential_with_key_and_signer.signer, + credential_with_key_and_signer.credential_with_key.clone(), + ) + .expect("Error creating group."); initial_group_member .group_states .borrow_mut() - .insert(core_group.context().group_id().clone(), core_group); + .insert(mls_group.group_id().clone(), mls_group); // If there is more than one member in the group, prepare proposals and // commit. Then distribute the Welcome message to the new // members. if group_config.members.len() > 1 { let mut group_states = initial_group_member.group_states.borrow_mut(); - let core_group = group_states + let mls_group = group_states .get_mut(&GroupId::from_slice(&group_id.to_be_bytes())) .expect("An unexpected error occurred."); + let mut key_packages = vec![]; for client_id in 1..group_config.members.len() { // Pull a KeyPackage from the key_store for the new member. let next_member_key_package = key_store @@ -173,96 +156,45 @@ pub(crate) fn setup( .expect("An unexpected error occurred.") .pop() .expect("An unexpected error occurred."); - // Have the initial member create an Add proposal using the new - // KeyPackage. - let add_proposal = core_group - .create_add_proposal( - framing_parameters, - next_member_key_package, - &credential_with_key_and_signer.signer, - ) - .expect("An unexpected error occurred."); - proposal_list.push(add_proposal); + key_packages.push(next_member_key_package.clone()); } // Create the commit based on the previously compiled list of // proposals. - for proposal in proposal_list { - core_group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - group_config.ciphersuite, - provider.crypto(), - proposal, - ) - .expect("Could not create staged proposal."), - ); - } - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .build(); - let create_commit_result = core_group - .create_commit(params, provider, &credential_with_key_and_signer.signer) - .expect("An unexpected error occurred."); - let welcome = create_commit_result - .welcome_option + let (_commit, welcome, _) = mls_group + .add_members( + provider, + &credential_with_key_and_signer.signer, + &key_packages, + ) .expect("An unexpected error occurred."); + let welcome = welcome.into_welcome().unwrap(); - core_group - .merge_staged_commit(provider, create_commit_result.staged_commit) + mls_group + .merge_pending_commit(provider) .expect("Error merging commit."); + let join_config = MlsGroupJoinConfig::builder() + .wire_format_policy(PURE_CIPHERTEXT_WIRE_FORMAT_POLICY) + .build(); + // Distribute the Welcome message to the other members. for client_id in 1..group_config.members.len() { let new_group_member = test_clients .get(group_config.members[client_id].name) .expect("An unexpected error occurred.") .borrow_mut(); - // Figure out which key package bundle we should use. This is - // a bit ugly and inefficient. - let member_secret = welcome - .secrets() - .iter() - .find(|x| { - new_group_member - .key_package_bundles - .borrow() - .iter() - .any(|y| { - y.key_package() - .hash_ref(provider.crypto()) - .expect("Could not hash KeyPackage.") - == x.new_member() - }) - }) - .expect("An unexpected error occurred."); - let kpb_position = new_group_member - .key_package_bundles - .borrow() - .iter() - .position(|y| { - y.key_package() - .hash_ref(provider.crypto()) - .expect("Could not hash KeyPackage.") - == member_secret.new_member() - }) - .expect("An unexpected error occurred."); - let key_package_bundle = new_group_member - .key_package_bundles - .borrow_mut() - .remove(kpb_position); // Create the local group state of the new member based on the // Welcome. - let new_group = match StagedCoreWelcome::new_from_welcome( - welcome.clone(), - Some(core_group.public_group().export_ratchet_tree().into()), - key_package_bundle, + let ratchet_tree = Some(mls_group.export_ratchet_tree().into()); + let new_group = StagedWelcome::new_from_welcome( provider, - ResumptionPskStore::new(1024), + &join_config, + welcome.clone(), + ratchet_tree, ) - .and_then(|staged_join| staged_join.into_core_group(provider)) - { - Ok(group) => group, - Err(err) => panic!("Error creating new group from Welcome: {err:?}"), - }; + .unwrap() + .into_group(provider) + .unwrap(); new_group_member .group_states diff --git a/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs b/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs index 0b430b6c5..38f9c623d 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_encryption.rs @@ -161,20 +161,17 @@ fn generate_credential( fn group( ciphersuite: Ciphersuite, provider: &impl OpenMlsProvider, -) -> (CoreGroup, CredentialWithKey, SignatureKeyPair) { +) -> (MlsGroup, CredentialWithKey, SignatureKeyPair) { let (credential_with_key, signer) = generate_credential( "Kreator".into(), ciphersuite.signature_algorithm(), provider, ); - let group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - credential_with_key.clone(), - ) - .build(provider, &signer) - .unwrap(); + let group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .build(provider, &signer, credential_with_key.clone()) + .unwrap(); (group, credential_with_key, signer) } @@ -184,15 +181,17 @@ fn receiver_group( ciphersuite: Ciphersuite, provider: &impl OpenMlsProvider, group_id: GroupId, -) -> (CoreGroup, CredentialWithKey, SignatureKeyPair) { +) -> (MlsGroup, CredentialWithKey, SignatureKeyPair) { let (credential_with_key, signer) = generate_credential( "Receiver".into(), ciphersuite.signature_algorithm(), provider, ); - let group = CoreGroup::builder(group_id, ciphersuite, credential_with_key.clone()) - .build(provider, &signer) + let group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_group_id(group_id) + .build(provider, &signer, credential_with_key.clone()) .unwrap(); (group, credential_with_key, signer) @@ -202,7 +201,7 @@ fn receiver_group( #[cfg(any(feature = "test-utils", test))] fn build_handshake_messages( sender_index: LeafNodeIndex, - group: &mut CoreGroup, + group: &mut MlsGroup, signer: &impl Signer, provider: &impl OpenMlsProvider, ) -> (Vec, Vec) { @@ -223,7 +222,7 @@ fn build_handshake_messages( Proposal::Remove(RemoveProposal { removed: LeafNodeIndex::new(7), }), // XXX: use random removed - group.context(), + group.export_group_context(), signer, ) .expect("An unexpected error occurred."), @@ -235,7 +234,10 @@ fn build_handshake_messages( provider.crypto(), group.ciphersuite(), &membership_key, - &group.context().tls_serialize_detached().unwrap(), + &group + .export_group_context() + .tls_serialize_detached() + .unwrap(), ) .expect("Error setting membership tag."); let ciphertext = PrivateMessage::encrypt_without_check( @@ -259,7 +261,7 @@ fn build_handshake_messages( #[cfg(any(feature = "test-utils", test))] fn build_application_messages( sender_index: LeafNodeIndex, - group: &mut CoreGroup, + group: &mut MlsGroup, signer: &impl Signer, provider: &impl OpenMlsProvider, ) -> (Vec, Vec) { @@ -276,7 +278,7 @@ fn build_application_messages( sender_index, &[1, 2, 3], &[4, 5, 6], - group.context(), + group.export_group_context(), signer, ) .expect("An unexpected error occurred."); @@ -286,7 +288,10 @@ fn build_application_messages( provider.crypto(), group.ciphersuite(), &membership_key, - &group.context().tls_serialize_detached().unwrap(), + &group + .export_group_context() + .tls_serialize_detached() + .unwrap(), ) .expect("Error setting membership tag."); let ciphertext = match PrivateMessage::encrypt_without_check( @@ -321,25 +326,25 @@ pub fn generate_test_vector( use crate::binary_tree::array_representation::TreeSize; let ciphersuite_name = ciphersuite; - let crypto = OpenMlsRustCrypto::default(); - let encryption_secret_bytes = crypto + let provider = OpenMlsRustCrypto::default(); + let encryption_secret_bytes = provider .rand() .random_vec(ciphersuite.hash_length()) .expect("An unexpected error occurred."); - let sender_data_secret = SenderDataSecret::random(ciphersuite, crypto.rand()); + let sender_data_secret = SenderDataSecret::random(ciphersuite, provider.rand()); let sender_data_secret_bytes = sender_data_secret.as_slice(); // Create sender_data_key/secret - let ciphertext = crypto + let ciphertext = provider .rand() .random_vec(77) .expect("An unexpected error occurred."); let sender_data_key = sender_data_secret - .derive_aead_key(crypto.crypto(), ciphersuite, &ciphertext) + .derive_aead_key(provider.crypto(), ciphersuite, &ciphertext) .expect("Could not derive AEAD key."); // Derive initial nonce from the key schedule using the ciphertext. let sender_data_nonce = sender_data_secret - .derive_aead_nonce(ciphersuite, crypto.crypto(), &ciphertext) + .derive_aead_nonce(ciphersuite, provider.crypto(), &ciphertext) .expect("Could not derive nonce."); let sender_data_info = SenderDataInfo { ciphertext: bytes_to_hex(&ciphertext), @@ -347,7 +352,7 @@ pub fn generate_test_vector( nonce: bytes_to_hex(sender_data_nonce.as_slice()), }; - let (mut group, _, signer) = group(ciphersuite, &crypto); + let (mut group, _, signer) = group(ciphersuite, &provider); *group.message_secrets_test_mut().sender_data_secret_mut() = SenderDataSecret::from_slice(sender_data_secret_bytes); @@ -372,7 +377,7 @@ pub fn generate_test_vector( let (application_secret_key, application_secret_nonce) = decryption_secret_tree .secret_for_decryption( ciphersuite, - crypto.crypto(), + provider.crypto(), sender_leaf, SecretType::ApplicationSecret, generation, @@ -382,7 +387,7 @@ pub fn generate_test_vector( let application_key_string = bytes_to_hex(application_secret_key.as_slice()); let application_nonce_string = bytes_to_hex(application_secret_nonce.as_slice()); let (application_plaintext, application_ciphertext) = - build_application_messages(sender_leaf, &mut group, &signer, &crypto); + build_application_messages(sender_leaf, &mut group, &signer, &provider); println!("Sender Group: {group:?}"); application.push(RatchetStep { key: application_key_string, @@ -395,7 +400,7 @@ pub fn generate_test_vector( let (handshake_secret_key, handshake_secret_nonce) = decryption_secret_tree .secret_for_decryption( ciphersuite, - crypto.crypto(), + provider.crypto(), sender_leaf, SecretType::HandshakeSecret, generation, @@ -406,7 +411,7 @@ pub fn generate_test_vector( let handshake_nonce_string = bytes_to_hex(handshake_secret_nonce.as_slice()); let (handshake_plaintext, handshake_ciphertext) = - build_handshake_messages(sender_leaf, &mut group, &signer, &crypto); + build_handshake_messages(sender_leaf, &mut group, &signer, &provider); handshake.push(RatchetStep { key: handshake_key_string, @@ -599,7 +604,10 @@ pub fn run_test_vector( sender_data_secret.clone(), MembershipKey::random(ciphersuite, provider.rand()), // we don't care about this value ConfirmationKey::random(ciphersuite, provider.rand()), // we don't care about this value - group.context().tls_serialize_detached().unwrap(), + group + .export_group_context() + .tls_serialize_detached() + .unwrap(), fresh_secret_tree.clone(), ); diff --git a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs index cf5985a8f..1dac2d5fe 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs @@ -62,7 +62,6 @@ //! * When protecting the Commit message, add the supplied confirmation tag use openmls_basic_credential::SignatureKeyPair; -use openmls_traits::types::SignatureScheme; use serde::{self, Deserialize, Serialize}; use crate::{ @@ -104,65 +103,6 @@ pub struct MessageProtectionTest { application_priv: String, } -fn generate_credential( - identity: Vec, - signature_algorithm: SignatureScheme, - provider: &impl crate::storage::OpenMlsProvider, -) -> (CredentialWithKey, SignatureKeyPair) { - let credential = BasicCredential::new(identity); - let signature_keys = SignatureKeyPair::new(signature_algorithm).unwrap(); - signature_keys.store(provider.storage()).unwrap(); - - ( - CredentialWithKey { - credential: credential.into(), - signature_key: signature_keys.to_public_vec().into(), - }, - signature_keys, - ) -} - -#[cfg(any(feature = "test-utils", test))] -fn group( - ciphersuite: Ciphersuite, - provider: &impl crate::storage::OpenMlsProvider, -) -> (CoreGroup, CredentialWithKey, SignatureKeyPair) { - let (credential_with_key, signer) = generate_credential( - "Kreator".into(), - ciphersuite.signature_algorithm(), - provider, - ); - - let group = CoreGroup::builder( - GroupId::random(provider.rand()), - ciphersuite, - credential_with_key.clone(), - ) - .build(provider, &signer) - .unwrap(); - - (group, credential_with_key, signer) -} - -#[cfg(any(feature = "test-utils", test))] -fn receiver_group( - ciphersuite: Ciphersuite, - provider: &impl crate::storage::OpenMlsProvider, - group_id: GroupId, -) -> (CoreGroup, CredentialWithKey, SignatureKeyPair) { - let (credential_with_key, signer) = generate_credential( - "Receiver".into(), - ciphersuite.signature_algorithm(), - provider, - ); - - let group = CoreGroup::builder(group_id, ciphersuite, credential_with_key.clone()) - .build(provider, &signer) - .unwrap(); - - (group, credential_with_key, signer) -} - #[cfg(test)] pub fn run_test_vector( test: MessageProtectionTest, @@ -218,7 +158,7 @@ pub fn run_test_vector( ciphersuite: Ciphersuite, test: &MessageProtectionTest, sender: bool, - ) -> CoreGroup { + ) -> MlsGroup { let group_context = GroupContext::new( ciphersuite, GroupId::from_slice(&hex_to_bytes(&test.group_id)), @@ -240,16 +180,18 @@ pub fn run_test_vector( random_own_signature_key.to_vec(), ); - let mut group = CoreGroup::builder( - group_context.group_id().clone(), - ciphersuite, - CredentialWithKey { - credential: credential.into(), - signature_key: random_own_signature_key.into(), - }, - ) - .build(provider, &signer) - .unwrap(); + let mut group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .with_wire_format_policy(MIXED_PLAINTEXT_WIRE_FORMAT_POLICY) + .build( + provider, + &signer, + CredentialWithKey { + credential: credential.into(), + signature_key: random_own_signature_key.into(), + }, + ) + .unwrap(); let credential = BasicCredential::new("Fake user".into()); let signature_keys = SignatureKeyPair::new(ciphersuite.signature_algorithm()).unwrap(); @@ -263,33 +205,10 @@ pub fn run_test_vector( }, ); let bob_key_package = bob_key_package_bundle.key_package(); - let framing_parameters = FramingParameters::new(&[], WireFormat::PublicMessage); - let bob_add_proposal = group - .create_add_proposal(framing_parameters, bob_key_package.clone(), &signer) - .expect("Could not create proposal."); - - group.proposal_store_mut().empty(); - group.proposal_store_mut().add( - QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - bob_add_proposal, - ) - .unwrap(), - ); - - let params = CreateCommitParams::builder() - .framing_parameters(framing_parameters) - .force_self_update(false) - .build(); - - let create_commit_result = group - .create_commit(params, provider, &signer) - .expect("Error creating Commit"); - - group - .merge_commit(provider, create_commit_result.staged_commit) - .expect("error merging pending commit"); + let (_commit, _welcome, _) = group + .add_members(provider, &signature_keys, &[bob_key_package.clone()]) + .unwrap(); + group.merge_pending_commit(provider).unwrap(); // Inject the test values into the group @@ -331,34 +250,19 @@ pub fn run_test_vector( MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.proposal_priv)).unwrap(); fn test_proposal_pub( - mut group: CoreGroup, + mut group: MlsGroup, provider: &impl crate::storage::OpenMlsProvider, - ciphersuite: Ciphersuite, proposal: ProposalIn, proposal_pub: MlsMessageIn, ) { - // Group stuff we need for openmls - let sender_ratchet_config = SenderRatchetConfiguration::new(0, 0); - // check that the proposal in proposal_pub == proposal - let decrypted_message = group - .decrypt_message( - provider.crypto(), - proposal_pub.into_protocol_message().unwrap(), - &sender_ratchet_config, - ) - .unwrap(); - - let processed_unverified_message = group - .public_group() - .parse_message(decrypted_message, group.message_secrets_store()) + let processed_message = group + .process_message(provider, proposal_pub.into_protocol_message().unwrap()) .unwrap(); - let processed_message: AuthenticatedContent = processed_unverified_message - .verify(ciphersuite, provider, ProtocolVersion::Mls10) - .unwrap() - .0; - match processed_message.content().to_owned() { - FramedContentBody::Proposal(p) => assert_eq!(proposal, p.into()), + match processed_message.content() { + ProcessedMessageContent::ProposalMessage(p) => { + assert_eq!(proposal, p.proposal().to_owned().into()) + } _ => panic!("Wrong processed message content"), } } @@ -366,41 +270,12 @@ pub fn run_test_vector( test_proposal_pub( setup_group(provider, ciphersuite, &test, false), provider, - ciphersuite, proposal.clone(), proposal_pub, ); - fn test_proposal_priv( - mut group: CoreGroup, - provider: &impl crate::storage::OpenMlsProvider, - proposal: ProposalIn, - proposal_priv: MlsMessageIn, - ) { - // Group stuff we need for openmls - let sender_ratchet_config = SenderRatchetConfiguration::new(0, 0); - - // decrypt private message - let processed_message = group - .process_message( - provider, - proposal_priv.into_protocol_message().unwrap(), - &sender_ratchet_config, - &[], - ) - .unwrap(); - - // check that proposal == processed_message - match processed_message.content().to_owned() { - ProcessedMessageContent::ProposalMessage(p) => { - assert_eq!(proposal, p.proposal().to_owned().into()) - } - _ => panic!("Wrong processed message content"), - } - } - let group = setup_group(provider, ciphersuite, &test, false); - test_proposal_priv(group, provider, proposal.clone(), proposal_priv); + test_proposal_pub(group, provider, proposal.clone(), proposal_priv); // Wrap `proposal` into a `PrivateMessage`. let group = setup_group(provider, ciphersuite, &test, false); @@ -416,10 +291,12 @@ pub fn run_test_vector( let my_proposal_priv = sender_group .encrypt(proposal_authenticated_content, 0, provider) .unwrap(); - let my_proposal_priv_out = - MlsMessageOut::from_private_message(my_proposal_priv, group.version()); + let my_proposal_priv_out = MlsMessageOut::from_private_message( + my_proposal_priv, + group.export_group_context().protocol_version(), + ); - test_proposal_priv( + test_proposal_pub( group, provider, proposal.clone(), @@ -428,7 +305,7 @@ pub fn run_test_vector( // Wrap `proposal` into a `PublicMessage`. let group = setup_group(provider, ciphersuite, &test, false); - let sender_group = setup_group(provider, ciphersuite, &test, true); + let mut sender_group = setup_group(provider, ciphersuite, &test, true); let proposal_authenticated_content = AuthenticatedContent::member_proposal( FramingParameters::new(&[], WireFormat::PublicMessage), sender_index, @@ -442,19 +319,16 @@ pub fn run_test_vector( .set_membership_tag( provider.crypto(), ciphersuite, - sender_group.message_secrets().membership_key(), - sender_group.message_secrets().serialized_context(), + &sender_group + .message_secrets_test_mut() + .membership_key() + .clone(), + sender_group.message_secrets_test_mut().serialized_context(), ) .expect("error setting membership tag"); let my_proposal_pub_out: MlsMessageOut = my_proposal_pub.into(); - test_proposal_pub( - group, - provider, - ciphersuite, - proposal, - my_proposal_pub_out.into(), - ); + test_proposal_pub(group, provider, proposal, my_proposal_pub_out.into()); } // Commit @@ -466,7 +340,7 @@ pub fn run_test_vector( MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.commit_priv)).unwrap(); fn test_commit_pub( - mut group: CoreGroup, + mut group: MlsGroup, provider: &impl crate::storage::OpenMlsProvider, ciphersuite: Ciphersuite, commit: CommitIn, @@ -475,7 +349,7 @@ pub fn run_test_vector( // Group stuff we need for openmls let sender_ratchet_config = SenderRatchetConfiguration::new(10, 10); - // check that the proposal in proposal_pub == proposal + // check that the commit in commit_pub == commit let decrypted_message = group .decrypt_message( provider.crypto(), @@ -485,8 +359,9 @@ pub fn run_test_vector( .unwrap(); let processed_unverified_message = group + .group() .public_group() - .parse_message(decrypted_message, group.message_secrets_store()) + .parse_message(decrypted_message, group.group().message_secrets_store()) .unwrap(); let processed_message: AuthenticatedContent = processed_unverified_message .verify(ciphersuite, provider, ProtocolVersion::Mls10) @@ -508,42 +383,7 @@ pub fn run_test_vector( commit_pub, ); - fn test_commit_priv( - mut group: CoreGroup, - provider: &impl crate::storage::OpenMlsProvider, - ciphersuite: Ciphersuite, - commit: CommitIn, - commit_priv: MlsMessageIn, - ) { - // Group stuff we need for openmls - let sender_ratchet_config = SenderRatchetConfiguration::new(10, 10); - - // check that the proposal in proposal_priv == proposal - let decrypted_message = group - .decrypt_message( - provider.crypto(), - commit_priv.into_protocol_message().unwrap(), - &sender_ratchet_config, - ) - .unwrap(); - - let processed_unverified_message = group - .public_group() - .parse_message(decrypted_message, group.message_secrets_store()) - .unwrap(); - let processed_message: AuthenticatedContent = processed_unverified_message - .verify(ciphersuite, provider, ProtocolVersion::Mls10) - .unwrap() - .0; - match processed_message.content().to_owned() { - FramedContentBody::Commit(c) => { - assert_eq!(commit, CommitIn::from(c)) - } - _ => panic!("Wrong processed message content"), - } - } - - test_commit_priv( + test_commit_pub( setup_group(provider, ciphersuite, &test, false), provider, ciphersuite, @@ -568,10 +408,12 @@ pub fn run_test_vector( let my_commit_pub = sender_group .encrypt(commit_authenticated_content, 0, provider) .unwrap(); - let my_commit_priv_out = - MlsMessageOut::from_private_message(my_commit_pub, group.version()); + let my_commit_priv_out = MlsMessageOut::from_private_message( + my_commit_pub, + group.export_group_context().protocol_version(), + ); - test_commit_priv( + test_commit_pub( group, provider, ciphersuite, @@ -581,7 +423,7 @@ pub fn run_test_vector( // Wrap `commit` into a `PublicMessage`. let group = setup_group(provider, ciphersuite, &test, false); - let sender_group = setup_group(provider, ciphersuite, &test, true); + let mut sender_group = setup_group(provider, ciphersuite, &test, true); let mut commit_authenticated_content = AuthenticatedContent::commit( FramingParameters::new(&[], WireFormat::PublicMessage), Sender::Member(sender_index), @@ -598,8 +440,11 @@ pub fn run_test_vector( .set_membership_tag( provider.crypto(), ciphersuite, - sender_group.message_secrets().membership_key(), - sender_group.message_secrets().serialized_context(), + &sender_group + .message_secrets_test_mut() + .membership_key() + .clone(), + sender_group.message_secrets_test_mut().serialized_context(), ) .expect("error setting membership tag"); let my_commit_pub_out: MlsMessageOut = my_commit_pub_msg.into(); @@ -620,22 +465,14 @@ pub fn run_test_vector( MlsMessageIn::tls_deserialize_exact(hex_to_bytes(&test.application_priv)).unwrap(); fn test_application_priv( - mut group: CoreGroup, + mut group: MlsGroup, provider: &impl crate::storage::OpenMlsProvider, application: Vec, application_priv: MlsMessageIn, ) { - // Group stuff we need for openmls - let sender_ratchet_config = SenderRatchetConfiguration::new(0, 0); - // check that the proposal in proposal_pub == proposal let processed_message = group - .process_message( - provider, - application_priv.into_ciphertext().unwrap(), - &sender_ratchet_config, - &[], - ) + .process_message(provider, application_priv.into_protocol_message().unwrap()) .unwrap(); match processed_message.into_content() { ProcessedMessageContent::ApplicationMessage(a) => { @@ -655,16 +492,14 @@ pub fn run_test_vector( // Wrap `application` into a `PrivateMessage`. let mut sender_group = setup_group(provider, ciphersuite, &test, true); let private_message = sender_group - .create_application_message(&[], &application, 0, provider, &signer) + .create_message(provider, &signer, &application) .unwrap(); - let my_application_priv_out = - MlsMessageOut::from_private_message(private_message, sender_group.version()); test_application_priv( setup_group(provider, ciphersuite, &test, false), provider, application.clone(), - my_application_priv_out.into(), + private_message.into(), ); } From 5e7ba805859f823b4b185312c5bf92805d877b44 Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Tue, 6 Aug 2024 15:46:42 +0200 Subject: [PATCH 36/43] Remove MlsSerializedMlsGroup (#1637) --- cli/src/user.rs | 1 - openmls/examples/large-groups.rs | 62 +++++++++++++++--------------- openmls/src/group/mls_group/mod.rs | 1 - openmls/src/group/mls_group/ser.rs | 62 ------------------------------ openmls/src/prelude.rs | 2 +- 5 files changed, 32 insertions(+), 96 deletions(-) delete mode 100644 openmls/src/group/mls_group/ser.rs diff --git a/cli/src/user.rs b/cli/src/user.rs index 9440996df..a0f005d9b 100644 --- a/cli/src/user.rs +++ b/cli/src/user.rs @@ -29,7 +29,6 @@ impl Contact { } } -#[derive(serde::Serialize, serde::Deserialize)] pub struct Group { group_name: String, conversation: Conversation, diff --git a/openmls/examples/large-groups.rs b/openmls/examples/large-groups.rs index 2b77a94a9..c255dfaae 100644 --- a/openmls/examples/large-groups.rs +++ b/openmls/examples/large-groups.rs @@ -14,7 +14,9 @@ use clap::Parser; use openmls::{ credentials::{BasicCredential, CredentialWithKey}, framing::{MlsMessageIn, MlsMessageOut, ProcessedMessageContent}, - group::{MlsGroup, MlsGroupCreateConfig, StagedWelcome, PURE_PLAINTEXT_WIRE_FORMAT_POLICY}, + group::{ + GroupId, MlsGroup, MlsGroupCreateConfig, StagedWelcome, PURE_PLAINTEXT_WIRE_FORMAT_POLICY, + }, prelude::LeafNodeIndex, prelude_test::*, treesync::LeafNodeParameters, @@ -30,6 +32,7 @@ struct Member { provider: OpenMlsRustCrypto, credential_with_key: CredentialWithKey, signer: SignatureKeyPair, + group_id: GroupId, } #[derive(Debug, Default, Serialize, Deserialize)] @@ -38,7 +41,7 @@ struct SerializableStore { } impl Member { - fn serialize(&self) -> (Vec, Vec, Vec) { + fn serialize(&self) -> (Vec, Vec, Vec, Vec) { let storage = self.provider.storage(); let mut serializable_storage = SerializableStore::default(); @@ -52,13 +55,15 @@ impl Member { serde_json::to_vec(&serializable_storage).unwrap(), serde_json::to_vec(&self.credential_with_key).unwrap(), serde_json::to_vec(&self.signer).unwrap(), + serde_json::to_vec(&self.group_id).unwrap(), ) } - fn load(storage: &[u8], ckey: &[u8], signer: &[u8]) -> Self { + fn load(storage: &[u8], ckey: &[u8], signer: &[u8], group_id: &[u8]) -> Self { let serializable_storage: SerializableStore = serde_json::from_slice(storage).unwrap(); let credential_with_key: CredentialWithKey = serde_json::from_slice(ckey).unwrap(); let signer: SignatureKeyPair = serde_json::from_slice(signer).unwrap(); + let group_id: GroupId = serde_json::from_slice(group_id).unwrap(); let provider = OpenMlsRustCrypto::default(); let mut ks_map = provider.storage().values.write().unwrap(); @@ -74,8 +79,15 @@ impl Member { provider, credential_with_key, signer, + group_id, } } + + fn group(&self) -> Option { + MlsGroup::load(self.provider.storage(), &self.group_id) + .ok() + .flatten() + } } #[inline(always)] @@ -218,12 +230,15 @@ mod generate { ) .expect("An unexpected error occurred."); + let group_id = creator_group.group_id().clone(); + vec![( creator_group, Member { provider: creator_provider, credential_with_key: creator_credential_with_key, signer: creator_signer, + group_id, }, )] }; @@ -281,6 +296,8 @@ mod generate { SetupVariants::CommitToFullGroup => (), // Commit after everyone was added. } + let group_id = member_i_group.group_id().clone(); + // Add new member to list members.push(( member_i_group, @@ -288,6 +305,7 @@ mod generate { provider: member_provider, credential_with_key, signer, + group_id, }, )); pb.inc(1); @@ -424,43 +442,31 @@ mod util { use super::{generate, *}; - const GROUPS_PATH: &str = "large-balanced-group-groups.json.gzip"; const MEMBERS_PATH: &str = "large-balanced-group-members.json.gzip"; - type Members = Vec, Vec, Vec)>>; + type Members = Vec<(Vec, Vec, Vec, Vec)>; /// Read benchmark setups from the fiels previously written. pub fn read(path: Option) -> Vec> { - let file = File::open(groups_file(&path)).unwrap(); - let mut reader = flate2::read::GzDecoder::new(file); - let groups: Vec> = serde_json::from_reader(&mut reader).unwrap(); - let file = File::open(members_file(&path)).unwrap(); let mut reader = flate2::read::GzDecoder::new(file); - let members: Members = serde_json::from_reader(&mut reader).unwrap(); + let members: Vec = serde_json::from_reader(&mut reader).unwrap(); - let members: Vec> = members + let members: Vec> = members .into_iter() .map(|members| { members .into_iter() - .map(|m| Member::load(&m.0, &m.1, &m.2)) + .map(|m| { + let m = Member::load(&m.0, &m.1, &m.2, &m.3); + + (m.group().unwrap(), m) + }) .collect() }) .collect(); - let mut out = vec![]; - for (g, m) in groups.into_iter().zip(members.into_iter()) { - out.push(g.into_iter().zip(m.into_iter()).collect()) - } - - out - } - - fn groups_file(path: &Option) -> std::path::PathBuf { - let path = path.clone().unwrap_or_default(); - let path = Path::new(&path); - path.join(GROUPS_PATH) + members } fn members_file(path: &Option) -> std::path::PathBuf { @@ -475,7 +481,6 @@ mod util { group_sizes: Option>, variant: Option, ) { - let mut groups = vec![]; let mut members = vec![]; let group_sizes = group_sizes.unwrap_or(generate::GROUP_SIZES.to_vec()); @@ -488,16 +493,11 @@ mod util { let (new_groups, new_members): (Vec, Vec) = new_groups.into_iter().unzip(); smaller_groups = Some((new_groups.clone(), new_members.clone())); - let new_members: Vec<(Vec, Vec, Vec)> = - new_members.into_iter().map(|m| m.serialize()).collect(); - groups.push(new_groups); + let new_members: Members = new_members.into_iter().map(|m| m.serialize()).collect(); members.push(new_members); } println!("Writing out files."); - let file = File::create(groups_file(&path)).unwrap(); - let mut writer = flate2::write::GzEncoder::new(file, flate2::Compression::default()); - serde_json::to_writer(&mut writer, &groups).unwrap(); let file = File::create(members_file(&path)).unwrap(); let mut writer = flate2::write::GzEncoder::new(file, flate2::Compression::default()); serde_json::to_writer(&mut writer, &members).unwrap(); diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 6b371d84c..23ce1016a 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -43,7 +43,6 @@ pub(crate) mod errors; pub(crate) mod membership; pub(crate) mod processing; pub(crate) mod proposal; -pub(crate) mod ser; // Tests #[cfg(test)] diff --git a/openmls/src/group/mls_group/ser.rs b/openmls/src/group/mls_group/ser.rs deleted file mode 100644 index cd7d85ff0..000000000 --- a/openmls/src/group/mls_group/ser.rs +++ /dev/null @@ -1,62 +0,0 @@ -// TODO #245: Remove this once we have a proper serialization format -#![allow(deprecated)] - -use super::*; -use crate::schedule::psk::store::ResumptionPskStore; - -use serde::{ - ser::{SerializeStruct, Serializer}, - Deserialize, Serialize, -}; - -/// Helper struct that contains the serializable values of an `MlsGroup. -#[deprecated( - since = "0.4.1", - note = "It is temporarily exposed, it will be private again after #245" -)] -#[derive(Serialize, Deserialize)] -pub struct SerializedMlsGroup { - mls_group_config: MlsGroupJoinConfig, - group: CoreGroup, - own_leaf_nodes: Vec, - resumption_psk_store: ResumptionPskStore, - group_state: MlsGroupState, -} - -#[allow(clippy::from_over_into)] -impl Into for SerializedMlsGroup { - fn into(self) -> MlsGroup { - MlsGroup { - mls_group_config: self.mls_group_config, - group: self.group, - own_leaf_nodes: self.own_leaf_nodes, - aad: Vec::new(), - group_state: self.group_state, - } - } -} - -impl Serialize for MlsGroup { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("SerializedMlsGroup", 6)?; - state.serialize_field("mls_group_config", &self.mls_group_config)?; - state.serialize_field("group", &self.group)?; - state.serialize_field("own_leaf_nodes", &self.own_leaf_nodes)?; - state.serialize_field("resumption_psk_store", &self.group.resumption_psk_store)?; - state.serialize_field("group_state", &self.group_state)?; - state.end() - } -} - -impl<'de> Deserialize<'de> for MlsGroup { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let sgroup = SerializedMlsGroup::deserialize(deserializer)?; - Ok(sgroup.into()) - } -} diff --git a/openmls/src/prelude.rs b/openmls/src/prelude.rs index 9b3aaba15..7e34d2d80 100644 --- a/openmls/src/prelude.rs +++ b/openmls/src/prelude.rs @@ -2,7 +2,7 @@ //! Include this to get access to all the public functions of OpenMLS. // MlsGroup -pub use crate::group::{core_group::Member, ser::*, *}; +pub use crate::group::{core_group::Member, *}; pub use crate::group::public_group::{errors::*, PublicGroup}; From e543c630edeb652e4791d1c666e44d333f7fdbd8 Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Wed, 7 Aug 2024 14:14:38 +0200 Subject: [PATCH 37/43] Remove serde for PublicGroup and CoreGroup (#1638) * Remove serde for PublicGroup and CoreGroup * Make PublicGroup::load public * Amend changelog --- CHANGELOG.md | 9 ++++++++- openmls/src/group/core_group/mod.rs | 2 +- openmls/src/group/public_group/mod.rs | 8 +++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05a7d457c..c7026e5d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.6.0-pre.2 (2024-08-XX) + +### Changed + +- [#1637](https://github.com/openmls/openmls/pull/1637): Remove `serde` from `MlsGroup`. +- [#1638](https://github.com/openmls/openmls/pull/1638): Remove `serde` from `PublicGroup`. `PublicGroup::load()` becomes public to load a group from the storage provider. + ## 0.6.0-pre.1 (2024-07-22) ### Added @@ -31,7 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1559](https://github.com/openmls/openmls/pull/1559): Remove the `PartialEq` type constraint on the error type of both the `OpenMlsRand` and `OpenMlsKeyStore` traits. Additionally, remove the `Clone` type constraint on the error type of the `OpenMlsRand` trait. - [#1565](https://github.com/openmls/openmls/pull/1565): Removed `OpenMlsKeyStore` and replace it with a new `StorageProvider` trait in the `openmls_traits` crate. - [#1606](https://github.com/openmls/openmls/pull/1606): Added additional `LeafNodeParameters` argument to `MlsGroup.self_update()` and `MlsGroup.propose_self_update()` to allow for updating the leaf node with custom parameters. `MlsGroup::join_by_external_commit()` now also takes optional parameters to set the capabilities and the extensions of the LeafNode. -- [#1615](https://github.com/openmls/openmls/pull/1615): Changes the AAD handlling. The AAD is no longer persisted and needs to be set before every API call that generates an `MlsMessageOut`. The functions `ProccessedMessage` to accees the AAD has been renamed to `aad()`. +- [#1615](https://github.com/openmls/openmls/pull/1615): Changes the AAD handling. The AAD is no longer persisted and needs to be set before every API call that generates an `MlsMessageOut`. The functions `ProccessedMessage` to accees the AAD has been renamed to `aad()`. ### Fixed diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 5ac4ac9aa..3ef8ca591 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -142,7 +142,7 @@ pub(crate) struct StagedCoreWelcome { path_keypairs: Option>, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug)] #[cfg_attr(any(test, feature = "test-utils"), derive(Clone, PartialEq))] pub(crate) struct CoreGroup { public_group: PublicGroup, diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index 86df39b79..3003bb0c9 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -60,7 +60,7 @@ mod tests; mod validation; /// This struct holds all public values of an MLS group. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug)] #[cfg_attr(any(test, feature = "test-utils"), derive(PartialEq, Clone))] pub struct PublicGroup { treesync: TreeSync, @@ -383,10 +383,8 @@ impl PublicGroup { Ok(()) } - /// Loads the [`PublicGroup`] from storage. Called from [`CoreGroup::load`]. - /// - /// [`CoreGroup::load`]: crate::group::core_group::CoreGroup::load - pub(crate) fn load( + /// Loads the [`PublicGroup`] corresponding to a [`GroupId`] from storage. + pub fn load( storage: &Storage, group_id: &GroupId, ) -> Result, Storage::Error> { From c69910e8f0bdc712be6f36e27c0c1153faa51272 Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Thu, 8 Aug 2024 14:28:19 +0200 Subject: [PATCH 38/43] Introduce PublicStorageProvider (#1639) Co-authored-by: Konrad Kohbrok --- openmls/src/group/public_group/builder.rs | 5 +- openmls/src/group/public_group/mod.rs | 14 +- .../src/group/public_group/staged_commit.rs | 5 +- openmls/src/storage.rs | 12 + openmls/src/treesync/diff.rs | 3 +- openmls/src/treesync/mod.rs | 4 +- openmls/src/treesync/node/encryption_keys.rs | 3 +- traits/src/public_storage.rs | 259 ++++++++++++++++++ traits/src/storage.rs | 2 +- traits/src/traits.rs | 1 + 10 files changed, 290 insertions(+), 18 deletions(-) create mode 100644 traits/src/public_storage.rs diff --git a/openmls/src/group/public_group/builder.rs b/openmls/src/group/public_group/builder.rs index ea0269c53..529872672 100644 --- a/openmls/src/group/public_group/builder.rs +++ b/openmls/src/group/public_group/builder.rs @@ -1,6 +1,4 @@ -use openmls_traits::{ - crypto::OpenMlsCrypto, signatures::Signer, types::Ciphersuite, OpenMlsProvider, -}; +use openmls_traits::{crypto::OpenMlsCrypto, signatures::Signer, types::Ciphersuite}; use super::{errors::PublicGroupBuildError, PublicGroup}; use crate::{ @@ -11,6 +9,7 @@ use crate::{ key_packages::Lifetime, messages::ConfirmationTag, schedule::CommitSecret, + storage::OpenMlsProvider, treesync::{ node::{encryption_keys::EncryptionKeyPair, leaf_node::Capabilities}, TreeSync, diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index 3003bb0c9..79914f804 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -36,7 +36,7 @@ use crate::{ ConfirmationTag, PathSecret, }, schedule::CommitSecret, - storage::{OpenMlsProvider, StorageProvider}, + storage::{OpenMlsProvider, PublicStorageProvider}, treesync::{ errors::{DerivePathError, TreeSyncFromNodesError}, node::{ @@ -355,10 +355,10 @@ impl PublicGroup { /// existing group, both inside [`PublicGroup`] and in [`CoreGroup`]. /// /// [`CoreGroup`]: crate::group::core_group::CoreGroup - pub(crate) fn store( + pub(crate) fn store( &self, storage: &Storage, - ) -> Result<(), Storage::Error> { + ) -> Result<(), Storage::PublicError> { let group_id = self.group_context.group_id(); storage.write_tree(group_id, self.treesync())?; storage.write_confirmation_tag(group_id, self.confirmation_tag())?; @@ -371,10 +371,10 @@ impl PublicGroup { } /// Deletes the [`PublicGroup`] from storage. - pub(crate) fn delete( + pub(crate) fn delete( &self, storage: &Storage, - ) -> Result<(), Storage::Error> { + ) -> Result<(), Storage::PublicError> { storage.delete_tree(self.group_id())?; storage.delete_confirmation_tag(self.group_id())?; storage.delete_context(self.group_id())?; @@ -384,10 +384,10 @@ impl PublicGroup { } /// Loads the [`PublicGroup`] corresponding to a [`GroupId`] from storage. - pub fn load( + pub fn load( storage: &Storage, group_id: &GroupId, - ) -> Result, Storage::Error> { + ) -> Result, Storage::PublicError> { let treesync = storage.treesync(group_id)?; let proposals: Vec<(ProposalRef, QueuedProposal)> = storage.queued_proposals(group_id)?; let group_context = storage.group_context(group_id)?; diff --git a/openmls/src/group/public_group/staged_commit.rs b/openmls/src/group/public_group/staged_commit.rs index 4595f7933..10016ef85 100644 --- a/openmls/src/group/public_group/staged_commit.rs +++ b/openmls/src/group/public_group/staged_commit.rs @@ -6,7 +6,6 @@ use crate::{ StagedCommit, }, messages::{proposals::ProposalOrRef, Commit}, - storage::StorageProvider, }; #[derive(Debug, Serialize, Deserialize)] @@ -274,11 +273,11 @@ impl PublicGroup { } /// Merges a [StagedCommit] into the public group state. - pub fn merge_commit( + pub fn merge_commit( &mut self, storage: &Storage, staged_commit: StagedCommit, - ) -> Result<(), MergeCommitError> { + ) -> Result<(), MergeCommitError> { match staged_commit.into_state() { StagedCommitState::PublicState(staged_state) => { self.merge_diff(staged_state.staged_diff); diff --git a/openmls/src/storage.rs b/openmls/src/storage.rs index 80a6a9744..ff6813283 100644 --- a/openmls/src/storage.rs +++ b/openmls/src/storage.rs @@ -34,8 +34,20 @@ pub mod kat_storage_stability; /// Throughout the code, this one should be used instead of `openmls_traits::storage::StorageProvider`. pub trait StorageProvider: openmls_traits::storage::StorageProvider {} +/// A convenience trait for the current version of the public storage. +/// Throughout the code, this one should be used instead of `openmls_traits::public_storage::PublicStorageProvider`. +pub trait PublicStorageProvider: + openmls_traits::public_storage::PublicStorageProvider +{ +} + impl> StorageProvider for P {} +impl> + PublicStorageProvider for P +{ +} + /// A convenience trait for the OpenMLS provider that defines the storage provider /// for the current version of storage. /// Throughout the code, this one should be used instead of `openmls_traits::OpenMlsProvider`. diff --git a/openmls/src/treesync/diff.rs b/openmls/src/treesync/diff.rs index b5fd68c2c..5d3ef2df9 100644 --- a/openmls/src/treesync/diff.rs +++ b/openmls/src/treesync/diff.rs @@ -21,7 +21,7 @@ use std::collections::HashSet; use log::debug; use openmls_traits::crypto::OpenMlsCrypto; -use openmls_traits::{signatures::Signer, types::Ciphersuite, OpenMlsProvider}; +use openmls_traits::{signatures::Signer, types::Ciphersuite}; use serde::{Deserialize, Serialize}; use super::node::leaf_node::UpdateLeafNodeParams; @@ -48,6 +48,7 @@ use crate::{ error::LibraryError, messages::PathSecret, schedule::CommitSecret, + storage::OpenMlsProvider, treesync::RatchetTree, }; diff --git a/openmls/src/treesync/mod.rs b/openmls/src/treesync/mod.rs index 484df545c..fbb60de19 100644 --- a/openmls/src/treesync/mod.rs +++ b/openmls/src/treesync/mod.rs @@ -25,7 +25,6 @@ use openmls_traits::{ crypto::OpenMlsCrypto, signatures::Signer, types::{Ciphersuite, CryptoError}, - OpenMlsProvider, }; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -62,6 +61,7 @@ use crate::{ key_packages::Lifetime, messages::{PathSecret, PathSecretError}, schedule::CommitSecret, + storage::OpenMlsProvider, }; // Private @@ -731,6 +731,8 @@ mod test { ciphersuite: Ciphersuite, provider: &impl OpenMlsProvider, ) { + use openmls_traits::OpenMlsProvider; + let (key_package, _, _) = crate::key_packages::tests::key_package(ciphersuite, provider); let node_in = NodeIn::from(Node::LeafNode(LeafNode::from(key_package))); let tests = [ diff --git a/openmls/src/treesync/node/encryption_keys.rs b/openmls/src/treesync/node/encryption_keys.rs index 8a757de47..13a18e243 100644 --- a/openmls/src/treesync/node/encryption_keys.rs +++ b/openmls/src/treesync/node/encryption_keys.rs @@ -4,7 +4,6 @@ use openmls_traits::{ crypto::OpenMlsCrypto, storage::{StorageProvider as StorageProviderTrait, CURRENT_VERSION}, types::{Ciphersuite, HpkeCiphertext, HpkeKeyPair}, - OpenMlsProvider, }; use serde::{Deserialize, Serialize}; use tls_codec::{TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, VLBytes}; @@ -12,7 +11,7 @@ use tls_codec::{TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, VLBy use crate::{ ciphersuite::{hpke, HpkePrivateKey, HpkePublicKey, Secret}, error::LibraryError, - storage::StorageProvider, + storage::{OpenMlsProvider, StorageProvider}, }; /// [`EncryptionKey`] contains an HPKE public key that allows the encryption of diff --git a/traits/src/public_storage.rs b/traits/src/public_storage.rs new file mode 100644 index 000000000..835b2881f --- /dev/null +++ b/traits/src/public_storage.rs @@ -0,0 +1,259 @@ +//! This module describes the public storage provider and type traits. +//! Applications that only want to use the `PublicGroup` only need to implement +//! the `PublicStorageProvider` trait, and not the `StorageProvider` trait. + +use crate::storage::StorageProvider; + +pub trait PublicStorageProvider { + /// An opaque error returned by all methods on this trait. + type PublicError: core::fmt::Debug + std::error::Error; + + /// Get the version of this provider. + fn version() -> u16 { + VERSION + } + + /// Write the TreeSync tree. + fn write_tree< + GroupId: crate::storage::traits::GroupId, + TreeSync: crate::storage::traits::TreeSync, + >( + &self, + group_id: &GroupId, + tree: &TreeSync, + ) -> Result<(), Self::PublicError>; + + /// Write the interim transcript hash. + fn write_interim_transcript_hash< + GroupId: crate::storage::traits::GroupId, + InterimTranscriptHash: crate::storage::traits::InterimTranscriptHash, + >( + &self, + group_id: &GroupId, + interim_transcript_hash: &InterimTranscriptHash, + ) -> Result<(), Self::PublicError>; + + /// Write the group context. + fn write_context< + GroupId: crate::storage::traits::GroupId, + GroupContext: crate::storage::traits::GroupContext, + >( + &self, + group_id: &GroupId, + group_context: &GroupContext, + ) -> Result<(), Self::PublicError>; + + /// Write the confirmation tag. + fn write_confirmation_tag< + GroupId: crate::storage::traits::GroupId, + ConfirmationTag: crate::storage::traits::ConfirmationTag, + >( + &self, + group_id: &GroupId, + confirmation_tag: &ConfirmationTag, + ) -> Result<(), Self::PublicError>; + + /// Returns all queued proposals for the group with group id `group_id`, or an empty vector of none are stored. + fn queued_proposals< + GroupId: crate::storage::traits::GroupId, + ProposalRef: crate::storage::traits::ProposalRef, + QueuedProposal: crate::storage::traits::QueuedProposal, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError>; + + /// Returns the TreeSync tree for the group with group id `group_id`. + fn treesync< + GroupId: crate::storage::traits::GroupId, + TreeSync: crate::storage::traits::TreeSync, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError>; + + /// Returns the group context for the group with group id `group_id`. + fn group_context< + GroupId: crate::storage::traits::GroupId, + GroupContext: crate::storage::traits::GroupContext, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError>; + + /// Returns the interim transcript hash for the group with group id `group_id`. + fn interim_transcript_hash< + GroupId: crate::storage::traits::GroupId, + InterimTranscriptHash: crate::storage::traits::InterimTranscriptHash, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError>; + + /// Returns the confirmation tag for the group with group id `group_id`. + fn confirmation_tag< + GroupId: crate::storage::traits::GroupId, + ConfirmationTag: crate::storage::traits::ConfirmationTag, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError>; + + /// Deletes the tree from storage + fn delete_tree>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError>; + + /// Deletes the confirmation tag from storage + fn delete_confirmation_tag>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError>; + + /// Deletes the group context for the group with given id + fn delete_context>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError>; + + /// Deletes the interim transcript hash for the group with given id + fn delete_interim_transcript_hash>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError>; +} + +impl PublicStorageProvider for T +where + T: StorageProvider, +{ + type PublicError = >::Error; + + fn write_tree< + GroupId: crate::storage::traits::GroupId, + TreeSync: crate::storage::traits::TreeSync, + >( + &self, + group_id: &GroupId, + tree: &TreeSync, + ) -> Result<(), Self::PublicError> { + >::write_tree(self, group_id, tree) + } + + fn write_interim_transcript_hash< + GroupId: crate::storage::traits::GroupId, + InterimTranscriptHash: crate::storage::traits::InterimTranscriptHash, + >( + &self, + group_id: &GroupId, + interim_transcript_hash: &InterimTranscriptHash, + ) -> Result<(), Self::PublicError> { + >::write_interim_transcript_hash( + self, + group_id, + interim_transcript_hash, + ) + } + + fn write_context< + GroupId: crate::storage::traits::GroupId, + GroupContext: crate::storage::traits::GroupContext, + >( + &self, + group_id: &GroupId, + group_context: &GroupContext, + ) -> Result<(), Self::PublicError> { + >::write_context(self, group_id, group_context) + } + + fn write_confirmation_tag< + GroupId: crate::storage::traits::GroupId, + ConfirmationTag: crate::storage::traits::ConfirmationTag, + >( + &self, + group_id: &GroupId, + confirmation_tag: &ConfirmationTag, + ) -> Result<(), Self::PublicError> { + >::write_confirmation_tag(self, group_id, confirmation_tag) + } + + fn queued_proposals< + GroupId: crate::storage::traits::GroupId, + ProposalRef: crate::storage::traits::ProposalRef, + QueuedProposal: crate::storage::traits::QueuedProposal, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError> { + >::queued_proposals(self, group_id) + } + + fn treesync< + GroupId: crate::storage::traits::GroupId, + TreeSync: crate::storage::traits::TreeSync, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError> { + >::treesync(self, group_id) + } + + fn group_context< + GroupId: crate::storage::traits::GroupId, + GroupContext: crate::storage::traits::GroupContext, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError> { + >::group_context(self, group_id) + } + + fn interim_transcript_hash< + GroupId: crate::storage::traits::GroupId, + InterimTranscriptHash: crate::storage::traits::InterimTranscriptHash, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError> { + >::interim_transcript_hash(self, group_id) + } + + fn confirmation_tag< + GroupId: crate::storage::traits::GroupId, + ConfirmationTag: crate::storage::traits::ConfirmationTag, + >( + &self, + group_id: &GroupId, + ) -> Result, Self::PublicError> { + >::confirmation_tag(self, group_id) + } + + fn delete_tree>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError> { + >::delete_tree(self, group_id) + } + + fn delete_confirmation_tag>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError> { + >::delete_confirmation_tag(self, group_id) + } + + fn delete_context>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError> { + >::delete_context(self, group_id) + } + + fn delete_interim_transcript_hash>( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError> { + >::delete_interim_transcript_hash(self, group_id) + } +} diff --git a/traits/src/storage.rs b/traits/src/storage.rs index dcb382da4..ea1f31204 100644 --- a/traits/src/storage.rs +++ b/traits/src/storage.rs @@ -513,7 +513,7 @@ pub trait StorageProvider { group_id: &GroupId, ) -> Result<(), Self::Error>; - /// Clear the proposal queue for the grou pwith the given id. + /// Clear the proposal queue for the group with the given id. fn clear_proposal_queue< GroupId: traits::GroupId, ProposalRef: traits::ProposalRef, diff --git a/traits/src/traits.rs b/traits/src/traits.rs index a1022d468..8f5e7f0e7 100644 --- a/traits/src/traits.rs +++ b/traits/src/traits.rs @@ -4,6 +4,7 @@ //! API of OpenMLS. pub mod crypto; +pub mod public_storage; pub mod random; pub mod signatures; pub mod storage; From be9fb71871f6e9c701d98c571870eefbd6e40fa6 Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Thu, 8 Aug 2024 15:50:10 +0200 Subject: [PATCH 39/43] Align storage provider naming (#1640) --- memory_storage/src/lib.rs | 2 +- memory_storage/src/test_store.rs | 2 +- traits/src/public_storage.rs | 2 +- traits/src/storage.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/memory_storage/src/lib.rs b/memory_storage/src/lib.rs index 217a606f3..6d973a761 100644 --- a/memory_storage/src/lib.rs +++ b/memory_storage/src/lib.rs @@ -417,7 +417,7 @@ impl StorageProvider for MemoryStorage { .collect::, _>>() } - fn treesync< + fn tree< GroupId: traits::GroupId, TreeSync: traits::TreeSync, >( diff --git a/memory_storage/src/test_store.rs b/memory_storage/src/test_store.rs index 2e379fb71..f24fa455a 100644 --- a/memory_storage/src/test_store.rs +++ b/memory_storage/src/test_store.rs @@ -175,7 +175,7 @@ impl StorageProvider for MemoryStorage { todo!() } - fn treesync, TreeSync: traits::TreeSync>( + fn tree, TreeSync: traits::TreeSync>( &self, _group_id: &GroupId, ) -> Result, Self::Error> { diff --git a/traits/src/public_storage.rs b/traits/src/public_storage.rs index 835b2881f..70d9932b3 100644 --- a/traits/src/public_storage.rs +++ b/traits/src/public_storage.rs @@ -196,7 +196,7 @@ where &self, group_id: &GroupId, ) -> Result, Self::PublicError> { - >::treesync(self, group_id) + >::tree(self, group_id) } fn group_context< diff --git a/traits/src/storage.rs b/traits/src/storage.rs index ea1f31204..892de66c2 100644 --- a/traits/src/storage.rs +++ b/traits/src/storage.rs @@ -287,7 +287,7 @@ pub trait StorageProvider { ) -> Result, Self::Error>; /// Returns the TreeSync tree for the group with group id `group_id`. - fn treesync, TreeSync: traits::TreeSync>( + fn tree, TreeSync: traits::TreeSync>( &self, group_id: &GroupId, ) -> Result, Self::Error>; From 5269b2ce851c34cc8cf11799e227cd861ce998ba Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Thu, 8 Aug 2024 20:28:20 +0200 Subject: [PATCH 40/43] Consistent handling of queued proposals. (#1641) Co-authored-by: Konrad Kohbrok --- CHANGELOG.md | 10 +++ openmls/src/group/core_group/process.rs | 2 - openmls/src/group/core_group/proposals.rs | 4 +- openmls/src/group/core_group/staged_commit.rs | 6 +- openmls/src/group/mls_group/membership.rs | 21 ++++-- openmls/src/group/mls_group/mod.rs | 2 + openmls/src/group/mls_group/proposal.rs | 4 +- .../tests_and_kats/tests/mls_group.rs | 4 +- openmls/src/group/public_group/mod.rs | 29 +++++++- .../src/group/public_group/staged_commit.rs | 3 + openmls/src/group/public_group/tests.rs | 2 +- openmls/tests/book_code.rs | 2 +- traits/src/public_storage.rs | 67 +++++++++++++++++++ 13 files changed, 137 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7026e5d3..e60134a73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.6.0-pre.2 (2024-08-XX) +### Added + +- [#1639](https://github.com/openmls/openmls/pull/1639): Introduce `PublicStorageProvider` trait to independently allow for the storage of `PublicGroup` instances. +- [#1641](https://github.com/openmls/openmls/pull/1641): Extend the `PublicGroup` API with `add_proposal()`, `remove_proposal()`, and `queued_proposals()`. + ### Changed - [#1637](https://github.com/openmls/openmls/pull/1637): Remove `serde` from `MlsGroup`. - [#1638](https://github.com/openmls/openmls/pull/1638): Remove `serde` from `PublicGroup`. `PublicGroup::load()` becomes public to load a group from the storage provider. +- [#1640](https://github.com/openmls/openmls/pull/1640): The storage provider function `treesync()` was renamed to `tree()` so that it is consistent with other treesync functions. + +### Fixed + +- [#1641](https://github.com/openmls/openmls/pull/1641): Fixed missing storage of queued proposals & clearing of the queued proposals. ## 0.6.0-pre.1 (2024-07-22) diff --git a/openmls/src/group/core_group/process.rs b/openmls/src/group/core_group/process.rs index 5b56b1b05..2d8608005 100644 --- a/openmls/src/group/core_group/process.rs +++ b/openmls/src/group/core_group/process.rs @@ -301,8 +301,6 @@ impl CoreGroup { self.message_secrets_store .add(past_epoch, message_secrets, leaves); } - // Empty the proposal store - self.proposal_store_mut().empty(); Ok(()) } } diff --git a/openmls/src/group/core_group/proposals.rs b/openmls/src/group/core_group/proposals.rs index 372426891..714408e86 100644 --- a/openmls/src/group/core_group/proposals.rs +++ b/openmls/src/group/core_group/proposals.rs @@ -53,11 +53,11 @@ impl ProposalStore { /// Removes a proposal from the store using its reference. It will return /// None if it wasn't found in the store. - pub(crate) fn remove(&mut self, proposal_ref: ProposalRef) -> Option<()> { + pub(crate) fn remove(&mut self, proposal_ref: &ProposalRef) -> Option<()> { let index = self .queued_proposals .iter() - .position(|p| p.proposal_reference() == proposal_ref)?; + .position(|p| &p.proposal_reference() == proposal_ref)?; self.queued_proposals.remove(index); Some(()) } diff --git a/openmls/src/group/core_group/staged_commit.rs b/openmls/src/group/core_group/staged_commit.rs index 30039bd20..b5d3f5d56 100644 --- a/openmls/src/group/core_group/staged_commit.rs +++ b/openmls/src/group/core_group/staged_commit.rs @@ -7,7 +7,8 @@ use self::public_group::staged_commit::PublicStagedCommitState; use super::{super::errors::*, *}; use crate::{ - ciphersuite::Secret, framing::mls_auth_content::AuthenticatedContent, + ciphersuite::{hash_ref::ProposalRef, Secret}, + framing::mls_auth_content::AuthenticatedContent, treesync::node::encryption_keys::EncryptionKeyPair, }; @@ -401,6 +402,9 @@ impl CoreGroup { } // Empty the proposal store + storage + .clear_proposal_queue::(group_id) + .map_err(MergeCommitError::StorageError)?; self.proposal_store_mut().empty(); Ok(Some(message_secrets)) diff --git a/openmls/src/group/mls_group/membership.rs b/openmls/src/group/mls_group/membership.rs index 6f0d41f59..d0c569d25 100644 --- a/openmls/src/group/mls_group/membership.rs +++ b/openmls/src/group/mls_group/membership.rs @@ -241,13 +241,22 @@ impl MlsGroup { .map_err(|_| LibraryError::custom("Creating a self removal should not fail"))?; let ciphersuite = self.ciphersuite(); + let queued_remove_proposal = QueuedProposal::from_authenticated_content_by_ref( + ciphersuite, + provider.crypto(), + remove_proposal.clone(), + )?; - self.proposal_store_mut() - .add(QueuedProposal::from_authenticated_content_by_ref( - ciphersuite, - provider.crypto(), - remove_proposal.clone(), - )?); + provider + .storage() + .queue_proposal( + self.group_id(), + &queued_remove_proposal.proposal_reference(), + &queued_remove_proposal, + ) + .map_err(MlsGroupStateError::StorageError)?; + + self.proposal_store_mut().add(queued_remove_proposal); self.reset_aad(); Ok(self.content_to_mls_message(remove_proposal, provider)?) diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 23ce1016a..09d52f76a 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -376,6 +376,8 @@ impl MlsGroup { storage.delete_own_leaf_nodes(self.group_id())?; storage.delete_group_state(self.group_id())?; + self.proposal_store_mut().empty(); + Ok(()) } diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index dee801740..7c703cc62 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -449,10 +449,10 @@ impl MlsGroup { pub fn remove_pending_proposal( &mut self, storage: &Storage, - proposal_ref: ProposalRef, + proposal_ref: &ProposalRef, ) -> Result<(), MlsGroupStateError> { storage - .remove_proposal(self.group_id(), &proposal_ref) + .remove_proposal(self.group_id(), proposal_ref) .map_err(MlsGroupStateError::StorageError)?; self.proposal_store_mut() .remove(proposal_ref) diff --git a/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs b/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs index a18a3d01c..0aa9712c5 100644 --- a/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs @@ -1105,13 +1105,13 @@ fn remove_prosposal_by_ref( assert_eq!(alice_group.proposal_store().proposals().count(), 1); // clearing the proposal by reference alice_group - .remove_pending_proposal(provider.storage(), reference.clone()) + .remove_pending_proposal(provider.storage(), &reference) .unwrap(); assert!(alice_group.proposal_store().is_empty()); // the proposal should not be stored anymore let err = alice_group - .remove_pending_proposal(provider.storage(), reference) + .remove_pending_proposal(provider.storage(), &reference) .unwrap_err(); assert!(matches!(err, MlsGroupStateError::PendingProposalNotFound)); diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index 79914f804..a94ff6d06 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -288,8 +288,33 @@ impl PublicGroup { } /// Add the [`QueuedProposal`] to the [`PublicGroup`]s internal [`ProposalStore`]. - pub fn add_proposal(&mut self, proposal: QueuedProposal) { - self.proposal_store.add(proposal) + pub fn add_proposal( + &mut self, + storage: &Storage, + proposal: QueuedProposal, + ) -> Result<(), Storage::PublicError> { + storage.queue_proposal(self.group_id(), &proposal.proposal_reference(), &proposal)?; + self.proposal_store.add(proposal); + Ok(()) + } + + /// Remove the Proposal with the given [`ProposalRef`] from the [`PublicGroup`]s internal [`ProposalStore`]. + pub fn remove_proposal( + &mut self, + storage: &Storage, + proposal_ref: &ProposalRef, + ) -> Result<(), Storage::PublicError> { + storage.remove_proposal(self.group_id(), proposal_ref)?; + self.proposal_store.remove(proposal_ref); + Ok(()) + } + + /// Return all queued proposals + pub fn queued_proposals( + &self, + storage: &Storage, + ) -> Result, Storage::PublicError> { + storage.queued_proposals(self.group_id()) } } diff --git a/openmls/src/group/public_group/staged_commit.rs b/openmls/src/group/public_group/staged_commit.rs index 10016ef85..187fc45e3 100644 --- a/openmls/src/group/public_group/staged_commit.rs +++ b/openmls/src/group/public_group/staged_commit.rs @@ -286,6 +286,9 @@ impl PublicGroup { } self.proposal_store.empty(); + storage + .clear_proposal_queue::(self.group_id()) + .map_err(MergeCommitError::StorageError)?; self.store(storage).map_err(MergeCommitError::StorageError) } } diff --git a/openmls/src/group/public_group/tests.rs b/openmls/src/group/public_group/tests.rs index e1dfc8ae4..f924a26ab 100644 --- a/openmls/src/group/public_group/tests.rs +++ b/openmls/src/group/public_group/tests.rs @@ -190,7 +190,7 @@ fn public_group(ciphersuite: Ciphersuite, provider: & Proposal::Remove(r) => assert_eq!(r.removed(), LeafNodeIndex::new(1)), _ => panic!("Unexpected proposal type"), } - public_group.add_proposal(*p); + public_group.add_proposal(provider.storage(), *p).unwrap(); } } diff --git a/openmls/tests/book_code.rs b/openmls/tests/book_code.rs index 573d58768..2000033f5 100644 --- a/openmls/tests/book_code.rs +++ b/openmls/tests/book_code.rs @@ -943,7 +943,7 @@ fn book_operations() { ) .expect("Could not create proposal to add Bob"); alice_group - .remove_pending_proposal(provider.storage(), proposal_ref) + .remove_pending_proposal(provider.storage(), &proposal_ref) .expect("The proposal was not found"); // ANCHOR_END: rollback_proposal_by_ref diff --git a/traits/src/public_storage.rs b/traits/src/public_storage.rs index 70d9932b3..56a7f12f3 100644 --- a/traits/src/public_storage.rs +++ b/traits/src/public_storage.rs @@ -53,6 +53,18 @@ pub trait PublicStorageProvider { confirmation_tag: &ConfirmationTag, ) -> Result<(), Self::PublicError>; + /// Enqueue a proposal. + fn queue_proposal< + GroupId: crate::storage::traits::GroupId, + ProposalRef: crate::storage::traits::ProposalRef, + QueuedProposal: crate::storage::traits::QueuedProposal, + >( + &self, + group_id: &GroupId, + proposal_ref: &ProposalRef, + proposal: &QueuedProposal, + ) -> Result<(), Self::PublicError>; + /// Returns all queued proposals for the group with group id `group_id`, or an empty vector of none are stored. fn queued_proposals< GroupId: crate::storage::traits::GroupId, @@ -122,6 +134,25 @@ pub trait PublicStorageProvider { &self, group_id: &GroupId, ) -> Result<(), Self::PublicError>; + + /// Removes an individual proposal from the proposal queue of the group with the provided id + fn remove_proposal< + GroupId: crate::storage::traits::GroupId, + ProposalRef: crate::storage::traits::ProposalRef, + >( + &self, + group_id: &GroupId, + proposal_ref: &ProposalRef, + ) -> Result<(), Self::PublicError>; + + /// Clear the proposal queue for the group with the given id. + fn clear_proposal_queue< + GroupId: crate::storage::traits::GroupId, + ProposalRef: crate::storage::traits::ProposalRef, + >( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError>; } impl PublicStorageProvider for T @@ -178,6 +209,19 @@ where >::write_confirmation_tag(self, group_id, confirmation_tag) } + fn queue_proposal< + GroupId: crate::storage::traits::GroupId, + ProposalRef: crate::storage::traits::ProposalRef, + QueuedProposal: crate::storage::traits::QueuedProposal, + >( + &self, + group_id: &GroupId, + proposal_ref: &ProposalRef, + proposal: &QueuedProposal, + ) -> Result<(), Self::PublicError> { + >::queue_proposal(self, group_id, proposal_ref, proposal) + } + fn queued_proposals< GroupId: crate::storage::traits::GroupId, ProposalRef: crate::storage::traits::ProposalRef, @@ -256,4 +300,27 @@ where ) -> Result<(), Self::PublicError> { >::delete_interim_transcript_hash(self, group_id) } + + fn remove_proposal< + GroupId: crate::storage::traits::GroupId, + ProposalRef: crate::storage::traits::ProposalRef, + >( + &self, + group_id: &GroupId, + proposal_ref: &ProposalRef, + ) -> Result<(), Self::PublicError> { + >::remove_proposal(self, group_id, proposal_ref) + } + + fn clear_proposal_queue< + GroupId: crate::storage::traits::GroupId, + ProposalRef: crate::storage::traits::ProposalRef, + >( + &self, + group_id: &GroupId, + ) -> Result<(), Self::PublicError> { + >::clear_proposal_queue::( + self, group_id, + ) + } } From 7d3cb92c0fdb8764a82652b5425eded8cee22f2f Mon Sep 17 00:00:00 2001 From: raphaelrobert Date: Mon, 12 Aug 2024 13:07:14 +0200 Subject: [PATCH 41/43] Remove OpenMlsProvider dependency of PublicGroup. (#1642) * Remove OpenMlsProvider dependency of PublicGroup. * Remove OpenMlsProvider from processing as well Co-authored-by: Konrad Kohbrok * Fix error types --------- Co-authored-by: Konrad Kohbrok --- CHANGELOG.md | 1 + openmls/src/framing/validation.rs | 22 ++----- .../core_group/new_from_external_init.rs | 3 +- .../src/group/core_group/new_from_welcome.rs | 3 +- openmls/src/group/core_group/process.rs | 6 +- openmls/src/group/errors.rs | 2 +- openmls/src/group/mls_group/application.rs | 2 +- openmls/src/group/mls_group/errors.rs | 64 +++++++++++-------- openmls/src/group/mls_group/exporting.rs | 4 +- openmls/src/group/mls_group/membership.rs | 2 +- openmls/src/group/mls_group/mod.rs | 6 +- openmls/src/group/mls_group/processing.rs | 2 +- openmls/src/group/mls_group/proposal.rs | 8 +-- .../tests_and_kats/tests/mls_group.rs | 2 +- openmls/src/group/public_group/mod.rs | 14 ++-- openmls/src/group/public_group/process.rs | 20 +++--- openmls/src/group/public_group/tests.rs | 14 ++-- .../tests/external_add_proposal.rs | 2 +- .../tests/external_commit_validation.rs | 5 +- .../tests/external_remove_proposal.rs | 5 +- .../tests/framing_validation.rs | 5 +- .../tests/group_context_extensions.rs | 5 +- .../tests/proposal_validation.rs | 29 ++++----- .../src/test_utils/test_framework/client.rs | 4 +- .../src/test_utils/test_framework/errors.rs | 13 ++-- openmls/src/test_utils/test_framework/mod.rs | 4 +- .../kats/kat_message_protection.rs | 2 +- openmls/tests/book_code.rs | 4 +- openmls/tests/mls_group.rs | 6 +- 29 files changed, 130 insertions(+), 129 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e60134a73..3f6ebf174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1637](https://github.com/openmls/openmls/pull/1637): Remove `serde` from `MlsGroup`. - [#1638](https://github.com/openmls/openmls/pull/1638): Remove `serde` from `PublicGroup`. `PublicGroup::load()` becomes public to load a group from the storage provider. - [#1640](https://github.com/openmls/openmls/pull/1640): The storage provider function `treesync()` was renamed to `tree()` so that it is consistent with other treesync functions. +- [#1642](https://github.com/openmls/openmls/pull/1642): `OpenMlsProvider` is no longer required for the `PublicGroup` API. The `PublicGroup` API now uses the `PublicStorageProvider` trait directly. `ProcessMessageError::InvalidSignature` was removed and replaced with `ValidationError::InvalidSignature`. ### Fixed diff --git a/openmls/src/framing/validation.rs b/openmls/src/framing/validation.rs index 0b124c736..914e75d05 100644 --- a/openmls/src/framing/validation.rs +++ b/openmls/src/framing/validation.rs @@ -34,14 +34,11 @@ use crate::{ core_group::{proposals::QueuedProposal, staged_commit::StagedCommit}, errors::ValidationError, }, - storage::OpenMlsProvider, tree::sender_ratchet::SenderRatchetConfiguration, treesync::TreeSync, versions::ProtocolVersion, }; -use self::mls_group::errors::ProcessMessageError; - use super::{ mls_auth_content::AuthenticatedContent, mls_auth_content_in::{AuthenticatedContentIn, VerifiableAuthenticatedContentIn}, @@ -271,23 +268,18 @@ impl UnverifiedMessage { /// Verify the [`UnverifiedMessage`]. Returns the [`AuthenticatedContent`] /// and the internal [`Credential`]. - pub(crate) fn verify( + pub(crate) fn verify( self, ciphersuite: Ciphersuite, - provider: &Provider, + crypto: &impl OpenMlsCrypto, protocol_version: ProtocolVersion, - ) -> Result<(AuthenticatedContent, Credential), ProcessMessageError> - { + ) -> Result<(AuthenticatedContent, Credential), ValidationError> { let content: AuthenticatedContentIn = self .verifiable_content - .verify(provider.crypto(), &self.sender_pk) - .map_err(|_| ProcessMessageError::InvalidSignature)?; - let content = content.validate( - ciphersuite, - provider.crypto(), - self.sender_context, - protocol_version, - )?; + .verify(crypto, &self.sender_pk) + .map_err(|_| ValidationError::InvalidSignature)?; + let content = + content.validate(ciphersuite, crypto, self.sender_context, protocol_version)?; Ok((content, self.credential)) } diff --git a/openmls/src/group/core_group/new_from_external_init.rs b/openmls/src/group/core_group/new_from_external_init.rs index ee69ced89..e8c387416 100644 --- a/openmls/src/group/core_group/new_from_external_init.rs +++ b/openmls/src/group/core_group/new_from_external_init.rs @@ -48,7 +48,8 @@ impl CoreGroup { }; let (public_group, group_info) = PublicGroup::from_external( - provider, + provider.crypto(), + provider.storage(), ratchet_tree, verifiable_group_info, // Existing proposals are discarded when joining by external commit. diff --git a/openmls/src/group/core_group/new_from_welcome.rs b/openmls/src/group/core_group/new_from_welcome.rs index ed47fe4a4..ae653b6a0 100644 --- a/openmls/src/group/core_group/new_from_welcome.rs +++ b/openmls/src/group/core_group/new_from_welcome.rs @@ -119,7 +119,8 @@ pub(in crate::group) fn build_staged_welcome( // Since there is currently only the external pub extension, there is no // group info extension of interest here. let (public_group, _group_info_extensions) = PublicGroup::from_external( - provider, + provider.crypto(), + provider.storage(), ratchet_tree, verifiable_group_info.clone(), ProposalStore::new(), diff --git a/openmls/src/group/core_group/process.rs b/openmls/src/group/core_group/process.rs index 2d8608005..e9b6cac05 100644 --- a/openmls/src/group/core_group/process.rs +++ b/openmls/src/group/core_group/process.rs @@ -45,12 +45,12 @@ impl CoreGroup { unverified_message: UnverifiedMessage, old_epoch_keypairs: Vec, leaf_node_keypairs: Vec, - ) -> Result> { + ) -> Result { // Checks the following semantic validation: // - ValSem010 // - ValSem246 (as part of ValSem010) let (content, credential) = - unverified_message.verify(self.ciphersuite(), provider, self.version())?; + unverified_message.verify(self.ciphersuite(), provider.crypto(), self.version())?; match content.sender() { Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal => { @@ -173,7 +173,7 @@ impl CoreGroup { message: impl Into, sender_ratchet_configuration: &SenderRatchetConfiguration, own_leaf_nodes: &[LeafNode], - ) -> Result> { + ) -> Result { let message: ProtocolMessage = message.into(); // Checks the following semantic validation: diff --git a/openmls/src/group/errors.rs b/openmls/src/group/errors.rs index 58eed0c99..9d242d2ed 100644 --- a/openmls/src/group/errors.rs +++ b/openmls/src/group/errors.rs @@ -517,7 +517,7 @@ pub enum CreateGroupContextExtProposalError { LeafNodeValidation(#[from] LeafNodeValidationError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - MlsGroupStateError(#[from] MlsGroupStateError), + MlsGroupStateError(#[from] MlsGroupStateError), /// See [`CreateCommitError`] for more details. #[error(transparent)] CreateCommitError(#[from] CreateCommitError), diff --git a/openmls/src/group/mls_group/application.rs b/openmls/src/group/mls_group/application.rs index 583a81325..1811973a5 100644 --- a/openmls/src/group/mls_group/application.rs +++ b/openmls/src/group/mls_group/application.rs @@ -18,7 +18,7 @@ impl MlsGroup { provider: &Provider, signer: &impl Signer, message: &[u8], - ) -> Result> { + ) -> Result { if !self.is_active() { return Err(CreateMessageError::GroupStateError( MlsGroupStateError::UseAfterEviction, diff --git a/openmls/src/group/mls_group/errors.rs b/openmls/src/group/mls_group/errors.rs index f5fff760f..0b76fbded 100644 --- a/openmls/src/group/mls_group/errors.rs +++ b/openmls/src/group/mls_group/errors.rs @@ -60,7 +60,7 @@ pub enum EmptyInputError { /// Group state error #[derive(Error, Debug, PartialEq, Clone)] -pub enum MlsGroupStateError { +pub enum MlsGroupStateError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -79,9 +79,6 @@ pub enum MlsGroupStateError { /// Requested pending proposal hasn't been found in local pending proposals #[error("Requested pending proposal hasn't been found in local pending proposals.")] PendingProposalNotFound, - /// An error ocurred while writing to storage - #[error("An error ocurred while writing to storage")] - StorageError(StorageError), } /// Error merging pending commit @@ -89,7 +86,7 @@ pub enum MlsGroupStateError { pub enum MergePendingCommitError { /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - MlsGroupStateError(#[from] MlsGroupStateError), + MlsGroupStateError(#[from] MlsGroupStateError), /// See [`MergeCommitError`] for more details. #[error(transparent)] MergeCommitError(#[from] MergeCommitError), @@ -97,7 +94,7 @@ pub enum MergePendingCommitError { /// Process message error #[derive(Error, Debug, PartialEq, Clone)] -pub enum ProcessMessageError { +pub enum ProcessMessageError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -109,10 +106,7 @@ pub enum ProcessMessageError { ValidationError(#[from] ValidationError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), - /// The message's signature is invalid. - #[error("The message's signature is invalid.")] - InvalidSignature, + GroupStateError(#[from] MlsGroupStateError), /// See [`StageCommitError`] for more details. #[error(transparent)] InvalidCommit(#[from] StageCommitError), @@ -126,13 +120,13 @@ pub enum ProcessMessageError { /// Create message error #[derive(Error, Debug, PartialEq, Clone)] -pub enum CreateMessageError { +pub enum CreateMessageError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), } /// Add members error @@ -149,7 +143,7 @@ pub enum AddMembersError { CreateCommitError(#[from] CreateCommitError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), /// Error writing to storage. #[error("Error writing to storage")] StorageError(StorageError), @@ -166,7 +160,7 @@ pub enum ProposeAddMemberError { UnsupportedExtensions, /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), /// See [`LeafNodeValidationError`] for more details. #[error(transparent)] LeafNodeValidation(#[from] LeafNodeValidationError), @@ -183,7 +177,7 @@ pub enum ProposeRemoveMemberError { LibraryError(#[from] LibraryError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), /// The member that should be removed can not be found. #[error("The member that should be removed can not be found.")] UnknownMember, @@ -206,7 +200,7 @@ pub enum RemoveMembersError { CreateCommitError(#[from] CreateCommitError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), /// The member that should be removed can not be found. #[error("The member that should be removed can not be found.")] UnknownMember, @@ -223,7 +217,10 @@ pub enum LeaveGroupError { LibraryError(#[from] LibraryError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), + /// An error ocurred while writing to storage + #[error("An error ocurred while writing to storage")] + StorageError(StorageError), } /// Self update error @@ -237,7 +234,7 @@ pub enum SelfUpdateError { CreateCommitError(#[from] CreateCommitError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), /// Error accessing the storage. #[error("Error accessing the storage.")] StorageError(StorageError), @@ -252,7 +249,7 @@ pub enum ProposeSelfUpdateError { /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), /// Error accessing storage. #[error("Error accessing storage.")] StorageError(StorageError), @@ -275,7 +272,7 @@ pub enum CommitToPendingProposalsError { CreateCommitError(#[from] CreateCommitError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), /// Error writing to storage #[error("Error writing to storage: {0}")] StorageError(StorageError), @@ -283,18 +280,18 @@ pub enum CommitToPendingProposalsError { /// Errors that can happen when exporting a group info object. #[derive(Error, Debug, PartialEq, Clone)] -pub enum ExportGroupInfoError { +pub enum ExportGroupInfoError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), } /// Export secret error #[derive(Error, Debug, PartialEq, Clone)] -pub enum ExportSecretError { +pub enum ExportSecretError { /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -303,24 +300,24 @@ pub enum ExportSecretError { KeyLengthTooLong, /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), } /// Propose PSK error #[derive(Error, Debug, PartialEq, Clone)] -pub enum ProposePskError { +pub enum ProposePskError { /// See [`PskError`] for more details. #[error(transparent)] Psk(#[from] PskError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), } -/// Export secret error +/// Proposal error #[derive(Error, Debug, PartialEq, Clone)] pub enum ProposalError { /// See [`LibraryError`] for more details. @@ -340,7 +337,7 @@ pub enum ProposalError { ProposeRemoveMemberError(#[from] ProposeRemoveMemberError), /// See [`MlsGroupStateError`] for more details. #[error(transparent)] - GroupStateError(#[from] MlsGroupStateError), + GroupStateError(#[from] MlsGroupStateError), /// See [`ValidationError`] for more details. #[error(transparent)] ValidationError(#[from] ValidationError), @@ -351,3 +348,14 @@ pub enum ProposalError { #[error("error writing proposal to storage")] StorageError(StorageError), } + +/// Remove proposal error +#[derive(Error, Debug, PartialEq, Clone)] +pub enum RemoveProposalError { + /// Couldn't find the proposal for the given `ProposalRef`. + #[error("Couldn't find the proposal for the given `ProposalRef`")] + ProposalNotFound, + /// Error erasing proposal from storage. + #[error("error writing proposal to storage")] + Storage(StorageError), +} diff --git a/openmls/src/group/mls_group/exporting.rs b/openmls/src/group/mls_group/exporting.rs index 36da09ea8..09fa39eff 100644 --- a/openmls/src/group/mls_group/exporting.rs +++ b/openmls/src/group/mls_group/exporting.rs @@ -18,7 +18,7 @@ impl MlsGroup { label: &str, context: &[u8], key_length: usize, - ) -> Result, ExportSecretError> { + ) -> Result, ExportSecretError> { let crypto = provider.crypto(); if self.is_active() { @@ -58,7 +58,7 @@ impl MlsGroup { provider: &Provider, signer: &impl Signer, with_ratchet_tree: bool, - ) -> Result> { + ) -> Result { Ok(self .group .export_group_info(provider.crypto(), signer, with_ratchet_tree)? diff --git a/openmls/src/group/mls_group/membership.rs b/openmls/src/group/mls_group/membership.rs index d0c569d25..a8d87538d 100644 --- a/openmls/src/group/mls_group/membership.rs +++ b/openmls/src/group/mls_group/membership.rs @@ -254,7 +254,7 @@ impl MlsGroup { &queued_remove_proposal.proposal_reference(), &queued_remove_proposal, ) - .map_err(MlsGroupStateError::StorageError)?; + .map_err(LeaveGroupError::StorageError)?; self.proposal_store_mut().add(queued_remove_proposal); diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 09d52f76a..964a82fcd 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -225,9 +225,7 @@ impl MlsGroup { /// Returns own credential. If the group is inactive, it returns a /// `UseAfterEviction` error. - pub fn credential( - &self, - ) -> Result<&Credential, MlsGroupStateError> { + pub fn credential(&self) -> Result<&Credential, MlsGroupStateError> { if !self.is_active() { return Err(MlsGroupStateError::UseAfterEviction); } @@ -439,7 +437,7 @@ impl MlsGroup { /// Check if the group is operational. Throws an error if the group is /// inactive or if there is a pending commit. - fn is_operational(&self) -> Result<(), MlsGroupStateError> { + fn is_operational(&self) -> Result<(), MlsGroupStateError> { match self.group_state { MlsGroupState::PendingCommit(_) => Err(MlsGroupStateError::PendingCommit), MlsGroupState::Inactive => Err(MlsGroupStateError::UseAfterEviction), diff --git a/openmls/src/group/mls_group/processing.rs b/openmls/src/group/mls_group/processing.rs index ee9016108..1a22eb683 100644 --- a/openmls/src/group/mls_group/processing.rs +++ b/openmls/src/group/mls_group/processing.rs @@ -28,7 +28,7 @@ impl MlsGroup { &mut self, provider: &Provider, message: impl Into, - ) -> Result> { + ) -> Result { // Make sure we are still a member of the group if !self.is_active() { return Err(ProcessMessageError::GroupStateError( diff --git a/openmls/src/group/mls_group/proposal.rs b/openmls/src/group/mls_group/proposal.rs index 7c703cc62..bf1fdb3de 100644 --- a/openmls/src/group/mls_group/proposal.rs +++ b/openmls/src/group/mls_group/proposal.rs @@ -4,7 +4,7 @@ use super::{ core_group::create_commit_params::CreateCommitParams, errors::{ProposalError, ProposeAddMemberError, ProposeRemoveMemberError}, CreateGroupContextExtProposalError, CustomProposal, GroupContextExtensionProposal, MlsGroup, - MlsGroupState, MlsGroupStateError, PendingCommitState, Proposal, + MlsGroupState, PendingCommitState, Proposal, RemoveProposalError, }; use crate::{ binary_tree::LeafNodeIndex, @@ -450,12 +450,12 @@ impl MlsGroup { &mut self, storage: &Storage, proposal_ref: &ProposalRef, - ) -> Result<(), MlsGroupStateError> { + ) -> Result<(), RemoveProposalError> { storage .remove_proposal(self.group_id(), proposal_ref) - .map_err(MlsGroupStateError::StorageError)?; + .map_err(RemoveProposalError::Storage)?; self.proposal_store_mut() .remove(proposal_ref) - .ok_or(MlsGroupStateError::PendingProposalNotFound) + .ok_or(RemoveProposalError::ProposalNotFound) } } diff --git a/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs b/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs index 0aa9712c5..dc08ebf34 100644 --- a/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs @@ -1113,7 +1113,7 @@ fn remove_prosposal_by_ref( let err = alice_group .remove_pending_proposal(provider.storage(), &reference) .unwrap_err(); - assert!(matches!(err, MlsGroupStateError::PendingProposalNotFound)); + assert!(matches!(err, RemoveProposalError::ProposalNotFound)); // the commit should have no proposal let (commit, _, _) = alice_group diff --git a/openmls/src/group/public_group/mod.rs b/openmls/src/group/public_group/mod.rs index a94ff6d06..32dffa739 100644 --- a/openmls/src/group/public_group/mod.rs +++ b/openmls/src/group/public_group/mod.rs @@ -36,7 +36,7 @@ use crate::{ ConfirmationTag, PathSecret, }, schedule::CommitSecret, - storage::{OpenMlsProvider, PublicStorageProvider}, + storage::PublicStorageProvider, treesync::{ errors::{DerivePathError, TreeSyncFromNodesError}, node::{ @@ -108,14 +108,14 @@ impl PublicGroup { /// This function performs basic validation checks and returns an error if /// one of the checks fails. See [`CreationFromExternalError`] for more /// details. - pub fn from_external( - provider: &Provider, + pub fn from_external( + crypto: &impl OpenMlsCrypto, + storage: &StorageProvider, ratchet_tree: RatchetTreeIn, verifiable_group_info: VerifiableGroupInfo, proposal_store: ProposalStore, - ) -> Result<(Self, GroupInfo), CreationFromExternalError> { + ) -> Result<(Self, GroupInfo), CreationFromExternalError> { let ciphersuite = verifiable_group_info.ciphersuite(); - let crypto = provider.crypto(); let group_id = verifiable_group_info.group_id(); let ratchet_tree = ratchet_tree @@ -173,7 +173,7 @@ impl PublicGroup { }; public_group - .store(provider.storage()) + .store(storage) .map_err(CreationFromExternalError::WriteToStorageError)?; Ok((public_group, group_info)) @@ -464,7 +464,7 @@ impl PublicGroup { #[cfg(test)] pub(crate) fn encrypt_path( &self, - provider: &impl OpenMlsProvider, + provider: &impl crate::storage::OpenMlsProvider, ciphersuite: Ciphersuite, path: &[PlainUpdatePathNode], group_context: &[u8], diff --git a/openmls/src/group/public_group/process.rs b/openmls/src/group/public_group/process.rs index 7244d0fc0..a689ec367 100644 --- a/openmls/src/group/public_group/process.rs +++ b/openmls/src/group/public_group/process.rs @@ -1,3 +1,4 @@ +use openmls_traits::crypto::OpenMlsCrypto; use tls_codec::Serialize; use crate::{ @@ -13,7 +14,6 @@ use crate::{ mls_group::errors::ProcessMessageError, past_secrets::MessageSecretsStore, }, messages::proposals::Proposal, - storage::OpenMlsProvider, }; use super::PublicGroup; @@ -127,12 +127,11 @@ impl PublicGroup { /// - ValSem244 /// - ValSem245 /// - ValSem246 (as part of ValSem010) - pub fn process_message( + pub fn process_message( &self, - provider: &Provider, + crypto: &impl OpenMlsCrypto, message: impl Into, - ) -> Result> { - let crypto = provider.crypto(); + ) -> Result { let protocol_message = message.into(); // Checks the following semantic validation: // - ValSem002 @@ -159,7 +158,7 @@ impl PublicGroup { let unverified_message = self .parse_message(decrypted_message, None) .map_err(ProcessMessageError::from)?; - self.process_unverified_message(provider, unverified_message) + self.process_unverified_message(crypto, unverified_message) } } @@ -190,17 +189,16 @@ impl PublicGroup { /// - ValSem242 /// - ValSem244 /// - ValSem246 (as part of ValSem010) - pub(crate) fn process_unverified_message( + pub(crate) fn process_unverified_message( &self, - provider: &Provider, + crypto: &impl OpenMlsCrypto, unverified_message: UnverifiedMessage, - ) -> Result> { - let crypto = provider.crypto(); + ) -> Result { // Checks the following semantic validation: // - ValSem010 // - ValSem246 (as part of ValSem010) let (content, credential) = - unverified_message.verify(self.ciphersuite(), provider, self.version())?; + unverified_message.verify(self.ciphersuite(), crypto, self.version())?; match content.sender() { Sender::Member(_) | Sender::NewMemberCommit | Sender::NewMemberProposal => { diff --git a/openmls/src/group/public_group/tests.rs b/openmls/src/group/public_group/tests.rs index f924a26ab..5cf00bcfe 100644 --- a/openmls/src/group/public_group/tests.rs +++ b/openmls/src/group/public_group/tests.rs @@ -51,7 +51,8 @@ fn public_group(ciphersuite: Ciphersuite, provider: & .unwrap(); let ratchet_tree = alice_group.export_ratchet_tree(); let (mut public_group, _extensions) = PublicGroup::from_external( - provider, + provider.crypto(), + provider.storage(), ratchet_tree.into(), verifiable_group_info, ProposalStore::new(), @@ -72,7 +73,7 @@ fn public_group(ciphersuite: Ciphersuite, provider: & ProtocolMessage::PublicMessage(public_message) => public_message, }; let processed_message = public_group - .process_message(provider, public_message) + .process_message(provider.crypto(), public_message) .unwrap(); // Further inspection of the message can take place here ... @@ -134,7 +135,7 @@ fn public_group(ciphersuite: Ciphersuite, provider: & // The public group processes let ppm = public_group - .process_message(provider, into_public_message(queued_messages)) + .process_message(provider.crypto(), into_public_message(queued_messages)) .unwrap(); public_group .merge_commit(provider.storage(), extract_staged_commit(ppm)) @@ -178,7 +179,7 @@ fn public_group(ciphersuite: Ciphersuite, provider: & // The public group processes let ppm = public_group - .process_message(provider, into_public_message(queued_messages)) + .process_message(provider.crypto(), into_public_message(queued_messages)) .unwrap(); // We have to add the proposal to the public group's proposal store. match ppm.into_content() { @@ -225,7 +226,10 @@ fn public_group(ciphersuite: Ciphersuite, provider: & // The public group processes let ppm = public_group - .process_message(provider, into_public_message(queued_messages.clone())) + .process_message( + provider.crypto(), + into_public_message(queued_messages.clone()), + ) .unwrap(); public_group .merge_commit(provider.storage(), extract_staged_commit(ppm)) diff --git a/openmls/src/group/tests_and_kats/tests/external_add_proposal.rs b/openmls/src/group/tests_and_kats/tests/external_add_proposal.rs index 40df36c7c..9c47a2bde 100644 --- a/openmls/src/group/tests_and_kats/tests/external_add_proposal.rs +++ b/openmls/src/group/tests_and_kats/tests/external_add_proposal.rs @@ -269,7 +269,7 @@ fn external_add_proposal_should_be_signed_by_key_package_it_references< alice_group .process_message(provider, invalid_proposal.into_protocol_message().unwrap()) .unwrap_err(), - ProcessMessageError::InvalidSignature + ProcessMessageError::ValidationError(ValidationError::InvalidSignature) )); } diff --git a/openmls/src/group/tests_and_kats/tests/external_commit_validation.rs b/openmls/src/group/tests_and_kats/tests/external_commit_validation.rs index 627381f26..df1031b64 100644 --- a/openmls/src/group/tests_and_kats/tests/external_commit_validation.rs +++ b/openmls/src/group/tests_and_kats/tests/external_commit_validation.rs @@ -509,7 +509,10 @@ fn test_valsem246() { // This shows that signature verification fails if the signature is not done // using the credential in the path. - assert!(matches!(err, ProcessMessageError::InvalidSignature)); + assert!(matches!( + err, + ProcessMessageError::ValidationError(ValidationError::InvalidSignature) + )); // This shows that the credential in the original path key package is actually bob's credential. let commit = if let FramedContentBody::Commit(commit) = public_message_commit.content() { diff --git a/openmls/src/group/tests_and_kats/tests/external_remove_proposal.rs b/openmls/src/group/tests_and_kats/tests/external_remove_proposal.rs index ead7cdfa0..3a5ad98a5 100644 --- a/openmls/src/group/tests_and_kats/tests/external_remove_proposal.rs +++ b/openmls/src/group/tests_and_kats/tests/external_remove_proposal.rs @@ -316,7 +316,10 @@ fn external_remove_proposal_should_fail_when_invalid_signature() { .unwrap(), ) .unwrap_err(); - assert!(matches!(error, ProcessMessageError::InvalidSignature)); + assert!(matches!( + error, + ProcessMessageError::ValidationError(ValidationError::InvalidSignature) + )); } #[openmls_test] diff --git a/openmls/src/group/tests_and_kats/tests/framing_validation.rs b/openmls/src/group/tests_and_kats/tests/framing_validation.rs index 4ddf658ed..3c515a11a 100644 --- a/openmls/src/group/tests_and_kats/tests/framing_validation.rs +++ b/openmls/src/group/tests_and_kats/tests/framing_validation.rs @@ -647,7 +647,10 @@ fn test_valsem010() { .process_message(provider, message_in) .expect_err("Could process message despite wrong signature."); - assert!(matches!(err, ProcessMessageError::InvalidSignature)); + assert!(matches!( + err, + ProcessMessageError::ValidationError(ValidationError::InvalidSignature) + )); // Positive case bob_group diff --git a/openmls/src/group/tests_and_kats/tests/group_context_extensions.rs b/openmls/src/group/tests_and_kats/tests/group_context_extensions.rs index 6d9fef48a..542f4367f 100644 --- a/openmls/src/group/tests_and_kats/tests/group_context_extensions.rs +++ b/openmls/src/group/tests_and_kats/tests/group_context_extensions.rs @@ -235,10 +235,7 @@ impl MemberState { } /// This wrapper that expects [`MlsGroup::process_message`] to return an error. - fn fail_processing( - &mut self, - msg: MlsMessageIn, - ) -> ProcessMessageError { + fn fail_processing(&mut self, msg: MlsMessageIn) -> ProcessMessageError { let msg = msg.into_protocol_message().unwrap(); let err_msg = format!( "expected an error when processing message at {}", diff --git a/openmls/src/group/tests_and_kats/tests/proposal_validation.rs b/openmls/src/group/tests_and_kats/tests/proposal_validation.rs index 21363dc3b..ae81628a2 100644 --- a/openmls/src/group/tests_and_kats/tests/proposal_validation.rs +++ b/openmls/src/group/tests_and_kats/tests/proposal_validation.rs @@ -1263,11 +1263,9 @@ fn test_valsem105() { KeyPackageTestVersion::ValidTestCase => { assert!(matches!( err, - ProcessMessageError::<::StorageError>::InvalidCommit( - StageCommitError::UpdatePathError( - ApplyUpdatePathError::PathLengthMismatch, - ), - ) + ProcessMessageError::InvalidCommit(StageCommitError::UpdatePathError( + ApplyUpdatePathError::PathLengthMismatch, + ),) )); } KeyPackageTestVersion::WrongCiphersuite => { @@ -1279,21 +1277,21 @@ fn test_valsem105() { assert!( matches!( err, - ProcessMessageError::<::StorageError>::InvalidCommit( + ProcessMessageError::InvalidCommit( StageCommitError::ProposalValidationError( ProposalValidationError::InvalidAddProposalCiphersuiteOrVersion, ), ) ) || matches!( err, - ProcessMessageError::<::StorageError>::ValidationError( + ProcessMessageError::ValidationError( ValidationError::KeyPackageVerifyError( KeyPackageVerifyError::InvalidLeafNodeSignature, ), ) ) || matches!( err, - ProcessMessageError::<::StorageError>::ValidationError( + ProcessMessageError::ValidationError( ValidationError::InvalidAddProposalCiphersuite, ) ) @@ -1306,14 +1304,14 @@ fn test_valsem105() { assert!( matches!( err, - ProcessMessageError::<::StorageError>::InvalidCommit( + ProcessMessageError::InvalidCommit( StageCommitError::ProposalValidationError( ProposalValidationError::InvalidAddProposalCiphersuiteOrVersion, ), ) ) || matches!( err, - ProcessMessageError::<::StorageError>::ValidationError( + ProcessMessageError::ValidationError( ValidationError::KeyPackageVerifyError( KeyPackageVerifyError::InvalidProtocolVersion, ), @@ -1325,14 +1323,14 @@ fn test_valsem105() { assert!( matches!( err, - ProcessMessageError::<::StorageError>::ValidationError( + ProcessMessageError::ValidationError( ValidationError::KeyPackageVerifyError( KeyPackageVerifyError::InvalidProtocolVersion, ), ) ) || matches!( err, - ProcessMessageError::<::StorageError>::InvalidCommit( + ProcessMessageError::InvalidCommit( StageCommitError::ProposalValidationError( ProposalValidationError::InsufficientCapabilities, ), @@ -1343,7 +1341,7 @@ fn test_valsem105() { KeyPackageTestVersion::UnsupportedCiphersuite => { assert!(matches!( err, - ProcessMessageError::<::StorageError>::InvalidCommit( + ProcessMessageError::InvalidCommit( StageCommitError::ProposalValidationError( ProposalValidationError::InsufficientCapabilities, ), @@ -2275,10 +2273,7 @@ fn test_valsem401_valsem402() { let bob_provider = Provider::default(); // TODO(#1354): This is currently not tested because we can't easily create invalid commits. - let bad_psks: [( - Vec, - ProcessMessageError<::StorageError>, - ); 0] = [ + let bad_psks: [(Vec, ProcessMessageError); 0] = [ // // ValSem401 // ( // vec![PreSharedKeyId::external( diff --git a/openmls/src/test_utils/test_framework/client.rs b/openmls/src/test_utils/test_framework/client.rs index 1d707f845..9b4ef9c1c 100644 --- a/openmls/src/test_utils/test_framework/client.rs +++ b/openmls/src/test_utils/test_framework/client.rs @@ -157,7 +157,9 @@ impl Client { group_state.clear_pending_commit(self.provider.storage())?; } // Process the message. - let processed_message = group_state.process_message(&self.provider, message.clone())?; + let processed_message = group_state + .process_message(&self.provider, message.clone()) + .map_err(ClientError::ProcessMessageError)?; match processed_message.into_content() { ProcessedMessageContent::ApplicationMessage(_) => {} diff --git a/openmls/src/test_utils/test_framework/errors.rs b/openmls/src/test_utils/test_framework/errors.rs index 370fe9ac4..3375426c1 100644 --- a/openmls/src/test_utils/test_framework/errors.rs +++ b/openmls/src/test_utils/test_framework/errors.rs @@ -23,7 +23,7 @@ pub enum SetupError { ClientError(#[from] ClientError), /// See [`ExportSecretError`] for more details. #[error(transparent)] - ExportSecretError(#[from] ExportSecretError), + ExportSecretError(#[from] ExportSecretError), /// See [`LibraryError`] for more details. #[error(transparent)] LibraryError(#[from] LibraryError), @@ -56,11 +56,8 @@ pub enum ClientError { #[error(transparent)] TlsCodecError(tls_codec::Error), /// See [`ProcessMessageError`] for more details. - #[error(transparent)] - ProcessMessageError(#[from] ProcessMessageError), - /// See [`MlsGroupStateError`] for more details. - #[error(transparent)] - MlsGroupStateError(#[from] MlsGroupStateError), + #[error("See ProcessMessageError for more details.")] + ProcessMessageError(ProcessMessageError), /// See [`AddMembersError`] for more details. #[error(transparent)] AddMembersError(#[from] AddMembersError), @@ -74,8 +71,8 @@ pub enum ClientError { #[error(transparent)] ProposeRemoveMemberError(#[from] ProposeRemoveMemberError), /// See [`ExportSecretError`] for more details. - #[error(transparent)] - ExportSecretError(#[from] ExportSecretError), + #[error("Error exporting secret")] + ExportSecretError(ExportSecretError), /// See [`NewGroupError`] for more details. #[error(transparent)] NewGroupError(#[from] NewGroupError), diff --git a/openmls/src/test_utils/test_framework/mod.rs b/openmls/src/test_utils/test_framework/mod.rs index 22bb330e8..3fec5bc23 100644 --- a/openmls/src/test_utils/test_framework/mod.rs +++ b/openmls/src/test_utils/test_framework/mod.rs @@ -354,7 +354,9 @@ impl MlsGroupTestSetup { ) .collect(); group.public_tree = sender_group.export_ratchet_tree(); - group.exporter_secret = sender_group.export_secret(&sender.provider, "test", &[], 32)?; + group.exporter_secret = sender_group + .export_secret(&sender.provider, "test", &[], 32) + .map_err(ClientError::ExportSecretError)?; Ok(()) } diff --git a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs index 1dac2d5fe..14d14eafb 100644 --- a/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs +++ b/openmls/src/tree/tests_and_kats/kats/kat_message_protection.rs @@ -364,7 +364,7 @@ pub fn run_test_vector( .parse_message(decrypted_message, group.group().message_secrets_store()) .unwrap(); let processed_message: AuthenticatedContent = processed_unverified_message - .verify(ciphersuite, provider, ProtocolVersion::Mls10) + .verify(ciphersuite, provider.crypto(), ProtocolVersion::Mls10) .unwrap() .0; match processed_message.content().to_owned() { diff --git a/openmls/tests/book_code.rs b/openmls/tests/book_code.rs index 2000033f5..ce5a84b3e 100644 --- a/openmls/tests/book_code.rs +++ b/openmls/tests/book_code.rs @@ -1117,9 +1117,7 @@ fn book_operations() { assert_eq!(sender_cred_from_msg, sender_cred_from_group); assert_eq!( &sender_cred_from_msg, - alice_group - .credential::() - .expect("Expected a credential.") + alice_group.credential().expect("Expected a credential.") ); } else { unreachable!("Expected an ApplicationMessage."); diff --git a/openmls/tests/mls_group.rs b/openmls/tests/mls_group.rs index c8a4c8aaa..cae9cb49c 100644 --- a/openmls/tests/mls_group.rs +++ b/openmls/tests/mls_group.rs @@ -176,7 +176,7 @@ fn mls_group_operations() { assert_eq!( &sender, alice_group - .credential::() + .credential() .expect("An unexpected error occurred.") ); } else { @@ -805,9 +805,7 @@ fn mls_group_operations() { // Check that Alice sent the message assert_eq!( &sender, - alice_group - .credential::() - .expect("Expected a credential") + alice_group.credential().expect("Expected a credential") ); } else { unreachable!("Expected an ApplicationMessage."); From 8c4671dd17654ab3a881787290c1d84095de594d Mon Sep 17 00:00:00 2001 From: Konrad Kohbrok Date: Tue, 13 Aug 2024 16:03:08 +0200 Subject: [PATCH 42/43] Remove `use_ratchet_tree_extension` storage (#1643) --- CHANGELOG.md | 4 +++- memory_storage/src/lib.rs | 27 --------------------------- memory_storage/src/test_store.rs | 22 ---------------------- openmls/src/group/core_group/mod.rs | 4 +--- openmls/src/group/mls_group/mod.rs | 8 +++++++- traits/src/storage.rs | 20 -------------------- 6 files changed, 11 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6ebf174..15f382821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,9 +16,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1637](https://github.com/openmls/openmls/pull/1637): Remove `serde` from `MlsGroup`. - [#1638](https://github.com/openmls/openmls/pull/1638): Remove `serde` from `PublicGroup`. `PublicGroup::load()` becomes public to load a group from the storage provider. -- [#1640](https://github.com/openmls/openmls/pull/1640): The storage provider function `treesync()` was renamed to `tree()` so that it is consistent with other treesync functions. - [#1642](https://github.com/openmls/openmls/pull/1642): `OpenMlsProvider` is no longer required for the `PublicGroup` API. The `PublicGroup` API now uses the `PublicStorageProvider` trait directly. `ProcessMessageError::InvalidSignature` was removed and replaced with `ValidationError::InvalidSignature`. +### Removed + + ### Fixed - [#1641](https://github.com/openmls/openmls/pull/1641): Fixed missing storage of queued proposals & clearing of the queued proposals. diff --git a/memory_storage/src/lib.rs b/memory_storage/src/lib.rs index 6d973a761..b59c3b787 100644 --- a/memory_storage/src/lib.rs +++ b/memory_storage/src/lib.rs @@ -270,7 +270,6 @@ const OWN_LEAF_NODE_INDEX_LABEL: &[u8] = b"OwnLeafNodeIndex"; const EPOCH_SECRETS_LABEL: &[u8] = b"EpochSecrets"; const RESUMPTION_PSK_STORE_LABEL: &[u8] = b"ResumptionPsk"; const MESSAGE_SECRETS_LABEL: &[u8] = b"MessageSecrets"; -const USE_RATCHET_TREE_LABEL: &[u8] = b"UseRatchetTree"; // related to MlsGroup const JOIN_CONFIG_LABEL: &[u8] = b"MlsGroupJoinConfig"; @@ -741,32 +740,6 @@ impl StorageProvider for MemoryStorage { self.delete::(OWN_LEAF_NODE_INDEX_LABEL, &serde_json::to_vec(group_id)?) } - fn use_ratchet_tree_extension>( - &self, - group_id: &GroupId, - ) -> Result, Self::Error> { - self.read(USE_RATCHET_TREE_LABEL, &serde_json::to_vec(group_id)?) - } - - fn set_use_ratchet_tree_extension>( - &self, - group_id: &GroupId, - value: bool, - ) -> Result<(), Self::Error> { - self.write::( - USE_RATCHET_TREE_LABEL, - &serde_json::to_vec(group_id)?, - serde_json::to_vec(&value)?, - ) - } - - fn delete_use_ratchet_tree_extension>( - &self, - group_id: &GroupId, - ) -> Result<(), Self::Error> { - self.delete::(USE_RATCHET_TREE_LABEL, &serde_json::to_vec(group_id)?) - } - fn group_epoch_secrets< GroupId: traits::GroupId, GroupEpochSecrets: traits::GroupEpochSecrets, diff --git a/memory_storage/src/test_store.rs b/memory_storage/src/test_store.rs index f24fa455a..949f42d68 100644 --- a/memory_storage/src/test_store.rs +++ b/memory_storage/src/test_store.rs @@ -388,28 +388,6 @@ impl StorageProvider for MemoryStorage { todo!() } - fn use_ratchet_tree_extension>( - &self, - _group_id: &GroupId, - ) -> Result, Self::Error> { - todo!() - } - - fn set_use_ratchet_tree_extension>( - &self, - _group_id: &GroupId, - _value: bool, - ) -> Result<(), Self::Error> { - todo!() - } - - fn delete_use_ratchet_tree_extension>( - &self, - _group_id: &GroupId, - ) -> Result<(), Self::Error> { - todo!() - } - fn group_epoch_secrets< GroupId: traits::GroupId, GroupEpochSecrets: traits::GroupEpochSecrets, diff --git a/openmls/src/group/core_group/mod.rs b/openmls/src/group/core_group/mod.rs index 3ef8ca591..d3f7f08ec 100644 --- a/openmls/src/group/core_group/mod.rs +++ b/openmls/src/group/core_group/mod.rs @@ -722,7 +722,6 @@ impl CoreGroup { self.public_group.store(storage)?; storage.write_own_leaf_index(group_id, &self.own_leaf_index())?; storage.write_group_epoch_secrets(group_id, &self.group_epoch_secrets)?; - storage.set_use_ratchet_tree_extension(group_id, self.use_ratchet_tree_extension)?; storage.write_message_secrets(group_id, &self.message_secrets_store)?; storage.write_resumption_psk_store(group_id, &self.resumption_psk_store)?; @@ -733,11 +732,11 @@ impl CoreGroup { pub(super) fn load( storage: &Storage, group_id: &GroupId, + use_ratchet_tree_extension: Option, ) -> Result, Storage::Error> { let public_group = PublicGroup::load(storage, group_id)?; let group_epoch_secrets = storage.group_epoch_secrets(group_id)?; let own_leaf_index = storage.own_leaf_index(group_id)?; - let use_ratchet_tree_extension = storage.use_ratchet_tree_extension(group_id)?; let message_secrets_store = storage.message_secrets(group_id)?; let resumption_psk_store = storage.resumption_psk_store(group_id)?; @@ -762,7 +761,6 @@ impl CoreGroup { self.public_group.delete(storage)?; storage.delete_own_leaf_index(self.group_id())?; storage.delete_group_epoch_secrets(self.group_id())?; - storage.delete_use_ratchet_tree_extension(self.group_id())?; storage.delete_message_secrets(self.group_id())?; storage.delete_all_resumption_psk_secrets(self.group_id())?; diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 964a82fcd..5a4363fb7 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -345,7 +345,13 @@ impl MlsGroup { group_id: &GroupId, ) -> Result, Storage::Error> { let group_config = storage.mls_group_join_config(group_id)?; - let core_group = CoreGroup::load(storage, group_id)?; + let core_group = CoreGroup::load( + storage, + group_id, + group_config + .as_ref() + .map(|config: &MlsGroupJoinConfig| config.use_ratchet_tree_extension), + )?; let own_leaf_nodes = storage.own_leaf_nodes(group_id)?; let aad = Vec::new(); let group_state = storage.group_state(group_id)?; diff --git a/traits/src/storage.rs b/traits/src/storage.rs index 892de66c2..593474e4a 100644 --- a/traits/src/storage.rs +++ b/traits/src/storage.rs @@ -151,14 +151,6 @@ pub trait StorageProvider { own_leaf_index: &LeafNodeIndex, ) -> Result<(), Self::Error>; - /// Returns the MlsGroupState for group with given id. - /// Sets whether to use the RatchetTreeExtension for the group with the given id. - fn set_use_ratchet_tree_extension>( - &self, - group_id: &GroupId, - value: bool, - ) -> Result<(), Self::Error>; - /// Writes the GroupEpochSecrets for the group with the given id. fn write_group_epoch_secrets< GroupId: traits::GroupId, @@ -355,12 +347,6 @@ pub trait StorageProvider { group_id: &GroupId, ) -> Result, Self::Error>; - /// Returns whether to use the RatchetTreeExtension for the group with the given id. - fn use_ratchet_tree_extension>( - &self, - group_id: &GroupId, - ) -> Result, Self::Error>; - /// Returns the GroupEpochSecrets for the group with the given id. fn group_epoch_secrets< GroupId: traits::GroupId, @@ -501,12 +487,6 @@ pub trait StorageProvider { group_id: &GroupId, ) -> Result<(), Self::Error>; - /// Deletes any preference about whether to use the RatchetTreeExtension for the group with the given id. - fn delete_use_ratchet_tree_extension>( - &self, - group_id: &GroupId, - ) -> Result<(), Self::Error>; - /// Deletes the GroupEpochSecrets for the group with the given id. fn delete_group_epoch_secrets>( &self, From ed075ead9eaadd4856c8d67fd0c4384fb0f02952 Mon Sep 17 00:00:00 2001 From: Konrad Kohbrok Date: Wed, 14 Aug 2024 08:30:55 +0200 Subject: [PATCH 43/43] Fix `MlsGroup::delete()` function to delete encryption keypairs (#1644) --- basic_credential/src/lib.rs | 12 ++++++ memory_storage/src/lib.rs | 25 +++++++---- openmls/src/group/mls_group/mod.rs | 15 +++++-- .../tests_and_kats/tests/mls_group.rs | 41 +++++++++++++++++-- 4 files changed, 77 insertions(+), 16 deletions(-) diff --git a/basic_credential/src/lib.rs b/basic_credential/src/lib.rs index 350ea4fc6..9dda632ba 100644 --- a/basic_credential/src/lib.rs +++ b/basic_credential/src/lib.rs @@ -137,6 +137,18 @@ impl SignatureKeyPair { .flatten() } + /// Delete a signature key pair from the key store. + pub fn delete>( + store: &T, + public_key: &[u8], + signature_scheme: SignatureScheme, + ) -> Result<(), T::Error> { + let id = StorageId { + value: id(public_key, signature_scheme), + }; + store.delete_signature_key_pair(&id) + } + /// Get the public key as byte slice. pub fn public(&self) -> &[u8] { self.public.as_ref() diff --git a/memory_storage/src/lib.rs b/memory_storage/src/lib.rs index b59c3b787..9635be228 100644 --- a/memory_storage/src/lib.rs +++ b/memory_storage/src/lib.rs @@ -203,10 +203,7 @@ impl MemoryStorage { log::trace!("{}", std::backtrace::Backtrace::capture()); let value: Vec> = match values.get(&storage_key) { - Some(list_bytes) => { - println!("{}", String::from_utf8(list_bytes.to_vec()).unwrap()); - serde_json::from_slice(list_bytes).unwrap() - } + Some(list_bytes) => serde_json::from_slice(list_bytes).unwrap(), None => vec![], }; @@ -426,7 +423,9 @@ impl StorageProvider for MemoryStorage { let values = self.values.read().unwrap(); let key = build_key::(TREE_LABEL, group_id); - let value = values.get(&key).unwrap(); + let Some(value) = values.get(&key) else { + return Ok(None); + }; let value = serde_json::from_slice(value).unwrap(); Ok(value) @@ -442,7 +441,9 @@ impl StorageProvider for MemoryStorage { let values = self.values.read().unwrap(); let key = build_key::(GROUP_CONTEXT_LABEL, group_id); - let value = values.get(&key).unwrap(); + let Some(value) = values.get(&key) else { + return Ok(None); + }; let value = serde_json::from_slice(value).unwrap(); Ok(value) @@ -458,7 +459,9 @@ impl StorageProvider for MemoryStorage { let values = self.values.read().unwrap(); let key = build_key::(INTERIM_TRANSCRIPT_HASH_LABEL, group_id); - let value = values.get(&key).unwrap(); + let Some(value) = values.get(&key) else { + return Ok(None); + }; let value = serde_json::from_slice(value).unwrap(); Ok(value) @@ -474,7 +477,9 @@ impl StorageProvider for MemoryStorage { let values = self.values.read().unwrap(); let key = build_key::(CONFIRMATION_TAG_LABEL, group_id); - let value = values.get(&key).unwrap(); + let Some(value) = values.get(&key) else { + return Ok(None); + }; let value = serde_json::from_slice(value).unwrap(); Ok(value) @@ -492,7 +497,9 @@ impl StorageProvider for MemoryStorage { let key = build_key::(SIGNATURE_KEY_PAIR_LABEL, public_key); - let value = values.get(&key).unwrap(); + let Some(value) = values.get(&key) else { + return Ok(None); + }; let value = serde_json::from_slice(value).unwrap(); Ok(value) diff --git a/openmls/src/group/mls_group/mod.rs b/openmls/src/group/mls_group/mod.rs index 5a4363fb7..1538f2021 100644 --- a/openmls/src/group/mls_group/mod.rs +++ b/openmls/src/group/mls_group/mod.rs @@ -369,16 +369,23 @@ impl MlsGroup { Ok(build()) } - /// Remove the persisted state from storage - pub fn delete( + /// Remove the persisted state of this group from storage. Note that + /// signature key material is not managed by OpenMLS and has to be removed + /// from the storage provider separately (if desired). + pub fn delete( &mut self, - storage: &StorageProvider, - ) -> Result<(), StorageProvider::Error> { + storage: &Storage, + ) -> Result<(), Storage::Error> { self.group.delete(storage)?; storage.delete_group_config(self.group_id())?; storage.clear_proposal_queue::(self.group_id())?; storage.delete_own_leaf_nodes(self.group_id())?; storage.delete_group_state(self.group_id())?; + storage.delete_encryption_epoch_key_pairs( + self.group_id(), + &self.epoch(), + self.own_leaf_index().u32(), + )?; self.proposal_store_mut().empty(); diff --git a/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs b/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs index dc08ebf34..849885fb3 100644 --- a/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs +++ b/openmls/src/group/mls_group/tests_and_kats/tests/mls_group.rs @@ -1,6 +1,8 @@ use mls_group::tests_and_kats::utils::setup_client; +use openmls_basic_credential::SignatureKeyPair; +use openmls_rust_crypto::MemoryStorage; use openmls_test::openmls_test; -use openmls_traits::OpenMlsProvider as _; +use openmls_traits::{storage::CURRENT_VERSION, OpenMlsProvider as _}; use tls_codec::{Deserialize, Serialize}; use crate::{ @@ -1260,7 +1262,7 @@ fn builder_pattern() { fn update_group_context_with_unknown_extension() { let alice_provider = Provider::default(); let (alice_credential_with_key, _alice_kpb, alice_signer, _alice_pk) = - setup_client("Alice", ciphersuite, &alice_provider); + setup_client("Alice", ciphersuite, provider); // === Define the unknown group context extension and initial data === const UNKNOWN_EXTENSION_TYPE: u16 = 0xff11; @@ -1288,7 +1290,7 @@ fn update_group_context_with_unknown_extension>:: + delete_key_package(alice_provider.storage(),&alice_kpb.key_package().hash_ref(provider.crypto()).unwrap()) + .unwrap(); + >:: + delete_encryption_key_pair(alice_provider.storage(),alice_kpb.key_package().leaf_node().encryption_key()).unwrap(); + + // alice creates MlsGroup + let mut alice_group = MlsGroup::builder() + .ciphersuite(ciphersuite) + .use_ratchet_tree_extension(true) + .build(provider, &alice_signer, alice_credential_with_key) + .expect("error creating group for alice using builder"); + + SignatureKeyPair::delete( + alice_provider.storage(), + alice_pk.as_slice(), + ciphersuite.signature_algorithm(), + ) + .unwrap(); + + // alice deletes the group + alice_group.delete(alice_provider.storage()).unwrap(); + + assert!(alice_provider.storage().values.read().unwrap().is_empty()); +}